浏览代码

REVIEW 角色管理(设置数据权限)

YunaiV 2 年之前
父节点
当前提交
7b700d875c

+ 2 - 2
src/api/system/permission/index.ts

@@ -22,12 +22,12 @@ export const getRoleMenuList = async (roleId: number) => {
 }
 
 // 赋予角色菜单权限
-export const assignRoleMenuApi = async (data: PermissionAssignRoleMenuReqVO) => {
+export const assignRoleMenu = async (data: PermissionAssignRoleMenuReqVO) => {
   return await request.post({ url: '/system/permission/assign-role-menu', data })
 }
 
 // 赋予角色数据权限
-export const assignRoleDataScopeApi = async (data: PermissionAssignRoleDataScopeReqVO) => {
+export const assignRoleDataScope = async (data: PermissionAssignRoleDataScopeReqVO) => {
   return await request.post({ url: '/system/permission/assign-role-data-scope', data })
 }
 

+ 3 - 0
src/api/system/role/index.ts

@@ -7,6 +7,8 @@ export interface RoleVO {
   sort: number
   status: number
   type: number
+  dataScope: number
+  dataScopeDeptIds: number[]
   createTime: Date
 }
 
@@ -49,6 +51,7 @@ export const updateRoleStatus = async (data: UpdateStatusReqVO) => {
 export const deleteRole = async (id: number) => {
   return await request.delete({ url: '/system/role/delete?id=' + id })
 }
+
 // 导出角色
 export const exportRole = (params) => {
   return request.download({

+ 0 - 174
src/views/system/role/DataPermissionForm.vue

@@ -1,174 +0,0 @@
-<template>
-  <Dialog :title="dialogScopeTitle" v-model="dialogScopeVisible" width="800">
-    <el-form
-      ref="dataPermissionFormRef"
-      :model="dataScopeForm"
-      :inline="true"
-      label-width="80px"
-      v-loading="formLoading"
-    >
-      <el-form-item label="角色名称">
-        <el-tag>{{ dataScopeForm.name }}</el-tag>
-      </el-form-item>
-      <el-form-item label="角色标识">
-        <el-tag>{{ dataScopeForm.code }}</el-tag>
-      </el-form-item>
-      <!-- 分配角色的数据权限对话框 -->
-      <el-form-item label="权限范围">
-        <el-select v-model="dataScopeForm.dataScope">
-          <el-option
-            v-for="item in dataScopeDictDatas"
-            :key="item.value"
-            :label="item.label"
-            :value="item.value"
-          />
-        </el-select>
-      </el-form-item>
-    </el-form>
-    <!-- 分配角色的菜单权限对话框 -->
-    <el-row>
-      <el-col :span="24">
-        <el-form-item
-          label="权限范围"
-          v-if="
-            actionScopeType === 'menu' ||
-            dataScopeForm.dataScope === SystemDataScopeEnum.DEPT_CUSTOM
-          "
-          style="display: flex"
-        >
-          <el-card class="card" shadow="never">
-            <template #header>
-              父子联动(选中父节点,自动选择子节点):
-              <el-switch
-                v-model="checkStrictly"
-                inline-prompt
-                active-text="是"
-                inactive-text="否"
-              />
-              全选/全不选:
-              <el-switch
-                v-model="treeNodeAll"
-                inline-prompt
-                active-text="是"
-                inactive-text="否"
-                @change="handleCheckedTreeNodeAll()"
-              />
-            </template>
-            <el-tree
-              ref="treeRef"
-              node-key="id"
-              show-checkbox
-              :check-strictly="!checkStrictly"
-              :props="defaultProps"
-              :data="treeOptions"
-              empty-text="加载中,请稍后"
-            />
-          </el-card>
-        </el-form-item> </el-col
-    ></el-row>
-
-    <!-- 操作按钮 -->
-    <template #footer>
-      <div class="dialog-footer">
-        <el-button
-          :title="t('action.save')"
-          :loading="actionLoading"
-          @click="submitScope()"
-          type="primary"
-          :disabled="formLoading"
-        >
-          保存
-        </el-button>
-        <el-button
-          :loading="actionLoading"
-          :title="t('dialog.close')"
-          @click="dialogScopeVisible = false"
-          >取 消</el-button
-        >
-      </div>
-    </template>
-  </Dialog>
-</template>
-
-<script setup lang="ts">
-import * as RoleApi from '@/api/system/role'
-import type { ElTree } from 'element-plus'
-import type { FormExpose } from '@/components/Form'
-import { handleTree, defaultProps } from '@/utils/tree'
-import { SystemDataScopeEnum } from '@/utils/constants'
-import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
-import * as DeptApi from '@/api/system/dept'
-import * as PermissionApi from '@/api/system/permission'
-// ========== CRUD 相关 ==========
-const actionLoading = ref(false) // 遮罩层
-const dataPermissionFormRef = ref<FormExpose>() // 表单 Ref
-const { t } = useI18n() // 国际化
-const dialogScopeTitle = ref('菜单权限')
-const dataScopeDictDatas = ref()
-const message = useMessage() // 消息弹窗
-const actionScopeType = ref('')
-// 选项
-const treeNodeAll = ref(false)
-const checkStrictly = ref(true)
-const dialogScopeVisible = ref(false) // 弹窗的是否展示
-const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
-const treeOptions = ref<any[]>([]) // 菜单树形结构
-const treeRef = ref<InstanceType<typeof ElTree>>()
-// ========== 数据权限 ==========
-const dataScopeForm = reactive({
-  id: 0,
-  name: '',
-  code: '',
-  dataScope: 0,
-  checkList: []
-})
-
-/** 打开弹窗 */
-const openModal = async (type: string, row: RoleApi.RoleVO) => {
-  dataScopeForm.id = row.id
-  dataScopeForm.name = row.name
-  dataScopeForm.code = row.code
-  actionScopeType.value = type
-  dialogScopeVisible.value = true
-  const deptRes = await DeptApi.getSimpleDeptList()
-  treeOptions.value = handleTree(deptRes)
-  const role = await RoleApi.getRole(row.id)
-  dataScopeForm.dataScope = role.dataScope
-  if (role.dataScopeDeptIds) {
-    role.dataScopeDeptIds?.forEach((item: any) => {
-      unref(treeRef)?.setChecked(item, true, false)
-    })
-  }
-}
-
-// 保存权限
-const submitScope = async () => {
-  const data = ref<PermissionApi.PermissionAssignRoleDataScopeReqVO>({
-    roleId: dataScopeForm.id,
-    dataScope: dataScopeForm.dataScope,
-    dataScopeDeptIds:
-      dataScopeForm.dataScope !== SystemDataScopeEnum.DEPT_CUSTOM
-        ? []
-        : (treeRef.value!.getCheckedKeys(false) as unknown as Array<number>)
-  })
-  await PermissionApi.assignRoleDataScopeApi(data.value)
-
-  message.success(t('common.updateSuccess'))
-  dialogScopeVisible.value = false
-}
-
-// 全选/全不选
-const handleCheckedTreeNodeAll = () => {
-  treeRef.value!.setCheckedNodes(treeNodeAll.value ? treeOptions.value : [])
-}
-
-const init = () => {
-  dataScopeDictDatas.value = getIntDictOptions(DICT_TYPE.SYSTEM_DATA_SCOPE)
-}
-
-defineExpose({ openModal }) // 提供 openModal 方法,用于打开弹窗
-// ========== 初始化 ==========
-onMounted(() => {
-  init()
-})
-</script>

+ 3 - 10
src/views/system/role/RoleAssignMenuForm.vue

@@ -1,12 +1,6 @@
 <template>
   <Dialog title="菜单权限" v-model="modelVisible">
-    <el-form
-      ref="formRef"
-      :model="formData"
-      :inline="true"
-      label-width="80px"
-      v-loading="formLoading"
-    >
+    <el-form ref="formRef" :model="formData" label-width="80px" v-loading="formLoading">
       <el-form-item label="角色名称">
         <el-tag>{{ formData.name }}</el-tag>
       </el-form-item>
@@ -53,7 +47,6 @@
 <script setup lang="ts">
 import { handleTree, defaultProps } from '@/utils/tree'
 import * as RoleApi from '@/api/system/role'
-import type { ElTree } from 'element-plus'
 import * as MenuApi from '@/api/system/menu'
 import * as PermissionApi from '@/api/system/permission'
 const { t } = useI18n() // 国际化
@@ -70,7 +63,7 @@ const formData = reactive({
 const formRef = ref() // 表单 Ref
 const menuOptions = ref<any[]>([]) // 菜单树形结构
 const menuExpand = ref(false) // 展开/折叠
-const treeRef = ref<InstanceType<typeof ElTree>>() // 树组件 Ref
+const treeRef = ref() // 菜单树组件 Ref
 const treeNodeAll = ref(false) // 全选/全不选
 
 /** 打开弹窗 */
@@ -112,7 +105,7 @@ const submitForm = async () => {
         ...(treeRef.value.getHalfCheckedKeys() as unknown as Array<number>) // 获得半选中的父节点
       ]
     }
-    await PermissionApi.assignRoleMenuApi(data)
+    await PermissionApi.assignRoleMenu(data)
     message.success(t('common.updateSuccess'))
     modelVisible.value = false
   } finally {

+ 161 - 0
src/views/system/role/RoleDataPermissionForm.vue

@@ -0,0 +1,161 @@
+<template>
+  <Dialog title="菜单权限" v-model="modelVisible" width="800">
+    <el-form ref="formRef" :model="formData" label-width="80px" v-loading="formLoading">
+      <el-form-item label="角色名称">
+        <el-tag>{{ formData.name }}</el-tag>
+      </el-form-item>
+      <el-form-item label="角色标识">
+        <el-tag>{{ formData.code }}</el-tag>
+      </el-form-item>
+      <el-form-item label="权限范围">
+        <el-select v-model="formData.dataScope">
+          <el-option
+            v-for="item in getIntDictOptions(DICT_TYPE.SYSTEM_DATA_SCOPE)"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value"
+          />
+        </el-select>
+      </el-form-item>
+    </el-form>
+    <el-form-item
+      label="权限范围"
+      v-if="formData.dataScope === SystemDataScopeEnum.DEPT_CUSTOM"
+      style="display: flex"
+    >
+      <el-card class="card" shadow="never">
+        <template #header>
+          全选/全不选:
+          <el-switch
+            v-model="treeNodeAll"
+            inline-prompt
+            active-text="是"
+            inactive-text="否"
+            @change="handleCheckedTreeNodeAll()"
+          />
+          全部展开/折叠:
+          <el-switch
+            v-model="deptExpand"
+            inline-prompt
+            active-text="展开"
+            inactive-text="折叠"
+            @change="handleCheckedTreeExpand"
+          />
+          父子联动(选中父节点,自动选择子节点):
+          <el-switch v-model="checkStrictly" inline-prompt active-text="是" inactive-text="否" />
+        </template>
+        <el-tree
+          ref="treeRef"
+          node-key="id"
+          show-checkbox
+          :check-strictly="!checkStrictly"
+          :props="defaultProps"
+          :data="deptOptions"
+          empty-text="加载中,请稍后"
+          default-expand-all
+        />
+      </el-card>
+    </el-form-item>
+    <template #footer>
+      <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
+      <el-button @click="modelVisible = false">取 消</el-button>
+    </template>
+  </Dialog>
+</template>
+<script setup lang="ts">
+import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
+import { handleTree, defaultProps } from '@/utils/tree'
+import { SystemDataScopeEnum } from '@/utils/constants'
+import * as RoleApi from '@/api/system/role'
+import * as DeptApi from '@/api/system/dept'
+import * as PermissionApi from '@/api/system/permission'
+const { t } = useI18n() // 国际化
+const message = useMessage() // 消息弹窗
+
+const modelVisible = ref(false) // 弹窗的是否展示
+const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
+const formData = reactive({
+  id: 0,
+  name: '',
+  code: '',
+  dataScope: undefined,
+  dataScopeDeptIds: []
+})
+const formRef = ref() // 表单 Ref
+const deptOptions = ref<any[]>([]) // 部门树形结构
+const deptExpand = ref(false) // 展开/折叠
+const treeRef = ref() // 菜单树组件 Ref
+const treeNodeAll = ref(false) // 全选/全不选
+const checkStrictly = ref(true) // 是否严格模式,即父子不关联
+
+/** 打开弹窗 */
+const open = async (row: RoleApi.RoleVO) => {
+  modelVisible.value = true
+  resetForm()
+  // 加载 Dept 列表。注意,必须放在前面,不然下面 setChecked 没数据节点
+  deptOptions.value = handleTree(await DeptApi.getSimpleDeptList())
+  // 设置数据
+  formData.id = row.id
+  formData.name = row.name
+  formData.code = row.code
+  formData.dataScope = row.dataScope
+  row.dataScopeDeptIds?.forEach((deptId: number) => {
+    treeRef.value.setChecked(deptId, true, false)
+  })
+}
+defineExpose({ open }) // 提供 open 方法,用于打开弹窗
+
+/** 提交表单 */
+const submitForm = async () => {
+  formLoading.value = true
+  try {
+    const data = {
+      roleId: formData.id,
+      dataScope: formData.dataScope,
+      dataScopeDeptIds:
+        formData.dataScope !== SystemDataScopeEnum.DEPT_CUSTOM
+          ? []
+          : treeRef.value.getCheckedKeys(false)
+    }
+    await PermissionApi.assignRoleDataScope(data)
+    message.success(t('common.updateSuccess'))
+    modelVisible.value = false
+  } finally {
+    formLoading.value = false
+  }
+}
+
+/** 重置表单 */
+const resetForm = () => {
+  // 重置选项
+  treeNodeAll.value = false
+  deptExpand.value = false
+  checkStrictly.value = true
+  // 重置表单
+  formData.value = {
+    id: 0,
+    name: '',
+    code: '',
+    dataScope: undefined,
+    dataScopeDeptIds: []
+  }
+  treeRef.value?.setCheckedNodes([])
+  formRef.value?.resetFields()
+}
+
+/** 全选/全不选 */
+const handleCheckedTreeNodeAll = () => {
+  treeRef.value.setCheckedNodes(treeNodeAll.value ? deptOptions.value : [])
+}
+
+/** 展开/折叠全部 */
+const handleCheckedTreeExpand = () => {
+  const nodes = treeRef.value?.store.nodesMap
+  for (let node in nodes) {
+    if (nodes[node].expanded === deptExpand.value) {
+      continue
+    }
+    nodes[node].expanded = deptExpand.value
+  }
+}
+</script>

+ 6 - 11
src/views/system/role/index.vue

@@ -118,7 +118,7 @@
             preIcon="ep:coin"
             title="数据权限"
             v-hasPermi="['system:permission:assign-role-data-scope']"
-            @click="handleScope('data', scope.row)"
+            @click="openDataPermissionForm(scope.row)"
           >
             数据权限
           </el-button>
@@ -145,9 +145,9 @@
   <!-- 表单弹窗:添加/修改 -->
   <RoleForm ref="formRef" @success="getList" />
   <!-- 表单弹窗:菜单权限 -->
-  <RoleAssignMenuForm ref="assignMenuFormRef" @success="getList" />
+  <RoleAssignMenuForm ref="assignMenuFormRef" />
   <!-- 表单弹窗:数据权限 -->
-  <DataPermissionForm ref="dataPermissionFormRef" @success="getList" />
+  <RoleDataPermissionForm ref="dataPermissionFormRef" />
 </template>
 <script setup lang="tsx">
 import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
@@ -156,7 +156,7 @@ import download from '@/utils/download'
 import * as RoleApi from '@/api/system/role'
 import RoleForm from './RoleForm.vue'
 import RoleAssignMenuForm from './RoleAssignMenuForm.vue'
-import DataPermissionForm from './DataPermissionForm.vue'
+import RoleDataPermissionForm from './RoleDataPermissionForm.vue'
 const message = useMessage() // 消息弹窗
 const { t } = useI18n() // 国际化
 
@@ -206,13 +206,8 @@ const openForm = (type: string, id?: number) => {
 
 /** 数据权限操作 */
 const dataPermissionFormRef = ref()
-
-const handleScope = async (type: string, row: RoleApi.RoleVO) => {
-  if (type === 'menu') {
-    assignMenuFormRef.value.openModal(type, row)
-  } else if (type === 'data') {
-    dataPermissionFormRef.value.openModal(type, row)
-  }
+const openDataPermissionForm = async (row: RoleApi.RoleVO) => {
+  dataPermissionFormRef.value.open(row)
 }
 
 /** 菜单权限操作 */