@microi.net/cli 4.9.5 → 4.9.7

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.
Files changed (133) hide show
  1. package/.codebuddy-plugin/marketplace.json +2 -2
  2. package/.codebuddy-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +1 -1
  4. package/.workbuddy-plugin/marketplace.json +2 -2
  5. package/.workbuddy-plugin/plugin.json +1 -1
  6. package/README.md +7 -12
  7. package/assets/build-meta.json +6 -5
  8. package/package.json +1 -1
  9. package/scripts/mcp-server.js +83 -83
  10. package/scripts/microi-cli.js +80 -92
  11. package/scripts/microi-codex-broker.js +418 -0
  12. package/scripts/microi-codex-router.js +159 -65
  13. package/scripts/microi-skills.meta.json +349 -190
  14. package/skills/.microi-skills-version.json +2 -2
  15. package/skills/.progressive-disclosure-manifest.json +3566 -0
  16. package/skills/ai-engine/SKILL.md +1 -1
  17. package/skills/ai-platform-governance/SKILL.md +22 -167
  18. package/skills/ai-platform-governance/references/progressive-01-/345/212/237/350/203/275/345/274/200/345/205/263.md +190 -0
  19. package/skills/app-store/SKILL.md +1 -1
  20. package/skills/business-blueprint/SKILL.md +1 -1
  21. package/skills/datasource-engine/SKILL.md +1 -1
  22. package/skills/dos-orm/SKILL.md +1 -1
  23. package/skills/job-engine/SKILL.md +1 -1
  24. package/skills/message-notification/SKILL.md +1 -1
  25. package/skills/microi-ai-application/SKILL.md +1 -1
  26. package/skills/microi-client-frontend/SKILL.md +18 -435
  27. 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 -0
  28. 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 +178 -0
  29. 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 +144 -0
  30. package/skills/microi-codex/SKILL.md +4 -4
  31. package/skills/microi-codex-installer/SKILL.md +25 -36
  32. package/skills/microi-datasource-mapping/SKILL.md +1 -1
  33. package/skills/microi-db-schema/SKILL.md +4 -4
  34. package/skills/microi-db-schema/references/schema-overview.md +1 -1
  35. package/skills/microi-db-schema/references/schema.md +1 -1
  36. package/skills/microi-db-schema/references/table-catalog.md +1 -1
  37. package/skills/microi-deployment/SKILL.md +1 -1
  38. package/skills/microi-docs-coverage/SKILL.md +1 -1
  39. package/skills/microi-form-engine/SKILL.md +2 -2
  40. package/skills/microi-form-layout/SKILL.md +20 -226
  41. 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 -0
  42. package/skills/microi-frontend-sdk/SKILL.md +18 -152
  43. 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 +171 -0
  44. package/skills/microi-left-right-layout/SKILL.md +1 -1
  45. package/skills/microi-microservice/SKILL.md +8 -1
  46. package/skills/microi-microservice/references/runtime-delivery.md +4 -0
  47. package/skills/microi-mobile-app-quality/SKILL.md +23 -289
  48. 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 +209 -0
  49. 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 +117 -0
  50. package/skills/microi-solution-quotation/SKILL.md +1 -1
  51. package/skills/microi-system-delivery/SKILL.md +17 -381
  52. package/skills/microi-system-delivery/references/progressive-01-/346/240/207/345/207/206/345/267/245/344/275/234/346/265/201.md +186 -0
  53. 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 +210 -0
  54. package/skills/microi-ui/SKILL.md +20 -170
  55. 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 -0
  56. package/skills/microi-uniapp-frontend/SKILL.md +27 -336
  57. 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 -0
  58. 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 -0
  59. package/skills/module-engine/SKILL.md +1 -1
  60. package/skills/ocr-engine/SKILL.md +1 -1
  61. package/skills/page-engine/SKILL.md +24 -272
  62. 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 -0
  63. 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 -0
  64. package/skills/performance-testing/SKILL.md +1 -1
  65. package/skills/playwright-e2e/SKILL.md +25 -591
  66. 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 -0
  67. 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 -0
  68. package/skills/playwright-e2e/references/progressive-03-microi-helper-/346/250/241/346/235/277.md +221 -0
  69. package/skills/playwright-e2e/references/progressive-04-ci-/345/273/272/350/256/256.md +69 -0
  70. package/skills/print-engine/SKILL.md +1 -1
  71. package/skills/production-readonly-audit/SKILL.md +1 -1
  72. package/skills/report-engine/SKILL.md +1 -1
  73. package/skills/scripts/optimize-progressive-disclosure.mjs +204 -0
  74. package/skills/scripts/refresh-progressive-disclosure.mjs +64 -0
  75. package/skills/scripts/validate-progressive-disclosure.mjs +52 -0
  76. package/skills/search-engine/SKILL.md +1 -1
  77. package/skills/spider-engine/SKILL.md +1 -1
  78. package/skills/translate-engine/SKILL.md +1 -1
  79. package/skills/ui-design/SKILL.md +27 -1462
  80. 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 -0
  81. package/skills/ui-design/references/progressive-02-/345/255/227/344/275/223.md +155 -0
  82. 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 -0
  83. 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 -0
  84. 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 -0
  85. 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 -0
  86. 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 -0
  87. 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 +142 -0
  88. package/skills/uniapp-mall-assets/SKILL.md +1 -1
  89. package/skills/unity-integration/SKILL.md +1 -1
  90. package/skills/v8-api-config/SKILL.md +1 -1
  91. package/skills/v8-cache-pattern/SKILL.md +1 -1
  92. package/skills/v8-crud-api/SKILL.md +21 -246
  93. 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 -0
  94. 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 -0
  95. package/skills/v8-debugging/SKILL.md +1 -1
  96. package/skills/v8-explorer-tree/SKILL.md +1 -1
  97. package/skills/v8-export-import/SKILL.md +16 -426
  98. 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 -0
  99. package/skills/v8-export-import/references/progressive-02-powerpoint-/345/257/274/345/207/272.md +202 -0
  100. 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 -0
  101. package/skills/v8-file-upload/SKILL.md +17 -355
  102. 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 +227 -0
  103. 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 +149 -0
  104. package/skills/v8-formengine-http/SKILL.md +1 -1
  105. package/skills/v8-frontend-events/SKILL.md +20 -206
  106. package/skills/v8-frontend-events/references/progressive-01-/345/210/227/350/241/250/344/272/213/344/273/266.md +219 -0
  107. package/skills/v8-http-integration/SKILL.md +15 -237
  108. package/skills/v8-http-integration/references/progressive-01-get-/350/257/267/346/261/202.md +220 -0
  109. 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 -0
  110. package/skills/v8-image-processing/SKILL.md +1 -1
  111. package/skills/v8-menu-buttons/SKILL.md +16 -512
  112. package/skills/v8-menu-buttons/references/progressive-01-2-/346/214/211/351/222/256/345/257/271/350/261/241-schema.md +221 -0
  113. 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 +224 -0
  114. 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 -0
  115. package/skills/v8-mongodb/SKILL.md +1 -1
  116. package/skills/v8-mq-mqtt/SKILL.md +12 -176
  117. package/skills/v8-mq-mqtt/references/progressive-01-v8-mqtt-iot-/347/211/251/350/201/224/347/275/221.md +181 -0
  118. package/skills/v8-saas-multi-tenant/SKILL.md +1 -1
  119. package/skills/v8-security/SKILL.md +17 -330
  120. package/skills/v8-security/references/progressive-01-2-/346/235/203/351/231/220/346/240/241/351/252/214.md +199 -0
  121. package/skills/v8-security/references/progressive-02-7-/346/227/245/345/277/227/350/256/260/345/275/225.md +158 -0
  122. package/skills/v8-sql-query/SKILL.md +1 -1
  123. package/skills/v8-table-event/SKILL.md +17 -237
  124. 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 +216 -0
  125. 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 -0
  126. package/skills/v8-template-engine/SKILL.md +1 -1
  127. package/skills/v8-utilities/SKILL.md +1 -1
  128. package/skills/v8-workflow/SKILL.md +20 -161
  129. 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 -0
  130. package/skills/workspace-conventions/SKILL.md +36 -369
  131. 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 -0
  132. 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 +196 -0
  133. 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 -0
