diff --git a/.changeset/early-cycles-drum.md b/.changeset/early-cycles-drum.md new file mode 100644 index 000000000..04924fcf9 --- /dev/null +++ b/.changeset/early-cycles-drum.md @@ -0,0 +1,6 @@ +--- +"@hi-ui/table": minor +"@hi-ui/hiui": minor +--- + +feat(table): 支持表头列合并 (#3032) diff --git a/packages/ui/table/src/TheadContent.tsx b/packages/ui/table/src/TheadContent.tsx index 4639b0b1a..d33f99b1e 100644 --- a/packages/ui/table/src/TheadContent.tsx +++ b/packages/ui/table/src/TheadContent.tsx @@ -57,10 +57,17 @@ export const TheadContent = forwardRef } } + const stickyColProps = getStickyColProps(col) + const cell = ( ) + // 当有表头列合并时,将当前列的索引设置为最后一个被合并的列的索引,这样在拖拽时,UI 和交互才更符合直觉 + const index = col.colSpan && col.colSpan > 1 ? colIndex + col.colSpan - 1 : colIndex + return resizable && colIndex !== colWidths.length - 1 ? ( draggableOpts={{ enableUserSelectHack: false }} handle={} height={0} - width={colWidths[colIndex] as number} + width={colWidths[index] as number} onResize={(evt, options) => { - onColumnResizable(evt, options, colIndex) + onColumnResizable(evt, options, index) }} onResizeStop={(evt, options) => { - onResizeStop?.(evt, options?.size, colIndex, colWidths) + onResizeStop?.(evt, options?.size, index, colWidths) }} > {cell} diff --git a/packages/ui/table/src/hooks/use-colgroup.ts b/packages/ui/table/src/hooks/use-colgroup.ts index b040ad96e..8918a82df 100644 --- a/packages/ui/table/src/hooks/use-colgroup.ts +++ b/packages/ui/table/src/hooks/use-colgroup.ts @@ -32,6 +32,7 @@ export const useColumns = ({ columns }: { columns: TableColumnItem[] }) => { title: raw.title, align: raw.align ?? 'left', render: raw.render, + colSpan: raw.colSpan, } }) @@ -62,7 +63,7 @@ export const useColumns = ({ columns }: { columns: TableColumnItem[] }) => { flattedColumns.forEach((column: any) => { if (isLeaf(column)) { column.rowSpan = maxColumnDepth - column.depth + 1 - column.colSpan = 1 + column.colSpan = column.colSpan ?? 1 } else { column.rowSpan = 1 column.colSpan = getLeafChildren(column).length diff --git a/packages/ui/table/src/types.ts b/packages/ui/table/src/types.ts index 0b01a5bd6..d930908a2 100644 --- a/packages/ui/table/src/types.ts +++ b/packages/ui/table/src/types.ts @@ -141,6 +141,10 @@ export type TableColumnItem = { * 多级表头 */ children?: TableColumnItem[] + /** + * 表头列合并,设置为 0 时,不渲染 + */ + colSpan?: number /** * 控制单元格自定义渲染 */ diff --git a/packages/ui/table/stories/col-menu.stories.tsx b/packages/ui/table/stories/col-menu.stories.tsx index 4d593dd4f..d3092c458 100644 --- a/packages/ui/table/stories/col-menu.stories.tsx +++ b/packages/ui/table/stories/col-menu.stories.tsx @@ -17,13 +17,12 @@ export const ColMenu = () => { dataKey: 'age', key: 2, width: 80, - sorter(pre, next) { + sorter(pre: any, next: any) { return pre.raw.age - next.raw.age }, }, { title: 'Home phone', - colSpan: 2, width: 180, dataKey: 'tel', key: 3, @@ -36,7 +35,6 @@ export const ColMenu = () => { }, { title: 'Home phone2', - colSpan: 2, width: 180, dataKey: 'tel2', key: 5, @@ -44,63 +42,54 @@ export const ColMenu = () => { { title: 'Home phone3', width: 180, - colSpan: 2, dataKey: 'tel3', key: 6, }, { title: 'Home phone4', width: 180, - colSpan: 2, dataKey: 'tel4', key: 7, }, { title: 'Home phone5', width: 180, - colSpan: 2, dataKey: 'tel5', key: 8, }, { title: 'Home phone6', width: 180, - colSpan: 2, dataKey: 'tel6', key: 9, }, { title: 'Home phone7', width: 180, - colSpan: 2, dataKey: 'tel7', key: 10, }, { title: 'Home phone8', width: 180, - colSpan: 2, dataKey: 'tel8', key: 11, }, { title: 'Home phone9', width: 180, - colSpan: 2, dataKey: 'tel9', key: 12, }, { title: 'Home phone10', width: 180, - colSpan: 2, dataKey: 'tel10', key: 13, }, { title: 'Home phone11', width: 180, - colSpan: 2, dataKey: 'tel11', key: 14, }, @@ -108,7 +97,6 @@ export const ColMenu = () => { { title: 'Home phone12', width: 180, - colSpan: 2, dataKey: 'tel12', key: 15, }, @@ -118,7 +106,7 @@ export const ColMenu = () => { dataKey: 'phone', width: 180, key: 16, - sorter(pre, next) { + sorter(pre: any, next: any) { return pre.phone - next.phone }, }, @@ -232,14 +220,6 @@ export const ColMenu = () => { }, ]) - const onHighlightedCol = (changedColInfo, highlightedColKeys) => { - console.log(changedColInfo, highlightedColKeys) - } - - const onChange = (pagination, sorter, extra) => { - console.log(pagination, sorter, extra) - } - return ( <>

ColMenu for Table

@@ -248,8 +228,12 @@ export const ColMenu = () => { columns={columns} data={data} showColMenu - onHighlightedCol={onHighlightedCol} - onChange={onChange} + onHighlightedCol={(changedColInfo, highlightedColKeys) => { + console.log(changedColInfo, highlightedColKeys) + }} + onChange={(sorter, extra) => { + console.log(sorter, extra) + }} /> diff --git a/packages/ui/table/stories/custom-filter.stories.tsx b/packages/ui/table/stories/custom-filter.stories.tsx index a5fd228f9..815f94624 100644 --- a/packages/ui/table/stories/custom-filter.stories.tsx +++ b/packages/ui/table/stories/custom-filter.stories.tsx @@ -196,7 +196,6 @@ export const CustomFilter = () => { /> ), - colSpan: 2, width: 180, render(text) { return {text} @@ -215,7 +214,6 @@ export const CustomFilter = () => { }, { title: 'Home phone2', - colSpan: 2, width: 180, render(text) { return {text} @@ -229,7 +227,6 @@ export const CustomFilter = () => { render(text) { return {text} }, - colSpan: 2, dataKey: 'tel3', key: 6, }, @@ -239,7 +236,6 @@ export const CustomFilter = () => { render(text) { return {text} }, - colSpan: 2, dataKey: 'tel4', key: 7, }, @@ -249,7 +245,6 @@ export const CustomFilter = () => { render(text) { return {text} }, - colSpan: 2, dataKey: 'tel5', key: 8, }, @@ -259,7 +254,6 @@ export const CustomFilter = () => { render(text) { return {text} }, - colSpan: 2, dataKey: 'tel6', key: 9, }, @@ -269,7 +263,6 @@ export const CustomFilter = () => { render(text) { return {text} }, - colSpan: 2, dataKey: 'tel7', key: 10, }, @@ -279,7 +272,6 @@ export const CustomFilter = () => { render(text) { return {text} }, - colSpan: 2, dataKey: 'tel8', key: 11, }, @@ -289,7 +281,6 @@ export const CustomFilter = () => { render(text) { return {text} }, - colSpan: 2, dataKey: 'tel9', key: 12, }, @@ -299,7 +290,6 @@ export const CustomFilter = () => { render(text) { return {text} }, - colSpan: 2, dataKey: 'tel10', key: 13, }, @@ -309,7 +299,6 @@ export const CustomFilter = () => { render(text) { return {text} }, - colSpan: 2, dataKey: 'tel11', key: 14, }, @@ -320,7 +309,6 @@ export const CustomFilter = () => { render(text) { return {text} }, - colSpan: 2, dataKey: 'tel12', key: 15, }, diff --git a/packages/ui/table/stories/data-sorter.stories.tsx b/packages/ui/table/stories/data-sorter.stories.tsx index a5a06a530..7db12381d 100644 --- a/packages/ui/table/stories/data-sorter.stories.tsx +++ b/packages/ui/table/stories/data-sorter.stories.tsx @@ -23,7 +23,6 @@ export const DataSorter = () => { }, { title: 'Home phone', - colSpan: 2, width: 180, dataKey: 'tel', key: 3, @@ -36,7 +35,6 @@ export const DataSorter = () => { }, { title: 'Home phone2', - colSpan: 2, width: 180, dataKey: 'tel2', key: 5, @@ -44,63 +42,54 @@ export const DataSorter = () => { { title: 'Home phone3', width: 180, - colSpan: 2, dataKey: 'tel3', key: 6, }, { title: 'Home phone4', width: 180, - colSpan: 2, dataKey: 'tel4', key: 7, }, { title: 'Home phone5', width: 180, - colSpan: 2, dataKey: 'tel5', key: 8, }, { title: 'Home phone6', width: 180, - colSpan: 2, dataKey: 'tel6', key: 9, }, { title: 'Home phone7', width: 180, - colSpan: 2, dataKey: 'tel7', key: 10, }, { title: 'Home phone8', width: 180, - colSpan: 2, dataKey: 'tel8', key: 11, }, { title: 'Home phone9', width: 180, - colSpan: 2, dataKey: 'tel9', key: 12, }, { title: 'Home phone10', width: 180, - colSpan: 2, dataKey: 'tel10', key: 13, }, { title: 'Home phone11', width: 180, - colSpan: 2, dataKey: 'tel11', key: 14, }, @@ -108,7 +97,6 @@ export const DataSorter = () => { { title: 'Home phone12', width: 180, - colSpan: 2, dataKey: 'tel12', key: 15, }, diff --git a/packages/ui/table/stories/header-colspan.stories.tsx b/packages/ui/table/stories/header-colspan.stories.tsx new file mode 100644 index 000000000..abcd9fb8a --- /dev/null +++ b/packages/ui/table/stories/header-colspan.stories.tsx @@ -0,0 +1,109 @@ +import React from 'react' +import Table from '../src' + +/** + * @title 表头列合并 + * @desc 只支持表头列合并,被合并的表头需要设置 colSpan 为 0,则该表头不显示 + */ +export const HeaderColSpan = () => { + return ( + <> +

Header ColSpan

+
+ { + console.log(text, row) + return text + '*' + }, + }, + { + title: '品类 + 型号', + dataKey: 'type', + align: 'center', + width: 100, + colSpan: 2, + }, + { + title: '规格', + dataKey: 'size', + width: 100, + colSpan: 0, + }, + { + title: '单价', + dataKey: 'price', + width: 100, + }, + { + title: '门店 + 库存', + dataKey: 'address', + align: 'center', + width: 100, + colSpan: 2, + }, + { + title: '库存', + dataKey: 'stock', + width: 100, + colSpan: 0, + }, + ]} + data={[ + { + name: '小米9', + type: '手机', + size: '6G+64G', + price: '3299.00', + address: '华润五彩城店', + stock: '29,000', + key: 1, + }, + { + name: '小米9 SE', + type: '手机', + size: '6G+64G 幻彩蓝', + price: '1999.00', + address: '清河店', + stock: '10,000', + key: 2, + }, + { + name: '小米8', + type: '手机', + size: '6G+64G 幻彩蓝', + price: '2599.00', + address: '双安店', + stock: '12,000', + key: 3, + }, + { + name: 'Redmi Note7', + type: '手机', + size: '6G+64G 幻彩蓝', + price: '999.00', + address: '华润五彩城店', + stock: '140,000', + key: 4, + }, + { + name: '小米8 SE', + type: '手机', + size: '6G+64G 幻彩蓝', + price: '699.00', + address: '双安店', + stock: '12,000', + key: 5, + }, + ]} + /> + + + ) +} diff --git a/packages/ui/table/stories/index.stories.tsx b/packages/ui/table/stories/index.stories.tsx index 32b860901..d5935dc13 100644 --- a/packages/ui/table/stories/index.stories.tsx +++ b/packages/ui/table/stories/index.stories.tsx @@ -19,6 +19,7 @@ export * from './striped.stories' export * from './bordered.stories' export * from './size.stories' export * from './draggable.stories' +export * from './header-colspan.stories' export * from './group-header.stories' export * from './fixed-header.stories' export * from './sticky-header.stories'