@yorha2b-lab/autodev 2.2.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/autodev.js +6 -2
- package/config.js +1 -0
- package/package.json +4 -1
- package/src/commands/handlers/api-handler.js +30 -14
- package/src/commands/handlers/page-handler.js +4 -2
- package/src/commands/handlers/part-handler.js +4 -2
- package/src/commands/watch.js +11 -33
- package/src/core/context.js +51 -0
- package/src/core/react-compiler.js +3 -3
- package/src/prompts/react/watch-api.js +1 -1
- package/src/services/llm.js +1 -1
- package/src/services/proxy-tower.js +85 -0
- package/src/utils/utils.js +32 -8
- package/templates/react/components/index.js +4 -2
- package/templates/react/handlebars/handleBlock.hbs +0 -4
- package/templates/react/handlebars/hookBlock.hbs +0 -6
- package/templates/react/handlebars/stateBlock.hbs +2 -1
package/bin/autodev.js
CHANGED
|
@@ -4,6 +4,7 @@ const path = require('path')
|
|
|
4
4
|
const chalk = require('chalk')
|
|
5
5
|
const { program } = require('commander')
|
|
6
6
|
const pkg = require(path.join(__dirname, '../package.json'))
|
|
7
|
+
const bunker = require(path.join(__dirname, '../src/core/context.js'))
|
|
7
8
|
const { language, matrixEffect, bootSequence } = require(path.join(__dirname, '../src/utils/utils.js'))
|
|
8
9
|
|
|
9
10
|
program
|
|
@@ -52,8 +53,11 @@ program
|
|
|
52
53
|
.description(language('开启全频道联动监控:支持 Page/Part/API 协同构筑', 'Start full-channel linked monitoring: Coordinated Page/Part/API construction'))
|
|
53
54
|
.action(() => {
|
|
54
55
|
bootSequence(pkg.version)
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
bunker.init(program.opts())
|
|
57
|
+
require(path.join(__dirname, '../src/commands/watch'))()
|
|
58
|
+
if (bunker.get().config.proxyTarget) {
|
|
59
|
+
require(path.join(__dirname, '../src/services/proxy-tower'))()
|
|
60
|
+
}
|
|
57
61
|
process.on('SIGINT', () => {
|
|
58
62
|
console.log('\n')
|
|
59
63
|
console.log(chalk.gray('--------------------------------------------------'))
|
package/config.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yorha2b-lab/autodev",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "基于视觉大模型的前端(react+Antd)全自动 CRUD 代码生成器",
|
|
5
5
|
"bin": {
|
|
6
6
|
"autodev": "bin/autodev.js"
|
|
@@ -46,5 +46,8 @@
|
|
|
46
46
|
"openai": "^6.24.0",
|
|
47
47
|
"ora": "^5.4.1",
|
|
48
48
|
"sharp": "^0.34.5"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"http-proxy": "^1.18.1"
|
|
49
52
|
}
|
|
50
53
|
}
|
|
@@ -3,12 +3,13 @@ const ora = require('ora')
|
|
|
3
3
|
const path = require('path')
|
|
4
4
|
const chalk = require('chalk')
|
|
5
5
|
|
|
6
|
-
module.exports = async (filePath,
|
|
6
|
+
module.exports = async (filePath, liveResponse = null) => {
|
|
7
7
|
|
|
8
|
-
const {
|
|
8
|
+
const { get } = require(path.join(__dirname, '../../core/context'))
|
|
9
|
+
const { config, options, language, unwrapSignal, alignResponseFields } = get()
|
|
9
10
|
|
|
10
11
|
const startTime = Date.now()
|
|
11
|
-
const fileName = path.basename(filePath, path.extname(filePath))
|
|
12
|
+
const fileName = liveResponse?.fileName ?? path.basename(filePath, path.extname(filePath))
|
|
12
13
|
const resourcePath = path.join(process.cwd(), config.pagesDir, fileName, 'resource.js')
|
|
13
14
|
|
|
14
15
|
const spinner = ora({
|
|
@@ -27,28 +28,26 @@ module.exports = async (filePath, context) => {
|
|
|
27
28
|
))
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
let responseStr = responseRaw
|
|
31
|
+
let rawJson = liveResponse ? liveResponse.data : JSON.parse(fs.readFileSync(filePath, 'utf8'))
|
|
32
32
|
let resourceStr = fs.readFileSync(resourcePath, 'utf8')
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
responseStr = JSON.stringify(normalizedData.slice(0, 1))
|
|
38
|
-
} catch (e) {
|
|
34
|
+
const coreArray = unwrapSignal(rawJson)
|
|
35
|
+
|
|
36
|
+
if (!coreArray || coreArray.length === 0) {
|
|
39
37
|
console.log(chalk.gray(language(
|
|
40
38
|
` [System] 数据格式非标准 JSON,将尝试原始字符对齐。`,
|
|
41
39
|
` [System] Data format is not standard JSON, will try to align by character.`)
|
|
42
40
|
))
|
|
43
41
|
}
|
|
44
42
|
|
|
43
|
+
const sampleData = coreArray && coreArray.length > 0 ? [coreArray[0]] : rawJson
|
|
44
|
+
|
|
45
45
|
spinner.text = chalk.yellow(language(
|
|
46
46
|
`🧑💻 9S: 正在扫描前后端字段差异... 执行语义桥接任务。`,
|
|
47
47
|
`🧑💻 9S: Scanning field differences... Bridging semantic gaps.`
|
|
48
48
|
))
|
|
49
49
|
|
|
50
|
-
const result = await alignResponseFields(options,
|
|
51
|
-
|
|
50
|
+
const result = await alignResponseFields(options, JSON.stringify(sampleData), resourceStr)
|
|
52
51
|
let changeCount = 0
|
|
53
52
|
const resultMapping = {}
|
|
54
53
|
Object.entries(result).forEach(([oldField, newField]) => {
|
|
@@ -63,10 +62,27 @@ module.exports = async (filePath, context) => {
|
|
|
63
62
|
const endTime = Date.now()
|
|
64
63
|
|
|
65
64
|
spinner.succeed(chalk.green(language(
|
|
66
|
-
`🤖 Pod 153: [肯定]
|
|
67
|
-
`🤖 Pod 153: [Affirmative]
|
|
65
|
+
`🤖 Pod 153: [肯定] 语义桥接协议执行完毕。已物理修正 ${changeCount} 处字段偏差。耗时: ${(endTime - startTime) / 1000}s`,
|
|
66
|
+
`🤖 Pod 153: [Affirmative] Semantic bridge protocol complete. Corrected ${changeCount} field deviations. Elapsed: ${(endTime - startTime) / 1000}s`
|
|
68
67
|
)))
|
|
69
68
|
|
|
69
|
+
if (changeCount > 0) {
|
|
70
|
+
console.log(chalk.gray(`\n┌────────────────── [ 9S 语义映射表 ] ──────────────────┐`))
|
|
71
|
+
Object.entries(resultMapping).forEach(([oldField, newField]) => {
|
|
72
|
+
// 💡 物理校准:计算空格数量,让箭头对齐在第 15 个字符位
|
|
73
|
+
// 中文字符长度 * 2 是为了抵消它在终端占的双倍宽度
|
|
74
|
+
const padding = " ".repeat(Math.max(1, 15 - oldField.length * 2))
|
|
75
|
+
console.log(
|
|
76
|
+
chalk.gray(` │ `) +
|
|
77
|
+
chalk.yellow(oldField) +
|
|
78
|
+
padding +
|
|
79
|
+
chalk.cyan(` -> `) +
|
|
80
|
+
chalk.white(newField)
|
|
81
|
+
)
|
|
82
|
+
})
|
|
83
|
+
console.log(chalk.gray(`└───────────────────────────────────────────────────────┘\n`))
|
|
84
|
+
}
|
|
85
|
+
|
|
70
86
|
if (fs.existsSync(filePath)) {
|
|
71
87
|
fs.unlinkSync(filePath)
|
|
72
88
|
}
|
|
@@ -4,13 +4,15 @@ const path = require('path')
|
|
|
4
4
|
const chalk = require('chalk')
|
|
5
5
|
const stringify = require('json-stringify-pretty-compact')
|
|
6
6
|
|
|
7
|
-
module.exports = async
|
|
7
|
+
module.exports = async filePath => {
|
|
8
|
+
|
|
9
|
+
const { get } = require(path.join(__dirname, '../../core/context'))
|
|
8
10
|
|
|
9
11
|
const {
|
|
10
12
|
config, language, menus,
|
|
11
13
|
pagePrompt, resourceTpl, indexTpl,
|
|
12
14
|
recognizePage, generateMock, resource, index
|
|
13
|
-
} =
|
|
15
|
+
} = get()
|
|
14
16
|
|
|
15
17
|
const startTime = Date.now()
|
|
16
18
|
const fileName = path.basename(filePath, path.extname(filePath))
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
2
|
const ora = require('ora')
|
|
3
|
+
const path = require('path')
|
|
3
4
|
const chalk = require('chalk')
|
|
4
5
|
const stringify = require('json-stringify-pretty-compact')
|
|
5
6
|
|
|
6
|
-
module.exports = async
|
|
7
|
+
module.exports = async filePath => {
|
|
7
8
|
|
|
8
|
-
const {
|
|
9
|
+
const { get } = require(path.join(__dirname, '../../core/context'))
|
|
10
|
+
const { language, partPrompt, recognizePage } = get()
|
|
9
11
|
|
|
10
12
|
const startTime = Date.now()
|
|
11
13
|
|
package/src/commands/watch.js
CHANGED
|
@@ -2,19 +2,15 @@ const fs = require('fs')
|
|
|
2
2
|
const path = require('path')
|
|
3
3
|
const chalk = require('chalk')
|
|
4
4
|
const chokidar = require('chokidar')
|
|
5
|
-
const Handlebars = require('handlebars')
|
|
6
5
|
const stringify = require('json-stringify-pretty-compact')
|
|
7
6
|
|
|
8
|
-
const {
|
|
9
|
-
const {
|
|
10
|
-
const {
|
|
7
|
+
const { get } = require(path.join(__dirname, '../core/context'))
|
|
8
|
+
const { copyTemplateDir } = require(path.join(__dirname, '../utils/utils.js'))
|
|
9
|
+
const { createTaskQueue } = require(path.join(__dirname, '../core/task-queue.js'))
|
|
11
10
|
|
|
12
|
-
const watch =
|
|
11
|
+
const watch = () => {
|
|
13
12
|
|
|
14
|
-
const config =
|
|
15
|
-
const template = options.template
|
|
16
|
-
const queue = createTaskQueue(2)
|
|
17
|
-
const menus = getExistingMenus()
|
|
13
|
+
const { menus, config, options, template, language, apiHandler, pageHandler, partHandler } = get()
|
|
18
14
|
|
|
19
15
|
const compilerPath = path.join(__dirname, `../core/${template}-compiler.js`)
|
|
20
16
|
if (!fs.existsSync(compilerPath)) {
|
|
@@ -22,8 +18,6 @@ const watch = options => {
|
|
|
22
18
|
return
|
|
23
19
|
}
|
|
24
20
|
|
|
25
|
-
const { index, resource } = require(compilerPath)
|
|
26
|
-
|
|
27
21
|
try {
|
|
28
22
|
if (config.hbsDir === '') {
|
|
29
23
|
copyTemplateDir(options, 'hooks', config.hooksDir)
|
|
@@ -34,23 +28,7 @@ const watch = options => {
|
|
|
34
28
|
console.error(language('❌ 模板构筑失败:', '❌ Template construction failed:'), error)
|
|
35
29
|
}
|
|
36
30
|
|
|
37
|
-
const
|
|
38
|
-
const indexTpl = Handlebars.compile(fs.readFileSync(path.join(tplDir, 'index.hbs'), 'utf-8'))
|
|
39
|
-
const resourceTpl = Handlebars.compile(fs.readFileSync(path.join(tplDir, 'resource.hbs'), 'utf-8'))
|
|
40
|
-
|
|
41
|
-
const context = {
|
|
42
|
-
menus, language,
|
|
43
|
-
config, options,
|
|
44
|
-
resource, index,
|
|
45
|
-
resourceTpl, indexTpl,
|
|
46
|
-
recognizePage, generateMock, alignResponseFields,
|
|
47
|
-
pagePrompt: require(path.join(__dirname, `../prompts/${template}/watch-page.js`)),
|
|
48
|
-
partPrompt: require(path.join(__dirname, `../prompts/${template}/watch-part.js`))
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const apiHandler = require('./handlers/api-handler')
|
|
52
|
-
const pageHandler = require('./handlers/page-handler')
|
|
53
|
-
const partHandler = require('./handlers/part-handler')
|
|
31
|
+
const queue = createTaskQueue(2)
|
|
54
32
|
|
|
55
33
|
queue.onIdle(() => {
|
|
56
34
|
console.log(chalk.green(language(
|
|
@@ -73,8 +51,8 @@ const watch = options => {
|
|
|
73
51
|
})
|
|
74
52
|
|
|
75
53
|
console.log(chalk.magenta(language(
|
|
76
|
-
'📡 Operator 6O: 呼叫 2B
|
|
77
|
-
'📡 Operator 6O: Calling 2B, all-channel linked monitoring is ready
|
|
54
|
+
'📡 Operator 6O: 呼叫 2B,地堡全频道联动监控已就绪!',
|
|
55
|
+
'📡 Operator 6O: Calling 2B, all-channel linked monitoring is ready!'
|
|
78
56
|
)))
|
|
79
57
|
|
|
80
58
|
watcher.on('add', filePath => {
|
|
@@ -82,13 +60,13 @@ const watch = options => {
|
|
|
82
60
|
const absolutePath = path.resolve(filePath)
|
|
83
61
|
|
|
84
62
|
if (absolutePath.includes('screenShot')) {
|
|
85
|
-
queue.add(() => pageHandler(filePath
|
|
63
|
+
queue.add(() => pageHandler(filePath))
|
|
86
64
|
}
|
|
87
65
|
else if (absolutePath.includes('screenPart')) {
|
|
88
|
-
queue.add(() => partHandler(filePath
|
|
66
|
+
queue.add(() => partHandler(filePath))
|
|
89
67
|
}
|
|
90
68
|
else if (absolutePath.includes('response')) {
|
|
91
|
-
queue.add(() => apiHandler(filePath
|
|
69
|
+
queue.add(() => apiHandler(filePath))
|
|
92
70
|
}
|
|
93
71
|
})
|
|
94
72
|
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const Handlebars = require('handlebars')
|
|
4
|
+
const apiHandler = require(path.join(__dirname, '../commands/handlers/api-handler'))
|
|
5
|
+
const pageHandler = require(path.join(__dirname, '../commands/handlers/page-handler'))
|
|
6
|
+
const partHandler = require(path.join(__dirname, '../commands/handlers/part-handler'))
|
|
7
|
+
const { recognizePage, generateMock, alignResponseFields } = require(path.join(__dirname, '../services/llm'))
|
|
8
|
+
const { language, getConfig, unwrapSignal, getExistingMenus } = require(path.join(__dirname, '../utils/utils'))
|
|
9
|
+
|
|
10
|
+
let instance = null // 💡 物理封存的全局实例
|
|
11
|
+
|
|
12
|
+
module.exports = {
|
|
13
|
+
/**
|
|
14
|
+
* @description 执行中枢神经元初始化
|
|
15
|
+
*/
|
|
16
|
+
init: options => {
|
|
17
|
+
|
|
18
|
+
const config = getConfig()
|
|
19
|
+
const menus = getExistingMenus()
|
|
20
|
+
const template = options.template
|
|
21
|
+
|
|
22
|
+
const compilerPath = path.join(__dirname, `./${template}-compiler.js`)
|
|
23
|
+
const { index, resource } = require(compilerPath)
|
|
24
|
+
|
|
25
|
+
const tplDir = config.hbsDir !== ''
|
|
26
|
+
? path.join(process.cwd(), config.hbsDir)
|
|
27
|
+
: path.join(__dirname, `../../templates/${template}`)
|
|
28
|
+
|
|
29
|
+
instance = {
|
|
30
|
+
menus, config, options, language, unwrapSignal,
|
|
31
|
+
resource, index, template,
|
|
32
|
+
apiHandler, pageHandler, partHandler,
|
|
33
|
+
recognizePage, generateMock, alignResponseFields,
|
|
34
|
+
indexTpl: Handlebars.compile(fs.readFileSync(path.join(tplDir, 'index.hbs'), 'utf-8')),
|
|
35
|
+
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
|
+
}
|
|
39
|
+
return instance
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @description 全频道信号接入:获取全局上下文
|
|
44
|
+
*/
|
|
45
|
+
get: () => {
|
|
46
|
+
if (!instance) {
|
|
47
|
+
throw new Error(language('🤖 Pod 042 报警:中枢神经元尚未初始化,无法建立信号连接。', '🤖 Pod 042 Warnning:Central neuron not initialized, cannot establish signal connection.'))
|
|
48
|
+
}
|
|
49
|
+
return instance
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -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('../utils/utils.js')
|
|
5
|
+
const { cleanCode, generateSmartImports } = require(path.join(__dirname, '../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 })) : '[]')
|
|
@@ -68,7 +68,7 @@ const index = ({ config, fileName, indexTpl, pageConfig }) => {
|
|
|
68
68
|
hasPagination: pageConfig.table.pagination,
|
|
69
69
|
operations: pageConfig.table.operation || [],
|
|
70
70
|
hasRowSelection: pageConfig.table.rowSelection,
|
|
71
|
-
formItems: hasTabs ? 'formItems[activeKey]' : 'formItems',
|
|
71
|
+
formItems: hasFormItems ? (hasTabs ? 'formItems[activeKey]' : 'formItems') : '[]',
|
|
72
72
|
pageStruct: hasFunctionButtons ? pageConfig.pageStruct : pageConfig.pageStruct?.filter(item => item !== 'FunctionButtonsBlock'),
|
|
73
73
|
}
|
|
74
74
|
|
|
@@ -94,7 +94,7 @@ const index = ({ config, fileName, indexTpl, pageConfig }) => {
|
|
|
94
94
|
Handlebars.registerPartial('handleBlock', `${fs.readFileSync(path.join(__dirname, '../../templates/react/handlebars/handleBlock.hbs'), 'utf-8')}\n`)
|
|
95
95
|
|
|
96
96
|
const bodyCode = indexTpl(viewData)
|
|
97
|
-
const importsStr = generateSmartImports(bodyCode, hasTabs)
|
|
97
|
+
const importsStr = generateSmartImports({ bodyCode, hasTabs, hasFormItems })
|
|
98
98
|
return cleanCode(`${importsStr}\n\n${bodyCode}`)
|
|
99
99
|
}
|
|
100
100
|
|
|
@@ -5,7 +5,7 @@ resourceStr是前端目前猜测的字段名(请从resourceStr中提取dataIndex
|
|
|
5
5
|
responseStr是后端的真实响应。
|
|
6
6
|
请比对两者,找出现有前端字段名应该被替换为哪个真实的后端字段名。
|
|
7
7
|
匹配规则: 1.完全相同 2.下划线/驼峰转换 3.语义相似。
|
|
8
|
-
请只输出一个JSON对象, Key
|
|
8
|
+
请只输出一个JSON对象, Key为前端猜测的字段名dataIndex, Value为Response里的真实字段名。
|
|
9
9
|
例如:{"key_1": "key1"}
|
|
10
10
|
如果没有找到对应的,请不要包含在结果中。
|
|
11
11
|
注意:⚠️⚠️如果有语义相近的两个字段优先取中文字段,比如"createByName"和"createBy",则取"createByName"
|
package/src/services/llm.js
CHANGED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
const http = require('http')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const zlib = require('zlib')
|
|
4
|
+
const chalk = require('chalk')
|
|
5
|
+
const httpProxy = require('http-proxy')
|
|
6
|
+
const { get } = require(path.join(__dirname, '../core/context'))
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @function startProxyTower
|
|
10
|
+
* @description [地堡 42153 联合波段] 启动流量拦截塔。
|
|
11
|
+
* 独立运行于后台,监听 42153 端口,物理截获联调过程中的 JSON 信号并触发语义对齐。
|
|
12
|
+
*/
|
|
13
|
+
module.exports = () => {
|
|
14
|
+
|
|
15
|
+
const { config, language, apiHandler, unwrapSignal } = get()
|
|
16
|
+
|
|
17
|
+
const TOWER_PORT = 42153
|
|
18
|
+
const hackedRegistry = new Map()
|
|
19
|
+
const proxy = httpProxy.createProxyServer({})
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
// 简单的辅助函数:提取 JSON 的所有 Key 作为“指纹”
|
|
23
|
+
const getJsonFingerprint = obj => {
|
|
24
|
+
if (!obj || typeof obj !== 'object') return ''
|
|
25
|
+
return Object.keys(Array.isArray(obj) ? (obj[0] || {}) : obj).sort().join(',')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const server = http.createServer((req, res) => {
|
|
29
|
+
|
|
30
|
+
const target = config.proxyTarget
|
|
31
|
+
|
|
32
|
+
if (!target) {
|
|
33
|
+
// 静默模式:如果不配置 proxyTarget,拦截塔只转发不处理(或报错提示)
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 💡 物理监控:劫持响应流
|
|
38
|
+
proxy.on('proxyRes', function (proxyRes, req, res) {
|
|
39
|
+
let body = []
|
|
40
|
+
proxyRes.on('data', chunk => body.push(chunk))
|
|
41
|
+
proxyRes.on('end', async () => {
|
|
42
|
+
const buffer = Buffer.concat(body)
|
|
43
|
+
const encoding = proxyRes.headers['content-encoding']
|
|
44
|
+
try {
|
|
45
|
+
const rawBody = encoding === 'gzip' ? zlib.gunzipSync(buffer) : buffer
|
|
46
|
+
const json = JSON.parse(rawBody.toString())
|
|
47
|
+
const referer = req.headers.referer || ''
|
|
48
|
+
const fileName = referer.split('?')[0].split('/').filter(Boolean).at(-1)
|
|
49
|
+
|
|
50
|
+
const fingerprint = getJsonFingerprint(unwrapSignal(json)) // 获取数据指纹
|
|
51
|
+
const lastFingerprint = hackedRegistry.get(fileName)
|
|
52
|
+
|
|
53
|
+
// 如果指纹没变,说明字段结构是一样的,无需再次骇入
|
|
54
|
+
if (lastFingerprint === fingerprint) {
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
console.log(chalk.cyan(language(
|
|
59
|
+
`\n📡 Pod 153: 截获运行时信号 [${fileName}]。执行自动对齐协议...`,
|
|
60
|
+
`\n📡 Pod 153: Captured runtime signal [${fileName}]. Executing semantic alignment protocol...`
|
|
61
|
+
)))
|
|
62
|
+
// 💡 直接调用 api-handler 物理更新 resource.js
|
|
63
|
+
await apiHandler(null, { fileName, data: json })
|
|
64
|
+
hackedRegistry.set(fileName, fingerprint)
|
|
65
|
+
} catch (e) {
|
|
66
|
+
console.log(e)
|
|
67
|
+
// 非 JSON 信号,保持静默
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
// 转发至真实后端
|
|
72
|
+
proxy.web(req, res, { target, changeOrigin: true })
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
server.listen(TOWER_PORT, () => {
|
|
76
|
+
console.log(chalk.magenta(language(
|
|
77
|
+
`✨ YoRHa 联合基站:信号拦截塔已在线 [波段: ${TOWER_PORT}]`,
|
|
78
|
+
`✨ YoRHa Joint Station: Signal Intercept Tower Online [Band: ${TOWER_PORT}]`
|
|
79
|
+
)))
|
|
80
|
+
console.log(chalk.gray(language(
|
|
81
|
+
`💡 指令:请将您的代理目标指向 http://localhost:${TOWER_PORT}`,
|
|
82
|
+
`💡 Command: Please point your local proxy target to http://localhost:${TOWER_PORT}`
|
|
83
|
+
)))
|
|
84
|
+
})
|
|
85
|
+
}
|
package/src/utils/utils.js
CHANGED
|
@@ -56,6 +56,30 @@ const cleanCode = str => {
|
|
|
56
56
|
.trim() + '\n'
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* @function unwrapSignal
|
|
61
|
+
* @description [地堡数据脱水机] 物理扫描 JSON 结构,剥离业务外壳(code/msg/total 等)。
|
|
62
|
+
* 目标:精准定位到核心的 Array。
|
|
63
|
+
*/
|
|
64
|
+
const unwrapSignal = (json) => {
|
|
65
|
+
if (Array.isArray(json)) return json
|
|
66
|
+
|
|
67
|
+
// 💡 扫描常见的数据仓库 Key
|
|
68
|
+
const dataKeys = ['data', 'list', 'items', 'datas', 'rows', 'result', 'payload', 'results', 'dataList']
|
|
69
|
+
for (const key of dataKeys) {
|
|
70
|
+
if (json[key] && Array.isArray(json[key])) return json[key]
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 💡 递归侦察:处理 data: { list: [...] } 这种二级套娃
|
|
74
|
+
for (const key in json) {
|
|
75
|
+
if (json[key] && typeof json[key] === 'object') {
|
|
76
|
+
const nested = unwrapSignal(json[key])
|
|
77
|
+
if (Array.isArray(nested)) return nested
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return null
|
|
81
|
+
}
|
|
82
|
+
|
|
59
83
|
/**
|
|
60
84
|
* 引导序列
|
|
61
85
|
* 模拟系统引导过程中的动画效果
|
|
@@ -174,25 +198,25 @@ const getExistingMenus = (dir = 'src/pages') => {
|
|
|
174
198
|
* @param {boolean} hasTabs - 是否包含标签页
|
|
175
199
|
* @returns {string} 拼接后的 import 语句
|
|
176
200
|
*/
|
|
177
|
-
const generateSmartImports = (
|
|
201
|
+
const generateSmartImports = ({ bodyCode, hasTabs, hasFormItems }) => {
|
|
178
202
|
const hooksLib = ['useTableQuery']
|
|
179
203
|
const reactLib = ['useState', 'useEffect', 'useRef', 'useMemo']
|
|
180
204
|
const componentsLib = ['MyTable', 'MyModalForm', 'MySearchForm']
|
|
181
205
|
const antdLib = ['Card', 'Space', 'Modal', 'Button', 'Alert', 'Table', 'Input', 'Select']
|
|
182
206
|
|
|
183
|
-
const usedAntd = antdLib.filter(name => new RegExp(`\\b${name}\\b`).test(
|
|
184
|
-
const usedHooks = hooksLib.filter(name => new RegExp(`\\b${name}\\b`).test(
|
|
185
|
-
const usedReact = reactLib.filter(name => new RegExp(`\\b${name}\\b`).test(
|
|
186
|
-
const usedComps = componentsLib.filter(name => new RegExp(`\\b${name}\\b`).test(
|
|
207
|
+
const usedAntd = antdLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
|
|
208
|
+
const usedHooks = hooksLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
|
|
209
|
+
const usedReact = reactLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
|
|
210
|
+
const usedComps = componentsLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
|
|
187
211
|
|
|
188
212
|
const imports = [
|
|
189
213
|
usedReact.length && `import { ${usedReact.join(', ')} } from 'react'`,
|
|
190
214
|
`import { request } from '../../utils/request'`,
|
|
191
215
|
`import { formatQuery } from '../../utils/utils'`,
|
|
192
|
-
`import { ${
|
|
216
|
+
usedAntd.length && `import { Form, ${usedAntd.join(', ')} } from 'antd'`,
|
|
193
217
|
...usedHooks.map(hook => `import { ${hook} } from '../../hooks/${hook}'`),
|
|
194
218
|
...usedComps.map(comp => `import { ${comp} } from '../../components/${comp}'`),
|
|
195
|
-
|
|
219
|
+
`import { ${hasTabs ? 'tabs, ' : ''}${hasFormItems ? 'formItems, ' : ''}modalItems, tableColumns} from './resource'`,
|
|
196
220
|
].sort((a, b) => a.length - b.length)
|
|
197
221
|
|
|
198
222
|
return imports.filter(Boolean).join('\n')
|
|
@@ -218,4 +242,4 @@ const copyTemplateDir = (options, templateSubDir, targetSubDir) => {
|
|
|
218
242
|
})
|
|
219
243
|
}
|
|
220
244
|
|
|
221
|
-
module.exports = { language, getConfig, cleanCode, matrixEffect, bootSequence, getExistingMenus, copyTemplateDir, generateSmartImports }
|
|
245
|
+
module.exports = { language, getConfig, cleanCode, unwrapSignal, matrixEffect, bootSequence, getExistingMenus, copyTemplateDir, generateSmartImports }
|
|
@@ -40,6 +40,7 @@ const AliyunOSSUpload = ({ value, onChange, ...restProps }) => {
|
|
|
40
40
|
try {
|
|
41
41
|
const result = await initOSS(url, options)
|
|
42
42
|
setOSSData(result)
|
|
43
|
+
return result
|
|
43
44
|
} catch (err) {
|
|
44
45
|
console.log(err)
|
|
45
46
|
}
|
|
@@ -79,12 +80,13 @@ const AliyunOSSUpload = ({ value, onChange, ...restProps }) => {
|
|
|
79
80
|
* 若令牌已失效(过期),则强行拦截并执行同步刷新协议,同时执行物理路径归一化。
|
|
80
81
|
*/
|
|
81
82
|
const beforeUpload = async file => {
|
|
83
|
+
let currentOSS = OSSData
|
|
82
84
|
const expire = Number(OSSData.expire) * 1000
|
|
83
85
|
if (expire < Date.now()) {
|
|
84
|
-
await init()
|
|
86
|
+
currentOSS = await init()
|
|
85
87
|
}
|
|
86
88
|
// 💡 物理路径重组:通过正则清除多余的路径分隔符,确保存储节点坐标唯一
|
|
87
|
-
file.url = `${
|
|
89
|
+
file.url = `${currentOSS?.dir ?? path}/${file.uid}_${file.name}`.replace(/\/\//g, '/')
|
|
88
90
|
return file
|
|
89
91
|
}
|
|
90
92
|
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
{{!--
|
|
2
|
-
📡 [地堡核心驱动引擎]:逻辑链路全自动构筑
|
|
3
|
-
1. 执行“传感器对齐”:从 URL 中提取历史信号,实现状态持久化
|
|
4
|
-
2. 激活“useTableQuery”:物理封存分页、搜索、及动态列加载逻辑
|
|
5
|
-
--}}
|
|
6
|
-
|
|
7
1
|
const initParams = {{#if hasTabs}}{ type: tabs[0].key }{{else}}{}{{/if}}
|
|
8
2
|
|
|
9
3
|
const query = formatQuery(Object.fromEntries(new URLSearchParams(location.search).entries()), {{formItems}})
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
|
|
2
|
-
{{!-- 📡 [地堡状态模组] 物理信号初始化:驱动弹窗、页签及数据勾选等核心交互单元 --}}
|
|
3
2
|
const rowKey = 'id'
|
|
3
|
+
{{#if hasFormItems}}
|
|
4
4
|
const [form] = Form.useForm()
|
|
5
|
+
{{/if}}
|
|
5
6
|
const [modal, setModal] = useState({ visible:false, title:'', formItems:modalItems })
|
|
6
7
|
{{#if hasRowSelection}}
|
|
7
8
|
const [selectedRows, setSelectedRows] = useState([])
|