携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第28天,点击查看活动详情
后台项目前端综合解决方案之通用功能开发
tagsView 原理及方案分析
tagsView 其实可以分为两个部分,一个是 tags,另一个则是 view
我们先来看一看 tags,那么什么是 tags 呢?其实很简单,它就是位于 appmain 之上的标签
知道了 tags,我们再来看看 views,其实脱离开 tags,views 指的就是一个用来渲染组件的位置,就像我们之前的 AppMain 一样,只不过这里的 views 可能稍微复杂一点,因为他要在渲染的基础上增加动画以及缓存这两个额外的功能
加上这两个功能可能回略显复杂,但是官网已经帮助我们处理了这个问题
明确了原理之后,我们就来看看实现方案
1、创建 tagsView 组件,用来处理 tags 的展示
2、处理基于路由的动态过渡,在 AppMain 中进行,用于处理 view 部分
整个的方案就是这么两部分,但是其中我们还需要处理一些相关的细节
1、监听路由变化,组成用于渲染的 tags 数据源
2、创建 tags 组件,根据数据源渲染 tag,渲染出来的 tags 需要同时具备
- 国际化
- 路由跳转
3、处理鼠标右键效果,根据右键处理对应数据源
4、处理基于路由的动态过度
创建 tags 数据源
tags 数据源分为两部分
1、保存数据:appmain 组件中进行
2、展示数据:tags 组件中进行
所以 tags 的数据我们最好把它保存到 vuex 中
在 constant 中新建常量
// tags
export const TAGS_VIEW = 'tagsView'
在 store/app 中创建 tagsViewList
import { LANG, TAGS_VIEW } from '@/constant'
import { getItem, setItem } from '@/utils/storage'
export default {
namespaced: true,
state: () => ({
tagsViewList: getItem(TAGS_VIEW) || []
}),
mutations: {
/**
* 添加 tags
*/
addTagsViewList(state, tag) {
const isFind = state.tagsViewList.find(item => {
return item.path === tag.path
})
// 处理重复
if (!isFind) {
state.tagsViewList.push(tag)
setItem(TAGS_VIEW, state.tagsViewList)
}
}
},
actions: {}
}
在 appmain 中监听路由变化
<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
store.commit('app/addTagsViewList', {
fullPath,
meta,
name,
params,
path,
query,
title: getTitle(to)
})
},
{
immediate: true
}
)
</script>
创建 utils/tags
const whiteList = ['/login', '/import', '/404', '/401']
/**
* path 是否需要被缓存
* @param {*} path
* @returns
*/
export function isTags(path) {
return !whiteList.includes(path)
}
生成 tagsView
目前数据已经被保存到 store 中,那么接下来我们就依赖数据渲染 tags
创建 store/app 中 tagsViewList 的快捷访问
tagsViewList: state => state.app.tagsViewList
创建 compenent/tagsview
<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>
在 layout/index 中导入
<div class="fixed-header">
<!-- 顶部的 navbar -->
<navbar />
<!-- tags -->
<tags-view></tags-view>
</div>
import TagsView from '@/components/TagsView'