openxiangda 1.0.268 → 1.0.270

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 (42) hide show
  1. package/README.md +20 -1
  2. package/lib/cli.js +417 -24
  3. package/lib/config.js +60 -56
  4. package/lib/design-gates.js +18 -0
  5. package/lib/migration-advice.js +19 -0
  6. package/lib/release-plan.js +2 -0
  7. package/lib/workspace-init.js +11 -4
  8. package/openxiangda-skills/SKILL.md +8 -0
  9. package/openxiangda-skills/references/architecture-patterns.md +218 -218
  10. package/openxiangda-skills/references/component-guide.md +122 -122
  11. package/openxiangda-skills/references/openxiangda-api.md +12 -0
  12. package/openxiangda-skills/references/platform-data-model.md +285 -285
  13. package/openxiangda-skills/references/resource-manifest-cheatsheet.md +32 -2
  14. package/openxiangda-skills/references/troubleshooting.md +224 -224
  15. package/openxiangda-skills/references/webhooks.md +213 -0
  16. package/openxiangda-skills/references/workspace-state.md +1 -1
  17. package/openxiangda-skills/skills/openxiangda-core/SKILL.md +1 -1
  18. package/openxiangda-skills/skills/openxiangda-workflow-automation/SKILL.md +3 -0
  19. package/package.json +26 -23
  20. package/packages/sdk/dist/{ProcessPreview-Cyk6uv-w.d.mts → ProcessPreview-DSUIJi5V.d.mts} +2 -3
  21. package/packages/sdk/dist/{ProcessPreview-Cyk6uv-w.d.ts → ProcessPreview-DSUIJi5V.d.ts} +2 -3
  22. package/packages/sdk/dist/components/index.d.mts +41 -42
  23. package/packages/sdk/dist/components/index.d.ts +41 -42
  24. package/packages/sdk/dist/{dataManagementApi-CE8Zyj3a.d.ts → dataManagementApi-Ro9itwm8.d.ts} +2 -2
  25. package/packages/sdk/dist/{dataManagementApi-4fSaCA5t.d.mts → dataManagementApi-p_HOhQnA.d.mts} +2 -2
  26. package/packages/sdk/dist/runtime/index.d.mts +3 -4
  27. package/packages/sdk/dist/runtime/index.d.ts +3 -4
  28. package/packages/sdk/dist/runtime/react.d.mts +5 -5
  29. package/packages/sdk/dist/runtime/react.d.ts +5 -5
  30. package/packages/sdk/src/build-source/scripts/utils/mime-types.mjs +70 -70
  31. package/packages/sdk/src/build-source/scripts/utils/oss-client.mjs +128 -128
  32. package/packages/sdk/src/build-source/scripts/utils/progress.mjs +57 -57
  33. package/templates/openxiangda-react-spa/.cursor/rules/openxiangda-resources.mdc +1 -0
  34. package/templates/openxiangda-react-spa/.cursor/rules/openxiangda.mdc +6 -0
  35. package/templates/openxiangda-react-spa/.qoder/rules/openxiangda-resources.md +1 -0
  36. package/templates/openxiangda-react-spa/.qoder/rules/openxiangda.md +6 -0
  37. package/templates/openxiangda-react-spa/AGENTS.md +8 -0
  38. package/templates/sy-lowcode-app-workspace/.cursor/rules/openxiangda-resources.mdc +2 -0
  39. package/templates/sy-lowcode-app-workspace/.cursor/rules/openxiangda.mdc +7 -1
  40. package/templates/sy-lowcode-app-workspace/.qoder/rules/openxiangda-resources.md +1 -0
  41. package/templates/sy-lowcode-app-workspace/.qoder/rules/openxiangda.md +7 -1
  42. package/templates/sy-lowcode-app-workspace/AGENTS.md +9 -1
