vue table 固定列
- 利用css position定位position: sticky属性实现;
- 利用JS addEventListener监听事件控制表头与tbody同时滑动
父组件使用
<list
:tableData="manager"
:tableConfig="mngInfo"
id="manager"
></list>
manager:[],//返回数据
// 表格数据
mngInfo:[
{
width: 140, // 表格宽度
percent: false, // 百分单位
isSort: false, // 是否排序
sortKey: '', // 排序字段
label:'表格一', // 表头 名称
left: 0, // 距左距离
color: '#fff', // 文字颜色
showSort: true, // 展示左侧序号
setData: false, // 处理单位
key: '', // k名
sizeKey: '', // 是否根据大小判断颜色 排序字段
unit: '', // 单位
stock: false, // 是否是 股票名称
codeKey: '', //股票代码key
nameKey: '', // 股票名称 key
toKLine: false, // 是否跳转k线
},
{
width: 140,
percent: false,
isSort: false,
sortKey: '',
label:'表格二',
left: '140',
color: '#fff',
showSort: false,
setData: false,
key: 'indiName',
sizeKey: '',
unit: '',
codeKey: '', //股票代码key
nameKey: '', // 股票名称 key
toKLine: 'false', // 是否跳转k线
stockType: '', // 股票后缀 key
},
{
width: 140,
percent: false,
isSort: false,
sortKey: '',
label:'表格三',
left: '',
color: '#fff',
showSort: false,
setData: false,
key: 'gender',
sizeKey: '',
unit: '',
},
{
width: 140,
percent: false,
isSort: false,
sortKey: '',
label:'表格四',
left: '',
color: '#fff',
showSort: false,
setData: false,
key: 'birthYear',
sizeKey: '',
unit: '',
},
],
import list from './list';
components: {list},
表格组件 list.vue
<template>
<div class="list-content" :id="id">
<div class="t-head" ref="tHead">
<span
v-for="(item,k) in tableConfig"
:key="k"
:class="['head']"
:style="{width: torem(item.width),left: torem(item.left)}"
@click.stop=""
>
<template >
<p v-html="item.label"></p>
</template>
</span>
</div>
<div class="t-data" ref="tData" id="tData">
<div class="t-tr" v-for="(item,i) in tableData" :key="i" :style="{width: torem(allWidth)}">
<span
v-for="(data,k) in tableConfig"
:key="k"
:class="['td',data.stock?'stock': '', color(item[data.sizeKey])]"
:style="{width: torem(data.width),left: torem(data.left),'font-size':torem(data.fontSize),'line-height':torem(data.lineHeight),'text-align':data.textAlign}"
@click="data.toKLine?toKLine(item):''"
>
<template v-if="data.showSort">
{{i+1}}
</template>
<template v-else-if="data.setData">
{{item[data.key]}}
</template>
<template v-else>
{{item[data.key] || '-'}}{{data.unit}}
</template>
</span>
</div>
<div class="no-data" v-show="tableData.length<1">暂无数据</div>
</div>
</div>
</template>
props: {
tableData: {
default: ()=> []
},
tableConfig: {
default: ()=> []
},
id: {
type: String,
default: 'listContent'
},
},
computed: {
//动态计算表格的宽度
allWidth(){
let width = 0;
this.tableConfig.forEach(item=>{
width+=item.width
});
return width + this.tableConfig.length;
}
},
methods: {
// 修改 vxe-table 中的滑动容器行为
bindTouchEvents () {
const element = document.getElementById(this.id);
if (element) {
element.addEventListener(
'touchstart',
event => {
this.touchX = event.changedTouches[0].clientX;
this.touchY = event.changedTouches[0].clientY;
},
true
)
element.addEventListener(
'touchmove',
event => {
event.preventDefault();
let html = document.getElementById('f10');//处理
// 计算手指偏移量
const offsetX = event.changedTouches[0].clientX - this.touchX
const offsetY = event.changedTouches[0].clientY - this.touchY
this.$refs.tHead.scrollLeft = this.$refs.tHead.scrollLeft - offsetX;
this.$refs.tData.scrollLeft = this.$refs.tData.scrollLeft - offsetX;
this.$parent.$el.scrollTop = this.$parent.$el.scrollTop -offsetY;
html.scrollTop = html.scrollTop - offsetY;
this.touchX = event.changedTouches[0].clientX
this.touchY = event.changedTouches[0].clientY
},
true
)
element.addEventListener(
'touchend',
event => {
this.touchX = event.changedTouches[0].clientX
this.touchY = event.changedTouches[0].clientY
},
true
)
} else {
console.warn('未获取到表格元素')
}
}
}
<style lang="scss" scoped>
.list-content {
// 表头
.t-head{
white-space: nowrap;
overflow-x: auto;
border-bottom: 1px solid #DEDEDE;
border-right: none;
font-size: 0;
& >span {
height: 48px;
border-right: 1px solid #DEDEDE;
font-size: 13px;
line-height: 18px;
color: #666666;
display: inline-flex;
align-items: center;
justify-content: center;
z-index: 0;
background: #F8F8F8;
vertical-align: top;
text-align: center;
&:nth-child(1){
position: -webkit-sticky;//固定第一列
position: sticky;
left: 0;
z-index: 2;
}
&:nth-child(2){
position: -webkit-sticky;//固定第二列
position: sticky;
z-index: 1;
}
&.sort{//排序
.icon-con{
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
transform: scale(0.6);
.icon {
font-size: 12px;
line-height: 12px;
}
}
&.act.desc{
.icon-con{
.icon{
&:nth-child(2){
color: #EA2827;
}
}
}
}
&.act.asc{
.icon-con{
.icon{
&:nth-child(1){
color: #EA2827;
}
}
}
}
}
}
}
// table体
.t-data{
overflow-x: auto;
box-sizing: border-box;
font-size: 0;
max-width: 100vw;
-webkit-overflow-scrolling: touch;
div{
white-space: nowrap;
border-bottom: 1px solid #DEDEDE;
display: flex;
align-items: stretch;
//隔行变色 todo end
& >span {
display: inline-block;
box-sizing: border-box;
margin-right: 1px;
font-size: 17px;
color: #333333;
display: inline-flex;
align-items: center;
justify-content: center;
z-index: 0;
padding: 8px 5px;
line-height: 21px;
white-space: normal;
&:nth-child(1){
position: -webkit-sticky;//固定第一列
position: sticky;
left: 0;
z-index: 2;
}
&:nth-child(2){
position: -webkit-sticky;//固定第二列
position: sticky;
z-index: 1;
}
}
}
}
}
</style>