@@ -0,0 +1,235 @@
1
+ # microi-form-layout 详细参考 1
2
+
3
+ > 按需读取;本文件由 SKILL.md 的原章节无损拆分。
4
+
5
+ <!-- microi-progressive:chunk id=microi-form-layout-006 sha256=a78b000a9fee46d8394b0b6cb186702e520ab513f7eff098f2226f1158e46970 -->
6
+ ## 3. 三种分组的存储与配置
7
+
8
+ ### 3.1 diy_table.Tabs(表级 Tab)
9
+
10
+ 存储:`diy_table.Tabs`(JSON 字符串)+ 每个字段的 `diy_field.Tab`(归属 Tab 名)。
11
+
12
+ ```jsonc
13
+ // diy_table.Tabs JSON 格式
14
+ [
15
+ { "Id": "basic", "Name": "基础信息", "Sort": 10 },
16
+ { "Id": "business","Name": "业务明细", "Sort": 20 },
17
+ { "Id": "attach", "Name": "附件备注", "Sort": 30 }
18
+ ]
19
+ ```
20
+
21
+ 字段归属:在 `diy_field.Tab` 写 `Id`(不是 `Name`)。`Tab` 留空的字段属于"非 Tab 字段"(即 `diy_table.Tabs` 之外的字段),会作为隐藏的剩余字段自动归到最后 Tab。
22
+
23
+ - 字段 `Tab="basic"` → 归属"基础信息"Tab
24
+ - 字段 `Tab=""` 且 `diy_table.Tabs` 存在 → 自动归到最后一个 Tab 的剩余字段
25
+ - 字段 `Tab=""` 且 `diy_table.Tabs` 不存在 → 全部在第一屏平铺
26
+
27
+ **不推荐用法**:把 `diy_table.Tabs` 拆出 3 个 Tab、每个 Tab 内只有 2~3 个字段。这会让用户必须点击 3 次 Tab 才能看完一张表,且首屏只看到 2~3 个字段。
28
+
29
+ ### 3.2 字段级 Tabs 控件(`diy_field.Component='Tabs'`)
30
+
31
+ 存储:`diy_field` 行 + `Config.FieldTabs`(JSON)。
32
+
33
+ ```jsonc
34
+ // diy_field 必要字段
35
+ {
36
+ "Id": "TabsField_Main",
37
+ "Name": "TabsMain",
38
+ "Label": "主分组",
39
+ "Component": "Tabs",
40
+ "Type": "varchar(50)",
41
+ "Sort": 50,
42
+ "Visible": 0, // 通常设为 0,因为 Tabs 本身是布局控件
43
+ "AppVisible": 0,
44
+ "Config": "{\"FieldTabs\":{...}}"
45
+ }
46
+
47
+ // Config.FieldTabs
48
+ {
49
+ "ScopeMode": "FieldCount", // 或 "Manual"
50
+ "TotalFieldCount": 0, // 0 表示直到下一个 Tabs
51
+ "DefaultActiveKey": "tab1",
52
+ "Type": "card", // "" | "card" | "border-card"
53
+ "Position": "top", // top | bottom | left | right
54
+ "Stretch": false,
55
+ "ShowFieldCount": true,
56
+ "CaptureRest": true,
57
+ "Description": "",
58
+ "Theme": "default",
59
+ "Tabs": [
60
+ { "Key": "tab1", "Title": "页签一", "Icon": "fas fa-info-circle", "FieldCount": 6, "Disabled": false }
61
+ ]
62
+ }
63
+ ```
64
+
65
+ **作用范围**:从该 Tabs 字段开始,到下一个 `Component in (Tabs, CollapseGroup, Divider)` 字段为止。
66
+
67
+ ### 3.3 字段级 CollapseGroup 折叠分组(`diy_field.Component='CollapseGroup'`)
68
+
69
+ 存储:`diy_field` 行 + `Config.CollapseGroup`(JSON)。
70
+
71
+ ```jsonc
72
+ // diy_field 必要字段
73
+ {
74
+ "Id": "CollapseGroup_MRP",
75
+ "Name": "MrpGroup",
76
+ "Label": "MRP 运算",
77
+ "Component": "CollapseGroup",
78
+ "Type": "",
79
+ "Sort": 120,
80
+ "Visible": 1,
81
+ "AppVisible": 1,
82
+ "FormWidth": 24,
83
+ "Config": "{\"CollapseGroup\":{...}}"
84
+ }
85
+
86
+ // Config.CollapseGroup
87
+ {
88
+ "DefaultCollapsed": false, // 默认展开;高频访问分组可设 false
89
+ "ScopeMode": "UntilNextGroup", // 直到下一个折叠/Tab/Divider
90
+ "FieldCount": 5, // ScopeMode=FieldCount 时生效
91
+ "Description": "MRP 运算结果与时间",
92
+ "Icon": "fas fa-calculator",
93
+ "Theme": "primary", // default | primary | success | warning | danger
94
+ "ShowFieldCount": true
95
+ }
96
+ ```
97
+
98
+ **作用范围**:从该 CollapseGroup 字段开始,到下一个 `Component in (Tabs, CollapseGroup, Divider)` 字段为止。
99
+
100
+ **默认值硬规则**:`CollapseGroup` 必须保存 `FormWidth=24`(PC 表单 100% 宽度);
101
+ `Config.CollapseGroup.ShowFieldCount` 省略时必须补为 `true`。只有用户明确要求隐藏数量时
102
+ 才允许写 `ShowFieldCount=false`,只有用户明确要求非整行实验布局时才允许覆盖宽度。
103
+
104
+ **与 Tab 的关键区别**:所有 CollapseGroup 标题**始终可见**,分组内字段**默认展开**或**默认收起**,但所有分组的字段**都在同一页面**,可同时展开多个。
105
+
106
+ ### 3.4 控件视觉对比
107
+
108
+ | 视觉表现 | Tabs | CollapseGroup |
109
+ |---------|------|---------------|
110
+ | 首屏可见字段数 | 仅一个 Tab 的字段 | **所有分组的标题 + 展开分组的字段** |
111
+ | 用户切换分组方式 | 必须点击 Tab 头 | 可直接滚动或逐个点击展开 |
112
+ | 同时看到多组 | ❌ | ✅ |
113
+ | 适合"展开后阅读" | ❌(频繁切换会烦) | ✅ |
114
+ | 适合"互斥分组" | ✅ | ❌ |
115
+
116
+ <!-- /microi-progressive:chunk -->
117
+ <!-- microi-progressive:chunk id=microi-form-layout-007 sha256=5944a965b1ac9a5c87a0e066af7f6489d2b5cd09e55fff7dc4e43d8d3a029fd6 -->
118
+ ## 7. 反例参考(必须避免)
119
+
120
+ ### 反例 1:MRP 运算 3 字段单独建 Tab
121
+
122
+ ```
123
+ ❌ 错误:
124
+ diy_table.Tabs = [
125
+ { Id: 'basic', Name: '基础信息' }, // 5 字段
126
+ { Id: 'mrp', Name: 'MRP 运算' }, // 3 字段
127
+ { Id: 'remark', Name: '备注' } // 1 字段
128
+ ]
129
+ // 用户打开表单,第一屏只看到 5 个"基础信息"字段,"MRP 运算"和"备注"被藏在 Tab 里
130
+
131
+ ✅ 正确:
132
+ // 不创建 diy_table.Tabs,把"MRP 运算" 3 字段用 CollapseGroup 收在表单末尾(默认展开)
133
+ // 把"备注"也用 CollapseGroup 或 Divider 收
134
+ // 第一屏用户能看到所有基础信息 + MRP 运算
135
+ ```
136
+
137
+ ### 反例 1.1:项目收款记录拆成 2/9/2 三个 Tab
138
+
139
+ ```
140
+ ❌ 错误:
141
+ 项目(2 个短字段) + 收款(9 个短字段) + 附件备注(2 个整行字段)分别建 Tab。
142
+ 结果是每页只有 1~5 行内容,桌面抽屉出现大面积空白,用户要切换三次才能看完整记录。
143
+
144
+ ✅ 正确:
145
+ 取消表级 Tab,按原顺序建立“项目信息 / 收款信息 / 附件备注”三个 CollapseGroup。
146
+ 核心组默认展开,低频附件备注可默认收起;保留原字段、数据源、必填规则和 V8 代码。
147
+ ```
148
+
149
+ ### 反例 2:13 字段表全平铺
150
+
151
+ ```
152
+ ❌ 错误:
153
+ // 13 字段全部 Tab 留空
154
+ // 用户必须向下滚动 3 屏才能看到所有字段
155
+
156
+ ✅ 正确:
157
+ // 13 字段按业务分两组:8 字段"基础信息" + 5 字段"业务明细"
158
+ // 用 1 个 diy_table.Tabs(基础信息 + 业务明细)
159
+ // 或用 1 个 CollapseGroup 把"业务明细"5 字段收起
160
+ ```
161
+
162
+ ### 反例 3:42 字段表用 6 个 Tab
163
+
164
+ ```
165
+ ❌ 错误:
166
+ // 6 个 Tab:基础(14) + 项目业主(2) + 发货通知(13) + 生产需求(3) + 审核(4) + ERP出库(4) + 其他(2)
167
+ // 用户要点 6 次才能看完,且"项目业主"和"生产需求"这种 2~3 字段的 Tab 完全没必要
168
+
169
+ ✅ 正确:
170
+ // 4 个 Tab:基础(14) + 发货通知+生产需求(16) + 审核+ERP出库(8) + 其他(4)
171
+ // 或 3 个 Tab + 内部嵌套 CollapseGroup
172
+ ```
173
+
174
+ <!-- /microi-progressive:chunk -->
175
+ <!-- microi-progressive:chunk id=microi-form-layout-008 sha256=1d93c83d865ef941f62c4e8e0dd6238489645bf53367b61e6e397543447dcdbd -->
176
+ ## 8. 快速参考代码片段
177
+
178
+ ### 8.1 MCP 创建表级 Tab
179
+
180
+ ```js
181
+ // 假设已创建 diy_table,通过 microi_update_table 设置 Tabs
182
+ // 注意:microi_create_table 不直接接收 Tabs JSON,需创建后 microi_update_table 补全
183
+ microi_update_table({
184
+ name: "yutaoliaojieguo",
185
+ // 暂未直接传 Tabs,需要通过 microi_update_table 文档化的方式补全
186
+ })
187
+ ```
188
+
189
+ > 实际写入 `diy_table.Tabs` 优先用 `microi_update_field` 之外的元数据写入方式或 `microi_upsert_engine` 委托接口引擎;后续 MCP 工具可补强 `Tabs` 参数。
190
+
191
+ ### 8.2 MCP 创建字段级 CollapseGroup
192
+
193
+ ```js
194
+ // 1. 创建一个 CollapseGroup 字段
195
+ microi_add_layout_field({
196
+ tableId: "01KTASHWEBE514R1XTB0WVJJRX",
197
+ name: "MrpGroup",
198
+ label: "MRP 运算",
199
+ component: "CollapseGroup",
200
+ sort: 150,
201
+ visible: 1,
202
+ appVisible: 1,
203
+ config: JSON.stringify({
204
+ CollapseGroup: {
205
+ DefaultCollapsed: false,
206
+ ScopeMode: "UntilNextGroup",
207
+ Description: "MRP 运算状态、批次号与时间",
208
+ Icon: "fas fa-calculator",
209
+ Theme: "primary",
210
+ ShowFieldCount: true
211
+ }
212
+ }),
213
+ confirmExecution: "MrpGroup"
214
+ })
215
+
216
+ // 工具默认写入 FormWidth=24;回读必须确认宽度为 24 且 ShowFieldCount=true。
217
+
218
+ // 2. 让"MRP 运算"相关字段归属到该 CollapseGroup
219
+ // 范围方式:把 CalcStatus、CalcBatchNo、MrpTime 三个字段的 Sort 排在 150~300 之间,
220
+ // 下一个 CollapseGroup/Tabs/Divider 字段之前的所有字段都属于该分组
221
+ ```
222
+
223
+ ### 8.3 MCP 把字段 Tab 归属到 diy_table.Tabs
224
+
225
+ ```js
226
+ // 创建表级 Tab
227
+ // 1. microi_update_table 设置 diy_table.Tabs 字段(待 MCP 工具补全)
228
+ // 2. 给字段写 Tab 归属
229
+ microi_update_field({
230
+ id: "01KVTWDJ7WXB3Z60HGJ5BPTBJ0",
231
+ tab: "basic" // 归属到 diy_table.Tabs.Id='basic' 的 Tab
232
+ })
233
+ ```
234
+
235
+ <!-- /microi-progressive:chunk -->
@@ -3,12 +3,14 @@ name: microi-frontend-sdk
3
3
  description: Microi 前端 SDK 使用规范,适用于 Vue 3、uni-app、H5、PC 网站与 Microi.Client 扩展。用于创建或修改前端请求、登录态、Token 续签、终端会话、上传、文件 URL、ApiEngine、FormEngine 或应用启动代码。
