说明

该文章是属于OverallAuth2.0系列文章,每周更新一篇该系列文章(从0到1完成系统开发)。

该系统文章,我会尽量说的非常详细,做到不管新手、老手都能看懂。

说明:OverallAuth2.0 是一个简单、易懂、功能强大的权限+可视化流程管理系统。

友情提醒:本篇文章是属于系列文章,看该文章前,建议先看之前文章,可以更好理解项目结构。

qq群:801913255

有兴趣的朋友,请关注我吧(*^▽^*)。

关注我,学不会你来打我

上篇回顾

在上一篇:
(系列九)使用Vue3+Element Plus创建前端框架(附源码)
博客中,我们说道,使用vue3+element plus 创建项目,成功实现了布局组件container+菜单组件Menu搭建框架。

布局样式如下:

然而我们只是实现了界面的搭建,并没有实现任何交互。

也因此有很多人在询问,如何做动态切换菜单。

我想说,不要慌,一切需求都会安排到位。

接下来我们就要实现菜单和路由的结合使用,做到动态切换菜单。

安装路由

命令:npm install vue-router

安装成功后,手动创建以下目录及文件

base-routes.ts 内容

import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
import Panel
from '../../views/panel/index.vue';

export
const routes: RouteRecordRaw[] =[];

routes.push(
{
path:
'/panel',
component: Panel,
name:
"面板",
},
{
path:
'/menu',
redirect:
'/menu/index',
meta: { title:
'菜单管理'},
name:
"菜单管理",
children: [
{
path:
'/menu',
name:
'菜单',
component: ()
=> import('../../views/menu/index.vue'),
meta: { title:
'菜单', requireAuth: true, affix: true, closable: false},
}
]
},
{
path:
'/user',
meta: { title:
'用户管理'},
name:
"用户管理",
children: [
{
path:
'/user',
name:
'用户',
component: ()
=> import('../../views/user/index.vue'),
meta: { title:
'用户'},
}]
},
)
//创建路由,并且暴露出去 const router =createRouter({
history: createWebHashHistory(),
//开发环境//history:createWebHistory(),//正式环境 routes
})
export
default router

该文件主要是配置菜单的json文件,及暴露路由。里面的属性应该不必多说,很容易看懂。

至于views文件夹中的vue文件内容,大家随便填写什么都可以,只要三个页面的内容不一样即可。

然后在main.ts中配置路由,全局变量。

如下图:

使用路由

做完以上步骤,接下来的工作就很简单了,我们只需要,在HelloWorld.vue(接上一篇文章代码),中修改代码如下

el-main中的内容替换为 <router-view></router-view>
el-menu中添加 router属性
然后导入base-routes.ts 文件,并添加如下代码
  const menu =routes;return{
menu,
};

完整的HelloWorld.vue代码如下

<template>
  <div style="height: calc(100vh); overflow: hidden">
    <el-container style="height: 100%; overflow: hidden">
      <el-aside width="auto">
        <el-menuclass="el-menu-vertical-demo"background-color="#545c64"text-color="#fff"active-text-color="#ffd04b"style="height: 100%"router>
          <div class="el-menu-box">
            <divclass="logo-image"style="width: 18px; height: 18px; background-size: 18px 18px"
            ></div>
            <div style="padding-left: 5px; padding-top: 7px">OverallAuth2.0
            </div>
          </div>
          <div v-for="menuItem in menu" :key="menuItem.path">
            <el-sub-menu
v
-if="menuItem.children && menuItem.children.length":index="menuItem.path":key="menuItem.name" > <template #title> <el-icon><location /></el-icon>{{ menuItem.name }}</template> <el-menu-item
v
-for="subMenuItem in menuItem.children":index="subMenuItem.path":route="{ name: subMenuItem.name }":key="subMenuItem.name"style="cursor: pointer" >{{ subMenuItem.name }}</el-menu-item> </el-sub-menu> <el-menu-item
v
-else:index="menuItem.path":key="menuItem.path":route="{ name: menuItem.name }"style="cursor: pointer" >{{ menuItem.name }}</el-menu-item> </div> </el-menu> </el-aside> <el-container> <el-header class="headerCss"> <div style="display: flex; height: 100%; align-items: center"> <div
style
=" text-align: left;
width:
50%;
font
-size: 18px;
display: flex;
" > <div class="logo-image" style="width: 32px; height: 32px"></div> <div style="padding-left: 10px; padding-top: 7px">OverallAuth2.0权限管理系统</div> </div> <div
style
=" text-align: right;
width:
50%;
display: flex;
justify
-content: right;
cursor: pointer;
" > <divclass="user-image"style="width: 22px; height: 22px; background-size: 22px 22px" ></div> <div style="padding-left: 5px; padding-top: 3px">微信公众号:不只是码农</div> </div> </div> </el-header> <el-main> <router-view></router-view> </el-main> </el-container> </el-container> </div> </template> <script lang="ts">import { defineComponent,ref } from "vue";
import { routes }
from "../router/module/base-routes";

