移动端原生实现列表列固定横向滚动功能

功能介绍:

在移动端开发中,会用到列表作为信息展示方式,一般希望上下滚动时,可以固定表头,左右滚动时,可以固定最左列。

需求:

1、列表可以使用数组循环遍历;
2、上下滚动时,可以固定表头在最顶端显示;
3、左右滚动时,可以固定左边一列或多列可以固定显示;
4、列表的列宽允许在数组中设置;

思路:

1、页面使用四个dom元素分别存储四种元素:

1)固定在左上角,完全不参与滚动表头元素;
2)固定在顶部,只允许左右滚动表头元素;
3)固定在左侧,只允许上下滚动列元素;
4)右下角,左右上下均可随意滚动列元素;

2、表头数组与列表数据数组之间互相联系,表头属性可以控制列表列排序、列表宽度、是否为固定列等;

3、四个dom之间增加联动,使用@scroll、scrollLeft、scrollTop;

示意图:

移动端原生实现列表列固定横向滚动功能_第1张图片

实现代码:

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;
}

效果图:

移动端原生实现列表列固定横向滚动功能_第2张图片

注意: 代码里的方法thItemClick是列排序功能,与此文章无关,实现代码未贴出,除此之外,其他未贴出的代码均与此文章所讲功能无关,忽略即可。

你可能感兴趣的:(vue,前端,列表固定,列表横向滚动,移动端列表固定滚动)