@yorha2b-lab/autodev 3.4.2 → 3.4.4
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/prompts/react/watch-page.js +1 -1
- package/src/prompts/react/watch-part.js +6 -2
- package/templates/react/components/MyModalForm.js +18 -20
- package/templates/react/components/MyModalTable.js +6 -5
- package/templates/react/components/MySearchForm.js +1 -2
- package/templates/react/components/MyTable.js +6 -4
- package/templates/react/handlebars/MyTable.hbs +7 -9
- package/templates/react/hooks/useTableQuery.js +16 -5
package/package.json
CHANGED
|
@@ -18,7 +18,7 @@ module.exports = `
|
|
|
18
18
|
- ⚠️ 属性精简规则:
|
|
19
19
|
- 默认输入框: 仅保留 [label, name] 属性。**严禁出现 type 属性**。
|
|
20
20
|
- 非默认组件: 仅当类型为 [auto, date, radio, select, upload, checkbox, textarea, daterange] 时才允许添加 type 属性。
|
|
21
|
-
**标准定义 (
|
|
21
|
+
**标准定义 (FormItem)**:
|
|
22
22
|
- date: 单日期
|
|
23
23
|
- daterange: 日期范围
|
|
24
24
|
- enum: 枚举类型
|
|
@@ -26,13 +26,17 @@ module.exports = `
|
|
|
26
26
|
- **行操作 (Operation)**: [{label:'操作名', action:'动词ByRecord'}]
|
|
27
27
|
|
|
28
28
|
## 2. 表单 (Form)
|
|
29
|
-
- **基础格式**: [{ label: '文本', name: 'englishName',type:''
|
|
29
|
+
- **基础格式**: [{ label: '文本', name: 'englishName',type:''}]
|
|
30
|
+
- **高级格式**: [{ title: '文本', layoutType:'',childItems:[{ label: '文本', name: 'englishName',type:'' }] }]
|
|
30
31
|
- 禁止输出:
|
|
31
32
|
- ***⚠️⚠️⚠️严禁输出columns,只允许输出formItems⚠️⚠️⚠️***
|
|
32
33
|
- ⚠️ 属性精简规则:
|
|
33
34
|
- 默认输入框: 仅保留 [label, name] 属性。**严禁出现 type 属性**。
|
|
34
35
|
- 非默认组件: 仅当类型为 [auto, date, radio, select, upload, checkbox, textarea, daterange] 时才允许添加 type 属性。
|
|
35
|
-
|
|
36
|
+
- ⚠️ layoutType:
|
|
37
|
+
- section: 段落布局(分段title+childItems项,无明显背景色,纯文本title)
|
|
38
|
+
- collapse: 折叠布局(折叠展开title+childItems项,有明显背景色,或文本前有展开箭头)
|
|
39
|
+
**标准定义 (FormItem)**:
|
|
36
40
|
- date: 单日期
|
|
37
41
|
- daterange: 日期范围
|
|
38
42
|
- enum: 枚举类型
|
|
@@ -5,28 +5,26 @@ import { useMemo, useState, useEffect } from 'react'
|
|
|
5
5
|
import { Row, Col, Form, Modal, Button, Collapse } from 'antd'
|
|
6
6
|
|
|
7
7
|
const LAYOUT_PROCESSORS = {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
<
|
|
12
|
-
|
|
8
|
+
section: ({ item, renderCore }) => (
|
|
9
|
+
<Col span={item.span ?? 24} style={{ marginBottom: 16 }}>
|
|
10
|
+
<div style={{ fontWeight: 'bold', marginBottom: 12, borderLeft: '4px solid #1890ff', paddingLeft: 8 }}>{item.title}</div>
|
|
11
|
+
<Row gutter={[24, 0]}>
|
|
12
|
+
{renderCore(item.childItems || [])}
|
|
13
|
+
</Row>
|
|
13
14
|
</Col>
|
|
14
15
|
),
|
|
15
|
-
// 手风琴布局
|
|
16
16
|
collapse: ({ item, renderCore }) => (
|
|
17
|
-
<Col span={item.span ?? 24}>
|
|
18
|
-
<Collapse
|
|
19
|
-
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
</Collapse.Panel>
|
|
25
|
-
))}
|
|
17
|
+
<Col span={item.span ?? 24} style={{ marginBottom: 16 }}>
|
|
18
|
+
<Collapse defaultActiveKey={['1']}>
|
|
19
|
+
<Collapse.Panel key='1' header={<span style={{ fontWeight: 'bold' }}>{item.title}</span>}>
|
|
20
|
+
<Row gutter={[24, 0]}>
|
|
21
|
+
{renderCore(item.childItems || [])}
|
|
22
|
+
</Row>
|
|
23
|
+
</Collapse.Panel>
|
|
26
24
|
</Collapse>
|
|
27
25
|
</Col>
|
|
28
26
|
),
|
|
29
|
-
default: ({
|
|
27
|
+
default: ({ item, renderCore }) => <Row gutter={[24, 0]}>{renderCore(item.childItems || [])}</Row>
|
|
30
28
|
}
|
|
31
29
|
|
|
32
30
|
// 定义“零部件增强协议”处理器,以后加任何新功能,只需要在这个对象里加一个 Key,不需要动主逻辑!
|
|
@@ -88,7 +86,7 @@ const FormRenderer = ({ form, formItems, tableConfig, setModalTable, setSelected
|
|
|
88
86
|
|
|
89
87
|
// 情况 B:标准作战零件
|
|
90
88
|
return (
|
|
91
|
-
<Col span={item.span ??
|
|
89
|
+
<Col span={item.span ?? 6} key={item.name || index}>
|
|
92
90
|
<MyBaseForm item={{ ...item, ...getRenderItemProps(item) }} form={form} />
|
|
93
91
|
</Col>
|
|
94
92
|
)
|
|
@@ -120,7 +118,7 @@ const FormRenderer = ({ form, formItems, tableConfig, setModalTable, setSelected
|
|
|
120
118
|
* formItems={[{ label: '截止日期', name: 'deadline', type: 'date' }]}
|
|
121
119
|
* />
|
|
122
120
|
*/
|
|
123
|
-
export const MyModalForm = ({ width, title, submit, record, visible, setModal, labelCol, formItems, wrapperCol, tableConfig, onValuesChange, handleModalTableOk }) => {
|
|
121
|
+
export const MyModalForm = ({ width, title, footer, submit, record, visible, setModal, labelCol, formItems, wrapperCol, tableConfig, onValuesChange, handleModalTableOk }) => {
|
|
124
122
|
|
|
125
123
|
const rowKey = tableConfig?.rowKey || 'id'
|
|
126
124
|
|
|
@@ -219,10 +217,10 @@ export const MyModalForm = ({ width, title, submit, record, visible, setModal, l
|
|
|
219
217
|
const tableRowSelection = useMemo(() => ({
|
|
220
218
|
selectedRowKeys: selectedTableRows.map(row => row[rowKey]),
|
|
221
219
|
onChange: (selectedRowKeys, selectedRows) => setSelectedTableRows(selectedRows),
|
|
222
|
-
}), [rowKey, selectedTableRows])
|
|
220
|
+
}), [rowKey, modalTable, selectedTableRows])
|
|
223
221
|
|
|
224
222
|
return (
|
|
225
|
-
<Modal centered title={title} width={width} open={visible} onOk={handleOk} destroyOnClose onCancel={handleCancel} confirmLoading={pending}>
|
|
223
|
+
<Modal centered title={title} width={width} open={visible} onOk={handleOk} footer={footer?.({ setModal })} destroyOnClose onCancel={handleCancel} confirmLoading={pending} bodyStyle={{ paddingBottom: 0 }}>
|
|
226
224
|
{modalTable.visible && <MyModalTable rowKey={rowKey} {...modalTable} rowSelection={tableRowSelection} onOk={modalTableOk} setModal={setModalTable} />}
|
|
227
225
|
<Form form={form} preserve={false} labelCol={labelCol} wrapperCol={wrapperCol} onValuesChange={(changed, all) => onValuesChange?.({ changed, all, form, record })}>
|
|
228
226
|
<Row gutter={[24, 0]}>
|
|
@@ -36,7 +36,7 @@ import { useTableQuery } from '../hooks/useTableQuery'
|
|
|
36
36
|
* operate={({ refresh, setModalForm }) => ({ label: '详情', onClick: (rec) => setModalForm(...) })}
|
|
37
37
|
* />
|
|
38
38
|
*/
|
|
39
|
-
export const MyModalTable = ({ api, onOk, title, width, footer, visible, operate, setModal, formItems, setModalForm, rowSelection, formatResponse, functionButtons, columns: modalColumns, rowKey = 'id', initialParams = {}, modalPagination = true }) => {
|
|
39
|
+
export const MyModalTable = ({ api, onOk, title, width, footer, visible, operate, setModal, formItems, setModalForm, rowSelection, formatResponse, functionButtons, columns: modalColumns, rowKey = 'id', scroll = { y: 400 }, initialParams = {}, modalPagination = true, isLocalPaging = false }) => {
|
|
40
40
|
|
|
41
41
|
const [form] = Form.useForm()
|
|
42
42
|
const [pending, setPending] = useState(false)
|
|
@@ -45,7 +45,7 @@ export const MyModalTable = ({ api, onOk, title, width, footer, visible, operate
|
|
|
45
45
|
* @description [独立信号链路]
|
|
46
46
|
* 在弹窗内部启动专属的 useTableQuery,实现了与主页面逻辑的“物理隔绝”。
|
|
47
47
|
*/
|
|
48
|
-
const { total, loading, columns, dataSource, search, refresh, setLoading, setSearch, setDataSource } = useTableQuery({ api, cols: modalColumns, initialParams, formatResponse })
|
|
48
|
+
const { total, loading, columns, dataSource, search, refresh, setLoading, setSearch, setDataSource } = useTableQuery({ api, cols: modalColumns, initialParams, formatResponse, isLocalPaging })
|
|
49
49
|
|
|
50
50
|
const handleSearch = values => setSearch({ ...search, ...values, pageNo: 1 })
|
|
51
51
|
|
|
@@ -78,7 +78,7 @@ export const MyModalTable = ({ api, onOk, title, width, footer, visible, operate
|
|
|
78
78
|
}, [columns, operate])
|
|
79
79
|
|
|
80
80
|
return (
|
|
81
|
-
<Modal centered destroyOnClose title={title} width={width} open={visible} onOk={handleOk} footer={footer} onCancel={() => setModal({ visible: false })} confirmLoading={pending}>
|
|
81
|
+
<Modal centered destroyOnClose title={title} width={width} open={visible} onOk={handleOk} footer={footer?.({ setModal })} onCancel={() => setModal({ visible: false })} confirmLoading={pending} bodyStyle={{ paddingBottom: 0 }}>
|
|
82
82
|
{/*
|
|
83
83
|
💡 [地堡战术细节]
|
|
84
84
|
syncUrlParams={false}:确保弹窗内的搜索行为不会干扰全局 URL 信号。
|
|
@@ -88,18 +88,19 @@ export const MyModalTable = ({ api, onOk, title, width, footer, visible, operate
|
|
|
88
88
|
💡 [高阶赋能逻辑]
|
|
89
89
|
functionButtons 能够获得对当前表格状态 (refresh, setLoading...) 的完全控制权。
|
|
90
90
|
*/}
|
|
91
|
-
{functionButtons?.length > 0 && <Space>{functionButtons.map(item => <Button key={item.name} type={item.type} onClick={() => item.onClick({ refresh, setLoading, setSearch, setDataSource })}>{item.name}</Button>)}</Space>}
|
|
91
|
+
{functionButtons?.length > 0 && <Space style={{ marginBottom: 12 }}>{functionButtons.map(item => <Button key={item.name} type={item.type} loading={loading} disabled={item.disabled} onClick={() => item.onClick({ refresh, setLoading, setSearch, setDataSource })}>{item.name}</Button>)}</Space>}
|
|
92
92
|
<MyTable
|
|
93
93
|
total={total}
|
|
94
94
|
rowKey={rowKey}
|
|
95
95
|
search={search}
|
|
96
|
+
scroll={scroll}
|
|
96
97
|
loading={loading}
|
|
97
|
-
scroll={{ y: 400 }}
|
|
98
98
|
columns={finalColumns}
|
|
99
99
|
dataSource={dataSource}
|
|
100
100
|
rowSelection={rowSelection}
|
|
101
101
|
pagination={modalPagination}
|
|
102
102
|
onChange={handleTableChange}
|
|
103
|
+
isLocalPaging={isLocalPaging}
|
|
103
104
|
setDataSource={setDataSource}
|
|
104
105
|
/>
|
|
105
106
|
</Modal>
|
|
@@ -22,7 +22,6 @@ import { Row, Col, Form, Button, Space } from 'antd'
|
|
|
22
22
|
* @param {React.ReactNode} [props.extraOperate] - 额外挂件:在按钮区域注入自定义战术单元
|
|
23
23
|
* @param {boolean} props.loading - 战场负载状态:请求中时锁定按钮,防止由于重复指令导致的逻辑冲突
|
|
24
24
|
* @param {boolean} [props.syncUrlParams=true] - 物理同步开关:是否将搜索状态实时镜像至 URL 链接中
|
|
25
|
-
* @param {Object} props.initialParams - [重要] 战术底价:定义不可被重置协议核平的核心业务参数(如 projectId)
|
|
26
25
|
*
|
|
27
26
|
* @example
|
|
28
27
|
* <MySearchForm
|
|
@@ -32,7 +31,7 @@ import { Row, Col, Form, Button, Space } from 'antd'
|
|
|
32
31
|
* formItems={[{ label: '状态', name: 'status', type: 'select', options: [...] }]}
|
|
33
32
|
* />
|
|
34
33
|
*/
|
|
35
|
-
export const MySearchForm = ({ form, search, loading, labelCol, setSearch, formItems, showLimit = 7,
|
|
34
|
+
export const MySearchForm = ({ form, search, loading, labelCol, setSearch, formItems, showLimit = 7, initialValues, initialParams, customReset, extraOperate, customFinish, onValuesChange, syncUrlParams = true, defaultPageSize = 10 }) => {
|
|
36
35
|
|
|
37
36
|
const [limit, setLimit] = useState(showLimit)
|
|
38
37
|
|
|
@@ -54,7 +54,7 @@ import { useRef, useMemo, useEffect, useCallback } from 'react'
|
|
|
54
54
|
* }}
|
|
55
55
|
* />
|
|
56
56
|
*/
|
|
57
|
-
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 }) => {
|
|
57
|
+
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' }, isLocalPaging = false, ...restProps }) => {
|
|
58
58
|
|
|
59
59
|
const tableRef = useRef(null)
|
|
60
60
|
const hasScrolledRef = useRef(false)
|
|
@@ -138,9 +138,11 @@ export const MyTable = ({ size, query, total, search, autoScroll, onChange, pagi
|
|
|
138
138
|
return {
|
|
139
139
|
total,
|
|
140
140
|
showSizeChanger: true,
|
|
141
|
-
current: Number(search?.pageNo),
|
|
142
|
-
pageSize: Number(search?.pageSize),
|
|
143
141
|
showTotal: (total) => `共 ${total} 条`,
|
|
142
|
+
...(isLocalPaging ? {} : {
|
|
143
|
+
current: Number(search?.pageNo),
|
|
144
|
+
pageSize: Number(search?.pageSize),
|
|
145
|
+
}),
|
|
144
146
|
...(typeof pagination === 'object' ? pagination : {})
|
|
145
147
|
}
|
|
146
148
|
}, [total, search, pagination])
|
|
@@ -183,7 +185,7 @@ export const MyTable = ({ size, query, total, search, autoScroll, onChange, pagi
|
|
|
183
185
|
pagination={paginationConfig}
|
|
184
186
|
components={isEditable ? components : undefined}
|
|
185
187
|
// 💡 信号注入:将行监听协议绑定至底层 tr
|
|
186
|
-
onRow={(record, index) => ({ record, index, ...(typeof
|
|
188
|
+
onRow={(record, index) => ({ record, index, ...(typeof lineFormChange === 'function' ? { onValuesChange: lineFormChange } : {}) })}
|
|
187
189
|
rowClassName={(record, index) => {
|
|
188
190
|
const externalClass = typeof rowClassName === 'function' ? rowClassName(record, index) : rowClassName
|
|
189
191
|
const classes = [externalClass]
|
|
@@ -21,17 +21,15 @@
|
|
|
21
21
|
{{#if hasPagination}}
|
|
22
22
|
{{{{raw}}}}
|
|
23
23
|
onChange={(pagination, filters, sorter) => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
window.history.replaceState(null, '', `${window.location.pathname}?${params.toString()}`)
|
|
28
|
-
setSearch(newSearch)
|
|
24
|
+
const newSearch = { ...search, pageNo: pagination.current ?? 1, pageSize: pagination.pageSize ?? 10 }
|
|
25
|
+
if (sorter.order) {
|
|
26
|
+
newSearch.orderBys = sorter.column
|
|
29
27
|
} else {
|
|
30
|
-
|
|
31
|
-
const params = new URLSearchParams(Object.fromEntries(Object.entries({ ...query, ...newSearch }).filter(([key, value]) => !['', null, undefined].includes(value))))
|
|
32
|
-
window.history.replaceState(null, '', `${window.location.pathname}?${params.toString()}`)
|
|
33
|
-
setSearch(newSearch)
|
|
28
|
+
newSearch.orderBys = undefined
|
|
34
29
|
}
|
|
30
|
+
const params = new URLSearchParams(Object.fromEntries(Object.entries({ ...query, ...newSearch }).filter(([key, value]) => !['', null, undefined].includes(value))))
|
|
31
|
+
window.history.replaceState(null, '', `${window.location.pathname}?${params.toString()}`)
|
|
32
|
+
setSearch(newSearch)
|
|
35
33
|
}}
|
|
36
34
|
{{{{/raw}}}}
|
|
37
35
|
{{/if}}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useRef, useState, useEffect, useCallback } from 'react'
|
|
1
|
+
import { useRef, useMemo, useState, useEffect, useCallback } from 'react'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @hook useTableQuery
|
|
@@ -29,7 +29,7 @@ import { useRef, useState, useEffect, useCallback } from 'react'
|
|
|
29
29
|
* formatResponse: (res) => ({ data: res.list, total: res.total })
|
|
30
30
|
* });
|
|
31
31
|
*/
|
|
32
|
-
export const useTableQuery = ({ api, cols, initialParams = {}, formatResponse }) => {
|
|
32
|
+
export const useTableQuery = ({ api, cols = [], initialParams = {}, formatResponse, isLocalPaging = false }) => {
|
|
33
33
|
|
|
34
34
|
const [total, setTotal] = useState(0)
|
|
35
35
|
const [columns, setColumns] = useState(cols)
|
|
@@ -41,6 +41,8 @@ export const useTableQuery = ({ api, cols, initialParams = {}, formatResponse })
|
|
|
41
41
|
const apiRef = useRef(api)
|
|
42
42
|
// 💡 [核心防御] 信号 ID 计数器:用于物理拦截由于网络时延导致的“竞态病毒(Race Condition)”
|
|
43
43
|
const fetchIdRef = useRef(0)
|
|
44
|
+
// 💡 物理记录仪:记录上一次“真正”的过滤参数(不含分页)
|
|
45
|
+
const lastFilterRef = useRef('')
|
|
44
46
|
// 💡 [核心防御] 数据解码协议引用:确保异步回调中永远能指向最新的指令地址
|
|
45
47
|
const formatResponseRef = useRef(formatResponse)
|
|
46
48
|
|
|
@@ -65,6 +67,15 @@ export const useTableQuery = ({ api, cols, initialParams = {}, formatResponse })
|
|
|
65
67
|
*/
|
|
66
68
|
const fetchData = useCallback(async () => {
|
|
67
69
|
if (!apiRef.current) return
|
|
70
|
+
|
|
71
|
+
if (isLocalPaging) {
|
|
72
|
+
const currentFilter = JSON.stringify(Object.fromEntries(Object.entries(search).filter(([k]) => !['pageNo', 'pageSize'].includes(k))))
|
|
73
|
+
if (lastFilterRef.current === currentFilter && dataSource.length > 0) {
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
lastFilterRef.current = currentFilter
|
|
77
|
+
}
|
|
78
|
+
|
|
68
79
|
setLoading(true)
|
|
69
80
|
// 💡 信号加标:为本次请求分配唯一的物理 ID
|
|
70
81
|
const currentFetchId = ++fetchIdRef.current
|
|
@@ -83,7 +94,7 @@ export const useTableQuery = ({ api, cols, initialParams = {}, formatResponse })
|
|
|
83
94
|
return prevCols
|
|
84
95
|
})
|
|
85
96
|
}
|
|
86
|
-
setDataSource(data)
|
|
97
|
+
setDataSource(data ?? [])
|
|
87
98
|
setTotal(total ?? 0)
|
|
88
99
|
} catch (error) {
|
|
89
100
|
console.error('查询失败', error)
|
|
@@ -93,11 +104,11 @@ export const useTableQuery = ({ api, cols, initialParams = {}, formatResponse })
|
|
|
93
104
|
setLoading(false)
|
|
94
105
|
}
|
|
95
106
|
}
|
|
96
|
-
}, [search])
|
|
107
|
+
}, [search, isLocalPaging, dataSource.length])
|
|
97
108
|
|
|
98
109
|
useEffect(() => {
|
|
99
110
|
fetchData()
|
|
100
111
|
}, [fetchData])
|
|
101
112
|
|
|
102
|
-
return { total, columns, loading, dataSource,
|
|
113
|
+
return { total, columns, search, loading, dataSource, setSearch, setLoading, setDataSource, refresh: fetchData }
|
|
103
114
|
}
|