@hzab/edit-table 0.0.1 → 0.1.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.
- package/CHANGELOG.md +4 -0
- package/README.md +18 -4
- package/package.json +58 -59
- package/src/Template/ExpandListTemplate/index.tsx +1 -8
- package/src/Template/NumberCellTemplate.tsx +1 -1
- package/src/common/WidthWrapHoc.tsx +14 -17
- package/src/index.less +23 -9
- package/src/index.tsx +58 -20
- package/src/Template/utils.ts +0 -288
- package/src/common/test.ts +0 -3
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -18,11 +18,24 @@ import Demo from "@hzab/edit-table";
|
|
|
18
18
|
|
|
19
19
|
## API
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
#### cellEditTableProps
|
|
22
22
|
|
|
23
|
-
|
|
|
24
|
-
|
|
|
25
|
-
|
|
|
23
|
+
| 属性名称 | 属性类型 | 必须 | 默认值 | 描述 |
|
|
24
|
+
| --------------- | -------- | ---- | ------ | ------------------ |
|
|
25
|
+
| reactGridProps | Object | 否 | | reactGrid 相关参数 |
|
|
26
|
+
| onChange | Function | 否 | | 单元格回调 |
|
|
27
|
+
| formulaConfig | Object | 否 | | 运算相关配置 |
|
|
28
|
+
| headerRowHeight | number | 否 | | 表头高度 |
|
|
29
|
+
| rowHeight | number | 否 | | 数据行高度 |
|
|
30
|
+
|
|
31
|
+
#### reactGridProps
|
|
32
|
+
|
|
33
|
+
| 属性名称 | 属性类型 | 必须 | 默认值 | 描述 |
|
|
34
|
+
| enableFillHandle | boolean | false | 开启后,选中单元格右下角会出现小方块,可拖拽快速复制 / 序列填充数据(类似 Excel 拖拽填充)|
|
|
35
|
+
| enableRangeSelection | boolean | false | 开启框选单元格区域(批量选中、批量编辑 / 填充必备)|
|
|
36
|
+
| onFocusLocationChanging | (location: CellLocation): boolean | 否 | 焦点 / 进入编辑态前回调,return false 阻止进入编辑)|
|
|
37
|
+
| customCellTemplates | Function | 否 | 注册自定义单元格模板(按钮、下拉、日期等单元格)|
|
|
38
|
+
| onSelectionChanged | Function | 否 | 选择区回调 |
|
|
26
39
|
|
|
27
40
|
# 组件开发流程
|
|
28
41
|
|
|
@@ -39,6 +52,7 @@ import Demo from "@hzab/edit-table";
|
|
|
39
52
|
## 命令
|
|
40
53
|
|
|
41
54
|
- Mac 执行该命令,设置 pre-commit 为可执行文件
|
|
55
|
+
|
|
42
56
|
- npm run mac-chmod
|
|
43
57
|
- chmod +x .husky && chmod +x .husky/pre-commit
|
|
44
58
|
|
package/package.json
CHANGED
|
@@ -1,59 +1,58 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@hzab/edit-table",
|
|
3
|
-
"version": "0.0
|
|
4
|
-
"description": "",
|
|
5
|
-
"main": "src",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"dev": "webpack serve -c ./config/webpack.config.js --env local",
|
|
8
|
-
"build": "webpack -c ./config/webpack.config.js --env production",
|
|
9
|
-
"version:alpha": "npm version prerelease --preid=alpha",
|
|
10
|
-
"publish:alpha": "npm publish --tag alpha",
|
|
11
|
-
"version:beta": "npm version prerelease --preid=beta",
|
|
12
|
-
"publish:beta": "npm publish --tag beta",
|
|
13
|
-
"version:patch": "npm version patch",
|
|
14
|
-
"version:minior": "npm version minor",
|
|
15
|
-
"version:major": "npm version major",
|
|
16
|
-
"publish:stable": "npm publish --access public",
|
|
17
|
-
"prepare": "husky install",
|
|
18
|
-
"mac-chmod": "chmod +x .husky && chmod +x .husky/pre-commit",
|
|
19
|
-
"docs": "typedoc"
|
|
20
|
-
},
|
|
21
|
-
"files": [
|
|
22
|
-
"src",
|
|
23
|
-
"CHANGELOG.md"
|
|
24
|
-
],
|
|
25
|
-
"keywords": [],
|
|
26
|
-
"author": "CaiYansong",
|
|
27
|
-
"license": "ISC",
|
|
28
|
-
"devDependencies": {
|
|
29
|
-
"@hzab/permissions": "1.0.0",
|
|
30
|
-
"@hzab/webpack-config": "1.1.4",
|
|
31
|
-
"@types/react": "17.0.62",
|
|
32
|
-
"@types/react-dom": "17.0.20",
|
|
33
|
-
"antd": "4.24.16",
|
|
34
|
-
"axios": "1.13.6",
|
|
35
|
-
"eslint": "8.30.0",
|
|
36
|
-
"less": "4.1.3",
|
|
37
|
-
"mobx": "6.7.0",
|
|
38
|
-
"mobx-react": "7.6.0",
|
|
39
|
-
"react": "17.0.2",
|
|
40
|
-
"react-dom": "17.0.2",
|
|
41
|
-
"react-router-dom": "6.14.1",
|
|
42
|
-
"typedoc": "0.24.8",
|
|
43
|
-
"typescript": "4.9.4",
|
|
44
|
-
"webpack": "5.75.0"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
"
|
|
48
|
-
},
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@hzab/edit-table",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "src",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "webpack serve -c ./config/webpack.config.js --env local",
|
|
8
|
+
"build": "webpack -c ./config/webpack.config.js --env production",
|
|
9
|
+
"version:alpha": "npm version prerelease --preid=alpha",
|
|
10
|
+
"publish:alpha": "npm publish --tag alpha",
|
|
11
|
+
"version:beta": "npm version prerelease --preid=beta",
|
|
12
|
+
"publish:beta": "npm publish --tag beta",
|
|
13
|
+
"version:patch": "npm version patch",
|
|
14
|
+
"version:minior": "npm version minor",
|
|
15
|
+
"version:major": "npm version major",
|
|
16
|
+
"publish:stable": "npm publish --access public",
|
|
17
|
+
"prepare": "husky install",
|
|
18
|
+
"mac-chmod": "chmod +x .husky && chmod +x .husky/pre-commit",
|
|
19
|
+
"docs": "typedoc"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"src",
|
|
23
|
+
"CHANGELOG.md"
|
|
24
|
+
],
|
|
25
|
+
"keywords": [],
|
|
26
|
+
"author": "CaiYansong",
|
|
27
|
+
"license": "ISC",
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@hzab/permissions": "1.0.0",
|
|
30
|
+
"@hzab/webpack-config": "1.1.4",
|
|
31
|
+
"@types/react": "17.0.62",
|
|
32
|
+
"@types/react-dom": "17.0.20",
|
|
33
|
+
"antd": "4.24.16",
|
|
34
|
+
"axios": "1.13.6",
|
|
35
|
+
"eslint": "8.30.0",
|
|
36
|
+
"less": "4.1.3",
|
|
37
|
+
"mobx": "6.7.0",
|
|
38
|
+
"mobx-react": "7.6.0",
|
|
39
|
+
"react": "17.0.2",
|
|
40
|
+
"react-dom": "17.0.2",
|
|
41
|
+
"react-router-dom": "6.14.1",
|
|
42
|
+
"typedoc": "0.24.8",
|
|
43
|
+
"typescript": "4.9.4",
|
|
44
|
+
"webpack": "5.75.0",
|
|
45
|
+
"@hzab/form-render": "^1.7.17",
|
|
46
|
+
"@hzab/list-render": "^1.11.0",
|
|
47
|
+
"@silevis/reactgrid": "^4.1.17"
|
|
48
|
+
},
|
|
49
|
+
"directories": {
|
|
50
|
+
"src": "src"
|
|
51
|
+
},
|
|
52
|
+
"lint-staged": {
|
|
53
|
+
"**.{js,jsx,ts,tsx,css,scss,less,json,html}": [
|
|
54
|
+
"prettier --write"
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
"dependencies": {}
|
|
58
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { MinusCircleOutlined, MinusSquareOutlined, PlusCircleOutlined } from "@ant-design/icons";
|
|
2
1
|
import { useEffect, useState } from "react";
|
|
2
|
+
import { MinusCircleOutlined, PlusCircleOutlined } from "@ant-design/icons";
|
|
3
3
|
import "./index.less";
|
|
4
4
|
|
|
5
5
|
export const ExpandListTemplate = (props) => {
|
|
@@ -29,10 +29,6 @@ export const ExpandListTemplate = (props) => {
|
|
|
29
29
|
}
|
|
30
30
|
}, [cell?.record]);
|
|
31
31
|
|
|
32
|
-
const colRender = function (record) {
|
|
33
|
-
const Slot = Slots[columnId];
|
|
34
|
-
return <Slot record={record} />;
|
|
35
|
-
};
|
|
36
32
|
/** 展开关闭子项 */
|
|
37
33
|
const onClickExpand = () => {
|
|
38
34
|
setExpandKeys((preExpandKeys) => {
|
|
@@ -57,9 +53,6 @@ export const ExpandListTemplate = (props) => {
|
|
|
57
53
|
) : (
|
|
58
54
|
<div style={{ width: 14 }}></div>
|
|
59
55
|
)}
|
|
60
|
-
<div className={`expandList-template-name`} style={{ marginLeft: record?.level * 8 }}>
|
|
61
|
-
{Slots?.[columnId] ? colRender(record) : record[columnId]}
|
|
62
|
-
</div>
|
|
63
56
|
</div>
|
|
64
57
|
);
|
|
65
58
|
};
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
UncertainCompatible,
|
|
16
16
|
} from "@silevis/reactgrid";
|
|
17
17
|
import * as React from "react";
|
|
18
|
-
import { parseLocaleNumber } from "
|
|
18
|
+
import { parseLocaleNumber } from "../common/utils";
|
|
19
19
|
|
|
20
20
|
// NOTE: all modules imported below may be imported from '@silevis/reactgrid'
|
|
21
21
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect, useMemo, useState } from "react";
|
|
1
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
Cell,
|
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
Uncertain,
|
|
13
13
|
UncertainCompatible,
|
|
14
14
|
} from "@silevis/reactgrid";
|
|
15
|
-
import { getVal } from "@hzab/list-render/src/common/utils";
|
|
16
15
|
import { InfoCircleOutlined } from "@ant-design/icons";
|
|
17
16
|
import { Popover } from "antd";
|
|
18
17
|
|
|
@@ -108,6 +107,12 @@ export const WidthWrapHoc = (WrapComponent: any) => {
|
|
|
108
107
|
const [isOpen, setIsOpen] = useState<boolean>(false);
|
|
109
108
|
const datatypeId = cell?.columnId + cell?.rowId;
|
|
110
109
|
const [comValue, setComValue] = useState(cell?.text);
|
|
110
|
+
const comValueInit = useRef<any>(cell?.text);
|
|
111
|
+
useEffect(() => {
|
|
112
|
+
setComValue(cell?.text);
|
|
113
|
+
comValueInit.current = cell?.text;
|
|
114
|
+
}, [cell?.text]);
|
|
115
|
+
|
|
111
116
|
useEffect(() => {
|
|
112
117
|
// 处理下拉选项弹层失焦逻辑
|
|
113
118
|
function onClick() {
|
|
@@ -120,21 +125,12 @@ export const WidthWrapHoc = (WrapComponent: any) => {
|
|
|
120
125
|
document.removeEventListener("click", onClick);
|
|
121
126
|
};
|
|
122
127
|
}, []);
|
|
123
|
-
const echoValue = useMemo(() => {
|
|
124
|
-
if (!cell?.getField) {
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
return getVal(
|
|
128
|
-
{ ...cell?.getField(), enum: cell?.getField()?.enum || cell?.values },
|
|
129
|
-
{ [cell?.getField().name]: cell.text },
|
|
130
|
-
);
|
|
131
|
-
}, [cell]);
|
|
132
128
|
|
|
133
129
|
const isValidator = useMemo(() => {
|
|
134
130
|
let flag = false;
|
|
135
131
|
let isVerify = false;
|
|
136
132
|
for (let it of cell?.validatorList || []) {
|
|
137
|
-
if (it?.pattern?.test(
|
|
133
|
+
if (it?.pattern?.test(comValue)) {
|
|
138
134
|
flag = true;
|
|
139
135
|
}
|
|
140
136
|
break;
|
|
@@ -142,7 +138,7 @@ export const WidthWrapHoc = (WrapComponent: any) => {
|
|
|
142
138
|
isVerify = !flag && cell?.validatorList?.length > 0;
|
|
143
139
|
|
|
144
140
|
return isVerify;
|
|
145
|
-
}, [cell?.validatorList,
|
|
141
|
+
}, [cell?.validatorList, comValue]);
|
|
146
142
|
|
|
147
143
|
return (
|
|
148
144
|
<div
|
|
@@ -151,7 +147,6 @@ export const WidthWrapHoc = (WrapComponent: any) => {
|
|
|
151
147
|
if (cell?.nonEditable) {
|
|
152
148
|
return;
|
|
153
149
|
}
|
|
154
|
-
(window as any).preCell = cell;
|
|
155
150
|
setIsOpen(true);
|
|
156
151
|
}}
|
|
157
152
|
>
|
|
@@ -164,6 +159,8 @@ export const WidthWrapHoc = (WrapComponent: any) => {
|
|
|
164
159
|
cell["commit"] = "change";
|
|
165
160
|
const targetValue = e?.target?.value ?? e;
|
|
166
161
|
setComValue(targetValue);
|
|
162
|
+
comValueInit.current = targetValue;
|
|
163
|
+
|
|
167
164
|
onCellChanged(
|
|
168
165
|
this.getCompatibleCell({
|
|
169
166
|
...cell,
|
|
@@ -178,7 +175,7 @@ export const WidthWrapHoc = (WrapComponent: any) => {
|
|
|
178
175
|
if (isValidator) {
|
|
179
176
|
cell["commit"] = "verify";
|
|
180
177
|
}
|
|
181
|
-
const targetValue =
|
|
178
|
+
const targetValue = comValueInit.current;
|
|
182
179
|
onCellChanged(
|
|
183
180
|
this.getCompatibleCell({
|
|
184
181
|
...cell,
|
|
@@ -202,8 +199,8 @@ export const WidthWrapHoc = (WrapComponent: any) => {
|
|
|
202
199
|
cell={cell}
|
|
203
200
|
></WrapComponent>
|
|
204
201
|
{isValidator && (
|
|
205
|
-
<Popover content="该字段不合法">
|
|
206
|
-
<InfoCircleOutlined
|
|
202
|
+
<Popover content={cell?.validatorMessage ?? "该字段不合法"}>
|
|
203
|
+
<InfoCircleOutlined className="validator-icon" />
|
|
207
204
|
</Popover>
|
|
208
205
|
)}
|
|
209
206
|
</div>
|
package/src/index.less
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
.grid-container {
|
|
2
2
|
overflow: auto;
|
|
3
3
|
position: relative;
|
|
4
|
-
max-height:
|
|
4
|
+
max-height: 100vh;
|
|
5
|
+
padding-bottom: 16px;
|
|
6
|
+
|
|
7
|
+
|
|
5
8
|
|
|
6
9
|
|
|
7
10
|
|
|
@@ -10,14 +13,6 @@
|
|
|
10
13
|
}
|
|
11
14
|
|
|
12
15
|
|
|
13
|
-
.reactgrid-content .rg-pane .rg-cell {
|
|
14
|
-
padding: 0;
|
|
15
|
-
|
|
16
|
-
>div:nth-child(1) {
|
|
17
|
-
padding: 0 4px;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
16
|
.rg-header-cell,
|
|
22
17
|
.rg-cell {
|
|
23
18
|
display: flex;
|
|
@@ -32,4 +27,23 @@
|
|
|
32
27
|
display: flex;
|
|
33
28
|
align-items: center;
|
|
34
29
|
justify-content: center;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.validator-icon {
|
|
33
|
+
position: relative;
|
|
34
|
+
background: #fff;
|
|
35
|
+
cursor: pointer;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.rg-rowSelection-cell {
|
|
39
|
+
label {
|
|
40
|
+
justify-content: center;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.grid-container-empty {
|
|
45
|
+
height: 100%;
|
|
46
|
+
display: flex;
|
|
47
|
+
align-items: center;
|
|
48
|
+
justify-content: center;
|
|
35
49
|
}
|
package/src/index.tsx
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { Checkbox, Empty, Spin } from "antd";
|
|
2
3
|
import { ReactGrid } from "@silevis/reactgrid";
|
|
3
4
|
import { TextTemplate } from "./Template/TextCellTemplate";
|
|
4
5
|
import { antdComponents, customComponents } from "@hzab/form-render/src/index";
|
|
5
6
|
import { WidthWrapHoc } from "./common/WidthWrapHoc";
|
|
6
7
|
import { NumberTemplate } from "./Template/NumberCellTemplate";
|
|
7
|
-
import { Checkbox, Spin } from "antd";
|
|
8
8
|
import { ExpandListTemplate } from "./Template/ExpandListTemplate";
|
|
9
9
|
import { convertColumnsToRows, flattenColumns, isReactElement } from "./common/utils";
|
|
10
10
|
import { childAntdComponent } from "./common/childAntdComponent";
|
|
@@ -29,6 +29,8 @@ interface CellEditTableProps {
|
|
|
29
29
|
query?: any;
|
|
30
30
|
loading?: boolean;
|
|
31
31
|
isExpand?: boolean;
|
|
32
|
+
getList?: () => void;
|
|
33
|
+
onRow?: (v: any, ri: number, ci: number) => void;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
export default function CellEditTable(props: CellEditTableProps) {
|
|
@@ -39,8 +41,8 @@ export default function CellEditTable(props: CellEditTableProps) {
|
|
|
39
41
|
onChange,
|
|
40
42
|
reactGridProps = {},
|
|
41
43
|
onEditSubmit,
|
|
42
|
-
headerRowHeight,
|
|
43
|
-
rowHeight,
|
|
44
|
+
headerRowHeight = 44,
|
|
45
|
+
rowHeight = 44,
|
|
44
46
|
reactGridStyle,
|
|
45
47
|
className,
|
|
46
48
|
Slots,
|
|
@@ -49,15 +51,22 @@ export default function CellEditTable(props: CellEditTableProps) {
|
|
|
49
51
|
query,
|
|
50
52
|
loading,
|
|
51
53
|
isExpand = false,
|
|
54
|
+
getList,
|
|
55
|
+
onRow,
|
|
52
56
|
} = props;
|
|
53
57
|
|
|
54
58
|
const [headerList, setHeaderList] = useState<any>([]);
|
|
55
59
|
const [rows, setRows] = useState<any>([]);
|
|
60
|
+
/** 初始行数据 */
|
|
61
|
+
const initRowsRef = useRef<any>([]);
|
|
62
|
+
const firstRender = useRef(true);
|
|
56
63
|
const [tableColumns, setTableColumns] = useState<any>([]);
|
|
57
64
|
const [selectedRowKeys, setSelectedRowKeys] = useState<any>([]);
|
|
58
65
|
const [newDataSource, setNewDataSource] = useState<any>([]);
|
|
59
66
|
const [expandKeys, setExpandKeys] = useState<any>([]);
|
|
60
67
|
const [headerCustomTemplate, setHeaderCustomTemplate] = useState<any>([]);
|
|
68
|
+
const gridRef = useRef<any>();
|
|
69
|
+
|
|
61
70
|
/** 递归处理子节点数据 */
|
|
62
71
|
const handleDataSource = (arr, level = 0) => {
|
|
63
72
|
arr?.forEach((el) => {
|
|
@@ -166,9 +175,9 @@ export default function CellEditTable(props: CellEditTableProps) {
|
|
|
166
175
|
if (key == el) {
|
|
167
176
|
const it = formulaConfig[key];
|
|
168
177
|
newCell = {
|
|
169
|
-
text: it?.setCellData(item),
|
|
170
|
-
value: it?.setCellData(item),
|
|
171
|
-
style: it?.setCellStyle(it?.setCellData(item)),
|
|
178
|
+
text: it?.setCellData && it?.setCellData(item),
|
|
179
|
+
value: it?.setCellData && it?.setCellData(item),
|
|
180
|
+
style: it?.setCellStyle && it?.setCellStyle((it?.setCellData && it?.setCellData(item)) || item),
|
|
172
181
|
};
|
|
173
182
|
}
|
|
174
183
|
}
|
|
@@ -184,6 +193,7 @@ export default function CellEditTable(props: CellEditTableProps) {
|
|
|
184
193
|
value: cellItem,
|
|
185
194
|
selectedValue: cellItem || "",
|
|
186
195
|
style: columnsItem?.type == "rowSelection" ? { display: "flex", justifyContent: "center" } : {},
|
|
196
|
+
...((onRow && onRow(item, index, ind)) || {}),
|
|
187
197
|
...((columnsItem?.onCell && columnsItem?.onCell(item, index, ind)) || {}),
|
|
188
198
|
...(columnsItem?.cellProps || {}),
|
|
189
199
|
setNewDataSource,
|
|
@@ -196,44 +206,70 @@ export default function CellEditTable(props: CellEditTableProps) {
|
|
|
196
206
|
}),
|
|
197
207
|
};
|
|
198
208
|
});
|
|
209
|
+
if (firstRender.current) {
|
|
210
|
+
initRowsRef.current = [...headerList, ...tempDataSource];
|
|
211
|
+
firstRender.current = false;
|
|
212
|
+
}
|
|
199
213
|
setRows([...headerList, ...tempDataSource]);
|
|
200
214
|
}, [newDataSource, tableColumns, headerList]);
|
|
201
215
|
|
|
202
216
|
// 单元格编辑回调
|
|
203
|
-
const handleCellsChanged = (changes) => {
|
|
217
|
+
const handleCellsChanged = async (changes) => {
|
|
218
|
+
const batchQuery: any = [];
|
|
204
219
|
if (!Array.isArray(changes) || changes.length === 0) return;
|
|
205
220
|
/** 批量提交 */
|
|
206
221
|
let isBatchSubmit = false;
|
|
207
222
|
if (changes?.length > 1) {
|
|
208
223
|
isBatchSubmit = true;
|
|
209
224
|
}
|
|
225
|
+
|
|
210
226
|
setRows((prevRows) => {
|
|
211
|
-
changes.forEach((change) => {
|
|
212
|
-
const hasDiff = JSON.stringify(
|
|
213
|
-
if (
|
|
227
|
+
changes.forEach(async (change) => {
|
|
228
|
+
// const hasDiff = JSON.stringify( change?.newCell?.text) != JSON.stringify(change.previousCell?.text);
|
|
229
|
+
// if (hasDiff) return;
|
|
214
230
|
const changeRowIdx = prevRows.findIndex((el) => el.rowId == change.rowId);
|
|
215
231
|
const changeColumnIdx = tableColumns.findIndex((el) => el.columnId == change.columnId);
|
|
216
232
|
prevRows[changeRowIdx].cells[changeColumnIdx] = { ...change.newCell };
|
|
233
|
+
batchQuery.push(handleArrayToObj(prevRows[changeRowIdx]));
|
|
217
234
|
if (formulaConfig) {
|
|
218
235
|
for (let key in formulaConfig) {
|
|
219
236
|
const item = formulaConfig[key];
|
|
220
237
|
const resultColumnIdx = tableColumns.findIndex((el) => el.columnId == key);
|
|
221
238
|
const newCell = {
|
|
222
239
|
...prevRows[changeRowIdx].cells[resultColumnIdx],
|
|
223
|
-
text: item?.setCellData(prevRows[changeRowIdx]),
|
|
224
|
-
value: item?.setCellData(prevRows[changeRowIdx]),
|
|
225
|
-
style:
|
|
240
|
+
text: item?.setCellData && item?.setCellData(prevRows[changeRowIdx]),
|
|
241
|
+
value: item?.setCellData && item?.setCellData(prevRows[changeRowIdx]),
|
|
242
|
+
style:
|
|
243
|
+
item?.setCellStyle &&
|
|
244
|
+
item?.setCellStyle(
|
|
245
|
+
(item?.setCellData && item?.setCellData(prevRows[changeRowIdx])) || prevRows[changeRowIdx],
|
|
246
|
+
),
|
|
226
247
|
};
|
|
227
248
|
prevRows[changeRowIdx].cells[resultColumnIdx] = newCell;
|
|
228
249
|
}
|
|
229
250
|
}
|
|
230
|
-
|
|
231
|
-
|
|
251
|
+
|
|
252
|
+
if (change?.newCell?.commit == "submit" && onEditSubmit && !isBatchSubmit) {
|
|
253
|
+
try {
|
|
254
|
+
await onEditSubmit(batchQuery);
|
|
255
|
+
} catch (err) {
|
|
256
|
+
getList && getList();
|
|
257
|
+
}
|
|
232
258
|
}
|
|
259
|
+
|
|
233
260
|
onChange && onChange(prevRows[changeRowIdx]);
|
|
234
261
|
});
|
|
262
|
+
initRowsRef.current = prevRows;
|
|
235
263
|
return [...prevRows];
|
|
236
264
|
});
|
|
265
|
+
|
|
266
|
+
if (isBatchSubmit) {
|
|
267
|
+
try {
|
|
268
|
+
onEditSubmit && (await onEditSubmit(batchQuery));
|
|
269
|
+
} catch (err) {
|
|
270
|
+
getList && getList();
|
|
271
|
+
}
|
|
272
|
+
}
|
|
237
273
|
};
|
|
238
274
|
/** 自定义组件注册 */
|
|
239
275
|
const baseTemplate = useMemo(() => {
|
|
@@ -325,7 +361,8 @@ export default function CellEditTable(props: CellEditTableProps) {
|
|
|
325
361
|
<Spin spinning={loading}>
|
|
326
362
|
<div className={`grid-container ${className}`} style={reactGridStyle}>
|
|
327
363
|
<ReactGrid
|
|
328
|
-
|
|
364
|
+
ref={gridRef}
|
|
365
|
+
rows={loading ? initRowsRef.current : rows}
|
|
329
366
|
columns={tableColumns}
|
|
330
367
|
onCellsChanged={handleCellsChanged}
|
|
331
368
|
enableFillHandle={true}
|
|
@@ -353,11 +390,12 @@ export default function CellEditTable(props: CellEditTableProps) {
|
|
|
353
390
|
Input: TextTemplate,
|
|
354
391
|
Number: NumberTemplate,
|
|
355
392
|
}}
|
|
356
|
-
|
|
357
|
-
// onSelectionChanged={(newSelection) => {
|
|
358
|
-
// console.log("选区发生变化", newSelection[0]);
|
|
359
|
-
// }}
|
|
360
393
|
/>
|
|
394
|
+
{dataSource?.length <= 0 && (
|
|
395
|
+
<div className="grid-container-empty">
|
|
396
|
+
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
|
|
397
|
+
</div>
|
|
398
|
+
)}
|
|
361
399
|
</div>
|
|
362
400
|
</Spin>
|
|
363
401
|
);
|
package/src/Template/utils.ts
DELETED
|
@@ -1,288 +0,0 @@
|
|
|
1
|
-
export const getNavigatorLanguage = (): string => navigator.language || "en-US";
|
|
2
|
-
|
|
3
|
-
function getLocaleSeparators(locale: string) {
|
|
4
|
-
const testNumber = 123456.789;
|
|
5
|
-
const localeFormattedNumber = Intl.NumberFormat(locale).format(testNumber);
|
|
6
|
-
|
|
7
|
-
// Get the thousands separator of the locale
|
|
8
|
-
const thousandsSeparator = localeFormattedNumber.split("123")[1][0];
|
|
9
|
-
|
|
10
|
-
// Get the decimal separator of the locale
|
|
11
|
-
const decimalSeparator = localeFormattedNumber.split("123")[1][4];
|
|
12
|
-
return { thousandsSeparator, decimalSeparator };
|
|
13
|
-
}
|
|
14
|
-
export function parseLocaleNumber(stringNumber: string, locale = getNavigatorLanguage()): number {
|
|
15
|
-
if (!stringNumber.trim()) return NaN;
|
|
16
|
-
const { thousandsSeparator, decimalSeparator } = getLocaleSeparators(locale);
|
|
17
|
-
const normalizedStringNumber = stringNumber.replace(/\u00A0/g, " "); // Replace non-breaking space with normal space
|
|
18
|
-
const numberString = normalizedStringNumber
|
|
19
|
-
.replace(new RegExp(`[${thousandsSeparator}\\s]`, "g"), "") // Replace thousands separator and white-space
|
|
20
|
-
.replace(new RegExp(`\\${decimalSeparator}`, "g"), "."); // Replace decimal separator
|
|
21
|
-
|
|
22
|
-
const trimmedNumberString = numberString.replace(/^(?!-)\D+|\D+$/g, ""); // Remove characters before first and after last number, but keep negative sign
|
|
23
|
-
if (trimmedNumberString === null || trimmedNumberString.trim().length === 0) {
|
|
24
|
-
return NaN;
|
|
25
|
-
}
|
|
26
|
-
return Number(trimmedNumberString);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
export function isReactElement(value) {
|
|
31
|
-
return value !== null && typeof value === "object";
|
|
32
|
-
}
|
|
33
|
-
const getTitle = (item) => {
|
|
34
|
-
return typeof item?.title == "function"
|
|
35
|
-
? isReactElement(item?.title())
|
|
36
|
-
? item?.title()?.props.children
|
|
37
|
-
: String(item?.title()) || ""
|
|
38
|
-
: String(item?.title) || ""
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function convertColumnsToRows(columns, headerRowHeight = 40) {
|
|
42
|
-
// 获取所有叶子节点
|
|
43
|
-
const getLeaves = (cols) => {
|
|
44
|
-
return cols.reduce((acc, col) => {
|
|
45
|
-
if (col.children?.length) {
|
|
46
|
-
return [...acc, ...getLeaves(col.children)];
|
|
47
|
-
}
|
|
48
|
-
return [...acc, col];
|
|
49
|
-
}, []);
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
// 计算最大深度
|
|
53
|
-
const getDepth = (cols, depth = 1) => {
|
|
54
|
-
return cols.reduce((max, col) => {
|
|
55
|
-
if (col.children?.length) {
|
|
56
|
-
return Math.max(max, getDepth(col.children, depth + 1));
|
|
57
|
-
}
|
|
58
|
-
return Math.max(max, depth);
|
|
59
|
-
}, depth);
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const leafColumns = getLeaves(columns);
|
|
63
|
-
const totalDepth = getDepth(columns);
|
|
64
|
-
const totalCols = leafColumns.length;
|
|
65
|
-
|
|
66
|
-
// 构建表头树
|
|
67
|
-
const buildTree = (cols, depth = 0) => {
|
|
68
|
-
return cols.map((col) => {
|
|
69
|
-
const hasChildren = col.children?.length > 0;
|
|
70
|
-
if (!hasChildren) {
|
|
71
|
-
return {
|
|
72
|
-
...col,
|
|
73
|
-
rowspan: totalDepth - depth,
|
|
74
|
-
colspan: 1,
|
|
75
|
-
_depth: depth,
|
|
76
|
-
_isLeaf: true,
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
const leafCount = getLeaves(col.children).length;
|
|
80
|
-
return {
|
|
81
|
-
...col,
|
|
82
|
-
rowspan: 1,
|
|
83
|
-
colspan: leafCount,
|
|
84
|
-
_depth: depth,
|
|
85
|
-
_isLeaf: false,
|
|
86
|
-
children: buildTree(col.children, depth + 1),
|
|
87
|
-
};
|
|
88
|
-
});
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
const tree = buildTree(columns, 0);
|
|
92
|
-
|
|
93
|
-
// 初始化行
|
|
94
|
-
const rows: any = Array.from({ length: totalDepth }, (_, i) => ({
|
|
95
|
-
rowId: `header-${i}`,
|
|
96
|
-
height: headerRowHeight,
|
|
97
|
-
cells: [],
|
|
98
|
-
}));
|
|
99
|
-
|
|
100
|
-
const handleType = (item) => {
|
|
101
|
-
if (["rowSelection", "ExpandListTemplate"]?.includes(item?.type)) {
|
|
102
|
-
return item?.type
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (isReactElement(typeof item?.title == "function" ? item?.title() : null)) {
|
|
106
|
-
return `${item?.dataIndex}`
|
|
107
|
-
}
|
|
108
|
-
return "header"
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
// 递归填充行
|
|
113
|
-
const fillRow = (nodes, rowIndex = 0) => {
|
|
114
|
-
nodes.forEach((node) => {
|
|
115
|
-
if (node._isLeaf) {
|
|
116
|
-
const cell = {
|
|
117
|
-
...node,
|
|
118
|
-
type: handleType(node),
|
|
119
|
-
text: getTitle(node),
|
|
120
|
-
dataIndex: node.dataIndex,
|
|
121
|
-
width: node.width,
|
|
122
|
-
rowspan: node.rowspan,
|
|
123
|
-
style: node?.style ? node?.style : {
|
|
124
|
-
background: "rgba(128, 128, 128, 0.1)",
|
|
125
|
-
display: "flex",
|
|
126
|
-
justifyContent: "center"
|
|
127
|
-
},
|
|
128
|
-
};
|
|
129
|
-
if (node.sorter) cell.sorter = node.sorter;
|
|
130
|
-
if (node.key) cell.key = node.key;
|
|
131
|
-
rows[rowIndex].cells.push(cell);
|
|
132
|
-
} else {
|
|
133
|
-
// 父节点:添加colspan
|
|
134
|
-
rows[rowIndex].cells.push({
|
|
135
|
-
...node,
|
|
136
|
-
type: handleType(node),
|
|
137
|
-
text: getTitle(node),
|
|
138
|
-
colspan: node.colspan,
|
|
139
|
-
style: node?.style ? node?.style : {
|
|
140
|
-
background: "rgba(128, 128, 128, 0.1)",
|
|
141
|
-
display: "flex",
|
|
142
|
-
justifyContent: "center"
|
|
143
|
-
},
|
|
144
|
-
});
|
|
145
|
-
if (node.children) {
|
|
146
|
-
fillRow(node.children, rowIndex + 1);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
fillRow(tree, 0);
|
|
153
|
-
|
|
154
|
-
// 补全每一行的cell,使长度一致
|
|
155
|
-
rows.forEach((row, rowIndex) => {
|
|
156
|
-
const newCells = [];
|
|
157
|
-
let colIndex = 0;
|
|
158
|
-
let cellIndex = 0;
|
|
159
|
-
|
|
160
|
-
while (colIndex < totalCols) {
|
|
161
|
-
const currentCell = row.cells[cellIndex];
|
|
162
|
-
|
|
163
|
-
if (!currentCell) {
|
|
164
|
-
// 如果当前行没有足够的cell,从叶子节点补充
|
|
165
|
-
const leaf = leafColumns[colIndex];
|
|
166
|
-
newCells.push({
|
|
167
|
-
...leaf,
|
|
168
|
-
type: handleType(leaf),
|
|
169
|
-
text: getTitle(leaf),
|
|
170
|
-
dataIndex: leaf.dataIndex,
|
|
171
|
-
width: leaf.width,
|
|
172
|
-
rowspan: rowIndex === totalDepth - 1 ? 1 : undefined,
|
|
173
|
-
style: leaf?.style ? leaf?.style : {
|
|
174
|
-
background: "rgba(128, 128, 128, 0.1)",
|
|
175
|
-
display: "flex",
|
|
176
|
-
justifyContent: "center"
|
|
177
|
-
},
|
|
178
|
-
});
|
|
179
|
-
colIndex++;
|
|
180
|
-
cellIndex++;
|
|
181
|
-
continue;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const colspan = currentCell.colspan || 1;
|
|
185
|
-
const rowspan = currentCell.rowspan || 1;
|
|
186
|
-
|
|
187
|
-
// 检查当前cell是否应该出现在这一行
|
|
188
|
-
// 如果rowspan > 1,表示它跨多行,但只在第一行出现
|
|
189
|
-
if (rowspan > 1 && rowIndex > 0) {
|
|
190
|
-
// 这个cell不应该出现在当前行,跳过
|
|
191
|
-
colIndex += colspan;
|
|
192
|
-
cellIndex++;
|
|
193
|
-
continue;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// 如果当前cell有colspan,需要补全后面的空cell
|
|
197
|
-
newCells.push(currentCell);
|
|
198
|
-
colIndex += colspan;
|
|
199
|
-
cellIndex++;
|
|
200
|
-
|
|
201
|
-
// 如果colspan > 1,后面补充空cell
|
|
202
|
-
for (let i = 1; i < colspan; i++) {
|
|
203
|
-
newCells.push({
|
|
204
|
-
type: "header",
|
|
205
|
-
text: "",
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
row.cells = newCells;
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
// 最后一行的特殊处理:确保所有叶子节点都有对应的cell
|
|
214
|
-
const lastRow = rows[totalDepth - 1];
|
|
215
|
-
const lastRowCells = [];
|
|
216
|
-
let leafIndex = 0;
|
|
217
|
-
|
|
218
|
-
// 遍历所有叶子节点
|
|
219
|
-
leafColumns.forEach((leaf, index) => {
|
|
220
|
-
// 检查是否已经在lastRow中有对应的cell
|
|
221
|
-
const existingCell = lastRow.cells.find((cell) => cell.dataIndex === leaf.dataIndex && cell.text === leaf.title);
|
|
222
|
-
|
|
223
|
-
if (existingCell) {
|
|
224
|
-
lastRowCells.push(existingCell);
|
|
225
|
-
} else {
|
|
226
|
-
// 创建新的cell
|
|
227
|
-
lastRowCells.push({
|
|
228
|
-
...leaf,
|
|
229
|
-
type: handleType(leaf),
|
|
230
|
-
text: getTitle(leaf),
|
|
231
|
-
dataIndex: leaf.dataIndex,
|
|
232
|
-
width: leaf.width,
|
|
233
|
-
rowspan: 1,
|
|
234
|
-
style: leaf?.style ? leaf?.style : {
|
|
235
|
-
background: "rgba(128, 128, 128, 0.1)",
|
|
236
|
-
display: "flex",
|
|
237
|
-
justifyContent: "center"
|
|
238
|
-
},
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
lastRow.cells = lastRowCells;
|
|
244
|
-
|
|
245
|
-
// 确保所有行的cell数量等于totalCols
|
|
246
|
-
rows.forEach((row) => {
|
|
247
|
-
// 如果cell数量少于totalCols,补充空cell
|
|
248
|
-
while (row.cells.length < totalCols) {
|
|
249
|
-
row.cells.push({
|
|
250
|
-
type: "header",
|
|
251
|
-
text: "",
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
return rows;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
export function flattenColumns(columns: any[]) {
|
|
261
|
-
const result: any[] = [];
|
|
262
|
-
|
|
263
|
-
function traverse(cols) {
|
|
264
|
-
cols.forEach((col) => {
|
|
265
|
-
if (col.children && col.children.length > 0) {
|
|
266
|
-
traverse(col.children);
|
|
267
|
-
} else {
|
|
268
|
-
// 创建新对象,只保留需要的属性
|
|
269
|
-
const { title, dataIndex, key, width, sorter, k, ...rest } = col;
|
|
270
|
-
|
|
271
|
-
result.push({
|
|
272
|
-
type: "Input",
|
|
273
|
-
dataIndex,
|
|
274
|
-
title,
|
|
275
|
-
...(key && { key }),
|
|
276
|
-
...(width && { width }),
|
|
277
|
-
...(sorter && { sorter }),
|
|
278
|
-
...(k && { k }),
|
|
279
|
-
// 如果需要保留其他属性
|
|
280
|
-
...rest,
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
traverse(columns);
|
|
287
|
-
return result;
|
|
288
|
-
}
|
package/src/common/test.ts
DELETED