diff --git a/docs/en-US/component/table.md b/docs/en-US/component/table.md index 68de4e000c..94dee6835d 100644 --- a/docs/en-US/component/table.md +++ b/docs/en-US/component/table.md @@ -95,6 +95,16 @@ table/grouping-header ::: +## Table with fixed group header + +fixed group head is supported + +:::demo The attribute `fixed` of the group header is determined by the outermost `el-table-column` + +table/fixed-column-and-group-header + +::: + ## Single select Single row selection is supported. diff --git a/docs/examples/table/fixed-column-and-group-header.vue b/docs/examples/table/fixed-column-and-group-header.vue new file mode 100644 index 0000000000..5a812be535 --- /dev/null +++ b/docs/examples/table/fixed-column-and-group-header.vue @@ -0,0 +1,73 @@ + + + diff --git a/packages/components/table/__tests__/table-column.test.ts b/packages/components/table/__tests__/table-column.test.ts index 42896f3b34..69354fd513 100644 --- a/packages/components/table/__tests__/table-column.test.ts +++ b/packages/components/table/__tests__/table-column.test.ts @@ -701,6 +701,68 @@ describe('table column', () => { wrapper.unmount() }) + it('should work with fixed', async () => { + const wrapper = mount({ + components: { + ElTable, + ElTableColumn, + }, + template: ` + + + + + + + + + + + + + + + + + + `, + + created() { + this.testData = getTestData() + }, + }) + + await doubleWait() + const lfhcolumns = wrapper + .findAll('.el-table__header tr') + .map((item) => item.findAll('.el-table-fixed-column--left')) + const lfbcolumns = wrapper.findAll( + '.el-table__body .el-table-fixed-column--left' + ) + const rfhcolumns = wrapper + .findAll('.el-table__header tr') + .map((item) => item.findAll('.el-table-fixed-column--right')) + const rfbcolumns = wrapper.findAll( + '.el-table__body .el-table-fixed-column--right' + ) + expect(lfbcolumns).toHaveLength(15) + expect(rfbcolumns).toHaveLength(10) + expect(lfhcolumns.at(0).at(0).classes()).toContain('is-last-column') + expect(lfhcolumns.at(1).at(1).classes()).toContain('is-last-column') + expect(getComputedStyle(lfhcolumns.at(1).at(1).element).left).toBe( + '200px' + ) + expect(getComputedStyle(lfhcolumns.at(2).at(1).element).left).toBe( + '100px' + ) + expect(rfhcolumns.at(0).at(0).classes()).toContain('is-first-column') + expect(rfhcolumns.at(1).at(0).classes()).toContain('is-first-column') + expect(getComputedStyle(rfhcolumns.at(1).at(0).element).right).toBe( + '50px' + ) + wrapper.unmount() + }) + it('el-table-column should callback itself', async () => { const TableColumn = { name: 'TableColumn', diff --git a/packages/components/table/src/store/watcher.ts b/packages/components/table/src/store/watcher.ts index b8621017ff..cf6ffdb3a0 100644 --- a/packages/components/table/src/store/watcher.ts +++ b/packages/components/table/src/store/watcher.ts @@ -84,8 +84,19 @@ function useWatcher() { if (!rowKey.value) throw new Error('[ElTable] prop row-key is required') } + // 更新 fixed + const updateChildFixed = (column: TableColumnCtx) => { + column.children?.forEach((childColumn) => { + childColumn.fixed = column.fixed + updateChildFixed(childColumn) + }) + } + // 更新列 const updateColumns = () => { + _columns.value.forEach((column) => { + updateChildFixed(column) + }) fixedColumns.value = _columns.value.filter( (column) => column.fixed === true || column.fixed === 'left' ) diff --git a/packages/components/table/src/table-body/styles-helper.ts b/packages/components/table/src/table-body/styles-helper.ts index 2b52210001..1313c27d11 100644 --- a/packages/components/table/src/table-body/styles-helper.ts +++ b/packages/components/table/src/table-body/styles-helper.ts @@ -67,9 +67,11 @@ function useStyles(props: Partial>) { column, }) } - const fixedStyle = column.isSubColumn - ? null - : getFixedColumnOffset(columnIndex, props?.fixed, props.store) + const fixedStyle = getFixedColumnOffset( + columnIndex, + props?.fixed, + props.store + ) ensurePosition(fixedStyle, 'left') ensurePosition(fixedStyle, 'right') return Object.assign({}, cellStyles, fixedStyle) @@ -82,16 +84,14 @@ function useStyles(props: Partial>) { column: TableColumnCtx, offset: number ) => { - const fixedClasses = column.isSubColumn - ? [] - : getFixedColumnsClass( - ns.b(), - columnIndex, - props?.fixed, - props.store, - undefined, - offset - ) + const fixedClasses = getFixedColumnsClass( + ns.b(), + columnIndex, + props?.fixed, + props.store, + undefined, + offset + ) const classes = [column.id, column.align, column.className, ...fixedClasses] const cellClassName = parent?.props.cellClassName if (typeof cellClassName === 'string') { diff --git a/packages/components/table/src/table-header/style.helper.ts b/packages/components/table/src/table-header/style.helper.ts index a81866b655..9df5745c92 100644 --- a/packages/components/table/src/table-header/style.helper.ts +++ b/packages/components/table/src/table-header/style.helper.ts @@ -48,14 +48,12 @@ function useStyle(props: TableHeaderProps) { column, }) } - const fixedStyle = column.isSubColumn - ? null - : getFixedColumnOffset( - columnIndex, - column.fixed, - props.store, - row as unknown as TableColumnCtx[] - ) + const fixedStyle = getFixedColumnOffset( + columnIndex, + column.fixed, + props.store, + row as unknown as TableColumnCtx[] + ) ensurePosition(fixedStyle, 'left') ensurePosition(fixedStyle, 'right') return Object.assign({}, headerCellStyles, fixedStyle) @@ -67,15 +65,13 @@ function useStyle(props: TableHeaderProps) { row: T, column: TableColumnCtx ) => { - const fixedClasses = column.isSubColumn - ? [] - : getFixedColumnsClass( - ns.b(), - columnIndex, - column.fixed, - props.store, - row as unknown as TableColumnCtx[] - ) + const fixedClasses = getFixedColumnsClass( + ns.b(), + columnIndex, + column.fixed, + props.store, + row as unknown as TableColumnCtx[] + ) const classes = [ column.id, column.order, diff --git a/packages/components/table/src/util.ts b/packages/components/table/src/util.ts index a0b3be1f2d..02c6e41945 100644 --- a/packages/components/table/src/util.ts +++ b/packages/components/table/src/util.ts @@ -1,6 +1,6 @@ // @ts-nocheck import { createPopper } from '@popperjs/core' -import { get } from 'lodash-unified' +import { flatMap, get } from 'lodash-unified' import escapeHtml from 'escape-html' import { hasOwn, throwError } from '@element-plus/utils' import { useZIndex } from '@element-plus/hooks' @@ -379,6 +379,18 @@ export function createTablePopper( return popperInstance } +function getCurrentColumns(column: TableColumnCtx): TableColumnCtx[] { + if (column.children) { + return flatMap(column.children, getCurrentColumns) + } else { + return [column] + } +} + +function getColSpan(colSpan: number, column: TableColumnCtx) { + return colSpan + column.colSpan +} + export const isFixedColumn = ( index: number, fixed: string | boolean, @@ -387,21 +399,18 @@ export const isFixedColumn = ( ) => { let start = 0 let after = index + const columns = store.states.columns.value if (realColumns) { - if (realColumns[index].colSpan > 1) { - // fixed column not supported in grouped header - return {} - } - // handle group - for (let i = 0; i < index; i++) { - start += realColumns[i].colSpan - } - after = start + realColumns[index].colSpan - 1 + // fixed column supported in grouped header + const curColumns = getCurrentColumns(realColumns[index]) + const preColumns = columns.slice(0, columns.indexOf(curColumns[0])) + + start = preColumns.reduce(getColSpan, 0) + after = start + curColumns.reduce(getColSpan, 0) - 1 } else { start = index } let fixedLayout - const columns = store.states.columns switch (fixed) { case 'left': if (after < store.states.fixedLeafColumnsLength.value) { @@ -411,7 +420,7 @@ export const isFixedColumn = ( case 'right': if ( start >= - columns.value.length - store.states.rightFixedLeafColumnsLength.value + columns.length - store.states.rightFixedLeafColumnsLength.value ) { fixedLayout = 'right' } @@ -421,7 +430,7 @@ export const isFixedColumn = ( fixedLayout = 'left' } else if ( start >= - columns.value.length - store.states.rightFixedLeafColumnsLength.value + columns.length - store.states.rightFixedLeafColumnsLength.value ) { fixedLayout = 'right' } @@ -444,13 +453,18 @@ export const getFixedColumnsClass = ( offset = 0 ) => { const classes: string[] = [] - const { direction, start } = isFixedColumn(index, fixed, store, realColumns) + const { direction, start, after } = isFixedColumn( + index, + fixed, + store, + realColumns + ) if (direction) { const isLeft = direction === 'left' classes.push(`${namespace}-fixed-column--${direction}`) if ( isLeft && - start + offset === store.states.fixedLeafColumnsLength.value - 1 + after + offset === store.states.fixedLeafColumnsLength.value - 1 ) { classes.push('is-last-column') } else if ( @@ -480,12 +494,11 @@ export const getFixedColumnOffset = ( store: any, realColumns?: TableColumnCtx[] ) => { - const { direction, start = 0 } = isFixedColumn( - index, - fixed, - store, - realColumns - ) + const { + direction, + start = 0, + after = 0, + } = isFixedColumn(index, fixed, store, realColumns) if (!direction) { return } @@ -493,10 +506,10 @@ export const getFixedColumnOffset = ( const isLeft = direction === 'left' const columns = store.states.columns.value if (isLeft) { - styles.left = columns.slice(0, index).reduce(getOffset, 0) + styles.left = columns.slice(0, start).reduce(getOffset, 0) } else { styles.right = columns - .slice(start + 1) + .slice(after + 1) .reverse() .reduce(getOffset, 0) }