Showing
3 changed files
with
283 additions
and
2 deletions
| @@ -72,7 +72,7 @@ | @@ -72,7 +72,7 @@ | ||
| 72 | import { TOption } from '/@/views/rule/linkedge/config/config.data'; | 72 | import { TOption } from '/@/views/rule/linkedge/config/config.data'; |
| 73 | import { PlusOutlined } from '@ant-design/icons-vue'; | 73 | import { PlusOutlined } from '@ant-design/icons-vue'; |
| 74 | import { useDrawer } from '/@/components/Drawer'; | 74 | import { useDrawer } from '/@/components/Drawer'; |
| 75 | - import RoleDrawer from '../../role/RoleDrawer.vue'; | 75 | + import RoleDrawer from '../../role/CustomRoleDrawer.vue'; |
| 76 | import OrganizationDrawer from '/@/views/system/organization/OrganizationDrawer.vue'; | 76 | import OrganizationDrawer from '/@/views/system/organization/OrganizationDrawer.vue'; |
| 77 | 77 | ||
| 78 | export default defineComponent({ | 78 | export default defineComponent({ |
src/views/system/role/CustomRoleDrawer.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <BasicDrawer | ||
| 3 | + v-bind="$attrs" | ||
| 4 | + @register="registerDrawer" | ||
| 5 | + showFooter | ||
| 6 | + :title="getTitle" | ||
| 7 | + width="500px" | ||
| 8 | + @ok="handleSubmit" | ||
| 9 | + > | ||
| 10 | + <BasicForm @register="registerForm"> | ||
| 11 | + <template #menu> | ||
| 12 | + <Spin :spinning="spinning"> | ||
| 13 | + <BasicTree | ||
| 14 | + v-if="treeData.length" | ||
| 15 | + checkable | ||
| 16 | + toolbar | ||
| 17 | + ref="treeRef" | ||
| 18 | + :treeData="treeData" | ||
| 19 | + @check="handleCheckClick" | ||
| 20 | + :replace-fields="{ title: 'name', key: 'id' }" | ||
| 21 | + :checkedKeys="roleMenus" | ||
| 22 | + title="菜单分配" | ||
| 23 | + /> | ||
| 24 | + </Spin> | ||
| 25 | + </template> | ||
| 26 | + </BasicForm> | ||
| 27 | + </BasicDrawer> | ||
| 28 | +</template> | ||
| 29 | +<script lang="ts"> | ||
| 30 | + import { defineComponent, ref, computed, unref, nextTick } from 'vue'; | ||
| 31 | + import { BasicForm, useForm } from '/@/components/Form/index'; | ||
| 32 | + import { formSchema, KeysTypeEnum, RoleMenuDictEnum } from './role.data'; | ||
| 33 | + import { BasicDrawer, useDrawerInner } from '/@/components/Drawer'; | ||
| 34 | + import { BasicTree, TreeActionType, TreeItem } from '/@/components/Tree'; | ||
| 35 | + import { useMessage } from '/@/hooks/web/useMessage'; | ||
| 36 | + const { t } = useI18n(); // 加载国际化 | ||
| 37 | + // 加载菜单数据 | ||
| 38 | + import { getMeMenuList, getMenusIdsByRoleId } from '/@/api/sys/menu'; | ||
| 39 | + import { useI18n } from '/@/hooks/web/useI18n'; | ||
| 40 | + import { MenuRecord } from '/@/api/sys/model/menuModel'; | ||
| 41 | + import { saveOrUpdateRoleInfoWithMenu } from '/@/api/system/system'; | ||
| 42 | + import { findDictItemByCode } from '/@/api/system/dict'; | ||
| 43 | + import { RoleEnum } from '/@/enums/roleEnum'; | ||
| 44 | + import { Spin } from 'ant-design-vue'; | ||
| 45 | + import { useRole } from '/@/hooks/business/useRole'; | ||
| 46 | + import { RoleListItem } from '/@/api/system/model/systemModel'; | ||
| 47 | + | ||
| 48 | + type TreeData = MenuRecord & TreeItem; | ||
| 49 | + | ||
| 50 | + export default defineComponent({ | ||
| 51 | + name: 'RoleDrawer', | ||
| 52 | + components: { BasicDrawer, BasicForm, BasicTree, Spin }, | ||
| 53 | + emits: ['success', 'register'], | ||
| 54 | + setup(_, { emit }) { | ||
| 55 | + const isUpdate = ref<boolean>(true); | ||
| 56 | + const treeData = ref<TreeData[]>([]); | ||
| 57 | + const roleMenus = ref<string[]>([]); | ||
| 58 | + const roleId = ref<string>(''); | ||
| 59 | + const treeRef = ref<Nullable<TreeActionType>>(); | ||
| 60 | + const checked = ref<string[]>([]); //需要选中的节点 | ||
| 61 | + const spinning = ref(false); | ||
| 62 | + const checkedKeysWithHalfChecked = ref<string[]>([]); | ||
| 63 | + | ||
| 64 | + const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({ | ||
| 65 | + labelWidth: 100, | ||
| 66 | + schemas: formSchema, | ||
| 67 | + showActionButtonGroup: false, | ||
| 68 | + }); | ||
| 69 | + | ||
| 70 | + const transformName = (data: TreeData[]) => { | ||
| 71 | + return data.map((item) => { | ||
| 72 | + item.name = t(item.name); | ||
| 73 | + if (item.children && item.children.length) { | ||
| 74 | + item.children = transformName(item.children as unknown as TreeData[]); | ||
| 75 | + } | ||
| 76 | + return item; | ||
| 77 | + }); | ||
| 78 | + }; | ||
| 79 | + | ||
| 80 | + const { isTenantAdmin, isSysadmin, getRole } = useRole(); | ||
| 81 | + const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner( | ||
| 82 | + async (data: { isUpdate: boolean; record: RoleListItem }) => { | ||
| 83 | + resetFields(); | ||
| 84 | + roleId.value = ''; | ||
| 85 | + // 在打开弹窗时清除所有选择的菜单 | ||
| 86 | + treeRef.value && treeRef.value?.setCheckedKeys([]); | ||
| 87 | + isUpdate.value = data.isUpdate; | ||
| 88 | + let roleType = unref(getRole) || RoleEnum.SYS_ADMIN; | ||
| 89 | + | ||
| 90 | + // 租户管理员创建角色时 菜单分配为客户菜单 | ||
| 91 | + if (unref(isTenantAdmin)) { | ||
| 92 | + roleType = RoleEnum.CUSTOMER_USER; | ||
| 93 | + } | ||
| 94 | + | ||
| 95 | + try { | ||
| 96 | + spinning.value = true; | ||
| 97 | + // 需要在setFieldsValue之前先填充treeData,否则Tree组件可能会报key not exist警告 | ||
| 98 | + | ||
| 99 | + // if (!unref(treeData).length) { | ||
| 100 | + // 获取全部的菜单 | ||
| 101 | + const menuListModel = await getMeMenuList(); | ||
| 102 | + treeData.value = transformName(menuListModel as unknown as TreeData[]); | ||
| 103 | + // } | ||
| 104 | + | ||
| 105 | + const keys = await getPermissionByRole(roleType); | ||
| 106 | + const { keyType } = RoleMenuDictEnum[roleType]; | ||
| 107 | + treeData.value = filterPermissionTreeData( | ||
| 108 | + unref(treeData) as unknown as TreeData[], | ||
| 109 | + keys, | ||
| 110 | + keyType | ||
| 111 | + ); | ||
| 112 | + | ||
| 113 | + // 如果编辑的是超级管理员 则不再过滤平台管理员禁用的权限 | ||
| 114 | + // 如果是超级管理员创建角色 创建角色属于平台管理员 因此过滤平台管理员禁用的权限 | ||
| 115 | + if (data?.record?.roleType !== RoleEnum.SYS_ADMIN && unref(isSysadmin)) { | ||
| 116 | + const keys = await getPermissionByRole(RoleEnum.PLATFORM_ADMIN); | ||
| 117 | + const { keyType } = RoleMenuDictEnum[RoleEnum.PLATFORM_ADMIN]; | ||
| 118 | + treeData.value = filterPermissionTreeData( | ||
| 119 | + unref(treeData) as unknown as TreeData[], | ||
| 120 | + keys, | ||
| 121 | + keyType | ||
| 122 | + ); | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + // 更新 | ||
| 126 | + if (unref(isUpdate)) { | ||
| 127 | + checked.value = []; | ||
| 128 | + roleId.value = data.record.id; | ||
| 129 | + | ||
| 130 | + //通过角色id去获取角色对应的菜单的ids | ||
| 131 | + checkedKeysWithHalfChecked.value = roleMenus.value = await getMenusIdsByRoleId( | ||
| 132 | + data.record.id | ||
| 133 | + ); | ||
| 134 | + excludeHalfCheckedKeys(unref(treeData)); | ||
| 135 | + await nextTick(); | ||
| 136 | + unref(treeRef)?.setCheckedKeys(roleMenus.value); | ||
| 137 | + setFieldsValue(data.record); | ||
| 138 | + } else { | ||
| 139 | + } | ||
| 140 | + } catch (error) { | ||
| 141 | + throw error; | ||
| 142 | + } finally { | ||
| 143 | + spinning.value = false; | ||
| 144 | + } | ||
| 145 | + } | ||
| 146 | + ); | ||
| 147 | + | ||
| 148 | + const getTitle = computed(() => (!unref(isUpdate) ? '新增角色' : '编辑角色')); | ||
| 149 | + | ||
| 150 | + async function handleSubmit() { | ||
| 151 | + setDrawerProps({ confirmLoading: true }); | ||
| 152 | + const { createMessage } = useMessage(); | ||
| 153 | + try { | ||
| 154 | + const values = await validate(); | ||
| 155 | + const treeCheckedKeys: string[] = (unref(treeRef)?.getCheckedKeys() as string[]) || []; | ||
| 156 | + const menu = [...new Set([...unref(checkedKeysWithHalfChecked), ...treeCheckedKeys])]; | ||
| 157 | + const req = { | ||
| 158 | + id: roleId.value, | ||
| 159 | + name: values.name, | ||
| 160 | + remark: values.remark, | ||
| 161 | + status: values.status, | ||
| 162 | + menu, | ||
| 163 | + }; | ||
| 164 | + if (req.menu == undefined) return createMessage.error('请勾选权限菜单'); | ||
| 165 | + saveOrUpdateRoleInfoWithMenu(req).then(() => { | ||
| 166 | + closeDrawer(); | ||
| 167 | + emit('success'); | ||
| 168 | + createMessage.success(`${unref(isUpdate) ? '编辑' : '新增'}成功`); | ||
| 169 | + }); | ||
| 170 | + } finally { | ||
| 171 | + setTimeout(() => { | ||
| 172 | + setDrawerProps({ confirmLoading: false }); | ||
| 173 | + }, 300); | ||
| 174 | + } | ||
| 175 | + } | ||
| 176 | + | ||
| 177 | + const getPermissionByRole = async (roleType: RoleEnum) => { | ||
| 178 | + try { | ||
| 179 | + const { key } = RoleMenuDictEnum[roleType]; | ||
| 180 | + const res = await findDictItemByCode({ dictCode: key }); | ||
| 181 | + return res.map((item) => item.itemValue); | ||
| 182 | + } catch (error) {} | ||
| 183 | + return []; | ||
| 184 | + }; | ||
| 185 | + | ||
| 186 | + const filterPermissionTreeData = ( | ||
| 187 | + data: MenuRecord[], | ||
| 188 | + permissionKeys: string[], | ||
| 189 | + keysType: KeysTypeEnum | ||
| 190 | + ): TreeData[] => { | ||
| 191 | + const permissionCompare = ( | ||
| 192 | + data: MenuRecord[], | ||
| 193 | + permissionKeys: string[], | ||
| 194 | + keysType: KeysTypeEnum | ||
| 195 | + ): TreeData[] => { | ||
| 196 | + return data.filter((item) => { | ||
| 197 | + item.name = t(item.name); | ||
| 198 | + const findFlag = permissionKeys.includes(item.permission); | ||
| 199 | + | ||
| 200 | + item.show = findFlag ? keysType === KeysTypeEnum.ENABLED : undefined; | ||
| 201 | + | ||
| 202 | + if (item.children && item.children.length) { | ||
| 203 | + if (item.show) return true; | ||
| 204 | + if (item.show === undefined) { | ||
| 205 | + item.children = permissionCompare(item.children, permissionKeys, keysType); | ||
| 206 | + item.show = item.children.some((item) => | ||
| 207 | + keysType === KeysTypeEnum.ENABLED | ||
| 208 | + ? item.show | ||
| 209 | + : item.show === undefined | ||
| 210 | + ? true | ||
| 211 | + : item.show | ||
| 212 | + ); | ||
| 213 | + return item.show; | ||
| 214 | + } | ||
| 215 | + } | ||
| 216 | + | ||
| 217 | + return keysType === KeysTypeEnum.ENABLED | ||
| 218 | + ? item.show | ||
| 219 | + : item.show === undefined | ||
| 220 | + ? true | ||
| 221 | + : item.show; | ||
| 222 | + }) as unknown as TreeData[]; | ||
| 223 | + }; | ||
| 224 | + | ||
| 225 | + return permissionCompare(data, permissionKeys, keysType); | ||
| 226 | + }; | ||
| 227 | + | ||
| 228 | + const excludeHalfCheckedKeys = (treeData: MenuRecord[]) => { | ||
| 229 | + const needExcludeKeys: string[] = []; | ||
| 230 | + const query = (data: MenuRecord[]) => { | ||
| 231 | + data.forEach((item) => { | ||
| 232 | + item.checked = roleMenus.value.includes(item.id); | ||
| 233 | + if (item.children && item.children.length) { | ||
| 234 | + query(item.children); | ||
| 235 | + item.checked = item.children.every((item) => item.checked); | ||
| 236 | + } | ||
| 237 | + if (!item.checked) { | ||
| 238 | + needExcludeKeys.push(item.id); | ||
| 239 | + } | ||
| 240 | + }); | ||
| 241 | + }; | ||
| 242 | + query(treeData); | ||
| 243 | + roleMenus.value = unref(roleMenus).filter((key) => !needExcludeKeys.includes(key)); | ||
| 244 | + return needExcludeKeys; | ||
| 245 | + }; | ||
| 246 | + | ||
| 247 | + const handleCheckClick = (selectedKeys: string[], event: CheckEvent) => { | ||
| 248 | + checkedKeysWithHalfChecked.value = [ | ||
| 249 | + ...selectedKeys, | ||
| 250 | + ...(event.halfCheckedKeys as string[]), | ||
| 251 | + ]; | ||
| 252 | + }; | ||
| 253 | + | ||
| 254 | + return { | ||
| 255 | + spinning, | ||
| 256 | + registerDrawer, | ||
| 257 | + registerForm, | ||
| 258 | + getTitle, | ||
| 259 | + handleSubmit, | ||
| 260 | + treeData, | ||
| 261 | + roleMenus, | ||
| 262 | + treeRef, | ||
| 263 | + handleCheckClick, | ||
| 264 | + }; | ||
| 265 | + }, | ||
| 266 | + }); | ||
| 267 | +</script> | ||
| 268 | + | ||
| 269 | +<style scoped lang="less"> | ||
| 270 | + :deep(.vben-basic-tree) { | ||
| 271 | + width: 100% !important; | ||
| 272 | + } | ||
| 273 | + | ||
| 274 | + :deep(.is-unflod) { | ||
| 275 | + display: none !important; | ||
| 276 | + } | ||
| 277 | + | ||
| 278 | + :deep(.is-flod) { | ||
| 279 | + display: none !important; | ||
| 280 | + } | ||
| 281 | +</style> |