功能介绍:
在移动端开发中,会用到列表作为信息展示方式,一般希望上下滚动时,可以固定表头,左右滚动时,可以固定最左列。
需求:
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}"><divclass="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><divclass="nomalHeadBox"style="{width: 'calc(100% - ' + fixedWid + ')';}"><div ref="nomalHeadBox" @scroll="scrollHList"><div class="thClass" :style="{width: nomalWid}"><divclass="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"><divv-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><spanv-if="it.prop === 'orderAmount' || it.prop === 'diffAmount'"v-format="'#,##0.##'">{{item[it.prop]}}</span><span v-if="it.prop === 'completionRate'">{{item[it.prop]}}%</span><spanv-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><divclass="nomalListBox"ref="nomalListBox":style="{width: 'calc(100% - '+fixedWid+')'}"@scroll="scrollList"><divclass="rLineSty":style="{width: nomalWid}"v-for="(item, index) in tBodyData":key="index"><divv-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><spanv-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
是列排序功能,与此文章无关,实现代码未贴出,除此之外,其他未贴出的代码均与此文章所讲功能无关,忽略即可。