FanLide 10 месяцев назад
Родитель
Сommit
76d43e5b29

+ 6 - 0
.env.dev

@@ -13,6 +13,12 @@ VITE_UPLOAD_TYPE=server
 VITE_UPLOAD_URL='http://localhost:48081/admin-api/infra/file/upload'
 # VITE_UPLOAD_URL='https://api.ifoodme.com/admin-api/infra/file/upload'
 
+# 是否使用本地图片映射
+VITE_USE_LOCAL_IMAGES=true
+
+# 本地图片根目录
+VITE_LOCAL_IMAGE_BASE=https://api.ifoodme.com
+
 # 接口地址
 VITE_API_URL=/admin-api
 

+ 6 - 0
.env.local

@@ -15,6 +15,12 @@ VITE_UPLOAD_TYPE=server
 VITE_UPLOAD_URL='https://api.ifoodme.com/admin-api/infra/file/upload'
 # VITE_UPLOAD_URL='http://localhost:48081/admin-api/infra/file/upload'
 
+# 是否使用本地图片映射
+VITE_USE_LOCAL_IMAGES=true
+
+# 本地图片根目录
+VITE_LOCAL_IMAGE_BASE=https://api.ifoodme.com
+
 
 # 接口地址
 VITE_API_URL=/admin-api

+ 6 - 0
.env.prod

@@ -11,6 +11,12 @@ VITE_UPLOAD_TYPE=server
 # 上传路径
 VITE_UPLOAD_URL='https://api.ifoodme.com/admin-api/infra/file/upload'
 
+# 是否使用本地图片映射
+VITE_USE_LOCAL_IMAGES=true
+
+# 本地图片根目录
+VITE_LOCAL_IMAGE_BASE=https://api.ifoodme.com
+
 # 接口地址
 VITE_API_URL=/admin-api
 

+ 28 - 0
public/local-images/README.md

@@ -0,0 +1,28 @@
+# 本地图片目录
+
+这个目录用于存放本地图片,用于替换远程图片URL。
+
+## 目录结构
+
+请按照以下结构组织图片:
+
+```
+local-images/
+  1/  # 对应服务器上的图片ID目录
+    image1.jpg
+    image2.png
+  2/
+    image3.jpg
+  ...
+```
+
+## 使用方法
+
+1. 将远程图片下载到本地
+2. 按照原始URL中的ID和文件名组织目录结构
+3. 在环境变量中设置 `VITE_USE_LOCAL_IMAGES=true` 启用本地图片映射
+
+## 示例
+
+远程URL: `http://localhost:48081/admin-api/infra/file/1/get/example.jpg`
+本地路径: `/local-images/1/example.jpg` 

+ 9 - 4
src/components/Materials/src/Materials.vue

@@ -3,7 +3,7 @@
     <ul v-for="(item, index) in value" :key="index" class="el-upload-list el-upload-list--picture-card">
       <li tabindex="0" class="el-upload-list__item is-ready" :style="'width: ' + width + 'px;height: ' + height + 'px'">
         <div>
-          <img :src="item" alt="" class="el-upload-list__item-thumbnail" />
+          <img :src="convertedImageUrl(item)" alt="" class="el-upload-list__item-thumbnail" />
           <span class="el-upload-list__item-actions">
             <span v-if="index != 0" class="el-upload-list__item-preview" @click="moveMaterial(index, 'up')">
               <Icon icon="ep:back" />
@@ -96,9 +96,9 @@
                     <el-card :body-style="{ padding: '5px' }">
                       <el-image
                         style="width: 100%; height: 100px"
-                        :src="VITE_MALL_H5_DOMAIN + item.url"
+                        :src="convertedImageUrl(item.url)"
                         fit="contain"
-                        :preview-src-list="[item.url]"
+                        :preview-src-list="[convertedImageUrl(item.url)]"
                         :z-index="9999"
                       />
                       <div>
@@ -150,7 +150,8 @@ import {
 } from '@/api/tools/materialgroup'
 import { getPage, addObj, delObj } from '@/api/tools/material'
 import { getAccessToken } from '@/utils/auth'
