@hzab/list-render 1.10.21-alpha.1 → 1.10.21-alpha.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.
@@ -1,23 +1,15 @@
1
- import React, { useEffect, useState } from "react";
1
+ import React, { useEffect, useMemo, useState } from "react";
2
2
  import { ReactGrid, Column, Row, CellChange, Cell, CellTemplate } from "@silevis/reactgrid";
3
3
  import "@silevis/reactgrid/styles.css";
4
4
  import _ from "lodash";
5
5
  import "./index.less";
6
-
7
- function isReactElement(value) {
8
- return value !== null && typeof value === "object";
9
- }
10
-
11
- // 自定义单元格:状态标签(可编辑、带颜色)
12
- export interface StatusCell extends Cell {
13
- type: "status"; // 唯一标识,必须
14
- text: string; // 状态文本(如:待办/进行中/完成)
15
- color?: string; // 自定义颜色
16
- validator?: (text: string) => boolean; // 可选:校验
17
- }
18
-
19
- // 合并所有单元格类型(供 rows 使用)
20
- export type CustomCellTypes = StatusCell;
6
+ import { TextTemplate } from "./Template/TextCellTemplate";
7
+ import { antdComponents, customComponents } from "@hzab/form-render/src/index";
8
+ import { WidthWrap } from "./Template/widthWrap";
9
+ import { NumberTemplate } from "./Template/NumberCellTemplate";
10
+ import { Checkbox } from "antd";
11
+ import { ExpandListTemplate } from "./Template/ExpandListTemplate";
12
+ import { convertColumnsToRows, flattenColumns } from "./Template/utils";
21
13
 
