perf: 优化权限管理

This commit is contained in:
kailong321200875 2023-11-12 11:37:28 +08:00
parent 92d436b8bb
commit efc1c25db8
11 changed files with 402 additions and 44 deletions

View File

@ -124,8 +124,6 @@ export default [
email: '@EMAIL',
// 创建时间
createTime: '@datetime',
// 角色
role: '@first',
// 用户id
id: toAnyString()
})

View File

@ -24,6 +24,8 @@ export default [
name: 'Dashboard',
status: Mock.Random.integer(0, 1),
id: 1,
type: 0,
parentId: undefined,
title: '首页',
meta: {
title: '首页',
@ -37,10 +39,23 @@ export default [
name: 'Analysis',
status: Mock.Random.integer(0, 1),
id: 2,
type: 1,
parentId: 1,
title: '分析页',
permissionList: [
{
label: '新增',
value: 'add'
},
{
label: '编辑',
value: 'edit'
}
],
meta: {
title: '分析页',
noCache: true
noCache: true,
permission: ['add', 'edit']
}
},
{
@ -49,7 +64,23 @@ export default [
name: 'Workplace',
status: Mock.Random.integer(0, 1),
id: 3,
type: 1,
parentId: 1,
title: '工作台',
permissionList: [
{
label: '新增',
value: 'add'
},
{
label: '编辑',
value: 'edit'
},
{
label: '删除',
value: 'delete'
}
],
meta: {
title: '工作台',
noCache: true
@ -60,7 +91,6 @@ export default [
{
path: '/external-link',
component: '#',
title: '文档',
meta: {
title: '文档',
icon: 'clarity:document-solid'
@ -68,12 +98,17 @@ export default [
name: 'ExternalLink',
status: Mock.Random.integer(0, 1),
id: 4,
type: 0,
parentId: undefined,
title: '文档',
children: [
{
path: 'https://element-plus-admin-doc.cn/',
name: 'DocumentLink',
status: Mock.Random.integer(0, 1),
id: 5,
type: 1,
parentId: 4,
title: '文档',
meta: {
title: '文档'
@ -88,6 +123,8 @@ export default [
name: 'Level',
status: Mock.Random.integer(0, 1),
id: 6,
type: 0,
parentId: undefined,
title: '菜单',
meta: {
title: '菜单',
@ -100,8 +137,10 @@ export default [
component: '##',
status: Mock.Random.integer(0, 1),
id: 7,
redirect: '/level/menu1/menu1-1/menu1-1-1',
type: 0,
parentId: 6,
title: '菜单1',
redirect: '/level/menu1/menu1-1/menu1-1-1',
meta: {
title: '菜单1'
},
@ -112,8 +151,10 @@ export default [
component: '##',
status: Mock.Random.integer(0, 1),
id: 8,
redirect: '/level/menu1/menu1-1/menu1-1-1',
type: 0,
parentId: 7,
title: '菜单1-1',
redirect: '/level/menu1/menu1-1/menu1-1-1',
meta: {
title: '菜单1-1',
alwaysShow: true
@ -125,7 +166,8 @@ export default [
component: 'views/Level/Menu111',
status: Mock.Random.integer(0, 1),
id: 9,
permission: ['edit', 'add', 'delete'],
type: 1,
parentId: 8,
title: '菜单1-1-1',
meta: {
title: '菜单1-1-1'
@ -139,7 +181,8 @@ export default [
component: 'views/Level/Menu12',
status: Mock.Random.integer(0, 1),
id: 10,
permission: ['edit', 'add', 'delete'],
type: 1,
parentId: 7,
title: '菜单1-2',
meta: {
title: '菜单1-2'
@ -153,7 +196,8 @@ export default [
component: 'views/Level/Menu2',
status: Mock.Random.integer(0, 1),
id: 11,
permission: ['edit', 'add', 'delete'],
type: 1,
parentId: 6,
title: '菜单2',
meta: {
title: '菜单2'
@ -168,6 +212,8 @@ export default [
name: 'Example',
status: Mock.Random.integer(0, 1),
id: 12,
type: 0,
parentId: undefined,
title: '综合示例',
meta: {
title: '综合示例',
@ -181,11 +227,29 @@ export default [
name: 'ExampleDialog',
status: Mock.Random.integer(0, 1),
id: 13,
type: 1,
parentId: 12,
title: '综合示例-弹窗',
permission: ['edit', 'add', 'delete'],
permissionList: [
{
label: '新增',
value: 'add'
},
{
label: '编辑',
value: 'edit'
},
{
label: '删除',
value: 'delete'
},
{
label: '查看',
value: 'view'
}
],
meta: {
title: '综合示例-弹窗',
permission: ['edit', 'add']
title: '综合示例-弹窗'
}
},
{
@ -194,11 +258,29 @@ export default [
name: 'ExamplePage',
status: Mock.Random.integer(0, 1),
id: 14,
permission: ['edit', 'add', 'delete'],
type: 1,
parentId: 12,
title: '综合示例-页面',
permissionList: [
{
label: '新增',
value: 'edit'
},
{
label: '编辑',
value: 'edit'
},
{
label: '删除',
value: 'delete'
},
{
label: '查看',
value: 'view'
}
],
meta: {
title: '综合示例-页面',
permission: ['edit', 'add']
title: '综合示例-页面'
}
},
{
@ -207,7 +289,8 @@ export default [
name: 'ExampleAdd',
status: Mock.Random.integer(0, 1),
id: 15,
permission: ['edit', 'add', 'delete'],
type: 1,
parentId: 12,
title: '综合示例-新增',
meta: {
title: '综合示例-新增',
@ -215,8 +298,7 @@ export default [
noCache: true,
hidden: true,
showMainRoute: true,
activeMenu: '/example/example-page',
permission: ['delete', 'add']
activeMenu: '/example/example-page'
}
},
{
@ -225,7 +307,8 @@ export default [
name: 'ExampleEdit',
status: Mock.Random.integer(0, 1),
id: 16,
permission: ['edit', 'add', 'delete'],
type: 1,
parentId: 12,
title: '综合示例-编辑',
meta: {
title: '综合示例-编辑',
@ -233,8 +316,7 @@ export default [
noCache: true,
hidden: true,
showMainRoute: true,
activeMenu: '/example/example-page',
permission: ['delete', 'add']
activeMenu: '/example/example-page'
}
},
{
@ -243,7 +325,8 @@ export default [
name: 'ExampleDetail',
status: Mock.Random.integer(0, 1),
id: 17,
permission: ['edit', 'add', 'delete'],
type: 1,
parentId: 12,
title: '综合示例-详情',
meta: {
title: '综合示例-详情',
@ -251,8 +334,7 @@ export default [
noCache: true,
hidden: true,
showMainRoute: true,
activeMenu: '/example/example-page',
permission: ['delete', 'edit']
activeMenu: '/example/example-page'
}
}
]

View File

@ -521,7 +521,7 @@ export default {
menu: {
menuName: '菜单名称',
icon: '图标',
permission: '权限标识',
permission: '按钮权限',
component: '组件',
path: '路径',
status: '状态',

View File

@ -35,7 +35,13 @@ const tableColumns = reactive<TableColumn[]>([
},
{
field: 'meta.title',
label: t('menu.menuName')
label: t('menu.menuName'),
slots: {
default: (data: any) => {
const title = data.row.meta.title
return <>{title}</>
}
}
},
{
field: 'meta.icon',

View File

@ -0,0 +1,67 @@
<script setup lang="ts">
import { FormSchema, Form } from '@/components/Form'
import { ElDrawer, ElButton } from 'element-plus'
import { reactive } from 'vue'
import { useForm } from '@/hooks/web/useForm'
import { useValidator } from '@/hooks/web/useValidator'
const modelValue = defineModel<boolean>()
const { required } = useValidator()
const formSchema = reactive<FormSchema[]>([
{
field: 'label',
label: 'label',
component: 'Input',
colProps: {
span: 24
}
},
{
field: 'value',
label: 'value',
component: 'Input',
colProps: {
span: 24
}
}
])
const { formRegister, formMethods } = useForm()
const { getFormData, getElFormExpose } = formMethods
const emit = defineEmits(['confirm'])
const rules = reactive({
label: [required()],
value: [required()]
})
const confirm = async () => {
const elFormExpose = await getElFormExpose()
if (!elFormExpose) return
const valid = await elFormExpose?.validate().catch((err) => {
console.log(err)
})
if (valid) {
const formData = await getFormData()
emit('confirm', formData)
modelValue.value = false
}
}
</script>
<template>
<ElDrawer v-model="modelValue" title="新增按钮权限">
<template #default>
<Form :rules="rules" @register="formRegister" :schema="formSchema" />
</template>
<template #footer>
<div>
<ElButton @click="() => (modelValue = false)">取消</ElButton>
<ElButton type="primary" @click="confirm">确认</ElButton>
</div>
</template>
</ElDrawer>
</template>

View File

@ -75,6 +75,24 @@ const detailSchema = ref<DescriptionsSchema[]>([
field: 'meta.activeMenu',
label: '高亮菜单'
},
{
field: 'permissionList',
label: '按钮权限',
span: 24,
slots: {
default: (data: any) => (
<>
{data?.permissionList?.map((v) => {
return (
<ElTag class="mr-1" key={v.value}>
{v.label}
</ElTag>
)
})}
</>
)
}
},
{
field: 'menuState',
label: '菜单状态',

View File

@ -1,11 +1,12 @@
<script setup lang="tsx">
import { Form, FormSchema } from '@/components/Form'
import { useForm } from '@/hooks/web/useForm'
import { PropType, reactive, watch } from 'vue'
import { PropType, reactive, watch, ref, unref } from 'vue'
import { useValidator } from '@/hooks/web/useValidator'
import { useI18n } from '@/hooks/web/useI18n'
import { getMenuListApi } from '@/api/menu'
import { ElTag, ElButton } from 'element-plus'
import AddButtonPermission from './AddButtonPermission.vue'
const { t } = useI18n()
@ -18,6 +19,16 @@ const props = defineProps({
}
})
const handleClose = async (tag: any) => {
const formData = await getFormData()
//
setValues({
permissionList: formData?.permissionList?.filter((v: any) => v.value !== tag.value)
})
}
const showDrawer = ref(false)
const formSchema = reactive<FormSchema[]>([
{
field: 'type',
@ -50,7 +61,7 @@ const formSchema = reactive<FormSchema[]>([
}
])
setValues({
component: ''
component: unref(cacheComponent)
})
} else {
setSchema([
@ -104,7 +115,7 @@ const formSchema = reactive<FormSchema[]>([
})
} else if (formData.type === 1) {
setValues({
component: ''
component: unref(cacheComponent) ?? ''
})
}
}
@ -127,7 +138,12 @@ const formSchema = reactive<FormSchema[]>([
value: '#',
componentProps: {
disabled: true,
placeholder: '#为顶级目录,##为子目录'
placeholder: '#为顶级目录,##为子目录',
on: {
change: (val: string) => {
cacheComponent.value = val
}
}
}
},
{
@ -176,18 +192,16 @@ const formSchema = reactive<FormSchema[]>([
},
formItemProps: {
slots: {
default: () => (
default: (data: any) => (
<>
<ElTag class="mx-1" closable disableTransitions={false}>
新增
</ElTag>
<ElTag class="mx-1" closable disableTransitions={false}>
编辑
</ElTag>
<ElTag class="mx-1" closable disableTransitions={false}>
删除
</ElTag>
<ElButton type="primary" size="small" onClick={() => console.log('添加权限')}>
{data?.permissionList?.map((v) => {
return (
<ElTag class="mr-1" key={v.value} closable onClose={() => handleClose(v)}>
{v.label}
</ElTag>
)
})}
<ElButton type="primary" size="small" onClick={() => (showDrawer.value = true)}>
添加权限
</ElButton>
</>
@ -252,10 +266,47 @@ const submit = async () => {
}
}
const cacheComponent = ref('')
watch(
() => props.currentRow,
(currentRow) => {
if (!currentRow) return
cacheComponent.value = currentRow.type === 1 ? currentRow.component : ''
if (currentRow.parentId === 0) {
setSchema([
{
field: 'component',
path: 'componentProps.disabled',
value: true
}
])
} else {
setSchema([
{
field: 'component',
path: 'componentProps.disabled',
value: false
}
])
}
if (currentRow.type === 1) {
setSchema([
{
field: 'component',
path: 'componentProps.disabled',
value: false
}
])
} else {
setSchema([
{
field: 'component',
path: 'componentProps.disabled',
value: true
}
])
}
setValues(currentRow)
},
{
@ -267,8 +318,16 @@ watch(
defineExpose({
submit
})
const confirm = async (data: any) => {
const formData = await getFormData()
setValues({
permissionList: [...(formData?.permissionList || []), data]
})
}
</script>
<template>
<Form :rules="rules" @register="formRegister" :schema="formSchema" />
<AddButtonPermission v-model="showDrawer" @confirm="confirm" />
</template>

View File

@ -9,6 +9,7 @@ import { Search } from '@/components/Search'
import { FormSchema } from '@/components/Form'
import { ContentWrap } from '@/components/ContentWrap'
import Write from './components/Write.vue'
import Detail from './components/Detail.vue'
import { Dialog } from '@/components/Dialog'
const { t } = useI18n()
@ -71,6 +72,9 @@ const tableColumns = reactive<TableColumn[]>([
<ElButton type="primary" onClick={() => action(row, 'edit')}>
{t('exampleDemo.edit')}
</ElButton>
<ElButton type="success" onClick={() => action(row, 'detail')}>
{t('exampleDemo.detail')}
</ElButton>
<ElButton type="danger">{t('exampleDemo.del')}</ElButton>
</>
)
@ -151,6 +155,7 @@ const save = async () => {
<Dialog v-model="dialogVisible" :title="dialogTitle">
<Write v-if="actionType !== 'detail'" ref="writeRef" :current-row="currentRow" />
<Detail v-else :current-row="currentRow" />
<template #footer>
<ElButton v-if="actionType !== 'detail'" type="primary" :loading="saveLoading" @click="save">

View File

@ -0,0 +1,106 @@
<script setup lang="tsx">
import { PropType, ref, unref, nextTick } from 'vue'
import { Descriptions, DescriptionsSchema } from '@/components/Descriptions'
import { ElTag, ElTree } from 'element-plus'
import { findIndex } from '@/utils'
import { getMenuListApi } from '@/api/menu'
defineProps({
currentRow: {
type: Object as PropType<any>,
default: () => undefined
}
})
const filterPermissionName = (value: string) => {
const index = findIndex(unref(currentTreeData)?.permissionList || [], (item) => {
return item.value === value
})
return (unref(currentTreeData)?.permissionList || [])[index].label ?? ''
}
const renderTag = (enable?: boolean) => {
return <ElTag type={!enable ? 'danger' : 'success'}>{enable ? '启用' : '禁用'}</ElTag>
}
const treeRef = ref<typeof ElTree>()
const currentTreeData = ref()
const nodeClick = (treeData: any) => {
currentTreeData.value = treeData
}
const treeData = ref<any[]>([])
const getMenuList = async () => {
const res = await getMenuListApi()
if (res) {
treeData.value = res.data.list
await nextTick()
}
}
getMenuList()
const detailSchema = ref<DescriptionsSchema[]>([
{
field: 'roleName',
label: '角色名称'
},
{
field: 'status',
label: '状态',
slots: {
default: (data: any) => {
return renderTag(data.status)
}
}
},
{
field: 'remark',
label: '备注',
span: 24
},
{
field: 'permissionList',
label: '菜单分配',
span: 24,
slots: {
default: () => {
return (
<>
<div class="flex w-full">
<div class="flex-1">
<ElTree
ref={treeRef}
node-key="id"
props={{ children: 'children', label: 'title' }}
highlight-current
expand-on-click-node={false}
data={treeData.value}
onNode-click={nodeClick}
>
{{
default: (data) => {
return <span>{data?.data?.title}</span>
}
}}
</ElTree>
</div>
<div class="flex-1">
{unref(currentTreeData)
? unref(currentTreeData)?.meta?.permission?.map((v: string) => {
return <ElTag class="ml-2 mt-2">{filterPermissionName(v)}</ElTag>
})
: null}
</div>
</div>
</>
)
}
}
}
])
</script>
<template>
<Descriptions :schema="detailSchema" :data="currentRow || {}" />
</template>

View File

@ -76,10 +76,10 @@ const formSchema = ref<FormSchema[]>([
</ElTree>
</div>
<div class="flex-1">
{unref(currentTreeData) && unref(currentTreeData)?.permission ? (
{unref(currentTreeData) && unref(currentTreeData)?.permissionList ? (
<ElCheckboxGroup v-model={unref(currentTreeData).meta.permission}>
{unref(currentTreeData)?.permission.map((v: string) => {
return <ElCheckbox label={v} />
{unref(currentTreeData)?.permissionList.map((v: any) => {
return <ElCheckbox label={v.value}>{v.label}</ElCheckbox>
})}
</ElCheckboxGroup>
) : null}

View File

@ -11,6 +11,7 @@ import { Search } from '@/components/Search'
import Write from './components/Write.vue'
import Detail from './components/Detail.vue'
import { Dialog } from '@/components/Dialog'
import { getRoleListApi } from '@/api/role'
import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
const { t } = useI18n()
@ -113,6 +114,22 @@ const crudSchemas = reactive<CrudSchema[]>([
label: t('userDemo.role'),
search: {
hidden: true
},
form: {
component: 'Select',
value: [],
componentProps: {
multiple: true,
collapseTags: true,
maxCollapseTags: 1
},
optionApi: async () => {
const res = await getRoleListApi()
return res.data?.list?.map((v) => ({
label: v.roleName,
value: v.id
}))
}
}
},
{