@yorha2b-lab/autodev 7.7.8 → 7.8.1

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 (35) hide show
  1. package/bin/bunker.js +3 -1
  2. package/config.js +2 -0
  3. package/package.json +1 -1
  4. package/src/constitution/inspector.js +21 -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/units/striker.js +1 -1
  18. package/src/workshop/react/blueprint/molds/MySearchForm.hbs +1 -1
  19. package/src/workshop/react/blueprint/molds/handleBlock.hbs +3 -4
  20. package/src/workshop/react/blueprint/molds/stateBlock.hbs +2 -0
  21. package/src/workshop/react/builder/index.js +1 -1
  22. package/src/workshop/react/extensions/useTableQuery.js +4 -65
  23. package/src/workshop/react/kit/utils.js +51 -102
  24. package/src/workshop/react/standardParts/AliyunOSSUpload.js +0 -45
  25. package/src/workshop/react/standardParts/DraggableContainer.js +6 -2
  26. package/src/workshop/react/standardParts/EditableCell.js +36 -74
  27. package/src/workshop/react/standardParts/MyBaseForm.js +4 -51
  28. package/src/workshop/react/standardParts/MyDescription.js +3 -3
  29. package/src/workshop/react/standardParts/MyForm.js +48 -18
  30. package/src/workshop/react/standardParts/MyModalForm.js +4 -85
  31. package/src/workshop/react/standardParts/MyModalTable.js +2 -55
  32. package/src/workshop/react/standardParts/MyPopoverTable.js +24 -0
  33. package/src/workshop/react/standardParts/MySearchForm.js +5 -63
  34. package/src/workshop/react/standardParts/MyTable.js +68 -148
  35. package/src/workshop/react/standardParts/index.js +67 -39
@@ -1,80 +1,20 @@
1
- import { Table } from 'antd'
1
+ import { Table, Space } from 'antd'
2
2
  import { DraggableContainer } from './DraggableContainer'
3
3
  import { EditableRow, EditableCell } from './EditableCell'
4
- import { useRef, useMemo, useEffect, useCallback } from 'react'
4
+ import { useRef, useMemo, useEffect, useCallback, createContext } from 'react'
5
5
 
