xsh_1997 1 tydzień temu
rodzic
commit
3e8abf8d37
3 zmienionych plików z 62 dodań i 18 usunięć
  1. 4 0
      shop-app/.env.production
  2. 9 5
      shop-app/config/index.js
  3. 49 13
      shop-app/utils/image.js

+ 4 - 0
shop-app/.env.production

@@ -7,5 +7,9 @@ ENV=production
7 7
 # 若依后端 API(与 ruoyi-ui 一致,由 Nginx 反代到真实后端)
8 8
 VITE_APP_BASE_API=/shop-api
9 9
 
10
+# H5 生产图片会自动用浏览器当前访问的 IP/域名,一般无需配置
11
+# 仅微信小程序/App 打包时取消注释并填写 https 域名:
12
+# VITE_APP_API_HOST=https://shop.xxx.com
13
+
10 14
 # H5 部署子路径(须与 manifest.json h5.router.base 一致,如 /bqH5/)
11 15
 VITE_APP_PUBLIC_PATH=bqShop

+ 9 - 5
shop-app/config/index.js

@@ -7,8 +7,12 @@
7 7
  * 打包 H5:uni build -p h5
8 8
  */
9 9
 
10
+/** 开发兜底 / 小程序未配域名时的默认值(生产 H5 不应依赖此项) */
10 11
 const apiHost = String(import.meta.env.VITE_APP_API_HOST || 'http://192.168.1.6:8020').replace(/\/$/, '')
11 12
 
13
+/** .env 显式配置的后端域名(小程序/App 生产环境必填 https) */
14
+const hostFromEnv = String(import.meta.env.VITE_APP_API_HOST || '').trim().replace(/\/$/, '')
15
+
12 16
 /** 与 ruoyi-ui 的 VUE_APP_BASE_API 同源配置(dev/prod 由 .env 注入) */
13 17
 const envBaseApi = String(import.meta.env.VITE_APP_BASE_API || '').trim()
14 18
 
