此篇文章主要介绍如何利用vuex实现多标签保持页面管理功能,效果如图所示:
希望本篇文章对有需要实现类似功能的小伙伴,有一点点借鉴效果,好了废话不多说一步一步来介绍。
运用的Vue技术点参考
关于vue-router应用参考: https://blog.youkuaiyun.com/sunshouyan/article/details/107016023
关于vuex应用参考:https://blog.youkuaiyun.com/sunshouyan/article/details/107020699
vue的components组件参数配置以及keep-alive的综合运用;
多标签保持功能实现具体效果
- 实现多标签页面打开;
- 实现按需求页面内容缓存;
- 实现浏览器刷新标签依然保持;
- 实现标签和菜单联动定位;
- 实现动态参数标签页面;
- 实现标签打开溢出鼠标滚轮滚动查看;
1. 平台整体框架搭建
首先具体实现功能之前,我们先对自己想要实现的前端交互样式有一个简单的规划,当然这个可以根据具体设计UI图调整,但基本思路不会变。
如下图:
a、顶部是公用的头部展示区域;
b、左侧是权限菜单展示区域;
c、右侧上部分是多标签展示区域;
d、右侧下部分是动态路由页面切换区域;
操作链:
正向操作:b菜单操作->产生c和d
反向操作:c标签操作-> 联动b和d
根据以上交互思路,我们简单把主框架拆分成不同的小组件,组成整体布局Layout;
Layout组成包括:
index.vue // 框架主入口组件
navBar.vue // 左侧动态菜单展示组件
tagNav.vue // 标签展示和操作组件
scrollBar.vue // 标签打开过多的情况,负责标签滚动计算和滚动
Layout / index.vue代码结构如下:
<template>
<div class="layout">
<!-- 公用头部区域 -->
<div class="layout-top">
<div class="logo">{
{
sysName}}</div>
<div class="info">
<span><i class="el-icon-s-custom"></i> {
{
userName}}</span>
<span><i class="el-icon-bell"></i> 消息</span>
<span><i class="el-icon-setting"></i> 设置</span>
<span @click="closeAllTags"><i class="el-icon-delete"></i> 清空标签</span>
</div>
</div>
<div class="layout-main">
<!-- 动态菜单展示区域 -->
<div class="layout-main-left">
<NavBar :isCollapse='menuCollapse'></NavBar>
</div>
<div class="layout-main-right">
<!-- 标签展示区域 -->
<div class="right-nav">
<TagNav></TagNav>
</div>
<!-- 标签动态路由区域 -->
<div class="right-inner">
<keep-alive :include="tagNavList">
<router-view></router-view>
</keep-alive>
</div>
</div>
</div>
</div>
</template>
<script>
/**
* @description 框架入口组件
* @author sunsy
* @createTime 2020/07/11
*/
import NavBar from "./navBar"
import TagNav from "./tagNav";
export default {
name: "layout",
components: {
TagNav,
NavBar,
},
data() {
return {
// 系统名称信息
sysName: "砖农吃瓜平台",
// 登录的用户信息
userName: "sunsy",
// 菜单栏是否是展开状态
menuCollapse: false,
};
},
computed: {
// 读取缓存的标签页面
tagNavList() {
return this.$store.state.tagNav.cachedPageName;
}
},
methods: {
// 清空之前缓存的所有标签以及缓存数据
closeAllTags() {
this.$store.dispatch('tagNav/clearAllTag');
}
},
created() {
//let user = localStorage.getItem("userInfo");
//if (user) {
// this.userInfo = JSON.parse(user);
// }
},
}
准备好主框架后,我们需要先准备动态菜单和路由,因为只有了菜单和路由的基础,才能产生我们的标签数据。
2. 准备动态菜单数据和路由配置
本文中示例项目中所有的请求都采用Mock模拟后端返回,所以如果有按照本文流程测试的小伙伴记得安装Mock依赖;
a. 菜单准备
mock/navlist.js数据格式如下:
需要注意点:
菜单里存在显性菜单和隐性菜单两种:
1.显性菜单指菜单名称需要在菜单列表显示出来;
2.隐性菜单指菜单名称不需要在菜单列表展示出来,但是又属于用户权限里的一个,一般出现在详情二级页面的访问的情况;
3.在以上Json数据中,type:0 指显性菜单; type:2 指隐性菜单; 可以根据自己情况配置测试。
ps: 其实还有一种菜单权限是指操作菜单type:1;(例如新增、删除、编辑按钮)但是这个牵扯到用户权限系统的时才使用,演示项目并不牵扯到用户权限,所以本文中不做具体介绍。
动态菜单采取了vuex状态管理,首次请求本地做缓存,以提高访问性能和速度。
strore/modules/auth/index.js文件代码:
/**
* @description 用户菜单权限状态管理模块
* @author sunsy
* @createTime 2019/08/19
*/
import axios from '@/util/ajax'
const state = {
navList: [],
}
const mutations = {
setNavList: (state, data) => {
state.navList = data
},
}
const actions = {
// 获取该用户的菜单列表
getNavList({
commit }) {
return axios.get({
url: '/api/menu',
data:''
}).then((res) => {
let data = res.data || [];
commit("setNavList", data);
});
},
// 将菜单列表扁平化形成权限列表
getPermissionList({
state }) {
return new Promise((resolve) => {
let permissionList = []
// 将菜单数据扁平化为一级
function flatNavList(arr) {
for (let v of arr) {
if (v.children && v.children.length) {
flatNavList(v.children)
} else {
permissionList.push(v)
}
}
}
flatNavList(state.navList)
resolve(permissionList)
})
},
// 清空缓存的菜单数据
clearAllMenu: ({
commit }) => {
commit("setNavList", []);
},
}
export default {
namespaced: true,
state,
mutations,
actions
}
b. 路由配置
模拟准备好菜单数据后,接下来就是准备菜单对应的组件以及路由和菜单映射关系配置。
router/staticRoute.js文件:
需要注意点:
1.需要用标签打开的路由,组件加载都是基于layout组件,所以组件引入的时候父组件记得配置为layout;
2.不需要标签打开的路由,不需要如此配置;
c. 菜单和路由初始化
菜单和对应的组件配置准备好后,接下来就是初始化路由的时候引入菜单并做相关的业务操作处理。
router/index.js 文件代码:
import Vue from 'vue'
import VueRouter from 'vue-router'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import store from '../store'
import staticRoute from './staticRoute'