mirror of
https://gitee.com/element-plus/element-plus.git
synced 2024-12-04 20:27:44 +08:00
fix(components): [table] fixed column supported in grouped header (#10096)
* fix(components): [table] fixed column supported in grouped header * fix(components): [table] fixed column supported in grouped header * fix(components): [table] fixed column supported in grouped header * fix(components): [table] fixed column supported in grouped header
This commit is contained in:
parent
dec859a3df
commit
9285109a0c
@ -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 select
|
||||||
|
|
||||||
Single row selection is supported.
|
Single row selection is supported.
|
||||||
|
73
docs/examples/table/fixed-column-and-group-header.vue
Normal file
73
docs/examples/table/fixed-column-and-group-header.vue
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<template>
|
||||||
|
<el-table :data="tableData" style="width: 100%" height="250">
|
||||||
|
<el-table-column prop="date" label="Date" width="150" />
|
||||||
|
<el-table-column prop="name" label="Name" width="150" />
|
||||||
|
<el-table-column prop="zip" label="Zip" width="150" />
|
||||||
|
<el-table-column label="Address Info" fixed="right">
|
||||||
|
<el-table-column prop="state" label="State" width="100" />
|
||||||
|
<el-table-column prop="city" label="City" width="120" />
|
||||||
|
<el-table-column prop="address" label="Address" width="250" />
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
const tableData = [
|
||||||
|
{
|
||||||
|
date: '2016-05-03',
|
||||||
|
name: 'Tom',
|
||||||
|
state: 'California',
|
||||||
|
city: 'Los Angeles',
|
||||||
|
address: 'No. 189, Grove St, Los Angeles',
|
||||||
|
zip: 'CA 90036',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
date: '2016-05-02',
|
||||||
|
name: 'Tom',
|
||||||
|
state: 'California',
|
||||||
|
city: 'Los Angeles',
|
||||||
|
address: 'No. 189, Grove St, Los Angeles',
|
||||||
|
zip: 'CA 90036',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
date: '2016-05-04',
|
||||||
|
name: 'Tom',
|
||||||
|
state: 'California',
|
||||||
|
city: 'Los Angeles',
|
||||||
|
address: 'No. 189, Grove St, Los Angeles',
|
||||||
|
zip: 'CA 90036',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
date: '2016-05-01',
|
||||||
|
name: 'Tom',
|
||||||
|
state: 'California',
|
||||||
|
city: 'Los Angeles',
|
||||||
|
address: 'No. 189, Grove St, Los Angeles',
|
||||||
|
zip: 'CA 90036',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
date: '2016-05-08',
|
||||||
|
name: 'Tom',
|
||||||
|
state: 'California',
|
||||||
|
city: 'Los Angeles',
|
||||||
|
address: 'No. 189, Grove St, Los Angeles',
|
||||||
|
zip: 'CA 90036',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
date: '2016-05-06',
|
||||||
|
name: 'Tom',
|
||||||
|
state: 'California',
|
||||||
|
city: 'Los Angeles',
|
||||||
|
address: 'No. 189, Grove St, Los Angeles',
|
||||||
|
zip: 'CA 90036',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
date: '2016-05-07',
|
||||||
|
name: 'Tom',
|
||||||
|
state: 'California',
|
||||||
|
city: 'Los Angeles',
|
||||||
|
address: 'No. 189, Grove St, Los Angeles',
|
||||||
|
zip: 'CA 90036',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
</script>
|
@ -701,6 +701,68 @@ describe('table column', () => {
|
|||||||
wrapper.unmount()
|
wrapper.unmount()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should work with fixed', async () => {
|
||||||
|
const wrapper = mount({
|
||||||
|
components: {
|
||||||
|
ElTable,
|
||||||
|
ElTableColumn,
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<el-table :data="testData">
|
||||||
|
<el-table-column prop="name" />
|
||||||
|
<el-table-column label="group" fixed="left">
|
||||||
|
<el-table-column label="group's group">
|
||||||
|
<el-table-column prop="runtime" width="100" fixed="right"/>
|
||||||
|
<el-table-column prop="director" width="100" fixed="right"/>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="director"/>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="director"/>
|
||||||
|
<el-table-column prop="runtime"/>
|
||||||
|
<el-table-column label="group2" fixed="right">
|
||||||
|
<el-table-column prop="runtime" width="100" fixed="left"/>
|
||||||
|
<el-table-column prop="director" width="50"/>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="runtime"/>
|
||||||
|
</el-table>
|
||||||
|
`,
|
||||||
|
|
||||||
|
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 () => {
|
it('el-table-column should callback itself', async () => {
|
||||||
const TableColumn = {
|
const TableColumn = {
|
||||||
name: 'TableColumn',
|
name: 'TableColumn',
|
||||||
|
@ -84,8 +84,19 @@ function useWatcher<T>() {
|
|||||||
if (!rowKey.value) throw new Error('[ElTable] prop row-key is required')
|
if (!rowKey.value) throw new Error('[ElTable] prop row-key is required')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新 fixed
|
||||||
|
const updateChildFixed = (column: TableColumnCtx<T>) => {
|
||||||
|
column.children?.forEach((childColumn) => {
|
||||||
|
childColumn.fixed = column.fixed
|
||||||
|
updateChildFixed(childColumn)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 更新列
|
// 更新列
|
||||||
const updateColumns = () => {
|
const updateColumns = () => {
|
||||||
|
_columns.value.forEach((column) => {
|
||||||
|
updateChildFixed(column)
|
||||||
|
})
|
||||||
fixedColumns.value = _columns.value.filter(
|
fixedColumns.value = _columns.value.filter(
|
||||||
(column) => column.fixed === true || column.fixed === 'left'
|
(column) => column.fixed === true || column.fixed === 'left'
|
||||||
)
|
)
|
||||||
|
@ -67,9 +67,11 @@ function useStyles<T>(props: Partial<TableBodyProps<T>>) {
|
|||||||
column,
|
column,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const fixedStyle = column.isSubColumn
|
const fixedStyle = getFixedColumnOffset(
|
||||||
? null
|
columnIndex,
|
||||||
: getFixedColumnOffset(columnIndex, props?.fixed, props.store)
|
props?.fixed,
|
||||||
|
props.store
|
||||||
|
)
|
||||||
ensurePosition(fixedStyle, 'left')
|
ensurePosition(fixedStyle, 'left')
|
||||||
ensurePosition(fixedStyle, 'right')
|
ensurePosition(fixedStyle, 'right')
|
||||||
return Object.assign({}, cellStyles, fixedStyle)
|
return Object.assign({}, cellStyles, fixedStyle)
|
||||||
@ -82,16 +84,14 @@ function useStyles<T>(props: Partial<TableBodyProps<T>>) {
|
|||||||
column: TableColumnCtx<T>,
|
column: TableColumnCtx<T>,
|
||||||
offset: number
|
offset: number
|
||||||
) => {
|
) => {
|
||||||
const fixedClasses = column.isSubColumn
|
const fixedClasses = getFixedColumnsClass(
|
||||||
? []
|
ns.b(),
|
||||||
: getFixedColumnsClass(
|
columnIndex,
|
||||||
ns.b(),
|
props?.fixed,
|
||||||
columnIndex,
|
props.store,
|
||||||
props?.fixed,
|
undefined,
|
||||||
props.store,
|
offset
|
||||||
undefined,
|
)
|
||||||
offset
|
|
||||||
)
|
|
||||||
const classes = [column.id, column.align, column.className, ...fixedClasses]
|
const classes = [column.id, column.align, column.className, ...fixedClasses]
|
||||||
const cellClassName = parent?.props.cellClassName
|
const cellClassName = parent?.props.cellClassName
|
||||||
if (typeof cellClassName === 'string') {
|
if (typeof cellClassName === 'string') {
|
||||||
|
@ -48,14 +48,12 @@ function useStyle<T>(props: TableHeaderProps<T>) {
|
|||||||
column,
|
column,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const fixedStyle = column.isSubColumn
|
const fixedStyle = getFixedColumnOffset<T>(
|
||||||
? null
|
columnIndex,
|
||||||
: getFixedColumnOffset<T>(
|
column.fixed,
|
||||||
columnIndex,
|
props.store,
|
||||||
column.fixed,
|
row as unknown as TableColumnCtx<T>[]
|
||||||
props.store,
|
)
|
||||||
row as unknown as TableColumnCtx<T>[]
|
|
||||||
)
|
|
||||||
ensurePosition(fixedStyle, 'left')
|
ensurePosition(fixedStyle, 'left')
|
||||||
ensurePosition(fixedStyle, 'right')
|
ensurePosition(fixedStyle, 'right')
|
||||||
return Object.assign({}, headerCellStyles, fixedStyle)
|
return Object.assign({}, headerCellStyles, fixedStyle)
|
||||||
@ -67,15 +65,13 @@ function useStyle<T>(props: TableHeaderProps<T>) {
|
|||||||
row: T,
|
row: T,
|
||||||
column: TableColumnCtx<T>
|
column: TableColumnCtx<T>
|
||||||
) => {
|
) => {
|
||||||
const fixedClasses = column.isSubColumn
|
const fixedClasses = getFixedColumnsClass<T>(
|
||||||
? []
|
ns.b(),
|
||||||
: getFixedColumnsClass<T>(
|
columnIndex,
|
||||||
ns.b(),
|
column.fixed,
|
||||||
columnIndex,
|
props.store,
|
||||||
column.fixed,
|
row as unknown as TableColumnCtx<T>[]
|
||||||
props.store,
|
)
|
||||||
row as unknown as TableColumnCtx<T>[]
|
|
||||||
)
|
|
||||||
const classes = [
|
const classes = [
|
||||||
column.id,
|
column.id,
|
||||||
column.order,
|
column.order,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import { createPopper } from '@popperjs/core'
|
import { createPopper } from '@popperjs/core'
|
||||||
import { get } from 'lodash-unified'
|
import { flatMap, get } from 'lodash-unified'
|
||||||
import escapeHtml from 'escape-html'
|
import escapeHtml from 'escape-html'
|
||||||
import { hasOwn, throwError } from '@element-plus/utils'
|
import { hasOwn, throwError } from '@element-plus/utils'
|
||||||
import { useZIndex } from '@element-plus/hooks'
|
import { useZIndex } from '@element-plus/hooks'
|
||||||
@ -379,6 +379,18 @@ export function createTablePopper(
|
|||||||
return popperInstance
|
return popperInstance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getCurrentColumns<T>(column: TableColumnCtx<T>): TableColumnCtx<T>[] {
|
||||||
|
if (column.children) {
|
||||||
|
return flatMap(column.children, getCurrentColumns)
|
||||||
|
} else {
|
||||||
|
return [column]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getColSpan<T>(colSpan: number, column: TableColumnCtx<T>) {
|
||||||
|
return colSpan + column.colSpan
|
||||||
|
}
|
||||||
|
|
||||||
export const isFixedColumn = <T>(
|
export const isFixedColumn = <T>(
|
||||||
index: number,
|
index: number,
|
||||||
fixed: string | boolean,
|
fixed: string | boolean,
|
||||||
@ -387,21 +399,18 @@ export const isFixedColumn = <T>(
|
|||||||
) => {
|
) => {
|
||||||
let start = 0
|
let start = 0
|
||||||
let after = index
|
let after = index
|
||||||
|
const columns = store.states.columns.value
|
||||||
if (realColumns) {
|
if (realColumns) {
|
||||||
if (realColumns[index].colSpan > 1) {
|
// fixed column supported in grouped header
|
||||||
// fixed column not supported in grouped header
|
const curColumns = getCurrentColumns(realColumns[index])
|
||||||
return {}
|
const preColumns = columns.slice(0, columns.indexOf(curColumns[0]))
|
||||||
}
|
|
||||||
// handle group
|
start = preColumns.reduce(getColSpan, 0)
|
||||||
for (let i = 0; i < index; i++) {
|
after = start + curColumns.reduce(getColSpan, 0) - 1
|
||||||
start += realColumns[i].colSpan
|
|
||||||
}
|
|
||||||
after = start + realColumns[index].colSpan - 1
|
|
||||||
} else {
|
} else {
|
||||||
start = index
|
start = index
|
||||||
}
|
}
|
||||||
let fixedLayout
|
let fixedLayout
|
||||||
const columns = store.states.columns
|
|
||||||
switch (fixed) {
|
switch (fixed) {
|
||||||
case 'left':
|
case 'left':
|
||||||
if (after < store.states.fixedLeafColumnsLength.value) {
|
if (after < store.states.fixedLeafColumnsLength.value) {
|
||||||
@ -411,7 +420,7 @@ export const isFixedColumn = <T>(
|
|||||||
case 'right':
|
case 'right':
|
||||||
if (
|
if (
|
||||||
start >=
|
start >=
|
||||||
columns.value.length - store.states.rightFixedLeafColumnsLength.value
|
columns.length - store.states.rightFixedLeafColumnsLength.value
|
||||||
) {
|
) {
|
||||||
fixedLayout = 'right'
|
fixedLayout = 'right'
|
||||||
}
|
}
|
||||||
@ -421,7 +430,7 @@ export const isFixedColumn = <T>(
|
|||||||
fixedLayout = 'left'
|
fixedLayout = 'left'
|
||||||
} else if (
|
} else if (
|
||||||
start >=
|
start >=
|
||||||
columns.value.length - store.states.rightFixedLeafColumnsLength.value
|
columns.length - store.states.rightFixedLeafColumnsLength.value
|
||||||
) {
|
) {
|
||||||
fixedLayout = 'right'
|
fixedLayout = 'right'
|
||||||
}
|
}
|
||||||
@ -444,13 +453,18 @@ export const getFixedColumnsClass = <T>(
|
|||||||
offset = 0
|
offset = 0
|
||||||
) => {
|
) => {
|
||||||
const classes: string[] = []
|
const classes: string[] = []
|
||||||
const { direction, start } = isFixedColumn(index, fixed, store, realColumns)
|
const { direction, start, after } = isFixedColumn(
|
||||||
|
index,
|
||||||
|
fixed,
|
||||||
|
store,
|
||||||
|
realColumns
|
||||||
|
)
|
||||||
if (direction) {
|
if (direction) {
|
||||||
const isLeft = direction === 'left'
|
const isLeft = direction === 'left'
|
||||||
classes.push(`${namespace}-fixed-column--${direction}`)
|
classes.push(`${namespace}-fixed-column--${direction}`)
|
||||||
if (
|
if (
|
||||||
isLeft &&
|
isLeft &&
|
||||||
start + offset === store.states.fixedLeafColumnsLength.value - 1
|
after + offset === store.states.fixedLeafColumnsLength.value - 1
|
||||||
) {
|
) {
|
||||||
classes.push('is-last-column')
|
classes.push('is-last-column')
|
||||||
} else if (
|
} else if (
|
||||||
@ -480,12 +494,11 @@ export const getFixedColumnOffset = <T>(
|
|||||||
store: any,
|
store: any,
|
||||||
realColumns?: TableColumnCtx<T>[]
|
realColumns?: TableColumnCtx<T>[]
|
||||||
) => {
|
) => {
|
||||||
const { direction, start = 0 } = isFixedColumn(
|
const {
|
||||||
index,
|
direction,
|
||||||
fixed,
|
start = 0,
|
||||||
store,
|
after = 0,
|
||||||
realColumns
|
} = isFixedColumn(index, fixed, store, realColumns)
|
||||||
)
|
|
||||||
if (!direction) {
|
if (!direction) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -493,10 +506,10 @@ export const getFixedColumnOffset = <T>(
|
|||||||
const isLeft = direction === 'left'
|
const isLeft = direction === 'left'
|
||||||
const columns = store.states.columns.value
|
const columns = store.states.columns.value
|
||||||
if (isLeft) {
|
if (isLeft) {
|
||||||
styles.left = columns.slice(0, index).reduce(getOffset, 0)
|
styles.left = columns.slice(0, start).reduce(getOffset, 0)
|
||||||
} else {
|
} else {
|
||||||
styles.right = columns
|
styles.right = columns
|
||||||
.slice(start + 1)
|
.slice(after + 1)
|
||||||
.reverse()
|
.reverse()
|
||||||
.reduce(getOffset, 0)
|
.reduce(getOffset, 0)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user