openxiangda 1.0.38 → 1.0.40
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 +4 -3
- package/lib/cli.js +135 -5
- package/openxiangda-skills/SKILL.md +8 -5
- package/openxiangda-skills/references/best-practices.md +29 -5
- package/openxiangda-skills/references/component-guide.md +79 -45
- package/openxiangda-skills/references/connector-resources.md +1 -1
- package/openxiangda-skills/references/data-views.md +130 -5
- package/openxiangda-skills/references/forms/component-registry.md +33 -1
- package/openxiangda-skills/references/pages/page-sdk.md +30 -2
- package/openxiangda-skills/references/resource-manifest-cheatsheet.md +37 -1
- package/openxiangda-skills/skills/openxiangda-core/SKILL.md +2 -2
- package/openxiangda-skills/skills/openxiangda-form/SKILL.md +9 -2
- package/openxiangda-skills/skills/openxiangda-page/SKILL.md +6 -4
- package/package.json +1 -1
- package/packages/sdk/dist/runtime/index.cjs +583 -1
- package/packages/sdk/dist/runtime/index.cjs.map +1 -1
- package/packages/sdk/dist/runtime/index.d.mts +5 -1
- package/packages/sdk/dist/runtime/index.d.ts +5 -1
- package/packages/sdk/dist/runtime/index.mjs +583 -1
- package/packages/sdk/dist/runtime/index.mjs.map +1 -1
- package/packages/sdk/src/build-source/scripts/build-forms.mjs +5 -1
- package/templates/sy-lowcode-app-workspace/.cursor/rules/openxiangda-form.mdc +4 -0
- package/templates/sy-lowcode-app-workspace/.cursor/rules/openxiangda-page.mdc +2 -0
- package/templates/sy-lowcode-app-workspace/.cursor/rules/openxiangda.mdc +3 -0
- package/templates/sy-lowcode-app-workspace/.qoder/rules/openxiangda-form.md +4 -0
- package/templates/sy-lowcode-app-workspace/.qoder/rules/openxiangda-page.md +2 -0
- package/templates/sy-lowcode-app-workspace/.qoder/rules/openxiangda.md +3 -0
- package/templates/sy-lowcode-app-workspace/AGENTS.md +5 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/README.md +8 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/catalog.json +32 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/decision-guide.md +20 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/design-style.md +48 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/src/pages/glass-home-dashboard/App.tsx +8 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/src/pages/glass-home-dashboard/GlassHomeDashboard.tsx +232 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/src/pages/glass-home-dashboard/index.tsx +10 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/src/pages/glass-home-dashboard/page.config.ts +14 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/src/pages/glass-home-dashboard/styles.css +196 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/src/pages/mint-analytics-dashboard/App.tsx +8 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/src/pages/mint-analytics-dashboard/MintAnalyticsDashboard.tsx +279 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/src/pages/mint-analytics-dashboard/index.tsx +10 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/src/pages/mint-analytics-dashboard/page.config.ts +14 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/src/pages/mint-analytics-dashboard/styles.css +163 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/src/pages/ops-monitor-dashboard/App.tsx +8 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/src/pages/ops-monitor-dashboard/OpsMonitorDashboard.tsx +306 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/src/pages/ops-monitor-dashboard/index.tsx +10 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/src/pages/ops-monitor-dashboard/page.config.ts +14 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/src/pages/ops-monitor-dashboard/styles.css +248 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/src/pages/work-order-list-drawer/App.tsx +8 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/src/pages/work-order-list-drawer/WorkOrderListDrawerPage.tsx +371 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/src/pages/work-order-list-drawer/index.tsx +10 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/src/pages/work-order-list-drawer/page.config.ts +14 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/src/pages/work-order-list-drawer/styles.css +182 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/src/shared/components/admin-ui-templates/DashboardPrimitives.tsx +832 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/src/shared/components/admin-ui-templates/chartOptions.ts +140 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/src/shared/components/admin-ui-templates/sampleData.ts +466 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/src/shared/components/admin-ui-templates/styles.css +874 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/src/shared/components/admin-ui-templates/types.ts +150 -0
- package/templates/sy-lowcode-app-workspace/src/forms/README.md +4 -0
- package/templates/sy-lowcode-app-workspace/src/main.tsx +4 -0
package/README.md
CHANGED
|
@@ -40,14 +40,14 @@ openxiangda app snapshot APP_XXXX --profile dev --json
|
|
|
40
40
|
|
|
41
41
|
User tokens are stored in `~/.openxiangda/profiles.json` with `0600` permissions. Shared workspace environment values, including `APP_OSS_*`, can live in `~/.openxiangda/.env` and are inherited by new workspaces. Project `.env` files still work and override the global defaults. Project state is stored in `.openxiangda/state.json` and contains only profile-specific resource IDs.
|
|
42
42
|
|
|
43
|
-
Feedback can be sent to a DingTalk custom robot from the CLI
|
|
43
|
+
Feedback can be sent to a DingTalk custom robot from the CLI. AI agents should proactively report platform defects, missing capabilities, unclear design rules, repeated workarounds, implementation uncertainty, and user-visible UX gaps during development. Store the robot settings in `~/.openxiangda/.env`, not in project files:
|
|
44
44
|
|
|
45
45
|
```env
|
|
46
46
|
OPENXIANGDA_FEEDBACK_DINGTALK_WEBHOOK=https://oapi.dingtalk.com/robot/send?access_token=...
|
|
47
47
|
OPENXIANGDA_FEEDBACK_DINGTALK_SECRET=SEC...
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
Submit directly when the report is clear; use preview when robot env is missing or you need to inspect the sanitized payload:
|
|
51
51
|
|
|
52
52
|
```bash
|
|
53
53
|
openxiangda feedback preview --profile dev --summary "发布后资源 404" --description "..."
|
|
@@ -110,7 +110,7 @@ openxiangda workspace bind --profile dev --app-type APP_XXXX
|
|
|
110
110
|
|
|
111
111
|
工程化资源放在工作区 `src/resources/` 下,由 `openxiangda resource validate|plan|publish|pull` 管理。`workspace publish` 会先构建并注册 workspace 表单/页面,再执行非破坏性资源 upsert,这样菜单、权限组、流程和表单设置可以解析最新的 profile-local ID。需要删除平台中 manifest 未声明的资源时,显式传 `--prune`。连接器页面运行时通过 `sdk.connector.invoke()` / `sdk.connector.call("connector.api")` 调用平台运行时接口,第三方密钥只保存在后端连接器配置中。
|
|
112
112
|
|
|
113
|
-
|
|
113
|
+
多表只读查询和固定口径统计优先声明 `src/resources/data-views/*.json` 数据视图,而不是在页面里手写多次单表查询再拼数据。默认数据视图是行级联表视图,适合工单+客户、订单+商品、项目+成员、报表列表、跨页面复用查询等读多写少场景;`viewType: "aggregate"` 是统计聚合视图,适合按客户、状态、月份等维度预聚合 count/sum/avg/min/max。发布时 CLI 会把 `formCode` 解析为当前 profile 的 `formUuid`,平台创建 PostgreSQL materialized view;页面通过 `sdk.dataView.query(code, params)` 查询行级视图,通过 `sdk.dataView.stats(code, params)` 查询聚合视图,也可以用 `sdk.dataSource.run()` 路由 `dataView.query` / `dataView.stats`。发布前应为常用筛选、排序、统计维度和时间桶声明 `indexes`,并确认用户能接受的刷新延迟;默认不要设置低于 5 分钟的定时刷新。数据视图只读,刷新后才反映源表变化,不适合单表 CRUD、写回源表、强实时状态、临时 BI 查询或简单 linkedForm 下拉。
|
|
114
114
|
|
|
115
115
|
常用数据视图命令:
|
|
116
116
|
|
|
@@ -122,6 +122,7 @@ openxiangda data-view list --profile dev
|
|
|
122
122
|
openxiangda data-view status ticket_with_customer --profile dev
|
|
123
123
|
openxiangda data-view refresh ticket_with_customer --profile dev
|
|
124
124
|
openxiangda data-view query ticket_with_customer --query-json query.json --profile dev
|
|
125
|
+
openxiangda data-view stats ticket_stats_by_customer --query-json stats-query.json --profile dev
|
|
125
126
|
```
|
|
126
127
|
|
|
127
128
|
AI-authored automation can use code-first resources:
|
package/lib/cli.js
CHANGED
|
@@ -107,7 +107,7 @@ Usage:
|
|
|
107
107
|
openxiangda automation logs <instanceId> [--automation <automationCode|automationId>] [--summary] [--redact] [--json]
|
|
108
108
|
openxiangda automation diagnose <automationCode|automationId> [--redact] [--json]
|
|
109
109
|
openxiangda automation publish|enable|disable <automationCode|automationId>
|
|
110
|
-
openxiangda data-view list|status|refresh|query <dataViewCode> [--profile name] [--json]
|
|
110
|
+
openxiangda data-view list|status|refresh|query|stats <dataViewCode> [--profile name] [--json]
|
|
111
111
|
openxiangda permission role-list|role-create|role-bind
|
|
112
112
|
openxiangda permission page-group-list|page-group-create|page-group-bind
|
|
113
113
|
openxiangda permission form-group-list|form-group-create|form-group-bind
|
|
@@ -2052,7 +2052,24 @@ async function dataView(args) {
|
|
|
2052
2052
|
return;
|
|
2053
2053
|
}
|
|
2054
2054
|
|
|
2055
|
-
|
|
2055
|
+
if (subcommand === 'stats') {
|
|
2056
|
+
const [code] = positional;
|
|
2057
|
+
if (!code) fail('用法: openxiangda data-view stats <dataViewCode> [--query-json file]');
|
|
2058
|
+
const data = await requestWithAuth(
|
|
2059
|
+
config,
|
|
2060
|
+
target.profileName,
|
|
2061
|
+
`/openxiangda-api/v1/apps/${encodeURIComponent(target.appType)}/data-views/${encodeURIComponent(code)}/stats`,
|
|
2062
|
+
{
|
|
2063
|
+
method: 'POST',
|
|
2064
|
+
body: buildDataViewQueryBody(flags),
|
|
2065
|
+
}
|
|
2066
|
+
);
|
|
2067
|
+
if (flags.json) return writeJson(data);
|
|
2068
|
+
print(JSON.stringify(data, null, 2));
|
|
2069
|
+
return;
|
|
2070
|
+
}
|
|
2071
|
+
|
|
2072
|
+
fail('用法: openxiangda data-view list|status|refresh|query|stats');
|
|
2056
2073
|
}
|
|
2057
2074
|
|
|
2058
2075
|
async function permission(args) {
|
|
@@ -2675,7 +2692,7 @@ async function commands(args) {
|
|
|
2675
2692
|
'menu list|create|bind|delete',
|
|
2676
2693
|
'workflow list|create|bind|pull|publish|delete|validate',
|
|
2677
2694
|
'automation list|create|bind|pull|publish|unpublish|enable|disable|delete|validate|cron-validate',
|
|
2678
|
-
'data-view list|status|refresh|query',
|
|
2695
|
+
'data-view list|status|refresh|query|stats',
|
|
2679
2696
|
'permission role-list|role-create|role-bind|role-users|role-add-users',
|
|
2680
2697
|
'permission page-group-list|page-group-create|page-group-bind',
|
|
2681
2698
|
'permission form-group-list|form-group-create|form-group-bind|form-summary|menu-permissions',
|
|
@@ -3379,11 +3396,24 @@ function validateResourceItem(kind, item, errors, warnings) {
|
|
|
3379
3396
|
}
|
|
3380
3397
|
if (kind === 'dataViews') {
|
|
3381
3398
|
const definition = item.definition || item;
|
|
3399
|
+
const viewType = String(definition.viewType || 'row').toLowerCase();
|
|
3382
3400
|
if (!item.name && !definition.name) errors.push(`${label}: 缺少 name`);
|
|
3383
3401
|
if (!definition.base) errors.push(`${label}: 缺少 base`);
|
|
3384
|
-
if (
|
|
3385
|
-
|
|
3402
|
+
if (viewType === 'aggregate') {
|
|
3403
|
+
if (Array.isArray(definition.select) && definition.select.length > 0) {
|
|
3404
|
+
errors.push(`${label}: 聚合数据视图不能同时使用 select`);
|
|
3405
|
+
}
|
|
3406
|
+
if (!Array.isArray(definition.measures) || definition.measures.length === 0) {
|
|
3407
|
+
errors.push(`${label}: 缺少 measures`);
|
|
3408
|
+
}
|
|
3409
|
+
} else if (viewType === 'row') {
|
|
3410
|
+
if (!Array.isArray(definition.select) || definition.select.length === 0) {
|
|
3411
|
+
errors.push(`${label}: 缺少 select`);
|
|
3412
|
+
}
|
|
3413
|
+
} else {
|
|
3414
|
+
errors.push(`${label}: 不支持的 viewType: ${definition.viewType}`);
|
|
3386
3415
|
}
|
|
3416
|
+
validateDataViewPerformance(label, definition, errors, warnings);
|
|
3387
3417
|
}
|
|
3388
3418
|
if (kind === 'pagePermissionGroups' && !item.name) errors.push(`${label}: 缺少 name`);
|
|
3389
3419
|
if (kind === 'formPermissionGroups') {
|
|
@@ -3405,6 +3435,105 @@ function validateResourceItem(kind, item, errors, warnings) {
|
|
|
3405
3435
|
}
|
|
3406
3436
|
}
|
|
3407
3437
|
|
|
3438
|
+
function validateDataViewPerformance(label, definition, errors, warnings) {
|
|
3439
|
+
const viewType = String(definition.viewType || 'row').toLowerCase();
|
|
3440
|
+
if (viewType !== 'row' && viewType !== 'aggregate') return;
|
|
3441
|
+
const outputAliases = collectDataViewOutputAliases(definition, viewType);
|
|
3442
|
+
const outputAliasSet = new Set(outputAliases);
|
|
3443
|
+
const indexes = Array.isArray(definition.indexes)
|
|
3444
|
+
? definition.indexes.filter(index => Array.isArray(index.fields) && index.fields.length > 0)
|
|
3445
|
+
: [];
|
|
3446
|
+
|
|
3447
|
+
for (const index of indexes) {
|
|
3448
|
+
for (const field of index.fields || []) {
|
|
3449
|
+
if (!outputAliasSet.has(field)) {
|
|
3450
|
+
errors.push(`${label}: 索引字段不在输出字段中: ${field}`);
|
|
3451
|
+
}
|
|
3452
|
+
}
|
|
3453
|
+
}
|
|
3454
|
+
|
|
3455
|
+
if (!indexes.length) {
|
|
3456
|
+
warnings.push(
|
|
3457
|
+
viewType === 'aggregate'
|
|
3458
|
+
? `${label}: 聚合数据视图未声明 indexes;请优先索引常用维度、时间桶或排序字段,避免统计查询扫描物化视图`
|
|
3459
|
+
: `${label}: 未声明 indexes;请为常用 filters/order 字段声明索引,避免运行时查询扫描物化视图`
|
|
3460
|
+
);
|
|
3461
|
+
}
|
|
3462
|
+
|
|
3463
|
+
if (viewType === 'aggregate') {
|
|
3464
|
+
const dimensionAliases = new Set(
|
|
3465
|
+
(Array.isArray(definition.dimensions) ? definition.dimensions : [])
|
|
3466
|
+
.map(item => item && item.as)
|
|
3467
|
+
.filter(Boolean)
|
|
3468
|
+
);
|
|
3469
|
+
const indexesCoverDimension =
|
|
3470
|
+
dimensionAliases.size === 0 ||
|
|
3471
|
+
indexes.some(index => index.fields.some(field => dimensionAliases.has(field)));
|
|
3472
|
+
if (indexes.length && !indexesCoverDimension) {
|
|
3473
|
+
warnings.push(`${label}: 聚合数据视图 indexes 未覆盖任何 dimension;按维度筛选或排序时可能扫描物化视图`);
|
|
3474
|
+
}
|
|
3475
|
+
if ((definition.measures || []).some(isCountDistinctMeasure)) {
|
|
3476
|
+
warnings.push(`${label}: 聚合数据视图使用 countDistinct;高基数字段刷新成本较高,请确认数据量和刷新频率`);
|
|
3477
|
+
}
|
|
3478
|
+
}
|
|
3479
|
+
|
|
3480
|
+
const refresh = definition.refresh && typeof definition.refresh === 'object' ? definition.refresh : {};
|
|
3481
|
+
if (refresh.mode === 'scheduled') {
|
|
3482
|
+
const intervalMs = estimateCronIntervalMsFromExpression(refresh.cron);
|
|
3483
|
+
if (intervalMs !== null && intervalMs < 5 * 60 * 1000) {
|
|
3484
|
+
warnings.push(`${label}: 数据视图刷新间隔低于 5 分钟;请先确认业务时间敏感度,高频刷新会增加源表和物化视图压力`);
|
|
3485
|
+
}
|
|
3486
|
+
}
|
|
3487
|
+
}
|
|
3488
|
+
|
|
3489
|
+
function collectDataViewOutputAliases(definition, viewType) {
|
|
3490
|
+
if (viewType === 'aggregate') {
|
|
3491
|
+
return [
|
|
3492
|
+
...(Array.isArray(definition.dimensions) ? definition.dimensions : []),
|
|
3493
|
+
...(Array.isArray(definition.measures) ? definition.measures : []),
|
|
3494
|
+
]
|
|
3495
|
+
.map(item => item && item.as)
|
|
3496
|
+
.filter(Boolean);
|
|
3497
|
+
}
|
|
3498
|
+
return (Array.isArray(definition.select) ? definition.select : [])
|
|
3499
|
+
.map(item => item && item.as)
|
|
3500
|
+
.filter(Boolean);
|
|
3501
|
+
}
|
|
3502
|
+
|
|
3503
|
+
function isCountDistinctMeasure(measure) {
|
|
3504
|
+
const normalized = String(measure?.type || '')
|
|
3505
|
+
.toLowerCase()
|
|
3506
|
+
.replace(/[-_]/g, '');
|
|
3507
|
+
return normalized === 'countdistinct' || normalized === 'distinctcount';
|
|
3508
|
+
}
|
|
3509
|
+
|
|
3510
|
+
function estimateCronIntervalMsFromExpression(cron) {
|
|
3511
|
+
const parts = String(cron || '')
|
|
3512
|
+
.trim()
|
|
3513
|
+
.split(/\s+/)
|
|
3514
|
+
.filter(Boolean);
|
|
3515
|
+
if (parts.length < 5) return null;
|
|
3516
|
+
const hasSeconds = parts.length >= 6;
|
|
3517
|
+
const seconds = hasSeconds ? parts[0] : '0';
|
|
3518
|
+
const minutes = hasSeconds ? parts[1] : parts[0];
|
|
3519
|
+
if (seconds === '*') return 1000;
|
|
3520
|
+
const secondStep = parseCronStep(seconds);
|
|
3521
|
+
if (secondStep) return secondStep * 1000;
|
|
3522
|
+
if (seconds === '0') {
|
|
3523
|
+
if (minutes === '*') return 60 * 1000;
|
|
3524
|
+
const minuteStep = parseCronStep(minutes);
|
|
3525
|
+
if (minuteStep) return minuteStep * 60 * 1000;
|
|
3526
|
+
}
|
|
3527
|
+
return null;
|
|
3528
|
+
}
|
|
3529
|
+
|
|
3530
|
+
function parseCronStep(field) {
|
|
3531
|
+
const match = String(field || '').match(/^(?:\*|0)\/(\d+)$/);
|
|
3532
|
+
if (!match) return null;
|
|
3533
|
+
const value = Number(match[1]);
|
|
3534
|
+
return Number.isFinite(value) && value > 0 ? value : null;
|
|
3535
|
+
}
|
|
3536
|
+
|
|
3408
3537
|
const NOTIFICATION_CHANNELS = new Set([
|
|
3409
3538
|
'inapp',
|
|
3410
3539
|
'email',
|
|
@@ -5936,6 +6065,7 @@ function buildDataViewQueryBody(flags = {}) {
|
|
|
5936
6065
|
if (fields.length > 0) body.fields = fields;
|
|
5937
6066
|
if (flags['filters-json']) body.filters = readJsonArg(flags['filters-json'], 'filters-json');
|
|
5938
6067
|
if (flags['filter-json']) body.filters = readJsonArg(flags['filter-json'], 'filter-json');
|
|
6068
|
+
if (flags['having-json']) body.having = readJsonArg(flags['having-json'], 'having-json');
|
|
5939
6069
|
if (flags['condition-type']) body.conditionType = String(flags['condition-type']).toUpperCase();
|
|
5940
6070
|
if (flags.keyword || flags.search) body.searchKeyWord = flags.keyword || flags.search;
|
|
5941
6071
|
if (flags.page) body.currentPage = Number(flags.page);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: openxiangda
|
|
3
|
-
description: Use OpenXiangda for ANY work inside a sy-lowcode-app-workspace or any private low-code platform (OpenXiangda / 湘搭 / 私有化低代码) — publishing / 发布 / 上线 / deploying / 部署 / shipping / releasing / 上传 / pushing apps, pages, forms, workflows, automations; creating / scaffolding / 创建 / 搭建 / 新建 apps, code pages, form pages, workflow forms, JS_CODE nodes; editing / 修改 / 编辑 schemas, options, fields, layouts, menus; managing / 管理 roles, page permission groups, form permission groups, form settings, public access, data views, connectors, notifications; diagnosing / 排查 / 诊断 / 看快照 app snapshots, executions, logs, version drift, profile isolation, token / login / profile issues; running the openxiangda CLI or anything that touches `.openxiangda/state.json`, `~/.openxiangda/profiles.json`, `/openxiangda-api/v1`, `OPENXIANGDA_PROFILE / BASE_URL / ACCESS_TOKEN / APP_TYPE`, `app-workspace.config.ts`. Trigger when the user mentions OpenXiangda / 湘搭 / 私有化低代码 / 低代码平台 / sy-lowcode-app-workspace, or when the workspace contains `.openxiangda/state.json` or `app-workspace.config.ts`. Always prefer this skill over running `pnpm publish:all`, `pnpm publish:oss`, `pnpm register`, or `lowcode-workspace publish-*` directly.
|
|
3
|
+
description: Use OpenXiangda for ANY work inside a sy-lowcode-app-workspace or any private low-code platform (OpenXiangda / 湘搭 / 私有化低代码) — publishing / 发布 / 上线 / deploying / 部署 / shipping / releasing / 上传 / pushing apps, pages, forms, workflows, automations; creating / scaffolding / 创建 / 搭建 / 新建 apps, code pages, form pages, workflow forms, JS_CODE nodes; editing / 修改 / 编辑 schemas, options, fields, layouts, menus; building form-entry UX / 表单录入体验 with OpenXiangda platform form components first, Ant Design / antd-mobile second, and no raw native HTML form controls in generated app code; managing / 管理 roles, page permission groups, form permission groups, form settings, public access, data views, connectors, notifications; proactively collecting and submitting platform feedback / 需求反馈 / 问题反馈 for bugs, unclear design rules, missing platform capabilities, repeated workarounds, and AI uncertainty; diagnosing / 排查 / 诊断 / 看快照 app snapshots, executions, logs, version drift, profile isolation, token / login / profile issues; running the openxiangda CLI or anything that touches `.openxiangda/state.json`, `~/.openxiangda/profiles.json`, `/openxiangda-api/v1`, `OPENXIANGDA_PROFILE / BASE_URL / ACCESS_TOKEN / APP_TYPE`, `app-workspace.config.ts`. Trigger when the user mentions OpenXiangda / 湘搭 / 私有化低代码 / 低代码平台 / sy-lowcode-app-workspace, or when the workspace contains `.openxiangda/state.json` or `app-workspace.config.ts`. Always prefer this skill over running `pnpm publish:all`, `pnpm publish:oss`, `pnpm register`, or `lowcode-workspace publish-*` directly.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# OpenXiangda
|
|
@@ -28,7 +28,7 @@ For any user-facing page (form pages, workflow form pages, custom code pages), O
|
|
|
28
28
|
| 角色 / 权限组 / 字段权限 / 数据范围 / 公开访问 | `openxiangda-permission-settings` | `openxiangda permission ...` / `openxiangda settings ...` |
|
|
29
29
|
| 看应用结构 / 快照 / 对比 / 诊断 / 排查 / 报错 | `openxiangda-inspect` | `openxiangda app snapshot <APP_XXX> --profile <name> --json` |
|
|
30
30
|
| 登录 / 切换平台 / profile / token / whoami | `openxiangda-core` | `openxiangda env --profile <name>` / `openxiangda auth status` |
|
|
31
|
-
|
|
|
31
|
+
| 多表只读联表 / 固定口径统计 / 看板指标 | `openxiangda-form` (data view) | declare `src/resources/data-views/<code>.json` → `resource publish` |
|
|
32
32
|
| 调外部 / 第三方 API / 钉钉 / 自建系统 | `openxiangda-page` (connector) | declare `src/resources/connectors/<code>.json` → `sdk.connector.invoke` |
|
|
33
33
|
|
|
34
34
|
### Hard rules — always
|
|
@@ -37,6 +37,8 @@ For any user-facing page (form pages, workflow form pages, custom code pages), O
|
|
|
37
37
|
- ✅ User token lives in `~/.openxiangda/profiles.json`; project state in `.openxiangda/state.json` (IDs only).
|
|
38
38
|
- ✅ Each profile (dev / prod / ...) has its own `appType` and resource IDs; never copy a `formUuid` / `pageId` / `workflowId` / `automationId` across profiles.
|
|
39
39
|
- ✅ Run `openxiangda update check --json` at the start of substantial work; if `updateAvailable`, run `openxiangda update install` and `openxiangda skill install --force`.
|
|
40
|
+
- ✅ For form-entry UX, pick components in this order: OpenXiangda platform form components → `antd` / `antd-mobile` wrappers → custom component only when neither exists.
|
|
41
|
+
- ✅ Proactively submit platform feedback when you discover a defect, missing capability, unclear rule, repeated workaround, user-visible UX gap, or implementation uncertainty worth improving.
|
|
40
42
|
|
|
41
43
|
### Hard rules — never
|
|
42
44
|
|
|
@@ -46,6 +48,7 @@ For any user-facing page (form pages, workflow form pages, custom code pages), O
|
|
|
46
48
|
- ❌ Using `openxiangda form create` / `form publish` / `page publish` as the normal page generation path. They are low-level repair commands.
|
|
47
49
|
- ❌ Running a full publish after editing one file. Default to `--changed --dry-run` → `--changed`, or targeted `--page` / `--form`.
|
|
48
50
|
- ❌ Storing tokens, AK, SK, or third-party API secrets in project files. Shared env (`APP_OSS_*`, feedback robot) goes to `~/.openxiangda/.env`.
|
|
51
|
+
- ❌ Raw native HTML form controls in AI-authored workspace code (`<input>`, `<select>`, `<textarea>`, file inputs, hand-written pickers, hand-written upload controls). They are allowed only inside OpenXiangda SDK/platform component internals.
|
|
49
52
|
|
|
50
53
|
## Platform Routing
|
|
51
54
|
|
|
@@ -121,7 +124,7 @@ When the user provides a root domain such as `https://yida.wisejob.cn/`, use it
|
|
|
121
124
|
- For routine AI edits, avoid reflexive full publish. First run `openxiangda workspace publish --profile <name> --changed --dry-run`, then use `--changed`, `--page <pageCode>`, `--form <formCode>`, or `--only pages/a,forms/b`. Use full publish only when broad shared/config changes intentionally affect many modules.
|
|
122
125
|
- 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.
|
|
123
126
|
- Shared workspace env values such as `APP_OSS_*` should live in `~/.openxiangda/.env` by default. Project `.env` files are only per-workspace overrides.
|
|
124
|
-
- For suspected platform defects, bugs,
|
|
127
|
+
- For suspected platform defects, bugs, missing capabilities, unclear design rules, repeated workarounds, AI uncertainty, or product optimization requests, proactively run `openxiangda feedback submit --yes` with a concise summary and detailed description. Include the command, error, relevant files, and logs/context files when available. If robot env is missing or submission fails, run/record `openxiangda feedback preview` and tell the user what could not be sent. After successful submission, tell the user what was reported and include the feedback fingerprint. The CLI redacts tokens, cookies, secrets, phone numbers, and emails before sending.
|
|
125
128
|
- For form pages, every visible field should have a concise user-facing `placeholder`. Use `tips` only for special constraints or non-obvious business rules; do not add tips to every field.
|
|
126
129
|
- Form pages should display only fields the user needs to see. Use `SelectField` / `RadioField` for enums. For data maintained by another form, such as class, college, customer, or project, use `SelectField` with `optionSource.type: "linkedForm"` so the runtime queries form data through the SDK and builds dropdown options; set `remoteSearch: true` and an explicit `searchFieldId` when the source data can be large. Do not make users maintain raw ID text fields, and do not use `AssociationFormField` for new form work.
|
|
127
130
|
- Permission scope keys, computed fields, sync fields, and developer/internal fields should be present only when needed for permissions or logic, and should normally be derived from visible select/person/department fields and marked `behavior: "HIDDEN"`. For select-derived scalar keys, use `valueSync`.
|
|
@@ -129,7 +132,7 @@ When the user provides a root domain such as `https://yida.wisejob.cn/`, use it
|
|
|
129
132
|
- All visible copy in forms and pages must be written for end users. Do not put implementation notes, developer explanations, schema descriptions, or "this area is generated by..." text into page sections, cards, labels, tips, or empty states.
|
|
130
133
|
- Use logical resource codes in local files. Platform-specific IDs such as `formUuid`, `pageId`, `workflowId`, and `automationId` must be isolated by profile.
|
|
131
134
|
- Put engineering-managed resources in `src/resources/` and use `openxiangda resource validate|plan|publish|pull`. `workspace publish` publishes workspace forms/pages first, then runs non-destructive resource upsert. Only pass `--prune` when the user explicitly wants local manifests to delete platform-side extras.
|
|
132
|
-
- For repeated read-only multi-form queries, create a data view manifest in `src/resources/data-views
|
|
135
|
+
- For repeated read-only multi-form queries or predefined dashboard metrics, create a data view manifest in `src/resources/data-views/`. Use row views plus `sdk.dataView.query` for joined lists/lookups; use `viewType: "aggregate"` plus `sdk.dataView.stats` for count/sum/avg/min/max statistics. Before choosing scheduled refresh, confirm the user's freshness tolerance; default to manual or 10 minutes and above unless fresher data is explicitly required. Add `indexes` for common runtime filters/order fields, aggregate dimensions, and time buckets. Do not use data views for single-form CRUD, simple linkedForm selects, real-time writes, write-back, or ad-hoc BI. Read `references/data-views.md` before designing one.
|
|
133
136
|
- For external APIs, create a connector manifest in `src/resources/connectors/` and call it from pages through `sdk.connector`; never put third-party API keys in page source.
|
|
134
137
|
- Before scaffolding a real app, complex page, data management page, portal shell, status lifecycle, role governance, or automation, read `references/best-practices.md` and pick the architecture first. Prefer copying the relevant pattern from `examples/best-practices/` into `src/` instead of generating a single large page file.
|
|
135
138
|
- 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.
|
|
@@ -195,5 +198,5 @@ Platform domain knowledge (read these first when generating forms or pages so th
|
|
|
195
198
|
- `references/style-system.md` — style isolation defaults, legacy namespace compatibility, and flexible Tailwind/CSS guidance. Use this before writing substantial page or form CSS.
|
|
196
199
|
- `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.
|
|
197
200
|
- `references/best-practices.md` — read this before implementing app-level business patterns; it points to `examples/best-practices/` and explains when to use status fields instead of workflow.
|
|
198
|
-
- `references/component-guide.md` —
|
|
201
|
+
- `references/component-guide.md` — component priority ladder: platform components first, `antd` / `antd-mobile` fallback, and no raw native form controls in AI-authored app code. Use this before introducing a new component.
|
|
199
202
|
- `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.
|
|
@@ -28,6 +28,9 @@ form codes, field names, permissions, and page route names.
|
|
|
28
28
|
- real approval workflow
|
|
29
29
|
- automation / JS_CODE
|
|
30
30
|
- role governance / permissions
|
|
31
|
+
- optional admin UI template:
|
|
32
|
+
`glass-home-dashboard`, `mint-analytics-dashboard`,
|
|
33
|
+
`ops-monitor-dashboard`, or `work-order-list-drawer`
|
|
31
34
|
2. Read the matching files in `examples/best-practices/`.
|
|
32
35
|
3. Copy the smallest useful slice into `src/`.
|
|
33
36
|
4. Keep view code thin; put reusable business logic in `domain/` and platform
|
|
@@ -167,16 +170,22 @@ groups or backend-side JS_CODE checks.
|
|
|
167
170
|
- Destructive or state-changing actions need confirmation.
|
|
168
171
|
- Show processing feedback and success/failure feedback.
|
|
169
172
|
- On failure, refresh or rollback the affected row instead of leaving stale UI.
|
|
173
|
+
- For PC CRUD, ticket, approval, and data-management pages, use overlay drawers
|
|
174
|
+
or modals for detail, create, edit, approve/reject, and process forms. Do not
|
|
175
|
+
keep a permanent right-side detail/form column that narrows the table.
|
|
170
176
|
- Keep interaction styles consistent across PC and mobile, but do not force the
|
|
171
177
|
same layout component onto both viewports.
|
|
172
178
|
|
|
173
179
|
## Library Selection
|
|
174
180
|
|
|
175
|
-
- Do not hand-write mature controls or engines. Use platform components
|
|
176
|
-
personnel, departments, files, images, rich text,
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
181
|
+
- Do not hand-write mature controls or engines. Use platform components first
|
|
182
|
+
for form-entry fields, personnel, departments, files, images, rich text,
|
|
183
|
+
signatures, locations, standard forms, and data lists.
|
|
184
|
+
- Use `antd` / `antd-mobile` as the fallback for non-platform page controls,
|
|
185
|
+
overlays, tables, steps, tabs, and temporary wrappers for missing platform
|
|
186
|
+
capabilities. AI-authored app/workspace code must not emit raw native form
|
|
187
|
+
controls; native inputs belong inside OpenXiangda SDK/platform component
|
|
188
|
+
internals only.
|
|
180
189
|
- Use ECharts for charts and dashboards.
|
|
181
190
|
- Use GSAP for complex timeline/scroll/sequence animations; simple transitions
|
|
182
191
|
can stay in CSS. If an animation-specific skill is available, read it before
|
|
@@ -189,6 +198,21 @@ groups or backend-side JS_CODE checks.
|
|
|
189
198
|
|
|
190
199
|
## Template Catalog
|
|
191
200
|
|
|
201
|
+
- `glass-home-dashboard`: optional default for PC admin home pages, management
|
|
202
|
+
portals, and business overviews. Includes sidebar, top search, welcome
|
|
203
|
+
banner, quick actions, KPI cards, charts, ranking, activity feed, query
|
|
204
|
+
states, and drawer form actions.
|
|
205
|
+
- `mint-analytics-dashboard`: optional default for data-analysis and BI-like
|
|
206
|
+
dashboards. Includes dense metric cards, line/bar/donut charts, notifications,
|
|
207
|
+
todos, business summary, system health, query states, and drawer form actions.
|
|
208
|
+
- `ops-monitor-dashboard`: optional default for realtime operations,
|
|
209
|
+
monitoring, task execution, alerts, system health, and device status. Uses
|
|
210
|
+
lightweight CSS motion by default; do not add GSAP unless a real timeline
|
|
211
|
+
requirement exists.
|
|
212
|
+
- `work-order-list-drawer`: default CRUD/list template for tickets, approvals,
|
|
213
|
+
data-management pages, orders, and assets. Includes filters, toolbar, status
|
|
214
|
+
stats, table, loading/empty/error branches, and right-side overlay drawers for
|
|
215
|
+
detail, create, edit, and process forms.
|
|
192
216
|
- `customer-profile`: standard form schema with validation, members,
|
|
193
217
|
departments, attachments, and child table.
|
|
194
218
|
- `service-ticket-lifecycle`: status lifecycle with ticket form, action-log
|
|
@@ -6,23 +6,36 @@
|
|
|
6
6
|
|
|
7
7
|
## 1. 核心原则
|
|
8
8
|
|
|
9
|
-
1.
|
|
10
|
-
2.
|
|
11
|
-
3.
|
|
12
|
-
4.
|
|
13
|
-
5.
|
|
9
|
+
1. **平台组件优先**:表单录入、平台数据选择、文件能力和数据管理默认先用 `openxiangda` 平台组件。它们已经接入组织架构、文件存储、权限、多端适配和平台数据格式,重写一遍 = 丢能力 + 后续无法维护。
|
|
10
|
+
2. **antd / antd-mobile 是第二选择**:只有平台组件不存在、或正在做非平台数据的页面控件时,才使用 `antd` / `antd-mobile`。自定义组件必须包装它们,不能从原生 DOM 开始造。
|
|
11
|
+
3. **AI-authored app code 禁止原生表单控件**:`src/forms/**`、`src/pages/**`、workspace 模板和示例中不要直接写 `<input>`、`<select>`、`<textarea>`、`<input type="file">`、手写 picker、手写 uploader、手写人员/部门选择器。原生控件只允许出现在 OpenXiangda SDK / 平台组件内部实现。
|
|
12
|
+
4. **成熟能力先调研开源方案**:图表、动画、拖拽、虚拟列表、富文本、日历、导入导出、复杂表格等,不要直接手写。先查当前项目依赖、官方文档和维护状态,再决定使用库或轻量封装。
|
|
13
|
+
5. **样式默认走 Tailwind 原生类**:业务页面默认使用 Tailwind 原生工具类和任意值;平台变量、平台语义类和 Ant Design token 主要用于平台组件、主题兼容或确实需要统一主题时,不作为业务页面的强制范式。
|
|
14
|
+
6. **谨慎覆盖组件内部 class**:组件库内部类名(`ant-select-selector` 等)是私有 API,下个版本就可能变;确实需要覆盖时要限定页面作用域,并优先考虑组件 props、`className`、CSS 变量或 `ConfigProvider`。
|
|
14
15
|
|
|
15
16
|
---
|
|
16
17
|
|
|
17
|
-
## 1.1
|
|
18
|
+
## 1.1 组件选择梯度
|
|
19
|
+
|
|
20
|
+
| 优先级 | 使用对象 | 适用场景 | 禁止事项 |
|
|
21
|
+
| --- | --- | --- | --- |
|
|
22
|
+
| 1 | OpenXiangda 平台组件 | 表单字段、平台数据选择、文件/图片/富文本/签名/定位、数据管理列表、标准表单容器 | 不要用 antd 或原生控件复刻 |
|
|
23
|
+
| 2 | `antd` / `antd-mobile` | 页面筛选、弹窗、抽屉、步骤条、非平台数据控件、平台暂无组件时的临时包装 | 不要手写 DOM 行为;保留 props/ref/a11y/键盘能力 |
|
|
24
|
+
| 3 | 自定义组件 | 平台和 antd 都不能满足的特殊业务交互 | 先反馈平台缺口;实现只写业务适配层 |
|
|
25
|
+
| 4 | 原生 HTML 控件 | 仅 OpenXiangda SDK / 平台组件内部实现 | AI 生成的 app/workspace 代码禁止使用 |
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 1.2 开源库选型规则
|
|
18
30
|
|
|
19
31
|
AI 在实现成熟交互前必须先判断是否已有可靠组件或库:
|
|
20
32
|
|
|
21
33
|
| 场景 | 优先方案 | 说明 |
|
|
22
34
|
| --- | --- | --- |
|
|
23
|
-
|
|
|
24
|
-
|
|
|
25
|
-
|
|
|
35
|
+
| 表单录入字段 | `openxiangda` 平台字段组件 | 文本、数字、日期、选择、人员、部门、附件、图片、富文本、位置、子表等优先 schema/platform field。 |
|
|
36
|
+
| 平台数据录入能力 | `openxiangda` 平台字段组件 | 人员、部门、附件、图片、富文本、签名、位置、数据管理列表必须平台组件。 |
|
|
37
|
+
| PC 页面控件、筛选、弹窗、表格、步骤条 | `antd` | 仅用于非平台数据控件或平台组件缺失时的包装。不要用原生 `<input>` / `<select>` 复刻。 |
|
|
38
|
+
| 移动端操作、列表、弹层、Tab | `antd-mobile` | 移动端不要强行套 PC 组件;平台字段组件已多端适配时直接用平台组件。 |
|
|
26
39
|
| 图表、仪表盘、经营看板 | `echarts` + `echarts-for-react` | 不要手写 canvas/SVG 图表,除非只是非常小的装饰图形。 |
|
|
27
40
|
| 复杂时间轴动画、编排动画、滚动动画 | `gsap` | 需要动画库时先查 GSAP 官方文档;如果当前 Agent 有动画/GSAP skill,先读取该 skill。简单 hover/transition 用 CSS 即可。 |
|
|
28
41
|
| 拖拽排序、看板、可拖卡片 | `@dnd-kit/*` | 不要自己处理 pointer/mouse/touch 全套事件。 |
|
|
@@ -32,10 +45,11 @@ AI 在实现成熟交互前必须先判断是否已有可靠组件或库:
|
|
|
32
45
|
|
|
33
46
|
选型流程:
|
|
34
47
|
|
|
35
|
-
1.
|
|
36
|
-
2.
|
|
37
|
-
3.
|
|
38
|
-
4.
|
|
48
|
+
1. 先查是否已有 OpenXiangda 平台组件或 schema 字段能表达。
|
|
49
|
+
2. 平台没有时,看模板 `package.json` 已有依赖,已有依赖优先复用。
|
|
50
|
+
3. 没有依赖但功能成熟复杂时,调研官方文档、维护状态、包体积和授权,再通过包管理器加入。
|
|
51
|
+
4. 页面代码只写业务适配层,不复制第三方库内部逻辑。
|
|
52
|
+
5. 新增依赖或平台能力缺口后,通过 `openxiangda feedback submit --yes` 反馈选择原因或能力缺口。
|
|
39
53
|
|
|
40
54
|
---
|
|
41
55
|
|
|
@@ -70,15 +84,21 @@ import {
|
|
|
70
84
|
|
|
71
85
|
---
|
|
72
86
|
|
|
73
|
-
## 3. 推荐使用平台组件(可自定义但不建议从零重做)
|
|
74
|
-
|
|
75
|
-
| 场景 | 平台组件 | 何时考虑自定义 |
|
|
76
|
-
| --- | --- | --- |
|
|
77
|
-
|
|
|
78
|
-
|
|
|
79
|
-
|
|
|
80
|
-
|
|
|
81
|
-
|
|
|
87
|
+
## 3. 推荐使用平台组件(可自定义但不建议从零重做)
|
|
88
|
+
|
|
89
|
+
| 场景 | 平台组件 | 何时考虑自定义 |
|
|
90
|
+
| --- | --- | --- |
|
|
91
|
+
| 短文本 | `TextField` | 需要复合输入(如扫码 + 输入)时,包装平台字段或 antd `Input` |
|
|
92
|
+
| 长文本 | `TextAreaField` | 需要模板片段、语音转写等增强时包装 |
|
|
93
|
+
| 数字 | `NumberField` | 需要特殊单位联动或计算器式输入时包装 |
|
|
94
|
+
| 下拉选择 | `SelectField` | 需要复杂远程搜索、级联、虚拟滚动时,包装而非重写 |
|
|
95
|
+
| 多选 / 单选 / 复选 | `MultiSelectField` / `RadioField` / `CheckboxField` | 需要特殊展示时包装 |
|
|
96
|
+
| 日期选择 | `DateField` | 特殊日期范围联动、节假日高亮 |
|
|
97
|
+
| 级联日期 / 级联选择 | `CascadeDateField` / `CascadeSelectField` | 需要特殊联动时包装 |
|
|
98
|
+
| 子表 | `SubFormField` | 需要完整行级业务动作时包装 |
|
|
99
|
+
| 表单容器 | `FormProvider` + `FormRenderer` | 需要极度定制布局(分栏、Tab 嵌套表单) |
|
|
100
|
+
| 审批时间线 | `ApprovalTimeline` | 需要自定义节点渲染(增加业务标签、跳转链接) |
|
|
101
|
+
| 摘要卡片 | `FormSummaryCard` | 需要高度自定义字段展示样式 |
|
|
82
102
|
|
|
83
103
|
自定义时仍应**包装**这些组件,而不是从 antd 从零实现一遍。
|
|
84
104
|
|
|
@@ -86,7 +106,9 @@ import {
|
|
|
86
106
|
|
|
87
107
|
## 4. 自定义组件开发规范
|
|
88
108
|
|
|
89
|
-
### 4.1
|
|
109
|
+
### 4.1 推荐:平台组件缺失时,基于 antd 包装 + 原生 Tailwind 类
|
|
110
|
+
|
|
111
|
+
先判断平台组件确实不能满足;如果是平台能力缺口,先用 `openxiangda feedback submit --yes` 反馈,再写本地包装作为临时方案。
|
|
90
112
|
|
|
91
113
|
```tsx
|
|
92
114
|
import { Select, SelectProps } from 'antd';
|
|
@@ -116,13 +138,14 @@ export function CustomSelect({ options, className, ...rest }: CustomSelectProps)
|
|
|
116
138
|
- 默认 `cssIsolation: "none"` 时不要额外给弹层加 `sy-app-workspace`;只有 legacy `namespace/shadow` 页面才需要给弹层或容器补 namespace class。
|
|
117
139
|
- 使用 `w-full`、`rounded-md`、`text-slate-600`、`border-slate-200` 等 Tailwind 原生类作为默认基线;如果组件视觉需要精调,可以继续使用 Tailwind 任意值或局部 CSS。
|
|
118
140
|
|
|
119
|
-
### 4.2
|
|
120
|
-
|
|
121
|
-
```tsx
|
|
122
|
-
// ❌
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
141
|
+
### 4.2 错误:从头实现选择器或原生表单控件
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
// ❌ 反例:AI-authored app code 不要这样写。
|
|
145
|
+
// 丢失键盘导航、可访问性、移动端体验、平台主题和平台数据格式。
|
|
146
|
+
function BadSelect({ options }) {
|
|
147
|
+
return (
|
|
148
|
+
<div style={{ border: '1px solid #d9d9d9', borderRadius: '4px', padding: '4px 11px' }}>
|
|
126
149
|
{options.map((opt) => (
|
|
127
150
|
<div key={opt.value} onClick={() => /* ... */}>
|
|
128
151
|
{opt.label}
|
|
@@ -130,14 +153,22 @@ function BadSelect({ options }) {
|
|
|
130
153
|
))}
|
|
131
154
|
</div>
|
|
132
155
|
);
|
|
133
|
-
}
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
```tsx
|
|
160
|
+
// ❌ 反例:表单录入不要直接写原生控件
|
|
161
|
+
<input value={name} onChange={(event) => setName(event.target.value)} />
|
|
162
|
+
<select value={type} onChange={(event) => setType(event.target.value)} />
|
|
163
|
+
<input type="file" onChange={uploadManually} />
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
问题清单:
|
|
167
|
+
- 硬编码颜色 `#d9d9d9` → 切主题失效。
|
|
168
|
+
- 丢失 antd 的键盘 / a11y / 输入法行为。
|
|
169
|
+
- 移动端无适配。
|
|
170
|
+
- 平台主题升级时不会跟随。
|
|
171
|
+
- 绕过平台表单 schema、字段权限、文件鉴权、组织架构和标准数据格式。
|
|
141
172
|
|
|
142
173
|
---
|
|
143
174
|
|
|
@@ -199,11 +230,12 @@ export function ActionButton(props) {
|
|
|
199
230
|
|
|
200
231
|
## 7. 常见错误(AI Agent 高频踩坑)
|
|
201
232
|
|
|
202
|
-
| 错误 | 正确做法 |
|
|
203
|
-
| --- | --- |
|
|
204
|
-
|
|
|
205
|
-
|
|
|
206
|
-
|
|
|
233
|
+
| 错误 | 正确做法 |
|
|
234
|
+
| --- | --- |
|
|
235
|
+
| 在 `src/forms/**` 或 `src/pages/**` 直接写 `<input>` / `<select>` / `<textarea>` | 用平台字段组件;平台没有时包装 antd / antd-mobile |
|
|
236
|
+
| 自己写人员选择器(搜员工 + 列表) | 用 `UserSelectField` |
|
|
237
|
+
| 自己实现部门树 | 用 `DepartmentSelectField` |
|
|
238
|
+
| 用 `<input type="file">` 上传 | 用 `AttachmentField` / `ImageField` |
|
|
207
239
|
| 用第三方富文本(TinyMCE 等) | 用 `EditorField` |
|
|
208
240
|
| 自己写列表 + 分页 + 导出 | 用 `DataManagementList` |
|
|
209
241
|
| 常规组件大量写 `style={{ color: '#333', padding: 16 }}` | 优先 Tailwind 原生工具类、任意值或局部 CSS;动态值和少量精调 inline style 可接受 |
|
|
@@ -216,8 +248,10 @@ export function ActionButton(props) {
|
|
|
216
248
|
|
|
217
249
|
## 速查 Checklist(提交前自检)
|
|
218
250
|
|
|
219
|
-
- [ ] 涉及人员 / 部门 / 文件 / 图片 / 富文本 / 签名 / 地图 / 列表 → 用了平台组件?
|
|
220
|
-
- [ ]
|
|
251
|
+
- [ ] 涉及人员 / 部门 / 文件 / 图片 / 富文本 / 签名 / 地图 / 列表 → 用了平台组件?
|
|
252
|
+
- [ ] 表单录入字段 → 优先使用了 `TextField` / `SelectField` / `DateField` 等平台字段,而不是 antd 或原生控件?
|
|
253
|
+
- [ ] 自定义组件 → 基于 antd / antd-mobile 包装并透传 props?
|
|
254
|
+
- [ ] app/workspace 源码 → 没有直接出现原生 `<input>` / `<select>` / `<textarea>` / file input / 手写 picker 或 uploader?
|
|
221
255
|
- [ ] 样式 → 默认用 Tailwind 原生类 / 任意值 / 局部 CSS;平台 token 只在主题兼容或平台组件需要时使用,且作用域收敛?
|
|
222
256
|
- [ ] 弹层 → 默认使用正常容器;只有 legacy 隔离页面才额外设置 `sy-app-workspace` 样式作用域?
|
|
223
257
|
- [ ] 多端 → PC 用 antd、Mobile 用 antd-mobile,业务逻辑共享?
|
|
@@ -28,7 +28,7 @@ Connector manifests use stable `code` values. The platform maps connector `code`
|
|
|
28
28
|
|
|
29
29
|
Notification manifests live under `src/resources/notifications/` and contain `templates` plus `typeConfigs`. See `notifications.md` before generating reminders or message templates.
|
|
30
30
|
|
|
31
|
-
Data view manifests live under `src/resources/data-views/` and define read-only materialized views for repeated multi-form joins. Use
|
|
31
|
+
Data view manifests live under `src/resources/data-views/` and define read-only materialized views for repeated multi-form joins or fixed aggregate statistics. Use row views with `sdk.dataView.query` for joined list/report/lookup sources, and aggregate views with `sdk.dataView.stats` for dashboard metrics where refresh lag is acceptable. Before generating one, confirm freshness tolerance and add indexes for common filters, sort fields, dimensions, and date buckets. Do not use them for single-form CRUD, simple linkedForm selects, real-time writes, write-back, or ad-hoc BI. See `data-views.md` before generating one.
|
|
32
32
|
|
|
33
33
|
```json
|
|
34
34
|
{
|