export
defaultdefineComponent({
setup() {
const menu =routes;return{
menu,
};
},
components: {},
});
</script> <style scoped>.el-menu-vertical-demo:not(.el-menu--collapse) {
width: 200px;
min
-height: 400px;
}
.el
-menu-box {
display: flex;
padding
-left: 25px;
align
-items: center;
height: 57px;
box
-shadow: 0 1px 4px #00152914;
border: 1px solid #
00152914;
color: white;
}
.el
-main {
padding
-top: 0px;
padding
-left: 1px;
padding
-right: 1px;
margin:
0;
}
.headerCss {
font
-size: 12px;
border: 1px solid #
00152914;
box
-shadow: 0 1px 4px #00152914;
justify
-content: right;
align
-items: center;/*display: flex;*/}
.logo
-image {
background
-image: url("../components/权限分配.png");
}
.user
-image {
background
-image: url("../components/用户.png");
}
.demo
-tabs /deep/ .el-tabs__header {
color: #
333; /*标签页头部字体颜色*/margin:0 0 5px !important;
}
.demo
-tabs /deep/ .el-tabs__nav-wrap {
padding
-left: 10px;
}
</style>

做好这些后,我们就能够动态切换菜单。

效果如下图

是不是很完美。

不,我想说,还没有完,还差的远。

接着看往下看

加入tab标签

可能大家也发现了,在我们点击左侧菜单时,访问过的菜单,在系统中没有历史访问标签。

现在我们做以下操作,把访问过的菜单记录到tab标签中,以防止系统重新对接口进行请求。

同样修改HelloWorld.vue文件。

把el-main标签中的内容换成

 <el-tabs
v
-if="tabsList.length > 0"v-model="defaultActive" class="demo-tabs"@click="tabsClick(defaultActive)"@tab-remove="tabRemoveClick" > <el-tab-pane
v
-for="item in tabsList":label="item.name":name="item.path":key="item.path":closable="item.path == '/panel' ? false : true"style="font-size: 16px;" > <router-view></router-view> </el-tab-pane> </el-tabs>
在el-menu-item标签中,加入菜单切换事件menuItemClick()
setup()方法中的内容替换成
setup() {const defaultActive = ref("/panel");const menu =routes;const tabsList = ref<RouteRecordRaw[]>([]);
onMounted(()
=>{
tabsList.value.push(routes[
0]);
router.push(routes[
0]);
});
//菜单项点击事件 function menuItemClick(subMenuItem: RouteRecordRaw) {//tabList中不存在则追加 if (!tabsList.value.some((sub) => sub.path ==subMenuItem.path)) {
tabsList.value.push(subMenuItem);
}
defaultActive.value
=subMenuItem.path;
}
//菜单标签点击事件 const tabsClick = (item: string) =>{
defaultActive.value
=item;
router.push({ path: item });
};
//菜单标签移除事件 const tabRemoveClick = (path: any) =>{
tabsList.value.map((item: { path:
string }, index: any) =>{if (item.path == path) tabsList.value.splice(index, 1); //index 当前元素索引;1:需要删除的元素个数 });
defaultActive.value
= "/panel";
router.push({ path:
"/panel"});
};
return{
menu,
tabsList,
defaultActive,
tabsClick,
tabRemoveClick,
menuItemClick,
};
},

以上是HelloWorld.vue文件中变动的代码。

我们来看下完整的HelloWorld.vue文件代码

<template>
  <div style="height: calc(100vh); overflow: hidden">
    <el-container style="height: 100%; overflow: hidden">
      <el-aside width="auto">
        <el-menu
