概述:通过beforeEach拦截。vuex+sessionStorage实现持久化(路由历史应该随浏览器关闭而清空)。el-scrollbar elementUi 的隐藏组件实现滚动条。

tag点击事件,调用方法计算scrollbr宽度,计算tag宽度,计算tag距离left(左边)多少px。tag宽度+距离右边再加20px左右,如果大于scrollbr宽度,证明tag所在位置已经到了最右边,实现点击tag之后scrollbr跟随滚动。

初始化数据渲染完成之后,通过当前路由获取下标,实现初始化计算。

1、利用路由守位前卫beforeEach拦截,每次路由记录到缓存。

import Vue from 'vue'
import VueRouter from 'vue-router'
import store from '../store'

// 导航首位拦截,判断是否登录,及访问权限
router.beforeEach((to, from, next) => {
	setRouterHistory(to)	
})

// 记录路由历史
const setRouterHistory = (to) => {
	let obj = {
		title: to.meta.title,
		path: to.path,
	}
	let routerHistory = store.state.app.routerHistory;
	routerHistory.push(obj);
    //map去重,避免重复记录重复路由
	let mapArr = () => {
		let map = new Map();
		for (let item of routerHistory) {
			if (!map.has(item.path)) {
				map.set(item.path, item);
			}
		}
		return [...map.values()];
	}
	let newArr = mapArr();
	store.commit('setRouterHistory', newArr)
}

2、vuex+sessionStorage实现持久化 app.js

import storage from '../storage.js'
const session = "sessionStorage"
export default {
	state: {
		routerHistory: storage.getValue('routerHistory') || [],
		nowRouter: storage.getValue('nowRouter'),
	},
	getters: {
        routerHistory: state => state.app.routerHistory,
    },
	mutations: {
		// 设置路由历史
		setRouterHistory(state, routerHistory) {
			state.routerHistory = routerHistory;
			storage.setValue('routerHistory', routerHistory);
		},
		// 清除路由历史
		cleanRouterHistory(state) {
			state.routerHistory = [];
			storage.cleanValue('routerHistory');
		},
		// 设置当前路由
		setNowRouter(state, nowRouter) {
			state.nowRouter = nowRouter;
			storage.setValue('nowRouter', nowRouter);
		},
		// 清除当前路由
		cleanNowRouter(state) {
			state.nowRouter = undefined;
			storage.cleanValue('nowRouter');
		},
	},
	actions: {},
}

3、本地储存文件,路由历史应该关闭浏览器就清除,所以应该使用sessionStorage

export default {
	setValue(key, value, type) {
		if (typeof value == "object") {
			// 如果是对象,就转字符串存储
			value = JSON.stringify(value)
		}
		return window[type || 'localStorage'].setItem(key, value);
	},
	getValue(key, type) {
		const data = window[type || 'localStorage'].getItem(key);
		if (data) {
			let value = data;
			if ((data.indexOf('{') === 0 || data.indexOf('[') === 0) && data.indexOf(':') >= 0) {
				// 如果是字符串对象,就转对象
				value = JSON.parse(data)
			}
			return value
		} else {
			return false
		}
	},
	cleanValue(key, type) {
		window[type || 'localStorage'].removeItem(key);
	}
}

4、把scrollbar和tag封装成组件,通过点击tag,获取tag位置,并计算scrollbar和tag宽度,实现根据tag位置,scrollbar跟随滚动

<template>
	<div class="div_">
		<el-scrollbar ref="scrollbar" :vertical="false" class="scrollbar_" @wheel.native.prevent="handleScroll">
			<el-tag :ref="'tag'+index" v-for="(item, index) in routerHistory" :key="index" :closable="index!=0"
				:type="nowRouter==item.path?'':'info'" @close="removeTag(item.path)" @click="tagClick(item.path,index)">
				{{item.title}}
			</el-tag>
		</el-scrollbar>
	</div>
</template>
<script>
	import {
		mapGetters
	} from 'vuex'
	export default {
		data() {
			return {
				nowRouter: '',
			}
		},
		computed: {
			...mapGetters(["routerHistory"]),
		},
		watch: {
			$route: {
				handler: function(val, oldVal) {
					this.nowRouter = val.path
					this.$store.commit('setNowRouter', val.path)
				},
				// 深度观察监听
				deep: true
			}
		},
		created() {
			this.nowRouter = this.$store.state.app.nowRouter;
			// 通过当前路由获取路由下标
			let nowRouterIndex = this.routerHistory.findIndex(item => item.path == this.nowRouter);
			//在页面渲染完成后调用计算滚动条位置
			this.$nextTick(() => {
				this.countSroll(nowRouterIndex)
			})
		},
		methods: {
			removeTag(routerPath) {
				// 删除tag返回新路由历史数组并存入vuex
				let newArr = this.routerHistory.filter(item => item.path !== routerPath);
				this.$store.commit('setRouterHistory', newArr)
				// 如果删除的路由是当前路由,路由跳转至目前路由历史数组最后一个路由
				if (this.$route.path == routerPath) {
					this.$router.push({
						path: newArr[newArr.length - 1].path
					})
				}
			},
			tagClick(path, index) {
				this.countSroll(index);
				this.$router.push(path);
			},
			countSroll(index) {
				// 获取tag宽度
				let tagWidth = this.$refs['tag' + index][0].$el.offsetWidth;
				// 获取tag距离右边宽度
				let tagMargin = this.$refs['tag' + index][0].$el.offsetLeft;
				// 获取sroll宽度
				let srollWidth = this.$refs.scrollbar.$el.offsetWidth;
				// 如果tag距离右边+tag宽度+20左右已经大于sroll宽度,表示tag已经到了尽头了
				if ((tagMargin + tagWidth + 20) > srollWidth) {
					this.$refs.scrollbar.$refs.wrap.scrollLeft = tagMargin
				} else {
					this.$refs.scrollbar.$refs.wrap.scrollLeft = 0
				}
			},
			handleScroll(e) {
				const eventDelta = e.wheelDelta || -e.deltaY * 40
				this.$refs.scrollbar.$refs.wrap.scrollLeft = this.$refs.scrollbar.$refs.wrap.scrollLeft + eventDelta / 4
			},
		},
	}
</script>
<style lang="less" scoped>
	.div_ {
		height: 48px;
		padding: 0 15px;
		background-color: white;
		box-sizing: border-box;

		.scrollbar_ {
			height: 100%;
			width: 100%;
			white-space: nowrap;

			::v-deep .el-scrollbar__view {
				height: 100%;
				display: flex;
				align-items: center;
			}
		}
	}

	.el-tag {
		margin-left: 10px;
		cursor: pointer
	}

	:first-child {
		margin: 0;
	}
</style>

5、home引用后,实现效果展示

Logo

前往低代码交流专区

更多推荐