@microi.net/cli 6.2.2 → 6.2.4

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 (57) 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/assets/build-meta.json +5 -5
  7. package/cordis.patch.yml +1 -1
  8. package/package.json +1 -1
  9. package/scripts/mcp-server.js +117 -116
  10. package/scripts/microi-cli.js +63 -63
  11. package/scripts/microi-skills.meta.json +268 -253
  12. package/skills/.microi-skills-version.json +2 -2
  13. package/skills/.progressive-disclosure-manifest.json +83 -83
  14. package/skills/app-store/SKILL.md +7 -6
  15. package/skills/microi-ai-application/SKILL.md +5 -5
  16. package/skills/microi-client-frontend/SKILL.md +11 -7
  17. package/skills/microi-codex-installer/SKILL.md +6 -0
  18. package/skills/microi-datasource-mapping/SKILL.md +1 -0
  19. package/skills/microi-db-schema/SKILL.md +8 -0
  20. package/skills/microi-docs-coverage/references/capability-map.md +10 -10
  21. package/skills/microi-form-engine/SKILL.md +52 -0
  22. package/skills/microi-form-engine/references/component-config-index.md +1 -1
  23. package/skills/microi-form-engine/references/data-source-events.md +6 -1
  24. package/skills/microi-form-layout/SKILL.md +1 -1
  25. 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 +6 -0
  26. package/skills/microi-left-right-layout/SKILL.md +22 -0
  27. package/skills/microi-microservice/SKILL.md +388 -382
  28. package/skills/microi-microservice/references/runtime-delivery.md +15 -0
  29. package/skills/microi-ui/SKILL.md +192 -192
  30. 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
  31. package/skills/module-engine/SKILL.md +7 -1
  32. package/skills/page-engine/SKILL.md +7 -1
  33. package/skills/page-engine/examples/go-view-operations.json +8467 -0
  34. package/skills/page-engine/examples/go-view-procurement.json +2800 -0
  35. package/skills/page-engine/references/go-view-json.md +62 -0
  36. package/skills/ui-design/SKILL.md +227 -223
  37. package/skills/ui-design/assets/pattern-showcase/index.html +2 -2
  38. package/skills/ui-design/assets/templates/MCI-DESIGN.md +221 -206
  39. package/skills/ui-design/references/data-workspace-density.md +77 -0
  40. package/skills/ui-design/references/design-pattern-library.md +186 -184
  41. package/skills/ui-design/references/mci-design-contract.md +163 -163
  42. 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 +2 -1
  43. package/skills/ui-design/references/progressive-02-/345/255/227/344/275/223.md +165 -164
  44. 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 +237 -235
  45. 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 +154 -152
  46. 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
  47. package/skills/uniapp-mall-assets/SKILL.md +7 -2
  48. package/skills/v8-crud-api/SKILL.md +2 -1
  49. package/skills/v8-explorer-tree/SKILL.md +22 -1
  50. package/skills/v8-menu-buttons/SKILL.md +2 -0
  51. package/skills/v8-saas-multi-tenant/SKILL.md +6 -0
  52. package/skills/v8-security/SKILL.md +11 -1
  53. package/skills/v8-sql-query/SKILL.md +38 -1
  54. package/skills/v8-sql-query/examples/business-query.js +18 -0
  55. package/skills/v8-table-event/SKILL.md +51 -2
  56. 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 +3 -1
  57. package/skills/vision-engine/references/video-timeline.md +52 -0
