Vue2+el-table实现表格行上下滚动
,表格单元格内容溢出左右滚动
TextScroll、TableWrapper
TextScroll 文本左右滚动容器组件
<template><div ref="wrapper" class="scroll-wrapper" @mouseenter="mouseenter" @mouseleave="mouseleave"><div ref="text" class="text" :style="{transform: `translateX(${translateX}px)`}"><slot>{{ text }}</slot></div></div>
</template><script>javascript">
export default {name: 'TextScroll',props: {text: [String, Number],step: {type: [String, Number],default: 0.2}},data() {return {translateX: 0,trip: 0,realStep: 1,frameId: null}},mounted() {const rect = this.$refs.text.getBoundingClientRect()const wrapperRect = this.$refs.wrapper.getBoundingClientRect()this.trip = rect.right - wrapperRect.width - rect.leftthis.mouseleave()this.$emit('hook:beforeDestroy', () => {this.mouseenter()})},methods: {mouseenter() {window.cancelAnimationFrame(this.frameId)},mouseleave() {if (this.trip > 8) {this.frameId = window.requestAnimationFrame(this.animate)}},animate() {if (Math.abs(this.translateX) >= this.trip) {this.realStep = this.step}if (this.translateX >= 0) {this.realStep = this.step * -1}this.translateX += this.realStepthis.frameId = window.requestAnimationFrame(this.animate)}}
}
</script><style lang="scss" scoped>
.scroll-wrapper {overflow: hidden;.text {padding: 0 4px;display: inline-block;white-space: nowrap;}
}
</style>
TableWrapper 表格上下滚动容器组件
- columns:表格列配置属性
Eg. [{ label: '表头', prop: 'name', formatter: (content,row)=>content style: { width: '160px' } }]
- data:表格配置数据
Eg.[{ name: '父类', child: [{name: '子类0'}] }]
- full:表格在容器内平铺展示
- tdspace:表格边框是否分隔
- expand:是否开启子行
- step:行上下滚动补偿
<template><div :class="['table-wrapper',{full:full}]"><table><thead><tr><th v-for="(column,index) in columns" :key="index" :class="`th-${index}`":style="Object.assign({},column.style||{},column.headStyle||{})">{{ column.label }}</th></tr></thead></table><el-scrollbar ref="scroll"><table v-if="data.length" :class="['table-body',{'td-space':tdspace}]":style="{transform: `translateY(${translateY}px)`}" @mouseenter="mouseenter"@mouseleave="mouseleave"><tbody><template v-for="(item,key) in data"><tr :key="key" :class="[`tr-${key}`, `border-${item.key}`]":style="{cursor: expand?'pointer':'auto'}" @click="doExpand(item.key)"><template v-for="(column,index) in columns"><td v-if="column.type === 'index'" :key="`${key}-${index}`" :class="`td-${index}`":style="column.style||{}">{{ key+1 }}</td><td v-else :key="`${key}-${index}`" :class="`td-${index}`":style="column.style||{}"><TextScroll:text="column.formatter?column.formatter(item[column.prop],item):item[column.prop]" /></td></template></tr><template v-if="item.child && expandKey[item.key]"><tr v-for="(v,i) in item.child" :key="`${item.key}-${i}`":class="[`tr-${key+i+1}`,`border-${item.key}`]" class="child"><template v-for="(column,index) in columns"><td v-if="column.type === 'index'" :key="`${key+i+1}-${index}`":class="`td-${index}`" :style="column.style||{}">{{ i+1 }}</td><td v-else :key="`${key+i+1}-${index}`" :class="`td-${index}`":style="column.style||{}"><TextScroll:text="column.formatter?column.formatter(v[column.prop],v):v[column.prop]" /></td></template></tr></template></template></tbody></table><div v-else class="empty">暂无数据</div></el-scrollbar></div>
</template><script>javascript">
import TextScroll from './TextScroll.vue'
export default {name: 'TablerWrapper',components: {TextScroll},props: {columns: Array,data: Array,full: Boolean,tdspace: {type: Boolean,default: false},expand: Boolean,step: {type: [String, Number],default: 0.3}},data() {return {translateY: 0,staticTranslateY: 0,trip: 0,realStep: 1,frameId: null,expandKey: {}}},watch: {data(n) {if (n.length) {this.expandKey = this.data.reduce((s, v) => {s[v.key] = truereturn s}, {})this.$nextTick(() => {this.initScroll()this.mouseleave()})}}},methods: {initScroll() {window.cancelAnimationFrame(this.frameId)const rect = this.$refs.scroll.$el.querySelector('.table-body').getBoundingClientRect()const wrapperRect = this.$refs.scroll.$el.querySelector('.el-scrollbar__view').getBoundingClientRect()this.trip = rect.bottom - wrapperRect.height - rect.top},mouseenter() {window.cancelAnimationFrame(this.frameId)const translateY = this.translateYthis.translateY = 0this.$nextTick(() => {this.$refs.scroll.$el.querySelector('.el-scrollbar__wrap').scrollTop = Math.abs(translateY)})},mouseleave() {const translateY = this.$refs.scroll.$el.querySelector('.el-scrollbar__wrap').scrollTopthis.$refs.scroll.$el.querySelector('.el-scrollbar__wrap').scrollTop = 0this.translateY = translateY * -1if (this.trip > 8) {this.frameId = window.requestAnimationFrame(this.animate)}},animate() {if (Math.abs(this.translateY) >= this.trip) {this.realStep = this.step}if (this.translateY >= 0) {this.realStep = this.step * -1}this.translateY += this.realStepthis.frameId = window.requestAnimationFrame(this.animate)},doExpand(key) {this.expandKey = { ...this.expandKey, [key]: !this.expandKey[key] }this.$nextTick(() => {this.initScroll()})}}
}
</script><style lang="scss" scoped>
.table-wrapper {color: #fff;display: flex;flex-direction: column;flex: 1;overflow: hidden;table {table-layout: fixed;border-collapse: separate;border-spacing: 0 3px;width: 100%;thead th {font-size: 14px;text-align: center;vertical-align: bottom;padding: 10px 5px;background: rgba(69, 167, 248, 0.2980392156862745);}td {color: #fff;font-size: 18px;padding: 6px 6px;text-align: center;line-height: 16px;vertical-align: middle;background: rgba(69, 167, 248, 0.09803921568627451);}.child {td {background: rgba(60, 165, 250, 0.298);}}td,th {&:first-child {border-radius: 4px 0 0 4px;}&:last-child {border-radius: 0 4px 4px 0;}}}.table-body.td-space {border-collapse: separate;border-spacing: 3px 3px;}.el-scrollbar {position: relative;flex: 1;overflow: hidden;}
}
.full {::v-deep .el-scrollbar__view {height: 100%;}.table-body {min-height: 100%;td {vertical-align: middle;}}
}
.empty {position: absolute;left: 50%;top: 50%;transform: translate(-50%, -50%);text-align: center;font-size: 12px;
}
</style>