feat(advance-table): header height auto compute

This commit is contained in:
JiwenBai 2019-10-25 19:28:04 +08:00
parent f3885202cf
commit 2191dd695c
11 changed files with 158 additions and 116 deletions

View File

@ -3,7 +3,7 @@
* @Company: Tusimple * @Company: Tusimple
* @Date: 2019-10-23 15:59:41 * @Date: 2019-10-23 15:59:41
* @LastEditors: Jiwen.bai * @LastEditors: Jiwen.bai
* @LastEditTime: 2019-10-23 16:34:30 * @LastEditTime: 2019-10-25 18:58:58
--> -->
# Senior Usage # Senior Usage
@ -18,6 +18,7 @@
:search="search" :search="search"
:pagination="{total:data.length,limit:10,custom:true}" :pagination="{total:data.length,limit:10,custom:true}"
@on-change="onChange" @on-change="onChange"
max-width="420px"
> >
<div slot="table-operation-batch-left"> <div slot="table-operation-batch-left">
<n-button size="small" @click="clear"> <n-button size="small" @click="clear">

View File

@ -1,13 +0,0 @@
/*
* @Author: Volankey@gmail.com
* @Company: Tusimple
* @Date: 2019-10-24 14:26:24
* @LastEditors: Jiwen.bai
* @LastEditTime: 2019-10-24 14:36:59
*/
import { Store, install, storeMixin } from './store'
export default {
Store,
install,
storeMixin
}

View File

@ -1,63 +0,0 @@
/*
* @Author: Volankey@gmail.com
* @Company: Tusimple
* @Date: 2019-10-24 14:26:37
* @LastEditors: Jiwen.bai
* @LastEditTime: 2019-10-24 14:50:03
*/
let Vue = null
export class Store {
constructor (options = {}) {
const store = this
store._vm = new Vue({
data: {
$$state: {
currentHoverRow: null
}
}
})
}
get state () {
return this._vm._data.$$state
}
commit (type, payload) {
Vue.set(this._vm._data.$$state, type, payload)
}
}
export function install (_Vue) {
if (Vue && _Vue === Vue) {
if (process.env.NODE_ENV !== 'production') {
console.error(
'[vuex] already installed. Vue.use(Vuex) should be called only once.'
)
}
return
}
Vue = _Vue
// applyMixin(Vue)
}
// function applyMixin (Vue) {
// const version = Number(Vue.version.split('.')[0])
// if (version >= 2) {
// Vue.mixin({ beforeCreate: vuexInit })
// }
// /**
// * Vuex init hook, injected into each instances init hooks list.
// */
// function vuexInit () {
// const options = this.$options
// // store injection
// if (options.store) {
// this.$store =
// typeof options.store === 'function' ? options.store() : options.store
// }
// // else if (options.parent && options.parent.$store) {
// // this.$store = options.parent.$store
// // }
// }
// }

View File

