@yorha2b-lab/autodev 7.7.7 → 7.8.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.
Files changed (31) hide show
  1. package/bin/bunker.js +2 -0
  2. package/config.js +2 -0
  3. package/package.json +1 -1
  4. package/src/constitution/inspector.js +20 -0
  5. package/src/constitution/system.js +2 -8
  6. package/src/dialogues/EN-US.js +8 -0
  7. package/src/dialogues/FR-FR.js +8 -0
  8. package/src/dialogues/JA-JP.js +8 -0
  9. package/src/dialogues/RU-RU.js +8 -0
  10. package/src/dialogues/ZH-CN.js +8 -0
  11. package/src/gateway/client.js +1 -1
  12. package/src/gateway/index.js +17 -7
  13. package/src/labs/index.js +5 -1
  14. package/src/labs/inspector.js +183 -0
  15. package/src/labs/scout.js +0 -2
  16. package/src/logistics/formatter.js +32 -0
  17. package/src/workshop/react/builder/index.js +1 -1
  18. package/src/workshop/react/extensions/useTableQuery.js +0 -51
  19. package/src/workshop/react/kit/utils.js +51 -102
  20. package/src/workshop/react/standardParts/AliyunOSSUpload.js +0 -45
  21. package/src/workshop/react/standardParts/DraggableContainer.js +1 -1
  22. package/src/workshop/react/standardParts/EditableCell.js +32 -74
  23. package/src/workshop/react/standardParts/MyBaseForm.js +1 -48
  24. package/src/workshop/react/standardParts/MyDescription.js +2 -2
  25. package/src/workshop/react/standardParts/MyForm.js +41 -15
  26. package/src/workshop/react/standardParts/MyModalForm.js +3 -84
  27. package/src/workshop/react/standardParts/MyModalTable.js +0 -53
  28. package/src/workshop/react/standardParts/MyPopoverTable.js +23 -0
  29. package/src/workshop/react/standardParts/MySearchForm.js +2 -60
  30. package/src/workshop/react/standardParts/MyTable.js +38 -122
  31. package/src/workshop/react/standardParts/index.js +5 -18
@@ -1,6 +1,11 @@
1
+ import dayjs from 'dayjs'
1
2
  import { MyBaseForm } from './MyBaseForm'
3
+ import { flattenFormItems } from '../utils/utils'
4
+ import { useContext, createContext } from 'react'
2
5
  import { Row, Col, Form, Button, Collapse } from 'antd'
3
6
 
