@microi.net/cli 5.6.0 → 5.6.1
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/.mcp.json +1 -1
- package/.workbuddy-plugin/marketplace.json +2 -2
- package/.workbuddy-plugin/plugin.json +1 -1
- package/README.md +71 -71
- package/assets/build-meta.json +7 -7
- package/assets/feature-matrix.json +129 -129
- package/cordis.patch.yml +1 -1
- package/package.json +1 -1
- package/scripts/codex-marketplace.json +20 -20
- package/scripts/mcp-codex-stdio-adapter.js +189 -189
- package/scripts/mcp-server.js +99 -99
- package/scripts/mcp-trae-windows-launcher.cmd +21 -21
- package/scripts/microi-cli-mcp.js +7 -7
- package/scripts/microi-cli.js +39 -36
- package/scripts/microi-codex-broker.js +447 -447
- package/scripts/microi-codex-router.js +582 -582
- package/scripts/microi-skills.meta.json +225 -222
- package/skills/.microi-skills-version.json +2 -2
- package/skills/.progressive-disclosure-manifest.json +37 -37
- package/skills/README.md +1 -1
- package/skills/app-store/SKILL.md +199 -182
- package/skills/dos-orm/SKILL.md +96 -81
- package/skills/message-notification/SKILL.md +152 -126
- package/skills/microi-client-frontend/SKILL.md +11 -5
- 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 +2 -2
- 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 +2 -2
- 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 +2 -2
- package/skills/microi-codex/SKILL.md +48 -46
- package/skills/microi-docs-coverage/SKILL.md +133 -123
- package/skills/microi-docs-coverage/references/capability-map.md +2 -2
- package/skills/microi-form-engine/SKILL.md +77 -53
- package/skills/microi-microservice/SKILL.md +17 -16
- package/skills/microi-sso/SKILL.md +91 -85
- package/skills/microi-system-delivery/SKILL.md +5 -4
- 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 +27 -22
- package/skills/module-engine/SKILL.md +17 -1
- package/skills/print-engine/SKILL.md +47 -39
- package/skills/scripts/sync-embedded-skills.mjs +46 -0
- package/skills/scripts/validate-progressive-disclosure.mjs +57 -52
- package/skills/system-observability/SKILL.md +16 -4
- package/skills/v8-api-config/SKILL.md +382 -378
- package/skills/v8-file-upload/SKILL.md +157 -154
- 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 +133 -133
- package/skills/v8-formengine-http/SKILL.md +19 -1
- package/skills/v8-security/SKILL.md +25 -25
- package/skills/v8-sql-query/SKILL.md +130 -128
- package/skills/v8-table-event/SKILL.md +29 -22
- 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 +19 -19
- package/skills/v8-workflow/SKILL.md +54 -45
- package/skills/workspace-conventions/SKILL.md +259 -236
- 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
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: v8-sql-query
|
|
3
|
-
description: Microi V8 安全 SQL 查询指南。用于选择 V8.FormEngine _Where 或 V8.Db.FromSql,处理参数化查询、联表、聚合并避免 SQL 注入。
|
|
3
|
+
description: Microi V8 安全 SQL 查询指南。用于选择 V8.FormEngine _Where 或 V8.Db.FromSql,处理参数化查询、联表、聚合并避免 SQL 注入。
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
> **Codex 非阻塞自动更新:** 当前宿主为 Codex 时,吾码 CLI、Codex 插件与工作区 AI/MCP 由后台自动更新;需要诊断时读取 `../microi-codex-installer/SKILL.md`。更新失败、等待空闲或尚未重载均不得阻断当前、正在进行或新建任务。非 Codex 宿主跳过此项。
|
|
7
7
|
|
|
8
8
|
# Microi V8 安全 SQL 查询
|
|
9
9
|
|
|
10
|
-
你正在开发 Microi 吾码平台的 V8 引擎代码。数据库查询有两种方式:`V8.FormEngine`(推荐)和 `V8.Db`(原始 SQL)。必须遵守安全规范。
|
|
11
|
-
|
|
12
|
-
## 性能门禁(必须执行)
|
|
13
|
-
|
|
14
|
-
写接口引擎前必须先做数据访问计划,避免“循环套循环查数据库”:
|
|
15
|
-
|
|
16
|
-
- 禁止在 `for` / `while` / `forEach` / `map` 循环内调用 `V8.FormEngine.GetFormData`、`GetTableData`、`V8.Db.FromSql`、`V8.ApiEngine.Run` 或 `V8.Http.*`。确实无法避免时,必须先说明原因,并加分页、缓存或限流。
|
|
17
|
-
- 多条 Id、编码、外键查询必须一次性用 `_Where: [['Id','In', ids]]`、SQL `IN`、JOIN 或聚合查询取回,再用内存字典映射。
|
|
18
|
-
- 需要父子、主从、用户/部门/角色名称映射时,先批量取关联表,只保留 `_SelectFields` 必要列,不要逐行查名称。
|
|
19
|
-
- 统计、计数、汇总优先让数据库一次 `GROUP BY` / `COUNT` / `SUM` 完成,不要把大表全部拉到 V8 里循环统计。
|
|
20
|
-
- 列表接口必须限制 `_PageSize`,管理端默认不要超过 100,导出或批处理必须显式分批。
|
|
21
|
-
- 每次查询都要写 `_SelectFields` 或明确 SQL 字段列表,禁止 `SELECT *` 用在大表、接口列表、循环前置查询中。
|
|
22
|
-
- 外部 HTTP、翻译、短信、AI 等慢调用不能放在数据库事务和大循环中;要么异步队列,要么批量预处理并设置超时。
|
|
23
|
-
- 返回前自检一次:数据库访问次数应与数据量无关或近似常数级,不能随着行数线性增长为 N 次查询。
|
|
24
|
-
|
|
25
|
-
## 首选:V8.FormEngine + _Where(自动防注入)
|
|
10
|
+
你正在开发 Microi 吾码平台的 V8 引擎代码。数据库查询有两种方式:`V8.FormEngine`(推荐)和 `V8.Db`(原始 SQL)。必须遵守安全规范。
|
|
11
|
+
|
|
12
|
+
## 性能门禁(必须执行)
|
|
13
|
+
|
|
14
|
+
写接口引擎前必须先做数据访问计划,避免“循环套循环查数据库”:
|
|
15
|
+
|
|
16
|
+
- 禁止在 `for` / `while` / `forEach` / `map` 循环内调用 `V8.FormEngine.GetFormData`、`GetTableData`、`V8.Db.FromSql`、`V8.ApiEngine.Run` 或 `V8.Http.*`。确实无法避免时,必须先说明原因,并加分页、缓存或限流。
|
|
17
|
+
- 多条 Id、编码、外键查询必须一次性用 `_Where: [['Id','In', ids]]`、SQL `IN`、JOIN 或聚合查询取回,再用内存字典映射。
|
|
18
|
+
- 需要父子、主从、用户/部门/角色名称映射时,先批量取关联表,只保留 `_SelectFields` 必要列,不要逐行查名称。
|
|
19
|
+
- 统计、计数、汇总优先让数据库一次 `GROUP BY` / `COUNT` / `SUM` 完成,不要把大表全部拉到 V8 里循环统计。
|
|
20
|
+
- 列表接口必须限制 `_PageSize`,管理端默认不要超过 100,导出或批处理必须显式分批。
|
|
21
|
+
- 每次查询都要写 `_SelectFields` 或明确 SQL 字段列表,禁止 `SELECT *` 用在大表、接口列表、循环前置查询中。
|
|
22
|
+
- 外部 HTTP、翻译、短信、AI 等慢调用不能放在数据库事务和大循环中;要么异步队列,要么批量预处理并设置超时。
|
|
23
|
+
- 返回前自检一次:数据库访问次数应与数据量无关或近似常数级,不能随着行数线性增长为 N 次查询。
|
|
24
|
+
|
|
25
|
+
## 首选:V8.FormEngine + _Where(自动防注入)
|
|
26
26
|
|
|
27
27
|
`_Where` 是参数化查询语法,自动防 SQL 注入,**永远优先使用**。
|
|
28
28
|
|
|
@@ -75,57 +75,59 @@ var newWhere = V8.Method.ParseWhere(V8.Param._Where);
|
|
|
75
75
|
V8.FormEngine.GetTableData('Table', { _Where: newWhere });
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
## 次选:V8.Db.FromSql(仅 SQL 字符串 + AddInParameter)
|
|
79
|
-
|
|
80
|
-
> **⚠️ FormEngine 优先原则:** 增删改操作(INSERT / UPDATE / DELETE)必须**优先**使用 `V8.FormEngine.AddFormData` / `UptFormData` / `UptFormDataByWhere` / `DelFormData` 等方法。**只有**多表 JOIN、复杂子查询、GROUP BY 聚合等 FormEngine 无法表达的场景才使用 `V8.Db.FromSql`。
|
|
81
|
-
|
|
82
|
-
> **⚠️ FromSql 调用规则:** `V8.Db.FromSql` 在 V8 中只传 SQL 字符串,不要把动态值作为第二个或后续参数传给 `FromSql`。动态值必须用链式 `.AddInParameter('@p0', value)` 绑定;否则会生成平台不支持的调用签名。
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
//
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
.AddInParameter("@
|
|
95
|
-
.
|
|
96
|
-
|
|
78
|
+
## 次选:V8.Db.FromSql(仅 SQL 字符串 + AddInParameter)
|
|
79
|
+
|
|
80
|
+
> **⚠️ FormEngine 优先原则:** 增删改操作(INSERT / UPDATE / DELETE)必须**优先**使用 `V8.FormEngine.AddFormData` / `UptFormData` / `UptFormDataByWhere` / `DelFormData` 等方法。**只有**多表 JOIN、复杂子查询、GROUP BY 聚合等 FormEngine 无法表达的场景才使用 `V8.Db.FromSql`。
|
|
81
|
+
|
|
82
|
+
> **⚠️ FromSql 调用规则:** `V8.Db.FromSql` 在 V8 中只传 SQL 字符串,不要把动态值作为第二个或后续参数传给 `FromSql`。动态值必须用链式 `.AddInParameter('@p0', value)` 绑定;否则会生成平台不支持的调用签名。
|
|
83
|
+
|
|
84
|
+
> **共享事务:** `V8.Db` 是主库会话,`V8.Db.FromSql` 不会自动加入接口引擎事务。需要与表单、其它 SQL 或流程共同提交/回滚的操作,必须使用 `V8.DbTrans.FromSql`;依赖本事务尚未提交结果的查询也一样。`V8.FormEngine`、`V8.ApiEngine.Run` 继续显式传第三参数 `V8.DbTrans`。禁止从安全代理取内部事务并自行提交。
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
// ❌ 错误形态:不要把动态值作为 FromSql 的第二个参数传入
|
|
88
|
+
|
|
89
|
+
// ✅ 优先:改用 FormEngine(单表增删改查都优先这样写)
|
|
90
|
+
V8.FormEngine.UptFormData('t', { Id: id, A: val1, B: val2 });
|
|
91
|
+
|
|
92
|
+
// ✅ 必须用原生 SQL 时:FromSql 只传 SQL,参数用 AddInParameter
|
|
93
|
+
V8.DbTrans.FromSql("UPDATE t SET A=@p0, B=@p1 WHERE Id=@p2")
|
|
94
|
+
.AddInParameter("@p0", val1)
|
|
95
|
+
.AddInParameter("@p1", val2)
|
|
96
|
+
.AddInParameter("@p2", id)
|
|
97
|
+
.ExecuteNonQuery();
|
|
98
|
+
```
|
|
97
99
|
|
|
98
100
|
当 `_Where` 无法满足复杂查询(多表 JOIN、子查询、聚合统计)时使用 `V8.Db`:
|
|
99
101
|
|
|
100
102
|
```javascript
|
|
101
|
-
// ✅ 安全:使用 @p0, @p1 占位符,并用 AddInParameter 绑定
|
|
102
|
-
var list = V8.Db.FromSql(
|
|
103
|
-
'SELECT a.Id, a.Name, b.OrderCount FROM Customer a LEFT JOIN (SELECT CustomerId, COUNT(*) OrderCount FROM OrderHeader GROUP BY CustomerId) b ON a.Id = b.CustomerId WHERE a.Status = @p0'
|
|
104
|
-
).AddInParameter("@p0", 1).ToArray();
|
|
105
|
-
|
|
106
|
-
// ✅ 安全:多个参数
|
|
107
|
-
var row = V8.Db.FromSql(
|
|
108
|
-
'SELECT * FROM SysUser WHERE Account = @p0 AND DeptId = @p1'
|
|
109
|
-
).AddInParameter("@p0", V8.Param.account)
|
|
110
|
-
.AddInParameter("@p1", V8.Param.deptId)
|
|
111
|
-
.First();
|
|
112
|
-
|
|
113
|
-
// 统计
|
|
114
|
-
var count = V8.Db.FromSql(
|
|
115
|
-
'SELECT COUNT(*) FROM OrderHeader WHERE Status = @p0 AND CreateTime >= @p1'
|
|
116
|
-
).AddInParameter("@p0", 1)
|
|
117
|
-
.AddInParameter("@p1", V8.Param.startDate)
|
|
118
|
-
.ToScalar();
|
|
119
|
-
|
|
120
|
-
// 非查询(UPDATE / INSERT / DELETE)
|
|
121
|
-
V8.
|
|
122
|
-
'UPDATE SysUser SET LastLoginTime = @p0 WHERE Id = @p1'
|
|
123
|
-
).AddInParameter("@p0", DateNow('yyyy-MM-dd HH:mm:ss'))
|
|
124
|
-
.AddInParameter("@p1", V8.CurrentUser.Id)
|
|
125
|
-
.ExecuteNonQuery();
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
> 老版本兼容:要把维护接口复制到旧部署时,不要假定 `DateNow` 或 `System.DateTime.Now.ToString(...)` 一定可用。若数据库类型固定,可直接使用该数据库的当前时间表达式(例如 MySQL 的 `NOW()`);跨数据库代码则应按数据库类型选择表达式或传入平台已确认支持的时间值。
|
|
103
|
+
// ✅ 安全:使用 @p0, @p1 占位符,并用 AddInParameter 绑定
|
|
104
|
+
var list = V8.Db.FromSql(
|
|
105
|
+
'SELECT a.Id, a.Name, b.OrderCount FROM Customer a LEFT JOIN (SELECT CustomerId, COUNT(*) OrderCount FROM OrderHeader GROUP BY CustomerId) b ON a.Id = b.CustomerId WHERE a.Status = @p0'
|
|
106
|
+
).AddInParameter("@p0", 1).ToArray();
|
|
107
|
+
|
|
108
|
+
// ✅ 安全:多个参数
|
|
109
|
+
var row = V8.Db.FromSql(
|
|
110
|
+
'SELECT * FROM SysUser WHERE Account = @p0 AND DeptId = @p1'
|
|
111
|
+
).AddInParameter("@p0", V8.Param.account)
|
|
112
|
+
.AddInParameter("@p1", V8.Param.deptId)
|
|
113
|
+
.First();
|
|
114
|
+
|
|
115
|
+
// 统计
|
|
116
|
+
var count = V8.Db.FromSql(
|
|
117
|
+
'SELECT COUNT(*) FROM OrderHeader WHERE Status = @p0 AND CreateTime >= @p1'
|
|
118
|
+
).AddInParameter("@p0", 1)
|
|
119
|
+
.AddInParameter("@p1", V8.Param.startDate)
|
|
120
|
+
.ToScalar();
|
|
121
|
+
|
|
122
|
+
// 非查询(UPDATE / INSERT / DELETE)
|
|
123
|
+
V8.DbTrans.FromSql(
|
|
124
|
+
'UPDATE SysUser SET LastLoginTime = @p0 WHERE Id = @p1'
|
|
125
|
+
).AddInParameter("@p0", DateNow('yyyy-MM-dd HH:mm:ss'))
|
|
126
|
+
.AddInParameter("@p1", V8.CurrentUser.Id)
|
|
127
|
+
.ExecuteNonQuery();
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
> 老版本兼容:要把维护接口复制到旧部署时,不要假定 `DateNow` 或 `System.DateTime.Now.ToString(...)` 一定可用。若数据库类型固定,可直接使用该数据库的当前时间表达式(例如 MySQL 的 `NOW()`);跨数据库代码则应按数据库类型选择表达式或传入平台已确认支持的时间值。
|
|
129
131
|
|
|
130
132
|
### V8.Db 方法速查
|
|
131
133
|
|
|
@@ -140,52 +142,52 @@ V8.Db.FromSql(
|
|
|
140
142
|
|
|
141
143
|
### 读写分离
|
|
142
144
|
|
|
143
|
-
```javascript
|
|
144
|
-
V8.Db.FromSql(...) // 主库(读写)
|
|
145
|
-
V8.DbRead.FromSql(...) // 从库(只读,适合报表和大量查询)
|
|
145
|
+
```javascript
|
|
146
|
+
V8.Db.FromSql(...) // 主库(读写)
|
|
147
|
+
V8.DbRead.FromSql(...) // 从库(只读,适合报表和大量查询)
|
|
146
148
|
// 未部署读写分离时 V8.DbRead 与 V8.Db 一致
|
|
147
149
|
```
|
|
148
150
|
|
|
149
|
-
### 跨应用查询(扩展数据库)
|
|
150
|
-
|
|
151
|
-
```javascript
|
|
152
|
-
var list = V8.Dbs.OracleDB1.FromSql('SELECT * FROM Table WHERE Id = @p0')
|
|
153
|
-
.AddInParameter("@p0", id)
|
|
154
|
-
.ToArray();
|
|
155
|
-
|
|
156
|
-
// 不写入 microi_database:创建仅当前请求使用的临时会话
|
|
157
|
-
var tempDb = V8.Dbs.Open(
|
|
158
|
-
'SqlServer',
|
|
159
|
-
'Server=127.0.0.1,1433;Database=app;User Id=user;Password=***;TrustServerCertificate=True;'
|
|
160
|
-
);
|
|
161
|
-
var tempList = tempDb.FromSql('SELECT Id, Name FROM Customer WHERE Status = @p0')
|
|
162
|
-
.AddInParameter('@p0', 1)
|
|
163
|
-
.ToArray();
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
`V8.Dbs.Open` 与保存连接均只支持 Dos.ORM 已认证的 `MySql`、`SqlServer`、`Oracle`、`PostgreSql`、`DaMeng`、`KingBase`。动态连接串只能来自可信服务端密钥或管理员代码,禁止使用 `V8.Param.ConnectionString`,禁止记录或返回。外部 SQL 的表名、列名、排序字段必须来自已校验元数据白名单;动态值继续使用 `AddInParameter`。
|
|
167
|
-
|
|
168
|
-
MCP 结构发现使用 `microi_inspect_external_database`,安全抽样默认使用只读的 `microi_query_external_database`。当用户明确要求数据库管理级能力时,使用独立的 `microi_execute_external_database`:它只允许后端确认的 `Level >= 9999` 当前用户调用,显式确认后可执行目标数据库账号有权执行的任意 DML、DDL、存储过程、数据库原生命令或多语句。输出行数限制只保护 MCP 传输,不限制数据库副作用;审计只记录 SQL 哈希、长度、模式和结果,不得记录 SQL 正文、连接串或密码。
|
|
169
|
-
|
|
170
|
-
不要用 `microi_get_db_schema` 读取第三方库,也不要在对话中搬运全库数据;持续同步应创建参数化、分页、幂等的服务端任务。最高权限入口不等于跨租户,也不能超越目标数据库账号自身权限。
|
|
151
|
+
### 跨应用查询(扩展数据库)
|
|
152
|
+
|
|
153
|
+
```javascript
|
|
154
|
+
var list = V8.Dbs.OracleDB1.FromSql('SELECT * FROM Table WHERE Id = @p0')
|
|
155
|
+
.AddInParameter("@p0", id)
|
|
156
|
+
.ToArray();
|
|
157
|
+
|
|
158
|
+
// 不写入 microi_database:创建仅当前请求使用的临时会话
|
|
159
|
+
var tempDb = V8.Dbs.Open(
|
|
160
|
+
'SqlServer',
|
|
161
|
+
'Server=127.0.0.1,1433;Database=app;User Id=user;Password=***;TrustServerCertificate=True;'
|
|
162
|
+
);
|
|
163
|
+
var tempList = tempDb.FromSql('SELECT Id, Name FROM Customer WHERE Status = @p0')
|
|
164
|
+
.AddInParameter('@p0', 1)
|
|
165
|
+
.ToArray();
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
`V8.Dbs.Open` 与保存连接均只支持 Dos.ORM 已认证的 `MySql`、`SqlServer`、`Oracle`、`PostgreSql`、`DaMeng`、`KingBase`。动态连接串只能来自可信服务端密钥或管理员代码,禁止使用 `V8.Param.ConnectionString`,禁止记录或返回。外部 SQL 的表名、列名、排序字段必须来自已校验元数据白名单;动态值继续使用 `AddInParameter`。
|
|
169
|
+
|
|
170
|
+
MCP 结构发现使用 `microi_inspect_external_database`,安全抽样默认使用只读的 `microi_query_external_database`。当用户明确要求数据库管理级能力时,使用独立的 `microi_execute_external_database`:它只允许后端确认的 `Level >= 9999` 当前用户调用,显式确认后可执行目标数据库账号有权执行的任意 DML、DDL、存储过程、数据库原生命令或多语句。输出行数限制只保护 MCP 传输,不限制数据库副作用;审计只记录 SQL 哈希、长度、模式和结果,不得记录 SQL 正文、连接串或密码。
|
|
171
|
+
|
|
172
|
+
不要用 `microi_get_db_schema` 读取第三方库,也不要在对话中搬运全库数据;持续同步应创建参数化、分页、幂等的服务端任务。最高权限入口不等于跨租户,也不能超越目标数据库账号自身权限。
|
|
171
173
|
|
|
172
174
|
## 数据库事务
|
|
173
175
|
|
|
174
176
|
### 接口引擎事务(自动管理)
|
|
175
177
|
|
|
176
178
|
```javascript
|
|
177
|
-
//
|
|
179
|
+
// 接口引擎创建 V8.DbTrans;V8.Db 是独立主库会话,不自动加入它:
|
|
178
180
|
// 返回 Code=1 → 自动提交事务
|
|
179
181
|
// 返回 Code≠1 → 自动回滚事务
|
|
180
182
|
// 手动调用 V8.DbTrans.Commit() 或 V8.DbTrans.Rollback() 均无效
|
|
181
|
-
V8.
|
|
182
|
-
.AddInParameter("@p0", 100)
|
|
183
|
-
.AddInParameter("@p1", fromId)
|
|
184
|
-
.ExecuteNonQuery();
|
|
185
|
-
V8.
|
|
186
|
-
.AddInParameter("@p0", 100)
|
|
187
|
-
.AddInParameter("@p1", toId)
|
|
188
|
-
.ExecuteNonQuery();
|
|
183
|
+
V8.DbTrans.FromSql('UPDATE Account SET Balance = Balance - @p0 WHERE Id = @p1')
|
|
184
|
+
.AddInParameter("@p0", 100)
|
|
185
|
+
.AddInParameter("@p1", fromId)
|
|
186
|
+
.ExecuteNonQuery();
|
|
187
|
+
V8.DbTrans.FromSql('UPDATE Account SET Balance = Balance + @p0 WHERE Id = @p1')
|
|
188
|
+
.AddInParameter("@p0", 100)
|
|
189
|
+
.AddInParameter("@p1", toId)
|
|
190
|
+
.ExecuteNonQuery();
|
|
189
191
|
|
|
190
192
|
// V8.DbTrans 可传给 FormEngine 和 ApiEngine.Run 共享事务
|
|
191
193
|
V8.FormEngine.UptFormData('Table1', { Id: 'x', Status: 1 }, V8.DbTrans);
|
|
@@ -196,16 +198,16 @@ V8.ApiEngine.Run('other-engine', { Id: 'x' }, V8.DbTrans);
|
|
|
196
198
|
|
|
197
199
|
```javascript
|
|
198
200
|
// 扩展数据库需要手动管理事务
|
|
199
|
-
var exTrans = V8.Dbs.OracleDB1.BeginTransaction();
|
|
200
|
-
try {
|
|
201
|
-
exTrans.FromSql('UPDATE t1 SET a = @p0 WHERE Id = @p1')
|
|
202
|
-
.AddInParameter("@p0", 1)
|
|
203
|
-
.AddInParameter("@p1", id1)
|
|
204
|
-
.ExecuteNonQuery();
|
|
205
|
-
exTrans.FromSql('UPDATE t2 SET b = @p0 WHERE Id = @p1')
|
|
206
|
-
.AddInParameter("@p0", 2)
|
|
207
|
-
.AddInParameter("@p1", id2)
|
|
208
|
-
.ExecuteNonQuery();
|
|
201
|
+
var exTrans = V8.Dbs.OracleDB1.BeginTransaction();
|
|
202
|
+
try {
|
|
203
|
+
exTrans.FromSql('UPDATE t1 SET a = @p0 WHERE Id = @p1')
|
|
204
|
+
.AddInParameter("@p0", 1)
|
|
205
|
+
.AddInParameter("@p1", id1)
|
|
206
|
+
.ExecuteNonQuery();
|
|
207
|
+
exTrans.FromSql('UPDATE t2 SET b = @p0 WHERE Id = @p1')
|
|
208
|
+
.AddInParameter("@p0", 2)
|
|
209
|
+
.AddInParameter("@p1", id2)
|
|
210
|
+
.ExecuteNonQuery();
|
|
209
211
|
exTrans.Commit();
|
|
210
212
|
} catch (ex) {
|
|
211
213
|
exTrans.Rollback();
|
|
@@ -224,11 +226,11 @@ V8.Db.FromSql(sql).ToArray(); // SQL 注入漏洞!
|
|
|
224
226
|
// ❌ 禁止:动态拼接表名
|
|
225
227
|
var sql = "SELECT * FROM " + V8.Param.tableName + " WHERE Id = @p0";
|
|
226
228
|
|
|
227
|
-
// ✅ 正确做法:单表查询优先使用 FormEngine + _Where
|
|
228
|
-
var result = V8.FormEngine.GetTableData('SysUser', {
|
|
229
|
-
_Where: [['Account', '=', V8.Param.account]],
|
|
230
|
-
_PageSize: 20
|
|
231
|
-
});
|
|
229
|
+
// ✅ 正确做法:单表查询优先使用 FormEngine + _Where
|
|
230
|
+
var result = V8.FormEngine.GetTableData('SysUser', {
|
|
231
|
+
_Where: [['Account', '=', V8.Param.account]],
|
|
232
|
+
_PageSize: 20
|
|
233
|
+
});
|
|
232
234
|
```
|
|
233
235
|
|
|
234
236
|
## 常见查询模式
|
|
@@ -271,21 +273,21 @@ var result = V8.FormEngine.GetTableData('Customer', {
|
|
|
271
273
|
### 关联查询(SQL JOIN)
|
|
272
274
|
|
|
273
275
|
```javascript
|
|
274
|
-
var list = V8.Db.FromSql(`
|
|
275
|
-
SELECT o.Id, o.OrderNo, o.TotalAmount, c.Name AS CustomerName
|
|
276
|
-
FROM OrderHeader o
|
|
277
|
-
INNER JOIN Customer c ON o.CustomerId = c.Id
|
|
278
|
-
WHERE o.Status = @p0 AND o.CreateTime >= @p1
|
|
279
|
-
ORDER BY o.CreateTime DESC
|
|
280
|
-
`).AddInParameter("@p0", 1)
|
|
281
|
-
.AddInParameter("@p1", V8.Param.startDate)
|
|
282
|
-
.ToArray();
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
## 注意事项
|
|
286
|
-
|
|
287
|
-
- `V8.Db.FromSql` 只传 SQL 字符串,参数占位符从 `@p0` 开始递增,动态值用 `.AddInParameter("@p0", value)` 绑定
|
|
288
|
-
- 服务端 `V8.FormEngine` 操作默认不触发表单 V8 事件;确需触发时在参数中加 `_InvokeType: 'Client'`
|
|
276
|
+
var list = V8.Db.FromSql(`
|
|
277
|
+
SELECT o.Id, o.OrderNo, o.TotalAmount, c.Name AS CustomerName
|
|
278
|
+
FROM OrderHeader o
|
|
279
|
+
INNER JOIN Customer c ON o.CustomerId = c.Id
|
|
280
|
+
WHERE o.Status = @p0 AND o.CreateTime >= @p1
|
|
281
|
+
ORDER BY o.CreateTime DESC
|
|
282
|
+
`).AddInParameter("@p0", 1)
|
|
283
|
+
.AddInParameter("@p1", V8.Param.startDate)
|
|
284
|
+
.ToArray();
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## 注意事项
|
|
288
|
+
|
|
289
|
+
- `V8.Db.FromSql` 只传 SQL 字符串,参数占位符从 `@p0` 开始递增,动态值用 `.AddInParameter("@p0", value)` 绑定
|
|
290
|
+
- 服务端 `V8.FormEngine` 操作默认不触发表单 V8 事件;确需触发时在参数中加 `_InvokeType: 'Client'`
|
|
289
291
|
- 查询结果数量较大时务必分页,`_PageSize` 默认最大 1000
|
|
290
292
|
- `V8.DbRead` 适用于不需要实时性的报表查询
|
|
291
293
|
- 接口引擎的事务由平台自动管理,**不要手动调用** `V8.DbTrans.Commit/Rollback`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: v8-table-event
|
|
3
|
-
description: Microi V8 表单事件开发。用于编写 InFormV8、SubmitFormV8、SubmitBeforeServerV8、SubmitAfterServerV8、OutFormV8、DataFilterV8 和事务感知事件。
|
|
3
|
+
description: Microi V8 表单事件开发。用于编写 InFormV8、SubmitFormV8、SubmitBeforeServerV8、SubmitAfterServerV8、OutFormV8、DataFilterV8 和事务感知事件。
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
> **Codex 非阻塞自动更新:** 当前宿主为 Codex 时,吾码 CLI、Codex 插件与工作区 AI/MCP 由后台自动更新;需要诊断时读取 `../microi-codex-installer/SKILL.md`。更新失败、等待空闲或尚未重载均不得阻断当前、正在进行或新建任务。非 Codex 宿主跳过此项。
|
|
@@ -13,29 +13,29 @@ description: Microi V8 表单事件开发。用于编写 InFormV8、SubmitFormV8
|
|
|
13
13
|
<!-- microi-progressive:chunk id=v8-table-event-000 sha256=dd86294c8cf42f580b2eb806d981c239c0efc2934505c0716e804de2f8a9235c -->
|
|
14
14
|
## 本地优先与版本头(必做)
|
|
15
15
|
|
|
16
|
-
AI 本地开发表单 V8 事件时,优先修改 `microi-v8-engine/<租户>/<项目>/表单引擎/.../<事件Label>(<EventType>).js` 本地文件,再通过 MCP 或 VS Code 插件同步到数据库。文件中文名必须取 `diy_table` 对应 `diy_field.Label`,其中 `SubmitFormV8` 为 `前端表单提交前V8事件(SubmitFormV8).js`,`OutFormV8` 为 `前端表单提交后V8事件(OutFormV8).js`。若插件显示“本地和远端不一致”,先读取本地与远端并合并有效差异,不能直接覆盖。
|
|
16
|
+
AI 本地开发表单 V8 事件时,优先修改 `microi-v8-engine/<租户>/<项目>/表单引擎/.../<事件Label>(<EventType>).js` 本地文件,再通过 MCP 或 VS Code 插件同步到数据库。文件中文名必须取 `diy_table` 对应 `diy_field.Label`,其中 `SubmitFormV8` 为 `前端表单提交前V8事件(SubmitFormV8).js`,`OutFormV8` 为 `前端表单提交后V8事件(OutFormV8).js`。若插件显示“本地和远端不一致”,先读取本地与远端并合并有效差异,不能直接覆盖。
|
|
17
17
|
|
|
18
18
|
每一次修改、上传、推送 `InFormV8.js`、`SubmitFormV8.js`、`SubmitBeforeServerV8.js`、`SubmitAfterServerV8.js`、`OutFormV8.js`、`DataFilterV8.js` 等事件文件,都必须维护顶部版本区域。版本号从 `v1.0.0` 开始;每次上传/推送/修改递增 1;补丁位和次版本位最大为 9 并向前进位(`v1.0.9 -> v1.1.0`、`v1.9.9 -> v2.0.0`、`v9.9.9 -> v10.0.0`)。代码头只写完整功能说明,不写修改历史、时间戳或 ChangeLog。
|
|
19
19
|
|
|
20
20
|
```javascript
|
|
21
21
|
/*
|
|
22
22
|
* V8 Event
|
|
23
|
-
* TableKey: 示例表Key
|
|
23
|
+
* TableKey: 示例表Key
|
|
24
24
|
* EventType: SubmitBeforeServerV8
|
|
25
25
|
* Version: v1.0.0
|
|
26
|
-
* 功能说明:
|
|
26
|
+
* 功能说明:
|
|
27
27
|
* - 完整说明该事件的触发时机、校验/加工逻辑、读写字段和阻止提交条件。
|
|
28
28
|
*/
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
同步流程:`读取远端 -> 修改本地并递增语义版本头 -> JS 语法检查 -> 保存远端 -> 回读远端确认版本头一致 -> 查询 mic_data_version 确认形成新版本 -> 执行触发该事件的最小表单操作验证`。表单设计器保存和 MCP 保存共用服务端代码版本链路;只有代码真实变化才新增版本,普通布局/字段属性保存不得产生代码版本。修改记录不得写进事件代码头。
|
|
32
|
-
|
|
33
|
-
使用 MCP 时,先调用 `microi_list_events` 发现事件,再用
|
|
34
|
-
`microi_get_event_code` 读取目标源码;修改后通过 `microi_save_event_code`
|
|
35
|
-
保存并回读。不要把接口引擎的保存工具用于表单事件,也不要在未读取远端
|
|
36
|
-
版本时直接覆盖。
|
|
37
|
-
|
|
38
|
-
生成 V8 事件代码时,代码内容本身(文件头、普通注释、`console.log`、返回 `Msg` 等)不要包含 `Microi`、`吾码` 等平台品牌文字,除非业务数据或字段值本身必须如此。生成代码要有可维护注释:每个 `function` 前写清用途、关键参数和返回值;提交前校验、提交后联动、字段显隐、数据脱敏、跨表写入、复杂条件判断等代码段前写短注释说明业务原因;避免“给变量赋值”这类无信息量注释。
|
|
31
|
+
同步流程:`读取远端 -> 修改本地并递增语义版本头 -> JS 语法检查 -> 保存远端 -> 回读远端确认版本头一致 -> 查询 mic_data_version 确认形成新版本 -> 执行触发该事件的最小表单操作验证`。表单设计器保存和 MCP 保存共用服务端代码版本链路;只有代码真实变化才新增版本,普通布局/字段属性保存不得产生代码版本。修改记录不得写进事件代码头。
|
|
32
|
+
|
|
33
|
+
使用 MCP 时,先调用 `microi_list_events` 发现事件,再用
|
|
34
|
+
`microi_get_event_code` 读取目标源码;修改后通过 `microi_save_event_code`
|
|
35
|
+
保存并回读。不要把接口引擎的保存工具用于表单事件,也不要在未读取远端
|
|
36
|
+
版本时直接覆盖。
|
|
37
|
+
|
|
38
|
+
生成 V8 事件代码时,代码内容本身(文件头、普通注释、`console.log`、返回 `Msg` 等)不要包含 `Microi`、`吾码` 等平台品牌文字,除非业务数据或字段值本身必须如此。生成代码要有可维护注释:每个 `function` 前写清用途、关键参数和返回值;提交前校验、提交后联动、字段显隐、数据脱敏、跨表写入、复杂条件判断等代码段前写短注释说明业务原因;避免“给变量赋值”这类无信息量注释。
|
|
39
39
|
|
|
40
40
|
<!-- /microi-progressive:chunk -->
|
|
41
41
|
<!-- microi-progressive:chunk id=v8-table-event-001 sha256=7bad55ca6781e6ca458a0333a47c71a3bad30334d146e940496c45c4ffa7ddf5 -->
|
|
@@ -57,8 +57,8 @@ AI 本地开发表单 V8 事件时,优先修改 `microi-v8-engine/<租户>/<
|
|
|
57
57
|
- 后端 V8 事件 / 接口引擎中调用 `V8.FormEngine` 增删改 → **不触发**表单 V8 事件
|
|
58
58
|
- 传入 `_InvokeType: 'Client'` → **触发**表单 V8 事件
|
|
59
59
|
- Postman 等直接调用接口 → 前端事件**不执行**,后端事件**仍执行**
|
|
60
|
-
- 服务器端提交前/后 V8 事件在**同一事务**中执行
|
|
61
|
-
- `diy_table.V8Limit` 是表后端提交前、提交后和数据处理 V8 的正向开关:缺失、`null`、`0/false` 均不设置 Jint 单次超时、语句、函数递归和累计分配预算,只有 `1/true` 才启用这些限制。旧 `V8Unlimited` 仅在新字段不存在时反向推断,新的 MCP、Manifest 和应用资源只写 `V8Limit`。进程常驻内存、取消、并发、接口嵌套深度、权限和数据库保护始终生效。
|
|
60
|
+
- 服务器端提交前/后 V8 事件在**同一事务**中执行
|
|
61
|
+
- `diy_table.V8Limit` 是表后端提交前、提交后和数据处理 V8 的正向开关:缺失、`null`、`0/false` 均不设置 Jint 单次超时、语句、函数递归和累计分配预算,只有 `1/true` 才启用这些限制。旧 `V8Unlimited` 仅在新字段不存在时反向推断,新的 MCP、Manifest 和应用资源只写 `V8Limit`。进程常驻内存、取消、并发、接口嵌套深度、权限和数据库保护始终生效。
|
|
62
62
|
|
|
63
63
|
<!-- /microi-progressive:chunk -->
|
|
64
64
|
<!-- microi-progressive:chunk id=v8-table-event-003 sha256=65a1e728982d4e534d06dc2440eac111fe53a08f0a3012c6a9f8b1de45b4b81f -->
|
|
@@ -75,7 +75,7 @@ if (V8.LoadMode === 'Design') return;
|
|
|
75
75
|
|
|
76
76
|
- ❌ **禁止** 在 `SubmitFormV8.js` 中调 `V8.FormSubmit()` —— 无限递归
|
|
77
77
|
- ❌ **禁止** 在 `FieldValueChange` 中 `V8.FormSet(同字段, ...)` —— 循环触发
|
|
78
|
-
- ❌ **禁止** 在后端提交前/后事件中再次写当前表;即使默认 Server 调用不递归,也可能覆盖本次增量数据、引发死锁或重复副作用。确需跨表联动时使用同一 `V8.DbTrans`,且不要传 `_InvokeType:'Client'`
|
|
78
|
+
- ❌ **禁止** 在后端提交前/后事件中再次写当前表;即使默认 Server 调用不递归,也可能覆盖本次增量数据、引发死锁或重复副作用。确需跨表联动时使用同一 `V8.DbTrans`,且不要传 `_InvokeType:'Client'`
|
|
79
79
|
|
|
80
80
|
### 3. 阻止提交(后端)
|
|
81
81
|
|
|
@@ -96,10 +96,17 @@ V8.ApiEngine.Run('other-engine', { Form: V8.Form }, V8.DbTrans);
|
|
|
96
96
|
// 不传 V8.DbTrans 会导致并行事务、可能死锁或脏读
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
-
### 5. 模板引擎与 DataFilterV8 的区别
|
|
99
|
+
### 5. 模板引擎与 DataFilterV8 的区别
|
|
100
100
|
|
|
101
101
|
- 数据**加工**(计算字段、脱敏、查关联表名)→ 用 `DataFilterV8`(后端,每行执行,可用 `V8.CacheData` 防 N+1)
|
|
102
|
-
- 数据**渲染**(颜色徽章、HTML、图片)→ 用【表格 V8 模板引擎】,详见 `v8-template-engine/SKILL.md`
|
|
102
|
+
- 数据**渲染**(颜色徽章、HTML、图片)→ 用【表格 V8 模板引擎】,详见 `v8-template-engine/SKILL.md`
|
|
103
|
+
|
|
104
|
+
### 6. 新增主键与跨表引用
|
|
105
|
+
|
|
106
|
+
- 原生新增管线先独立确定真实主键;`SubmitBeforeServerV8` 中的 `V8.Form.Id` 可能尚未提供。不得自行给 `V8.Form.Id` 补一个 GUID,并把它当作最终插入主键去创建条码、明细、关系或审计。
|
|
107
|
+
- 依赖最终主键的附属记录放在 `SubmitAfterServerV8`,核验 `V8.Form.Id` 后在同一 `V8.DbTrans` 中回读主记录,再创建附属记录。After 仍在提交前,任一附属写入失败必须返回失败并回滚主表;外部消息仍走 outbox。
|
|
108
|
+
- 原生删除事件的权威删除前记录由 `V8.Form` 提供,`V8.OldForm` 可能为空;删除审计、关系清理应按 `FormSubmitAction` 选择上下文。原生删除的 `Form.Version` 来自数据库快照,不能把它当作客户端最后看到的版本。需要拒绝过期删除时,通过受控接口接收期望版本、锁定主库记录并验证,再在同一事务执行删除。
|
|
109
|
+
- 回归必须比较“新增接口返回 Id、主表实际 Id、附属外键、审计 RowId”四者一致,并覆盖唯一冲突时主表及附属记录同时回滚,不能只断言新增接口 `Code=1`。
|
|
103
110
|
|
|
104
111
|
---
|
|
105
112
|
|
|
@@ -108,8 +115,8 @@ V8.ApiEngine.Run('other-engine', { Form: V8.Form }, V8.DbTrans);
|
|
|
108
115
|
## 前端事件特有 API
|
|
109
116
|
|
|
110
117
|
```javascript
|
|
111
|
-
// 普通 diy-form 中设置字段值(触发目标字段值变更事件)
|
|
112
|
-
// diy-table 列表上下文只更新当前行和模板,不递归触发目标字段 V8
|
|
118
|
+
// 普通 diy-form 中设置字段值(触发目标字段值变更事件)
|
|
119
|
+
// diy-table 列表上下文只更新当前行和模板,不递归触发目标字段 V8
|
|
113
120
|
V8.FormSet('FieldName', 'value');
|
|
114
121
|
V8.FormSet('DropdownField', { Id: 1, Name: '选项' }); // 下拉框
|
|
115
122
|
|
|
@@ -127,9 +134,9 @@ var isReadonly = V8.Field.UserName.Readonly;
|
|
|
127
134
|
V8.Tips('操作成功', true); // 成功提示(1秒消失)
|
|
128
135
|
V8.Tips('操作失败', false); // 错误提示(5秒消失)
|
|
129
136
|
|
|
130
|
-
// 确认框
|
|
131
|
-
V8.ConfirmTips('确定删除?', function() { /* 确定 */ }, function() { /* 取消 */ });
|
|
132
|
-
// content 使用 HTML 模式渲染;只能传固定文案或已转义内容,复杂交互用 OpenAppDialog
|
|
137
|
+
// 确认框
|
|
138
|
+
V8.ConfirmTips('确定删除?', function() { /* 确定 */ }, function() { /* 取消 */ });
|
|
139
|
+
// content 使用 HTML 模式渲染;只能传固定文案或已转义内容,复杂交互用 OpenAppDialog
|
|
133
140
|
|
|
134
141
|
// 前端 HTTP 请求
|
|
135
142
|
V8.Post('/api/xxx', { key: 'value' }, function(res) { });
|
|
@@ -104,13 +104,13 @@ if (V8.FormSubmitAction === 'Insert' || V8.FormSubmitAction === 'Update') {
|
|
|
104
104
|
// 此时数据已成功写入数据库(仍在事务中)
|
|
105
105
|
// 返回 { Code: 0 } 仍可回滚事务
|
|
106
106
|
|
|
107
|
-
// 新增后:自动创建关联数据。保持默认 Server 调用,不触发目标表事件
|
|
108
|
-
if (V8.FormSubmitAction === 'Insert') {
|
|
109
|
-
V8.FormEngine.AddFormData('UserProfile', {
|
|
110
|
-
UserId: V8.Form.Id,
|
|
111
|
-
NickName: V8.Form.Name
|
|
112
|
-
}, V8.DbTrans);
|
|
113
|
-
}
|
|
107
|
+
// 新增后:自动创建关联数据。保持默认 Server 调用,不触发目标表事件
|
|
108
|
+
if (V8.FormSubmitAction === 'Insert') {
|
|
109
|
+
V8.FormEngine.AddFormData('UserProfile', {
|
|
110
|
+
UserId: V8.Form.Id,
|
|
111
|
+
NickName: V8.Form.Name
|
|
112
|
+
}, V8.DbTrans);
|
|
113
|
+
}
|
|
114
114
|
|
|
115
115
|
// 更新后:同步更新其它表的冗余字段
|
|
116
116
|
if (V8.FormSubmitAction === 'Update') {
|
|
@@ -129,14 +129,14 @@ V8.ApiEngine.Run('send-notification', {
|
|
|
129
129
|
}, V8.DbTrans);
|
|
130
130
|
|
|
131
131
|
// 记录操作日志
|
|
132
|
-
V8.Method.AddSysLog({
|
|
132
|
+
V8.Method.AddSysLog({
|
|
133
133
|
Title: V8.FormSubmitAction + ' ' + V8.TableModel.Name,
|
|
134
134
|
Content: JSON.stringify({ Id: V8.Form.Id }),
|
|
135
135
|
Type: '业务日志'
|
|
136
|
-
});
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
`SubmitAfterServerV8` 的“After”仍是“写入后、提交前”。需要在事务真正提交后才发布的缓存版本、跨节点通知等副作用,不能直接在事件中执行。平台为 `microi_database` 提供专用 `V8.Method.RefreshExtensionDatabases()`:事件调用时只登记提交后回调,提交成功才递增当前租户共享 Redis 版本,回滚时自动丢弃。普通业务的外部消息仍优先使用同事务 outbox,不能把任意不可撤销副作用都塞进内存回调。
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
`SubmitAfterServerV8` 的“After”仍是“写入后、提交前”。需要在事务真正提交后才发布的缓存版本、跨节点通知等副作用,不能直接在事件中执行。平台为 `microi_database` 提供专用 `V8.Method.RefreshExtensionDatabases()`:事件调用时只登记提交后回调,提交成功才递增当前租户共享 Redis 版本,回滚时自动丢弃。普通业务的外部消息仍优先使用同事务 outbox,不能把任意不可撤销副作用都塞进内存回调。
|
|
140
140
|
|
|
141
141
|
<!-- /microi-progressive:chunk -->
|
|
142
142
|
<!-- microi-progressive:chunk id=v8-table-event-009 sha256=d415302a8108d9e11755a046bdbffe0c84a135ec6ed0e6b1dc3fc128a8ef38b6 -->
|
|
@@ -181,21 +181,21 @@ if (V8.Form.Phone) {
|
|
|
181
181
|
|
|
182
182
|
| 变量 | 说明 | 可用事件 |
|
|
183
183
|
|------|------|---------|
|
|
184
|
-
| `V8.Form` |
|
|
185
|
-
| `V8.OldForm` | 已加载的修改前旧数据 | 普通表单;提交前/后 |
|
|
184
|
+
| `V8.Form` | 当前表单数据;原生新增提交前可能没有 Id,依赖真实主键的跨表写入在提交后事件执行 | 全部 |
|
|
185
|
+
| `V8.OldForm` | 已加载的修改前旧数据 | 普通表单;提交前/后 |
|
|
186
186
|
| `V8.FormMode` | `'Add'` / `'Edit'` / `'View'` | 全部 |
|
|
187
187
|
| `V8.FormOutAction` | `'Insert'`/`'Update'`/`'Close'`/`'Delete'` | FormOut |
|
|
188
188
|
| `V8.FormSubmitAction` | `'Insert'` / `'Update'` / `'Delete'` | SubmitBefore |
|
|
189
189
|
| `V8.EventName` | 当前事件名 | 全部 |
|
|
190
190
|
| `V8.CurrentUser` | 当前用户 | 全部 |
|
|
191
191
|
| `V8.TableId` / `V8.TableName` | 当前表 Id / Name | 全部 |
|
|
192
|
-
| `V8.SelectedData` / `V8.TableRowSelected` | 选中的行数组 | 列表/批量按钮 |
|
|
192
|
+
| `V8.SelectedData` / `V8.TableRowSelected` | 选中的行数组 | 列表/批量按钮 |
|
|
193
193
|
| `V8.CurrentTableData` | 当前表当页数据 | 全部 |
|
|
194
194
|
| `V8.ClientType` | `'PC'`/`'IOS'`/`'Android'`/`'H5'`/`'WeChat'` | 全部 |
|
|
195
|
-
| `V8.ThisValue` | 当前字段新值:可能是对象、原始值或行内 `{New,Old}` | FieldValueChange |
|
|
196
|
-
| `V8.OldValue` | 当前字段旧值,仅行内编辑可靠 | 表格行内 FieldValueChange |
|
|
197
|
-
| `V8.KeyCode` | 键盘事件的键码 | FieldOnKeyup |
|
|
198
|
-
| `V8.Event` | 原生事件;键盘事件目前不提供 | FieldSlotButtonClick 等显式事件 |
|
|
195
|
+
| `V8.ThisValue` | 当前字段新值:可能是对象、原始值或行内 `{New,Old}` | FieldValueChange |
|
|
196
|
+
| `V8.OldValue` | 当前字段旧值,仅行内编辑可靠 | 表格行内 FieldValueChange |
|
|
197
|
+
| `V8.KeyCode` | 键盘事件的键码 | FieldOnKeyup |
|
|
198
|
+
| `V8.Event` | 原生事件;键盘事件目前不提供 | FieldSlotButtonClick 等显式事件 |
|
|
199
199
|
| `V8.ParentV8` | 子表中访问父表 V8 对象 | 子表事件 |
|
|
200
200
|
|
|
201
201
|
### 后端事件
|