@microi.net/cli 4.6.2

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 (112) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +66 -0
  3. package/dist/mcp-codex-stdio-adapter.js +189 -0
  4. package/dist/mcp-server.js +972 -0
  5. package/dist/mcp-trae-windows-launcher.cmd +21 -0
  6. package/dist/microi-cli-mcp.js +7 -0
  7. package/dist/microi-cli.js +1645 -0
  8. package/dist/microi-skills.meta.json +335 -0
  9. package/dist/microi.skills/.microi-skills-version.json +6 -0
  10. package/dist/microi.skills/README.md +276 -0
  11. package/dist/microi.skills/ai-engine/SKILL.md +140 -0
  12. package/dist/microi.skills/ai-engine/agents/openai.yaml +4 -0
  13. package/dist/microi.skills/app-store/SKILL.md +105 -0
  14. package/dist/microi.skills/app-store/agents/openai.yaml +4 -0
  15. package/dist/microi.skills/business-blueprint/SKILL.md +184 -0
  16. package/dist/microi.skills/datasource-engine/SKILL.md +89 -0
  17. package/dist/microi.skills/datasource-engine/agents/openai.yaml +4 -0
  18. package/dist/microi.skills/dos-orm/SKILL.md +76 -0
  19. package/dist/microi.skills/dos-orm/references/api-reference.md +229 -0
  20. package/dist/microi.skills/job-engine/SKILL.md +141 -0
  21. package/dist/microi.skills/job-engine/agents/openai.yaml +4 -0
  22. package/dist/microi.skills/message-notification/SKILL.md +113 -0
  23. package/dist/microi.skills/message-notification/agents/openai.yaml +6 -0
  24. package/dist/microi.skills/message-notification/references/contracts.md +99 -0
  25. package/dist/microi.skills/microi-ai-app-auth.js +651 -0
  26. package/dist/microi.skills/microi-ai-application/SKILL.md +80 -0
  27. package/dist/microi.skills/microi-ai-application/agents/openai.yaml +4 -0
  28. package/dist/microi.skills/microi-ai-application/references/frontend-baseline.md +164 -0
  29. package/dist/microi.skills/microi-client-frontend/SKILL.md +562 -0
  30. package/dist/microi.skills/microi-datasource-mapping/SKILL.md +108 -0
  31. package/dist/microi.skills/microi-db-schema/SKILL.md +170 -0
  32. package/dist/microi.skills/microi-db-schema/agents/openai.yaml +4 -0
  33. package/dist/microi.skills/microi-db-schema/references/core-tables.md +695 -0
  34. package/dist/microi.skills/microi-db-schema/references/form-component-options.md +256 -0
  35. package/dist/microi.skills/microi-db-schema/references/schema-overview.md +203 -0
  36. package/dist/microi.skills/microi-db-schema/references/schema.md +647 -0
  37. package/dist/microi.skills/microi-db-schema/references/table-catalog.md +1607 -0
  38. package/dist/microi.skills/microi-deployment/SKILL.md +117 -0
  39. package/dist/microi.skills/microi-deployment/references/deployment-matrix.md +94 -0
  40. package/dist/microi.skills/microi-docs-coverage/SKILL.md +91 -0
  41. package/dist/microi.skills/microi-docs-coverage/references/capability-map.md +65 -0
  42. package/dist/microi.skills/microi-docs-coverage/scripts/audit-doc-skill-coverage.mjs +887 -0
  43. package/dist/microi.skills/microi-form-engine/SKILL.md +159 -0
  44. package/dist/microi.skills/microi-form-engine/references/component-catalog.md +116 -0
  45. package/dist/microi.skills/microi-form-engine/references/data-source-events.md +117 -0
  46. package/dist/microi.skills/microi-form-layout/SKILL.md +373 -0
  47. package/dist/microi.skills/microi-frontend-sdk/SKILL.md +304 -0
  48. package/dist/microi.skills/microi-left-right-layout/SKILL.md +132 -0
  49. package/dist/microi.skills/microi-microservice/SKILL.md +115 -0
  50. package/dist/microi.skills/microi-microservice/references/runtime-delivery.md +145 -0
  51. package/dist/microi.skills/microi-mobile-app-quality/SKILL.md +436 -0
  52. package/dist/microi.skills/microi-solution-quotation/SKILL.md +76 -0
  53. package/dist/microi.skills/microi-solution-quotation/agents/openai.yaml +4 -0
  54. package/dist/microi.skills/microi-solution-quotation/scripts/build_solution_quote.py +296 -0
  55. package/dist/microi.skills/microi-system-delivery/SKILL.md +446 -0
  56. package/dist/microi.skills/microi-ui/SKILL.md +321 -0
  57. package/dist/microi.skills/microi-uniapp-frontend/SKILL.md +483 -0
  58. package/dist/microi.skills/microi.v8.js +1758 -0
  59. package/dist/microi.skills/module-engine/SKILL.md +131 -0
  60. package/dist/microi.skills/module-engine/references/module-config.md +174 -0
  61. package/dist/microi.skills/page-engine/SKILL.md +397 -0
  62. package/dist/microi.skills/performance-testing/SKILL.md +207 -0
  63. package/dist/microi.skills/playwright-e2e/SKILL.md +769 -0
  64. package/dist/microi.skills/print-engine/SKILL.md +237 -0
  65. package/dist/microi.skills/production-readonly-audit/SKILL.md +39 -0
  66. package/dist/microi.skills/report-engine/SKILL.md +69 -0
  67. package/dist/microi.skills/report-engine/agents/openai.yaml +4 -0
  68. package/dist/microi.skills/search-engine/SKILL.md +73 -0
  69. package/dist/microi.skills/search-engine/agents/openai.yaml +4 -0
  70. package/dist/microi.skills/spider-engine/SKILL.md +188 -0
  71. package/dist/microi.skills/translate-engine/SKILL.md +91 -0
  72. package/dist/microi.skills/translate-engine/agents/openai.yaml +4 -0
  73. package/dist/microi.skills/ui-design/SKILL.md +1575 -0
  74. package/dist/microi.skills/ui-design/assets/pattern-showcase/app.js +54 -0
  75. package/dist/microi.skills/ui-design/assets/pattern-showcase/index.html +163 -0
  76. package/dist/microi.skills/ui-design/assets/pattern-showcase/styles.css +311 -0
  77. package/dist/microi.skills/ui-design/assets/templates/MCI-DESIGN.md +98 -0
  78. package/dist/microi.skills/ui-design/references/design-pattern-library.md +171 -0
  79. package/dist/microi.skills/ui-design/references/mci-design-contract.md +84 -0
  80. package/dist/microi.skills/ui-design/references/motion-and-media.md +71 -0
  81. package/dist/microi.skills/ui-design/references/product-flow-recipes.md +94 -0
  82. package/dist/microi.skills/uniapp-mall-assets/SKILL.md +105 -0
  83. package/dist/microi.skills/v8-api-config/SKILL.md +272 -0
  84. package/dist/microi.skills/v8-cache-pattern/SKILL.md +286 -0
  85. package/dist/microi.skills/v8-crud-api/SKILL.md +398 -0
  86. package/dist/microi.skills/v8-debugging/SKILL.md +279 -0
  87. package/dist/microi.skills/v8-explorer-tree/SKILL.md +224 -0
  88. package/dist/microi.skills/v8-export-import/SKILL.md +590 -0
  89. package/dist/microi.skills/v8-file-upload/SKILL.md +497 -0
  90. package/dist/microi.skills/v8-formengine-http/SKILL.md +218 -0
  91. package/dist/microi.skills/v8-frontend-events/SKILL.md +349 -0
  92. package/dist/microi.skills/v8-frontend-events/references/bluetooth-print-api.md +107 -0
  93. package/dist/microi.skills/v8-frontend-events/references/bluetooth-print.md +185 -0
  94. package/dist/microi.skills/v8-http-integration/SKILL.md +379 -0
  95. package/dist/microi.skills/v8-image-processing/SKILL.md +187 -0
  96. package/dist/microi.skills/v8-image-processing/agents/openai.yaml +4 -0
  97. package/dist/microi.skills/v8-image-processing/references/api-reference.md +620 -0
  98. package/dist/microi.skills/v8-menu-buttons/SKILL.md +661 -0
  99. package/dist/microi.skills/v8-mongodb/SKILL.md +149 -0
  100. package/dist/microi.skills/v8-mq-mqtt/SKILL.md +227 -0
  101. package/dist/microi.skills/v8-saas-multi-tenant/SKILL.md +193 -0
  102. package/dist/microi.skills/v8-security/SKILL.md +417 -0
  103. package/dist/microi.skills/v8-sql-query/SKILL.md +290 -0
  104. package/dist/microi.skills/v8-table-event/SKILL.md +385 -0
  105. package/dist/microi.skills/v8-template-engine/SKILL.md +165 -0
  106. package/dist/microi.skills/v8-utilities/SKILL.md +79 -0
  107. package/dist/microi.skills/v8-utilities/references/client-api-index.md +136 -0
  108. package/dist/microi.skills/v8-utilities/references/platform-http-routes.md +80 -0
  109. package/dist/microi.skills/v8-utilities/references/server-api-index.md +129 -0
  110. package/dist/microi.skills/v8-workflow/SKILL.md +322 -0
  111. package/dist/microi.skills/workspace-conventions/SKILL.md +479 -0
  112. package/package.json +40 -0
