@yorha2b-lab/autodev 3.2.0 → 3.3.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.
@@ -21,7 +21,7 @@
21
21
  "columns": [
22
22
  {
23
23
  "title": "职业",
24
- "dataIndex": "jobName",
24
+ "dataIndex": "job",
25
25
  "type": "text"
26
26
  },
27
27
  {
@@ -29,35 +29,50 @@
29
29
  "dataIndex": "firstAwakening",
30
30
  "type": "text"
31
31
  },
32
+ {
33
+ "title": "一觉立绘",
34
+ "dataIndex": "firstAwakeningImage",
35
+ "type": "image"
36
+ },
32
37
  {
33
38
  "title": "二觉",
34
39
  "dataIndex": "secondAwakening",
35
40
  "type": "text"
36
41
  },
42
+ {
43
+ "title": "二觉立绘",
44
+ "dataIndex": "secondAwakeningImage",
45
+ "type": "image"
46
+ },
37
47
  {
38
48
  "title": "三觉",
39
49
  "dataIndex": "thirdAwakening",
40
50
  "type": "text"
41
51
  },
52
+ {
53
+ "title": "三觉立绘",
54
+ "dataIndex": "thirdAwakeningImage",
55
+ "type": "image"
56
+ },
42
57
  {
43
58
  "title": "武器类型",
44
59
  "dataIndex": "weaponType",
45
- "type": "enum"
60
+ "type": "tag"
46
61
  },
47
62
  {
48
63
  "title": "职业类型",
49
64
  "dataIndex": "jobType",
50
- "type": "enum"
65
+ "type": "tag"
51
66
  },
52
67
  {
53
68
  "title": "登场时间",
54
- "dataIndex": "launchDate",
69
+ "dataIndex": "releaseDate",
55
70
  "type": "date"
56
71
  }
57
72
  ],
58
- "pagination": true,
59
- "expandable": true,
60
73
  "rowSelection": true,