4
4
  ---
5
5
 
6
- > **Codex 强制前置:** 当前宿主为 Codex 时,在使用本 Skill 前必须先完整读取 `../microi-codex-installer/SKILL.md`,完成“Codex 每任务最新版硬门禁”;门禁未通过不得继续本 Skill。非 Codex 宿主跳过此项。
6
+ > **Codex 非阻塞自动更新:** 当前宿主为 Codex 时,吾码 CLI、Codex 插件与工作区 AI/MCP 由后台自动更新;需要诊断时读取 `../microi-codex-installer/SKILL.md`。更新失败、等待空闲或尚未重载均不得阻断当前、正在进行或新建任务。非 Codex 宿主跳过此项。
7
7
 
8
8
  # Microi 前端 SDK
9
9
 
10
10
  所有 Vue 3 前端项目都应使用 `microi.skills/microi.v8.js` 作为统一的 Microi 前端 SDK。新项目不要复制旧版 Vue2/Vuex 请求封装,也不要重新手写 token、上传、文件 URL、ApiEngine 或 FormEngine 层。
11
11
 
12
+ <!-- microi-progressive:begin -->
13
+ <!-- microi-progressive:chunk id=microi-frontend-sdk-000 sha256=06f944bc009a4e773ae6d5496d435d3e4a4fdb23d59107dac9cedffcfdf18f86 -->
12
14
  ## 必须采用的模式