@@ -1,207 +1,207 @@
1
- # ui-design 详细参考 7
2
-
3
- > 按需读取;本文件由 SKILL.md 的原章节无损拆分。
4
-
5
- <!-- microi-progressive:chunk id=ui-design-020 sha256=2efd631603aff04a45f251f0fc06ac83b39b2aebc1551b311920aac4ac6b9273 -->
6
- ## 速查:从头搭建一个移动端页面
7
-
8
- ```vue
9
- <template>
10
- <div class="mci-mobile-page">
11
- <!-- 顶部导航 -->
12
- <header class="mci-navbar">
13
- <h1 class="mci-navbar__title">{{ title }}</h1>
14
- </header>
15
-
16
- <!-- 主内容 -->
17
- <main class="page-content">
18
- <section
19
- v-for="(item, i) in list"
20
- :key="item.id"
21
- class="mci-card mci-stagger-item"
22
- :style="{ '--mci-index': i }"
23
- >
24
- <span class="mci-tag mci-tag--hot">HOT</span>
25
- <h3>{{ item.name }}</h3>
26
- <p class="mci-text-gradient price">{{ item.price }}</p>
27
- <button class="mci-btn">立即查看</button>
28
- </section>
29
- </main>
30
-
31
- <!-- 底部 Tabbar -->
32
- <nav class="mci-tabbar">
33
- <a class="mci-tabbar__item mci-tabbar__item--active"><svg class="mci-tabbar__icon" aria-hidden="true"><use href="#icon-home" /></svg><span>首页</span></a>
34
- <a class="mci-tabbar__item"><svg class="mci-tabbar__icon" aria-hidden="true"><use href="#icon-message" /></svg><span>消息</span></a>
35
- <a class="mci-tabbar__item"><svg class="mci-tabbar__icon" aria-hidden="true"><use href="#icon-user" /></svg><span>我的</span></a>
36
- </nav>
37
- </div>
38
- </template>
39
-
40
- <style lang="scss" scoped>
41
- .page-content {
42
- padding: var(--mci-space-4);
43
- padding-bottom: calc(var(--mci-touch-target) + var(--mci-safe-bottom) + var(--mci-space-8));
44
- display: flex;
45
- flex-direction: column;
46
- gap: var(--mci-space-4);
47
- }
48
- .price {
49
- font-size: var(--mci-text-2xl);
50
- font-weight: var(--mci-font-bold);
51
- }
52
- </style>
53
- ```
54
-
55
-
56
- ---
57
-
58
- <!-- /microi-progressive:chunk -->
59
- <!-- microi-progressive:chunk id=ui-design-021 sha256=210d5091c308f562b56bce93b257a74ed1894cf0f15393f703149c05c3ac2584 -->
60
- ## 🚨 移动端低代码项目落地踩坑(必读,2026.5)
61
-
62
- 实战中频繁出现的 7 类问题,统一按以下规则处理。
63
-
64
- ### 1. 路由前缀不要硬编码租户名
65
-
66
- - 错误:在 `manifest.json` 写死 `"router": { "base": "/fixed-tenant/" }`。
67
- - 正确:默认使用 `"router": { "base": "/" }`;租户由运行配置、`OsClient` 和请求头/参数确定。
68
- - 不要自行把租户名拼进 API 路径。平台接口使用 `/api/...`、`/apiengine/...` 等标准路由。
69
-
70
- ### 2. tabBar 使用本地静态 PNG 图标
71
-
72
- - uni-app/微信小程序 tabBar 的 `iconPath`、`selectedIconPath` 使用项目内静态 PNG。
73
- - 不使用 emoji、字体图标、远程 URL;SVG 仅在目标端明确支持并已做真实设备验证时使用。
74
- - 普通态与选中态保持相同画布和轮廓,推荐 60×60 至 81×81 px,并验证深浅色背景可读性。
75
-
76
- ### 3. 字号样式不要通配所有 `text`
77
-
78
- Scoped SCSS 中的 `.entry text { font-size: 40rpx; }` 会同时放大图标文字和子标签。图标、标题、说明必须使用独立 class:
79
-
80
- ```scss
81
- .entry .entry-icon { font-size: 40rpx; }
82
- .entry .entry-label { font-size: 22rpx; }
83
- ```
84
-
85
- ### 4. 个人中心/详情入口按信息层级选布局
86
-
87
- - 高频资产、订单状态、服务入口适合 4~5 列网格。
88
- - 设置、安全、协议等低频入口适合纵向列表。
89
- - 单元格须有稳定触控区域、清晰标签和一致间距;不要为了追求密度牺牲可读性。
90
-
91
- ### 5. 每个可点击元素都要有反馈
92
-
93
- ```scss
94
- .cell, .entry-item, .product-card, .zone-card {
95
- position: relative;
96
- transition: transform .2s ease, box-shadow .2s ease;
97
- }
98
- .cell:active, .entry-item:active {
99
- transform: scale(.94);
100
- }
101
- @keyframes fadein-up {
102
- from { opacity: 0; transform: translateY(16rpx); }
103
- to { opacity: 1; transform: translateY(0); }
104
- }
105
- .animate-fadein { animation: fadein-up .45s ease both; }
106
- @media (prefers-reduced-motion: reduce) {
107
- .animate-fadein { animation: none; }
108
- }
109
- ```
110
-
111
- 动效只表达状态,不承担业务完成事实;提交、支付、安装等动作仍以服务端返回和可恢复状态为准。
112
-
113
- ### 6. 品牌名和 Logo 的用户可见位置保持一致
114
-
115
- 同步检查 `manifest.json` 的 `name/h5.title`、`pages.json` 的导航标题、登录/注册/首页品牌区、空状态和分享标题。控制台中的技术标识可保留,但用户可见文案必须统一。
116
-
117
- ### 7. 接口地址由标准引擎工具维护
118
-
119
- - `microi_create_engine`/`microi_upsert_engine` 会维护 `ApiEngineKey`、`ApiAddress` 和路由缓存。
120
- - 写入超时先用标准 get/list 工具回读,不得立即重复创建。
121
- - 禁止通过手工 SQL、直接修改 `sys_apiengine` 或自行拼 Redis Key 修补路由;这会绕过租户、审计、缓存失效和控制面权限。
122
- - 长任务使用后台任务并持久化进度;普通接口写入后以远端回读为最终依据。
123
-
124
- <!-- /microi-progressive:chunk -->
125
- <!-- microi-progressive:chunk id=ui-design-022 sha256=8f8a9a69e2816682ac2b6a26d932c23240c477191d4f3029a821dafe23250c7b -->
126
- ## 🔗 关联字段:保存真实 Id,界面展示可读标签
127
-
128
- 关联字段的数据库事实值通常是 `XxxId`。表单使用 `JoinForm`、`OpenTable` 或带数据源的 `Select` 显示名称,`SelectSaveField` 保存 `Id`,`SelectLabel` 展示名称。不要因为列表默认显示 Id,就强制所有业务表冗余一对可编辑的 `XxxId/XxxName` 字段。
129
-
130
- ### Select 数据源示例
131
-
132
- ```json
133
- {
134
- "DataSource": "Sql",
135
- "Sql": "select Id, Name from mall_category where Name like '%$Keyword$%' limit 0,20",
136
- "SelectLabel": "Name",
137
- "SelectSaveField": "Id",
138
- "SelectSaveFormat": "Text",
139
- "EnableSearch": true,
140
- "DataSourceSqlRemote": true
141
- }
142
- ```
143
-
144
- - `$Keyword$` 是平台数据源占位符,不要把浏览器输入直接拼进任意 SQL。
145
- - 关联表、字段和数据源必须来自当前租户,并受当前表单/菜单的授权与数据范围约束。
146
- - 大表使用远程搜索和分页,不一次性把全表拉到浏览器。
147
- - 列表展示名称优先配置 Join/模块关联字段或受控数据源,不把隐藏 Id 暴露给用户。
148
-
149
- ### 什么时候增加 `XxxName` 快照字段
150
-
151
- 只有在业务明确要求“保存当时名称”、离线展示、历史审计或高频报表需要时才增加 `XxxName`。它是快照/冗余字段,不是外键事实源,并遵守:
152
-
153
- 1. 后端 SubmitBefore/接口引擎在同一事务内根据 `XxxId` 校验并写入名称。
154
- 2. 前端 V8 可以即时回显,但不能作为唯一一致性保障。
155
- 3. 关联名称后续变化时,先明确历史快照是否应随之更新。
156
- 4. 批量回填必须参数化、可恢复、可幂等,并经过明确写入确认。
157
-
158
- ### MCP 建模流程
159
-
160
- 1. 先用 `microi_get_db_schema` 获取真实表和字段。
161
- 2. 使用 `microi_build_field_config` 生成 JoinForm/OpenTable/Select 配置。
162
- 3. 使用 `microi_add_field` 或 `microi_update_field` 写入字段。
163
- 4. 修改 KeyValue/Config 后回读 `microi_get_field_list`,并执行 `microi_refresh_schema_cache`。
164
- 5. 在真实菜单下验证普通角色能看到标签,但不能借数据源越权读取其它表或行。
165
- ---
166
-
167
- <!-- /microi-progressive:chunk -->
168
- <!-- microi-progressive:chunk id=ui-design-023 sha256=9767f1e46e8491d63a9f9a00fadef9125d124841a79141f07dd7e4529da3bc25 -->
169
- ## 表单布局规范(Column)
170
-
171
- > 平台默认设计标准:所有 `diy_table` **应使用双列布局** (`Column = 2`),更紧凑现代,符合主流后台 SaaS 视觉密度。
172
-
173
- ### 创建表时
174
-
175
- ```jsonc
176
- microi_create_table {
177
- "name": "Crm_Customer",
178
- "description": "客户",
179
- "column": 2 // ✅ 默认就是 2,无需显式传,但推荐写明
180
- }
181
- ```
182
-
183
- ### 修复存量表(一次性把所有 `Column=null` 改成 2)
184
-
185
- ```jsonc
186
- microi_update_table {
187
- "name": "Crm_Customer",
188
- "column": 2
189
- }
190
- ```
191
-
192
- ### 何时使用 Column=1(单列)
193
-
194
- - 工作流审批表单(字段少且需要专注)
195
- - 移动端优先表单(手机宽度不够双列)
196
- - 含大量富文本/长文本字段的内容编辑表
197
-
198
- ### 何时使用 Column=3(三列)
199
-
200
- - 字段≥18 的"基础档案"类大表(员工、商品 SKU、设备清单)
201
- - 桌面分辨率≥1920px 的内部管理后台
202
-
203
- > 修改 `Column` 后会自动清缓存(`microi_update_table` 后端走 `UptFormData('diy_table')` + 主动 `RefreshSchemaCache`),前端硬刷新(Ctrl+Shift+R)即可看到效果。
204
-
205
- ---
206
-
207
- <!-- /microi-progressive:chunk -->
1
+ # ui-design 详细参考 7
2
+
3
+ > 按需读取;本文件由 SKILL.md 的原章节无损拆分。
4
+
5
+ <!-- microi-progressive:chunk id=ui-design-020 sha256=8af48a2af341cf338cbb3937def060827f39f40795330c2cca7c342897f48ce7 -->
6
+ ## 速查:从头搭建一个移动端页面
7
+
8
+ ```vue
9
+ <template>
10
+ <div class="mci-mobile-page">
11
+ <!-- 顶部导航 -->
12
+ <header class="mci-navbar">
13
+ <h1 class="mci-navbar__title">{{ title }}</h1>
14
+ </header>
15
+
16
+ <!-- 主内容 -->
17
+ <main class="page-content">
18
+ <section
19
+ v-for="(item, i) in list"
20
+ :key="item.id"
21
+ class="mci-card"
22
+ :style="{ '--mci-index': i }"
23
+ >
24
+ <span class="mci-tag mci-tag--hot">HOT</span>
25
+ <h3>{{ item.name }}</h3>
26
+ <p class="price">{{ item.price }}</p>
27
+ <button class="mci-btn">立即查看</button>
28
+ </section>
29
+ </main>
30
+
31
+ <!-- 底部 Tabbar -->
32
+ <nav class="mci-tabbar">
33
+ <a class="mci-tabbar__item mci-tabbar__item--active"><svg class="mci-tabbar__icon" aria-hidden="true"><use href="#icon-home" /></svg><span>首页</span></a>
34
+ <a class="mci-tabbar__item"><svg class="mci-tabbar__icon" aria-hidden="true"><use href="#icon-message" /></svg><span>消息</span></a>
35
+ <a class="mci-tabbar__item"><svg class="mci-tabbar__icon" aria-hidden="true"><use href="#icon-user" /></svg><span>我的</span></a>
36
+ </nav>
37
+ </div>
38
+ </template>
39
+
40
+ <style lang="scss" scoped>
41
+ .page-content {
42
+ padding: var(--mci-space-4);
43
+ padding-bottom: calc(var(--mci-touch-target) + var(--mci-safe-bottom) + var(--mci-space-3));
44
+ display: flex;
45
+ flex-direction: column;
46
+ gap: var(--mci-space-3);
47
+ }
48
+ .price {
49
+ font-size: var(--mci-text-2xl);
50
+ font-weight: var(--mci-font-bold);
51
+ }
52
+ </style>
53
+ ```
54
+
55
+
56
+ ---
57
+
58
+ <!-- /microi-progressive:chunk -->
59
+ <!-- microi-progressive:chunk id=ui-design-021 sha256=8a8884b0b064e7805cd03d904541b12da4539e73172d31f00d32c1ecfaee32f6 -->
60
+ ## 🚨 移动端低代码项目落地踩坑(必读,2026.5)
61
+
62
+ 实战中频繁出现的 7 类问题,统一按以下规则处理。
63
+
64
+ ### 1. 路由前缀不要硬编码租户名
65
+
66
+ - 错误:在 `manifest.json` 写死 `"router": { "base": "/fixed-tenant/" }`。
67
+ - 正确:默认使用 `"router": { "base": "/" }`;租户由运行配置、`OsClient` 和请求头/参数确定。
68
+ - 不要自行把租户名拼进 API 路径。平台接口使用 `/api/...`、`/apiengine/...` 等标准路由。
69
+
70
+ ### 2. tabBar 使用本地静态 PNG 图标
71
+
72
+ - uni-app/微信小程序 tabBar 的 `iconPath`、`selectedIconPath` 使用项目内静态 PNG。
73
+ - 不使用 emoji、字体图标、远程 URL;SVG 仅在目标端明确支持并已做真实设备验证时使用。
74
+ - 普通态与选中态保持相同画布和轮廓,推荐 60×60 至 81×81 px,并验证深浅色背景可读性。
75
+
76
+ ### 3. 字号样式不要通配所有 `text`
77
+
78
+ Scoped SCSS 中的 `.entry text { font-size: 40rpx; }` 会同时放大图标文字和子标签。图标、标题、说明必须使用独立 class:
79
+
80
+ ```scss
81
+ .entry .entry-icon { font-size: 40rpx; }
82
+ .entry .entry-label { font-size: 22rpx; }
83
+ ```
84
+
85
+ ### 4. 个人中心/详情入口按信息层级选布局
86
+
87
+ - 高频资产、订单状态、服务入口适合 4~5 列网格。
88
+ - 设置、安全、协议等低频入口适合纵向列表。
89
+ - 单元格须有稳定触控区域、清晰标签和一致间距;不要为了追求密度牺牲可读性。
90
+
91
+ ### 5. 每个可点击元素都要有反馈
92
+
93
+ ```scss
94
+ .cell, .entry-item, .product-card, .zone-card {
95
+ position: relative;
96
+ transition: transform .2s ease, box-shadow .2s ease;
97
+ }
98
+ .cell:active, .entry-item:active {
99
+ transform: scale(.94);
100
+ }
101
+ @keyframes fadein-up {
102
+ from { opacity: 0; transform: translateY(16rpx); }
103
+ to { opacity: 1; transform: translateY(0); }
104
+ }
105
+ .animate-fadein { animation: fadein-up .45s ease both; }
106
+ @media (prefers-reduced-motion: reduce) {
107
+ .animate-fadein { animation: none; }
108
+ }
109
+ ```
110
+
111
+ 动效只表达状态,不承担业务完成事实;提交、支付、安装等动作仍以服务端返回和可恢复状态为准。
112
+
113
+ ### 6. 品牌名和 Logo 的用户可见位置保持一致
114
+
115
+ 同步检查 `manifest.json` 的 `name/h5.title`、`pages.json` 的导航标题、登录/注册/首页品牌区、空状态和分享标题。控制台中的技术标识可保留,但用户可见文案必须统一。
116
+
117
+ ### 7. 接口地址由标准引擎工具维护
118
+
119
+ - `microi_create_engine`/`microi_upsert_engine` 会维护 `ApiEngineKey`、`ApiAddress` 和路由缓存。
120
+ - 写入超时先用标准 get/list 工具回读,不得立即重复创建。
121
+ - 禁止通过手工 SQL、直接修改 `sys_apiengine` 或自行拼 Redis Key 修补路由;这会绕过租户、审计、缓存失效和控制面权限。
122
+ - 长任务使用后台任务并持久化进度;普通接口写入后以远端回读为最终依据。
123
+
124
+ <!-- /microi-progressive:chunk -->
125
+ <!-- microi-progressive:chunk id=ui-design-022 sha256=4962a1ec7bc49ae1a2706c58fb6f929304ca254e3fd4d8b626fefbc17067bce2 -->
126
+ ## 🔗 关联字段:保存真实 Id,界面展示可读标签
127
+
128
+ 关联字段的数据库事实值通常是 `XxxId`。表单使用 `JoinForm`、`OpenTable` 或带数据源的 `Select` 显示名称,`SelectSaveField` 保存 `Id`,`SelectLabel` 展示名称。不要因为列表默认显示 Id,就强制所有业务表冗余一对可编辑的 `XxxId/XxxName` 字段。
129
+
130
+ ### Select 数据源示例
131
+
132
+ ```json
133
+ {
134
+ "DataSource": "Sql",
135
+ "Sql": "select Id, Name from mall_category where Name like '%$Keyword$%' limit 0,20",
136
+ "SelectLabel": "Name",
137
+ "SelectSaveField": "Id",
138
+ "SelectSaveFormat": "Text",
139
+ "EnableSearch": true,
140
+ "DataSourceSqlRemote": true
141
+ }
142
+ ```
143
+
144
+ - `$Keyword$` 是平台数据源占位符,不要把浏览器输入直接拼进任意 SQL。
145
+ - 关联表、字段和数据源必须来自当前租户,并受当前表单/菜单的授权与数据范围约束。
146
+ - 大表使用远程搜索和分页,不一次性把全表拉到浏览器。
147
+ - 列表展示名称优先配置 Join/模块关联字段或受控数据源,不把隐藏 Id 暴露给用户。
148
+
149
+ ### 什么时候增加 `XxxName` 快照字段
150
+
151
+ 只有在业务明确要求“保存当时名称”、离线展示、历史审计或高频报表需要时才增加 `XxxName`。它是快照/冗余字段,不是外键事实源,并遵守:
152
+
153
+ 1. 后端 SubmitBefore/接口引擎在同一事务内根据 `XxxId` 校验并写入名称。
154
+ 2. 前端 V8 可以即时回显,但不能作为唯一一致性保障。
155
+ 3. 关联名称后续变化时,先明确历史快照是否应随之更新。
156
+ 4. 批量回填必须参数化、可恢复、可幂等,并经过明确写入确认。
157
+
158
+ ### MCP 建模流程
159
+
160
+ 1. 先用 `microi_get_db_schema` 获取真实表和字段。
161
+ 2. 使用 `microi_build_field_config` 生成 JoinForm/OpenTable/Select 配置。
162
+ 3. 使用 `microi_add_field` 或 `microi_update_field` 写入字段。
163
+ 4. 修改 KeyValue/Config 后回读 `microi_get_field_list`,并执行 `microi_refresh_schema_cache`。
164
+ 5. 在真实菜单下验证普通角色能看到标签,但不能借数据源越权读取其它表或行。
165
+ ---
166
+
167
+ <!-- /microi-progressive:chunk -->
168
+ <!-- microi-progressive:chunk id=ui-design-023 sha256=1c731f6915dae4de788e84af3f6a59cf5d116a4de1f9ca5541f13b55110521ca -->
169
+ ## 表单布局规范(Column)
170
+
171
+ > 平台默认设计标准:所有 `diy_table` **应使用双列布局** (`Column = 2`),更紧凑现代,符合主流后台 SaaS 视觉密度。
172
+
173
+ ### 创建表时
174
+
175
+ ```jsonc
176
+ microi_create_table {
177
+ "name": "Crm_Customer",
178
+ "description": "客户",
179
+ "column": 2 // ✅ 默认就是 2,无需显式传,但推荐写明
180
+ }
181
+ ```
182
+
183
+ ### 修复存量表(一次性把所有 `Column=null` 改成 2)
184
+
185
+ ```jsonc
186
+ microi_update_table {
187
+ "name": "Crm_Customer",
188
+ "column": 2
189
+ }
190
+ ```
191
+
192
+ ### 何时使用 Column=1(单列)
193
+
194
+ - 工作流审批表单(字段少且需要专注)
195
+ - 移动端优先表单(手机宽度不够双列)
196
+ - 含大量富文本/长文本字段的内容编辑表
197
+
198
+ ### 何时使用 Column=3(三列)
199
+
200
+ - 字段≥18 的"基础档案"类大表(员工、商品 SKU、设备清单)
201
+ - 桌面分辨率≥1920px 的内部管理后台
202
+
203
+ > 修改 `Column` 后会自动清缓存(`microi_update_table` 后端走 `UptFormData('diy_table')` + 主动 `RefreshSchemaCache`),前端硬刷新(Ctrl+Shift+R)即可看到效果。
204
+
205
+ ---
206
+
207
+ <!-- /microi-progressive:chunk -->
@@ -29,13 +29,18 @@ Microi 上传字段存在四种必须长期兼容的存储形态,不能假设
29
29
  | 已允许的 `https://` 绝对地址 | 校验协议和允许域名后使用 |
