功能介绍:
在移动端开发中,会用到列表作为信息展示方式,一般希望上下滚动时,可以固定表头,左右滚动时,可以固定最左列。
需求:
1、列表可以使用数组循环遍历;
2、上下滚动时,可以固定表头在最顶端显示;
3、左右滚动时,可以固定左边一列或多列可以固定显示;
4、列表的列宽允许在数组中设置;
思路:
1、页面使用四个dom元素分别存储四种元素:
1)固定在左上角,完全不参与滚动表头元素;
2)固定在顶部,只允许左右滚动表头元素;
3)固定在左侧,只允许上下滚动列元素;
4)右下角,左右上下均可随意滚动列元素;
2、表头数组与列表数据数组之间互相联系,表头属性可以控制列表列排序、列表宽度、是否为固定列等;
3、四个dom之间增加联动,使用@scroll、scrollLeft、scrollTop;
示意图:
实现代码:
html代码:
<div class="table-box">
<div class="listFlexSty">
<div class="fixedHeadBox" :style="{width: fixedWid}">
<div
class="thClass"
v-for="(item, index) in fixedHead"
:key="index"
:style="{width: item.width, justifyContent:item.name === '名称'?'flex-start':'',padding:item.name === '名称'?'0 10px':''}"
@click="thItemClick(item)">
<div>{{item.name}}</div>
<div class="playIconSty">
<div class="topArrow"></div>
<div class="bottomArrow"></div>
</div>
</div>
</div>
<div
class="nomalHeadBox"
style="
{
width: 'calc(100% - ' + fixedWid + ')';
}
">
<div ref="nomalHeadBox" @scroll="scrollHList">
<div class="thClass" :style="{width: nomalWid}">
<div
class="thClass"
v-for="(item, index) in nomalHead"
:key="index"
:style="{width: item.width,padding:item.name === '折扣偏差'?'0 10px':''}"
@click="thItemClick(item)">
<div>{{item.name}}</div>
<div class="playIconSty">
<div class="topArrow"></div>
<div class="bottomArrow"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div style="height: calc(100% - 40px); overflow: auto" id="dataBodyId">
<div v-show="tBodyData.length!==0" class="listFlexSty">
<div class="fixedListBox" :style="{width: fixedWid}">
<div ref="fixedListBox" @scroll="scrollFList">
<div class="rLineSty" v-for="(item, index) in tBodyData" :key="index">
<div
v-for="(it, inx) in fixedHead"
:key="inx"
:style="{width: it.width, justifyContent:it.name === '名称'?'flex-start':'',padding:it.name === '名称'?'0 10px':''}"
class="thClass">
<span v-if="it.prop === 'storeName' || it.prop === 'curDiscount'"
>{{item[it.prop]}}</span
>
<span
v-if="it.prop === 'orderAmount' || it.prop === 'diffAmount'"
v-format="'#,##0.##'"
>{{item[it.prop]}}</span
>
<span v-if="it.prop === 'completionRate'">{{item[it.prop]}}%</span>
<span
v-if="it.prop === 'yearEarlier'"
:class="item[it.prop]<0?'downArrow':item[it.prop]>0?'upArrow':''"
>{{item[it.prop]}}%</span
>
<span v-if="it.prop === 'diffDiscount'"
>{{item[it.prop]>0?'+':''}}{{item[it.prop]}}</span
>
</div>
</div>
</div>
</div>
<div
class="nomalListBox"
ref="nomalListBox"
:style="{width: 'calc(100% - '+fixedWid+')'}"
@scroll="scrollList">
<div
class="rLineSty"
:style="{width: nomalWid}"
v-for="(item, index) in tBodyData"
:key="index">
<div
v-for="(it, inx) in nomalHead"
:key="inx"
:style="{width: it.width,padding:it.name === '折扣偏差'?'0 10px':''}"
class="thClass">
<span v-if="it.prop === 'storeName' || it.prop === 'curDiscount'"
>{{item[it.prop]}}</span
>
<span v-if="it.prop === 'orderAmount' || it.prop === 'diffAmount'" v-format="'#,##0.##'"
>{{item[it.prop]}}</span
>
<span v-if="it.prop === 'completionRate'">{{item[it.prop]}}%</span>
<span
v-if="it.prop === 'yearEarlier'"
:class="item[it.prop]<0?'downArrow':item[it.prop]>0?'upArrow':''"
>{{item[it.prop]}}%</span
>
<span v-if="it.prop === 'diffDiscount'"
>{{item[it.prop]>0?'+':''}}{{item[it.prop]}}</span
>
</div>
</div>
</div>
</div>
<div v-show="tBodyData.length>0 && !finished" class="bottomTip" @click="moreLoad">
<span style="color: #999999">展开查看更多</span>
<van-icon name="arrow-down" color="#999999" />
</div>
<div v-show="tBodyData.length>0 && finished" class="bottomTip">
<span style="color: #999999">已加载完全部数据</span>
</div>
<div v-show="tBodyData.length===0" class="noData">暂无数据</div>
</div>
</div>
js代码:
data(){
return {
// 下面是首页底部列表数据相关字段
tHeadData: [
{ name: '名称', prop: 'storeName', width: '100px', isfixed: true },
{ name: '总业绩(元)', prop: 'orderAmount', width: '80px' },
{ name: '平均折扣', prop: 'curDiscount', width: '80px' },
{ name: '同比', prop: 'yearEarlier', width: '60px' },
{ name: '完成率', prop: 'completionRate', width: '80px' },
{ name: '缺口(元)', prop: 'diffAmount', width: '100px' },
{ name: '折扣偏差', prop: 'diffDiscount', width: '80px' }
],
tBodyData: [],
fixedHead: [],
nomalHead: [],
fixedWid: '',
nomalWid: ''
}
},
methods: {
// 列表数据相关
initData() {
this.fixedHead = this.tHeadData.filter(item => {
return item.isfixed;
});
this.nomalHead = this.tHeadData.filter(item => {
return !item.isfixed;
});
this.initSize();
},
initSize() {
let fwid = 0;
let nwid = 0;
this.fixedHead.forEach(item => {
// 此处以px单位为例
const len = item.width.length - 2;
const width = item.width.substring(0, len) - 0;
fwid += width;
});
this.nomalHead.forEach(item => {
const len = item.width.length - 2;
const width = item.width.substring(0, len) - 0;
nwid += width;
});
this.fixedWid = fwid + 'px';
this.nomalWid = nwid + 'px';
},
// 首页下方数据列表联动相关
scrollHList() {
this.$refs.nomalListBox.scrollLeft = this.$refs.nomalHeadBox.scrollLeft;
},
scrollFList() {
this.$refs.nomalListBox.scrollTop = this.$refs.fixedListBox.scrollTop;
},
scrollList() {
this.$refs.fixedListBox.scrollTop = this.$refs.nomalListBox.scrollTop;
this.$refs.nomalHeadBox.scrollLeft = this.$refs.nomalListBox.scrollLeft;
}
}
css代码:
.table-box {
width: 100%;
height: calc(100% - 80px);
overflow: hidden;
}
.listFlexSty {
display: flex;
}
.fixedHeadBox {
height: 40px;
line-height: 40px;
color: #333333;
font-size: 12px;
}
.nomalHeadBox {
height: 40px;
line-height: 40px;
overflow: hidden;
color: #333333;
font-size: 12px;
}
.fixedListBox {
height: 100%;
overflow: hidden;
color: #666666;
font-size: 12px;
}
.nomalListBox {
height: 100%;
overflow: auto;
color: #666666;
font-size: 12px;
}
.thClass {
display: flex;
align-items: center;
justify-content: flex-end;
}
.rLineSty {
height: 34px;
padding: 10px 0;
display: flex;
}
.rLineSty > div {
display: -webkit-box;
-webkit-line-clamp: 2;
overflow: hidden;
}
/* 隐藏滚动条 */
/* 隐藏右边表格头部滚动条 */
.nomalHeadBox > div {
overflow: auto;
height: calc(100% + 10px);
}
/* 隐藏左边列表滚动条 */
.fixedListBox > div {
overflow: auto;
height: 100%;
width: calc(100% + 10px);
}
.noDataNew {
height: calc(100% - 40px);
display: flex;
align-items: center;
justify-content: center;
color: #999;
font-size: 12px;
}
效果图:
注意: 代码里的方法thItemClick
是列排序功能,与此文章无关,实现代码未贴出,除此之外,其他未贴出的代码均与此文章所讲功能无关,忽略即可。