prd-workflow-cli 1.3.0 → 1.4.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.
- package/.agent/workflows/prd-a2ui-guide.md +33 -36
- package/.agent/workflows/prd-b1-planning-draft.md +45 -0
- package/.agent/workflows/prd-b2-planning-breakdown.md +53 -0
- package/.agent/workflows/prd-c1-requirement-list.md +63 -0
- package/.agent/workflows/prd-p0-project-info.md +45 -0
- package/.agent/workflows/prd-r1-review.md +45 -0
- package/.agent/workflows/prd-r2-review.md +43 -0
- package/.antigravity/rules.md +14 -0
- package/.cursorrules +26 -0
- package/a2ui-viewer/index.html +17 -1
- package/bin/prd-cli.js +25 -4
- package/commands/a2ui-server.js +27 -5
- package/commands/check.js +509 -0
- package/commands/stats.js +192 -0
- package/docs/RULE-SYSTEM-ROADMAP.md +286 -0
- package/package.json +3 -1
- package/rules/index.json +614 -0
- package/rules/schemas/a2ui.schema.json +545 -0
- package/rules/schemas/rules.schema.json +221 -0
- package/scripts/inject-rules.js +167 -0
- package/templates/a2ui-standalone.html +127 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "PRD Rules Index",
|
|
4
|
+
"description": "PRD-CLI 规则索引的 JSON Schema",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": [
|
|
7
|
+
"version",
|
|
8
|
+
"rules"
|
|
9
|
+
],
|
|
10
|
+
"properties": {
|
|
11
|
+
"$schema": {
|
|
12
|
+
"type": "string"
|
|
13
|
+
},
|
|
14
|
+
"version": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
|
17
|
+
},
|
|
18
|
+
"lastUpdated": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"format": "date"
|
|
21
|
+
},
|
|
22
|
+
"categories": {
|
|
23
|
+
"type": "object",
|
|
24
|
+
"additionalProperties": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"properties": {
|
|
27
|
+
"name": {
|
|
28
|
+
"type": "string"
|
|
29
|
+
},
|
|
30
|
+
"nameEn": {
|
|
31
|
+
"type": "string"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"required": [
|
|
35
|
+
"name"
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"rules": {
|
|
40
|
+
"type": "array",
|
|
41
|
+
"items": {
|
|
42
|
+
"$ref": "#/definitions/rule"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"validators": {
|
|
46
|
+
"type": "object",
|
|
47
|
+
"additionalProperties": {
|
|
48
|
+
"$ref": "#/definitions/validator"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"scopeMapping": {
|
|
52
|
+
"type": "object",
|
|
53
|
+
"additionalProperties": {
|
|
54
|
+
"type": "string"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"definitions": {
|
|
59
|
+
"rule": {
|
|
60
|
+
"type": "object",
|
|
61
|
+
"required": [
|
|
62
|
+
"id",
|
|
63
|
+
"category",
|
|
64
|
+
"severity",
|
|
65
|
+
"scope",
|
|
66
|
+
"description",
|
|
67
|
+
"validatorType"
|
|
68
|
+
],
|
|
69
|
+
"properties": {
|
|
70
|
+
"id": {
|
|
71
|
+
"type": "string",
|
|
72
|
+
"pattern": "^[A-Z]\\d{3}$",
|
|
73
|
+
"description": "规则 ID,格式为 X000"
|
|
74
|
+
},
|
|
75
|
+
"category": {
|
|
76
|
+
"type": "string",
|
|
77
|
+
"enum": [
|
|
78
|
+
"G",
|
|
79
|
+
"D",
|
|
80
|
+
"F",
|
|
81
|
+
"S",
|
|
82
|
+
"V",
|
|
83
|
+
"I",
|
|
84
|
+
"U",
|
|
85
|
+
"C",
|
|
86
|
+
"W"
|
|
87
|
+
],
|
|
88
|
+
"description": "规则分类"
|
|
89
|
+
},
|
|
90
|
+
"severity": {
|
|
91
|
+
"type": "string",
|
|
92
|
+
"enum": [
|
|
93
|
+
"CRITICAL",
|
|
94
|
+
"HIGH",
|
|
95
|
+
"MEDIUM",
|
|
96
|
+
"LOW"
|
|
97
|
+
],
|
|
98
|
+
"description": "严重程度"
|
|
99
|
+
},
|
|
100
|
+
"scope": {
|
|
101
|
+
"type": "array",
|
|
102
|
+
"items": {
|
|
103
|
+
"type": "string",
|
|
104
|
+
"enum": [
|
|
105
|
+
"global",
|
|
106
|
+
"p0",
|
|
107
|
+
"b1",
|
|
108
|
+
"b2",
|
|
109
|
+
"plan",
|
|
110
|
+
"c0",
|
|
111
|
+
"c1",
|
|
112
|
+
"version",
|
|
113
|
+
"r1",
|
|
114
|
+
"r2",
|
|
115
|
+
"iteration",
|
|
116
|
+
"a2ui"
|
|
117
|
+
]
|
|
118
|
+
},
|
|
119
|
+
"description": "适用范围"
|
|
120
|
+
},
|
|
121
|
+
"description": {
|
|
122
|
+
"type": "string",
|
|
123
|
+
"description": "规则描述"
|
|
124
|
+
},
|
|
125
|
+
"validatorType": {
|
|
126
|
+
"type": "string",
|
|
127
|
+
"enum": [
|
|
128
|
+
"program",
|
|
129
|
+
"ai-self-check",
|
|
130
|
+
"hybrid"
|
|
131
|
+
],
|
|
132
|
+
"description": "校验类型"
|
|
133
|
+
},
|
|
134
|
+
"validator": {
|
|
135
|
+
"type": [
|
|
136
|
+
"string",
|
|
137
|
+
"null"
|
|
138
|
+
],
|
|
139
|
+
"description": "校验器名称"
|
|
140
|
+
},
|
|
141
|
+
"checkLogic": {
|
|
142
|
+
"type": "string",
|
|
143
|
+
"description": "校验逻辑说明"
|
|
144
|
+
},
|
|
145
|
+
"examples": {
|
|
146
|
+
"type": "object",
|
|
147
|
+
"properties": {
|
|
148
|
+
"correct": {
|
|
149
|
+
"type": "string"
|
|
150
|
+
},
|
|
151
|
+
"wrong": {
|
|
152
|
+
"type": "string"
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
"triggers": {
|
|
157
|
+
"type": "array",
|
|
158
|
+
"items": {
|
|
159
|
+
"type": "string"
|
|
160
|
+
},
|
|
161
|
+
"description": "触发关键词"
|
|
162
|
+
},
|
|
163
|
+
"savePoints": {
|
|
164
|
+
"type": "array",
|
|
165
|
+
"items": {
|
|
166
|
+
"type": "string"
|
|
167
|
+
},
|
|
168
|
+
"description": "保存触发点"
|
|
169
|
+
},
|
|
170
|
+
"questionTemplates": {
|
|
171
|
+
"type": "array",
|
|
172
|
+
"items": {
|
|
173
|
+
"type": "string"
|
|
174
|
+
},
|
|
175
|
+
"description": "质疑问题模板"
|
|
176
|
+
},
|
|
177
|
+
"checkItems": {
|
|
178
|
+
"type": "array",
|
|
179
|
+
"items": {
|
|
180
|
+
"type": "string"
|
|
181
|
+
},
|
|
182
|
+
"description": "检查项"
|
|
183
|
+
},
|
|
184
|
+
"dimensions": {
|
|
185
|
+
"type": "array",
|
|
186
|
+
"items": {
|
|
187
|
+
"type": "string"
|
|
188
|
+
},
|
|
189
|
+
"description": "检查维度"
|
|
190
|
+
},
|
|
191
|
+
"dimensionDetails": {
|
|
192
|
+
"type": "object",
|
|
193
|
+
"description": "维度详细说明"
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
"validator": {
|
|
198
|
+
"type": "object",
|
|
199
|
+
"required": [
|
|
200
|
+
"description",
|
|
201
|
+
"status"
|
|
202
|
+
],
|
|
203
|
+
"properties": {
|
|
204
|
+
"description": {
|
|
205
|
+
"type": "string"
|
|
206
|
+
},
|
|
207
|
+
"implementedIn": {
|
|
208
|
+
"type": "string"
|
|
209
|
+
},
|
|
210
|
+
"status": {
|
|
211
|
+
"type": "string",
|
|
212
|
+
"enum": [
|
|
213
|
+
"planned",
|
|
214
|
+
"implemented",
|
|
215
|
+
"deprecated"
|
|
216
|
+
]
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 规则注入工具
|
|
3
|
+
*
|
|
4
|
+
* 用于在 workflow 文件头部注入规则子集表
|
|
5
|
+
*
|
|
6
|
+
* 用法:
|
|
7
|
+
* node scripts/inject-rules.js # 注入所有 workflow
|
|
8
|
+
* node scripts/inject-rules.js prd-c1 # 注入指定 workflow
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
|
|
14
|
+
// 加载规则索引
|
|
15
|
+
function loadRules() {
|
|
16
|
+
const rulesPath = path.join(__dirname, '../rules/index.json');
|
|
17
|
+
return JSON.parse(fs.readFileSync(rulesPath, 'utf-8'));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Scope 到 Workflow 的映射
|
|
21
|
+
const scopeToWorkflow = {
|
|
22
|
+
'p0': 'prd-p0-project-info',
|
|
23
|
+
'b1': 'prd-b1-planning-draft',
|
|
24
|
+
'b2': 'prd-b2-planning-breakdown',
|
|
25
|
+
'c1': 'prd-c1-requirement-list',
|
|
26
|
+
'r1': 'prd-r1-review',
|
|
27
|
+
'r2': 'prd-r2-review'
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// 获取指定 scope 的规则
|
|
31
|
+
function getRulesForScope(rules, scope) {
|
|
32
|
+
return rules.filter(rule =>
|
|
33
|
+
rule.scope.includes(scope) || rule.scope.includes('global')
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 生成规则表格 Markdown
|
|
38
|
+
function generateRulesTable(rules, scopeName) {
|
|
39
|
+
const lines = [
|
|
40
|
+
'',
|
|
41
|
+
'---',
|
|
42
|
+
'',
|
|
43
|
+
'## 🚨 本阶段必须遵守的规则',
|
|
44
|
+
'',
|
|
45
|
+
'> ⚠️ AI 在执行任务前必须逐条确认以下规则。输出时需包含自检清单。',
|
|
46
|
+
'',
|
|
47
|
+
'| ID | 规则 | 严重程度 | 自检 |',
|
|
48
|
+
'|----|------|----------|------|'
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
rules.forEach(rule => {
|
|
52
|
+
const severity = rule.severity === 'CRITICAL' ? '🔴 严重' :
|
|
53
|
+
rule.severity === 'HIGH' ? '🟠 高' :
|
|
54
|
+
rule.severity === 'MEDIUM' ? '🟡 中' : '🟢 低';
|
|
55
|
+
lines.push(`| ${rule.id} | ${rule.description} | ${severity} | ☐ |`);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
lines.push('');
|
|
59
|
+
lines.push('**自检清单模板**(AI 输出时必须包含):');
|
|
60
|
+
lines.push('```');
|
|
61
|
+
lines.push(`## ✅ 规则自检 (${scopeName})`);
|
|
62
|
+
rules.forEach(rule => {
|
|
63
|
+
lines.push(`- [ ] ${rule.id}: ${rule.description.substring(0, 30)}...`);
|
|
64
|
+
});
|
|
65
|
+
lines.push('```');
|
|
66
|
+
lines.push('');
|
|
67
|
+
lines.push('---');
|
|
68
|
+
lines.push('');
|
|
69
|
+
|
|
70
|
+
return lines.join('\n');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 在 workflow 文件中注入规则表格
|
|
74
|
+
function injectRulesToWorkflow(workflowPath, rules, scopeName) {
|
|
75
|
+
const content = fs.readFileSync(workflowPath, 'utf-8');
|
|
76
|
+
|
|
77
|
+
// 检查是否已有规则表格
|
|
78
|
+
if (content.includes('## 🚨 本阶段必须遵守的规则')) {
|
|
79
|
+
console.log(` 跳过(已有规则表格): ${path.basename(workflowPath)}`);
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// 找到第一个 # 标题的位置(跳过 frontmatter)
|
|
84
|
+
const lines = content.split('\n');
|
|
85
|
+
let insertIndex = 0;
|
|
86
|
+
let inFrontmatter = false;
|
|
87
|
+
|
|
88
|
+
for (let i = 0; i < lines.length; i++) {
|
|
89
|
+
if (lines[i].startsWith('---')) {
|
|
90
|
+
if (!inFrontmatter) {
|
|
91
|
+
inFrontmatter = true;
|
|
92
|
+
} else {
|
|
93
|
+
inFrontmatter = false;
|
|
94
|
+
}
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// 找到第一个 # 开头的标题
|
|
99
|
+
if (!inFrontmatter && lines[i].startsWith('# ')) {
|
|
100
|
+
// 在标题之后插入
|
|
101
|
+
insertIndex = i + 1;
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (insertIndex === 0) {
|
|
107
|
+
console.log(` 跳过(未找到标题): ${path.basename(workflowPath)}`);
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 生成规则表格
|
|
112
|
+
const rulesTable = generateRulesTable(rules, scopeName);
|
|
113
|
+
|
|
114
|
+
// 插入规则表格
|
|
115
|
+
lines.splice(insertIndex, 0, rulesTable);
|
|
116
|
+
|
|
117
|
+
fs.writeFileSync(workflowPath, lines.join('\n'));
|
|
118
|
+
console.log(` ✅ 已注入: ${path.basename(workflowPath)} (${rules.length} 条规则)`);
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 主函数
|
|
123
|
+
function main() {
|
|
124
|
+
const rulesIndex = loadRules();
|
|
125
|
+
const workflowDir = path.join(__dirname, '../.agent/workflows');
|
|
126
|
+
|
|
127
|
+
console.log('🔧 规则注入工具');
|
|
128
|
+
console.log('');
|
|
129
|
+
|
|
130
|
+
let injected = 0;
|
|
131
|
+
let skipped = 0;
|
|
132
|
+
|
|
133
|
+
// 遍历所有 scope
|
|
134
|
+
for (const [scope, workflowName] of Object.entries(scopeToWorkflow)) {
|
|
135
|
+
const workflowPath = path.join(workflowDir, `${workflowName}.md`);
|
|
136
|
+
|
|
137
|
+
if (!fs.existsSync(workflowPath)) {
|
|
138
|
+
console.log(` 跳过(文件不存在): ${workflowName}.md`);
|
|
139
|
+
skipped++;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const rules = getRulesForScope(rulesIndex.rules, scope);
|
|
144
|
+
|
|
145
|
+
if (injectRulesToWorkflow(workflowPath, rules, scope.toUpperCase())) {
|
|
146
|
+
injected++;
|
|
147
|
+
} else {
|
|
148
|
+
skipped++;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
console.log('');
|
|
153
|
+
console.log(`完成:注入 ${injected} 个,跳过 ${skipped} 个`);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// 导出函数供其他模块使用
|
|
157
|
+
module.exports = {
|
|
158
|
+
loadRules,
|
|
159
|
+
getRulesForScope,
|
|
160
|
+
generateRulesTable,
|
|
161
|
+
injectRulesToWorkflow
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// 如果直接运行脚本
|
|
165
|
+
if (require.main === module) {
|
|
166
|
+
main();
|
|
167
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<title>{{TITLE}} - PRD UI 原型</title>
|
|
8
|
+
<!-- React -->
|
|
9
|
+
<script src="https://unpkg.com/react@18/umd/react.production.min.js" crossorigin></script>
|
|
10
|
+
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js" crossorigin></script>
|
|
11
|
+
<!-- Ant Design -->
|
|
12
|
+
<link rel="stylesheet" href="https://unpkg.com/antd@5/dist/reset.css">
|
|
13
|
+
<script src="https://unpkg.com/dayjs@1/dayjs.min.js"></script>
|
|
14
|
+
<script src="https://unpkg.com/antd@5/dist/antd.min.js"></script>
|
|
15
|
+
<!-- Icons -->
|
|
16
|
+
<script src="https://unpkg.com/@ant-design/icons@5/dist/index.umd.min.js"></script>
|
|
17
|
+
<style>
|
|
18
|
+
body {
|
|
19
|
+
margin: 0;
|
|
20
|
+
padding: 24px;
|
|
21
|
+
background: #f5f5f5;
|
|
22
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
#root {
|
|
26
|
+
max-width: 1400px;
|
|
27
|
+
margin: 0 auto;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.page-header {
|
|
31
|
+
margin-bottom: 16px;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.page-title {
|
|
35
|
+
font-size: 20px;
|
|
36
|
+
font-weight: 600;
|
|
37
|
+
color: rgba(0, 0, 0, 0.88);
|
|
38
|
+
margin: 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.meta-info {
|
|
42
|
+
background: #fff;
|
|
43
|
+
border: 1px solid #d9d9d9;
|
|
44
|
+
border-radius: 6px;
|
|
45
|
+
padding: 12px 16px;
|
|
46
|
+
margin-bottom: 16px;
|
|
47
|
+
font-size: 13px;
|
|
48
|
+
color: rgba(0, 0, 0, 0.65);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.meta-info span {
|
|
52
|
+
margin-right: 24px;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.meta-info strong {
|
|
56
|
+
color: rgba(0, 0, 0, 0.88);
|
|
57
|
+
}
|
|
58
|
+
</style>
|
|
59
|
+
</head>
|
|
60
|
+
|
|
61
|
+
<body>
|
|
62
|
+
<div class="meta-info">
|
|
63
|
+
<span><strong>需求编号:</strong>{{REQ_ID}}</span>
|
|
64
|
+
<span><strong>界面名称:</strong>{{NAME}}</span>
|
|
65
|
+
<span><strong>确认时间:</strong>{{DATE}}</span>
|
|
66
|
+
</div>
|
|
67
|
+
<div id="root"></div>
|
|
68
|
+
|
|
69
|
+
<script>
|
|
70
|
+
// UI 数据(内嵌)
|
|
71
|
+
const UI_DATA = {{ JSON_DATA }};
|
|
72
|
+
|
|
73
|
+
// A2UI 渲染器
|
|
74
|
+
const { ConfigProvider, Card, Button, Input, Select, Table, Tabs, Tag, Badge, Space, Row, Col, Typography, Divider, Alert, Upload, Form } = antd;
|
|
75
|
+
const { Title, Text } = Typography;
|
|
76
|
+
const { TextArea } = Input;
|
|
77
|
+
const { PlusOutlined, InboxOutlined } = icons;
|
|
78
|
+
const { Dragger } = Upload;
|
|
79
|
+
|
|
80
|
+
const A2UIRenderer = ({ data }) => {
|
|
81
|
+
const renderNode = (node) => {
|
|
82
|
+
if (!node) return null;
|
|
83
|
+
const { type, children, ...props } = node;
|
|
84
|
+
|
|
85
|
+
switch (type) {
|
|
86
|
+
case 'Page': return React.createElement('div', { key: props.id }, props.title && React.createElement('div', { className: 'page-header' }, React.createElement('h1', { className: 'page-title' }, props.title)), children && children.map((child, i) => renderNode({ ...child, key: i })));
|
|
87
|
+
case 'Panel': return React.createElement(Card, { key: props.key, title: props.title, extra: props.extra && React.createElement(Space, null, props.extra.map((btn, i) => React.createElement(Button, { key: i, type: btn.variant === 'primary' ? 'primary' : 'default' }, btn.text || btn))), style: { marginBottom: 16 } }, children && children.map((child, i) => renderNode({ ...child, key: i })));
|
|
88
|
+
case 'Row': return React.createElement(Row, { key: props.key, gutter: 16 }, children && children.map((child, i) => renderNode({ ...child, key: i })));
|
|
89
|
+
case 'Col': return React.createElement(Col, { key: props.key, flex: 1 }, children && children.map((child, i) => renderNode({ ...child, key: i })));
|
|
90
|
+
case 'Input': return React.createElement(Form.Item, { key: props.key, label: props.label, required: props.required, style: { marginBottom: 16 } }, React.createElement(Input, { placeholder: props.placeholder }));
|
|
91
|
+
case 'Textarea': return React.createElement(Form.Item, { key: props.key, label: props.label, style: { marginBottom: 16 } }, React.createElement(TextArea, { placeholder: props.placeholder, rows: props.rows || 4 }));
|
|
92
|
+
case 'Select': return React.createElement(Form.Item, { key: props.key, label: props.label, style: { marginBottom: 16 } }, React.createElement(Select, { placeholder: '请选择', options: (props.options || []).map(opt => ({ value: typeof opt === 'string' ? opt : opt.value, label: typeof opt === 'string' ? opt : opt.label })), style: { width: '100%' } }));
|
|
93
|
+
case 'Button': return React.createElement(Button, { key: props.key, type: props.variant === 'secondary' ? 'default' : props.variant === 'danger' ? 'primary' : 'primary', danger: props.variant === 'danger', style: { marginRight: 8 } }, props.text);
|
|
94
|
+
case 'Text': return React.createElement(Text, { key: props.key, style: { display: 'block', marginBottom: 8 } }, props.content);
|
|
95
|
+
case 'Tabs': return React.createElement(Tabs, { key: props.key, items: (props.items || []).map((item, i) => ({ key: String(i), label: item })), style: { marginBottom: 16 } });
|
|
96
|
+
case 'Table':
|
|
97
|
+
const columns = (props.columns || []).map(col => {
|
|
98
|
+
const column = { key: col.key || col, dataIndex: col.key || col, title: col.title || col };
|
|
99
|
+
if (col.type === 'link') column.render = (text) => React.createElement('a', null, text);
|
|
100
|
+
else if (col.type === 'badge') column.render = (text) => React.createElement(Tag, { color: col.variantMap?.[text] === 'success' ? 'green' : col.variantMap?.[text] === 'warning' ? 'orange' : col.variantMap?.[text] === 'danger' ? 'red' : 'blue' }, text);
|
|
101
|
+
else if (col.type === 'status') column.render = (text) => React.createElement(Badge, { status: text === '已发布' ? 'success' : 'default', text });
|
|
102
|
+
else if (col.type === 'actions') column.render = () => React.createElement(Space, null, React.createElement('a', null, '编辑'), React.createElement('a', null, '复制'), React.createElement('a', { style: { color: '#ff4d4f' } }, '删除'));
|
|
103
|
+
return column;
|
|
104
|
+
});
|
|
105
|
+
return React.createElement(Table, { key: props.key, columns, dataSource: (props.data || []).map((row, i) => ({ ...row, key: i })), pagination: false, size: 'middle' });
|
|
106
|
+
case 'Badge': return React.createElement(Tag, { key: props.key, color: props.variant === 'success' ? 'green' : props.variant === 'warning' ? 'orange' : props.variant === 'danger' ? 'red' : 'blue' }, props.text);
|
|
107
|
+
case 'Card': return React.createElement(Card, { key: props.key, size: 'small', style: { marginBottom: 12 } }, React.createElement(Row, { justify: 'space-between', align: 'middle' }, React.createElement(Col, null, React.createElement(Space, { direction: 'vertical', size: 0 }, React.createElement(Text, { strong: true }, props.title), props.status && React.createElement(Badge, { status: props.status === '已发布' ? 'success' : 'default', text: props.status }))), props.actions && React.createElement(Col, null, React.createElement(Space, null, props.actions.map((action, i) => React.createElement(Button, { key: i, size: 'small' }, action.text || action))))));
|
|
108
|
+
case 'Upload': return React.createElement(Dragger, { key: props.key }, React.createElement('p', { className: 'ant-upload-drag-icon' }, React.createElement(InboxOutlined)), React.createElement('p', { className: 'ant-upload-text' }, props.text || '点击或拖拽文件上传'));
|
|
109
|
+
case 'Divider': return React.createElement(Divider, { key: props.key });
|
|
110
|
+
case 'Alert': return React.createElement(Alert, { key: props.key, type: props.variant === 'danger' ? 'error' : props.variant || 'info', message: props.content || props.text, showIcon: true, style: { marginBottom: 16 } });
|
|
111
|
+
case 'Diagram': return React.createElement('div', { key: props.key, style: { background: 'linear-gradient(135deg, #1677ff 0%, #722ed1 100%)', borderRadius: 8, padding: 32, minHeight: 300 } }, props.title && React.createElement('div', { style: { color: 'white', fontSize: 18, fontWeight: 600, textAlign: 'center', marginBottom: 24 } }, props.title), React.createElement('div', { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 16 } }, children && children.map((child, i) => renderNode({ ...child, key: i }))));
|
|
112
|
+
case 'Box': return React.createElement(Card, { key: props.key, size: 'small', style: { minWidth: 120, textAlign: 'center', borderLeft: props.color ? `3px solid ${props.color}` : undefined } }, React.createElement(Text, { strong: true }, props.title), props.desc && React.createElement('div', null, React.createElement(Text, { type: 'secondary', style: { fontSize: 12 } }, props.desc)));
|
|
113
|
+
case 'Arrow': return React.createElement('div', { key: props.key, style: { color: 'white', fontSize: 24, textAlign: 'center' } }, (props.direction === 'up' ? '↑' : props.direction === 'left' ? '←' : props.direction === 'right' ? '→' : '↓'), props.label && React.createElement('span', { style: { fontSize: 12, marginLeft: 8 } }, props.label));
|
|
114
|
+
case 'Layer': return React.createElement('div', { key: props.key, style: { display: 'flex', flexWrap: 'wrap', gap: 12, justifyContent: 'center', width: '100%' } }, props.title && React.createElement('div', { style: { width: '100%', textAlign: 'center', color: 'rgba(255,255,255,0.8)', fontSize: 12, marginBottom: 8 } }, props.title), children && children.map((child, i) => renderNode({ ...child, key: i })));
|
|
115
|
+
case 'DiagramGroup': return React.createElement('div', { key: props.key, style: { background: 'rgba(255,255,255,0.1)', border: '1px dashed rgba(255,255,255,0.3)', borderRadius: 8, padding: 16, width: '100%' } }, props.title && React.createElement('div', { style: { color: 'rgba(255,255,255,0.9)', fontSize: 14, marginBottom: 12 } }, props.title), React.createElement('div', { style: { display: 'flex', flexWrap: 'wrap', gap: 12, justifyContent: 'center' } }, children && children.map((child, i) => renderNode({ ...child, key: i }))));
|
|
116
|
+
default: return React.createElement(Alert, { key: props.key, type: 'warning', message: `未知组件: ${type}` });
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
return React.createElement(ConfigProvider, { theme: { token: { colorPrimary: '#1677ff', borderRadius: 6 } } }, renderNode(data));
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const root = ReactDOM.createRoot(document.getElementById('root'));
|
|
123
|
+
root.render(React.createElement(A2UIRenderer, { data: UI_DATA }));
|
|
124
|
+
</script>
|
|
125
|
+
</body>
|
|
126
|
+
|
|
127
|
+
</html>
|