@@ -34,13 +38,13 @@ function resolveBaseApi() {
34 38
 
35 39
 export const BASE_API = resolveBaseApi()
36 40
 
37
-/** 后端完整地址(含 IP/域名),来自 .env 的 VITE_APP_API_HOST */
38
-export const API_HOST = apiHost
39
-
40 41
 /**
41
- * 图片/文件访问根地址(始终用完整 IP/域名
42
- * 说明:<image> 标签不会走 Vite 的 /dev-api 代理,必须用 API_HOST 直连后端
42
+ * 后端完整地址(开发 / 小程序打包用 .env 的 VITE_APP_API_HOST)
43
+ * H5 生产图片在运行时由 getFileBase() 自动取 window.location.origin
43 44
  */
45
+export const API_HOST = hostFromEnv || apiHost
46
+
47
+/** 图片根地址(构建时);H5 生产以运行时 getFileBase() 为准 */
44 48
 export const FILE_BASE = API_HOST
45 49
 
46 50
 /**

+ 49 - 13
shop-app/utils/image.js

@@ -1,6 +1,7 @@
1
-import { joinFileUrl, API_HOST } from '@/config'
1
+import { FILE_BASE, API_HOST } from '@/config'
2 2
 
3 3
 const PLACEHOLDER_PATHS = ['/static/logo.png']
4
+const API_PROXY_PREFIX = /^\/(dev-api|prod-api|shop-api)\//i
4 5
 
5 6
 /** 是否外链或 data URL */
6 7
 export function isExternalUrl(url) {
@@ -8,7 +9,7 @@ export function isExternalUrl(url) {
8 9
   return /^(https?:|data:|\/\/)/i.test(String(url).trim())
9 10
 }
10 11
 
11
-/** 去掉 /dev-api 等代理前缀,得到后端真实路径 */
12
+/** 去掉 /dev-api、/shop-api 等代理前缀,得到后端真实路径 */
12 13
 function stripApiProxyPrefix(path) {
13 14
   return String(path).replace(/^\/(dev-api|prod-api|shop-api)/, '') || '/'
14 15
 }
@@ -18,6 +19,32 @@ function isPlaceholderPath(path) {
18 19
   return PLACEHOLDER_PATHS.includes(raw)
19 20
 }
20 21
 
22
+/**
23
+ * 图片根地址
24
+ * - H5 生产:自动取浏览器当前访问的 IP/域名(window.location.origin)
25
+ * - H5 开发 / 小程序:用 .env 里的 VITE_APP_API_HOST
26
+ */
27
+export function getFileBase() {
28
+  // #ifdef H5
29
+  if (import.meta.env.PROD && typeof window !== 'undefined') {
30
+    const origin = window.location?.origin
31
+    if (origin && origin !== 'null') {
32
+      return origin.replace(/\/$/, '')
33
+    }
34
+  }
35
+  // #endif
36
+  return String(FILE_BASE).replace(/\/$/, '')
37
+}
38
+
39
+/** 拼接图片完整 URL */
40
+function joinFileUrlRuntime(path) {
41
+  const base = getFileBase()
42
+  if (!path) return base
43
+  if (/^https?:\/\//i.test(path)) return path
44
+  const p = path.startsWith('/') ? path : `/${path}`
45
+  return base + p
46
+}
47
+
21 48
 /**
22 49
  * 是否已是可直接使用的图片地址(避免重复拼接)
23 50
  */
@@ -26,14 +53,17 @@ export function isResolvedFileUrl(url) {
26 53
   const raw = String(url).trim()
27 54
   if (isExternalUrl(raw)) return true
28 55
   if (raw.startsWith('/static/')) return true
56
+  const fileBase = getFileBase()
57
+  if (fileBase && (raw === fileBase || raw.startsWith(`${fileBase}/`))) return true
58
+  if (API_PROXY_PREFIX.test(raw)) return true
29 59
   const apiHost = String(API_HOST).replace(/\/$/, '')
30
-  return !!(apiHost && (raw === apiHost || raw.startsWith(`${apiHost}/`)))
60
+  return !!(apiHost && apiHost.startsWith('http') && (raw === apiHost || raw.startsWith(`${apiHost}/`)))
31 61
 }
32 62
 
33 63
 /**
34
- * 将后端返回的图片路径转为可展示的完整 URL(可重复调用)
35
- * 结果形如:http://192.168.1.6:8020/profile/upload/xxx.jpg
36
- * @param {string} path bannerImage / categoryPic / mainPic 等
64
+ * 将后端返回的图片路径转为可展示的 URL(可重复调用)
65
+ * - H5 生产:http://当前访问IP/profile/upload/xxx.jpg(自动)
66
+ * - 开发:http://VITE_APP_API_HOST/profile/upload/xxx.jpg
37 67
  */
38 68
 export function resolveFileUrl(path) {
39 69
   if (path == null || path === '') {
@@ -43,26 +73,32 @@ export function resolveFileUrl(path) {
43 73
   if (!raw || isPlaceholderPath(raw)) {
44 74
     return ''
45 75
   }
46
-  if (isExternalUrl(raw)) {
76
+  if (isExternalUrl(raw) || raw.startsWith('/static/')) {
77
+    return raw
78
+  }
79
+
80
+  const fileBase = getFileBase()
81
+  if (fileBase && (raw === fileBase || raw.startsWith(`${fileBase}/`))) {
47 82
     return raw
48 83
   }
49
-  if (raw.startsWith('/static/')) {
84
+  if (API_PROXY_PREFIX.test(raw)) {
50 85
     return raw
51 86
   }
87
+
52 88
   const apiHost = String(API_HOST).replace(/\/$/, '')
53
-  if (apiHost && (raw === apiHost || raw.startsWith(`${apiHost}/`))) {
89
+  if (apiHost && apiHost.startsWith('http') && (raw === apiHost || raw.startsWith(`${apiHost}/`))) {
54 90
     return raw
55 91
   }
56
-  if (/^\/(dev-api|prod-api|shop-api)\//i.test(raw)) {
92
+
93
+  if (API_PROXY_PREFIX.test(raw)) {
57 94
     raw = stripApiProxyPrefix(raw)
58 95
   }
59 96
   const normalized = raw.startsWith('/') ? raw : `/${raw}`
60
-  return joinFileUrl(normalized)
97
+  return joinFileUrlRuntime(normalized)
61 98
 }
62 99
 
63 100
 /**
64
- * 将逗号分隔或数组形式的图片路径转为完整 URL 列表
65
- * @param {string|string[]|null|undefined} pathOrList
101
+ * 将逗号分隔或数组形式的图片路径转为 URL 列表
66 102
  */
67 103
 export function resolveFileUrlList(pathOrList) {
68 104
   if (pathOrList == null || pathOrList === '') return []