@yorha2b-lab/autodev 3.4.1 → 3.4.3
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/context.js +1 -6
- package/src/core/react-compiler.js +1 -2
- package/src/utils/utils.js +1 -1
- package/templates/react/components/MyModalForm.js +2 -2
- package/templates/react/components/MyModalTable.js +6 -5
- package/templates/react/components/MySearchForm.js +11 -5
- package/templates/react/components/MyTable.js +6 -4
- package/templates/react/handlebars/MySearchForm.hbs +1 -1
- package/templates/react/handlebars/MyTable.hbs +7 -9
- package/templates/react/handlebars/hookBlock.hbs +2 -2
- package/templates/react/hooks/useTableQuery.js +16 -5
package/package.json
CHANGED
package/src/core/context.js
CHANGED
|
@@ -61,10 +61,5 @@ module.exports = {
|
|
|
61
61
|
/**
|
|
62
62
|
* @description 全频道信号接入:获取全局上下文
|
|
63
63
|
*/
|
|
64
|
-
get: () =>
|
|
65
|
-
if (!instance) {
|
|
66
|
-
throw new Error(language('🤖 Pod 042 报警:中枢神经元尚未初始化,无法建立信号连接。', '🤖 Pod 042 Warnning:Central neuron not initialized, cannot establish signal connection.'))
|
|
67
|
-
}
|
|
68
|
-
return instance
|
|
69
|
-
}
|
|
64
|
+
get: () => instance
|
|
70
65
|
}
|
|
@@ -14,7 +14,6 @@ const resource = ({ pageConfig, resourceTpl }) => {
|
|
|
14
14
|
|
|
15
15
|
const hasTabs = pageConfig.tabs?.length > 0
|
|
16
16
|
const hasFormItems = pageConfig.formItems?.length > 0
|
|
17
|
-
const hasDateColumn = processedColumns.some(item => item.type === 'date')
|
|
18
17
|
|
|
19
18
|
const columnsData = processedColumns.map(item => {
|
|
20
19
|
delete item.type
|
|
@@ -31,7 +30,7 @@ const resource = ({ pageConfig, resourceTpl }) => {
|
|
|
31
30
|
}
|
|
32
31
|
|
|
33
32
|
const bodyCode = resourceTpl(viewData)
|
|
34
|
-
const importsStr = generateSmartImports({ module: 'resource', hasTabs, bodyCode, hasFormItems
|
|
33
|
+
const importsStr = generateSmartImports({ module: 'resource', hasTabs, bodyCode, hasFormItems })
|
|
35
34
|
return cleanCode(`${importsStr}\n\n${bodyCode}`)
|
|
36
35
|
}
|
|
37
36
|
|
package/src/utils/utils.js
CHANGED
|
@@ -305,7 +305,7 @@ const copyTemplateDir = (options, templateSubDir, targetSubDir) => {
|
|
|
305
305
|
* @param {boolean} hasTabs - 是否包含标签页
|
|
306
306
|
* @returns {string} 拼接后的 import 语句
|
|
307
307
|
*/
|
|
308
|
-
const generateSmartImports = ({ module, hasTabs, bodyCode, hasFormItems
|
|
308
|
+
const generateSmartImports = ({ module, hasTabs, bodyCode, hasFormItems }) => {
|
|
309
309
|
|
|
310
310
|
const hooksLib = ['useTableQuery']
|
|
311
311
|
const utilsLib = ['timeRender', 'moneyRender']
|
|
@@ -120,7 +120,7 @@ const FormRenderer = ({ form, formItems, tableConfig, setModalTable, setSelected
|
|
|
120
120
|
* formItems={[{ label: '截止日期', name: 'deadline', type: 'date' }]}
|
|
121
121
|
* />
|
|
122
122
|
*/
|
|
123
|
-
export const MyModalForm = ({ width, title, submit, record, visible, setModal, labelCol, formItems, wrapperCol, tableConfig, onValuesChange, handleModalTableOk }) => {
|
|
123
|
+
export const MyModalForm = ({ width, title, footer, submit, record, visible, setModal, labelCol, formItems, wrapperCol, tableConfig, onValuesChange, handleModalTableOk }) => {
|
|
124
124
|
|
|
125
125
|
const rowKey = tableConfig?.rowKey || 'id'
|
|
126
126
|
|
|
@@ -222,7 +222,7 @@ export const MyModalForm = ({ width, title, submit, record, visible, setModal, l
|
|
|
222
222
|
}), [rowKey, modalTable, selectedTableRows])
|
|
223
223
|
|
|
224
224
|
return (
|
|
225
|
-
<Modal centered title={title} width={width} open={visible} onOk={handleOk} destroyOnClose onCancel={handleCancel} confirmLoading={pending}>
|
|
225
|
+
<Modal centered title={title} width={width} open={visible} onOk={handleOk} footer={footer?.({ setModal })} destroyOnClose onCancel={handleCancel} confirmLoading={pending} bodyStyle={{ paddingBottom: 0 }}>
|
|
226
226
|
{modalTable.visible && <MyModalTable rowKey={rowKey} {...modalTable} rowSelection={tableRowSelection} onOk={modalTableOk} setModal={setModalTable} />}
|
|
227
227
|
<Form form={form} preserve={false} labelCol={labelCol} wrapperCol={wrapperCol} onValuesChange={(changed, all) => onValuesChange?.({ changed, all, form, record })}>
|
|
228
228
|
<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>
|
|
@@ -31,7 +31,7 @@ import { Row, Col, Form, Button, Space } from 'antd'
|
|
|
31
31
|
* formItems={[{ label: '状态', name: 'status', type: 'select', options: [...] }]}
|
|
32
32
|
* />
|
|
33
33
|
*/
|
|
34
|
-
export const MySearchForm = ({ form, search, loading, labelCol, setSearch, formItems, showLimit = 7, initialValues, customReset, extraOperate, customFinish, onValuesChange, syncUrlParams = true, defaultPageSize = 10 }) => {
|
|
34
|
+
export const MySearchForm = ({ form, search, loading, labelCol, setSearch, formItems, showLimit = 7, initialValues, initialParams, customReset, extraOperate, customFinish, onValuesChange, syncUrlParams = true, defaultPageSize = 10 }) => {
|
|
35
35
|
|
|
36
36
|
const [limit, setLimit] = useState(showLimit)
|
|
37
37
|
|
|
@@ -42,14 +42,19 @@ export const MySearchForm = ({ form, search, loading, labelCol, setSearch, formI
|
|
|
42
42
|
const handleReset = () => {
|
|
43
43
|
form.resetFields()
|
|
44
44
|
if (syncUrlParams) {
|
|
45
|
-
//
|
|
46
|
-
|
|
45
|
+
// 💡 修正逻辑:只有当 formItems 里【没有任何一项】包含这个 key 时,才视为默认参数
|
|
46
|
+
const defaultQueryEntries = Object.entries(initialValues).filter(([key, _]) => !formItems.some(item => item.name.includes(key)))
|
|
47
|
+
const defaultQuery = Object.fromEntries(defaultQueryEntries)
|
|
48
|
+
const params = new URLSearchParams(defaultQuery)
|
|
49
|
+
// 💡 执行物理重写:将 URL 恢复为仅含默认参数的状态
|
|
50
|
+
const newUrl = params.toString() ? `${window.location.pathname}?${params.toString()}` : window.location.pathname
|
|
51
|
+
window.history.replaceState(null, '', newUrl)
|
|
47
52
|
}
|
|
48
53
|
if (customReset) {
|
|
49
54
|
customReset()
|
|
50
55
|
} else {
|
|
51
56
|
// 逻辑重置:强制返回第一页,并恢复默认吞吐量
|
|
52
|
-
setSearch({ pageNo: 1, pageSize: search.pageSize ?? defaultPageSize })
|
|
57
|
+
setSearch({ pageNo: 1, pageSize: search.pageSize ?? defaultPageSize, ...initialParams })
|
|
53
58
|
}
|
|
54
59
|
}
|
|
55
60
|
|
|
@@ -79,7 +84,8 @@ export const MySearchForm = ({ form, search, loading, labelCol, setSearch, formI
|
|
|
79
84
|
})
|
|
80
85
|
if (syncUrlParams) {
|
|
81
86
|
// 💡 [黑科技 2] 镜像至 URL
|
|
82
|
-
const
|
|
87
|
+
const currentQuery = Object.fromEntries(new URLSearchParams(window.location.search).entries())
|
|
88
|
+
const params = new URLSearchParams(Object.fromEntries(Object.entries({ ...currentQuery, ...formattedValues }).filter(([key, value]) => !['', null, undefined].includes(value))))
|
|
83
89
|
window.history.replaceState(null, '', `${window.location.pathname}?${params.toString()}`)
|
|
84
90
|
}
|
|
85
91
|
if (customFinish) {
|
|
@@ -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, onValuesChange: lineFormChange })}
|
|
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]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<MySearchForm form={form} search={search} initialValues={query} loading={loading} setSearch={setSearch} formItems={ {{formItems}} } />
|
|
1
|
+
<MySearchForm form={form} search={search} initialValues={query} loading={loading} setSearch={setSearch} formItems={ {{formItems}} } initialParams={initialParams} />
|
|
@@ -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,10 +1,10 @@
|
|
|
1
|
-
const
|
|
1
|
+
const initialParams = {{#if hasTabs}}{ type: tabs[0].key }{{else}}{}{{/if}}
|
|
2
2
|
|
|
3
3
|
const query = formatQuery(Object.fromEntries(new URLSearchParams(location.search).entries()), {{formItems}})
|
|
4
4
|
|
|
5
5
|
const { total, columns, loading, dataSource, search, setSearch, setDataSource, refresh } = useTableQuery({
|
|
6
6
|
api: async params => await request('/api/{{fileName}}', { method: 'POST', body: params }),
|
|
7
|
-
initialParams: { pageNo: 1, pageSize: 10, ...
|
|
7
|
+
initialParams: { pageNo: 1, pageSize: 10, ...initialParams, ...query },
|
|
8
8
|
cols:tableColumns,
|
|
9
9
|
formatResponse: response => {
|
|
10
10
|
return {
|
|
@@ -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
|
}
|