@hzab/list-render 1.10.21-alpha.0 → 1.10.21-alpha.2

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.
@@ -0,0 +1,340 @@
1
+ import React, { useEffect, useMemo, useState } from "react";
2
+ import { ReactGrid, Column, Row, CellChange, Cell, CellTemplate } from "@silevis/reactgrid";
3
+ import "@silevis/reactgrid/styles.css";
4
+ import _ from "lodash";
5
+ import "./index.less";
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";
13
+
14
+ export default function CellEditTable(props: any) {
15
+ const {
16
+ columns,
17
+ dataSource,
18
+ formulaConfig,
19
+ onChange,
20
+ reactGridProps = {},
21
+ onEditSubmit,
22
+ headerRowHeight,
23
+ rowHeight,
24
+ reactGridStyle,
25
+ className,
26
+ Slots,
27
+ rowSelection,
28
+ rowKey,
29
+ query,
30
+ } = props;
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]);
41
+ /**将数组转为对象 */
42
+ const handleArrayToObj = (item) => {
43
+ const record = item?.cells?.[0]?.record;
44
+ const fieldList = ["_$actions", "rowSelection"];
45
+ let obj: any = {
46
+ id: item?.rowId,
47
+ };
48
+ item?.cells?.forEach((el) => {
49
+ if (el?.columnId && !fieldList?.includes(el?.columnId)) {
50
+ obj[el?.columnId] = el?.text || el?.value || el?.selectedValue || "";
51
+ }
52
+ });
53
+
54
+ return {
55
+ ...record,
56
+ ...obj,
57
+ };
58
+ };
59
+ /** 表头 */
60
+ useEffect(() => {
61
+ if (Array.isArray(columns) && columns?.length > 0) {
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);
89
+ setTableColumns(
90
+ newColumns?.map((item, index) => ({
91
+ ...item,
92
+ columnId: item?.dataIndex || item?.key || "",
93
+ })),
94
+ );
95
+ }
96
+ }, [columns, rowSelection]);
97
+
98
+ /** 数据行 */
99
+ useEffect(() => {
100
+ const columnsIds = tableColumns?.map((el) => el?.columnId);
101
+ const tempDataSource = newDataSource?.map((item, index) => {
102
+ return {
103
+ rowId: item[rowKey],
104
+ height: rowHeight,
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
+
130
+ return {
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]),
152
+ };
153
+ }),
154
+ };
155
+ });
156
+ setRows([...headerList, ...tempDataSource]);
157
+ }, [newDataSource, tableColumns, rowSelection]);
158
+ console.log(rows, "rowsrows");
159
+
160
+ // 单元格编辑回调
161
+ const handleCellsChanged = (changes) => {
162
+ console.log(changes, "changeschangeschanges");
163
+
164
+ setRows((prevRows) => {
165
+ changes.forEach((change) => {
166
+ const changeRowIdx = prevRows.findIndex((el) => el.rowId == change.rowId);
167
+ const changeColumnIdx = tableColumns.findIndex((el) => el.columnId == change.columnId);
168
+ prevRows[changeRowIdx].cells[changeColumnIdx] = change.newCell;
169
+ if (formulaConfig) {
170
+ for (let key in formulaConfig) {
171
+ const item = formulaConfig[key];
172
+ const resultColumnIdx = tableColumns.findIndex((el) => el.columnId == key);
173
+ const newCell = {
174
+ ...prevRows[changeRowIdx].cells[resultColumnIdx],
175
+ text: item?.setCellData(prevRows[changeRowIdx]),
176
+ value: item?.setCellData(prevRows[changeRowIdx]),
177
+ style: item?.setCellStyle(item?.setCellData(prevRows[changeRowIdx])),
178
+ };
179
+ prevRows[changeRowIdx].cells[resultColumnIdx] = newCell;
180
+ }
181
+ }
182
+ onEditSubmit && onEditSubmit(handleArrayToObj(prevRows[changeRowIdx]));
183
+ onChange && onChange(prevRows[changeRowIdx]);
184
+ });
185
+ return [...prevRows];
186
+ });
187
+ };
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
+
282
+ return (
283
+ <div className={`grid-container ${className}`} style={reactGridStyle}>
284
+ <ReactGrid
285
+ rows={rows}
286
+ columns={tableColumns}
287
+ onCellsChanged={handleCellsChanged}
288
+ enableFillHandle={true}
289
+ enableRangeSelection={true}
290
+ horizontalStickyBreakpoint={120}
291
+ // stickyLeftColumns={1}
292
+ // onFocusLocationChanging={(location) => {
293
+ // return false;
294
+ // }}
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
+
334
+ // onSelectionChanged={(newSelection) => {
335
+ // console.log("选区发生变化", newSelection[0]);
336
+ // }}
337
+ />
338
+ </div>
339
+ );
340
+ }
@@ -30,9 +30,8 @@ export interface EditableCellProps extends React.HTMLAttributes<HTMLElement> {
30
30
  onSave: (field: { type: string; name: string }) => void;
31
31
  onCancel: () => void;
32
32
  topProps: {
33
- tableConf: { hasEdit: boolean | Function };
33
+ config: { 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;
36
35
  };
37
36
  children: React.ReactNode;
38
37
  }
@@ -57,8 +56,8 @@ export const EditableCell: React.FC<EditableCellProps> = ({
57
56
  const field = getField && getField();
58
57
  const fPattern = field?.["x-pattern"];
59
58
  const fDisplay = field?.["x-display"];
60
- const { editMode = "modal", canEditCallback } = topProps || {};
61
- const { hasEdit } = topProps?.tableConf || {};
59
+ const { editMode = "modal" } = topProps || {};
60
+ const { hasEdit } = topProps?.config || {};
62
61
  const fieldRef = useRef();
63
62
 
64
63
  // 弹窗编辑模式
@@ -67,11 +66,7 @@ export const EditableCell: React.FC<EditableCellProps> = ({
67
66
  }
68
67
  // 无编辑权限或禁止编辑
69
68
  const _hasEdit = hasEdit && typeof hasEdit === "function" ? hasEdit(record, index) : hasEdit !== false;
70
- let _editable =
71
- _hasEdit &&
72
- editable &&
73
- !(fPattern === "disabled" || fPattern === "readOnly" || fDisplay === "none") &&
74
- (canEditCallback ? canEditCallback(record, field, dataIndex) : true);
69
+ let _editable = _hasEdit && editable && !(fPattern === "disabled" || fPattern === "readOnly" || fDisplay === "none");
75
70
  if (!_editable) {
76
71
  return <td {...restProps}>{children}</td>;
77
72
  }
@@ -540,6 +540,7 @@ const ListRender = forwardRef(function (props, parentRef) {
540
540
  i18n={i18n}
541
541
  onEditSubmit={onEditSubmit}
542
542
  topProps={props}
543
+ cellEditTableProps={props?.cellEditTableProps}
543
544
  />
544
545
  ) : null}
545
546
 
@@ -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";
@@ -17,13 +17,13 @@ import { getVal, getFieldList } from "../common/utils";
17
17
  import { handleReactions } from "../common/handleReactions";
18
18
 
19
19
  import { useEditTable, EditableCell } from "../components/Formily/FormilyEditTable";
20
-
20
+ import CellEditTable from "../components/CellEditTable"
21
21
  import "./index.less";
22
22
 
23
23
  const scenario = "table-render";
24
24
 
25
25
  const TableRender = forwardRef(function (props, tableRef) {
26
- const { topProps, config = {}, query = {}, i18n, setList } = props;
26
+ const { topProps, config = {}, query = {}, i18n, setList, cellEditTableProps } = props;
27
27
  const { tableDel = "删除", tableEdit = "编辑", tableDelTip = "确认删除该项?", tableDetail = "详情" } = i18n || {};
28
28
  const {
29
29
  dragColConf = {},
@@ -34,6 +34,7 @@ const TableRender = forwardRef(function (props, tableRef) {
34
34
  tableEmptyValue,
35
35
  isShowTableFilter = false,
36
36
  isTableSortXIdex = false,
37
+ isCellEditTable = false
37
38
  } = config || {};
38
39
  const { editMode = "modal" } = topProps || {};
39
40
  const isEditTable = editMode !== "modal";
@@ -129,6 +130,7 @@ const TableRender = forwardRef(function (props, tableRef) {
129
130
  scope: props.schemaScope,
130
131
  formilyRef,
131
132
  });
133
+
132
134
  _fieldList.forEach((field, colIndex) => {
133
135
  const fieldSchemas = formilyRef.current?.fields;
134
136
  if (field.inTable !== false) {
@@ -219,18 +221,28 @@ const TableRender = forwardRef(function (props, tableRef) {
219
221
  </span>
220
222
  );
221
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
+ }))
222
229
 
223
230
  columns.push({
224
231
  // field, // HACK: 直接传入 field 在 title 传入 ReactNode,内部深克隆导致页面报错白屏。使用函数获取解决
225
232
  getField: () => field,
226
233
  editable: true,
227
234
  ..._colConf,
228
- onCell: (record, rowIndex) =>
229
- _colConf?.onCell?.({ ...record, _field: { ...field, ...(fieldSchemas?.[name] || {}) } }, rowIndex) || {},
235
+ onCell: (record, rowIndex, ci) =>
236
+ _colConf?.onCell?.({ ...record, _field: { ...field, ...(fieldSchemas?.[name] || {}) } }, rowIndex, ci) || {},
230
237
  // 函数式传入,解决 title ReactNode 传入报错问题(table 组件内部对 columns 进行 lodash.deepClone 导致 ReactNode 变成对象无法正常渲染) Uncaught TypeError: this.queryFeedbacks is not a function
231
238
  title: () => _title,
232
239
  key: name,
233
240
  dataIndex: name,
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,
234
246
  render: getColRender(colRender),
235
247
  });
236
248
  }
@@ -245,6 +257,8 @@ const TableRender = forwardRef(function (props, tableRef) {
245
257
  ..._colConf,
246
258
  title: "操作",
247
259
  key: "_$actions",
260
+ type: "_$actions",
261
+ nonEditable: true,
248
262
  render: getColRender(function (text, record, index) {
249
263
  const { onEdit, onDel, onSearch, getList } = props;
250
264
  const slotProps = { text, record, index, onEdit: onEdit, onDel: onDel, onSearch, getList };
@@ -421,15 +435,26 @@ const TableRender = forwardRef(function (props, tableRef) {
421
435
  </Popover>
422
436
  </div>
423
437
  ) : null}
424
- {isEditTable ? (
425
- <FormProvider form={formRender}>
426
- <Form layout="vertical">
427
- <FormConsumer>{() => <Table {...tableProps} />}</FormConsumer>
428
- </Form>
429
- </FormProvider>
430
- ) : (
431
- <Table {...tableProps} />
432
- )}
438
+ {
439
+ isCellEditTable ? <CellEditTable
440
+ {...tableProps}
441
+ {...cellEditTableProps}
442
+ onEditSubmit={props?.onEditSubmit}
443
+ Slots={props?.Slots}
444
+ rowSelection={config?.rowSelection}
445
+ query={query}
446
+ /> : <>
447
+ {isEditTable ? (
448
+ <FormProvider form={formRender}>
449
+ <Form layout="vertical">
450
+ <FormConsumer>{() => <Table {...tableProps} />}</FormConsumer>
451
+ </Form>
452
+ </FormProvider>
453
+ ) : (
454
+ <Table {...tableProps} />
455
+ )}
456
+ </>
457
+ }
433
458
  <FormilyField
434
459
  schema={props.schema}
435
460
  formilyRef={formilyRef}