13
15
 
14
16
  将 SDK 复制到项目源码目录,通常是:
@@ -53,6 +55,8 @@ export function createApp() {
53
55
 
54
56
  页面和业务接口模块应从项目请求模块导入已配置实例或薄封装函数,不要直接从标准 skill 文件导入。
55
57
 
58
+ <!-- /microi-progressive:chunk -->
59
+ <!-- microi-progressive:chunk id=microi-frontend-sdk-001 sha256=f1c2ab1fadc01dbe8ea4b9de98f7c02192c3f2cfed8b792fe62f8ef516b67d83 -->
56
60
  ## 必须委托 SDK 的能力
57
61
 
58
62
  - `ApiEngine.Run`:直接调用 `/apiengine/{key}` 时使用 `V8.ApiEngine.Run(key, data)`。
@@ -66,6 +70,8 @@ export function createApp() {
66
70
 
67
71
  `Microi.Client` 主后台运行时已内置前后端同构的 `V8.Http.Get/Post/Patch` 及对应 Response 方法;表单事件、按钮 V8 等宿主前端新代码必须优先使用 `V8.Http`,旧 `V8.Post/Get` 仅作兼容保留,其参数和兼容规则以 `v8-http-integration/SKILL.md` 为准。独立项目使用本 SDK、且不在主后台 V8 宿主中时,才使用 SDK 自身的小写 `V8.get/post`、`ApiEngine`、`FormEngine`;不要把它们与宿主旧版大写 `V8.Post/Get` 混为一谈,也不要假设浏览器可以绕过第三方接口的 CORS。
68
72
 
73
+ <!-- /microi-progressive:chunk -->
74
+ <!-- microi-progressive:chunk id=microi-frontend-sdk-002 sha256=5842c30af751f60041e4435efe5c993144a874cb2e9d97c41fb37d1f06d6474e -->
69
75
  ## 登录与验证码封装
70
76
 
71
77
  SDK 或项目请求模块必须提供登录所需的系统配置和验证码薄封装,不要让页面散落手写。
@@ -113,6 +119,8 @@ AI 生成的前端微服务不能假定永远在主平台 iframe/micro-app 宿
113
119
  - 登录仍签发平台 DiyToken,不创建平行 Token、平行用户表或微服务自有密码体系。失效事件回到登录态,Token 续签仍按本 Skill 的单实例规则处理。
114
120
  - 宿主额外传入 `permissionContext={sysMenuId,moduleEngineKey,diyTableId}`。SDK/服务层需要访问 FormEngine 时使用真实授权 `moduleEngineKey`;该对象不能代替后端权限,也不能成为放宽匿名接口的理由。
115
121
 
122
+ <!-- /microi-progressive:chunk -->
123
+ <!-- microi-progressive:chunk id=microi-frontend-sdk-003 sha256=d5d1984e6cd4efbb2340f984473146c66bb342d60bb571454c457f5673b1c68f -->
116
124
  ## 请求头规则
117
125
 
118
126
  SDK 的 `buildHeaders` 必须集中处理所有请求头,不能让页面、业务 wrapper 或上传逻辑各自拼接租户和鉴权头。
@@ -123,85 +131,8 @@ SDK 的 `buildHeaders` 必须集中处理所有请求头,不能让页面、业
123
131
  - 小程序授权登录、账号登录、刷新 Token、FormEngine、ApiEngine、上传都必须走同一套去重逻辑。
124
132
  - 验收时检查真实网络请求:不得出现 `osclient: demo, demo`、`Authorization: Bearer xxx, Bearer xxx` 这类逗号合并值。
125
133
 
126
- ## Token、当前登录用户与当前终端登录协议
127
-
128
- Microi 后端不是只保存一个全局 Token。每个租户、每个 `sys_user` 在 Redis 中维护一份 `CurrentToken`,其中 `CurrentUser` 表示平台当前登录用户,`Tokens` 表示该用户的多个当前终端登录。每个终端项至少包含 `Token`、`ClientType`、`Did`、`IP`、`CreateTime`、`UpdateTime`;退出、管理员清除登录信息、同终端重新登录或 Token 轮换都会影响该列表。
129
-
130
- 登录必须同时标记终端类型和稳定设备 Id:
131
-
132
- ```js
133
- const V8 = createMicroiV8({
134
- apiBase,
135
- osClient,
136
- clientType: 'Mobile', // PC / Mobile / H5 / App / WxMiniProgram / VSCode / MCP
137
- didKey: 'microi_did'
138
- });
139
-
140
- const result = await V8.Login({
141
- Account,
142
- Pwd,
143
- _ClientType: 'Mobile'
144
- });
145
- ```
146
-
147
- - PC 后台传 `_ClientType:'PC'`,有效期读取 SaaS 引擎 `SessionAuthTimeout`,单位分钟,默认 20 分钟。
148
- - VS Code 传 `_ClientType:'VSCode'`,优先读取 `VSCodeAccessTokenLifetime`,否则读取 `AccessTokenLifetime`,单位天,默认 30 天。
149
- - MCP 传 `_ClientType:'MCP'`,优先读取 `McpAccessTokenLifetime`,否则读取 `AccessTokenLifetime`,单位天,默认 30 天。
150
- - Mobile、H5、App、各类小程序及其它非 PC 终端读取 `AccessTokenLifetime`,单位天,默认 30 天。
151
- - `did` 通过请求头发送,同一安装或浏览器配置必须稳定持久化;不要每次请求生成新值。标准 SDK 使用 `V8.getDid()` 自动生成和复用。
152
- - Token 优先从响应头 `authorization` 读取,并立即覆盖本地旧 Token;兼容接口才从响应体读取。每个受保护请求都要接收响应头中的新 Token,因为后端可能在普通请求中自动轮换。
153
-
154
- ### 续签时机
155
-
156
- 不要把本地固定 15 分钟当作所有终端的有效期。读取 JWT 的 `exp` 与 `MicroiTokenIssuedAt`,在到期前按以下规则触发以旧换新:
157
-
158
- ```text
159
- 提前量 = lifetime / 10
160
- 最少提前 5 分钟
161
- 最多提前 1 天
162
- ```
163
-
164
- 因此默认 PC 20 分钟会在约第 15 分钟续签;默认移动端、VS Code 30 天会在到期前 1 天进入续签窗口。调用:
165
-
166
- ```js
167
- V8.startTokenMaintenance();
168
-
169
- // UniApp/App/小程序每次回到前台
170
- await V8.resumeAuthSession(false);
171
-
172
- // 主动以旧换新
173
- const result = await V8.refreshToken();
174
- ```
175
-
176
- - Web 同时监听 `visibilitychange`、`focus`、`pageshow`。浏览器可能休眠后台标签页并暂停 `setInterval`,恢复可见时必须立即检查,不能等下一个定时周期。
177
- - UniApp/App/小程序在 `App.onShow` 调用 `resumeAuthSession(false)`。
178
- - VS Code 在扩展激活后维护 Token,并在 `vscode.window.onDidChangeWindowState` 恢复焦点时立即检查。
179
- - 多请求、多 Tab 续签必须 single-flight。PC 后台可使用 Web Locks;收到响应时,如果本地 Token 已被其它 Tab 更新,旧请求不得把旧 Token 覆盖回来或清掉新登录态。
180
- - 调用 `/api/SysUser/RefreshToken` 时同时传旧 `authorization`、当前 `OsClient`、原终端 `_ClientType`,请求头继续传稳定 `did`。不要频繁无条件换新。
181
-
182
- ### 失效提示与租户边界
183
-
184
- 受保护接口返回 `Code=1001/1002`,或 RefreshToken 返回登录失效时,必须原样展示后端 `Msg`,禁止覆盖成固定“登录已过期”。后端会返回 `DataAppend` 诊断:
185
-
186
- | `ReasonCode` | 处理方式 |
187
- |---|---|
188
- | `JwtExpired` / `SessionExpired` | 展示已过期分钟、小时或天以及过期时间,然后清理当前终端会话并重新登录 |
189
- | `TenantMismatch` | 提示 Token 所属租户与当前请求租户,切换租户或重新登录;禁止把该 Token 用于当前租户 |
190
- | `TokenReplaced` | 先检查本地 Token 是否已被其它 Tab/并发请求更新;有新 Token 时重试一次,否则重新登录 |
191
- | `SessionMissing` | 服务端登录态已退出、被管理员清除或缓存已重建;清理本地 Token 并重新登录 |
192
- | `AuthVersionChanged` | 后端安全版本已升级,必须重新登录 |
193
- | `MalformedToken` / `MissingClaims` | Token 无法继续使用,清理并重新登录 |
194
-
195
- 不要显示完整 Token、用户密码或密钥。日志只记录 `ReasonCode`、终端类型、脱敏 `did`、请求租户和 Token 租户。`TokenOsClient` 只用于提示和诊断,真正鉴权仍以服务端签名、租户和 Redis 当前终端列表为准。
196
-
197
- ### Token 验收
198
-
199
- - PC、移动端、VS Code 分别登录,回读 JWT `ClientType`、`Did` 和有效期,确认命中对应 SaaS 配置。
200
- - 模拟页面隐藏超过 PC 有效期后恢复,确认先执行续签;若已无法续签,提示精确显示过期时长。
201
- - 使用 A 租户 Token 请求 B 租户,确认返回 `TenantMismatch`,提示同时包含 Token 租户与当前租户且不泄漏 Token。
202
- - 同一旧 Token 并发调用两次 RefreshToken,确认复用同一新 Token,后续请求成功。
203
- - 管理员调用 `ClearUserLoginInfo` 后,旧 Token 返回 `SessionMissing` 或等价明确原因,前端不再循环续签。
204
-
134
+ <!-- /microi-progressive:chunk -->
135
+ <!-- microi-progressive:chunk id=microi-frontend-sdk-004 sha256=c5f546fd4ef770459d42239b40af81d52399a3623340472b703cffe09a7b5d1e -->
205
136
  ## 上传规则
206
137
 
207
138
  `V8.uploadFile` 是 Microi 前端唯一允许的上传入口。SDK 实现必须:
@@ -218,6 +149,8 @@ const result = await V8.refreshToken();
218
149
 
219
150
  当上传突然报 `移动端文件上传路径不合法!` 时,先检查实际 multipart 表单字段和请求头。在 Microi 移动端/会员 Token 流程中,后端会在 HDFS 上传前校验 `Path`;错误的 `Content-Type` 会导致后端读不到表单字段,并表现为路径错误。
220
151
 
152
+ <!-- /microi-progressive:chunk -->
153
+ <!-- microi-progressive:chunk id=microi-frontend-sdk-005 sha256=1a9d0a33adbff849decf01d114e72cad96f80a6122f0b281cecf9092bbcd0c42 -->
221
154
  ## 项目封装规则
222
155
 
223
156
  面向业务页面的函数名要保持稳定。如果已有项目导出 `callEngine`、`formEngineGet`、`getImageUrl`、`parseImages` 或 `uploadFile`,保留这些导出,内部委托给 `V8`。这样既能统一 SDK,又能避免大面积改页面。
@@ -240,77 +173,10 @@ export function getImageUrl(value) {
240
173
  uni.request({ url: apiBase + '/apiengine/' + key, header: { Token: token } });
241
174
  ```
242
175
 
243
- ## 仅支持 Vue 3
244
-
245
- 新的 Microi 前端工作只支持 Vue 3。不要把 Vue2、Vuex、`Vue.prototype` 或 Vue2/uni-app 条件编译加入 `microi.v8.js`。状态管理属于项目本身,通常使用 Pinia 或本地组合函数;SDK 只负责平台访问、请求、鉴权、上传、资源 URL 和小工具。
246
-
247
- ## Key-Value 枚举的跨端约定(强制)
248
-
249
- - PC、UniApp、小程序和 Web 页面遇到简单枚举时,应从字段元数据或业务接口返回的公开 `{Key,Value}` 选项获取数据源;`Value` 只负责展示,`Key` 才能进入表单值、URL、缓存键和接口筛选参数。
250
- - 不得把中文 `Value` 当作查询条件,也不得在各端复制维护互相漂移的中文/英文映射。若业务接口已返回选项投影,优先直接消费;本地常量只能作为接口暂时不可用时的同 Key 兜底。
251
- - 页面 URL 需要保存筛选状态时写入稳定英文 Key,返回页面后按 Key 恢复选中项;切换语言只替换 Value,不得改变 URL 和数据库值。
252
- - 兼容历史数据时,客户端可以短期识别旧 Value,但提交和新 URL 必须立即归一为 Key;长期迁移由服务端完成并回读验证。
253
-
254
- ## 界面层独立
255
-
256
- SDK 不得导入 Element Plus、uni-ui、uView、TDesign、FirstUI、Pinia、Vue Router 或 axios。界面反馈通过可配置适配器提供:
257
-
258
- - `toast(message)`
259
- - `confirm(message)`
260
- - `onAuthExpired(body, V8)`
261
- - optional `requestAdapter(options)`
262
-
263
- 这样同一个 SDK 才能同时用于 uni-app、PC 网站、后台扩展页面和文档演示。
264
-
265
- ## 验证
266
-
267
- 将项目改为使用 SDK 后:
268
-
269
- - 运行相关构建或类型检查。
270
- - 至少测试一次需要登录的 ApiEngine 调用和一次匿名调用。
271
- - 用 `assetUrl` 测试一个图片或上传 JSON 字段。
272
- - 如果任务涉及鉴权,测试 Token 过期行为。
273
- - 对 uni-app H5,同时验证移动视口和 PC 浏览器手机壳下 SDK 正常工作。
274
-
275
- ### 复盘:生产构建被 `.env.local` 的 localhost 地址污染
276
-
277
- - 触发场景:本地开发通过 `.env.local` 指向 `localhost` API,发布后的官网仍请求开发者电脑的 loopback 地址,线上出现 `Failed to fetch`。
278
- - 根因:Vite 会在所有模式加载 `.env.local`;它不是仅开发模式文件。若生产模式没有更高优先级配置,loopback 地址会被编译进正式产物。
279
- - 通用规则:本地 API 只写入 `.env.development.local`;生产项目必须提供 `.env.production`。独立官网还要在统一 ApiBase 解析层拒绝“生产构建或非本地域名 + localhost/127.0.0.1/::1”,并安全回退到明确的正式 API。
280
- - 自动化检查:生产构建后扫描 JS 产物不得包含本地 ApiBase,并在正式域名上下文断言接口请求 origin 等于配置的生产 API;本地 `npm run dev` 仍应命中开发 API。
176
+ <!-- /microi-progressive:chunk -->
177
+ ## 详细参考路由(渐进披露)
281
178
 
282
- ## 搭配 MCI-UI
283
-
284
- SDK 负责平台能力,MCI-UI 负责产品界面。新的 Microi Vue3 项目应同时使用:
285
-
286
- - `microi.skills/microi.v8.js`:请求、Token、上传、文件 URL、ApiEngine/FormEngine。
287
- - `Microi.UI/src/theme`:`--mci-*` 设计变量。
288
- - `Microi.UI/src/uniapp`:移动端/UniApp 组件。
289
- - `Microi.UI/src/web`:PC 官网和响应式网站组件。
290
-
291
- 不要在 SDK 内解决界面状态、骨架屏、富文本间距或安全区布局。这一层应使用 MCI-UI 组件处理。
292
-
293
- ## MicroApp 宿主 Token 同步
294
-
295
- Vue3 前端微服务通过 `window.microApp.getData()` 接收主平台上下文时,不能只把 `token` 放进普通配置对象后假设请求会自动携带。标准 `microi.v8.js` 必须支持 `config.token`,且 `getToken()` 要优先读取运行时 token,再回退到 `storage[tokenKey]`。微服务必须复用同一个 V8 客户端实例,不能在每次按钮点击时重新 `createMicroiV8()`。
296
-
297
- `getData()` 中的 Token 是宿主传入的快照,只能用于首次引导或宿主确实下发了不同值时更新;不能在每次 `configureMicroiV8()` 时用旧快照覆盖 SDK 已从响应头取得的新 Token。推荐同时配置 `onTokenChanged`,把新 Token 与发起请求所用的旧 Token 回传宿主,宿主通过 `DiyCommon.ApplyAuthorizationToken(newToken, requestToken)` 接力并防止多标签页旧响应回写:
298
-
299
- ```js
300
- const microiV8 = V8; // 模块级单例
301
- let appliedHostToken = '';
302
-
303
- microiV8.configure({
304
- apiBase: ctx.apiBase,
305
- osClient: ctx.osClient,
306
- onTokenChanged: (token, requestToken) => {
307
- window.microApp?.dispatch?.({ type: 'micro-app:token', data: { token, requestToken } });
308
- }
309
- });
310
- if (ctx.token && ctx.token !== appliedHostToken) {
311
- appliedHostToken = ctx.token;
312
- microiV8.setToken(ctx.token);
313
- }
314
- ```
179
+ 仅在当前任务涉及对应主题时读取;下列文件合计保留了原 SKILL.md 的全部详细知识。
315
180
 
316
- 普通 `request`、浏览器 `fetch(FormData)` 上传和 `uni.uploadFile` 都必须读取响应头的新 Token。验收时必须连续执行至少两个需要登录态的请求(前一个允许发生 Token 轮换),确认后一个仍返回 `Code=1`;不能只看页面首屏渲染成功。
181
+ - [references/progressive-01-token-当前登录用户与当前终端登录协议.md](references/progressive-01-token-当前登录用户与当前终端登录协议.md):Token、当前登录用户与当前终端登录协议;仅支持 Vue 3;Key-Value 枚举的跨端约定(强制);界面层独立;验证;搭配 MCI-UI;MicroApp 宿主 Token 同步
182
+ <!-- microi-progressive:end -->
@@ -0,0 +1,171 @@
1
+ # microi-frontend-sdk 详细参考 1
2
+
3
+ > 按需读取;本文件由 SKILL.md 的原章节无损拆分。
4
+
5
+ <!-- microi-progressive:chunk id=microi-frontend-sdk-006 sha256=a834ee9855361ec5ed3f466de8d8fef53de89e40a6882b563694471d060a0dfe -->
6
+ ## Token、当前登录用户与当前终端登录协议
7
+
8
+ Microi 后端不是只保存一个全局 Token。每个租户、每个 `sys_user` 在 Redis 中维护一份 `CurrentToken`,其中 `CurrentUser` 表示平台当前登录用户,`Tokens` 表示该用户的多个当前终端登录。每个终端项至少包含 `Token`、`ClientType`、`Did`、`IP`、`CreateTime`、`UpdateTime`;退出、管理员清除登录信息、同终端重新登录或 Token 轮换都会影响该列表。
9
+
10
+ 登录必须同时标记终端类型和稳定设备 Id:
11
+
12
+ ```js
13
+ const V8 = createMicroiV8({
14
+ apiBase,
15
+ osClient,
16
+ clientType: 'Mobile', // PC / Mobile / H5 / App / WxMiniProgram / VSCode / MCP
17
+ didKey: 'microi_did'
18
+ });
19
+
20
+ const result = await V8.Login({
21
+ Account,
22
+ Pwd,
23
+ _ClientType: 'Mobile'
24
+ });
25
+ ```
26
+
27
+ - PC 后台传 `_ClientType:'PC'`,有效期读取 SaaS 引擎 `SessionAuthTimeout`,单位分钟,默认 20 分钟。
28
+ - VS Code 传 `_ClientType:'VSCode'`,优先读取 `VSCodeAccessTokenLifetime`,否则读取 `AccessTokenLifetime`,单位天,默认 30 天。
29
+ - MCP 传 `_ClientType:'MCP'`,优先读取 `McpAccessTokenLifetime`,否则读取 `AccessTokenLifetime`,单位天,默认 30 天。
30
+ - Mobile、H5、App、各类小程序及其它非 PC 终端读取 `AccessTokenLifetime`,单位天,默认 30 天。
31
+ - `did` 通过请求头发送,同一安装或浏览器配置必须稳定持久化;不要每次请求生成新值。标准 SDK 使用 `V8.getDid()` 自动生成和复用。
32
+ - Token 优先从响应头 `authorization` 读取,并立即覆盖本地旧 Token;兼容接口才从响应体读取。每个受保护请求都要接收响应头中的新 Token,因为后端可能在普通请求中自动轮换。
33
+
34
+ ### 续签时机
35
+
36
+ 不要把本地固定 15 分钟当作所有终端的有效期。读取 JWT 的 `exp` 与 `MicroiTokenIssuedAt`,在到期前按以下规则触发以旧换新:
37
+
38
+ ```text
39
+ 提前量 = lifetime / 10
40
+ 最少提前 5 分钟
41
+ 最多提前 1 天
42
+ ```
43
+
44
+ 因此默认 PC 20 分钟会在约第 15 分钟续签;默认移动端、VS Code 30 天会在到期前 1 天进入续签窗口。调用:
45
+
46
+ ```js
47
+ V8.startTokenMaintenance();
48
+
49
+ // UniApp/App/小程序每次回到前台
50
+ await V8.resumeAuthSession(false);
51
+
52
+ // 主动以旧换新
53
+ const result = await V8.refreshToken();
54
+ ```
55
+
56
+ - Web 同时监听 `visibilitychange`、`focus`、`pageshow`。浏览器可能休眠后台标签页并暂停 `setInterval`,恢复可见时必须立即检查,不能等下一个定时周期。
57
+ - UniApp/App/小程序在 `App.onShow` 调用 `resumeAuthSession(false)`。
58
+ - VS Code 在扩展激活后维护 Token,并在 `vscode.window.onDidChangeWindowState` 恢复焦点时立即检查。
59
+ - 多请求、多 Tab 续签必须 single-flight。PC 后台可使用 Web Locks;收到响应时,如果本地 Token 已被其它 Tab 更新,旧请求不得把旧 Token 覆盖回来或清掉新登录态。
60
+ - 调用 `/api/SysUser/RefreshToken` 时同时传旧 `authorization`、当前 `OsClient`、原终端 `_ClientType`,请求头继续传稳定 `did`。不要频繁无条件换新。
61
+
62
+ ### 失效提示与租户边界
63
+
64
+ 受保护接口返回 `Code=1001/1002`,或 RefreshToken 返回登录失效时,必须原样展示后端 `Msg`,禁止覆盖成固定“登录已过期”。后端会返回 `DataAppend` 诊断:
65
+
66
+ | `ReasonCode` | 处理方式 |
67
+ |---|---|
68
+ | `JwtExpired` / `SessionExpired` | 展示已过期分钟、小时或天以及过期时间,然后清理当前终端会话并重新登录 |
69
+ | `TenantMismatch` | 提示 Token 所属租户与当前请求租户,切换租户或重新登录;禁止把该 Token 用于当前租户 |
70
+ | `TokenReplaced` | 先检查本地 Token 是否已被其它 Tab/并发请求更新;有新 Token 时重试一次,否则重新登录 |
71
+ | `SessionMissing` | 服务端登录态已退出、被管理员清除或缓存已重建;清理本地 Token 并重新登录 |
72
+ | `AuthVersionChanged` | 后端安全版本已升级,必须重新登录 |
73
+ | `MalformedToken` / `MissingClaims` | Token 无法继续使用,清理并重新登录 |
74
+
75
+ 不要显示完整 Token、用户密码或密钥。日志只记录 `ReasonCode`、终端类型、脱敏 `did`、请求租户和 Token 租户。`TokenOsClient` 只用于提示和诊断,真正鉴权仍以服务端签名、租户和 Redis 当前终端列表为准。
76
+
77
+ ### Token 验收
78
+
79
+ - PC、移动端、VS Code 分别登录,回读 JWT `ClientType`、`Did` 和有效期,确认命中对应 SaaS 配置。
80
+ - 模拟页面隐藏超过 PC 有效期后恢复,确认先执行续签;若已无法续签,提示精确显示过期时长。
81
+ - 使用 A 租户 Token 请求 B 租户,确认返回 `TenantMismatch`,提示同时包含 Token 租户与当前租户且不泄漏 Token。
82
+ - 同一旧 Token 并发调用两次 RefreshToken,确认复用同一新 Token,后续请求成功。
83
+ - 管理员调用 `ClearUserLoginInfo` 后,旧 Token 返回 `SessionMissing` 或等价明确原因,前端不再循环续签。
84
+
85
+ <!-- /microi-progressive:chunk -->
86
+ <!-- microi-progressive:chunk id=microi-frontend-sdk-007 sha256=32ff522b3d30d94eb6cc1747b1f1238e35c1c3d43091f3ce308067e320ecb2a4 -->
87
+ ## 仅支持 Vue 3
88
+
89
+ 新的 Microi 前端工作只支持 Vue 3。不要把 Vue2、Vuex、`Vue.prototype` 或 Vue2/uni-app 条件编译加入 `microi.v8.js`。状态管理属于项目本身,通常使用 Pinia 或本地组合函数;SDK 只负责平台访问、请求、鉴权、上传、资源 URL 和小工具。
90
+
91
+ <!-- /microi-progressive:chunk -->
92
+ <!-- microi-progressive:chunk id=microi-frontend-sdk-008 sha256=0c59d00b2e54dd61d194d6246dd787a456ab8f12d4abe6dc330b002f41d7ee26 -->
93
+ ## Key-Value 枚举的跨端约定(强制)
94
+
95
+ - PC、UniApp、小程序和 Web 页面遇到简单枚举时,应从字段元数据或业务接口返回的公开 `{Key,Value}` 选项获取数据源;`Value` 只负责展示,`Key` 才能进入表单值、URL、缓存键和接口筛选参数。
96
+ - 不得把中文 `Value` 当作查询条件,也不得在各端复制维护互相漂移的中文/英文映射。若业务接口已返回选项投影,优先直接消费;本地常量只能作为接口暂时不可用时的同 Key 兜底。
97
+ - 页面 URL 需要保存筛选状态时写入稳定英文 Key,返回页面后按 Key 恢复选中项;切换语言只替换 Value,不得改变 URL 和数据库值。
98
+ - 兼容历史数据时,客户端可以短期识别旧 Value,但提交和新 URL 必须立即归一为 Key;长期迁移由服务端完成并回读验证。
99
+
100
+ <!-- /microi-progressive:chunk -->
101
+ <!-- microi-progressive:chunk id=microi-frontend-sdk-009 sha256=2e428f404342ad21af261e5f5876e34e2b9fc9455791bf0ad73ff4ad5b3e2ed0 -->
102
+ ## 界面层独立
103
+
104
+ SDK 不得导入 Element Plus、uni-ui、uView、TDesign、FirstUI、Pinia、Vue Router 或 axios。界面反馈通过可配置适配器提供:
105
+
106
+ - `toast(message)`
107
+ - `confirm(message)`
108
+ - `onAuthExpired(body, V8)`
109
+ - optional `requestAdapter(options)`
110
+
111
+ 这样同一个 SDK 才能同时用于 uni-app、PC 网站、后台扩展页面和文档演示。
112
+
113
+ <!-- /microi-progressive:chunk -->
114
+ <!-- microi-progressive:chunk id=microi-frontend-sdk-010 sha256=ee237c45899f7204d3da1a2d589a89268c2db2a9b765f68fc7097eadf8da7104 -->
115
+ ## 验证
116
+
117
+ 将项目改为使用 SDK 后:
118
+
119
+ - 运行相关构建或类型检查。
120
+ - 至少测试一次需要登录的 ApiEngine 调用和一次匿名调用。
121
+ - 用 `assetUrl` 测试一个图片或上传 JSON 字段。
122
+ - 如果任务涉及鉴权,测试 Token 过期行为。
123
+ - 对 uni-app H5,同时验证移动视口和 PC 浏览器手机壳下 SDK 正常工作。
124
+
125
+ ### 复盘:生产构建被 `.env.local` 的 localhost 地址污染
126
+
127
+ - 触发场景:本地开发通过 `.env.local` 指向 `localhost` API,发布后的官网仍请求开发者电脑的 loopback 地址,线上出现 `Failed to fetch`。
128
+ - 根因:Vite 会在所有模式加载 `.env.local`;它不是仅开发模式文件。若生产模式没有更高优先级配置,loopback 地址会被编译进正式产物。
129
+ - 通用规则:本地 API 只写入 `.env.development.local`;生产项目必须提供 `.env.production`。独立官网还要在统一 ApiBase 解析层拒绝“生产构建或非本地域名 + localhost/127.0.0.1/::1”,并安全回退到明确的正式 API。
130
+ - 自动化检查:生产构建后扫描 JS 产物不得包含本地 ApiBase,并在正式域名上下文断言接口请求 origin 等于配置的生产 API;本地 `npm run dev` 仍应命中开发 API。
131
+
132
+ <!-- /microi-progressive:chunk -->
133
+ <!-- microi-progressive:chunk id=microi-frontend-sdk-011 sha256=81ac75e383ea5a32ae45aeff1276fc2b8f56141961e70cc3956ae818aaf2fa1d -->
134
+ ## 搭配 MCI-UI
135
+
136
+ SDK 负责平台能力,MCI-UI 负责产品界面。新的 Microi Vue3 项目应同时使用:
137
+
138
+ - `microi.skills/microi.v8.js`:请求、Token、上传、文件 URL、ApiEngine/FormEngine。
139
+ - `Microi.UI/src/theme`:`--mci-*` 设计变量。
140
+ - `Microi.UI/src/uniapp`:移动端/UniApp 组件。
141
+ - `Microi.UI/src/web`:PC 官网和响应式网站组件。
142
+
143
+ 不要在 SDK 内解决界面状态、骨架屏、富文本间距或安全区布局。这一层应使用 MCI-UI 组件处理。
144
+
145
+ <!-- /microi-progressive:chunk -->
146
+ <!-- microi-progressive:chunk id=microi-frontend-sdk-012 sha256=d869adb0abd87d9ba03b58faa84944a61895773a6c3642f5c20c8a03e315a2ce -->
147
+ ## MicroApp 宿主 Token 同步
148
+
149
+ Vue3 前端微服务通过 `window.microApp.getData()` 接收主平台上下文时,不能只把 `token` 放进普通配置对象后假设请求会自动携带。标准 `microi.v8.js` 必须支持 `config.token`,且 `getToken()` 要优先读取运行时 token,再回退到 `storage[tokenKey]`。微服务必须复用同一个 V8 客户端实例,不能在每次按钮点击时重新 `createMicroiV8()`。
150
+
151
+ `getData()` 中的 Token 是宿主传入的快照,只能用于首次引导或宿主确实下发了不同值时更新;不能在每次 `configureMicroiV8()` 时用旧快照覆盖 SDK 已从响应头取得的新 Token。推荐同时配置 `onTokenChanged`,把新 Token 与发起请求所用的旧 Token 回传宿主,宿主通过 `DiyCommon.ApplyAuthorizationToken(newToken, requestToken)` 接力并防止多标签页旧响应回写:
152
+
153
+ ```js
154
+ const microiV8 = V8; // 模块级单例
155
+ let appliedHostToken = '';
156
+
157
+ microiV8.configure({
158
+ apiBase: ctx.apiBase,
159
+ osClient: ctx.osClient,
160
+ onTokenChanged: (token, requestToken) => {
161
+ window.microApp?.dispatch?.({ type: 'micro-app:token', data: { token, requestToken } });
162
+ }
163
+ });
164
+ if (ctx.token && ctx.token !== appliedHostToken) {
165
+ appliedHostToken = ctx.token;
166
+ microiV8.setToken(ctx.token);
167
+ }
168
+ ```
169
+
170
+ 普通 `request`、浏览器 `fetch(FormData)` 上传和 `uni.uploadFile` 都必须读取响应头的新 Token。验收时必须连续执行至少两个需要登录态的请求(前一个允许发生 Token 轮换),确认后一个仍返回 `Code=1`;不能只看页面首屏渲染成功。
171
+ <!-- /microi-progressive:chunk -->
@@ -3,7 +3,7 @@ name: microi-left-right-layout
3
3
  description: Microi 吾码模块引擎“树形+表格/表单”左右结构配置规范。用于通过 MCP、模块引擎或源码配置 `diy_LeftJoinRightView`,把项目、分类、组织等主数据作为左树,并用主外键过滤右侧列表;覆盖字段语义、初始化 V8、移动端自适应、幂等写入和回读验收。
4
4
  ---
5
5
 
6
- > **Codex 强制前置:** 当前宿主为 Codex 时,在使用本 Skill 前必须先完整读取 `../microi-codex-installer/SKILL.md`,完成“Codex 每任务最新版硬门禁”;门禁未通过不得继续本 Skill。非 Codex 宿主跳过此项。
6
+ > **Codex 非阻塞自动更新:** 当前宿主为 Codex 时,吾码 CLI、Codex 插件与工作区 AI/MCP 由后台自动更新;需要诊断时读取 `../microi-codex-installer/SKILL.md`。更新失败、等待空闲或尚未重载均不得阻断当前、正在进行或新建任务。非 Codex 宿主跳过此项。
7
7
 
8
8
  # Microi 左右树表配置规范
9
9