openxiangda 1.0.0

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 (121) hide show
  1. package/README.md +58 -0
  2. package/bin/openxiangda.js +11 -0
  3. package/lib/cli.js +2423 -0
  4. package/lib/config.js +121 -0
  5. package/lib/http.js +47 -0
  6. package/lib/skills.js +371 -0
  7. package/lib/utils.js +87 -0
  8. package/lib/workspace-init.js +139 -0
  9. package/openxiangda-skills/SKILL.md +128 -0
  10. package/openxiangda-skills/references/architecture-patterns.md +242 -0
  11. package/openxiangda-skills/references/automation-v3.md +129 -0
  12. package/openxiangda-skills/references/component-guide.md +198 -0
  13. package/openxiangda-skills/references/forms/component-registry.md +53 -0
  14. package/openxiangda-skills/references/forms/form-schema.md +109 -0
  15. package/openxiangda-skills/references/forms/layout-and-rules.md +24 -0
  16. package/openxiangda-skills/references/openxiangda-api.md +466 -0
  17. package/openxiangda-skills/references/pages/page-sdk.md +13 -0
  18. package/openxiangda-skills/references/pages/publish-flow.md +36 -0
  19. package/openxiangda-skills/references/pages/workspace-structure.md +38 -0
  20. package/openxiangda-skills/references/permissions-settings.md +147 -0
  21. package/openxiangda-skills/references/platform-data-model.md +305 -0
  22. package/openxiangda-skills/references/style-system.md +492 -0
  23. package/openxiangda-skills/references/troubleshooting.md +246 -0
  24. package/openxiangda-skills/references/workflow-v3.md +105 -0
  25. package/openxiangda-skills/references/workspace-state.md +45 -0
  26. package/openxiangda-skills/skills/openxiangda-app/SKILL.md +64 -0
  27. package/openxiangda-skills/skills/openxiangda-core/SKILL.md +143 -0
  28. package/openxiangda-skills/skills/openxiangda-form/SKILL.md +76 -0
  29. package/openxiangda-skills/skills/openxiangda-inspect/SKILL.md +40 -0
  30. package/openxiangda-skills/skills/openxiangda-page/SKILL.md +62 -0
  31. package/openxiangda-skills/skills/openxiangda-permission-settings/SKILL.md +95 -0
  32. package/openxiangda-skills/skills/openxiangda-workflow-automation/SKILL.md +97 -0
  33. package/package.json +126 -0
  34. package/packages/sdk/bin/lowcode-workspace.mjs +4 -0
  35. package/packages/sdk/dist/build/index.cjs +33 -0
  36. package/packages/sdk/dist/build/index.cjs.map +1 -0
  37. package/packages/sdk/dist/build/index.d.mts +40 -0
  38. package/packages/sdk/dist/build/index.d.ts +40 -0
  39. package/packages/sdk/dist/build/index.mjs +8 -0
  40. package/packages/sdk/dist/build/index.mjs.map +1 -0
  41. package/packages/sdk/dist/components/index.cjs +18700 -0
  42. package/packages/sdk/dist/components/index.cjs.map +1 -0
  43. package/packages/sdk/dist/components/index.d.mts +2094 -0
  44. package/packages/sdk/dist/components/index.d.ts +2094 -0
  45. package/packages/sdk/dist/components/index.mjs +18649 -0
  46. package/packages/sdk/dist/components/index.mjs.map +1 -0
  47. package/packages/sdk/dist/runtime/index.cjs +1469 -0
  48. package/packages/sdk/dist/runtime/index.cjs.map +1 -0
  49. package/packages/sdk/dist/runtime/index.d.mts +831 -0
  50. package/packages/sdk/dist/runtime/index.d.ts +831 -0
  51. package/packages/sdk/dist/runtime/index.mjs +1420 -0
  52. package/packages/sdk/dist/runtime/index.mjs.map +1 -0
  53. package/packages/sdk/dist/styles/antd-theme.cjs +60 -0
  54. package/packages/sdk/dist/styles/antd-theme.cjs.map +1 -0
  55. package/packages/sdk/dist/styles/antd-theme.d.mts +5 -0
  56. package/packages/sdk/dist/styles/antd-theme.d.ts +5 -0
  57. package/packages/sdk/dist/styles/antd-theme.mjs +35 -0
  58. package/packages/sdk/dist/styles/antd-theme.mjs.map +1 -0
  59. package/packages/sdk/dist/styles/tailwind-preset.cjs +2641 -0
  60. package/packages/sdk/dist/styles/tailwind-preset.cjs.map +1 -0
  61. package/packages/sdk/dist/styles/tailwind-preset.d.mts +75 -0
  62. package/packages/sdk/dist/styles/tailwind-preset.d.ts +75 -0
  63. package/packages/sdk/dist/styles/tailwind-preset.mjs +2618 -0
  64. package/packages/sdk/dist/styles/tailwind-preset.mjs.map +1 -0
  65. package/packages/sdk/dist/styles/tokens.css +73 -0
  66. package/packages/sdk/src/build-source/README.md +9 -0
  67. package/packages/sdk/src/build-source/bin/lowcode-workspace.mjs +7 -0
  68. package/packages/sdk/src/build-source/package.json +34 -0
  69. package/packages/sdk/src/build-source/scripts/build-forms.mjs +824 -0
  70. package/packages/sdk/src/build-source/scripts/build-forms.runtime-entry.test.ts +18 -0
  71. package/packages/sdk/src/build-source/scripts/build-pages.mjs +793 -0
  72. package/packages/sdk/src/build-source/scripts/build-workspace.mjs +64 -0
  73. package/packages/sdk/src/build-source/scripts/publish-all.mjs +127 -0
  74. package/packages/sdk/src/build-source/scripts/publish-oss.mjs +149 -0
  75. package/packages/sdk/src/build-source/scripts/register-bundle.mjs +1 -0
  76. package/packages/sdk/src/build-source/scripts/register.mjs +329 -0
  77. package/packages/sdk/src/build-source/scripts/sync-schema.mjs +301 -0
  78. package/packages/sdk/src/build-source/scripts/utils/form-api.mjs +639 -0
  79. package/packages/sdk/src/build-source/scripts/utils/form-api.test.ts +244 -0
  80. package/packages/sdk/src/build-source/scripts/utils/form-runtime-assets.mjs +57 -0
  81. package/packages/sdk/src/build-source/scripts/utils/form-runtime-assets.test.ts +135 -0
  82. package/packages/sdk/src/build-source/scripts/utils/incremental.mjs +210 -0
  83. package/packages/sdk/src/build-source/scripts/utils/load-config.mjs +257 -0
  84. package/packages/sdk/src/build-source/scripts/utils/load-config.test.ts +44 -0
  85. package/packages/sdk/src/build-source/scripts/utils/mime-types.mjs +70 -0
  86. package/packages/sdk/src/build-source/scripts/utils/namespace-css.mjs +61 -0
  87. package/packages/sdk/src/build-source/scripts/utils/oss-client.mjs +128 -0
  88. package/packages/sdk/src/build-source/scripts/utils/pages.mjs +80 -0
  89. package/packages/sdk/src/build-source/scripts/utils/progress.mjs +57 -0
  90. package/packages/sdk/src/build-source/scripts/utils/register-payload.mjs +89 -0
  91. package/packages/sdk/src/build-source/scripts/utils/register-payload.test.ts +76 -0
  92. package/packages/sdk/src/build-source/scripts/utils/runtime-css-check.mjs +44 -0
  93. package/packages/sdk/src/build-source/scripts/utils/runtime-css-check.test.ts +54 -0
  94. package/packages/sdk/src/build-source/scripts/utils/schema-transform.mjs +130 -0
  95. package/packages/sdk/src/build-source/scripts/utils/schema-transform.test.ts +141 -0
  96. package/packages/sdk/src/build-source/scripts/utils/tailwind-config.mjs +227 -0
  97. package/packages/sdk/src/build-source/scripts/utils/tailwind-config.test.ts +187 -0
  98. package/packages/sdk/src/build-source/src/cli.mjs +679 -0
  99. package/templates/sy-lowcode-app-workspace/app-workspace.config.ts +34 -0
  100. package/templates/sy-lowcode-app-workspace/examples/forms/customer/page.tsx +1 -0
  101. package/templates/sy-lowcode-app-workspace/examples/forms/customer/schema.ts +35 -0
  102. package/templates/sy-lowcode-app-workspace/index.html +12 -0
  103. package/templates/sy-lowcode-app-workspace/package.json +49 -0
  104. package/templates/sy-lowcode-app-workspace/postcss.config.cjs +6 -0
  105. package/templates/sy-lowcode-app-workspace/scripts/build-js-code.mjs +100 -0
  106. package/templates/sy-lowcode-app-workspace/src/dev/App.tsx +26 -0
  107. package/templates/sy-lowcode-app-workspace/src/forms/.gitkeep +1 -0
  108. package/templates/sy-lowcode-app-workspace/src/forms/README.md +48 -0
  109. package/templates/sy-lowcode-app-workspace/src/index.css +28 -0
  110. package/templates/sy-lowcode-app-workspace/src/js-code-nodes/.gitkeep +1 -0
  111. package/templates/sy-lowcode-app-workspace/src/js-code-nodes/types.d.ts +3 -0
  112. package/templates/sy-lowcode-app-workspace/src/main.tsx +36 -0
  113. package/templates/sy-lowcode-app-workspace/src/pages/.gitkeep +1 -0
  114. package/templates/sy-lowcode-app-workspace/src/shared/form-schema.ts +128 -0
  115. package/templates/sy-lowcode-app-workspace/src/types/app-workspace.types.ts +31 -0
  116. package/templates/sy-lowcode-app-workspace/tailwind.config.cjs +30 -0
  117. package/templates/sy-lowcode-app-workspace/tsconfig.app.json +24 -0
  118. package/templates/sy-lowcode-app-workspace/tsconfig.js-code-nodes.json +15 -0
  119. package/templates/sy-lowcode-app-workspace/tsconfig.json +7 -0
  120. package/templates/sy-lowcode-app-workspace/tsconfig.node.json +10 -0
  121. package/templates/sy-lowcode-app-workspace/vite.config.ts +32 -0