30
30
  | 公有对象存储相对路径 | 拼接当前租户 `FileServer` |
31
31
  | `/file/...` 平台本地文件路由 | 拼接当前 API Base |
32
- | `/micro-app/v3/...` 应用稳定解析路由 | 拼接当前 API Base;这是动态指针路由,不是 HDFS 对象 Key,禁止拼接 `FileServer` |
32
+ | `/micro-app/{tenantLower}/{AppKey}/index.html` 当前公开体验入口 | 应用体验专用 authority resolver 生成可信 API Base 下的绝对 URL;直接返回已提交 HTML,地址栏不跳到 typed v3、CDN/NAS 或版本目录;不能拼 `FileServer` |
33
+ | `/micro-app/v3/...` 内部应用资产解析路由 | 拼接当前 API Base;这是内部动态指针协议,不是 HDFS 对象 Key,也不作为用户当前体验链接 |
33
34
  | 私有对象 | 后端鉴权后签发短期 URL,前端只使用临时 URL |
34
35
  | `blob:` | 仅用于本页创建且能及时 `revokeObjectURL` 的预览 |
35
36
  | `data:` | 仅允许经过大小和 MIME 校验的图片预览;禁止用于富文本任意 HTML |
36
37
 
37
38
  `FileServer` 是对象存储/CDN 地址,不等同于 API 服务。租户切换时必须使用当前 `OsClient` 的运行期配置,禁止在源码中写死域名或租户目录。
