@yorha2b-lab/autodev 1.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/.env.example ADDED
@@ -0,0 +1,12 @@
1
+ # AI模型API配置
2
+ # 请替换为你的实际API密钥
3
+ API_KEY=your_api_key_here
4
+
5
+ # API基础URL
6
+ # 如果使用OpenAI官方API,可以设置为: https://api.openai.com/v1
7
+ # 如果使用其他兼容API,请设置对应的基础URL
8
+ BASE_URL=your_api_base_url_here
9
+
10
+ # 可选:自定义模型配置
11
+ # VISION_MODEL=qwen3.5-plus
12
+ # TEXT_MODEL=qwen-turbo
@@ -0,0 +1,118 @@
1
+ # 贡献指南
2
+
3
+ 感谢您对 Auto CRUD Copilot 项目的关注!我们欢迎任何形式的贡献,包括但不限于:
4
+
5
+ - 🐛 报告Bug
6
+ - 💡 提出新功能建议
7
+ - 📝 改进文档
8
+ - 🔧 提交代码修复
9
+ - 🎨 添加新模板或组件
10
+
11
+ ## 🚀 如何贡献
12
+
13
+ ### 报告Bug
14
+
15
+ 如果您发现了Bug,请通过以下步骤报告:
16
+
17
+ 1. 检查是否已有相关的Issue
18
+ 2. 如果没有,请创建新的Issue
19
+ 3. 在Issue中提供以下信息:
20
+ - 问题描述
21
+ - 复现步骤
22
+ - 期望行为
23
+ - 实际行为
24
+ - 环境信息(操作系统、Node版本等)
25
+ - 相关截图或错误日志
26
+
27
+ ### 提出新功能建议
28
+
29
+ 1. 检查是否已有相关的Issue或讨论
30
+ 2. 如果没有,请创建新的Issue
31
+ 3. 在Issue中详细描述:
32
+ - 功能描述
33
+ - 使用场景
34
+ - 预期效果
35
+ - 可能的实现方案
36
+
37
+ 特别欢迎 Vue / Angular 开发者提交对应的 Template 和 Compiler 实现! 我们已经为您预留好了插件接口。
38
+
39
+ ### 提交代码
40
+
41
+ 1. Fork 本仓库
42
+ 2. 创建您的特性分支 (`git checkout -b feature/AmazingFeature`)
43
+ 3. 提交您的更改 (`git commit -m 'Add some AmazingFeature'`)
44
+ 4. 推送到分支 (`git push origin feature/AmazingFeature`)
45
+ 5. 创建一个 Pull Request
46
+
47
+ ## 📋 开发指南
48
+
49
+ ### 环境准备
50
+
51
+ 1. 克隆仓库
52
+ ```bash
53
+ git clone https://github.com/yorha2b-lab/auto-crud-copilot.git
54
+ cd auto-crud-copilot
55
+ ```
56
+
57
+ 2. 安装依赖
58
+ ```bash
59
+ npm install
60
+ ```
61
+
62
+ 3. 创建环境变量文件
63
+ ```bash
64
+ cp .env.example .env
65
+ # 编辑 .env 文件,填入您的API密钥
66
+ ```
67
+
68
+ ### 项目结构
69
+
70
+ ```
71
+ auto-crud-copilot/
72
+ ├── bin/ # 可执行文件
73
+ ├── src/
74
+ │ ├── commands/ # 命令实现
75
+ │ ├── core/ # 核心逻辑
76
+ │ ├── prompts/ # AI提示词
77
+ │ ├── services/ # 服务层
78
+ │ └── utils/ # 工具函数
79
+ ├── templates/ # 代码模板
80
+ │ ├── react/
81
+ │ ├── vue/
82
+ │ └── angular/
83
+ └── config.js # 配置文件
84
+ ```
85
+
86
+ - 提交信息遵循[Conventional Commits](https://www.conventionalcommits.org/zh-hans/v1.0.0/)规范
87
+
88
+ ### 测试
89
+
90
+ 在提交代码前,请确保:
91
+
92
+ 1. 代码通过所有测试
93
+ 2. 代码符合项目的编码规范
94
+
95
+ ## 📝 Pull Request 流程
96
+
97
+ 1. 确保您的PR描述清晰,说明了更改的目的和实现方式
98
+ 2. 确保您的代码没有合并冲突
99
+ 3. 确保所有测试通过
100
+ 4. 等待维护者审查和合并
101
+
102
+ ## 🏷️ 发布流程
103
+
104
+ 项目维护者负责发布新版本,遵循[语义化版本](https://semver.org/lang/zh-CN/)规范。
105
+
106
+ ## 🤝 行为准则
107
+
108
+ 请遵守我们的行为准则,保持友好和尊重的交流环境。
109
+
110
+ ## 📞 联系方式
111
+
112
+ 如有任何问题,请通过以下方式联系我们:
113
+
114
+ - 创建Issue
115
+
116
+ ---
117
+
118
+ 再次感谢您的贡献!🎉
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 yorha2b-Lab
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,173 @@
1
+ # Auto CRUD Copilot
2
+
3
+ [![NPM Version](https://img.shields.io/npm/v/@yorha2b-lab/autodev.svg?style=flat-square)](https://www.npmjs.com/package/@yorha2b-lab/autodev)
4
+ [![NPM Downloads](https://img.shields.io/npm/dm/@yorha2b-lab/autodev.svg?style=flat-square)](https://www.npmjs.com/package/@yorha2b-lab/autodev)
5
+ [![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)
6
+ [![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)
7
+
8
+ 基于视觉大模型的前端(Umi+Antd)全自动 CRUD 代码生成器 🚀
9
+
10
+ ![Demo](https://via.placeholder.com/800x400?text=Coming+Soon)
11
+
12
+ 拒绝重复劳动!为什么你需要 Auto CRUD Copilot?
13
+ 告别手写 Table/Form:截图即代码,提升你的开发效率,告别烦人的复制粘贴。
14
+ 拒绝接口联调痛苦:自动读取 Swagger,AI 帮你抹平前后端字段命名差异。
15
+ 架构解耦:核心逻辑与 UI 框架分离,React/Vue/Angular 均可适配。
16
+
17
+ ## ✨ 特性
18
+
19
+ - 🖼️ **视觉识别**: 通过截图自动识别页面结构,生成对应的CRUD代码
20
+ - 🔧 **智能生成**: 支持React、Vue、Angular等多种前端框架模板
21
+ - 📊 **表格组件**: 自动生成可编辑、可排序、可筛选的数据表格
22
+ - 📝 **表单组件**: 智能生成搜索表单和模态框表单
23
+ - 🔌 **API对齐**: 自动对齐Swagger接口与前端字段映射
24
+ - 🎨 **UI组件**: 支持多种UI组件类型(输入框、选择器、日期等)
25
+ - 📱 **响应式**: 生成的代码支持响应式布局
26
+
27
+ ## 🚀 快速开始
28
+
29
+ ### 安装
30
+
31
+ ```bash
32
+ npm install -g auto-crud-copilot
33
+ ```
34
+
35
+ ### 环境配置
36
+
37
+ 创建 `.env` 文件并配置以下环境变量:
38
+
39
+ ```bash
40
+ # AI模型API配置
41
+ API_KEY=your_api_key_here
42
+ BASE_URL=your_api_base_url_here
43
+ ```
44
+
45
+ ### 基本使用
46
+
47
+ 1. **生成完整CRUD页面**
48
+
49
+ ```bash
50
+ # 将页面截图放入screenShot目录
51
+ autodev watch:page
52
+ ```
53
+
54
+ 2. **生成局部UI组件**
55
+
56
+ ```bash
57
+ # 将组件截图放入screenPart目录
58
+ autodev watch:part
59
+ ```
60
+
61
+ 3. **对齐API字段**
62
+
63
+ ```bash
64
+ # 将Swagger文档放入swagger目录
65
+ autodev watch:api
66
+ ```
67
+
68
+ ## 📖 详细使用指南
69
+
70
+ ### watch:page 命令
71
+
72
+ 监听 `screenShot` 目录下的截图文件,自动生成完整的增删改查页面。
73
+
74
+ ```bash
75
+ autodev watch:page -t react
76
+ ```
77
+
78
+ 参数说明:
79
+ - `-t, --template <type>`: 指定前端框架模板,默认为 `react`
80
+
81
+ ### watch:part 命令
82
+
83
+ 监听 `screenPart` 目录下的截图文件,自动生成局部UI组件。
84
+
85
+ ```bash
86
+ autodev watch:part -t vue
87
+ ```
88
+
89
+ ### watch:api 命令
90
+
91
+ 监听 `swagger` 目录下的API文档,自动对齐真实接口字段。
92
+
93
+ ```bash
94
+ autodev watch:api
95
+ ```
96
+
97
+ ## 📁 项目结构
98
+
99
+ ```
100
+ your-project/
101
+ ├── screenShot/ # 页面截图目录
102
+ ├── screenPart/ # 组件截图目录
103
+ ├── swagger/ # API文档目录
104
+ ├── mock/ # 生成的Mock数据
105
+ ├── src/
106
+ │ ├── pages/ # 生成的页面代码
107
+ │ ├── components/ # 生成的组件代码
108
+ │ ├── hooks/ # 生成的Hook代码
109
+ └── config.js # 配置文件
110
+ ```
111
+
112
+ ## ⚙️ 配置
113
+
114
+ 在 `config.js` 中可以配置以下选项:
115
+
116
+ ```javascript
117
+ module.exports = {
118
+ // 是否需要生成 Mock 数据
119
+ needMock: false,
120
+
121
+ // 使用的 AI 模型类型
122
+ visionModel: 'qwen3.5-plus',
123
+ textModel: 'qwen-turbo',
124
+
125
+ // 目标项目的目录路径
126
+ hooksDir: 'src/hooks',
127
+ pagesDir: 'src/pages',
128
+ utilsDir: 'src/utils',
129
+ componentsDir: 'src/components',
130
+ }
131
+ ```
132
+
133
+ ## 🎯 支持的UI组件
134
+
135
+ - 输入框 (Input)
136
+ - 数字输入框 (InputNumber)
137
+ - 选择器 (Select)
138
+ - 单选框 (Radio)
139
+ - 复选框 (Checkbox)
140
+ - 日期选择器 (DatePicker)
141
+ - 日期范围选择器 (RangePicker)
142
+ - 级联选择器 (Cascader)
143
+ - 自动完成 (AutoComplete)
144
+ - 文本域 (TextArea)
145
+ - 文件上传 (Upload)
146
+
147
+ ## 🤝 贡献
148
+
149
+ 欢迎贡献代码!请阅读 [CONTRIBUTING.md](CONTRIBUTING.md) 了解如何参与项目开发。
150
+
151
+ ## 📄 许可证
152
+
153
+ 本项目采用 [MIT](LICENSE) 许可证。
154
+
155
+ ## 🙏 致谢
156
+
157
+ 感谢以下开源项目:
158
+
159
+ - [OpenAI](https://openai.com/) - 提供强大的AI能力
160
+ - [Ant Design](https://ant.design/) - 优秀的企业级UI设计语言
161
+ - [UmiJS](https://umijs.org/) - 企业级前端应用框架
162
+
163
+ ## 📞 联系方式
164
+
165
+ 如有问题或建议,欢迎提交 [Issue](https://github.com/yorha2b-lab/auto-crud-copilot/issues)。
166
+
167
+ ## 🛠️ 常见问题 (FAQ)
168
+ Q: 这个工具收费吗?
169
+ A: 工具本身开源免费,但调用的 AI 模型(如 GPT-4v, Qwen-VL)可能需要你配置自己的 API Key。建议使用阿里云 Qwen-VL 等高性价比模型。
170
+
171
+ ---
172
+
173
+ ⭐ 如果这个项目对你有帮助,请给个Star支持一下!
package/bin/autodev.js ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env node
2
+ const path = require('path')
3
+ const chalk = require('chalk')
4
+ const figlet = require('figlet')
5
+
6
+ console.log(chalk.cyan(figlet.textSync('AutoDev', { horizontalLayout: 'full' })))
7
+ console.log(chalk.gray('--------------------------------------------------'))
8
+ console.log(chalk.white(' [System] ') + chalk.green('YoRHa No.2 Type B Unit: ') + chalk.cyan('Online'))
9
+ console.log(chalk.white(' [Mission] ') + chalk.yellow('Generate Frontend CRUD: ') + chalk.cyan('Awaiting Command'))
10
+ console.log(chalk.gray('--------------------------------------------------\n'))
11
+
12
+ require("dotenv").config({
13
+ path: path.resolve(__dirname, '../.env')
14
+ })
15
+
16
+ const { program } = require('commander')
17
+ const watchApi = require('../src/commands/watch-api')
18
+ const watchPage = require('../src/commands/watch-page')
19
+ const watchPart = require('../src/commands/watch-part')
20
+
21
+ program.version('1.0.0').description('AI驱动的前端CRUD代码生成器')
22
+
23
+ program.option('-t, --template <type>', '指定前端框架模板', 'react')
24
+
25
+ program
26
+ .command('watch:page')
27
+ .description('监听截图,自动生成完整的增删改查页面')
28
+ .action(() => watchPage(program.opts()))
29
+
30
+ program
31
+ .command('watch:part')
32
+ .description('监听截图,自动生成局部 UI 组件')
33
+ .action(() => watchPart(program.opts()))
34
+
35
+ program
36
+ .command('watch:api')
37
+ .description('监听 Swagger,自动对齐真实接口字段')
38
+ .action(() => watchApi())
39
+
40
+ program.parse(process.argv)
package/config.js ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Auto CRUD Copilot 配置文件
3
+ *
4
+ * 此文件包含了项目运行所需的所有配置项
5
+ */
6
+
7
+ module.exports = {
8
+ // 是否需要生成 Mock 数据
9
+ needMock: false,
10
+
11
+ // 使用的 AI 模型类型
12
+ visionModel: 'qwen3.5-plus',
13
+
14
+ textModel: 'qwen-turbo',
15
+
16
+ // 目标项目的 hooks 目录路径
17
+ hooksDir: 'src/hooks',
18
+
19
+ // 目标项目的 pages 目录路径
20
+ pagesDir: 'src/pages',
21
+
22
+ // 目标项目的 utils 目录路径
23
+ utilsDir: 'src/utils',
24
+
25
+ // 目标项目的 components 目录路径
26
+ componentsDir: 'src/components',
27
+
28
+ }
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@yorha2b-lab/autodev",
3
+ "version": "1.0.0",
4
+ "description": "基于视觉大模型的前端(Umi+Antd)全自动 CRUD 代码生成器",
5
+ "bin": {
6
+ "autodev": "bin/autodev.js"
7
+ },
8
+ "scripts": {
9
+ "test": "echo \"Error: no test specified\" && exit 1"
10
+ },
11
+ "keywords": [
12
+ "crud",
13
+ "generator",
14
+ "ai",
15
+ "vision",
16
+ "react",
17
+ "vue",
18
+ "angular",
19
+ "antd",
20
+ "umi",
21
+ "code-generation",
22
+ "automation",
23
+ "frontend",
24
+ "scaffold"
25
+ ],
26
+ "author": "yorha2b-Lab",
27
+ "license": "MIT",
28
+ "type": "commonjs",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/yorha2b-lab/auto-crud-copilot.git"
32
+ },
33
+ "bugs": {
34
+ "url": "https://github.com/yorha2b-lab/auto-crud-copilot/issues"
35
+ },
36
+ "homepage": "https://github.com/yorha2b-lab/auto-crud-copilot#readme",
37
+ "dependencies": {
38
+ "chokidar": "^5.0.0",
39
+ "commander": "^14.0.3",
40
+ "dotenv": "^17.3.1",
41
+ "handlebars": "^4.7.8",
42
+ "json-stringify-pretty-compact": "^4.0.0",
43
+ "json5": "^2.2.3",
44
+ "openai": "^6.24.0",
45
+ "sharp": "^0.34.5",
46
+ "chalk": "^4.1.2",
47
+ "figlet": "^1.10.0",
48
+ "ora": "^5.4.1"
49
+ }
50
+ }
@@ -0,0 +1,41 @@
1
+ const fs = require('fs')
2
+ const path = require('path')
3
+ const chokidar = require('chokidar')
4
+ const config = require('../../config.js')
5
+ const { alignSwaggerFields } = require('../services/llm.js')
6
+
7
+ const watchApi = () => {
8
+ const watcher = chokidar.watch('./swagger', {
9
+ persistent: true,
10
+ ignored: /(^|[\/\\])\../,
11
+ awaitWriteFinish: {
12
+ stabilityThreshold: 500,
13
+ pollInterval: 100
14
+ }
15
+ })
16
+
17
+ watcher.on('add', async filePath => {
18
+ const startTime = Date.now()
19
+ const fileName = path.basename(filePath, path.extname(filePath))
20
+ console.log(`🚀 检测到新文件: ${fileName}, 开始识别...`)
21
+
22
+ try {
23
+ const swaggerStr = fs.readFileSync(filePath, 'utf8')
24
+ let resourceStr = fs.readFileSync(`./${config.pagesDir}/${fileName}/resource.js`, 'utf8')
25
+ const result = await alignSwaggerFields(swaggerStr, resourceStr)
26
+ Object.entries(result).forEach(([oldField, newField]) => {
27
+ if (oldField === newField) return
28
+ const regex = new RegExp(`(dataIndex|name)\\s*:\\s*['"]${oldField}['"]`, 'g')
29
+ resourceStr = resourceStr.replace(regex, `$1: '${newField}'`)
30
+ })
31
+ fs.writeFileSync(path.join(`./${config.pagesDir}/${fileName}/resource.js`), resourceStr.replace(/"(\w+)":/g, '$1:').replace(/"/g, "'"))
32
+ const endTime = Date.now()
33
+ //fs.unlinkSync(filePath)
34
+ console.log(`识别完成:耗时 ${(endTime - startTime) / 1000} 秒`)
35
+ } catch (error) {
36
+ console.error(`识别 ${fileName} 失败:`, error)
37
+ }
38
+ })
39
+ }
40
+
41
+ module.exports = watchApi
@@ -0,0 +1,94 @@
1
+ const fs = require('fs')
2
+ const ora = require('ora')
3
+ const path = require('path')
4
+ const chokidar = require('chokidar')
5
+ const Handlebars = require('handlebars')
6
+ const config = require('../../config.js')
7
+ const pagePrompt = require('../prompts/watch-page.js')
8
+ const { createTaskQueue } = require('../core/task-queue')
9
+ const stringify = require('json-stringify-pretty-compact')
10
+ const { recognizePage, generateMock } = require('../services/llm.js')
11
+ const { copyHooks, copyComponents, getExistingMenus } = require('../utils/utils.js')
12
+
13
+ const watchPage = options => {
14
+
15
+ const compilerPath = path.join(__dirname, `../core/${options.template}-compiler.js`)
16
+
17
+ if (!fs.existsSync(compilerPath)) {
18
+ console.error(`\n❌ 糟糕!暂不支持 [${options.template}] 框架的自动生成。`)
19
+ console.log(`💡 提示:目前仅内置了 react 编译器。`)
20
+ console.log(`🚀 强烈欢迎社区大佬提 PR 补充 ${options.template}-compiler.js !\n`)
21
+ return
22
+ }
23
+
24
+ const { index, resource } = require(compilerPath)
25
+
26
+ try {
27
+ copyHooks(options)
28
+ copyComponents(options)
29
+ } catch (error) {
30
+ console.error('❌ 程序运行出错:', error)
31
+ }
32
+ /**
33
+ * 创建任务队列,限制并发数为 2。
34
+ *
35
+ * 为什么限制并发?
36
+ * 1. 视觉大模型的 API 计费昂贵且通常有严格的 TPM/RPM (每分钟请求数) 限流。
37
+ * 2. 如果用户一次性丢入 5 张截图,全量并发极易触发 HTTP 429 Too Many Requests 报错。
38
+ * 3. 并发设为 2 是实测下来速度与稳定性的最佳平衡点。
39
+ */
40
+ const queue = createTaskQueue(2)
41
+ const menus = getExistingMenus()
42
+ const tplDir = path.join(__dirname, `../../templates/${options.template}`)
43
+ const indexTpl = Handlebars.compile(fs.readFileSync(path.join(tplDir, 'index.hbs'), 'utf-8'))
44
+ const resourceTpl = Handlebars.compile(fs.readFileSync(path.join(tplDir, 'resource.hbs'), 'utf-8'))
45
+ queue.onIdle(() => {
46
+ console.log('💾 所有排队的截图已处理完成,正在统一更新 menu 配置...')
47
+ const constantDir = path.join(process.cwd(), config.utilsDir)
48
+ if (!fs.existsSync(constantDir)) fs.mkdirSync(constantDir, { recursive: true })
49
+ fs.writeFileSync(path.join(constantDir, 'constant.js'), `export const menus = ${stringify.default(menus, { indent: 4, maxLength: 50 })}`)
50
+ console.log('✅ Menu 配置更新成功!')
51
+ })
52
+ const watcher = chokidar.watch('./screenShot', {
53
+ persistent: true,
54
+ ignored: /(^|[\/\\])\../,
55
+ awaitWriteFinish: {
56
+ stabilityThreshold: 500,
57
+ pollInterval: 100
58
+ }
59
+ })
60
+ watcher.on('add', filePath => {
61
+ queue.add(async () => {
62
+ const startTime = Date.now()
63
+ const mockDir = path.join(process.cwd(), 'mock')
64
+ const fileName = path.basename(filePath, path.extname(filePath))
65
+ const targetDir = path.join(process.cwd(), config.pagesDir, fileName)
66
+
67
+ try {
68
+ if (!fs.existsSync(mockDir)) fs.mkdirSync(mockDir, { recursive: true })
69
+ if (!fs.existsSync(targetDir)) fs.mkdirSync(targetDir, { recursive: true })
70
+ const spinner = ora(`🚀 Pod 042: 开始处理排队任务: ${fileName}...`)
71
+ spinner.start()
72
+ const pageConfig = await recognizePage(pagePrompt, filePath)
73
+ fs.writeFileSync(path.join(targetDir, 'resource.js'), resource({ pageConfig, resourceTpl }))
74
+ fs.writeFileSync(path.join(targetDir, 'index.js'), index({ fileName, indexTpl, pageConfig }))
75
+ if (!menus.find(m => m.key === fileName)) {
76
+ menus.push({ label: fileName, key: fileName })
77
+ }
78
+ if (config.needMock) {
79
+ console.log(`🚀 开始生成 ${fileName} 的 Mock 数据...`)
80
+ const rawContent = await generateMock(pageConfig.table.columns, fileName)
81
+ fs.writeFileSync(path.join(mockDir, `${fileName}.js`), `export default ${stringify.default(rawContent, { indent: 4, maxLength: 200 })}`.replaceAll(`"'`, `"`).replaceAll(`'"`, `"`))
82
+ console.log(`生成 ${fileName} 的 Mock 数据耗时: ${(Date.now() - startTime) / 1000} 秒`)
83
+ }
84
+ const endTime = Date.now()
85
+ //fs.unlinkSync(filePath)
86
+ spinner.succeed(`✅ Pod 042: 模块 [${fileName}] 装配完成!耗时 ${(endTime - startTime) / 1000} 秒`)
87
+ } catch (error) {
88
+ console.error(`❌ 处理失败: ${filePath}`, error)
89
+ }
90
+ })
91
+ })
92
+ }
93
+
94
+ module.exports = watchPage
@@ -0,0 +1,46 @@
1
+ const fs = require('fs')
2
+ const chokidar = require('chokidar')
3
+ const partPrompt = require('../prompts/watch-part.js')
4
+ const { recognizePage } = require('../services/llm.js')
5
+ const stringify = require('json-stringify-pretty-compact')
6
+
7
+ const watchPart = () => {
8
+ const watcher = chokidar.watch('./screenPart', {
9
+ persistent: true,
10
+ ignored: /(^|[\/\\])\../,
11
+ awaitWriteFinish: {
12
+ stabilityThreshold: 500,
13
+ pollInterval: 100
14
+ }
15
+ })
16
+ watcher.on('add', async filePath => {
17
+ try {
18
+ const startTime = Date.now()
19
+ console.log(`🚀 检测到新图片: 开始识别...`)
20
+ const pageConfig = await recognizePage(partPrompt, filePath)
21
+ const optionDict = pageConfig.optionDict || {}
22
+ delete pageConfig.optionDict
23
+ let mainConfigStr = stringify.default(pageConfig, { indent: 4, maxLength: 200 })
24
+ .replace(/"(\w+)":/g, '$1:')
25
+ .replace(/"/g, "'")
26
+ .replace(/['"]_CODE_([\s\S]*?)_CODE_['"]/g, '$1')
27
+ .replace(/_CODE_/g, '')
28
+ let optionsCodeStr = ''
29
+ Object.keys(optionDict).forEach(key => {
30
+ const varName = key.replace('_CODE_', '')
31
+ const optionsArray = optionDict[key]
32
+ const arrayItemsStr = optionsArray.map(opt => ` { label: '${opt.label}', value: '${opt.value}' }`).join(',\n')
33
+ optionsCodeStr += `\nexport const ${varName} = [\n${arrayItemsStr}\n]\n`
34
+ })
35
+ const finalResult = `${mainConfigStr}\n${optionsCodeStr}`
36
+ const endTime = Date.now()
37
+ console.log(`🎉 识别完成!耗时 ${(endTime - startTime) / 1000} 秒\n================\n\n${finalResult}\n\n================`)
38
+ } catch (error) {
39
+ console.error('识别图片失败', error)
40
+ } finally {
41
+ fs.unlinkSync(filePath)
42
+ }
43
+ })
44
+ }
45
+
46
+ module.exports = watchPart
@@ -0,0 +1,53 @@
1
+ const Handlebars = require('handlebars')
2
+ const stringify = require('json-stringify-pretty-compact')
3
+ const { cleanCode, generateSmartImports } = require('../utils/utils.js')
4
+
5
+ Handlebars.registerHelper('stringify', (context, maxLength = 200) => context ? new Handlebars.SafeString(stringify.default(context, { indent: 4, maxLength })) : '[]')
6
+
7
+ const resource = ({ pageConfig, resourceTpl }) => {
8
+ const hasTabs = pageConfig.tabs?.length > 0
9
+
10
+ const viewData = {
11
+ hasTabs,
12
+ tabs: pageConfig.tabs,
13
+ formItems: pageConfig.formItems,
14
+ formItemsData: hasTabs ? Object.fromEntries(pageConfig.tabs.map(tab => [tab.key, pageConfig.formItems])) : pageConfig.formItems,
15
+ columnsData: hasTabs ? Object.fromEntries(pageConfig.tabs.map(tab => [tab.key, pageConfig.table.columns])) : pageConfig.table.columns,
16
+ dictBlocks: pageConfig.formItems
17
+ ?.filter(item => item.type === 'select')
18
+ ?.map(item => ({ name: item.options.replace('_CODE_', ''), data: pageConfig.optionDict[item.options] ?? [] }))
19
+ }
20
+
21
+ const rawCode = resourceTpl(viewData)
22
+ return cleanCode(rawCode)
23
+ }
24
+
25
+ const index = ({ fileName, indexTpl, pageConfig }) => {
26
+ const hasTabs = pageConfig.tabs?.length > 0
27
+ const hasOperate = pageConfig.table.operation?.length > 0
28
+
29
+ let columnsValue = hasTabs ? 'columns[activeKey]' : 'columns'
30
+ if (hasOperate) {
31
+ columnsValue = `${columnsValue}.concat(operate)`
32
+ }
33
+
34
+ const viewData = {
35
+ hasTabs,
36
+ fileName,
37
+ hasOperate,
38
+ columnsValue,
39
+ tabs: pageConfig.tabs,
40
+ hasPagination: pageConfig.table.pagination,
41
+ operations: pageConfig.table.operation || [],
42
+ hasRowSelection: pageConfig.table.rowSelection,
43
+ hasStaticInfo: pageConfig.table.staticInfo?.has,
44
+ staticInfoText: pageConfig.table.staticInfo?.text,
45
+ functionButtons: pageConfig.functionButton?.filter(item => !['查询', '重置'].includes(item.btn)) || []
46
+ }
47
+
48
+ const bodyCode = indexTpl(viewData)
49
+ const importsStr = generateSmartImports(bodyCode, hasTabs)
50
+ return cleanCode(`${importsStr}\n\n${bodyCode}`)
51
+ }
52
+
53
+ module.exports = { index, resource }