首页IT科技vue 权限管理(vue3 一个基于pinia简单易懂的系统权限管理实现方案,vue-router动态路由异步问题解决)

vue 权限管理(vue3 一个基于pinia简单易懂的系统权限管理实现方案,vue-router动态路由异步问题解决)

时间2025-06-20 17:45:11分类IT科技浏览4795
导读:前情提要 作为项目经验稀少的vue开发者来说,在关键技术点上的经验不多,我希望通过我的思想和实践,把好的东西分享在这里,目的是进一步促进技术交流。项目即将完成,权限是最后的收尾工作,好的权限实现方案,可以让我们没有后顾之忧,也可以提升项目的运行速度。...

前情提要

作为项目经验稀少的vue开发者来说              ,在关键技术点上的经验不多                    ,我希望通过我的思想和实践      ,把好的东西分享在这里       ,目的是进一步促进技术交流             。项目即将完成                    ,权限是最后的收尾工作             ,好的权限实现方案       ,可以让我们没有后顾之忧                    ,也可以提升项目的运行速度                    。

应用场景

在开发之前             ,我粗略的浏览了一些权限实现方法,可以说智者见智吧                    ,例如一种实现方案是在router的守卫里判断                   ,我认为虽然实现了功能,但是增加了路由的功能压力       。我们的需求是登录后即获知权限              ,根据权限提供功能;根据以上俩点需求我做出了如下计划:

找一个合适的入口                   ,判断要提供的菜单以及用户拥有什么权限      ,不能影响页面加载 在对应的页面中拿到权限              ,提供需要的功能 必须整个系统都可以轻易拿到                    ,且要避免安全问题

实战解析

大多的实现方案是添加路由      ,我也实践了一次       ,发现存在路由重定向问题                    ,于是我准备反其道而行             ,初始提供所有路由       ,根据权限移除不需要的路由                    ,上面我分析到             ,我不想给导航守卫添加过多的压力,避免影响页面渲染                    ,所以我利用了pinia状态库+router.removeRoute来实现路由的控制;

1              、控制添加路由

首先我们应该准备基础的router配置                   ,包括公共页面,异常页面等:

//router/index.js import { createRouter, createWebHistory } from vue-router import { useUsersStore } from "@/store/user"; import Cookies from "js-cookie"; import { isUserTime } from "@/tool/unitl.js" const routes = [ { path: /, name: Index, component: () => import(@/view/Index.vue), }, { path: /login, name: Login, component: () => import(@/view/Login.vue) }, { path: /business, name: Business, component: () => import(@/view/business/Index.vue) }, { path: /business/project, name: BusinessProject, component: () => import(@/view/business/Project.vue) }, { path: /commerce, name: Commerce, component: () => import(@/view/commerce/Index.vue) }, { path: /commerce/project, name: CommerceProject, component: () => import(@/view/commerce/Project.vue) }, { path: /consult, name: Consult, component: () => import(@/view/consult/Index.vue) }, { path: /consult/project, name: ConsultProject, component: () => import(@/view/consult/Project.vue) }, { path: /finance, name: Finance, component: () => import(@/view/finance/Index.vue), children: [ { path: projects/project, name: FinanceProjectsProject, component: () => import(@/view/finance/projects/Project.vue) }, { path: employee/project, name: FinanceEmployeeProject, component: () => import(@/view/finance/employee/Project.vue) }, { path: account, name: FinanceAccount, component: () => import(@/view/finance/account/Index.vue) }, { path: authority/project, name: FinanceAuthority, component: () => import(@/view/finance/authority/Project.vue) } ] }, { path: /chief, name: Chief, component: () => import(@/view/chief/Index.vue), children: [ { path: first_examine/project, name: firstExamineProject, component: () => import(@/view/chief/first_examine/Project.vue) }, { path: second_examine/project, name: secondExamineProject, component: () => import(@/view/chief/second_examine/Project.vue) } ] }, { path: /email, name: Email, component: () => import(@/view/email/Index.vue) }, { path: /person, name: Person, component: () => import(@/view/person/Detail.vue) }, { path: /:pathMatch(.*)*, name: 404, component: () => import(@C/tool/Page404.vue) } ] const router = createRouter({ history: createWebHistory(), //history routes }) router.beforeEach(async (to, from) => { //pinia仓库可以最早出现的地方              ,路由初始化完毕 const store = useUsersStore(); if (to.name == 404 && from.name == Login) { //处理404问题                   ,并且从login跳转任何404页面重置为Login页面 return { name: Login }; } else if (store.isLogin) { //不是404      ,登录状态在              ,正常跳转 return true; } else if (isUserTime() && Cookies.get("loginPwd")) { //刷新进入                    ,判断是否过期了登录时间      ,Cookie中是否存在密码 //如果存在免登录       ,如果登录过程存在路由不存在的问题则404                    ,否则正常跳转 let exist = await store.Login(to); if (!exist) return { name: 404 }; } else return { name: "Login" }; }); export default router

