@microi.net/cli 5.3.9 → 5.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,13 +11,25 @@ description: Microi UniApp、H5、商城与 PC 页面中的上传图片、附件
11
11
 
12
12
  ## 先识别资源类型
13
13
 
14
- Microi 上传字段通常保存对象存储 Key 或相对路径,例如 `/demo/product/20260101/p1.jpg`。页面不能把相对路径直接交给 `<image>` / `<img>`,也不能一律拼接 API Base。
14
+ Microi 上传字段存在四种必须长期兼容的存储形态,不能假设字段一定是字符串:
15
+
16
+ | 存储形态 | 示例 | 兼容要求 |
17
+ |---|---|---|
18
+ | 旧版绝对地址字符串 | `https://cdn.example.com/a.png` | 校验协议和允许域名后原样使用,禁止再次拼接 `FileServer` |
19
+ | 旧版相对路径字符串 | `/tenant/a.png` | 仅此类纯相对对象 Key 拼接当前租户 `FileServer` |
20
+ | 新版单图对象 | `{ FilePathName:'/tenant/a.png', Name:'a.png' }` | 提取路径字段;保留对象的名称、大小、Id 等元数据,不得把整个对象转成字符串 |
21
+ | 新版多图数组 | `[{ FilePathName:'/tenant/a.png' }, ...]` | 逐项解析并保持原顺序;单图展示取第一个有效路径,多图展示返回全部有效项 |
22
+
23
+ 数据库或接口还可能返回上述对象/数组的 JSON 字符串,解析器也必须兼容。标准路径键按以下顺序识别:`Url`、`FileUrl`、`FileURL`、`PreviewUrl`、`PreviewURL`、`FullUrl`、`Path`、`FilePathName`、`FilePath`、`FullPath`、`Src`、`Href`,并兼容相应 camelCase 键。
24
+
25
+ 页面不能把相对路径直接交给 `<image>` / `<img>`,也不能一律拼接 API Base。项目已使用 `microi.v8.js` 时,优先直接复用 `V8.extractUploadPath`、返回路径数组的 `V8.normalizeUploadValue`、保留 `{ item, path }` 元数据的 `V8.normalizeUploadEntries`、`V8.assetUrl` / `V8.resolveAssetUrl` 和 `V8.resolveFileUrl`,不得在业务页面另造一套不完整的解析规则。
15
26
 
16
27
  | 类型 | 处理方式 |
17
28
  |---|---|
18
29
  | 已允许的 `https://` 绝对地址 | 校验协议和允许域名后使用 |
19
30
  | 公有对象存储相对路径 | 拼接当前租户 `FileServer` |
20
31
  | `/file/...` 平台本地文件路由 | 拼接当前 API Base |
32
+ | `/micro-app/v3/...` 应用稳定解析路由 | 拼接当前 API Base;这是动态指针路由,不是 HDFS 对象 Key,禁止拼接 `FileServer` |
21
33
  | 私有对象 | 后端鉴权后签发短期 URL,前端只使用临时 URL |
22
34
  | `blob:` | 仅用于本页创建且能及时 `revokeObjectURL` 的预览 |
23
35
  | `data:` | 仅允许经过大小和 MIME 校验的图片预览;禁止用于富文本任意 HTML |
@@ -26,15 +38,54 @@ Microi 上传字段通常保存对象存储 Key 或相对路径,例如 `/demo/
26
38
 
27
39
  ## 统一资源解析函数
28
40
 
29
- 项目应在共享请求/资源模块中实现一个 `resolveAssetUrl`,所有页面复用同一逻辑:
41
+ 未使用 `microi.v8.js` 的项目,应在共享请求/资源模块中实现等价的“提取上传路径 + 解析资源 URL”两层函数,所有页面复用同一逻辑:
30
42
 
31
43
  ```js
44
+ const uploadPathKeys = [
45
+ 'Url', 'FileUrl', 'FileURL', 'PreviewUrl', 'PreviewURL', 'FullUrl',
46
+ 'Path', 'FilePathName', 'FilePath', 'FullPath', 'Src', 'Href',
47
+ 'url', 'fileUrl', 'previewUrl', 'fullUrl', 'path', 'filePathName', 'filePath', 'fullPath', 'src', 'href'
48
+ ];
49
+
50
+ export function extractUploadPath(raw, depth = 0) {
51
+ if (depth > 4 || raw === null || raw === undefined) return '';
52
+ if (Array.isArray(raw)) {
53
+ for (const item of raw) {
54
+ const path = extractUploadPath(item, depth + 1);
55
+ if (path) return path;
56
+ }
57
+ return '';
58
+ }
59
+ if (typeof raw === 'object') {
60
+ for (const key of uploadPathKeys) {
61
+ const path = extractUploadPath(raw[key], depth + 1);
62
+ if (path) return path;
63
+ }
64
+ return '';
65
+ }
66
+ const value = String(raw).trim();
67
+ if (!value) return '';
68
+ if (/^[{[]/.test(value)) {
69
+ try { return extractUploadPath(JSON.parse(value), depth + 1); } catch (_) {}
70
+ }
71
+ return value;
72
+ }
73
+
74
+ export function normalizeUploadValue(raw) {
75
+ let value = raw;
76
+ if (typeof value === 'string' && /^[{[]/.test(value.trim())) {
77
+ try { value = JSON.parse(value); } catch (_) {}
78
+ }
79
+ const items = Array.isArray(value) ? value : [value];
80
+ return items.map(item => ({ item, path: extractUploadPath(item) })).filter(entry => entry.path);
81
+ }
82
+
32
83
  export function resolveAssetUrl(raw, {
33
84
  apiBase,
34
85
  fileServer,
35
86
  allowedHosts = []
36
87
  }) {
37
- const value = String(raw || '').trim();
88
+ const value = extractUploadPath(raw);
38
89
  if (!value) return '';
39
90
 
40
91
  if (/^https:\/\//i.test(value)) {
@@ -44,6 +95,9 @@ export function resolveAssetUrl(raw, {
44
95
  if (/^blob:/i.test(value)) return value;
45
96
  if (/^data:image\/(png|jpeg|gif|webp);base64,/i.test(value)) return value;
46
97
  if (value.startsWith('/file/')) return `${apiBase.replace(/\/$/, '')}${value}`;
98
+ if (/^\/?micro-app\/v3(?:\/|$)/i.test(value)) {
99
+ return `${apiBase.replace(/\/$/, '')}/${value.replace(/^\/+/, '')}`;
100
+ }
47
101
 
48
102
  const relative = value.replace(/^\/+/, '');
49
103
  return `${fileServer.replace(/\/$/, '')}/${relative}`;
@@ -103,7 +157,9 @@ function imageUrl(path) {
103
157
 
104
158
  ## 验收清单
105
159
 
106
- - [ ] 公有对象、本地文件、私有对象、绝对 URL 的路径分支均有测试
160
+ - [ ] 公有对象、本地文件、`/micro-app/v3` 动态路由、私有对象、绝对 URL 的路径分支均有测试
161
+ - [ ] 绝对地址字符串、相对路径字符串、单图对象、多图数组及其 JSON 字符串均有测试
162
+ - [ ] 单图从数组取第一个有效路径,多图保持原顺序;解析 URL 时不丢失或改写上传对象元数据
107
163
  - [ ] 切换两个 `OsClient` 后使用各自 `FileServer`,无跨租户路径或缓存复用
108
164
  - [ ] 私有 URL 越权、过期、篡改签名和复制到其它账号均失败
109
165
  - [ ] 网络面板中没有硬编码客户域名、真实租户或永久签名 URL