@yorha2b-lab/autodev 3.0.0 → 3.1.1
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/autodev.js +7 -6
- package/config.js +2 -0
- package/example/example.json +57 -55
- package/package.json +1 -1
- package/src/commands/handlers/api-handler.js +16 -6
- package/src/commands/handlers/page-handler.js +4 -4
- package/src/commands/handlers/part-handler.js +12 -16
- package/src/commands/watch.js +3 -3
- package/src/core/context.js +8 -8
- package/src/core/react-compiler.js +9 -7
- package/src/prompts/react/watch-api.js +31 -11
- package/src/prompts/react/watch-page.js +59 -44
- package/src/prompts/react/watch-part.js +16 -8
- package/src/services/llm.js +4 -2
- package/src/services/proxy-tower.js +10 -3
- package/src/utils/utils.js +113 -29
- package/templates/react/handlebars/stateBlock.hbs +2 -2
package/bin/autodev.js
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
const fs = require('fs')
|
|
3
3
|
const path = require('path')
|
|
4
4
|
const chalk = require('chalk')
|
|
5
|
+
const pkg = require('../package.json')
|
|
5
6
|
const { program } = require('commander')
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const { language, matrixEffect, bootSequence } = require(path.join(__dirname, '../src/utils/utils.js'))
|
|
7
|
+
const bunker = require('../src/core/context')
|
|
8
|
+
const { language, matrixEffect, bootSequence } = require('../src/utils/utils.js')
|
|
9
9
|
|
|
10
10
|
program
|
|
11
11
|
.version(pkg.version)
|
|
@@ -54,9 +54,10 @@ program
|
|
|
54
54
|
.action(() => {
|
|
55
55
|
bootSequence(pkg.version)
|
|
56
56
|
bunker.init(program.opts())
|
|
57
|
-
require(
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
require('../src/commands/watch.js')()
|
|
58
|
+
const { config } = bunker.get()
|
|
59
|
+
if (config.proxyTarget && !config.needMock && config.enableAutoAlignment) {
|
|
60
|
+
require('../src/services/proxy-tower.js')()
|
|
60
61
|
}
|
|
61
62
|
process.on('SIGINT', () => {
|
|
62
63
|
console.log('\n')
|
package/config.js
CHANGED
package/example/example.json
CHANGED
|
@@ -1,75 +1,75 @@
|
|
|
1
1
|
{
|
|
2
2
|
"tabs": [
|
|
3
3
|
{
|
|
4
|
-
"
|
|
4
|
+
"label": "鬼剑士",
|
|
5
5
|
"key": "ghostSwordsman"
|
|
6
6
|
},
|
|
7
7
|
{
|
|
8
|
-
"
|
|
8
|
+
"label": "格斗家",
|
|
9
9
|
"key": "fighter"
|
|
10
10
|
},
|
|
11
11
|
{
|
|
12
|
-
"
|
|
12
|
+
"label": "神枪手",
|
|
13
13
|
"key": "gunner"
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
|
-
"
|
|
16
|
+
"label": "魔法师",
|
|
17
17
|
"key": "mage"
|
|
18
18
|
}
|
|
19
19
|
],
|
|
20
|
-
"staticInfo": {
|
|
21
|
-
"has": false,
|
|
22
|
-
"text": ""
|
|
23
|
-
},
|
|
24
20
|
"table": {
|
|
25
|
-
"pagination": true,
|
|
26
|
-
"expandable": true,
|
|
27
|
-
"rowSelection": true,
|
|
28
|
-
"operation": [
|
|
29
|
-
{
|
|
30
|
-
"label": "查看",
|
|
31
|
-
"action": "viewByRecord"
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
"label": "编辑",
|
|
35
|
-
"action": "editByRecord"
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
"label": "删除",
|
|
39
|
-
"action": "deleteByRecord"
|
|
40
|
-
}
|
|
41
|
-
],
|
|
42
21
|
"columns": [
|
|
43
22
|
{
|
|
44
23
|
"title": "职业",
|
|
45
|
-
"dataIndex": "
|
|
24
|
+
"dataIndex": "jobName",
|
|
25
|
+
"type": "text"
|
|
46
26
|
},
|
|
47
27
|
{
|
|
48
28
|
"title": "一觉",
|
|
49
|
-
"dataIndex": "firstAwakening"
|
|
29
|
+
"dataIndex": "firstAwakening",
|
|
30
|
+
"type": "text"
|
|
50
31
|
},
|
|
51
32
|
{
|
|
52
33
|
"title": "二觉",
|
|
53
|
-
"dataIndex": "secondAwakening"
|
|
34
|
+
"dataIndex": "secondAwakening",
|
|
35
|
+
"type": "text"
|
|
54
36
|
},
|
|
55
37
|
{
|
|
56
38
|
"title": "三觉",
|
|
57
|
-
"dataIndex": "thirdAwakening"
|
|
39
|
+
"dataIndex": "thirdAwakening",
|
|
40
|
+
"type": "text"
|
|
58
41
|
},
|
|
59
42
|
{
|
|
60
43
|
"title": "武器类型",
|
|
61
44
|
"dataIndex": "weaponType",
|
|
62
|
-
"
|
|
45
|
+
"type": "enum"
|
|
63
46
|
},
|
|
64
47
|
{
|
|
65
48
|
"title": "职业类型",
|
|
66
49
|
"dataIndex": "jobType",
|
|
67
|
-
"
|
|
50
|
+
"type": "enum"
|
|
68
51
|
},
|
|
69
52
|
{
|
|
70
53
|
"title": "登场时间",
|
|
71
|
-
"dataIndex": "
|
|
72
|
-
"
|
|
54
|
+
"dataIndex": "launchDate",
|
|
55
|
+
"type": "date"
|
|
56
|
+
}
|
|
57
|
+
],
|
|
58
|
+
"pagination": true,
|
|
59
|
+
"expandable": true,
|
|
60
|
+
"rowSelection": true,
|
|
61
|
+
"operation": [
|
|
62
|
+
{
|
|
63
|
+
"label": "查看",
|
|
64
|
+
"action": "viewByRecord"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"label": "编辑",
|
|
68
|
+
"action": "editByRecord"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"label": "删除",
|
|
72
|
+
"action": "deleteByRecord"
|
|
73
73
|
}
|
|
74
74
|
]
|
|
75
75
|
},
|
|
@@ -77,34 +77,44 @@
|
|
|
77
77
|
{
|
|
78
78
|
"label": "转职职业",
|
|
79
79
|
"name": "transferJob",
|
|
80
|
-
"type": "select"
|
|
81
|
-
"options": "_CODE_transferJobOptions_CODE_"
|
|
80
|
+
"type": "select"
|
|
82
81
|
},
|
|
83
82
|
{
|
|
84
83
|
"label": "武器类型",
|
|
85
84
|
"name": "weaponType",
|
|
86
|
-
"type": "select"
|
|
87
|
-
"options": "_CODE_weaponTypeOptions_CODE_"
|
|
85
|
+
"type": "select"
|
|
88
86
|
},
|
|
89
87
|
{
|
|
90
88
|
"label": "职业类型",
|
|
91
89
|
"name": "jobType",
|
|
92
|
-
"type": "select"
|
|
93
|
-
"options": "_CODE_jobTypeOptions_CODE_"
|
|
90
|
+
"type": "select"
|
|
94
91
|
},
|
|
95
92
|
{
|
|
96
93
|
"label": "觉醒名称关键词",
|
|
97
|
-
"name": "
|
|
94
|
+
"name": "awakeningNameKeyword"
|
|
98
95
|
},
|
|
99
96
|
{
|
|
100
97
|
"label": "登场时间",
|
|
101
|
-
"name": "
|
|
98
|
+
"name": "launchDateStart,launchDateEnd",
|
|
102
99
|
"type": "daterange"
|
|
103
100
|
}
|
|
104
101
|
],
|
|
102
|
+
"staticInfo": {
|
|
103
|
+
"has": false,
|
|
104
|
+
"text": ""
|
|
105
|
+
},
|
|
105
106
|
"optionDict": {
|
|
106
|
-
"
|
|
107
|
-
|
|
107
|
+
"transferJobOptions": [
|
|
108
|
+
{
|
|
109
|
+
"label": "剑魂",
|
|
110
|
+
"value": "soulBender"
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"label": "狂战士",
|
|
114
|
+
"value": "berserker"
|
|
115
|
+
}
|
|
116
|
+
],
|
|
117
|
+
"weaponTypeOptions": [
|
|
108
118
|
{
|
|
109
119
|
"label": "光剑",
|
|
110
120
|
"value": "lightSword"
|
|
@@ -118,33 +128,25 @@
|
|
|
118
128
|
"value": "greatSword"
|
|
119
129
|
}
|
|
120
130
|
],
|
|
121
|
-
"
|
|
131
|
+
"jobTypeOptions": [
|
|
122
132
|
{
|
|
123
133
|
"label": "物理",
|
|
124
134
|
"value": "physical"
|
|
125
135
|
},
|
|
126
136
|
{
|
|
127
137
|
"label": "魔法",
|
|
128
|
-
"value": "
|
|
138
|
+
"value": "magic"
|
|
129
139
|
}
|
|
130
140
|
]
|
|
131
141
|
},
|
|
132
142
|
"functionButton": [
|
|
133
143
|
{
|
|
134
144
|
"btn": "批量删除",
|
|
135
|
-
"action": "
|
|
145
|
+
"action": "deleteBySelected"
|
|
136
146
|
},
|
|
137
147
|
{
|
|
138
148
|
"btn": "批量导出",
|
|
139
|
-
"action": "
|
|
140
|
-
},
|
|
141
|
-
{
|
|
142
|
-
"btn": "查询",
|
|
143
|
-
"action": "search"
|
|
144
|
-
},
|
|
145
|
-
{
|
|
146
|
-
"btn": "重置",
|
|
147
|
-
"action": "reset"
|
|
149
|
+
"action": "exportJobData"
|
|
148
150
|
}
|
|
149
151
|
],
|
|
150
152
|
"pageStruct": [
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@ const chalk = require('chalk')
|
|
|
5
5
|
|
|
6
6
|
module.exports = async (filePath, liveResponse = null) => {
|
|
7
7
|
|
|
8
|
-
const { get } = require(
|
|
8
|
+
const { get } = require('../../core/context')
|
|
9
9
|
const { config, options, language, unwrapSignal, alignResponseFields } = get()
|
|
10
10
|
|
|
11
11
|
const startTime = Date.now()
|
|
@@ -14,8 +14,8 @@ module.exports = async (filePath, liveResponse = null) => {
|
|
|
14
14
|
|
|
15
15
|
const spinner = ora({
|
|
16
16
|
text: chalk.yellow(language(
|
|
17
|
-
`🤖 Pod 153: [侦察] 发现加密数据源 [${fileName}]
|
|
18
|
-
`🤖 Pod 153: [Recon] Encrypted data source [${fileName}] detected. Initiating semantic alignment
|
|
17
|
+
`🤖 Pod 153: [侦察] 发现加密数据源 [${fileName}]。正在尝试执行语义对齐协议...\n`,
|
|
18
|
+
`🤖 Pod 153: [Recon] Encrypted data source [${fileName}] detected. Initiating semantic alignment...\n`
|
|
19
19
|
)),
|
|
20
20
|
color: 'yellow'
|
|
21
21
|
}).start()
|
|
@@ -47,7 +47,17 @@ module.exports = async (filePath, liveResponse = null) => {
|
|
|
47
47
|
`🧑💻 9S: Scanning field differences... Bridging semantic gaps.`
|
|
48
48
|
))
|
|
49
49
|
|
|
50
|
-
const
|
|
50
|
+
const extractKeys = str => {
|
|
51
|
+
const keys = []
|
|
52
|
+
const regex = /(dataIndex|name)\s*:\s*['"]([^'"]+)['"]/g
|
|
53
|
+
let match
|
|
54
|
+
while ((match = regex.exec(str)) !== null) {
|
|
55
|
+
keys.push(match[2])
|
|
56
|
+
}
|
|
57
|
+
return Array.from(new Set(keys))
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const result = await alignResponseFields(options, JSON.stringify(sampleData), extractKeys(resourceStr).join(','))
|
|
51
61
|
let changeCount = 0
|
|
52
62
|
const resultMapping = {}
|
|
53
63
|
Object.entries(result).forEach(([oldField, newField]) => {
|
|
@@ -67,7 +77,7 @@ module.exports = async (filePath, liveResponse = null) => {
|
|
|
67
77
|
)))
|
|
68
78
|
|
|
69
79
|
if (changeCount > 0) {
|
|
70
|
-
console.log(chalk.
|
|
80
|
+
console.log(chalk.magenta(`\n┌────── [ YoRHa Autonomous Backend Alignment ] ──────┐`))
|
|
71
81
|
Object.entries(resultMapping).forEach(([oldField, newField]) => {
|
|
72
82
|
// 💡 物理校准:计算空格数量,让箭头对齐在第 15 个字符位
|
|
73
83
|
// 中文字符长度 * 2 是为了抵消它在终端占的双倍宽度
|
|
@@ -80,7 +90,7 @@ module.exports = async (filePath, liveResponse = null) => {
|
|
|
80
90
|
chalk.white(newField)
|
|
81
91
|
)
|
|
82
92
|
})
|
|
83
|
-
console.log(chalk.
|
|
93
|
+
console.log(chalk.magenta(`└───────────────────────────────────────────────────────┘\n`))
|
|
84
94
|
}
|
|
85
95
|
|
|
86
96
|
if (fs.existsSync(filePath)) {
|
|
@@ -6,7 +6,7 @@ const stringify = require('json-stringify-pretty-compact')
|
|
|
6
6
|
|
|
7
7
|
module.exports = async filePath => {
|
|
8
8
|
|
|
9
|
-
const { get } = require(
|
|
9
|
+
const { get } = require('../../core/context')
|
|
10
10
|
|
|
11
11
|
const {
|
|
12
12
|
config, language, menus,
|
|
@@ -46,11 +46,11 @@ module.exports = async filePath => {
|
|
|
46
46
|
`\n🤖 Pod 042: [报告] 拦截到实弹请求。正在空投标准模拟包: example.json\n`,
|
|
47
47
|
`\n🤖 Pod 042: [Report] Real-fire request intercepted. Dropping simulation package: example.json\n`
|
|
48
48
|
)))
|
|
49
|
-
pageConfig = require(
|
|
49
|
+
pageConfig = require('../../../example/example.json')
|
|
50
50
|
} else {
|
|
51
51
|
spinner.text = chalk.cyan(language(
|
|
52
|
-
`🤖 Pod 042:
|
|
53
|
-
`🤖 Pod 042: Uploading visual metadata to Command for semantic analysis
|
|
52
|
+
`🤖 Pod 042: 正在上传视觉元数据至司令部进行语义分析...\n`,
|
|
53
|
+
`🤖 Pod 042: Uploading visual metadata to Command for semantic analysis...\n`
|
|
54
54
|
))
|
|
55
55
|
pageConfig = await recognizePage(pagePrompt, filePath)
|
|
56
56
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
2
|
const ora = require('ora')
|
|
3
|
-
const path = require('path')
|
|
4
3
|
const chalk = require('chalk')
|
|
5
4
|
const stringify = require('json-stringify-pretty-compact')
|
|
5
|
+
const { cleanCode, formatFormItemAndColumns } = require('../../utils/utils')
|
|
6
6
|
|
|
7
7
|
module.exports = async filePath => {
|
|
8
8
|
|
|
9
|
-
const { get } = require(
|
|
9
|
+
const { get } = require('../../core/context')
|
|
10
10
|
const { language, partPrompt, recognizePage } = get()
|
|
11
11
|
|
|
12
12
|
const startTime = Date.now()
|
|
@@ -21,27 +21,23 @@ module.exports = async filePath => {
|
|
|
21
21
|
|
|
22
22
|
try {
|
|
23
23
|
spinner.text = chalk.cyan(language(
|
|
24
|
-
`🤖 Pod 042: 正在从神经云网络提取 UI
|
|
25
|
-
`🤖 Pod 042: Extracting UI metadata from neural cloud network
|
|
24
|
+
`🤖 Pod 042: 正在从神经云网络提取 UI 元数据...\n`,
|
|
25
|
+
`🤖 Pod 042: Extracting UI metadata from neural cloud network...\n`
|
|
26
26
|
))
|
|
27
27
|
|
|
28
28
|
const pageConfig = await recognizePage(partPrompt, filePath)
|
|
29
29
|
|
|
30
|
-
const
|
|
31
|
-
delete pageConfig.optionDict
|
|
30
|
+
const { formItems, dictBlocks, processedColumns } = formatFormItemAndColumns({ pageConfig })
|
|
32
31
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
.replace(/['"]_CODE_([\s\S]*?)_CODE_['"]/g, '$1') // 还原代码片段
|
|
37
|
-
.replace(/_CODE_/g, '')
|
|
32
|
+
const result = Object.fromEntries(Object.entries({ formItems, processedColumns }).filter(([key, value]) => value?.length > 0))
|
|
33
|
+
|
|
34
|
+
let mainConfigStr = cleanCode(stringify.default(result, { indent: 4, maxLength: 200 }))
|
|
38
35
|
|
|
39
36
|
let optionsCodeStr = ''
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
const optionsArray = optionDict[key]
|
|
37
|
+
dictBlocks.forEach(key => {
|
|
38
|
+
const optionsArray = pageConfig.optionDict?.[key] ?? []
|
|
43
39
|
const arrayItemsStr = optionsArray.map(opt => ` { label: '${opt.label}', value: '${opt.value}' }`).join(',\n')
|
|
44
|
-
optionsCodeStr += `\nexport const ${
|
|
40
|
+
optionsCodeStr += `\nexport const ${key} = [\n${arrayItemsStr}\n]\n`
|
|
45
41
|
})
|
|
46
42
|
|
|
47
43
|
const finalResult = `${mainConfigStr}\n${optionsCodeStr}`
|
|
@@ -63,7 +59,7 @@ module.exports = async filePath => {
|
|
|
63
59
|
`│ 命令:请手动将上述代码块物理装配至您的目标文件中。`,
|
|
64
60
|
`│ Command: Please manually assemble the above code block into your target file.`
|
|
65
61
|
)))
|
|
66
|
-
console.log(chalk.magenta(
|
|
62
|
+
console.log(chalk.magenta(`└───────────────────────────────────────────────────────────────────┘\n`))
|
|
67
63
|
|
|
68
64
|
if (fs.existsSync(filePath)) {
|
|
69
65
|
fs.unlinkSync(filePath)
|
package/src/commands/watch.js
CHANGED
|
@@ -4,9 +4,9 @@ const chalk = require('chalk')
|
|
|
4
4
|
const chokidar = require('chokidar')
|
|
5
5
|
const stringify = require('json-stringify-pretty-compact')
|
|
6
6
|
|
|
7
|
-
const { get } = require(
|
|
8
|
-
const { copyTemplateDir } = require(
|
|
9
|
-
const { createTaskQueue } = require(
|
|
7
|
+
const { get } = require('../core/context')
|
|
8
|
+
const { copyTemplateDir } = require('../utils/utils.js')
|
|
9
|
+
const { createTaskQueue } = require('../core/task-queue.js')
|
|
10
10
|
|
|
11
11
|
const watch = () => {
|
|
12
12
|
|
package/src/core/context.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
2
|
const path = require('path')
|
|
3
3
|
const Handlebars = require('handlebars')
|
|
4
|
-
const apiHandler = require(
|
|
5
|
-
const pageHandler = require(
|
|
6
|
-
const partHandler = require(
|
|
7
|
-
const { recognizePage, generateMock, alignResponseFields } = require(
|
|
8
|
-
const { language, getConfig, unwrapSignal, getExistingMenus } = require(
|
|
4
|
+
const apiHandler = require('../commands/handlers/api-handler')
|
|
5
|
+
const pageHandler = require('../commands/handlers/page-handler')
|
|
6
|
+
const partHandler = require('../commands/handlers/part-handler')
|
|
7
|
+
const { recognizePage, generateMock, alignResponseFields } = require('../services/llm')
|
|
8
|
+
const { language, getConfig, unwrapSignal, isQuerySignal, getExistingMenus } = require('../utils/utils')
|
|
9
9
|
|
|
10
10
|
let instance = null // 💡 物理封存的全局实例
|
|
11
11
|
|
|
@@ -27,14 +27,14 @@ module.exports = {
|
|
|
27
27
|
: path.join(__dirname, `../../templates/${template}`)
|
|
28
28
|
|
|
29
29
|
instance = {
|
|
30
|
-
menus, config, options, language, unwrapSignal,
|
|
31
30
|
resource, index, template,
|
|
32
31
|
apiHandler, pageHandler, partHandler,
|
|
33
32
|
recognizePage, generateMock, alignResponseFields,
|
|
33
|
+
pagePrompt: require(`../prompts/${template}/watch-page.js`),
|
|
34
|
+
partPrompt: require(`../prompts/${template}/watch-part.js`),
|
|
35
|
+
menus, config, options, language, unwrapSignal, isQuerySignal,
|
|
34
36
|
indexTpl: Handlebars.compile(fs.readFileSync(path.join(tplDir, 'index.hbs'), 'utf-8')),
|
|
35
37
|
resourceTpl: Handlebars.compile(fs.readFileSync(path.join(tplDir, 'resource.hbs'), 'utf-8')),
|
|
36
|
-
pagePrompt: require(path.join(__dirname, `../prompts/${template}/watch-page.js`)),
|
|
37
|
-
partPrompt: require(path.join(__dirname, `../prompts/${template}/watch-part.js`))
|
|
38
38
|
}
|
|
39
39
|
return instance
|
|
40
40
|
},
|
|
@@ -2,7 +2,7 @@ const fs = require('fs')
|
|
|
2
2
|
const path = require('path')
|
|
3
3
|
const Handlebars = require('handlebars')
|
|
4
4
|
const stringify = require('json-stringify-pretty-compact')
|
|
5
|
-
const { cleanCode, generateSmartImports } = require(
|
|
5
|
+
const { cleanCode, generateSmartImports, formatFormItemAndColumns } = require('../utils/utils.js')
|
|
6
6
|
|
|
7
7
|
Handlebars.registerHelper('raw', options => options.fn())
|
|
8
8
|
Handlebars.registerHelper('stringify', (context, maxLength = 200) => context ? new Handlebars.SafeString(stringify.default(context, { indent: 4, maxLength })) : '[]')
|
|
@@ -18,15 +18,15 @@ const resource = ({ pageConfig, resourceTpl }) => {
|
|
|
18
18
|
|
|
19
19
|
const hasTabs = pageConfig.tabs?.length > 0
|
|
20
20
|
|
|
21
|
+
const { formItems, processedColumns, dictBlocks } = formatFormItemAndColumns({ pageConfig })
|
|
22
|
+
|
|
21
23
|
const viewData = {
|
|
22
24
|
hasTabs,
|
|
25
|
+
formItems,
|
|
23
26
|
tabs: pageConfig.tabs,
|
|
24
|
-
|
|
25
|
-
formItemsData: hasTabs ? Object.fromEntries(pageConfig.tabs.map(tab => [tab.key,
|
|
26
|
-
columnsData: hasTabs ? Object.fromEntries(pageConfig.tabs.map(tab => [tab.key,
|
|
27
|
-
dictBlocks: pageConfig.formItems
|
|
28
|
-
?.filter(item => item.type === 'select')
|
|
29
|
-
?.map(item => ({ name: item.options.replaceAll('_CODE_', ''), data: pageConfig.optionDict[item.options] ?? [] }))
|
|
27
|
+
dictBlocks: dictBlocks.map(item => ({ name: item, data: pageConfig.optionDict[item] ?? [] })),
|
|
28
|
+
formItemsData: hasTabs ? Object.fromEntries(pageConfig.tabs.map(tab => [tab.key, formItems])) : formItems,
|
|
29
|
+
columnsData: hasTabs ? Object.fromEntries(pageConfig.tabs.map(tab => [tab.key, processedColumns])) : processedColumns,
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
const rawCode = resourceTpl(viewData)
|
|
@@ -43,6 +43,8 @@ const resource = ({ pageConfig, resourceTpl }) => {
|
|
|
43
43
|
*/
|
|
44
44
|
const index = ({ config, fileName, indexTpl, pageConfig }) => {
|
|
45
45
|
|
|
46
|
+
console.log(stringify.default(pageConfig, { indent: 4, maxLength: 200 }))
|
|
47
|
+
|
|
46
48
|
const hasTabs = pageConfig.tabs?.length > 0
|
|
47
49
|
const hasFormItems = pageConfig.formItems?.length > 0
|
|
48
50
|
const hasOperate = pageConfig.table.operation?.length > 0
|
|
@@ -1,12 +1,32 @@
|
|
|
1
1
|
module.exports = (responseStr, resourceStr) => `
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
2
|
+
目标:执行【前端虚拟字段】与【后端真实响应】的物理重组与对账。
|
|
3
|
+
|
|
4
|
+
## 1. 核心输入 (Signal Capture)
|
|
5
|
+
- **前端现状 (Source Keys)**:
|
|
6
|
+
${resourceStr}
|
|
7
|
+
|
|
8
|
+
- **后端样本 (Target Data)**:
|
|
9
|
+
${responseStr}
|
|
10
|
+
|
|
11
|
+
## 2. 匹配算法优先级 (Alignment Protocol)
|
|
12
|
+
请严格按以下优先级执行逻辑映射,直到找到唯一目标:
|
|
13
|
+
1. **L1 - 物理全等**: 字符完全一致 (例: userId == userId)。
|
|
14
|
+
2. **L2 - 格式重组**: 下划线与驼峰转换 (例: user_id == userId)。
|
|
15
|
+
3. **L3 - 语义穿透**: 业务含义高度一致 (例: amount == price, phone == mobile)。
|
|
16
|
+
|
|
17
|
+
## 3. ⚠️ 强制冲突修正 (Conflict Resolution)
|
|
18
|
+
若存在多个潜在目标,执行以下强制判定:
|
|
19
|
+
- **中文字义优先**: 优先匹配能解释前端视觉标签含义的字段。
|
|
20
|
+
- **长度优先**: 若 "createByName" 与 "createBy" 同时存在,强制选择 "createByName"。
|
|
21
|
+
|
|
22
|
+
## 4. 输出约束 (Output Restrictions)
|
|
23
|
+
- **严禁输出解释性文字、严禁 Markdown 标签**。
|
|
24
|
+
- **仅输出合法的 JSON 对象**。
|
|
25
|
+
- **过滤机制**: 若某字段在后端响应中完全找不到对应映射,物理拦截(不输出)。
|
|
26
|
+
|
|
27
|
+
## 5. 示例参考 (Example)
|
|
28
|
+
Input:
|
|
29
|
+
Source: { dataIndex: 'userName' }, Response: { name: '2B' }
|
|
30
|
+
Output:
|
|
31
|
+
{"userName": "name"}
|
|
32
|
+
`.trim()
|
|
@@ -1,45 +1,60 @@
|
|
|
1
1
|
module.exports = `
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
2
|
+
# Task: Full-Page UI Analysis & Structural Assembly
|
|
3
|
+
执行全页面视觉特征提取,并按照物理布局顺序输出标准的构筑协议。
|
|
4
|
+
|
|
5
|
+
## 1. 核心命名协议 (Naming Convention)
|
|
6
|
+
- **驼峰命名**: 所有变量及函数名必须遵循 camelCase。
|
|
7
|
+
- **⚠️ 严禁中文**: 绝对禁止在变量名、键名中使用中文字符。
|
|
8
|
+
- **物理屏蔽关键字**: 禁止使用 JS 保留字 (export, delete, const, let, class, default 等)。
|
|
9
|
+
|
|
10
|
+
## 2. 零部件构筑规范 (Component Specifications)
|
|
11
|
+
|
|
12
|
+
### 标签页 (Tabs)
|
|
13
|
+
- **识别条件**: 仅当下部有显著长横线 (Ink Bar) 或呈现包裹感 (Card style) 时判定。
|
|
14
|
+
- **排除项**: 表格上方的独立按钮必须归类为 [functionButton]。
|
|
15
|
+
|
|
16
|
+
### 搜索项 (FormItems)
|
|
17
|
+
- **基础格式**: [{ label: '文本', name: 'englishName',type:'' }]
|
|
18
|
+
- ⚠️ 属性精简规则:
|
|
19
|
+
- 默认输入框: 仅保留 [label, name] 属性。**严禁出现 type 属性**。
|
|
20
|
+
- 非默认组件: 仅当类型为 [auto, date, radio, select, upload, checkbox, textarea, daterange] 时才允许添加 type 属性。
|
|
21
|
+
**标准定义 (Columns)**:
|
|
22
|
+
- date: 单日期
|
|
23
|
+
- daterange: 日期范围
|
|
24
|
+
- enum: 枚举类型
|
|
25
|
+
- text: 普通文本(默认)
|
|
26
|
+
- 命名规范:
|
|
27
|
+
- daterange: name 必须设为 '字段英文名start,字段英文名end'。
|
|
28
|
+
|
|
29
|
+
### 统计条 (StaticInfo)
|
|
30
|
+
- **独立存在**: 位于表格外周的汇总信息。输出格式: { has: true, text: '具体内容' }。
|
|
31
|
+
|
|
32
|
+
### 表格主体 (Table)
|
|
33
|
+
- **标准项**: {title:'列名', dataIndex:'列名英文名词',type:''}。
|
|
34
|
+
- **配置项**: [pagination, expandable, rowSelection] 均为布尔值false,只有页面明确有对应功能时才为true。
|
|
35
|
+
- **标准定义 (Columns)**:
|
|
36
|
+
- ⚠️ **操作锁定**: 严禁在 columns 数组中包含“操作”列。
|
|
37
|
+
- date: 时间/日期列
|
|
38
|
+
- money: 金额列
|
|
39
|
+
- index: 序号列
|
|
40
|
+
- enum: 枚举列
|
|
41
|
+
- text: 普通文本(默认)
|
|
42
|
+
- **行操作 (Operation)**: [{label:'操作名', action:'动词ByRecord'}]。
|
|
43
|
+
|
|
44
|
+
### 全局功能按钮 (FunctionButton)
|
|
45
|
+
- **格式**: [{btn:'显示文本', action:'动作名'}]。
|
|
46
|
+
- **联动命名**: 若与行操作重复,action 必须加上 'BySelected' 前缀。
|
|
47
|
+
- **导出协议**: action 设为 'exportData' 或 'export+模块名'。
|
|
48
|
+
|
|
49
|
+
## 3. 布局逻辑编排 (Page Structure)
|
|
50
|
+
- **字段**: [pageStruct] 定义从上至下的物理堆叠顺序。
|
|
51
|
+
- **白名单**: 只能从 [AlertInfo, MySearchForm, FunctionButtonsBlock, MyTable] 中选择。
|
|
52
|
+
- **⚠️ 约束**: 严禁在 pageStruct 中包含 Tabs 或其他未定义组件;并且只有真实存在的组件才能被包含。
|
|
53
|
+
|
|
54
|
+
## 4. 输出约束 (Output Format)
|
|
55
|
+
- **格式**: 纯 JSON 对象,严禁 Markdown 标签包围。
|
|
56
|
+
- **字典映射 (OptionDict)**: 必须包含所有 select 类型所需的 Options 数组,键名格式为 字段英文名+Options ,元素格式为 {label:'', value:''}。
|
|
57
|
+
|
|
58
|
+
**JSON 骨架要求**:
|
|
59
|
+
{ "tabs": [], "table": {}, "formItems": [], "staticInfo": {}, "optionDict": {}, "functionButton": [], "pageStruct": [] }
|
|
60
|
+
`.trim();
|
|
@@ -8,31 +8,39 @@ module.exports = `
|
|
|
8
8
|
3. 必须严格遵守以下字段规范。
|
|
9
9
|
|
|
10
10
|
## 1. 表格 (Table)
|
|
11
|
-
- 结构: { columns: [{ title: '', dataIndex: '' }] }
|
|
11
|
+
- 结构: { columns: [{ title: '列名', dataIndex: '列名英文名词', type: '' }] }
|
|
12
12
|
- ⚠️ 字段白名单: [title, dataIndex, sorter, render, filters, onFilter]
|
|
13
13
|
- 禁止输出:
|
|
14
14
|
- 严禁在 columns 中包含“操作”列。
|
|
15
15
|
- ***⚠️⚠️⚠️严禁输出formItems,只允许输出coloumns⚠️⚠️⚠️***
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
16
|
+
- **标准定义 (Columns)**:
|
|
17
|
+
- ⚠️ **操作锁定**: 严禁在 columns 数组中包含“操作”列。
|
|
18
|
+
- date: 时间/日期列
|
|
19
|
+
- money: 金额列
|
|
20
|
+
- index: 序号列
|
|
21
|
+
- enum: 枚举列
|
|
22
|
+
- text: 普通文本(默认)
|
|
23
|
+
- **行操作 (Operation)**: [{label:'操作名', action:'动词ByRecord'}]
|
|
20
24
|
|
|
21
25
|
## 2. 表单 (Form)
|
|
22
|
-
-
|
|
26
|
+
- **基础格式**: [{ label: '文本', name: 'englishName',type:'' }]
|
|
23
27
|
- 禁止输出:
|
|
24
28
|
- ***⚠️⚠️⚠️严禁输出columns,只允许输出formItems⚠️⚠️⚠️***
|
|
25
29
|
- ⚠️ 属性精简规则:
|
|
26
30
|
- 默认输入框: 仅保留 [label, name] 属性。**严禁出现 type 属性**。
|
|
27
31
|
- 非默认组件: 仅当类型为 [auto, date, radio, select, upload, checkbox, textarea, daterange] 时才允许添加 type 属性。
|
|
32
|
+
**标准定义 (Columns)**:
|
|
33
|
+
- date: 单日期
|
|
34
|
+
- daterange: 日期范围
|
|
35
|
+
- enum: 枚举类型
|
|
36
|
+
- text: 普通文本(默认)
|
|
28
37
|
- 命名规范:
|
|
29
38
|
- daterange: name 必须设为 '字段英文名start,字段英文名end'。
|
|
30
|
-
- 选择类 (radio/select/checkbox): options 必须设为 _CODE_字段英文名Options_CODE_。
|
|
31
39
|
- 增强属性:
|
|
32
40
|
- 必填校验: 若图片中 label 前有红色星号,必须加入 rules:[{required:true,message:'xxx不能为空'}]。
|
|
33
41
|
- 文字单位: 若项末尾有单位(如 元/kg),必须存入 unit 属性。
|
|
34
42
|
|
|
35
43
|
## 3. 下拉选项字典 (OptionDict)
|
|
36
|
-
- 结构: optionDict: {
|
|
44
|
+
- 结构: optionDict: { 字段英文名Options: [] }
|
|
37
45
|
- 内容: 数组元素必须为 {label: '', value: ''} 格式。
|
|
38
46
|
`
|
package/src/services/llm.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
2
|
const path = require('path')
|
|
3
3
|
const chalk = require('chalk')
|
|
4
|
-
const { language, getConfig } = require(
|
|
4
|
+
const { language, getConfig } = require('../utils/utils.js')
|
|
5
5
|
|
|
6
6
|
let client = null
|
|
7
7
|
|
|
@@ -19,6 +19,7 @@ const getOpenAI = () => {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
const askAI = async (model, messages, retryCount = 0) => {
|
|
22
|
+
|
|
22
23
|
if (retryCount > 3) {
|
|
23
24
|
throw new Error(language(
|
|
24
25
|
'YoRHa 司令部连接中断:请检查网络状态或黑盒共鸣情况。',
|
|
@@ -31,6 +32,8 @@ const askAI = async (model, messages, retryCount = 0) => {
|
|
|
31
32
|
const response = await openai.chat.completions.create({
|
|
32
33
|
model,
|
|
33
34
|
messages,
|
|
35
|
+
top_p: 0.1,
|
|
36
|
+
temperature: 0.01,
|
|
34
37
|
response_format: { type: 'json_object' }
|
|
35
38
|
})
|
|
36
39
|
let raw = response.choices[0].message.content.trim()
|
|
@@ -38,7 +41,6 @@ const askAI = async (model, messages, retryCount = 0) => {
|
|
|
38
41
|
const match = raw.match(/[\{\[][\s\S]*[\}\]]/)
|
|
39
42
|
const JSON5 = require('json5')
|
|
40
43
|
return JSON5.parse(match ? match[0] : raw)
|
|
41
|
-
|
|
42
44
|
} catch (err) {
|
|
43
45
|
const statusCode = err.status || err.response?.status
|
|
44
46
|
const isAuthError = err.message.includes('401') || err.message.includes('402') || [401, 402].includes(statusCode)
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
const http = require('http')
|
|
2
|
-
const path = require('path')
|
|
3
2
|
const zlib = require('zlib')
|
|
4
3
|
const chalk = require('chalk')
|
|
5
4
|
const httpProxy = require('http-proxy')
|
|
6
|
-
const { get } = require(
|
|
5
|
+
const { get } = require('../core/context')
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* @function startProxyTower
|
|
@@ -12,7 +11,7 @@ const { get } = require(path.join(__dirname, '../core/context'))
|
|
|
12
11
|
*/
|
|
13
12
|
module.exports = () => {
|
|
14
13
|
|
|
15
|
-
const { config, language, apiHandler, unwrapSignal } = get()
|
|
14
|
+
const { config, language, apiHandler, unwrapSignal, isQuerySignal } = get()
|
|
16
15
|
|
|
17
16
|
const TOWER_PORT = 42153
|
|
18
17
|
const hackedRegistry = new Map()
|
|
@@ -44,6 +43,14 @@ module.exports = () => {
|
|
|
44
43
|
try {
|
|
45
44
|
const rawBody = encoding === 'gzip' ? zlib.gunzipSync(buffer) : buffer
|
|
46
45
|
const json = JSON.parse(rawBody.toString())
|
|
46
|
+
|
|
47
|
+
const coreData = unwrapSignal(json) // 先脱水
|
|
48
|
+
|
|
49
|
+
// 💡 这一步就是地堡的“火控系统”
|
|
50
|
+
if (!isQuerySignal(req, json, coreData)) {
|
|
51
|
+
return // 增删改信号,直接丢弃,保持静默
|
|
52
|
+
}
|
|
53
|
+
|
|
47
54
|
const referer = req.headers.referer || ''
|
|
48
55
|
const fileName = referer.split('?')[0].split('/').filter(Boolean).at(-1)
|
|
49
56
|
|
package/src/utils/utils.js
CHANGED
|
@@ -46,10 +46,15 @@ const getConfig = () => {
|
|
|
46
46
|
*/
|
|
47
47
|
const cleanCode = str => {
|
|
48
48
|
return str
|
|
49
|
+
// 物理超度 Markdown 糖衣(解决```json 报错)
|
|
50
|
+
.replace(/```[a-z]*\n?/gi, '')
|
|
51
|
+
.replace(/```/g, '')
|
|
52
|
+
// 逻辑自愈 处理 AI 错误转义的引号(把 \" 还原回 ")
|
|
53
|
+
.replace(/\\"/g, '"')
|
|
54
|
+
.replace(/['"]?_CODE_([\s\S]*?)_CODE_['"]?/g, '$1') // 去掉 _CODE_ 包裹的代码
|
|
55
|
+
.replace(/_CODE_/g, '') // 兜底清理
|
|
49
56
|
.replace(/"(\w+)":/g, '$1:') // 去掉 key 的双引号
|
|
50
57
|
.replace(/"/g, "'") // 双引号全部转单引号
|
|
51
|
-
.replace(/['"]_CODE_([\s\S]*?)_CODE_['"]/g, '$1') // 去掉 _CODE_ 包裹的代码
|
|
52
|
-
.replace(/_CODE_/g, '') // 兜底清理
|
|
53
58
|
.replace(/[ \t]+$/gm, '') // 去除每一行行尾的多余空格
|
|
54
59
|
.replace(/\n{3,}/g, '\n\n') // 将3个或以上的换行符压缩成2个换行符
|
|
55
60
|
.replace(/^\s+/, '') // 去掉文件头部的空行
|
|
@@ -61,7 +66,7 @@ const cleanCode = str => {
|
|
|
61
66
|
* @description [地堡数据脱水机] 物理扫描 JSON 结构,剥离业务外壳(code/msg/total 等)。
|
|
62
67
|
* 目标:精准定位到核心的 Array。
|
|
63
68
|
*/
|
|
64
|
-
const unwrapSignal =
|
|
69
|
+
const unwrapSignal = json => {
|
|
65
70
|
if (Array.isArray(json)) return json
|
|
66
71
|
|
|
67
72
|
// 💡 扫描常见的数据仓库 Key
|
|
@@ -103,6 +108,31 @@ const bootSequence = async version => {
|
|
|
103
108
|
}
|
|
104
109
|
}
|
|
105
110
|
|
|
111
|
+
/**
|
|
112
|
+
* @function isQuerySignal
|
|
113
|
+
* @description [语义雷达]
|
|
114
|
+
* 核心逻辑:不纠结请求是怎么发的,只看带回来的货(Response)长什么样。
|
|
115
|
+
*/
|
|
116
|
+
const isQuerySignal = (req, json, coreData) => {
|
|
117
|
+
|
|
118
|
+
const url = req.url.toLowerCase()
|
|
119
|
+
// 💡 1. 物理红区:只要 URL 包含这些动作,无论返回什么都视为“非列表”
|
|
120
|
+
const actionKeywords = ['add', 'delete', 'update', 'save', 'remove', 'edit', 'insert', 'create', 'export', 'upload']
|
|
121
|
+
if (actionKeywords.some(key => url.includes(key))) return false
|
|
122
|
+
// 💡 2. 物理金标准:脱壳后的核心物资是一个【非空数组】
|
|
123
|
+
// 只要带回了一堆长得一样的对象,那它 99.9% 就是列表页
|
|
124
|
+
const hasListData = Array.isArray(coreData) && coreData.length > 0
|
|
125
|
+
// 💡 3. 语义辅助:如果响应里包含“分页指纹”
|
|
126
|
+
// 有时候第一页刚好没数据(coreData 是空数组),但 JSON 里带有 total, page 等字段
|
|
127
|
+
const hasPaginationFingerprint = ['total', 'records', 'page', 'size', 'count'].some(key => {
|
|
128
|
+
const k = key.toLowerCase()
|
|
129
|
+
// 在原始 JSON 的第一层寻找分页相关的 key
|
|
130
|
+
return Object.keys(json).some(rawKey => rawKey.toLowerCase().includes(k))
|
|
131
|
+
})
|
|
132
|
+
// 结论:具备列表特征或者是分页指纹的,判定为查询信号
|
|
133
|
+
return hasListData || hasPaginationFingerprint
|
|
134
|
+
}
|
|
135
|
+
|
|
106
136
|
/**
|
|
107
137
|
* 矩阵效果
|
|
108
138
|
* 模拟数据物理封存过程中的矩阵效果
|
|
@@ -137,12 +167,15 @@ const matrixEffect = async (duration = 1500) => {
|
|
|
137
167
|
'5f 43 4f 44 45 5f' // _CODE_
|
|
138
168
|
]
|
|
139
169
|
|
|
170
|
+
const threshold = 3000
|
|
140
171
|
const endTime = Date.now() + duration
|
|
141
|
-
const isLegendary = currentTotal >= 3000
|
|
142
172
|
const width = process.stdout.columns || 80
|
|
173
|
+
const isLegendary = currentTotal >= threshold
|
|
143
174
|
|
|
144
175
|
if (isLegendary) {
|
|
145
|
-
|
|
176
|
+
const achievement = `${threshold}+`
|
|
177
|
+
const hex = achievement.split('').map(char => char.charCodeAt(0).toString(16)).join(' ')
|
|
178
|
+
coreFragments.push(chalk.yellow.bold(hex))
|
|
146
179
|
coreFragments.push(chalk.yellow.bold('4c 45 47 45 4e 44')) // "LEGEND"
|
|
147
180
|
}
|
|
148
181
|
|
|
@@ -152,10 +185,10 @@ const matrixEffect = async (duration = 1500) => {
|
|
|
152
185
|
console.log(chalk.white(' [System] ') + chalk.green(language('所有构筑数据已同步至 Bunker 存储节点。', 'All data synced to Bunker storage nodes.')))
|
|
153
186
|
if (currentTotal !== 0) {
|
|
154
187
|
if (isLegendary) {
|
|
155
|
-
console.log(chalk.yellow.bold(language(` [Achievement] 物理克隆总数已超越
|
|
188
|
+
console.log(chalk.yellow.bold(language(` [Achievement] 物理克隆总数已超越 ${threshold} 战略阈值!当前战力:${currentTotal}`, ` [Achievement] Physical clone count has exceeded ${threshold} strategic threshold! Current power: ${currentTotal}`)))
|
|
156
189
|
console.log(chalk.yellow(language(' [Bunker] 恭喜指挥官,您的构筑协议已成为人类荣光的一部分。', ' [Bunker] Congratulations, your construction protocol is now part of humanity.')))
|
|
157
190
|
} else {
|
|
158
|
-
console.log(chalk.cyan(language(` [System] 当前构筑总数:${currentTotal}。距离
|
|
191
|
+
console.log(chalk.cyan(language(` [System] 当前构筑总数:${currentTotal}。距离 ${threshold} 勋章还剩 ${threshold - currentTotal} 次。`, ` [System] Current clones: ${currentTotal}. ${threshold - currentTotal} to Achievement.`)))
|
|
159
192
|
}
|
|
160
193
|
}
|
|
161
194
|
console.log(chalk.cyan(language(' [System] 如果它能帮您节省时间,请在 GitHub 上给它点个赞 ⭐。', ' [System] If it saves you time, feel free to give it a ⭐ on GitHub.')))
|
|
@@ -191,6 +224,77 @@ const getExistingMenus = (dir = 'src/pages') => {
|
|
|
191
224
|
.map(file => ({ label: file, key: file }))
|
|
192
225
|
}
|
|
193
226
|
|
|
227
|
+
/**
|
|
228
|
+
* @function formatFormItemAndColumns
|
|
229
|
+
* @description [地堡逻辑转录引擎] 执行核心的语义对齐与代码物理注入协议。
|
|
230
|
+
* 该函数负责将 AI 识别出的视觉类型标签(money/date/enum等)转化为标准的 React 渲染逻辑,
|
|
231
|
+
* 并自动提取全频道所需的字典引用(Dictionary Blocks)。
|
|
232
|
+
*
|
|
233
|
+
* @param {Object} params - 构筑参数包
|
|
234
|
+
* @param {Object} params.pageConfig - 由 Pod 042 扫描出的原始页面配置对象
|
|
235
|
+
*
|
|
236
|
+
* @returns {Object} 返回物理装配完成的数据包:
|
|
237
|
+
* @returns {Array} .formItems - 已注入 Options 指令的表单零部件清单
|
|
238
|
+
* @returns {Array} .processedColumns - 已执行逻辑注入(Render 补丁)的表格列清单
|
|
239
|
+
* @returns {Array} .dictBlocks - 全频道去重后的字典(Options)变量名清单
|
|
240
|
+
*/
|
|
241
|
+
const formatFormItemAndColumns = ({ pageConfig }) => {
|
|
242
|
+
|
|
243
|
+
const codePresets = {
|
|
244
|
+
money: 'text => moneyRender(text)',
|
|
245
|
+
date: 'text => timeRender({time: text})',
|
|
246
|
+
index: '(_, record, index) => index + 1',
|
|
247
|
+
enum: dataIndex => `text => ${dataIndex}Options.find(item => item.value === text)?.label??text`
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const columns = pageConfig.table?.columns ?? pageConfig?.columns ?? []
|
|
251
|
+
|
|
252
|
+
const tableDicts = columns?.filter(item => item.type === 'enum')?.map(item => `${item.dataIndex}Options`) ?? []
|
|
253
|
+
const formDicts = pageConfig.formItems?.filter(item => item.type === 'select')?.map(item => `${item.name}Options`) ?? []
|
|
254
|
+
const dictBlocks = Array.from(new Set([...formDicts, ...tableDicts]))
|
|
255
|
+
|
|
256
|
+
const formItems = pageConfig.formItems?.map(item => ({
|
|
257
|
+
...item,
|
|
258
|
+
...(item.type === 'select' ? { options: `_CODE_${item.name}Options_CODE_` } : {})
|
|
259
|
+
}))
|
|
260
|
+
|
|
261
|
+
const processedColumns = columns?.map(col => {
|
|
262
|
+
if (col.type && codePresets[col.type]) {
|
|
263
|
+
// 💡 物理注入:根据标签,强行塞入标准化的 JS 代码字符串
|
|
264
|
+
const renderCode = typeof codePresets[col.type] === 'function' ? codePresets[col.type](col.dataIndex) : codePresets[col.type]
|
|
265
|
+
delete col.type
|
|
266
|
+
return {
|
|
267
|
+
...col,
|
|
268
|
+
render: `_CODE_${renderCode}_CODE_` // 重新打标,交给 cleanCode 处理
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
delete col.type
|
|
272
|
+
return col
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
return { formItems, dictBlocks, processedColumns }
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* 复制模板目录到目标项目
|
|
280
|
+
* @param {Object} options - 命令行选项
|
|
281
|
+
* @param {string} templateSubDir - 模板子目录(如 'hooks'、'components')
|
|
282
|
+
* @param {string} targetSubDir - 目标子目录(如 'src/hooks'、'src/components')
|
|
283
|
+
*/
|
|
284
|
+
const copyTemplateDir = (options, templateSubDir, targetSubDir) => {
|
|
285
|
+
const targetDir = path.join(process.cwd(), targetSubDir)
|
|
286
|
+
const sourceDir = path.join(__dirname, `../../templates/${options.template}/${templateSubDir}`)
|
|
287
|
+
if (!fs.existsSync(sourceDir)) return
|
|
288
|
+
fs.mkdirSync(targetDir, { recursive: true })
|
|
289
|
+
fs.readdirSync(sourceDir).forEach(file => {
|
|
290
|
+
const src = path.join(sourceDir, file)
|
|
291
|
+
const dest = path.join(targetDir, file)
|
|
292
|
+
if (!fs.existsSync(dest)) {
|
|
293
|
+
fs.cpSync(src, dest, { recursive: true })
|
|
294
|
+
}
|
|
295
|
+
})
|
|
296
|
+
}
|
|
297
|
+
|
|
194
298
|
/**
|
|
195
299
|
* 生成智能导入语句
|
|
196
300
|
* 根据代码中实际使用的依赖,自动生成对应的 import 语句
|
|
@@ -213,33 +317,13 @@ const generateSmartImports = ({ bodyCode, hasTabs, hasFormItems }) => {
|
|
|
213
317
|
usedReact.length && `import { ${usedReact.join(', ')} } from 'react'`,
|
|
214
318
|
`import { request } from '../../utils/request'`,
|
|
215
319
|
`import { formatQuery } from '../../utils/utils'`,
|
|
216
|
-
usedAntd.length && `import { Form, ${usedAntd.join(', ')} } from 'antd'`,
|
|
217
320
|
...usedHooks.map(hook => `import { ${hook} } from '../../hooks/${hook}'`),
|
|
218
321
|
...usedComps.map(comp => `import { ${comp} } from '../../components/${comp}'`),
|
|
322
|
+
usedAntd.length && `import { ${hasFormItems ? 'Form, ' : ''}${usedAntd.join(', ')} } from 'antd'`,
|
|
219
323
|
`import { ${hasTabs ? 'tabs, ' : ''}${hasFormItems ? 'formItems, ' : ''}modalItems, tableColumns} from './resource'`,
|
|
220
324
|
].sort((a, b) => a.length - b.length)
|
|
221
325
|
|
|
222
326
|
return imports.filter(Boolean).join('\n')
|
|
223
327
|
}
|
|
224
328
|
|
|
225
|
-
|
|
226
|
-
* 复制模板目录到目标项目
|
|
227
|
-
* @param {Object} options - 命令行选项
|
|
228
|
-
* @param {string} templateSubDir - 模板子目录(如 'hooks'、'components')
|
|
229
|
-
* @param {string} targetSubDir - 目标子目录(如 'src/hooks'、'src/components')
|
|
230
|
-
*/
|
|
231
|
-
const copyTemplateDir = (options, templateSubDir, targetSubDir) => {
|
|
232
|
-
const targetDir = path.join(process.cwd(), targetSubDir)
|
|
233
|
-
const sourceDir = path.join(__dirname, `../../templates/${options.template}/${templateSubDir}`)
|
|
234
|
-
if (!fs.existsSync(sourceDir)) return
|
|
235
|
-
fs.mkdirSync(targetDir, { recursive: true })
|
|
236
|
-
fs.readdirSync(sourceDir).forEach(file => {
|
|
237
|
-
const src = path.join(sourceDir, file)
|
|
238
|
-
const dest = path.join(targetDir, file)
|
|
239
|
-
if (!fs.existsSync(dest)) {
|
|
240
|
-
fs.cpSync(src, dest, { recursive: true })
|
|
241
|
-
}
|
|
242
|
-
})
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
module.exports = { language, getConfig, cleanCode, unwrapSignal, matrixEffect, bootSequence, getExistingMenus, copyTemplateDir, generateSmartImports }
|
|
329
|
+
module.exports = { language, getConfig, cleanCode, unwrapSignal, matrixEffect, bootSequence, isQuerySignal, getExistingMenus, copyTemplateDir, generateSmartImports, formatFormItemAndColumns }
|
|
@@ -3,7 +3,6 @@ const rowKey = 'id'
|
|
|
3
3
|
{{#if hasFormItems}}
|
|
4
4
|
const [form] = Form.useForm()
|
|
5
5
|
{{/if}}
|
|
6
|
-
const [modal, setModal] = useState({ visible:false, title:'', formItems:modalItems })
|
|
7
6
|
{{#if hasRowSelection}}
|
|
8
7
|
const [selectedRows, setSelectedRows] = useState([])
|
|
9
8
|
{{/if}}
|
|
@@ -12,4 +11,5 @@ const [activeKey, setActiveKey] = useState(tabs[0].key)
|
|
|
12
11
|
{{/if}}
|
|
13
12
|
{{#if hasExpandable}}
|
|
14
13
|
const [expandedRowKeys, setExpandedRowKeys] = useState([])
|
|
15
|
-
{{/if}}
|
|
14
|
+
{{/if}}
|
|
15
|
+
const [modal, setModal] = useState({ visible:false, title:'', formItems:modalItems })
|