@zat-design/sisyphus-scene 4.5.9 → 4.5.10-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/sisyphus-scene.mjs +20 -14
- package/mcp/sisyphus-react.json +1 -4
- package/package.json +1 -1
- package/scripts/install-mcp.mjs +53 -26
- package/skills/sisyphus-scene/agents/openai.yaml +3 -4
- package/skills/sisyphus-scene/assets/ai-contract.json +1 -1
- package/skills/sisyphus-scene/evals/fixtures/invalid-antd-fallback.tsx +3 -2
- package/skills/sisyphus-scene/evals/fixtures/valid-decisions.json +4 -4
- package/skills/sisyphus-scene/evals/fixtures/valid-page.tsx +7 -5
- package/skills/sisyphus-scene/evals/trigger-cases.yaml +9 -10
- package/skills/sisyphus-scene/references/approved-examples.yaml +1 -1
- package/skills/sisyphus-scene/references/scene-model.md +19 -20
- package/skills/sisyphus-scene/references/validation-rules.md +0 -1
- package/skills/sisyphus-scene/references/workflow.md +6 -7
- package/skills/sisyphus-scene/scripts/inspect-project.mjs +0 -1
- package/skills/sisyphus-scene/scripts/install.mjs +12 -4
- package/skills/sisyphus-scene-legacy/SKILL.md +78 -43
- package/skills/sisyphus-scene-legacy/agents/openai.yaml +3 -3
package/bin/sisyphus-scene.mjs
CHANGED
|
@@ -38,30 +38,36 @@ if (!['sisyphus-scene', 'sisyphus-scene-legacy'].includes(options.skill)) {
|
|
|
38
38
|
throw new Error(`不支持的 skill:${options.skill}`);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
const platform = options.platform === 'all'
|
|
42
|
-
? 'claude,cursor,codex'
|
|
43
|
-
: options.platform;
|
|
41
|
+
const platform = options.platform === 'all' ? 'claude,cursor,codex' : options.platform;
|
|
44
42
|
const selectedPlatforms = platform.split(',');
|
|
45
43
|
for (const item of selectedPlatforms) {
|
|
46
44
|
if (!['claude', 'cursor', 'codex'].includes(item)) throw new Error(`不支持的平台:${item}`);
|
|
47
45
|
}
|
|
48
46
|
|
|
49
|
-
const run = (script, args) =>
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
const run = (script, args) =>
|
|
48
|
+
execFileSync(process.execPath, [path.join(scripts, script), ...args], {
|
|
49
|
+
encoding: 'utf8',
|
|
50
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
51
|
+
});
|
|
53
52
|
|
|
54
53
|
const skillArgs = [
|
|
55
|
-
'--platform',
|
|
56
|
-
|
|
57
|
-
'--
|
|
58
|
-
|
|
54
|
+
'--platform',
|
|
55
|
+
platform,
|
|
56
|
+
'--scope',
|
|
57
|
+
options.scope,
|
|
58
|
+
'--skill',
|
|
59
|
+
options.skill,
|
|
60
|
+
'--project',
|
|
61
|
+
path.resolve(options.project),
|
|
59
62
|
];
|
|
60
63
|
const configureMcp = options.skill === 'sisyphus-scene' && !options.skipMcp;
|
|
61
64
|
const mcpArgs = [
|
|
62
|
-
'--platform',
|
|
63
|
-
|
|
64
|
-
'--
|
|
65
|
+
'--platform',
|
|
66
|
+
platform,
|
|
67
|
+
'--scope',
|
|
68
|
+
options.scope,
|
|
69
|
+
'--project',
|
|
70
|
+
path.resolve(options.project),
|
|
65
71
|
];
|
|
66
72
|
|
|
67
73
|
const preview = {
|
package/mcp/sisyphus-react.json
CHANGED
package/package.json
CHANGED
package/scripts/install-mcp.mjs
CHANGED
|
@@ -29,7 +29,8 @@ const parseArgs = (argv) => {
|
|
|
29
29
|
|
|
30
30
|
const args = parseArgs(process.argv.slice(2));
|
|
31
31
|
const platformInput = String(args.platform || 'all');
|
|
32
|
-
const platforms =
|
|
32
|
+
const platforms =
|
|
33
|
+
platformInput === 'all' ? ['claude', 'cursor', 'codex'] : platformInput.split(',');
|
|
33
34
|
const projectRoot = path.resolve(String(args.project || process.cwd()));
|
|
34
35
|
const scope = String(args.scope || 'project');
|
|
35
36
|
const write = Boolean(args.write);
|
|
@@ -49,32 +50,36 @@ const fragment = JSON.parse(fs.readFileSync(fragmentPath, 'utf8'));
|
|
|
49
50
|
const server = fragment?.mcpServers?.['sisyphus-react'];
|
|
50
51
|
if (!server || typeof server !== 'object') throw new Error(`MCP 片段无效:${fragmentPath}`);
|
|
51
52
|
|
|
52
|
-
const targets =
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
53
|
+
const targets =
|
|
54
|
+
scope === 'user'
|
|
55
|
+
? {
|
|
56
|
+
claude: path.join(os.homedir(), '.claude.json'),
|
|
57
|
+
cursor: path.join(os.homedir(), '.cursor', 'mcp.json'),
|
|
58
|
+
}
|
|
59
|
+
: {
|
|
60
|
+
claude: path.join(projectRoot, '.mcp.json'),
|
|
61
|
+
cursor: path.join(projectRoot, '.cursor', 'mcp.json'),
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const codexHome =
|
|
65
|
+
scope === 'user'
|
|
66
|
+
? path.resolve(process.env.CODEX_HOME || path.join(os.homedir(), '.codex'))
|
|
67
|
+
: path.join(projectRoot, '.codex');
|
|
65
68
|
|
|
66
69
|
const codexEnv = { ...process.env, CODEX_HOME: codexHome };
|
|
67
70
|
|
|
68
|
-
const runCodex = (commandArgs) =>
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
const runCodex = (commandArgs) =>
|
|
72
|
+
spawnSync('codex', commandArgs, {
|
|
73
|
+
encoding: 'utf8',
|
|
74
|
+
env: codexEnv,
|
|
75
|
+
});
|
|
72
76
|
|
|
73
77
|
const inspectCodex = () => {
|
|
74
78
|
if (!fs.existsSync(codexHome)) {
|
|
75
79
|
const version = runCodex(['--version']);
|
|
76
80
|
if (version.error?.code === 'ENOENT') throw new Error('未找到 Codex CLI,无法配置 Codex MCP。');
|
|
77
|
-
if (version.status !== 0)
|
|
81
|
+
if (version.status !== 0)
|
|
82
|
+
throw new Error(`Codex CLI 不可用:${version.stderr || version.stdout}`);
|
|
78
83
|
return { status: 'missing' };
|
|
79
84
|
}
|
|
80
85
|
|
|
@@ -90,7 +95,9 @@ const inspectCodex = () => {
|
|
|
90
95
|
const transport = value.transport || value;
|
|
91
96
|
const expectedArgs = server.args || [];
|
|
92
97
|
if (transport.command !== server.command || !sameJson(transport.args || [], expectedArgs)) {
|
|
93
|
-
throw new Error(
|
|
98
|
+
throw new Error(
|
|
99
|
+
`${path.join(codexHome, 'config.toml')} 已存在不同的 sisyphus-react 配置;请人工确认。`,
|
|
100
|
+
);
|
|
94
101
|
}
|
|
95
102
|
return { status: 'unchanged' };
|
|
96
103
|
};
|
|
@@ -102,7 +109,11 @@ const readConfig = (target) => {
|
|
|
102
109
|
throw new Error(`${target} 必须是 JSON 对象。`);
|
|
103
110
|
}
|
|
104
111
|
if (value.mcpServers === undefined) value.mcpServers = {};
|
|
105
|
-
if (
|
|
112
|
+
if (
|
|
113
|
+
!value.mcpServers ||
|
|
114
|
+
typeof value.mcpServers !== 'object' ||
|
|
115
|
+
Array.isArray(value.mcpServers)
|
|
116
|
+
) {
|
|
106
117
|
throw new Error(`${target}.mcpServers 必须是对象。`);
|
|
107
118
|
}
|
|
108
119
|
return value;
|
|
@@ -112,10 +123,16 @@ const sameJson = (left, right) => {
|
|
|
112
123
|
if (Object.is(left, right)) return true;
|
|
113
124
|
if (!left || !right || typeof left !== 'object' || typeof right !== 'object') return false;
|
|
114
125
|
if (Array.isArray(left) !== Array.isArray(right)) return false;
|
|
115
|
-
if (Array.isArray(left))
|
|
126
|
+
if (Array.isArray(left))
|
|
127
|
+
return (
|
|
128
|
+
left.length === right.length && left.every((value, index) => sameJson(value, right[index]))
|
|
129
|
+
);
|
|
116
130
|
const leftKeys = Object.keys(left).sort();
|
|
117
131
|
const rightKeys = Object.keys(right).sort();
|
|
118
|
-
return
|
|
132
|
+
return (
|
|
133
|
+
leftKeys.length === rightKeys.length &&
|
|
134
|
+
leftKeys.every((key, index) => key === rightKeys[index] && sameJson(left[key], right[key]))
|
|
135
|
+
);
|
|
119
136
|
};
|
|
120
137
|
const results = [];
|
|
121
138
|
|
|
@@ -124,8 +141,16 @@ for (const platform of platforms) {
|
|
|
124
141
|
const inspected = inspectCodex();
|
|
125
142
|
if (inspected.status === 'missing' && write) {
|
|
126
143
|
fs.mkdirSync(codexHome, { recursive: true });
|
|
127
|
-
const result = runCodex([
|
|
128
|
-
|
|
144
|
+
const result = runCodex([
|
|
145
|
+
'mcp',
|
|
146
|
+
'add',
|
|
147
|
+
'sisyphus-react',
|
|
148
|
+
'--',
|
|
149
|
+
server.command,
|
|
150
|
+
...(server.args || []),
|
|
151
|
+
]);
|
|
152
|
+
if (result.status !== 0)
|
|
153
|
+
throw new Error(`写入 Codex MCP 配置失败:${result.stderr || result.stdout}`);
|
|
129
154
|
}
|
|
130
155
|
results.push({
|
|
131
156
|
platform,
|
|
@@ -155,4 +180,6 @@ for (const platform of platforms) {
|
|
|
155
180
|
results.push({ platform, target, status: write ? 'added' : 'would-add' });
|
|
156
181
|
}
|
|
157
182
|
|
|
158
|
-
process.stdout.write(
|
|
183
|
+
process.stdout.write(
|
|
184
|
+
`${JSON.stringify({ projectRoot, scope, mode: write ? 'write' : 'check', results }, null, 2)}\n`,
|
|
185
|
+
);
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
interface:
|
|
2
|
-
display_name:
|
|
3
|
-
short_description:
|
|
4
|
-
default_prompt:
|
|
2
|
+
display_name: 'Sisyphus 业务场景生成'
|
|
3
|
+
short_description: '从截图或需求生成可验证的保险业务页面脚手架'
|
|
4
|
+
default_prompt: 'Use $sisyphus-scene to match this requirement to a business scene and generate a verified React scaffold.'
|
|
5
5
|
|
|
6
6
|
policy:
|
|
7
7
|
allow_implicit_invocation: true
|
|
8
|
-
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Tabs } from 'antd';
|
|
2
2
|
import { ProTable } from '@zat-design/sisyphus-react';
|
|
3
3
|
|
|
4
|
-
export const InvalidFallback = () =>
|
|
5
|
-
|
|
4
|
+
export const InvalidFallback = () => (
|
|
5
|
+
<ProTable tableId="invalid" columns={[]} tabs={<Tabs items={[]} />} />
|
|
6
|
+
);
|
|
@@ -8,13 +8,13 @@
|
|
|
8
8
|
"apiEvidence": {
|
|
9
9
|
"tool": "sisyphus-get-component-api",
|
|
10
10
|
"target": "ProForm",
|
|
11
|
-
"libraryVersion": "4.5.
|
|
11
|
+
"libraryVersion": "4.5.10-beta.2",
|
|
12
12
|
"schemaVersion": 2
|
|
13
13
|
},
|
|
14
14
|
"demoEvidence": {
|
|
15
15
|
"tool": "sisyphus-get-example",
|
|
16
16
|
"target": "ProForm/demos/base.tsx",
|
|
17
|
-
"libraryVersion": "4.5.
|
|
17
|
+
"libraryVersion": "4.5.10-beta.2",
|
|
18
18
|
"schemaVersion": 2,
|
|
19
19
|
"exampleId": "ProForm/demos/base.tsx"
|
|
20
20
|
}
|
|
@@ -28,13 +28,13 @@
|
|
|
28
28
|
"apiEvidence": {
|
|
29
29
|
"tool": "sisyphus-get-component-api",
|
|
30
30
|
"target": "ProTable",
|
|
31
|
-
"libraryVersion": "4.5.
|
|
31
|
+
"libraryVersion": "4.5.10-beta.2",
|
|
32
32
|
"schemaVersion": 2
|
|
33
33
|
},
|
|
34
34
|
"demoEvidence": {
|
|
35
35
|
"tool": "sisyphus-get-example",
|
|
36
36
|
"target": "ProTable/demos/base.tsx",
|
|
37
|
-
"libraryVersion": "4.5.
|
|
37
|
+
"libraryVersion": "4.5.10-beta.2",
|
|
38
38
|
"schemaVersion": 2,
|
|
39
39
|
"exampleId": "ProTable/demos/base.tsx"
|
|
40
40
|
}
|
|
@@ -5,10 +5,13 @@ const { useAntdTable } = ProTable;
|
|
|
5
5
|
|
|
6
6
|
export const PolicyQueryPage = () => {
|
|
7
7
|
const [form] = ProForm.useForm();
|
|
8
|
-
const { tableProps, onSearch, onReset } = useAntdTable(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
const { tableProps, onSearch, onReset } = useAntdTable(
|
|
9
|
+
async () => ({ data: { list: [], total: 0 } }),
|
|
10
|
+
{
|
|
11
|
+
form,
|
|
12
|
+
rowKey: 'id',
|
|
13
|
+
},
|
|
14
|
+
);
|
|
12
15
|
|
|
13
16
|
return (
|
|
14
17
|
<>
|
|
@@ -22,4 +25,3 @@ export const PolicyQueryPage = () => {
|
|
|
22
25
|
</>
|
|
23
26
|
);
|
|
24
27
|
};
|
|
25
|
-
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
schema_version: 1
|
|
2
2
|
should_trigger:
|
|
3
|
-
-
|
|
4
|
-
-
|
|
5
|
-
-
|
|
6
|
-
-
|
|
7
|
-
-
|
|
3
|
+
- '根据这张截图生成一个保单查询列表页骨架'
|
|
4
|
+
- '做一个带状态 Tab 和新增抽屉的理赔配置页面'
|
|
5
|
+
- '参考 claim 应用实现一个多 Tab 详情编辑页'
|
|
6
|
+
- '这个布局不在已有场景里,请根据 Sisyphus demo 组合'
|
|
7
|
+
- '做一个右侧步骤锚点、统一校验的长表单'
|
|
8
8
|
should_not_trigger:
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
|
|
9
|
+
- '解释一下 React useEffect'
|
|
10
|
+
- '修复这个现有组件的业务 bug'
|
|
11
|
+
- '给 antd4 项目生成 CRUD 页面'
|
|
12
|
+
- '给 Sisyphus v3 项目兼容一个旧页面'
|
|
@@ -7,7 +7,7 @@ examples: []
|
|
|
7
7
|
# status: approved
|
|
8
8
|
# application: xk-wjs-claim-web
|
|
9
9
|
# capabilities: [search-form, data-table, table-tabs, drawer-form]
|
|
10
|
-
# sisyphus_version: "4.5.
|
|
10
|
+
# sisyphus_version: "4.5.10-beta.2"
|
|
11
11
|
# antd_range: ">=6 <7"
|
|
12
12
|
# paths:
|
|
13
13
|
# - src/pages/litigationManagement/index.tsx
|
|
@@ -9,28 +9,28 @@
|
|
|
9
9
|
|
|
10
10
|
## 页面原语
|
|
11
11
|
|
|
12
|
-
| 能力
|
|
13
|
-
|
|
14
|
-
| search-form
|
|
15
|
-
| data-table
|
|
16
|
-
| table-tabs
|
|
17
|
-
| drawer-form
|
|
18
|
-
| detail-navigation | 列表到详情、返回和 URL 状态
|
|
19
|
-
| detail-tabs
|
|
20
|
-
| detail-sections
|
|
21
|
-
| step-anchor-form
|
|
12
|
+
| 能力 | 说明 |
|
|
13
|
+
| ----------------- | ------------------------------ |
|
|
14
|
+
| search-form | 查询条件与查询/重置行为 |
|
|
15
|
+
| data-table | 表格、分页、加载和数据源 |
|
|
16
|
+
| table-tabs | 同列表状态过滤或独立子页面切换 |
|
|
17
|
+
| drawer-form | 新增、编辑、查看弹窗或抽屉 |
|
|
18
|
+
| detail-navigation | 列表到详情、返回和 URL 状态 |
|
|
19
|
+
| detail-tabs | 横向 Tab 切换的详情/编辑分区 |
|
|
20
|
+
| detail-sections | 纵向详情或折叠分区 |
|
|
21
|
+
| step-anchor-form | 纵向步骤锚点和跨分区统一校验 |
|
|
22
22
|
|
|
23
23
|
## 注册场景
|
|
24
24
|
|
|
25
|
-
| 中文场景
|
|
26
|
-
|
|
27
|
-
| 查询列表页
|
|
28
|
-
| Tab 分组列表页
|
|
29
|
-
| 列表弹窗操作页
|
|
30
|
-
| 多 Tab 详情编辑页 | detail-tabs
|
|
31
|
-
| 分区块详情页
|
|
32
|
-
| 列表跳转详情页
|
|
33
|
-
| 步骤条分区表单页
|
|
25
|
+
| 中文场景 | 必需能力 | 关键识别信号 |
|
|
26
|
+
| ----------------- | ------------------------------------------ | ---------------------------------- |
|
|
27
|
+
| 查询列表页 | search-form + data-table | 查询区 + 表格 |
|
|
28
|
+
| Tab 分组列表页 | search-form + data-table + table-tabs | 表格上方状态/业务 Tab |
|
|
29
|
+
| 列表弹窗操作页 | search-form + data-table + drawer-form | 头部或操作列打开表单 |
|
|
30
|
+
| 多 Tab 详情编辑页 | detail-tabs | 横向 Tab,一次显示一个分区 |
|
|
31
|
+
| 分区块详情页 | detail-sections | 纵向分区,无步骤锚点 |
|
|
32
|
+
| 列表跳转详情页 | data-table + detail-navigation + detail-\* | 操作列进入独立详情路由 |
|
|
33
|
+
| 步骤条分区表单页 | detail-sections + step-anchor-form | 纵向分区 + 右侧步骤锚点 + 统一校验 |
|
|
34
34
|
|
|
35
35
|
## 识别规则
|
|
36
36
|
|
|
@@ -48,4 +48,3 @@
|
|
|
48
48
|
3. 对每个追加原语查询 Sisyphus demo。
|
|
49
49
|
4. 组合后重新检查表单所有权、校验边界、数据源和导航职责。
|
|
50
50
|
5. 组合冲突时以用户明确交互为准,必要时改为 `none` 并构造临时场景。
|
|
51
|
-
|
|
@@ -56,12 +56,12 @@
|
|
|
56
56
|
|
|
57
57
|
每条 `CapabilityRequirement` 必须对应一条 `ComponentDecision`。
|
|
58
58
|
|
|
59
|
-
| 状态
|
|
60
|
-
|
|
61
|
-
| covered
|
|
62
|
-
| partial
|
|
63
|
-
| missing
|
|
64
|
-
| unverified | MCP 不可用、版本不符或证据不足
|
|
59
|
+
| 状态 | 判定 | 下一步 |
|
|
60
|
+
| ---------- | -------------------------------- | ------------------- |
|
|
61
|
+
| covered | API 与 demo 足以实现 | 进入生成 |
|
|
62
|
+
| partial | 需要多个 Sisyphus 原语或项目适配 | 继续组合并复查 |
|
|
63
|
+
| missing | MCP 明确无对应能力 | 允许查询 Ant Design |
|
|
64
|
+
| unverified | MCP 不可用、版本不符或证据不足 | 立即阻断 |
|
|
65
65
|
|
|
66
66
|
不得把“搜索没找到”“白名单没写”“记忆中不存在”判为 `missing`。
|
|
67
67
|
|
|
@@ -91,4 +91,3 @@ Ant Design 仍无法覆盖时检索项目公共组件。只使用有源码、调
|
|
|
91
91
|
- 无法确认会改变公共接口或业务数据流的关键需求
|
|
92
92
|
|
|
93
93
|
停止时输出已完成的证据、未解决能力、建议的独立前置任务,不用占位代码伪装完成。
|
|
94
|
-
|
|
@@ -39,9 +39,18 @@ if (!['copy', 'link'].includes(mode)) throw new Error(`不支持的 mode:${mod
|
|
|
39
39
|
|
|
40
40
|
const projectRoot = path.resolve(String(args.project || process.cwd()));
|
|
41
41
|
const platformDirectories = {
|
|
42
|
-
claude:
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
claude:
|
|
43
|
+
scope === 'user'
|
|
44
|
+
? path.join(os.homedir(), '.claude', 'skills')
|
|
45
|
+
: path.join(projectRoot, '.claude', 'skills'),
|
|
46
|
+
codex:
|
|
47
|
+
scope === 'user'
|
|
48
|
+
? path.join(os.homedir(), '.agents', 'skills')
|
|
49
|
+
: path.join(projectRoot, '.agents', 'skills'),
|
|
50
|
+
cursor:
|
|
51
|
+
scope === 'user'
|
|
52
|
+
? path.join(os.homedir(), '.cursor', 'skills')
|
|
53
|
+
: path.join(projectRoot, '.cursor', 'skills'),
|
|
45
54
|
};
|
|
46
55
|
|
|
47
56
|
const selectedPlatforms = platform === 'all' ? Object.keys(platformDirectories) : [platform];
|
|
@@ -97,4 +106,3 @@ for (const name of selectedPlatforms) {
|
|
|
97
106
|
}
|
|
98
107
|
|
|
99
108
|
process.stdout.write(`${JSON.stringify({ source, mode, scope, dryRun, results }, null, 2)}\n`);
|
|
100
|
-
|
|
@@ -10,6 +10,7 @@ description: 基于旧版 @zat-design/sisyphus-react v3 与 antd4 规则生成
|
|
|
10
10
|
## 触发条件
|
|
11
11
|
|
|
12
12
|
用户提供以下任意组合时触发:
|
|
13
|
+
|
|
13
14
|
- 查询/搜索字段描述
|
|
14
15
|
- 表格列描述
|
|
15
16
|
- 抽屉/弹框表单字段描述
|
|
@@ -20,6 +21,7 @@ description: 基于旧版 @zat-design/sisyphus-react v3 与 antd4 规则生成
|
|
|
20
21
|
触发后,首先从用户描述中提取以下信息。缺少必要项时主动追问,可选项缺失则使用默认值:
|
|
21
22
|
|
|
22
23
|
### 必要项
|
|
24
|
+
|
|
23
25
|
1. **页面名称**(中文 + 英文,用于文件命名和注释)
|
|
24
26
|
2. **查询字段**:字段名、label、类型
|
|
25
27
|
3. **表格列**:字段名、label、渲染方式
|
|
@@ -27,6 +29,7 @@ description: 基于旧版 @zat-design/sisyphus-react v3 与 antd4 规则生成
|
|
|
27
29
|
5. **主接口**:list / add / update 的 URL 和请求方法
|
|
28
30
|
|
|
29
31
|
### 可选项
|
|
32
|
+
|
|
30
33
|
6. **抽屉表单字段**:字段名、label、类型、必填、联动规则(缺省时沿用查询字段生成基础表单)
|
|
31
34
|
7. **是否分组**(ProCollapse):默认不分组;用户说"分组"或"分 XX 区域"时启用
|
|
32
35
|
8. **字典映射字段**:需要用接口映射显示名的字段(如 teamId → teamName)
|
|
@@ -39,12 +42,12 @@ description: 基于旧版 @zat-design/sisyphus-react v3 与 antd4 规则生成
|
|
|
39
42
|
|
|
40
43
|
**检查 package.json 确认版本:**
|
|
41
44
|
|
|
42
|
-
| 特征
|
|
43
|
-
|
|
44
|
-
| `@zat-design/sisyphus-react` | `^3.x`
|
|
45
|
-
| antd
|
|
46
|
-
| 列类型
|
|
47
|
-
| 日期
|
|
45
|
+
| 特征 | v3 | v4 |
|
|
46
|
+
| ---------------------------- | ----------------------------------- | ------------------------------------------ |
|
|
47
|
+
| `@zat-design/sisyphus-react` | `^3.x` | `^4.x` |
|
|
48
|
+
| antd | `antd4` | `antd` |
|
|
49
|
+
| 列类型 | `ProColumnProps` / `ProTableColumn` | `ProFormColumnType` / `ProTableColumnType` |
|
|
50
|
+
| 日期 | `moment` | `dayjs` |
|
|
48
51
|
|
|
49
52
|
找不到 package.json 则询问用户版本,默认给 v3(当前项目主要版本)。
|
|
50
53
|
|
|
@@ -231,10 +234,15 @@ const XxxPage: React.FC = () => {
|
|
|
231
234
|
form={searchForm}
|
|
232
235
|
columns={formColumns}
|
|
233
236
|
onFinish={() => refresh()}
|
|
234
|
-
onCancel={() => {
|
|
237
|
+
onCancel={() => {
|
|
238
|
+
searchForm.resetFields();
|
|
239
|
+
refresh();
|
|
240
|
+
}}
|
|
235
241
|
/>
|
|
236
242
|
<div style={{ margin: '0 0 12px' }}>
|
|
237
|
-
<Button type="primary" onClick={handleAdd}
|
|
243
|
+
<Button type="primary" onClick={handleAdd}>
|
|
244
|
+
新增
|
|
245
|
+
</Button>
|
|
238
246
|
</div>
|
|
239
247
|
<ProTable
|
|
240
248
|
{...tableProps}
|
|
@@ -272,14 +280,25 @@ export const getFormColumns = (): ProFormColumnType[] => [
|
|
|
272
280
|
{ type: 'Input', label: '工号', name: 'query_no' },
|
|
273
281
|
|
|
274
282
|
// ProEnum:枚举下拉,allowClear 必加(查询表单允许清空)
|
|
275
|
-
{
|
|
283
|
+
{
|
|
284
|
+
type: 'ProEnum',
|
|
285
|
+
label: '状态',
|
|
286
|
+
name: 'onDuty',
|
|
287
|
+
fieldProps: { code: 'onDuty', allowClear: true },
|
|
288
|
+
},
|
|
276
289
|
|
|
277
290
|
// Select/Radio 静态选项:用 dataSource(不是 options)
|
|
278
291
|
{
|
|
279
292
|
type: 'Select',
|
|
280
293
|
label: '类型',
|
|
281
294
|
name: 'type',
|
|
282
|
-
fieldProps: {
|
|
295
|
+
fieldProps: {
|
|
296
|
+
dataSource: [
|
|
297
|
+
{ label: '启用', value: 'Y' },
|
|
298
|
+
{ label: '停用', value: 'N' },
|
|
299
|
+
],
|
|
300
|
+
allowClear: true,
|
|
301
|
+
},
|
|
283
302
|
},
|
|
284
303
|
|
|
285
304
|
// ProSelect:接口数据下拉
|
|
@@ -291,7 +310,7 @@ export const getFormColumns = (): ProFormColumnType[] => [
|
|
|
291
310
|
allowClear: true,
|
|
292
311
|
fieldNames: { label: 'teamName', value: 'id' },
|
|
293
312
|
useRequest: { service: getTeamOptions },
|
|
294
|
-
transformResponse: (res: any) => res?.data?.status === 200 ? res.data.data || [] : [],
|
|
313
|
+
transformResponse: (res: any) => (res?.data?.status === 200 ? res.data.data || [] : []),
|
|
295
314
|
},
|
|
296
315
|
},
|
|
297
316
|
];
|
|
@@ -321,9 +340,13 @@ const getTableColumns = (): ProTableColumnType[] => {
|
|
|
321
340
|
width: 150,
|
|
322
341
|
render: (_: any, record: XxxRecord) => (
|
|
323
342
|
<Space>
|
|
324
|
-
<Button type="link" onClick={() => handleEdit(record)}
|
|
343
|
+
<Button type="link" onClick={() => handleEdit(record)}>
|
|
344
|
+
编辑
|
|
345
|
+
</Button>
|
|
325
346
|
{record.enable === 'N' && (
|
|
326
|
-
<Button type="link" danger onClick={() => handleDelete(record)}
|
|
347
|
+
<Button type="link" danger onClick={() => handleDelete(record)}>
|
|
348
|
+
删除
|
|
349
|
+
</Button>
|
|
327
350
|
)}
|
|
328
351
|
</Space>
|
|
329
352
|
),
|
|
@@ -332,7 +355,7 @@ const getTableColumns = (): ProTableColumnType[] => {
|
|
|
332
355
|
};
|
|
333
356
|
|
|
334
357
|
// JSX 调用
|
|
335
|
-
<ProTable {...tableProps} rowKey="id" columns={getTableColumns()} scroll={{ x: 'max-content' }}
|
|
358
|
+
<ProTable {...tableProps} rowKey="id" columns={getTableColumns()} scroll={{ x: 'max-content' }} />;
|
|
336
359
|
```
|
|
337
360
|
|
|
338
361
|
操作列按钮如果逻辑复杂,可以提取为 `render` 辅助函数放在 `getTableColumns` 上方:
|
|
@@ -352,7 +375,9 @@ const getTableColumns = () => [
|
|
|
352
375
|
width: 150,
|
|
353
376
|
render: (_: any, record: XxxRecord) => (
|
|
354
377
|
<Space>
|
|
355
|
-
<Button type="link" onClick={() => handleEdit(record)}
|
|
378
|
+
<Button type="link" onClick={() => handleEdit(record)}>
|
|
379
|
+
编辑
|
|
380
|
+
</Button>
|
|
356
381
|
{renderStatusAction(record)}
|
|
357
382
|
</Space>
|
|
358
383
|
),
|
|
@@ -365,7 +390,10 @@ const getTableColumns = () => [
|
|
|
365
390
|
#### 默认模式(平铺,字段少时使用)
|
|
366
391
|
|
|
367
392
|
```tsx
|
|
368
|
-
export const getModalFormColumns = (
|
|
393
|
+
export const getModalFormColumns = (
|
|
394
|
+
currentRecord?: XxxRecord | null,
|
|
395
|
+
form?: any,
|
|
396
|
+
): ProFormColumnType[] => [
|
|
369
397
|
{ type: 'Input', label: '名称', name: 'name', required: true },
|
|
370
398
|
|
|
371
399
|
// ProSelect 多选
|
|
@@ -378,7 +406,7 @@ export const getModalFormColumns = (currentRecord?: XxxRecord | null, form?: any
|
|
|
378
406
|
mode: 'multiple',
|
|
379
407
|
fieldNames: { label: 'roleName', value: 'id' },
|
|
380
408
|
useRequest: { service: getRoleOptions },
|
|
381
|
-
transformResponse: (res: any) => res?.data?.status === 200 ? res.data.data || [] : [],
|
|
409
|
+
transformResponse: (res: any) => (res?.data?.status === 200 ? res.data.data || [] : []),
|
|
382
410
|
},
|
|
383
411
|
},
|
|
384
412
|
|
|
@@ -411,7 +439,7 @@ export const getModalFormColumns = (currentRecord?: XxxRecord | null, form?: any
|
|
|
411
439
|
service: () => getLeaderList(values.dutieId),
|
|
412
440
|
options: { ready: !!values.dutieId, refreshDeps: [values.dutieId] },
|
|
413
441
|
},
|
|
414
|
-
transformResponse: (res: any) => res?.data?.status === 200 ? res.data.data || [] : [],
|
|
442
|
+
transformResponse: (res: any) => (res?.data?.status === 200 ? res.data.data || [] : []),
|
|
415
443
|
}),
|
|
416
444
|
},
|
|
417
445
|
];
|
|
@@ -420,7 +448,10 @@ export const getModalFormColumns = (currentRecord?: XxxRecord | null, form?: any
|
|
|
420
448
|
#### 分组模式(用户明确要求时)
|
|
421
449
|
|
|
422
450
|
```tsx
|
|
423
|
-
export const getModalFormColumns = (
|
|
451
|
+
export const getModalFormColumns = (
|
|
452
|
+
currentRecord?: XxxRecord | null,
|
|
453
|
+
form?: any,
|
|
454
|
+
): ProFormColumnType[] => [
|
|
424
455
|
{
|
|
425
456
|
type: 'ProCollapse',
|
|
426
457
|
fieldProps: {
|
|
@@ -437,9 +468,7 @@ export const getModalFormColumns = (currentRecord?: XxxRecord | null, form?: any
|
|
|
437
468
|
fieldProps: {
|
|
438
469
|
title: '服务范围',
|
|
439
470
|
level: '2',
|
|
440
|
-
children: [
|
|
441
|
-
{ type: 'Input', label: '软电账户', name: 'callNo', required: true },
|
|
442
|
-
],
|
|
471
|
+
children: [{ type: 'Input', label: '软电账户', name: 'callNo', required: true }],
|
|
443
472
|
},
|
|
444
473
|
},
|
|
445
474
|
];
|
|
@@ -455,11 +484,9 @@ import axios from '../../../../utils/intercept'; // 路径按项目层级调整
|
|
|
455
484
|
export const getXxxList = (params: any) =>
|
|
456
485
|
axios({ method: 'post', url: `/xxx/list/${params.pageSize}/${params.pageNum}`, data: params });
|
|
457
486
|
|
|
458
|
-
export const addXxx = (data: any) =>
|
|
459
|
-
axios({ method: 'post', url: '/xxx/add', data });
|
|
487
|
+
export const addXxx = (data: any) => axios({ method: 'post', url: '/xxx/add', data });
|
|
460
488
|
|
|
461
|
-
export const updateXxx = (data: any) =>
|
|
462
|
-
axios({ method: 'post', url: '/xxx/update', data });
|
|
489
|
+
export const updateXxx = (data: any) => axios({ method: 'post', url: '/xxx/update', data });
|
|
463
490
|
|
|
464
491
|
// 字典接口(有字典映射时)
|
|
465
492
|
// export const getXxxOptions = () =>
|
|
@@ -493,6 +520,7 @@ export interface XxxFormValues {
|
|
|
493
520
|
**仅在有特殊转换时生成。无转换需求则不创建此文件。**
|
|
494
521
|
|
|
495
522
|
常见需要转换的场景:
|
|
523
|
+
|
|
496
524
|
- 多选数组 → 逗号分隔字符串(提交前)/ 逗号字符串 → 数组(回显)
|
|
497
525
|
- `depId` 数字 → `[depId]` 数组(ProTree/ProCascader 回显)
|
|
498
526
|
- 日期 moment/dayjs → 格式化字符串(提交前)
|
|
@@ -526,25 +554,30 @@ export const transformRecordToFormValues = (record: any): any => {
|
|
|
526
554
|
生成代码时逐条核对:
|
|
527
555
|
|
|
528
556
|
### 状态
|
|
557
|
+
|
|
529
558
|
- [ ] 多个状态用一个 `useSetState<PageState>`,不拆成多个 `useState`
|
|
530
559
|
- [ ] PageState 用 interface 声明,不用内联类型
|
|
531
560
|
|
|
532
561
|
### Hooks 使用
|
|
562
|
+
|
|
533
563
|
- [ ] `useMemo` 仅用于无外部依赖的纯配置(如 `getFormColumns()`)
|
|
534
564
|
- [ ] `getTableColumns`(依赖 handler)不加 `useMemo`
|
|
535
565
|
- [ ] `getModalFormColumns`(依赖运行时数据)不加 `useMemo`
|
|
536
566
|
- [ ] `useEffect` 依赖数组不含函数、form 实例
|
|
537
567
|
|
|
538
568
|
### Placeholder
|
|
569
|
+
|
|
539
570
|
- [ ] **禁止** `fieldProps: { placeholder: '请输入' }` 或 `placeholder: '请选择'`(组件自动添加)
|
|
540
571
|
- [ ] 仅特殊 placeholder 才手动指定(如 `placeholder: '自动获取'`)
|
|
541
572
|
- [ ] 如 fieldProps 只有 placeholder 且为默认值,移除整个 fieldProps
|
|
542
573
|
|
|
543
574
|
### 区块顺序
|
|
575
|
+
|
|
544
576
|
- [ ] 导入 → Props/Hooks → 状态 → 配置 → 事件处理 → API → useEffect → JSX
|
|
545
577
|
- [ ] 所有 `useEffect` 集中在 return 之前,不分散
|
|
546
578
|
|
|
547
579
|
### Config 纯净性
|
|
580
|
+
|
|
548
581
|
- [ ] config 文件不含 `useState`/`useRequest`/`useEffect`
|
|
549
582
|
- [ ] 运行时数据(dictMaps、form、currentRecord)通过参数传入
|
|
550
583
|
|
|
@@ -552,29 +585,29 @@ export const transformRecordToFormValues = (record: any): any => {
|
|
|
552
585
|
|
|
553
586
|
## 字段类型速查
|
|
554
587
|
|
|
555
|
-
| 场景
|
|
556
|
-
|
|
557
|
-
| 普通文本输入
|
|
558
|
-
| 数字输入
|
|
559
|
-
| 静态选项下拉
|
|
560
|
-
| 枚举/字典下拉 | `ProEnum`
|
|
561
|
-
| 接口数据下拉
|
|
562
|
-
| 树形选择
|
|
563
|
-
| 日期
|
|
564
|
-
| 日期范围
|
|
565
|
-
| 分组折叠
|
|
566
|
-
| 文件上传
|
|
588
|
+
| 场景 | sisyphus type | 关键 fieldProps |
|
|
589
|
+
| ------------- | ------------------ | ------------------------------------------------------------------------ |
|
|
590
|
+
| 普通文本输入 | `Input` | `maxLength` |
|
|
591
|
+
| 数字输入 | `InputNumber` | `min`, `max`, `precision` |
|
|
592
|
+
| 静态选项下拉 | `Select` / `Radio` | `dataSource: [{label, value}]`(**不是 options**) |
|
|
593
|
+
| 枚举/字典下拉 | `ProEnum` | `code`, `allowClear`, `mode: 'multiple'` |
|
|
594
|
+
| 接口数据下拉 | `ProSelect` | `useRequest.service`, `transformResponse`, `fieldNames`, `mode` |
|
|
595
|
+
| 树形选择 | `ProTree` | `mode: 'treeSelect'`, `useRequest`, `transformResponse`, `onFieldChange` |
|
|
596
|
+
| 日期 | `DatePicker` | `valueType: 'date'` 或 `'dateTime'` |
|
|
597
|
+
| 日期范围 | `RangePicker` | `valueType: 'date'` |
|
|
598
|
+
| 分组折叠 | `ProCollapse` | `title`, `level: '2'`, `children: [...]` |
|
|
599
|
+
| 文件上传 | `ProUpload` | 见 sisyphus-assistant |
|
|
567
600
|
|
|
568
601
|
---
|
|
569
602
|
|
|
570
603
|
## 联动规则速查
|
|
571
604
|
|
|
572
|
-
| 需求
|
|
573
|
-
|
|
574
|
-
| 条件显示字段
|
|
575
|
-
| 动态必填
|
|
605
|
+
| 需求 | 写法 |
|
|
606
|
+
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
607
|
+
| 条件显示字段 | `show: (values) => !!values.xxx` |
|
|
608
|
+
| 动态必填 | `required: (values) => values.xxx === 'Y'` |
|
|
576
609
|
| 选项依赖另一字段 | `fieldProps: (values) => ({ useRequest: { service: () => getXxx(values.dep), options: { ready: !!values.dep, refreshDeps: [values.dep] } } })` |
|
|
577
|
-
| 字段变化时联动
|
|
610
|
+
| 字段变化时联动 | `fieldProps: { onFieldChange: (value, _, { form }) => form.setFieldValue('xxx', undefined) }` |
|
|
578
611
|
|
|
579
612
|
---
|
|
580
613
|
|
|
@@ -597,6 +630,7 @@ export const transformRecordToFormValues = (record: any): any => {
|
|
|
597
630
|
## 示例
|
|
598
631
|
|
|
599
632
|
**用户输入:**
|
|
633
|
+
|
|
600
634
|
```
|
|
601
635
|
页面:角色管理(RolePage)
|
|
602
636
|
查询:角色名(roleName:Input), 状态(status:ProEnum/code=status)
|
|
@@ -606,6 +640,7 @@ export const transformRecordToFormValues = (record: any): any => {
|
|
|
606
640
|
```
|
|
607
641
|
|
|
608
642
|
**生成结果:**
|
|
643
|
+
|
|
609
644
|
- 无 ProCollapse(字段少,默认平铺)
|
|
610
645
|
- 无 utils(无特殊转换)
|
|
611
646
|
- 无字典映射(状态用 enumName 直接渲染)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
interface:
|
|
2
|
-
display_name:
|
|
3
|
-
short_description:
|
|
4
|
-
default_prompt:
|
|
2
|
+
display_name: 'Sisyphus 旧版业务页面'
|
|
3
|
+
short_description: '仅为 v3 与 antd4 项目生成兼容页面'
|
|
4
|
+
default_prompt: 'Use $sisyphus-scene-legacy only for projects that explicitly use Sisyphus v3 or antd4.'
|
|
5
5
|
|
|
6
6
|
policy:
|
|
7
7
|
allow_implicit_invocation: false
|