openxiangda 1.0.35 → 1.0.37
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/README.md +12 -0
- package/lib/cli.js +44 -4
- package/lib/workspace-bootstrap.js +238 -0
- package/openxiangda-skills/SKILL.md +43 -4
- package/openxiangda-skills/references/component-guide.md +10 -11
- package/openxiangda-skills/references/resource-manifest-cheatsheet.md +348 -0
- package/openxiangda-skills/references/style-system.md +14 -18
- package/openxiangda-skills/references/troubleshooting.md +13 -13
- package/openxiangda-skills/skills/openxiangda-app/SKILL.md +18 -2
- package/openxiangda-skills/skills/openxiangda-core/SKILL.md +37 -1
- package/openxiangda-skills/skills/openxiangda-form/SKILL.md +37 -2
- package/openxiangda-skills/skills/openxiangda-inspect/SKILL.md +26 -2
- package/openxiangda-skills/skills/openxiangda-page/SKILL.md +38 -4
- package/openxiangda-skills/skills/openxiangda-permission-settings/SKILL.md +19 -2
- package/openxiangda-skills/skills/openxiangda-workflow-automation/SKILL.md +26 -2
- package/package.json +1 -1
- package/packages/sdk/dist/runtime/index.cjs +67 -30
- package/packages/sdk/dist/runtime/index.cjs.map +1 -1
- package/packages/sdk/dist/runtime/index.mjs +67 -30
- package/packages/sdk/dist/runtime/index.mjs.map +1 -1
- package/packages/sdk/dist/styles/antd-theme.cjs +11 -3
- package/packages/sdk/dist/styles/antd-theme.cjs.map +1 -1
- package/packages/sdk/dist/styles/antd-theme.d.mts +2 -1
- package/packages/sdk/dist/styles/antd-theme.d.ts +2 -1
- package/packages/sdk/dist/styles/antd-theme.mjs +11 -3
- package/packages/sdk/dist/styles/antd-theme.mjs.map +1 -1
- package/packages/sdk/dist/styles/tailwind-preset.cjs +0 -1
- package/packages/sdk/dist/styles/tailwind-preset.cjs.map +1 -1
- package/packages/sdk/dist/styles/tailwind-preset.d.mts +0 -1
- package/packages/sdk/dist/styles/tailwind-preset.d.ts +0 -1
- package/packages/sdk/dist/styles/tailwind-preset.mjs +0 -1
- package/packages/sdk/dist/styles/tailwind-preset.mjs.map +1 -1
- package/packages/sdk/dist/styles/tokens.css +1 -0
- package/packages/sdk/src/build-source/scripts/build-forms.mjs +135 -50
- package/packages/sdk/src/build-source/scripts/build-pages.mjs +37 -10
- package/packages/sdk/src/build-source/scripts/register.mjs +2 -0
- package/packages/sdk/src/build-source/scripts/utils/load-config.mjs +3 -2
- package/packages/sdk/src/build-source/scripts/utils/register-payload.test.ts +2 -1
- package/packages/sdk/src/build-source/scripts/utils/tailwind-config.mjs +9 -7
- package/packages/sdk/src/build-source/scripts/utils/tailwind-config.test.ts +6 -4
- package/packages/sdk/src/build-source/src/cli.mjs +17 -0
- package/templates/sy-lowcode-app-workspace/.cursor/rules/openxiangda-form.mdc +20 -0
- package/templates/sy-lowcode-app-workspace/.cursor/rules/openxiangda-page.mdc +19 -0
- package/templates/sy-lowcode-app-workspace/.cursor/rules/openxiangda-resources.mdc +28 -0
- package/templates/sy-lowcode-app-workspace/.cursor/rules/openxiangda-workflow-automation.mdc +21 -0
- package/templates/sy-lowcode-app-workspace/.cursor/rules/openxiangda.mdc +47 -0
- package/templates/sy-lowcode-app-workspace/.qoder/rules/openxiangda-form.md +34 -0
- package/templates/sy-lowcode-app-workspace/.qoder/rules/openxiangda-page.md +37 -0
- package/templates/sy-lowcode-app-workspace/.qoder/rules/openxiangda-resources.md +46 -0
- package/templates/sy-lowcode-app-workspace/.qoder/rules/openxiangda-workflow-automation.md +46 -0
- package/templates/sy-lowcode-app-workspace/.qoder/rules/openxiangda.md +47 -0
- package/templates/sy-lowcode-app-workspace/AGENTS.md +92 -0
- package/templates/sy-lowcode-app-workspace/app-workspace.config.ts +3 -3
- package/templates/sy-lowcode-app-workspace/package.json +7 -0
- package/templates/sy-lowcode-app-workspace/postcss.config.cjs +0 -15
- package/templates/sy-lowcode-app-workspace/scripts/guard-publish.mjs +29 -0
- package/templates/sy-lowcode-app-workspace/src/main.tsx +1 -12
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
# Resource Manifest Cheatsheet
|
|
2
|
+
|
|
3
|
+
> 1-2 屏速查:`src/resources/` 下各类型 manifest 的最小可用模板与对应运行时调用方式。
|
|
4
|
+
> 这里只放"复制即用"的骨架;字段语义详见对应的 reference 文档。
|
|
5
|
+
|
|
6
|
+
## 通用约定
|
|
7
|
+
|
|
8
|
+
- **本地用逻辑 code**:所有 manifest 用稳定的 `formCode` / `pageCode` / `workflowCode` / `automationCode` / `connectorCode` / `dataViewCode` / `roleCode` / `notificationType`。
|
|
9
|
+
- **平台 ID 由 CLI 解析**:`openxiangda resource publish --profile <name>` 把 code 解析成当前 profile 的真实 ID 并写回 `.openxiangda/state.json`。
|
|
10
|
+
- **永远不写密钥**:第三方 API key、token、secret、password、authorization、headers 等绝不出现在 `src/resources/`,平台管理员在后台配置。
|
|
11
|
+
- **多 profile 不共享 ID**:`dev` / `prod` 各自维护一份资源 ID 映射,复制 manifest 即可复用,不要复制平台 ID。
|
|
12
|
+
|
|
13
|
+
## 命令
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
openxiangda resource validate --profile <name> # 静态校验所有 manifest
|
|
17
|
+
openxiangda resource plan --profile <name> # diff:本地 vs. 平台
|
|
18
|
+
openxiangda resource publish --profile <name> # 非破坏性 upsert(不 prune)
|
|
19
|
+
openxiangda resource publish --profile <name> --prune # 删除 manifest 未声明的平台资源(谨慎)
|
|
20
|
+
openxiangda resource pull --profile <name> # 拉取平台资源回写到本地(用于初次同步)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 1. Connector — `src/resources/connectors/<code>.json`
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"code": "crm",
|
|
28
|
+
"name": "CRM Service",
|
|
29
|
+
"url": "https://crm.internal.example.com/api",
|
|
30
|
+
"authType": "apiKey",
|
|
31
|
+
"authConfig": {
|
|
32
|
+
"apiKey": { "key": "X-API-Key", "value": "internal-secret", "in": "header" }
|
|
33
|
+
},
|
|
34
|
+
"userContext": {
|
|
35
|
+
"enabled": true,
|
|
36
|
+
"inject": [
|
|
37
|
+
{ "target": "header", "key": "X-User-Id", "value": "userId" },
|
|
38
|
+
{ "target": "body", "key": "operator", "value": "user" }
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
"apis": [
|
|
42
|
+
{
|
|
43
|
+
"code": "getCustomer",
|
|
44
|
+
"name": "Get Customer",
|
|
45
|
+
"method": "POST",
|
|
46
|
+
"path": "/customers/search",
|
|
47
|
+
"requestBodyType": "json",
|
|
48
|
+
"responseType": "json"
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
页面调用:
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
const data = await sdk.connector.call("crm.getCustomer", { body: { keyword } });
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
不要把 `authConfig` 里的真实 secret 写进 manifest;用 placeholder,由平台管理员在后台覆盖。完整字段见 [`connector-resources.md`](connector-resources.md)。
|
|
61
|
+
|
|
62
|
+
## 2. Data View — `src/resources/data-views/<code>.json`
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"code": "ticket_with_customer",
|
|
67
|
+
"name": "Ticket With Customer",
|
|
68
|
+
"base": { "formCode": "service_ticket", "alias": "ticket" },
|
|
69
|
+
"joins": [
|
|
70
|
+
{
|
|
71
|
+
"type": "left",
|
|
72
|
+
"formCode": "customer",
|
|
73
|
+
"alias": "customer",
|
|
74
|
+
"on": [
|
|
75
|
+
{ "left": "ticket.customer.value", "op": "=", "right": "customer.form_instance_id" }
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
],
|
|
79
|
+
"select": [
|
|
80
|
+
{ "field": "ticket.form_instance_id", "as": "ticketId" },
|
|
81
|
+
{ "field": "ticket.title", "as": "ticketTitle" },
|
|
82
|
+
{ "field": "ticket.status.label", "as": "statusLabel" },
|
|
83
|
+
{ "field": "ticket.status.value", "as": "statusValue" },
|
|
84
|
+
{ "field": "customer.name", "as": "customerName" }
|
|
85
|
+
],
|
|
86
|
+
"indexes": [{ "fields": ["ticketId"], "unique": true }],
|
|
87
|
+
"refresh": { "mode": "scheduled", "cron": "0 */10 * * * *" },
|
|
88
|
+
"permissionGroups": [
|
|
89
|
+
{ "code": "ticket_query", "name": "Ticket Query", "roles": ["manager"], "operations": ["query"] }
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
页面调用:
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
const list = await sdk.dataView.query("ticket_with_customer", {
|
|
98
|
+
filters: [{ field: "statusValue", op: "in", values: ["open", "processing"] }],
|
|
99
|
+
order: [{ field: "ticketId", direction: "desc" }],
|
|
100
|
+
page: { current: 1, size: 20 },
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
适用:跨表只读联表 / 报表 / 大列表性能优化。**不适用**:单表 CRUD、`linkedForm` 下拉、强实时、写回。完整规则见 [`data-views.md`](data-views.md)。
|
|
105
|
+
|
|
106
|
+
## 3. Notification — `src/resources/notifications/<code>.json`
|
|
107
|
+
|
|
108
|
+
```json
|
|
109
|
+
{
|
|
110
|
+
"templates": [
|
|
111
|
+
{
|
|
112
|
+
"code": "reservation_reminder",
|
|
113
|
+
"name": "预约提醒",
|
|
114
|
+
"content": "{{title}}",
|
|
115
|
+
"variables": ["title", "instrumentName", "startTime"],
|
|
116
|
+
"channelsConfig": {
|
|
117
|
+
"inapp": { "enabled": true, "content": "{{instrumentName}} 将于 {{startTime}} 开始" },
|
|
118
|
+
"dingding": { "enabled": true, "content": "{{title}}" }
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
],
|
|
122
|
+
"typeConfigs": [
|
|
123
|
+
{
|
|
124
|
+
"notificationType": "reservation_reminder",
|
|
125
|
+
"templateCode": "reservation_reminder",
|
|
126
|
+
"enabled": true,
|
|
127
|
+
"priority": 0
|
|
128
|
+
}
|
|
129
|
+
]
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
页面调用:
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
await sdk.notification.sendByType({
|
|
137
|
+
notificationType: "reservation_reminder",
|
|
138
|
+
recipientId: userId,
|
|
139
|
+
payload: { title: "预约提醒", instrumentName, startTime },
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
JS_CODE 调用:`ctx.notification.sendByType({ ... })`。允许的 channels:`inapp` / `email` / `dingding` / `wechat` / `thirdparty_todo`。完整规则见 [`notifications.md`](notifications.md)。
|
|
144
|
+
|
|
145
|
+
## 4. Workflow — `src/resources/workflows/<code>/workflow.json`(manifest)+ `src/workflows/<code>/workflow.ts`(代码优先)
|
|
146
|
+
|
|
147
|
+
```jsonc
|
|
148
|
+
// src/resources/workflows/customer_approval/workflow.json
|
|
149
|
+
{
|
|
150
|
+
"code": "customer_approval",
|
|
151
|
+
"formCode": "customer",
|
|
152
|
+
"kind": "workflow_v3",
|
|
153
|
+
"definitionFile": "definition.v3.json",
|
|
154
|
+
"previewFile": "preview.json"
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
代码优先(推荐):
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
// src/workflows/customer_approval/workflow.ts
|
|
162
|
+
import { defineWorkflow } from "openxiangda/workflow";
|
|
163
|
+
|
|
164
|
+
export default defineWorkflow({
|
|
165
|
+
name: "客户审批",
|
|
166
|
+
trigger: { type: "form_submit", formCode: "customer" },
|
|
167
|
+
nodes: [
|
|
168
|
+
/* approval / copy / branch / js_code 节点 */
|
|
169
|
+
],
|
|
170
|
+
});
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
CLI 编译为 `definition.v3.json` + `preview.json`,平台运行时仍走标准工作流引擎。完整规则见 [`workflow-v3.md`](workflow-v3.md)。
|
|
174
|
+
|
|
175
|
+
## 5. Automation — `src/resources/automations/<code>/{definition.code.json,preview.json}` + `src/automations/<code>/index.ts`
|
|
176
|
+
|
|
177
|
+
```jsonc
|
|
178
|
+
// src/resources/automations/notify_on_submit/definition.code.json
|
|
179
|
+
{
|
|
180
|
+
"code": "notify_on_submit",
|
|
181
|
+
"name": "提交后通知",
|
|
182
|
+
"kind": "automation_code_ts",
|
|
183
|
+
"trigger": {
|
|
184
|
+
"version": 2,
|
|
185
|
+
"mode": "event",
|
|
186
|
+
"event": { "source": "form_data", "action": "submitted" },
|
|
187
|
+
"filters": { "formCode": "customer" }
|
|
188
|
+
},
|
|
189
|
+
"sourceFile": { "localPath": "src/automations/notify_on_submit/index.ts" },
|
|
190
|
+
"previewFile": "preview.json",
|
|
191
|
+
"timeout": 30000
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
// src/automations/notify_on_submit/index.ts
|
|
197
|
+
export default async function (ctx) {
|
|
198
|
+
ctx.logger.info("automation start", { formCode: ctx.formData.current.formCode });
|
|
199
|
+
await ctx.notification.sendByType({
|
|
200
|
+
notificationType: "submission_notice",
|
|
201
|
+
recipientId: ctx.operator.userId,
|
|
202
|
+
payload: { /* ... */ },
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
`trigger_v2` 事件源:`form_data` / `form_field` / `workflow_task` / `workflow_process` / `mode: "scheduled"`(fixed_time / form_date_field)。完整规则见 [`automation-v3.md`](automation-v3.md)。
|
|
208
|
+
|
|
209
|
+
## 6. JS_CODE V2 — `src/js-code-nodes/<scriptCode>/index.ts`
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
export default async function (ctx) {
|
|
213
|
+
const { formData, operator, methods, notification, platform, utils, console } = ctx;
|
|
214
|
+
const result = await methods.queryManyData({
|
|
215
|
+
formCode: "customer",
|
|
216
|
+
formUuid: ctx.app.formUuids.customer,
|
|
217
|
+
searchCondition: [/* 查询条件 */],
|
|
218
|
+
});
|
|
219
|
+
return { count: result.length };
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
工作流 / 自动化 v3 JSON 节点引用:
|
|
224
|
+
|
|
225
|
+
```json
|
|
226
|
+
{
|
|
227
|
+
"id": "sync_customer",
|
|
228
|
+
"type": "js_code",
|
|
229
|
+
"data": {
|
|
230
|
+
"label": "同步客户",
|
|
231
|
+
"runtimeMode": "trusted_node",
|
|
232
|
+
"sourceType": "file_snapshot",
|
|
233
|
+
"scriptCode": "sync_customer",
|
|
234
|
+
"sourceFile": { "localPath": "src/js-code-nodes/sync_customer/index.ts" },
|
|
235
|
+
"timeout": 30000
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
构建:`pnpm build-js-code --script sync_customer`(先 `tsc` 再打包到 `dist/js-code-nodes/<code>/index.cjs`)。CLI validate/create/publish 时会上传快照、用 `{ bucketName, objectName, sha256 }` 替换 `sourceFile.localPath`。
|
|
241
|
+
|
|
242
|
+
## 7. Role — `src/resources/roles/<code>.json`
|
|
243
|
+
|
|
244
|
+
```json
|
|
245
|
+
{
|
|
246
|
+
"code": "sales",
|
|
247
|
+
"name": "销售"
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## 8. Page Permission Group — `src/resources/permissions/page-groups/<code>.json`
|
|
252
|
+
|
|
253
|
+
```json
|
|
254
|
+
{
|
|
255
|
+
"code": "sales_pages",
|
|
256
|
+
"name": "销售页面",
|
|
257
|
+
"roles": ["sales"],
|
|
258
|
+
"formCodes": ["customer", "orders"],
|
|
259
|
+
"pageCodes": ["dashboard"]
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
`formCodes` / `pageCodes` / `menuCodes` 留空表示对匹配角色全部可见。
|
|
264
|
+
|
|
265
|
+
## 9. Form Permission Group — `src/resources/permissions/form-groups/<formCode>/<groupCode>.json`
|
|
266
|
+
|
|
267
|
+
```json
|
|
268
|
+
{
|
|
269
|
+
"code": "sales_view",
|
|
270
|
+
"name": "销售查看",
|
|
271
|
+
"type": "view",
|
|
272
|
+
"roles": ["sales"],
|
|
273
|
+
"operations": ["view"],
|
|
274
|
+
"dataScope": {
|
|
275
|
+
"type": "expression",
|
|
276
|
+
"match": "all",
|
|
277
|
+
"conditions": [
|
|
278
|
+
{ "field": "ownerScopeKey", "op": "=", "valueRef": "currentUser.id" }
|
|
279
|
+
]
|
|
280
|
+
},
|
|
281
|
+
"fieldPermissions": {
|
|
282
|
+
"internalNote": "hidden",
|
|
283
|
+
"amount": "readonly"
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## 10. Form Settings — `src/resources/settings/forms/<formCode>.json`
|
|
289
|
+
|
|
290
|
+
```json
|
|
291
|
+
{
|
|
292
|
+
"code": "customer",
|
|
293
|
+
"settings": {
|
|
294
|
+
"submitButtonText": "提交",
|
|
295
|
+
"successMessage": "提交成功"
|
|
296
|
+
},
|
|
297
|
+
"indexes": [
|
|
298
|
+
{ "fields": ["customerCode"], "unique": true },
|
|
299
|
+
{ "fields": ["status", "ownerDept"] }
|
|
300
|
+
],
|
|
301
|
+
"dataManagement": { "enabled": true, "default": "list" },
|
|
302
|
+
"publicAccess": { "enabled": false }
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
## 11. Menu — `src/resources/menus/<code>.json`
|
|
307
|
+
|
|
308
|
+
```json
|
|
309
|
+
{
|
|
310
|
+
"code": "main",
|
|
311
|
+
"name": "主菜单",
|
|
312
|
+
"type": "nav",
|
|
313
|
+
"children": [
|
|
314
|
+
{ "code": "customer-entry", "name": "客户信息", "type": "receipt", "formCode": "customer" },
|
|
315
|
+
{ "code": "orders-entry", "name": "订单", "type": "receipt", "formCode": "orders" },
|
|
316
|
+
{ "code": "dashboard-entry","name": "驾驶舱", "type": "page", "pageCode": "dashboard" }
|
|
317
|
+
]
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## 选型决策树
|
|
322
|
+
|
|
323
|
+
```text
|
|
324
|
+
需要展示数据?
|
|
325
|
+
├─ 单表 CRUD → 表单页 + DataManagementList(不要 data view)
|
|
326
|
+
├─ 多表只读联表 → src/resources/data-views/
|
|
327
|
+
├─ 表单字段下拉 → SelectField + optionSource.type: "linkedForm"(不要 data view)
|
|
328
|
+
└─ 大屏 / 报表 → 原生报表 → ECharts page
|
|
329
|
+
|
|
330
|
+
需要后端逻辑?
|
|
331
|
+
├─ 真有审批 → workflow(src/workflows/<code>/workflow.ts)
|
|
332
|
+
├─ 状态流转 → 表单 status 字段 + 状态机 + automation
|
|
333
|
+
├─ 提交/字段触发 → automation(trigger_v2 + src/automations/<code>/index.ts)
|
|
334
|
+
├─ 定时任务 → automation(mode: "scheduled")
|
|
335
|
+
└─ 复杂后端逻辑 → JS_CODE V2 trusted_node(src/js-code-nodes/<code>/index.ts)
|
|
336
|
+
|
|
337
|
+
需要外部数据?
|
|
338
|
+
├─ 第三方 HTTP → src/resources/connectors/ + sdk.connector.call
|
|
339
|
+
└─ 钉钉 / 飞书等 → 同上,平台后端配置 OAuth/AK,前端只引用 connector code
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## 常见错误
|
|
343
|
+
|
|
344
|
+
- ❌ 把 `formUuid` / `pageId` 等平台 ID 写进 manifest。**用 code,CLI 自动解析。**
|
|
345
|
+
- ❌ 把 API key、token、密码写进 manifest。**用占位符,平台后台填真实值。**
|
|
346
|
+
- ❌ 同时在 manifest 与平台后台编辑同一个资源(漂移源)。**统一以 manifest 为单一来源**,需要看平台版本就 `resource pull`。
|
|
347
|
+
- ❌ 用 data view 代替 `linkedForm` 下拉、单表 CRUD 或写回。**data view 是只读 + 延迟刷新。**
|
|
348
|
+
- ❌ 在 page 源码里 hardcode `/api/notification-config/*` 或 `/connectors/actions/invoke`。**用 `sdk.notification` / `sdk.connector`。**
|
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
1. **业务页面默认写原生 Tailwind**:优先使用 `bg-white`、`border`、`border-slate-200`、`text-slate-600`、`shadow-sm`、`rounded-lg`、`p-4`、`gap-4`、`grid-cols-[240px_1fr]` 这类 Tailwind 原生类和任意值。
|
|
10
10
|
2. **不要强制平台 token**:不要把 `bg-container`、`text-secondary`、`rounded-form`、`shadow-card` 当默认示例或默认范式。历史项目或平台组件需要时可以兼容使用,但新业务页面不推荐主动依赖。
|
|
11
11
|
3. **不要直接套 shadcn token**:`bg-card`、`text-muted-foreground`、`text-foreground`、`bg-background` 等不是 Tailwind 原生类。除非项目已经在 `tailwind.config.cjs` 明确配置这些 token,否则必须改成 Tailwind 原生类或任意值。
|
|
12
|
-
4.
|
|
13
|
-
5. **
|
|
12
|
+
4. **默认不启用样式隔离**:新发布页面默认 `cssIsolation: "none"`,Tailwind 类按原样输出,不需要 `.sy-app-workspace` 前缀即可生效。
|
|
13
|
+
5. **legacy 隔离仅用于兼容**:历史页面或显式配置 `cssIsolation: "namespace" | "shadow"` 时,runtime 仍会保留 `.sy-app-workspace`、legacy portal 和旧样式前缀。
|
|
14
|
+
6. **CSS 作用域要收敛**:页面级 CSS 写在 `styles.css`,选择器优先限定在页面根类下,避免无意覆盖宿主平台或其他页面。
|
|
14
15
|
|
|
15
16
|
---
|
|
16
17
|
|
|
@@ -60,12 +61,12 @@ export function DashboardPage() {
|
|
|
60
61
|
### 2.4 局部 CSS
|
|
61
62
|
|
|
62
63
|
```css
|
|
63
|
-
.
|
|
64
|
+
.queue-booking-page {
|
|
64
65
|
min-height: 100%;
|
|
65
66
|
background: #f8fafc;
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
.
|
|
69
|
+
.queue-booking-page .queue-booking-page__calendar-grid {
|
|
69
70
|
display: grid;
|
|
70
71
|
grid-template-columns: repeat(7, minmax(120px, 1fr));
|
|
71
72
|
gap: 12px;
|
|
@@ -113,11 +114,11 @@ module.exports = {
|
|
|
113
114
|
|
|
114
115
|
这个配置的作用:
|
|
115
116
|
|
|
116
|
-
- `openxiangdaPreset`
|
|
117
|
+
- `openxiangdaPreset` 保留平台组件 safelist、平台 token 兼容类和平台组件需要的工具类;不再强制 Tailwind 输出 `.sy-app-workspace` 前缀。
|
|
117
118
|
- `...openxiangdaContent` 确保平台组件内部 class 会被 Tailwind 扫描到。
|
|
118
119
|
- `blocklist` 避免 Tailwind 误生成日期字符串相关的异常类。
|
|
119
120
|
|
|
120
|
-
|
|
121
|
+
不要移除这些平台构建能力;自由写 Tailwind 原生类和保留平台组件兼容能力并不冲突。
|
|
121
122
|
|
|
122
123
|
---
|
|
123
124
|
|
|
@@ -160,21 +161,15 @@ body {
|
|
|
160
161
|
|
|
161
162
|
## 5. Ant Design 和弹层
|
|
162
163
|
|
|
163
|
-
PC 控件优先用 `antd`,移动端控件优先用 `antd-mobile
|
|
164
|
+
PC 控件优先用 `antd`,移动端控件优先用 `antd-mobile`。默认 `cssIsolation: "none"` 时使用 Ant Design 默认 `ant` class 和 `document.body` 弹层容器即可;只有 legacy `namespace/shadow` 页面才需要显式挂到当前隔离容器:
|
|
164
165
|
|
|
165
166
|
```tsx
|
|
166
|
-
<ConfigProvider
|
|
167
|
-
getPopupContainer={(trigger) =>
|
|
168
|
-
trigger?.closest('.sy-app-workspace') ??
|
|
169
|
-
(document.querySelector('.sy-app-workspace') as HTMLElement) ??
|
|
170
|
-
document.body
|
|
171
|
-
}
|
|
172
|
-
>
|
|
167
|
+
<ConfigProvider>
|
|
173
168
|
<App />
|
|
174
169
|
</ConfigProvider>
|
|
175
170
|
```
|
|
176
171
|
|
|
177
|
-
|
|
172
|
+
legacy 页面可使用 `getPopupContainer={(trigger) => trigger?.closest(".sy-app-workspace") ?? document.body}`。不要无作用域覆盖 `.ant-select-selector`、`.ant-modal` 等私有 class;确实需要覆盖时限定在页面根类下。
|
|
178
173
|
|
|
179
174
|
---
|
|
180
175
|
|
|
@@ -188,9 +183,10 @@ OpenXiangda 仍保留部分平台语义类和 CSS 变量,主要用于:
|
|
|
188
183
|
|
|
189
184
|
常见兼容类包括 `text-primary`、`text-secondary`、`bg-container`、`bg-layout`、`border-secondary`、`rounded-form`、`shadow-card` 等。AI 编写新业务页面时不要默认使用这些类;除非用户明确要求跟随平台变量主题,或当前项目已有一致的 token 体系。
|
|
190
185
|
|
|
191
|
-
|
|
186
|
+
如果需要统一主题,推荐把主题限制在应用根类下;兼容旧页面时也可以同时声明 `.sy-app-workspace`:
|
|
192
187
|
|
|
193
188
|
```css
|
|
189
|
+
.theme-brand,
|
|
194
190
|
.sy-app-workspace.theme-brand {
|
|
195
191
|
--sy-color-primary: #0f766e;
|
|
196
192
|
--sy-radius-md: 8px;
|
|
@@ -218,5 +214,5 @@ warning 不会阻断构建,但它通常意味着页面会出现“颜色、边
|
|
|
218
214
|
- [ ] 页面样式默认使用 Tailwind 原生类和任意值,而不是平台 token 类?
|
|
219
215
|
- [ ] 没有使用未配置的 shadcn token 类,例如 `bg-card`、`text-muted-foreground`、`text-foreground`?
|
|
220
216
|
- [ ] `tailwind.config.cjs` 保留 OpenXiangda preset、content 扫描和 blocklist?
|
|
221
|
-
- [ ] 业务 CSS
|
|
222
|
-
- [ ]
|
|
217
|
+
- [ ] 业务 CSS 限定在页面根类下,而不是全局裸覆盖?
|
|
218
|
+
- [ ] 只有 legacy `namespace/shadow` 页面才额外配置 `.sy-app-workspace` 弹层容器?
|
|
@@ -23,18 +23,18 @@
|
|
|
23
23
|
|
|
24
24
|
**症状**:Select / Dropdown / Popup / Modal 等组件的弹出层没有样式,直接跑到屏幕外,或呈现为无样式的裸 HTML。
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
**解决方案**:
|
|
29
|
-
1.
|
|
30
|
-
2.
|
|
31
|
-
3.
|
|
32
|
-
4.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
```tsx
|
|
36
|
-
import { useRef } from 'react';
|
|
37
|
-
import { ConfigProvider } from 'antd';
|
|
26
|
+
**根因**:新页面默认 `cssIsolation: "none"`,弹层使用 `document.body` 和默认 Ant Design 样式即可。只有历史页面显式启用 `cssIsolation: "namespace" | "shadow"` 时,弹层渲染到 `document.body` 才可能脱离 legacy 样式作用域;移动端 Popup 也可能因 namespace 未被继承而失去样式。
|
|
27
|
+
|
|
28
|
+
**解决方案**:
|
|
29
|
+
1. 新页面优先保持 `cssIsolation: "none"`,不要为了修弹层问题手动加 `.sy-app-workspace`。
|
|
30
|
+
2. legacy `shadow` 页面优先迁移为 `none`;无法迁移时可使用 `namespace` 兼容模式。
|
|
31
|
+
3. legacy 页面为 antd `ConfigProvider` 配置 `getPopupContainer` 指向页面根容器。
|
|
32
|
+
4. 对于 legacy antd-mobile 页面,确保 `Popup` / `Modal` 的 `getContainer` 指向正确容器,并按需补 namespace class。
|
|
33
|
+
|
|
34
|
+
**legacy 示例**:
|
|
35
|
+
```tsx
|
|
36
|
+
import { useRef } from 'react';
|
|
37
|
+
import { ConfigProvider } from 'antd';
|
|
38
38
|
|
|
39
39
|
const rootRef = useRef<HTMLDivElement>(null);
|
|
40
40
|
|
|
@@ -188,7 +188,7 @@ export default defineConfig({
|
|
|
188
188
|
**解决方案**:
|
|
189
189
|
1. 在 `vite.config.ts` 的 `optimizeDeps.include` 中显式添加 `antd-mobile`,避免运行时多次预构建。
|
|
190
190
|
2. 调整 Tailwind preflight 策略,确保不会覆盖 antd-mobile 的基础样式(必要时关闭 preflight 或使用 `important` 选择器)。
|
|
191
|
-
3.
|
|
191
|
+
3. 仅 legacy `namespace/shadow` 页面需要确保 antd-mobile 组件渲染在 `sy-app-workspace` namespace 容器内,弹层 `getContainer` 指向该容器;新页面默认不需要。
|
|
192
192
|
|
|
193
193
|
**示例**:
|
|
194
194
|
```typescript
|
|
@@ -1,11 +1,27 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: openxiangda-app
|
|
3
|
-
description: Manage OpenXiangda low-code apps
|
|
3
|
+
description: Manage OpenXiangda low-code apps and workspaces — create / scaffold / bind / inspect / publish apps, manage menus / nav / 导航 / 菜单, app snapshots, and the profile-isolated `.openxiangda/state.json` resource map. Trigger on 创建应用 / 新建 app / scaffold app / 初始化工作区 / workspace init / workspace bind / appType / APP_XXX / app snapshot / 应用快照 / 菜单 / menu / nav, or when the user starts work in an empty folder that should become a sy-lowcode-app-workspace.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# OpenXiangda App
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
## When to use this skill
|
|
9
|
+
|
|
10
|
+
- User wants to **create / scaffold a new app** or initialize a `sy-lowcode-app-workspace`.
|
|
11
|
+
- User wants to **bind** the workspace to an existing platform app (`APP_XXX`) for one profile.
|
|
12
|
+
- User asks about **menus / navigation / app snapshot / appType / .openxiangda/state.json shape**.
|
|
13
|
+
- Before scaffolding a real business app: read the architecture-pattern decision via `references/best-practices.md`.
|
|
14
|
+
|
|
15
|
+
## Decision card — new app vs. bind existing
|
|
16
|
+
|
|
17
|
+
| Situation | Action |
|
|
18
|
+
|---|---|
|
|
19
|
+
| Empty folder, no `.openxiangda/state.json`, user did NOT mention an `appType` | `openxiangda workspace init <dir> --profile <name> --app-name "..."` (creates a NEW app) |
|
|
20
|
+
| User explicitly provides `APP_XXX` or asks to reuse an existing app | `workspace init <dir> --profile <name> --app-type APP_XXX` |
|
|
21
|
+
| Existing workspace, no binding for the target profile | `openxiangda workspace bind --profile <name> --app-type APP_XXX` |
|
|
22
|
+
| Workspace already bound for the target profile | use it; never re-bind silently |
|
|
23
|
+
|
|
24
|
+
**Never** search the platform for similar app names to "reuse" — local `.openxiangda/state.json` is authoritative.
|
|
9
25
|
|
|
10
26
|
## Required Context
|
|
11
27
|
|
|
@@ -1,10 +1,46 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: openxiangda-core
|
|
3
|
-
description: Core OpenXiangda CLI workflow
|
|
3
|
+
description: Core OpenXiangda CLI workflow — normal-user token login, platform profiles (dev / prod / staging), workspace init / bind / publish, multi-platform release, environment / version / token diagnostics. Trigger on 发布 / 上线 / 部署 / publish / deploy / ship / release, 登录 / login / 切换平台 / switch profile / token / whoami / auth, `openxiangda env` / `auth status` / `update check`, `OPENXIANGDA_PROFILE / BASE_URL / ACCESS_TOKEN / APP_TYPE`, or any release operation that must inject the profile token into a sy-lowcode-app-workspace publish.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# OpenXiangda Core
|
|
7
7
|
|
|
8
|
+
## When to use this skill
|
|
9
|
+
|
|
10
|
+
- User wants to **publish / deploy / release / 发布 / 上线 / 部署** anything from a `sy-lowcode-app-workspace`.
|
|
11
|
+
- User asks about **login / profile / token / whoami / 切平台 / 多环境**.
|
|
12
|
+
- User runs into **CLI version drift, skill mismatch, missing OSS env, or `OPENXIANGDA_*` env questions**.
|
|
13
|
+
- Any other skill needs to **resolve the active profile / appType / baseUrl** before write operations.
|
|
14
|
+
|
|
15
|
+
## Default publish recipe (read this before any release)
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# 1. preflight
|
|
19
|
+
openxiangda env --profile <name>
|
|
20
|
+
openxiangda auth status --profile <name>
|
|
21
|
+
openxiangda update check --json # if updateAvailable: openxiangda update install
|
|
22
|
+
|
|
23
|
+
# 2. preview the change set (NEVER skip --dry-run on routine edits)
|
|
24
|
+
openxiangda workspace publish --profile <name> --changed --dry-run
|
|
25
|
+
|
|
26
|
+
# 3. publish only what changed
|
|
27
|
+
openxiangda workspace publish --profile <name> --changed
|
|
28
|
+
# or targeted:
|
|
29
|
+
openxiangda workspace publish --profile <name> --page <pageCode>
|
|
30
|
+
openxiangda workspace publish --profile <name> --form <formCode>
|
|
31
|
+
openxiangda workspace publish --profile <name> --only pages/dashboard,forms/customer
|
|
32
|
+
|
|
33
|
+
# 4. full publish only when shared/config changes intentionally affect many modules
|
|
34
|
+
openxiangda workspace publish --profile <name>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## DO NOT
|
|
38
|
+
|
|
39
|
+
- ❌ `pnpm publish:all` / `pnpm publish:oss` / `pnpm register` / `lowcode-workspace publish-*` directly. They are workspace internals and miss `OPENXIANGDA_PROFILE / BASE_URL / ACCESS_TOKEN / APP_TYPE` injection.
|
|
40
|
+
- ❌ Reuse a `formUuid` / `pageId` / `workflowId` / `automationId` from `dev` for `prod`. Each profile has its own resource map under `.openxiangda/state.json`.
|
|
41
|
+
- ❌ Run a full `workspace publish` reflexively after editing one file.
|
|
42
|
+
- ❌ Ask for AK/SK or store tokens in project files.
|
|
43
|
+
|
|
8
44
|
## Login
|
|
9
45
|
|
|
10
46
|
Use ordinary platform login:
|
|
@@ -1,11 +1,46 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: openxiangda-form
|
|
3
|
-
description: Create
|
|
3
|
+
description: Create / edit / publish OpenXiangda normal forms and workflow forms in `sy-lowcode-app-workspace` — schema, fields, options, validation rules, layout, linkedForm select sources, hidden permission scope keys, top-level FormEffect, workflow form bundles, single-form publish. Trigger on 创建表单 / 新建表单 / 表单字段 / 修改 schema / form field / placeholder / options / SelectField / linkedForm / FormEffect / workflow form / 审批表单页 / form schema / formCode / src/forms, or any change to `src/forms/<code>/{schema.ts,page.tsx}`.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# OpenXiangda Form
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
## When to use this skill
|
|
9
|
+
|
|
10
|
+
- User wants to **create / edit a form (`src/forms/<code>/`)**, change fields, options, layout, validation, or top-level FormEffect.
|
|
11
|
+
- User wants a **workflow form page** (used together with `openxiangda-workflow-automation`).
|
|
12
|
+
- User wants to **publish only a form** (`workspace publish --form <code>`) or pull / inspect an existing form schema.
|
|
13
|
+
- User asks how form values are persisted (option `{label, value}`, attachment shape, member fields, etc.).
|
|
14
|
+
|
|
15
|
+
## Quick recipe
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# 1. preflight
|
|
19
|
+
openxiangda env --profile <name>
|
|
20
|
+
openxiangda form list --profile <name>
|
|
21
|
+
|
|
22
|
+
# 2. edit source
|
|
23
|
+
# src/forms/<formCode>/schema.ts — fields, options, rules, top-level FormEffect[]
|
|
24
|
+
# src/forms/<formCode>/page.tsx — presentation only
|
|
25
|
+
|
|
26
|
+
# 3. preview + publish single form
|
|
27
|
+
openxiangda workspace publish --profile <name> --form <formCode> --dry-run
|
|
28
|
+
openxiangda workspace publish --profile <name> --form <formCode>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## DO / DO NOT
|
|
32
|
+
|
|
33
|
+
- ✅ `defineFormSchema` from `openxiangda` and default-export it.
|
|
34
|
+
- ✅ Every visible field has a concise user-facing `placeholder`. Use `tips` only for special constraints.
|
|
35
|
+
- ✅ Use `SelectField` / `RadioField` for enums; for cross-form sources use `SelectField` with `optionSource.type: "linkedForm"` (and `remoteSearch: true` + `searchFieldId` when source is large).
|
|
36
|
+
- ✅ Permission scope keys / sync keys / derived fields stay in schema with `behavior: "HIDDEN"`, derived from visible select/person/department fields via `valueSync`.
|
|
37
|
+
- ✅ Always provide `options` for option components (Select / MultiSelect / Radio / Checkbox / CascadeSelect).
|
|
38
|
+
- ❌ `AssociationFormField` for new form work (use `linkedForm` SelectField).
|
|
39
|
+
- ❌ Top-level `schema.rules` as validation array. Top-level `rules` is `FormEffect[]` only (`when` / `then`).
|
|
40
|
+
- ❌ Extra fields for system metadata (creator / updater / created/updated time / depts) — platform creates them automatically.
|
|
41
|
+
- ❌ Developer notes / implementation comments inside labels / placeholders / tips / section titles / empty states.
|
|
42
|
+
- ❌ `openxiangda form create` as page generation; it is a low-level repair command.
|
|
43
|
+
- ❌ Copy `formUuid` from dev to prod — each profile has its own `formUuid` under `.openxiangda/state.json`.
|
|
9
44
|
|
|
10
45
|
## Required Workspace Flow
|
|
11
46
|
|
|
@@ -1,11 +1,35 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: openxiangda-inspect
|
|
3
|
-
description: Read-only OpenXiangda diagnosis
|
|
3
|
+
description: **Read-only** OpenXiangda diagnosis — app snapshots (应用快照), forms, workflows, automations, automation executions / logs, permissions, profile drift, version mismatch, missing OSS env, broken bundle URLs, page render failures (`options is undefined`, missing styles, raw value labels). Trigger on 诊断 / 排查 / 看一下 / 对比 / diagnose / inspect / debug / why / 为什么 / 报错 / error / failure / 跳越 / drift / snapshot / 快照, or any read-only verification before / after a change. Switch to the relevant write skill (`openxiangda-form` / `-page` / `-workflow-automation` / `-permission-settings`) for remediation.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# OpenXiangda Inspect
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
## When to use this skill
|
|
9
|
+
|
|
10
|
+
- User wants to **see what's on the platform** for an app / form / page / workflow / automation / permission group.
|
|
11
|
+
- User reports a **bug, error, drift, or unexpected platform state** and you need evidence before changing anything.
|
|
12
|
+
- You need to **compare** local `.openxiangda/state.json` with the live platform.
|
|
13
|
+
- You need **automation execution traces / logs** to understand why a trigger didn't fire or a node failed.
|
|
14
|
+
|
|
15
|
+
## Decision card
|
|
16
|
+
|
|
17
|
+
| Scenario | Command |
|
|
18
|
+
|---|---|
|
|
19
|
+
| Before any non-trivial edit to an existing app | `openxiangda app snapshot APP_XXX --profile <name> --json` |
|
|
20
|
+
| Form schema looks wrong on platform | `openxiangda inspect form <code> --profile <name> --json` |
|
|
21
|
+
| Workflow / automation doesn't run as expected | `openxiangda inspect workflow <code> --profile <name> --json` / `automation executions <code>` / `automation logs` / `automation diagnose <code>` |
|
|
22
|
+
| Permission visibility doesn't match expectation | `openxiangda inspect permissions <formCode> --profile <name> --json` |
|
|
23
|
+
| Local state out of sync with platform | `openxiangda form list / page list / workflow list / automation list --profile <name>` |
|
|
24
|
+
|
|
25
|
+
## Rules
|
|
26
|
+
|
|
27
|
+
- ✅ **Read-only.** This skill never writes / publishes / mutates.
|
|
28
|
+
- ✅ Use **logical local codes** first; pass live IDs only when the code is missing from `.openxiangda/state.json`, and only for the current profile.
|
|
29
|
+
- ✅ Cross-check unexpected values against `references/platform-data-model.md` (option `{label, value}`, attachment shape, member fields, JSONB) before claiming a bug.
|
|
30
|
+
- ✅ Cross-check render / runtime issues against `references/troubleshooting.md` before patching symptoms.
|
|
31
|
+
- ❌ Treat IDs from another profile as evidence — always confirm with `openxiangda env --profile <name>` first.
|
|
32
|
+
- ❌ Mutate from this skill. For remediation, hand off to the relevant write skill.
|
|
9
33
|
|
|
10
34
|
## Commands
|
|
11
35
|
|