权限控制

This commit is contained in:
Jason 2022-04-20 12:10:22 +08:00
parent f20f1af16b
commit 143da05631
30 changed files with 322 additions and 550 deletions

View File

@ -17,7 +17,7 @@
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"vue": "^3.2.25", "vue": "^3.2.25",
"vue-echarts": "^6.0.0", "vue-echarts": "^6.0.0",
"vue-router": "^4.0.0-0", "vue-router": "^4.0.14",
"vuedraggable": "^4.1.0", "vuedraggable": "^4.1.0",
"vuex": "^4.0.0-0" "vuex": "^4.0.0-0"
}, },

View File

@ -1,18 +1,14 @@
<template> <template>
<keep-alive> <router-view v-if="routerAlive" />
<router-view v-if="keepAlive && routerAlive" />
</keep-alive>
<router-view v-if="!keepAlive && routerAlive" />
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, ref, nextTick, provide, onMounted } from 'vue' import { defineComponent, ref, nextTick, provide, onMounted } from 'vue'
import { useAdmin } from './core/hooks/app' import { useAdmin } from './core/hooks/app'
export default defineComponent({ export default defineComponent({
setup() { setup() {
const { store, route } = useAdmin() const { store, route } = useAdmin()
const routerAlive = ref(true) const routerAlive = ref(true)
const keepAlive = computed(() => route.meta.keepAlive)
const reload = () => { const reload = () => {
routerAlive.value = false routerAlive.value = false
nextTick(() => { nextTick(() => {
@ -36,8 +32,7 @@ export default defineComponent({
document.head.appendChild(favicon) document.head.appendChild(favicon)
}) })
return { return {
routerAlive, routerAlive
keepAlive
} }
} }
}) })

View File

@ -56,9 +56,9 @@ export function apiConfigGetMenu() {
return request.get('/system/menu/lists') return request.get('/system/menu/lists')
} }
// 角色权限 // 菜单路由
export function apiConfigGetAuth() { export function apiConfigGetRoutes() {
return request.get('/config/getAuth') return request.get('/system/menu/menus')
} }
/* 菜单 */ /* 菜单 */

View File

@ -3,7 +3,7 @@ import { App } from 'vue'
const modules = import.meta.globEager('./modules/*.ts') const modules = import.meta.globEager('./modules/*.ts')
export default (app: App<Element>) => { export default (app: App<Element>) => {
Object.keys(modules).forEach(key => { Object.keys(modules).forEach(key => {
const name = key.replace(/^\.\/(.*)\.\w+$/, '$1') const name = key.replace(/^\.\/.*\/(.*)\.\w+$/, '$1')
app.directive(name, modules[key].default) app.directive(name, modules[key].default)
}) })
} }

View File

@ -0,0 +1,29 @@
/**
* perm ()
*
* <el-button v-perm="['system:admin:edit']"></el-button>
* copyValue为需要复制的值
*/
import store from "@/store"
export default {
mounted: (el: HTMLElement, binding: any) => {
const { value } = binding
const permissions = store.getters && store.getters.permission
const all_permission = "*";
if (Array.isArray(value)) {
if (value.length > 0) {
const hasPermission = permissions.some((key: string) => {
return all_permission == key || value.includes(key)
})
if (!hasPermission) {
el.parentNode && el.parentNode.removeChild(el)
}
}
} else {
throw new Error(`like v-perm="['system:admin:edit']`)
}
}
}

View File

@ -0,0 +1,68 @@
import { RouteRecordRaw, RouterView } from "vue-router"
export enum MenuType{
Catalogue = 'M',
Menu = 'C',
Button = 'A'
}
// 匹配views里面所有的.vue文件动态引入
const modules = import.meta.glob('@/views/**/*.vue')
// 过滤路由所需要的数据
export function filterAsyncRoutes(routes: any[], firstRoute = true) {
return routes.map((route => {
const routeRecord = createRouteRecord(route, firstRoute)
if (route.children != null && route.children && route.children.length) {
routeRecord.children = filterAsyncRoutes(route.children, false)
}
return routeRecord
}))
}
// 创建一条路由记录
export function createRouteRecord(route: any, firstRoute: boolean): RouteRecordRaw {
//@ts-ignore
const routeRecord: RouteRecordRaw = {
path: firstRoute ? `/${route.paths}` : route.paths,
name: route.paths,
meta: {
hidden: !route.isShow,
keepAlive: !!route.isCache,
title: route.menuName,
perms: route.perms,
query: route.params,
icon: route.menuIcon,
activePath: route.selected
},
}
switch (route.menuType) {
case MenuType.Catalogue:
routeRecord.component = RouterView
break
case MenuType.Menu:
routeRecord.component = loadRouteView(route.component)
break
}
return routeRecord
}
console.log(modules)
// 动态加载组件
export function loadRouteView(component: string) {
try {
const key = Object.keys(modules).find((key) => {
return key.includes(`${component}.vue`)
})
if (key) {
return modules[key]
} else {
throw Error(`找不到组件${component},请确保组件路径正确`)
}
} catch (error) {
console.error(error)
return RouterView
}
}

1
admin/src/env.d.ts vendored
View File

@ -17,3 +17,4 @@ declare module 'nprogress' {
export function start(): void export function start(): void
export function done(): void export function done(): void
} }

View File

@ -11,18 +11,9 @@
background-color="#2a2c41" background-color="#2a2c41"
:default-active="currentPath" :default-active="currentPath"
text-color="#E5E5E5" text-color="#E5E5E5"
router
> >
<template v-for="(item, index) in sidebar" :key="index"> <template v-for="(item, index) in sidebar" :key="index">
<sub-menu :route="item"> <sub-menu :route="item" :path="item.path"/>
<template v-for="(item, index) in item?.children" :key="index">
<sub-menu :route="item">
<template v-for="(item, index) in item?.children" :key="index">
<sub-menu :route="item"></sub-menu>
</template>
</sub-menu>
</template>
</sub-menu>
</template> </template>
</el-menu> </el-menu>
</el-scrollbar> </el-scrollbar>
@ -41,7 +32,7 @@ export default defineComponent({
setup() { setup() {
const { store, route } = useAdmin() const { store, route } = useAdmin()
const sidebar = computed(() => store.state.permission.sidebar) const sidebar = computed(() => store.state.permission.sidebar)
const currentPath = computed(() => route.meta?.parent ?? route.path) const currentPath = computed(() => route.meta?.activeMenu ?? route.path)
const config = computed(() => store.getters.config) const config = computed(() => store.getters.config)
return { return {
config, config,

View File

@ -1,21 +1,38 @@
<template> <template>
<template v-if="!route.meta.hidden"> <div v-if="!route.meta.hidden">
<el-sub-menu v-if="hasChildren" :index="route.path"> <el-sub-menu v-if="hasChildren" :index="path">
<template #title> <template #title>
<i class="iconfont m-r-10" :class="route.meta.icon"></i> <i class="iconfont m-r-10" :class="route.meta.icon"></i>
<span>{{ route.meta.title }}</span> <span>{{ route.meta.title }}</span>
</template> </template>
<slot></slot> <template #default>
<sub-menu
v-for="(item, index) in route.children"
:key="index"
:route="item"
:path="resolvePath(item.path)"
/>
</template>
</el-sub-menu> </el-sub-menu>
<el-menu-item v-else :index="route.path"> <router-link
v-else
:to="{
path: path,
query: resolveQuery
}"
>
<el-menu-item :index="path">
<i class="iconfont m-r-10" :class="route.meta.icon"></i> <i class="iconfont m-r-10" :class="route.meta.icon"></i>
<span>{{ route.meta.title }}</span> <span>{{ route.meta.title }}</span>
</el-menu-item> </el-menu-item>
</template> </router-link>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent } from 'vue' import { queryToObject } from '@/utils/util'
import { isQuery } from '@/utils/validate'
import { computed, defineComponent, toRefs } from 'vue'
import { RouteRecordRaw } from 'vue-router' import { RouteRecordRaw } from 'vue-router'
export default defineComponent({ export default defineComponent({
components: {}, components: {},
@ -23,15 +40,40 @@ export default defineComponent({
route: { route: {
type: Object, type: Object,
default: () => ({}) default: () => ({})
},
path: {
type: String
} }
}, },
setup(props) { setup(props) {
const { path, route } = toRefs(props)
//
const hasChildren = computed(() => { const hasChildren = computed(() => {
const children: RouteRecordRaw[] = props.route.children ?? [] const children: RouteRecordRaw[] = route.value.children ?? []
return !!children.filter(item => !item.meta?.hidden).length return !!children.filter(item => !item.meta?.hidden).length
}) })
//
const resolvePath = computed(() => (p?: string) => {
return p !== undefined ? `${path.value}/${p}` : path.value
})
// '{id:1}'|| id=1 => {id: 1}
const resolveQuery = computed(() => {
const query = route.value.query
try {
if (isQuery(query)) {
return queryToObject(query)
} else {
return JSON.parse(query)
}
} catch (error) {
}
})
return { return {
hasChildren hasChildren,
resolvePath,
resolveQuery
} }
} }
}) })

View File

@ -2,18 +2,25 @@
<div class="layout-main"> <div class="layout-main">
<el-scrollbar> <el-scrollbar>
<div class="p-15"> <div class="p-15">
<perm /> <router-view />
</div> </div>
</el-scrollbar> </el-scrollbar>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue' import { useAdmin } from '@/core/hooks/app'
import Perm from './perm.vue' import { computed, defineComponent } from 'vue'
export default defineComponent({ export default defineComponent({
components: { name: 'layout-main',
Perm setup() {
const { route } = useAdmin()
const keepAlive = computed(() => {
return route.meta.keepAlive
})
return {
keepAlive
}
} }
}) })
</script> </script>

View File

@ -4,7 +4,7 @@
import NProgress from 'nprogress' import NProgress from 'nprogress'
import store from './store' import store from './store'
import router, { asyncRoutes } from './router' import router from './router'
import 'nprogress/nprogress.css' import 'nprogress/nprogress.css'
// NProgress配置 // NProgress配置
@ -22,16 +22,28 @@ router.beforeEach(async (to, from, next) => {
const token = store.getters.token const token = store.getters.token
if (token) { if (token) {
// 获取用户信息 // 获取用户信息
if (store.getters.permission == null) { if (store.getters.permission.length === 0) {
store.commit('permission/setSidebar', asyncRoutes[0].children) try {
await store.dispatch('user/getUser') await store.dispatch('user/getUser')
await store.dispatch('permission/getPermission') const routes = await store.dispatch('permission/generateRoutes')
routes.forEach((route: any) => {
router.addRoute('index', route) // 动态添加可访问路由表
})
console.log(router.getRoutes())
if (to.path === '/login') {
next({ path: '/' })
} else {
next({ ...to, replace: true }) //确保addRoutes已完成
}
} catch {
await store.dispatch('user/logout')
next({ path: loginPath, query: { redirect: to.fullPath } })
NProgress.done()
} }
if (to.path === loginPath) {
next({ path: defaultPath })
} else { } else {
next() next()
} }
} else if (whiteList.includes(to.path as string)) { } else if (whiteList.includes(to.path as string)) {
// 在免登录白名单,直接进入 // 在免登录白名单,直接进入
next() next()

View File

@ -1,55 +1,76 @@
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router' import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
import workbench from '@/views/workbench/index.vue'
import Layout from '@/layout/index.vue' import Layout from '@/layout/index.vue'
import Error404 from '@/views/error/404.vue' /**
import Error500 from '@/views/error/500.vue' * Note: 路由配置项
// Router modules *
import setting from './modules/setting' * name:'router-name' // 设定路由的名字,一定要填写不然使用<keep-alive>时会出现各种问题
import permission from './modules/permission' *
import decoration from './modules/decoration' * meta : {
import content from './modules/content' keepAlive: true // 如果设置为true则不会被 <keep-alive> 缓存(默认 false)
import channel from './modules/channel' title: 'title' // 设置该路由在侧边栏的名字
import application from './modules/application' icon: 'svg-name' // 设置该路由的图标
export const asyncRoutes: Array<RouteRecordRaw> = [ activeMenu: '/system/user' // 当路由设置了该属性,则会高亮相对应的侧边栏。
query: '{"id": 1}' // 访问路由的默认传递参数
hidden: true // 当设置 true 的时候该路由不会再侧边栏出现
}
*/
// 公共路由
export const constantRoutes: Array<RouteRecordRaw> = [
{ {
path: '/', path: '/',
redirect: 'workbench', redirect: 'workbench',
name: 'index',
component: Layout
},
{
path: '/permission',
component: Layout, component: Layout,
children: [ children: [
{ {
path: '/workbench', path: 'admin/edit',
component: workbench, component: () => import('@/views/permission/admin/edit.vue'),
meta: { title: '工作台', icon: 'icon-home', permission: ['view'] } meta: { title: '编辑管理员', activeMenu: '/permission/admin' }
}, },
// decoration, // 装修管理 {
// application,// 应用管理 path: 'menu/edit',
// content, // 内容管理 component: () => import('@/views/permission/menu/edit.vue'),
// channel, // 渠道管理 meta: { title: '编辑菜单', activeMenu: '/permission/menu' }
permission, },
setting {
] path: 'role/edit',
component: () => import('@/views/permission/role/edit.vue'),
meta: { title: '编辑角色', activeMenu: '/permission/role' }
} }
] ]
},
export const constRoutes: Array<RouteRecordRaw> = [
{ {
path: '/login', path: '/login',
component: () => import('@/views/account/login.vue') component: () => import('@/views/account/login.vue')
}, },
{ {
path: '/error/500', path: '/500',
component: Error500 component: () => import('@/views/error/500.vue')
}, },
{ path: '/:pathMatch(.*)*', name: '404', component: Error404 } {
path: '/:pathMatch(.*)*',
component: () => import('@/views/error/404.vue')
}
] ]
export const getAsyncRoutes = () => {
return asyncRoutes
}
const router = createRouter({ const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), history: createWebHistory(import.meta.env.BASE_URL),
routes: [...asyncRoutes, ...constRoutes] routes: constantRoutes,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { top: 0 }
}
},
}) })
export default router export default router

View File

@ -1,40 +0,0 @@
import { RouteRecordRaw, RouterView } from 'vue-router'
const routes: RouteRecordRaw = {
path: '/application',
redirect: '/application/notification',
component: RouterView,
meta: { title: '应用管理', icon: 'icon-setting' },
children: [
{
path: '/application/notification',
redirect: '/application/notification/index',
component: RouterView,
meta: { title: '消息通知' },
children: [
{
path: '/application/notification/index',
component: () => import('@/views/application/notification/index.vue'),
meta: { title: '通知设置', permission: ['view'] },
},
{
path: '/application/notification/detail',
component: () => import('@/views/application/notification/detail.vue'),
meta: { hidden: true, title: '通知设置', permission: ['view'] },
},
{
path: '/application/sms/index',
component: () => import('@/views/application/sms/index.vue'),
meta: { title: '短信设置', permission: ['view'] },
},
{
path: '/application/sms/detail',
component: () => import('@/views/application/sms/detail.vue'),
meta: { hidden: true, title: '短信设置', permission: ['view'] },
},
],
},
],
}
export default routes

View File

@ -1,106 +0,0 @@
import { RouteRecordRaw, RouterView } from "vue-router"
const routes: RouteRecordRaw = {
path: '/channel',
name: 'channel',
meta: { title: '渠道管理', icon: 'icon-setting' },
component: RouterView,
children: [{
path: '/channel/mp_wechat',
name: 'mp_wechat',
meta: {
title: '微信公众号',
parentPath: '/channel',
},
component: RouterView,
children: [{
path: '/channel/mp_wechat/index',
name: 'mp_wechat_index',
meta: {
title: '渠道设置',
parentPath: '/channel',
permission: ['view']
},
component: () => import('@/views/channel/mp_wechat/index.vue'),
}, {
path: '/channel/mp_wechat/menu',
name: 'mp_wechat_menu',
meta: {
title: '菜单管理',
parentPath: '/channel',
permission: ['view']
},
component: () => import('@/views/channel/mp_wechat/menu.vue'),
}, {
path: '/channel/mp_wechat/reply/follow_reply',
name: 'follow_reply',
meta: {
title: '关注回复',
parentPath: '/channel',
permission: ['view']
},
component: () => import('@/views/channel/mp_wechat/reply/follow_reply.vue'),
}, {
path: '/channel/mp_wechat/reply/keyword_reply',
name: 'keyword_reply',
meta: {
title: '关键字回复',
parentPath: '/channel',
permission: ['view']
},
component: () => import('@/views/channel/mp_wechat/reply/keyword_reply.vue'),
}, {
path: '/channel/mp_wechat/reply/default_reply',
name: 'default_reply',
meta: {
title: '默认回复',
parentPath: '/channel',
permission: ['view']
},
component: () => import('@/views/channel/mp_wechat/reply/default_reply.vue'),
}, {
path: '/channel/mp_wechat/reply/reply_edit',
name: 'reply_edit',
meta: {
title: '默认编辑',
parentPath: '/channel',
hidden: true,
permission: ['view']
},
component: () => import('@/views/channel/mp_wechat/reply/reply_edit.vue'),
}]
}, {
path: '/channel/wechat_app',
name: 'wechat_app',
meta: {
title: '微信小程序',
parentPath: '/channel'
},
component: () => import('@/views/channel/wechat_app/index.vue')
}, {
path: '/channel/app_store',
name: 'app_store',
meta: {
title: 'APP',
parentPath: '/channel',
},
component: () => import('@/views/channel/app_store/index.vue'),
}, {
path: '/channel/h5_store',
name: 'h5_store',
meta: {
title: 'H5',
parentPath: '/channel',
},
component: () => import('@/views/channel/h5_store/index.vue')
}, {
path: '/wechat/wechat_platform',
name: 'wechat_platform',
meta: {
title: '微信开放平台',
parentPath: '/channel',
},
component: () => import('@/views/channel/wechat_platform/index.vue')
}]
}
export default routes

View File

@ -1,66 +0,0 @@
import { RouteRecordRaw, RouterView } from 'vue-router'
const routes: RouteRecordRaw = {
path: '/content',
redirect: '/content/advertising',
component: RouterView,
meta: { title: '内容管理', icon: 'icon-setting' },
children: [
// {
// path: '/content/advertising',
// redirect: '/content/advertising/lists',
// component: RouterView,
// meta: { title: '广告管理' },
// children: [
// {
// path: '/content/advertising/lists',
// component: () => import('@/views/content/advertising/advertising.vue'),
// meta: { title: '广告列表' },
// },
// {
// path: '/content/advertising/advertising_edit',
// component: () => import('@/views/decoration/advertising_edit.vue'),
// meta: {
// title: '广告列表',
// parent: '/content/advertising/lists',
// hidden: true,
// },
// },
// {
// path: '/content/advertising/position',
// component: () => import('@/views/content/advertising/position.vue'),
// meta: { title: '广告位' },
// },
// ],
// },
{
path: '/content/information',
redirect: '/content/information/lists',
component: RouterView,
meta: { title: '资讯管理' },
children: [
{
path: '/content/information/lists',
component: () => import('@/views/content/information/lists.vue'),
meta: { title: '资讯列表' },
},
{
path: '/content/information/information_edit',
component: () => import('@/views/content/information/information_edit.vue'),
meta: {
title: '资讯列表',
parent: '/content/information/lists',
hidden: true,
},
},
{
path: '/content/information/position',
component: () => import('@/views/content/information/category.vue'),
meta: { title: '资讯分类' },
},
],
},
],
}
export default routes

View File

@ -1,38 +0,0 @@
import { RouteRecordRaw, RouterView } from 'vue-router'
const routes: RouteRecordRaw = {
path: '/decoration',
redirect: '/decoration/home',
component: RouterView,
meta: { title: '装修管理', icon: 'icon-setting' },
children: [
{
path: '/decoration/home',
component: () => import('@/views/decoration/home.vue'),
meta: { title: '首页装修' },
},
{
path: '/decoration/home_edit',
component: () => import('@/views/decoration/home_edit.vue'),
meta: { title: '首页装修', parent: '/decoration/home', hidden: true },
},
{
path: '/decoration/tabbar',
component: () => import('@/views/decoration/tabbar.vue'),
meta: { title: '底部标签栏' },
},
{
path: '/decoration/advertising',
component: () => import('@/views/decoration/advertising.vue'),
meta: { title: '广告管理' },
},
{
path: '/decoration/advertising_edit',
component: () => import('@/views/decoration/advertising_edit.vue'),
meta: { title: '广告管理', parent: '/decoration/advertising', hidden: true },
},
],
}
export default routes

View File

@ -1,54 +0,0 @@
import { RouteRecordRaw, RouterView } from 'vue-router'
const routes: RouteRecordRaw = {
path: '/permission',
redirect: '/permission/admin',
component: RouterView,
meta: { title: '权限管理', icon: 'icon-quanxian' },
children: [
{
path: '/permission/admin',
component: () => import('@/views/permission/admin/index.vue'),
meta: { title: '管理员', permission: ['view'] }
},
{
path: '/permission/admin/edit',
component: () => import('@/views/permission/admin/edit.vue'),
meta: {
title: '管理员',
parent: '/permission/admin',
hidden: true
}
},
{
path: '/permission/role',
component: () => import('@/views/permission/role/index.vue'),
meta: { title: '角色', permission: ['view'] }
},
{
path: '/permission/role/edit',
component: () => import('@/views/permission/role/edit.vue'),
meta: {
title: '角色',
parent: '/permission/role',
hidden: true
}
},
{
path: '/permission/menu',
component: () => import('@/views/permission/menu/index.vue'),
meta: { title: '菜单', permission: ['view'] }
},
{
path: '/permission/menu/edit',
component: () => import('@/views/permission/menu/edit.vue'),
meta: {
title: '菜单',
parent: '/permission/menu',
hidden: true
}
}
]
}
export default routes

View File

@ -1,132 +0,0 @@
import { RouteRecordRaw, RouterView } from 'vue-router'
const routes: RouteRecordRaw = {
path: '/setting',
redirect: '/setting/service',
component: RouterView,
meta: { title: '系统设置', icon: 'icon-setting' },
children: [
{
path: '/setting/service',
redirect: '/setting/service/online_service',
component: RouterView,
meta: {
title: '客服设置',
hidden: true
},
children: [
{
path: '/setting/service/online_service',
component: () => import('@/views/setting/service/online_service.vue'),
meta: {
title: '在线客服'
}
}
]
},
{
path: '/setting/website',
redirect: '/setting/website/information',
component: RouterView,
meta: { title: '网站设置' },
children: [
{
path: '/setting/website/information',
component: () => import('@/views/setting/website/information.vue'),
meta: {
title: '网站信息',
permission: ['view']
}
},
{
path: '/setting/website/filing',
component: () => import('@/views/setting/website/filing.vue'),
meta: {
title: '备案信息',
permission: ['view']
}
},
{
path: '/setting/website/protocol',
component: () => import('@/views/setting/website/protocol.vue'),
meta: {
title: '政策/协议',
permission: ['view'],
hidden: true
}
}
]
},
{
path: '/setting/user',
redirect: '/setting/user',
component: RouterView,
meta: {
title: '用户设置',
hidden: true
},
children: [
{
path: '/setting/user',
component: () => import('@/views/setting/user/index.vue'),
meta: {
title: '用户设置',
permission: ['view']
}
},
{
path: '/setting/user/login',
component: () => import('@/views/setting/user/login.vue'),
meta: {
title: '登录注册',
permission: ['view']
}
}
]
},
{
path: '/setting/system',
redirect: '/setting/system/environment',
component: RouterView,
meta: { title: '系统维护' },
children: [
{
path: '/setting/website/environment',
component: () => import('@/views/setting/system/environment.vue'),
meta: {
title: '系统环境',
permission: ['view']
}
},
{
path: '/setting/website/journal',
component: () => import('@/views/setting/system/journal.vue'),
meta: {
title: '系统日志',
permission: ['view']
}
},
{
path: '/setting/website/cache',
component: () => import('@/views/setting/system/cache.vue'),
meta: {
title: '系统缓存',
permission: ['view']
}
}
]
},
{
// component: RouterView,
path: '/setting/personal/personal_data',
component: () => import('@/views/setting/personal/personal_data.vue'),
meta: {
title: '个人设置',
permission: ['view'],
hidden: true
}
}
]
}
export default routes

View File

@ -9,8 +9,7 @@ const getters: GetterTree<rootState, any> = {
// 通用配置 // 通用配置
config: state => state.app.config, config: state => state.app.config,
// 权限列表 // 权限列表
permission: state => state.permission.permission, permission: state => state.user.permissions
isAdmin: state => state.permission.isAdmin
} }
export default getters export default getters

View File

@ -7,7 +7,8 @@ export interface AppModule {
const app: Module<AppModule, any> = { const app: Module<AppModule, any> = {
namespaced: true, namespaced: true,
state: { state: {
config: {} // 公共配置
config: {},
}, },
mutations: { mutations: {
setConfig(state, data) { setConfig(state, data) {

View File

@ -1,45 +1,40 @@
import { Module } from 'vuex' import { Module } from 'vuex'
import { RouteRecordRaw } from 'vue-router' import { RouteRecordRaw } from 'vue-router'
import { apiConfigGetAuth } from '@/api/auth' import { apiConfigGetRoutes } from '@/api/auth'
import { filterAsyncRoutes } from '@/core/lib/router'
export interface PermissionModule { export interface PermissionModule {
routes: Array<RouteRecordRaw>
sidebar: Array<RouteRecordRaw> sidebar: Array<RouteRecordRaw>
permission: any[] | null
isAdmin: number
} }
const permission: Module<PermissionModule, any> = { const permission: Module<PermissionModule, any> = {
namespaced: true, namespaced: true,
state: { state: {
// 路由
routes: [],
// 左侧菜单 // 左侧菜单
sidebar: [], sidebar: []
// 权限列表
permission: null,
// 是否是管理员
isAdmin: 0
}, },
getters: {}, getters: {},
mutations: { mutations: {
setSidebar(state, data) { setSidebar(state, data) {
state.sidebar = data state.sidebar = data
}, },
setPermission(state, data) {
state.permission = data.auth
state.isAdmin = data.root
}
}, },
actions: { actions: {
getPermission({ commit }) { generateRoutes({ commit }) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// apiConfigGetAuth() apiConfigGetRoutes()
// .then(data => { .then((data: any) => {
// commit('setPermission', data) const rdata = JSON.parse(JSON.stringify(data))
// resolve(data) const routes = filterAsyncRoutes(rdata)
// }) commit('setSidebar', routes)
// .catch(err => { resolve(routes)
// reject(err) })
// }) .catch(err => {
reject(err)
resolve({}) })
}) })
} }
} }

View File

@ -5,12 +5,14 @@ import { apiLogin, apiLogout, apiUserInfo } from '@/api/user'
export interface UserModule { export interface UserModule {
token: string token: string
user: object user: object
permissions: any[]
} }
const user: Module<UserModule, any> = { const user: Module<UserModule, any> = {
namespaced: true, namespaced: true,
state: { state: {
token: cache.get(TOKEN) || '', token: cache.get(TOKEN) || '',
user: {} user: {},
permissions: []
}, },
mutations: { mutations: {
setToken(state, data) { setToken(state, data) {
@ -19,13 +21,15 @@ const user: Module<UserModule, any> = {
}, },
setUser(state, data) { setUser(state, data) {
state.user = data state.user = data
},
setPermission(state, permissions) {
state.permissions = permissions
} }
}, },
actions: { actions: {
// 登录 // 登录
login({ commit }, data) { login({ commit }, data) {
const { account, password } = data const { account, password } = data
console.log(data, '----------------------')
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
apiLogin({ apiLogin({
@ -48,6 +52,7 @@ const user: Module<UserModule, any> = {
.then(data => { .then(data => {
commit('setToken', '') commit('setToken', '')
commit('setUser', {}) commit('setUser', {})
commit('setPermission', [])
cache.remove(TOKEN) cache.remove(TOKEN)
resolve(data) resolve(data)
}) })
@ -60,8 +65,9 @@ const user: Module<UserModule, any> = {
getUser({ commit }) { getUser({ commit }) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
apiUserInfo() apiUserInfo()
.then(data => { .then((data: any) => {
commit('setUser', data) commit('setUser', data)
commit('setPermission', data.permissions)
resolve(data) resolve(data)
}) })
.catch(error => { .catch(error => {

View File

@ -8,3 +8,5 @@
@import 'common'; @import 'common';
// element样式 // element样式
@import 'element'; @import 'element';
// 动画
@import 'transition';

View File

@ -0,0 +1,27 @@
/* fade */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s;
}
.fade-enter,
.fade-leave-active {
opacity: 0;
}
/* fade-transform */
.fade-transform-leave-active,
.fade-transform-enter-active {
transition: all .5s;
}
.fade-transform-enter {
opacity: 0;
transform: translateX(-25px);
}
.fade-transform-leave-to {
opacity: 0;
transform: translateX(25px);
}

View File

@ -0,0 +1,8 @@
/**
* @description id=1&name=2
* @param { String } str
* @returns { Boolean }
*/
export function isQuery(str: string) {
return /([^?&=]+)=([^?&=]*)/g.test(str)
}

View File

@ -117,6 +117,7 @@ export default defineComponent({
query: { redirect } query: { redirect }
} = route } = route
const path = typeof redirect === 'string' ? redirect : '/' const path = typeof redirect === 'string' ? redirect : '/'
router.replace(path) router.replace(path)
}) })
.catch(err => { .catch(err => {

View File

@ -28,7 +28,7 @@
</el-card> </el-card>
<el-card v-loading="pager.loading" class="m-t-15" shadow="never"> <el-card v-loading="pager.loading" class="m-t-15" shadow="never">
<router-link to="/permission/admin/edit"> <router-link to="/permission/admin/edit">
<el-button type="primary" size="small">新增管理员</el-button> <el-button v-perm="['system:admin:add']" type="primary" size="small">新增管理员</el-button>
</router-link> </router-link>
<div class="m-t-15"> <div class="m-t-15">
<el-table :data="pager.lists"> <el-table :data="pager.lists">
@ -69,6 +69,7 @@
<el-table-column label="操作" width="150" fixed="right"> <el-table-column label="操作" width="150" fixed="right">
<template #default="{ row }"> <template #default="{ row }">
<router-link <router-link
v-perm="['system:admin:edit']"
class="m-r-10" class="m-r-10"
:to="{ :to="{
path: '/permission/admin/edit', path: '/permission/admin/edit',
@ -79,7 +80,7 @@
> >
<el-button type="text">编辑</el-button> <el-button type="text">编辑</el-button>
</router-link> </router-link>
<popup class="m-r-10 inline" @confirm="handleDelete(row.id)"> <popup v-perm="['system:admin:del']" class="m-r-10 inline" @confirm="handleDelete(row.id)">
<template #trigger> <template #trigger>
<el-button type="text">删除</el-button> <el-button type="text">删除</el-button>
</template> </template>

View File

@ -2,7 +2,7 @@
<div class="menu"> <div class="menu">
<el-card shadow="never"> <el-card shadow="never">
<router-link to="/permission/menu/edit"> <router-link to="/permission/menu/edit">
<el-button type="primary" size="small">添加菜单</el-button> <el-button v-perm="['system:menu:add']" type="primary" size="small">添加菜单</el-button>
</router-link> </router-link>
<el-table <el-table
@ -36,6 +36,7 @@
<el-table-column label="操作"> <el-table-column label="操作">
<template #default="scope"> <template #default="scope">
<router-link <router-link
v-perm="['system:menu:edit']"
class="m-r-10" class="m-r-10"
:to="{ :to="{
path: '/permission/menu/edit', path: '/permission/menu/edit',
@ -47,7 +48,7 @@
<el-button type="text" size="mini">编辑</el-button> <el-button type="text" size="mini">编辑</el-button>
</router-link> </router-link>
<popup class="m-r-10 inline" @confirm="handleDelete(scope.row.id)"> <popup v-perm="['system:menu:del']" class="m-r-10 inline" @confirm="handleDelete(scope.row.id)">
<template #trigger> <template #trigger>
<el-button type="text" size="mini">删除</el-button> <el-button type="text" size="mini">删除</el-button>
</template> </template>

View File

@ -2,7 +2,7 @@
<div class="role"> <div class="role">
<el-card shadow="never"> <el-card shadow="never">
<router-link to="/permission/role/edit"> <router-link to="/permission/role/edit">
<el-button type="primary" size="small">新增角色</el-button> <el-button v-perm="['system:role:add']" type="primary" size="small">新增角色</el-button>
</router-link> </router-link>
<div v-loading="pager.loading" class="m-t-15"> <div v-loading="pager.loading" class="m-t-15">
<div class="m-t-15"> <div class="m-t-15">
@ -22,6 +22,7 @@
<template #default="{ row }"> <template #default="{ row }">
<!-- 编辑 --> <!-- 编辑 -->
<router-link <router-link
v-perm="['system:role:edit']"
class="m-r-10" class="m-r-10"
:to="{ :to="{
path: '/permission/role/edit', path: '/permission/role/edit',
@ -33,7 +34,7 @@
<el-button type="text" size="mini">编辑</el-button> <el-button type="text" size="mini">编辑</el-button>
</router-link> </router-link>
<!-- 删除 --> <!-- 删除 -->
<popup class="m-r-10 inline" @confirm="handleDelete(row.id)"> <popup v-perm="['system:role:del']" class="m-r-10 inline" @confirm="handleDelete(row.id)">
<template #trigger> <template #trigger>
<el-button type="text" size="mini">删除</el-button> <el-button type="text" size="mini">删除</el-button>
</template> </template>