diff --git a/CHANGELOG.md b/CHANGELOG.md index 054fa8d5c..972d0a19f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,7 +37,6 @@ All notable changes to this project will be documented in this file. See [standa ### Bug Fixes - 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)) - > > > > > > > 272326794683ecd3ed1846ad38fd4258b23dc0f7 ## [4.2.0](https://github.com/DTStack/dt-react-component/compare/v4.1.0...v4.2.0) (2023-08-24) diff --git a/docs/guide/migration-v4.md b/docs/guide/migration-v4.md index 614e8cc60..7a6962041 100644 --- a/docs/guide/migration-v4.md +++ b/docs/guide/migration-v4.md @@ -150,9 +150,10 @@ useCookieListener( - 新增 `observerEle` 属性,支持自定义监听元素。 -#### SpreadSheet [#325](https://github.com/DTStack/dt-react-component/pull/325) +#### SpreadSheet [#325](https://github.com/DTStack/dt-react-component/pull/325)、[#545](https://github.com/DTStack/dt-react-component/pull/545) - 新增 `className` 属性,可自定义外层组件的 class 名。 +- 删除 `showCopyWithHeader` 属性,使用 `copyTypes` 属性代替,值为数组,可传入 'copyData'、'copyHeaders'、'copyHeadersAndData',分别代表的功能为:复制值、复制列名、复制列名和值。 #### KeyEventListener [#326](https://github.com/DTStack/dt-react-component/pull/326) diff --git a/src/spreadSheet/__tests__/index.test.tsx b/src/spreadSheet/__tests__/index.test.tsx index 9c147915f..83f304ab9 100644 --- a/src/spreadSheet/__tests__/index.test.tsx +++ b/src/spreadSheet/__tests__/index.test.tsx @@ -37,7 +37,7 @@ describe('test spreadSheet ', () => { const { getByText, unmount } = render(); const cell = getByText('zhangsan'); fireEvent.contextMenu(cell); - const copyBtn = getByText('复制'); + const copyBtn = getByText('复制值'); expect(copyBtn).toBeInTheDocument(); fireEvent.click(copyBtn); unmount(); @@ -45,11 +45,27 @@ describe('test spreadSheet ', () => { test('copy value with header', () => { const { getByText, unmount } = render( - + ); const cell = getByText('zhangsan'); fireEvent.contextMenu(cell); - const copyBtn = getByText('复制值以及列名'); + const copyBtn = getByText('复制值'); + expect(copyBtn).toBeInTheDocument(); + fireEvent.click(copyBtn); + unmount(); + }); + + test('copy value with header', () => { + const { getByText, unmount } = render( + + ); + const cell = getByText('zhangsan'); + fireEvent.contextMenu(cell); + const copyBtn = getByText('复制列名和值'); expect(copyBtn).toBeInTheDocument(); fireEvent.click(copyBtn); unmount(); diff --git a/src/spreadSheet/demos/basic.tsx b/src/spreadSheet/demos/basic.tsx index 5e308a4a1..e2c984980 100644 --- a/src/spreadSheet/demos/basic.tsx +++ b/src/spreadSheet/demos/basic.tsx @@ -3,14 +3,35 @@ import { SpreadSheet } from 'dt-react-component'; export default () => { return ( - + <> + 右键菜单:复制值、复制列名 + + +
+ 右键菜单:复制值、复制列名、复制列名和值 + + ); }; diff --git a/src/spreadSheet/demos/changeData.tsx b/src/spreadSheet/demos/changeData.tsx new file mode 100644 index 000000000..dc27ebc72 --- /dev/null +++ b/src/spreadSheet/demos/changeData.tsx @@ -0,0 +1,51 @@ +import React, { useRef, useState } from 'react'; +import { Button } from 'antd'; +import { SpreadSheet } from 'dt-react-component'; + +export default () => { + const _columns = ['name', 'gender', 'age', 'address']; + const _data = [ + ['zhangsan', 'male', '20', 'xihu'], + ['lisi', 'male', '18', 'yuhang'], + [' 前面有空格', '后面有空格 ', '中间有 空 格', 'yuhang'], + ]; + const [columns, setColumns] = useState(_columns); + const [data, setData] = useState(_data); + const hotTableInstanceRef = useRef(null); + + const handleData = () => { + setData(data?.length === 2 ? _data : _data.slice(0, 2)); + }; + + const handleColumns = () => { + setColumns(columns?.length === 3 ? _columns : _columns.slice(0, 3)); + }; + + const handleRef = () => { + console.log(hotTableInstanceRef?.current?.hotInstance?.getData()); + }; + + return ( + <> + + + + + + + ); +}; diff --git a/src/spreadSheet/index.md b/src/spreadSheet/index.md index 06aab0348..12c8c2972 100644 --- a/src/spreadSheet/index.md +++ b/src/spreadSheet/index.md @@ -3,7 +3,7 @@ title: SpreadSheet 多功能表 group: 组件 toc: content demo: - cols: 2 + cols: 1 --- # SpreadSheet 多功能表 @@ -14,16 +14,17 @@ demo: ## 示例 -基础使用 + + ## API ### SpreadSheet -| 参数 | 说明 | 类型 | 默认值 | -| -------------------------- | -------------------------------------- | ----------------- | ------ | -| data | 表格数据 | `Array(二维数组)` | - | -| columns | 列名 | `Array` | - | -| className | 外层组件的 class 名 | `string` | - | -| options.showCopyWithHeader | 右键菜单中是否展示“复制值以及列名”按钮 | `boolean` | - | -| options.trimWhitespace | 是否去除内容里的空格 | `boolean` | true | +| 参数 | 说明 | 类型 | 默认值 | +| ---------------------- | ------------------------------------------------------ | ------------------------------------------------------------ | -------------- | +| data | 表格数据 | `Array(二维数组)` | - | +| columns | 列名 | `Array` | - | +| className | 外层组件的 class 名 | `string` | - | +| options.copyTypes | 右键菜单中展示的选项 复制值/复制列名/复制列名和值 按钮 | `Array<'copyData' \| 'copyHeaders' \| 'copyHeadersAndData'>` | "['copyData']" | +| options.trimWhitespace | 是否去除内容里的空格 | `boolean` | true | diff --git a/src/spreadSheet/index.tsx b/src/spreadSheet/index.tsx index 6086683f9..758935477 100644 --- a/src/spreadSheet/index.tsx +++ b/src/spreadSheet/index.tsx @@ -8,9 +8,11 @@ import CopyUtils from '../utils/copy'; import 'handsontable/dist/handsontable.full.css'; import './style.scss'; +type ICopyType = 'copyData' | 'copyHeaders' | 'copyHeadersAndData'; + type IOptions = HotTableProps & { - /** 是否展示复制值以及列名 */ - showCopyWithHeader?: boolean; + // 右键菜单中展示的选项 复制值/复制列名/复制列名和值 按钮 */ + copyTypes?: ICopyType[]; }; export interface ISpreadSheetProps { @@ -33,10 +35,8 @@ const SpreadSheet: React.FC = forwardRef( const tableRef = useRef(null); const copyUtils = new CopyUtils(); const _timer = useRef(); - const { showCopyWithHeader, ...restProps } = options || {}; - useImperativeHandle(ref, () => ({ - tableRef, - })); + const { copyTypes = [], ...restProps } = options || {}; + useImperativeHandle(ref, () => tableRef.current); useEffect(() => { if (tableRef.current) { removeRenderClock(); @@ -53,7 +53,7 @@ const SpreadSheet: React.FC = forwardRef( clearTimeout(_timer.current); }; - const getData = () => { + const getShowData = () => { let showData = data; if (!showData?.length) { const emptyArr = new Array(columns.length).fill('', 0, columns.length); @@ -75,56 +75,101 @@ const SpreadSheet: React.FC = forwardRef( } }; - const beforeCopy = (arr: any[]) => { - /** - * 去除格式化 - */ + /** + * 去除格式化 + */ + const beforeCopy = (arr: Array>) => { const value = arr .map((row: any[]) => { return row.join('\t'); }) .join('\n'); + copyUtils.copy(value); return false; }; const getContextMenu = () => { - const items: Record = { - copy: { - name: '复制', - callback: function (this: any, _key: any) { - const indexArr = this.getSelected(); - // eslint-disable-next-line prefer-spread - const copyDataArr = this.getData.apply(this, indexArr[0]); - beforeCopy(copyDataArr); - }, - }, + // 获取值 + const getCopyData = () => { + // 调用的是 handsontable 的方法(在 handsontable.d.ts) + const selectedIndexArr = tableRef.current?.hotInstance?.getSelected(); + let dataArr: Array = []; + + if (Array.isArray(selectedIndexArr)) { + selectedIndexArr.forEach((arr, index) => { + const [r, c, r2, c2] = arr || []; + const colData: [] = + tableRef.current?.hotInstance?.getData(r, c, r2, c2) || []; + if (index === 0) { + dataArr.push(...colData); + } else { + dataArr = dataArr.map((item: any[], index: number) => { + return item.concat(colData[index]); + }); + } + }); + } + return dataArr; }; - if (showCopyWithHeader) { - const copyWithHeaderItem = { - name: '复制值以及列名', - callback: function (this: any, _key: any, selection: any) { - const indexArr = this.getSelected(); - // eslint-disable-next-line prefer-spread - let copyDataArr = this.getData.apply(this, indexArr[0]); - const columnStart = selection?.[0]?.start?.col; - const columnEnd = selection?.[0]?.end?.col; - let columnArr; + // 获取列名 + const getCopyHeaders = (selection: Array) => { + let headerArr: Array = []; + if (Array.isArray(selection)) { + selection.forEach((it) => { + const columnStart = it.start?.col; + const columnEnd = it.end?.col; if (columnStart !== undefined && columnEnd !== undefined) { - columnArr = columns.slice(columnStart, columnEnd + 1); - } - if (columnArr) { - copyDataArr = [columnArr, ...copyDataArr]; + headerArr = headerArr.concat(columns.slice(columnStart, columnEnd + 1)); } - beforeCopy(copyDataArr); - }, - }; - // 目前版本不支持 copy_with_column_headers 暂时用 cut 代替,以达到与copy类似的表现 - items['cut'] = copyWithHeaderItem; + }); + } + return headerArr; + }; + + const copyDataItem = { + name: '复制值', + callback: function (_key: string) { + const copyDataArr = getCopyData(); + beforeCopy(copyDataArr); + }, + }; + const copyHeadersItem = { + name: '复制列名', + callback: function (_key: string, selection: Array) { + const copyHeaders = getCopyHeaders(selection); + beforeCopy([copyHeaders]); + }, + }; + const copyHeadersAndDataItem = { + name: '复制列名和值', + callback: function (_key: string, selection: Array) { + const copyDataArr = getCopyData(); + const copyHeaders = getCopyHeaders(selection); + beforeCopy([copyHeaders, ...copyDataArr]); + }, + }; + + // 目前 items 在 https://github.com/handsontable/handsontable/blob/6.2.2/handsontable.d.ts#L779,自定义方法也可以被执行 + const items: Partial> = {}; + if (Array.isArray(copyTypes) && copyTypes?.length) { + // 复制值 + if (copyTypes.includes('copyData')) { + items['copyData'] = copyDataItem; + } + // 复制列名 + if (copyTypes.includes('copyHeaders')) { + items['copyHeaders'] = copyHeadersItem; + } + // 复制列名和值 + if (copyTypes.includes('copyHeadersAndData')) { + items['copyHeadersAndData'] = copyHeadersAndDataItem; + } + } else { + items['copyData'] = copyDataItem; } - return { - items, - } as any; + + return { items } as any; }; return ( @@ -143,7 +188,7 @@ const SpreadSheet: React.FC = forwardRef( : columns?.[index as number]; return `${title}`; }} - data={getData()} + data={getShowData()} mergeCells={getMergeCells()} cell={getCell()} readOnly diff --git a/src/utils/copy.tsx b/src/utils/copy.tsx index 896deb616..ac7a4e0bc 100644 --- a/src/utils/copy.tsx +++ b/src/utils/copy.tsx @@ -60,7 +60,10 @@ export default class CopyUtils { let succeeded; try { - succeeded = document.execCommand('copy'); + // 浏览器兼容性处理,当前语法已废弃,延迟处理可以保证复制成功 + setTimeout(() => { + succeeded = document.execCommand('copy'); + }); } catch (err) { succeeded = false; }