7
+ const FormContext = createContext({})
8
+
4
9
  const LAYOUT_PROCESSORS = {
5
10
  section: ({ item, renderCore }) => (
6
11
  <Col span={item.span ?? 24} style={{ marginBottom: 16 }}>
@@ -24,9 +29,7 @@ const LAYOUT_PROCESSORS = {
24
29
  default: ({ item, renderCore }) => <Row gutter={[24, 0]}>{renderCore(item.childItems || [])}</Row>
25
30
  }
26
31
 
27
- // 定义“零部件增强协议”处理器,以后加任何新功能,只需要在这个对象里加一个 Key,不需要动主逻辑!
28
32
  const ACTION_PROCESSORS = {
29
- // 处理尾操作按钮
30
33
  addonAfter: (item, { tableConfig, setModalTable, setSelectedTableRows }) => ({
31
34
  props: {
32
35
  addonAfter: (
@@ -43,12 +46,12 @@ const ACTION_PROCESSORS = {
43
46
  // prefix: (item) => ({ props: { prefix: <Icon type={item.prefix} /> } }),
44
47
  }
45
48
 
46
- const FormRenderer = ({ form, options, formItems, renderAction, tableProps = {} }) => {
49
+ const FormRenderer = ({ form, options, formItems }) => {
50
+
51
+ const { tableProps, renderAction } = useContext(FormContext)
47
52
 
48
- // 定义核心渲染逻辑,供递归使用
49
- const renderCore = items => <FormRenderer form={form} options={options} formItems={items} renderAction={renderAction} tableProps={tableProps} />
53
+ const renderCore = items => <FormRenderer form={form} options={options} formItems={items} />
50
54
 
51
- // 定义单项动作注入逻辑
52
55
  const getRenderItemProps = item => {
53
56
  if (!item.renderAction) return {}
54
57
  const props = {}
@@ -61,14 +64,12 @@ const FormRenderer = ({ form, options, formItems, renderAction, tableProps = {}
61
64
  }
62
65
 
63
66
  return formItems.map((item, index) => {
64
- // 💡 保持纯粹不可变:通过解构浅拷贝,绝不污染原始 formItems 对象!
65
67
  const processedItem = {
66
68
  ...item,
67
69
  ...(item.renderAction && renderAction?.[item.name] ? { render: renderAction[item.name] } : {}),
68
70
  ...(['select', 'radio', 'checkbox'].includes(item.type) && !item.options ? { options: options?.[item.name] || [] } : {})
69
71
  }
70
72
 
71
- // 情况 A:发现容器型零件 (含有 layoutType)
72
73
  if (processedItem.layoutType && LAYOUT_PROCESSORS[processedItem.layoutType]) {
73
74
  const Layout = LAYOUT_PROCESSORS[processedItem.layoutType] ?? LAYOUT_PROCESSORS.default
74
75
  return (
@@ -80,7 +81,6 @@ const FormRenderer = ({ form, options, formItems, renderAction, tableProps = {}
80
81
  )
81
82
  }
82
83
 
83
- // 情况 B:标准作战零件
84
84
  return (
85
85
  <Col span={processedItem.span ?? 6} key={processedItem.name || index}>
86
86
  <MyBaseForm item={{ ...processedItem, ...getRenderItemProps(processedItem) }} form={form} />
@@ -89,16 +89,42 @@ const FormRenderer = ({ form, options, formItems, renderAction, tableProps = {}
89
89
  })
90
90
  }
91
91
 
92
- export const MyForm = ({ formItems = [], externalForm, labelCol, wrapperCol, tableProps, onValuesChange, layout = 'horizontal', options = {}, renderAction = {} }) => {
92
+ export const MyForm = ({ record, formItems = [], externalForm, labelCol, wrapperCol, tableProps, onValuesChange, layout = 'horizontal', options = {}, renderAction = {} }) => {
93
93
 
94
94
  const [internalForm] = Form.useForm()
95
95
  const form = externalForm || internalForm
96
96
 
97
+ useEffect(() => {
98
+ if (record && Object.keys(record).length > 0) {
99
+ const itemMap = new Map(flattenFormItems(formItems).map(i => [i.name, i]))
100
+ const initialData = {}
101
+ Object.entries(record).forEach(([key, value]) => {
102
+ const config = itemMap.get(key)
103
+ if (config?.type?.includes('date') && value) {
104
+ if (Array.isArray(value)) {
105
+ initialData[key] = value.map(v => dayjs(v))
106
+ } else if (typeof value === 'string' && value.includes(',')) {
107
+ initialData[key] = value.split(',').map(v => dayjs(v))
108
+ } else {
109
+ initialData[key] = dayjs(value)
110
+ }
111
+ } else if ((config?.type === 'select' && config.mode) || config?.type === 'checkbox') {
112
+ initialData[key] = Array.isArray(value) ? value : value.split(config.separator ?? ',')
113
+ } else {
114
+ initialData[key] = value
115
+ }
116
+ })
117
+ form.setFieldsValue(initialData)
118
+ }
119
+ }, [form, record, formItems])
120
+
97
121
  return (
98
- <Form form={form} layout={layout} preserve={false} labelCol={labelCol} wrapperCol={wrapperCol} onValuesChange={onValuesChange} scrollToFirstError={{ behavior: 'smooth', block: 'center' }}>
99
- <Row gutter={[24, 0]}>
100
- <FormRenderer form={form} options={options} formItems={formItems} renderAction={renderAction} tableProps={tableProps} />
101
- </Row>
102
- </Form>
122
+ <FormContext.Provider value={{ tableProps, renderAction }}>
123
+ <Form form={form} layout={layout} preserve={false} labelCol={labelCol} wrapperCol={wrapperCol} onValuesChange={onValuesChange} scrollToFirstError={{ behavior: 'smooth', block: 'center' }}>
124
+ <Row gutter={[24, 0]}>
125
+ <FormRenderer form={form} options={options} formItems={formItems} />
126
+ </Row>
127
+ </Form>
128
+ </FormContext.Provider>
103
129
  )
104
130
  }
@@ -1,44 +1,9 @@
1
- import dayjs from 'dayjs'
2
1
  import { MyForm } from './MyForm'
3
2
  import { Form, Modal } from 'antd'
3
+ import { useMemo, useState } from 'react'
4
4
  import { MyModalTable } from './MyModalTable'
5
- import { useRef, useMemo, useState, useEffect } from 'react'
5
+ import { flattenFormItems } from '../utils/utils'
6
6
 
7
- const flattenFormItems = items => {
8
- if (!Array.isArray(items)) return []
9
- return items.flatMap(item => {
10
- if (item.childItems) {
11
- return flattenFormItems(item.childItems)
12
- }
13
- return item
14
- })
15
- }
16
-
17
- /**
18
- * @component MyModalForm
19
- * @description [地堡核心构筑舱] 通用弹窗表单组件。
20
- * 集成了“语义回显自愈”、“自动时间戳转换”及“原子级提交锁定”三大核心协议。
21
- *
22
- * @param {Object} props - 构筑参数
23
- * @param {string} [props.title] - 舱体视觉标题
24
- * @param {Object} [props.labelCol] - 标签布局校准
25
- * @param {Object} [props.wrapperCol] - 控件布局校准
26
- * @param {string|number} [props.width] - 舱体物理宽度
27
- * @param {boolean} props.visible - 激活信号:控制弹窗的物理显示状态
28
- * @param {Array} props.modalItems - 零部件清单:定义表单内部的输入单元
29
- * @param {Object} [props.record] - 初始物资包:用于编辑模式的数据回显
30
- * @param {Function} props.submit - 数据发射协议:提交表单后的核心回调函数
31
- * @param {Function} props.setModal - 指令中心:用于更新弹窗状态(开启/关闭)
32
- * @param {Function} [props.onValuesChange] - 联动传感:捕获表单内部的信号波动
33
- *
34
- * @example
35
- * <MyModalForm
36
- * title='构筑新模块'
37
- * visible={visible}
38
- * submit={async (vals) => await save(vals)}
39
- * modalItems={[{ label: '截止日期', name: 'deadline', type: 'date' }]}
40
- * />
41
- */
42
7
  export const MyModalForm = ({ extra, width, title, layout, okText, options, footer, submit, record, visible, setModal, labelCol, modalItems, wrapperCol, cancelText, tableConfig, okButtonProps, renderAction, onValuesChange, handleModalTableOk }) => {
43
8
 
44
9
  const rowKey = tableConfig?.rowKey || 'id'
@@ -48,47 +13,6 @@ export const MyModalForm = ({ extra, width, title, layout, okText, options, foot
48
13
  const [modalTable, setModalTable] = useState({})
49
14
  const [selectedTableRows, setSelectedTableRows] = useState([])
50
15
 
51
- const prevVisibleRef = useRef(false)
52
-
53
- /**
54
- * @description [数据回显协议] 当构筑舱开启时,自动对初始物资进行“语义格式化”。
55
- * 将后端传输的字符串/数字时间戳重新转化为地堡可读的 `dayjs` 对象。
56
- */
57
- useEffect(() => {
58
- if (visible && !prevVisibleRef.current) {
59
- if (record && Object.keys(record).length > 0) {
60
- const itemMap = new Map(flattenFormItems(modalItems).map(i => [i.name, i]))
61
- const initialData = {}
62
- Object.entries(record).forEach(([key, value]) => {
63
- const config = itemMap.get(key)
64
- // 语义识别:若零部件类型为日期且数值存在,执行物理转化
65
- if (config?.type?.includes('date') && value) {
66
- if (Array.isArray(value)) {
67
- // 处理范围日期
68
- initialData[key] = value.map(v => dayjs(v))
69
- } else if (typeof value === 'string' && value.includes(',')) {
70
- // 处理逗号分隔的字符串日期
71
- initialData[key] = value.split(',').map(v => dayjs(v))
72
- } else {
73
- // 处理单日期
74
- initialData[key] = dayjs(value)
75
- }
76
- } else {
77
- initialData[key] = value
78
- }
79
- })
80
- form.setFieldsValue(initialData)
81
- }
82
- }
83
- prevVisibleRef.current = visible
84
- }, [visible, record, form, modalItems])
85
-
86
- /**
87
- * @async
88
- * @function handleOk
89
- * @description [物理加压提交] 执行表单校验,并自动启动“时间戳转换协议”。
90
- * 确保发射至后端的数据包符合标准 Unix 时间戳(毫秒)规范。
91
- */
92
16
  const handleOk = async (extraParams = {}) => {
93
17
  try {
94
18
  const values = await form.validateFields()
@@ -97,10 +21,8 @@ export const MyModalForm = ({ extra, width, title, layout, okText, options, foot
97
21
  const val = formattedValues[item.name]
98
22
  if (item.type?.includes('date') && val) {
99
23
  if (Array.isArray(val)) {
100
- // 物理压实:将范围日期转化为逗号分隔的时间戳字符串
101
24
  formattedValues[item.name] = val.map(v => v.valueOf()).join(',')
102
25
  } else {
103
- // 物理转化:将 dayjs 对象还原为原始数值(毫秒)
104
26
  formattedValues[item.name] = val.valueOf()
105
27
  }
106
28
  }
@@ -118,10 +40,6 @@ export const MyModalForm = ({ extra, width, title, layout, okText, options, foot
118
40
 
119
41
  const modalTableOk = () => handleModalTableOk?.({ form, selectedTableRows, setModalTable })
120
42
 
121
- /**
122
- * @function handleCancel
123
- * @description 执行“撤退协议”:关闭构筑舱并清除当前逻辑残留。
124
- */
125
43
  const handleCancel = () => {
126
44
  setModal({ visible: false })
127
45
  form.resetFields()
@@ -153,6 +71,7 @@ export const MyModalForm = ({ extra, width, title, layout, okText, options, foot
153
71
  {modalTable.visible && <MyModalTable rowKey={rowKey} {...modalTable} rowSelection={tableRowSelection} onOk={modalTableOk} setModal={setModalTable} />}
154
72
  {extra?.header?.(ctx)}
155
73
  <MyForm
74
+ record={record}
156
75
  layout={layout}
157
76
  options={options}
158
77
  externalForm={form}
@@ -4,58 +4,17 @@ import { MySearchForm } from './MySearchForm'
4
4
  import { Form, Modal, Space, Button } from 'antd'
5
5
  import { useTableQuery } from '../hooks/useTableQuery'
6
6
 
7
- /**
8
- * @component MyModalTable
9
- * @description [地堡支援型作战单元] 弹窗数据选择平台。
10
- * 专门用于执行“关联选择”、“子数据审计”或“二级逻辑骇入”任务。
11
- * 具备独立的数据获取链路、可选的搜索过滤系统以及与主构筑舱(setModalForm)的跨维协同能力。
12
- *
13
- * @param {Object} props - 支援参数
14
- * @param {string} [props.title] - 舱体视觉标题
15
- * @param {Function} props.api - 弹窗内独立的数据源指令
16
- * @param {string|number} [props.width] - 舱体物理宽度
17
- * @param {Object} [props.rowSelection] - 战术勾选:用于多选物资
18
- * @param {boolean} props.visible - 激活信号:控制支援单元的显示状态
19
- * @param {Function} props.setModal - 单元注销中心:用于关闭当前弹窗
20
- * @param {Array} props.columns - 零部件初始清单 (别名: modalColumns)
21
- * @param {Function} [props.onOk] - 战果物理确认:按下确认按钮后的异步回调协议
22
- * @param {Object} [props.initialParams={}] - 初始战备物资:定义的初始搜索参数
23
- * @param {React.ReactNode} [props.footer] - 底座指令区:支持完全自定义操作按钮
24
- * @param {Array} [props.formItems] - 局部搜索零部件:用于在弹窗内执行精准情报过滤
25
- * @param {Function} props.formatResponse - 数据清洗协议:对 API 吐出的原始物资进行格式化
26
- * @param {boolean|Object} [props.modalPagination=true] - 局部后勤协议:弹窗内是否开启分页
27
- * @param {Array} [props.functionButtons] - 功能挂件组:位于表格上方的操作按钮,可直接操控表格状态
28
- * @param {Function} props.operate - 动态火力配置:生成操作列的函数,接收 { refresh, setModalForm } 句柄
29
- * @param {Function} [props.setModalForm] - [重要] 跨维调用:由主页面注入的表单控制句柄,实现“弹窗开弹窗”的非耦合交互
30
- *
31
- * @example
32
- * <MyModalTable
33
- * title='关联用户片段'
34
- * api={async params=>await fetchUsers(params)}
35
- * setModalForm={setModal} // 💡 借用主页面的表单构筑舱
36
- * operate={({ refresh, setModalForm }) => ({ label: '详情', onClick: (rec) => setModalForm(...) })}
37
- * />
38
- */
39
7
  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
8
 
41
9
  const [form] = Form.useForm()
42
10
  const [pending, setPending] = useState(false)
43
11
 
44
- /**
45
- * @description [独立信号链路]
46
- * 在弹窗内部启动专属的 useTableQuery,实现了与主页面逻辑的“物理隔绝”。
47
- */
48
12
  const { total, loading, columns, dataSource, search, refresh, setLoading, setSearch, setDataSource } = useTableQuery({ api, cols: modalColumns, initialParams, formatResponse, isLocalPaging })
49
13
 
50
14
  const handleSearch = values => setSearch({ ...search, ...values, pageNo: 1 })
51
15
 
52
16
  const handleTableChange = (pagination, filters, sorter) => setSearch({ ...search, pageNo: pagination.current, pageSize: pagination.pageSize, orderBy: sorter.column ? sorter.field : undefined })
53
17
 
54
- /**
55
- * @async
56
- * @function handleOk
57
- * @description [确认指令闭环] 执行确认操作并启动原子级锁定(Pending),防止重复指令干扰。
58
- */
59
18
  const handleOk = async () => {
60
19
  if (onOk) {
61
20
  try {
@@ -69,25 +28,13 @@ export const MyModalTable = ({ api, onOk, title, width, footer, visible, operate
69
28
  }
70
29
  }
71
30
 
72
- /**
73
- * @constant finalColumns
74
- * @description [火力全开] 将 Hook 生成的动态列与指挥官注入的 operate 操作列进行物理合并。
75
- */
76
31
  const finalColumns = useMemo(() => {
77
32
  return operate ? columns.concat(operate({ refresh, setModalForm })) : columns
78
33
  }, [columns, operate])
79
34
 
80
35
  return (
81
36
  <Modal centered destroyOnClose title={title} width={width} open={visible} onOk={handleOk} footer={footer?.({ setModal })} onCancel={() => setModal({ visible: false })} confirmLoading={pending} bodyStyle={{ paddingBottom: 0 }}>
82
- {/*
83
- 💡 [地堡战术细节]
84
- syncUrlParams={false}:确保弹窗内的搜索行为不会干扰全局 URL 信号。
85
- */}
86
37
  {formItems?.length > 0 && <MySearchForm form={form} search={search} formItems={formItems} setSearch={handleSearch} syncUrlParams={false} />}
87
- {/*
88
- 💡 [高阶赋能逻辑]
89
- functionButtons 能够获得对当前表格状态 (refresh, setLoading...) 的完全控制权。
90
- */}
91
38
  {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
39
  <MyTable
93
40
  total={total}
@@ -0,0 +1,23 @@
1
+ import { Popover } from 'antd'
2
+ import { useState } from 'react'
3
+ import { MyTable } from './MyTable'
4
+
5
+ export const MyPopoverTable = ({ api, columns, children, formatter }) => {
6
+
7
+ const [loading, setLoading] = useState(false)
8
+ const [dataSource, setDataSource] = useState([])
9
+
10
+ const fetchData = async () => {
11
+ if (loading) return
12
+ setLoading(true)
13
+ const response = await api()
14
+ const datas = formatter(response)
15
+ setDataSource(datas)
16
+ }
17
+
18
+ return (
19
+ <Popover trigger='click' content={<MyTable columns={columns} loading={loading} dataSource={dataSource} />}>
20
+ <span onClick={fetchData}>{children}</span>
21
+ </Popover>
22
+ )
23
+ }
@@ -4,34 +4,6 @@ import { useMemo, useState, useEffect } from 'react'
4
4
  import { Row, Col, Form, Button, Space } from 'antd'
5
5
  import { SearchOutlined, RedoOutlined } from '@ant-design/icons'
6
6
 
7
- /**
8
- * @component MySearchForm
9
- * @description [地堡战术搜索中心] 高级搜索表单组件。
10
- * 集成了“URL 状态同步(持久化)”、“日期范围物理拆解”以及“动态伸缩布局”三大核心能力。
11
- *
12
- * @param {Object} props - 指挥官指令包
13
- * @param {Object} props.form - Antd Form 实例,表单的主控舵盘
14
- * @param {Function} [props.customReset] - 逻辑劫持:自定义重置协议
15
- * @param {Function} [props.customFinish] - 逻辑劫持:自定义提交协议
16
- * @param {Array} props.formItems - 零部件清单:定义所有的搜索输入单元
17
- * @param {number} [props.showLimit=7] - 初始可见能级:默认展示的零部件数量
18
- * @param {number} [props.defaultPageSize=10] - 默认吞吐量:重置时的初始每页条数
19
- * @param {Function} [props.onValuesChange] - 联动传感:当表单值变动时的即时反馈
20
- * @param {Object} props.initialValues - 初始信号:从 URL 或缓存中解析出的初始参数
21
- * @param {React.ReactNode} [props.extra] - 额外挂件:在按钮区域注入自定义战术单元
22
- * @param {Function} props.setSearch - 状态分发器:用于更新全局搜索参数并触发重新构筑
23
- * @param {Object} props.search - 当前的全局搜索状态(包含 pageNo, pageSize 及各过滤项)
24
- * @param {boolean} props.loading - 战场负载状态:请求中时锁定按钮,防止由于重复指令导致的逻辑冲突
25
- * @param {boolean} [props.syncUrlParams=true] - 物理同步开关:是否将搜索状态实时镜像至 URL 链接中
26
- *
27
- * @example
28
- * <MySearchForm
29
- * form={form}
30
- * search={search}
31
- * setSearch={setSearch}
32
- * formItems={[{ label: '状态', name: 'status', type: 'select', options: [...] }]}
33
- * />
34
- */
35
7
  export const MySearchForm = ({ form, extra, search, options, loading, labelCol, setSearch, formItems, showLimit = 7, initialValues = {}, initialParams = {}, customReset, customFinish, renderAction, onValuesChange, syncUrlParams = true, defaultPageSize = 10 }) => {
36
8
 
37
9
  const [limit, setLimit] = useState(showLimit)
@@ -42,55 +14,39 @@ export const MySearchForm = ({ form, extra, search, options, loading, labelCol,
42
14
  ...(['select'].includes(item.type) ? { options: item.options ?? options?.[item.name] ?? [] } : {})
43
15
  })), [options, formItems, renderAction])
44
16
 
45
- /**
46
- * @function handleReset
47
- * @description 执行“归零协议”:清除当前所有搜索信号,并可选择性擦除 URL 中的物理记录。
48
- */
49
17
  const handleReset = () => {
50
18
  form.resetFields()
51
19
  if (syncUrlParams) {
52
- // 💡 修正逻辑:只有当 formItems 里【没有任何一项】包含这个 key 时,才视为默认参数
53
20
  const defaultQueryEntries = Object.entries(initialValues).filter(([key, _]) => !formItems.some(item => item.name.includes(key)))
54
21
  const defaultQuery = Object.fromEntries(defaultQueryEntries)
55
22
  const params = new URLSearchParams(defaultQuery)
56
- // 💡 执行物理重写:将 URL 恢复为仅含默认参数的状态
57
23
  const newUrl = params.toString() ? `${window.location.pathname}?${params.toString()}` : window.location.pathname
58
24
  window.history.replaceState(null, '', newUrl)
59
25
  }
60
26
  if (customReset) {
61
27
  customReset()
62
28
  } else {
63
- // 逻辑重置:强制返回第一页,并恢复默认吞吐量
64
29
  setSearch({ pageNo: 1, pageSize: search.pageSize ?? defaultPageSize, ...initialParams })
65
30
  }
66
31
  }
67
32
 
68
- /**
69
- * @function handleFinish
70
- * @description 执行“数据发射协议”:对原始表单数据进行脱水和转换,尤其处理复杂的日期范围协议。
71
- * @param {Object} values - 原始表单数据包
72
- */
73
33
  const handleFinish = values => {
74
34
  const formattedValues = { ...values }
75
35
  Object.keys(formattedValues).forEach(key => {
76
36
  const val = formattedValues[key]
77
37
  const isDateType = formItems.find(item => item.name === key)?.type?.includes('date')
78
- // 💡 [黑科技 1] 日期自动转换与拆解
79
38
  if (isDateType && val) {
80
39
  if (Array.isArray(val) && val.length > 0) {
81
- // 物理拆分:当 name 为 'start,end' 格式时,自动拆解为两个独立的后端参数
82
40
  const [startKey, endKey] = key.split(',')
83
- formattedValues[startKey] = val[0].startOf('day').valueOf()// 修正为当日 00:00:00
84
- formattedValues[endKey] = val[1].endOf('day').valueOf()// 修正为当日 23:59:59
41
+ formattedValues[startKey] = val[0].startOf('day').valueOf()
42
+ formattedValues[endKey] = val[1].endOf('day').valueOf()
85
43
  delete formattedValues[key]
86
44
  } else {
87
- // 单日转换:转化为毫秒级数字
88
45
  formattedValues[key] = val.valueOf()
89
46
  }
90
47
  }
91
48
  })
92
49
  if (syncUrlParams) {
93
- // 💡 [黑科技 2] 镜像至 URL
94
50
  const currentQuery = Object.fromEntries(new URLSearchParams(window.location.search).entries())
95
51
  const params = new URLSearchParams(Object.fromEntries(Object.entries({ ...currentQuery, ...formattedValues }).filter(([key, value]) => !['', null, undefined].includes(value))))
96
52
  window.history.replaceState(null, '', `${window.location.pathname}?${params.toString()}`)
@@ -102,27 +58,20 @@ export const MySearchForm = ({ form, extra, search, options, loading, labelCol,
102
58
  }
103
59
  }
104
60
 
105
- /**
106
- * @description [信号自愈] 初始化逻辑:
107
- * 将来自 URL 的字符串信号重新转化为地堡可读的 `dayjs` 对象。
108
- */
109
61
  useEffect(() => {
110
62
  if (Object.values(initialValues).length > 0 && formItems.length > 0) {
111
63
  const newValues = {}
112
64
  formItems.forEach(item => {
113
- // 处理单字段回显
114
65
  if (initialValues[item.name] !== undefined) {
115
66
  const value = initialValues[item.name]
116
67
  newValues[item.name] = item.type?.includes('date') ? dayjs(Number(value)) : value
117
68
  }
118
- // 处理双字段(日期范围)回显:执行物理重组
119
69
  if (item.name?.includes(',') && item.type?.includes('date')) {
120
70
  const [startKey, endKey] = item.name.split(',')
121
71
  if (initialValues[startKey] && initialValues[endKey]) {
122
72
  newValues[item.name] = [dayjs(Number(initialValues[startKey])), dayjs(Number(initialValues[endKey]))]
123
73
  }
124
74
  }
125
- // 处理多选框回显
126
75
  if (item.hasOwnProperty('mode') && !!initialValues[item.name]) {
127
76
  newValues[item.name] = initialValues[item.name]?.split(',')
128
77
  }
@@ -136,22 +85,15 @@ export const MySearchForm = ({ form, extra, search, options, loading, labelCol,
136
85
  <Row gutter={24}>
137
86
  {searchItems.slice(0, limit)?.map((item, ind) => (
138
87
  <Col key={ind} span={item.span ?? 6}>
139
- {/* 物理装配:利用底层 MyBaseForm 单元进行渲染 */}
140
88
  <MyBaseForm item={item} form={form} />
141
89
  </Col>
142
90
  ))}
143
- {/*
144
- 战术控制区:
145
- 1. 'flex=auto' 确保按钮组始终靠右对齐。
146
- 2. 'showLimit' 控制页面纵向熵值,防止过多搜索项挤占战场视野。
147
- */}
148
91
  <Col flex='auto' style={{ textAlign: 'right' }}>
149
92
  <Form.Item>
150
93
  <Space>
151
94
  <Button type='primary' icon={<SearchOutlined />} loading={loading} htmlType='submit'>查询</Button>
152
95
  <Button icon={<RedoOutlined />} onClick={handleReset} loading={loading} htmlType='reset'>重置</Button>
153
96
  {extra}
154
- {/* 动态伸缩协议:根据当前零部件数量决定是否显示展开/收起按钮 */}
155
97
  {formItems.length > showLimit && (<a onClick={() => setLimit(formItems.length > limit ? formItems.length : showLimit)}>{formItems.length > limit ? '展开' : '收起'}</a>)}
156
98
  </Space>
157
99
  </Form.Item>