@yorha2b-lab/autodev 1.0.25 → 1.0.27

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/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Auto CRUD Copilot
2
2
 
3
+ [English](./README_EN.md) | 简体中文
4
+
3
5
  [![NPM Version](https://img.shields.io/npm/v/@yorha2b-lab/autodev.svg?style=flat-square)](https://www.npmjs.com/package/@yorha2b-lab/autodev)
4
6
  [![NPM Downloads](https://img.shields.io/npm/dm/@yorha2b-lab/autodev.svg?style=flat-square)](https://www.npmjs.com/package/@yorha2b-lab/autodev)
5
7
  [![GitHub Stars](https://img.shields.io/github/stars/yorha2b-lab/auto-crud-copilot.svg?style=flat-square)](https://github.com/yorha2b-lab/auto-crud-copilot/stargazers)
package/README_EN.md ADDED
@@ -0,0 +1,194 @@
1
+ # Auto CRUD Copilot
2
+
3
+ English | [简体中文](./README.md)
4
+
5
+ [![NPM Version](https://img.shields.io/npm/v/@yorha2b-lab/autodev.svg?style=flat-square)](https://www.npmjs.com/package/@yorha2b-lab/autodev)
6
+ [![NPM Downloads](https://img.shields.io/npm/dm/@yorha2b-lab/autodev.svg?style=flat-square)](https://www.npmjs.com/package/@yorha2b-lab/autodev)
7
+ [![GitHub Stars](https://img.shields.io/github/stars/yorha2b-lab/auto-crud-copilot.svg?style=flat-square)](https://github.com/yorha2b-lab/auto-crud-copilot/stargazers)
8
+ [![License](https://img.shields.io/npm/l/@yorha2b-lab/autodev.svg?style=flat-square)](https://github.com/yorha2b-lab/auto-crud-copilot/blob/main/LICENSE)
9
+
10
+ An AI-powered frontend CRUD code generator based on vision models (Umi+Antd) 🚀
11
+
12
+ <video src="https://github.com/user-attachments/assets/6d42fce1-c815-482b-8c6a-4a3b9415ba47.mp4" controls="controls" muted="muted" style="max-width: 100%; display: block; margin: 20px auto;">
13
+ </video>
14
+
15
+ Say goodbye to repetitive work! Why do you need Auto CRUD Copilot?
16
+ Stop writing Tables/Forms manually: Screenshot to code, boost your development efficiency, and say goodbye to tedious copy-paste.
17
+ Eliminate API integration pain: Automatically read Response data, and let AI help you bridge frontend-backend field naming differences.
18
+ Decoupled architecture: Core logic separated from UI frameworks, supporting React/Vue/Angular.
19
+
20
+ ## ✨ Features
21
+
22
+ - 🖼️ **Visual Recognition**: Automatically recognize page structures from screenshots and generate corresponding CRUD code
23
+ - 🔧 **Smart Generation**: Support multiple frontend framework templates including React, Vue, and Angular
24
+ - 📊 **Table Components**: Automatically generate editable, sortable, and filterable data tables
25
+ - 📝 **Form Components**: Intelligently generate search forms and modal forms
26
+ - 🔌 **API Alignment**: Automatically align Response interfaces with frontend field mappings
27
+ - 🎨 **UI Components**: Support various UI component types (input, select, date picker, etc.)
28
+ - 📱 **Responsive**: Generated code supports responsive layout
29
+
30
+ ## 🚀 Quick Start
31
+
32
+ ### Installation
33
+
34
+ ```bash
35
+ npm install -g @yorha2b-lab/autodev
36
+ ```
37
+
38
+ ### Environment Setup
39
+
40
+ Quick start
41
+
42
+ ```bash
43
+ autodev init
44
+ ```
45
+
46
+ Create a `.env` file and configure the following environment variables:
47
+
48
+ ```bash
49
+ # AI Model API Configuration
50
+ API_KEY=your_api_key_here
51
+ BASE_URL=your_api_base_url_here
52
+ ```
53
+
54
+ 🚗 **Get API Key**: [Alibaba Cloud Bailian Console](https://bailian.console.aliyun.com/cn-beijing?tab=doc#/doc/)
55
+
56
+ ### Basic Usage
57
+
58
+ 1. **Generate Complete CRUD Pages**
59
+
60
+ ```bash
61
+ # Put page screenshots in the screenShot directory
62
+ autodev watch:page
63
+ ```
64
+
65
+ 2. **Generate Partial UI Components**
66
+
67
+ ```bash
68
+ # Put component screenshots in the screenPart directory
69
+ autodev watch:part
70
+ ```
71
+
72
+ 3. **Align API Fields**
73
+
74
+ ```bash
75
+ autodev watch:api
76
+ ```
77
+
78
+ ## 📖 Detailed Usage Guide
79
+
80
+ ### watch:page Command
81
+
82
+ Monitor screenshot files in the `screenShot` directory and automatically generate complete CRUD pages.
83
+
84
+ ```bash
85
+ autodev watch:page -t react
86
+ ```
87
+
88
+ Parameters:
89
+ - `-t, --template <type>`: Specify frontend framework template, default is `react`
90
+
91
+ ### watch:part Command
92
+
93
+ Monitor screenshot files in the `screenPart` directory and automatically generate partial UI components.
94
+
95
+ ```bash
96
+ autodev watch:part -t vue
97
+ ```
98
+
99
+ ### watch:api Command
100
+
101
+ Monitor JSON files in the `response` directory (named after modules) and automatically align real API fields.
102
+
103
+ ```bash
104
+ autodev watch:api
105
+ ```
106
+
107
+ ## 📁 Project Structure
108
+
109
+ ```
110
+ your-project/
111
+ ├── screenShot/ # Page screenshot directory
112
+ ├── screenPart/ # Component screenshot directory
113
+ ├── response/ # Response directory
114
+ ├── mock/ # Generated Mock data
115
+ ├── src/
116
+ │ ├── pages/ # Generated page code
117
+ │ ├── components/ # Generated component code
118
+ │ ├── hooks/ # Generated Hook code
119
+ └── config.js # Configuration file
120
+ ```
121
+
122
+ ## ⚙️ Configuration
123
+
124
+ You can configure the following options in `config.js`:
125
+
126
+ ```javascript
127
+ module.exports = {
128
+ // Whether to generate Mock data
129
+ needMock: false,
130
+
131
+ // AI model types to use
132
+ visionModel: 'qwen3.5-plus',
133
+ textModel: 'qwen-turbo',
134
+
135
+ // Target project directory paths
136
+ hooksDir: 'src/hooks',
137
+ pagesDir: 'src/pages',
138
+ utilsDir: 'src/utils',
139
+ componentsDir: 'src/components',
140
+ }
141
+ ```
142
+
143
+ ## 🎯 Supported UI Components
144
+
145
+ - Input
146
+ - InputNumber
147
+ - Select
148
+ - Radio
149
+ - Checkbox
150
+ - DatePicker
151
+ - RangePicker
152
+ - Cascader
153
+ - AutoComplete
154
+ - TextArea
155
+ - Upload
156
+
157
+ ## 🤝 Contributing
158
+
159
+ Contributions are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) to learn how to participate in project development.
160
+
161
+ ## 📄 License
162
+
163
+ This project is licensed under the [MIT](LICENSE) license.
164
+
165
+ ## 🙏 Acknowledgments
166
+
167
+ Thanks to the following open-source projects:
168
+
169
+ - [OpenAI](https://openai.com/) - Providing powerful AI capabilities
170
+ - [Ant Design](https://ant.design/) - Excellent enterprise-level UI design language
171
+ - [UmiJS](https://umijs.org/) - Enterprise-level frontend application framework
172
+
173
+ ## 📞 Contact
174
+
175
+ If you have any questions or suggestions, feel free to submit an [Issue](https://github.com/yorha2b-lab/auto-crud-copilot/issues).
176
+
177
+ ## 🛠️ FAQ
178
+
179
+ **Q: Is this tool free?**
180
+ A: The tool itself is open-source and free, but the AI models it calls (such as GPT-4v, Qwen-VL) may require you to configure your own API Key. We recommend using cost-effective models like Alibaba Cloud's Qwen-VL.
181
+
182
+ ---
183
+
184
+ ## ⚖️ Disclaimer
185
+
186
+ Auto CRUD Copilot is a **fan-made, non-commercial, open-source tool**.
187
+
188
+ - The themes, names (YoRHa, 2B, etc.), and catchphrases included in this project are inspired by **NieR:Automata**, which is a trademark and copyright of **Square Enix Co., Ltd. / PlatinumGames Inc.**
189
+ - This project is not affiliated with, endorsed by, or representative of Square Enix in any way.
190
+ - Please support the original masterpiece: [NieR:Automata Official Site](https://nierautomata.square-enix-games.com/).
191
+
192
+ **Glory to Mankind.**
193
+
194
+ ⭐ If this project helps you, please give it a Star to show your support!
package/bin/autodev.js CHANGED
@@ -5,47 +5,74 @@ const chalk = require('chalk')
5
5
  const figlet = require('figlet')
6
6
  const { program } = require('commander')
7
7
  const pkg = require(path.join(__dirname, '../package.json'))
8
+ const { language } = require(path.join(__dirname, '../src/utils/utils.js'))
8
9
 
9
10
  console.log(chalk.cyan(figlet.textSync('AutoDev', { horizontalLayout: 'full' })))
10
11
  console.log(chalk.gray('Booting System...'))
12
+ console.log(chalk.white(' [System] ') + chalk.green('Locale Detection: ') + chalk.cyan(language('ZH-CN', 'EN-US')))
11
13
  console.log(chalk.white(' [System] ') + chalk.green('YoRHa No.2 Type B Unit: ') + chalk.cyan('Online'))
12
14
  console.log(chalk.white(' [System] ') + chalk.green('Scanner Type 9S Unit: ') + chalk.cyan('Standby'))
13
15
  console.log(chalk.white(' [Mission] ') + chalk.yellow('Frontend Architecture Construction: ') + chalk.cyan('Awaiting Command'))
14
16
  console.log(chalk.white(' [Bunker] ') + chalk.magenta('Glory to mankind. (人类荣光永存)'))
15
17
  console.log(chalk.gray('--------------------------------------------------\n'))
16
18
 
17
- program.version(pkg.version).description('AI驱动的前端CRUD代码生成器')
18
- program.option('-t, --template <type>', '指定前端框架模板', 'react')
19
+ // --- Commander 配置 ---
20
+ program
21
+ .version(pkg.version)
22
+ .description(language('AI驱动的前端CRUD代码生成器', 'AI-powered frontend CRUD code generator'))
23
+
24
+ program.option('-t, --template <type>', language('指定前端框架模板', 'Specify frontend framework template'), 'react')
19
25
 
20
26
  program
21
27
  .command('init')
22
- .description('在当前目录初始化 .env 和 config.js 配置文件')
28
+ .description(language('在当前目录初始化 .env 和 config.js 配置文件', 'Initialize .env and config.js in the current directory'))
23
29
  .action(() => {
24
30
  const tplConfig = path.resolve(__dirname, '../config.js')
25
31
  const tplEnv = path.resolve(__dirname, '../.env.example')
26
32
  const targetEnv = path.join(process.cwd(), '.env')
27
33
  const targetConfig = path.join(process.cwd(), 'config.js')
34
+
28
35
  fs.mkdirSync(path.join(process.cwd(), 'response'), { recursive: true })
29
36
  fs.mkdirSync(path.join(process.cwd(), 'screenShot'), { recursive: true })
30
37
  fs.mkdirSync(path.join(process.cwd(), 'screenPart'), { recursive: true })
38
+
39
+ // .env 处理
31
40
  if (fs.existsSync(targetEnv)) {
32
- console.log(chalk.yellow('🤖 Pod 042: [警告] 终端已存在 .env 文件,跳过生成以免覆盖数据。'));
41
+ console.log(chalk.yellow(language(
42
+ '🤖 Pod 042: [警告] 终端已存在 .env 文件,跳过生成以免覆盖数据。',
43
+ '🤖 Pod 042: [Warning] .env already exists. Skipping to avoid data overwrite.'
44
+ )));
33
45
  } else {
34
46
  fs.copyFileSync(tplEnv, targetEnv);
35
- console.log(chalk.green('🤖 Pod 042: [报告] .env 存储完毕。'));
47
+ console.log(chalk.green(language(
48
+ '🤖 Pod 042: [报告] .env 存储完毕。',
49
+ '🤖 Pod 042: [Report] .env storage complete.'
50
+ )));
36
51
  }
52
+
53
+ // config.js 处理
37
54
  if (fs.existsSync(targetConfig)) {
38
- console.log(chalk.yellow('🤖 Pod 042: [警告] 终端已存在 config.js 文件,跳过生成以免覆盖数据。'));
55
+ console.log(chalk.yellow(language(
56
+ '🤖 Pod 042: [警告] 终端已存在 config.js 文件,跳过生成以免覆盖数据。',
57
+ '🤖 Pod 042: [Warning] config.js already exists. Skipping to avoid data overwrite.'
58
+ )));
39
59
  } else {
40
60
  fs.copyFileSync(tplConfig, targetConfig)
41
- console.log(chalk.green('🤖 Pod 042: [报告] config.js 存储完毕。'));
61
+ console.log(chalk.green(language(
62
+ '🤖 Pod 042: [报告] config.js 存储完毕。',
63
+ '🤖 Pod 042: [Report] config.js storage complete.'
64
+ )));
42
65
  }
43
- console.log(chalk.cyan('📡 Operator 6O: 呼叫 2B,环境配置文件已下发至本地终端!'))
66
+
67
+ console.log(chalk.cyan(language(
68
+ '📡 Operator 6O: 呼叫 2B,环境配置文件已下发至本地终端!',
69
+ '📡 Operator 6O: Calling 2B, environmental config files have been deployed to your local terminal!'
70
+ )))
44
71
  })
45
72
 
46
73
  program
47
74
  .command('watch:page')
48
- .description('监听截图,自动生成完整的增删改查页面')
75
+ .description(language('监听截图,自动生成完整的增删改查页面', 'Watch screenshots and auto-generate complete CRUD pages'))
49
76
  .action(() => {
50
77
  const watchPage = require('../src/commands/watch-page')
51
78
  watchPage(program.opts())
@@ -53,7 +80,7 @@ program
53
80
 
54
81
  program
55
82
  .command('watch:part')
56
- .description('监听截图,自动生成局部 UI 组件')
83
+ .description(language('监听截图,自动生成局部 UI 组件', 'Watch screenshots and auto-generate partial UI components'))
57
84
  .action(() => {
58
85
  const watchPart = require('../src/commands/watch-part')
59
86
  watchPart(program.opts())
@@ -61,7 +88,7 @@ program
61
88
 
62
89
  program
63
90
  .command('watch:api')
64
- .description('监听 Response,自动对齐真实接口字段')
91
+ .description(language('监听 Response,自动对齐真实接口字段', 'Watch responses and auto-align real API fields'))
65
92
  .action(() => {
66
93
  const watchApi = require('../src/commands/watch-api')
67
94
  watchApi()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yorha2b-lab/autodev",
3
- "version": "1.0.25",
3
+ "version": "1.0.27",
4
4
  "description": "基于视觉大模型的前端(Umi+Antd)全自动 CRUD 代码生成器",
5
5
  "bin": {
6
6
  "autodev": "bin/autodev.js"
@@ -5,8 +5,8 @@ const chalk = require('chalk')
5
5
  const chokidar = require('chokidar')
6
6
 
7
7
  const watchApi = () => {
8
-
9
- const { getConfig } = require('../utils/utils.js')
8
+ // 导入配置文件和多语言函数
9
+ const { getConfig, language } = require('../utils/utils.js')
10
10
  const { alignResponseFields } = require('../services/llm.js')
11
11
 
12
12
  const config = getConfig()
@@ -23,26 +23,44 @@ const watchApi = () => {
23
23
  watcher.on('add', async filePath => {
24
24
  const startTime = Date.now()
25
25
  const fileName = path.basename(filePath, path.extname(filePath))
26
- const spinner = ora(chalk.cyan(`🤖 Pod 153: [报告] 侦测到未知的 Response 数据源 [${fileName}],开始进行语义对齐...`))
26
+
27
+ // 使用 language 函数包装 Pod 153 的启动报告
28
+ const spinner = ora(chalk.cyan(language(
29
+ `🤖 Pod 153: [报告] 侦测到未知的 Response 数据源 [${fileName}],开始进行语义对齐...`,
30
+ `🤖 Pod 153: [Report] Detected unknown Response data source [${fileName}]. Commencing semantic alignment...`
31
+ )))
27
32
  spinner.start()
28
33
 
29
34
  try {
30
35
  const responseStr = fs.readFileSync(filePath, 'utf8')
31
36
  let resourceStr = fs.readFileSync(`./${config.pagesDir}/${fileName}/resource.js`, 'utf8')
37
+
38
+ // 调用 AI 服务进行字段对齐
32
39
  const result = await alignResponseFields(responseStr, resourceStr)
40
+
33
41
  Object.entries(result).forEach(([oldField, newField]) => {
34
42
  if (oldField === newField) return
35
43
  const regex = new RegExp(`(dataIndex|name)\\s*:\\s*['"]${oldField}['"]`, 'g')
36
44
  resourceStr = resourceStr.replace(regex, `$1: '${newField}'`)
37
45
  })
46
+
38
47
  fs.writeFileSync(path.join(`./${config.pagesDir}/${fileName}/resource.js`), resourceStr)
39
48
  const endTime = Date.now()
40
- //fs.unlinkSync(filePath)
41
- spinner.succeed(chalk.green(`🤖 Pod 153: [肯定] 字段对齐协议执行完毕:耗时 ${(endTime - startTime) / 1000} 秒。9S 的骇客任务很顺利。`))
49
+
50
+ // 成功报告:带上 9S 的骇客梗
51
+ spinner.succeed(chalk.green(language(
52
+ `🤖 Pod 153: [肯定] 字段对齐协议执行完毕:耗时 ${(endTime - startTime) / 1000} 秒。9S 的骇客任务很顺利。`,
53
+ `🤖 Pod 153: [Affirmative] Field alignment protocol executed. Time: ${(endTime - startTime) / 1000}s. 9S's hacking mission was successful.`
54
+ )))
55
+
42
56
  } catch (error) {
43
- console.log(chalk.red(`🤖 Pod 153: [警告] 骇入 [${fileName}] 失败。接口数据遭遇强力防火墙:${error}`))
57
+ // 失败报告:提到“防火墙”
58
+ spinner.fail(chalk.red(language(
59
+ `🤖 Pod 153: [警告] 骇入 [${fileName}] 失败。接口数据遭遇强力防火墙:${error}`,
60
+ `🤖 Pod 153: [Warning] Hacking into [${fileName}] failed. Encountered a powerful firewall in API data: ${error}`
61
+ )))
44
62
  }
45
63
  })
46
64
  }
47
65
 
48
- module.exports = watchApi
66
+ module.exports = watchApi
@@ -1,132 +1,135 @@
1
1
  const fs = require('fs')
2
- const ora = require('ora') // 命令行加载动画
2
+ const ora = require('ora')
3
3
  const path = require('path')
4
4
  const chalk = require('chalk')
5
5
 
6
-
7
6
  /**
8
7
  * 监听截图目录,自动生成 CRUD 页面
9
8
  * @param {Object} options - 命令行选项
10
- * @param {string} options.template - 前端框架模板(react/vue/angular)
9
+ * @param {string} options.template - 前端框架模板
11
10
  */
12
11
  const watchPage = options => {
12
+ const chokidar = require('chokidar')
13
+ const Handlebars = require('handlebars')
14
+ const pagePrompt = require('../prompts/watch-page.js')
15
+ const { createTaskQueue } = require('../core/task-queue')
16
+ const stringify = require('json-stringify-pretty-compact')
17
+ const { recognizePage, generateMock } = require('../services/llm.js')
13
18
 
14
- const chokidar = require('chokidar') // 文件监听库
15
- const Handlebars = require('handlebars') // 模板引擎
16
- const pagePrompt = require('../prompts/watch-page.js') // 页面识别提示词
17
- const { createTaskQueue } = require('../core/task-queue') // 任务队列,控制并发
18
- const stringify = require('json-stringify-pretty-compact') // 格式化 JSON
19
- const { recognizePage, generateMock } = require('../services/llm.js') // AI 服务
20
- const { getConfig, copyTemplateDir, getExistingMenus } = require('../utils/utils.js') // 工具函数
19
+ const { language, getConfig, copyTemplateDir, getExistingMenus } = require('../utils/utils.js')
21
20
 
22
21
  const config = getConfig()
23
22
 
24
- // 根据框架加载对应的编译器(react-compiler.js / vue-compiler.js 等)
25
23
  const compilerPath = path.join(__dirname, `../core/${options.template}-compiler.js`)
26
24
 
27
- // 检查是否支持该框架
25
+ // 1. 检查框架支持
28
26
  if (!fs.existsSync(compilerPath)) {
29
- console.error(`\n❌ 糟糕!暂不支持 [${options.template}] 框架的自动生成。`)
30
- console.log(`💡 提示:目前仅内置了 react 编译器。`)
31
- console.log(`🚀 强烈欢迎社区大佬提 PR 补充 ${options.template}-compiler.js !\n`)
27
+ console.error(chalk.red(language(
28
+ `\n❌ 糟糕!暂不支持 [${options.template}] 框架的自动生成。`,
29
+ `\n❌ Errors! Automatic generation for [${options.template}] is not supported.`
30
+ )))
31
+ console.log(language(
32
+ `💡 提示:目前仅内置了 react 编译器。`,
33
+ `💡 Note: Only the react compiler is currently built-in.`
34
+ ))
35
+ console.log(language(
36
+ `🚀 强烈欢迎社区大佬提 PR 补充 ${options.template}-compiler.js !\n`,
37
+ `🚀 PRs for ${options.template}-compiler.js are highly welcomed!\n`
38
+ ))
32
39
  return
33
40
  }
34
41
 
35
- // 加载编译器的 index 和 resource 函数
36
42
  const { index, resource } = require(compilerPath)
37
43
 
38
44
  try {
39
- // 首次运行时复制模板到目标项目
40
45
  copyTemplateDir(options, 'hooks', config.hooksDir)
41
46
  copyTemplateDir(options, 'utils', config.utilsDir)
42
47
  copyTemplateDir(options, 'components', config.componentsDir)
43
48
  } catch (error) {
44
- console.error('❌ 程序运行出错:', error)
49
+ console.error(language('❌ 程序运行出错:', '❌ Runtime error:'), error)
45
50
  }
46
51
 
47
- /**
48
- * 创建任务队列,限制并发数为 2。
49
- *
50
- * 为什么限制并发?
51
- * 1. 视觉大模型的 API 计费昂贵且通常有严格的 TPM/RPM (每分钟请求数) 限流。
52
- * 2. 如果用户一次性丢入 5 张截图,全量并发极易触发 HTTP 429 Too Many Requests 报错。
53
- * 3. 并发设为 2 是实测下来速度与稳定性的最佳平衡点。
54
- */
55
52
  const queue = createTaskQueue(2)
56
-
57
- // 获取已有的菜单配置
58
53
  const menus = getExistingMenus()
59
54
 
60
- // 加载模板文件
61
55
  const tplDir = path.join(__dirname, `../../templates/${options.template}`)
62
56
  const indexTpl = Handlebars.compile(fs.readFileSync(path.join(tplDir, 'index.hbs'), 'utf-8'))
63
57
  const resourceTpl = Handlebars.compile(fs.readFileSync(path.join(tplDir, 'resource.hbs'), 'utf-8'))
64
58
 
65
- // 所有任务完成后统一更新菜单配置
59
+ // 2. 队列空闲时的菜单同步
66
60
  queue.onIdle(() => {
67
- console.log(chalk.green('🤖 Pod 042: [报告] 所有排队的视觉数据已解析完毕,正在向 Bunker 同步 Menu 路由配置...'))
61
+ console.log(chalk.green(language(
62
+ '🤖 Pod 042: [报告] 所有排队的视觉数据已解析完毕,正在向 Bunker 同步 Menu 路由配置...',
63
+ '🤖 Pod 042: [Report] Queued visual data processed. Syncing Menu configuration to the Bunker...'
64
+ )))
68
65
  const constantDir = path.join(process.cwd(), config.utilsDir)
69
66
  if (!fs.existsSync(constantDir)) fs.mkdirSync(constantDir, { recursive: true })
70
- // 格式化菜单配置为 JS 代码
71
67
  fs.writeFileSync(path.join(constantDir, 'constant.js'), `export const menus = ${stringify.default(menus, { indent: 4, maxLength: 50 })}`)
72
- console.log(chalk.green('🤖 Pod 042: [肯定] Menu 配置同步成功。'))
68
+ console.log(chalk.green(language(
69
+ '🤖 Pod 042: [肯定] Menu 配置同步成功。',
70
+ '🤖 Pod 042: [Affirmative] Menu configuration synced successfully.'
71
+ )))
73
72
  })
74
73
 
75
- // 监听 screenShot 目录
76
74
  const watcher = chokidar.watch('./screenShot', {
77
- persistent: true, // 持续监听
78
- ignored: /(^|[\/\\])\../, // 忽略隐藏文件
79
- awaitWriteFinish: { // 等待文件写入完成
80
- stabilityThreshold: 500, // 稳定性阈值(毫秒)
81
- pollInterval: 100 // 轮询间隔
82
- }
75
+ persistent: true,
76
+ ignored: /(^|[\/\\])\../,
77
+ awaitWriteFinish: { stabilityThreshold: 500, pollInterval: 100 }
83
78
  })
84
79
 
85
- // 文件添加事件
80
+ // 3. 文件添加事件
86
81
  watcher.on('add', filePath => {
87
82
  queue.add(async () => {
88
83
  const startTime = Date.now()
89
84
  const mockDir = path.join(process.cwd(), 'mock')
90
85
  const fileName = path.basename(filePath, path.extname(filePath))
91
86
  const targetDir = path.join(process.cwd(), config.pagesDir, fileName)
92
- const spinner = ora(chalk.cyan(`🤖 Pod 042: [报告] 捕获到新的视觉图像 [${fileName}],开始执行构筑程序...`))
87
+
88
+ const spinner = ora(chalk.cyan(language(
89
+ `🤖 Pod 042: [报告] 捕获到新的视觉图像 [${fileName}],开始执行构筑程序...`,
90
+ `🤖 Pod 042: [Report] Captured new visual image [${fileName}]. Initializing construction...`
91
+ )))
93
92
  spinner.start()
93
+
94
94
  try {
95
- // 创建必要的目录
96
95
  if (!fs.existsSync(mockDir)) fs.mkdirSync(mockDir, { recursive: true })
97
96
  if (!fs.existsSync(targetDir)) fs.mkdirSync(targetDir, { recursive: true })
98
97
 
99
- // 1. 识别页面结构(调用视觉大模型)
100
98
  const pageConfig = await recognizePage(pagePrompt, filePath)
101
-
102
- // 2. 生成 resource.js(表格列、表单配置、字典等)
103
99
  fs.writeFileSync(path.join(targetDir, 'resource.js'), resource({ pageConfig, resourceTpl }))
104
-
105
- // 3. 生成 index.js(页面组件)
106
100
  fs.writeFileSync(path.join(targetDir, 'index.js'), index({ fileName, indexTpl, pageConfig }))
107
101
 
108
- // 4. 如果是新页面,添加到菜单配置
109
102
  if (!menus.find(m => m.key === fileName)) {
110
103
  menus.push({ label: fileName, key: fileName })
111
104
  }
112
105
 
113
- // 5. 生成 Mock 数据(可选)
106
+ // 4. 生成 Mock 数据
114
107
  if (config.needMock) {
115
- console.log(chalk.yellow(`🧑‍💻 9S: 交给我吧 2B!正在骇入并伪造 [${fileName}] 的 Mock 数据...`))
108
+ console.log(chalk.yellow(language(
109
+ `🧑‍💻 9S: 交给我吧 2B!正在骇入并伪造 [${fileName}] 的 Mock 数据...`,
110
+ `🧑‍💻 9S: Leave it to me, 2B! Hacking and forging Mock data for [${fileName}]...`
111
+ )))
116
112
  const rawContent = await generateMock(pageConfig.table.columns, fileName)
117
- // 格式化 Mock 数据
118
113
  fs.writeFileSync(path.join(mockDir, `${fileName}.js`), `export default ${stringify.default(rawContent, { indent: 4, maxLength: 200 })}`.replaceAll(`"'`, `"`).replaceAll(`'"`, `"`))
119
- console.log(chalk.green(`🤖 Pod 042: [报告] Mock 数据注入完成,耗时: ${(Date.now() - startTime) / 1000} 秒`))
114
+ console.log(chalk.green(language(
115
+ `🤖 Pod 042: [报告] Mock 数据注入完成,耗时: ${(Date.now() - startTime) / 1000} 秒`,
116
+ `🤖 Pod 042: [Report] Mock data injection complete. Elapsed time: ${(Date.now() - startTime) / 1000}s`
117
+ )))
120
118
  }
121
119
 
122
120
  const endTime = Date.now()
123
- //fs.unlinkSync(filePath) // 可选:处理完成后删除截图
124
- spinner.succeed(chalk.green(`🤖 Pod 042: [报告] 模块 [${fileName}] 物理装配完成!耗时 ${(endTime - startTime) / 1000} 秒`))
121
+ spinner.succeed(chalk.green(language(
122
+ `🤖 Pod 042: [报告] 模块 [${fileName}] 物理装配完成!耗时 ${(endTime - startTime) / 1000} 秒`,
123
+ `🤖 Pod 042: [Report] Module [${fileName}] assembly complete! Elapsed time: ${(endTime - startTime) / 1000}s`
124
+ )))
125
125
  } catch (error) {
126
- spinner.fail(chalk.red(`🤖 Pod 042: [警告] 模块 [${fileName}] 构筑失败。原因:${error}`))
126
+ spinner.fail(chalk.red(language(
127
+ `🤖 Pod 042: [警告] 模块 [${fileName}] 构筑失败。原因:${error}`,
128
+ `🤖 Pod 042: [Warning] Module [${fileName}] construction failed. Reason: ${error}`
129
+ )))
127
130
  }
128
131
  })
129
132
  })
130
133
  }
131
134
 
132
- module.exports = watchPage
135
+ module.exports = watchPage
@@ -5,8 +5,9 @@ const chokidar = require('chokidar')
5
5
  const stringify = require('json-stringify-pretty-compact')
6
6
 
7
7
  const watchPart = () => {
8
-
8
+ // 导入核心逻辑和多语言函数
9
9
  const { recognizePage } = require('../services/llm.js')
10
+ const { language } = require('../utils/utils.js')
10
11
 
11
12
  const watcher = chokidar.watch('./screenPart', {
12
13
  persistent: true,
@@ -18,19 +19,28 @@ const watchPart = () => {
18
19
  })
19
20
 
20
21
  watcher.on('add', async filePath => {
21
- const spinner = ora(chalk.cyan(`🤖 Pod 042: [报告] 捕获到新的视觉图像,开始执行构筑程序...`))
22
+ // 使用 language 函数包装 Pod 报告
23
+ const spinner = ora(chalk.cyan(language(
24
+ `🤖 Pod 042: [报告] 捕获到新的视觉图像,开始执行构筑程序...`,
25
+ `🤖 Pod 042: [Report] Captured new visual image. Initializing construction...`
26
+ )))
22
27
  spinner.start()
28
+
23
29
  try {
24
30
  const startTime = Date.now()
25
31
  const partPrompt = require('../prompts/watch-part.js')
26
32
  const pageConfig = await recognizePage(partPrompt, filePath)
33
+
27
34
  const optionDict = pageConfig.optionDict || {}
28
35
  delete pageConfig.optionDict
36
+
37
+ // 处理配置字符串映射
29
38
  let mainConfigStr = stringify.default(pageConfig, { indent: 4, maxLength: 200 })
30
39
  .replace(/"(\w+)":/g, '$1:')
31
40
  .replace(/"/g, "'")
32
41
  .replace(/['"]_CODE_([\s\S]*?)_CODE_['"]/g, '$1')
33
42
  .replace(/_CODE_/g, '')
43
+
34
44
  let optionsCodeStr = ''
35
45
  Object.keys(optionDict).forEach(key => {
36
46
  const varName = key.replace('_CODE_', '')
@@ -38,11 +48,22 @@ const watchPart = () => {
38
48
  const arrayItemsStr = optionsArray.map(opt => ` { label: '${opt.label}', value: '${opt.value}' }`).join(',\n')
39
49
  optionsCodeStr += `\nexport const ${varName} = [\n${arrayItemsStr}\n]\n`
40
50
  })
51
+
41
52
  const finalResult = `${mainConfigStr}\n${optionsCodeStr}`
42
53
  const endTime = Date.now()
43
- spinner.succeed(`🤖 Pod 042: [报告] 识别程序结束!耗时 ${(endTime - startTime) / 1000} 秒\n================\n\n${finalResult}\n\n================`)
54
+
55
+ // 成功报告
56
+ spinner.succeed(language(
57
+ `🤖 Pod 042: [报告] 识别程序结束!耗时 ${(endTime - startTime) / 1000} 秒\n================\n\n${finalResult}\n\n================`,
58
+ `🤖 Pod 042: [Report] Recognition complete! Elapsed time: ${(endTime - startTime) / 1000}s\n================\n\n${finalResult}\n\n================`
59
+ ))
60
+
44
61
  } catch (error) {
45
- spinner.fail(chalk.red(`🤖 Pod 042: [警告] 识别程序失败。原因:${error}`))
62
+ // 失败报告
63
+ spinner.fail(chalk.red(language(
64
+ `🤖 Pod 042: [警告] 识别程序失败。原因:${error}`,
65
+ `🤖 Pod 042: [Warning] Recognition failed. Reason: ${error}`
66
+ )))
46
67
  } finally {
47
68
  if (fs.existsSync(filePath)) {
48
69
  fs.unlinkSync(filePath)
@@ -51,4 +72,4 @@ const watchPart = () => {
51
72
  })
52
73
  }
53
74
 
54
- module.exports = watchPart
75
+ module.exports = watchPart
@@ -8,4 +8,5 @@ responseStr是后端的真实响应。
8
8
  请只输出一个JSON对象, Key为前端猜测的旧名字, Value为Response里的真实新名字。
9
9
  例如:{"key_1": "key1"}
10
10
  如果没有找到对应的,请不要包含在结果中。
11
- `.trim()
11
+ 注意:⚠️⚠️如果有语义相近的两个字段优先取中文字段,比如"createByName"和"createBy",则取"createByName"
12
+ 。`.trim()
@@ -1,16 +1,15 @@
1
1
  const fs = require('fs')
2
2
  const path = require('path')
3
3
  const JSON5 = require('json5')
4
- const sharp = require('sharp') // 图片压缩库,用于处理截图
4
+ const sharp = require('sharp')
5
5
  const chalk = require('chalk')
6
6
  const OpenAI = require('openai')
7
- const { getConfig } = require('../utils/utils.js')
7
+ const { getConfig, language } = require('../utils/utils.js') // 导入语言助手
8
8
 
9
9
  let client = null
10
10
 
11
11
  /**
12
12
  * 获取 OpenAI 客户端实例(单例模式)
13
- * 优先使用项目根目录的 .env 文件,其次使用包内的 .env
14
13
  */
15
14
  const getOpenAI = () => {
16
15
  if (!client) {
@@ -26,18 +25,18 @@ const getOpenAI = () => {
26
25
 
27
26
  /**
28
27
  * 调用 AI 模型并解析响应
29
- * @param {string} model - 模型名称(如 qwen3.5-plus, qwen-turbo)
30
- * @param {Array} messages - 对话消息数组
31
- * @param {number} retryCount - 重试次数
32
- * @returns {Object} 解析后的 JSON 对象
33
28
  */
34
29
  const askAI = async (model, messages, retryCount = 0) => {
35
- // 最多重试 3 次,避免无限循环
36
- if (retryCount > 3) throw new Error('YoRHa 司令部连接中断:请检查网络状态或黑盒共鸣情况。')
30
+ // 最终重试失败:YoRHa 司令部连接中断
31
+ if (retryCount > 3) {
32
+ throw new Error(language(
33
+ 'YoRHa 司令部连接中断:请检查网络状态或黑盒共鸣情况。',
34
+ 'YoRHa Command Link Severed: Check network status or Black Box resonance.'
35
+ ))
36
+ }
37
37
 
38
38
  try {
39
39
  const openai = getOpenAI()
40
- // 调用 AI 模型,强制返回 JSON 格式
41
40
  const response = await openai.chat.completions.create({
42
41
  model,
43
42
  messages,
@@ -45,21 +44,26 @@ const askAI = async (model, messages, retryCount = 0) => {
45
44
  })
46
45
 
47
46
  let raw = response.choices[0].message.content.trim()
48
-
49
- // 清理可能的 Markdown 标记(```json ... ```)
50
47
  raw = raw.replace(/^```json\n?/, '').replace(/\n?```$/, '')
51
-
52
- // 提取 JSON 内容(支持对象和数组)
53
48
  const match = raw.match(/[\{\[][\s\S]*[\}\]]/)
54
-
55
- // 使用 JSON5 解析(支持单引号、注释等非标准 JSON)
56
49
  return JSON5.parse(match ? match[0] : raw)
50
+
57
51
  } catch (err) {
58
52
  const isAuthError = err.message.includes('401') || err.message.includes('402');
59
53
  if (isAuthError) {
60
- throw new Error('[系统警告] YoRHa 司令部拒绝访问:API Key 无效或额度耗尽,请检查!');
54
+ // 权限错误:司令部拒绝访问
55
+ throw new Error(language(
56
+ '[系统警告] YoRHa 司令部拒绝访问:API Key 无效或额度耗尽,请检查!',
57
+ '[SYSTEM ALERT] YoRHa Command Center Access Denied: Invalid API Key or quota exhausted!'
58
+ ));
61
59
  }
62
- console.warn(chalk.yellow(`[系统警告] 神经云网络连接不稳,正在尝试重新链接... (第 ${retryCount + 1} 次重试)`), err.message)
60
+
61
+ // 重试警告:神经云网络连接不稳
62
+ console.warn(chalk.yellow(language(
63
+ `[系统警告] 神经云网络连接不稳,正在尝试重新链接... (第 ${retryCount + 1} 次重试)`,
64
+ `[SYSTEM ALERT] Neural Network instability detected. Attempting to re-establish link... (Retry #${retryCount + 1})`
65
+ )), err.message)
66
+
63
67
  return askAI(model, messages, retryCount + 1)
64
68
  }
65
69
  }
@@ -67,9 +71,6 @@ const askAI = async (model, messages, retryCount = 0) => {
67
71
  module.exports = {
68
72
  /**
69
73
  * 生成 Mock 数据
70
- * @param {Array} columns - 表格列配置
71
- * @param {string} fileName - 文件名
72
- * @returns {string} 生成的 Mock 数据字符串
73
74
  */
74
75
  generateMock: async (columns, fileName) => {
75
76
  const config = getConfig()
@@ -85,16 +86,12 @@ module.exports = {
85
86
  },
86
87
 
87
88
  /**
88
- * 识别页面截图并提取结构信息
89
- * @param {string} prompt - 提示词
90
- * @param {string} filePath - 截图文件路径
91
- * @returns {Object} 识别后的页面配置对象
89
+ * 识别页面截图
92
90
  */
93
91
  recognizePage: async (prompt, filePath) => {
94
92
  const config = getConfig()
95
93
  const { UI_DESIGNER } = require('../prompts/system.js')
96
94
 
97
- // 压缩图片到 1280px 宽度(避免超出 token 限制),质量 80%
98
95
  const compressedBuffer = await sharp(filePath)
99
96
  .resize(1280, null, { withoutEnlargement: true })
100
97
  .jpeg({ quality: 80 })
@@ -117,10 +114,7 @@ module.exports = {
117
114
  },
118
115
 
119
116
  /**
120
- * 对齐 Response 接口字段与前端字段
121
- * @param {string} responseStr - 真实接口响应数据
122
- * @param {string} resourceStr - 前端 resource.js 内容
123
- * @returns {Object} 字段映射对象 { oldName: newName }
117
+ * 对齐 Response 接口字段
124
118
  */
125
119
  alignResponseFields: async (responseStr, resourceStr) => {
126
120
  const config = getConfig()
@@ -1,6 +1,9 @@
1
1
  const fs = require('fs')
2
2
  const path = require('path')
3
3
 
4
+ const isCN = Intl.DateTimeFormat().resolvedOptions().locale.includes('zh')
5
+ const language = (zh, en) => (isCN ? zh : en)
6
+
4
7
  /**
5
8
  * 获取项目配置信息
6
9
  * 优先读取项目根目录的 config.js,如果不存在则使用默认配置
@@ -106,4 +109,4 @@ const copyTemplateDir = (options, templateSubDir, targetSubDir) => {
106
109
  }
107
110
 
108
111
  // 导出工具函数
109
- module.exports = { getConfig, cleanCode, getExistingMenus, copyTemplateDir, generateSmartImports }
112
+ module.exports = { language, getConfig, cleanCode, getExistingMenus, copyTemplateDir, generateSmartImports }