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

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

时间2025-09-18 03:43:33分类IT科技浏览6686
导读:前情提要 作为项目经验稀少的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
网站seo关键词优化排名(SEO网站排名关键词优化:提升网站排名的有效方法) Echarts设置宽度(Echarts图表使用后台数据不显示问题解决)