Merge branch 'master' into deploy

This commit is contained in:
Pan 2019-03-15 18:13:50 +08:00
commit 0e9f32f25a
20 changed files with 604 additions and 103 deletions

38
src/api/role.js Normal file
View File

@ -0,0 +1,38 @@
import request from '@/utils/request'
export function getRoutes() {
return request({
url: '/routes',
method: 'get'
})
}
export function getRoles() {
return request({
url: '/roles',
method: 'get'
})
}
export function deleteRole(id) {
return request({
url: `/roles/${id}`,
method: 'delete'
})
}
export function addRole(data) {
return request({
url: '/roles',
method: 'post',
data
})
}
export function updateRole(key, data) {
return request({
url: `/roles/${key}`,
method: 'put',
data
})
}

View File

@ -33,8 +33,8 @@ export default {
}
},
computed: {
routers() {
return this.$store.getters.permission_routers
routes() {
return this.$store.getters.permission_routes
},
lang() {
return this.$store.getters.language
@ -42,10 +42,10 @@ export default {
},
watch: {
lang() {
this.searchPool = this.generateRouters(this.routers)
this.searchPool = this.generateRoutes(this.routes)
},
routers() {
this.searchPool = this.generateRouters(this.routers)
routes() {
this.searchPool = this.generateRoutes(this.routes)
},
searchPool(list) {
this.initFuse(list)
@ -59,7 +59,7 @@ export default {
}
},
mounted() {
this.searchPool = this.generateRouters(this.routers)
this.searchPool = this.generateRoutes(this.routes)
},
methods: {
click() {
@ -100,10 +100,10 @@ export default {
},
// Filter out the routes that can be displayed in the sidebar
// And generate the internationalized title
generateRouters(routers, basePath = '/', prefixTitle = []) {
generateRoutes(routes, basePath = '/', prefixTitle = []) {
let res = []
for (const router of routers) {
for (const router of routes) {
// skip hidden router
if (router.hidden) { continue }
@ -125,11 +125,11 @@ export default {
}
}
// recursive child routers
// recursive child routes
if (router.children) {
const tempRouters = this.generateRouters(router.children, data.path, data.title)
if (tempRouters.length >= 1) {
res = [...res, ...tempRouters]
const tempRoutes = this.generateRoutes(router.children, data.path, data.title)
if (tempRoutes.length >= 1) {
res = [...res, ...tempRoutes]
}
}
}

View File

@ -146,6 +146,19 @@ export default {
this.selcetRecursion(child, select, children)
})
}
},
updateTreeNode(item) {
return new Promise(resolve => {
const { _id, _parent } = item
const index = _id.split('-').slice(-1)[0] // get last index
if (_parent) {
_parent.children.splice(index, 1, item)
resolve(this.data)
} else {
this.data.splice(index, 1, item)
resolve(this.data)
}
})
}
}
}

View File

@ -0,0 +1,42 @@
import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event'
/**
* How to use
* <el-table height="100px" v-el-height-adaptive-table="{bottomOffset: 30}">...</el-table>
* el-table height is must be set
* bottomOffset: 30(default) // The height of the table from the bottom of the page.
*/
const doResize = (el, binding, vnode) => {
const { componentInstance: $table } = vnode
const { value } = binding
if (!$table.height) {
throw new Error(`el-$table must set the height. Such as height='100px'`)
}
const bottomOffset = (value && value.bottomOffset) || 30
if (!$table) return
const height = window.innerHeight - el.getBoundingClientRect().top - bottomOffset
$table.layout.setHeight(height)
$table.doLayout()
}
export default {
bind(el, binding, vnode) {
el.resizeListener = () => {
doResize(el, binding, vnode)
}
addResizeListener(el, el.resizeListener)
},
inserted(el, binding, vnode) {
doResize(el, binding, vnode)
},
unbind(el) {
removeResizeListener(el, el.resizeListener)
}
}

View File

@ -0,0 +1,14 @@
import adaptive from './adaptive'
const install = function(Vue) {
Vue.directive('el-height-adaptive-table', adaptive)
}
if (window.Vue) {
window['el-height-adaptive-table'] = adaptive
Vue.use(install); // eslint-disable-line
}
adaptive.install = install
export default adaptive

View File

@ -1,14 +1,18 @@
import './waves.css'
export default{
bind(el, binding) {
el.addEventListener('click', e => {
const context = '@@wavesContext'
function handleClick(el, binding) {
function handle(e) {
const customOpts = Object.assign({}, binding.value)
const opts = Object.assign({
const opts = Object.assign(
{
ele: el, // 波纹作用元素
type: 'hit', // hit 点击位置扩散 center中心点扩展
color: 'rgba(0, 0, 0, 0.15)' // 波纹颜色
}, customOpts)
},
customOpts
)
const target = opts.ele
if (target) {
target.style.position = 'relative'
@ -25,18 +29,45 @@ export default{
}
switch (opts.type) {
case 'center':
ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px'
ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px'
ripple.style.top = rect.height / 2 - ripple.offsetHeight / 2 + 'px'
ripple.style.left = rect.width / 2 - ripple.offsetWidth / 2 + 'px'
break
default:
ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.documentElement.scrollTop || document.body.scrollTop) + 'px'
ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.documentElement.scrollLeft || document.body.scrollLeft) + 'px'
ripple.style.top =
(e.pageY - rect.top - ripple.offsetHeight / 2 - document.documentElement.scrollTop ||
document.body.scrollTop) + 'px'
ripple.style.left =
(e.pageX - rect.left - ripple.offsetWidth / 2 - document.documentElement.scrollLeft ||
document.body.scrollLeft) + 'px'
}
ripple.style.backgroundColor = opts.color
ripple.className = 'waves-ripple z-active'
return false
}
}, false)
}
}
if (!el[context]) {
el[context] = {
removeHandle: handle
}
} else {
el[context].removeHandle = handle
}
return handle
}
export default {
bind(el, binding) {
el.addEventListener('click', handleClick(el, binding), false)
},
update(el, binding) {
el.removeEventListener('click', el[context].removeHandle, false)
el.addEventListener('click', handleClick(el, binding), false)
},
unbind(el) {
el.removeEventListener('click', el[context].removeHandle, false)
el[context] = null
delete el[context]
}
}

View File

@ -6,6 +6,7 @@ export default {
guide: 'Guide',
permission: 'Permission',
pagePermission: 'Page Permission',
rolePermission: 'Role Permission',
directivePermission: 'Directive Permission',
icons: 'Icons',
components: 'Components',
@ -87,9 +88,14 @@ export default {
github: 'Github Repository'
},
permission: {
addRole: 'New Role',
editPermission: 'Edit Permission',
roles: 'Your roles',
switchRoles: 'Switch roles',
tips: 'In some cases it is not suitable to use v-permission, such as element Tab component or el-table-column and other asynchronous rendering dom cases which can only be achieved by manually setting the v-if.'
tips: 'In some cases it is not suitable to use v-permission, such as element Tab component or el-table-column and other asynchronous rendering dom cases which can only be achieved by manually setting the v-if.',
delete: 'Delete',
confirm: 'Confirm',
cancel: 'Cancel'
},
guide: {
description: 'The guide page is useful for some people who entered the project for the first time. You can briefly introduce the features of the project. Demo is based on ',

View File

@ -5,6 +5,7 @@ export default {
documentation: 'Documentación',
guide: 'Guía',
permission: 'Permisos',
rolePermission: 'Permisos de rol',
pagePermission: 'Permisos de la página',
directivePermission: 'Permisos de la directiva',
icons: 'Iconos',
@ -87,9 +88,14 @@ export default {
github: 'Repositorio Github'
},
permission: {
addRole: 'Nuevo rol',
editPermission: 'Permiso de edición',
roles: 'Tus permisos',
switchRoles: 'Cambiar permisos',
tips: 'In some cases it is not suitable to use v-permission, such as element Tab component or el-table-column and other asynchronous rendering dom cases which can only be achieved by manually setting the v-if.'
tips: 'In some cases it is not suitable to use v-permission, such as element Tab component or el-table-column and other asynchronous rendering dom cases which can only be achieved by manually setting the v-if.',
delete: 'Borrar',
confirm: 'Confirmar',
cancel: 'Cancelar'
},
guide: {
description: 'The guide page is useful for some people who entered the project for the first time. You can briefly introduce the features of the project. Demo is based on ',

View File

@ -5,6 +5,7 @@ export default {
documentation: '文档',
guide: '引导页',
permission: '权限测试页',
rolePermission: '角色权限',
pagePermission: '页面权限',
directivePermission: '指令权限',
icons: '图标',
@ -87,9 +88,14 @@ export default {
github: 'Github 地址'
},
permission: {
addRole: '新增角色',
editPermission: '编辑权限',
roles: '你的权限',
switchRoles: '切换权限',
tips: '在某些情况下,不适合使用 v-permission。例如Element-UI 的 Tab 组件或 el-table-column 以及其它动态渲染 dom 的场景。你只能通过手动设置 v-if 来实现。'
tips: '在某些情况下,不适合使用 v-permission。例如Element-UI 的 Tab 组件或 el-table-column 以及其它动态渲染 dom 的场景。你只能通过手动设置 v-if 来实现。',
delete: '删除',
confirm: '确定',
cancel: '取消'
},
guide: {
description: '引导页对于一些第一次进入项目的人很有用,你可以简单介绍下项目的功能。本 Demo 是基于',

View File

@ -3,6 +3,7 @@ import loginAPI from './login'
import articleAPI from './article'
import remoteSearchAPI from './remoteSearch'
import transactionAPI from './transaction'
import roleAPI from './role'
// 修复在使用 MockJS 情况下,设置 withCredentials = true且未被拦截的跨域请求丢失 Cookies 的问题
// https://github.com/nuysoft/Mock/issues/300
@ -23,6 +24,13 @@ Mock.mock(/\/login\/login/, 'post', loginAPI.loginByUsername)
Mock.mock(/\/login\/logout/, 'post', loginAPI.logout)
Mock.mock(/\/user\/info\.*/, 'get', loginAPI.getUserInfo)
// 角色相关
Mock.mock(/\/routes/, 'get', roleAPI.getRoutes)
Mock.mock(/\/roles/, 'get', roleAPI.getRoles)
Mock.mock(/\/roles$/, 'post', roleAPI.addRole)
Mock.mock(/\/roles\/[A-Za-z0-9]+/, 'put', roleAPI.updateRole)
Mock.mock(/\/roles\/[A-Za-z0-9]+/, 'delete', roleAPI.deleteRole)
// 文章相关
Mock.mock(/\/article\/list/, 'get', articleAPI.getList)
Mock.mock(/\/article\/detail/, 'get', articleAPI.getArticle)

61
src/mock/role.js Normal file
View File

@ -0,0 +1,61 @@
import Mock from 'mockjs'
import { deepClone } from '@/utils'
import { filterAsyncRoutes } from '@/store/modules/permission'
import { asyncRoutes, constantRoutes } from '@/router'
const routes = deepClone([...constantRoutes, ...asyncRoutes])
const roles = [
{
key: 'admin',
name: 'admin',
description: 'Super Administrator. Have access to view all pages.',
routes: routes
},
{
key: 'editor',
name: 'editor',
description: 'Normal Editor. Can see all pages except permission page',
routes: filterAsyncRoutes(routes, ['editor'])
},
{
key: 'visitor',
name: 'visitor',
description: 'Just a visitor. Can only see the home page and the document page',
routes: [{
path: '',
redirect: 'dashboard',
children: [
{
path: 'dashboard',
name: 'Dashboard',
meta: { title: 'dashboard', icon: 'dashboard' }
}
]
}]
}
]
export default {
getRoutes() {
return routes
},
getRoles() {
return roles
},
addRole() {
return Mock.mock('@integer(300, 5000)')
},
updateRole() {
const res = {
data: 'success'
}
return res
},
deleteRole() {
const res = {
data: 'success'
}
return res
}
}

View File

@ -3,13 +3,13 @@ import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import { getToken } from '@/utils/auth' // getToken from cookie
import { getToken } from '@/utils/auth' // get token from cookie
NProgress.configure({ showSpinner: false }) // NProgress Configuration
// permission judge function
function hasPermission(roles, permissionRoles) {
if (roles.indexOf('admin') >= 0) return true // admin permission passed directly
if (roles.includes('admin')) return true // admin permission passed directly
if (!permissionRoles) return true
return roles.some(role => permissionRoles.indexOf(role) >= 0)
}
@ -18,20 +18,28 @@ const whiteList = ['/login', '/auth-redirect']// no redirect whitelist
router.beforeEach((to, from, next) => {
NProgress.start() // start progress bar
if (getToken()) { // determine if there has token
if (getToken()) {
// determine if there has token
/* has token*/
if (to.path === '/login') {
next({ path: '/' })
NProgress.done() // if current page is dashboard will not trigger afterEach hook, so manually handle it
} else {
if (store.getters.roles.length === 0) { // 判断当前用户是否已拉取完user_info信息
store.dispatch('GetUserInfo').then(res => { // 拉取user_info
const roles = res.data.roles // note: roles must be a array! such as: ['editor','develop']
store.dispatch('GenerateRoutes', { roles }).then(() => { // 根据roles权限生成可访问的路由表
router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
if (store.getters.roles.length === 0) {
// 判断当前用户是否已拉取完user_info信息
store
.dispatch('GetUserInfo')
.then(res => {
// 拉取user_info
const roles = res.data.roles // note: roles must be a object array! such as: [{id: '1', name: 'editor'}, {id: '2', name: 'developer'}]
store.dispatch('GenerateRoutes', { roles }).then(accessRoutes => {
// 根据roles权限生成可访问的路由表
router.addRoutes(accessRoutes) // 动态添加可访问路由表
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
})
}).catch((err) => {
})
.catch(err => {
store.dispatch('FedLogOut').then(() => {
Message.error(err)
next({ path: '/' })
@ -49,7 +57,8 @@ router.beforeEach((to, from, next) => {
}
} else {
/* has no token*/
if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入
if (whiteList.indexOf(to.path) !== -1) {
// 在免登录白名单,直接进入
next()
} else {
next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页

View File

@ -33,7 +33,7 @@ import nestedRouter from './modules/nested'
affix: true if true, the tag will affix in the tags-view
}
**/
export const constantRouterMap = [
export const constantRoutes = [
{
path: '/redirect',
component: Layout,
@ -81,7 +81,6 @@ export const constantRouterMap = [
{
path: '/documentation',
component: Layout,
redirect: '/documentation/index',
children: [
{
path: 'index',
@ -109,10 +108,10 @@ export const constantRouterMap = [
export default new Router({
// mode: 'history', // require service support
scrollBehavior: () => ({ y: 0 }),
routes: constantRouterMap
routes: constantRoutes
})
export const asyncRouterMap = [
export const asyncRoutes = [
{
path: '/permission',
component: Layout,
@ -141,6 +140,15 @@ export const asyncRouterMap = [
title: 'directivePermission'
// if do not set roles, means: this page does not require permission
}
},
{
path: 'role',
component: () => import('@/views/permission/role'),
name: 'RolePermission',
meta: {
title: 'rolePermission',
roles: ['admin']
}
}
]
},

View File

@ -12,8 +12,8 @@ const getters = {
status: state => state.user.status,
roles: state => state.user.roles,
setting: state => state.user.setting,
permission_routers: state => state.permission.routers,
addRouters: state => state.permission.addRouters,
permission_routes: state => state.permission.routes,
addRoutes: state => state.permission.addRoutes,
errorLogs: state => state.errorLog.logs
}
export default getters

View File

@ -1,4 +1,4 @@
import { asyncRouterMap, constantRouterMap } from '@/router'
import { asyncRoutes, constantRoutes } from '@/router'
/**
* 通过meta.role判断是否与当前用户权限匹配
@ -15,17 +15,17 @@ function hasPermission(roles, route) {
/**
* 递归过滤异步路由表返回符合用户角色权限的路由表
* @param routes asyncRouterMap
* @param routes asyncRoutes
* @param roles
*/
function filterAsyncRouter(routes, roles) {
export function filterAsyncRoutes(routes, roles) {
const res = []
routes.forEach(route => {
const tmp = { ...route }
if (hasPermission(roles, tmp)) {
if (tmp.children) {
tmp.children = filterAsyncRouter(tmp.children, roles)
tmp.children = filterAsyncRoutes(tmp.children, roles)
}
res.push(tmp)
}
@ -36,27 +36,27 @@ function filterAsyncRouter(routes, roles) {
const permission = {
state: {
routers: [],
addRouters: []
routes: [],
addRoutes: []
},
mutations: {
SET_ROUTERS: (state, routers) => {
state.addRouters = routers
state.routers = constantRouterMap.concat(routers)
SET_ROUTES: (state, routes) => {
state.addRoutes = routes
state.routes = constantRoutes.concat(routes)
}
},
actions: {
GenerateRoutes({ commit }, data) {
return new Promise(resolve => {
const { roles } = data
let accessedRouters
let accessedRoutes
if (roles.includes('admin')) {
accessedRouters = asyncRouterMap
accessedRoutes = asyncRoutes
} else {
accessedRouters = filterAsyncRouter(asyncRouterMap, roles)
accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
}
commit('SET_ROUTERS', accessedRouters)
resolve()
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
})
}
}

View File

@ -136,7 +136,8 @@ export function param2Obj(url) {
decodeURIComponent(search)
.replace(/"/g, '\\"')
.replace(/&/g, '","')
.replace(/=/g, '":"') +
.replace(/=/g, '":"')
.replace(/\+/g, ' ') +
'"}'
)
}

View File

@ -9,7 +9,7 @@
:collapse-transition="false"
mode="vertical"
>
<sidebar-item v-for="route in permission_routers" :key="route.path" :item="route" :base-path="route.path"/>
<sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path"/>
</el-menu>
</el-scrollbar>
</template>
@ -23,7 +23,7 @@ export default {
components: { SidebarItem },
computed: {
...mapGetters([
'permission_routers',
'permission_routes',
'sidebar'
]),
variables() {

View File

@ -45,8 +45,8 @@ export default {
visitedViews() {
return this.$store.state.tagsView.visitedViews
},
routers() {
return this.$store.state.permission.routers
routes() {
return this.$store.state.permission.routes
}
},
watch: {
@ -93,7 +93,7 @@ export default {
return tags
},
initTags() {
const affixTags = this.affixTags = this.filterAffixTags(this.routers)
const affixTags = this.affixTags = this.filterAffixTags(this.routes)
for (const tag of affixTags) {
// Must have tag name
if (tag.name) {

View File

@ -0,0 +1,267 @@
<template>
<div class="app-container">
<el-button type="primary" @click="handleAddRole">{{ $t('permission.addRole') }}</el-button>
<el-table :data="rolesList" style="width: 100%;margin-top:30px;" border>
<el-table-column align="center" label="Role Key" width="220">
<template slot-scope="scope">{{ scope.row.key }}</template>
</el-table-column>
<el-table-column align="center" label="Role Name" width="220">
<template slot-scope="scope">{{ scope.row.name }}</template>
</el-table-column>
<el-table-column align="header-center" label="Description">
<template slot-scope="scope">{{ scope.row.description }}</template>
</el-table-column>
<el-table-column align="center" label="Operations">
<template slot-scope="scope">
<el-button type="primary" size="small" @click="handleEdit(scope)">{{ $t('permission.editPermission') }}</el-button>
<el-button type="danger" size="small" @click="handleDelete(scope)">{{ $t('permission.delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<el-dialog :visible.sync="dialogVisible" :title="dialogType==='edit'?'Edit Role':'New Role'">
<el-form :model="role" label-width="80px" label-position="left" >
<el-form-item label="Name">
<el-input v-model="role.name" placeholder="Role Name"/>
</el-form-item>
<el-form-item label="Desc">
<el-input
v-model="role.description"
:autosize="{ minRows: 2, maxRows: 4}"
type="textarea"
placeholder="Role Description"/>
</el-form-item>
<el-form-item label="Menus">
<el-tree ref="tree" :check-strictly="checkStrictly" :data="routesData" :props="defaultProps" show-checkbox node-key="path" class="permission-tree"/>
</el-form-item>
</el-form>
<div style="text-align:right;">
<el-button type="danger" @click="dialogVisible=false">{{ $t('permission.cancel') }}</el-button>
<el-button type="primary" @click="confirmRole">{{ $t('permission.confirm') }}</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import path from 'path'
import { deepClone } from '@/utils'
import { getRoutes, getRoles, addRole, deleteRole, updateRole } from '@/api/role'
import i18n from '@/lang'
const defaultRole = {
key: '',
name: '',
description: '',
routes: []
}
export default {
data() {
return {
role: Object.assign({}, defaultRole),
routes: [],
rolesList: [],
dialogVisible: false,
dialogType: 'new',
checkStrictly: false,
defaultProps: {
children: 'children',
label: 'title'
}
}
},
computed: {
routesData() {
return this.routes
}
},
created() {
// Mock: get all routes and roles list from server
this.getRoutes()
this.getRoles()
},
methods: {
async getRoutes() {
const res = await getRoutes()
this.serviceRoutes = res.data
const routes = this.generateRoutes(res.data)
this.routes = this.i18n(routes)
},
async getRoles() {
const res = await getRoles()
this.rolesList = res.data
},
i18n(routes) {
const app = routes.map(route => {
route.title = i18n.t(`route.${route.title}`)
if (route.children) {
route.children = this.i18n(route.children)
}
return route
})
return app
},
// Reshape the routes structure so that it looks the same as the sidebar
generateRoutes(routes, basePath = '/') {
const res = []
for (let route of routes) {
// skip some route
if (route.hidden) { continue }
const onlyOneShowingChild = this.onlyOneShowingChild(route.children, route)
if (route.children && onlyOneShowingChild && !route.alwaysShow) {
route = onlyOneShowingChild
}
const data = {
path: path.resolve(basePath, route.path),
title: route.meta && route.meta.title
}
// recursive child routes
if (route.children) {
data.children = this.generateRoutes(route.children, data.path)
}
res.push(data)
}
return res
},
generateArr(routes) {
let data = []
routes.forEach(route => {
data.push(route)
if (route.children) {
const temp = this.generateArr(route.children)
if (temp.length > 0) {
data = [...data, ...temp]
}
}
})
return data
},
handleAddRole() {
this.role = Object.assign({}, defaultRole)
if (this.$refs.tree) {
this.$refs.tree.setCheckedNodes([])
}
this.dialogType = 'new'
this.dialogVisible = true
},
handleEdit(scope) {
this.dialogType = 'edit'
this.dialogVisible = true
this.checkStrictly = true
this.role = deepClone(scope.row)
this.$nextTick(() => {
const routes = this.generateRoutes(this.role.routes)
this.$refs.tree.setCheckedNodes(this.generateArr(routes))
// set checked state of a node not affects its father and child nodes
this.checkStrictly = false
})
},
handleDelete({ $index, row }) {
this.$confirm('Confirm to remove the role?', 'Warning', {
confirmButtonText: 'Confirm',
cancelButtonText: 'Cancel',
type: 'warning'
})
.then(async() => {
await deleteRole(row.id)
this.rolesList.splice($index, 1)
this.$message({
type: 'success',
message: 'Delete succed!'
})
})
.catch(err => { console.error(err) })
},
generateTree(routes, basePath = '/', checkedKeys) {
const res = []
for (const route of routes) {
const routePath = path.resolve(basePath, route.path)
// recursive child routes
if (route.children) {
route.children = this.generateTree(route.children, routePath, checkedKeys)
}
if (checkedKeys.includes(routePath) || (route.children && route.children.length >= 1)) {
res.push(route)
}
}
return res
},
async confirmRole() {
const isEdit = this.dialogType === 'edit'
const checkedKeys = this.$refs.tree.getCheckedKeys()
this.role.routes = this.generateTree(deepClone(this.serviceRoutes), '/', checkedKeys)
if (isEdit) {
await updateRole(this.role.key, this.role)
for (let index = 0; index < this.rolesList.length; index++) {
if (this.rolesList[index].key === this.role.key) {
this.rolesList.splice(index, 1, Object.assign({}, this.role))
break
}
}
} else {
const { data } = await addRole(this.role)
this.role.key = data
this.rolesList.push(this.role)
}
const { description, key, name } = this.role
this.dialogVisible = false
this.$notify({
title: 'Success',
dangerouslyUseHTMLString: true,
message: `
<div>Role Key: ${key}</div>
<div>Role Nmae: ${name}</div>
<div>Description: ${description}</div>
`,
type: 'success'
})
},
// reference: src/view/layout/components/Sidebar/SidebarItem.vue
onlyOneShowingChild(children = [], parent) {
let onlyOneChild = null
const showingChildren = children.filter(item => !item.hidden)
// When there is only one child route, the child route is displayed by default
if (showingChildren.length === 1) {
onlyOneChild = showingChildren[0]
onlyOneChild.path = path.resolve(parent.path, onlyOneChild.path)
return onlyOneChild
}
// Show parent if there are no child route to display
if (showingChildren.length === 0) {
onlyOneChild = { ... parent, path: '', noShowingChildren: true }
return onlyOneChild
}
return false
}
}
}
</script>
<style lang="scss" scoped>
.app-container {
.roles-table {
margin-top: 30px;
}
.permission-tree {
margin-bottom: 30px;
}
}
</style>

View File

@ -127,17 +127,8 @@ export default {
this.tempItem = Object.assign({}, row)
this.dialogFormVisible = true
},
updateItem() {
const data = this.$refs.TreeTable.getData()
const { _id } = this.tempItem
for (let i = 0; i < data.length; i++) {
if (data[i]._id === _id) {
data.splice(i, 1, Object.assign({}, this.tempItem))
break
}
}
async updateItem() {
await this.$refs.TreeTable.updateTreeNode(this.tempItem)
this.dialogFormVisible = false
},
addMenuItem(row, type) {