@yorha2b-lab/autodev 1.1.0 → 2.0.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.
@@ -0,0 +1,80 @@
1
+ const fs = require('fs')
2
+ const ora = require('ora')
3
+ const path = require('path')
4
+ const chalk = require('chalk')
5
+
6
+ module.exports = async (filePath, context) => {
7
+
8
+ const { config, options, language, alignResponseFields } = context
9
+
10
+ const startTime = Date.now()
11
+ const fileName = path.basename(filePath, path.extname(filePath))
12
+ const resourcePath = path.join(process.cwd(), config.pagesDir, fileName, 'resource.js')
13
+
14
+ const spinner = ora({
15
+ text: chalk.yellow(language(
16
+ `🤖 Pod 153: [侦察] 发现加密数据源 [${fileName}]。正在尝试执行语义对齐协议...`,
17
+ `🤖 Pod 153: [Recon] Encrypted data source [${fileName}] detected. Initiating semantic alignment...`
18
+ )),
19
+ color: 'yellow'
20
+ }).start()
21
+
22
+ try {
23
+ if (!fs.existsSync(resourcePath)) {
24
+ throw new Error(language(
25
+ `地堡数据库中未检索到模块 [${fileName}] 的资源文件。请先执行视觉构筑。`,
26
+ `Module [${fileName}] resource not found in Bunker database. Please execute visual construction first.`
27
+ ))
28
+ }
29
+
30
+ const responseRaw = fs.readFileSync(filePath, 'utf8')
31
+ let responseStr = responseRaw
32
+ let resourceStr = fs.readFileSync(resourcePath, 'utf8')
33
+
34
+ try {
35
+ const parsed = JSON.parse(responseRaw)
36
+ const normalizedData = Array.isArray(parsed) ? parsed : [parsed]
37
+ responseStr = JSON.stringify(normalizedData.slice(0, 1))
38
+ } catch (e) {
39
+ console.log(chalk.gray(language(
40
+ ` [System] 数据格式非标准 JSON,将尝试原始字符对齐。`,
41
+ ` [System] Data format is not standard JSON, will try to align by character.`)
42
+ ))
43
+ }
44
+
45
+ spinner.text = chalk.yellow(language(
46
+ `🧑‍💻 9S: 正在扫描前后端字段差异... 执行语义桥接任务。`,
47
+ `🧑‍💻 9S: Scanning field differences... Bridging semantic gaps.`
48
+ ))
49
+
50
+ const result = await alignResponseFields(options, responseStr, resourceStr)
51
+
52
+ let changeCount = 0
53
+ const resultMapping = {}
54
+ Object.entries(result).forEach(([oldField, newField]) => {
55
+ if (oldField === newField) return
56
+ const regex = new RegExp(`(dataIndex|name)\\s*:\\s*['"]${oldField}['"]`, 'g')
57
+ resourceStr = resourceStr.replace(regex, `$1: '${newField}'`)
58
+ changeCount++
59
+ resultMapping[oldField] = newField
60
+ })
61
+
62
+ fs.writeFileSync(resourcePath, resourceStr)
63
+ const endTime = Date.now()
64
+
65
+ spinner.succeed(chalk.green(language(
66
+ `🤖 Pod 153: [肯定] 结果映射完成${JSON.stringify(resultMapping)}。已更正 ${changeCount} 处语义偏差。耗时: ${(endTime - startTime) / 1000}s`,
67
+ `🤖 Pod 153: [Affirmative] Result mapping completed${JSON.stringify(resultMapping)}. Corrected ${changeCount} semantic deviations. Elapsed: ${(endTime - startTime) / 1000}s`
68
+ )))
69
+
70
+ if (fs.existsSync(filePath)) {
71
+ fs.unlinkSync(filePath)
72
+ }
73
+
74
+ } catch (error) {
75
+ spinner.fail(chalk.red(language(
76
+ `🤖 Pod 153: [严重警告] 信号对齐失败。接口数据遭遇强力防火墙:${error}`,
77
+ `🤖 Pod 153: [CRITICAL] Alignment failed. Encountered powerful API firewall: ${error}`
78
+ )))
79
+ }
80
+ }
@@ -0,0 +1,92 @@
1
+ const fs = require('fs')
2
+ const ora = require('ora')
3
+ const path = require('path')
4
+ const chalk = require('chalk')
5
+ const stringify = require('json-stringify-pretty-compact')
6
+
7
+ module.exports = async (filePath, context) => {
8
+
9
+ const {
10
+ config, language, menus,
11
+ pagePrompt, resourceTpl, indexTpl,
12
+ recognizePage, generateMock, resource, index
13
+ } = context
14
+
15
+ const startTime = Date.now()
16
+ const fileName = path.basename(filePath, path.extname(filePath))
17
+ const mockDir = path.join(process.cwd(), 'mock')
18
+ const targetDir = path.join(process.cwd(), config.pagesDir, fileName)
19
+
20
+ const spinner = ora({
21
+ text: chalk.cyan(language(
22
+ `[System] 视觉传感器捕捉完成: [${fileName}]。正在调配地堡资源进行物理构筑...`,
23
+ `[System] Visual sensor captured: [${fileName}]. Allocating Bunker resources for physical construction...`
24
+ )),
25
+ color: 'cyan'
26
+ }).start()
27
+
28
+ try {
29
+
30
+ if (fs.existsSync(targetDir)) {
31
+ spinner.warn(chalk.yellow(language(
32
+ `🤖 Pod 042: [拦截] 模块 [${fileName}] 已存在于地堡数据库中。为了防止数据覆盖,构筑协议已跳过。`,
33
+ `🤖 Pod 042: [Intercept] Module [${fileName}] already exists. Construction skipped to prevent overwrite.`
34
+ )))
35
+ return
36
+ }
37
+
38
+ if (!fs.existsSync(mockDir)) fs.mkdirSync(mockDir, { recursive: true })
39
+ if (!fs.existsSync(targetDir)) fs.mkdirSync(targetDir, { recursive: true })
40
+
41
+ let pageConfig
42
+ if (config.useDemo) {
43
+ spinner.text = chalk.yellow(language(
44
+ `🤖 Pod 042: [警告] 拦截到实弹请求。正在空投标准模拟包: example.json`,
45
+ `🤖 Pod 042: [Warning] Real-fire request intercepted. Dropping simulation package: example.json`
46
+ ))
47
+ pageConfig = require(path.join(__dirname, '../../../example/example.json'))
48
+ } else {
49
+ spinner.text = chalk.cyan(language(
50
+ `🤖 Pod 042: 正在上传视觉元数据至司令部进行语义分析...`,
51
+ `🤖 Pod 042: Uploading visual metadata to Command for semantic analysis...`
52
+ ))
53
+ pageConfig = await recognizePage(pagePrompt, filePath)
54
+ }
55
+
56
+ fs.writeFileSync(path.join(targetDir, 'resource.js'), resource({ pageConfig, resourceTpl }))
57
+ fs.writeFileSync(path.join(targetDir, 'index.js'), index({ config, fileName, indexTpl, pageConfig }))
58
+
59
+ if (!menus.find(m => m.key === fileName)) {
60
+ menus.push({ label: fileName, key: fileName })
61
+ }
62
+
63
+ if (config.needMock) {
64
+ spinner.text = chalk.yellow(language(
65
+ `🧑‍💻 9S: 正在突破目标防火墙... 执行数据伪装程序 [${fileName}]`,
66
+ `🧑‍💻 9S: Piercing target firewall... Executing data camouflage [${fileName}]`
67
+ ))
68
+ const rawContent = await generateMock(pageConfig.table.columns, fileName)
69
+ fs.writeFileSync(path.join(mockDir, `${fileName}.js`), `export default ${stringify.default(rawContent, { indent: 4, maxLength: 200 })}`)
70
+ console.log(chalk.green(language(
71
+ `\n[Success] 9S 报告: Mock 数据生成成功`,
72
+ `\n[Success] 9S Report: Mock data generation successful.`
73
+ )))
74
+ }
75
+
76
+ const endTime = Date.now()
77
+ spinner.succeed(chalk.green(language(
78
+ `🤖 Pod 042: [报告] 构筑协议执行完毕。模块 [${fileName}] 物理装配达成。耗时: ${(endTime - startTime) / 1000}s`,
79
+ `🤖 Pod 042: [Report] Protocol complete. Physical assembly of [${fileName}] achieved. Elapsed: ${(endTime - startTime) / 1000}s`
80
+ )))
81
+
82
+ if (fs.existsSync(filePath)) {
83
+ fs.unlinkSync(filePath)
84
+ }
85
+
86
+ } catch (error) {
87
+ spinner.fail(chalk.red(language(
88
+ `🤖 Pod 042: [警告] 构筑中断。检测到逻辑病毒或连接超时:${error}`,
89
+ `🤖 Pod 042: [Warning] Construction aborted. Logic virus or timeout detected: ${error}`
90
+ )))
91
+ }
92
+ }
@@ -0,0 +1,76 @@
1
+ const fs = require('fs')
2
+ const ora = require('ora')
3
+ const chalk = require('chalk')
4
+ const stringify = require('json-stringify-pretty-compact')
5
+
6
+ module.exports = async (filePath, context) => {
7
+
8
+ const { language, partPrompt, recognizePage } = context
9
+
10
+ const startTime = Date.now()
11
+
12
+ const spinner = ora({
13
+ text: chalk.cyan(language(
14
+ `[System] 侦测到局部视觉样本。正在执行“碎片构筑协议”...`,
15
+ `[System] Partial visual sample detected. Executing "Fragment Construction Protocol"...`
16
+ )),
17
+ color: 'cyan'
18
+ }).start()
19
+
20
+ try {
21
+ spinner.text = chalk.cyan(language(
22
+ `🤖 Pod 042: 正在从神经云网络提取 UI 元数据...`,
23
+ `🤖 Pod 042: Extracting UI metadata from neural cloud network...`
24
+ ))
25
+
26
+ const pageConfig = await recognizePage(partPrompt, filePath)
27
+
28
+ const optionDict = pageConfig.optionDict || {}
29
+ delete pageConfig.optionDict
30
+
31
+ let mainConfigStr = stringify.default(pageConfig, { indent: 4, maxLength: 200 })
32
+ .replace(/"(\w+)":/g, '$1:') // 去掉 key 的双引号
33
+ .replace(/"/g, "'") // 全量替换为单引号
34
+ .replace(/['"]_CODE_([\s\S]*?)_CODE_['"]/g, '$1') // 还原代码片段
35
+ .replace(/_CODE_/g, '')
36
+
37
+ let optionsCodeStr = ''
38
+ Object.keys(optionDict).forEach(key => {
39
+ const varName = key.replaceAll('_CODE_', '')
40
+ const optionsArray = optionDict[key]
41
+ const arrayItemsStr = optionsArray.map(opt => ` { label: '${opt.label}', value: '${opt.value}' }`).join(',\n')
42
+ optionsCodeStr += `\nexport const ${varName} = [\n${arrayItemsStr}\n]\n`
43
+ })
44
+
45
+ const finalResult = `${mainConfigStr}\n${optionsCodeStr}`
46
+ const endTime = Date.now()
47
+
48
+ spinner.stop()
49
+
50
+ console.log(chalk.magenta(`\n┌────────────────── [ YoRHa Construction Output ] ──────────────────┐`))
51
+ console.log(chalk.gray(`│ Source: ${filePath}`))
52
+ console.log(chalk.gray(`│ Protocol: Partial UI Fragment | Status: SUCCESS`))
53
+ console.log(chalk.magenta(`├────────────────────────────────────────────────────────────────────┘`))
54
+ console.log(chalk.white(finalResult))
55
+ console.log(chalk.magenta(`┌────────────────────────────────────────────────────────────────────┐`))
56
+ console.log(chalk.green(language(
57
+ `│ 🤖 Pod 042 报告:局部构筑达成。耗时: ${(endTime - startTime) / 1000}s`,
58
+ `│ 🤖 Pod 042 Report: Partial construction achieved. Elapsed: ${(endTime - startTime) / 1000}s`
59
+ )))
60
+ console.log(chalk.green(language(
61
+ `│ 命令:请手动将上述代码块物理装配至您的目标文件中。`,
62
+ `│ Command: Please manually assemble the above code block into your target file.`
63
+ )))
64
+ console.log(chalk.magenta(`└────────────────────────────────────────────────────────────────────┘\n`))
65
+
66
+ if (fs.existsSync(filePath)) {
67
+ fs.unlinkSync(filePath)
68
+ }
69
+
70
+ } catch (error) {
71
+ spinner.fail(chalk.red(language(
72
+ `🤖 Pod 042: [警告] 碎片构筑失败。系统检测到逻辑干扰:${error}`,
73
+ `🤖 Pod 042: [Warning] Fragment construction failed. Logic interference detected: ${error}`
74
+ )))
75
+ }
76
+ }
@@ -0,0 +1,96 @@
1
+ const fs = require('fs')
2
+ const path = require('path')
3
+ const chalk = require('chalk')
4
+ const chokidar = require('chokidar')
5
+ const Handlebars = require('handlebars')
6
+ const stringify = require('json-stringify-pretty-compact')
7
+
8
+ const { createTaskQueue } = require('../core/task-queue')
9
+ const { recognizePage, generateMock, alignResponseFields } = require('../services/llm.js')
10
+ const { language, getConfig, getExistingMenus, copyTemplateDir } = require('../utils/utils.js')
11
+
12
+ const watch = options => {
13
+
14
+ const config = getConfig()
15
+ const template = options.template
16
+ const queue = createTaskQueue(2)
17
+ const menus = getExistingMenus()
18
+
19
+ const compilerPath = path.join(__dirname, `../core/${template}-compiler.js`)
20
+ if (!fs.existsSync(compilerPath)) {
21
+ console.error(chalk.red(language(`❌ 暂不支持 [${template}] 框架。`, `❌ [${template}] framework not supported.`)))
22
+ return
23
+ }
24
+
25
+ const { index, resource } = require(compilerPath)
26
+
27
+ try {
28
+ if (config.hbsDir === '') {
29
+ copyTemplateDir(options, 'hooks', config.hooksDir)
30
+ copyTemplateDir(options, 'utils', config.utilsDir)
31
+ copyTemplateDir(options, 'components', config.componentsDir)
32
+ }
33
+ } catch (error) {
34
+ console.error(language('❌ 模板构筑失败:', '❌ Template construction failed:'), error)
35
+ }
36
+
37
+ const tplDir = config.hbsDir !== '' ? path.join(process.cwd(), config.hbsDir) : path.join(__dirname, `../../templates/${template}`)
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')
54
+
55
+ queue.onIdle(() => {
56
+ console.log(chalk.green(language(
57
+ '\n🤖 Pod 042: [报告] 队列已空,正在向 Bunker 同步 Menu 路由配置...',
58
+ '\n🤖 Pod 042: [Report] Queue empty. Syncing Menu configuration to Bunker...'
59
+ )))
60
+ const utilsDir = path.join(process.cwd(), config.utilsDir)
61
+ if (!fs.existsSync(utilsDir)) fs.mkdirSync(utilsDir, { recursive: true })
62
+ fs.writeFileSync(path.join(utilsDir, 'menus.js'), `export const menus = ${stringify.default(menus, { indent: 4, maxLength: 50 })}`)
63
+ console.log(chalk.gray(language(
64
+ '\n🤖 [System] 未检测到运行中的任务,系统待机中...\n',
65
+ '\n🤖 [System] System is STANDBY.\n'
66
+ )))
67
+ })
68
+
69
+ const watcher = chokidar.watch(['./screenShot', './screenPart', './response'], {
70
+ persistent: true,
71
+ ignored: /(^|[\/\\])\../,
72
+ awaitWriteFinish: { stabilityThreshold: 500, pollInterval: 100 }
73
+ })
74
+
75
+ console.log(chalk.magenta(language(
76
+ '📡 Operator 6O: 呼叫 2B,地堡全频道联动监控已就绪!\n',
77
+ '📡 Operator 6O: Calling 2B, all-channel linked monitoring is ready!\n'
78
+ )))
79
+
80
+ watcher.on('add', filePath => {
81
+
82
+ const absolutePath = path.resolve(filePath)
83
+
84
+ if (absolutePath.includes('screenShot')) {
85
+ queue.add(() => pageHandler(filePath, context))
86
+ }
87
+ else if (absolutePath.includes('screenPart')) {
88
+ queue.add(() => partHandler(filePath, context))
89
+ }
90
+ else if (absolutePath.includes('response')) {
91
+ queue.add(() => apiHandler(filePath, context))
92
+ }
93
+ })
94
+ }
95
+
96
+ module.exports = watch
@@ -4,9 +4,7 @@ const Handlebars = require('handlebars')
4
4
  const stringify = require('json-stringify-pretty-compact')
5
5
  const { cleanCode, generateSmartImports } = require('../utils/utils.js')
6
6
 
7
- // 注册 Handlebars 辅助函数,用于将原始内容渲染到模板中,不进行任何处理
8
7
  Handlebars.registerHelper('raw', options => options.fn())
9
- // 注册 Handlebars 辅助函数,用于将对象格式化为 JSON 字符串
10
8
  Handlebars.registerHelper('stringify', (context, maxLength = 200) => context ? new Handlebars.SafeString(stringify.default(context, { indent: 4, maxLength })) : '[]')
11
9
 
12
10
  /**
@@ -17,25 +15,20 @@ Handlebars.registerHelper('stringify', (context, maxLength = 200) => context ? n
17
15
  * @returns {string} 生成的 resource.js 代码
18
16
  */
19
17
  const resource = ({ pageConfig, resourceTpl }) => {
20
- // 判断是否存在标签页配置
18
+
21
19
  const hasTabs = pageConfig.tabs?.length > 0
22
20
 
23
- // 构建模板数据
24
21
  const viewData = {
25
- hasTabs, // 是否有标签页
26
- tabs: pageConfig.tabs, // 标签页配置
27
- formItems: pageConfig.formItems, // 表单项配置
28
- // 如果有标签页,将 formItems 按 tab 分组;否则直接使用数组
22
+ hasTabs,
23
+ tabs: pageConfig.tabs,
24
+ formItems: pageConfig.formItems,
29
25
  formItemsData: hasTabs ? Object.fromEntries(pageConfig.tabs.map(tab => [tab.key, pageConfig.formItems])) : pageConfig.formItems,
30
- // 如果有标签页,将 columns 按 tab 分组;否则直接使用数组
31
26
  columnsData: hasTabs ? Object.fromEntries(pageConfig.tabs.map(tab => [tab.key, pageConfig.table.columns])) : pageConfig.table.columns,
32
- // 提取所有下拉框字典配置
33
27
  dictBlocks: pageConfig.formItems
34
- ?.filter(item => item.type === 'select') // 过滤出下拉框类型
35
- ?.map(item => ({ name: item.options.replaceAll('_CODE_', ''), data: pageConfig.optionDict[item.options] ?? [] })) // 转换为字典格式
28
+ ?.filter(item => item.type === 'select')
29
+ ?.map(item => ({ name: item.options.replaceAll('_CODE_', ''), data: pageConfig.optionDict[item.options] ?? [] }))
36
30
  }
37
31
 
38
- // 渲染模板并清理代码
39
32
  const rawCode = resourceTpl(viewData)
40
33
  return cleanCode(rawCode)
41
34
  }
@@ -54,14 +47,11 @@ const index = ({ config, fileName, indexTpl, pageConfig }) => {
54
47
  const hasFormItems = pageConfig.formItems?.length > 0
55
48
  const hasOperate = pageConfig.table.operation?.length > 0
56
49
 
57
- // 动态确定 columns 引用方式:有标签页时使用 activeKey 索引,否则直接使用 columns
58
50
  let columnsValue = hasTabs ? 'columns[activeKey]' : 'columns'
59
- // 如果存在操作列,拼接到 columns 后面
60
51
  if (hasOperate) {
61
52
  columnsValue = `${columnsValue}.concat(operate)`
62
53
  }
63
54
 
64
- // 构建模板数据
65
55
  const viewData = {
66
56
  hasTabs,
67
57
  fileName,
@@ -99,13 +89,9 @@ const index = ({ config, fileName, indexTpl, pageConfig }) => {
99
89
  Handlebars.registerPartial('handleBlock', `${fs.readFileSync(path.join(__dirname, '../../templates/react/handlebars/handleBlock.hbs'), 'utf-8')}\n`)
100
90
  Handlebars.registerPartial('FunctionButtonsBlock', `${fs.readFileSync(path.join(__dirname, '../../templates/react/handlebars/functionButtonsBlock.hbs'), 'utf-8')}\n`)
101
91
 
102
- // 渲染模板
103
92
  const bodyCode = indexTpl(viewData)
104
- // 生成智能导入语句
105
93
  const importsStr = generateSmartImports(bodyCode, hasTabs)
106
- // 清理代码并返回
107
94
  return cleanCode(`${importsStr}\n\n${bodyCode}`)
108
95
  }
109
96
 
110
- // 导出 React 编译器的两个核心函数
111
97
  module.exports = { index, resource }
@@ -1,4 +1,4 @@
1
1
  module.exports = (columns, fileName) => `
2
- 根据这些字段生成一个简单的接口mock数据: ${JSON.stringify(columns)},
3
- 格式为{POST /api/${fileName}:{data:模块对象列表}}
2
+ 根据columns的dataIndex字段生成一个简单的接口mock数据: ${JSON.stringify(columns)},
3
+ 格式为{POST /api/${fileName}:{data:2条真实mock数据]}}
4
4
  `
@@ -1,27 +1,32 @@
1
1
  module.exports = `
2
- 识别图片页面结构输出一个JSON对象,不要包含任何Markdown标签
3
- 1.表格
4
- { columns:[{ title: '普通列', dataIndex:'对应的英文名词' }]}
5
- ⚠️⚠️⚠️注意:
6
- 请不要在columns里加上操作列
7
- 如果列需要排序请加上sorter:true
8
- 如果是时间列,请加入render:_CODE_text=>timeRender({time:text})_CODE_
9
- 如果是序号列直接渲染为{ title: '序号',render: _CODE_(_, record, index) => index + 1_CODE_ }
10
- 如果是下拉框列(所对应的查询项明确是下拉框),请加入render:_CODE_text=>对应的options.find(item=>item.value===text)?.label_CODE_
11
- 如果列需要过滤请加上filters:[],onFilter: _CODE_(value, record) => record[对应的英文名词].includes(value)_CODE_
12
- 2.查询表单
13
- formItems: [{ label: '文本', name: '对应的英文名词' }],
14
- ⚠️⚠️⚠️注意:
15
- 如果是下拉框,请加入type:'select',options:_CODE_对应的英文名词Options_CODE_
16
- 如果是时间范围查询,请加入type:'daterange',name:'对应的英文名词start,对应的英文名词end'
17
- 3.下拉选项字典
18
- optionDict:{_CODE_对应的英文名词Options_CODE_:[]]}
19
- 数组值为对应列展示的内容组成的类似{label:'',value:''}的数组
20
- 4.弹窗表单
21
- modalItems:[{label:'基础项',name:'对应的英文名词',type:'text',rules:[{required:true,message:'字段不能为空'}]}]
22
- ⚠️⚠️⚠️注意:
23
- type:text/date/daterange/radio/checkbox/select/auto/textarea,如果是text可以省略
24
- 如果type是auto/radio/select/checkbox,请加入options:_CODE_对应的英文名词Options_CODE_
25
- 如果表单项末尾有文字描述,请将文字描述放在unit属性中
26
- ** 只输出modalItems就可以,不需要对应的formItems和columns **
2
+ 识别图片页面结构输出一个JSON对象
3
+
4
+ 1. 严禁包含任何 Markdown 标签。
5
+ 2. 严禁包含注释或解释文字。
6
+ 3. 必须严格遵守以下字段规范。
7
+
8
+ ## 1. 表格 (Table)
9
+ - 结构: { columns: [{ title: '', dataIndex: '' }] }
10
+ - ⚠️ 字段白名单: [title, dataIndex, sorter, render, filters, onFilter]
11
+ - 禁止输出: 严禁在 columns 中包含“操作”列。
12
+ - 特殊逻辑:
13
+ - 时间列: render 必须为 _CODE_text=>timeRender({time:text})_CODE_
14
+ - 序号列: render 必须为 _CODE_(_, record, index) => index + 1_CODE_
15
+ - 下拉映射列: render 必须为 _CODE_text=>对应的options.find(item=>item.value===text)?.label_CODE_
16
+
17
+ ## 2. 表单 (Form)
18
+ - 结构: formItems: [{label:'',name:'',type:'',options:[]}]
19
+ - ⚠️ 属性精简规则:
20
+ - 默认输入框: 仅保留 [label, name] 属性。**严禁出现 type 属性**。
21
+ - 非默认组件: 仅当类型为 [auto, date, radio, select, upload, checkbox, textarea, daterange] 时才允许添加 type 属性。
22
+ - 命名规范:
23
+ - daterange: name 必须设为 '字段英文名start,字段英文名end'。
24
+ - 选择类 (radio/select/checkbox): options 必须设为 _CODE_字段英文名Options_CODE_。
25
+ - 增强属性:
26
+ - 必填校验: 若图片中 label 前有红色星号,必须加入 rules:[{required:true,message:'xxx不能为空'}]。
27
+ - 文字单位: 若项末尾有单位(如 元/kg),必须存入 unit 属性。
28
+
29
+ ## 3. 下拉选项字典 (OptionDict)
30
+ - 结构: optionDict: { _CODE_字段名Options_CODE_: [] }
31
+ - 内容: 数组元素必须为 {label: '', value: ''} 格式。
27
32
  `
@@ -1,13 +1,10 @@
1
1
  const fs = require('fs')
2
2
  const path = require('path')
3
3
  const chalk = require('chalk')
4
- const { language, getConfig, checkPromptPath } = require('../utils/utils.js') // 导入语言助手
4
+ const { language, getConfig } = require('../utils/utils.js')
5
5
 
6
6
  let client = null
7
7
 
8
- /**
9
- * 获取 OpenAI 客户端实例(单例模式)
10
- */
11
8
  const getOpenAI = () => {
12
9
  if (!client) {
13
10
  const OpenAI = require('openai')
@@ -21,11 +18,7 @@ const getOpenAI = () => {
21
18
  return client
22
19
  }
23
20
 
24
- /**
25
- * 调用 AI 模型并解析响应
26
- */
27
21
  const askAI = async (model, messages, retryCount = 0) => {
28
- // 最终重试失败:YoRHa 司令部连接中断
29
22
  if (retryCount > 3) {
30
23
  throw new Error(language(
31
24
  'YoRHa 司令部连接中断:请检查网络状态或黑盒共鸣情况。',
@@ -50,14 +43,12 @@ const askAI = async (model, messages, retryCount = 0) => {
50
43
  const statusCode = err.status || err.response?.status
51
44
  const isAuthError = err.message.includes('401') || err.message.includes('402') || [401, 402].includes(statusCode)
52
45
  if (isAuthError) {
53
- // 权限错误:司令部拒绝访问
54
46
  throw new Error(language(
55
47
  '[系统警告] YoRHa 司令部拒绝访问:API Key 无效或额度耗尽,请检查!',
56
48
  '[SYSTEM ALERT] YoRHa Command Center Access Denied: Invalid API Key or quota exhausted!'
57
49
  ))
58
50
  }
59
51
 
60
- // 重试警告:神经云网络连接不稳
61
52
  console.warn(chalk.yellow(language(
62
53
  `[系统警告] 神经云网络连接不稳,正在尝试重新链接... (第 ${retryCount + 1} 次重试)`,
63
54
  `[SYSTEM ALERT] Neural Network instability detected. Attempting to re-establish link... (Retry #${retryCount + 1})`
@@ -68,9 +59,6 @@ const askAI = async (model, messages, retryCount = 0) => {
68
59
  }
69
60
 
70
61
  module.exports = {
71
- /**
72
- * 生成 Mock 数据
73
- */
74
62
  generateMock: async (columns, fileName) => {
75
63
  const config = getConfig()
76
64
  const mockPrompt = require('../prompts/mock.js')
@@ -83,10 +71,6 @@ module.exports = {
83
71
  ]
84
72
  )
85
73
  },
86
-
87
- /**
88
- * 识别页面截图
89
- */
90
74
  recognizePage: async (prompt, filePath) => {
91
75
  const config = getConfig()
92
76
  const { UI_DESIGNER } = require('../prompts/system.js')
@@ -111,16 +95,10 @@ module.exports = {
111
95
  ]
112
96
  )
113
97
  },
114
-
115
- /**
116
- * 对齐 Response 接口字段
117
- */
118
98
  alignResponseFields: async (options, responseStr, resourceStr) => {
119
99
  const config = getConfig()
120
100
  const { API_DESIGNER } = require('../prompts/system.js')
121
- const promptPath = `../prompts/${options.template}/watch-api.js`
122
- checkPromptPath(promptPath)
123
- const apiPrompt = require(promptPath)
101
+ const apiPrompt = require(`../prompts/${options.template}/watch-api.js`)
124
102
  return askAI(
125
103
  config.textModel,
126
104
  [
@@ -25,7 +25,6 @@ const getConfig = () => {
25
25
  * 大模型输出的标准 JSON 无法携带 render: (text) => <Tag> 等箭头函数。
26
26
  * 我们在 Prompt 中要求大模型用 _CODE_ 占位符包裹函数字符串,
27
27
  * 在此处用正则剥离外层的双引号和占位符,将其还原为真正的可执行 JS 代码。
28
- *
29
28
  * @param {string} str - 大模型返回的 JSON 字符串
30
29
  * @returns {string} 清理后的可执行 JS 代码
31
30
  */
@@ -41,22 +40,6 @@ const cleanCode = str => {
41
40
  .trim() + '\n'
42
41
  }
43
42
 
44
- /**
45
- * 检查 Prompt 文件是否存在
46
- * @param {string} promptPath - Prompt 文件路径
47
- * @returns {void}
48
- */
49
- const checkPromptPath = promptPath => {
50
- const promptAbsPath = path.resolve(__dirname, promptPath)
51
- if (!fs.existsSync(promptAbsPath)) {
52
- const displayPath = promptPath.slice(3)
53
- throw new Error(language(
54
- `🤖 Pod 042: [警告] 侦测到未知的构筑协议 ${displayPath},地堡数据库暂无此类资料。`,
55
- `🤖 Pod 042: [Warning] Unknown construction protocol ${displayPath}. No such protocol found in the bunker database.`
56
- ))
57
- }
58
- }
59
-
60
43
  /**
61
44
  * 获取已有的页面菜单配置
62
45
  * @param {string} dir - 页面目录路径(默认 'src/pages')
@@ -73,24 +56,21 @@ const getExistingMenus = (dir = 'src/pages') => {
73
56
  /**
74
57
  * 生成智能导入语句
75
58
  * 根据代码中实际使用的依赖,自动生成对应的 import 语句
76
- *
77
59
  * @param {string} codeStr - 生成的代码字符串
78
60
  * @param {boolean} hasTabs - 是否包含标签页
79
61
  * @returns {string} 拼接后的 import 语句
80
62
  */
81
63
  const generateSmartImports = (codeStr, hasTabs) => {
82
- const hooksLib = ['useTableQuery'] // 自定义 hooks 库
83
- const reactLib = ['useState', 'useEffect', 'useRef', 'useMemo'] // React 核心库
84
- const componentsLib = ['MyTable', 'MyModalForm', 'MySearchForm'] // 自定义组件库
85
- const antdLib = ['Card', 'Space', 'Modal', 'Button', 'Alert', 'Table', 'Input', 'Select'] // Ant Design 组件库
64
+ const hooksLib = ['useTableQuery']
65
+ const reactLib = ['useState', 'useEffect', 'useRef', 'useMemo']
66
+ const componentsLib = ['MyTable', 'MyModalForm', 'MySearchForm']
67
+ const antdLib = ['Card', 'Space', 'Modal', 'Button', 'Alert', 'Table', 'Input', 'Select']
86
68
 
87
- // 检测代码中实际使用的组件
88
69
  const usedAntd = antdLib.filter(name => new RegExp(`\\b${name}\\b`).test(codeStr))
89
70
  const usedHooks = hooksLib.filter(name => new RegExp(`\\b${name}\\b`).test(codeStr))
90
71
  const usedReact = reactLib.filter(name => new RegExp(`\\b${name}\\b`).test(codeStr))
91
72
  const usedComps = componentsLib.filter(name => new RegExp(`\\b${name}\\b`).test(codeStr))
92
73
 
93
- // 构建 import 语句数组
94
74
  const imports = [
95
75
  usedReact.length && `import { ${usedReact.join(', ')} } from 'react'`,
96
76
  `import { request } from '../../utils/request'`,
@@ -99,9 +79,9 @@ const generateSmartImports = (codeStr, hasTabs) => {
99
79
  ...usedHooks.map(hook => `import { ${hook} } from '../../hooks/${hook}'`),
100
80
  ...usedComps.map(comp => `import { ${comp} } from '../../components/${comp}'`),
101
81
  usedAntd.length && `import { Form, ${usedAntd.join(', ')} } from 'antd'`
102
- ].sort((a, b) => a.length - b.length) // 按长度排序(短的在前)
82
+ ].sort((a, b) => a.length - b.length)
103
83
 
104
- return imports.filter(Boolean).join('\n') // 过滤空值并拼接
84
+ return imports.filter(Boolean).join('\n')
105
85
  }
106
86
 
107
87
  /**
@@ -124,5 +104,4 @@ const copyTemplateDir = (options, templateSubDir, targetSubDir) => {
124
104
  })
125
105
  }
126
106
 
127
- // 导出工具函数
128
- module.exports = { language, getConfig, cleanCode, getExistingMenus, copyTemplateDir, checkPromptPath, generateSmartImports }
107
+ module.exports = { language, getConfig, cleanCode, getExistingMenus, copyTemplateDir, generateSmartImports }