@yorha2b-lab/autodev 1.0.28 → 1.0.30
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/package.json +1 -1
- package/src/core/react-compiler.js +2 -1
- package/templates/react/components/MyBaseForm.js +10 -9
- package/templates/react/components/MyModalForm.js +18 -6
- package/templates/react/components/MyModalTable.js +3 -3
- package/templates/react/components/MySearchForm.js +35 -19
- package/templates/react/components/MyTable.js +70 -50
- package/templates/react/components/index.js +6 -6
- package/templates/react/index.hbs +2 -2
- package/templates/react/utils/utils.js +11 -11
package/package.json
CHANGED
|
@@ -62,6 +62,7 @@ const index = ({ config, fileName, indexTpl, pageConfig }) => {
|
|
|
62
62
|
hasOperate, // 是否有操作列
|
|
63
63
|
columnsValue, // columns 引用表达式
|
|
64
64
|
tabs: pageConfig.tabs, // 标签页配置
|
|
65
|
+
responseSuccess: config.responseSuccess, // 成功回调函数
|
|
65
66
|
hasExpandable: pageConfig.table.expandable, // 是否有展开行
|
|
66
67
|
hasPagination: pageConfig.table.pagination, // 是否有分页
|
|
67
68
|
operations: pageConfig.table.operation || [], // 操作列配置
|
|
@@ -72,7 +73,7 @@ const index = ({ config, fileName, indexTpl, pageConfig }) => {
|
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
// 渲染模板
|
|
75
|
-
const bodyCode = indexTpl(viewData)
|
|
76
|
+
const bodyCode = indexTpl(viewData)
|
|
76
77
|
// 生成智能导入语句
|
|
77
78
|
const importsStr = generateSmartImports(bodyCode, hasTabs)
|
|
78
79
|
// 清理代码并返回
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { Fragment } from 'react'
|
|
1
2
|
import { formNode } from './index'
|
|
2
|
-
import { Form,
|
|
3
|
-
import { PlusOutlined
|
|
3
|
+
import { Form, Button } from 'antd'
|
|
4
|
+
import { PlusOutlined } from '@ant-design/icons'
|
|
4
5
|
|
|
5
6
|
const getValueFromEvent = e => {
|
|
6
7
|
if (Array.isArray(e)) {
|
|
@@ -9,14 +10,16 @@ const getValueFromEvent = e => {
|
|
|
9
10
|
return e?.fileList
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
const renderFormContent = ({ item }) => {
|
|
13
|
+
const renderFormContent = ({ item, prefixName = [] }) => {
|
|
13
14
|
|
|
14
15
|
if (!item.name) {
|
|
15
16
|
return item.value
|
|
16
17
|
}
|
|
17
18
|
|
|
19
|
+
const namePath = Array.isArray(prefixName) ? [...prefixName, ...(Array.isArray(item.name) ? item.name : [item.name])] : item.name
|
|
20
|
+
|
|
18
21
|
const inputNode = (
|
|
19
|
-
<Form.Item noStyle name={
|
|
22
|
+
<Form.Item noStyle name={namePath} rules={item.rules} initialValue={item.value} {...(['upload', 'ossUpload'].includes(item.type) ? { getValueFromEvent, valuePropName: 'fileList' } : { valuePropName: item.valuePropName })}>
|
|
20
23
|
{formNode({ item })}
|
|
21
24
|
</Form.Item>
|
|
22
25
|
)
|
|
@@ -41,11 +44,9 @@ export const MyBaseForm = ({ item, form }) => {
|
|
|
41
44
|
{(fields, { add, remove }) => (
|
|
42
45
|
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
|
43
46
|
{fields.map((field, index) => (
|
|
44
|
-
<
|
|
45
|
-
{item.render(field, index, { add, remove }, form)}
|
|
46
|
-
|
|
47
|
-
{fields.length === index + 1 && <PlusCircleOutlined onClick={() => add()} />}
|
|
48
|
-
</Space>
|
|
47
|
+
<Fragment key={field.key}>
|
|
48
|
+
{item.render(field, index, { add, remove }, form, renderFormContent)}
|
|
49
|
+
</Fragment>
|
|
49
50
|
))}
|
|
50
51
|
{fields.length === 0 && <Button type='dashed' onClick={() => add()} block icon={<PlusOutlined />}>添加{item.label || '项'}</Button>}
|
|
51
52
|
</div>
|
|
@@ -12,14 +12,26 @@ export const MyModalForm = ({ width, title, submit, record, visible, setModal, l
|
|
|
12
12
|
if (visible) {
|
|
13
13
|
form.resetFields()
|
|
14
14
|
if (record && Object.keys(record).length > 0) {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
const itemMap = new Map(formItems.map(i => [i.name, i]))
|
|
16
|
+
const initialData = {}
|
|
17
|
+
Object.entries(record).forEach(([key, value]) => {
|
|
18
|
+
const config = itemMap.get(key)
|
|
19
|
+
if (config?.type?.includes('date') && value) {
|
|
20
|
+
if (Array.isArray(value)) {
|
|
21
|
+
initialData[key] = value.map(v => dayjs(isNaN(v) ? v : Number(v)))
|
|
22
|
+
} else if (typeof value === 'string' && value.includes(',')) {
|
|
23
|
+
initialData[key] = value.split(',').map(v => dayjs(Number(v)))
|
|
24
|
+
} else {
|
|
25
|
+
initialData[key] = dayjs(isNaN(value) ? value : Number(value))
|
|
26
|
+
}
|
|
27
|
+
} else {
|
|
28
|
+
initialData[key] = value
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
form.setFieldsValue(initialData)
|
|
20
32
|
}
|
|
21
33
|
}
|
|
22
|
-
}, [visible, record, form])
|
|
34
|
+
}, [visible, record, form, formItems])
|
|
23
35
|
|
|
24
36
|
const handleOk = async () => {
|
|
25
37
|
try {
|
|
@@ -4,9 +4,9 @@ import { MySearchForm } from './MySearchForm'
|
|
|
4
4
|
import { useTableQuery } from '../hooks/useTableQuery'
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
export const MyModalTable = ({ api, onOk, title, width, footer, visible, columns, setModal, formItems, rowSelection, formatResponse }) => {
|
|
7
|
+
export const MyModalTable = ({ api, onOk, title, width, footer, visible, columns, setModal, formItems, rowSelection, formatResponse, extraParams = {} }) => {
|
|
8
8
|
|
|
9
|
-
const { total, loading, dataSource, search, setSearch } = useTableQuery(api, formatResponse,
|
|
9
|
+
const { total, loading, dataSource, search, setSearch } = useTableQuery(api, formatResponse, extraParams)
|
|
10
10
|
|
|
11
11
|
const handleSearch = values => setSearch({ ...search, ...values, pageNo: 1 })
|
|
12
12
|
|
|
@@ -21,7 +21,7 @@ export const MyModalTable = ({ api, onOk, title, width, footer, visible, columns
|
|
|
21
21
|
|
|
22
22
|
return (
|
|
23
23
|
<Modal centered destroyOnHidden title={title} width={width} open={visible} onOk={handleOk} footer={footer} onCancel={() => setModal({ visible: false })}>
|
|
24
|
-
{formItems?.length > 0 && <MySearchForm search={search} formItems={formItems} setSearch={handleSearch} />}
|
|
24
|
+
{formItems?.length > 0 && <MySearchForm search={search} formItems={formItems} setSearch={handleSearch} syncUrlParams={false} />}
|
|
25
25
|
<MyTable
|
|
26
26
|
loading={loading}
|
|
27
27
|
columns={columns}
|
|
@@ -4,12 +4,14 @@ import { MyBaseForm } from './MyBaseForm'
|
|
|
4
4
|
import { useState, useEffect } from 'react'
|
|
5
5
|
import { Row, Col, Form, Button, Space } from 'antd'
|
|
6
6
|
|
|
7
|
-
export const MySearchForm = ({ form, search, loading, labelCol, setSearch, formItems, showLimit = 7, initialValues, customReset, extraOperate, customFinish, onValuesChange, defaultPageSize = 10 }) => {
|
|
7
|
+
export const MySearchForm = ({ form, search, loading, labelCol, setSearch, formItems, showLimit = 7, initialValues, customReset, extraOperate, customFinish, onValuesChange, syncUrlParams = true, defaultPageSize = 10 }) => {
|
|
8
8
|
|
|
9
9
|
const [limit, setLimit] = useState(showLimit)
|
|
10
10
|
|
|
11
11
|
const handleReset = () => {
|
|
12
|
-
|
|
12
|
+
if (syncUrlParams) {
|
|
13
|
+
history.push({ search: '' })
|
|
14
|
+
}
|
|
13
15
|
if (customReset) {
|
|
14
16
|
customReset()
|
|
15
17
|
} else {
|
|
@@ -19,19 +21,24 @@ export const MySearchForm = ({ form, search, loading, labelCol, setSearch, formI
|
|
|
19
21
|
|
|
20
22
|
const handleFinish = values => {
|
|
21
23
|
const formattedValues = { ...values }
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
if (syncUrlParams) {
|
|
25
|
+
Object.keys(formattedValues).forEach(key => {
|
|
26
|
+
const val = formattedValues[key]
|
|
27
|
+
const isDateType = formItems.find(item => item.name === key)?.type?.includes('date')
|
|
28
|
+
if (isDateType && val) {
|
|
29
|
+
if (Array.isArray(val) && val.length > 0) {
|
|
30
|
+
const [startKey, endKey] = key.split(',')
|
|
31
|
+
formattedValues[startKey] = val[0].startOf('day').valueOf()
|
|
32
|
+
formattedValues[endKey] = val[1].endOf('day').valueOf()
|
|
33
|
+
delete formattedValues[key]
|
|
34
|
+
} else {
|
|
35
|
+
formattedValues[key] = val.valueOf()
|
|
36
|
+
}
|
|
30
37
|
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
38
|
+
})
|
|
39
|
+
const params = new URLSearchParams(Object.fromEntries(Object.entries(formattedValues).filter(([key, value]) => !['', null, undefined].includes(value))))
|
|
40
|
+
history.push({ search: params.toString() })
|
|
41
|
+
}
|
|
35
42
|
if (customFinish) {
|
|
36
43
|
customFinish(formattedValues)
|
|
37
44
|
} else {
|
|
@@ -41,11 +48,20 @@ export const MySearchForm = ({ form, search, loading, labelCol, setSearch, formI
|
|
|
41
48
|
|
|
42
49
|
useEffect(() => {
|
|
43
50
|
if (Object.values(initialValues).length > 0 && formItems.length > 0) {
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
51
|
+
const newValues = {}
|
|
52
|
+
formItems.forEach(item => {
|
|
53
|
+
if (initialValues[item.name] !== undefined) {
|
|
54
|
+
const value = initialValues[item.name]
|
|
55
|
+
newValues[item.name] = item.type?.includes('date') ? dayjs(Number(value)) : value
|
|
56
|
+
}
|
|
57
|
+
if (item.name.includes(',') && item.type?.includes('date')) {
|
|
58
|
+
const [startKey, endKey] = item.name.split(',')
|
|
59
|
+
if (initialValues[startKey] && initialValues[endKey]) {
|
|
60
|
+
newValues[item.name] = [dayjs(Number(initialValues[startKey])), dayjs(Number(initialValues[endKey]))]
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
form.setFieldsValue(newValues)
|
|
49
65
|
}
|
|
50
66
|
}, [initialValues, form, formItems])
|
|
51
67
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Table } from 'antd'
|
|
2
|
-
import { useRef, useEffect } from 'react'
|
|
2
|
+
import { useRef, useMemo, useEffect } from 'react'
|
|
3
3
|
import { EditableRow, EditableCell } from './EditableCell'
|
|
4
4
|
|
|
5
5
|
const components = {
|
|
@@ -9,61 +9,81 @@ const components = {
|
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export const MyTable = ({ size, query, onSave, autoScroll, onChange, pagination, rowClassName, columns = [], rowSelection, rowKey = 'id', loading = false, dataSource = [],
|
|
12
|
+
export const MyTable = ({ size, query, onSave, autoScroll, onChange, pagination, rowClassName, columns = [], rowSelection, rowKey = 'id', loading = false, dataSource = [], scroll = { x: 'max-content' }, ...restProps }) => {
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
const
|
|
15
|
+
const tableRef = useRef(null)
|
|
16
|
+
const hasScrolledRef = useRef(false)
|
|
16
17
|
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
18
|
+
const isEditable = useMemo(() => columns.some(col => col.editType), [columns])
|
|
19
|
+
|
|
20
|
+
const mergedColumns = useMemo(() => {
|
|
21
|
+
return columns.map(col => {
|
|
22
|
+
if (!col.editType) {
|
|
23
|
+
return col
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
...col,
|
|
27
|
+
onCell: (record) => ({
|
|
28
|
+
record,
|
|
29
|
+
title: col.title,
|
|
30
|
+
rules: col.rules,
|
|
31
|
+
handleSave: onSave,
|
|
32
|
+
options: col.options,
|
|
33
|
+
editType: col.editType,
|
|
34
|
+
editable: !!col.editType,
|
|
35
|
+
dataIndex: col.dataIndex,
|
|
36
|
+
}),
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
}, [columns, onSave])
|
|
35
40
|
|
|
36
41
|
useEffect(() => {
|
|
37
|
-
if (
|
|
38
|
-
if (
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
|
|
42
|
+
if (!autoScroll || !dataSource?.length || query?.rowIndex === undefined) return
|
|
43
|
+
if (hasScrolledRef.current === query.rowIndex) return
|
|
44
|
+
const timer = setTimeout(() => {
|
|
45
|
+
const tableElement = tableRef.current
|
|
46
|
+
if (!tableElement) return
|
|
47
|
+
const trList = tableElement.getElementsByClassName('ant-table-row')
|
|
48
|
+
const tr = trList[Number(query.rowIndex)]
|
|
49
|
+
if (tr) {
|
|
50
|
+
tr.scrollIntoView({ behavior: 'smooth', block: 'center' })
|
|
51
|
+
tr.classList.add('row-highlight-pulse')
|
|
52
|
+
setTimeout(() => tr.classList.remove('row-highlight-pulse'), 1000)
|
|
53
|
+
hasScrolledRef.current = query.rowIndex
|
|
54
|
+
}
|
|
55
|
+
}, 300)
|
|
56
|
+
return () => clearTimeout(timer)
|
|
57
|
+
}, [dataSource, query?.rowIndex, autoScroll, tableRef])
|
|
52
58
|
|
|
53
59
|
return (
|
|
54
|
-
<
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
60
|
+
<div ref={tableRef}>
|
|
61
|
+
<Table
|
|
62
|
+
{...restProps}
|
|
63
|
+
rowKey={rowKey}
|
|
64
|
+
scroll={scroll}
|
|
65
|
+
loading={loading}
|
|
66
|
+
onChange={onChange}
|
|
67
|
+
columns={mergedColumns}
|
|
68
|
+
dataSource={dataSource}
|
|
69
|
+
pagination={pagination}
|
|
70
|
+
size={size ?? 'middle'}
|
|
71
|
+
rowSelection={rowSelection}
|
|
72
|
+
components={isEditable ? components : undefined}
|
|
73
|
+
rowClassName={(record, index) => {
|
|
74
|
+
const externalClass = typeof rowClassName === 'function' ? rowClassName(record, index) : rowClassName
|
|
75
|
+
const classes = [externalClass]
|
|
76
|
+
if (isEditable) classes.push('editable-row')
|
|
77
|
+
if (autoScroll && query?.rowIndex === index) classes.push('search-highlight-row')
|
|
78
|
+
return classes.filter(Boolean).join(' ')
|
|
79
|
+
}}
|
|
80
|
+
/>
|
|
81
|
+
<style jsx>{`
|
|
82
|
+
:global(.row-highlight-pulse) {
|
|
83
|
+
background-color: #e6f4ff !important;
|
|
84
|
+
transition: background-color 0.5s ease;
|
|
85
|
+
}
|
|
86
|
+
`}</style>
|
|
87
|
+
</div>
|
|
68
88
|
)
|
|
69
89
|
}
|
|
@@ -3,7 +3,7 @@ import { useState, useEffect } from 'react'
|
|
|
3
3
|
import { UploadOutlined } from '@ant-design/icons'
|
|
4
4
|
import { Button, Tree, Radio, Input, Upload, Select, Cascader, Checkbox, DatePicker, InputNumber, TreeSelect, AutoComplete } from 'antd'
|
|
5
5
|
|
|
6
|
-
const AliyunOSSUpload = ({ style, value, disabled, onChange }) => {
|
|
6
|
+
const AliyunOSSUpload = ({ path, style, value, disabled, onChange }) => {
|
|
7
7
|
|
|
8
8
|
const [OSSData, setOSSData] = useState()
|
|
9
9
|
|
|
@@ -23,7 +23,7 @@ const AliyunOSSUpload = ({ style, value, disabled, onChange }) => {
|
|
|
23
23
|
const handleChange = ({ fileList }) => onChange?.([...fileList])
|
|
24
24
|
|
|
25
25
|
const onRemove = file => {
|
|
26
|
-
const files = (value || []).filter(v => v.
|
|
26
|
+
const files = (value || []).filter(v => v.uid !== file.uid)
|
|
27
27
|
onChange?.(files)
|
|
28
28
|
}
|
|
29
29
|
|
|
@@ -42,7 +42,7 @@ const AliyunOSSUpload = ({ style, value, disabled, onChange }) => {
|
|
|
42
42
|
if (expire < Date.now()) {
|
|
43
43
|
await init()
|
|
44
44
|
}
|
|
45
|
-
file.url = `${OSSData.dir}/${file.name}`.replace(/\/\//g, '/')
|
|
45
|
+
file.url = `${OSSData.dir ?? path}/${file.uid}_${file.name}`.replace(/\/\//g, '/')
|
|
46
46
|
return file
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -94,11 +94,11 @@ export const formNode = ({ item }) => {
|
|
|
94
94
|
case 'textarea':
|
|
95
95
|
return <Input.TextArea autoSize={{ minRows: 4 }} {...commonProps} />
|
|
96
96
|
case 'cascader':
|
|
97
|
-
return <Cascader options={item.options}
|
|
98
|
-
case 'select':
|
|
99
|
-
return <Select allowClear mode={item.mode} options={item.options} {...commonProps} />
|
|
97
|
+
return <Cascader showSearch allowClear options={item.options} {...commonProps} />
|
|
100
98
|
case 'upload':
|
|
101
99
|
return <Upload {...item.uploadProps} {...commonProps}>{item.content ?? '上传文件'}</Upload>
|
|
100
|
+
case 'select':
|
|
101
|
+
return <Select allowClear showSearch mode={item.mode} options={item.options} {...commonProps} />
|
|
102
102
|
default:
|
|
103
103
|
return <Input allowClear {...commonProps} />
|
|
104
104
|
}
|
|
@@ -25,7 +25,7 @@ export default props => {
|
|
|
25
25
|
{{#each operations}}
|
|
26
26
|
const {{this.action}} = async record => {
|
|
27
27
|
const response = await request('/api')
|
|
28
|
-
if(
|
|
28
|
+
if({{{../responseSuccess}}}){
|
|
29
29
|
refresh()
|
|
30
30
|
}
|
|
31
31
|
}
|
|
@@ -40,7 +40,7 @@ export default props => {
|
|
|
40
40
|
content: '确认{{this.btn}}吗?',
|
|
41
41
|
onOk: async () => {
|
|
42
42
|
const response = await request('/api')
|
|
43
|
-
if(
|
|
43
|
+
if({{{../responseSuccess}}}){
|
|
44
44
|
refresh()
|
|
45
45
|
}
|
|
46
46
|
}
|
|
@@ -6,16 +6,6 @@ export const paramsFormat = params => {
|
|
|
6
6
|
return new URLSearchParams(cleanParams).toString()
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
export const formatValue = ({ item, ind }) => {
|
|
10
|
-
if (dayjs.isDayjs(item[1])) {
|
|
11
|
-
return item[1].startOf('day').valueOf()
|
|
12
|
-
}
|
|
13
|
-
if (dayjs.isDayjs(item[1]?.[ind])) {
|
|
14
|
-
return ind === 0 ? item[1]?.[ind]?.startOf('day').valueOf() : item[1]?.[ind]?.endOf('day').valueOf()
|
|
15
|
-
}
|
|
16
|
-
return item[1]?.toString()?.trim() ?? undefined
|
|
17
|
-
}
|
|
18
|
-
|
|
19
9
|
export const initOSS = async (url, options) => {
|
|
20
10
|
const OSS = require('ali-oss')
|
|
21
11
|
const crypto = require('crypto-js')
|
|
@@ -60,7 +50,17 @@ export const exportDataToExcel = async (url, options, columns, fileName, resultK
|
|
|
60
50
|
const response = await request(url, options)
|
|
61
51
|
if (response?.[resultKey]?.length > 0) {
|
|
62
52
|
const XLSX = require('xlsx')
|
|
63
|
-
const datas = response?.[resultKey]?.map(item => Object.fromEntries(columns.map(col =>
|
|
53
|
+
const datas = response?.[resultKey]?.map(item => Object.fromEntries(columns.map(col => {
|
|
54
|
+
let value = item[col.dataIndex]
|
|
55
|
+
if (col.exportRender) {
|
|
56
|
+
value = col.exportRender(value, item, index)
|
|
57
|
+
}
|
|
58
|
+
if (col.render) {
|
|
59
|
+
const rendered = col.render(value, item, index)
|
|
60
|
+
value = typeof rendered === 'object' ? (rendered?.props?.children || value) : rendered
|
|
61
|
+
}
|
|
62
|
+
return [col.title, value]
|
|
63
|
+
})))
|
|
64
64
|
const workbook = XLSX.utils.book_new()
|
|
65
65
|
const header = columns.map(item => item.title)
|
|
66
66
|
const worksheet = XLSX.utils.json_to_sheet(datas, { header })
|