开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第7天,点击查看活动详情
什么是tagsView
tagsView分为两部分
- tags
- view
tags需要实现点击跳转,删除,增加等功能
view只需要增加切换动画和缓存功能
tags实现
数据处理
tags的数据保存在vuex中
首先处理增加tag,增加时要判断该跳转的路由是否已经存在,不能重复添加,添加tag的方法写在mutations中
// TAGS_VIEW为常量
import { LANG, TAGS_VIEW } from '@/constant'
import { getItem, setItem } from '@/utils/storage'
export default {
namespaced: true,
state: () => ({
...
// tags数据
tagsViewList: getItem(TAGS_VIEW) || []
}),
mutations: {
// 添加 tags
addTagsViewList(state, tag) {
// 确认是否重复
const isFind = state.tagsViewList.find(item => {
return item.path === tag.path
})
// 处理重复
if (!isFind) {
// 增加tag
state.tagsViewList.push(tag)
// 增加后更新本地存储
setItem(TAGS_VIEW, state.tagsViewList)
}
}
},
actions: {}
}
当路由跳转时,需要判断跳转的路由是否需要添加tag,如登录注册页面,404页面和401页面就不需要添加tag
处理这个问题,新建util/tags.js
定义一个数组,在数组中保存不需要添加tag的路由,跳转路由时,判断跳转的路由是否在数组中,如在数组,则不添加
const whiteList = ['/login', '/404', '/401']
// 是否添加到tag
export function isTags(path) {
return !whiteList.includes(path)
}
在App.vue中,监听路由的变化。 如果需要添加到tag,从中解构出有用的信息,保存在vuex中
获取页签标题时,如果route中存在meta.title,直接获取即可,如果不存在,就将path中的最后一位当作标题
<script setup>
import { watch } from 'vue'
import { isTags } from '@/utils/tags'
import { generateTitle } from '@/utils/i18n'
import { useRoute } from 'vue-router'
import { useStore } from 'vuex'
const route = useRoute()
/**
* 生成 title
*/
const getTitle = route => {
let title = ''
if (!route.meta) {
// 处理无 meta 的路由
const pathArr = route.path.split('/')
title = pathArr[pathArr.length - 1]
} else {
title = generateTitle(route.meta.title)
}
return title
}
/**
* 监听路由变化
*/
const store = useStore()
watch(
route,
(to, from) => {
if (!isTags(to.path)) return
const { fullPath, meta, name, params, path, query } = to
// 触发增加tag方法
store.commit('app/addTagsViewList', {
fullPath,
meta,
name,
params,
path,
query,
// 页签标题
title: getTitle(to)
})
},
{
immediate: true
}
)
</script>
generateTitle为处理国际化方法,传入title,获取对应的值
export function generateTitle(title) {
return i18n.global.t('msg.route.' + title)
}
国际化处理
监听到语言变化后,更新对应的标题,先声明监听语言变化的方法
util/i18n.js
// 监听语言变化
export function watchSwitchLang(...cbs) {
watch(
() => store.getters.language,
() => {
cbs.forEach((cb) => cb())
}
)
}
在vuex中,创建修改title的方法
// 传入下标和更新后的tag
changeTagsView(state, { index, tag }) {
state.tagsViewList[index] = tag
setItem(TAGS_VIEW, state.tagsViewList)
}
在App.vue中调用监听方法,语言变化后,遍历tags数据源,更新tag时,只需要更新tag的标题即可,只需要再次调用getTitle方法
import { watchSwitchLang } from '@/utils/i18n'
watchSwitchLang(() => {
store.getters.tagsViewList.forEach((route, index) => {
store.commit('app/changeTagsView', {
index,
tag: {
...route,
title: getTitle(route)
}
})
})
})
页面渲染
这样数据就处理好了,拿到数据渲染
使用router-link将to指定为每个tag保存的路径即可
动态类名使选中的tag有不同的样式
新建components/tagsview/index.vue
<template>
<div class="tags-view-container">
<router-link
class="tags-view-item"
:class="isActive(tag) ? 'active' : ''"
:style="{
backgroundColor: isActive(tag) ? $store.getters.cssVar.menuBg : '',
borderColor: isActive(tag) ? $store.getters.cssVar.menuBg : ''
}"
v-for="(tag, index) in $store.getters.tagsViewList"
:key="tag.fullPath"
:to="{ path: tag.fullPath }"
>
{{ tag.title }}
<i
v-show="!isActive(tag)"
class="el-icon-close"
@click.prevent.stop="onCloseClick(index)"
/>
</router-link>
</div>
</template>
<script setup>
import { useRoute } from 'vue-router'
const route = useRoute()
// 是否被选中
const isActive = tag => {
return tag.path === route.path
}
// 关闭tag事件
const onCloseClick = index => {}
</script>
<style lang="scss" scoped>
.tags-view-container {
height: 34px;
width: 100%;
background: #fff;
border-bottom: 1px solid #d8dce5;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
.tags-view-item {
display: inline-block;
position: relative;
cursor: pointer;
height: 26px;
line-height: 26px;
border: 1px solid #d8dce5;
color: #495060;
background: #fff;
padding: 0 8px;
font-size: 12px;
margin-left: 5px;
margin-top: 4px;
&:first-of-type {
margin-left: 15px;
}
&:last-of-type {
margin-right: 15px;
}
&.active {
color: #fff;
&::before {
content: '';
background: #fff;
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
position: relative;
margin-right: 4px;
}
}
// close 按钮
.el-icon-close {
width: 16px;
height: 16px;
line-height: 10px;
vertical-align: 2px;
border-radius: 50%;
text-align: center;
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
transform-origin: 100% 50%;
&:before {
transform: scale(0.6);
display: inline-block;
vertical-align: -3px;
}
&:hover {
background-color: #b4bccc;
color: #fff;
}
}
}
}
</style>
点击tag后,左侧菜单栏也需要选中与其对应的选项
el-menu中default-active属性为默认激活菜单的 index,将其绑定一个计算属性
<el-menu
:default-active="activeMenu"
router
/>
activeMenu返回当前路由的path即可
const route = useRoute()
const activeMenu = computed(() => {
const { path } = route
// console.log('我在这里啊')
return path
})
由于下篇文章要实现点击后选择多种删除方法,关闭tag事件onCloseClick将在下篇文章补充