38
39
 
40
+ 当前体验 URL 的租户段使用可信连接 OsClient 小写,AppKey 与公开范围取同源权威应用记录。显式历史版本使用已回读的实际 `{可信FileServer}/{tenantLower}/micro-app/{AppKey}/{exactPublishedVersion}/index.html`,保留 FileServer 路径前缀;数字 `CurrentVersion` 不能推算语义版本。旧 `/{version}/...` 短 API 或 `?version=` 不是通用 V3 历史读取协议。签名 URL、历史地址、私有或未知公开性的入口不改写成 current;数据库相对 `PreviewUrl/PublicPublishPath` 与 typed `StableResolverPath` 保持内部协议,不为展示批量改表。
41
+
42
+ 应用导航与上传资源解析分开:官网、工作台和商城的专用体验 resolver 先按权威应用身份生成绝对 API URL;通用 `V8.assetUrl/resolveAssetUrl` 不承担相对短入口识别,不能把相对短入口直接交给它或声称本次已升级 SDK。其既有绝对 HTTP(S) 地址分支继续保留原地址;下方示例用于上传资源,不代替应用体验入口决策。
43
+
39
44
  - 历史配置的原始 `FileServer` 主地址兼容 HTTP/HTTPS。公有相对路径的兼容回退保留协议、端口和路径前缀,不强制升级 HTTPS,也不改拼 API Base;仅此原始主地址允许 HTTP。备用绑定、源地址映射和别名仍限定 HTTPS 并通过原验证,私有路径、带凭据地址及危险协议的限制不变。
