增加页面权限

This commit is contained in:
zouzhibing 2022-04-01 17:07:38 +08:00
parent 43773ce133
commit a19b0c48fe
19 changed files with 271 additions and 44 deletions

View File

@ -1,15 +1,17 @@
# vue-admin-plus
# 基于Vue3.0+TS+Element-plus实现后台管理系统
## 下载依赖
## 安装项目
```
# 克隆项目
git clone -b vue3.0-antdv https://github.com/chuzhixin/vue-admin-better.git
# 安装依赖
yarn install
```
### 运行
```
# 本地开发 启动项目
yarn serve
```
### 打包
```
yarn build

View File

@ -14,6 +14,7 @@
"mavon-editor": "^2.10.4",
"md-editor-v3": "^1.11.3",
"nprogress": "^0.2.0",
"path-to-regexp": "^6.2.0",
"print-js": "^1.6.0",
"vue": "^3.0.0",
"vue-cropper": "^1.0.3",

View File

@ -0,0 +1,50 @@
<template>
<el-breadcrumb class="app-breadcrumb" separator="/">
<transition-group>
<el-breadcrumb-item v-for="(item,index) in obj.levelList" :key="item.path">
<span v-if="item.redirect==='noRedirect'||index==obj.levelList.length-1" class="no-redirect">{{ item.meta.title }}</span>
<a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a>
</el-breadcrumb-item>
</transition-group>
</el-breadcrumb>
</template>
<script lang="ts" setup>
import pathToRegexp from 'path-to-regexp'
import {onMounted, reactive, watch} from "vue";
import {useRoute} from 'vue-router'
const obj = reactive({levelList:{}})
const route = useRoute()
//
const getBreadcrumb = ()=>{
let matched = route.matched.filter(item => item.meta && item.meta.title)
const first = matched[0]
obj.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
}
onMounted(()=>{
getBreadcrumb()
watch(route,()=>{
if (route.path.startsWith('/redirect/')) {
return
}
getBreadcrumb()
})
})
</script>
<style lang="scss" scoped>
.app-breadcrumb.el-breadcrumb {
margin-left: 10px;
display: inline-block;
font-size: 14px;
margin-bottom: 4px;
.no-redirect {
cursor: text;
}
}
</style>

View File

@ -1,8 +1,14 @@
<template>
<section class="app-main">
<router-view v-slot="{ Component }">
<component :is="Component" />
</router-view>
<router-view v-slot="{ Component }">
<transition name="fade-transform" mode="out-in">
<div>
<component :is="Component" />
</div>
</transition>
</router-view>
</section>
</template>

View File

@ -33,6 +33,9 @@
return store.state.permission.routes
})
console.log('permission_routes',permission_routes)
const activeMenu = computed(()=>{
const { meta, path } = route
// if set path, the sidebar will highlight the path you set

View File

@ -1,10 +1,16 @@
<template>
<div class="m-layout-header" :style="{left:`${isCollapse?'56':'210'}px`}">
<div class="header">
<div>
<el-icon class="icon" v-if="isCollapse" @click="handleCollapse(false)"><expand /></el-icon>
<el-icon class="icon" v-else @click="handleCollapse(true)"><fold/></el-icon>
<div class="left">
<div>
<el-icon class="icon" v-if="isCollapse" @click="handleCollapse(false)"><expand /></el-icon>
<el-icon class="icon" v-else @click="handleCollapse(true)"><fold/></el-icon>
</div>
<u-hamburger/>
</div>
<div class="right">
<u-screen-full/>
<el-dropdown @command="commandAction">
@ -31,6 +37,7 @@
<script lang="ts" setup>
import Personal from './Personal.vue'
import TagViews from '../TagsView/index.vue'
import UHamburger from "@/components/u-Hamburger/index.vue"
import UScreenFull from '@/components/u-screenfull/index.vue'
import {computed, ref,} from 'vue'
import {useRouter} from 'vue-router'
@ -99,6 +106,10 @@
display: flex;
align-items: center;
justify-content: space-between;
.left{
display: flex;
align-items: center;
}
.right{
display: flex;
align-items: center;

View File

@ -24,13 +24,15 @@ router.beforeEach(async(to, from, next) => {
if (to.path === '/login') {
// 如果已登录,请重定向到主页
next({ path: '/' })
NProgress.done()
NProgress.done()
} else {
try {
// 路由添加进去了没有及时更新 需要重新进去一次拦截
if(hasRoles){
const accessRoutes = await store.dispatch('permission/generateRoutes', 'roles')
console.log('accessRoutes==',accessRoutes)
if(!store.state.permission.routes.length){
// 获取权限列表进行接口访问 因为这里页面要切换权限
// const roles = await store.dispatch('user/getInfo')
const accessRoutes = await store.dispatch('permission/generateRoutes', store.getters.roles)
hasRoles = false
accessRoutes.forEach(item => router.addRoute(item)) // 动态添加访问路由表
next({ ...to, replace: true }) // // 这里相当于push到一个页面 不在进入路由拦截
@ -38,8 +40,10 @@ router.beforeEach(async(to, from, next) => {
next() // // 如果不传参数就会重新执行路由拦截,重新进到这里
}
} catch (error) {
next(`/login?redirect=${to.path}`)
next(`/login?redirect=${to.path}`)
}
}
}else{
if (whiteList.indexOf(to.path) !== -1) {

View File

@ -7,6 +7,7 @@ import chatRouter from './modules/chat'
import componentsRouter from './modules/components'
import othersRouter from './modules/other'
import externalLink from './modules/externalLink'
import permissionRouter from './modules/permission'
interface extendRoute {
@ -31,7 +32,7 @@ export const constantRoutes: Array<RouteRecordRaw&extendRoute> = [
path: '/home',
component: () => import('@/views/home/index.vue'),
name: 'home',
meta: { title: '首页', icon: 'film', affix: true }
meta: { title: '首页', icon: 'film', affix: true ,role:['other']}
},
]
},
@ -44,7 +45,8 @@ export const asyncRoutes = [
chatRouter,
componentsRouter,
othersRouter,
externalLink
externalLink,
permissionRouter
]

View File

@ -9,26 +9,27 @@ const chartsRouter = {
name: 'Charts',
meta: {
title: '图表',
icon: 'trend-charts'
icon: 'trend-charts',
roles:['other']
},
children: [
{
path: 'line',
component: () => import('@/views/charts/line.vue'),
name: 'line',
meta: { title: '折现图', noCache: true }
meta: { title: '折现图', noCache: true , roles:['other'] }
},
{
path: 'simple',
component: () => import('@/views/charts/simple.vue'),
name: 'charts-simple',
meta: { title: '简单图表', noCache: true }
meta: { title: '简单图表', noCache: true , roles:['other'] }
},
{
path: 'complex',
component: () => import('@/views/charts/complex.vue'),
name: 'charts-complex',
meta: { title: '复杂图表', noCache: true }
meta: { title: '复杂图表', noCache: true , roles:['other'] }
},
]
}

View File

@ -9,26 +9,27 @@ const componentsRouter = {
name: 'components',
meta: {
title: '组件',
icon: 'Histogram'
icon: 'Histogram',
roles:['other']
},
children: [
{
path: 'editor',
component: () => import('@/views/components-demo/editor.vue'),
name: 'editor',
meta: { title: '富文本编辑器', noCache: true }
meta: { title: '富文本编辑器', noCache: true, roles:['other'] }
},
{
path: 'mark-down',
component: () => import('@/views/components-demo/mark-down.vue'),
name: 'mark-down',
meta: { title: 'markDown', noCache: true }
meta: { title: 'markDown', noCache: true , roles:['other']}
},
{
path: 'form',
component: () => import('@/views/components-demo/form.vue'),
name: 'form',
meta: { title: '表单', noCache: true }
meta: { title: '表单', noCache: true , roles:['other']}
},
]
}

View File

@ -0,0 +1,24 @@
/** When your routing table is too long, you can split it into small modules**/
import Layout from "@/layout/index.vue";
const permissionRouter = {
path: '/permission',
component: Layout,
redirect: 'noRedirect',
name: 'permission',
meta: {
title: '权限测试页',
icon: 'trend-charts', roles:['other']
},
children: [
{
path: 'page',
component: () => import('@/views/permission/page.vue'),
name: 'page',
meta: { title: '页面权限', noCache: true,icon: 'trend-charts', roles:['other'] }
},
]
}
export default permissionRouter

View File

@ -2,6 +2,7 @@ const getters = {
permission_routes: state => state.permission.routes,
isCollapse: state => state.app.isCollapse,
userInfo: state => state.user.userInfo,
roles: state => state.user.roles,
}
export default getters

View File

@ -2,7 +2,7 @@ import {Module} from "vuex";
import { asyncRoutes, constantRoutes } from '@/router/index'
/**
* Use meta.role to determine if the current user has permission
* 使 meta.role
* @param roles
* @param route
*/
@ -10,12 +10,13 @@ function hasPermission(roles, route) {
if (route.meta && route.meta.roles) {
return roles.some(role => route.meta.roles.includes(role))
} else {
return true
// return true
return false
}
}
/**
* Filter asynchronous routing tables by recursion
*
* @param routes asyncRoutes
* @param roles
*/
@ -32,6 +33,7 @@ export function filterAsyncRoutes(routes, roles) {
}
})
return res
}
@ -46,21 +48,22 @@ const mutations = {
state.addRoutes = routes
state.routes = constantRoutes.concat(routes)
console.log('===============',state.routes)
}
}
const actions = {
generateRoutes({ commit }, roles) {
return new Promise(resolve => {
// let accessedRoutes
// if (roles.includes('admin')) {
// accessedRoutes = asyncRoutes || []
// } else {
// accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
// }
commit('SET_ROUTES', asyncRoutes)
resolve(asyncRoutes)
// 在这判断是否有权限,哪些角色拥有哪些权限
let accessedRoutes
if (roles.includes('admin')) {
accessedRoutes = asyncRoutes || []
} else {
accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
}
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
})
}
}

View File

@ -2,7 +2,8 @@ import {Module} from "vuex";
import { getToken, setToken, removeToken } from '@/utils/auth'
const state = {
token: getToken(),
userInfo:localStorage.userInfo?JSON.parse(localStorage.userInfo):{}
userInfo:localStorage.userInfo?JSON.parse(localStorage.userInfo):{},
roles: localStorage.roles?JSON.parse(localStorage.roles):[],
}
const mutations = {
@ -13,27 +14,40 @@ const mutations = {
localStorage.userInfo = JSON.stringify(userInfo)
state.userInfo = userInfo
},
SET_ROLES: (state, roles) => {
localStorage.roles = JSON.stringify(roles)
state.roles = roles
}
}
const actions = {
// 登录
login({ commit }, userInfo) {
console.log('=========')
login({ commit,dispatch }, userInfo) {
const { username, password } = userInfo
return new Promise((resolve, reject) => {
return new Promise(async (resolve, reject) => {
commit('SET_TOKEN', username)
commit('SET_INFO', userInfo)
await dispatch('getInfo', ['admin']) // 获取权限列表 默认就是超级管理员,因为没有进行接口请求 写死
setToken(username)
resolve(username)
})
},
// 获取用户信息 ,如实际应用中 可以通过token通过请求接口在这里获取用户信息
getInfo({ commit, state },roles) {
return new Promise((resolve, reject) =>{
commit('SET_ROLES', roles)
resolve(roles)
} )
},
// 退出
logout({ commit, state, dispatch }) {
return new Promise((resolve, reject) => {
removeToken()
commit('SET_TOKEN', '')
commit('SET_INFO', '')
commit('SET_ROLES', '')
resolve(null)
})
},

View File

@ -1,2 +1,3 @@
@import './variables.scss';
@import './sidebar.scss';
@import "./transition.scss";

View File

@ -0,0 +1,62 @@
// global transition css
/* fade */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.28s;
}
.fade-enter,
.fade-leave-active {
opacity: 0;
}
//
///* fade-transform */
//.fade-transform-leave-active,
//.fade-transform-enter-active {
// transition: all .2s;
//}
//
//.fade-transform-enter-from {
// opacity: 0;
// transform: translateX(-30px);
//}
//
//.fade-transform-leave-to {
// opacity: 0;
// transform: translateX(30px);
//}
/* breadcrumb transition */
.breadcrumb-enter-active,
.breadcrumb-leave-active {
transition: all .2s;
}
.breadcrumb-enter-from,
.breadcrumb-leave-active {
opacity: 0;
//transform: translateX(20px);
}
.breadcrumb-move {
transition: all .2s;
}
.breadcrumb-leave-active {
position: absolute;
}
.fade-transform-leave-active,
.fade-transform-enter-active {
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1), border 0s;
}
.fade-transform-enter-from {
opacity: 0;
}
.fade-transform-leave-to {
opacity: 0;
}

View File

@ -11,7 +11,7 @@
<div class="name"></div>
<div class="description"></div>
<div class="list">
<div>昵称有你</div>
<div>昵称小狼</div>
<div>职业前端</div>
<div>公司小公司</div>
<div>年龄1993/09</div>
@ -32,6 +32,9 @@
<el-divider></el-divider>
<div style="margin-bottom: 15px"><h5>最喜欢的一句话</h5></div>
<div>---------- 开心最重要</div>
<el-divider></el-divider>
<div style="margin-bottom: 15px"><h5>如果对你有帮助的话可以麻烦点一颗 star!</h5></div>
</div>
</el-card>
</el-col>

View File

@ -0,0 +1,33 @@
<template>
<div class="m-permission-page">
<div style="margin-bottom: 20px">
权限列表{{store.getters.roles}}
</div>
<el-radio-group v-model="switchRoles">
<el-radio-button label="other" />
<el-radio-button label="admin" />
</el-radio-group>
</div>
</template>
<script lang="ts" setup>
import {computed, ref} from "vue";
import {useStore} from "vuex"
const store = useStore()
const switchRoles = computed({
get(){
return store.getters.roles[0]
},
set(val){
(async ()=>{
await store.dispatch("user/getInfo",[val])
location.reload()
})()
}
})
</script>
<style>
</style>

View File

@ -6255,6 +6255,11 @@ path-to-regexp@0.1.7:
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
path-to-regexp@^6.2.0:
version "6.2.0"
resolved "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-6.2.0.tgz#f7b3803336104c346889adece614669230645f38"
integrity sha512-f66KywYG6+43afgE/8j/GoiNyygk/bnoCbps++3ErRKsIYkGGupyv07R2Ok5m9i67Iqc+T2g1eAUGUPzWhYTyg==
path-type@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f"