@@ -0,0 +1,139 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { spawnSync } = require('child_process');
4
+ const { getProfile, loadConfig, saveProjectState } = require('./config');
5
+
6
+ const ROOT_DIR = path.join(__dirname, '..');
7
+ const TEMPLATE_DIR = path.join(ROOT_DIR, 'templates', 'sy-lowcode-app-workspace');
8
+
9
+ function initWorkspace(options = {}) {
10
+ const targetDir = path.resolve(options.dir || process.cwd());
11
+ const packageName =
12
+ options.name || path.basename(targetDir) || 'sy-lowcode-app-workspace';
13
+ const force = Boolean(options.force);
14
+ const install = Boolean(options.install);
15
+ const profileName = options.profile || null;
16
+ const appType = options.appType || null;
17
+
18
+ if ((profileName && !appType) || (!profileName && appType)) {
19
+ throw new Error('workspace init 绑定应用时必须同时提供 --profile 和 --app-type');
20
+ }
21
+
22
+ ensureCanInitialize(targetDir, force);
23
+ copyTemplate(TEMPLATE_DIR, targetDir, {
24
+ __WORKSPACE_PACKAGE_NAME__: packageName,
25
+ });
26
+
27
+ let bound = null;
28
+ if (profileName && appType) {
29
+ const config = loadConfig();
30
+ const resolved = getProfile(config, profileName);
31
+ const state = {
32
+ version: 1,
33
+ profiles: {
34
+ [resolved.profileName]: {
35
+ baseUrl: resolved.profile.baseUrl,
36
+ appType,
37
+ resources: {
38
+ forms: {},
39
+ pages: {},
40
+ workflows: {},
41
+ automations: {},
42
+ menus: {},
43
+ roles: {},
44
+ pagePermissionGroups: {},
45
+ formPermissionGroups: {},
46
+ },
47
+ updatedAt: new Date().toISOString(),
48
+ },
49
+ },
50
+ };
51
+ saveProjectState(state, targetDir);
52
+ bound = {
53
+ profile: resolved.profileName,
54
+ appType,
55
+ };
56
+ }
57
+
58
+ if (install) {
59
+ runInstall(targetDir);
60
+ }
61
+
62
+ return {
63
+ targetDir,
64
+ packageName,
65
+ templateDir: TEMPLATE_DIR,
66
+ installedDependencies: install,
67
+ bound,
68
+ nextSteps: buildNextSteps(targetDir, install, bound),
69
+ };
70
+ }
71
+
72
+ function ensureCanInitialize(targetDir, force) {
73
+ if (!fs.existsSync(TEMPLATE_DIR)) {
74
+ throw new Error(`workspace 模板不存在: ${TEMPLATE_DIR}`);
75
+ }
76
+ if (!fs.existsSync(targetDir)) {
77
+ fs.mkdirSync(targetDir, { recursive: true });
78
+ return;
79
+ }
80
+ if (!fs.statSync(targetDir).isDirectory()) {
81
+ throw new Error(`目标路径不是目录: ${targetDir}`);
82
+ }
83
+ const entries = fs
84
+ .readdirSync(targetDir)
85
+ .filter(name => !['.DS_Store'].includes(name));
86
+ if (entries.length > 0 && !force) {
87
+ throw new Error(`目标目录非空: ${targetDir}。如需写入请传 --force`);
88
+ }
89
+ }
90
+
91
+ function copyTemplate(sourceDir, targetDir, replacements) {
92
+ for (const entry of fs.readdirSync(sourceDir, { withFileTypes: true })) {
93
+ const sourcePath = path.join(sourceDir, entry.name);
94
+ const targetPath = path.join(targetDir, entry.name);
95
+ if (entry.isDirectory()) {
96
+ fs.mkdirSync(targetPath, { recursive: true });
97
+ copyTemplate(sourcePath, targetPath, replacements);
98
+ continue;
99
+ }
100
+ if (!entry.isFile()) continue;
101
+ fs.mkdirSync(path.dirname(targetPath), { recursive: true });
102
+ const content = fs.readFileSync(sourcePath, 'utf8');
103
+ fs.writeFileSync(targetPath, applyReplacements(content, replacements));
104
+ }
105
+ }
106
+
107
+ function applyReplacements(content, replacements) {
108
+ return Object.entries(replacements).reduce(
109
+ (result, [key, value]) => result.split(key).join(value),
110
+ content
111
+ );
112
+ }
113
+
114
+ function runInstall(targetDir) {
115
+ const result = spawnSync('pnpm', ['install'], {
116
+ cwd: targetDir,
117
+ stdio: 'inherit',
118
+ env: process.env,
119
+ });
120
+ if (result.status !== 0) {
121
+ throw new Error('pnpm install 执行失败');
122
+ }
123
+ }
124
+
125
+ function buildNextSteps(targetDir, installedDependencies, bound) {
126
+ const steps = [`cd ${targetDir}`];
127
+ if (!installedDependencies) steps.push('pnpm install');
128
+ if (!bound) {
129
+ steps.push('openxiangda workspace bind --profile <name> --app-type APP_XXXX');
130
+ }
131
+ const profileArg = bound ? bound.profile : '<name>';
132
+ steps.push(`openxiangda env --profile ${profileArg}`);
133
+ steps.push(`openxiangda workspace publish --profile ${profileArg}`);
134
+ return steps;
135
+ }
136
+
137
+ module.exports = {
138
+ initWorkspace,
139
+ };
@@ -0,0 +1,128 @@
1
+ ---
2
+ name: openxiangda
3
+ description: Use OpenXiangda to help AI agents build and publish apps on a private low-code platform through the openxiangda CLI, ordinary user login tokens, platform profiles, workspace initialization, workspace binding, and app snapshots; use when the user mentions OpenXiangda, 湘搭, 私有化低代码平台, sy-lowcode-app-workspace, profile/domain login, or multi-platform publishing.
4
+ ---
5
+
6
+ # OpenXiangda
7
+
8
+ OpenXiangda is a lightweight bridge between an AI coding tool and a private low-code platform. It does not ask users for AK/SK. The user provides a platform domain, logs in as a normal platform user, and the CLI calls `/openxiangda-api/v1` with that user's token.
9
+
10
+ For page generation, OpenXiangda must use `sy-lowcode-app-workspace`: form pages, workflow form pages, and custom code pages are implemented as workspace source files, built into bundles, uploaded to OSS, and registered through `openxiangda workspace publish`. Do not generate final user-facing pages by relying on the platform's legacy default schema page.
11
+
12
+ ## Platform Routing
13
+
14
+ Private platform routing is fixed:
15
+
16
+ - Backend services are under `/service`.
17
+ - Platform management UI is under `/platform`.
18
+ - App runtime access is under `/view`.
19
+
20
+ When the user provides a root domain such as `https://yida.wisejob.cn/`, use it directly with the CLI; OpenXiangda normalizes the API base to `https://yida.wisejob.cn/service`. If the user provides a copied `/platform` or `/view` URL, treat it as the same platform and normalize to `/service`. Do not call `/openxiangda-api/v1` at the root domain.
21
+
22
+ ## Required Flow
23
+
24
+ 1. Ask for the target platform domain if it is not known.
25
+ 2. Configure a profile:
26
+ ```bash
27
+ openxiangda platform add dev https://dev-lowcode.example.com
28
+ openxiangda platform use dev
29
+ ```
30
+ 3. If not logged in, run:
31
+ ```bash
32
+ openxiangda login --profile dev
33
+ ```
34
+ Or combine domain and login:
35
+ ```bash
36
+ openxiangda login https://dev-lowcode.example.com --profile dev
37
+ ```
38
+ 4. Check current context before any write:
39
+ ```bash
40
+ openxiangda env --profile dev
41
+ openxiangda auth status --profile dev
42
+ ```
43
+ 5. Ensure there is a local app workspace. If the current directory is not a `sy-lowcode-app-workspace` and no app workspace exists yet, initialize one:
44
+ ```bash
45
+ openxiangda workspace init ./my-app-workspace --profile dev --app-type APP_XXXX
46
+ cd ./my-app-workspace
47
+ pnpm install
48
+ ```
49
+ 6. Bind the local workspace to the app for the selected platform:
50
+ ```bash
51
+ openxiangda app list --profile dev
52
+ # or create one when needed:
53
+ openxiangda app create "示例应用" --profile dev
54
+ openxiangda workspace bind --profile dev --app-type APP_XXXX
55
+ cd /path/to/sy-lowcode-app-workspace
56
+ openxiangda workspace publish --profile dev
57
+ ```
58
+ 7. For multi-platform publishing, always pass `--profile` explicitly:
59
+ ```bash
60
+ openxiangda workspace publish --profile prod
61
+ ```
62
+
63
+ ## Rules
64
+
65
+ - Never ask the user for `appKey`, `appSecret`, `AK`, or `SK`.
66
+ - If `openxiangda` is not available in PATH, the CLI package is not installed or linked. In this repo, use `npm link`; in a packaged install, use `npm install -g .` or the published package.
67
+ - If there is no `sy-lowcode-app-workspace`, create one with `openxiangda workspace init <dir>` before writing forms, pages, or JS_CODE nodes.
68
+ - Never treat `openxiangda form create` as page generation. It only creates a low-level platform form shell for diagnostics or for workspace publish internals. The source of user-facing pages is `sy-lowcode-app-workspace`.
69
+ - Publish normal form pages, workflow form pages, and custom code pages through `openxiangda workspace publish --profile <name>` from the app workspace.
70
+ - Never store token data in the project directory. User tokens live in `~/.openxiangda/profiles.json`; project state lives in `.openxiangda/state.json` and stores only IDs and mappings.
71
+ - Use logical resource codes in local files. Platform-specific IDs such as `formUuid`, `pageId`, `workflowId`, and `automationId` must be isolated by profile.
72
+ - Before publishing to another platform, verify the workspace is bound for that profile. Resource IDs from one profile must not be reused for another profile.
73
+ - Use `openxiangda app snapshot <APP_XXX> --profile <name> --json` for diagnosis before changing an existing app.
74
+ - Run write commands that update `.openxiangda/state.json` sequentially within the same workspace. Read-only commands can run in parallel.
75
+ - For workflow/automation JS_CODE nodes, prefer V2 `runtimeMode: "trusted_node"`. AI-authored source must be TypeScript under `sy-lowcode-app-workspace/src/js-code-nodes/<scriptCode>/index.ts`. `pnpm build-js-code --script <scriptCode>` runs TypeScript validation before bundling, and `sourceFile.localPath` should point to the TS source; the CLI builds, uploads, and replaces it with snapshot metadata during validate/create.
76
+
77
+ ## Subskills
78
+
79
+ - `skills/openxiangda-core/SKILL.md`: command workflow and API contract details.
80
+ - `skills/openxiangda-app/SKILL.md`: app creation, binding, snapshots, and profile-isolated workspace state.
81
+ - `skills/openxiangda-form/SKILL.md`: workspace-based form and workflow form page development, bundle registration, and form publishing.
82
+ - `skills/openxiangda-page/SKILL.md`: custom code page development and publishing in `sy-lowcode-app-workspace`.
83
+ - `skills/openxiangda-workflow-automation/SKILL.md`: workflow v3 definitions, automation triggers, publishing, enabling, and profile-isolated IDs.
84
+ - `skills/openxiangda-permission-settings/SKILL.md`: app roles, page permission groups, form permission groups, and settings-oriented guidance.
85
+ - `skills/openxiangda-inspect/SKILL.md`: read-only app and resource diagnosis using snapshots and profile-local resource IDs.
86
+
87
+ ## API Namespace
88
+
89
+ OpenXiangda uses `/openxiangda-api/v1`. Old `/dingtalk-api/v1.0` APIs are compatibility only and should not be used for new OpenXiangda workflows.
90
+
91
+ On private deployments, the public API URL is normally `<platform-domain>/service/openxiangda-api/v1`.
92
+
93
+ For endpoint details, read `references/openxiangda-api.md` only when you need request or response fields.
94
+
95
+ ## References Index
96
+
97
+ Load these references on demand instead of memorizing them. Subskills cite the same files with relative paths.
98
+
99
+ Core CLI / state:
100
+
101
+ - `references/openxiangda-api.md` — `/openxiangda-api/v1` request and response fields.
102
+ - `references/workspace-state.md` — `.openxiangda/state.json` shape and profile isolation rules.
103
+
104
+ Form authoring:
105
+
106
+ - `references/forms/form-schema.md` — `defineFormSchema` structure, field types, top-level effects.
107
+ - `references/forms/component-registry.md` — supported form components and their props.
108
+ - `references/forms/layout-and-rules.md` — layout containers, field rules, and validation patterns.
109
+
110
+ Page authoring:
111
+
112
+ - `references/pages/workspace-structure.md` — `src/pages/<pageCode>/` layout and config.
113
+ - `references/pages/page-sdk.md` — `openxiangda/runtime` data and runtime APIs.
114
+ - `references/pages/publish-flow.md` — workspace publish steps for code pages.
115
+
116
+ Workflow / automation / permissions:
117
+
118
+ - `references/workflow-v3.md` — workflow v3 definitions, JS_CODE nodes, trusted_node mode.
119
+ - `references/automation-v3.md` — automation triggers, conditions, and publishing.
120
+ - `references/permissions-settings.md` — roles, page/form permission groups, settings.
121
+
122
+ Platform domain knowledge (read these first when generating forms or pages so the output matches the live platform behavior):
123
+
124
+ - `references/platform-data-model.md` — how the platform persists field values (JSONB, `{label, value}` for option fields, attachment shape, etc.). Use this whenever you write, render, or diagnose form data.
125
+ - `references/style-system.md` — three-layer style architecture, CSS namespace, and the no-hardcoded-color rule. Use this before writing any page or form CSS.
126
+ - `references/architecture-patterns.md` — CRUD data flow, `DataManagementList` pattern, recommended directory layout for `src/pages` and `src/forms`. Use this before scaffolding a new page or list view.
127
+ - `references/component-guide.md` — when to pick platform components vs. raw Ant Design vs. custom components, with the rules for option fields. Use this before introducing a new component.
128
+ - `references/troubleshooting.md` — known failure modes (missing styles, `options` runtime errors, broken option rendering, etc.) and how to fix them. Use this when something does not behave as expected.
@@ -0,0 +1,242 @@
1
+ # Architecture Patterns
2
+
3
+ > 面向 AI Agent 的平台架构范式指南:理解「表单为数据存储中枢、代码页承载业务逻辑」的低代码开发模型,并据此构建可发布的应用。
4
+
5
+ ---
6
+
7
+ ## 1. 平台设计理念
8
+
9
+ ### 1.1 表单 = 数据存储
10
+
11
+ - **每个表单(Form)= 一张数据表**:表单 schema 定义即数据结构(字段、类型、校验、关联)。
12
+ - 平台底层按字段建表:简单字段使用原生 SQL 类型列,复杂字段使用 json/jsonb 列存储结构化对象。AI 无需关心建表 DDL、迁移脚本或 CRUD 接口。
13
+ - 表单除了承担「数据录入 UI」,还会自动暴露:列表查询、详情、增删改查 API、字段级权限、流程触发钩子。
14
+
15
+ ### 1.2 低代码 = Schema + 代码页
16
+
17
+ ```
18
+ 低代码应用 = 表单(定义数据结构)+ 代码页(实现业务逻辑/编排/可视化)
19
+ ```
20
+
21
+ - **表单层**:声明式 schema (`src/forms/xxx/schema.ts`),负责字段与数据契约。
22
+ - **代码页层**:React/TSX (`src/pages/xxx/`),负责非录入场景下的业务交互、数据可视化、聚合操作。
23
+
24
+ ### 1.3 「零后端开发」数据管理模式
25
+
26
+ | 传统后端 | OpenXiangda 模式 |
27
+ | --- | --- |
28
+ | 设计表结构 + 迁移 | 编写表单 schema |
29
+ | 编写 REST API | 平台自动生成数据 API |
30
+ | 维护权限中间件 | 表单字段级权限 + 部门/角色 |
31
+ | 自建文件存储 | `AttachmentField` 直接接入平台存储 |
32
+ | 自研流程引擎 | 表单 + workflow + js-code-nodes |
33
+
34
+ AI Agent 在该平台上写代码时,**不应该考虑数据库、表结构、后端接口**——这些由表单 schema 自动派生。
35
+
36
+ ---
37
+
38
+ ## 2. 标准 CRUD 数据流
39
+
40
+ ### 2.1 数据流向
41
+
42
+ ```
43
+ ┌──────────────┐ ┌─────────────────┐ ┌────────────────┐ ┌────────────┐
44
+ │ 表单页 │───▶│ 数据存储 │───▶│ 数据管理页 │───▶│ 详情页 │
45
+ │ (录入/编辑) │ │ (平台数据表) │ │ (列表/查询) │ │ (查看) │
46
+ └──────────────┘ └─────────────────┘ └────────────────┘ └────────────┘
47
+ ▲ │
48
+ └────────────────────────────────────────────┘
49
+ (从列表点编辑回填)
50
+ ```
51
+
52
+ ### 2.2 实现步骤
53
+
54
+ 1. **定义表单 schema**:`src/forms/xxx/schema.ts` → 生成数据表 + 录入页。
55
+ 2. **编写数据管理页**:`src/pages/xxx/index.tsx` 内使用 `DataManagementList` 组件做列表/查询/批量操作。
56
+ 3. **绑定菜单入口**:`openxiangda menu create --type=page --target=/pages/xxx`(指向自定义代码页,**而不是** view 类型)。
57
+
58
+ ### 2.3 反模式:不要使用平台内置 View
59
+
60
+ > ❌ `openxiangda menu create --type=view --formUuid=FORM_XXX`
61
+
62
+ 平台内置 View 视图灵活性差:列宽/操作列/筛选项受限、无法嵌入业务交互、无法做联动。
63
+
64
+ > ✅ **统一使用「自定义代码页 + `DataManagementList`」组合**。
65
+
66
+ #### `DataManagementList` 的优势
67
+
68
+ - 完全自定义列渲染(`render` 函数、React 节点)。
69
+ - 自定义行操作 / 批量操作按钮。
70
+ - 自定义筛选区(部门选择、人员选择、级联)。
71
+ - 与页面内其他组件(Drawer、Modal、Tabs)自由组合。
72
+ - 仍复用平台分页、查询、导出能力。
73
+
74
+ ---
75
+
76
+ ## 3. 数据管理页的标准构建方式
77
+
78
+ ### 3.1 最小可用示例
79
+
80
+ ```tsx
81
+ // src/pages/instrument-list/index.tsx
82
+ import { DataManagementList } from 'openxiangda';
83
+ import { Tag, message } from 'antd';
84
+ import { useNavigate } from 'react-router-dom';
85
+
86
+ export default function InstrumentListPage() {
87
+ const navigate = useNavigate();
88
+
89
+ return (
90
+ <DataManagementList
91
+ formUuid="FORM_INSTRUMENT"
92
+ columns={[
93
+ { fieldId: 'name', title: '仪器名称' },
94
+ { fieldId: 'code', title: '编号', width: 140 },
95
+ {
96
+ fieldId: 'status',
97
+ title: '状态',
98
+ render: (val) => (
99
+ <Tag color={val.value === 'idle' ? 'green' : 'orange'}>
100
+ {val.label}
101
+ </Tag>
102
+ ),
103
+ },
104
+ { fieldId: 'department', title: '所属部门' },
105
+ ]}
106
+ actions={[
107
+ {
108
+ label: '预约',
109
+ onClick: (record) => navigate(`/forms/reservation/new?instrumentId=${record.id}`),
110
+ },
111
+ {
112
+ label: '编辑',
113
+ onClick: (record) => navigate(`/forms/instrument/${record.id}/edit`),
114
+ },
115
+ ]}
116
+ batchActions={['export', 'delete']}
117
+ filters={[
118
+ { fieldId: 'status', type: 'select' },
119
+ { fieldId: 'department', type: 'department-select' },
120
+ { fieldId: 'owner', type: 'user-select' },
121
+ ]}
122
+ onRowClick={(record) => navigate(`/pages/instrument-detail/${record.id}`)}
123
+ />
124
+ );
125
+ }
126
+ ```
127
+
128
+ ### 3.2 关键 Props 速查
129
+
130
+ | Prop | 类型 | 说明 |
131
+ | --- | --- | --- |
132
+ | `formUuid` | `string` | 绑定的表单(数据源) |
133
+ | `columns` | `Column[]` | 列定义;`render` 可返回任意 React 节点 |
134
+ | `actions` | `Action[]` | 行级操作按钮 |
135
+ | `batchActions` | `('export' \| 'delete' \| BatchAction)[]` | 批量操作 |
136
+ | `filters` | `Filter[]` | 筛选区字段;支持 `select` / `date-range` / `user-select` / `department-select` 等 |
137
+ | `queryParams` | `Record<string, any>` | 固定查询条件(如「只看我创建的」) |
138
+ | `onRowClick` | `(record) => void` | 行点击事件,常用于跳转详情 |
139
+
140
+ ---
141
+
142
+ ## 4. 页面类型与适用场景
143
+
144
+ | 页面类型 | 实现方式 | 适用场景 |
145
+ | --- | --- | --- |
146
+ | 表单录入 / 编辑页 | `src/forms/xxx/` (`schema.ts` + `page.tsx`) | 新增数据、修改数据 |
147
+ | 数据管理页 | `src/pages/xxx/` 使用 `DataManagementList` | 列表、查询、批量操作、导出 |
148
+ | 流程表单页 | `src/forms/xxx/` 关联 workflow | 审批流程发起、节点处理 |
149
+ | 自定义业务页 | `src/pages/xxx/` 自由编码 | 仪表盘、看板、复杂交互、对外门户 |
150
+ | 详情页 | `src/pages/xxx/[id].tsx` 使用 `FormSummaryCard` 等 | 查看单条数据、关联记录 |
151
+
152
+ > AI Agent 在创建新页面前,先按上表判断「应该走表单还是代码页」,避免把列表场景误塞进表单页。
153
+
154
+ ---
155
+
156
+ ## 5. 多端架构(PC / Mobile)
157
+
158
+ ### 5.1 分离策略
159
+
160
+ - **页面层按端独立**:`src/pages/pc/xxx/` 与 `src/pages/mobile/xxx/`,避免单文件内 `if (isMobile)` 满天飞。
161
+ - **入口统一调度**:
162
+
163
+ ```tsx
164
+ // src/pages/instrument/index.tsx
165
+ import { useDeviceDetect } from 'openxiangda';
166
+ import PcPage from './pc';
167
+ import MobilePage from './mobile';
168
+
169
+ export default function InstrumentEntry() {
170
+ const { isMobile } = useDeviceDetect();
171
+ return isMobile ? <MobilePage /> : <PcPage />;
172
+ }
173
+ ```
174
+
175
+ - **UI 库分端**:
176
+ - PC → `antd`
177
+ - Mobile → `antd-mobile`
178
+ - **逻辑层共享**:`src/domain/` 与 `src/shared/services/` 在两端复用,确保业务规则一处实现。
179
+
180
+ ### 5.2 共享 / 分离原则
181
+
182
+ | 层 | PC/Mobile 是否共享 |
183
+ | --- | --- |
184
+ | 业务规则(domain) | ✅ 共享 |
185
+ | 数据服务(services) | ✅ 共享 |
186
+ | Hooks(hooks) | ✅ 多数共享,端相关的拆 `usePcXxx` / `useMobileXxx` |
187
+ | 表单 schema | ✅ 共享(平台组件已多端适配) |
188
+ | 页面布局 / 组件 | ❌ 分离 |
189
+
190
+ ---
191
+
192
+ ## 6. 目录组织规范
193
+
194
+ ```
195
+ src/
196
+ ├── forms/ # 表单(每个目录 = 一张数据表)
197
+ │ └── instrument/
198
+ │ ├── schema.ts # 字段定义
199
+ │ └── page.tsx # 录入页(可选自定义布局)
200
+ ├── pages/ # 自定义代码页(含数据管理页)
201
+ │ ├── instrument-list/ # 列表/管理
202
+ │ ├── instrument-detail/ # 详情
203
+ │ └── dashboard/ # 仪表盘等业务页
204
+ ├── domain/ # 纯业务逻辑(无 UI、可单测)
205
+ │ └── instrument/
206
+ │ ├── rules.ts
207
+ │ └── types.ts
208
+ ├── shared/
209
+ │ ├── services/ # 数据服务层(封装 SDK 调用)
210
+ │ ├── hooks/ # 跨页面 hooks
211
+ │ ├── components/ # 跨页面共享组件
212
+ │ └── utils/ # 通用工具函数
213
+ └── js-code-nodes/ # 工作流 JS 脚本(由 build-js-code.mjs 打包)
214
+ ```
215
+
216
+ ### 6.1 命名约定
217
+
218
+ - 目录与文件统一 `kebab-case`:`instrument-list/`,`use-instrument-form.ts`。
219
+ - React 组件文件首字母大写:`DataPanel.tsx`,与默认导出一致。
220
+ - 表单目录名 = 表单业务名(不含 `Form` 后缀)。
221
+ - 数据管理页目录名建议 `xxx-list`,详情页 `xxx-detail`。
222
+
223
+ ### 6.2 依赖方向(单向)
224
+
225
+ ```
226
+ pages ──▶ shared ──▶ domain
227
+ forms ──▶ shared ──▶ domain
228
+ ```
229
+
230
+ - `domain/` 不依赖 `shared/` 或 UI。
231
+ - `shared/` 不依赖 `pages/` 或 `forms/`。
232
+ - 反向依赖(如 domain 导入 pages)= 立刻拒绝。
233
+
234
+ ---
235
+
236
+ ## 速查 Checklist(AI Agent 自检)
237
+
238
+ - [ ] 新增数据场景:是否已经定义表单 schema?
239
+ - [ ] 列表/查询场景:是否使用 `DataManagementList` 而非平台 view?
240
+ - [ ] 菜单绑定:是否指向代码页 `--type=page`?
241
+ - [ ] PC/Mobile:是否分离页面、共享 domain?
242
+ - [ ] 目录:业务逻辑是否落在 `domain/`,未污染 UI 层?
@@ -0,0 +1,129 @@
1
+ # Automation V3 Reference
2
+
3
+ Automations have two files:
4
+
5
+ - `trigger.json`: when the automation runs.
6
+ - `automation.json`: what the automation does.
7
+
8
+ ## Trigger Config
9
+
10
+ Form submit trigger:
11
+
12
+ ```json
13
+ {
14
+ "type": "form_data_submitted",
15
+ "appType": "APP_XXX",
16
+ "formUuid": "FORM_XXX",
17
+ "enabled": true
18
+ }
19
+ ```
20
+
21
+ Form update trigger:
22
+
23
+ ```json
24
+ {
25
+ "type": "form_data_updated",
26
+ "appType": "APP_XXX",
27
+ "formUuid": "FORM_XXX",
28
+ "enabled": true
29
+ }
30
+ ```
31
+
32
+ Scheduled trigger:
33
+
34
+ ```json
35
+ {
36
+ "type": "scheduled",
37
+ "appType": "APP_XXX",
38
+ "enabled": true,
39
+ "scheduleType": "fixed_time",
40
+ "fixedTime": {
41
+ "startTime": "2026-05-21T09:00:00+08:00",
42
+ "cronExpression": "0 0 9 * * *"
43
+ }
44
+ }
45
+ ```
46
+
47
+ Workflow trigger:
48
+
49
+ ```json
50
+ {
51
+ "type": "workflow_process_completed",
52
+ "appType": "APP_XXX",
53
+ "formUuid": "FORM_XXX",
54
+ "processDefinitionId": "workflow-id",
55
+ "enabled": true
56
+ }
57
+ ```
58
+
59
+ Use CLI flags such as `--form-code customer` so the CLI can fill `formUuid` from the current profile. Do not hardcode IDs from another profile.
60
+
61
+ ## Definition JSON
62
+
63
+ Minimum shape:
64
+
65
+ ```json
66
+ {
67
+ "version": "v3",
68
+ "nodes": [
69
+ { "id": "start", "type": "start", "data": { "label": "开始" } },
70
+ { "id": "notify", "type": "work_notification", "data": { "label": "通知" } },
71
+ { "id": "end", "type": "end", "data": { "label": "结束" } }
72
+ ],
73
+ "edges": [
74
+ { "id": "e1", "source": "start", "target": "notify" },
75
+ { "id": "e2", "source": "notify", "target": "end" }
76
+ ]
77
+ }
78
+ ```
79
+
80
+ Supported node types:
81
+
82
+ - `start`
83
+ - `end`
84
+ - `condition`
85
+ - `condition_branch`
86
+ - `branch`
87
+ - `js_code`
88
+ - `data_retrieve_single`
89
+ - `data_retrieve_batch`
90
+ - `data_create`
91
+ - `data_update`
92
+ - `connector_call`
93
+ - `callback_wait`
94
+ - `work_notification`
95
+ - `dingtalk_card`
96
+
97
+ ## JS_CODE V2
98
+
99
+ Use trusted Node JS_CODE nodes for AI/admin automation logic:
100
+
101
+ ```json
102
+ {
103
+ "id": "sync_customer",
104
+ "type": "js_code",
105
+ "data": {
106
+ "label": "同步客户",
107
+ "runtimeMode": "trusted_node",
108
+ "sourceType": "file_snapshot",
109
+ "scriptCode": "sync_customer",
110
+ "sourceFile": {
111
+ "localPath": "src/js-code-nodes/sync_customer/index.ts"
112
+ },
113
+ "timeout": 30000
114
+ }
115
+ }
116
+ ```
117
+
118
+ Author source in `sy-lowcode-app-workspace/src/js-code-nodes/<scriptCode>/index.ts`. AI-authored source must be TypeScript. Build with `pnpm build-js-code --script <scriptCode>`; the command runs TypeScript validation before bundling. During validate/create, the CLI uploads the generated bundle, replaces `sourceFile.localPath` with `{ bucketName, objectName, sha256, ... }`, and the backend verifies sha256 before execution.
119
+
120
+ Scripts may use `module.exports = async (ctx) => {}`, `require`, `process`, `Buffer`, legacy `methods.*`, arbitrary HTTP, and `platform.api` for `/openxiangda-api/v1`.
121
+
122
+ Validation rules:
123
+
124
+ - `version` must be `v3`.
125
+ - `nodes` and `edges` must be arrays.
126
+ - Exactly one `start` node is required.
127
+ - Each node needs `id`, `type`, and object `data`.
128
+ - Each edge needs `id`, `source`, and `target`.
129
+ - Use `openxiangda automation validate --strict` before publishing.