@microi.net/cli 5.7.8 → 5.7.9
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.
- package/.codebuddy-plugin/marketplace.json +2 -2
- package/.codebuddy-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.workbuddy-plugin/marketplace.json +2 -2
- package/.workbuddy-plugin/plugin.json +1 -1
- package/LICENSE +21 -21
- package/README.md +71 -71
- package/assets/build-meta.json +6 -6
- package/assets/feature-matrix.json +138 -138
- package/assets/logo.svg +4 -4
- package/cordis.patch.yml +1 -1
- package/package.json +1 -1
- package/scripts/mcp-codex-stdio-adapter.js +0 -0
- package/scripts/mcp-server.js +102 -101
- package/scripts/microi-cli.js +57 -57
- package/scripts/microi-codex-broker.js +450 -450
- package/scripts/microi-codex-router.js +0 -0
- package/scripts/microi-skills.meta.json +366 -366
- package/skills/.microi-skills-version.json +2 -2
- package/skills/.progressive-disclosure-manifest.json +27 -27
- package/skills/README.md +286 -286
- package/skills/ai-engine/SKILL.md +265 -265
- package/skills/ai-engine/agents/openai.yaml +4 -4
- package/skills/ai-platform-governance/SKILL.md +177 -177
- package/skills/ai-platform-governance/references/progressive-01-/345/212/237/350/203/275/345/274/200/345/205/263.md +190 -190
- package/skills/app-store/SKILL.md +392 -396
- package/skills/app-store/agents/openai.yaml +4 -4
- package/skills/business-blueprint/SKILL.md +193 -193
- package/skills/datasource-engine/SKILL.md +90 -90
- package/skills/datasource-engine/agents/openai.yaml +4 -4
- package/skills/dos-orm/references/api-reference.md +229 -229
- package/skills/email-engine/SKILL.md +78 -78
- package/skills/email-engine/references/v8-email.md +34 -34
- package/skills/job-engine/SKILL.md +171 -172
- package/skills/job-engine/agents/openai.yaml +4 -4
- package/skills/message-notification/SKILL.md +155 -156
- package/skills/message-notification/agents/openai.yaml +5 -5
- package/skills/message-notification/references/contracts.md +102 -102
- package/skills/microi-ai-app-auth.js +652 -652
- package/skills/microi-ai-application/SKILL.md +102 -106
- package/skills/microi-ai-application/agents/openai.yaml +4 -4
- package/skills/microi-ai-application/references/frontend-baseline.md +164 -164
- package/skills/microi-client-frontend/SKILL.md +235 -240
- package/skills/microi-client-frontend/references/progressive-01-3-/345/212/250/346/200/201/346/214/211/351/222/256/347/263/273/347/273/237.md +144 -144
- package/skills/microi-client-frontend/references/progressive-02-8-/350/277/220/350/241/214/346/227/266/351/253/230/351/242/221/345/235/221/345/244/215/347/233/230.md +196 -196
- package/skills/microi-client-frontend/references/progressive-03-vue3-/345/211/215/347/253/257/345/276/256/346/234/215/345/212/241/345/256/277/344/270/273/350/247/204/345/210/231.md +167 -167
- package/skills/microi-codex/SKILL.md +72 -76
- package/skills/microi-codex-installer/SKILL.md +225 -223
- package/skills/microi-codex-installer/agents/openai.yaml +7 -7
- package/skills/microi-datasource-mapping/SKILL.md +107 -111
- package/skills/microi-db-schema/SKILL.md +175 -175
- package/skills/microi-db-schema/agents/openai.yaml +4 -4
- package/skills/microi-db-schema/references/core-tables.md +695 -695
- package/skills/microi-db-schema/references/form-component-options.md +256 -256
- package/skills/microi-db-schema/references/schema-overview.md +202 -202
- package/skills/microi-db-schema/references/schema.md +646 -646
- package/skills/microi-db-schema/references/table-catalog.md +1599 -1599
- package/skills/microi-deployment/references/deployment-matrix.md +101 -101
- package/skills/microi-docs-coverage/references/capability-map.md +0 -1
- package/skills/microi-docs-coverage/scripts/audit-doc-skill-coverage.mjs +894 -894
- package/skills/microi-form-engine/SKILL.md +226 -226
- package/skills/microi-form-engine/references/component-catalog.md +216 -216
- package/skills/microi-form-engine/references/data-source-events.md +124 -124
- package/skills/microi-form-layout/SKILL.md +205 -205
- package/skills/microi-form-layout/references/progressive-01-3-/344/270/211/347/247/215/345/210/206/347/273/204/347/232/204/345/255/230/345/202/250/344/270/216/351/205/215/347/275/256.md +235 -235
- package/skills/microi-frontend-sdk/SKILL.md +188 -188
- package/skills/microi-frontend-sdk/references/progressive-01-token-/345/275/223/345/211/215/347/231/273/345/275/225/347/224/250/346/210/267/344/270/216/345/275/223/345/211/215/347/273/210/347/253/257/347/231/273/345/275/225/345/215/217/350/256/256.md +176 -176
- package/skills/microi-left-right-layout/SKILL.md +141 -141
- package/skills/microi-microservice/SKILL.md +322 -323
- package/skills/microi-microservice/references/runtime-delivery.md +278 -278
- package/skills/microi-mobile-app-quality/SKILL.md +181 -181
- package/skills/microi-mobile-app-quality/references/progressive-01-4-/351/207/215/350/246/201/346/214/211/351/222/256/345/277/205/351/241/273/345/270/246/345/233/276/346/240/207.md +213 -213
- package/skills/microi-mobile-app-quality/references/progressive-02-9-/344/270/273/351/242/230/345/210/207/346/215/242/345/277/205/351/241/273/347/234/237/345/256/236/344/270/224/345/205/250/345/261/200/347/224/237/346/225/210.md +128 -128
- package/skills/microi-solution-quotation/SKILL.md +78 -78
- package/skills/microi-solution-quotation/agents/openai.yaml +4 -4
- package/skills/microi-solution-quotation/scripts/build_solution_quote.py +296 -296
- package/skills/microi-sso/references/acceptance.md +49 -49
- package/skills/microi-sso/references/configuration-and-security.md +53 -53
- package/skills/microi-sso/references/inbound.md +53 -53
- package/skills/microi-sso/references/outbound.md +39 -39
- package/skills/microi-system-delivery/SKILL.md +134 -134
- package/skills/microi-system-delivery/references/progressive-01-/346/240/207/345/207/206/345/267/245/344/275/234/346/265/201.md +193 -193
- package/skills/microi-system-delivery/references/progressive-02-/350/207/252/345/212/250/345/214/226/346/265/213/350/257/225/345/277/205/351/241/273/350/246/206/347/233/226/347/232/204/345/235/221.md +189 -189
- package/skills/microi-ui/SKILL.md +174 -174
- package/skills/microi-ui/references/progressive-01-/347/247/273/345/212/250/347/253/257/345/234/272/346/231/257/350/223/235/345/233/276.md +183 -183
- package/skills/microi-uniapp-frontend/SKILL.md +193 -193
- package/skills/microi-uniapp-frontend/references/progressive-01-/347/247/273/345/212/250/347/253/257/345/210/206/347/261/273-/345/217/214/346/240/217/345/210/227/350/241/250/347/213/254/347/253/213/346/273/232/345/212/250.md +225 -225
- package/skills/microi-uniapp-frontend/references/progressive-02-/345/205/263/351/224/256/344/270/232/345/212/241/350/265/204/344/272/247/344/270/215/345/276/227/351/273/230/350/256/244/351/200/211/344/270/255.md +154 -154
- package/skills/microi.v8.js +1921 -1921
- package/skills/module-engine/SKILL.md +237 -250
- package/skills/module-engine/references/module-config.md +204 -204
- package/skills/ocr-engine/SKILL.md +113 -113
- package/skills/ocr-engine/agents/openai.yaml +4 -4
- package/skills/page-engine/SKILL.md +180 -191
- package/skills/page-engine/references/progressive-01-/346/211/200/346/234/211/347/273/204/344/273/266/347/261/273/345/236/213.md +234 -234
- package/skills/page-engine/references/progressive-02-/347/211/210/346/234/254/345/216/206/345/217/262-/345/271/266/345/217/221/344/277/235/345/255/230/344/270/216/345/233/236/346/273/232.md +60 -60
- package/skills/performance-testing/SKILL.md +221 -221
- package/skills/playwright-e2e/SKILL.md +197 -197
- package/skills/playwright-e2e/references/progressive-01-/345/205/250/350/207/252/345/212/250/347/231/273/345/275/225-/345/205/215/351/252/214/350/257/201/347/240/201-/344/275/206/344/270/215/345/205/215/345/257/206/347/240/201-/345/277/205/350/257/273.md +173 -173
- package/skills/playwright-e2e/references/progressive-02-/346/226/207/345/255/227/345/257/271/346/257/224/345/272/246/344/270/216/345/217/257/350/257/273/346/200/247/350/207/252/345/212/250/345/214/226/346/243/200/346/237/245-/345/277/205/345/201/232.md +183 -183
- package/skills/playwright-e2e/references/progressive-03-microi-helper-/346/250/241/346/235/277.md +221 -221
- package/skills/playwright-e2e/references/progressive-04-ci-/345/273/272/350/256/256.md +115 -115
- package/skills/print-engine/SKILL.md +259 -259
- package/skills/production-readonly-audit/SKILL.md +41 -41
- package/skills/report-engine/SKILL.md +71 -71
- package/skills/report-engine/agents/openai.yaml +4 -4
- package/skills/scripts/optimize-progressive-disclosure.mjs +204 -204
- package/skills/scripts/refresh-progressive-disclosure.mjs +64 -64
- package/skills/search-engine/SKILL.md +75 -75
- package/skills/search-engine/agents/openai.yaml +4 -4
- package/skills/spider-engine/SKILL.md +190 -190
- package/skills/system-observability/SKILL.md +238 -238
- package/skills/system-observability/references/memory-incident-triage.md +70 -70
- package/skills/translate-engine/SKILL.md +140 -140
- package/skills/translate-engine/agents/openai.yaml +4 -4
- package/skills/ui-design/SKILL.md +191 -191
- package/skills/ui-design/assets/templates/MCI-DESIGN.md +198 -198
- package/skills/ui-design/references/design-pattern-library.md +184 -184
- package/skills/ui-design/references/mci-design-contract.md +163 -163
- package/skills/ui-design/references/progressive-01-/351/242/234/350/211/262/344/275/223/347/263/273-css-variables-/346/224/257/346/214/201/344/270/273/351/242/230/345/210/207/346/215/242.md +218 -218
- package/skills/ui-design/references/progressive-02-/345/255/227/344/275/223.md +137 -137
- package/skills/ui-design/references/progressive-03-/345/212/250/346/225/210/350/247/204/350/214/203-/344/270/260/345/257/214/344/275/206/344/270/215/345/215/241.md +235 -235
- package/skills/ui-design/references/progressive-04-/347/273/204/344/273/266/351/243/216/346/240/274/351/200/237/346/237/245.md +152 -152
- package/skills/ui-design/references/progressive-05-/347/247/273/345/212/250/347/253/257/344/270/223/347/224/250/350/247/204/350/214/203.md +238 -238
- package/skills/ui-design/references/progressive-06-/344/270/273/351/242/230/345/210/207/346/215/242/345/256/236/347/216/260.md +194 -194
- package/skills/ui-design/references/progressive-07-/351/200/237/346/237/245-/344/273/216/345/244/264/346/220/255/345/273/272/344/270/200/344/270/252/347/247/273/345/212/250/347/253/257/351/241/265/351/235/242.md +207 -207
- package/skills/ui-design/references/progressive-08-/350/241/250/345/215/225/345/210/206/347/273/204/350/247/204/350/214/203-tabs-vs-collapsegroup-/345/274/272/345/210/266.md +117 -117
- package/skills/uniapp-mall-assets/SKILL.md +176 -176
- package/skills/unity-integration/SKILL.md +155 -155
- package/skills/unity-integration/agents/openai.yaml +4 -4
- package/skills/unity-integration/references/ai-app-delivery.md +103 -103
- package/skills/unity-integration/references/sdk-api.md +82 -82
- package/skills/unity-integration/references/toolbox-migration.md +66 -66
- package/skills/unity-integration/references/webgl-hosting.md +57 -57
- package/skills/v8-cache-pattern/SKILL.md +306 -306
- package/skills/v8-crud-api/SKILL.md +175 -175
- package/skills/v8-crud-api/references/progressive-01-/346/237/245/350/257/242/345/210/227/350/241/250-/345/210/206/351/241/265.md +226 -226
- package/skills/v8-crud-api/references/progressive-02-where-/346/235/241/344/273/266/350/257/255/346/263/225/351/200/237/346/237/245.md +49 -49
- package/skills/v8-debugging/SKILL.md +284 -284
- package/skills/v8-explorer-tree/SKILL.md +228 -228
- package/skills/v8-export-import/SKILL.md +209 -209
- package/skills/v8-export-import/references/progressive-01-excellayout-/351/253/230/347/272/247/350/207/252/347/224/261/345/270/203/345/261/200.md +211 -211
- package/skills/v8-export-import/references/progressive-02-powerpoint-/345/257/274/345/207/272.md +202 -202
- package/skills/v8-export-import/references/progressive-03-/345/256/211/345/205/250-/346/200/247/350/203/275/346/263/250/346/204/217.md +42 -42
- package/skills/v8-file-upload/SKILL.md +256 -263
- package/skills/v8-file-upload/references/progressive-01-/345/205/254/346/234/211/346/241/266-vs-/347/247/201/346/234/211/346/241/266.md +101 -101
- package/skills/v8-file-upload/references/progressive-02-office-/346/226/207/344/273/266/345/234/250/347/272/277/347/274/226/350/276/221/347/211/210/346/234/254/345/217/267/350/247/204/345/210/231.md +150 -150
- package/skills/v8-formengine-http/SKILL.md +219 -219
- package/skills/v8-frontend-events/SKILL.md +178 -178
- package/skills/v8-frontend-events/references/bluetooth-print-api.md +135 -135
- package/skills/v8-frontend-events/references/bluetooth-print.md +246 -246
- package/skills/v8-frontend-events/references/progressive-01-/345/210/227/350/241/250/344/272/213/344/273/266.md +219 -219
- package/skills/v8-http-integration/SKILL.md +182 -182
- package/skills/v8-http-integration/references/progressive-01-get-/350/257/267/346/261/202.md +220 -220
- package/skills/v8-http-integration/references/progressive-02-/351/224/231/350/257/257/345/244/204/347/220/206/346/250/241/345/274/217.md +44 -44
- package/skills/v8-image-processing/SKILL.md +190 -190
- package/skills/v8-image-processing/agents/openai.yaml +4 -4
- package/skills/v8-image-processing/references/api-reference.md +623 -623
- package/skills/v8-menu-buttons/SKILL.md +180 -180
- package/skills/v8-menu-buttons/references/progressive-01-2-/346/214/211/351/222/256/345/257/271/350/261/241-schema.md +222 -222
- package/skills/v8-menu-buttons/references/progressive-02-8-/346/250/241/345/274/217-f-/345/220/216/345/217/260/344/273/273/345/212/241/346/214/211/351/222/256-/351/225/277/344/273/273/345/212/241.md +228 -228
- package/skills/v8-menu-buttons/references/progressive-03-10-/345/217/215/346/250/241/345/274/217-/351/201/277/345/205/215.md +104 -104
- package/skills/v8-mongodb/SKILL.md +192 -192
- package/skills/v8-mq-mqtt/SKILL.md +175 -176
- package/skills/v8-mq-mqtt/references/mqtt-production.md +342 -342
- package/skills/v8-mq-mqtt/references/progressive-01-v8-mqtt-iot-/347/211/251/350/201/224/347/275/221.md +181 -181
- package/skills/v8-mq-mqtt/scripts/check-mqtt-skill-coverage.mjs +203 -203
- package/skills/v8-saas-multi-tenant/SKILL.md +219 -219
- package/skills/v8-security/SKILL.md +168 -168
- package/skills/v8-security/references/progressive-01-2-/346/235/203/351/231/220/346/240/241/351/252/214.md +198 -198
- package/skills/v8-security/references/progressive-02-7-/346/227/245/345/277/227/350/256/260/345/275/225.md +158 -158
- package/skills/v8-sql-query/SKILL.md +302 -302
- package/skills/v8-table-event/SKILL.md +144 -144
- package/skills/v8-table-event/references/progressive-01-informv8-js-/350/241/250/345/215/225/346/211/223/345/274/200/344/272/213/344/273/266.md +197 -197
- package/skills/v8-table-event/references/progressive-02-/345/211/215/347/253/257/344/272/213/344/273/266/345/220/215-v8-eventname-/345/217/257/350/203/275/347/232/204/345/200/274.md +46 -46
- package/skills/v8-tcp-integration/SKILL.md +147 -147
- package/skills/v8-tcp-integration/agents/openai.yaml +4 -4
- package/skills/v8-template-engine/SKILL.md +167 -167
- package/skills/v8-utilities/SKILL.md +90 -90
- package/skills/v8-utilities/references/client-api-index.md +143 -143
- package/skills/v8-utilities/references/platform-http-routes.md +83 -83
- package/skills/v8-utilities/references/server-api-index.md +188 -188
- package/skills/v8-workflow/SKILL.md +216 -243
- package/skills/v8-workflow/references/progressive-01-/350/212/202/347/202/271/345/274/200/345/247/213-v8-/344/272/213/344/273/266.md +180 -180
- package/skills/vision-engine/SKILL.md +159 -159
- package/skills/vision-engine/agents/openai.yaml +4 -4
- package/skills/vision-engine/references/architecture-and-acceptance.md +194 -194
- package/skills/workspace-conventions/SKILL.md +261 -261
- package/skills/workspace-conventions/references/progressive-01-/347/211/210/346/234/254/346/233/264/346/226/260/346/227/245/345/277/227/344/277/235/346/212/244/350/247/204/345/210/231-/345/274/272/345/210/266.md +208 -208
- package/skills/workspace-conventions/references/progressive-02-microi-net-api-/346/234/254/345/234/260/345/220/257/345/212/250/347/272/246/345/256/232.md +216 -216
- package/skills/workspace-conventions/references/progressive-03-cli-/344/270/216-ide-/346/217/222/344/273/266/351/224/231/347/211/210/345/205/261/345/255/230/347/272/246/345/256/232.md +27 -27
|
@@ -1,122 +1,122 @@
|
|
|
1
|
-
# ui-design 详细参考 8
|
|
2
|
-
|
|
3
|
-
> 按需读取;本文件由 SKILL.md 的原章节无损拆分。
|
|
4
|
-
|
|
5
|
-
<!-- microi-progressive:chunk id=ui-design-024 sha256=7af53d03d0828232aaf7ab54d6527f5e2cf292d8eb493d75d9ab42adc3030ba3 -->
|
|
6
|
-
## 表单分组规范:Tabs vs CollapseGroup(强制)
|
|
7
|
-
|
|
8
|
-
> **核心原则**:默认**不分组** → 小业务域用 **`CollapseGroup` 折叠分组** → 只有足够长或强任务隔离的业务域才用 **`Tabs` 分页**。禁止只按字段数判断:双列布局中 8 个短字段约占 4 行,仍不应独占 Tab;普通业务域通常达到 6 个有效表单行才考虑 Tab。
|
|
9
|
-
|
|
10
|
-
### 三种分组方式
|
|
11
|
-
|
|
12
|
-
| 方式 | 存储位置 | 控件 | 适用场景 |
|
|
13
|
-
|------|---------|------|---------|
|
|
14
|
-
| **A. 表级 Tab** | `diy_table.Tabs` + 字段 `Tab` | 表单顶部 Tab 条 | 大表单拆 2~5 个业务域,每域通常 ≥6 个有效表单行,或属于扫码/子表/代码编辑等强任务模式 |
|
|
15
|
-
| **B. 字段级 CollapseGroup** | `diy_field.Component='CollapseGroup'` + `Config.CollapseGroup` | 折叠面板标题 | 小业务域(≤5 个有效表单行),**所有分组同屏可见、可同时展开** |
|
|
16
|
-
| **C. 字段级 Tabs 控件** | `diy_field.Component='Tabs'` + `Config.FieldTabs` | 字段是 Tab 容器 | 嵌套 Tab 场景(不推荐优先用) |
|
|
17
|
-
|
|
18
|
-
### 表单规模与分组决策表
|
|
19
|
-
|
|
20
|
-
| 有效表单行与任务特征 | 推荐方案 |
|
|
21
|
-
|---------|---------|
|
|
22
|
-
| ≤ 6 行且无复杂控件 | **不分组**(直接平铺) |
|
|
23
|
-
| 7 ~ 12 行且无强任务隔离 | `CollapseGroup` 收起次要字段,或直接平铺 |
|
|
24
|
-
| > 12 行且至少两个业务域各 ≥ 6 行 | `diy_table.Tabs`(大域)或混合(Tab + 嵌套 CollapseGroup) |
|
|
25
|
-
| 扫码、报工、大型子表、代码编辑等强任务模式 | 可独立使用 `diy_table.Tabs`,不受普通行数阈值限制 |
|
|
26
|
-
|
|
27
|
-
### CollapseGroup Config 示例
|
|
28
|
-
|
|
29
|
-
```jsonc
|
|
30
|
-
// diy_field 必要字段
|
|
31
|
-
{
|
|
32
|
-
"Component": "CollapseGroup",
|
|
33
|
-
"Type": "varchar(50)",
|
|
34
|
-
"Visible": 1,
|
|
35
|
-
"AppVisible": 1,
|
|
36
|
-
"Config": "{\"CollapseGroup\":{\"DefaultCollapsed\":false,\"ScopeMode\":\"UntilNextGroup\",\"Description\":\"MRP 运算状态、批次号与时间\",\"Icon\":\"fas fa-calculator\",\"Theme\":\"primary\",\"ShowFieldCount\":true}}"
|
|
37
|
-
}
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
### 必做与禁止
|
|
41
|
-
|
|
42
|
-
- ✅ 任何 Tab / CollapseGroup 必须有 `Description` 解释分组用途。
|
|
43
|
-
- ✅ CollapseGroup 必须设 `Icon`(如 `fas fa-calculator`、`fas fa-info-circle`),不默认空白。
|
|
44
|
-
- ✅ 修改 Tab/CollapseGroup 配置后必须 `microi_refresh_schema_cache`。
|
|
45
|
-
- ❌ **禁止**为 ≤5 个有效表单行的业务域单独建 Tab;8~10 个双列短字段通常也应使用 CollapseGroup。
|
|
46
|
-
- ❌ **禁止**仅凭 13~30 个原始字段决定平铺或分 Tab;总有效行超过 6 且存在明确业务域时,至少使用 CollapseGroup 分组。
|
|
47
|
-
- ❌ **禁止**给 Tabs/CollapseGroup/Divider/Alert 等布局控件设 `FormWidth=24`(天然占整行)。
|
|
48
|
-
- ❌ **禁止**只创建 Tab 不写字段的 `Tab` 归属。
|
|
49
|
-
- ❌ 布局优化不得顺带覆盖字段 `Config/V8Code/KeyupV8Code` 或表级 V8 事件;修改前后必须回读比较,发现 Tab 显隐 API 时先适配再迁移。
|
|
50
|
-
|
|
51
|
-
完整规范、Config JSON 模板、回读验收清单和反例参考见 `microi-form-layout/SKILL.md`。
|
|
52
|
-
|
|
53
|
-
---
|
|
54
|
-
|
|
55
|
-
<!-- /microi-progressive:chunk -->
|
|
56
|
-
<!-- microi-progressive:chunk id=ui-design-025 sha256=e6e11a8153bc5d1a1c5e9f1868aaae9a101843961fcf94dba42681b0a6091ad7 -->
|
|
57
|
-
## 缓存刷新(解决"我改了字段但页面不变"问题)
|
|
58
|
-
|
|
59
|
-
平台对 `diy_field` 的字段列表有 Redis 缓存,键格式 `Microi:{OsClient}:FormData:diy_table_field_list:{TableId|TableName}`。
|
|
60
|
-
|
|
61
|
-
**何时缓存会失效**:
|
|
62
|
-
- ✅ 通过 `microi_add_field` / `microi_update_field` / `microi_update_table` 走原生 API → 自动清
|
|
63
|
-
- ✅ 通过低代码后台界面操作(diy_table 表单事件触发)→ 自动清
|
|
64
|
-
- ❌ 直接 `V8.FormEngine.UptFormData('diy_field', ...)` → **不会触发清缓存**(这是历史 bug)
|
|
65
|
-
|
|
66
|
-
**何时手动清**:
|
|
67
|
-
```jsonc
|
|
68
|
-
microi_refresh_schema_cache { "tables": ["mall_address", "mall_member"] }
|
|
69
|
-
```
|
|
70
|
-
该工具会清除每张表的 6 个 key 变种(`diy_table` / `Diy_Table` / `diy_table_field_list` × `id|name`)。
|
|
71
|
-
|
|
72
|
-
---
|
|
73
|
-
|
|
74
|
-
<!-- /microi-progressive:chunk -->
|
|
75
|
-
<!-- microi-progressive:chunk id=ui-design-026 sha256=3d53780b3ae17dabba22a2567d0d6e2c51e4dbce504d6e341b737881a3c4e749 -->
|
|
76
|
-
## 接口引擎匿名访问
|
|
77
|
-
|
|
78
|
-
登录、注册、首页公共数据等接口必须 `AllowAnonymous=1`,否则未登录用户调用会拿到 `null`:
|
|
79
|
-
|
|
80
|
-
```jsonc
|
|
81
|
-
microi_set_engine_anonymous {
|
|
82
|
-
"apiEngineKeys": ["mall_member_login", "mall_member_register", "mall_home_data"],
|
|
83
|
-
"allowAnonymous": 1
|
|
84
|
-
}
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
<!-- /microi-progressive:chunk -->
|
|
89
|
-
<!-- microi-progressive:chunk id=ui-design-027 sha256=e6e15cc34871821da89f70cb2a5326942d9d90fe99ed5c5a315415ac57f1532c -->
|
|
90
|
-
## MCI-UI 与第三方组件库策略
|
|
91
|
-
|
|
92
|
-
Microi 的 UI 规范不应该只停留在 skills 文档。面向品牌长期建设时,应形成可复用的 MCI-UI 体系:设计变量、基础样式、组件约定、示例站点、移动端与 PC 网站组件库。
|
|
93
|
-
|
|
94
|
-
- **默认规则**:当用户没有主动指定 UI 风格、UI 库或品牌视觉时,AI 必须默认采用 Microi吾码UI(Microi.UI / MCI-UI)作为移动端、PC 官网、企业网站、产品站、活动页和响应式网站的设计基础。
|
|
95
|
-
- **自动识别**:只要项目属于 Microi 生态、吾码源码、吾码客户项目,或需求中出现“移动端项目、网站、企业站、商城、会员中心、资产页、官网、H5、uni-app、Vue3”等关键词,即使用户没有单独说明 UI 风格,也应自动套用本规范与 `Microi.UI/` 组件。
|
|
96
|
-
- **落地要求**:业务页面优先使用 `MciPage`、`MciSection`、`MciButton`、`MciCard`、`MciCell`、`MciTabs`、`MciSkeleton`、`MciDataState`、`MciThemePanel` 等组件或项目级 `mci-*` 封装;不要重新发明一套分散样式。
|
|
97
|
-
- **例外场景**:后台管理系统继续使用 Element Plus + Microi theme;强行业 UI 或客户指定视觉可以定制主题 token,但仍优先保留 `--mci-*` 变量、骨架屏、安全区和动效规范。
|
|
98
|
-
- UniApp 项目不强制业务页面直接依赖某一个第三方 UI 库。推荐把 `uni-ui` 作为官方跨端基础组件底座之一,但业务视觉必须通过 `MCI-UI Mobile` 或项目级 `mci-*` 组件封装承载,避免页面直接散落 `uni-ui/uView/FirstUI/TDesign` 风格。
|
|
99
|
-
- PC 后台管理系统继续使用 Element Plus,不替换选型;但主题变量、间距、骨架屏、空态、安全区、表单密度和品牌色必须服从 `--mci-*` 设计变量。
|
|
100
|
-
- PC 官网、产品站、文档站、营销页和响应式网站应优先使用 `MCI-UI Web` 的设计变量与轻量组件。只有当页面是强表单、强数据录入或后台化工具时,才引入 Element Plus、TDesign Vue、Naive UI、Arco Design Vue 等成熟组件库作为底座。
|
|
101
|
-
- MCI-UI 应分层建设:`@microi/theme` 负责 tokens;`@microi/v8` 负责前端 SDK;`@microi/ui-mobile` 面向 UniApp;`@microi/ui-web` 面向官网和响应式站点;`Microi.Client` 后台则用 Element Plus + MCI theme。
|
|
102
|
-
- `microi.doc` 作为 VitePress 官方文档站,应逐步成为 MCI-UI 的展示入口:组件演示、设计变量、移动端骨架屏、安全区、富文本、上传资源、主题切换都应该有可查看示例,而不是只写在 skill 中。
|
|
103
|
-
|
|
104
|
-
<!-- /microi-progressive:chunk -->
|
|
105
|
-
<!-- microi-progressive:chunk id=ui-design-028 sha256=eee54b657b766f1d1ef137fb1df07fc8eec0b5363502838475eda81f9d031512 -->
|
|
1
|
+
# ui-design 详细参考 8
|
|
2
|
+
|
|
3
|
+
> 按需读取;本文件由 SKILL.md 的原章节无损拆分。
|
|
4
|
+
|
|
5
|
+
<!-- microi-progressive:chunk id=ui-design-024 sha256=7af53d03d0828232aaf7ab54d6527f5e2cf292d8eb493d75d9ab42adc3030ba3 -->
|
|
6
|
+
## 表单分组规范:Tabs vs CollapseGroup(强制)
|
|
7
|
+
|
|
8
|
+
> **核心原则**:默认**不分组** → 小业务域用 **`CollapseGroup` 折叠分组** → 只有足够长或强任务隔离的业务域才用 **`Tabs` 分页**。禁止只按字段数判断:双列布局中 8 个短字段约占 4 行,仍不应独占 Tab;普通业务域通常达到 6 个有效表单行才考虑 Tab。
|
|
9
|
+
|
|
10
|
+
### 三种分组方式
|
|
11
|
+
|
|
12
|
+
| 方式 | 存储位置 | 控件 | 适用场景 |
|
|
13
|
+
|------|---------|------|---------|
|
|
14
|
+
| **A. 表级 Tab** | `diy_table.Tabs` + 字段 `Tab` | 表单顶部 Tab 条 | 大表单拆 2~5 个业务域,每域通常 ≥6 个有效表单行,或属于扫码/子表/代码编辑等强任务模式 |
|
|
15
|
+
| **B. 字段级 CollapseGroup** | `diy_field.Component='CollapseGroup'` + `Config.CollapseGroup` | 折叠面板标题 | 小业务域(≤5 个有效表单行),**所有分组同屏可见、可同时展开** |
|
|
16
|
+
| **C. 字段级 Tabs 控件** | `diy_field.Component='Tabs'` + `Config.FieldTabs` | 字段是 Tab 容器 | 嵌套 Tab 场景(不推荐优先用) |
|
|
17
|
+
|
|
18
|
+
### 表单规模与分组决策表
|
|
19
|
+
|
|
20
|
+
| 有效表单行与任务特征 | 推荐方案 |
|
|
21
|
+
|---------|---------|
|
|
22
|
+
| ≤ 6 行且无复杂控件 | **不分组**(直接平铺) |
|
|
23
|
+
| 7 ~ 12 行且无强任务隔离 | `CollapseGroup` 收起次要字段,或直接平铺 |
|
|
24
|
+
| > 12 行且至少两个业务域各 ≥ 6 行 | `diy_table.Tabs`(大域)或混合(Tab + 嵌套 CollapseGroup) |
|
|
25
|
+
| 扫码、报工、大型子表、代码编辑等强任务模式 | 可独立使用 `diy_table.Tabs`,不受普通行数阈值限制 |
|
|
26
|
+
|
|
27
|
+
### CollapseGroup Config 示例
|
|
28
|
+
|
|
29
|
+
```jsonc
|
|
30
|
+
// diy_field 必要字段
|
|
31
|
+
{
|
|
32
|
+
"Component": "CollapseGroup",
|
|
33
|
+
"Type": "varchar(50)",
|
|
34
|
+
"Visible": 1,
|
|
35
|
+
"AppVisible": 1,
|
|
36
|
+
"Config": "{\"CollapseGroup\":{\"DefaultCollapsed\":false,\"ScopeMode\":\"UntilNextGroup\",\"Description\":\"MRP 运算状态、批次号与时间\",\"Icon\":\"fas fa-calculator\",\"Theme\":\"primary\",\"ShowFieldCount\":true}}"
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 必做与禁止
|
|
41
|
+
|
|
42
|
+
- ✅ 任何 Tab / CollapseGroup 必须有 `Description` 解释分组用途。
|
|
43
|
+
- ✅ CollapseGroup 必须设 `Icon`(如 `fas fa-calculator`、`fas fa-info-circle`),不默认空白。
|
|
44
|
+
- ✅ 修改 Tab/CollapseGroup 配置后必须 `microi_refresh_schema_cache`。
|
|
45
|
+
- ❌ **禁止**为 ≤5 个有效表单行的业务域单独建 Tab;8~10 个双列短字段通常也应使用 CollapseGroup。
|
|
46
|
+
- ❌ **禁止**仅凭 13~30 个原始字段决定平铺或分 Tab;总有效行超过 6 且存在明确业务域时,至少使用 CollapseGroup 分组。
|
|
47
|
+
- ❌ **禁止**给 Tabs/CollapseGroup/Divider/Alert 等布局控件设 `FormWidth=24`(天然占整行)。
|
|
48
|
+
- ❌ **禁止**只创建 Tab 不写字段的 `Tab` 归属。
|
|
49
|
+
- ❌ 布局优化不得顺带覆盖字段 `Config/V8Code/KeyupV8Code` 或表级 V8 事件;修改前后必须回读比较,发现 Tab 显隐 API 时先适配再迁移。
|
|
50
|
+
|
|
51
|
+
完整规范、Config JSON 模板、回读验收清单和反例参考见 `microi-form-layout/SKILL.md`。
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
<!-- /microi-progressive:chunk -->
|
|
56
|
+
<!-- microi-progressive:chunk id=ui-design-025 sha256=e6e11a8153bc5d1a1c5e9f1868aaae9a101843961fcf94dba42681b0a6091ad7 -->
|
|
57
|
+
## 缓存刷新(解决"我改了字段但页面不变"问题)
|
|
58
|
+
|
|
59
|
+
平台对 `diy_field` 的字段列表有 Redis 缓存,键格式 `Microi:{OsClient}:FormData:diy_table_field_list:{TableId|TableName}`。
|
|
60
|
+
|
|
61
|
+
**何时缓存会失效**:
|
|
62
|
+
- ✅ 通过 `microi_add_field` / `microi_update_field` / `microi_update_table` 走原生 API → 自动清
|
|
63
|
+
- ✅ 通过低代码后台界面操作(diy_table 表单事件触发)→ 自动清
|
|
64
|
+
- ❌ 直接 `V8.FormEngine.UptFormData('diy_field', ...)` → **不会触发清缓存**(这是历史 bug)
|
|
65
|
+
|
|
66
|
+
**何时手动清**:
|
|
67
|
+
```jsonc
|
|
68
|
+
microi_refresh_schema_cache { "tables": ["mall_address", "mall_member"] }
|
|
69
|
+
```
|
|
70
|
+
该工具会清除每张表的 6 个 key 变种(`diy_table` / `Diy_Table` / `diy_table_field_list` × `id|name`)。
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
<!-- /microi-progressive:chunk -->
|
|
75
|
+
<!-- microi-progressive:chunk id=ui-design-026 sha256=3d53780b3ae17dabba22a2567d0d6e2c51e4dbce504d6e341b737881a3c4e749 -->
|
|
76
|
+
## 接口引擎匿名访问
|
|
77
|
+
|
|
78
|
+
登录、注册、首页公共数据等接口必须 `AllowAnonymous=1`,否则未登录用户调用会拿到 `null`:
|
|
79
|
+
|
|
80
|
+
```jsonc
|
|
81
|
+
microi_set_engine_anonymous {
|
|
82
|
+
"apiEngineKeys": ["mall_member_login", "mall_member_register", "mall_home_data"],
|
|
83
|
+
"allowAnonymous": 1
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
<!-- /microi-progressive:chunk -->
|
|
89
|
+
<!-- microi-progressive:chunk id=ui-design-027 sha256=e6e15cc34871821da89f70cb2a5326942d9d90fe99ed5c5a315415ac57f1532c -->
|
|
90
|
+
## MCI-UI 与第三方组件库策略
|
|
91
|
+
|
|
92
|
+
Microi 的 UI 规范不应该只停留在 skills 文档。面向品牌长期建设时,应形成可复用的 MCI-UI 体系:设计变量、基础样式、组件约定、示例站点、移动端与 PC 网站组件库。
|
|
93
|
+
|
|
94
|
+
- **默认规则**:当用户没有主动指定 UI 风格、UI 库或品牌视觉时,AI 必须默认采用 Microi吾码UI(Microi.UI / MCI-UI)作为移动端、PC 官网、企业网站、产品站、活动页和响应式网站的设计基础。
|
|
95
|
+
- **自动识别**:只要项目属于 Microi 生态、吾码源码、吾码客户项目,或需求中出现“移动端项目、网站、企业站、商城、会员中心、资产页、官网、H5、uni-app、Vue3”等关键词,即使用户没有单独说明 UI 风格,也应自动套用本规范与 `Microi.UI/` 组件。
|
|
96
|
+
- **落地要求**:业务页面优先使用 `MciPage`、`MciSection`、`MciButton`、`MciCard`、`MciCell`、`MciTabs`、`MciSkeleton`、`MciDataState`、`MciThemePanel` 等组件或项目级 `mci-*` 封装;不要重新发明一套分散样式。
|
|
97
|
+
- **例外场景**:后台管理系统继续使用 Element Plus + Microi theme;强行业 UI 或客户指定视觉可以定制主题 token,但仍优先保留 `--mci-*` 变量、骨架屏、安全区和动效规范。
|
|
98
|
+
- UniApp 项目不强制业务页面直接依赖某一个第三方 UI 库。推荐把 `uni-ui` 作为官方跨端基础组件底座之一,但业务视觉必须通过 `MCI-UI Mobile` 或项目级 `mci-*` 组件封装承载,避免页面直接散落 `uni-ui/uView/FirstUI/TDesign` 风格。
|
|
99
|
+
- PC 后台管理系统继续使用 Element Plus,不替换选型;但主题变量、间距、骨架屏、空态、安全区、表单密度和品牌色必须服从 `--mci-*` 设计变量。
|
|
100
|
+
- PC 官网、产品站、文档站、营销页和响应式网站应优先使用 `MCI-UI Web` 的设计变量与轻量组件。只有当页面是强表单、强数据录入或后台化工具时,才引入 Element Plus、TDesign Vue、Naive UI、Arco Design Vue 等成熟组件库作为底座。
|
|
101
|
+
- MCI-UI 应分层建设:`@microi/theme` 负责 tokens;`@microi/v8` 负责前端 SDK;`@microi/ui-mobile` 面向 UniApp;`@microi/ui-web` 面向官网和响应式站点;`Microi.Client` 后台则用 Element Plus + MCI theme。
|
|
102
|
+
- `microi.doc` 作为 VitePress 官方文档站,应逐步成为 MCI-UI 的展示入口:组件演示、设计变量、移动端骨架屏、安全区、富文本、上传资源、主题切换都应该有可查看示例,而不是只写在 skill 中。
|
|
103
|
+
|
|
104
|
+
<!-- /microi-progressive:chunk -->
|
|
105
|
+
<!-- microi-progressive:chunk id=ui-design-028 sha256=eee54b657b766f1d1ef137fb1df07fc8eec0b5363502838475eda81f9d031512 -->
|
|
106
106
|
## MCI-UI 源码落地位置
|
|
107
|
-
|
|
108
|
-
MCI-UI 已在吾码源码根目录落地:`Microi.UI/`。
|
|
109
|
-
|
|
110
|
-
- 新的移动端 UniApp/H5 项目应优先使用 `Microi.UI/src/uniapp` 中的 `MciPage`、`MciNavbar`、`MciButton`、`MciCard`、`MciCell`、`MciSection`、`MciTabs`、`MciMetricCard`、`MciActionBar`、`MciAvatar`、`MciProductCard`、`MciSkeleton`、`MciDataState`、`MciRichText`,再按业务补项目组件。
|
|
111
|
-
- 新的 PC 官网、产品站、文档站、响应式网站应优先使用 `Microi.UI/src/web` 和 `Microi.UI/src/theme`,不要直接套后台 Element Plus 风格。
|
|
112
|
-
- `Microi.UI/src/theme/tokens.css` 是品牌 token 源头;新组件颜色、圆角、阴影、间距、安全区、骨架屏都必须走 `--mci-*` 变量。
|
|
113
|
-
- `Microi.UI/src/theme/runtime.js` 是主题运行时入口;项目应通过 `initMciDesign()`、`setMciPalette()`、`setMciShape()`、`setMciTheme()` 统一设置黑白红橙黄绿青蓝紫主色、圆角/扁平、亮暗主题和动效偏好。
|
|
114
|
-
- `MciPage` 默认带页面入场动效;业务页如果有特殊路由转场,可以关闭 `animated` 后使用项目级转场,但不能让动态页面无反馈地直接闪现。
|
|
115
|
-
- `MciButton`、`MciCard` 必须保留 hover/pressed/focus/sheen 等基础反馈;业务组件可以封装样式,但不能删掉交互状态。
|
|
107
|
+
|
|
108
|
+
MCI-UI 已在吾码源码根目录落地:`Microi.UI/`。
|
|
109
|
+
|
|
110
|
+
- 新的移动端 UniApp/H5 项目应优先使用 `Microi.UI/src/uniapp` 中的 `MciPage`、`MciNavbar`、`MciButton`、`MciCard`、`MciCell`、`MciSection`、`MciTabs`、`MciMetricCard`、`MciActionBar`、`MciAvatar`、`MciProductCard`、`MciSkeleton`、`MciDataState`、`MciRichText`,再按业务补项目组件。
|
|
111
|
+
- 新的 PC 官网、产品站、文档站、响应式网站应优先使用 `Microi.UI/src/web` 和 `Microi.UI/src/theme`,不要直接套后台 Element Plus 风格。
|
|
112
|
+
- `Microi.UI/src/theme/tokens.css` 是品牌 token 源头;新组件颜色、圆角、阴影、间距、安全区、骨架屏都必须走 `--mci-*` 变量。
|
|
113
|
+
- `Microi.UI/src/theme/runtime.js` 是主题运行时入口;项目应通过 `initMciDesign()`、`setMciPalette()`、`setMciShape()`、`setMciTheme()` 统一设置黑白红橙黄绿青蓝紫主色、圆角/扁平、亮暗主题和动效偏好。
|
|
114
|
+
- `MciPage` 默认带页面入场动效;业务页如果有特殊路由转场,可以关闭 `animated` 后使用项目级转场,但不能让动态页面无反馈地直接闪现。
|
|
115
|
+
- `MciButton`、`MciCard` 必须保留 hover/pressed/focus/sheen 等基础反馈;业务组件可以封装样式,但不能删掉交互状态。
|
|
116
116
|
- 第三方 UI 库只能作为底层能力或局部补充,不能绕过 MCI-UI 直接决定产品视觉。
|
|
117
117
|
|
|
118
|
-
<!-- /microi-progressive:chunk -->
|
|
119
|
-
<!-- microi-progressive:chunk id=ui-design-029 sha256=470bda7401f3a1d6713872d89370bb89785e693b9e94862a7df4808db0200065 -->
|
|
118
|
+
<!-- /microi-progressive:chunk -->
|
|
119
|
+
<!-- microi-progressive:chunk id=ui-design-029 sha256=470bda7401f3a1d6713872d89370bb89785e693b9e94862a7df4808db0200065 -->
|
|
120
120
|
## VitePress 中文文档布局规范
|
|
121
121
|
|
|
122
122
|
`microi.doc` 不是纯文本仓库,而是 Microi 产品体验的一部分。创建或重构
|
|
@@ -139,4 +139,4 @@ MCI-UI 已在吾码源码根目录落地:`Microi.UI/`。
|
|
|
139
139
|
- Sass 位于 `html:lang(zh)` 嵌套作用域时,暗色根状态写 `&.dark`,编译结果必须是 `html:lang(zh).dark`;不得写成会生成 `html:lang(zh) .dark` 的后代选择器。单元测试应编译 SCSS 并阻止这一回归。
|
|
140
140
|
- 常规正文和次要文字对其实际面板底色至少满足 WCAG AA 4.5:1,大标题至少 3:1;渐变面板按最不利的实色底计算,不能只看 token 名称推断对比度。
|
|
141
141
|
- “全站扫描”必须输出逐页档案和问题结果;静态通过后仍要遍历全部中文路由做 H1、横向溢出和低对比冒烟,并对五类代表页执行桌面/移动、亮/暗截图矩阵。扫描数量、构建成功或单页截图均不能单独证明全站已经美化。
|
|
142
|
-
<!-- /microi-progressive:chunk -->
|
|
142
|
+
<!-- /microi-progressive:chunk -->
|
|
@@ -1,176 +1,176 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: uniapp-mall-assets
|
|
3
|
-
description: Microi UniApp、H5、商城与 PC 页面中的上传图片、附件、FileServer、私有文件临时 URL 和富文本资源安全规范。
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
> **Codex 非阻塞自动更新:** 当前宿主为 Codex 时,吾码 CLI、Codex 插件与工作区 AI/MCP 由后台自动更新;需要诊断时读取 `../microi-codex-installer/SKILL.md`。更新失败、等待空闲或尚未重载均不得阻断当前、正在进行或新建任务。非 Codex 宿主跳过此项。
|
|
7
|
-
|
|
8
|
-
# Microi UniApp / 商城资源路径规范
|
|
9
|
-
|
|
10
|
-
本 Skill 只描述平台通用资源规则。不得写入客户名称、真实 `OsClient`、客户应用路径、客户接口 Key 或特定商城结算规则。
|
|
11
|
-
|
|
12
|
-
## 先识别资源类型
|
|
13
|
-
|
|
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`,不得在业务页面另造一套不完整的解析规则。
|
|
26
|
-
|
|
27
|
-
| 类型 | 处理方式 |
|
|
28
|
-
|---|---|
|
|
29
|
-
| 已允许的 `https://` 绝对地址 | 校验协议和允许域名后使用 |
|
|
30
|
-
| 公有对象存储相对路径 | 拼接当前租户 `FileServer` |
|
|
31
|
-
| `/file/...` 平台本地文件路由 | 拼接当前 API Base |
|
|
32
|
-
| `/micro-app/v3/...` 应用稳定解析路由 | 拼接当前 API Base;这是动态指针路由,不是 HDFS 对象 Key,禁止拼接 `FileServer` |
|
|
33
|
-
| 私有对象 | 后端鉴权后签发短期 URL,前端只使用临时 URL |
|
|
34
|
-
| `blob:` | 仅用于本页创建且能及时 `revokeObjectURL` 的预览 |
|
|
35
|
-
| `data:` | 仅允许经过大小和 MIME 校验的图片预览;禁止用于富文本任意 HTML |
|
|
36
|
-
|
|
37
|
-
`FileServer` 是对象存储/CDN 地址,不等同于 API 服务。租户切换时必须使用当前 `OsClient` 的运行期配置,禁止在源码中写死域名或租户目录。
|
|
38
|
-
|
|
39
|
-
### 跨端上传字段的形态与桶属性
|
|
40
|
-
|
|
41
|
-
- 控件读取嵌套 `Config.ImgUpload/FileUpload.Multiple` 与 `MaxCount`;`Multiple=false` 保存单图对象,不能因数量上限或默认九图值而保存数组。已有单元素数组在单图控件中按第一项读取,不在查看时回写业务数据。
|
|
42
|
-
- 上传时透传字段授权上下文与配置的 `Limit`,后端仍独立校验字段策略。上传响应中的实际 `Limit` 属于可持久化元数据,必须保留;临时 `Url/PreviewUrl` 与本地预览地址不得入库。
|
|
43
|
-
- 历史对象缺少 `Limit` 时,以字段配置作为取址依据。已有对象明确 `Limit=true/1` 时,即使字段当前为公有,也必须签名;私有字段上下文与实际私有标记优先于公有提示。签发失败保持空地址,禁止尝试把私有路径拼到公有 CDN。
|
|
44
|
-
|
|
45
|
-
## 统一资源解析函数
|
|
46
|
-
|
|
47
|
-
未使用 `microi.v8.js` 的项目,应在共享请求/资源模块中实现等价的“提取上传路径 + 解析资源 URL”两层函数,所有页面复用同一逻辑:
|
|
48
|
-
|
|
49
|
-
```js
|
|
50
|
-
const uploadPathKeys = [
|
|
51
|
-
'Url', 'FileUrl', 'FileURL', 'PreviewUrl', 'PreviewURL', 'FullUrl',
|
|
52
|
-
'Path', 'FilePathName', 'FilePath', 'FullPath', 'Src', 'Href',
|
|
53
|
-
'url', 'fileUrl', 'previewUrl', 'fullUrl', 'path', 'filePathName', 'filePath', 'fullPath', 'src', 'href'
|
|
54
|
-
];
|
|
55
|
-
|
|
56
|
-
export function extractUploadPath(raw, depth = 0) {
|
|
57
|
-
if (depth > 4 || raw === null || raw === undefined) return '';
|
|
58
|
-
if (Array.isArray(raw)) {
|
|
59
|
-
for (const item of raw) {
|
|
60
|
-
const path = extractUploadPath(item, depth + 1);
|
|
61
|
-
if (path) return path;
|
|
62
|
-
}
|
|
63
|
-
return '';
|
|
64
|
-
}
|
|
65
|
-
if (typeof raw === 'object') {
|
|
66
|
-
for (const key of uploadPathKeys) {
|
|
67
|
-
const path = extractUploadPath(raw[key], depth + 1);
|
|
68
|
-
if (path) return path;
|
|
69
|
-
}
|
|
70
|
-
return '';
|
|
71
|
-
}
|
|
72
|
-
const value = String(raw).trim();
|
|
73
|
-
if (!value) return '';
|
|
74
|
-
if (/^[{[]/.test(value)) {
|
|
75
|
-
try { return extractUploadPath(JSON.parse(value), depth + 1); } catch (_) {}
|
|
76
|
-
}
|
|
77
|
-
return value;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export function normalizeUploadValue(raw) {
|
|
81
|
-
let value = raw;
|
|
82
|
-
if (typeof value === 'string' && /^[{[]/.test(value.trim())) {
|
|
83
|
-
try { value = JSON.parse(value); } catch (_) {}
|
|
84
|
-
}
|
|
85
|
-
const items = Array.isArray(value) ? value : [value];
|
|
86
|
-
return items.map(item => ({ item, path: extractUploadPath(item) })).filter(entry => entry.path);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export function resolveAssetUrl(raw, {
|
|
90
|
-
apiBase,
|
|
91
|
-
fileServer,
|
|
92
|
-
allowedHosts = []
|
|
93
|
-
}) {
|
|
94
|
-
const value = extractUploadPath(raw);
|
|
95
|
-
if (!value) return '';
|
|
96
|
-
|
|
97
|
-
if (/^https:\/\//i.test(value)) {
|
|
98
|
-
const host = new URL(value).hostname.toLowerCase();
|
|
99
|
-
return allowedHosts.includes(host) ? value : '';
|
|
100
|
-
}
|
|
101
|
-
if (/^blob:/i.test(value)) return value;
|
|
102
|
-
if (/^data:image\/(png|jpeg|gif|webp);base64,/i.test(value)) return value;
|
|
103
|
-
if (value.startsWith('/file/')) return `${apiBase.replace(/\/$/, '')}${value}`;
|
|
104
|
-
if (/^\/?micro-app\/v3(?:\/|$)/i.test(value)) {
|
|
105
|
-
return `${apiBase.replace(/\/$/, '')}/${value.replace(/^\/+/, '')}`;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const relative = value.replace(/^\/+/, '');
|
|
109
|
-
return `${fileServer.replace(/\/$/, '')}/${relative}`;
|
|
110
|
-
}
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
示例:
|
|
114
|
-
|
|
115
|
-
```vue
|
|
116
|
-
<script setup>
|
|
117
|
-
import { resolveAssetUrl } from '@/utils/assets.js';
|
|
118
|
-
|
|
119
|
-
function imageUrl(path) {
|
|
120
|
-
return resolveAssetUrl(path, {
|
|
121
|
-
apiBase: runtimeConfig.apiBase,
|
|
122
|
-
fileServer: runtimeConfig.fileServer,
|
|
123
|
-
allowedHosts: runtimeConfig.assetAllowedHosts
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
</script>
|
|
127
|
-
|
|
128
|
-
<template>
|
|
129
|
-
<image v-if="row.MainImg" :src="imageUrl(row.MainImg)" mode="aspectFill" />
|
|
130
|
-
</template>
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
不要让业务页面各自拼 `${API_BASE}/${path}` 或 `${FILE_SERVER}/${path}`;这会造成租户切换、私有桶、绝对 URL 和本地文件路由行为不一致。
|
|
134
|
-
|
|
135
|
-
## 大资源与小程序包体门禁
|
|
136
|
-
|
|
137
|
-
- Hero、Banner、资讯封面、视频、音频、字体和大型占位图等公开资源,先按真实显示尺寸压缩,再上传当前 `OsClient` 的公有 HDFS;数据库和配置只保存相对 `Path`,客户端运行时读取 `SysConfig.FileServer` 组成 CDN 地址。
|
|
138
|
-
- 发布前扫描真实小程序构建目录。单个非离线关键资源超过 `256 KB` 时默认不得进入主包;主包静态资源达到 `1.5 MB` 或距平台硬上限不足 `300 KB` 时必须中止上传并输出最大文件清单。
|
|
139
|
-
- 上传完成必须执行 CDN 匿名 GET 回读,验证 `200`、媒体类型、字节数,重要资源比对 SHA-256;同时回读业务字段确认已由 `/static/...` 改为 HDFS 相对路径。只有上传成功、数据库回读、CDN 回读和构建产物瘦身全部通过,才能关闭问题。
|
|
140
|
-
- 原始设计素材移到不会参与构建的资料目录;主包只保留轻量失败占位和离线关键图标。远程资源加载失败时显示该占位,禁止回退为同一张大图的本地副本。
|
|
141
|
-
|
|
142
|
-
## 私有文件
|
|
143
|
-
|
|
144
|
-
- 私有对象 Key 不能直接转换成可长期访问的公网地址。
|
|
145
|
-
- 前端通过受保护接口请求临时 URL;后端使用 `V8.Method.GetPrivateFileUrl({ FilePathName })` 或等价受控能力。
|
|
146
|
-
- 后端必须验证当前用户、`OsClient`、记录范围和字段绑定关系,不能只凭文件路径签名。
|
|
147
|
-
- 临时 URL 设置较短有效期,不写入数据库、不进入长期缓存、不记录完整签名参数。
|
|
148
|
-
- 下载响应使用安全的 `Content-Disposition`、MIME 白名单与文件名清洗。
|
|
149
|
-
|
|
150
|
-
## 富文本与 CSS 资源
|
|
151
|
-
|
|
152
|
-
- 富文本 HTML 先做标签、属性和协议白名单清洗,再改写允许的 `src` / `href`。
|
|
153
|
-
- 禁止 `javascript:`、任意 `data:text/html`、事件属性和未知 iframe。
|
|
154
|
-
- `background-image`、Markdown 图片、视频封面和头像与普通 `<image>` 使用同一资源解析策略。
|
|
155
|
-
- 商品详情等富文本可让图片自适应宽度,但文本容器仍需留出安全边距;不得在文件名或页面文案暴露成本、导入批次、内部目录或生成规则。
|
|
156
|
-
|
|
157
|
-
## 上传与预览边界
|
|
158
|
-
|
|
159
|
-
- 前端扩展名、大小提示只是体验校验;服务端仍须验证单文件大小、请求总量、租户配额、真实 MIME、魔数和文件名。
|
|
160
|
-
- 对象 Key 必须由服务端生成并包含租户隔离前缀,不能信任客户端提交的完整路径。
|
|
161
|
-
- SVG、HTML、脚本、压缩包和办公文档按风险策略处理;可执行内容不在同源页面直接渲染。
|
|
162
|
-
- 预览失败时显示受控占位,不回退到未经校验的原始 URL。
|
|
163
|
-
|
|
164
|
-
## 验收清单
|
|
165
|
-
|
|
166
|
-
- [ ] 公有对象、本地文件、`/micro-app/v3` 动态路由、私有对象、绝对 URL 的路径分支均有测试
|
|
167
|
-
- [ ] 绝对地址字符串、相对路径字符串、单图对象、多图数组及其 JSON 字符串均有测试
|
|
168
|
-
- [ ] 单图从数组取第一个有效路径,多图保持原顺序;解析 URL 时不丢失或改写上传对象元数据
|
|
169
|
-
- [ ] 切换两个 `OsClient` 后使用各自 `FileServer`,无跨租户路径或缓存复用
|
|
170
|
-
- [ ] 私有 URL 越权、过期、篡改签名和复制到其它账号均失败
|
|
171
|
-
- [ ] 网络面板中没有硬编码客户域名、真实租户或永久签名 URL
|
|
172
|
-
- [ ] 关键列表、详情、上传、预览页面有截图和真实资源加载断言
|
|
173
|
-
- [ ] `blob:` URL 及时释放,大文件预览不会持续占用内存
|
|
174
|
-
- [ ] 富文本协议/标签白名单可阻止 XSS
|
|
175
|
-
|
|
176
|
-
参见 `microi.skills/v8-file-upload/SKILL.md`、`microi.skills/microi-uniapp-frontend/SKILL.md` 和 `microi.skills/playwright-e2e/SKILL.md`。
|
|
1
|
+
---
|
|
2
|
+
name: uniapp-mall-assets
|
|
3
|
+
description: Microi UniApp、H5、商城与 PC 页面中的上传图片、附件、FileServer、私有文件临时 URL 和富文本资源安全规范。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
> **Codex 非阻塞自动更新:** 当前宿主为 Codex 时,吾码 CLI、Codex 插件与工作区 AI/MCP 由后台自动更新;需要诊断时读取 `../microi-codex-installer/SKILL.md`。更新失败、等待空闲或尚未重载均不得阻断当前、正在进行或新建任务。非 Codex 宿主跳过此项。
|
|
7
|
+
|
|
8
|
+
# Microi UniApp / 商城资源路径规范
|
|
9
|
+
|
|
10
|
+
本 Skill 只描述平台通用资源规则。不得写入客户名称、真实 `OsClient`、客户应用路径、客户接口 Key 或特定商城结算规则。
|
|
11
|
+
|
|
12
|
+
## 先识别资源类型
|
|
13
|
+
|
|
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`,不得在业务页面另造一套不完整的解析规则。
|
|
26
|
+
|
|
27
|
+
| 类型 | 处理方式 |
|
|
28
|
+
|---|---|
|
|
29
|
+
| 已允许的 `https://` 绝对地址 | 校验协议和允许域名后使用 |
|
|
30
|
+
| 公有对象存储相对路径 | 拼接当前租户 `FileServer` |
|
|
31
|
+
| `/file/...` 平台本地文件路由 | 拼接当前 API Base |
|
|
32
|
+
| `/micro-app/v3/...` 应用稳定解析路由 | 拼接当前 API Base;这是动态指针路由,不是 HDFS 对象 Key,禁止拼接 `FileServer` |
|
|
33
|
+
| 私有对象 | 后端鉴权后签发短期 URL,前端只使用临时 URL |
|
|
34
|
+
| `blob:` | 仅用于本页创建且能及时 `revokeObjectURL` 的预览 |
|
|
35
|
+
| `data:` | 仅允许经过大小和 MIME 校验的图片预览;禁止用于富文本任意 HTML |
|
|
36
|
+
|
|
37
|
+
`FileServer` 是对象存储/CDN 地址,不等同于 API 服务。租户切换时必须使用当前 `OsClient` 的运行期配置,禁止在源码中写死域名或租户目录。
|
|
38
|
+
|
|
39
|
+
### 跨端上传字段的形态与桶属性
|
|
40
|
+
|
|
41
|
+
- 控件读取嵌套 `Config.ImgUpload/FileUpload.Multiple` 与 `MaxCount`;`Multiple=false` 保存单图对象,不能因数量上限或默认九图值而保存数组。已有单元素数组在单图控件中按第一项读取,不在查看时回写业务数据。
|
|
42
|
+
- 上传时透传字段授权上下文与配置的 `Limit`,后端仍独立校验字段策略。上传响应中的实际 `Limit` 属于可持久化元数据,必须保留;临时 `Url/PreviewUrl` 与本地预览地址不得入库。
|
|
43
|
+
- 历史对象缺少 `Limit` 时,以字段配置作为取址依据。已有对象明确 `Limit=true/1` 时,即使字段当前为公有,也必须签名;私有字段上下文与实际私有标记优先于公有提示。签发失败保持空地址,禁止尝试把私有路径拼到公有 CDN。
|
|
44
|
+
|
|
45
|
+
## 统一资源解析函数
|
|
46
|
+
|
|
47
|
+
未使用 `microi.v8.js` 的项目,应在共享请求/资源模块中实现等价的“提取上传路径 + 解析资源 URL”两层函数,所有页面复用同一逻辑:
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
const uploadPathKeys = [
|
|
51
|
+
'Url', 'FileUrl', 'FileURL', 'PreviewUrl', 'PreviewURL', 'FullUrl',
|
|
52
|
+
'Path', 'FilePathName', 'FilePath', 'FullPath', 'Src', 'Href',
|
|
53
|
+
'url', 'fileUrl', 'previewUrl', 'fullUrl', 'path', 'filePathName', 'filePath', 'fullPath', 'src', 'href'
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
export function extractUploadPath(raw, depth = 0) {
|
|
57
|
+
if (depth > 4 || raw === null || raw === undefined) return '';
|
|
58
|
+
if (Array.isArray(raw)) {
|
|
59
|
+
for (const item of raw) {
|
|
60
|
+
const path = extractUploadPath(item, depth + 1);
|
|
61
|
+
if (path) return path;
|
|
62
|
+
}
|
|
63
|
+
return '';
|
|
64
|
+
}
|
|
65
|
+
if (typeof raw === 'object') {
|
|
66
|
+
for (const key of uploadPathKeys) {
|
|
67
|
+
const path = extractUploadPath(raw[key], depth + 1);
|
|
68
|
+
if (path) return path;
|
|
69
|
+
}
|
|
70
|
+
return '';
|
|
71
|
+
}
|
|
72
|
+
const value = String(raw).trim();
|
|
73
|
+
if (!value) return '';
|
|
74
|
+
if (/^[{[]/.test(value)) {
|
|
75
|
+
try { return extractUploadPath(JSON.parse(value), depth + 1); } catch (_) {}
|
|
76
|
+
}
|
|
77
|
+
return value;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function normalizeUploadValue(raw) {
|
|
81
|
+
let value = raw;
|
|
82
|
+
if (typeof value === 'string' && /^[{[]/.test(value.trim())) {
|
|
83
|
+
try { value = JSON.parse(value); } catch (_) {}
|
|
84
|
+
}
|
|
85
|
+
const items = Array.isArray(value) ? value : [value];
|
|
86
|
+
return items.map(item => ({ item, path: extractUploadPath(item) })).filter(entry => entry.path);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function resolveAssetUrl(raw, {
|
|
90
|
+
apiBase,
|
|
91
|
+
fileServer,
|
|
92
|
+
allowedHosts = []
|
|
93
|
+
}) {
|
|
94
|
+
const value = extractUploadPath(raw);
|
|
95
|
+
if (!value) return '';
|
|
96
|
+
|
|
97
|
+
if (/^https:\/\//i.test(value)) {
|
|
98
|
+
const host = new URL(value).hostname.toLowerCase();
|
|
99
|
+
return allowedHosts.includes(host) ? value : '';
|
|
100
|
+
}
|
|
101
|
+
if (/^blob:/i.test(value)) return value;
|
|
102
|
+
if (/^data:image\/(png|jpeg|gif|webp);base64,/i.test(value)) return value;
|
|
103
|
+
if (value.startsWith('/file/')) return `${apiBase.replace(/\/$/, '')}${value}`;
|
|
104
|
+
if (/^\/?micro-app\/v3(?:\/|$)/i.test(value)) {
|
|
105
|
+
return `${apiBase.replace(/\/$/, '')}/${value.replace(/^\/+/, '')}`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const relative = value.replace(/^\/+/, '');
|
|
109
|
+
return `${fileServer.replace(/\/$/, '')}/${relative}`;
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
示例:
|
|
114
|
+
|
|
115
|
+
```vue
|
|
116
|
+
<script setup>
|
|
117
|
+
import { resolveAssetUrl } from '@/utils/assets.js';
|
|
118
|
+
|
|
119
|
+
function imageUrl(path) {
|
|
120
|
+
return resolveAssetUrl(path, {
|
|
121
|
+
apiBase: runtimeConfig.apiBase,
|
|
122
|
+
fileServer: runtimeConfig.fileServer,
|
|
123
|
+
allowedHosts: runtimeConfig.assetAllowedHosts
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
</script>
|
|
127
|
+
|
|
128
|
+
<template>
|
|
129
|
+
<image v-if="row.MainImg" :src="imageUrl(row.MainImg)" mode="aspectFill" />
|
|
130
|
+
</template>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
不要让业务页面各自拼 `${API_BASE}/${path}` 或 `${FILE_SERVER}/${path}`;这会造成租户切换、私有桶、绝对 URL 和本地文件路由行为不一致。
|
|
134
|
+
|
|
135
|
+
## 大资源与小程序包体门禁
|
|
136
|
+
|
|
137
|
+
- Hero、Banner、资讯封面、视频、音频、字体和大型占位图等公开资源,先按真实显示尺寸压缩,再上传当前 `OsClient` 的公有 HDFS;数据库和配置只保存相对 `Path`,客户端运行时读取 `SysConfig.FileServer` 组成 CDN 地址。
|
|
138
|
+
- 发布前扫描真实小程序构建目录。单个非离线关键资源超过 `256 KB` 时默认不得进入主包;主包静态资源达到 `1.5 MB` 或距平台硬上限不足 `300 KB` 时必须中止上传并输出最大文件清单。
|
|
139
|
+
- 上传完成必须执行 CDN 匿名 GET 回读,验证 `200`、媒体类型、字节数,重要资源比对 SHA-256;同时回读业务字段确认已由 `/static/...` 改为 HDFS 相对路径。只有上传成功、数据库回读、CDN 回读和构建产物瘦身全部通过,才能关闭问题。
|
|
140
|
+
- 原始设计素材移到不会参与构建的资料目录;主包只保留轻量失败占位和离线关键图标。远程资源加载失败时显示该占位,禁止回退为同一张大图的本地副本。
|
|
141
|
+
|
|
142
|
+
## 私有文件
|
|
143
|
+
|
|
144
|
+
- 私有对象 Key 不能直接转换成可长期访问的公网地址。
|
|
145
|
+
- 前端通过受保护接口请求临时 URL;后端使用 `V8.Method.GetPrivateFileUrl({ FilePathName })` 或等价受控能力。
|
|
146
|
+
- 后端必须验证当前用户、`OsClient`、记录范围和字段绑定关系,不能只凭文件路径签名。
|
|
147
|
+
- 临时 URL 设置较短有效期,不写入数据库、不进入长期缓存、不记录完整签名参数。
|
|
148
|
+
- 下载响应使用安全的 `Content-Disposition`、MIME 白名单与文件名清洗。
|
|
149
|
+
|
|
150
|
+
## 富文本与 CSS 资源
|
|
151
|
+
|
|
152
|
+
- 富文本 HTML 先做标签、属性和协议白名单清洗,再改写允许的 `src` / `href`。
|
|
153
|
+
- 禁止 `javascript:`、任意 `data:text/html`、事件属性和未知 iframe。
|
|
154
|
+
- `background-image`、Markdown 图片、视频封面和头像与普通 `<image>` 使用同一资源解析策略。
|
|
155
|
+
- 商品详情等富文本可让图片自适应宽度,但文本容器仍需留出安全边距;不得在文件名或页面文案暴露成本、导入批次、内部目录或生成规则。
|
|
156
|
+
|
|
157
|
+
## 上传与预览边界
|
|
158
|
+
|
|
159
|
+
- 前端扩展名、大小提示只是体验校验;服务端仍须验证单文件大小、请求总量、租户配额、真实 MIME、魔数和文件名。
|
|
160
|
+
- 对象 Key 必须由服务端生成并包含租户隔离前缀,不能信任客户端提交的完整路径。
|
|
161
|
+
- SVG、HTML、脚本、压缩包和办公文档按风险策略处理;可执行内容不在同源页面直接渲染。
|
|
162
|
+
- 预览失败时显示受控占位,不回退到未经校验的原始 URL。
|
|
163
|
+
|
|
164
|
+
## 验收清单
|
|
165
|
+
|
|
166
|
+
- [ ] 公有对象、本地文件、`/micro-app/v3` 动态路由、私有对象、绝对 URL 的路径分支均有测试
|
|
167
|
+
- [ ] 绝对地址字符串、相对路径字符串、单图对象、多图数组及其 JSON 字符串均有测试
|
|
168
|
+
- [ ] 单图从数组取第一个有效路径,多图保持原顺序;解析 URL 时不丢失或改写上传对象元数据
|
|
169
|
+
- [ ] 切换两个 `OsClient` 后使用各自 `FileServer`,无跨租户路径或缓存复用
|
|
170
|
+
- [ ] 私有 URL 越权、过期、篡改签名和复制到其它账号均失败
|
|
171
|
+
- [ ] 网络面板中没有硬编码客户域名、真实租户或永久签名 URL
|
|
172
|
+
- [ ] 关键列表、详情、上传、预览页面有截图和真实资源加载断言
|
|
173
|
+
- [ ] `blob:` URL 及时释放,大文件预览不会持续占用内存
|
|
174
|
+
- [ ] 富文本协议/标签白名单可阻止 XSS
|
|
175
|
+
|
|
176
|
+
参见 `microi.skills/v8-file-upload/SKILL.md`、`microi.skills/microi-uniapp-frontend/SKILL.md` 和 `microi.skills/playwright-e2e/SKILL.md`。
|