:
default-active="defaultActive" class="el-menu-vertical-demo"background-color="#545c64"text-color="#fff"active-text-color="#ffd04b"style="height: 100%"router> <div class="el-menu-box"> <divclass="logo-image"style="width: 18px; height: 18px; background-size: 18px 18px" ></div> <div style="padding-left: 5px; padding-top: 7px">OverallAuth2.0 </div> </div> <div v-for="menuItem in menu" :key="menuItem.path"> <el-sub-menu
v
-if="menuItem.children && menuItem.children.length":index="menuItem.path":key="menuItem.name" > <template #title> <el-icon><location /></el-icon>{{ menuItem.name }}</template> <el-menu-item
v
-for="subMenuItem in menuItem.children":index="subMenuItem.path":route="{ name: subMenuItem.name }":key="subMenuItem.name"@click="menuItemClick(subMenuItem)"style="cursor: pointer" >{{ subMenuItem.name }}</el-menu-item> </el-sub-menu> <el-menu-item
v
-else:index="menuItem.path":key="menuItem.path":route="{ name: menuItem.name }"@click="menuItemClick(menuItem)"style="cursor: pointer" >{{ menuItem.name }}</el-menu-item> </div> </el-menu> </el-aside> <el-container> <el-header class="headerCss"> <div style="display: flex; height: 100%; align-items: center"> <div
style
=" text-align: left;
width:
50%;
font
-size: 18px;
display: flex;
" > <div class="logo-image" style="width: 32px; height: 32px"></div> <div style="padding-left: 10px; padding-top: 7px">OverallAuth2.0权限管理系统</div> </div> <div
style
=" text-align: right;
width:
50%;
display: flex;
justify
-content: right;
cursor: pointer;
" > <divclass="user-image"style="width: 22px; height: 22px; background-size: 22px 22px" ></div> <div style="padding-left: 5px; padding-top: 3px">微信公众号:不只是码农</div> </div> </div> </el-header> <el-main> <el-tabs
v
-if="tabsList.length > 0"v-model="defaultActive" class="demo-tabs"@click="tabsClick(defaultActive)"@tab-remove="tabRemoveClick" > <el-tab-pane
v
-for="item in tabsList":label="item.name":name="item.path":key="item.path":closable="item.path == '/panel' ? false : true"style="font-size: 16px" > <router-view></router-view> </el-tab-pane> </el-tabs> </el-main> </el-container> </el-container> </div> </template> <script lang="ts">import { defineComponent, onMounted,ref } from "vue";
import router, { routes }
from "../router/module/base-routes";
import { RouteRecordRaw }
from "vue-router";

export
defaultdefineComponent({
setup() {
const defaultActive = ref("/panel");const menu =routes;const tabsList = ref<RouteRecordRaw[]>([]);//初始加载dom onMounted(() =>{
tabsList.value.push(routes[
0]); //默认打开第一个标签 router.push(routes[0]);
});
//菜单项点击事件 function menuItemClick(subMenuItem: RouteRecordRaw) {//tabList中不存在则追加 if (!tabsList.value.some((sub) => sub.path ==subMenuItem.path)) {
tabsList.value.push(subMenuItem);
}
defaultActive.value
=subMenuItem.path;
}
//菜单标签点击事件 const tabsClick = (item: string) =>{
defaultActive.value
=item;
router.push({ path: item });
};
//菜单标签移除事件 const tabRemoveClick = (path: any) =>{
tabsList.value.map((item: { path:
string }, index: any) =>{if (item.path == path) tabsList.value.splice(index, 1); //index 当前元素索引;1:需要删除的元素个数 });
defaultActive.value
= "/panel";
router.push({ path:
"/panel"});
};
return{
menu,
tabsList,
defaultActive,
tabsClick,
tabRemoveClick,
menuItemClick,
};
},
components: {},
});
</script> <style scoped>.el-menu-vertical-demo:not(.el-menu--collapse) {
width: 200px;
min
-height: 400px;
}
.el
-menu-box {
display: flex;
padding
-left: 25px;
align
-items: center;
height: 57px;
box
-shadow: 0 1px 4px #00152914;
border: 1px solid #
00152914;
color: white;
}
.el
-main {
padding
-top: 0px;
padding
-left: 1px;
padding
-right: 1px;
margin:
0;
}
.headerCss {
font
-size: 12px;
border: 1px solid #
00152914;
box
-shadow: 0 1px 4px #00152914;
justify
-content: right;
align
-items: center;/*display: flex;*/}
.logo
-image {
background
-image: url("../components/权限分配.png");
}
.user
-image {
background
-image: url("../components/用户.png");
}
.demo
-tabs /deep/ .el-tabs__header {
color: #
333; /*标签页头部字体颜色*/margin:0 0 5px !important;
}
.demo
-tabs /deep/ .el-tabs__nav-wrap {
padding
-left: 10px;
}
</style>

我们看下效果

后端WebApi
预览地址:http://139.155.137.144:8880/swagger/index.html

前端vue 预览地址:http://139.155.137.144:8881

关注公众号:发送【权限】,获取前后端代码

有兴趣的朋友,请关注我微信公众号吧(*^▽^*)。

关注我:一个全栈多端的宝藏博主,定时分享技术文章,不定时分享开源项目。关注我,带你认识不一样的程序世界

标签: none

添加新评论