@@ -683,7 +683,37 @@ const result = await sdk.function.invoke("reservation_reminder_summary", {
683
683
 
684
684
  适用边界:可复用后端业务逻辑、跨页面/自动化/流程共享的查询编排、连接器调用、通知编排、受控平台 API 调用。App Function 支持 `ctx.form.queryOne/queryMany/getById/createOne/updateOne/updateById`、`ctx.dataView`、`ctx.connector`、`ctx.notification`、`ctx.platform.roles`、`ctx.platform.api` 等受控 helper,当前 MVP 不暴露原始 SQL/Redis。应用角色查询和成员维护优先使用 `ctx.platform.roles.list/findByCode/addUsers/removeUser`;底层 `ctx.platform.api` 返回 HTTP 包装与平台 envelope,需要自行解包。已发布可信代码可以访问当前租户、当前应用内的资源;function manifest 的 `resources` 是可选映射、审计和影响分析信息,不再是逐函数权限白名单。页面用户仍不能直接提交内部表单,跨应用和跨租户访问仍被拒绝。运行时接口默认需要应用自动化管理权限;普通用户页面要调用时,用 `definitionJson.runtimeInvoke.audience` 声明 `authenticated`、`page_permission_group`、`app_roles`、`platform_roles` 或 `scope_policy`,使用 `roleCodes` 匹配应用角色、使用 `platformRoleCodes` 匹配同步身份 `SCHOOL_GUARDIAN`、`SCHOOL_STUDENT`、`SCHOOL_TEACHER`,不要把 `"*"`、`"all-app-roles"` 写进角色编码。若表单只能由函数/流程写入,在 `src/resources/settings/forms/<formCode>.json` 设置 `runtimeWrite.mode="function_only"` 关闭原始写入接口。
685
685
 
686
- ## 5. Workflow — `src/resources/workflows/<code>/workflow.json`(manifest)+ `src/workflows/<code>/workflow.ts`(代码优先)
686
+ ## 5. Inbound Webhook — `src/resources/webhooks/<code>.json`
687
+
688
+ ```json
689
+ {
690
+ "code": "yuquan_access",
691
+ "name": "玉泉门禁开门事件",
692
+ "targetFunctionCode": "qfyy_access_event",
693
+ "idempotencyQueryParam": "nonce",
694
+ "maxBodyBytes": 262144,
695
+ "status": "active"
696
+ }
697
+ ```
698
+
699
+ Webhook 只声明公开入口到固定 App Function 的映射,不包含 Secret 或验签规则。
700
+ 供应商 Secret 在目标 Function 顶层 `secretRefs` 声明,源码通过
701
+ `await ctx.secrets.get(name)` 读取,并且必须在任何表单查询、写入、连接器或通知
702
+ 调用之前使用 `input.rawBody` 验签。平台保存原始 UTF-8 Body、Base64 Body、原始
703
+ Query 字符串、重复参数数组、解析 JSON 和安全请求头;投递为 at-least-once,应用
704
+ 还必须用 `input.idempotencyKey` 对业务写入做幂等保护。
705
+
706
+ ```bash
707
+ openxiangda resource validate webhook --profile <name>
708
+ openxiangda resource plan webhook --only yuquan_access --profile <name> --json
709
+ openxiangda resource publish webhook --only yuquan_access --change <id> --profile <name>
710
+ openxiangda webhook deliveries yuquan_access --profile <name> --json
711
+ ```
712
+
713
+ 完整 Function 输入、HMAC-SHA1 常量时间比较、返回状态和玉泉门禁示例见
714
+ [`webhooks.md`](webhooks.md)。
715
+
716
+ ## 6. Workflow — `src/resources/workflows/<code>/workflow.json`(manifest)+ `src/workflows/<code>/workflow.ts`(代码优先)
687
717
 
688
718
  ```jsonc
689
719
  // src/resources/workflows/customer_approval/workflow.json
@@ -713,7 +743,7 @@ export default defineWorkflow({
713
743
 
714
744
  CLI 编译为 `definition.v3.json` + `preview.json`,平台运行时仍走标准工作流引擎。完整规则见 [`workflow-v3.md`](workflow-v3.md)。
715
745
 
716
- ## 6. Automation — `src/resources/automations/<code>/{definition.code.json,preview.json}` + `src/automations/<code>/index.ts`
746
+ ## 7. Automation — `src/resources/automations/<code>/{definition.code.json,preview.json}` + `src/automations/<code>/index.ts`
717
747
 
718
748
  ```jsonc
719
749
  // src/resources/automations/notify_on_submit/definition.code.json
@@ -1,23 +1,23 @@
1
- # OpenXiangda 常见问题排查指南
2
-
3
- > 本文档面向 AI Agent,收录在实际低代码项目(如 zjnu-dxyq-lowcode)开发中遇到的典型问题及解决方案。遇到报错或异常现象时,请优先在此查找匹配项;若无匹配,再结合源码与日志分析。
4
-
5
- ## 快速索引
6
-
7
- | # | 问题 | 关键报错 / 现象 |
8
- | --- | --- | --- |
9
- | 1 | antd/antd-mobile 弹层样式丢失 | Select/Modal 弹层无样式或跑到屏幕外 |
10
- | 2 | SelectField options 为 undefined 导致 .map 报错 | `Cannot read properties of undefined (reading 'map')` |
11
- | 3 | ESM 模块动态导入路径错误 | `ERR_UNSUPPORTED_ESM_URL_SCHEME` / `Cannot find module` |
12
- | 4 | Windows 环境 OSS 上传路径错误 | 发布后资源 404,URL 含 `\` |
13
- | 5 | 构建时表单 API 常量替换失败 | formApi.ts 中环境变量未替换 |
14
- | 6 | Tailwind 样式被清除(PurgeCSS) | 生产构建后样式丢失 |
15
- | 7 | React 多实例冲突 | `Invalid hook call` |
16
- | 8 | 移动端 antd-mobile 组件样式不生效 | 组件渲染但无样式 |
1
+ # OpenXiangda 常见问题排查指南
2
+
3
+ > 本文档面向 AI Agent,收录在实际低代码项目(如 zjnu-dxyq-lowcode)开发中遇到的典型问题及解决方案。遇到报错或异常现象时,请优先在此查找匹配项;若无匹配,再结合源码与日志分析。
4
+
5
+ ## 快速索引
6
+
7
+ | # | 问题 | 关键报错 / 现象 |
8
+ | --- | --- | --- |
9
+ | 1 | antd/antd-mobile 弹层样式丢失 | Select/Modal 弹层无样式或跑到屏幕外 |
10
+ | 2 | SelectField options 为 undefined 导致 .map 报错 | `Cannot read properties of undefined (reading 'map')` |
11
+ | 3 | ESM 模块动态导入路径错误 | `ERR_UNSUPPORTED_ESM_URL_SCHEME` / `Cannot find module` |
12
+ | 4 | Windows 环境 OSS 上传路径错误 | 发布后资源 404,URL 含 `\` |
13
+ | 5 | 构建时表单 API 常量替换失败 | formApi.ts 中环境变量未替换 |
14
+ | 6 | Tailwind 样式被清除(PurgeCSS) | 生产构建后样式丢失 |
15
+ | 7 | React 多实例冲突 | `Invalid hook call` |
16
+ | 8 | 移动端 antd-mobile 组件样式不生效 | 组件渲染但无样式 |
17
17
  | 9 | 数据格式理解错误导致展示异常 | 页面显示 `[object Object]` |
18
18
  | 10 | workspace publish 环境变量缺失 | `OPENXIANGDA_ACCESS_TOKEN is not set` |
19
19
  | 11 | DOCX 二进制契约异常 | 真实字节不是 `PK`,或自定义 FormProvider 把 Blob 请求错误转给 JSON 通道 |
20
-
20
+
21
21
  ---
22
22
 
23
23
  ## 问题:DOCX 响应是 Base64 文本
@@ -45,9 +45,9 @@
45
45
  ---
46
46
 
47
47
  ## 问题:antd/antd-mobile 弹层样式丢失
48
-
49
- **症状**:Select / Dropdown / Popup / Modal 等组件的弹出层没有样式,直接跑到屏幕外,或呈现为无样式的裸 HTML。
50
-
48
+
49
+ **症状**:Select / Dropdown / Popup / Modal 等组件的弹出层没有样式,直接跑到屏幕外,或呈现为无样式的裸 HTML。
50
+
51
51
  **根因**:新页面默认 `cssIsolation: "none"`,弹层使用 `document.body` 和默认 Ant Design 样式即可。只有历史页面显式启用 `cssIsolation: "namespace" | "shadow"` 时,弹层渲染到 `document.body` 才可能脱离 legacy 样式作用域;移动端 Popup 也可能因 namespace 未被继承而失去样式。
52
52
 
53
53
  **解决方案**:
@@ -60,212 +60,212 @@
60
60
  ```tsx
61
61
  import { useRef } from 'react';
62
62
  import { ConfigProvider } from 'antd';
63
-
64
- const rootRef = useRef<HTMLDivElement>(null);
65
-
66
- <div ref={rootRef} className="sy-app-workspace">
67
- <ConfigProvider getPopupContainer={() => rootRef.current!}>
68
- <App />
69
- </ConfigProvider>
70
- </div>
71
- ```
72
-
73
- **预防措施**:在 workspace 初始化阶段就统一约定弹层容器策略,避免后续逐个组件修复。
74
-
75
- ---
76
-
77
- ## 问题:SelectField options 为 undefined 导致 .map 报错
78
-
79
- **症状**:渲染 `SelectField` / `RadioField` / `CheckboxField` 时抛出 `Cannot read properties of undefined (reading 'map')`。
80
-
81
- **根因**:这些需要枚举值的字段组件在 schema 中未提供 `options` 属性,组件内部直接调用 `options.map(...)`。
82
-
83
- **解决方案**:
84
- - 确保所有选择类字段在 schema 中至少包含 `options: []`。
85
- - 使用 `createFormSchema` 工厂函数,它会自动为缺失的 options 字段补齐空数组。
86
-
87
- **预防措施**:
88
- ```typescript
89
- // form-schema.ts 中的保护逻辑
90
- const NEEDS_OPTIONS = ['SelectField', 'RadioField', 'CheckboxField'];
91
-
92
- for (const field of schema.fields) {
93
- if (NEEDS_OPTIONS.includes(field.componentName) && !Array.isArray(field.options)) {
94
- field.options = [];
95
- }
96
- }
97
- ```
98
-
99
- ---
100
-
101
- ## 问题:ESM 模块动态导入路径错误
102
-
103
- **症状**:执行 `sync-schema` 等脚本时报 `ERR_UNSUPPORTED_ESM_URL_SCHEME` 或 `Cannot find module`。
104
-
105
- **根因**:Node.js 的 `import()` 在部分环境下(尤其是 Windows)不接受纯文件路径,必须使用 `file://` URL 形式。
106
-
107
- **解决方案**:
108
- ```javascript
109
- import { pathToFileURL } from 'url';
110
-
111
- // 错误写法(可能在 Windows 下报错)
112
- const module = await import(tmpFile);
113
-
114
- // 正确写法
115
- const module = await import(pathToFileURL(tmpFile).href);
116
- ```
117
-
118
- **预防措施**:所有动态 `import()` 文件路径统一通过 `pathToFileURL().href` 转换,CI 中加入 Windows 平台冒烟测试。
119
-
120
- ---
121
-
122
- ## 问题:Windows 环境 OSS 上传路径错误
123
-
124
- **症状**:发布完成后访问页面,CSS / JS 资源返回 404,浏览器 Network 面板中可见 URL 内出现反斜杠 `\`。
125
-
126
- **根因**:Windows 文件系统使用 `\` 作为路径分隔符,而 OSS 对象 key 必须使用 `/`,未做归一化时直接拼接路径会导致非法 key。
127
-
128
- **解决方案**:
129
- ```javascript
130
- function toOssKeyPath(file) {
131
- return String(file).replace(/\\/g, '/');
132
- }
133
-
134
- const ossKey = toOssKeyPath(path.relative(distDir, absFile));
135
- await ossClient.put(ossKey, absFile);
136
- ```
137
-
138
- **预防措施**:在上传函数入口处统一调用 `toOssKeyPath`,禁止直接拼接原始路径。
139
-
140
- ---
141
-
142
- ## 问题:构建时表单 API 常量替换失败
143
-
144
- **症状**:生成的 `formApi.ts` 中,环境变量(如 `APP_ID`、`BASE_URL`)等常量值没有被正确替换为目标 profile 的真实值。
145
-
146
- **根因**:旧的替换正则只覆盖了简单字符串字面量,无法匹配对象字面量、函数调用、模板字符串等复杂初始化表达式。
147
-
148
- **解决方案**:使用更宽松的正则匹配 `=` 到 `;` 之间的任意表达式:
149
- ```javascript
150
- const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
151
- const regex = new RegExp(
152
- `((?:export\\s+)?const\\s+${escaped}\\s*=\\s*)([^;]+);`
153
- );
154
- source = source.replace(regex, (_, prefix) => `${prefix}${JSON.stringify(value)};`);
155
- ```
156
-
157
- **预防措施**:替换后立即写一个 sanity check,比较替换前后的 AST 是否仅常量值发生变化。
158
-
159
- ---
160
-
161
- ## 问题:Tailwind 样式被清除(PurgeCSS 问题)
162
-
163
- **症状**:开发环境(`vite dev`)下页面样式正常,但 `vite build` 生产产物中部分组件样式丢失。
164
-
165
- **根因**:Tailwind v3+ 的 `content` 配置未包含来自 SDK / 组件库的源文件,组件库内使用的类名在编译期被 PurgeCSS 误删。
166
-
167
- **解决方案**:
168
- ```javascript
169
- // tailwind.config.cjs
170
- module.exports = {
171
- content: [
172
- './src/**/*.{ts,tsx}',
63
+
64
+ const rootRef = useRef<HTMLDivElement>(null);
65
+
66
+ <div ref={rootRef} className="sy-app-workspace">
67
+ <ConfigProvider getPopupContainer={() => rootRef.current!}>
68
+ <App />
69
+ </ConfigProvider>
70
+ </div>
71
+ ```
72
+
73
+ **预防措施**:在 workspace 初始化阶段就统一约定弹层容器策略,避免后续逐个组件修复。
74
+
75
+ ---
76
+
77
+ ## 问题:SelectField options 为 undefined 导致 .map 报错
78
+
79
+ **症状**:渲染 `SelectField` / `RadioField` / `CheckboxField` 时抛出 `Cannot read properties of undefined (reading 'map')`。
80
+
81
+ **根因**:这些需要枚举值的字段组件在 schema 中未提供 `options` 属性,组件内部直接调用 `options.map(...)`。
82
+
83
+ **解决方案**:
84
+ - 确保所有选择类字段在 schema 中至少包含 `options: []`。
85
+ - 使用 `createFormSchema` 工厂函数,它会自动为缺失的 options 字段补齐空数组。
86
+
87
+ **预防措施**:
88
+ ```typescript
89
+ // form-schema.ts 中的保护逻辑
90
+ const NEEDS_OPTIONS = ['SelectField', 'RadioField', 'CheckboxField'];
91
+
92
+ for (const field of schema.fields) {
93
+ if (NEEDS_OPTIONS.includes(field.componentName) && !Array.isArray(field.options)) {
94
+ field.options = [];
95
+ }
96
+ }
97
+ ```
98
+
99
+ ---
100
+
101
+ ## 问题:ESM 模块动态导入路径错误
102
+
103
+ **症状**:执行 `sync-schema` 等脚本时报 `ERR_UNSUPPORTED_ESM_URL_SCHEME` 或 `Cannot find module`。
104
+
105
+ **根因**:Node.js 的 `import()` 在部分环境下(尤其是 Windows)不接受纯文件路径,必须使用 `file://` URL 形式。
106
+
107
+ **解决方案**:
108
+ ```javascript
109
+ import { pathToFileURL } from 'url';
110
+
111
+ // 错误写法(可能在 Windows 下报错)
112
+ const module = await import(tmpFile);
113
+
114
+ // 正确写法
115
+ const module = await import(pathToFileURL(tmpFile).href);
116
+ ```
117
+
118
+ **预防措施**:所有动态 `import()` 文件路径统一通过 `pathToFileURL().href` 转换,CI 中加入 Windows 平台冒烟测试。
119
+
120
+ ---
121
+
122
+ ## 问题:Windows 环境 OSS 上传路径错误
123
+
124
+ **症状**:发布完成后访问页面,CSS / JS 资源返回 404,浏览器 Network 面板中可见 URL 内出现反斜杠 `\`。
125
+
126
+ **根因**:Windows 文件系统使用 `\` 作为路径分隔符,而 OSS 对象 key 必须使用 `/`,未做归一化时直接拼接路径会导致非法 key。
127
+
128
+ **解决方案**:
129
+ ```javascript
130
+ function toOssKeyPath(file) {
131
+ return String(file).replace(/\\/g, '/');
132
+ }
133
+
134
+ const ossKey = toOssKeyPath(path.relative(distDir, absFile));
135
+ await ossClient.put(ossKey, absFile);
136
+ ```
137
+
138
+ **预防措施**:在上传函数入口处统一调用 `toOssKeyPath`,禁止直接拼接原始路径。
139
+
140
+ ---
141
+
142
+ ## 问题:构建时表单 API 常量替换失败
143
+
144
+ **症状**:生成的 `formApi.ts` 中,环境变量(如 `APP_ID`、`BASE_URL`)等常量值没有被正确替换为目标 profile 的真实值。
145
+
146
+ **根因**:旧的替换正则只覆盖了简单字符串字面量,无法匹配对象字面量、函数调用、模板字符串等复杂初始化表达式。
147
+
148
+ **解决方案**:使用更宽松的正则匹配 `=` 到 `;` 之间的任意表达式:
149
+ ```javascript
150
+ const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
151
+ const regex = new RegExp(
152
+ `((?:export\\s+)?const\\s+${escaped}\\s*=\\s*)([^;]+);`
153
+ );
154
+ source = source.replace(regex, (_, prefix) => `${prefix}${JSON.stringify(value)};`);
155
+ ```
156
+
157
+ **预防措施**:替换后立即写一个 sanity check,比较替换前后的 AST 是否仅常量值发生变化。
158
+
159
+ ---
160
+
161
+ ## 问题:Tailwind 样式被清除(PurgeCSS 问题)
162
+
163
+ **症状**:开发环境(`vite dev`)下页面样式正常,但 `vite build` 生产产物中部分组件样式丢失。
164
+
165
+ **根因**:Tailwind v3+ 的 `content` 配置未包含来自 SDK / 组件库的源文件,组件库内使用的类名在编译期被 PurgeCSS 误删。
166
+
167
+ **解决方案**:
168
+ ```javascript
169
+ // tailwind.config.cjs
170
+ module.exports = {
171
+ content: [
172
+ './src/**/*.{ts,tsx}',
173
173
  './node_modules/openxiangda/packages/sdk/dist/**/*.{mjs,cjs}', // 关键:包含 SDK 分发文件
174
174
  ],
175
175
  presets: [require('openxiangda/tailwind-preset')],
176
176
  };
177
- ```
178
-
179
- **预防措施**:每次新增第三方组件库时,同步将其分发产物路径加入 `content` 数组;将 `pnpm build` 纳入 CI,对比构建产物体积异常增减。
180
-
181
- ---
182
-
183
- ## 问题:React 多实例冲突
184
-
185
- **症状**:运行时抛出 `Invalid hook call. Hooks can only be called inside of the body of a function component.`,组件中所有 hooks 失效。
186
-
187
- **根因**:项目最终打包产物中存在多个 React 实例,通常是因为组件库与宿主项目各自引入了不同版本的 `react` / `react-dom`。
188
-
189
- **解决方案**:
190
- ```typescript
191
- // vite.config.ts
192
- import { defineConfig } from 'vite';
193
-
194
- export default defineConfig({
195
- resolve: {
196
- dedupe: ['react', 'react-dom', 'antd'],
197
- },
198
- });
199
- ```
200
-
201
- **预防措施**:
202
- - 在 `package.json` 中将 `react` / `react-dom` 设为 SDK 的 `peerDependencies`。
203
- - 使用 `pnpm why react` 排查重复依赖。
204
-
205
- ---
206
-
207
- ## 问题:移动端 antd-mobile 组件样式不生效
208
-
209
- **症状**:antd-mobile 组件 DOM 已渲染,但样式全部丢失,呈现裸 HTML。
210
-
211
- **根因**:antd-mobile 使用 CSS-in-JS / 预编译样式,可能被 Tailwind 的 preflight 重置覆盖,或受 namespace 隔离影响。
212
-
213
- **解决方案**:
214
- 1. 在 `vite.config.ts` 的 `optimizeDeps.include` 中显式添加 `antd-mobile`,避免运行时多次预构建。
215
- 2. 调整 Tailwind preflight 策略,确保不会覆盖 antd-mobile 的基础样式(必要时关闭 preflight 或使用 `important` 选择器)。
177
+ ```
178
+
179
+ **预防措施**:每次新增第三方组件库时,同步将其分发产物路径加入 `content` 数组;将 `pnpm build` 纳入 CI,对比构建产物体积异常增减。
180
+
181
+ ---
182
+
183
+ ## 问题:React 多实例冲突
184
+
185
+ **症状**:运行时抛出 `Invalid hook call. Hooks can only be called inside of the body of a function component.`,组件中所有 hooks 失效。
186
+
187
+ **根因**:项目最终打包产物中存在多个 React 实例,通常是因为组件库与宿主项目各自引入了不同版本的 `react` / `react-dom`。
188
+
189
+ **解决方案**:
190
+ ```typescript
191
+ // vite.config.ts
192
+ import { defineConfig } from 'vite';
193
+
194
+ export default defineConfig({
195
+ resolve: {
196
+ dedupe: ['react', 'react-dom', 'antd'],
197
+ },
198
+ });
199
+ ```
200
+
201
+ **预防措施**:
202
+ - 在 `package.json` 中将 `react` / `react-dom` 设为 SDK 的 `peerDependencies`。
203
+ - 使用 `pnpm why react` 排查重复依赖。
204
+
205
+ ---
206
+
207
+ ## 问题:移动端 antd-mobile 组件样式不生效
208
+
209
+ **症状**:antd-mobile 组件 DOM 已渲染,但样式全部丢失,呈现裸 HTML。
210
+
211
+ **根因**:antd-mobile 使用 CSS-in-JS / 预编译样式,可能被 Tailwind 的 preflight 重置覆盖,或受 namespace 隔离影响。
212
+
213
+ **解决方案**:
214
+ 1. 在 `vite.config.ts` 的 `optimizeDeps.include` 中显式添加 `antd-mobile`,避免运行时多次预构建。
215
+ 2. 调整 Tailwind preflight 策略,确保不会覆盖 antd-mobile 的基础样式(必要时关闭 preflight 或使用 `important` 选择器)。
216
216
  3. 仅 legacy `namespace/shadow` 页面需要确保 antd-mobile 组件渲染在 `sy-app-workspace` namespace 容器内,弹层 `getContainer` 指向该容器;新页面默认不需要。
217
-
218
- **示例**:
219
- ```typescript
220
- // vite.config.ts
221
- export default defineConfig({
222
- optimizeDeps: {
223
- include: ['antd-mobile', 'antd-mobile/es/locales/zh-CN'],
224
- },
225
- });
226
- ```
227
-
228
- **预防措施**:移动端项目模板中预置好 antd-mobile 的 vite 与 Tailwind 配置,禁止业务方随意修改 preflight。
229
-
230
- ---
231
-
232
- ## 问题:数据格式理解错误导致展示异常
233
-
234
- **症状**:页面显示 `[object Object]`、空白,或筛选条件无法命中数据。
235
-
236
- **根因**:AI 将平台返回的 `{label, value}` 结构字段当作纯字符串处理,直接渲染对象到 JSX。
237
-
238
- **解决方案**:参考 `platform-data-model.md`,选择类字段在平台中存储为对象(单选为对象,多选为对象数组),渲染时需取 `label` 字段:
239
- ```tsx
240
- // 错误:直接展示对象
241
- <span>{record.department}</span> // 显示 [object Object]
242
-
243
- // 正确:取 label
244
- <span>{record.department?.label}</span>
245
-
246
- // 多选字段
247
- <span>{record.tags?.map(t => t.label).join(', ')}</span>
248
- ```
249
-
250
- **预防措施**:
251
- - 编辑器中为选择类字段统一定义 `OptionValue = { label: string; value: string }` 类型。
252
- - 列表 / 详情页渲染前,封装 `formatOption(value)` 工具函数。
253
-
254
- ---
255
-
256
- ## 问题:workspace publish 环境变量缺失
257
-
258
- **症状**:执行发布命令时报错 `OPENXIANGDA_ACCESS_TOKEN is not set` 或类似缺少配置项的错误。
259
-
260
- **根因**:没有通过 `openxiangda` CLI 触发发布,CLI 负责从安全存储读取 profile 配置并注入到子进程的环境变量中;直接运行 `pnpm` 脚本会绕过这一步骤。
261
-
262
- **解决方案**:
263
- - 始终通过 CLI 发布:
264
- ```bash
265
- openxiangda workspace publish --profile <profile-name>
266
- ```
267
- - **不要**直接执行 `pnpm run publish:all` / `node scripts/publish.js`,这些命令缺少 token 注入。
268
-
269
- **预防措施**:
270
- - 在 workspace 的 `package.json` 中将真正的发布脚本设为内部脚本(前缀 `_`),README 与 SKILL 文档统一引导走 CLI 入口。
271
- - 必要时在脚本入口处增加守卫,检测到 `OPENXIANGDA_ACCESS_TOKEN` 缺失时输出明确指引:`请通过 openxiangda workspace publish 发布`。
217
+
218
+ **示例**:
219
+ ```typescript
220
+ // vite.config.ts
221
+ export default defineConfig({
222
+ optimizeDeps: {
223
+ include: ['antd-mobile', 'antd-mobile/es/locales/zh-CN'],
224
+ },
225
+ });
226
+ ```
227
+
228
+ **预防措施**:移动端项目模板中预置好 antd-mobile 的 vite 与 Tailwind 配置,禁止业务方随意修改 preflight。
229
+
230
+ ---
231
+
232
+ ## 问题:数据格式理解错误导致展示异常
233
+
234
+ **症状**:页面显示 `[object Object]`、空白,或筛选条件无法命中数据。
235
+
236
+ **根因**:AI 将平台返回的 `{label, value}` 结构字段当作纯字符串处理,直接渲染对象到 JSX。
237
+
238
+ **解决方案**:参考 `platform-data-model.md`,选择类字段在平台中存储为对象(单选为对象,多选为对象数组),渲染时需取 `label` 字段:
239
+ ```tsx
240
+ // 错误:直接展示对象
241
+ <span>{record.department}</span> // 显示 [object Object]
242
+
243
+ // 正确:取 label
244
+ <span>{record.department?.label}</span>
245
+
246
+ // 多选字段
247
+ <span>{record.tags?.map(t => t.label).join(', ')}</span>
248
+ ```
249
+
250
+ **预防措施**:
251
+ - 编辑器中为选择类字段统一定义 `OptionValue = { label: string; value: string }` 类型。
252
+ - 列表 / 详情页渲染前,封装 `formatOption(value)` 工具函数。
253
+
254
+ ---
255
+
256
+ ## 问题:workspace publish 环境变量缺失
257
+
258
+ **症状**:执行发布命令时报错 `OPENXIANGDA_ACCESS_TOKEN is not set` 或类似缺少配置项的错误。
259
+
260
+ **根因**:没有通过 `openxiangda` CLI 触发发布,CLI 负责从安全存储读取 profile 配置并注入到子进程的环境变量中;直接运行 `pnpm` 脚本会绕过这一步骤。
261
+
262
+ **解决方案**:
263
+ - 始终通过 CLI 发布:
264
+ ```bash
265
+ openxiangda workspace publish --profile <profile-name>
266
+ ```
267
+ - **不要**直接执行 `pnpm run publish:all` / `node scripts/publish.js`,这些命令缺少 token 注入。
268
+
269
+ **预防措施**:
270
+ - 在 workspace 的 `package.json` 中将真正的发布脚本设为内部脚本(前缀 `_`),README 与 SKILL 文档统一引导走 CLI 入口。
271
+ - 必要时在脚本入口处增加守卫,检测到 `OPENXIANGDA_ACCESS_TOKEN` 缺失时输出明确指引:`请通过 openxiangda workspace publish 发布`。