40
45
  - 公共解析器以 `Microi.Client/src/utils/public-resource-router.mjs` 为单一事实源;修改后运行 `node microi.skills/scripts/sync-public-resource-router.mjs --write` 同步三份 SDK 生成块并执行 `--check`。PlatformService 的 SDK 属于其源码输入,变更后必须按原生 Bundle 入口重建及更新四个所属应用包,不能只更新 PC 或手改嵌入 Base64。
41
46
 
@@ -171,7 +176,7 @@ function imageUrl(path) {
171
176
 
172
177
  ## 验收清单
173
178
 
174
- - [ ] 公有对象、本地文件、`/micro-app/v3` 动态路由、私有对象、绝对 URL 的路径分支均有测试
179
+ - [ ] 公有对象、本地文件、精确 API 固定入口、`/micro-app/v3` 内部动态路由、私有对象、绝对 URL 的路径分支均有测试;历史与签名地址不被规范化为 current
175
180
  - [ ] 绝对地址字符串、相对路径字符串、单图对象、多图数组及其 JSON 字符串均有测试
176
181
  - [ ] 单图从数组取第一个有效路径,多图保持原顺序;解析 URL 时不丢失或改写上传对象元数据
177
182
  - [ ] 切换两个 `OsClient` 后使用各自 `FileServer`,无跨租户路径或缓存复用
@@ -38,12 +38,13 @@ Microi.net.Api 普通本地启动不要额外设置 `ASPNETCORE_ENVIRONMENT` / `
38
38
  生成接口引擎代码时,代码内容本身(文件头、普通注释、`console.log`、返回 `Msg` 等)不要包含 `Microi`、`吾码` 等平台品牌文字,除非业务数据或字段值本身必须如此。生成代码要有可维护注释:每个 `function` 前写清用途、关键参数和返回值;跨表事务、权限校验、状态机、金额/库存计算、复杂 `_Where` 条件等代码段前写短注释说明业务原因;避免“给变量赋值”这类无信息量注释。
39
39
 
40
40
  <!-- /microi-progressive:chunk -->
41
- <!-- microi-progressive:chunk id=v8-crud-api-001 sha256=93eb45145fcf6849628c73e51f8ae5e450532b425bf763d63587a44dc84bd7aa -->
41
+ <!-- microi-progressive:chunk id=v8-crud-api-001 sha256=6aab6dc9a9cefda0938f29ca5f13c85c5c86ccfde52b9f31e8510465fc56a730 -->
42
42
  ## 核心规则
43
43
 
44
44
  - 接口引擎文件是纯 JavaScript(Jint 引擎,非 Node.js)
45
45
  - 全局对象 `V8` 是所有后端能力的入口
46
46
  - 通过 `V8.Param` 获取前端传入的参数(URL参数 / form-data / payload-json)
47
+ - 业务接口禁止接收客户端 SQL 并执行,包含改名/嵌套/编码的整段 SQL、表列及 WHERE/JOIN/ORDER BY 片段。SQL 结构必须是可信服务端固定代码或白名单定义;前端只传有界业务值或有限 queryKey,动态值用 `_Where` / `.AddInParameter`。参数绑定不保护不可信 SQL 正文,不能以只读、仅 SELECT 或管理员登录代替此规则。受权设计期 SQL 配置/数据库管理 MCP 不等于业务接口可透传 SQL。发布前按 `../v8-sql-query/SKILL.md` 检查请求到执行点的数据流和拒绝负例。
47
48
  - 严格业务字段白名单先逐项排除本入口支持的宿主传输字段:固定 `ApiEngineKey / ApiAddress`、调用类型、设备和 HTTP 请求元数据;不得把整个下划线前缀视为安全字段,不解析 `_RawBody` 重写动作,也不从传输字段取授权。历史调试器的 `TestParam1` 仅兼容 null/undefined、最多 200 字符的字符串、有限数字或布尔值并立即丢弃;对象、数组、超长值和其它未知字段继续拒绝。真实 HTTP 与内部调用都须验收,普通岗位无初始化配置副作用,权限继续读取权威当前用户与目标租户。
48
49
  - `V8.Param` 中嵌套 JSON 的 `JObject/JArray` 可能是 Jint 宿主对象,`Array.isArray` 不一定成立。需要严格数组、布尔或结构校验时,在业务入口对有界业务参数使用 `JSON.parse(JSON.stringify(V8.Param || {}))` 转为纯 JSON,再执行原字段白名单、类型、大小与权限校验;不能把只有 `length` 的对象当数组。只规范化业务参数,不复制或信任客户端身份、`V8.CurrentUser`、`V8.DbTrans` 或 `V8.WF`;大文件继续使用专用文件通道。验收须同时覆盖真实 HTTP 宿主集合和离线 JavaScript 数组。
49
50
  - 通过 `V8.CurrentUser` 获取当前登录用户信息
@@ -28,7 +28,7 @@ MCP 后端实现位于 `Microi.Server/Microi.MCP/`:`V8Engine/V8McpLogic*.cs`
28
28
  | 按钮显隐 | `<按钮Name>_显隐判断(<按钮Id>)` | `指派_显隐判断(01K3X...).js` |
29
29
  | 按钮分类目录 | `<按钮类型中文>(<EnglishField>)` | `[行]更多按钮(MoreBtns)/` |
30
30
 
31
- ### 0.1 7 类表单 V8 事件中文名(以 `diy_table` 对应 `diy_field.Label` 为准)
31
+ ### 0.1 表单 V8 事件中文名(以 `diy_table` 对应 `diy_field.Label` 为准)
32
32
 
33
33
  | EventType | 中文名 |
34
34
  |-----------|--------|
@@ -40,6 +40,27 @@ MCP 后端实现位于 `Microi.Server/Microi.MCP/`:`V8Engine/V8McpLogic*.cs`
40
40
  | OutFormV8 | 前端表单提交后V8事件 |
41
41
  | DataFilterV8 | 后端数据过滤V8事件 |
42
42
 
43
+ 日志、评论、版本还包括以下 12 个独立事件。它们同样位于表单 V8 事件目录,只有非空代码才落文件;
44
+ 保存使用标准事件接口和版本链,后端源码保持管理员设计期边界,不随普通表单返回。
45
+
46
+ | EventType | 中文名 |
47
+ |---|---|
48
+ | DataLogBeforeClientV8 | 前端数据日志写入前V8事件 |
49
+ | DataLogBeforeServerV8 | 后端数据日志写入前V8事件 |
50
+ | DataLogAfterServerV8 | 后端数据日志写入后V8事件 |
51
+ | DataLogAfterClientV8 | 前端数据日志写入后V8事件 |
52
+ | DataCommentBeforeClientV8 | 前端数据评论写入前V8事件 |
53
+ | DataCommentBeforeServerV8 | 后端数据评论写入前V8事件 |
54
+ | DataCommentAfterServerV8 | 后端数据评论写入后V8事件 |
55
+ | DataCommentAfterClientV8 | 前端数据评论写入后V8事件 |
56
+ | DataVersionBeforeClientV8 | 前端数据版本写入前V8事件 |
57
+ | DataVersionBeforeServerV8 | 后端数据版本写入前V8事件 |
58
+ | DataVersionAfterServerV8 | 后端数据版本写入后V8事件 |
59
+ | DataVersionAfterClientV8 | 前端数据版本写入后V8事件 |
60
+
61
+ 完整触发顺序、活动上下文、共享父事务和幂等回执规则见 `../v8-table-event/SKILL.md`。
62
+ `DataFilterV8` 是 `ServerDataV8` 历史别名,写入时规范为真实属性,不能创建第二份事件。
63
+
43
64
  > 统一用“后端”,不要再生成“服务端”或“服务器端”可见目录/文件名。
44
65
 
45
66
  ### 0.2 4 类字段 V8 事件中文名
@@ -7,6 +7,8 @@ description: Microi 菜单按钮与 Tab V8 指南。用于配置 sys_menu MoreBt
7
7
 
8
8
  # v8-menu-buttons — 菜单按钮 / Tab / 批量操作 V8 写法
9
9
 
10
+ 系统帐号、角色、岗位的官方批量删除使用 `platform-directory-batch-delete`:固定 `Kind=User/Role/Job`,从 `V8.TableRowSelected` 收集 1–50 个 Id,确认后提交 `Ids` 并刷新列表;`DryRun=true` 仅校验。按钮显隐不能替代后端当前租户管理员校验。禁止批量删除当前帐号、admin 或平台管理员帐号/角色。帐号/角色复用原可信目录动作且逐项提交,必须展示 `DeletedCount/FailedCount/Results`;有失败不能提示整批成功,不得盲重试未知结果。不能让前端指定表名、SQL、身份或绕过权限。
11
+
10
12
  > 适用于 sys_menu 表的 MoreBtns、FormBtns、BatchSelectMoreBtns、PageTabs、ExportMoreBtns、PageBtns 字段。
11
13
  > 这些字段统一存为 **JSON 字符串**(数组),每个数组元素是一个按钮/Tab 对象。
12
14
  > AI 通过 MCP `microi_create_module` 创建菜单时应**主动**传入这些字段,业务系统才"活"。
@@ -70,6 +70,12 @@ IP、端口和完整 URL 只登记入口,不提交官方 ESA 域名绑定。
70
70
  `SaasPromotionSecurity.TryResolveProvisioningDomain` 必须同时兼容,单发微服务不能
71
71
  绕过旧 API 的裸域校验。公开试用继续保持独立域名为空,不扩大匿名开通权限。
72
72
 
73
+ 创建成功页优先将已校验、归一化的填写地址作为主入口,保留 HTTP(S)、IP、端口与
74
+ 子路径;宿主生成的安全启动地址作为单独备用入口。未做访问探测只能说明“访问未验证”,
75
+ 不能因状态未知就改成“待配置”或隐藏正确的填写地址。`Upgrade.Before/Target/After`
76
+ 如 `7.6.17.0` 是数据库迁移游标,应收在升级详情中,不能标作 API/PC 产品版本。
77
+ API 版本仅展示可信健康结果实际回读的 `BackendVersion`,Web 版本未知时不推测。
78
+
73
79
  ### 推广中心与匿名空库开通
74
80
 
75
81
  - 主租户现有 `V8.FormEngine` 允许显式指定子租户;子租户被 `V8TenantContext.EnforceOsClient`
@@ -150,9 +150,19 @@ Secret 只通过租户管理员专用端点写入租户绑定的认证密文。
150
150
  - 没有字段上下文的普通上传默认保持私有安全目录;管理员可在普通 `sys_config.HdfsUploadRules` 按可信角色授予业务目录(含有界 glob)与明确公有权限。必须先拒绝实际路径中的通配符、穿越和平台保留目录,再做模式匹配;通配符不是匿名/跨租户/发布授权。微信待审图片、头像、裁剪/压缩原图等更强规则继续优先。测试同时证明公有字段/目录可用、私有请求/伪造上下文/保留目录不可绕过。
151
151
 
152
152
  <!-- /microi-progressive:chunk -->
153
- <!-- microi-progressive:chunk id=v8-security-002 sha256=fdc1cbd297d141bfef8e9e0ee58a972410ffc9b6b778b217b5610f86139f514c -->
153
+ <!-- microi-progressive:chunk id=v8-security-002 sha256=4fbec4fff91a887a9daa2386d60375e5b411e51fd305a3d89086bc4619231144 -->
154
154
  ## 1. 防 SQL 注入
155
155
 
156
+ ### SQL 来源与执行结构边界
157
+
158
+ 业务接口引擎禁止接收前端 SQL 并执行。SQL 正文、表列、WHERE/JOIN/ORDER BY、运算符和
159
+ 存储过程名必须来自可信服务端固定代码或白名单定义;前端仅传业务值或有限 `queryKey`。
160
+ 禁止把 `V8.Param`、Header、URL/Form/JSON、`_RawBody` 的整段或片段经 `sql/query/rawSql/SqlWhere`
161
+ 等别名、嵌套对象、编码解码、间接变量或跨引擎转发后执行。不能仅封禁一个参数名。
162
+ `.AddInParameter` 只绑定值,不能保护客户端控制的 SQL 正文;“仅 SELECT”、关键字正则、
163
+ 只读连接或管理员登录也不能替代来源校验。受权设计期元数据与独立数据库管理 MCP 仍遵守
164
+ 其控制面权限,不变成普通业务 SQL 代理。安全示例与负例验收见 `../v8-sql-query/SKILL.md`。
165
+
156
166
  ### 必须:参数化查询
157
167
 
158
168
  ```javascript
@@ -9,6 +9,29 @@ description: Microi V8 安全 SQL 查询指南。用于选择 V8.FormEngine _Whe
9
9
 
10
10
  你正在开发 Microi 吾码平台的 V8 引擎代码。数据库查询有两种方式:`V8.FormEngine`(推荐)和 `V8.Db`(原始 SQL)。必须遵守安全规范。
11
11
 
12
+ ## 业务接口禁止接收客户端 SQL(强制)
13
+
14
+ 接口引擎的 SQL 正文与执行结构只能来自可信、受版本管理的服务端代码或服务端白名单查询定义。
15
+ 前端只传业务值,或有限的 `queryKey` / 排序键,由服务端映射为固定查询;值使用 `_Where` 或
16
+ `.AddInParameter()` 绑定。参数化只能保护值,不能让不可信 SQL 正文本身变安全。
17
+
18
+ - 禁止将 `V8.Param`、Header、URL、Form、JSON、`_RawBody` 中的完整 SQL 或片段传给
19
+ `FromSql/Execute*`。`sql/query/rawSql/SqlWhere/SqlJoin/orderBy` 只是例子;改名、嵌套、
20
+ Base64/加密、间接变量或转发另一个引擎,都不能改变来源不可信的事实。
21
+ - 禁止透传客户端表名、列名、运算符、WHERE/JOIN/ORDER BY 或存储过程名。需要动态排序时,
22
+ 只允许有限业务键映射为服务端固定列与方向;未知键拒绝。FormEngine 受权字段筛选协议仍可用,
23
+ 自建业务接口须限定字段/操作符并追加不可被客户端覆盖的用户、租户和业务范围。
24
+ - “仅 SELECT”、禁分号/关键字正则、只读连接、已登录/管理员、转义或参数绑定都不能代替上述来源边界。
25
+ 不为普通业务新建通用 SQL 执行代理或匿名 SQL 接口。
26
+ - 发布前检查从请求源到 SQL 执行点的数据流,包括别名和嵌套调用;用未知业务参数、伪造查询键、
27
+ SQL 片段与越权用户负例验证拒绝,正常参数验证固定 SQL 与绑定值分离。
28
+
29
+ [可执行安全示例](examples/business-query.js) 展示 `Input` 白名单、固定查询键/排序映射与本人范围。
30
+ 示例不是通用授权器,正式业务仍应校验所需角色、动作、数据范围和数据库方言。
31
+ 管理员设计期保存的字段 SQL、模块 `SqlWhere/SqlJoin` 与独立受控的数据库管理 MCP 工具属于控制面;
32
+ 这些能力仍保留原权限与审计,不能据此生成前端可传 SQL 的业务接口。服务端合法参数化
33
+ `V8.Db.FromSql` 能力不变。
34
+
12
35
  ## 性能门禁(必须执行)
13
36
 
14
37
  写接口引擎前必须先做数据访问计划,避免“循环套循环查数据库”:
@@ -85,7 +108,7 @@ V8.FormEngine.GetTableData('Table', { _Where: objectWhere });
85
108
 
86
109
  ## 次选:V8.Db.FromSql(仅 SQL 字符串 + AddInParameter)
87
110
 
88
- > **⚠️ FormEngine 优先原则:** 增删改操作(INSERT / UPDATE / DELETE)必须**优先**使用 `V8.FormEngine.AddFormData` / `UptFormData` / `UptFormDataByWhere` / `DelFormData` 等方法。**只有**多表 JOIN、复杂子查询、GROUP BY 聚合等 FormEngine 无法表达的场景才使用 `V8.Db.FromSql`。
111
+ > **⚠️ FormEngine 优先原则:** 增删改操作(INSERT / UPDATE / DELETE)必须**优先**使用 `V8.FormEngine.AddFormData` / `UptFormData` / `UptFormDataByWhere` / `DelFormData` 等方法。只有多表 JOIN、复杂子查询、GROUP BY 聚合等 FormEngine 无法表达的场景,或下文明确要求保留业务修改时间的原子计数,才使用参数化 SQL。
89
112
 
90
113
  > **⚠️ FromSql 调用规则:** `V8.Db.FromSql` 在 V8 中只传 SQL 字符串,不要把动态值作为第二个或后续参数传给 `FromSql`。动态值必须用链式 `.AddInParameter('@p0', value)` 绑定;否则会生成平台不支持的调用签名。
91
114
 
@@ -224,6 +247,20 @@ try {
224
247
  }
225
248
  ```
226
249
 
250
+ ## 仅更新计数而保留业务修改时间
251
+
252
+ 普通 `FormEngine.UptFormData` 会自动推进系统 `UpdateTime`,不能靠 `_NotSaveField` 抑制。
253
+ 浏览量、收藏量等不代表内容修改的计数,如果业务明确要求保持修改时间与版本,允许使用
254
+ 当前事务中的固定列、参数化原子 SQL;权限校验、目标定位和公开范围仍沿用原流程。
255
+ 计数增量使用 `Count = COALESCE(Count,0) + 1`,不能先读后写丢失并发增量。
256
+ 收藏等同时写状态与总数的场景须在同一事务锁定固定业务行,更新状态、聚合总数并回读;
257
+ 重复请求保持幂等,禁止手动提交接口引擎事务。SQL 不得顺带修改 `UpdateTime` 或内容版本。
258
+
259
+ 列表使用修改时间排序时,要明确权威时间、空值与非法日期的回退、稳定唯一键平局规则,
260
+ 有时区ISO值与无时区审计值混用时,先查明该业务的权威时区并归一到同一时间轴,不能按浏览器或执行进程的本地TZ解析。
261
+ 并在服务端全局排序后分页,不能只排序已加载的首屏。验收同时回读计数、原始时间、版本,
262
+ 覆盖重复请求、超过一页、非空旧业务日期加较新系统修改日期;不要通过改历史日期或单条特殊置顶掩盖问题。
263
+
227
264
  ## 绝对禁止
228
265
 
229
266
  ```javascript
@@ -0,0 +1,18 @@
1
+ // 本示例只接受 Input 内业务参数;真实入口还须校验本业务的当前用户、角色与动作权限。
2
+ // 查询正文、表列和排序结构全部来自服务端常量,前端只选择有限 queryKey / sort 键。
3
+ var input = JSON.parse(JSON.stringify(V8.Param.Input || {}));
4
+ if (!input || typeof input !== 'object' || Array.isArray(input)) return { Code: 0, Msg: 'Input 必须为对象' };
5
+ var allowed = ['queryKey', 'status', 'sort'];
6
+ if (Object.keys(input).some(function (key) { return allowed.indexOf(key) < 0; })) return { Code: 0, Msg: '包含不支持的业务参数' };
7
+ if (!V8.CurrentUser || !V8.CurrentUser.Id) return { Code: 1001, Msg: '请先登录' };
8
+ if (input.queryKey !== 'my-orders') return { Code: 0, Msg: '查询类型无效' };
9
+ if (['Pending', 'Completed'].indexOf(input.status) < 0) return { Code: 0, Msg: '状态无效' };
10
+ var sorts = { 'created-desc': ' ORDER BY CreateTime DESC, Id DESC', 'amount-desc': ' ORDER BY Amount DESC, Id DESC' };
11
+ var sort = input.sort === undefined ? 'created-desc' : input.sort;
12
+ if (typeof sort !== 'string' || !Object.prototype.hasOwnProperty.call(sorts, sort)) return { Code: 0, Msg: '排序无效' };
13
+ // 当前用户归属条件不可由前端 userId / 租户 / SQL 片段覆盖。分页为此有界查询的固定上限。
14
+ var rows = V8.Db.FromSql('SELECT Id, Code, Amount, Status FROM Biz_Order WHERE IsDeleted = 0 AND UserId = @userId AND Status = @status' + sorts[sort] + ' LIMIT 20')
15
+ .AddInParameter('@userId', V8.CurrentUser.Id)
16
+ .AddInParameter('@status', input.status)
17
+ .ToArray();
18
+ return { Code: 1, Data: rows };