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"
:scroll-x="scrollX"
@scroll="handleHeaderScroll"
@set-active="setActive"
/>
<table-body
ref="body"
@ -104,6 +105,10 @@ export default {
},
handleHeaderScroll (...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" />
</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
v-if="paginatedData.length === 0"
class="n-data-table__empty"
@ -148,6 +106,9 @@ function normalizeColumn (column) {
defaultColumn[key] = column[key]
}
})
if (!column.key && column.type === 'selection') {
defaultColumn.key = 'selection'
}
return defaultColumn
}
@ -245,6 +206,32 @@ export default {
}
},
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 () {
return this.columns
.map(column => normalizeColumn(column))
@ -252,12 +239,10 @@ export default {
leftFixedColumns () {
return this.normalizedColumns
.filter(column => column.fixed === 'left')
.map(column => Object.assign({}, column, { fixed: false }))
},
rightFixedColumns () {
return this.normalizedColumns
.filter(column => column.fixed === 'right')
.map(column => Object.assign({}, column, { fixed: false }))
},
filteredData () {
const syntheticActiveFilters = this.syntheticActiveFilters
@ -453,6 +438,9 @@ export default {
allRowsChecked () {
return this.countOfCurrentPageCheckedRows === this.paginatedData.length
}
// handleScroll () {
// }
},
watch: {
syntheticCurrentPage () {
@ -519,16 +507,12 @@ export default {
getScrollElements () {
const header = this.$refs.mainTable.getHeaderElement()
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 {
header,
body,
fixedLeftBody,
fixedRightBody
body
}
},
handleMainTableHeaderScroll (e) {
handleMainTableHeaderScroll (e, active) {
if (!this.scrollingPart || this.scrollingPart === 'head') {
if (this.scrollingPart !== 'head') this.scrollingPart = 'head'
if (this.scrollTimerId) window.clearTimeout(this.scrollTimerId)
@ -569,9 +553,7 @@ export default {
} = e.target
const {
header: headerEl,
body: bodyEl,
fixedLeftBody: leftBodyEl,
fixedRightBody: rightBodyEl
body: bodyEl
} = this.getScrollElements()
if (part === 'main') {
if (headerEl) {
@ -582,12 +564,6 @@ export default {
if (bodyEl && 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
}
},

View File

@ -11,7 +11,7 @@
:show-rail="!fixed"
@scroll="handleScroll"
>
<table class="n-data-table-table">
<table ref="body" class="n-data-table-table">
<colgroup>
<col
v-for="(column, index) in columns"
@ -29,7 +29,7 @@
}"
:class="Object.assign(
{
'n-data-table-tr--hover': hoveringRowIndex === index
'n-data-table-tr--hover': hoveringRowIndex === index,
},
createClassObject(typeof rowClassName === 'function'
? createClassObject(rowClassName(rowData, index))
@ -42,13 +42,18 @@
<td
:key="column.key"
: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--ellipsis': column.ellipsis,
[`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
@ -133,6 +138,13 @@ export default {
type: Boolean,
default: false
}
},
data () {
return {
activeLeft: {},
activeRight: {}
}
},
computed: {
rowKey () {

View File

@ -1,5 +1,6 @@
<template>
<div
ref="table"
:class="{
[`n-${theme}-theme`]: theme
}"
@ -27,15 +28,21 @@
<!-- th height should minus 1 compared with props.height, since border is contained -->
<th
:key="column.key"
:ref="column.key"
:style="{
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--filterable': isColumnFilterable(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)"
>
@ -146,6 +153,12 @@ export default {
default: false
}
},
data: function () {
return {
activeLeft: [],
activeRight: []
}
},
computed: {
pagination () {
return this.NDataTable.syntheticPagination
@ -169,13 +182,45 @@ export default {
return {
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: {
isColumnSortable,
isColumnFilterable,
createCustomWidthStyle,
handleScroll (e) {
this.setActiveRight(e.target)
this.setActiveLeft(e.target)
this.$emit('set-active', this.activeLeft, this.activeRight)
this.$emit('scroll', e)
},
handleCheckboxInput (column) {
@ -194,6 +239,36 @@ export default {
const activeSorter = this.NDataTable.syntheticActiveSorter
const nextSorter = createNextSorter(column.key, activeSorter, column.sorter)
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;
border-top-left-radius: 6px;
border-top-right-radius: 6px;
overflow: hidden;
// overflow: hidden;
line-height: 1.5;
}
@include b(data-table-table-wrapper) {
@ -21,46 +21,6 @@
transition: border-color .3s $--n-ease-in-out-cubic-bezier;
}
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) {
@ -82,8 +42,12 @@
}
@include b(data-table-tbody) {
@include b(data-table-tr) {
color: red;
&: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) {
background-color: map-get($--data-table-row-background-color, 'hover');
@ -105,13 +69,52 @@
border: none;
transition:
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;
@include m(ellipsis) {
text-overflow: ellipsis;
overflow: hidden;
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;
border-bottom: 1px solid $--data-table-border-color;
}
@ -135,6 +138,7 @@
white-space: nowrap;
}
}
border-color: $--data-table-border-color;
color: $--data-table-header-text-color;
@include m(sortable) {
cursor: pointer;
@ -142,6 +146,46 @@
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) {
@ -178,6 +222,9 @@
}
}
}
@include b(scrollbar-content) {
overflow: unset;
}
}
@include b(data-table) {
@include once {
@ -192,7 +239,6 @@
}
@include b(data-table-base-table-header) {
@include once {
overflow: scroll;
transition: border-color .3s $--n-ease-in-out-cubic-bezier;
&::-webkit-scrollbar {
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 {
$--data-table-border-color: $--n-divider-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-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: (
'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;
$--data-table-header-cell-background-color: (
'default': transparent,
@ -19,3 +20,13 @@
'active': $--n-primary-color
) !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 {
$--data-table-border-color: $--n-divider-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-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: (
'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;
$--data-table-header-cell-background-color: (
'default': transparent,
@ -19,3 +20,14 @@
'active': $--n-primary-color
) !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)
);
}