From dac72dfd58c47ec46cfa7d00e9ae13365c81edfa Mon Sep 17 00:00:00 2001 From: chaishi Date: Tue, 5 Jul 2022 22:11:23 +0800 Subject: [PATCH 1/3] fix(table): null is an object --- src/table/EditableCell.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/table/EditableCell.tsx b/src/table/EditableCell.tsx index 9df14c829..60c53ab12 100644 --- a/src/table/EditableCell.tsx +++ b/src/table/EditableCell.tsx @@ -97,7 +97,7 @@ const EditableCell = (props: EditableCellProps) => { if (!isSame(args[0].value, get(row, col.colKey))) { outsideAbortEvent?.(...args); } - // 此处必须在事件执行完成后异步销毁编辑组件,否则会导致事件清楚不及时引起的其他问题 + // 此处必须在事件执行完成后异步销毁编辑组件,否则会导致事件清除不及时引起的其他问题 const timer = setTimeout(() => { setIsEdit(false); clearTimeout(timer); @@ -165,7 +165,7 @@ const EditableCell = (props: EditableCellProps) => { useEffect(() => { let val = get(row, col.colKey); - if (typeof val === 'object') { + if (typeof val === 'object' && val !== null) { val = val instanceof Array ? [...val] : { ...val }; } setEditValue(val); @@ -218,7 +218,6 @@ const EditableCell = (props: EditableCellProps) => { tips={errorMessage} {...componentProps} {...listeners} - // @ts-ignore value={editValue} onChange={onEditChange} /> From 2d7d28a8a81a275a4e9ea5327ca368ac242b2e9a Mon Sep 17 00:00:00 2001 From: chaishi Date: Tue, 5 Jul 2022 22:54:36 +0800 Subject: [PATCH 2/3] =?UTF-8?q?feat(table):=20=E6=A0=91=E5=BD=A2=E7=BB=93?= =?UTF-8?q?=E6=9E=84=E6=94=AF=E6=8C=81=E6=87=92=E5=8A=A0=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/table/_example/tree.jsx | 122 ++++++++++++++++++++++++++------ src/table/hooks/tree-store.ts | 75 +++++++++++++------- src/table/hooks/useTreeData.tsx | 10 +-- 3 files changed, 156 insertions(+), 51 deletions(-) diff --git a/src/table/_example/tree.jsx b/src/table/_example/tree.jsx index 100edd64e..643610b29 100644 --- a/src/table/_example/tree.jsx +++ b/src/table/_example/tree.jsx @@ -1,23 +1,28 @@ import React, { useState, useRef } from 'react'; -import { EnhancedTable, MessagePlugin, Button, Popconfirm, Checkbox, Space } from 'tdesign-react'; -import { MoveIcon, ChevronRightIcon, ChevronDownIcon } from 'tdesign-icons-react'; +import { EnhancedTable, MessagePlugin, Button, Popconfirm, Checkbox, Space, Loading } from 'tdesign-react'; +import { ChevronRightIcon, ChevronDownIcon, MoveIcon, AddRectangleIcon, MinusRectangleIcon } from 'tdesign-icons-react'; +import { useMemo } from 'react'; + +function getObject(i, currentPage) { + return { + id: i, + key: `我是 ${i}_${currentPage} 号`, + platform: i % 2 === 0 ? '共有' : '私有', + type: ['String', 'Number', 'Array', 'Object'][i % 4], + default: ['-', '0', '[]', '{}'][i % 4], + detail: { + position: `读取 ${i} 个数据的嵌套信息值`, + }, + needed: i % 4 === 0 ? '是' : '否', + description: '数据源', + }; +} function getData(currentPage = 1) { const data = []; const pageInfo = `第 ${currentPage} 页`; for (let i = 0; i < 5; i++) { - const obj = { - id: i, - key: `我是 ${i}_${currentPage} 号(${pageInfo})`, - platform: i % 2 === 0 ? '共有' : '私有', - type: ['String', 'Number', 'Array', 'Object'][i % 4], - default: ['-', '0', '[]', '{}'][i % 4], - detail: { - position: `读取 ${i} 个数据的嵌套信息值`, - }, - needed: i % 4 === 0 ? '是' : '否', - description: '数据源', - }; + const obj = getObject(i, currentPage); // 第一行不设置子节点 obj.list = new Array(2).fill(null).map((t, j) => { const secondIndex = 100 * j + (i + 1) * 10; @@ -42,12 +47,27 @@ function getData(currentPage = 1) { } data.push(obj); } + // 懒加载1 + data.push({ + ...getObject(66666, currentPage), + /** 如果子节点为懒加载,则初始值设置为 true */ + list: true, + key: '我是懒加载节点 66666,点我体验', + }); + // 懒加载2 + data.push({ + ...getObject(88888, currentPage), + /** 如果子节点为懒加载,则初始值设置为 true */ + list: true, + key: '我是懒加载节点 88888,点我体验 ', + }); return data; } export default function TableTree() { const table = useRef(null); const [data, setData] = useState(getData()); + const [lazyLoadingData, setLazyLoadingData] = useState(null); const [expandAll, setExpandAll] = useState(false); const [customTreeExpandAndFoldIcon, setCustomTreeExpandAndFoldIcon] = useState(false); const [pagination, setPagination] = useState({ @@ -85,17 +105,40 @@ export default function TableTree() { }; const appendTo = (row) => { - console.log(table.current); - const randomKey = Math.round(Math.random() * Math.random() * 1000) + 10000; + const randomKey1 = Math.round(Math.random() * Math.random() * 1000) + 10000; table.current.appendTo(row.key, { - id: randomKey, - key: `我是 ${randomKey} 号`, + id: randomKey1, + key: `我是 ${randomKey1} 号`, platform: '私有', type: 'Number', }); - MessagePlugin.success(`已插入子节点我是 ${randomKey} 号,请展开查看`); + MessagePlugin.success(`已插入子节点我是 ${randomKey1} 号,请展开查看`); + + // 一次性添加多个子节点。示例代码有效,勿删!!! + // appendMultipleDataTo(row); }; + function appendMultipleDataTo(row) { + const randomKey1 = Math.round(Math.random() * Math.random() * 1000) + 10000; + const randomKey2 = Math.round(Math.random() * Math.random() * 1000) + 10000; + const newData = [ + { + id: randomKey1, + key: `我是 ${randomKey1} 号`, + platform: '私有', + type: 'Number', + }, + { + id: randomKey2, + key: `我是 ${randomKey2} 号`, + platform: '私有', + type: 'Number', + }, + ]; + table.current.appendTo(row.key, newData); + MessagePlugin.success(`已插入子节点我是 ${randomKey1} 和 ${randomKey2} 号,请展开查看`); + } + // 当前节点之前,新增兄弟节前 const insertBefore = (row) => { const randomKey = Math.round(Math.random() * Math.random() * 1000) + 10000; @@ -193,7 +236,7 @@ export default function TableTree() { const rowData = table.current.getData(id); table.current.toggleExpandData(rowData); // 或者 - // this.$refs.table.toggleExpandData({ rowIndex: rowData.rowIndex, row: rowData.row }); + // table.current.toggleExpandData({ rowIndex: rowData.rowIndex, row: rowData.row }); }); }; @@ -231,6 +274,42 @@ export default function TableTree() { setData(getData(pageInfo.current)); }; + // 懒加载图标渲染 + function lazyLoadingTreeIconRender(params) { + const { type, row } = params; + if (lazyLoadingData?.id === row?.id) { + return ; + } + return type === 'expand' ? : ; + } + + const treeExpandIconRender = useMemo(() => { + // 懒加载图标渲染 + if (lazyLoadingData) return lazyLoadingTreeIconRender; + // 自定义展开图标 + if (customTreeExpandAndFoldIcon) return renderTreeExpandAndFoldIcon; + return undefined; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [lazyLoadingData, customTreeExpandAndFoldIcon]); + + function onTreeExpandChange(context) { + console.log(context.rowState.expanded ? '展开' : '收起', context); + /** + * 如果是懒加载,请确认自己完成了以下几个步骤 + * 1. 提前设置 children 值为 true; + * 2. 在 onTreeExpandChange 事件中处理异步数据; + * 3. 自定义展开图标渲染 lazyLoadingTreeIconRender + */ + if (context.row.list === true) { + setLazyLoadingData(context.row); + const timer = setTimeout(() => { + appendMultipleDataTo(context.row); + setLazyLoadingData(null); + clearTimeout(timer); + }, 200); + } + } + return ( @@ -265,9 +344,10 @@ export default function TableTree() { columns={columns} tree={{ childrenKey: 'list', treeNodeColumnIndex: 2 /** , defaultExpandAll: true */ }} dragSort="row-handler" - treeExpandAndFoldIcon={customTreeExpandAndFoldIcon ? renderTreeExpandAndFoldIcon : undefined} + treeExpandAndFoldIcon={treeExpandIconRender} pagination={pagination} onPageChange={onPageChange} + onTreeExpandChange={onTreeExpandChange} > {/* diff --git a/src/table/hooks/tree-store.ts b/src/table/hooks/tree-store.ts index d7c055d84..a1e822623 100644 --- a/src/table/hooks/tree-store.ts +++ b/src/table/hooks/tree-store.ts @@ -84,6 +84,9 @@ class TableTreeStore { log.error('EnhancedTable', '`rowKey` could be wrong, can not get rowValue from `data` by `rowKey`.'); return []; } + const childrenNodes = get(p.row, keys.childrenKey); + // childrenNodes = true,表示懒加载,直接返回,暂时不做展开处理 + if (childrenNodes === true) return dataSource; const r = this.treeDataMap.get(rowValue); r.rowIndex = p.rowIndex; r.expanded = !r.expanded; @@ -194,52 +197,73 @@ class TableTreeStore { } /** - * 为当前节点添加子节点,默认添加到最后一个节点 + * 为当前节点添加子节点,默认添加到最后一个节点。允许添加单个或多个 * @param rowValue 当前节点唯一标识 * @param newData 待添加的新节点 */ - appendTo(rowValue: string | number, newData: T, dataSource: T[], keys: KeysType): T[] { + appendTo(rowValue: string | number, newData: T | T[], dataSource: T[], keys: KeysType): T[] { const state = this.treeDataMap.get(rowValue); if (!this.validateDataExist(state, rowValue)) return dataSource; - const newRowValue = get(newData, keys.rowKey); - const mapState = this.treeDataMap.get(newRowValue); - if (!this.validateDataDoubleExist(mapState, newRowValue)) return dataSource; const children: T[] = get(state.row, keys.childrenKey); // 子节点不存在,则表示为叶子节点 const isShowNewNode = state.expanded || !children?.length; - const rowIndex = isShowNewNode ? state.rowIndex + (state.expandChildrenLength || 0) + 1 : -1; - const newState = { - id: newRowValue, - row: newData, - rowIndex, - level: state.level + 1, - expanded: false, - expandChildrenLength: 0, - disabled: false, - path: [...state.path], - parent: state, - }; - newState.path = newState.path.concat(newState); + // 添加多个子节点时,需去除重复子节点 + const tmpData = newData instanceof Array ? newData : [newData]; + const newChildrenData: T[] = []; + const newChildrenStates: TableRowState[] = []; + let firstNewChildrenIndex = -1; + for (let i = 0, len = tmpData.length; i < len; i++) { + const oneData = tmpData[i]; + const newRowValue = get(oneData, keys.rowKey); + const mapState = this.treeDataMap.get(newRowValue); + if (!this.validateDataDoubleExist(mapState, newRowValue)) { + log.warn('Table', `Duplicated Data \`${newRowValue}\` has been removed.`); + } else { + const rowIndex = isShowNewNode ? state.rowIndex + (state.expandChildrenLength || 0) + (i + 1) : -1; + if (i === 0) { + firstNewChildrenIndex = rowIndex; + } + const newState = { + id: newRowValue, + row: oneData, + rowIndex, + level: state.level + 1, + expanded: false, + expandChildrenLength: 0, + disabled: false, + path: [...state.path], + parent: state, + }; + newState.path = newState.path.concat(newState); + newChildrenData.push(oneData); + newChildrenStates.push(newState); + this.treeDataMap.set(newRowValue, newState); + } + } + if (!newChildrenData.length) return dataSource; + if (children?.length) { - state.row[keys.childrenKey].push(newData); + state.row[keys.childrenKey] = state.row[keys.childrenKey].concat(newChildrenData); } else { - state.row[keys.childrenKey] = [newData]; + state.row[keys.childrenKey] = newChildrenData; state.expanded = true; } - this.treeDataMap.set(newRowValue, newState); + // 如果当前节点为展开状态,则需要继续处理 if (isShowNewNode) { - dataSource.splice(newState.rowIndex, 0, newData); + dataSource.splice(firstNewChildrenIndex, 0, ...newChildrenData); // 更新父元素及祖先元素展开子节点的数量 - updateRowExpandLength(this.treeDataMap, state.row, 1, 'insert', { + const newChildrenCount = newChildrenData.length || 1; + updateRowExpandLength(this.treeDataMap, state.row, newChildrenCount, 'insert', { rowKey: keys.rowKey, childrenKey: keys.childrenKey, }); // 更新 rowIndex 之后的下标 updateRowIndex(this.treeDataMap, dataSource, { - minRowIndex: newState.rowIndex, + minRowIndex: firstNewChildrenIndex, rowKey: keys.rowKey, type: 'add', + count: newChildrenData.length, }); } @@ -681,6 +705,7 @@ export function updateRowIndex( minRowIndex?: number; maxRowIndex?: number; type?: 'add' | 'remove'; + count?: number; }, ) { const start = extra.minRowIndex || 0; @@ -691,6 +716,6 @@ export function updateRowIndex( if (!state) { log.warn('Table', 'tree map went wrong'); } - state.rowIndex = rowIndex; + state.rowIndex = rowIndex + (extra?.count || 1) - 1; } } diff --git a/src/table/hooks/useTreeData.tsx b/src/table/hooks/useTreeData.tsx index 3ca1ee37b..1da9aff49 100644 --- a/src/table/hooks/useTreeData.tsx +++ b/src/table/hooks/useTreeData.tsx @@ -120,15 +120,15 @@ export default function useTreeData(props: TdEnhancedTableProps) { const colStyle = getTreeNodeStyle(currentState?.level); const classes = { [tableTreeClasses.inlineCol]: !!col.ellipsis }; const childrenNodes = get(p.row, rowDataKeys.childrenKey); - if (childrenNodes && childrenNodes instanceof Array) { + if ((childrenNodes && childrenNodes instanceof Array) || childrenNodes === true) { const expanded = store.treeDataMap.get(get(p.row, rowDataKeys.rowKey))?.expanded; - const type = expanded ? 'expand' : 'fold'; + const type = expanded ? 'fold' : 'expand'; const defaultIconNode = t(locale.treeExpandAndFoldIcon, { type }) || (expanded ? : ); - const iconNode = treeExpandAndFoldIcon ? treeExpandAndFoldIcon({ type }) : defaultIconNode; + const iconNode = treeExpandAndFoldIcon ? treeExpandAndFoldIcon({ type, ...p }) : defaultIconNode; return (
- {!!childrenNodes.length && ( + {!!(childrenNodes.length || childrenNodes === true) && ( toggleExpandData({ ...p, trigger: 'inner' })}> {iconNode} @@ -186,7 +186,7 @@ export default function useTreeData(props: TdEnhancedTableProps) { * @param key 当前节点唯一标识 * @param newData 待添加的新节点 */ - function appendTo(key: TableRowValue, newData: T) { + function appendTo(key: TableRowValue, newData: T | T[]) { if (!key) { setDataSource([...store.appendToRoot(newData, dataSource, rowDataKeys)]); return; From 43368388a234be5c6205b984b0dcaa7c43ffc212 Mon Sep 17 00:00:00 2001 From: chaishi Date: Tue, 5 Jul 2022 22:57:13 +0800 Subject: [PATCH 3/3] test(table): update snapshots --- .../__snapshots__/table.test.tsx.snap | 320 +++++++++++++++++- test/ssr/__snapshots__/ssr.test.js.snap | 2 +- 2 files changed, 311 insertions(+), 11 deletions(-) diff --git a/src/table/__tests__/__snapshots__/table.test.tsx.snap b/src/table/__tests__/__snapshots__/table.test.tsx.snap index d01a1783e..a49038341 100644 --- a/src/table/__tests__/__snapshots__/table.test.tsx.snap +++ b/src/table/__tests__/__snapshots__/table.test.tsx.snap @@ -21673,7 +21673,7 @@ exports[`tree.jsx 1`] = ` > - 我是 0_1 号(第 1 页) + 我是 0_1 号
@@ -21801,7 +21801,7 @@ exports[`tree.jsx 1`] = ` - 我是 1_1 号(第 1 页) + 我是 1_1 号 @@ -21951,7 +21951,7 @@ exports[`tree.jsx 1`] = ` - 我是 2_1 号(第 1 页) + 我是 2_1 号 @@ -22101,7 +22101,7 @@ exports[`tree.jsx 1`] = ` - 我是 3_1 号(第 1 页) + 我是 3_1 号 @@ -22251,7 +22251,7 @@ exports[`tree.jsx 1`] = ` - 我是 4_1 号(第 1 页) + 我是 4_1 号 + + + + + 共有 + + +
+ + + + + +
+ +
+
+ + + + + + + + + +
+ 66666 +
+ + +
+
+ + + + + + + 我是懒加载节点 66666,点我体验 +
+
+ + + 共有 + + +
+ + + + + +
+ +
+
+ + + + + + + + + +
+ 88888 +
+ + +
+
+ + + + + + + 我是懒加载节点 88888,点我体验
diff --git a/test/ssr/__snapshots__/ssr.test.js.snap b/test/ssr/__snapshots__/ssr.test.js.snap index bc3dcb83b..c10fa0615 100644 --- a/test/ssr/__snapshots__/ssr.test.js.snap +++ b/test/ssr/__snapshots__/ssr.test.js.snap @@ -814,7 +814,7 @@ exports[`ssr snapshot test renders ./src/table/_example/select-single.jsx correc exports[`ssr snapshot test renders ./src/table/_example/single-sort.jsx correctly 1`] = `"
排序方式:{"sortBy":"status","descending":true}
集群名称
状态
存活时间(s)
管理员
JQTest1

健康

1000jenny;peter
JQTest2

警告

1000jenny
JQTest3

异常

500jenny
JQTest4

警告

1500peter
"`; -exports[`ssr snapshot test renders ./src/table/_example/tree.jsx correctly 1`] = `"
Total 100 items
请选择
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
"`; +exports[`ssr snapshot test renders ./src/table/_example/tree.jsx correctly 1`] = `"
Total 100 items
请选择
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
"`; exports[`ssr snapshot test renders ./src/table/_example/tree-select.jsx correctly 1`] = `"
"`;