当前位置: 首页 > news >正文

vite+ts+mock+vue-router+pinia实现vue的路由权限

0.权限管理

前端的权限管理主要分为如下:

  • 接口权限
  • 路由权限
  • 菜单权限
  • 按钮权限

权限是对特定资源的访问许可,所谓权限控制,也就是确保用户只能访问到被分配的资源

1.项目搭建

创建vite项目

yarn create vite

配置别名

npm install path --savenpm install @types/node --save-dev

tsconfig.json
在这里插入图片描述

    "baseUrl": ".","paths": {"@/*": ["src/*"],"@components/*": ["./src/components/*"]},

vite.config.ts

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from "path";// https://vitejs.dev/config/
export default defineConfig({resolve: {// 配置路径别名alias: {"@": path.resolve(__dirname, "./src"),"@components": path.resolve(__dirname, "./src/components"),},},plugins: [vue(),]})

创建基础路由

npm i vue-router@4
const routes = [{name: "Home",path: "/",component: () => import("@/views/Home.vue"),},];export default routes; //导出
import { createRouter, createWebHistory } from "vue-router";
import routes from "./router";const router = createRouter({history: createWebHistory(),routes,
});
export default router;
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router'
const app =createApp(App)
app.use(router)
app.mount('#app')
<script setup lang="ts">
</script><template><router-view></router-view>
</template><style scoped></style>

配置mock

yarn add mockjs vite-plugin-mock -D
import { MockMethod } from 'vite-plugin-mock';
export default [{url: `/api/list`,method: 'get',response: () => {return [{name:'tom',age:16,nation:'USA'}];},},] as MockMethod[];

vite.config.ts配置

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
import { viteMockServe } from 'vite-plugin-mock'// https://vitejs.dev/config/
export default defineConfig({resolve: {// 配置路径别名alias: {"@": path.resolve(__dirname, "./src"),"@components": path.resolve(__dirname, "./src/components"),},},plugins: [vue(),viteMockServe({mockPath: './src/mock'})]
})

使用

<script setup lang="ts">
import axios from 'axios';axios.get('/api/list').then(res=>{console.log(res.data);})
</script><template>
<h1>超市管理系统首页</h1></template><style scoped></style>

在这里插入图片描述

安装pinia

yarn add pinia
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router'
import { createPinia } from 'pinia'
const pinia = createPinia()const app =createApp(App)
app.use(router)
app.use(pinia)
app.mount('#app')
import { defineStore } from 'pinia'export const useMarkStore = defineStore('mark', {state: () => ({ count: 0 }),getters: {double: (state) => state.count * 2,},actions: {increment() {this.count++},},
})
<script setup lang="ts">
import { useMarkStore } from '@/store/store';
import axios from 'axios';const store= useMarkStore()
axios.get('/api/list').then(res=>{console.log(res.data);})
</script><template>
<h1>超市管理系统首页</h1>
<h1>{{ store.count }}</h1></template><style scoped></style>

在这里插入图片描述

安装js-cookie插件

yarn add js-cookie

2.路由权限(包括菜单权限和按钮权限)

目录结构:
在这里插入图片描述

mock

import { MockMethod } from "vite-plugin-mock";
export default [{url: `/api/list`,method: "post",response: ({ body }) => {return body;},},{url: `/api/login`, //登录逻辑method: "post",response: ({ body }) => {let data = {};if (body.username == "tom") {data = {id: "1111",token: "4566adasdqfrqwd",};} else if (body.username == "amy") {data = {id: "222",token: "45184adaczz52za",};}return data;},},{url: `/api/getRoutes`, //简单方案:根据用户返回不同路由,真实后端逻辑:根据登录用户的角色去表里查授权该角色的的菜单method: "post",response: ({ body }) => {console.log(body);const routes = [];if (body.id == "1111") {routes.push({name: "page1",path: "/page1",component: "/Page1.vue",},{name: "page2",path: "/page2",component: "/Page2.vue",});} else if (body.id == "222") {routes.push( {name: "page3",path: "/page3",component: "/Page3.vue",});}return routes;},},
] as MockMethod[];

router

在这里插入图片描述

store

import { defineStore } from "pinia";
import Cookies from "js-cookie";
import axios from "axios";
import routes from '@/router/router'
const modules = import.meta.glob("../views/**/*.vue");export const useMarkStore = defineStore("mark", {state: () => ({pageRoutes: <any>[], //当前页面缓存路由asyncRoutes: <any>[],//从接口获取到的路由数组}),getters: {},actions: {SET_ROUTES(_routes: any[]){//设置state中的值this.$state.asyncRoutes=_routesthis.$state.pageRoutes=routes.concat(_routes)},getRouter() {//从后端接口获取到动态路由let _id = Cookies.get("id");if (_id) {return new Promise((resolve, reject) => {axios.post("/api/getRoutes", { id: _id }).then((res) => {console.log(res);let _data = res.data;let newData = this.parseRouter(_data);this.SET_ROUTES(newData)resolve(newData);});});}},parseRouter(_data: Array<any>) {//处理后端返回的路由数据=》vite项目能解析的路由格式let _newArr: Array<any> = [];_data.forEach((item: any) => {let newItem = Object.assign({}, item);let comp = item.component;newItem.component = modules[`../views${comp}`];_newArr.push(newItem);});return _newArr;},},getButtonCode(){//按钮权限思路://1.在登录的时候拉取按钮权限编码code['EXPORT_LIST','OPEN_MODAL']//2.将编码缓存本地//3.页面通过v-if控制按钮或是自义定指令控制按钮}
});

permission.ts

import router from "@/router";
import { useMarkStore } from "@/store/store";
import Cookies from "js-cookie";//获取view下所有的vue文件
// const modules = import.meta.glob('../views/**/*.vue')//   export const getCurrRoutes=(name:string)=>{//   }// await axios.post('/api/getRoutes',{username:'tom'}).then(res=>{
//     console.log(res);
//     let _data=res.data
//     _data.forEach((item:any)=>{
//         let newItem=Object.assign({},item)
//         let comp=item.component//         newItem.component=modules[`../views${comp}`]
//         router.addRoute(newItem)
//     })// })
//白名单
const whiteList=['/about','/new','/login']//路由守卫
router.beforeEach(async(to,from,next)=>{const store= useMarkStore()const token=Cookies.get("token")console.log(token);if(token){if(to.path=='login'){next('/')}else{//判断是否拿了路由规则if(store.asyncRoutes.length==0){//格式化好的路由const _temp:any=  await store.getRouter()_temp.forEach((item:any)=>router.addRoute(item))//继续跳转next(to.path)}else{if(to.matched.length!=0){next()}else{alert('无页面权限')next(from.path)}}}}else{if(whiteList.indexOf(to.path)!= -1){next()}else{next('/login')}}
})

Login.vue

<script setup lang="ts">
import axios from 'axios';
import { ref } from 'vue';
import Cookies from 'js-cookie'
import { useRouter } from 'vue-router';
const router =useRouter()
const username=ref()const login=()=>{axios.post('/api/login',{username:username.value}).then(res=>{console.log(res);if(res.data.token){Cookies.set('token',res.data.token)Cookies.set('id',res.data.id)router.push('/')}})  axios.post('/api/getRoutes',{username:username.value}).then(res=>{console.log(res);})
}
</script><template>
<h1>登录</h1>
<div>用户名:<input type="text" v-model="username"></div>
<button @click="login">登录</button></template><style scoped></style>

Home.vue

<script setup lang="ts">
import { useMarkStore } from '@/store/store';
import axios from 'axios';
import Cookies from 'js-cookie'const store= useMarkStore()
axios.post('/api/list',{params:{name:'aaa'}}).then(res=>{console.log(res);})import { useRouter } from 'vue-router';
const router =useRouter()
const loginout=()=>{Cookies.remove('token')Cookies.remove('id')router.push('/login')
}</script><template><div @click="loginout">登出</div>
<h1>超市管理系统首页</h1>
<div>当前用户{{  }}</div>
<div>当前用户可用菜单:</div>
<div v-if="store.asyncRoutes" style="display: flex;flex-direction: column;" ><a v-for="item in store.asyncRoutes" :href="item.path">{{ item?.name }}</a>
</div></template><style scoped></style>

main.ts

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router'
import { createPinia } from 'pinia'
import  '@/util/permission'
const pinia = createPinia()const app =createApp(App)
app.use(router)
app.use(pinia)
app.mount('#app')

按钮权限思路

 getButtonCode(){//按钮权限思路://1.在登录的时候拉取按钮权限编码code['EXPORT_LIST','OPEN_MODAL']//2.将编码缓存本地//3.页面通过v-if控制按钮或是自义定指令控制按钮}

前端控制权限

//1.在路由配置中配置白名单
{name: "Login",path: "/login",component: () => import("@/views/Login.vue"),meta:{whiteList:['admin','tom']}},//2.在路由守卫beforeEach中判断当前用户角色是否在meta中,是就next()

效果

在这里插入图片描述
tom登录
在这里插入图片描述
amy登录

在这里插入图片描述

3.接口权限

登录完拿到token,将token存起来,通过axios请求拦截器进行拦截,每次请求的时候头部携带token

axios.interceptors.request.use(config => {config.headers['token'] = cookie.get('token')return config
})
axios.interceptors.response.use(res=>{},{response}=>{if (response.data.code === 40099 || response.data.code === 40098) { //token过期或者错误router.push('/login')}
})

4.注意点

mock


const login=()=>{//这里传参有三种方式:data,params,{}axios.post('/api/login',{username:'tom'}).then(res=>{console.log(res);}) 
import { MockMethod } from "vite-plugin-mock";
export default [{url: `/api/login`, //登录逻辑method: "post",response: ({ body }) => {//获取传的参数使用bodyreturn body;},},] as MockMethod[];

addRoute

动态添加路由在vite中不能使用以下方法:

// 路由拼接
function loadView(view:string) {return () => import(`@/views/${view}`)
}

上面的代码会报错:TypeError: Failed to resolve module specifier,应该采用import.meta.glob方式

import router from "@/router";
import axios from "axios";
//获取view下所有的vue文件
const modules = import.meta.glob('../views/**/*.vue')//这里需要将异步获取的值改为同步
await axios.post('/api/getRoutes',{username:'tom'}).then(res=>{console.log(res);let _data=res.data_data.forEach((item:any)=>{let newItem=Object.assign({},item)let comp=item.componentnewItem.component=modules[`../views${comp}`]router.addRoute(newItem)})})

5.源码地址

https://gitee.com/beekim/vue-route-mgr

参考:
https://cloud.tencent.com/developer/article/1794300
https://github.com/vitejs/vite/discussions/2746
https://blog.csdn.net/lucklymm/article/details/125420877
https://blog.csdn.net/weixin_43239880/article/details/129922664
https://blog.csdn.net/qq_36651686/article/details/116520731

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • C# NetworkStream 流的详解与示例
  • 【Linux学习】深入了解Linux中进程状态及其转换
  • golang中的字节序 binary BigEndian 大端 , LittleEndian 小端 理解与write写入注意事项
  • docker 版 mysql 主从同步
  • idea 出现 cpu占用100%
  • 02.爬虫---HTTP基本原理
  • 【openlayers系统学习】3.4波段数学计算(计算NDVI)
  • 【计算机网络】第三章——数据链路层概述
  • RabbitMQ安装及配套Laravel使用
  • 生物识别技术存在的问题及需要考虑的关键事项
  • 【qt】标准型模型 下
  • C++之第八课
  • BL121DT网关在智能电网分布式能源管理中的应用钡铼技术协议网关
  • Python的基本使用(numpy、pandas、matplotlib)
  • 配置docker阿里云镜像地址
  • canvas绘制圆角头像
  • in typeof instanceof ===这些运算符有什么作用
  • iOS筛选菜单、分段选择器、导航栏、悬浮窗、转场动画、启动视频等源码
  • JavaScript 基础知识 - 入门篇(一)
  • JS+CSS实现数字滚动
  • js继承的实现方法
  • Linux编程学习笔记 | Linux多线程学习[2] - 线程的同步
  • maven工程打包jar以及java jar命令的classpath使用
  • PAT A1092
  • spring + angular 实现导出excel
  • Vue.js-Day01
  • 官方解决所有 npm 全局安装权限问题
  • 海量大数据大屏分析展示一步到位:DataWorks数据服务+MaxCompute Lightning对接DataV最佳实践...
  • 七牛云假注销小指南
  • 前端临床手札——文件上传
  • 前端设计模式
  • 悄悄地说一个bug
  • 手写一个CommonJS打包工具(一)
  • 用Python写一份独特的元宵节祝福
  • 在Unity中实现一个简单的消息管理器
  • FaaS 的简单实践
  • 新年再起“裁员潮”,“钢铁侠”马斯克要一举裁掉SpaceX 600余名员工 ...
  • ​直流电和交流电有什么区别为什么这个时候又要变成直流电呢?交流转换到直流(整流器)直流变交流(逆变器)​
  • $.ajax()参数及用法
  • (1)(1.9) MSP (version 4.2)
  • (4)事件处理——(7)简单事件(Simple events)
  • (C语言)fgets与fputs函数详解
  • (react踩过的坑)Antd Select(设置了labelInValue)在FormItem中initialValue的问题
  • (Redis使用系列) Springboot 使用redis实现接口幂等性拦截 十一
  • (创新)基于VMD-CNN-BiLSTM的电力负荷预测—代码+数据
  • (动手学习深度学习)第13章 计算机视觉---微调
  • (二)WCF的Binding模型
  • (二)测试工具
  • (二十五)admin-boot项目之集成消息队列Rabbitmq
  • (附源码)spring boot智能服药提醒app 毕业设计 102151
  • (附源码)计算机毕业设计SSM保险客户管理系统
  • (理论篇)httpmoudle和httphandler一览
  • (每日一问)操作系统:常见的 Linux 指令详解
  • (原)本想说脏话,奈何已放下
  • (原創) 如何刪除Windows Live Writer留在本機的文章? (Web) (Windows Live Writer)