Skip to content

Commit a33d8f9

Browse files
committed
refactor: add copyTypes and remove showCopyWithHeader
1 parent dcc7e5d commit a33d8f9

File tree

7 files changed

+201
-67
lines changed

7 files changed

+201
-67
lines changed

CHANGELOG.md

-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ All notable changes to this project will be documented in this file. See [standa
7070
### Bug Fixes
7171

7272
- fix immediate option in userList ([#378](https://github.com/DTStack/dt-react-component/issues/378)) ([b8992f3](https://github.com/DTStack/dt-react-component/commit/b8992f38f918dde10cb6349e39aca2c524e70853))
73-
> > > > > > > 272326794683ecd3ed1846ad38fd4258b23dc0f7
7473

7574
## [4.2.0](https://github.com/DTStack/dt-react-component/compare/v4.1.0...v4.2.0) (2023-08-24)
7675

docs/guide/migration-v4.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,10 @@ useCookieListener(
150150

151151
- 新增 `observerEle` 属性,支持自定义监听元素。
152152

153-
#### SpreadSheet [#325](https://github.com/DTStack/dt-react-component/pull/325)
153+
#### SpreadSheet [#325](https://github.com/DTStack/dt-react-component/pull/325)[#545](https://github.com/DTStack/dt-react-component/pull/545)
154154

155155
- 新增 `className` 属性,可自定义外层组件的 class 名。
156+
- 删除 `showCopyWithHeader` 属性,使用 `copyTypes` 属性代替,值为数组,可传入 'copyData'、'copyHeaders'、'copyHeadersAndData',分别代表的功能为:复制值、复制列名、复制列名和值。
156157

157158
#### KeyEventListener [#326](https://github.com/DTStack/dt-react-component/pull/326)
158159

src/spreadSheet/__tests__/index.test.tsx

+19-3
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,35 @@ describe('test spreadSheet ', () => {
2929
const { getByText, unmount } = render(<SpreadSheet data={data} columns={columns} />);
3030
const cell = getByText('zhangsan');
3131
fireEvent.contextMenu(cell);
32-
const copyBtn = getByText('复制');
32+
const copyBtn = getByText('复制值');
3333
expect(copyBtn).toBeInTheDocument();
3434
fireEvent.click(copyBtn);
3535
unmount();
3636
});
3737

3838
test('copy value with header', () => {
3939
const { getByText, unmount } = render(
40-
<SpreadSheet data={data} columns={columns} options={{ showCopyWithHeader: true }} />
40+
<SpreadSheet data={data} columns={columns} options={{ copyTypes: ['copyData'] }} />
4141
);
4242
const cell = getByText('zhangsan');
4343
fireEvent.contextMenu(cell);
44-
const copyBtn = getByText('复制值以及列名');
44+
const copyBtn = getByText('复制值');
45+
expect(copyBtn).toBeInTheDocument();
46+
fireEvent.click(copyBtn);
47+
unmount();
48+
});
49+
50+
test('copy value with header', () => {
51+
const { getByText, unmount } = render(
52+
<SpreadSheet
53+
data={data}
54+
columns={columns}
55+
options={{ copyTypes: ['copyData', 'copyHeadersAndData'] }}
56+
/>
57+
);
58+
const cell = getByText('zhangsan');
59+
fireEvent.contextMenu(cell);
60+
const copyBtn = getByText('复制列名和值');
4561
expect(copyBtn).toBeInTheDocument();
4662
fireEvent.click(copyBtn);
4763
unmount();

src/spreadSheet/demos/basic.tsx

+30-9
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,35 @@ import { SpreadSheet } from 'dt-react-component';
33

44
export default () => {
55
return (
6-
<SpreadSheet
7-
columns={['name', 'gender', 'age', 'address']}
8-
data={[
9-
['zhangsan', 'male', '20', 'xihu'],
10-
['lisi', 'male', '18', 'yuhang'],
11-
[' 前面有空格', '后面有空格 ', '中间有 空 格', 'yuhang'],
12-
]}
13-
options={{ trimWhitespace: false }}
14-
/>
6+
<>
7+
<span>右键菜单:复制值、复制列名</span>
8+
<SpreadSheet
9+
columns={['name', 'gender', 'age', 'address']}
10+
data={[
11+
['zhangsan', 'male', '20', 'xihu'],
12+
['lisi', 'male', '18', 'yuhang'],
13+
[' 前面有空格', '后面有空格 ', '中间有 空 格', 'yuhang'],
14+
]}
15+
options={{
16+
trimWhitespace: false,
17+
copyTypes: ['copyData', 'copyHeaders'],
18+
}}
19+
/>
20+
21+
<br />
22+
<span>右键菜单:复制值、复制列名、复制列名和值</span>
23+
<SpreadSheet
24+
columns={['name', 'gender', 'age', 'address']}
25+
data={[
26+
['zhangsan', 'male', '20', 'xihu'],
27+
['lisi', 'male', '18', 'yuhang'],
28+
[' 前面有空格', '后面有空格 ', '中间有 空 格', 'yuhang'],
29+
]}
30+
options={{
31+
trimWhitespace: false,
32+
copyTypes: ['copyData', 'copyHeaders', 'copyHeadersAndData'],
33+
}}
34+
/>
35+
</>
1536
);
1637
};

src/spreadSheet/demos/changeData.tsx

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import React, { useRef, useState } from 'react';
2+
import { Button } from 'antd';
3+
import { SpreadSheet } from 'dt-react-component';
4+
5+
export default () => {
6+
const _columns = ['name', 'gender', 'age', 'address'];
7+
const _data = [
8+
['zhangsan', 'male', '20', 'xihu'],
9+
['lisi', 'male', '18', 'yuhang'],
10+
[' 前面有空格', '后面有空格 ', '中间有 空 格', 'yuhang'],
11+
];
12+
const [columns, setColumns] = useState(_columns);
13+
const [data, setData] = useState(_data);
14+
const hotTableInstanceRef = useRef<any>(null);
15+
16+
const handleData = () => {
17+
setData(data?.length === 2 ? _data : _data.slice(0, 2));
18+
};
19+
20+
const handleColumns = () => {
21+
setColumns(columns?.length === 3 ? _columns : _columns.slice(0, 3));
22+
};
23+
24+
const handleRef = () => {
25+
console.log(hotTableInstanceRef?.current?.hotInstance?.getData());
26+
};
27+
28+
return (
29+
<>
30+
<Button style={{ margin: '0 12px 12px 0' }} type="primary" onClick={handleColumns}>
31+
改变列
32+
</Button>
33+
<Button style={{ margin: '0 12px 12px 0' }} type="primary" onClick={handleData}>
34+
改变数据
35+
</Button>
36+
<Button style={{ margin: '0 12px 12px 0' }} type="primary" onClick={handleRef}>
37+
使用 HotTable 示例(打印数据)
38+
</Button>
39+
40+
<SpreadSheet
41+
ref={hotTableInstanceRef}
42+
columns={columns}
43+
data={data}
44+
options={{
45+
trimWhitespace: false,
46+
copyTypes: ['copyData', 'copyHeaders', 'copyHeadersAndData'],
47+
}}
48+
/>
49+
</>
50+
);
51+
};

src/spreadSheet/index.md

+10-9
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: SpreadSheet 多功能表
33
group: 组件
44
toc: content
55
demo:
6-
cols: 2
6+
cols: 1
77
---
88

99
# SpreadSheet 多功能表
@@ -14,16 +14,17 @@ demo:
1414

1515
## 示例
1616

17-
<code src="./demos/basic.tsx">基础使用</code>
17+
<code src="./demos/basic.tsx" title="基础使用"></code>
18+
<code src="./demos/changeData.tsx" title="改变数据"></code>
1819

1920
## API
2021

2122
### SpreadSheet
2223

23-
| 参数 | 说明 | 类型 | 默认值 |
24-
| -------------------------- | -------------------------------------- | ----------------- | ------ |
25-
| data | 表格数据 | `Array(二维数组)` | - |
26-
| columns | 列名 | `Array` | - |
27-
| className | 外层组件的 class 名 | `string` | - |
28-
| options.showCopyWithHeader | 右键菜单中是否展示“复制值以及列名”按钮 | `boolean` | - |
29-
| options.trimWhitespace | 是否去除内容里的空格 | `boolean` | true |
24+
| 参数 | 说明 | 类型 | 默认值 |
25+
| ---------------------- | ------------------------------------------------------ | ------------------------------------------------------------ | -------------- |
26+
| data | 表格数据 | `Array(二维数组)` | - |
27+
| columns | 列名 | `Array` | - |
28+
| className | 外层组件的 class 名 | `string` | - |
29+
| options.copyTypes | 右键菜单中展示的选项 复制值/复制列名/复制列名和值 按钮 | `Array<'copyData' \| 'copyHeaders' \| 'copyHeadersAndData'>` | "['copyData']" |
30+
| options.trimWhitespace | 是否去除内容里的空格 | `boolean` | true |

src/spreadSheet/index.tsx

+89-44
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ import 'handsontable/languages/zh-CN.js';
88
import 'handsontable/dist/handsontable.full.css';
99
import './style.scss';
1010

11+
type ICopyType = 'copyData' | 'copyHeaders' | 'copyHeadersAndData';
12+
1113
type IOptions = HotTableProps & {
12-
/** 是否展示复制值以及列名 */
13-
showCopyWithHeader?: boolean;
14+
// 右键菜单中展示的选项 复制值/复制列名/复制列名和值 按钮 */
15+
copyTypes?: ICopyType[];
1416
};
1517

1618
export interface ISpreadSheetProps {
@@ -33,10 +35,8 @@ const SpreadSheet: React.FC<ISpreadSheetProps> = forwardRef(
3335
const tableRef = useRef<any>(null);
3436
const copyUtils = new CopyUtils();
3537
const _timer = useRef<NodeJS.Timeout>();
36-
const { showCopyWithHeader, ...restProps } = options || {};
37-
useImperativeHandle(ref, () => ({
38-
tableRef,
39-
}));
38+
const { copyTypes = [], ...restProps } = options || {};
39+
useImperativeHandle(ref, () => tableRef.current);
4040
useEffect(() => {
4141
if (tableRef.current) {
4242
removeRenderClock();
@@ -53,7 +53,7 @@ const SpreadSheet: React.FC<ISpreadSheetProps> = forwardRef(
5353
clearTimeout(_timer.current);
5454
};
5555

56-
const getData = () => {
56+
const getShowData = () => {
5757
let showData = data;
5858
if (!showData?.length) {
5959
const emptyArr = new Array(columns.length).fill('', 0, columns.length);
@@ -75,56 +75,101 @@ const SpreadSheet: React.FC<ISpreadSheetProps> = forwardRef(
7575
}
7676
};
7777

78-
const beforeCopy = (arr: any[]) => {
79-
/**
80-
* 去除格式化
81-
*/
78+
/**
79+
* 去除格式化
80+
*/
81+
const beforeCopy = (arr: Array<Array<any>>) => {
8282
const value = arr
8383
.map((row: any[]) => {
8484
return row.join('\t');
8585
})
8686
.join('\n');
87+
8788
copyUtils.copy(value);
8889
return false;
8990
};
9091

9192
const getContextMenu = () => {
92-
const items: Record<string, { name: string; callback: Function }> = {
93-
copy: {
94-
name: '复制',
95-
callback: function (this: any, _key: any) {
96-
const indexArr = this.getSelected();
97-
// eslint-disable-next-line prefer-spread
98-
const copyDataArr = this.getData.apply(this, indexArr[0]);
99-
beforeCopy(copyDataArr);
100-
},
101-
},
93+
// 获取值
94+
const getCopyData = () => {
95+
// 调用的是 handsontable 的方法(在 handsontable.d.ts)
96+
const selectedIndexArr = tableRef.current?.hotInstance?.getSelected();
97+
let dataArr: Array<any> = [];
98+
99+
if (Array.isArray(selectedIndexArr)) {
100+
selectedIndexArr.forEach((arr, index) => {
101+
const [r, c, r2, c2] = arr || [];
102+
const colData: [] =
103+
tableRef.current?.hotInstance?.getData(r, c, r2, c2) || [];
104+
if (index === 0) {
105+
dataArr.push(...colData);
106+
} else {
107+
dataArr = dataArr.map((item: any[], index: number) => {
108+
return item.concat(colData[index]);
109+
});
110+
}
111+
});
112+
}
113+
return dataArr;
102114
};
103-
if (showCopyWithHeader) {
104-
const copyWithHeaderItem = {
105-
name: '复制值以及列名',
106-
callback: function (this: any, _key: any, selection: any) {
107-
const indexArr = this.getSelected();
108-
// eslint-disable-next-line prefer-spread
109-
let copyDataArr = this.getData.apply(this, indexArr[0]);
110-
const columnStart = selection?.[0]?.start?.col;
111-
const columnEnd = selection?.[0]?.end?.col;
112-
let columnArr;
115+
// 获取列名
116+
const getCopyHeaders = (selection: Array<any>) => {
117+
let headerArr: Array<any> = [];
118+
if (Array.isArray(selection)) {
119+
selection.forEach((it) => {
120+
const columnStart = it.start?.col;
121+
const columnEnd = it.end?.col;
113122
if (columnStart !== undefined && columnEnd !== undefined) {
114-
columnArr = columns.slice(columnStart, columnEnd + 1);
115-
}
116-
if (columnArr) {
117-
copyDataArr = [columnArr, ...copyDataArr];
123+
headerArr = headerArr.concat(columns.slice(columnStart, columnEnd + 1));
118124
}
119-
beforeCopy(copyDataArr);
120-
},
121-
};
122-
// 目前版本不支持 copy_with_column_headers 暂时用 cut 代替,以达到与copy类似的表现
123-
items['cut'] = copyWithHeaderItem;
125+
});
126+
}
127+
return headerArr;
128+
};
129+
130+
const copyDataItem = {
131+
name: '复制值',
132+
callback: function (_key: string) {
133+
const copyDataArr = getCopyData();
134+
beforeCopy(copyDataArr);
135+
},
136+
};
137+
const copyHeadersItem = {
138+
name: '复制列名',
139+
callback: function (_key: string, selection: Array<any>) {
140+
const copyHeaders = getCopyHeaders(selection);
141+
beforeCopy([copyHeaders]);
142+
},
143+
};
144+
const copyHeadersAndDataItem = {
145+
name: '复制列名和值',
146+
callback: function (_key: string, selection: Array<any>) {
147+
const copyDataArr = getCopyData();
148+
const copyHeaders = getCopyHeaders(selection);
149+
beforeCopy([copyHeaders, ...copyDataArr]);
150+
},
151+
};
152+
153+
// 目前 items 在 https://github.com/handsontable/handsontable/blob/6.2.2/handsontable.d.ts#L779,自定义方法也可以被执行
154+
const items: Partial<Record<ICopyType, any>> = {};
155+
if (Array.isArray(copyTypes) && copyTypes?.length) {
156+
// 复制值
157+
if (copyTypes.includes('copyData')) {
158+
items['copyData'] = copyDataItem;
159+
}
160+
// 复制列名
161+
if (copyTypes.includes('copyHeaders')) {
162+
items['copyHeaders'] = copyHeadersItem;
163+
}
164+
// 复制列名和值
165+
if (copyTypes.includes('copyHeadersAndData')) {
166+
items['copyHeadersAndData'] = copyHeadersAndDataItem;
167+
}
168+
} else {
169+
items['copyData'] = copyDataItem;
124170
}
125-
return {
126-
items,
127-
} as any;
171+
172+
return { items } as any;
128173
};
129174

130175
return (
@@ -143,7 +188,7 @@ const SpreadSheet: React.FC<ISpreadSheetProps> = forwardRef(
143188
: columns?.[index as number];
144189
return `<span title="${title}">${title}</span>`;
145190
}}
146-
data={getData()}
191+
data={getShowData()}
147192
mergeCells={getMergeCells()}
148193
cell={getCell()}
149194
readOnly

0 commit comments

Comments
 (0)