22
14
  export default function CellEditTable(props: any) {
23
15
  const {
@@ -29,84 +21,155 @@ export default function CellEditTable(props: any) {
29
21
  onEditSubmit,
30
22
  headerRowHeight,
31
23
  rowHeight,
24
+ reactGridStyle,
25
+ className,
26
+ Slots,
27
+ rowSelection,
28
+ rowKey,
29
+ query,
32
30
  } = props;
33
- const [headerList, setHeaderList] = useState([]);
34
- const [rows, setRows] = useState<any>([]);
35
- const [tableColumns, setTableColumns] = useState([]);
36
31
 
32
+ const [headerList, setHeaderList] = useState<any>([]);
33
+ const [rows, setRows] = useState<any>([]);
34
+ const [tableColumns, setTableColumns] = useState<any>([]);
35
+ const [selectedRowKeys, setSelectedRowKeys] = useState<any>([]);
36
+ const [newDataSource, setNewDataSource] = useState<any>([]);
37
+ const [expandKeys, setExpandKeys] = useState<any>([]);
38
+ useEffect(() => {
39
+ setNewDataSource(dataSource);
40
+ }, [dataSource]);
37
41
  /**将数组转为对象 */
38
42
  const handleArrayToObj = (item) => {
39
- let obj = {
43
+ const record = item?.cells?.[0]?.record;
44
+ const fieldList = ["_$actions", "rowSelection"];
45
+ let obj: any = {
40
46
  id: item?.rowId,
41
47
  };
42
48
  item?.cells?.forEach((el) => {
43
- if (el?.columnsId) {
44
- obj[el?.columnsId] = el?.value || el?.selectedValue || el?.text || "";
49
+ if (el?.columnId && !fieldList?.includes(el?.columnId)) {
50
+ obj[el?.columnId] = el?.text || el?.value || el?.selectedValue || "";
45
51
  }
46
52
  });
47
- return obj;
53
+
54
+ return {
55
+ ...record,
56
+ ...obj,
57
+ };
48
58
  };
49
59
  /** 表头 */
50
60
  useEffect(() => {
51
61
  if (Array.isArray(columns) && columns?.length > 0) {
52
- const cells = columns?.map((item) => {
53
- return {
54
- type: "header",
55
- text:
56
- typeof item?.title == "function"
57
- ? isReactElement(item?.title())
58
- ? item?.title()?.props.children
59
- : item?.title() || ""
60
- : item?.title || "",
61
- };
62
- });
63
- setHeaderList([
64
- {
65
- rowId: "header",
66
- height: headerRowHeight,
67
- cells: cells,
68
- },
69
- ]);
62
+ if (rowSelection) {
63
+ columns.unshift({
64
+ type: "rowSelection",
65
+ width: rowSelection?.width || 30,
66
+ key: "rowSelection",
67
+ style: rowSelection?.style || {
68
+ background: "rgba(128, 128, 128, 0.1)",
69
+ display: "flex",
70
+ justifyContent: "center",
71
+ },
72
+ });
73
+ }
74
+ // const cells = columns?.map((item, index) => {
75
+ // return {
76
+ // type: item?.type == "rowSelection" ? "rowSelection" : "header",
77
+ // text:
78
+ // typeof item?.title == "function"
79
+ // ? isReactElement(item?.title())
80
+ // ? item?.title()?.props.children
81
+ // : String(item?.title()) || ""
82
+ // : String(item?.title) || "",
83
+ // style: item?.type == "rowSelection" ? item?.style : {},
84
+ // onCell: item?.onCell,
85
+ // };
86
+ // });
87
+ setHeaderList(convertColumnsToRows(columns, headerRowHeight));
88
+ const newColumns = flattenColumns(columns);
70
89
  setTableColumns(
71
- columns?.map((item, index) => ({
90
+ newColumns?.map((item, index) => ({
72
91
  ...item,
73
- columnId: item?.dataIndex || "",
92
+ columnId: item?.dataIndex || item?.key || "",
74
93
  })),
75
94
  );
76
95
  }
77
- }, [columns]);
96
+ }, [columns, rowSelection]);
97
+
78
98
  /** 数据行 */
79
99
  useEffect(() => {
80
100
  const columnsIds = tableColumns?.map((el) => el?.columnId);
81
- const newDataSource = dataSource?.map((item, index) => {
101
+ const tempDataSource = newDataSource?.map((item, index) => {
82
102
  return {
83
- rowId: item?.id,
103
+ rowId: item[rowKey],
84
104
  height: rowHeight,
85
105
  cells: columnsIds?.map((el, ind) => {
106
+ let newCell = {};
107
+ const columnsItem = tableColumns[ind];
108
+ const cellItem = item[el];
109
+ let type: any = columnsItem?.type ?? "text";
110
+ if (!rowSelection && ind == 0 && item?.children) {
111
+ type = "ExpandListTemplate";
112
+ }
113
+ if (rowSelection && ind == 1) {
114
+ type = "ExpandListTemplate";
115
+ }
116
+
117
+ if (formulaConfig) {
118
+ for (let key in formulaConfig) {
119
+ if (key == el) {
120
+ const it = formulaConfig[key];
121
+ newCell = {
122
+ text: it?.setCellData(item),
123
+ value: it?.setCellData(item),
124
+ style: it?.setCellStyle(it?.setCellData(item)),
125
+ };
126
+ }
127
+ }
128
+ }
129
+
86
130
  return {
87
- type: tableColumns[ind]?.cellType ?? "text",
88
- text: typeof item[el] == "string" ? item[el] : "",
89
- columnsId: el,
90
- nonEditable: !tableColumns[ind]?.editable,
91
- ...item,
131
+ ...columnsItem,
132
+ record: item,
133
+ rowId: item?.id || columnsItem?.type,
134
+ type: type,
135
+ nonEditable: columnsItem?.nonEditable,
136
+ text: String(cellItem || ""),
137
+ value: cellItem,
138
+ selectedValue: cellItem || "",
139
+ // rowspan: item?.extendedConfig?.name == el ? item?.extendedConfig?.rowspan || 0 : 0,
140
+ // colspan: item?.extendedConfig?.name == el ? item?.extendedConfig?.colspan || 0 : 0,
141
+ style: columnsItem?.type == "rowSelection" ? { display: "flex", justifyContent: "center" } : {},
142
+ ...((columnsItem?.onCell && columnsItem?.onCell(item, index, ind)) || {}),
143
+ ...(columnsItem?.cellProps || {}),
144
+ setNewDataSource,
145
+ setExpandKeys,
146
+ expandKeys,
147
+ Slots,
148
+ ...newCell,
149
+
150
+ // style:item?.extendedConfig?.name==el?item?.extendedConfig?.style||{}:{}
151
+ // ...handleType(item[el],tableColumns[ind]),
92
152
  };
93
153
  }),
94
154
  };
95
155
  });
96
- setRows([...headerList, ...newDataSource]);
97
- }, [dataSource, tableColumns]);
156
+ setRows([...headerList, ...tempDataSource]);
157
+ }, [newDataSource, tableColumns, rowSelection]);
158
+ console.log(rows, "rowsrows");
98
159
 
99
160
  // 单元格编辑回调
100
161
  const handleCellsChanged = (changes) => {
162
+ console.log(changes, "changeschangeschanges");
163
+
101
164
  setRows((prevRows) => {
102
165
  changes.forEach((change) => {
103
- const changeRowIdx = prevRows.findIndex((el) => el.rowId === change.rowId);
104
- const changeColumnIdx = tableColumns.findIndex((el) => el.columnId === change.columnId);
166
+ const changeRowIdx = prevRows.findIndex((el) => el.rowId == change.rowId);
167
+ const changeColumnIdx = tableColumns.findIndex((el) => el.columnId == change.columnId);
105
168
  prevRows[changeRowIdx].cells[changeColumnIdx] = change.newCell;
106
169
  if (formulaConfig) {
107
170
  for (let key in formulaConfig) {
108
171
  const item = formulaConfig[key];
109
- const resultColumnIdx = tableColumns.findIndex((el) => el.columnId === key);
172
+ const resultColumnIdx = tableColumns.findIndex((el) => el.columnId == key);
110
173
  const newCell = {
111
174
  ...prevRows[changeRowIdx].cells[resultColumnIdx],
112
175
  text: item?.setCellData(prevRows[changeRowIdx]),
@@ -123,20 +186,151 @@ export default function CellEditTable(props: any) {
123
186
  });
124
187
  };
125
188
 
189
+ /**提取组件内部子组件 */
190
+ function extractSubComponents(comp, compName): any {
191
+ if (!comp || typeof comp !== "object") return {};
192
+
193
+ return Object.entries(comp).reduce((result, [key, value]) => {
194
+ // 匹配 antd 规范子组件命名:XXPicker / XX / 驼峰组件名
195
+ // 排除普通函数、数字、布尔、内部私有属性(带下划线)、原型上的方法
196
+ const isNativeFunc = ["render", "defaultProps", "propTypes", "displayName", "Group", "$$typeof"].includes(key);
197
+ // 判定为子组件:值是函数(React组件本质是函数/类),且不是内部方法
198
+ const isSubComponent = typeof comp == "object" && !isNativeFunc;
199
+
200
+ if (isSubComponent) {
201
+ result[`${compName}.${key}`] = value;
202
+ }
203
+ return result;
204
+ }, {});
205
+ }
206
+ /** 自定义组件注册 */
207
+ const baseTemplate = useMemo(() => {
208
+ const mergeTemplate: any = { ...(antdComponents || {}), ...(customComponents || {}) };
209
+ const newMergeTemplate: any = {};
210
+ for (let key in mergeTemplate) {
211
+ for (let it in extractSubComponents(mergeTemplate[key], key)) {
212
+ newMergeTemplate[it] = WidthWrap({ WrapComponent: extractSubComponents(mergeTemplate[key], key)[it] });
213
+ }
214
+ newMergeTemplate[key] = WidthWrap({ WrapComponent: mergeTemplate[key] });
215
+ }
216
+ return newMergeTemplate;
217
+ }, [antdComponents, customComponents]);
218
+
219
+ const ActionDom = (props) => {
220
+ return (
221
+ <div onPointerDown={(e) => e.stopPropagation()}>
222
+ {Slots?.tableActionsSlot && <Slots.tableActionsSlot {...props} />}
223
+ {Slots?.actionPrefixSlot && <Slots.actionPrefixSlot {...props} />}
224
+ {Slots?.actionCenterSlot && <Slots.actionCenterSlot {...props} />}
225
+ {Slots?.actionSuffixSlot && <Slots.actionSuffixSlot {...props} />}
226
+ </div>
227
+ );
228
+ };
229
+
230
+ const CheckBoxDom = (props) => {
231
+ return (
232
+ <Checkbox
233
+ {...props}
234
+ checked={
235
+ rowSelection?.selectedRowKeys?.includes((props?.record && props?.record[rowKey]) || "") ||
236
+ rowSelection?.selectedRowKeys?.length == dataSource?.length
237
+ }
238
+ indeterminate={
239
+ !props?.rowId &&
240
+ rowSelection?.selectedRowKeys?.length > 0 &&
241
+ rowSelection?.selectedRowKeys?.length < dataSource?.length
242
+ }
243
+ onChange={(e) => {
244
+ if (e.target.checked && props?.rowId) {
245
+ const newSelectedRowKeys = [...rowSelection?.selectedRowKeys, props?.record[rowKey]];
246
+ const rows = dataSource?.filter((el) => newSelectedRowKeys?.includes(el[rowKey]));
247
+
248
+ rowSelection?.onChange(newSelectedRowKeys, rows);
249
+ } else if (!e.target.checked && props?.rowId) {
250
+ const newSelectedRowKeys = rowSelection?.selectedRowKeys?.filter((el) => el != props?.record[rowKey]);
251
+ const rows = dataSource?.filter((el) => newSelectedRowKeys?.includes(el[rowKey]));
252
+
253
+ rowSelection?.onChange(
254
+ rowSelection?.selectedRowKeys?.filter((el) => el != props?.record[rowKey]),
255
+ rows,
256
+ );
257
+ } else if (e.target.checked && !props?.rowId) {
258
+ const newSelectedRowKeys = dataSource?.map((el) => el[rowKey]);
259
+ const rows = dataSource?.filter((el) => newSelectedRowKeys?.includes(el[rowKey]));
260
+
261
+ rowSelection?.onChange(
262
+ dataSource?.map((el) => el[rowKey]),
263
+ rows,
264
+ );
265
+ } else if (!e.target.checked && !props?.rowId) {
266
+ rowSelection?.onChange([], []);
267
+ }
268
+ }}
269
+ ></Checkbox>
270
+ );
271
+ };
272
+ useEffect(() => {
273
+ if (rowSelection) {
274
+ const rows = dataSource?.filter((el) => selectedRowKeys?.includes(el[rowKey]));
275
+ rowSelection?.onChange(selectedRowKeys, rows);
276
+ }
277
+ }, [selectedRowKeys, dataSource]);
278
+ useEffect(() => {
279
+ setSelectedRowKeys([]);
280
+ }, [query?.pageNum]);
281
+
126
282
  return (
127
- <div className="grid-container">
283
+ <div className={`grid-container ${className}`} style={reactGridStyle}>
128
284
  <ReactGrid
129
285
  rows={rows}
130
286
  columns={tableColumns}
131
287
  onCellsChanged={handleCellsChanged}
132
288
  enableFillHandle={true}
133
289
  enableRangeSelection={true}
134
- stickyLeftColumns={1}
135
- // customCellTemplates={{ status: StatusCellTemplate }}
290
+ horizontalStickyBreakpoint={120}
291
+ // stickyLeftColumns={1}
136
292
  // onFocusLocationChanging={(location) => {
137
293
  // return false;
138
294
  // }}
139
295
  {...reactGridProps}
296
+ onFocusLocationChanging={(cell: any) => {
297
+ const datatypeId = cell?.columnId + cell?.rowId;
298
+ window.gridEditId = datatypeId;
299
+
300
+ if (["_$actions", "rowSelection"]?.includes(cell?.columnId)) {
301
+ return false;
302
+ }
303
+
304
+ if (reactGridProps?.onFocusLocationChanging) {
305
+ return reactGridProps?.onFocusLocationChanging(cell, rows);
306
+ }
307
+ return cell;
308
+ }}
309
+ customCellTemplates={{
310
+ ...baseTemplate,
311
+ ...reactGridProps?.customCellTemplates,
312
+ _$actions: WidthWrap(
313
+ {
314
+ WrapComponent: ActionDom,
315
+ },
316
+ true,
317
+ ),
318
+ rowSelection: WidthWrap(
319
+ {
320
+ WrapComponent: CheckBoxDom,
321
+ },
322
+ true,
323
+ ),
324
+ Input: TextTemplate,
325
+ Number: NumberTemplate,
326
+ ExpandListTemplate: WidthWrap(
327
+ {
328
+ WrapComponent: ExpandListTemplate,
329
+ },
330
+ true,
331
+ ),
332
+ }}
333
+
140
334
  // onSelectionChanged={(newSelection) => {
141
335
  // console.log("选区发生变化", newSelection[0]);
142
336
  // }}
@@ -30,8 +30,9 @@ export interface EditableCellProps extends React.HTMLAttributes<HTMLElement> {
30
30
  onSave: (field: { type: string; name: string }) => void;
31
31
  onCancel: () => void;
32
32
  topProps: {
33
- config: { hasEdit: boolean | Function };
33
+ tableConf: { hasEdit: boolean | Function };
34
34
  editMode: "modal" | "line" | "line-cell" | "cell";
35
+ canEditCallback: (record: Partial<Item> & { key: React.Key }, field: { type: string; name: string }, dataIndex: string) => void;
35
36
  };
36
37
  children: React.ReactNode;
37
38
  }
@@ -56,8 +57,8 @@ export const EditableCell: React.FC<EditableCellProps> = ({
56
57
  const field = getField && getField();
57
58
  const fPattern = field?.["x-pattern"];
58
59
  const fDisplay = field?.["x-display"];
59
- const { editMode = "modal" } = topProps || {};
60
- const { hasEdit } = topProps?.config || {};
60
+ const { editMode = "modal", canEditCallback } = topProps || {};
61
+ const { hasEdit } = topProps?.tableConf || {};
61
62
  const fieldRef = useRef();
62
63
 
63
64
  // 弹窗编辑模式
@@ -66,7 +67,11 @@ export const EditableCell: React.FC<EditableCellProps> = ({
66
67
  }
67
68
  // 无编辑权限或禁止编辑
68
69
  const _hasEdit = hasEdit && typeof hasEdit === "function" ? hasEdit(record, index) : hasEdit !== false;
69
- let _editable = _hasEdit && editable && !(fPattern === "disabled" || fPattern === "readOnly" || fDisplay === "none");
70
+ let _editable =
71
+ _hasEdit &&
72
+ editable &&
73
+ !(fPattern === "disabled" || fPattern === "readOnly" || fDisplay === "none") &&
74
+ (canEditCallback ? canEditCallback(record, field, dataIndex) : true);
70
75
  if (!_editable) {
71
76
  return <td {...restProps}>{children}</td>;
72
77
  }
@@ -3,7 +3,7 @@ import { Form, Table, Button, Popconfirm, Tooltip, Checkbox, Popover } from "ant
3
3
  import { QuestionCircleOutlined, FilterOutlined, SettingOutlined, MenuOutlined } from "@ant-design/icons";
4
4
  import { SortableContainer, SortableElement, SortableHandle } from "react-sortable-hoc";
5
5
  import { arrayMoveImmutable } from "array-move";
6
- import _, { isFunction } from "lodash";
6
+ import _, { isFunction, values } from "lodash";
7
7
 
8
8
  import { FormProvider, FormConsumer } from "@formily/react";
9
9
  import { createForm, onFormValuesChange, onFieldValueChange } from "@formily/core";
@@ -221,19 +221,28 @@ const TableRender = forwardRef(function (props, tableRef) {
221
221
  </span>
222
222
  );
223
223
  }
224
+ const selectList = fieldSchemas?.[name]?.dataSource?.map((el) => ({
225
+ ...el,
226
+ label: el?.[field?.["x-component-props"]?.["fieldNames"]?.["label"]] || el?.label,
227
+ value: el?.[field?.["x-component-props"]?.["fieldNames"]?.["value"]] || el?.value,
228
+ }))
224
229
 
225
230
  columns.push({
226
231
  // field, // HACK: 直接传入 field 在 title 传入 ReactNode,内部深克隆导致页面报错白屏。使用函数获取解决
227
232
  getField: () => field,
228
233
  editable: true,
229
234
  ..._colConf,
230
- onCell: (record, rowIndex) =>
231
- _colConf?.onCell?.({ ...record, _field: { ...field, ...(fieldSchemas?.[name] || {}) } }, rowIndex) || {},
235
+ onCell: (record, rowIndex, ci) =>
236
+ _colConf?.onCell?.({ ...record, _field: { ...field, ...(fieldSchemas?.[name] || {}) } }, rowIndex, ci) || {},
232
237
  // 函数式传入,解决 title ReactNode 传入报错问题(table 组件内部对 columns 进行 lodash.deepClone 导致 ReactNode 变成对象无法正常渲染) Uncaught TypeError: this.queryFeedbacks is not a function
233
238
  title: () => _title,
234
239
  key: name,
235
240
  dataIndex: name,
236
- cellType: field?.cellType,
241
+ type: field["x-validator"] == "number" ? "Number" : comName,
242
+ nonEditable: editMode != "cell",
243
+ values: field?.enum || selectList || [],
244
+ cellProps: field.cellProps,
245
+ children: field?.children,
237
246
  render: getColRender(colRender),
238
247
  });
239
248
  }
@@ -248,6 +257,8 @@ const TableRender = forwardRef(function (props, tableRef) {
248
257
  ..._colConf,
249
258
  title: "操作",
250
259
  key: "_$actions",
260
+ type: "_$actions",
261
+ nonEditable: true,
251
262
  render: getColRender(function (text, record, index) {
252
263
  const { onEdit, onDel, onSearch, getList } = props;
253
264
  const slotProps = { text, record, index, onEdit: onEdit, onDel: onDel, onSearch, getList };
@@ -429,6 +440,9 @@ const TableRender = forwardRef(function (props, tableRef) {
429
440
  {...tableProps}
430
441
  {...cellEditTableProps}
431
442
  onEditSubmit={props?.onEditSubmit}
443
+ Slots={props?.Slots}
444
+ rowSelection={config?.rowSelection}
445
+ query={query}
432
446
  /> : <>
433
447
  {isEditTable ? (
434
448
  <FormProvider form={formRender}>