REVIEW 角色管理(设置菜单权限)
							parent
							
								
									70fa36a262
								
							
						
					
					
						commit
						ecde723fa2
					
				| 
						 | 
				
			
			@ -17,7 +17,7 @@ export interface PermissionAssignRoleDataScopeReqVO {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// 查询角色拥有的菜单权限
 | 
			
		||||
export const listRoleMenusApi = async (roleId: number) => {
 | 
			
		||||
export const getRoleMenuList = async (roleId: number) => {
 | 
			
		||||
  return await request.get({ url: '/system/permission/list-role-resources?roleId=' + roleId })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,152 +0,0 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <Dialog :title="dialogScopeTitle" v-model="dialogScopeVisible" width="800">
 | 
			
		||||
    <el-form
 | 
			
		||||
      ref="menuPermissionFormRef"
 | 
			
		||||
      :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-row>
 | 
			
		||||
        <el-col :span="24">
 | 
			
		||||
          <el-form-item label="权限范围" 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>
 | 
			
		||||
    </el-form>
 | 
			
		||||
    <!-- 操作按钮 -->
 | 
			
		||||
    <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 { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
 | 
			
		||||
import * as MenuApi from '@/api/system/menu'
 | 
			
		||||
import * as PermissionApi from '@/api/system/permission'
 | 
			
		||||
// ========== CRUD 相关 ==========
 | 
			
		||||
const actionLoading = ref(false) // 遮罩层
 | 
			
		||||
const menuPermissionFormRef = ref<FormExpose>() // 表单 Ref
 | 
			
		||||
const { t } = useI18n() // 国际化
 | 
			
		||||
const dialogScopeTitle = ref('菜单权限')
 | 
			
		||||
const dataScopeDictDatas = ref()
 | 
			
		||||
const message = useMessage() // 消息弹窗
 | 
			
		||||
const actionScopeType = ref('')
 | 
			
		||||
// 选项
 | 
			
		||||
const checkStrictly = ref(true)
 | 
			
		||||
const treeNodeAll = ref(false)
 | 
			
		||||
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 menuRes = await MenuApi.getSimpleMenusList()
 | 
			
		||||
  treeOptions.value = handleTree(menuRes)
 | 
			
		||||
  const role = await PermissionApi.listRoleMenusApi(row.id)
 | 
			
		||||
  if (role) {
 | 
			
		||||
    role?.forEach((item: any) => {
 | 
			
		||||
      unref(treeRef)?.setChecked(item, true, false)
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 保存权限
 | 
			
		||||
const submitScope = async () => {
 | 
			
		||||
  const data = ref<PermissionApi.PermissionAssignRoleMenuReqVO>({
 | 
			
		||||
    roleId: dataScopeForm.id,
 | 
			
		||||
    menuIds: [
 | 
			
		||||
      ...(treeRef.value!.getCheckedKeys(false) as unknown as Array<number>),
 | 
			
		||||
      ...(treeRef.value!.getHalfCheckedKeys() as unknown as Array<number>)
 | 
			
		||||
    ]
 | 
			
		||||
  })
 | 
			
		||||
  await PermissionApi.assignRoleMenuApi(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>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,161 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <Dialog title="菜单权限" v-model="modelVisible">
 | 
			
		||||
    <el-form
 | 
			
		||||
      ref="formRef"
 | 
			
		||||
      :model="formData"
 | 
			
		||||
      :inline="true"
 | 
			
		||||
      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-card class="cardHeight">
 | 
			
		||||
          <template #header>
 | 
			
		||||
            全选/全不选:
 | 
			
		||||
            <el-switch
 | 
			
		||||
              v-model="treeNodeAll"
 | 
			
		||||
              inline-prompt
 | 
			
		||||
              active-text="是"
 | 
			
		||||
              inactive-text="否"
 | 
			
		||||
              @change="handleCheckedTreeNodeAll"
 | 
			
		||||
            />
 | 
			
		||||
            全部展开/折叠:
 | 
			
		||||
            <el-switch
 | 
			
		||||
              v-model="menuExpand"
 | 
			
		||||
              inline-prompt
 | 
			
		||||
              active-text="展开"
 | 
			
		||||
              inactive-text="折叠"
 | 
			
		||||
              @change="handleCheckedTreeExpand"
 | 
			
		||||
            />
 | 
			
		||||
          </template>
 | 
			
		||||
          <el-tree
 | 
			
		||||
            ref="treeRef"
 | 
			
		||||
            node-key="id"
 | 
			
		||||
            show-checkbox
 | 
			
		||||
            :props="defaultProps"
 | 
			
		||||
            :data="menuOptions"
 | 
			
		||||
            empty-text="加载中,请稍候"
 | 
			
		||||
          />
 | 
			
		||||
        </el-card>
 | 
			
		||||
      </el-form-item>
 | 
			
		||||
    </el-form>
 | 
			
		||||
    <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 { 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() // 国际化
 | 
			
		||||
const message = useMessage() // 消息弹窗
 | 
			
		||||
 | 
			
		||||
const modelVisible = ref(false) // 弹窗的是否展示
 | 
			
		||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
 | 
			
		||||
const formData = reactive({
 | 
			
		||||
  id: 0,
 | 
			
		||||
  name: '',
 | 
			
		||||
  code: '',
 | 
			
		||||
  menuIds: []
 | 
			
		||||
})
 | 
			
		||||
const formRef = ref() // 表单 Ref
 | 
			
		||||
const menuOptions = ref<any[]>([]) // 菜单树形结构
 | 
			
		||||
const menuExpand = ref(false) // 展开/折叠
 | 
			
		||||
const treeRef = ref<InstanceType<typeof ElTree>>() // 树组件 Ref
 | 
			
		||||
const treeNodeAll = ref(false) // 全选/全不选
 | 
			
		||||
 | 
			
		||||
/** 打开弹窗 */
 | 
			
		||||
const open = async (row: RoleApi.RoleVO) => {
 | 
			
		||||
  modelVisible.value = true
 | 
			
		||||
  resetForm()
 | 
			
		||||
  // 加载 Menu 列表。注意,必须放在前面,不然下面 setChecked 没数据节点
 | 
			
		||||
  menuOptions.value = handleTree(await MenuApi.getSimpleMenusList())
 | 
			
		||||
  // 设置数据
 | 
			
		||||
  formData.id = row.id
 | 
			
		||||
  formData.name = row.name
 | 
			
		||||
  formData.code = row.code
 | 
			
		||||
  formLoading.value = true
 | 
			
		||||
  try {
 | 
			
		||||
    formData.value.menuIds = await PermissionApi.getRoleMenuList(row.id)
 | 
			
		||||
    // 设置选中
 | 
			
		||||
    formData.value.menuIds.forEach((menuId: number) => {
 | 
			
		||||
      treeRef.value.setChecked(menuId, true, false)
 | 
			
		||||
    })
 | 
			
		||||
  } finally {
 | 
			
		||||
    formLoading.value = false
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
 | 
			
		||||
 | 
			
		||||
/** 提交表单 */
 | 
			
		||||
const submitForm = async () => {
 | 
			
		||||
  // 校验表单
 | 
			
		||||
  if (!formRef) return
 | 
			
		||||
  const valid = await formRef.value.validate()
 | 
			
		||||
  if (!valid) return
 | 
			
		||||
  // 提交请求
 | 
			
		||||
  formLoading.value = true
 | 
			
		||||
  try {
 | 
			
		||||
    const data = {
 | 
			
		||||
      roleId: formData.id,
 | 
			
		||||
      menuIds: [
 | 
			
		||||
        ...(treeRef.value.getCheckedKeys(false) as unknown as Array<number>), // 获得当前选中节点
 | 
			
		||||
        ...(treeRef.value.getHalfCheckedKeys() as unknown as Array<number>) // 获得半选中的父节点
 | 
			
		||||
      ]
 | 
			
		||||
    }
 | 
			
		||||
    await PermissionApi.assignRoleMenuApi(data)
 | 
			
		||||
    message.success(t('common.updateSuccess'))
 | 
			
		||||
    modelVisible.value = false
 | 
			
		||||
  } finally {
 | 
			
		||||
    formLoading.value = false
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 重置表单 */
 | 
			
		||||
const resetForm = () => {
 | 
			
		||||
  // 重置选项
 | 
			
		||||
  treeNodeAll.value = false
 | 
			
		||||
  menuExpand.value = false
 | 
			
		||||
  // 重置表单
 | 
			
		||||
  formData.value = {
 | 
			
		||||
    id: 0,
 | 
			
		||||
    name: '',
 | 
			
		||||
    code: '',
 | 
			
		||||
    menuIds: []
 | 
			
		||||
  }
 | 
			
		||||
  treeRef.value?.setCheckedNodes([])
 | 
			
		||||
  formRef.value?.resetFields()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 全选/全不选 */
 | 
			
		||||
const handleCheckedTreeNodeAll = () => {
 | 
			
		||||
  treeRef.value.setCheckedNodes(treeNodeAll.value ? menuOptions.value : [])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 展开/折叠全部 */
 | 
			
		||||
const handleCheckedTreeExpand = () => {
 | 
			
		||||
  const nodes = treeRef.value?.store.nodesMap
 | 
			
		||||
  for (let node in nodes) {
 | 
			
		||||
    if (nodes[node].expanded === menuExpand.value) {
 | 
			
		||||
      continue
 | 
			
		||||
    }
 | 
			
		||||
    nodes[node].expanded = menuExpand.value
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.cardHeight {
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  max-height: 400px;
 | 
			
		||||
  overflow-y: scroll;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			@ -108,7 +108,7 @@
 | 
			
		|||
            preIcon="ep:basketball"
 | 
			
		||||
            title="菜单权限"
 | 
			
		||||
            v-hasPermi="['system:permission:assign-role-menu']"
 | 
			
		||||
            @click="handleScope('menu', scope.row)"
 | 
			
		||||
            @click="openAssignMenuForm(scope.row)"
 | 
			
		||||
          >
 | 
			
		||||
            菜单权限
 | 
			
		||||
          </el-button>
 | 
			
		||||
| 
						 | 
				
			
			@ -145,18 +145,18 @@
 | 
			
		|||
  <!-- 表单弹窗:添加/修改 -->
 | 
			
		||||
  <RoleForm ref="formRef" @success="getList" />
 | 
			
		||||
  <!-- 表单弹窗:菜单权限 -->
 | 
			
		||||
  <MenuPermissionForm ref="menuPermissionFormRef" @success="getList" />
 | 
			
		||||
  <RoleAssignMenuForm ref="assignMenuFormRef" @success="getList" />
 | 
			
		||||
  <!-- 表单弹窗:数据权限 -->
 | 
			
		||||
  <DataPermissionForm ref="dataPermissionFormRef" @success="getList" />
 | 
			
		||||
</template>
 | 
			
		||||
<script setup lang="tsx">
 | 
			
		||||
import * as RoleApi from '@/api/system/role'
 | 
			
		||||
import RoleForm from './RoleForm.vue'
 | 
			
		||||
import MenuPermissionForm from './MenuPermissionForm.vue'
 | 
			
		||||
import DataPermissionForm from './DataPermissionForm.vue'
 | 
			
		||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
 | 
			
		||||
import { dateFormatter } from '@/utils/formatTime'
 | 
			
		||||
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'
 | 
			
		||||
const message = useMessage() // 消息弹窗
 | 
			
		||||
const { t } = useI18n() // 国际化
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -204,19 +204,23 @@ const openForm = (type: string, id?: number) => {
 | 
			
		|||
  formRef.value.open(type, id)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 菜单权限操作 */
 | 
			
		||||
const menuPermissionFormRef = ref()
 | 
			
		||||
/** 数据权限操作 */
 | 
			
		||||
const dataPermissionFormRef = ref()
 | 
			
		||||
 | 
			
		||||
const handleScope = async (type: string, row: RoleApi.RoleVO) => {
 | 
			
		||||
  if (type === 'menu') {
 | 
			
		||||
    menuPermissionFormRef.value.openModal(type, row)
 | 
			
		||||
    assignMenuFormRef.value.openModal(type, row)
 | 
			
		||||
  } else if (type === 'data') {
 | 
			
		||||
    dataPermissionFormRef.value.openModal(type, row)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 菜单权限操作 */
 | 
			
		||||
const assignMenuFormRef = ref()
 | 
			
		||||
const openAssignMenuForm = async (row: RoleApi.RoleVO) => {
 | 
			
		||||
  assignMenuFormRef.value.open(row)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 删除按钮操作 */
 | 
			
		||||
const handleDelete = async (id: number) => {
 | 
			
		||||
  try {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -64,11 +64,10 @@
 | 
			
		|||
<script setup lang="ts" name="TenantPackageForm">
 | 
			
		||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
 | 
			
		||||
import { CommonStatusEnum } from '@/utils/constants'
 | 
			
		||||
import { defaultProps } from '@/utils/tree'
 | 
			
		||||
import { defaultProps, handleTree } from '@/utils/tree'
 | 
			
		||||
import * as TenantPackageApi from '@/api/system/tenantPackage'
 | 
			
		||||
import * as MenuApi from '@/api/system/menu'
 | 
			
		||||
import { ElTree } from 'element-plus'
 | 
			
		||||
import { handleTree } from '@/utils/tree'
 | 
			
		||||
const { t } = useI18n() // 国际化
 | 
			
		||||
const message = useMessage() // 消息弹窗
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue