赞
踩
前端的权限管理主要分为如下:
权限是对特定资源的访问许可,所谓权限控制,也就是确保用户只能访问到被分配的资源
创建vite项目
yarn create vite
配置别名
npm install path --save
npm 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
目录结构:
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[];
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=_routes this.$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控制按钮或是自义定指令控制按钮 } });
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') } } })
<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>
<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>
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登录
登录完拿到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')
}
})
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 }) => {//获取传的参数使用body
return body;
},
},
] as MockMethod[];
动态添加路由在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.component newItem.component=modules[`../views${comp}`] router.addRoute(newItem) }) })
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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。