以上代码为router所有前期需要准备的代码             ,已经我处理路由异常情况的实现方案       ,核心是利用:

async await 俩个关键字实现                    ,因为在登录后             ,我们配置移除菜单和权限是需要时间的,必须要严格控制异步                    ,这里也顺便处理了路由输入回车跳转情况                   , 通过await store.Login(to);传入了to的参数来判断如何跳转,接下来参考下我的pinia仓库实现过程; //src/store/user.ts import { defineStore } from pinia import { apiGetUser } from @/axios/user.js import CryptoJS from crypto-js import router from @/router/index.js // 第一个参数是应用程序中 store 的唯一 id export const useUsersStore = defineStore(users, { state: () => { return { isLogin: false, phone: sessionStorage.UserN ? CryptoJS.AES.decrypt( sessionStorage.getItem(UserN), "abc!" ).toString(CryptoJS.enc.Utf8) : , pwd: "", deptno: "", idCard: "", lockState: "", birthday: "", name: "", sex: "男", power: ,//权限:0-admin,1-部门总负责人,2-部门项目负责人,3-部门成员 showMenu: , } }, actions: { Login(to) { let _that = this; let MenuArray = [Business, BusinessProject, Commerce, CommerceProject, Consult, ConsultProject, Finance, Chief]; return new Promise((t, f) => { _that.isLogin = true; let data = new FormData(); data.append(loginAct, _that.phone) apiGetUser(data).then(res => { let { deptno, idCard, lockState, name, sex, birthday } = res, showMenu = deptno.charAt(0); console.log(res); _that.$patch({ deptno, idCard, lockState, name, sex, birthday, showMenu }) if (showMenu == R) { t(true) } else { //过滤菜单项 switch (showMenu) { case A: MenuArray = MenuArray.filter(item => { return item.indexOf(B) == -1 }); break; case B: MenuArray = MenuArray.filter(item => { return item.indexOf(Com) == -1 }); break; case C: MenuArray = MenuArray.filter(item => { return item.indexOf(Con) == -1 }); break; case D: MenuArray = MenuArray.filter(item => { return item.indexOf(F) == -1 }); break; case E: MenuArray = MenuArray.filter(item => { return item.indexOf(Ch) == -1 }); break; } //循环移除 let Length = MenuArray.length; MenuArray.forEach((item, index) => { if (index == Length - 1) { router.removeRoute(item); if(to.name && router.hasRoute(to.name)){ t(true) }else{ t(false) } } else { router.removeRoute(item) } }) } }) }) }, }, })

这里我直接展示了我的代码              ,如果你不熟悉pinia,要抓紧补了                   ,在用户登录验证成功后      ,这里会调用user库的login函数              ,也会携带路由要to的参数                    ,这里只需要明确几个点即可:

MenuArray      ,将来用来循环removeRoute使用的数组       ,映射路由name 根据自己的配置规则                    ,筛选 MenuArray 通过index == Length - 1 严格控制最后一次移除             ,并且判断to.name是否合法 理解await原理       ,await必须出现在async函数中                    ,且 返回函数如果是Promise             ,则只接受resolved中的值; router, 熟悉vue3的同志知道 有一个useRouter 驱动函数,在我们的方案里不能那样使用                    ,因为useRouter必须在setup函数中使用;

2                    、实践观察

我们的需求完美实现                   ,接下来就是把不需要的菜单隐藏掉

3      、控制功能

路由我们已经控制住了,隐藏菜单就很容易了              ,加载userStore 获取条件v-if即可                   ,例如在header组件中:

//header.vue import { useUsersStore } from "@/store/user.js" const user = useUsersStore(); //html <el-menu-item index="/">首页</el-menu-item> <el-menu-item v-if="AR.indexOf(user.showMenu) != -1" index="/business">业务部</el-menu-item> <el-menu-item v-if="BR.indexOf(user.showMenu) != -1" index="/commerce">商务部</el-menu-item> <el-menu-item v-if="CR.indexOf(user.showMenu) != -1" index="/consult">咨询部</el-menu-item> <el-menu-item v-if="DR.indexOf(user.showMenu) != -1" index="/finance">财务部</el-menu-item> <el-menu-item v-if="ER.indexOf(user.showMenu) != -1" index="/chief">总工办</el-menu-item>

这边根据自己团队的权限规范条件处理即可      ,其他的控制举一反三

4       、解决异步路由问题

router.beforeEach(async (to, from) => { //pinia仓库可以最早出现的地方              ,路由初始化完毕 const store = useUsersStore(); if (to.name == 404 && from.name == Login) { //处理404问题                    ,并且从login跳转任何404页面重置为Login页面 return { name: Login }; } else if (store.isLogin) { //不是404      ,登录状态在       ,正常跳转 return true; } else if (isUserTime() && Cookies.get("loginPwd")) { //刷新进入                    ,判断是否过期了登录时间             ,Cookie中是否存在密码 //如果存在免登录       ,登录过程存在路由不存在问题则404                    ,否则正常跳转 let exist = await store.Login(to); if (!exist) return { name: 404 }; } else return { name: "Login" }; });

这里是我项目的路由守卫             ,它已经完成绝大部分功能,登录权限判断                    ,404处理                   ,刷新进入,状态持久化              ,异步操作在网络环境中是无法避免的                   ,router官方示例也明确提醒了我们如何使用      ,这里我们是利用服务器数据判断要移除的菜单              ,且不说http响应异步                    ,router.removeRoute()也只能是一次删除一个      ,所以在store.Login(to)我严格控制了逻辑       ,并且也解决了上述问题,使用我这样的全局守卫来配置必须要理清楚逻辑:依靠 async及await来实现                    ,缺点是会有短暂的延迟             ,还不够流畅       ,但是笔者由于时间问题                    ,不能再优化下去             ,希望大家吸取精华去掉糟粕             。

至此我的分享结束,如果大家有更好的解决方案                    ,希望可以进一步交流                    。

最后

📚 vue专栏

☃️ 个人简介:一个喜爱技术的人       。

🌞 励志格言: 脚踏实地                   ,虚心学习      。

❗如果文章还可以,记得用你可爱的小手点赞👍关注✅              ,我会在第一时间回                    、回访                   ,欢迎进一步交流                    。
声明:本站所有文章      ,如无特殊说明或标注              ,均为本站原创发布              。任何个人或组织                    ,在未征得本站同意时      ,禁止复制             、盗用       、采集                    、发布本站内容到任何网站             、书籍等各类媒体平台      。如若本站内容侵犯了原著者的合法权益       ,可联系我们进行处理                   。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
真没想到(死链查询与管理(死链产生的原因和解决方法))