@ -3,7 +3,7 @@
* @Company: Tusimple * @Company: Tusimple
* @Date: 2019-10-23 16:06:59 * @Date: 2019-10-23 16:06:59
* @LastEditors: Jiwen.bai * @LastEditors: Jiwen.bai
* @LastEditTime: 2019-10-24 15:55:27 * @LastEditTime: 2019-10-25 16:28:53
--> -->
<template> <template>
<!-- table body --> <!-- table body -->
@ -18,7 +18,7 @@
v-for="(column, i) in columns" v-for="(column, i) in columns"
:key="i" :key="i"
:style="computeCustomWidthStl(column)" :style="computeCustomWidthStl(column)"
/> >
</colgroup> </colgroup>
<n-tbody v-show="!loading"> <n-tbody v-show="!loading">
<n-tr <n-tr
@ -29,6 +29,8 @@
? rowClassName(rowData, i) ? rowClassName(rowData, i)
: rowClassName : rowClassName
" "
@mouseenter.native="e => onRowHover(e, rowData, i)"
@mouseleave.native="e => onRowLeave(e, rowData, i)"
> >
<template v-for="column in columns"> <template v-for="column in columns">
<n-td <n-td
@ -78,11 +80,14 @@
<script> <script>
import row from '../row/index.js' import row from '../row/index.js'
import { addClass, removeClass } from '../utils'
import { storageMixin } from '../store'
export default { export default {
components: { components: {
row row
}, },
mixins: [storageMixin],
props: { props: {
tableStl: { tableStl: {
type: Object, type: Object,
@ -120,6 +125,21 @@ export default {
data () { data () {
return {} return {}
}, },
watch: {
'$store.state.currentHoverRow' (index, oldIndex) {
const hoverClassName = 'n-table__tr--hover'
const rowsDom = this.$el.querySelectorAll('table tr')
const oldRowDom = rowsDom[oldIndex]
const newRowDom = rowsDom[index]
console.log('TCL: newRowDom', newRowDom)
if (oldRowDom) {
removeClass(oldRowDom, hoverClassName)
}
if (newRowDom) {
addClass(newRowDom, hoverClassName)
}
}
},
mounted () { mounted () {
if (this.headerRefName) { if (this.headerRefName) {
let headerRef = this.$parent.$refs[this.headerRefName] let headerRef = this.$parent.$refs[this.headerRefName]
@ -127,6 +147,13 @@ export default {
} }
}, },
methods: { methods: {
onRowHover (e, rowData, index) {
console.log('TCL: onRowHover -> e, rowData, index', e, rowData, index)
this.$store.commit('currentHoverRow', index)
},
onRowLeave (e, rowData) {
this.$store.commit('currentHoverRow', null)
},
computeCustomWidthStl (column) { computeCustomWidthStl (column) {
if (column.width) { if (column.width) {
let width = column.width let width = column.width

View File

@ -3,7 +3,7 @@
* @Company: Tusimple * @Company: Tusimple
* @Date: 2019-10-24 15:16:41 * @Date: 2019-10-24 15:16:41
* @LastEditors: Jiwen.bai * @LastEditors: Jiwen.bai
* @LastEditTime: 2019-10-24 15:17:28 * @LastEditTime: 2019-10-25 19:24:21
--> -->
<template> <template>
<n-table <n-table
@ -17,9 +17,9 @@
v-for="(column, i) in columns" v-for="(column, i) in columns"
:key="i" :key="i"
:style="computeCustomWidthStl(column)" :style="computeCustomWidthStl(column)"
> />
<col v-if="scrollBarWidth" :width="scrollBarWidth" > <col v-if="scrollBarWidth" :width="scrollBarWidth" />
</colgroup> </colgroup>
<n-thead> <n-thead>
<n-tr> <n-tr>
@ -99,6 +99,10 @@ export default {
PopFilter PopFilter
}, },
props: { props: {
height: {
type: Number,
default: null
},
colGroupStl: { colGroupStl: {
type: Object, type: Object,
default: () => ({}) default: () => ({})
@ -152,11 +156,19 @@ export default {
this.$emit('on-checkbox-all-change', v) this.$emit('on-checkbox-all-change', v)
}, },
computeAlign (column) { computeAlign (column) {
let stl = {}
if (column.align) { if (column.align) {
return { Object.assign(stl, {
'text-align': column.align 'text-align': column.align
} })
} }
let height = this.height
console.log('TCL: computeAlign -> this.height', this.height)
if (height !== null) {
stl.height = `${height}px`
}
return stl
}, },
sortInput (value, column, sorter) { sortInput (value, column, sorter) {
const sortIndexs = {} const sortIndexs = {}

View File

@ -1,6 +1,15 @@
/*
* @Author: Volankey@gmail.com
* @Company: Tusimple
* @Date: 2019-10-25 10:13:50
* @LastEditors: Jiwen.bai
* @LastEditTime: 2019-10-25 11:24:13
*/
import Scaffold from './src/main.vue' import Scaffold from './src/main.vue'
Scaffold.install = function (Vue) { Scaffold.install = function(Vue) {
Scaffold.Vue = Vue
Vue.component(Scaffold.name, Scaffold) Vue.component(Scaffold.name, Scaffold)
} }

View File

@ -3,7 +3,7 @@
* @Company: Tusimple * @Company: Tusimple
* @Date: 2019-10-23 15:57:17 * @Date: 2019-10-23 15:57:17
* @LastEditors: Jiwen.bai * @LastEditors: Jiwen.bai
* @LastEditTime: 2019-10-24 15:31:26 * @LastEditTime: 2019-10-25 19:23:59
--> -->
<template> <template>
<div <div
@ -35,13 +35,17 @@
<slot name="table-operation-search-right" /> <slot name="table-operation-search-right" />
</div> </div>
</div> </div>
<div ref="tbodyWrapper" class="n-advance-table__tbody"> <div
ref="tbodyWrapper"
class="n-advance-table__tbody"
:style="tableWrapperStl"
>
<div class="n-advance-table__fixed--left n-advance-table__fixed"> <div class="n-advance-table__fixed--left n-advance-table__fixed">
<table-header <table-header
ref="fixedLeftHeader" ref="fixedLeftHeader"
:height="headerHeight"
:columns="fixedLeftColumn" :columns="fixedLeftColumn"
:col-group-stl="colGroup" :col-group-stl="colGroup"
:scroll-bar-width="scrollBarWidth"
:sort-indexs="sortIndexs" :sort-indexs="sortIndexs"
:selected-filter="selectedFilter" :selected-filter="selectedFilter"
:showing-data="showingData" :showing-data="showingData"
@ -93,6 +97,9 @@
v-if="pagination !== false && showingData.length" v-if="pagination !== false && showingData.length"
class="n-advance-table__pagination" class="n-advance-table__pagination"
> >
<button @click="add">
{{ $store.state.currentHoverRow }}
</button>
<n-pagination v-model="currentPage" :page-count="pageCount" /> <n-pagination v-model="currentPage" :page-count="pageCount" />
</div> </div>
</div> </div>
@ -105,17 +112,18 @@ import withapp from '../../../mixins/withapp'
import themeable from '../../../mixins/themeable' import themeable from '../../../mixins/themeable'
import TableHeader from '../header/header' import TableHeader from '../header/header'
import TableBody from '../body/body' import TableBody from '../body/body'
import store, { storeMixin } from '../store' import { Store, storageMixin } from '../store'
export default { export default {
store () {
return new Store()
},
name: 'NAdvanceTable', name: 'NAdvanceTable',
store,
components: { components: {
TableBody, TableBody,
searchInput, searchInput,
TableHeader TableHeader
}, },
mixins: [storeMixin, withapp, themeable], mixins: [storageMixin, withapp, themeable],
props: { props: {
search: { search: {
/** /**
@ -189,6 +197,7 @@ export default {
}, },
data () { data () {
return { return {
headerHeight: 0,
copyData: [], copyData: [],
sortIndexs: {}, sortIndexs: {},
wrapperWidth: 'unset', wrapperWidth: 'unset',
@ -205,6 +214,16 @@ export default {
} }
}, },
computed: { computed: {
tableWrapperStl () {
let stl = {}
if (this.maxWidth) {
stl.maxWidth =
typeof this.maxWidth === 'number'
? this.maxWidth + 'px'
: this.maxWidth
}
return stl
},
fixedLeftColumn () { fixedLeftColumn () {
return this.columns return this.columns
.filter(column => { .filter(column => {
@ -297,12 +316,6 @@ export default {
? this.maxHeight + 'px' ? this.maxHeight + 'px'
: this.maxHeight : this.maxHeight
} }
if (this.maxWidth) {
stl.maxWidth =
typeof this.maxWidth === 'number'
? this.maxWidth + 'px'
: this.maxWidth
}
if (this.minHeight !== 'unset') { if (this.minHeight !== 'unset') {
stl.minHeight = stl.minHeight =
typeof this.minHeight === 'number' typeof this.minHeight === 'number'
@ -374,6 +387,9 @@ export default {
} }
}, },
watch: { watch: {
'$store.state.currentHoverRow' (index, oldIndex) {
console.log('TCL: index, oldIndex', index, oldIndex)
},
currentPage (val) { currentPage (val) {
if (this.pagination.custom === true) { if (this.pagination.custom === true) {
this.useRemoteChange() this.useRemoteChange()
@ -450,11 +466,11 @@ export default {
this.tbodyWidth = this.relTable.offsetWidth this.tbodyWidth = this.relTable.offsetWidth
this.headerRealEl = this.$refs.header.$el.querySelector('thead') this.headerRealEl = this.$refs.header.$el.querySelector('thead')
this.headerHeight = this.headerRealEl.offsetHeight
this.fixedLeftTBodyEl = this.$refs.fixedLeftTbody.$el this.fixedLeftTBodyEl = this.$refs.fixedLeftTbody.$el
// console.log(this.wrapperWidth, this.tbodyWidth) // console.log(this.wrapperWidth, this.tbodyWidth)
this.init() this.init()
this.$store.commit('currentHoverRow', 1)
// window.addEventListener('resize', this.init) // window.addEventListener('resize', this.init)
}, },
@ -462,6 +478,12 @@ export default {
// window.removeEventListener('resize', this.init) // window.removeEventListener('resize', this.init)
}, },
methods: { methods: {
add () {
this.$store.commit(
'currentHoverRow',
this.$store.state.currentHoverRow + 1
)
},
onBodyScrolll (event) { onBodyScrolll (event) {
this.headerRealEl.style.transform = `translate3d(-${event.target.scrollLeft}px,0,0)` this.headerRealEl.style.transform = `translate3d(-${event.target.scrollLeft}px,0,0)`
if (this.fixedLeftTBodyEl) { if (this.fixedLeftTBodyEl) {

View File

@ -1,22 +1,45 @@
/* /*
* @Author: Volankey@gmail.com * @Author: Volankey@gmail.com
* @Company: Tusimple * @Company: Tusimple
* @Date: 2019-10-24 14:03:55 * @Date: 2019-10-25 11:31:12
* @LastEditors: Jiwen.bai * @LastEditors: Jiwen.bai
* @LastEditTime: 2019-10-24 14:49:04 * @LastEditTime: 2019-10-25 11:33:00
*/ */
import Vue from 'vue' let Vue = null
import Vuex from './TableStore' export class Store {
Vue.use(Vuex) constructor (options = {}) {
const store = this
export default new Vuex.Store() store._vm = new Vue({
export const storeMixin = { data: {
beforeCreate: function vuexInit () { $$state: {
const options = this.$options currentHoverRow: 1
// store injection }
if (options.store) { }
this.$store = })
typeof options.store === 'function' ? options.store() : options.store }
} get state () {
return this._vm._data.$$state
}
commit (type, payload) {
Vue.set(this._vm._data.$$state, type, payload)
// this.state[type] = payload
} }
} }
function vuexInit () {
const options = this.$options
const _Vue = options.Vue
if (_Vue) {
Vue = _Vue
}
// store injection
if (options.store) {
this.$store =
typeof options.store === 'function' ? options.store() : options.store
} else if (options.parent && options.parent.$store) {
this.$store = options.parent.$store
}
}
export const storageMixin = {
beforeCreate: vuexInit
}

View File

@ -0,0 +1,14 @@
/*
* @Author: Volankey@gmail.com
* @Company: Tusimple
* @Date: 2019-10-24 18:07:27
* @LastEditors: Jiwen.bai
* @LastEditTime: 2019-10-24 18:07:27
*/
export const removeClass = (dom, className) => {
dom.classList.remove(className)
}
export const addClass = (dom, className) => {
dom.classList.add(className)
}

View File

@ -98,6 +98,9 @@
word-wrap: break-word; word-wrap: break-word;
word-break: break-all; word-break: break-all;
table-layout: fixed; table-layout: fixed;
tbody tr:hover {
background-color: transparent;
}
} }
&::-webkit-scrollbar { &::-webkit-scrollbar {
width: 5px; width: 5px;
@ -130,6 +133,9 @@
border-radius: 2.5px; border-radius: 2.5px;
background: $--table-scrollbar-color; background: $--table-scrollbar-color;
} }
tr.n-table__tr--hover {
background-color: $--table-row-hover;
}
} }
@include e(header) { @include e(header) {
overflow: hidden; overflow: hidden;
@ -137,6 +143,9 @@
i { i {
color: $--table-header-icon-color; color: $--table-header-icon-color;
} }
th {
box-sizing: border-box;
}
} }
} }
} }

View File

@ -1,5 +1,5 @@
@import "./mixins/mixins.scss"; @import './mixins/mixins.scss';
@import "./themes/vars.scss"; @import './themes/vars.scss';
@include themes-mixin { @include themes-mixin {
@include b(table) { @include b(table) {
@ -34,6 +34,7 @@
background-color: $--table-body-background-color; background-color: $--table-body-background-color;
color: $--table-body-color; color: $--table-body-color;
tr { tr {
transition: background-color 0.3s;
td { td {
padding: 16px 6px 12px 19px; padding: 16px 6px 12px 19px;
text-align: left; text-align: left;