74
+ "expandable": true,
75
+ "pagination": true,
61
76
  "operation": [
62
77
  {
63
78
  "label": "查看",
@@ -76,7 +91,7 @@
76
91
  "formItems": [
77
92
  {
78
93
  "label": "转职职业",
79
- "name": "transferJob",
94
+ "name": "jobTransfer",
80
95
  "type": "select"
81
96
  },
82
97
  {
@@ -91,62 +106,42 @@
91
106
  },
92
107
  {
93
108
  "label": "觉醒名称关键词",
94
- "name": "awakeningNameKeyword"
109
+ "name": "awakeningKeyword",
110
+ "type": "text"
95
111
  },
96
112
  {
97
113
  "label": "登场时间",
98
- "name": "launchDateStart,launchDateEnd",
114
+ "name": "releaseDateStart,releaseDateEnd",
99
115
  "type": "daterange"
100
116
  }
101
117
  ],
102
- "staticInfo": {
103
- "has": false,
104
- "text": ""
105
- },
118
+ "staticInfo": {},
106
119
  "optionDict": {
107
- "transferJobOptions": [
120
+ "jobTransferOptions": [
108
121
  {
109
- "label": "剑魂",
110
- "value": "soulBender"
111
- },
112
- {
113
- "label": "狂战士",
114
- "value": "berserker"
122
+ "label": "选择转职",
123
+ "value": "selectJob"
115
124
  }
116
125
  ],
117
- "weaponTypeOptions": [
118
- {
119
- "label": "光剑",
120
- "value": "lightSword"
121
- },
122
- {
123
- "label": "太刀",
124
- "value": "katana"
125
- },
126
- {
127
- "label": "巨剑",
128
- "value": "greatSword"
129
- }
130
- ],
131
- "jobTypeOptions": [
132
- {
133
- "label": "物理",
134
- "value": "physical"
135
- },
136
- {
137
- "label": "魔法",
138
- "value": "magic"
139
- }
140
- ]
126
+ "weaponTypeOptions": [],
127
+ "jobTypeOptions": []
141
128
  },
142
129
  "functionButton": [
130
+ {
131
+ "btn": "查询",
132
+ "action": "search"
133
+ },
134
+ {
135
+ "btn": "重置",
136
+ "action": "reset"
137
+ },
143
138
  {
144
139
  "btn": "批量删除",
145
- "action": "deleteBySelected"
140
+ "action": "batchDeleteBySelected"
146
141
  },
147
142
  {
148
143
  "btn": "批量导出",
149
- "action": "exportJobData"
144
+ "action": "exportData"
150
145
  }
151
146
  ],
152
147
  "pageStruct": [
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yorha2b-lab/autodev",
3
- "version": "3.2.0",
3
+ "version": "3.3.0",
4
4
  "description": "基于视觉大模型的前端(react+Antd)全自动 CRUD 代码生成器",
5
5
  "bin": {
6
6
  "autodev": "bin/autodev.js"
@@ -1,3 +1,4 @@
1
+ const stringify = require('json-stringify-pretty-compact')
1
2
  const { cleanCode, generateSmartImports, formatFormItemAndColumns } = require('../utils/utils.js')
2
3
 
3
4
  /**
@@ -9,21 +10,29 @@ const { cleanCode, generateSmartImports, formatFormItemAndColumns } = require('.
9
10
  */
10
11
  const resource = ({ pageConfig, resourceTpl }) => {
11
12
 
13
+ const { formItems, processedColumns, dictBlocks } = formatFormItemAndColumns({ pageConfig })
14
+
12
15
  const hasTabs = pageConfig.tabs?.length > 0
16
+ const hasFormItems = pageConfig.formItems?.length > 0
17
+ const hasDateColumn = processedColumns.some(item => item.type === 'date')
13
18
 
14
- const { formItems, processedColumns, dictBlocks } = formatFormItemAndColumns({ pageConfig })
19
+ const columnsData = processedColumns.map(item => {
20
+ delete item.type
21
+ return item
22
+ })
15
23
 
16
24
  const viewData = {
17
25
  hasTabs,
18
- formItems,
26
+ hasFormItems,
19
27
  tabs: pageConfig.tabs,
20
28
  dictBlocks: dictBlocks.map(item => ({ name: item, data: pageConfig.optionDict[item] ?? [] })),
21
29
  formItemsData: hasTabs ? Object.fromEntries(pageConfig.tabs.map(tab => [tab.key, formItems])) : formItems,
22
- columnsData: hasTabs ? Object.fromEntries(pageConfig.tabs.map(tab => [tab.key, processedColumns])) : processedColumns,
30
+ columnsData: hasTabs ? Object.fromEntries(pageConfig.tabs.map(tab => [tab.key, columnsData])) : columnsData,
23
31
  }
24
32
 
25
- const rawCode = resourceTpl(viewData)
26
- return cleanCode(rawCode)
33
+ const bodyCode = resourceTpl(viewData)
34
+ const importsStr = generateSmartImports({ module: 'resource', hasTabs, bodyCode, hasFormItems, hasDateColumn })
35
+ return cleanCode(`${importsStr}\n\n${bodyCode}`)
27
36
  }
28
37
 
29
38
  /**
@@ -38,23 +47,31 @@ const index = ({ config, fileName, indexTpl, pageConfig }) => {
38
47
 
39
48
  const hasTabs = pageConfig.tabs?.length > 0
40
49
  const hasFormItems = pageConfig.formItems?.length > 0
41
- const hasOperate = pageConfig.table.operation?.length > 0
50
+ const hasOperate = pageConfig.table?.operation?.length > 0
51
+ const hasImageColumn = pageConfig.table?.columns?.some(item => item.type === 'image')
42
52
  const pageStruct = pageConfig.pageStruct?.filter(item => item.toLowerCase() !== 'tabs') || []
43
53
  const functionButtons = pageConfig.functionButton?.filter(item => !['查询', '重置'].includes(item.btn)) || []
44
54
  const hasFunctionButtons = functionButtons.length > 0
55
+ const needRenderAction = hasImageColumn
45
56
 
46
57
  let columnsValue = hasTabs ? 'columns[activeKey]' : 'columns'
47
58
  if (hasOperate) {
48
59
  columnsValue = `${columnsValue}.concat(operate)`
49
60
  }
50
61
 
62
+ const imgAction = Object.fromEntries(pageConfig.table?.columns?.filter(item => item.type === 'image')?.map(item => [item.dataIndex, `_CODE_(_, record) => <a onClick={() => showImg(record, '${item.dataIndex}')}>查看图片</a>_CODE_`]))
63
+ const renderAction = `{${stringify.default({ ...imgAction }, { indent: 4, maxLength: 120 })}}`.replace(/\n/g, '\n ')
64
+
51
65
  const viewData = {
52
66
  hasTabs,
53
67
  fileName,
54
68
  hasOperate,
55
69
  columnsValue,
56
70
  hasFormItems,
71
+ renderAction,
72
+ hasImageColumn,
57
73
  functionButtons,
74
+ needRenderAction,
58
75
  tabs: pageConfig.tabs,
59
76
  responseSuccess: config.responseSuccess,
60
77
  hasExpandable: pageConfig.table.expandable,
@@ -67,7 +84,7 @@ const index = ({ config, fileName, indexTpl, pageConfig }) => {
67
84
  }
68
85
 
69
86
  const bodyCode = indexTpl(viewData)
70
- const importsStr = generateSmartImports({ bodyCode, hasTabs, hasFormItems })
87
+ const importsStr = generateSmartImports({ module: 'index', hasTabs, bodyCode, hasFormItems })
71
88
  return cleanCode(`${importsStr}\n\n${bodyCode}`)
72
89
  }
73
90
 
@@ -34,11 +34,14 @@ module.exports = `
34
34
  - **配置项**: [pagination, expandable, rowSelection] 均为布尔值false,只有页面明确有对应功能时才为true。
35
35
  - **标准定义 (Columns)**:
36
36
  - ⚠️ **操作锁定**: 严禁在 columns 数组中包含“操作”列。
37
- - date: 时间/日期列
37
+ - enum: 枚举列
38
38
  - money: 金额列
39
39
  - index: 序号列
40
- - enum: 枚举列
40
+ - image: 图片列
41
+ - date: 时间/日期列
41
42
  - text: 普通文本(默认)
43
+ - tag: 标签列(有背景色的标签)
44
+ - badge: 徽标列(文本前有颜色小圆点)
42
45
  - **行操作 (Operation)**: [{label:'操作名', action:'动词ByRecord'}]。
43
46
 
44
47
  ### 全局功能按钮 (FunctionButton)
@@ -49,7 +52,7 @@ module.exports = `
49
52
  ## 3. 布局逻辑编排 (Page Structure)
50
53
  - **字段**: [pageStruct] 定义从上至下的物理堆叠顺序。
51
54
  - **白名单**: 只能从 [AlertInfo, MySearchForm, FunctionButtonsBlock, MyTable] 中选择。
52
- - **⚠️ 约束**: 严禁在 pageStruct 中包含 Tabs 或其他未定义组件;并且只有真实存在的组件才能被包含。
55
+ - **⚠️ 约束**: 严禁在 pageStruct 中包含 其他未定义组件;并且只有真实存在的组件才能被包含。
53
56
 
54
57
  ## 4. 输出约束 (Output Format)
55
58
  - **格式**: 纯 JSON 对象,严禁 Markdown 标签包围。
@@ -15,11 +15,14 @@ module.exports = `
15
15
  - ***⚠️⚠️⚠️严禁输出formItems,只允许输出coloumns⚠️⚠️⚠️***
16
16
  - **标准定义 (Columns)**:
17
17
  - ⚠️ **操作锁定**: 严禁在 columns 数组中包含“操作”列。
18
- - date: 时间/日期列
18
+ - enum: 枚举列
19
19
  - money: 金额列
20
20
  - index: 序号列
21
- - enum: 枚举列
21
+ - image: 图片列
22
+ - date: 时间/日期列
22
23
  - text: 普通文本(默认)
24
+ - tag: 标签列(有背景色的标签)
25
+ - badge: 徽标列(文本前有颜色小圆点)
23
26
  - **行操作 (Operation)**: [{label:'操作名', action:'动词ByRecord'}]
24
27
 
25
28
  ## 2. 表单 (Form)
@@ -244,7 +244,9 @@ const formatFormItemAndColumns = ({ pageConfig }) => {
244
244
  money: 'text => moneyRender(text)',
245
245
  date: 'text => timeRender({time: text})',
246
246
  index: '(_, record, index) => index + 1',
247
- enum: dataIndex => `text => ${dataIndex}Options.find(item => item.value === text)?.label??text`
247
+ tag: `text => <Tag color='magenta'>{text}</Tag>`,
248
+ badge: `text => <Badge status='success' text={text} />`,
249
+ enum: dataIndex => `text => ${dataIndex}Options.find(item => item.value === text)?.label??text`,
248
250
  }
249
251
 
250
252
  const columns = pageConfig.table?.columns ?? pageConfig?.columns ?? []
@@ -259,16 +261,17 @@ const formatFormItemAndColumns = ({ pageConfig }) => {
259
261
  }))
260
262
 
261
263
  const processedColumns = columns?.map(col => {
264
+ if (['image'].includes(col.type)) {
265
+ return { ...col, renderAction: true }
266
+ }
262
267
  if (col.type && codePresets[col.type]) {
263
268
  // 💡 物理注入:根据标签,强行塞入标准化的 JS 代码字符串
264
269
  const renderCode = typeof codePresets[col.type] === 'function' ? codePresets[col.type](col.dataIndex) : codePresets[col.type]
265
- delete col.type
266
270
  return {
267
271
  ...col,
268
272
  render: `_CODE_${renderCode}_CODE_` // 重新打标,交给 cleanCode 处理
269
273
  }
270
274
  }
271
- delete col.type
272
275
  return col
273
276
  })
274
277
 
@@ -302,25 +305,32 @@ const copyTemplateDir = (options, templateSubDir, targetSubDir) => {
302
305
  * @param {boolean} hasTabs - 是否包含标签页
303
306
  * @returns {string} 拼接后的 import 语句
304
307
  */
305
- const generateSmartImports = ({ bodyCode, hasTabs, hasFormItems }) => {
308
+ const generateSmartImports = ({ module, hasTabs, bodyCode, hasFormItems, hasDateColumn }) => {
309
+
306
310
  const hooksLib = ['useTableQuery']
311
+ const utilsLib = ['timeRender', 'moneyRender']
307
312
  const reactLib = ['useState', 'useEffect', 'useRef', 'useMemo']
308
- const componentsLib = ['MyTable', 'MyModalForm', 'MySearchForm']
309
- const antdLib = ['Card', 'Space', 'Modal', 'Button', 'Alert', 'Table', 'Input', 'Select']
313
+ const componentsLib = ['MyTable', 'MyImage', 'MyModalForm', 'MySearchForm']
314
+ const antdLib = ['Tag', 'Card', 'Badge', 'Space', 'Modal', 'Alert', 'Image', 'Table', 'Input', 'Select', 'Button']
310
315
 
311
316
  const usedAntd = antdLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
317
+ const usedUtils = utilsLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
312
318
  const usedHooks = hooksLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
313
319
  const usedReact = reactLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
314
320
  const usedComps = componentsLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
315
321
 
316
322
  const imports = [
317
323
  usedReact.length && `import { ${usedReact.join(', ')} } from 'react'`,
318
- `import { request } from '../../utils/request'`,
319
- `import { formatQuery } from '../../utils/utils'`,
324
+ usedAntd.length && `import { ${hasFormItems && module === 'index' ? 'Form, ' : ''}${usedAntd.join(', ')} } from 'antd'`,
320
325
  ...usedHooks.map(hook => `import { ${hook} } from '../../hooks/${hook}'`),
321
326
  ...usedComps.map(comp => `import { ${comp} } from '../../components/${comp}'`),
322
- usedAntd.length && `import { ${hasFormItems ? 'Form, ' : ''}${usedAntd.join(', ')} } from 'antd'`,
323
- `import { ${hasTabs ? 'tabs, ' : ''}${hasFormItems ? 'formItems, ' : ''}modalItems, tableColumns} from './resource'`,
327
+ ...(module === 'index' ? [
328
+ `import { request } from '../../utils/request'`,
329
+ `import { formatQuery } from '../../utils/utils'`,
330
+ `import { ${hasTabs ? 'tabs, ' : ''}${hasFormItems ? 'formItems, ' : ''}modalItems, tableColumns} from './resource'`
331
+ ] : [
332
+ usedUtils.length && `import { ${usedUtils.join(', ')} } from '../../utils/utils'`
333
+ ]),
324
334
  ].sort((a, b) => a.length - b.length)
325
335
 
326
336
  return imports.filter(Boolean).join('\n')
@@ -0,0 +1,11 @@
1
+ import { Image } from 'antd'
2
+
3
+ export const MyImage = ({ img, setImg }) => {
4
+ return (
5
+ <div style={{ display: 'none' }}>
6
+ <Image.PreviewGroup preview={{ visible: img.visible, onVisibleChange: vis => setImg({ ...img, visible: vis }) }}>
7
+ {img.list.map((img, index) => (<Image key={index} src={img} />))}
8
+ </Image.PreviewGroup>
9
+ </div>
10
+ )
11
+ }
@@ -35,7 +35,7 @@ import { useRef, useMemo, useEffect, useCallback } from 'react'
35
35
  * customSave={(row) => saveApi(row)}
36
36
  * />
37
37
  */
38
- export const MyTable = ({ size, query, total, search, autoScroll, onChange, pagination, rowClassName, customSave, setDataSource, lineFormChange, columns = [], rowSelection, rowKey = 'id', loading = false, dataSource = [], scroll = { x: 'max-content' }, ...restProps }) => {
38
+ export const MyTable = ({ size, query, total, search, autoScroll, onChange, pagination, renderAction, rowClassName, customSave, setDataSource, lineFormChange, columns = [], rowSelection, rowKey = 'id', loading = false, dataSource = [], scroll = { x: 'max-content' }, ...restProps }) => {
39
39
 
40
40
  const tableRef = useRef(null)
41
41
  const hasScrolledRef = useRef(false)
@@ -82,6 +82,9 @@ export const MyTable = ({ size, query, total, search, autoScroll, onChange, pagi
82
82
  const mergedColumns = useMemo(() => {
83
83
  return columns.map(col => {
84
84
  if (!col.editType) {
85
+ if (col.renderAction && renderAction[col.dataIndex]) {
86
+ return { ...col, render: renderAction[col.dataIndex] }
87
+ }
85
88
  return col
86
89
  }
87
90
  return {
@@ -102,7 +105,7 @@ export const MyTable = ({ size, query, total, search, autoScroll, onChange, pagi
102
105
  }),
103
106
  }
104
107
  })
105
- }, [columns, handleSave])
108
+ }, [columns, handleSave, renderAction])
106
109
 
107
110
  /**
108
111
  * @constant paginationConfig
@@ -8,13 +8,16 @@
8
8
  search={search}
9
9
  loading={loading}
10
10
  dataSource={dataSource}
11
- columns={ {{columnsValue}} }
12
11
  setDataSource={setDataSource}
12
+ columns={ {{columnsValue}} }
13
13
  {{#if hasExpandable}}
14
14
  {{{{raw}}}}
15
15
  expandable={{ onExpand, expandedRowKeys, expandedRowRender, columnWidth: 24 }}
16
16
  {{{{/raw}}}}
17
17
  {{/if}}
18
+ {{#if needRenderAction}}
19
+ renderAction={{{renderAction}}}
20
+ {{/if}}
18
21
  {{#if hasPagination}}
19
22
  {{{{raw}}}}
20
23
  onChange={(pagination, filters, sorter) => {
@@ -46,6 +46,20 @@ const expandedRowRender = record => (<div>自定义渲染</div>)
46
46
 
47
47
  {{/if}}
48
48
 
49
+
50
+ {{#if hasImageColumn}}
51
+ const showImg = (record, dataIndex) => {
52
+ const list = record[dataIndex]?.split(',') ?? []
53
+ const isUrl=list.some(item=>item.startsWith('http'))
54
+ if(isUrl){
55
+ setImg({ list, visible: true })
56
+ }else{
57
+ //自行解决图片路径问题
58
+ }
59
+ }
60
+
61
+ {{/if}}
62
+
49
63
  const modalSubmit = async values => {
50
64
  let response
51
65
  if(modal.type==='add'){
@@ -3,6 +3,9 @@ const rowKey = 'id'
3
3
  {{#if hasFormItems}}
4
4
  const [form] = Form.useForm()
5
5
  {{/if}}
6
+ {{#if hasImageColumn}}
7
+ const [img, setImg] = useState({})
8
+ {{/if}}
6
9
  {{#if hasRowSelection}}
7
10
  const [selectedRows, setSelectedRows] = useState([])
8
11
  {{/if}}
@@ -2,10 +2,10 @@ export default props => {
2
2
 
3
3
  {{> stateBlock }}
4
4
 
5
- {{> hookBlock }}
6
-
7
5
  {{> handleBlock }}
8
6
 
7
+ {{> hookBlock }}
8
+
9
9
  {{#if hasOperate}}
10
10
  {{> tableOperate }}
11
11
  {{/if}}
@@ -22,6 +22,7 @@ export default props => {
22
22
  return (
23
23
  <Card {{#if hasTabs}}tabList={tabs} activeTabKey={activeKey} onTabChange={onTabChange}{{/if}}>
24
24
  {modal.visible && <MyModalForm {...modal} submit={modalSubmit} setModal={setModal} />}
25
+ {{#if hasImageColumn}}{img.visible && <MyImage img={img} setImg={setImg}/>}{{/if}}
25
26
  {{#each pageStruct as |partialName|}}
26
27
  {{> (partialName) @root }}
27
28
  {{/each}}
@@ -1,5 +1,3 @@
1
- import { timeRender } from '../../utils/utils'
2
-
3
1
  {{#if hasTabs}}
4
2
  export const tabs = {{{stringify tabs 50}}}
5
3
  {{/if}}
@@ -9,7 +7,7 @@ export const {{this.name}} = {{{stringify this.data 50}}}
9
7
 
10
8
  {{/each}}
11
9
 
12
- {{#if formItems.length}}
10
+ {{#if hasFormItems}}
13
11
  export const formItems = {{{stringify formItemsData 150}}}
14
12
  {{/if}}
15
13