mirror of
https://github.com/element-plus/element-plus.git
synced 2026-03-13 07:51:17 +08:00
refactor: [el-table] using sticky position refactor fixed column (#4414)
* refactor: [el-table] using sticky position refactor fixed column re #4399 * fix: solve the misalignment issue * fix: optimize code
This commit is contained in:
@@ -66,25 +66,43 @@ describe('table column', () => {
|
||||
|
||||
it('fixed', async () => {
|
||||
const wrapper = createTable(
|
||||
'fixed label="test1"',
|
||||
'fixed label="test1" width="100px"',
|
||||
'fixed="right" label="test2"',
|
||||
'fixed="left" label="test3"'
|
||||
)
|
||||
await nextTick()
|
||||
const leftFixedHeaderColumns = wrapper.findAll(
|
||||
'.el-table__header .el-table-fixed-column--left'
|
||||
)
|
||||
const leftFixedBodyColumns = wrapper.findAll(
|
||||
'.el-table__body .el-table-fixed-column--left'
|
||||
)
|
||||
const rightFixedHeaderColumns = wrapper.findAll(
|
||||
'.el-table__header .el-table-fixed-column--right'
|
||||
)
|
||||
const rightFixedBodyColumns = wrapper.findAll(
|
||||
'.el-table__body .el-table-fixed-column--right'
|
||||
)
|
||||
expect(leftFixedHeaderColumns).toHaveLength(2)
|
||||
expect(leftFixedBodyColumns).toHaveLength(10)
|
||||
expect(rightFixedHeaderColumns).toHaveLength(1)
|
||||
expect(rightFixedBodyColumns).toHaveLength(5)
|
||||
expect(leftFixedHeaderColumns.at(0).text()).toBe('test1')
|
||||
expect(leftFixedHeaderColumns.at(1).text()).toBe('test3')
|
||||
expect(leftFixedHeaderColumns.at(1).classes()).toContain('is-last-column')
|
||||
expect(rightFixedHeaderColumns.at(0).text()).toBe('test2')
|
||||
expect(rightFixedHeaderColumns.at(0).classes()).toContain(
|
||||
'is-first-column'
|
||||
)
|
||||
expect(getComputedStyle(leftFixedHeaderColumns.at(0).element).left).toBe(
|
||||
'0px'
|
||||
)
|
||||
expect(getComputedStyle(leftFixedHeaderColumns.at(1).element).left).toBe(
|
||||
'100px'
|
||||
)
|
||||
expect(
|
||||
wrapper
|
||||
.findAll('.el-table__fixed th:not(.is-hidden)')
|
||||
.map((node) => node.text())
|
||||
).toEqual(['test1', 'test3'])
|
||||
|
||||
expect(
|
||||
wrapper
|
||||
.findAll('.el-table__fixed-right th:not(.is-hidden)')
|
||||
.map((node) => node.text())
|
||||
).toEqual(['test2'])
|
||||
expect(
|
||||
wrapper.find('.el-table__body-wrapper').attributes('style')
|
||||
).toBeFalsy()
|
||||
getComputedStyle(rightFixedHeaderColumns.at(0).element).right
|
||||
).toBe('0px')
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
@@ -1017,12 +1035,10 @@ describe('table column', () => {
|
||||
})
|
||||
|
||||
await nextTick()
|
||||
expect(Object.keys(wrapper.find('.el-table__fixed')).length).toEqual(0)
|
||||
expect(wrapper.find('.el-table-fixed-column--left').exists()).toBeFalsy()
|
||||
wrapper.vm.fixed = true
|
||||
await nextTick()
|
||||
expect(
|
||||
Object.keys(wrapper.find('.el-table__fixed')).length
|
||||
).toBeGreaterThan(0)
|
||||
expect(wrapper.find('.el-table-fixed-column--left').exists()).toBeTruthy()
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
|
||||
@@ -63,7 +63,9 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
render() {
|
||||
const data = this.store.states.data.value || []
|
||||
const { wrappedRowRender, store } = this
|
||||
const data = store.states.data.value || []
|
||||
const columns = store.states.columns.value
|
||||
return h(
|
||||
'table',
|
||||
{
|
||||
@@ -73,10 +75,10 @@ export default defineComponent({
|
||||
border: '0',
|
||||
},
|
||||
[
|
||||
hColgroup(this.store.states.columns.value),
|
||||
hColgroup(columns),
|
||||
h('tbody', {}, [
|
||||
data.reduce((acc: VNode[], row) => {
|
||||
return acc.concat(this.wrappedRowRender(row, acc.length))
|
||||
return acc.concat(wrappedRowRender(row, acc.length))
|
||||
}, []),
|
||||
]),
|
||||
]
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { getCurrentInstance } from 'vue'
|
||||
|
||||
import {
|
||||
getFixedColumnOffset,
|
||||
getFixedColumnsClass,
|
||||
ensurePosition,
|
||||
} from '../util'
|
||||
import type { TableColumnCtx } from '../table-column/defaults'
|
||||
import type { Table } from '../table/defaults'
|
||||
import type { TableBodyProps } from './defaults'
|
||||
@@ -7,24 +11,7 @@ import type { TableBodyProps } from './defaults'
|
||||
function useStyles<T>(props: Partial<TableBodyProps<T>>) {
|
||||
const instance = getCurrentInstance()
|
||||
const parent = instance.parent as Table<T>
|
||||
const isColumnHidden = (index) => {
|
||||
if (props.fixed === 'left') {
|
||||
return index >= props.store.states.fixedLeafColumnsLength.value
|
||||
} else if (props.fixed === 'right') {
|
||||
return (
|
||||
index <
|
||||
props.store.states.columns.value.length -
|
||||
props.store.states.rightFixedLeafColumnsLength.value
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
index < props.store.states.fixedLeafColumnsLength.value ||
|
||||
index >=
|
||||
props.store.states.columns.value.length -
|
||||
props.store.states.rightFixedLeafColumnsLength.value
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const getRowStyle = (row: T, rowIndex: number) => {
|
||||
const rowStyle = parent.props.rowStyle
|
||||
if (typeof rowStyle === 'function') {
|
||||
@@ -74,15 +61,23 @@ function useStyles<T>(props: Partial<TableBodyProps<T>>) {
|
||||
column: TableColumnCtx<T>
|
||||
) => {
|
||||
const cellStyle = parent.props.cellStyle
|
||||
let cellStyles = cellStyle ?? {}
|
||||
if (typeof cellStyle === 'function') {
|
||||
return cellStyle.call(null, {
|
||||
cellStyles = cellStyle.call(null, {
|
||||
rowIndex,
|
||||
columnIndex,
|
||||
row,
|
||||
column,
|
||||
})
|
||||
}
|
||||
return cellStyle
|
||||
const fixedStyle = getFixedColumnOffset(
|
||||
columnIndex,
|
||||
props.fixed,
|
||||
props.store
|
||||
)
|
||||
ensurePosition(fixedStyle, 'left')
|
||||
ensurePosition(fixedStyle, 'right')
|
||||
return Object.assign({}, cellStyles, fixedStyle)
|
||||
}
|
||||
|
||||
const getCellClass = (
|
||||
@@ -91,12 +86,10 @@ function useStyles<T>(props: Partial<TableBodyProps<T>>) {
|
||||
row: T,
|
||||
column: TableColumnCtx<T>
|
||||
) => {
|
||||
const classes = [column.id, column.align, column.className]
|
||||
|
||||
if (isColumnHidden(columnIndex)) {
|
||||
classes.push('is-hidden')
|
||||
}
|
||||
|
||||
const fixedClasses = column.isSubColumn
|
||||
? []
|
||||
: getFixedColumnsClass(columnIndex, props.fixed, props.store)
|
||||
const classes = [column.id, column.align, column.className, ...fixedClasses]
|
||||
const cellClassName = parent.props.cellClassName
|
||||
if (typeof cellClassName === 'string') {
|
||||
classes.push(cellClassName)
|
||||
@@ -164,7 +157,6 @@ function useStyles<T>(props: Partial<TableBodyProps<T>>) {
|
||||
getCellClass,
|
||||
getSpan,
|
||||
getColspanRealWidth,
|
||||
isColumnHidden,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ interface TableColumnCtx<T> {
|
||||
filterable: boolean | FilterMethods<T> | Filters
|
||||
order: string
|
||||
isColumnGroup: boolean
|
||||
isSubColumn: boolean
|
||||
columns: TableColumnCtx<T>[]
|
||||
getColumnIndex: () => number
|
||||
no: number
|
||||
|
||||
@@ -78,6 +78,7 @@ export default defineComponent({
|
||||
filteredValue: [],
|
||||
filterPlacement: '',
|
||||
isColumnGroup: false,
|
||||
isSubColumn: false,
|
||||
filterOpened: false,
|
||||
// sort 相关属性
|
||||
sortable,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { defineComponent, h } from 'vue'
|
||||
import { hGutter, hColgroup } from '../h-helper'
|
||||
import { hColgroup } from '../h-helper'
|
||||
import useStyle from './style-helper'
|
||||
import type { Store } from '../store'
|
||||
|
||||
@@ -42,31 +42,40 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const { hasGutter, getRowClasses, columns } = useStyle(
|
||||
props as TableFooter<DefaultRow>
|
||||
)
|
||||
const { hasGutter, getCellClasses, getCellStyles, columns, gutterWidth } =
|
||||
useStyle(props as TableFooter<DefaultRow>)
|
||||
return {
|
||||
getRowClasses,
|
||||
getCellClasses,
|
||||
getCellStyles,
|
||||
hasGutter,
|
||||
gutterWidth,
|
||||
columns,
|
||||
}
|
||||
},
|
||||
render() {
|
||||
const {
|
||||
hasGutter,
|
||||
gutterWidth,
|
||||
columns,
|
||||
getCellStyles,
|
||||
getCellClasses,
|
||||
summaryMethod,
|
||||
sumText,
|
||||
} = this
|
||||
const data = this.store.states.data.value
|
||||
let sums = []
|
||||
if (this.summaryMethod) {
|
||||
sums = this.summaryMethod({
|
||||
columns: this.columns,
|
||||
data: this.store.states.data.value,
|
||||
if (summaryMethod) {
|
||||
sums = summaryMethod({
|
||||
columns,
|
||||
data,
|
||||
})
|
||||
} else {
|
||||
this.columns.forEach((column, index) => {
|
||||
columns.forEach((column, index) => {
|
||||
if (index === 0) {
|
||||
sums[index] = this.sumText
|
||||
sums[index] = sumText
|
||||
return
|
||||
}
|
||||
const values = this.store.states.data.value.map((item) =>
|
||||
Number(item[column.property])
|
||||
)
|
||||
const values = data.map((item) => Number(item[column.property]))
|
||||
const precisions = []
|
||||
let notNumber = true
|
||||
values.forEach((value) => {
|
||||
@@ -100,25 +109,23 @@ export default defineComponent({
|
||||
border: '0',
|
||||
},
|
||||
[
|
||||
hColgroup(this.columns, this.hasGutter),
|
||||
hColgroup(columns, hasGutter),
|
||||
h(
|
||||
'tbody',
|
||||
{
|
||||
class: [{ 'has-gutter': this.hasGutter }],
|
||||
class: [{ 'has-gutter': hasGutter }],
|
||||
},
|
||||
[
|
||||
h('tr', {}, [
|
||||
...this.columns.map((column, cellIndex) =>
|
||||
...columns.map((column, cellIndex) =>
|
||||
h(
|
||||
'td',
|
||||
{
|
||||
key: cellIndex,
|
||||
colspan: column.colSpan,
|
||||
rowspan: column.rowSpan,
|
||||
class: [
|
||||
...this.getRowClasses(column, cellIndex),
|
||||
'el-table__cell',
|
||||
],
|
||||
class: getCellClasses(columns, cellIndex, hasGutter),
|
||||
style: getCellStyles(column, cellIndex, hasGutter),
|
||||
},
|
||||
[
|
||||
h(
|
||||
@@ -131,7 +138,13 @@ export default defineComponent({
|
||||
]
|
||||
)
|
||||
),
|
||||
this.hasGutter && hGutter(),
|
||||
hasGutter &&
|
||||
h('td', {
|
||||
class: 'el-table__fixed-right-patch el-table__cell',
|
||||
style: {
|
||||
width: `${gutterWidth}px`,
|
||||
},
|
||||
}),
|
||||
]),
|
||||
]
|
||||
),
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import { computed, getCurrentInstance } from 'vue'
|
||||
import {
|
||||
getFixedColumnOffset,
|
||||
getFixedColumnsClass,
|
||||
ensurePosition,
|
||||
ensureRightFixedStyle,
|
||||
} from '../util'
|
||||
import useMapState from './mapState-helper'
|
||||
|
||||
import type { Table } from '../table/defaults'
|
||||
import type { TableColumnCtx } from '../table-column/defaults'
|
||||
import type { TableFooter } from '.'
|
||||
@@ -8,60 +13,67 @@ import type { TableFooter } from '.'
|
||||
function useStyle<T>(props: TableFooter<T>) {
|
||||
const instance = getCurrentInstance()
|
||||
const table = instance.parent as Table<T>
|
||||
const store = table.store
|
||||
|
||||
const {
|
||||
leftFixedLeafCount,
|
||||
rightFixedLeafCount,
|
||||
columnsCount,
|
||||
leftFixedCount,
|
||||
rightFixedCount,
|
||||
columns,
|
||||
} = useMapState<T>()
|
||||
const { columns } = useMapState<T>()
|
||||
|
||||
const hasGutter = computed(() => {
|
||||
return !props.fixed && !table.layout.gutterWidth
|
||||
return (
|
||||
!props.fixed &&
|
||||
table.layout.gutterWidth > 0 &&
|
||||
table.layout.height.value &&
|
||||
table.layout.bodyScrollHeight.value > table.layout.bodyHeight.value
|
||||
)
|
||||
})
|
||||
const isCellHidden = (
|
||||
index: number,
|
||||
|
||||
const gutterWidth = computed(() => {
|
||||
return table.layout.gutterWidth
|
||||
})
|
||||
const getCellClasses = (
|
||||
columns: TableColumnCtx<T>[],
|
||||
column: TableColumnCtx<T>
|
||||
cellIndex: number,
|
||||
hasGutter: boolean
|
||||
) => {
|
||||
if (props.fixed || props.fixed === 'left') {
|
||||
return index >= leftFixedLeafCount.value
|
||||
} else if (props.fixed === 'right') {
|
||||
let before = 0
|
||||
for (let i = 0; i < index; i++) {
|
||||
before += columns[i].colSpan
|
||||
}
|
||||
return before < columnsCount.value - rightFixedLeafCount.value
|
||||
} else if (!props.fixed && column.fixed) {
|
||||
// hide cell when footer instance is not fixed and column is fixed
|
||||
return true
|
||||
} else {
|
||||
return (
|
||||
index < leftFixedCount.value ||
|
||||
index >= columnsCount.value - rightFixedCount.value
|
||||
)
|
||||
}
|
||||
}
|
||||
const getRowClasses = (column: TableColumnCtx<T>, cellIndex: number) => {
|
||||
const classes = [column.id, column.align, column.labelClassName]
|
||||
const column = columns[cellIndex]
|
||||
const classes = [
|
||||
'el-table__cell',
|
||||
column.id,
|
||||
column.align,
|
||||
column.labelClassName,
|
||||
...getFixedColumnsClass(cellIndex, column.fixed, props.store),
|
||||
]
|
||||
if (column.className) {
|
||||
classes.push(column.className)
|
||||
}
|
||||
if (isCellHidden(cellIndex, store.states.columns.value, column)) {
|
||||
classes.push('is-hidden')
|
||||
}
|
||||
if (!column.children) {
|
||||
classes.push('is-leaf')
|
||||
}
|
||||
if (hasGutter && cellIndex === columns.length - 1) {
|
||||
classes.push('last')
|
||||
}
|
||||
return classes
|
||||
}
|
||||
|
||||
const getCellStyles = (
|
||||
column: TableColumnCtx<T>,
|
||||
cellIndex: number,
|
||||
hasGutter: boolean
|
||||
) => {
|
||||
const fixedStyle = getFixedColumnOffset(
|
||||
cellIndex,
|
||||
column.fixed,
|
||||
props.store
|
||||
)
|
||||
ensureRightFixedStyle(fixedStyle, hasGutter)
|
||||
ensurePosition(fixedStyle, 'left')
|
||||
ensurePosition(fixedStyle, 'right')
|
||||
return fixedStyle
|
||||
}
|
||||
|
||||
return {
|
||||
hasGutter,
|
||||
getRowClasses,
|
||||
gutterWidth,
|
||||
getCellClasses,
|
||||
getCellStyles,
|
||||
columns,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,14 @@ export default defineComponent({
|
||||
const { tableLayout, onColumnsChange, onScrollableChange } =
|
||||
useLayoutObserver(parent)
|
||||
const hasGutter = computed(() => {
|
||||
return !props.fixed && tableLayout.gutterWidth
|
||||
return (
|
||||
!props.fixed &&
|
||||
tableLayout.gutterWidth > 0 &&
|
||||
tableLayout.bodyScrollHeight.value > tableLayout.bodyHeight.value
|
||||
)
|
||||
})
|
||||
const gutterWidth = computed(() => {
|
||||
return tableLayout.gutterWidth
|
||||
})
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
@@ -104,6 +111,7 @@ export default defineComponent({
|
||||
columns: storeData.columns,
|
||||
filterPanels,
|
||||
hasGutter,
|
||||
gutterWidth,
|
||||
onColumnsChange,
|
||||
onScrollableChange,
|
||||
columnRows,
|
||||
@@ -123,6 +131,26 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
render() {
|
||||
const {
|
||||
columns,
|
||||
isGroup,
|
||||
hasGutter,
|
||||
columnRows,
|
||||
gutterWidth,
|
||||
getHeaderCellStyle,
|
||||
getHeaderCellClass,
|
||||
getHeaderRowClass,
|
||||
getHeaderRowStyle,
|
||||
handleHeaderClick,
|
||||
handleHeaderContextMenu,
|
||||
handleMouseDown,
|
||||
handleMouseMove,
|
||||
handleSortClick,
|
||||
handleMouseOut,
|
||||
store,
|
||||
$parent,
|
||||
} = this
|
||||
let rowSpan = 1
|
||||
return h(
|
||||
'table',
|
||||
{
|
||||
@@ -132,113 +160,135 @@ export default defineComponent({
|
||||
class: 'el-table__header',
|
||||
},
|
||||
[
|
||||
hColgroup(this.columns, this.hasGutter),
|
||||
hColgroup(columns, hasGutter),
|
||||
h(
|
||||
'thead',
|
||||
{
|
||||
class: { 'is-group': this.isGroup, 'has-gutter': this.hasGutter },
|
||||
class: { 'is-group': isGroup, 'has-gutter': hasGutter },
|
||||
},
|
||||
this.columnRows.map((subColumns, rowIndex) =>
|
||||
columnRows.map((subColumns, rowIndex) =>
|
||||
h(
|
||||
'tr',
|
||||
{
|
||||
class: this.getHeaderRowClass(rowIndex),
|
||||
class: getHeaderRowClass(rowIndex),
|
||||
key: rowIndex,
|
||||
style: this.getHeaderRowStyle(rowIndex),
|
||||
style: getHeaderRowStyle(rowIndex),
|
||||
},
|
||||
subColumns.map((column, cellIndex) =>
|
||||
h(
|
||||
'th',
|
||||
{
|
||||
class: this.getHeaderCellClass(
|
||||
rowIndex,
|
||||
cellIndex,
|
||||
subColumns,
|
||||
column
|
||||
),
|
||||
colspan: column.colSpan,
|
||||
key: `${column.id}-thead`,
|
||||
rowSpan: column.rowSpan,
|
||||
style: this.getHeaderCellStyle(
|
||||
rowIndex,
|
||||
cellIndex,
|
||||
subColumns,
|
||||
column
|
||||
),
|
||||
onClick: ($event) => this.handleHeaderClick($event, column),
|
||||
onContextmenu: ($event) =>
|
||||
this.handleHeaderContextMenu($event, column),
|
||||
onMousedown: ($event) =>
|
||||
this.handleMouseDown($event, column),
|
||||
onMousemove: ($event) =>
|
||||
this.handleMouseMove($event, column),
|
||||
onMouseout: this.handleMouseOut,
|
||||
},
|
||||
[
|
||||
h(
|
||||
'div',
|
||||
{
|
||||
class: [
|
||||
'cell',
|
||||
column.filteredValue &&
|
||||
column.filteredValue.length > 0
|
||||
? 'highlight'
|
||||
: '',
|
||||
column.labelClassName,
|
||||
],
|
||||
},
|
||||
[
|
||||
column.renderHeader
|
||||
? column.renderHeader({
|
||||
subColumns
|
||||
.map((column, cellIndex) => {
|
||||
if (column.rowSpan > rowSpan) {
|
||||
rowSpan = column.rowSpan
|
||||
}
|
||||
return h(
|
||||
'th',
|
||||
{
|
||||
class: getHeaderCellClass(
|
||||
rowIndex,
|
||||
cellIndex,
|
||||
subColumns,
|
||||
column
|
||||
),
|
||||
colspan: column.colSpan,
|
||||
key: `${column.id}-thead`,
|
||||
rowSpan: column.rowSpan,
|
||||
style: getHeaderCellStyle(
|
||||
rowIndex,
|
||||
cellIndex,
|
||||
subColumns,
|
||||
column,
|
||||
hasGutter
|
||||
),
|
||||
onClick: ($event) => handleHeaderClick($event, column),
|
||||
onContextmenu: ($event) =>
|
||||
handleHeaderContextMenu($event, column),
|
||||
onMousedown: ($event) => handleMouseDown($event, column),
|
||||
onMousemove: ($event) => handleMouseMove($event, column),
|
||||
onMouseout: handleMouseOut,
|
||||
},
|
||||
[
|
||||
h(
|
||||
'div',
|
||||
{
|
||||
class: [
|
||||
'cell',
|
||||
column.filteredValue &&
|
||||
column.filteredValue.length > 0
|
||||
? 'highlight'
|
||||
: '',
|
||||
column.labelClassName,
|
||||
],
|
||||
},
|
||||
[
|
||||
column.renderHeader
|
||||
? column.renderHeader({
|
||||
column,
|
||||
$index: cellIndex,
|
||||
store,
|
||||
_self: $parent,
|
||||
})
|
||||
: column.label,
|
||||
column.sortable &&
|
||||
h(
|
||||
'span',
|
||||
{
|
||||
onClick: ($event) =>
|
||||
handleSortClick($event, column),
|
||||
class: 'caret-wrapper',
|
||||
},
|
||||
[
|
||||
h('i', {
|
||||
onClick: ($event) =>
|
||||
handleSortClick(
|
||||
$event,
|
||||
column,
|
||||
'ascending'
|
||||
),
|
||||
class: 'sort-caret ascending',
|
||||
}),
|
||||
h('i', {
|
||||
onClick: ($event) =>
|
||||
handleSortClick(
|
||||
$event,
|
||||
column,
|
||||
'descending'
|
||||
),
|
||||
class: 'sort-caret descending',
|
||||
}),
|
||||
]
|
||||
),
|
||||
column.filterable &&
|
||||
h(FilterPanel, {
|
||||
store: $parent.store,
|
||||
placement:
|
||||
column.filterPlacement || 'bottom-start',
|
||||
column,
|
||||
$index: cellIndex,
|
||||
store: this.store,
|
||||
_self: this.$parent,
|
||||
})
|
||||
: column.label,
|
||||
column.sortable &&
|
||||
h(
|
||||
'span',
|
||||
{
|
||||
onClick: ($event) =>
|
||||
this.handleSortClick($event, column),
|
||||
class: 'caret-wrapper',
|
||||
upDataColumn: (key, value) => {
|
||||
column[key] = value
|
||||
},
|
||||
}),
|
||||
]
|
||||
),
|
||||
]
|
||||
)
|
||||
})
|
||||
.concat(
|
||||
hasGutter && rowIndex === 0
|
||||
? [
|
||||
h(
|
||||
'th',
|
||||
{
|
||||
class: 'el-table__fixed-right-patch el-table__cell',
|
||||
key: `el-table--scrollbar`,
|
||||
rowSpan,
|
||||
style: {
|
||||
width: `${gutterWidth}px`,
|
||||
},
|
||||
[
|
||||
h('i', {
|
||||
onClick: ($event) =>
|
||||
this.handleSortClick(
|
||||
$event,
|
||||
column,
|
||||
'ascending'
|
||||
),
|
||||
class: 'sort-caret ascending',
|
||||
}),
|
||||
h('i', {
|
||||
onClick: ($event) =>
|
||||
this.handleSortClick(
|
||||
$event,
|
||||
column,
|
||||
'descending'
|
||||
),
|
||||
class: 'sort-caret descending',
|
||||
}),
|
||||
]
|
||||
),
|
||||
column.filterable &&
|
||||
h(FilterPanel, {
|
||||
store: this.$parent.store,
|
||||
placement: column.filterPlacement || 'bottom-start',
|
||||
column,
|
||||
upDataColumn: (key, value) => {
|
||||
column[key] = value
|
||||
},
|
||||
}),
|
||||
},
|
||||
[]
|
||||
),
|
||||
]
|
||||
),
|
||||
]
|
||||
: []
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { getCurrentInstance } from 'vue'
|
||||
|
||||
import {
|
||||
getFixedColumnsClass,
|
||||
getFixedColumnOffset,
|
||||
ensurePosition,
|
||||
ensureRightFixedStyle,
|
||||
} from '../util'
|
||||
import type { TableColumnCtx } from '../table-column/defaults'
|
||||
import type { Table } from '../table/defaults'
|
||||
import type { TableHeaderProps } from '.'
|
||||
@@ -7,33 +12,6 @@ import type { TableHeaderProps } from '.'
|
||||
function useStyle<T>(props: TableHeaderProps<T>) {
|
||||
const instance = getCurrentInstance()
|
||||
const parent = instance.parent as Table<T>
|
||||
const storeData = parent.store.states
|
||||
const isCellHidden = (
|
||||
index: number,
|
||||
columns: TableColumnCtx<T>[]
|
||||
): boolean => {
|
||||
let start = 0
|
||||
for (let i = 0; i < index; i++) {
|
||||
start += columns[i].colSpan
|
||||
}
|
||||
const after = start + columns[index].colSpan - 1
|
||||
if (props.fixed === 'left') {
|
||||
return after >= storeData.fixedLeafColumnsLength.value
|
||||
} else if (props.fixed === 'right') {
|
||||
return (
|
||||
start <
|
||||
storeData.columns.value.length -
|
||||
storeData.rightFixedLeafColumnsLength.value
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
after < storeData.fixedLeafColumnsLength.value ||
|
||||
start >=
|
||||
storeData.columns.value.length -
|
||||
storeData.rightFixedLeafColumnsLength.value
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const getHeaderRowStyle = (rowIndex: number) => {
|
||||
const headerRowStyle = parent.props.headerRowStyle
|
||||
@@ -59,18 +37,28 @@ function useStyle<T>(props: TableHeaderProps<T>) {
|
||||
rowIndex: number,
|
||||
columnIndex: number,
|
||||
row: T,
|
||||
column: TableColumnCtx<T>
|
||||
column: TableColumnCtx<T>,
|
||||
hasGutter: boolean
|
||||
) => {
|
||||
const headerCellStyle = parent.props.headerCellStyle
|
||||
if (typeof headerCellStyle === 'function') {
|
||||
return headerCellStyle.call(null, {
|
||||
let headerCellStyles = parent.props.headerCellStyle ?? {}
|
||||
if (typeof headerCellStyles === 'function') {
|
||||
headerCellStyles = headerCellStyles.call(null, {
|
||||
rowIndex,
|
||||
columnIndex,
|
||||
row,
|
||||
column,
|
||||
})
|
||||
}
|
||||
return headerCellStyle
|
||||
const fixedStyle = getFixedColumnOffset<T>(
|
||||
columnIndex,
|
||||
column.fixed,
|
||||
props.store,
|
||||
row as unknown as TableColumnCtx<T>[]
|
||||
)
|
||||
ensureRightFixedStyle(fixedStyle, hasGutter)
|
||||
ensurePosition(fixedStyle, 'left')
|
||||
ensurePosition(fixedStyle, 'right')
|
||||
return Object.assign({}, headerCellStyles, fixedStyle)
|
||||
}
|
||||
|
||||
const getHeaderCellClass = (
|
||||
@@ -79,19 +67,22 @@ function useStyle<T>(props: TableHeaderProps<T>) {
|
||||
row: T,
|
||||
column: TableColumnCtx<T>
|
||||
) => {
|
||||
const fixedClasses = column.isSubColumn
|
||||
? []
|
||||
: getFixedColumnsClass<T>(
|
||||
columnIndex,
|
||||
column.fixed,
|
||||
props.store,
|
||||
row as unknown as TableColumnCtx<T>[]
|
||||
)
|
||||
const classes = [
|
||||
column.id,
|
||||
column.order,
|
||||
column.headerAlign,
|
||||
column.className,
|
||||
column.labelClassName,
|
||||
...fixedClasses,
|
||||
]
|
||||
if (
|
||||
rowIndex === 0 &&
|
||||
isCellHidden(columnIndex, row as unknown as TableColumnCtx<T>[])
|
||||
) {
|
||||
classes.push('is-hidden')
|
||||
}
|
||||
|
||||
if (!column.children) {
|
||||
classes.push('is-leaf')
|
||||
|
||||
@@ -60,6 +60,7 @@ const convertToRows = <T>(
|
||||
column.rowSpan = maxLevel - column.level + 1
|
||||
} else {
|
||||
column.rowSpan = 1
|
||||
column.children.forEach((col) => (col.isSubColumn = true))
|
||||
}
|
||||
rows[column.level - 1].push(column)
|
||||
})
|
||||
|
||||
@@ -29,6 +29,7 @@ class TableLayout<T> {
|
||||
footerHeight: Ref<null | number> // Table Footer Height
|
||||
viewportHeight: Ref<null | number> // Table Height - Scroll Bar Height
|
||||
bodyHeight: Ref<null | number> // Table Height - Table Header Height
|
||||
bodyScrollHeight: Ref<number>
|
||||
fixedBodyHeight: Ref<null | number> // Table Height - Table Header Height - Scroll Bar Height
|
||||
gutterWidth: number
|
||||
constructor(options: Record<string, any>) {
|
||||
@@ -50,6 +51,7 @@ class TableLayout<T> {
|
||||
this.footerHeight = ref(44)
|
||||
this.viewportHeight = ref(null)
|
||||
this.bodyHeight = ref(null)
|
||||
this.bodyScrollHeight = ref(0)
|
||||
this.fixedBodyHeight = ref(null)
|
||||
this.gutterWidth = scrollbarWidth()
|
||||
for (const name in options) {
|
||||
@@ -136,7 +138,8 @@ class TableLayout<T> {
|
||||
|
||||
updateElsHeight() {
|
||||
if (!this.table.$ready) return nextTick(() => this.updateElsHeight())
|
||||
const { headerWrapper, appendWrapper, footerWrapper } = this.table.refs
|
||||
const { headerWrapper, appendWrapper, footerWrapper, bodyWrapper } =
|
||||
this.table.refs
|
||||
this.appendHeight.value = appendWrapper ? appendWrapper.offsetHeight : 0
|
||||
if (this.showHeader && !headerWrapper) return
|
||||
|
||||
@@ -163,8 +166,13 @@ class TableLayout<T> {
|
||||
? footerWrapper.offsetHeight
|
||||
: 0)
|
||||
if (this.height.value !== null) {
|
||||
if (this.bodyHeight.value === null) {
|
||||
requestAnimationFrame(() => this.updateElsHeight())
|
||||
}
|
||||
this.bodyHeight.value =
|
||||
tableHeight - headerHeight - footerHeight + (footerWrapper ? 1 : 0)
|
||||
this.bodyScrollHeight.value =
|
||||
bodyWrapper.querySelector('.el-table__body')?.scrollHeight!
|
||||
}
|
||||
this.fixedBodyHeight.value = this.scrollX.value
|
||||
? this.bodyHeight.value - this.gutterWidth
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
ref="tableWrapper"
|
||||
:class="[
|
||||
{
|
||||
'el-table--fit': fit,
|
||||
@@ -22,56 +23,62 @@
|
||||
:style="style"
|
||||
@mouseleave="handleMouseLeave()"
|
||||
>
|
||||
<div ref="hiddenColumns" class="hidden-columns">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<div
|
||||
v-if="showHeader"
|
||||
ref="headerWrapper"
|
||||
v-mousewheel="handleHeaderFooterMousewheel"
|
||||
class="el-table__header-wrapper"
|
||||
>
|
||||
<table-header
|
||||
ref="tableHeader"
|
||||
:border="border"
|
||||
:default-sort="defaultSort"
|
||||
:store="store"
|
||||
:style="{
|
||||
width: layout.bodyWidth.value ? layout.bodyWidth.value + 'px' : '',
|
||||
}"
|
||||
@set-drag-visible="setDragVisible"
|
||||
/>
|
||||
</div>
|
||||
<div ref="bodyWrapper" :style="[bodyHeight]" class="el-table__body-wrapper">
|
||||
<table-body
|
||||
:context="context"
|
||||
:highlight="highlightCurrentRow"
|
||||
:row-class-name="rowClassName"
|
||||
:tooltip-effect="tooltipEffect"
|
||||
:row-style="rowStyle"
|
||||
:store="store"
|
||||
:stripe="stripe"
|
||||
:style="{
|
||||
width: bodyWidth,
|
||||
}"
|
||||
/>
|
||||
<div
|
||||
v-if="isEmpty"
|
||||
ref="emptyBlock"
|
||||
:style="emptyBlockStyle"
|
||||
class="el-table__empty-block"
|
||||
>
|
||||
<span class="el-table__empty-text">
|
||||
<slot name="empty">{{ emptyText || t('el.table.emptyText') }}</slot>
|
||||
</span>
|
||||
<div class="el-table__inner-wrapper">
|
||||
<div ref="hiddenColumns" class="hidden-columns">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<div
|
||||
v-if="$slots.append"
|
||||
ref="appendWrapper"
|
||||
class="el-table__append-wrapper"
|
||||
v-if="showHeader"
|
||||
ref="headerWrapper"
|
||||
v-mousewheel="handleHeaderFooterMousewheel"
|
||||
class="el-table__header-wrapper"
|
||||
>
|
||||
<slot name="append"></slot>
|
||||
<table-header
|
||||
ref="tableHeader"
|
||||
:border="border"
|
||||
:default-sort="defaultSort"
|
||||
:store="store"
|
||||
:style="tableBodyStyles"
|
||||
@set-drag-visible="setDragVisible"
|
||||
/>
|
||||
</div>
|
||||
<div ref="bodyWrapper" :style="bodyHeight" class="el-table__body-wrapper">
|
||||
<table-body
|
||||
:context="context"
|
||||
:highlight="highlightCurrentRow"
|
||||
:row-class-name="rowClassName"
|
||||
:tooltip-effect="tooltipEffect"
|
||||
:row-style="rowStyle"
|
||||
:store="store"
|
||||
:stripe="stripe"
|
||||
:style="{
|
||||
width: bodyWidth,
|
||||
}"
|
||||
/>
|
||||
<div
|
||||
v-if="isEmpty"
|
||||
ref="emptyBlock"
|
||||
:style="emptyBlockStyle"
|
||||
class="el-table__empty-block"
|
||||
>
|
||||
<span class="el-table__empty-text">
|
||||
<slot name="empty">{{ computedEmptyText }}</slot>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="$slots.append"
|
||||
ref="appendWrapper"
|
||||
class="el-table__append-wrapper"
|
||||
>
|
||||
<slot name="append"></slot>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="border || isGroup" class="el-table__border-left-patch"></div>
|
||||
<div
|
||||
v-if="layout.scrollX.value && layout.height.value"
|
||||
class="el-table__border-bottom-patch"
|
||||
:style="borderBottomPatchStyles"
|
||||
></div>
|
||||
</div>
|
||||
<div
|
||||
v-if="showSummary"
|
||||
@@ -84,170 +91,11 @@
|
||||
:border="border"
|
||||
:default-sort="defaultSort"
|
||||
:store="store"
|
||||
:style="{
|
||||
width: layout.bodyWidth.value ? layout.bodyWidth.value + 'px' : '',
|
||||
}"
|
||||
:sum-text="sumText || t('el.table.sumText')"
|
||||
:style="tableBodyStyles"
|
||||
:sum-text="computedSumText"
|
||||
:summary-method="summaryMethod"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="store.states.fixedColumns.value.length > 0"
|
||||
ref="fixedWrapper"
|
||||
v-mousewheel="handleFixedMousewheel"
|
||||
:style="[
|
||||
{
|
||||
width: layout.fixedWidth.value ? layout.fixedWidth.value + 'px' : '',
|
||||
},
|
||||
fixedHeight,
|
||||
]"
|
||||
class="el-table__fixed"
|
||||
>
|
||||
<div
|
||||
v-if="showHeader"
|
||||
ref="fixedHeaderWrapper"
|
||||
class="el-table__fixed-header-wrapper"
|
||||
>
|
||||
<table-header
|
||||
ref="fixedTableHeader"
|
||||
:border="border"
|
||||
:store="store"
|
||||
:style="{
|
||||
width: bodyWidth,
|
||||
}"
|
||||
fixed="left"
|
||||
@set-drag-visible="setDragVisible"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
ref="fixedBodyWrapper"
|
||||
:style="[
|
||||
{
|
||||
top: layout.headerHeight.value + 'px',
|
||||
},
|
||||
fixedBodyHeight,
|
||||
]"
|
||||
class="el-table__fixed-body-wrapper"
|
||||
>
|
||||
<table-body
|
||||
:highlight="highlightCurrentRow"
|
||||
:row-class-name="rowClassName"
|
||||
:tooltip-effect="tooltipEffect"
|
||||
:row-style="rowStyle"
|
||||
:store="store"
|
||||
:stripe="stripe"
|
||||
:style="{
|
||||
width: bodyWidth,
|
||||
}"
|
||||
fixed="left"
|
||||
/>
|
||||
<div
|
||||
v-if="$slots.append"
|
||||
:style="{ height: layout.appendHeight.value + 'px' }"
|
||||
class="el-table__append-gutter"
|
||||
></div>
|
||||
</div>
|
||||
<div
|
||||
v-if="showSummary"
|
||||
v-show="!isEmpty"
|
||||
ref="fixedFooterWrapper"
|
||||
class="el-table__fixed-footer-wrapper"
|
||||
>
|
||||
<table-footer
|
||||
:border="border"
|
||||
:store="store"
|
||||
:style="{
|
||||
width: bodyWidth,
|
||||
}"
|
||||
:sum-text="sumText || t('el.table.sumText')"
|
||||
:summary-method="summaryMethod"
|
||||
fixed="left"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="store.states.rightFixedColumns.value.length > 0"
|
||||
ref="rightFixedWrapper"
|
||||
v-mousewheel="handleFixedMousewheel"
|
||||
:style="[
|
||||
{
|
||||
width: layout.rightFixedWidth.value
|
||||
? layout.rightFixedWidth.value + 'px'
|
||||
: '',
|
||||
right: layout.scrollY.value
|
||||
? (border ? layout.gutterWidth : layout.gutterWidth || 0) + 'px'
|
||||
: '',
|
||||
},
|
||||
fixedHeight,
|
||||
]"
|
||||
class="el-table__fixed-right"
|
||||
>
|
||||
<div
|
||||
v-if="showHeader"
|
||||
ref="rightFixedHeaderWrapper"
|
||||
class="el-table__fixed-header-wrapper"
|
||||
>
|
||||
<table-header
|
||||
ref="rightFixedTableHeader"
|
||||
:border="border"
|
||||
:store="store"
|
||||
:style="{
|
||||
width: bodyWidth,
|
||||
}"
|
||||
fixed="right"
|
||||
@set-drag-visible="setDragVisible"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
ref="rightFixedBodyWrapper"
|
||||
:style="[{ top: layout.headerHeight.value + 'px' }, fixedBodyHeight]"
|
||||
class="el-table__fixed-body-wrapper"
|
||||
>
|
||||
<table-body
|
||||
:highlight="highlightCurrentRow"
|
||||
:row-class-name="rowClassName"
|
||||
:tooltip-effect="tooltipEffect"
|
||||
:row-style="rowStyle"
|
||||
:store="store"
|
||||
:stripe="stripe"
|
||||
:style="{
|
||||
width: bodyWidth,
|
||||
}"
|
||||
fixed="right"
|
||||
/>
|
||||
<div
|
||||
v-if="$slots.append"
|
||||
:style="{ height: layout.appendHeight.value + 'px' }"
|
||||
class="el-table__append-gutter"
|
||||
></div>
|
||||
</div>
|
||||
<div
|
||||
v-if="showSummary"
|
||||
v-show="!isEmpty"
|
||||
ref="rightFixedFooterWrapper"
|
||||
class="el-table__fixed-footer-wrapper"
|
||||
>
|
||||
<table-footer
|
||||
:border="border"
|
||||
:store="store"
|
||||
:style="{
|
||||
width: bodyWidth,
|
||||
}"
|
||||
:sum-text="sumText || t('el.table.sumText')"
|
||||
:summary-method="summaryMethod"
|
||||
fixed="right"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="store.states.rightFixedColumns.value.length > 0"
|
||||
ref="rightFixedPatch"
|
||||
:style="{
|
||||
width: layout.scrollY.value ? layout.gutterWidth + 'px' : '0',
|
||||
height: layout.headerHeight.value + 'px',
|
||||
}"
|
||||
class="el-table__fixed-right-patch"
|
||||
></div>
|
||||
<div
|
||||
v-show="resizeProxyVisible"
|
||||
ref="resizeProxy"
|
||||
@@ -350,6 +198,8 @@ export default defineComponent({
|
||||
bodyWidth,
|
||||
resizeState,
|
||||
doLayout,
|
||||
tableBodyStyles,
|
||||
borderBottomPatchStyles,
|
||||
} = useStyle<Row>(props, layout, store, table)
|
||||
|
||||
const debouncedUpdateLayout = debounce(doLayout, 50)
|
||||
@@ -362,6 +212,14 @@ export default defineComponent({
|
||||
doLayout,
|
||||
debouncedUpdateLayout,
|
||||
}
|
||||
const computedSumText = computed(
|
||||
() => props.sumText || t('el.table.sumText')
|
||||
)
|
||||
|
||||
const computedEmptyText = computed(() => {
|
||||
return props.emptyText || t('el.table.emptyText')
|
||||
})
|
||||
|
||||
return {
|
||||
layout,
|
||||
store,
|
||||
@@ -377,7 +235,9 @@ export default defineComponent({
|
||||
isGroup,
|
||||
bodyWidth,
|
||||
bodyHeight,
|
||||
tableBodyStyles,
|
||||
emptyBlockStyle,
|
||||
borderBottomPatchStyles,
|
||||
debouncedUpdateLayout,
|
||||
handleFixedMousewheel,
|
||||
fixedHeight,
|
||||
@@ -394,6 +254,8 @@ export default defineComponent({
|
||||
t,
|
||||
setDragVisible,
|
||||
context: table,
|
||||
computedSumText,
|
||||
computedEmptyText,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
unref,
|
||||
nextTick,
|
||||
} from 'vue'
|
||||
import throttle from 'lodash/throttle'
|
||||
import {
|
||||
addResizeListener,
|
||||
removeResizeListener,
|
||||
@@ -94,6 +93,19 @@ function useStyle<T>(
|
||||
)
|
||||
})
|
||||
|
||||
const tableBodyStyles = computed(() => {
|
||||
return {
|
||||
width: layout.bodyWidth.value ? `${layout.bodyWidth.value}px` : '',
|
||||
}
|
||||
})
|
||||
|
||||
const borderBottomPatchStyles = computed(() => {
|
||||
return {
|
||||
bottom: `${layout.gutterWidth}px`,
|
||||
right: `${layout.gutterWidth}px`,
|
||||
}
|
||||
})
|
||||
|
||||
const doLayout = () => {
|
||||
if (shouldUpdateHeight.value) {
|
||||
layout.updateElsHeight()
|
||||
@@ -134,23 +146,15 @@ function useStyle<T>(
|
||||
el.className = classList.join(' ')
|
||||
}
|
||||
const setScrollClass = (className: string) => {
|
||||
const { bodyWrapper } = table.refs
|
||||
setScrollClassByEl(bodyWrapper, className)
|
||||
const { tableWrapper } = table.refs
|
||||
setScrollClassByEl(tableWrapper, className)
|
||||
}
|
||||
const syncPostion = throttle(function () {
|
||||
const syncPostion = function () {
|
||||
if (!table.refs.bodyWrapper) return
|
||||
const { scrollLeft, scrollTop, offsetWidth, scrollWidth } =
|
||||
table.refs.bodyWrapper
|
||||
const {
|
||||
headerWrapper,
|
||||
footerWrapper,
|
||||
fixedBodyWrapper,
|
||||
rightFixedBodyWrapper,
|
||||
} = table.refs
|
||||
const { scrollLeft, offsetWidth, scrollWidth } = table.refs.bodyWrapper
|
||||
const { headerWrapper, footerWrapper } = table.refs
|
||||
if (headerWrapper) headerWrapper.scrollLeft = scrollLeft
|
||||
if (footerWrapper) footerWrapper.scrollLeft = scrollLeft
|
||||
if (fixedBodyWrapper) fixedBodyWrapper.scrollTop = scrollTop
|
||||
if (rightFixedBodyWrapper) rightFixedBodyWrapper.scrollTop = scrollTop
|
||||
const maxScrollLeftPosition = scrollWidth - offsetWidth - 1
|
||||
if (scrollLeft >= maxScrollLeftPosition) {
|
||||
setScrollClass('is-scrolling-right')
|
||||
@@ -159,7 +163,7 @@ function useStyle<T>(
|
||||
} else {
|
||||
setScrollClass('is-scrolling-middle')
|
||||
}
|
||||
}, 10)
|
||||
}
|
||||
|
||||
const bindEvents = () => {
|
||||
table.refs.bodyWrapper.addEventListener('scroll', syncPostion, {
|
||||
@@ -335,6 +339,8 @@ function useStyle<T>(
|
||||
bodyWidth,
|
||||
resizeState,
|
||||
doLayout,
|
||||
tableBodyStyles,
|
||||
borderBottomPatchStyles,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { hasOwn } from '@vue/shared'
|
||||
import { createPopper } from '@popperjs/core'
|
||||
import { PopupManager } from '@element-plus/utils/popup-manager'
|
||||
import { getValueByPath } from '@element-plus/utils/util'
|
||||
import scrollbarWidth from '@element-plus/utils/scrollbar-width'
|
||||
import { off, on } from '@element-plus/utils/dom'
|
||||
|
||||
import type {
|
||||
@@ -370,3 +371,143 @@ export function createTablePopper(
|
||||
on(trigger, 'mouseleave', removePopper)
|
||||
return popperInstance
|
||||
}
|
||||
|
||||
export const isFixedColumn = <T>(
|
||||
index: number,
|
||||
fixed: string | boolean,
|
||||
store: any,
|
||||
realColumns?: TableColumnCtx<T>[]
|
||||
) => {
|
||||
let start = 0
|
||||
let after = index
|
||||
if (realColumns) {
|
||||
// handle group
|
||||
for (let i = 0; i < index; i++) {
|
||||
start += realColumns[i].colSpan
|
||||
}
|
||||
after = start + realColumns[index].colSpan - 1
|
||||
} else {
|
||||
start = index
|
||||
}
|
||||
let fixedLayout
|
||||
const columns = store.states.columns
|
||||
switch (fixed) {
|
||||
case 'left':
|
||||
if (after < store.states.fixedLeafColumnsLength.value) {
|
||||
fixedLayout = 'left'
|
||||
}
|
||||
break
|
||||
case 'right':
|
||||
if (
|
||||
start >=
|
||||
columns.value.length - store.states.rightFixedLeafColumnsLength.value
|
||||
) {
|
||||
fixedLayout = 'right'
|
||||
}
|
||||
break
|
||||
default:
|
||||
if (after < store.states.fixedLeafColumnsLength.value) {
|
||||
fixedLayout = 'left'
|
||||
} else if (
|
||||
start >=
|
||||
columns.value.length - store.states.rightFixedLeafColumnsLength.value
|
||||
) {
|
||||
fixedLayout = 'right'
|
||||
}
|
||||
}
|
||||
return fixedLayout
|
||||
? {
|
||||
direction: fixedLayout,
|
||||
start,
|
||||
after,
|
||||
}
|
||||
: {}
|
||||
}
|
||||
|
||||
export const getFixedColumnsClass = <T>(
|
||||
index: number,
|
||||
fixed: string | boolean,
|
||||
store: any,
|
||||
realColumns?: TableColumnCtx<T>[]
|
||||
) => {
|
||||
const classes: string[] = []
|
||||
const { direction, start } = isFixedColumn(index, fixed, store, realColumns)
|
||||
if (direction) {
|
||||
const isLeft = direction === 'left'
|
||||
classes.push(`el-table-fixed-column--${direction}`)
|
||||
if (isLeft && start === store.states.fixedLeafColumnsLength.value - 1) {
|
||||
classes.push('is-last-column')
|
||||
} else if (
|
||||
!isLeft &&
|
||||
start ===
|
||||
store.states.columns.value.length -
|
||||
store.states.rightFixedLeafColumnsLength.value
|
||||
) {
|
||||
classes.push('is-first-column')
|
||||
}
|
||||
}
|
||||
return classes
|
||||
}
|
||||
|
||||
function getOffset<T>(offset: number, column: TableColumnCtx<T>) {
|
||||
return (
|
||||
offset +
|
||||
(Number.isNaN(column.realWidth) ? Number(column.width) : column.realWidth)
|
||||
)
|
||||
}
|
||||
|
||||
export const getFixedColumnOffset = <T>(
|
||||
index: number,
|
||||
fixed: string | boolean,
|
||||
store: any,
|
||||
realColumns?: TableColumnCtx<T>[]
|
||||
) => {
|
||||
const { direction, start = 0 } = isFixedColumn(
|
||||
index,
|
||||
fixed,
|
||||
store,
|
||||
realColumns
|
||||
)
|
||||
if (!direction) {
|
||||
return
|
||||
}
|
||||
const styles: any = {}
|
||||
const isLeft = direction === 'left'
|
||||
const columns = store.states.columns.value
|
||||
if (isLeft) {
|
||||
styles.left = columns.slice(0, index).reduce(getOffset, 0)
|
||||
} else {
|
||||
styles.right = columns
|
||||
.slice(start + 1)
|
||||
.reverse()
|
||||
.reduce(getOffset, 0)
|
||||
}
|
||||
return styles
|
||||
}
|
||||
|
||||
export function getCellStyle<T>(
|
||||
column: TableColumnCtx<T>,
|
||||
cellIndex: number,
|
||||
hasGutter: boolean,
|
||||
gutterWidth: number,
|
||||
store: any
|
||||
) {
|
||||
const fixedStyle = getFixedColumnOffset(cellIndex, column.fixed, store)
|
||||
ensureRightFixedStyle(fixedStyle, hasGutter)
|
||||
ensurePosition(fixedStyle, 'left')
|
||||
ensurePosition(fixedStyle, 'right')
|
||||
return fixedStyle
|
||||
}
|
||||
|
||||
export const ensureRightFixedStyle = (style, hasGutter: boolean) => {
|
||||
if (hasGutter && style && !Number.isNaN(style.right)) {
|
||||
style.right += scrollbarWidth()
|
||||
}
|
||||
}
|
||||
|
||||
export const ensurePosition = (style, key: string) => {
|
||||
if (!style) return
|
||||
if (!Number.isNaN(style[key])) {
|
||||
style[key] = `${style[key]}px`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,19 @@
|
||||
font-size: 14px;
|
||||
color: var(--el-table-text-color);
|
||||
|
||||
@include e(inner-wrapper) {
|
||||
position: relative;
|
||||
// 表格底部伪 border,总是有的
|
||||
&::before {
|
||||
@include extend-rule(border-pseudo);
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
z-index: 3;
|
||||
}
|
||||
}
|
||||
|
||||
// when data is empty
|
||||
@include e(empty-block) {
|
||||
min-height: 60px;
|
||||
@@ -102,7 +115,7 @@
|
||||
|
||||
@include m(scrollable-y) {
|
||||
.#{$namespace}-table__body-wrapper {
|
||||
overflow-y: auto;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +138,7 @@
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
text-align: left;
|
||||
|
||||
z-index: 1;
|
||||
@include when(center) {
|
||||
text-align: center;
|
||||
}
|
||||
@@ -193,7 +206,6 @@
|
||||
}
|
||||
|
||||
th.#{$namespace}-table__cell {
|
||||
overflow: hidden;
|
||||
user-select: none;
|
||||
background-color: var(--el-table-header-bg-color);
|
||||
|
||||
@@ -233,7 +245,19 @@
|
||||
|
||||
// 拥有多级表头
|
||||
@include m((group, border)) {
|
||||
border: var(--el-table-border);
|
||||
@include e((inner-wrapper, footer-wrapper)) {
|
||||
tr:first-child {
|
||||
td,
|
||||
th {
|
||||
&:first-child {
|
||||
border-left: var(--el-table-border);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@include e((footer-wrapper)) {
|
||||
border-top: var(--el-table-border);
|
||||
}
|
||||
|
||||
@include share-rule(border-pseudo) {
|
||||
content: '';
|
||||
@@ -241,30 +265,56 @@
|
||||
background-color: var(--el-table-border-color);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
// 表格右部伪 border
|
||||
&::after {
|
||||
@include extend-rule(border-pseudo);
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
// 表格底部伪 border,总是有的
|
||||
&::before {
|
||||
@include extend-rule(border-pseudo);
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
// table--border
|
||||
@include m(border) {
|
||||
border-right: none;
|
||||
border-bottom: none;
|
||||
@include e(inner-wrapper) {
|
||||
&::after {
|
||||
@include extend-rule(border-pseudo);
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
z-index: 3;
|
||||
}
|
||||
}
|
||||
|
||||
&::before {
|
||||
@include extend-rule(border-pseudo);
|
||||
top: -1px;
|
||||
left: 0;
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
&::after {
|
||||
@include extend-rule(border-pseudo);
|
||||
top: -1px;
|
||||
right: 0;
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
@include e(inner-wrapper) {
|
||||
border-right: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
@include e(footer-wrapper) {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@include e(footer-wrapper) {
|
||||
margin-top: -2px;
|
||||
td.#{$namespace}-table__cell {
|
||||
&.last {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fix: #1013
|
||||
// &.#{$namespace}-loading-parent--relative {
|
||||
@@ -273,6 +323,9 @@
|
||||
|
||||
.#{$namespace}-table__cell {
|
||||
border-right: var(--el-table-border);
|
||||
&:first-child .cell {
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
th.#{$namespace}-table__cell.gutter:last-of-type {
|
||||
@@ -289,83 +342,50 @@
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
@include e((fixed, fixed-right)) {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
box-shadow: var(--el-table-fixed-box-shadow);
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background-color: var(--el-border-color-lighter);
|
||||
z-index: 4;
|
||||
}
|
||||
}
|
||||
|
||||
@include e(fixed-right-patch) {
|
||||
position: absolute;
|
||||
top: -1px;
|
||||
right: 0;
|
||||
background-color: var(--el-table-header-bg-color);
|
||||
border-bottom: var(--el-table-border);
|
||||
}
|
||||
|
||||
@include e(fixed-right) {
|
||||
top: 0;
|
||||
left: auto;
|
||||
right: 0;
|
||||
|
||||
.#{$namespace}-table__fixed-header-wrapper,
|
||||
.#{$namespace}-table__fixed-body-wrapper,
|
||||
.#{$namespace}-table__fixed-footer-wrapper {
|
||||
left: auto;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@include e(fixed-header-wrapper) {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
@include e(fixed-footer-wrapper) {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
z-index: 3;
|
||||
|
||||
& tbody td.#{$namespace}-table__cell {
|
||||
border-top: var(--el-table-border);
|
||||
background-color: var(--el-table-row-hover-bg-color);
|
||||
color: var(--el-table-text-color);
|
||||
}
|
||||
}
|
||||
|
||||
@include e(fixed-body-wrapper) {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 37px;
|
||||
overflow: hidden;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
@include e((header-wrapper, body-wrapper, footer-wrapper)) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@include e(footer-wrapper) {
|
||||
margin-top: -1px;
|
||||
td.#{$namespace}-table__cell {
|
||||
border-top: var(--el-table-border);
|
||||
tr {
|
||||
td,
|
||||
th {
|
||||
&.#{$namespace}-table-fixed-column--left,
|
||||
&.#{$namespace}-table-fixed-column--right {
|
||||
position: sticky !important;
|
||||
z-index: 2;
|
||||
background: #fff;
|
||||
&.is-last-column,
|
||||
&.is-first-column {
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
width: 10px;
|
||||
bottom: -1px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
box-shadow: inset -10px 0 10px -10px rgb(0 0 0 / 15%);
|
||||
touch-action: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
&.is-first-column {
|
||||
&::before {
|
||||
left: -10px;
|
||||
}
|
||||
}
|
||||
&.is-last-column {
|
||||
&::before {
|
||||
right: -10px;
|
||||
box-shadow: inset 10px 0 10px -10px rgb(0 0 0 / 15%);
|
||||
}
|
||||
}
|
||||
}
|
||||
&.#{$namespace}-table__fixed-right-patch {
|
||||
position: sticky !important;
|
||||
z-index: 2;
|
||||
background: #fff;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -383,42 +403,55 @@
|
||||
}
|
||||
}
|
||||
|
||||
@include when(scrolling-left) {
|
||||
.#{$namespace}-table-fixed-column--left.is-last-column {
|
||||
&::before {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
&.#{$namespace}-table--border {
|
||||
.#{$namespace}-table-fixed-column--left {
|
||||
&.is-last-column {
|
||||
&.#{$namespace}-table__cell {
|
||||
border-right: var(--el-table-border);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include when(scrolling-right) {
|
||||
.#{$namespace}-table-fixed-column--right.is-first-column {
|
||||
&::before {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
.#{$namespace}-table-fixed-column--left.is-last-column.#{$namespace}-table__cell {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
|
||||
@include when(scrolling-middle) {
|
||||
.#{$namespace}-table-fixed-column--left.is-last-column.#{$namespace}-table__cell {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
|
||||
@include when(scrolling-none) {
|
||||
.#{$namespace}-table-fixed-column--left,
|
||||
.#{$namespace}-table-fixed-column--right {
|
||||
&.is-first-column,
|
||||
&.is-last-column {
|
||||
&::before {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include e(body-wrapper) {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
@include when(scrolling-none) {
|
||||
~ .#{$namespace}-table__fixed,
|
||||
~ .#{$namespace}-table__fixed-right {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
@include when(scrolling-left) {
|
||||
~ .#{$namespace}-table__fixed {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
@include when(scrolling-right) {
|
||||
~ .#{$namespace}-table__fixed-right {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$namespace}-table--border {
|
||||
@include when(scrolling-right) {
|
||||
~ .#{$namespace}-table__fixed-right {
|
||||
border-left: var(--el-table-border);
|
||||
}
|
||||
}
|
||||
|
||||
@include when(scrolling-left) {
|
||||
~ .#{$namespace}-table__fixed {
|
||||
border-right: var(--el-table-border);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.caret-wrapper {
|
||||
@@ -519,6 +552,33 @@
|
||||
}
|
||||
}
|
||||
|
||||
@include e(border-left-patch) {
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
z-index: 3;
|
||||
position: absolute;
|
||||
background-color: var(--el-table-border-color);
|
||||
}
|
||||
|
||||
@include e(border-bottom-patch) {
|
||||
left: 0;
|
||||
height: 1px;
|
||||
z-index: 3;
|
||||
position: absolute;
|
||||
background-color: var(--el-table-border-color);
|
||||
}
|
||||
|
||||
@include e(border-right-patch) {
|
||||
top: 0;
|
||||
height: 100%;
|
||||
width: 1px;
|
||||
z-index: 3;
|
||||
position: absolute;
|
||||
background-color: var(--el-table-border-color);
|
||||
}
|
||||
|
||||
@include m(enable-row-transition) {
|
||||
.#{$namespace}-table__body td.#{$namespace}-table__cell {
|
||||
transition: background-color 0.25s ease;
|
||||
@@ -531,14 +591,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
@include m(fluid-height) {
|
||||
.#{$namespace}-table__fixed,
|
||||
.#{$namespace}-table__fixed-right {
|
||||
bottom: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
[class*='#{$namespace}-table__row--level'] {
|
||||
.#{$namespace}-table__expand-icon {
|
||||
display: inline-block;
|
||||
|
||||
Reference in New Issue
Block a user