-const { VITE_MALL_H5_DOMAIN } = import.meta.env
+import { convertImageUrl } from '@/utils/image-helper'
+
 const { t } = useI18n()
 
 const props = defineProps({
@@ -453,6 +454,10 @@ function sureUrls() {
     emit('update:modelValue', value.value[0])
   }
 }
+
+const convertedImageUrl = (url: string) => {
+  return convertImageUrl(url);
+}
 </script>
 
 <style lang="scss" scoped>

+ 37 - 0
src/components/common/AppImage.vue

@@ -0,0 +1,37 @@
+<template>
+  <img :src="processedSrc" :alt="alt" v-bind="$attrs" @error="handleError" />
+</template>
+
+<script setup lang="ts">
+import { computed, ref } from 'vue'
+import { convertImageUrl } from '@/utils/image-helper'
+
+const props = defineProps({
+  src: {
+    type: String,
+    required: true
+  },
+  alt: {
+    type: String,
+    default: ''
+  },
+  fallbackSrc: {
+    type: String,
+    default: '/images/no-image.png'
+  }
+})
+
+const imgError = ref(false)
+
+const processedSrc = computed(() => {
+  if (imgError.value && props.fallbackSrc) {
+    return props.fallbackSrc
+  }
+  return convertImageUrl(props.src)
+})
+
+const handleError = () => {
+  console.warn(`图片加载失败: ${props.src}`)
+  imgError.value = true
+}
+</script>

+ 19 - 0
src/config/image-mapping.ts

@@ -0,0 +1,19 @@
+/**
+ * 图片映射配置
+ * 可以为特定的远程图片URL指定本地路径
+ */
+export const imageMapping: Record<string, string> = {
+  // 远程URL: 本地路径
+  // 示例:
+  // 'http://localhost:48081/admin-api/infra/file/1/get/example.jpg': '/local-images/custom/special-example.jpg',
+  // 可以添加更多映射...
+}
+
+/**
+ * 获取映射的本地路径
+ * @param url 远程图片URL
+ * @returns 映射的本地路径,如果没有映射则返回null
+ */
+export const getMappedImagePath = (url: string): string | null => {
+  return imageMapping[url] || null
+}

+ 6 - 0
src/main.ts

@@ -43,6 +43,9 @@ import VueUeditorWrap from 'vue-ueditor-wrap'
 
 import VueDOMPurifyHTML from 'vue-dompurify-html' // 解决v-html 的安全隐患
 
+// 导入全局图片组件
+import AppImage from '@/components/common/AppImage.vue'
+
 // 创建实例
 const setupAll = async () => {
   const app = createApp(App)
@@ -62,6 +65,9 @@ const setupAll = async () => {
 
   setupAuth(app)
 
+  // 注册全局图片组件
+  app.component('AppImage', AppImage)
+
   await router.isReady()
 
   app.use(VueDOMPurifyHTML)

+ 74 - 0
src/utils/image-helper.ts

@@ -0,0 +1,74 @@
+/**
+ * 图片URL处理工具
+ * 将远程图片URL转换为本地路径
+ */
+import { getMappedImagePath } from '@/config/image-mapping'
+
+/**
+ * 转换图片URL为本地路径
+ * @param url 原始图片URL
+ * @returns 转换后的URL
+ */
+export const convertImageUrl = (url: string): string => {
+  if (!url) return ''
+
+  // 判断是否启用本地图片映射
+  const useLocalMapping = import.meta.env.VITE_USE_LOCAL_IMAGES === 'true'
+  if (!useLocalMapping) return url
+
+  // 首先检查是否有特定的映射配置
+  const mappedPath = getMappedImagePath(url)
+  if (mappedPath) {
+    return mappedPath
+  }
+
+  // 方案1: URL前缀替换
+  const apiBaseUrl = import.meta.env.VITE_API_BASE_URL || ''
+  const localImageBase = import.meta.env.VITE_LOCAL_IMAGE_BASE || '/local-images'
+
+  // 处理完整URL的情况
+  if (url.startsWith('http://') || url.startsWith('https://')) {
+    // 如果URL包含API基础路径,替换为本地路径
+    if (url.includes(apiBaseUrl)) {
+      return url.replace(apiBaseUrl, localImageBase)
+    }
+  }
+
+  // 方案4: 特定路径模式匹配
+  // 处理相对路径或特定格式的URL
+  if (url.includes('/admin-api/infra/file')) {
+    // 提取图片路径部分
+    const matches = url.match(/\/admin-api\/infra\/file\/(\d+)\/get\/(.*)/)
+    if (matches && matches.length >= 3) {
+      const folderId = matches[1]
+      const fileName = matches[2]
+
+      // 构建本地图片路径
+      return `${localImageBase}/${folderId}/${fileName}`
+    }
+  }
+
+  // 如果没有匹配任何规则,返回原始URL
+  return url
+}
+
+/**
+ * 检查URL是否为图片
+ * @param url 要检查的URL
+ * @returns 是否为图片URL
+ */
+export const isImageUrl = (url: string): boolean => {
+  if (!url) return false
+  return /\.(jpg|jpeg|png|gif|bmp|webp)$/i.test(url)
+}
+
+/**
+ * 获取图片文件名
+ * @param url 图片URL
+ * @returns 文件名
+ */
+export const getImageFileName = (url: string): string => {
+  if (!url) return ''
+  const parts = url.split('/')
+  return parts[parts.length - 1]
+}