modify(dataTable): use sticky to fix column

This commit is contained in:
songwanli2025@163.com 2020-02-27 13:02:46 +08:00
parent d8f6c1ed58
commit af92da94bf
8 changed files with 260 additions and 112 deletions

1
nano.save Normal file
View File

@ -0,0 +1 @@
z

View File

@ -9,6 +9,7 @@
:fixed="fixed" :fixed="fixed"
:scroll-x="scrollX" :scroll-x="scrollX"
@scroll="handleHeaderScroll" @scroll="handleHeaderScroll"
@set-active="setActive"
/> />
<table-body <table-body
ref="body" ref="body"
@ -104,6 +105,10 @@ export default {
}, },
handleHeaderScroll (...args) { handleHeaderScroll (...args) {
this.$emit('header-scroll', ...args) this.$emit('header-scroll', ...args)
},
setActive (activeLeft, activeRight) {
this.$refs.body.activeLeft = activeLeft
this.$refs.body.activeRight = activeRight
} }
} }
} }

View File

@ -27,48 +27,6 @@
> >
<slot name="append" /> <slot name="append" />
</base-table> </base-table>
<div
v-if="rightFixedColumns.length"
class="n-data-table-table-wrapper n-data-table-table-wrapper--right-fixed"
:class="{
'n-data-table-table-wrapper--active': mainTableScrollContainerWidth && mainTableScrollContainerWidth + horizontalScrollLeft < scrollX
}"
>
<base-table
ref="rightFixedTable"
placement="right"
:header-height="headerHeight"
:columns="rightFixedColumns"
:data="paginatedData"
:body-style="bodyStyle"
:row-class-name="rowClassName"
:tr-heights="trHeights"
:loading="loading"
:fixed="true"
@scroll="handleTableRightBodyScroll"
/>
</div>
<div
v-if="leftFixedColumns.length"
class="n-data-table-table-wrapper n-data-table-table-wrapper--left-fixed"
:class="{
'n-data-table-table-wrapper--active': horizontalScrollLeft > 0
}"
>
<base-table
ref="leftFixedTable"
:header-height="headerHeight"
:columns="leftFixedColumns"
:data="paginatedData"
:body-style="bodyStyle"
:row-class-name="rowClassName"
header-ref-name="header"
:tr-heights="trHeights"
:loading="loading"
:fixed="true"
@scroll="handleTableLeftBodyScroll"
/>
</div>
<div <div
v-if="paginatedData.length === 0" v-if="paginatedData.length === 0"
class="n-data-table__empty" class="n-data-table__empty"
@ -148,6 +106,9 @@ function normalizeColumn (column) {
defaultColumn[key] = column[key] defaultColumn[key] = column[key]
} }
}) })
if (!column.key && column.type === 'selection') {
defaultColumn.key = 'selection'
}
return defaultColumn return defaultColumn
} }
@ -245,6 +206,32 @@ export default {
} }
}, },
computed: { computed: {
currentFixedColumnLeft () {
return (column) => {
const index = this.leftFixedColumns.indexOf(column)
if (index < 0) {
return
}
let left = 0
for (let i = 0; i < index; i++) {
left = left + this.leftFixedColumns[i].width
}
return left + 'px'
}
},
currentFixedColumnRight () {
return (column) => {
const index = this.rightFixedColumns.indexOf(column)
if (index < 0) {
return
}
let right = 0
for (let i = this.rightFixedColumns.length - 1; i > index; i--) {
right = right + this.rightFixedColumns[i].width
}
return right + 'px'
}
},
normalizedColumns () { normalizedColumns () {
return this.columns return this.columns
.map(column => normalizeColumn(column)) .map(column => normalizeColumn(column))
@ -252,12 +239,10 @@ export default {
leftFixedColumns () { leftFixedColumns () {
return this.normalizedColumns return this.normalizedColumns
.filter(column => column.fixed === 'left') .filter(column => column.fixed === 'left')
.map(column => Object.assign({}, column, { fixed: false }))
}, },
rightFixedColumns () { rightFixedColumns () {
return this.normalizedColumns return this.normalizedColumns
.filter(column => column.fixed === 'right') .filter(column => column.fixed === 'right')
.map(column => Object.assign({}, column, { fixed: false }))
}, },
filteredData () { filteredData () {
const syntheticActiveFilters = this.syntheticActiveFilters const syntheticActiveFilters = this.syntheticActiveFilters
@ -453,6 +438,9 @@ export default {
allRowsChecked () { allRowsChecked () {
return this.countOfCurrentPageCheckedRows === this.paginatedData.length return this.countOfCurrentPageCheckedRows === this.paginatedData.length
} }
// handleScroll () {
// }
}, },
watch: { watch: {
syntheticCurrentPage () { syntheticCurrentPage () {
@ -519,16 +507,12 @@ export default {
getScrollElements () { getScrollElements () {
const header = this.$refs.mainTable.getHeaderElement() const header = this.$refs.mainTable.getHeaderElement()
const body = this.$refs.mainTable ? this.$refs.mainTable.getBodyElement() : null const body = this.$refs.mainTable ? this.$refs.mainTable.getBodyElement() : null
const fixedLeftBody = this.$refs.leftFixedTable ? this.$refs.leftFixedTable.getBodyElement() : null
const fixedRightBody = this.$refs.rightFixedTable ? this.$refs.rightFixedTable.getBodyElement() : null
return { return {
header, header,
body, body
fixedLeftBody,
fixedRightBody
} }
}, },
handleMainTableHeaderScroll (e) { handleMainTableHeaderScroll (e, active) {
if (!this.scrollingPart || this.scrollingPart === 'head') { if (!this.scrollingPart || this.scrollingPart === 'head') {
if (this.scrollingPart !== 'head') this.scrollingPart = 'head' if (this.scrollingPart !== 'head') this.scrollingPart = 'head'
if (this.scrollTimerId) window.clearTimeout(this.scrollTimerId) if (this.scrollTimerId) window.clearTimeout(this.scrollTimerId)
@ -569,9 +553,7 @@ export default {
} = e.target } = e.target
const { const {
header: headerEl, header: headerEl,
body: bodyEl, body: bodyEl
fixedLeftBody: leftBodyEl,
fixedRightBody: rightBodyEl
} = this.getScrollElements() } = this.getScrollElements()
if (part === 'main') { if (part === 'main') {
if (headerEl) { if (headerEl) {
@ -582,12 +564,6 @@ export default {
if (bodyEl && bodyEl.scrollTop !== scrollTop) { if (bodyEl && bodyEl.scrollTop !== scrollTop) {
bodyEl.scrollTop = scrollTop bodyEl.scrollTop = scrollTop
} }
if (leftBodyEl && leftBodyEl.scrollTop !== scrollTop) {
leftBodyEl.scrollTop = scrollTop
}
if (rightBodyEl && rightBodyEl.scrollTop !== scrollTop) {
rightBodyEl.scrollTop = scrollTop
}
this.mainTableScrollContainerWidth = bodyEl.offsetWidth this.mainTableScrollContainerWidth = bodyEl.offsetWidth
} }
}, },

View File

@ -11,7 +11,7 @@
:show-rail="!fixed" :show-rail="!fixed"
@scroll="handleScroll" @scroll="handleScroll"
> >
<table class="n-data-table-table"> <table ref="body" class="n-data-table-table">
<colgroup> <colgroup>
<col <col
v-for="(column, index) in columns" v-for="(column, index) in columns"
@ -29,7 +29,7 @@
}" }"
:class="Object.assign( :class="Object.assign(
{ {
'n-data-table-tr--hover': hoveringRowIndex === index 'n-data-table-tr--hover': hoveringRowIndex === index,
}, },
createClassObject(typeof rowClassName === 'function' createClassObject(typeof rowClassName === 'function'
? createClassObject(rowClassName(rowData, index)) ? createClassObject(rowClassName(rowData, index))
@ -42,13 +42,18 @@
<td <td
:key="column.key" :key="column.key"
:style="{ :style="{
textAlign: column.align || null textAlign: column.align || null,
left: NDataTable.currentFixedColumnLeft(column),
right: NDataTable.currentFixedColumnRight(column)
}" }"
class="n-data-table-td" class="n-data-table-td"
:class="{ :class="{
'n-data-table-td--ellipsis': column.ellipsis, 'n-data-table-td--ellipsis': column.ellipsis,
[`n-data-table-td--${column.align}-align`]: column.align, [`n-data-table-td--${column.align}-align`]: column.align,
...(column.className && createClassObject(column.className)) ...(column.className && createClassObject(column.className)),
[`n-data-table-td--fixed-${column.fixed}`]: column.width && column.fixed,
'n-data-table-td--shadow-after': activeLeft[column.key],
'n-data-table-td--shadow-before': activeRight[column.key]
}" }"
> >
<n-checkbox <n-checkbox
@ -133,6 +138,13 @@ export default {
type: Boolean, type: Boolean,
default: false default: false
} }
},
data () {
return {
activeLeft: {},
activeRight: {}
}
}, },
computed: { computed: {
rowKey () { rowKey () {

View File

@ -1,5 +1,6 @@
<template> <template>
<div <div
ref="table"
:class="{ :class="{
[`n-${theme}-theme`]: theme [`n-${theme}-theme`]: theme
}" }"
@ -27,15 +28,21 @@
<!-- th height should minus 1 compared with props.height, since border is contained --> <!-- th height should minus 1 compared with props.height, since border is contained -->
<th <th
:key="column.key" :key="column.key"
:ref="column.key"
:style="{ :style="{
textAlign: column.align || null, textAlign: column.align || null,
height: height && `${height - 1}px` height: height && `${height - 1}px`,
left: NDataTable.currentFixedColumnLeft(column),
right: NDataTable.currentFixedColumnRight(column)
}" }"
class="n-data-table-th" class="n-data-table-th"
:class="{ :class="{
'n-data-table-th--filterable': isColumnFilterable(column), 'n-data-table-th--filterable': isColumnFilterable(column),
'n-data-table-th--sortable': isColumnSortable(column), 'n-data-table-th--sortable': isColumnSortable(column),
'n-data-table-th--ellipsis': column.ellipsis 'n-data-table-th--ellipsis': column.ellipsis,
[`n-data-table-th--fixed-${column.fixed}`]: column.fixed,
'n-data-table-th--shadow-after': activeLeft[column.key],
'n-data-table-th--shadow-before': activeRight[column.key]
}" }"
@click="handleHeaderClick($event, column)" @click="handleHeaderClick($event, column)"
> >
@ -146,6 +153,12 @@ export default {
default: false default: false
} }
}, },
data: function () {
return {
activeLeft: [],
activeRight: []
}
},
computed: { computed: {
pagination () { pagination () {
return this.NDataTable.syntheticPagination return this.NDataTable.syntheticPagination
@ -169,13 +182,45 @@ export default {
return { return {
overflow: !this.fixed ? 'scroll' : 'hidden' overflow: !this.fixed ? 'scroll' : 'hidden'
} }
},
fixedColumnsLeft () {
let columnsLeft = {}
let left = 0
let columns = this.columns
columns.map((column) => {
if (this.NDataTable.leftFixedColumns.indexOf(column) > -1) {
columnsLeft[column.key] = left
} }
left = left + this.$refs[column.key][0].offsetWidth
})
return columnsLeft
},
fixedColumnsRight () {
let columnsRight = {}
let right = 0
let columns = this.columns
for (let i = columns.length - 1; i >= 0; i--) {
if (this.NDataTable.rightFixedColumns.indexOf(this.columns[i]) > -1) {
columnsRight[columns[i].key] = right
}
right = right + this.$refs[columns[i].key][0].offsetWidth
}
return columnsRight
}
},
mounted () {
this.setActiveRight(this.$refs.body)
this.setActiveLeft(this.$refs.body)
this.$emit('set-active', this.activeLeft, this.activeRight)
}, },
methods: { methods: {
isColumnSortable, isColumnSortable,
isColumnFilterable, isColumnFilterable,
createCustomWidthStyle, createCustomWidthStyle,
handleScroll (e) { handleScroll (e) {
this.setActiveRight(e.target)
this.setActiveLeft(e.target)
this.$emit('set-active', this.activeLeft, this.activeRight)
this.$emit('scroll', e) this.$emit('scroll', e)
}, },
handleCheckboxInput (column) { handleCheckboxInput (column) {
@ -194,6 +239,36 @@ export default {
const activeSorter = this.NDataTable.syntheticActiveSorter const activeSorter = this.NDataTable.syntheticActiveSorter
const nextSorter = createNextSorter(column.key, activeSorter, column.sorter) const nextSorter = createNextSorter(column.key, activeSorter, column.sorter)
this.NDataTable.changeSorter(nextSorter) this.NDataTable.changeSorter(nextSorter)
},
setActiveRight (target) {
const rightFixedColumns = this.NDataTable.rightFixedColumns
const scrollLeft = target.scrollLeft
const tableWidth = this.$refs.table.offsetWidth
const scrollWidth = target.scrollWidth
for (let i = 0; i < rightFixedColumns.length; i++) {
this.activeRight = {}
if (scrollLeft + this.fixedColumnsRight[rightFixedColumns[i].key] + tableWidth < scrollWidth) {
this.activeRight[rightFixedColumns[i].key] = true
break
}
}
},
setActiveLeft (target) {
const leftFixedColumns = this.NDataTable.leftFixedColumns
const scrollLeft = target.scrollLeft
let leftWidth = 0
for (let i = 0; i < leftFixedColumns.length; i++) {
if (scrollLeft > this.fixedColumnsLeft[leftFixedColumns[i].key] - leftWidth) {
this.activeLeft = {}
this.activeLeft[leftFixedColumns[i].key] = true
leftWidth = leftWidth + leftFixedColumns[i].width
continue
} else if (i === 0) {
this.activeLeft = {}
} else {
break
}
}
} }
} }
} }

View File

@ -13,7 +13,7 @@
transition: border-color .3s $--n-ease-in-out-cubic-bezier; transition: border-color .3s $--n-ease-in-out-cubic-bezier;
border-top-left-radius: 6px; border-top-left-radius: 6px;
border-top-right-radius: 6px; border-top-right-radius: 6px;
overflow: hidden; // overflow: hidden;
line-height: 1.5; line-height: 1.5;
} }
@include b(data-table-table-wrapper) { @include b(data-table-table-wrapper) {
@ -21,46 +21,6 @@
transition: border-color .3s $--n-ease-in-out-cubic-bezier; transition: border-color .3s $--n-ease-in-out-cubic-bezier;
} }
background-color: $--data-table-body-background-color; background-color: $--data-table-body-background-color;
@include m(left-fixed) {
@include once {
left: 0;
}
border-right: 1px solid $--data-table-border-color;
@include m(active) {
@include once {
transition: box-shadow 0.2s;
}
box-shadow: $--data-table-fixed-column-box-shadow
}
}
@include m(right-fixed) {
@include once {
right: 0;
}
border-left: 1px solid $--data-table-border-color;
@include m(active) {
box-shadow: $--data-table-fixed-column-box-shadow
}
}
@include once {
@include m(left-fixed, right-fixed) {
border-radius: 0;
top: 0;
bottom: 0;
position: absolute;
display: inline-block;
overflow: hidden;
transition:
box-shadow .3s $--n-ease-in-out-cubic-bezier,
border-color .3s $--n-ease-in-out-cubic-bezier;
@include b(data-table-base-table-wrapper) {
position: relative;
@include b(data-table-table) {
width: unset;
}
}
}
}
} }
} }
@include b(data-table-table) { @include b(data-table-table) {
@ -82,8 +42,12 @@
} }
@include b(data-table-tbody) { @include b(data-table-tbody) {
@include b(data-table-tr) { @include b(data-table-tr) {
color: red;
&:hover { &:hover {
background-color: map-get($--data-table-row-background-color, 'hover'); background-color: map-get($--data-table-row-background-color, 'hover');
@include b(data-table-td) {
background-color: map-get($--data-table-row-background-color, 'hover');
}
} }
@include m(hover) { @include m(hover) {
background-color: map-get($--data-table-row-background-color, 'hover'); background-color: map-get($--data-table-row-background-color, 'hover');
@ -105,13 +69,52 @@
border: none; border: none;
transition: transition:
border-color .3s $--n-ease-in-out-cubic-bezier, border-color .3s $--n-ease-in-out-cubic-bezier,
background-color .3s $--n-ease-in-out-cubic-bezier,
color .3s $--n-ease-in-out-cubic-bezier; color .3s $--n-ease-in-out-cubic-bezier;
@include m(ellipsis) { @include m(ellipsis) {
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
} }
@include m(fixed-left) {
left: 0;
position: sticky;
} }
@include m(fixed-right) {
right: 0;
position: sticky;
}
}
@include m(shadow-before) {
border-left: 1px solid $--data-table-border-color;
background-color: $--data-table-body-background-color;
&::before {
content: "";
width: 16px;
display: inline-block;
box-shadow: inset -16px 0 12px -16px $--data-table-fixed-column-box-shadow-color;
position: absolute;
left: -16px;
top: 0;
bottom: 0;
transition: box-shadow 0.2s;
}
}
@include m(shadow-after) {
border-right: 1px solid $--data-table-border-color;
background-color: $--data-table-body-background-color;
&::before {
content: "";
width: 16px;
display: inline-block;
box-shadow: inset 16px 0 12px -16px $--data-table-fixed-column-box-shadow-color;
position: absolute;
right: -16px;
top: 0;bottom: 0;
transition: box-shadow 0.2s;
}
}
border-color: $--data-table-border-color;
color: $--data-table-body-text-color; color: $--data-table-body-text-color;
border-bottom: 1px solid $--data-table-border-color; border-bottom: 1px solid $--data-table-border-color;
} }
@ -135,6 +138,7 @@
white-space: nowrap; white-space: nowrap;
} }
} }
border-color: $--data-table-border-color;
color: $--data-table-header-text-color; color: $--data-table-header-text-color;
@include m(sortable) { @include m(sortable) {
cursor: pointer; cursor: pointer;
@ -142,6 +146,46 @@
background-color: map-get($--data-table-header-cell-background-color, 'hover'); background-color: map-get($--data-table-header-cell-background-color, 'hover');
} }
} }
@include m(fixed-left) {
left: 0;
position: sticky;
z-index: 1;
background-color: $--data-table-header-fixed-background-color;
}
@include m(fixed-right) {
right: 0;
position: sticky;
z-index: 1;
background-color: $--data-table-header-fixed-background-color;
}
@include m(shadow-before) {
border-left: 1px solid $--data-table-border-color;
&::before {
content: "";
width: 16px;
display: inline-block;
box-shadow: inset -16px 0 12px -16px $--data-table-fixed-column-box-shadow-color;
position: absolute;
left:-16px;
top: 0;
bottom: 0;
transition: box-shadow 0.2s;
}
}
@include m(shadow-after) {
border-right: 1px solid $--data-table-border-color;
&::before {
content: "";
width: 16px;
display: inline-block;
box-shadow: inset 16px 0 12px -16px $--data-table-fixed-column-box-shadow-color;
position: absolute;
right: -16px;
top: 0;
bottom: 0;
transition: box-shadow 0.2s;
}
}
} }
} }
@include not-m(single-line) { @include not-m(single-line) {
@ -178,6 +222,9 @@
} }
} }
} }
@include b(scrollbar-content) {
overflow: unset;
}
} }
@include b(data-table) { @include b(data-table) {
@include once { @include once {
@ -192,7 +239,6 @@
} }
@include b(data-table-base-table-header) { @include b(data-table-base-table-header) {
@include once { @include once {
overflow: scroll;
transition: border-color .3s $--n-ease-in-out-cubic-bezier; transition: border-color .3s $--n-ease-in-out-cubic-bezier;
&::-webkit-scrollbar { &::-webkit-scrollbar {
width: 0; width: 0;
@ -288,3 +334,13 @@
} }
} }
} }
@function mix-color-with-alpha-color ($color, $alpha-color) {
$alpha-color-weight: percentage(alpha($alpha-color));
$temp-color-1: scale-color($color, $lightness: -$alpha-color-weight);
$temp-color-2: scale-color($alpha-color, $lightness: -(100% - $alpha-color-weight));
@return rgb(
red($temp-color-1) + red($temp-color-2),
green($temp-color-1) + green($temp-color-2),
blue($temp-color-1) + blue($temp-color-2)
);
}

View File

@ -1,12 +1,13 @@
@mixin setup-dark-data-table { @mixin setup-dark-data-table {
$--data-table-border-color: $--n-divider-color !global; $--data-table-border-color: $--n-divider-color !global;
$--data-table-header-text-color: $--n-primary-text-color !global; $--data-table-header-text-color: $--n-primary-text-color !global;
$--data-table-fixed-column-box-shadow: (0 0 12px 0 rgba(0, 0, 0, .18)) !global; $--data-table-fixed-column-box-shadow-color: rgba(0, 0, 0, .18) !global;
$--data-table-body-background-color: $--n-card-background-color !global; $--data-table-body-background-color: $--n-card-background-color !global;
$--data-table-header-background-color: $--n-table-header-background-color !global; $--data-table-header-background-color: $--n-table-header-background-color !global;
$--data-table-header-fixed-background-color: (mix-color-with-alpha-color($--data-table-body-background-color,$--data-table-header-background-color)) !global;
$--data-table-row-background-color: ( $--data-table-row-background-color: (
'default': $--n-card-background-color, 'default': $--n-card-background-color,
'hover': change-color($--n-primary-color, $alpha: 0.10) 'hover': mix-color-with-alpha-color($--data-table-body-background-color,change-color($--n-primary-color, $alpha: 0.10))
) !global; ) !global;
$--data-table-header-cell-background-color: ( $--data-table-header-cell-background-color: (
'default': transparent, 'default': transparent,
@ -19,3 +20,13 @@
'active': $--n-primary-color 'active': $--n-primary-color
) !global; ) !global;
} }
@function mix-color-with-alpha-color ($color, $alpha-color) {
$alpha-color-weight: percentage(alpha($alpha-color));
$temp-color-1: scale-color($color, $lightness: -$alpha-color-weight);
$temp-color-2: scale-color($alpha-color, $lightness: -(100% - $alpha-color-weight));
@return rgb(
red($temp-color-1) + red($temp-color-2),
green($temp-color-1) + green($temp-color-2),
blue($temp-color-1) + blue($temp-color-2)
);
}

View File

@ -1,12 +1,13 @@
@mixin setup-light-data-table { @mixin setup-light-data-table {
$--data-table-border-color: $--n-divider-color !global; $--data-table-border-color: $--n-divider-color !global;
$--data-table-header-text-color: $--n-primary-text-color !global; $--data-table-header-text-color: $--n-primary-text-color !global;
$--data-table-fixed-column-box-shadow: (0 0 12px 0 rgba(0, 0, 0, .18)) !global; $--data-table-fixed-column-box-shadow-color: rgba(0, 0, 0, .18) !global;
$--data-table-body-background-color: $--n-card-background-color !global; $--data-table-body-background-color: $--n-card-background-color !global;
$--data-table-header-background-color: $--n-table-header-background-color !global; $--data-table-header-background-color: $--n-table-header-background-color !global;
$--data-table-header-fixed-background-color: mix-color-with-alpha-color($--data-table-body-background-color, $--data-table-header-background-color) !global;
$--data-table-row-background-color: ( $--data-table-row-background-color: (
'default': $--n-card-background-color, 'default': $--n-card-background-color,
'hover': $--n-pending-overlay-color 'hover': mix-color-with-alpha-color($--data-table-body-background-color,$--n-pending-overlay-color)
) !global; ) !global;
$--data-table-header-cell-background-color: ( $--data-table-header-cell-background-color: (
'default': transparent, 'default': transparent,
@ -19,3 +20,14 @@
'active': $--n-primary-color 'active': $--n-primary-color
) !global; ) !global;
} }
@function mix-color-with-alpha-color ($color, $alpha-color) {
$alpha-color-weight: percentage(alpha($alpha-color));
$temp-color-1: scale-color($color, $lightness: -$alpha-color-weight);
$temp-color-2: scale-color($alpha-color, $lightness: -(100% - $alpha-color-weight));
@return rgb(
red($temp-color-1) + red($temp-color-2),
green($temp-color-1) + green($temp-color-2),
blue($temp-color-1) + blue($temp-color-2)
);
}