6
- /**
7
- * @component MyTable
8
- * @description [地堡核心战术平台] 高级数据表格组件。
9
- * 它是地堡系统的“火力中心”,集成了行内编辑、自动滚动定位、动态分页协议及 Optimistic UI (乐观更新) 等工业级特性。
10
- *
11
- * @param {Object} props - 平台配置参数
12
- * @param {number} props.total - 物资总量:用于构筑分页器的刻度
13
- * @param {Object} [props.rowSelection] - 战术勾选:配置行选择逻辑
14
- * @param {string} [props.rowKey='id'] - 唯一标识:每一行物资的数字指纹
15
- * @param {boolean} [props.loading=false] - 战场负载状态:显示加载动画
16
- * @param {Array} [props.dataSource=[]] - 物理物资包:表格展示的原始数据
17
- * @param {Object} props.search - 当前物流状态:包含 pageNo 和 pageSize
18
- * @param {Object} [props.query] - 侦察参数:包含 rowIndex 用于执行物理追踪定位
19
- * @param {boolean} [props.autoScroll] - 视觉引导开关:是否开启自动滚动至目标行逻辑
20
- * @param {Function} [props.lineFormChange] - 行级信号监听:捕获行内表单的实时变动
21
- * @param {Function} props.onChange - 战位协同回调:分页、排序、筛选变动时的通信协议
22
- * @param {boolean|Object} [props.pagination] - 分页配置:支持布尔开关或物理对象覆盖
23
- * @param {Function} [props.customSave] - 物理封存协议:行内编辑保存后的外部持久化回调
24
- * @param {Array} props.columns - 零部件清单:定义列的构筑协议,支持 editType 等扩展属性
25
- * @param {Object} [props.scroll={x:'max-content'}] - 滚动配置:默认开启横向物理溢出防护
26
- * @param {string} [props.size='middle'] - 视觉能级:表格的物理尺寸 (small/middle/large)
27
- * @param {Function} props.setDataSource - 状态设置器:直接操作地堡的原始数据流(支持函数式更新)
28
- * @param {string|Function} [props.rowClassName] - 行视觉涂装:支持静态字符串或基于逻辑的动态类名
29
- *
30
- * @param {Object} [props.renderAction] - [后期物理绑定协议] 运行时逻辑注入仓库。
31
- * @description
32
- * 这是地堡系统的“黑客骇入”接口。
33
- * 由于 resource.js 必须保持纯净的静态 JSON 格式以确保主权安全性,
34
- * 凡是涉及“查看图片”、“跳转链接”等需要运行时函数支持的列,
35
- * 统一通过该协议在渲染阶段执行“逻辑对冲”。
36
- *
37
- * @logic
38
- * 1. 物理寻址:通过 col.dataIndex 锁定目标零部件位点。
39
- * 2. 逻辑脱壳:删除临时标记字段 [renderAction: true]。
40
- * 3. 语义注入:将 index.js 中定义的真实 Action 物理焊接到 columns.render 属性上。
41
- *
42
- * @example
43
- * // 1. 在 resource.js 中标记:
44
- * { title: '图片', dataIndex: 'imgUrl', renderAction: true }
45
- *
46
- * // 2. 在 index.js 中提供弹药:
47
- * <MyTable
48
- * total={100}
49
- * columns={columns}
50
- * dataSource={data}
51
- * setDataSource={setData}
52
- * customSave={(row) => saveApi(row)}
53
- * renderAction={{
54
- * imgUrl: (text, record) => <a onClick={() => showImg(record)}>查看物资</a>
55
- * }}
56
- * />
57
- */
58
- export const MyTable = ({ size, query, total, search, options, autoScroll, onChange, setSearch, pagination, editRender = {}, renderAction, rowClassName, customSave, setDataSource, lineFormChange, columns = [], rowSelection, rowKey = 'id', loading = false, dataSource = [], draggable = false, scroll = { x: 'max-content' }, isLocalPaging = false, ...restProps }) => {
6
+ export const TableContext = createContext(null)
7
+
8
+ export const MyTable = ({ size, query, total, search, options, autoScroll, onChange, editingKey, setSearch, pagination, editRender = {}, renderAction, rowClassName, customSave, setDataSource, lineFormChange, columns = [], rowSelection, rowKey = 'id', editMode = 'cellEdit', loading = false, dataSource = [], draggable = false, scroll = { x: 'max-content' }, isLocalPaging = false, ...restProps }) => {
59
9
 
60
10
  const tableRef = useRef(null)
61
11
  const hasScrolledRef = useRef(false)
62
12
  const dataSourceRef = useRef(dataSource)
63
13
  dataSourceRef.current = dataSource
64
14
 
65
- /**
66
- * @description [权限侦察] 自动扫描列配置。若存在 editType 型号,则激活“骇入模式(编辑模式)”。
67
- */
68
15
  const isEditable = useMemo(() => columns.some(col => col.editType), [columns])
69
16
  const hasCustomComponents = isEditable || draggable
70
17
 
71
- /**
72
- * @function handleSave
73
- * @description [原子级物理封存] 执行数据更新协议。
74
- * 采用函数式状态更新(prev => ...)以彻底消除闭航陷阱(Stale Closure),确保数据高频修改时的绝对准确。
75
- *
76
- * @param {Object} row - 修改后的行片段
77
- */
78
18
  const handleSave = useCallback((row) => {
79
19
  const prev = dataSourceRef.current
80
20
  const index = prev.findIndex(item => row[rowKey] === item[rowKey])
@@ -87,87 +27,72 @@ export const MyTable = ({ size, query, total, search, options, autoScroll, onCha
87
27
  customSave?.(updatedRow, newData)
88
28
  }, [rowKey, customSave, setDataSource])
89
29
 
90
- /**
91
- * @constant components
92
- * @description [零部件注册] 注入地堡特有的可编辑 Row 和 Cell 单元。
93
- */
94
30
  const components = useMemo(() => {
95
31
  if (!hasCustomComponents) return undefined
96
32
  const c = { body: {} }
97
- // 1. 只有有编辑列时,才挂载行内编辑单元
98
33
  if (isEditable) {
99
34
  c.body.row = EditableRow
100
35
  c.body.cell = EditableCell
101
36
  }
102
- // 2. 只有开启了拖拽,才挂载拖拽容器
103
37
  if (draggable) {
104
- c.body.wrapper = props => <DraggableContainer props={props} customSave={customSave} setDataSource={setDataSource} dataSourceRef={dataSourceRef} />
38
+ c.body.wrapper = DraggableContainer
105
39
  }
106
40
  return c
107
- }, [isEditable, draggable, customSave, setDataSource])
108
-
41
+ }, [isEditable, draggable])
109
42
 
110
- /**
111
- * @constant mergedColumns
112
- * @description [元数据重组] 将指挥官的静态列定义转化为带逻辑注入的“动态作战列”。
113
- * 自动处理 Cell 级的 disabled 状态和 editable 权限。
114
- */
115
43
  const mergedColumns = useMemo(() => {
116
44
  return columns.map(col => {
117
- if (!col.editType) {
118
- let currentCol = { ...col }
119
- if (col.renderAction && renderAction?.[col.dataIndex]) {
120
- currentCol.render = renderAction[col.dataIndex]
121
- }
122
- if (col.search) {
123
- currentCol = {
124
- ...currentCol,
125
- filterSearch: true,
126
- onFilter: (value, record) => col.remote ? undefined : col.customFilter({ value, record, dataIndex: col.dataIndex }),
127
- filterDropdown: ({ close, confirm, selectedKeys, clearFilters, setSelectedKeys }) => (
128
- <Space direction='vertical' style={{ padding: 12 }}>
129
- {col.customFilterDropdown({ dataIndex: col.dataIndex, selectedKeys, setSelectedKeys })}
130
- <div style={{ display: 'flex' }}>
131
- <a style={{ marginLeft: 'auto', marginRight: 8 }} onClick={() => {
132
- if (col.remote) {
133
- setSearch(prev => ({ ...prev, pageNo: 1, [col.dataIndex]: col.formatFilter?.(selectedKeys) ?? selectedKeys?.toString() }))
134
- }
135
- confirm()
136
- }}>确定</a>
137
- <a onClick={() => {
138
- if (col.remote) {
139
- setSearch(prev => ({ ...prev, pageNo: 1, [col.dataIndex]: undefined }))
140
- }
141
- clearFilters()
142
- }}>重置</a>
143
- </div>
144
- </Space>
145
- ),
146
- }
45
+ let currentCol = { ...col }
46
+ if (col.renderAction && renderAction?.[col.dataIndex]) {
47
+ currentCol.render = renderAction[col.dataIndex]
48
+ }
49
+ if (col.search) {
50
+ currentCol = {
51
+ ...currentCol,
52
+ filterSearch: true,
53
+ ...(col.remote ? {} : { onFilter: (value, record) => col.customFilter({ value, record, dataIndex: col.dataIndex }) }),
54
+ filterDropdown: ({ close, confirm, selectedKeys, clearFilters, setSelectedKeys }) => (
55
+ <Space direction='vertical' style={{ padding: 12 }}>
56
+ {col.customFilterDropdown({ dataIndex: col.dataIndex, selectedKeys, setSelectedKeys })}
57
+ <div style={{ display: 'flex' }}>
58
+ <a style={{ marginLeft: 'auto', marginRight: 8 }} onClick={() => {
59
+ if (col.remote) {
60
+ setSearch(prev => ({ ...prev, pageNo: 1, [col.dataIndex]: col.formatFilter?.(selectedKeys) ?? selectedKeys?.toString() }))
61
+ }
62
+ confirm()
63
+ }}>确定</a>
64
+ <a onClick={() => {
65
+ if (col.remote) {
66
+ setSearch(prev => ({ ...prev, pageNo: 1, [col.dataIndex]: undefined }))
67
+ }
68
+ clearFilters()
69
+ }}>重置</a>
70
+ </div>
71
+ </Space>
72
+ ),
147
73
  }
74
+ }
75
+ if (!col.editType) {
148
76
  return currentCol
149
77
  }
150
78
  return {
151
- ...col,
79
+ ...currentCol,
152
80
  onCell: (record) => ({
153
- ...col,
81
+ ...currentCol,
154
82
  record,
83
+ rowKey,
84
+ editMode,
155
85
  handleSave,
86
+ editingKey,
156
87
  editRender: editRender?.[col.dataIndex],
157
88
  options: col.options ?? options?.[col.dataIndex],
158
- // 💡 条件防御逻辑:支持针对单行的物理锁定
159
89
  disabled: col.specialDisabled ? record.disabled : col.disabled,
160
90
  editable: col.specialEditType ? record.needEdit && !!col.editType : !!col.editType,
161
91
  }),
162
92
  }
163
93
  })
164
- }, [columns, options, handleSave, editRender, renderAction])
94
+ }, [rowKey, columns, options, editMode, setSearch, editingKey, editRender, renderAction, handleSave])
165
95
 
166
- /**
167
- * @constant paginationConfig
168
- * @description [后勤对齐协议] 自动将地堡的 search 状态映射为 Antd 分页模型。
169
- * 具备类型自适应能力:支持 boolean 开关或 Object 属性覆盖。
170
- */
171
96
  const paginationConfig = useMemo(() => {
172
97
 
173
98
  if (!pagination) return false
@@ -177,28 +102,22 @@ export const MyTable = ({ size, query, total, search, options, autoScroll, onCha
177
102
  showSizeChanger: true,
178
103
  showTotal: (total) => `共 ${total} 条`,
179
104
  ...(isLocalPaging ? {} : {
180
- current: Number(search?.pageNo),
181
- pageSize: Number(search?.pageSize),
105
+ current: Number(search?.pageNo ?? 1),
106
+ pageSize: Number(search?.pageSize ?? 10),
182
107
  }),
183
108
  ...(typeof pagination === 'object' ? pagination : {})
184
109
  }
185
110
  }, [total, search, pagination, isLocalPaging])
186
111
 
187
- /**
188
- * @description [视觉追踪协议] 当检测到 query.rowIndex 信号时,执行“物理重定向”。
189
- * 表格将自动平滑滚动至目标行并注入高亮背景(#e6f4ff)。
190
- */
191
112
  useEffect(() => {
192
113
  if (!autoScroll || !dataSource?.length || query?.rowIndex === undefined) return
193
114
  if (hasScrolledRef.current === query.rowIndex) return
194
115
  const timer = setTimeout(() => {
195
116
  const tableElement = tableRef.current
196
117
  if (!tableElement) return
197
- // 物理侦察:查找底层 Antd 生成的行节点
198
118
  const trList = tableElement.getElementsByClassName('ant-table-row')
199
119
  const tr = trList[Number(query.rowIndex)]
200
120
  if (tr) {
201
- // 物理定位:平滑引导视觉中心
202
121
  tr.scrollIntoView({ behavior: 'smooth', block: 'center' })
203
122
  tr.style.backgroundColor = '#e6f4ff'
204
123
  hasScrolledRef.current = query.rowIndex
@@ -209,28 +128,29 @@ export const MyTable = ({ size, query, total, search, options, autoScroll, onCha
209
128
 
210
129
  return (
211
130
  <div ref={tableRef}>
212
- <Table
213
- {...restProps}
214
- rowKey={rowKey}
215
- scroll={scroll}
216
- loading={loading}
217
- onChange={onChange}
218
- columns={mergedColumns}
219
- dataSource={dataSource}
220
- size={size ?? 'middle'}
221
- components={components}
222
- rowSelection={rowSelection}
223
- pagination={paginationConfig}
224
- // 💡 信号注入:将行监听协议绑定至底层 tr
225
- onRow={(record, index) => ({ record, index, draggable, ...(typeof lineFormChange === 'function' ? { onValuesChange: lineFormChange } : {}) })}
226
- rowClassName={(record, index) => {
227
- const externalClass = typeof rowClassName === 'function' ? rowClassName(record, index) : rowClassName
228
- const classes = [externalClass]
229
- if (isEditable) classes.push('editable-row')
230
- if (autoScroll && query?.rowIndex === index) classes.push('search-highlight-row')
231
- return classes.filter(Boolean).join(' ')
232
- }}
233
- />
131
+ <TableContext.Provider value={{ customSave, setDataSource, dataSourceRef }}>
132
+ <Table
133
+ {...restProps}
134
+ rowKey={rowKey}
135
+ scroll={scroll}
136
+ loading={loading}
137
+ onChange={onChange}
138
+ columns={mergedColumns}
139
+ dataSource={dataSource}
140
+ size={size ?? 'middle'}
141
+ components={components}
142
+ rowSelection={rowSelection}
143
+ pagination={paginationConfig}
144
+ onRow={(record, index) => ({ record, index, draggable, columns: mergedColumns, ...(typeof lineFormChange === 'function' ? { onValuesChange: lineFormChange } : {}) })}
145
+ rowClassName={(record, index) => {
146
+ const externalClass = typeof rowClassName === 'function' ? rowClassName(record, index) : rowClassName
147
+ const classes = [externalClass]
148
+ if (isEditable) classes.push('editable-row')
149
+ if (autoScroll && query?.rowIndex === index) classes.push('search-highlight-row')
150
+ return classes.filter(Boolean).join(' ')
151
+ }}
152
+ />
153
+ </TableContext.Provider>
234
154
  </div>
235
155
  )
236
156
  }
@@ -1,30 +1,68 @@
1
+ import { Fragment } from 'react'
2
+ import { MyBaseForm } from './MyBaseForm'
1
3
  import { AliyunOSSUpload } from './AliyunOSSUpload'
2
- import { Tree, Form, Radio, Input, Upload, Select, Cascader, Checkbox, DatePicker, InputNumber, TreeSelect, AutoComplete } from 'antd'
4
+ import { Tree, Radio, Input, Upload, Select, Cascader, Checkbox, DatePicker, InputNumber, TreeSelect, AutoComplete } from 'antd'
3
5
 
4
- /**
5
- * @function formNode
6
- * @description 智能零部件装配器:根据构筑协议(item.type)动态产出对应的 Ant Design 交互单元。
7
- *
8
- * @param {Object} params - 装配参数
9
- * @param {Object} params.item - 零部件配置对象
10
- * @param {string|number} [params.item.width] - 宽度校准
11
- * @param {boolean} [params.item.readOnly] - 物理锁定开关
12
- * @param {string} [params.item.placeholder] - 视觉占位提示
13
- * @param {Object} [params.item.props] - 透传给底层组件的原始控制参数
14
- * @param {Array} [params.item.options] - 数据字典(用于 select/radio/tree 等)
15
- * @param {string} params.item.type - 构筑类型(date/number/ossUpload/select/daterange 等)
16
- *
17
- * @returns {React.ReactNode} 物理装配完成的 UI 单元
18
- */
19
- export const formNode = ({ item }) => {
6
+ const componentDict = {
7
+ date: { Component: DatePicker },
8
+ number: { Component: InputNumber },
9
+ ossUpload: { Component: AliyunOSSUpload },
10
+ default: {
11
+ Component: Input,
12
+ props: () => ({ allowClear: true })
13
+ },
14
+ daterange: {
15
+ Component: DatePicker.RangePicker,
16
+ props: () => ({ placeholder: undefined })
17
+ },
18
+ textarea: {
19
+ Component: Input.TextArea,
20
+ props: () => ({ autoSize: { minRows: 4 } })
21
+ },
22
+ upload: {
23
+ Component: Upload,
24
+ props: ({ item }) => ({ ...item.uploadProps })
25
+ },
26
+ auto: {
27
+ Component: AutoComplete,
28
+ props: ({ item }) => ({ options: item.options })
29
+ },
30
+ checkbox: {
31
+ Component: Checkbox.Group,
32
+ props: ({ item }) => ({ options: item.options })
33
+ },
34
+ radio: {
35
+ Component: Radio.Group,
36
+ props: ({ item }) => ({ options: item.options })
37
+ },
38
+ treeSelect: {
39
+ Component: TreeSelect,
40
+ props: ({ item }) => ({ treeData: item.options })
41
+ },
42
+ tree: {
43
+ Component: Tree,
44
+ props: ({ item }) => ({ checkable: true, treeData: item.options })
45
+ },
46
+ cascader: {
47
+ Component: Cascader,
48
+ props: ({ item }) => ({ allowClear: true, showSearch: true, options: item.options })
49
+ },
50
+ select: {
51
+ Component: Select,
52
+ props: ({ item }) => ({ allowClear: true, showSearch: true, mode: item.mode, options: item.options, optionFilterProp: 'label' })
53
+ },
54
+ }
55
+
56
+ export const formNode = ({ item, form }) => {
20
57
 
21
58
  if (item.type === 'group' && Array.isArray(item.groups)) {
22
59
  return (
23
60
  <Input.Group compact style={{ width: item.width ?? '100%', display: 'flex', ...item.style }}>
24
- {item.groups.map(subItem => (
25
- <Form.Item noStyle key={subItem.name} name={subItem.name} rules={subItem.rules} initialValue={subItem.value}>
26
- {formNode({ item: subItem })}
27
- </Form.Item>
61
+ {item.groups.map((subItem, ind) => (
62
+ <Fragment key={subItem.name}>
63
+ <div style={{ flex: 1, minWidth: 0, marginRight: 8 }}><MyBaseForm item={subItem} form={form} /></div>
64
+ {ind < item.groups.length - 1 && <span style={{ color: '#888', whiteSpace: 'nowrap', marginRight: 8 }}>{item.separator}</span>}
65
+ </Fragment>
28
66
  ))}
29
67
  </Input.Group>
30
68
  )
@@ -34,25 +72,15 @@ export const formNode = ({ item }) => {
34
72
  disabled: item.readOnly,
35
73
  style: { width: item.width ?? '100%' },
36
74
  placeholder: item.placeholder ?? `请${['select', 'cascader', 'date'].includes(item.type) ? '选择' : '输入'}`,
37
- ...item.props
38
75
  }
39
76
 
40
- const componentDict = {
41
- date: <DatePicker {...commonProps} />,
42
- number: <InputNumber {...commonProps} />,
43
- default: <Input allowClear {...commonProps} />,
44
- ossUpload: <AliyunOSSUpload {...commonProps} />,
45
- auto: <AutoComplete options={item.options} {...commonProps} />,
46
- radio: <Radio.Group options={item.options} {...commonProps} />,
47
- tree: <Tree checkable treeData={item.options} {...commonProps} />,
48
- treeSelect: <TreeSelect treeData={item.options} {...commonProps} />,
49
- checkbox: <Checkbox.Group options={item.options} {...commonProps} />,
50
- textarea: <Input.TextArea autoSize={{ minRows: 4 }} {...commonProps} />,
51
- daterange: <DatePicker.RangePicker {...commonProps} placeholder={undefined} />,
52
- cascader: <Cascader showSearch allowClear options={item.options} {...commonProps} />,
53
- upload: <Upload {...item.uploadProps} {...commonProps}>{item.content ?? '上传文件'}</Upload>,
54
- select: <Select allowClear showSearch mode={item.mode} options={item.options} optionFilterProp='label' {...commonProps} />,
55
- }
77
+ const config = componentDict[item.type] ?? componentDict.default
78
+ const Component = config.Component
79
+ const specialProps = config.props?.({ item, form }) ?? {}
56
80
 
57
- return componentDict[item.type] ?? componentDict.default
81
+ return (
82
+ <Component {...commonProps} {...specialProps} {...item.props}>
83
+ {item.type === 'upload' && (item.content ?? '上传文件')}
84
+ </Component>
85
+ )
58
86
  }