mirror of
https://github.com/element-plus/element-plus.git
synced 2024-11-21 01:02:59 +08:00
refactor: [el-table] refactoring table with el-scrollbar (#5384)
* refactor: [el-table] refactoring table with el-scrollbar * refactor: remove gutter * fix: remove unused * fix: optimize code
This commit is contained in:
parent
d05acba3d0
commit
13be920c9a
@ -2,13 +2,7 @@ import { h } from 'vue'
|
||||
|
||||
import type { TableColumnCtx } from './table-column/defaults'
|
||||
|
||||
export function hGutter() {
|
||||
return h('col', {
|
||||
name: 'gutter',
|
||||
})
|
||||
}
|
||||
|
||||
export function hColgroup<T>(columns: TableColumnCtx<T>[], hasGutter = false) {
|
||||
export function hColgroup<T>(columns: TableColumnCtx<T>[]) {
|
||||
return h('colgroup', {}, [
|
||||
...columns.map((column) =>
|
||||
h('col', {
|
||||
@ -16,6 +10,5 @@ export function hColgroup<T>(columns: TableColumnCtx<T>[], hasGutter = false) {
|
||||
key: column.id,
|
||||
})
|
||||
),
|
||||
hasGutter && hGutter(),
|
||||
])
|
||||
}
|
||||
|
@ -53,12 +53,13 @@ function useLayoutObserver<T>(root: Table<T>) {
|
||||
}
|
||||
|
||||
const onScrollableChange = (layout: TableLayout<T>) => {
|
||||
const cols = root.vnode.el.querySelectorAll('colgroup > col[name=gutter]')
|
||||
const cols =
|
||||
root.vnode.el?.querySelectorAll('colgroup > col[name=gutter]') || []
|
||||
for (let i = 0, j = cols.length; i < j; i++) {
|
||||
const col = cols[i]
|
||||
col.setAttribute('width', layout.scrollY.value ? layout.gutterWidth : '0')
|
||||
}
|
||||
const ths = root.vnode.el.querySelectorAll('th.gutter')
|
||||
const ths = root.vnode.el?.querySelectorAll('th.gutter') || []
|
||||
for (let i = 0, j = ths.length; i < j; i++) {
|
||||
const th = ths[i]
|
||||
th.style.width = layout.scrollY.value ? `${layout.gutterWidth}px` : '0'
|
||||
|
@ -1,15 +1,13 @@
|
||||
import { getCurrentInstance, ref, h } from 'vue'
|
||||
import { ref, h, inject } from 'vue'
|
||||
import debounce from 'lodash/debounce'
|
||||
import { getStyle, hasClass } from '@element-plus/utils/dom'
|
||||
import { createTablePopper, getCell, getColumnByCell } from '../util'
|
||||
|
||||
import { TABLE_INJECTION_KEY } from '../tokens'
|
||||
import type { TableColumnCtx } from '../table-column/defaults'
|
||||
import type { Table } from '../table/defaults'
|
||||
import type { TableBodyProps } from './defaults'
|
||||
|
||||
function useEvents<T>(props: Partial<TableBodyProps<T>>) {
|
||||
const instance = getCurrentInstance()
|
||||
const parent = instance.parent as Table<T>
|
||||
const parent = inject(TABLE_INJECTION_KEY)
|
||||
const tooltipContent = ref('')
|
||||
const tooltipTrigger = ref(h('div'))
|
||||
const handleEvent = (event: Event, row: T, name: string) => {
|
||||
|
@ -5,25 +5,25 @@ import {
|
||||
watch,
|
||||
onUnmounted,
|
||||
onUpdated,
|
||||
inject,
|
||||
} from 'vue'
|
||||
import { isClient } from '@vueuse/core'
|
||||
import { addClass, removeClass } from '@element-plus/utils/dom'
|
||||
import { hColgroup } from '../h-helper'
|
||||
import useLayoutObserver from '../layout-observer'
|
||||
import { removePopper } from '../util'
|
||||
import { TABLE_INJECTION_KEY } from '../tokens'
|
||||
import useRender from './render-helper'
|
||||
import defaultProps from './defaults'
|
||||
|
||||
import type { VNode } from 'vue'
|
||||
import type { DefaultRow, Table } from '../table/defaults'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ElTableBody',
|
||||
props: defaultProps,
|
||||
setup(props) {
|
||||
const instance = getCurrentInstance()
|
||||
const parent = instance.parent as Table<DefaultRow>
|
||||
|
||||
const parent = inject(TABLE_INJECTION_KEY)
|
||||
const { wrappedRowRender, tooltipContent, tooltipTrigger } =
|
||||
useRender(props)
|
||||
const { onColumnsChange, onScrollableChange } = useLayoutObserver(parent)
|
||||
|
@ -1,19 +1,13 @@
|
||||
import { h, getCurrentInstance, computed } from 'vue'
|
||||
import { h, computed, inject } from 'vue'
|
||||
import { getRowIdentity } from '../util'
|
||||
import { TABLE_INJECTION_KEY } from '../tokens'
|
||||
import useEvents from './events-helper'
|
||||
import useStyles from './styles-helper'
|
||||
|
||||
import type { TableBodyProps } from './defaults'
|
||||
import type {
|
||||
RenderRowData,
|
||||
Table,
|
||||
TreeNode,
|
||||
TableProps,
|
||||
} from '../table/defaults'
|
||||
import type { RenderRowData, TreeNode, TableProps } from '../table/defaults'
|
||||
|
||||
function useRender<T>(props: Partial<TableBodyProps<T>>) {
|
||||
const instance = getCurrentInstance()
|
||||
const parent = instance.parent as Table<T>
|
||||
const parent = inject(TABLE_INJECTION_KEY)
|
||||
const {
|
||||
handleDoubleClick,
|
||||
handleClick,
|
||||
|
@ -1,16 +1,15 @@
|
||||
import { getCurrentInstance } from 'vue'
|
||||
import { inject } from 'vue'
|
||||
import {
|
||||
getFixedColumnOffset,
|
||||
getFixedColumnsClass,
|
||||
ensurePosition,
|
||||
} from '../util'
|
||||
import { TABLE_INJECTION_KEY } from '../tokens'
|
||||
import type { TableColumnCtx } from '../table-column/defaults'
|
||||
import type { Table } from '../table/defaults'
|
||||
import type { TableBodyProps } from './defaults'
|
||||
|
||||
function useStyles<T>(props: Partial<TableBodyProps<T>>) {
|
||||
const instance = getCurrentInstance()
|
||||
const parent = instance.parent as Table<T>
|
||||
const parent = inject(TABLE_INJECTION_KEY)
|
||||
|
||||
const getRowStyle = (row: T, rowIndex: number) => {
|
||||
const rowStyle = parent.props.rowStyle
|
||||
|
@ -42,26 +42,18 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const { hasGutter, getCellClasses, getCellStyles, columns, gutterWidth } =
|
||||
useStyle(props as TableFooter<DefaultRow>)
|
||||
const { getCellClasses, getCellStyles, columns } = useStyle(
|
||||
props as TableFooter<DefaultRow>
|
||||
)
|
||||
return {
|
||||
getCellClasses,
|
||||
getCellStyles,
|
||||
hasGutter,
|
||||
gutterWidth,
|
||||
columns,
|
||||
}
|
||||
},
|
||||
render() {
|
||||
const {
|
||||
hasGutter,
|
||||
gutterWidth,
|
||||
columns,
|
||||
getCellStyles,
|
||||
getCellClasses,
|
||||
summaryMethod,
|
||||
sumText,
|
||||
} = this
|
||||
const { columns, getCellStyles, getCellClasses, summaryMethod, sumText } =
|
||||
this
|
||||
const data = this.store.states.data.value
|
||||
let sums = []
|
||||
if (summaryMethod) {
|
||||
@ -109,45 +101,32 @@ export default defineComponent({
|
||||
border: '0',
|
||||
},
|
||||
[
|
||||
hColgroup(columns, hasGutter),
|
||||
h(
|
||||
'tbody',
|
||||
{
|
||||
class: [{ 'has-gutter': hasGutter }],
|
||||
},
|
||||
[
|
||||
h('tr', {}, [
|
||||
...columns.map((column, cellIndex) =>
|
||||
h(
|
||||
'td',
|
||||
{
|
||||
key: cellIndex,
|
||||
colspan: column.colSpan,
|
||||
rowspan: column.rowSpan,
|
||||
class: getCellClasses(columns, cellIndex, hasGutter),
|
||||
style: getCellStyles(column, cellIndex, hasGutter),
|
||||
},
|
||||
[
|
||||
h(
|
||||
'div',
|
||||
{
|
||||
class: ['cell', column.labelClassName],
|
||||
},
|
||||
[sums[cellIndex]]
|
||||
),
|
||||
]
|
||||
)
|
||||
),
|
||||
hasGutter &&
|
||||
h('td', {
|
||||
class: 'el-table__fixed-right-patch el-table__cell',
|
||||
style: {
|
||||
width: `${gutterWidth}px`,
|
||||
},
|
||||
}),
|
||||
]),
|
||||
]
|
||||
),
|
||||
hColgroup(columns),
|
||||
h('tbody', [
|
||||
h('tr', {}, [
|
||||
...columns.map((column, cellIndex) =>
|
||||
h(
|
||||
'td',
|
||||
{
|
||||
key: cellIndex,
|
||||
colspan: column.colSpan,
|
||||
rowspan: column.rowSpan,
|
||||
class: getCellClasses(columns, cellIndex),
|
||||
style: getCellStyles(column, cellIndex),
|
||||
},
|
||||
[
|
||||
h(
|
||||
'div',
|
||||
{
|
||||
class: ['cell', column.labelClassName],
|
||||
},
|
||||
[sums[cellIndex]]
|
||||
),
|
||||
]
|
||||
)
|
||||
),
|
||||
]),
|
||||
]),
|
||||
]
|
||||
)
|
||||
},
|
||||
|
@ -1,38 +1,16 @@
|
||||
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 '.'
|
||||
|
||||
function useStyle<T>(props: TableFooter<T>) {
|
||||
const instance = getCurrentInstance()
|
||||
const table = instance.parent as Table<T>
|
||||
|
||||
const { columns } = useMapState<T>()
|
||||
|
||||
const hasGutter = computed(() => {
|
||||
return (
|
||||
!props.fixed &&
|
||||
table.layout.gutterWidth > 0 &&
|
||||
table.layout.height.value &&
|
||||
table.layout.bodyScrollHeight.value > table.layout.bodyHeight.value
|
||||
)
|
||||
})
|
||||
|
||||
const gutterWidth = computed(() => {
|
||||
return table.layout.gutterWidth
|
||||
})
|
||||
const getCellClasses = (
|
||||
columns: TableColumnCtx<T>[],
|
||||
cellIndex: number,
|
||||
hasGutter: boolean
|
||||
) => {
|
||||
const getCellClasses = (columns: TableColumnCtx<T>[], cellIndex: number) => {
|
||||
const column = columns[cellIndex]
|
||||
const classes = [
|
||||
'el-table__cell',
|
||||
@ -47,31 +25,21 @@ function useStyle<T>(props: TableFooter<T>) {
|
||||
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 getCellStyles = (column: TableColumnCtx<T>, cellIndex: number) => {
|
||||
const fixedStyle = getFixedColumnOffset(
|
||||
cellIndex,
|
||||
column.fixed,
|
||||
props.store
|
||||
)
|
||||
ensureRightFixedStyle(fixedStyle, hasGutter)
|
||||
ensurePosition(fixedStyle, 'left')
|
||||
ensurePosition(fixedStyle, 'right')
|
||||
return fixedStyle
|
||||
}
|
||||
|
||||
return {
|
||||
hasGutter,
|
||||
gutterWidth,
|
||||
getCellClasses,
|
||||
getCellStyles,
|
||||
columns,
|
||||
|
@ -1,7 +1,6 @@
|
||||
import {
|
||||
defineComponent,
|
||||
getCurrentInstance,
|
||||
computed,
|
||||
onMounted,
|
||||
nextTick,
|
||||
ref,
|
||||
@ -62,18 +61,7 @@ export default defineComponent({
|
||||
const parent = instance.parent as Table<unknown>
|
||||
const storeData = parent.store.states
|
||||
const filterPanels = ref({})
|
||||
const { tableLayout, onColumnsChange, onScrollableChange } =
|
||||
useLayoutObserver(parent)
|
||||
const hasGutter = computed(() => {
|
||||
return (
|
||||
!props.fixed &&
|
||||
tableLayout.gutterWidth > 0 &&
|
||||
tableLayout.bodyScrollHeight.value > tableLayout.bodyHeight.value
|
||||
)
|
||||
})
|
||||
const gutterWidth = computed(() => {
|
||||
return tableLayout.gutterWidth
|
||||
})
|
||||
const { onColumnsChange, onScrollableChange } = useLayoutObserver(parent)
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
const { prop, order } = props.defaultSort
|
||||
@ -110,8 +98,6 @@ export default defineComponent({
|
||||
return {
|
||||
columns: storeData.columns,
|
||||
filterPanels,
|
||||
hasGutter,
|
||||
gutterWidth,
|
||||
onColumnsChange,
|
||||
onScrollableChange,
|
||||
columnRows,
|
||||
@ -134,9 +120,7 @@ export default defineComponent({
|
||||
const {
|
||||
columns,
|
||||
isGroup,
|
||||
hasGutter,
|
||||
columnRows,
|
||||
gutterWidth,
|
||||
getHeaderCellStyle,
|
||||
getHeaderCellClass,
|
||||
getHeaderRowClass,
|
||||
@ -160,11 +144,11 @@ export default defineComponent({
|
||||
class: 'el-table__header',
|
||||
},
|
||||
[
|
||||
hColgroup(columns, hasGutter),
|
||||
hColgroup(columns),
|
||||
h(
|
||||
'thead',
|
||||
{
|
||||
class: { 'is-group': isGroup, 'has-gutter': hasGutter },
|
||||
class: { 'is-group': isGroup },
|
||||
},
|
||||
columnRows.map((subColumns, rowIndex) =>
|
||||
h(
|
||||
@ -174,121 +158,92 @@ export default defineComponent({
|
||||
key: rowIndex,
|
||||
style: getHeaderRowStyle(rowIndex),
|
||||
},
|
||||
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',
|
||||
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
|
||||
),
|
||||
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,
|
||||
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`,
|
||||
$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,
|
||||
upDataColumn: (key, value) => {
|
||||
column[key] = value
|
||||
},
|
||||
}),
|
||||
]
|
||||
: []
|
||||
),
|
||||
]
|
||||
)
|
||||
})
|
||||
)
|
||||
)
|
||||
),
|
||||
|
@ -3,7 +3,6 @@ import {
|
||||
getFixedColumnsClass,
|
||||
getFixedColumnOffset,
|
||||
ensurePosition,
|
||||
ensureRightFixedStyle,
|
||||
} from '../util'
|
||||
import type { TableColumnCtx } from '../table-column/defaults'
|
||||
import type { Table } from '../table/defaults'
|
||||
@ -37,8 +36,7 @@ function useStyle<T>(props: TableHeaderProps<T>) {
|
||||
rowIndex: number,
|
||||
columnIndex: number,
|
||||
row: T,
|
||||
column: TableColumnCtx<T>,
|
||||
hasGutter: boolean
|
||||
column: TableColumnCtx<T>
|
||||
) => {
|
||||
let headerCellStyles = parent.props.headerCellStyle ?? {}
|
||||
if (typeof headerCellStyles === 'function') {
|
||||
@ -55,7 +53,6 @@ function useStyle<T>(props: TableHeaderProps<T>) {
|
||||
props.store,
|
||||
row as unknown as TableColumnCtx<T>[]
|
||||
)
|
||||
ensureRightFixedStyle(fixedStyle, hasGutter)
|
||||
ensurePosition(fixedStyle, 'left')
|
||||
ensurePosition(fixedStyle, 'right')
|
||||
return Object.assign({}, headerCellStyles, fixedStyle)
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { nextTick, ref, isRef } from 'vue'
|
||||
import { hasOwn } from '@vue/shared'
|
||||
import { isClient } from '@vueuse/core'
|
||||
import scrollbarWidth from '@element-plus/utils/scrollbar-width'
|
||||
import { parseHeight } from './util'
|
||||
import type { Ref } from 'vue'
|
||||
|
||||
@ -53,7 +52,7 @@ class TableLayout<T> {
|
||||
this.bodyHeight = ref(null)
|
||||
this.bodyScrollHeight = ref(0)
|
||||
this.fixedBodyHeight = ref(null)
|
||||
this.gutterWidth = scrollbarWidth()
|
||||
this.gutterWidth = 0
|
||||
for (const name in options) {
|
||||
if (hasOwn(options, name)) {
|
||||
if (isRef(this[name])) {
|
||||
|
@ -15,6 +15,7 @@
|
||||
'el-table--enable-row-transition':
|
||||
(store.states.data.value || []).length !== 0 &&
|
||||
(store.states.data.value || []).length < 100,
|
||||
'has-footer': showSummary,
|
||||
},
|
||||
tableSize ? `el-table--${tableSize}` : '',
|
||||
className,
|
||||
@ -43,42 +44,39 @@
|
||||
/>
|
||||
</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>
|
||||
<el-scrollbar ref="scrollWrapper" :height="height">
|
||||
<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>
|
||||
</el-scrollbar>
|
||||
</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"
|
||||
@ -105,10 +103,11 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, getCurrentInstance, computed } from 'vue'
|
||||
import { defineComponent, getCurrentInstance, computed, provide } from 'vue'
|
||||
import debounce from 'lodash/debounce'
|
||||
import { Mousewheel } from '@element-plus/directives'
|
||||
import { useLocale } from '@element-plus/hooks'
|
||||
import ElScrollbar from '@element-plus/components/scrollbar'
|
||||
import { createStore } from './store/helper'
|
||||
import TableLayout from './table-layout'
|
||||
import TableHeader from './table-header'
|
||||
@ -117,6 +116,7 @@ import TableFooter from './table-footer'
|
||||
import useUtils from './table/utils-helper'
|
||||
import useStyle from './table/style-helper'
|
||||
import defaultProps from './table/defaults'
|
||||
import { TABLE_INJECTION_KEY } from './tokens'
|
||||
|
||||
import type { Table } from './table/defaults'
|
||||
|
||||
@ -130,6 +130,7 @@ export default defineComponent({
|
||||
TableHeader,
|
||||
TableBody,
|
||||
TableFooter,
|
||||
ElScrollbar,
|
||||
},
|
||||
props: defaultProps,
|
||||
emits: [
|
||||
@ -156,6 +157,7 @@ export default defineComponent({
|
||||
type Row = typeof props.data[number]
|
||||
const { t } = useLocale()
|
||||
const table = getCurrentInstance() as Table<Row>
|
||||
provide(TABLE_INJECTION_KEY, table)
|
||||
const store = createStore<Row>(table, props)
|
||||
table.store = store
|
||||
const layout = new TableLayout<Row>({
|
||||
@ -190,6 +192,7 @@ export default defineComponent({
|
||||
handleHeaderFooterMousewheel,
|
||||
tableSize,
|
||||
bodyHeight,
|
||||
height,
|
||||
emptyBlockStyle,
|
||||
handleFixedMousewheel,
|
||||
fixedHeight,
|
||||
@ -199,7 +202,6 @@ export default defineComponent({
|
||||
resizeState,
|
||||
doLayout,
|
||||
tableBodyStyles,
|
||||
borderBottomPatchStyles,
|
||||
} = useStyle<Row>(props, layout, store, table)
|
||||
|
||||
const debouncedUpdateLayout = debounce(doLayout, 50)
|
||||
@ -235,9 +237,9 @@ export default defineComponent({
|
||||
isGroup,
|
||||
bodyWidth,
|
||||
bodyHeight,
|
||||
height,
|
||||
tableBodyStyles,
|
||||
emptyBlockStyle,
|
||||
borderBottomPatchStyles,
|
||||
debouncedUpdateLayout,
|
||||
handleFixedMousewheel,
|
||||
fixedHeight,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
onBeforeUnmount,
|
||||
computed,
|
||||
ref,
|
||||
watchEffect,
|
||||
@ -99,13 +99,6 @@ function useStyle<T>(
|
||||
}
|
||||
})
|
||||
|
||||
const borderBottomPatchStyles = computed(() => {
|
||||
return {
|
||||
bottom: `${layout.gutterWidth}px`,
|
||||
right: `${layout.gutterWidth}px`,
|
||||
}
|
||||
})
|
||||
|
||||
const doLayout = () => {
|
||||
if (shouldUpdateHeight.value) {
|
||||
layout.updateElsHeight()
|
||||
@ -150,8 +143,10 @@ function useStyle<T>(
|
||||
setScrollClassByEl(tableWrapper, className)
|
||||
}
|
||||
const syncPostion = function () {
|
||||
if (!table.refs.bodyWrapper) return
|
||||
const { scrollLeft, offsetWidth, scrollWidth } = table.refs.bodyWrapper
|
||||
if (!table.refs.scrollWrapper) return
|
||||
const scrollContainer = table.refs.scrollWrapper.wrap$
|
||||
if (!scrollContainer) return
|
||||
const { scrollLeft, offsetWidth, scrollWidth } = scrollContainer
|
||||
const { headerWrapper, footerWrapper } = table.refs
|
||||
if (headerWrapper) headerWrapper.scrollLeft = scrollLeft
|
||||
if (footerWrapper) footerWrapper.scrollLeft = scrollLeft
|
||||
@ -166,7 +161,7 @@ function useStyle<T>(
|
||||
}
|
||||
|
||||
const bindEvents = () => {
|
||||
table.refs.bodyWrapper.addEventListener('scroll', syncPostion, {
|
||||
table.refs.scrollWrapper.wrap$?.addEventListener('scroll', syncPostion, {
|
||||
passive: true,
|
||||
})
|
||||
if (props.fit) {
|
||||
@ -175,11 +170,15 @@ function useStyle<T>(
|
||||
on(window, 'resize', doLayout)
|
||||
}
|
||||
}
|
||||
onUnmounted(() => {
|
||||
onBeforeUnmount(() => {
|
||||
unbindEvents()
|
||||
})
|
||||
const unbindEvents = () => {
|
||||
table.refs.bodyWrapper?.removeEventListener('scroll', syncPostion, true)
|
||||
table.refs.scrollWrapper.wrap$?.removeEventListener(
|
||||
'scroll',
|
||||
syncPostion,
|
||||
true
|
||||
)
|
||||
if (props.fit) {
|
||||
removeResizeListener(table.vnode.el as ResizableElement, resizeListener)
|
||||
} else {
|
||||
@ -217,6 +216,20 @@ function useStyle<T>(
|
||||
? `${(bodyWidth_.value as number) - (scrollY.value ? gutterWidth : 0)}px`
|
||||
: ''
|
||||
})
|
||||
|
||||
const height = computed(() => {
|
||||
const headerHeight = layout.headerHeight.value || 0
|
||||
const bodyHeight = layout.bodyHeight.value
|
||||
const footerHeight = layout.footerHeight.value || 0
|
||||
if (props.height) {
|
||||
return bodyHeight ? bodyHeight : undefined
|
||||
} else if (props.maxHeight) {
|
||||
const maxHeight = parseHeight(props.maxHeight)
|
||||
return maxHeight - footerHeight - (props.showHeader ? headerHeight : 0)
|
||||
}
|
||||
return undefined
|
||||
})
|
||||
|
||||
const bodyHeight = computed(() => {
|
||||
const headerHeight = layout.headerHeight.value || 0
|
||||
const bodyHeight = layout.bodyHeight.value
|
||||
@ -331,6 +344,7 @@ function useStyle<T>(
|
||||
handleHeaderFooterMousewheel,
|
||||
tableSize,
|
||||
bodyHeight,
|
||||
height,
|
||||
emptyBlockStyle,
|
||||
handleFixedMousewheel,
|
||||
fixedHeight,
|
||||
@ -340,7 +354,6 @@ function useStyle<T>(
|
||||
resizeState,
|
||||
doLayout,
|
||||
tableBodyStyles,
|
||||
borderBottomPatchStyles,
|
||||
}
|
||||
}
|
||||
|
||||
|
5
packages/components/table/src/tokens.ts
Normal file
5
packages/components/table/src/tokens.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import type { InjectionKey } from 'vue'
|
||||
import type { DefaultRow, Table } from './table/defaults'
|
||||
|
||||
export const TABLE_INJECTION_KEY: InjectionKey<Table<DefaultRow>> =
|
||||
Symbol('ElTable')
|
@ -2,7 +2,6 @@ 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 {
|
||||
@ -489,26 +488,6 @@ export const getFixedColumnOffset = <T>(
|
||||
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])) {
|
||||
|
@ -83,10 +83,12 @@
|
||||
padding: 10px;
|
||||
|
||||
label.#{$namespace}-checkbox {
|
||||
display: block;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 5px;
|
||||
margin-bottom: 8px;
|
||||
margin-bottom: 12px;
|
||||
margin-left: 5px;
|
||||
height: unset;
|
||||
}
|
||||
|
||||
.#{$namespace}-checkbox:last-child {
|
||||
|
@ -32,6 +32,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.has-footer {
|
||||
@include e(inner-wrapper) {
|
||||
&::before {
|
||||
bottom: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// when data is empty
|
||||
@include e(empty-block) {
|
||||
min-height: 60px;
|
||||
@ -263,7 +271,7 @@
|
||||
content: '';
|
||||
position: absolute;
|
||||
background-color: var(--el-table-border-color);
|
||||
z-index: 1;
|
||||
z-index: 3;
|
||||
}
|
||||
}
|
||||
|
||||
@ -309,11 +317,6 @@
|
||||
|
||||
@include e(footer-wrapper) {
|
||||
margin-top: -2px;
|
||||
td.#{$namespace}-table__cell {
|
||||
&.last {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fix: #1013
|
||||
@ -403,6 +406,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
@include e((header-wrapper, body-wrapper)) {
|
||||
.#{$namespace}-table-column--selection {
|
||||
.el-checkbox {
|
||||
height: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include when(scrolling-left) {
|
||||
.#{$namespace}-table-fixed-column--left.is-last-column {
|
||||
&::before {
|
||||
@ -452,6 +463,9 @@
|
||||
@include e(body-wrapper) {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
.#{$namespace}-scrollbar__bar {
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
|
||||
.caret-wrapper {
|
||||
@ -546,9 +560,8 @@
|
||||
|
||||
& i {
|
||||
color: var(--el-color-info);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
vertical-align: middle;
|
||||
transform: scale(0.75);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user