提示:文章內(nèi)容仔細(xì)看一些,或者直接粘貼復(fù)制,效果滿滿
前言
提示:文章大概
1、項(xiàng)目:前后端分離
2、前端:基于Vite創(chuàng)建的Vue3項(xiàng)目
3、后端:沒有,模擬的后端數(shù)據(jù)
4、關(guān)于路徑“@”符號(hào)——vite.config.js 文件里修改
提示:以下是本篇文章正文內(nèi)容,下面案例可供復(fù)制粘貼使用,嘎嘎爽
一、技術(shù)棧
- Vite 創(chuàng)建 Vue3 項(xiàng)目
# 1.創(chuàng)建項(xiàng)目
npm create vite@latest
# 2.下載依賴
npm install
# 3.運(yùn)行項(xiàng)目
npm run dev
- Element-plus
# 1.下載
npm install element-plus --save
# 2.main.js 引入
// main.ts
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')
- Vue-Router
# 1.安裝
npm install vue-router@4
- nprogress (進(jìn)度條——非必選,好看而已)
npm i nprogress -S
二、項(xiàng)目結(jié)構(gòu)
三、菜單組件和數(shù)據(jù)
說明:
- AsideMenu.vue 引用 LeftSubMenu.vue 組件,并父傳子傳入后端數(shù)據(jù)
- LeftSubMenu.vue 組件加載數(shù)據(jù)
- menuData.json 后端模擬數(shù)據(jù)文件
1、AsideMenu.vue 組件
代碼如下(示例):
<template>
<el-menu router :default-active="activeMenu" :class="'menu-left'" :default-openeds="openedsArr" text-color="#fff">
<LeftSubMenu :menuData="treeMenu"></LeftSubMenu>
</el-menu>
</template>
<script setup>
import LeftSubMenu from "./LeftSubMenu.vue";
import { computed } from "vue";
import { useRouter } from "vue-router";
import treeMenu from './menuData.json';
const openedsArr = treeMenu.map((item) => {
return item.path;
});
const activeMenu = computed(() => {
const router = useRouter();
const { meta, path } = router.currentRoute.value;
if (meta.matchPath2) {
return meta.matchPath2;
} else {
return path;
}
});
</script>
<style scoped>
.menu-left {
flex: 1;
padding: 0 8px;
border-right: none;
background: none;
}
.menu-left:deep(.el-menu),
.menu-left:deep(.el-sub-menu__title:hover) {
background: none;
}
.menu-left:deep(.el-menu-item),
.menu-left:deep(.el-sub-menu__title) {
height: 36px;
margin-bottom: 4px;
border-radius: 4px;
color: var(--text-main-color) !important;
}
.menu-left:deep(.el-menu-item:hover .icon),
.menu-left:deep(.el-menu-item.is-active .icon) {
filter: invert(100%);
-webkit-filter: invert(100%);
}
.menu-left:deep(.el-menu-item:hover),
.menu-left:deep(.el-menu-item.is-active) {
color: #ffffff !important;
background-color: #eecece;
}
</style>
2、LeftSubMenu.vue
代碼如下(示例):
<template>
<template v-for="item in props.menuData">
<el-sub-menu :key="item.path" v-if="item.children && item.children.length > 0" :index="item.path">
<template #title>
<el-icon>
<component :is="item.icon"></component>
</el-icon>
<span>{{ item.meta.title }}</span>
</template>
<LeftSubMenu :menuData="item.children"></LeftSubMenu>
</el-sub-menu>
<el-menu-item :key="item.id" v-else :index="item.path" :disabled="item.disabled">
<template #title>
<!-- <img class="icon pd-r-10" :src="item.icon" /> -->
<el-icon>
<component :is="item.icon"></component>
</el-icon>
<span>{{ item.meta.title }}</span>
</template>
</el-menu-item>
</template>
</template>
<script setup>
import LeftSubMenu from "./LeftSubMenu.vue";
import { computed, onMounted } from "vue";
import { useRouter } from "vue-router";
const props = defineProps({
menuData: {
type: Array,
default: [],
},
});
onMounted(() => {
console.log(props.menuData, "Item打印數(shù)據(jù)");
});
const curRoute = computed(() => {
const router = useRouter();
const { path } = router.currentRoute.value;
return path;
});
</script>
3、menuData.json 數(shù)據(jù)
數(shù)據(jù)參數(shù)說明:
- menuType: 0為菜單組,1為菜單(可跳轉(zhuǎn))
- children: 子路由
數(shù)據(jù)說明:不復(fù)制
{
"id": "1", // 唯一id
"name": "Home", // 組件名稱
"path": "/home", // 路由
"component": "/home/index.vue", // 組件文件位置
"menuType": "1", // 組件類型
"icon": "Discount", // 圖標(biāo)
"sort": 0, // 排序規(guī)則
"meta": {
"title": "系統(tǒng)首頁", // 組件名稱
"requiresAuth": null, // 是否需要身份驗(yàn)證
"roles": [], // 用戶角色或權(quán)限
"breadcrumb": [ // 定義面包屑導(dǎo)航
{}
],
"keepAlive": null // 是否需要緩存
},
"children": [] // 子路由
}
代碼如下(示例):
[
{
"id": "1",
"name": "Home",
"path": "/home",
"component": "/home/index.vue",
"menuType": "1",
"icon": "Discount",
"sort": 0,
"meta": {
"title": "系統(tǒng)首頁",
"requiresAuth": null,
"roles": [],
"breadcrumb": [
{}
],
"keepAlive": null
},
"children": []
},
{
"id": "2",
"name": "System",
"path": "/system",
"component": "/system/index.vue",
"menuType": "0",
"icon": "Operation",
"sort": 0,
"meta": {
"title": "系統(tǒng)管理",
"requiresAuth": null,
"roles": [],
"breadcrumb": [
{}
],
"keepAlive": null
},
"children": [
{
"id": "211",
"name": "User",
"path": "/user",
"component": "/user/index.vue",
"menuType": "1",
"icon": "user",
"sort": 0,
"meta": {
"title": "用戶管理",
"requiresAuth": null,
"roles": [],
"breadcrumb": [
{}
],
"keepAlive": null
},
"children": []
},
{
"id": "222",
"name": "Menu",
"path": "/menu",
"component": "/menu/index.vue",
"menuType": "1",
"icon": "Menu",
"sort": 0,
"meta": {
"title": "菜單管理",
"requiresAuth": null,
"roles": [],
"breadcrumb": [
{}
],
"keepAlive": null
},
"children": []
},
{
"id": "223",
"name": "Role",
"path": "/role",
"component": "/role/index.vue",
"menuType": "1",
"icon": "Avatar",
"sort": 0,
"meta": {
"title": "角色管理",
"requiresAuth": null,
"roles": [],
"breadcrumb": [
{}
],
"keepAlive": null
},
"children": []
}
]
},
{
"id": "3",
"name": "Log",
"path": "/log",
"component": "/log/index.vue",
"menuType": "1",
"icon": "Notebook",
"sort": 0,
"meta": {
"title": "日志管理",
"requiresAuth": null,
"roles": [],
"breadcrumb": [
{}
],
"keepAlive": null
},
"children": []
},
{
"id": "4",
"name": "Study",
"path": "/study",
"component": "/study/index.vue",
"menuType": "0",
"icon": "Notebook",
"sort": 0,
"meta": {
"title": "學(xué)習(xí)管理",
"requiresAuth": null,
"roles": [],
"breadcrumb": [
{}
],
"keepAlive": null
},
"children": [
{
"id": "441",
"name": "StudyUser",
"path": "/studyUser",
"component": "/study/user/index.vue",
"menuType": "0",
"icon": "Notebook",
"sort": 0,
"meta": {
"title": "用戶管理",
"requiresAuth": null,
"roles": [],
"breadcrumb": [
{}
],
"keepAlive": null
},
"children": [
{
"id": "4441",
"name": "Student",
"path": "/student",
"component": "/study/user/student/index.vue",
"menuType": "1",
"icon": "Notebook",
"sort": 0,
"meta": {
"title": "學(xué)生管理",
"requiresAuth": null,
"roles": [],
"breadcrumb": [
{}
],
"keepAlive": null
},
"children": []
},
{
"id": "4442",
"name": "Teacher",
"path": "/teacher",
"component": "/study/user/teacher/index.vue",
"menuType": "1",
"icon": "Notebook",
"sort": 0,
"meta": {
"title": "教師管理",
"requiresAuth": null,
"roles": [],
"breadcrumb": [
{}
],
"keepAlive": null
},
"children": []
}
]
},
{
"id": "3",
"name": "Log",
"path": "/log",
"component": "/log/index.vue",
"menuType": "1",
"icon": "Notebook",
"sort": 0,
"meta": {
"title": "打卡記錄",
"requiresAuth": null,
"roles": [],
"breadcrumb": [
{}
],
"keepAlive": null
},
"children": []
}
]
}
]
四、router 配置
說明:
- router.addRouter({}) 函數(shù)即動(dòng)態(tài)路由,它是臨時(shí)性的,就是一旦刷新就會(huì)清除掉添加的動(dòng)態(tài)路由信息
- 需要重新定位到 localhost:8080 來刷新,重新獲取路由信息,方便調(diào)試
- 因?yàn)槭乔岸遂o態(tài)數(shù)據(jù),所以正常,只要連接后端,請(qǐng)求數(shù)據(jù)后,緩存本地,每次刷新從本地獲取即可
- 文章只是完成動(dòng)態(tài)路由的實(shí)現(xiàn),數(shù)據(jù)的持久性存儲(chǔ),各位根據(jù)自己項(xiàng)目自身完善
1、router/index.js
代碼如下(示例):
import {
createRouter,
createWebHashHistory
} from 'vue-router';
import NotFound from '@/pages/404/404.vue' // pages 文件下創(chuàng)建404文件,再創(chuàng)建一個(gè)404.vue
const routes = [
{ path: "/", component: () => import('@/pages/manage/ManageMain.vue') }, // 登錄頁
{
path: "/manage", name: 'Manage', component: () => import('@/pages/manage/ManageMain.vue'), // 主頁
},
{ path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
]
const router = createRouter({
// 4. 內(nèi)部提供了 history 模式的實(shí)現(xiàn)。為了簡單起見,我們?cè)谶@里使用 hash 模式。
history: createWebHashHistory(),
routes, // `routes: routes` 的縮寫
})
// 導(dǎo)出實(shí)例, permission.js 引入
export default router
2、permission.js (與main.js 同級(jí))
說明:
- 注意 NProgress 的引入、配置、使用
- 動(dòng)態(tài)路由的添加(主要是 router.addRoute ,其他的都是根據(jù)后端 json 文件的參數(shù)來判斷,不同的參數(shù)配置,不同的判斷,這只是我喜歡的參數(shù)配置)
- 路徑的拼接(component: () => import(/* @vite-ignore */
./views${item.component}
)) - 具體的根據(jù)自己的情況配置,打印就知道了,
- 按照我的配置,就不需要改動(dòng)
代碼如下(示例):
// 說明:路由守衛(wèi)文件
// 引入
import router from "./router";
// 判斷用戶無token 返回登錄頁提示有用
import { ElMessage } from 'element-plus';
// 進(jìn)度條
import NProgress from 'nprogress';
// 簡單配置 進(jìn)度條,可以不配置:在axios中我們不再做配置,以用來區(qū)分。
NProgress.inc(0.2)
NProgress.configure({ easing: 'ease', speed: 500, showSpinner: false })
// 一、前置守衛(wèi)
router.beforeEach((to, from, next) => {
// 進(jìn)度條
NProgress.start();
// 1、動(dòng)態(tài)路由
addRoutes();
// 2、中間處理(token)
// 3、最后放行
next();
})
// 動(dòng)態(tài)路由獲?。鹤?之后完善項(xiàng)目直接考慮在登錄的時(shí)候直接獲取
// 直接緩存在 pinia 里
// 這里直接取數(shù)據(jù),不請(qǐng)求
import { getTreeMenu } from '@/api/index.js';
import menuData from '@/components/menu2/menuData.json';
function addRoutes() {
// 1、后端數(shù)據(jù)
createRouters(menuData);
console.log("router/index.js打印router已有的路由信息", router.getRoutes());
}
// 拼接路由
function createRouters(result) {
result.forEach((item) => {
// 1、類型為0的菜單,子路由不為空,將子路由添加到manage里
if (item.menuType === '0' && item.children.length > 0) {
item.children.forEach((children) => {
createRouterTemplate('Manage', children);
})
}
// 2、menuType == 1, 子路由為空
if (item.menuType === '1' && item.children.length === 0) {
createRouterTemplate('Manage', item);
}
// 3、遞歸層級(jí)
if (item.children.length > 0) {
createRouters(item.children);
}
});
}
// 把router 的動(dòng)態(tài)路由進(jìn)行封裝
function createRouterTemplate(fatherRouter, item) {
router.addRoute(fatherRouter, {
path: item.path,
name: item.name,
meta: {
title: item.meta.title, // 面包屑用
requiresAuth: item.meta.requiresAuth,
roles: item.meta.roles,
breadcrumb: item.meta.breadcrumb,
keepAlive: item.meta.keepAlive
},
// /* @vite-ignore */ :處理vite動(dòng)態(tài)導(dǎo)入的警告
component: () => import(/* @vite-ignore */ `./views${item.component}`)
})
}
// 二、后置守衛(wèi)
router.afterEach((to) => {
// 標(biāo)簽抬頭
document.title = to.meta.title;
// 進(jìn)度條
NProgress.done();
})
// main.js 導(dǎo)入的為這個(gè)router
export default router
3、main.js
說明:
- 1.注意 router 的引用文件
- 2.注意 nprogress 的引用
- 3.注意全局定義Element-Plus圖標(biāo)
- 4.注意Vue3動(dòng)態(tài)圖標(biāo)的使用
# Vue3 動(dòng)態(tài)圖標(biāo)的使用
<el-icon><component :is="item.icon"></component></el-icon>
代碼如下(示例):
import { createApp } from 'vue'
import './style.css';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import App from './App.vue';
// import router from './router'; // 原router
import router from './permission'; // 現(xiàn)router
//Icon全局引入
import * as icons from "@element-plus/icons-vue";
// 進(jìn)度條
import 'nprogress/nprogress.css';
const app = createApp(App);
// ElementPlus
app.use(ElementPlus);
// Icon全局注冊(cè)
Object.keys(icons).forEach(key => {
app.component(key, icons[key])
})
app.use(router);
app.mount('#app')
五、效果
刪除menuData.json 文件的某一個(gè)路由,界面將不展示?。?!文章來源:http://www.zghlxwxcb.cn/news/detail-787176.html
六、給個(gè)點(diǎn)贊和收藏
七、參考文獻(xiàn)
參考文章 — https://www.cnblogs.com/lpkshuai/p/17346600.html文章來源地址http://www.zghlxwxcb.cn/news/detail-787176.html
到了這里,關(guān)于Vue3+Vue-Router+Element-Plus根據(jù)后端數(shù)據(jù)實(shí)現(xiàn)前端動(dòng)態(tài)路由——權(quán)限管理模塊的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!