@@ -0,0 +1,218 @@
1
+ ---
2
+ name: v8-formengine-http
3
+ description: 移动端 / 外部系统通过 HTTP 直接调用 Microi FormEngine(GetTableData / GetFormData / Add / Upt / Del)的 RESTful 路由约定与排错指南
4
+ ---
5
+
6
+ # FormEngine HTTP 路由约定(外部系统调用)
7
+
8
+ > 适用于:uni-app H5、原生 App、Postman、第三方系统、Playwright/Cypress 自动化测试等任何**没有进入 V8 引擎**的客户端。
9
+ > 不适用于:服务器端 V8 接口引擎内部 — 那种情况请直接 `V8.FormEngine.GetTableData(...)`。
10
+
11
+ ## ⚠️ 最常见错误
12
+
13
+ ```text
14
+ ❌ POST /formengine/{表名}/gettabledata → 404
15
+ ❌ POST /api/formengine/{表名}/gettabledata → 404
16
+ ✅ POST /api/formengine/gettabledata-{表名} → OK (动态短路由)
17
+ ✅ POST /api/formengine/GetTableData body 中带 FormEngineKey → OK (标准路由)
18
+ ```
19
+
20
+ 平台路由由 `Microi.Server/Microi.net.Api/Handler/DynamicApiEngine.cs` 的 `FormEngineRoutes` 字典决定,**只认上面两种形式**。
21
+
22
+ ## 路由总表
23
+
24
+ ### 形式一:标准 Controller 路由 — 推荐
25
+ 全部为 `POST`,URL 不含表名,`FormEngineKey` 放在 Body 中。
26
+
27
+ | URL | 动作 |
28
+ | --- | --- |
29
+ | `/api/formengine/GetFormData` | 取一条 |
30
+ | `/api/formengine/GetTableData` | 取列表(分页) |
31
+ | `/api/formengine/AddFormData` | 新增 |
32
+ | `/api/formengine/UptFormData` | 按 Id 修改 |
33
+ | `/api/formengine/UptFormDataByWhere` | 按 _Where 批量改 |
34
+ | `/api/formengine/DelFormData` | 删除(Id / Ids) |
35
+ | `/api/formengine/DelFormDataByWhere` | 按 _Where 批量删 |
36
+ | `/api/formengine/GetFormDataAnonymous` | 匿名取一条 |
37
+ | `/api/formengine/GetTableDataAnonymous` | 匿名取列表 |
38
+ | `/api/formengine/AddFormDataAnonymous` | 匿名新增 |
39
+
40
+ ### 形式二:动态短路由(表名写进 URL,URL-friendly)
41
+ | URL 前缀 | 等价于 |
42
+ | --- | --- |
43
+ | `/api/formengine/getformdata-{table}` | `/api/formengine/GetFormData` |
44
+ | `/api/formengine/get-formdata-{table}` | `/api/formengine/GetFormData` |
45
+ | `/api/formengine/gettabledata-{table}` | `/api/formengine/GetTableData` |
46
+ | `/api/formengine/get-tabledata-{table}` | `/api/formengine/GetTableData` |
47
+ | `/api/formengine/addformdata-{table}` | `/api/formengine/AddFormData` |
48
+ | `/api/formengine/add-formdata-{table}` | `/api/formengine/AddFormData` |
49
+ | `/api/formengine/uptformdata-{table}` | `/api/formengine/UptFormData` |
50
+ | `/api/formengine/upt-formdata-{table}` | `/api/formengine/UptFormData` |
51
+ | `/api/formengine/delformdata-{table}` | `/api/formengine/DelFormData` |
52
+ | `/api/formengine/del-formdata-{table}` | `/api/formengine/DelFormData` |
53
+
54
+ URL 中的表名小写最稳,平台不区分大小写。匿名版本目前**只在形式一上有**(`GetTableDataAnonymous` 等)。
55
+
56
+ ## 请求 Header
57
+
58
+ | Header | 是否必填 | 说明 |
59
+ | --- | --- | --- |
60
+ | `Content-Type` | 必填 | `application/json` 推荐 |
61
+ | `OsClient` | 必填 | 租户标识;亦可放 querystring `?OsClient=xxx` 或 body |
62
+ | `authorization` | 鉴权接口必填 | `Bearer <Token>`;兼容旧客户端的 `Token` Header |
63
+ | `did` | 登录及鉴权接口推荐 | 当前终端稳定设备标识;首次生成后持久化,不能每次请求随机变化 |
64
+
65
+ 注意:上表是 FormEngine 路由约定。ApiEngine HTTP 复测和移动端 `callEngine` 使用稳定路径 `/apiengine/{key}`,租户通过唯一的 `osclient` Header 传递,JSON/Form Body 可冗余携带 `OsClient`。普通 POST/PUT/PATCH/DELETE 禁止追加 `--OsClient--...--`;特殊路径只用于无法设置 Header/Form/Query 的 GET/HEAD 或第三方回调。
66
+
67
+ 平台可能通过响应 Header `authorization`(兼容 `token`)续签或替换 Token。客户端必须立即保存新 Token,并保证并发旧响应不能覆盖已经写入的新 Token。跨域部署还必须在 CORS 中暴露 `authorization`、`token` 等需要读取的响应 Header。Token 属于凭据,禁止写入 URL、日志、错误上报和页面源码。
68
+
69
+ ## FormEngine 数据授权边界
70
+
71
+ “Token 有效”只代表身份有效,不代表可以访问任意表。客户端 FormEngine CRUD 还会经过表、菜单、角色和行级范围授权:
72
+
73
+ 1. 受保护的平台敏感表优先拒绝普通用户;匿名接口也不能绕过。
74
+ 2. 标准菜单页应携带真实 `_SysMenuId`,或使用 `ModuleEngineKey`。服务器会严格校验该菜单是否绑定目标表、当前角色是否拥有菜单及对应操作权限;列表、计数、导出追加该菜单的数据范围。
75
+ 3. 列表、写入、导入、导出显式传错 `_SysMenuId` / `ModuleEngineKey` 会直接拒绝,不会降级为其它菜单或表权限。单行详情只要当前角色拥有至少一个直接绑定同表的菜单(或精确表级 `Read` 权限)即可读取,不应用菜单 `SqlWhere` / `SqlJoin`;旧客户端携带过期菜单 Id 时也按该规则恢复。
76
+ 4. 为兼容历史前端 V8/外部客户端,普通 CRUD 未传菜单上下文时,服务器会从当前用户的版本化授权缓存中查找“当前角色已获授权且直接绑定目标表”的菜单。无菜单列表仍安全合并候选菜单的数据范围,不能靠漏参绕过;详情按同表菜单访问,写入按 `Add/Edit/Del` 动作权限。也可以使用角色的精确表级 `Read/Add/Edit/Del` 授权。`JoinTables` 不是独立访问授权。
77
+ 5. 导入、导出必须锚定具体菜单,不能依赖无菜单兼容推断。
78
+ 6. `TableChild` 子表委托由标准表单运行时生成 `_TableChildAuth`,服务器重新校验父菜单、父记录、字段绑定、子表和外键,并强制父子范围。它是内部不透明上下文,外部客户端不要手工拼装。
79
+ 7. `_InvokeType:'Client'` 只决定是否执行客户端语义的表单事件,不是权限开关。可信服务器调用标记也不能通过 HTTP Body 伪造。
80
+
81
+ 因此:
82
+
83
+ - 标准模块、详情页和其字段元数据请求优先传真实 `_SysMenuId`,使服务器确定菜单绑定与操作权限;其中只有列表、计数、导出应用菜单查询范围。
84
+ - 历史无 `_SysMenuId` 请求可继续工作,但前提是当前用户确实拥有目标表对应的菜单或精确表权限;不能把兼容推断理解为“有 Token 即可查表”。
85
+ - 历史单表字段请求可在菜单缺失/过期时回退到当前角色另一个引用同表的已授权菜单;`GetDiyFieldByDiyTables` 批量字段请求按“第一张主表必须授权、后续表逐张授权并过滤”兼容。后续未授权/保护表不会导致主表失败,也不会返回其字段配置;这些规则不授予数据行权限。
86
+ - 表单设计器 `/api/DiyField/UptDiyFieldList` 属于 `Level >= 9999` 控制面:外层一次授权、字段归属校验、同事务批量更新、批次末一次缓存/版本刷新。禁止在 100+ 字段循环中逐条调用完整 `UptFormDataAsync("diy_field", ...)`,否则会重复执行授权、V8、日志及 SaaS/Redis 缓存工作。
87
+ - 接口引擎内的 `V8.FormEngine.*` 属于服务器端调用,不要求客户端 `_SysMenuId`。但接口引擎本身必须正确限制谁能调用,并在服务端校验业务对象范围,不能把表名和条件原样交给不可信客户端。
88
+ - 客户端新增、修改、删除分别校验真实菜单的 `Add`、`Edit`、`Del` 权限。`SqlWhere` / `SqlJoin` 是模块查询过滤,不是行级写权限:不得把它们追加到写入 SQL,也不得因查询包含 Join 拒绝已获授权的主表写入。
89
+ - 进入 `SubmitBeforeServerV8` / `SubmitAfterServerV8` 后,事件内 FormEngine/数据库调用与接口引擎一样属于可信服务器执行,可实现当前租户内的跨表事务。需要“只能修改本人数据”等业务约束以及归属字段写入时,应在这里或专用接口引擎中完成。
90
+
91
+ ## Body 结构(POST JSON)
92
+
93
+ ```jsonc
94
+ {
95
+ "OsClient": "demo",
96
+ "FormEngineKey": "mall_product", // 形式一必填;形式二可选(已在 URL 中)
97
+ "_SysMenuId": "目标菜单Id", // 标准模块推荐;必须是真实绑定目标表且当前角色有权访问的菜单
98
+ "_Where": [["Status","=","OnSale"], ["AND","Stock",">",0]],
99
+ "_SelectFields": ["Id","Title","CurrentPrice","MainImg"],
100
+ "_OrderBy": "SoldCount",
101
+ "_OrderByType": "DESC",
102
+ "_PageIndex": 1,
103
+ "_PageSize": 20
104
+ }
105
+ ```
106
+
107
+ 写操作(Add/Upt)将业务字段平铺到 body:
108
+ ```jsonc
109
+ { "OsClient":"demo", "FormEngineKey":"biz_order",
110
+ "Id":"01ABC...", "Quantity": 2, "Selected": 1 }
111
+ ```
112
+
113
+ ## 响应 DosResult 标准格式
114
+
115
+ ```jsonc
116
+ { "Code": 1, "Data": [...], "DataCount": 123, "Msg": "" }
117
+ ```
118
+ | Code | 含义 |
119
+ | --- | --- |
120
+ | 1 | 成功 |
121
+ | 0 | 业务失败(看 `Msg`) |
122
+ | 1001 | 登录身份已过期 / Token 无效 |
123
+ | 1002 | 身份验证失败(OsClient 与 Token 不匹配) |
124
+
125
+ ## 客户端封装样板(uni-app)
126
+
127
+ ```javascript
128
+ const BASE = 'https://api.itdos.com';
129
+ const OS_CLIENT = runtimeConfig.osClient;
130
+ const TOKEN_KEY = 'mall_token';
131
+ const DID_KEY = 'mall_did';
132
+
133
+ function normalizeToken(value) {
134
+ return String(value || '').replace(/^Bearer\s+/i, '').trim();
135
+ }
136
+ function getToken() {
137
+ return normalizeToken(uni.getStorageSync(TOKEN_KEY));
138
+ }
139
+ function getDid() {
140
+ let did = uni.getStorageSync(DID_KEY);
141
+ if (!did) {
142
+ did = `uni-${Date.now()}-${Math.random().toString(36).slice(2)}`;
143
+ uni.setStorageSync(DID_KEY, did);
144
+ }
145
+ return did;
146
+ }
147
+ function readHeader(headers, name) {
148
+ const key = Object.keys(headers || {}).find(k => k.toLowerCase() === name);
149
+ return key ? headers[key] : '';
150
+ }
151
+ function applyResponseToken(headers, requestToken) {
152
+ const responseToken = normalizeToken(
153
+ readHeader(headers, 'authorization') || readHeader(headers, 'token')
154
+ );
155
+ if (!responseToken) return;
156
+
157
+ const currentToken = getToken();
158
+ // 旧请求若只回显旧 Token,不得覆盖其它请求/标签页已保存的新 Token。
159
+ if (currentToken && requestToken && currentToken !== requestToken && responseToken === requestToken) {
160
+ return;
161
+ }
162
+ uni.setStorageSync(TOKEN_KEY, responseToken);
163
+ }
164
+
165
+ function formEngineRequest(action, table, body = {}) {
166
+ return new Promise((resolve, reject) => {
167
+ const requestToken = getToken();
168
+ uni.request({
169
+ url: `${BASE}/api/formengine/${action}-${table}`,
170
+ method: 'POST',
171
+ header: {
172
+ 'Content-Type': 'application/json',
173
+ 'OsClient': OS_CLIENT,
174
+ 'authorization': requestToken ? `Bearer ${requestToken}` : '',
175
+ 'did': getDid()
176
+ },
177
+ data: { OsClient: OS_CLIENT, FormEngineKey: table, ...body },
178
+ success: (res) => {
179
+ applyResponseToken(res.header || res.headers, requestToken);
180
+ resolve(res.data || {});
181
+ },
182
+ fail: reject
183
+ });
184
+ });
185
+ }
186
+ export const formEngineGet = (t, w) => formEngineRequest('gettabledata', t, w);
187
+ export const formEngineGetOne = (t, w) => formEngineRequest('getformdata', t, w);
188
+ export const formEngineAdd = (t, d) => formEngineRequest('addformdata', t, d);
189
+ export const formEngineUpt = (t, d) => formEngineRequest('uptformdata', t, d);
190
+ export const formEngineDel = (t, d) => formEngineRequest('delformdata', t, d);
191
+ ```
192
+
193
+ ## 排错速查
194
+
195
+ | 现象 | 真实原因 |
196
+ | --- | --- |
197
+ | 404 Not Found | URL 缺 `/api/` 前缀,或表名/动作之间用 `/` 而不是 `-` |
198
+ | 405 Method Not Allowed | 用了 GET(FormEngine 全部为 POST) |
199
+ | 1001 登录身份已过期 | 没传 Token / Token 过期 / Redis 重启 |
200
+ | 1002 身份验证失败 | OsClient 不匹配 |
201
+ | `NoAuth` / `您没有权限做此操作` | Token 有效但菜单、表操作权限、表绑定角色、行级范围或敏感表策略不允许 |
202
+ | 显式传 `_SysMenuId` 后无权限 | 列表/写入使用错误菜单会严格拒绝;唯一详情在当前角色仍拥有另一个同表菜单时可恢复,不应用菜单查询范围 |
203
+ | 无 `_SysMenuId` 仍无权限 | 当前角色没有直接绑定该表的菜单授权,也没有精确表级授权 |
204
+ | 并发后提示 TokenReplaced / MissingToken | 客户端没有接收响应新 Token,或旧请求/其它标签页覆盖或清除了共享新 Token |
205
+ | Code:0 表不存在 | `FormEngineKey` 在 `diy_table` 不存在 |
206
+ | Code:0 字段不存在 | `_Where` / `_SelectFields` 写了表上没有的字段 |
207
+ | 返回 `null` 而不是 DosResult | Controller 抛了异常被吞,到后端日志看 `Microi.Core` 报错 |
208
+
209
+ ## 与服务器端 V8 的对照
210
+
211
+ | 客户端 HTTP 路由 | V8 内等价写法 |
212
+ | --- | --- |
213
+ | POST `/api/formengine/gettabledata-mall_product` | `V8.FormEngine.GetTableData('mall_product', {...})` |
214
+ | POST `/api/formengine/getformdata-mall_member` | `V8.FormEngine.GetFormData('mall_member', {...})` |
215
+ | POST `/api/formengine/uptformdata-mall_shopping_cart` | `V8.FormEngine.UptFormData('mall_shopping_cart', {...})` |
216
+
217
+ > 客户端 HTTP 调用**会**触发 `SubmitBeforeServerV8`、`SubmitAfterServerV8`、`DataFilterV8` 等服务端事件;
218
+ > 而 V8 引擎内调用 `V8.FormEngine.*` 默认**不**触发,除非显式传 `_InvokeType:'Client'`。`_InvokeType` 只影响事件语义,不授予任何客户端表权限。
@@ -0,0 +1,349 @@
1
+ ---
2
+ name: v8-frontend-events
3
+ description: Microi 前端 V8 事件与客户端能力指南。用于编写浏览器端字段、按钮、列表事件,或使用 V8.EventName、V8.Form、V8.Print 蓝牙打印、扫码、弹窗、表单联动和界面交互。
4
+ ---
5
+
6
+ # Microi V8 前端事件大全
7
+
8
+ 你正在为 Microi 吾码平台编写 **前端 V8 事件** 代码。前端事件运行在浏览器,通过 `V8.EventName` 区分事件类型,可访问表单/列表/弹窗等丰富的客户端 API。
9
+
10
+ > **表单生命周期事件**(InFormV8、SubmitFormV8、SubmitBeforeServerV8、SubmitAfterServerV8、OutFormV8、DataFilterV8)见 `v8-table-event/SKILL.md`。
11
+ > **菜单按钮事件**(MoreBtns/FormBtns 等)见 `v8-menu-buttons/SKILL.md`。
12
+ > 本文重点是 **字段事件、按钮事件、列表事件、模板引擎、其它前端钩子**。
13
+
14
+ ## 能力路由
15
+
16
+ - 查询前端 V8 全部上下文、导航、表单、列表、网络、引擎与工具入口时,读取 `../v8-utilities/references/client-api-index.md`。
17
+ - 需求包含“蓝牙打印、标签打印、TSC/TSPL、ESC/POS、小票打印、佳博打印机”时,必须先读取 `references/bluetooth-print.md`;需要完整指令签名、编码或位图参数时,再读取 `references/bluetooth-print-api.md`。
18
+ - 浏览器模板打印、PDF/纸张模板、`mic_print`、`PageObj`、`PrintObj` 使用 `print-engine/SKILL.md`,不要与直接蓝牙指令混为一套 API。
19
+ - 扫码使用 `V8.Method.ScanCode`,结果从 Promise/回调取得;`V8.ScanCodeRes` 只作兼容结果槽,详见客户端 API 索引。
20
+
21
+ ## 字段事件(在【字段属性】中配置)
22
+
23
+ ### FieldValueChange — 值变更事件(最常用)
24
+
25
+ ```javascript
26
+ // V8.EventName === 'FieldValueChange'
27
+ // V8.ThisValue — 当前字段新值
28
+ // V8.OldValue — 仅表格行内字段事件可靠提供;普通表单读取 V8.OldForm
29
+ // V8.Form — 整个表单数据
30
+ // V8.FormMode — 'Add' / 'Edit' / 'View'
31
+ // V8.LoadMode — 'Design' 表示设计器中,'View' 是真实表单
32
+
33
+ // ★ 关键:设计模式下不要执行业务逻辑(防止设计器卡顿)
34
+ if (V8.LoadMode === 'Design') return;
35
+
36
+ // 选择部门 → 联动加载该部门下的人员到 联系人 控件
37
+ var deptId = V8.ThisValue && V8.ThisValue.Id;
38
+ if (deptId) {
39
+ var users = await V8.FormEngine.GetTableData('Diy_Employee', {
40
+ _SelectFields: ['Id', 'Name', 'Account'],
41
+ _Where: [['DeptId', '=', deptId]]
42
+ });
43
+ V8.FieldSet('Contact', 'Data', users.Code === 1 ? users.Data : []);
44
+ } else {
45
+ V8.FieldSet('Contact', 'Data', []);
46
+ }
47
+
48
+ // 联动设置另一字段值
49
+ V8.FormSet('CustomerName', V8.ThisValue.Name);
50
+
51
+ // 联动显隐
52
+ V8.FieldSet('TaxNo', 'Visible', V8.ThisValue === '企业');
53
+
54
+ // 联动必填
55
+ V8.FieldSet('Reason', 'Required', V8.ThisValue === '退款');
56
+ ```
57
+
58
+ ### FieldOnKeyup — 键盘抬起事件
59
+
60
+ ```javascript
61
+ // V8.EventName === 'FieldOnKeyup'
62
+ // 表格行内键盘事件为 V8.EventName === 'TableFieldOnKeyup'
63
+ // V8.KeyCode — 键码;当前键盘 V8 不提供原生 V8.Event
64
+
65
+ if (V8.KeyCode === 13) {
66
+ V8.FormSubmit({ CloseForm: false });
67
+ }
68
+ ```
69
+
70
+ ### V8CodeBlur — 失焦专用代码
71
+
72
+ ```javascript
73
+ // 当前运行时仍使用 V8.EventName === 'FieldValueChange'
74
+ // V8.ThisValue 是失焦时的当前输入值
75
+ // 失焦校验手机号
76
+ if (V8.ThisValue && !/^1[3-9]\d{9}$/.test(V8.ThisValue)) {
77
+ V8.Tips('手机号格式不正确', false);
78
+ V8.Form.Phone = ''; // 静默清空,避免再次触发当前字段事件
79
+ }
80
+ ```
81
+
82
+ ### FieldSlotButtonClick — 单行文本插槽按钮
83
+
84
+ 单行文本 `Text` 开启【插槽按钮】后,必须在【插槽按钮V8代码】中配置行为,不要再写“弹出表格Id”。
85
+
86
+ ```javascript
87
+ // V8.EventName === 'FieldSlotButtonClick'
88
+ // V8.ThisValue — 当前输入框值
89
+ // V8.Event — 原生点击事件
90
+
91
+ if (V8.LoadMode === 'Design') return;
92
+
93
+ V8.OpenAnyTable({
94
+ SysMenuId: '目标业务菜单Id',
95
+ MultipleSelect: false,
96
+ SubmitEvent: function (selectData, callback) {
97
+ callback({ Code: 1, Data: selectData });
98
+ }
99
+ });
100
+ // 或 V8.OpenAnyForm / V8.OpenAppDialog / V8.ApiEngine.Run 等任意前端 V8 能力
101
+ ```
102
+
103
+ `ReadOnlyButton` 的产品文案是【禁用插槽按钮】:只控制按钮是否可点击,不等同于字段只读,应保留用于权限和状态控制。
104
+
105
+ ## 按钮事件
106
+
107
+ ### V8BtnRun — 按钮点击执行(菜单按钮、表单按钮)
108
+
109
+ ```javascript
110
+ // V8.Form — 当前行/表单数据
111
+ // V8.FormMode — Add / Edit / View
112
+ // V8.TableId — 当前 diy_table 的 Id
113
+ // V8.TableRowSelected — 批量按钮中选中的行数组
114
+ // V8.ClientType — 'PC' / 'IOS' / 'Android' / 'H5' / 'WeChat'
115
+
116
+ V8.ConfirmTips('确认审核通过?', function() {
117
+ V8.ApiEngine.Run({
118
+ ApiEngineKey: 'order_approve',
119
+ Id: V8.Form.Id
120
+ }, function(r) {
121
+ if (r.Code === 1) { V8.Tips('审核成功', true); V8.RefreshTable({ _PageIndex: 1 }); }
122
+ else V8.Tips(r.Msg || '失败', false);
123
+ });
124
+ });
125
+ ```
126
+
127
+ ### V8BtnLimit — 按钮显隐(V8CodeShow)
128
+
129
+ ```javascript
130
+ // 推荐:直接 return boolean
131
+ return V8.Form.Status === '待审核' && V8.CurrentUser.RoleName.indexOf('审批员') !== -1;
132
+
133
+ // 兼容旧写法:V8.Result = true/false
134
+ // V8.Result = V8.Form.Status === '待审核';
135
+ ```
136
+
137
+ ## 列表事件
138
+
139
+ ### TableRowClick — 行点击
140
+
141
+ ```javascript
142
+ // V8.Form === 被点击的行
143
+ console.log('点击行:', V8.Form.Id);
144
+ // 自定义跳转
145
+ V8.OpenAnyForm({ TableName: 'OrderDetail', Id: V8.Form.Id, FormMode: 'View' });
146
+ ```
147
+
148
+ ### OpenTableBefore — 打开列表前(拦截/初始化筛选)
149
+
150
+ ```javascript
151
+ // 固定弹出表格的可选数据范围;搜索、高级筛选、分页不会移除此条件
152
+ V8.OpenTableSetWhere(V8.Field.CustomerId, [
153
+ ['OwnerId', '=', V8.CurrentUser.Id]
154
+ ]);
155
+ ```
156
+
157
+ ### OpenTableSubmit — 列表查询提交前(追加条件)
158
+
159
+ ```javascript
160
+ // V8.Param 是即将发起查询的参数
161
+ V8.Param._Where = V8.Param._Where || [];
162
+ V8.Param._Where.push(['DeptId', '=', V8.CurrentUser.DeptId]);
163
+ ```
164
+
165
+ ### PageTab — 页签切换
166
+
167
+ ```javascript
168
+ // PageTab:"待办"
169
+ V8.SearchSet({ Status: '待办' });
170
+ V8.RefreshTable({ _PageIndex: 1 });
171
+ ```
172
+
173
+ ## 模板引擎事件
174
+
175
+ `TableTemplateEngine` / `FormTemplateEngine` — 见 `v8-template-engine/SKILL.md`。
176
+
177
+ ## 工作流事件(前端)
178
+
179
+ `WFNodeEnd` — 流程节点结束后前端通知。详见 `v8-workflow/SKILL.md`。
180
+
181
+ ## 常用前端 API
182
+
183
+ | API | 说明 |
184
+ |-----|------|
185
+ | `V8.Tips(msg, ok?)` | 浮层提示。`ok=true` 绿色 |
186
+ | `V8.ConfirmTips(msg, cb)` | 回调式确认弹窗;内容按 HTML 渲染,只能传可信/已转义文本 |
187
+ | `V8.FormSet(field, value)` | 普通表单会触发目标字段 V8;列表上下文只更新当前行/模板 |
188
+ | `V8.FieldSet(field, prop, value)` | 设置字段属性;跨上下文只依赖顶层 Visible/Required/Readonly/Data |
189
+ | `V8.FormSubmit({CloseForm:true})` | 提交当前表单 |
190
+ | `V8.RefreshTable({_PageIndex:1})` | 刷新表格(-1 保持当前页) |
191
+ | `V8.SearchSet({field: value})` | 设置筛选条件 |
192
+ | `V8.OpenAnyForm({...})` | 打开任意表单(弹窗/抽屉) |
193
+ | `V8.OpenAnyTable({...})` | 打开任意列表 |
194
+ | `V8.OpenDialog({...})` | 打开自定义弹窗 |
195
+ | `V8.OpenAppDialog({...})` | 按 AppKey 打开已发布在线微服务页面,支持 Dialog/Drawer 与结果回调 |
196
+ | `V8.ApiEngine.Run({ApiEngineKey, ...})` | 调接口引擎(前端,参数对象格式) |
197
+ | `V8.FormEngine.GetTableData(name, params, cb)` | 前端查列表(参数对象、回调或 await) |
198
+ | `V8.Post(url, data, cb, errCb, headers, contentType)` | 通用 POST |
199
+ | `V8.Method.ScanCode({...})` | 调用当前终端支持的扫码能力 |
200
+ | `V8.Print.isConnected()` | 检查当前蓝牙写特征是否仍可用 |
201
+ | `V8.Print.OpenBluetoothPage()` | 在用户手势中打开蓝牙连接页,返回 Promise |
202
+ | `V8.Print.prepareSend(bytes)` | 串行分包发送 TSC 或 ESC/POS 字节,必须 `await` |
203
+
204
+ `V8.OpenAnyForm` 只发起打开动作,不返回“用户关闭后的 Promise”。需要替换
205
+ 子表单保存时,通过 `EventReplace.Submit(v8, param, callback)` 注册提交替换;
206
+ 其中小写 `v8` 是子表单上下文,外层 `V8` 仍是父上下文。自定义提交结束后
207
+ 必须调用 `callback(DosResult)`,否则子表单会一直等待。
208
+
209
+ ### 蓝牙打印最小安全流程
210
+
211
+ ```javascript
212
+ if (!V8.Print) {
213
+ V8.Tips('当前客户端未加载蓝牙打印能力', false);
214
+ return;
215
+ }
216
+
217
+ if (!V8.Print.isConnected()) {
218
+ var connected = await V8.Print.OpenBluetoothPage();
219
+ if (!connected || !V8.Print.isConnected()) return;
220
+ }
221
+
222
+ var command = V8.Print.createNew(); // TSC/TSPL 标签
223
+ command.setSize(60, 40);
224
+ command.setGap(2);
225
+ command.setCls();
226
+ command.setText(20, 20, 'TSS24.BF2', 1, 1, '测试标签');
227
+ command.setPagePrint();
228
+
229
+ try {
230
+ await V8.Print.prepareSend(command.getData());
231
+ V8.Tips('打印数据已发送', true);
232
+ } catch (error) {
233
+ V8.Tips('发送失败:' + (error.message || error), false);
234
+ }
235
+ ```
236
+
237
+ `prepareSend` 成功只证明字节已经写入蓝牙特征,不代表打印机已走纸、无缺纸或无硬件故障。批量打印必须逐条 `await`,不得用固定 `setTimeout` 猜测完成时间,也不得用 `Promise.all` 并发写同一设备。完整挂载范围、连接语义、批量恢复、安全与硬件验收见 `references/bluetooth-print.md`;源码级 TSC/ESC 方法表见 `references/bluetooth-print-api.md`。
238
+
239
+ ### 常用上下文差异
240
+
241
+ | 变量 | 可用范围 |
242
+ |------|----------|
243
+ | `V8.OldForm` | 普通表单已加载旧数据后可用;服务端提交前/后事件也可用 |
244
+ | `V8.OldValue` | 仅表格行内字段值变更可靠提供 |
245
+ | `V8.Event` | 插槽按钮等显式传原生事件的场景;键盘事件使用 `V8.KeyCode` |
246
+ | `V8.Row/Rows/RowIndex` | 表格行事件 |
247
+ | `V8.TableRowSelected/SelectedData` | 列表批量按钮,互为兼容别名 |
248
+ | `V8.SearchParam` | 列表 `{Keyword, Where}` 搜索快照 |
249
+ | `V8.SysMenuModel` | 列表/菜单按钮 |
250
+ | `V8.DataAppend` | 打开表单、列表、弹窗时传入的附加数据 |
251
+
252
+ ### 在线微服务弹窗 V8.OpenAppDialog
253
+
254
+ 复杂定制页面使用 `V8.OpenAppDialog`,不要在 V8 事件中内嵌大量 HTML/CSS:
255
+
256
+ ```js
257
+ V8.OpenAppDialog({
258
+ AppKey: 'customer_profile_editor', // 必传:sys_microiservice.MsKey
259
+ RoutePath: '/edit', // 可选,默认 /
260
+ Version: '', // 可选,空值自动使用当前 BuildVersion
261
+ Title: '编辑客户资料',
262
+ TitleIcon: 'fas fa-user-edit',
263
+ Width: 'min(920px, calc(100vw - 32px))',
264
+ OpenType: 'Dialog', // Dialog / Drawer
265
+ Data: { id: V8.Form.Id }, // 子应用 dialogData,只放普通数据
266
+ OnSuccess: function (data) {
267
+ V8.RefreshTable({ _PageIndex: -1 });
268
+ },
269
+ OnCancel: function (data) {},
270
+ OnError: function (error) {
271
+ V8.Tips(error.message || '加载失败', false);
272
+ }
273
+ });
274
+ ```
275
+
276
+ 子应用用 `window.microApp.getData()` 获取自动下发的 `apiBase`、`osClient`、`token`、`appKey`、`version`、`microRoute`、`dialog` 和 `dialogData`;用 `window.microApp.dispatch({type:'app-dialog:success', data:{...}})` 返回成功结果。完整参数表和结果协议见 `v8-menu-buttons/SKILL.md`。
277
+
278
+ ### ConfirmTips 的 HTML 安全边界
279
+
280
+ `V8.ConfirmTips` 当前是 callback API,且内容使用 HTML 模式渲染。只传固定文案或经过 HTML 转义的简单展示;严禁直接拼接用户输入、接口消息、数据库富文本和不可信 URL。三个以上字段、上传、表格、Tab、步骤条、代码编辑器或需要复用的页面必须使用 `V8.OpenAppDialog`。
281
+
282
+ ## 前端 FormEngine 菜单上下文与兼容授权
283
+
284
+ 前端 V8 不需要为每个历史项目手工补 `_SysMenuId`。新版 PC 表单引擎通过作用域 FormEngine facade 透明处理菜单上下文:
285
+
286
+ - V8 调用的目标表就是当前菜单绑定表时,facade 自动注入真实 `_SysMenuId`;如果业务代码已经显式传入 `_SysMenuId` 或兼容的 `ModuleEngineKey`,平台保留显式值,由后端做严格精确校验。
287
+ - V8 查询其它表时,不得把当前主表菜单 Id 带给目标表。facade 保持无菜单,由后端根据当前登录用户有效角色可访问的 `sys_menu` 缓存推断该目标表及当前操作权限。
288
+ - 历史 V8 的对象参数、`表名 + 参数`、Promise、回调、批量参数等调用形式继续兼容。业务代码不得自行伪造角色、菜单或 `_TrustedServerInvocation`。
289
+ - 平台敏感表仍对普通客户端硬拒绝;Import/Export 仍必须携带目标模块的真实菜单上下文及专项权限,不能依赖无菜单推断。
290
+ - 菜单配置的 `SqlWhere`、`SqlJoin` / `JoinTables` 由后端追加到真实查询。前端追加 `_Where` 只能进一步缩小结果,不能扩大或覆盖服务端数据范围。
291
+ - 标准 `TableChild` 自动携带内部 `_TableChildAuth` 关系提示。服务端仍会重载父/子表、菜单和字段配置,校验父记录范围并强制子表外键;业务 V8 禁止构造、缓存、跨父记录复用该对象。
292
+
293
+ ```javascript
294
+ // 假设当前菜单绑定 Customer:平台 facade 自动注入真实菜单,无需历史 V8 手工改造
295
+ var current = await V8.FormEngine.GetFormData('Customer', { Id: V8.Form.Id });
296
+
297
+ // 跨表:不要传当前表的菜单 Id;后端按用户对 Product 的菜单授权推断
298
+ var products = await V8.FormEngine.GetTableData('Product', {
299
+ _Where: [['Status', '=', 1]],
300
+ _PageSize: 20
301
+ });
302
+ ```
303
+
304
+ 后端接口引擎和后端表单 V8 不是浏览器调用链:平台只在服务端内部构造参数时写入 `_TrustedServerInvocation`,所以它们调用 `V8.FormEngine` 不要求 `_SysMenuId`。该标记不能从浏览器 JSON、URL 或表单参数获得,前端 V8 也不得尝试设置。
305
+
306
+ ### 前端 FormEngine 方法矩阵
307
+
308
+ 前端 facade 当前公开 `GetFormData`、`GetFormDataAnonymous`、`GetTableData`、`GetTableTree`、`AddFormData`、`AddFormDataBatch`、`UptFormData`、`UptFormDataBatch`、`UptFormDataByWhere`、`DelFormData`、`DelFormDataBatch`、`DelFormDataByWhere`。全部返回 Promise,并兼容可选 callback。
309
+
310
+ 前端没有 `GetTableDataCount`、`GetTableDataTree`(前端名称是 `GetTableTree`)、`AddTableData`、`UptTableData`、`DelTableData`、`AddField`。Import/Export 是独立端点与专项菜单权限,也不是 facade 方法。
311
+
312
+ ## 异步写法(async/await vs 回调)
313
+
314
+ ```javascript
315
+ // ✅ 推荐 async/await
316
+ var r = await V8.FormEngine.GetTableData('Product', { _PageSize: 10 });
317
+ if (r.Code === 1) { /* ... */ }
318
+
319
+ // ✅ 也可回调式
320
+ V8.FormEngine.GetTableData('Product', { _PageSize: 10 }, function(r) {
321
+ if (r.Code === 1) { /* ... */ }
322
+ });
323
+ ```
324
+
325
+ ## 死循环陷阱
326
+
327
+ ❌ **禁止** 在 `SubmitFormV8.js` 里调用 `V8.FormSubmit()` —— 会无限递归
328
+ ⚠️ **避免** 在 `FieldValueChange` 里 `V8.FormSet(同字段)` —— 前端会阻止同步直接重入,但异步回写或多字段互相赋值仍可能形成循环;需要静默赋值时使用 `V8.Form.字段名 = value`
329
+ ❌ **禁止** 在 `InFormV8.js` 里写大量同步 `V8.FormEngine.Get*` —— 阻塞渲染
330
+
331
+ ### 下拉框对象赋值
332
+
333
+ ```javascript
334
+ // 会更新下拉选项并触发 SelectUser 的值变更 V8。
335
+ // 对象至少包含 SelectSaveField、SelectLabel 对应的属性;字段事件需要的其它属性也要传入。
336
+ V8.FormSet('SelectUser', { Id: 'u1', Name: '张三', DeptId: 'd1' });
337
+
338
+ // 响应式静默赋值:界面会更新,但不触发目标字段 V8,
339
+ // 也不会执行 FormSet 的修改字段记录、模板通知等处理。
340
+ V8.Form.SelectUser = { Id: 'u1', Name: '张三', DeptId: 'd1' };
341
+ ```
342
+
343
+ ## 设计模式保护(CRITICAL)
344
+
345
+ ```javascript
346
+ // 任何前端字段事件都应在头部加这个判断!
347
+ if (V8.LoadMode === 'Design') return;
348
+ ```
349
+ 否则在【表单设计器】中编辑字段时,事件会被误触发,可能弹提示、报错或触发副作用。
@@ -0,0 +1,107 @@
1
+ # V8.Print TSC 与 ESC/POS 源码 API
2
+
3
+ 本表直接按 `Microi.Client/src/utils/ble/tsc.js`、`esc.js` 与编码文件整理。方法名
4
+ (包括历史拼写)必须与源码完全一致,不能根据打印机手册自行改名。
5
+
6
+ ## 目录
7
+
8
+ - [构建器与编码](#构建器与编码)
9
+ - [TSC/TSPL 的 28 个方法](#tsctspl-的-28-个方法)
10
+ - [ESC/POS 的 25 个方法](#escpos-的-25-个方法)
11
+ - [参数与组合规则](#参数与组合规则)
12
+
13
+ ## 构建器与编码
14
+
15
+ ```javascript
16
+ var tsc = V8.Print.createNew();
17
+ var esc = V8.Print.createNewESC();
18
+ ```
19
+
20
+ 两个构建器都把指令累积到普通字节数组,`getData()` 返回该数组。TSC 的全部文本命令、
21
+ ESC 的 `setText` 和二维码内容通过本地 `TextEncoder('gb18030', {
22
+ NONSTANDARD_allowLegacyEncoding: true })` 编码;映射表来自同目录的
23
+ `encoding-indexes.js`,不需要网络请求。
24
+
25
+ ## TSC/TSPL 的 28 个方法
26
+
27
+ | 方法 | 当前生成的指令/作用 |
28
+ |---|---|
29
+ | `init()` | 空操作,历史兼容入口 |
30
+ | `addCommand(content)` | 以 GB18030 追加原始 TSC 文本;只用于固定可信命令 |
31
+ | `setSize(width,height)` | `SIZE width mm,height mm` |
32
+ | `setSpeed(speed)` | `SPEED speed` |
33
+ | `setDensity(density)` | `DENSITY density` |
34
+ | `setGap(gap)` | `GAP gap mm,0 mm` |
35
+ | `setBline(bline)` | `BLINE bline mm,0 mm`,黑标纸 |
36
+ | `setCountry(country)` | `COUNTRY country` |
37
+ | `setCodepage(codepage)` | `CODEPAGE codepage` |
38
+ | `setCls()` | `CLS`,清除图像缓冲区 |
39
+ | `setFeed(feed)` | `FEED feed`,向前走纸 |
40
+ | `setBackFeed(backup)` | `BACKFEED backup`,回拉 |
41
+ | `setDirection(direction)` | `DIRECTION direction` |
42
+ | `setReference(x,y)` | `REFERENCE x,y`,坐标原点 |
43
+ | `setFromfeed()` | 注意源码拼写;实际生成 `FORMFEED` |
44
+ | `setHome()` | `HOME`,定位下一张标签 |
45
+ | `setSound(level,interval)` | `SOUND level,interval` |
46
+ | `setLimitfeed(limit)` | `LIMITFEED limit` |
47
+ | `setBar(x,y,width,height)` | `BAR` 线条 |
48
+ | `setBox(x1,y1,x2,y2,thickness)` | `BOX` 方框 |
49
+ | `setErase(x,y,width,height)` | `ERASE` 清除区域 |
50
+ | `setReverse(x,y,width,height)` | `REVERSE` 区域反相 |
51
+ | `setText(x,y,font,xScale,yScale,text)` | `TEXT`;旋转值在源码中固定为 `0` |
52
+ | `setQR(x,y,level,width,mode,content)` | `QRCODE`;旋转值固定为 `0` |
53
+ | `setBarCode(x,y,type,height,readable,narrow,wide,content)` | `BARCODE`;旋转值固定为 `0` |
54
+ | `setBitmap(x,y,mode,imageData)` | 生成 TSC `BITMAP` 二进制数据 |
55
+ | `setPagePrint()` | `PRINT 1,1` |
56
+ | `getData()` | 返回当前字节数组 |
57
+
58
+ 最小顺序通常是 `setSize` → `setGap`/`setBline` → `setCls` → 内容 →
59
+ `setPagePrint` → `getData`。字体名、条码类型、纸张传感器、速度和浓度由打印机固件决定,
60
+ 构建器不验证范围。
61
+
62
+ ## ESC/POS 的 25 个方法
63
+
64
+ | 方法 | 当前生成的指令/作用 |
65
+ |---|---|
66
+ | `init()` | ESC `@` 初始化 |
67
+ | `setText(content)` | 以 GB18030 追加文字 |
68
+ | `setFontSize(n)` | GS `! n` |
69
+ | `bold(n)` | ESC `E n` |
70
+ | `setUnderline(n)` | ESC `- n` |
71
+ | `setUnderline2(n)` | FS `- n` |
72
+ | `setSelectSizeOfModuleForQRCode(n)` | 设置二维码模块尺寸,源码钳制到 1–15 |
73
+ | `setSelectErrorCorrectionLevelForQRCode(n)` | 设置二维码纠错值,源码不验证范围 |
74
+ | `setStoreQRCodeData(content)` | 以 GB18030 暂存二维码内容 |
75
+ | `setPrintQRCode()` | 输出已暂存二维码 |
76
+ | `setHorTab()` | 水平 Tab |
77
+ | `setAbsolutePrintPosition(where)` | 设置绝对横向位置 |
78
+ | `setRelativePrintPositon(where)` | 设置相对横向位置;`Positon` 是现有公开拼写 |
79
+ | `setSelectJustification(which)` | 对齐:常见值 0 左、1 中、2 右 |
80
+ | `space(n)` | 设置水平制表位置 |
81
+ | `setLeftMargin(n)` | 设置左边距 |
82
+ | `textMarginRight(n)` | 设置字符右间距 |
83
+ | `rowSpace(n)` | 设置行间距 |
84
+ | `setPrintingAreaWidth(width)` | 设置打印区域宽度 |
85
+ | `setSound(n,t)` | 蜂鸣器;大于 9 钳制为 9,小于 0 改为 1,0 会原样保留 |
86
+ | `setBitmap(imageData)` | 生成 ESC/POS 光栅位图数据 |
87
+ | `setPrint()` | 换行/打印当前行 |
88
+ | `setPrintAndFeed(feed)` | 打印并走纸指定单位 |
89
+ | `setPrintAndFeedRow(row)` | 打印并走纸指定行数 |
90
+ | `getData()` | 返回当前字节数组 |
91
+
92
+ ESC/POS 二维码需要按顺序调用尺寸、纠错、`setStoreQRCodeData`、
93
+ `setPrintQRCode`。源码中虽保留条码类型数组,但没有公开 ESC/POS 条码方法,不能虚构
94
+ `setBarCode`;条码需求应先确认型号并扩展实现。
95
+
96
+ ## 参数与组合规则
97
+
98
+ - `setBitmap` 的参数是 ImageData 风格对象:`width`、`height`、RGBA `data`。它不是图片
99
+ URL、Base64 或 DOM `<img>`;调用前先在 Canvas 得到 `getImageData()`。
100
+ - 当前位图算法按透明/非透明像素进行非常简单的黑白映射,不做通用抖动和灰度阈值处理;
101
+ 大图先缩放、二值化,逐型号验证方向、颜色和内存。
102
+ - TSC `setText`、`setQR`、`setBarCode` 把值放入带双引号的协议字段。移除双引号、CR/LF、
103
+ NUL 和控制字符,限制长度;不要让接口返回值直达 `addCommand`。
104
+ - ESC 参数最终作为单字节或低/高字节写入,负数、浮点数、超范围值可能截断或生成无效命令。
105
+ 坐标、宽度、走纸、字号和蜂鸣器参数应先转为目标机型允许的整数。
106
+ - GB18030 只解决字节编码,不能替代打印机代码页、中文字库和字体配置。二维码通常比文字
107
+ 字库更能稳定承载 Unicode 业务标识,但仍受打印机二维码命令实现限制。