RoleAssignMenuForm.vue 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. <template>
  2. <Dialog title="菜单权限" v-model="modelVisible">
  3. <el-form
  4. ref="formRef"
  5. :model="formData"
  6. :inline="true"
  7. label-width="80px"
  8. v-loading="formLoading"
  9. >
  10. <el-form-item label="角色名称">
  11. <el-tag>{{ formData.name }}</el-tag>
  12. </el-form-item>
  13. <el-form-item label="角色标识">
  14. <el-tag>{{ formData.code }}</el-tag>
  15. </el-form-item>
  16. <el-form-item label="菜单权限">
  17. <el-card class="cardHeight">
  18. <template #header>
  19. 全选/全不选:
  20. <el-switch
  21. v-model="treeNodeAll"
  22. inline-prompt
  23. active-text="是"
  24. inactive-text="否"
  25. @change="handleCheckedTreeNodeAll"
  26. />
  27. 全部展开/折叠:
  28. <el-switch
  29. v-model="menuExpand"
  30. inline-prompt
  31. active-text="展开"
  32. inactive-text="折叠"
  33. @change="handleCheckedTreeExpand"
  34. />
  35. </template>
  36. <el-tree
  37. ref="treeRef"
  38. node-key="id"
  39. show-checkbox
  40. :props="defaultProps"
  41. :data="menuOptions"
  42. empty-text="加载中,请稍候"
  43. />
  44. </el-card>
  45. </el-form-item>
  46. </el-form>
  47. <template #footer>
  48. <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
  49. <el-button @click="modelVisible = false">取 消</el-button>
  50. </template>
  51. </Dialog>
  52. </template>
  53. <script setup lang="ts">
  54. import { handleTree, defaultProps } from '@/utils/tree'
  55. import * as RoleApi from '@/api/system/role'
  56. import type { ElTree } from 'element-plus'
  57. import * as MenuApi from '@/api/system/menu'
  58. import * as PermissionApi from '@/api/system/permission'
  59. const { t } = useI18n() // 国际化
  60. const message = useMessage() // 消息弹窗
  61. const modelVisible = ref(false) // 弹窗的是否展示
  62. const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
  63. const formData = reactive({
  64. id: 0,
  65. name: '',
  66. code: '',
  67. menuIds: []
  68. })
  69. const formRef = ref() // 表单 Ref
  70. const menuOptions = ref<any[]>([]) // 菜单树形结构
  71. const menuExpand = ref(false) // 展开/折叠
  72. const treeRef = ref<InstanceType<typeof ElTree>>() // 树组件 Ref
  73. const treeNodeAll = ref(false) // 全选/全不选
  74. /** 打开弹窗 */
  75. const open = async (row: RoleApi.RoleVO) => {
  76. modelVisible.value = true
  77. resetForm()
  78. // 加载 Menu 列表。注意,必须放在前面,不然下面 setChecked 没数据节点
  79. menuOptions.value = handleTree(await MenuApi.getSimpleMenusList())
  80. // 设置数据
  81. formData.id = row.id
  82. formData.name = row.name
  83. formData.code = row.code
  84. formLoading.value = true
  85. try {
  86. formData.value.menuIds = await PermissionApi.getRoleMenuList(row.id)
  87. // 设置选中
  88. formData.value.menuIds.forEach((menuId: number) => {
  89. treeRef.value.setChecked(menuId, true, false)
  90. })
  91. } finally {
  92. formLoading.value = false
  93. }
  94. }
  95. defineExpose({ open }) // 提供 open 方法,用于打开弹窗
  96. /** 提交表单 */
  97. const submitForm = async () => {
  98. // 校验表单
  99. if (!formRef) return
  100. const valid = await formRef.value.validate()
  101. if (!valid) return
  102. // 提交请求
  103. formLoading.value = true
  104. try {
  105. const data = {
  106. roleId: formData.id,
  107. menuIds: [
  108. ...(treeRef.value.getCheckedKeys(false) as unknown as Array<number>), // 获得当前选中节点
  109. ...(treeRef.value.getHalfCheckedKeys() as unknown as Array<number>) // 获得半选中的父节点
  110. ]
  111. }
  112. await PermissionApi.assignRoleMenuApi(data)
  113. message.success(t('common.updateSuccess'))
  114. modelVisible.value = false
  115. } finally {
  116. formLoading.value = false
  117. }
  118. }
  119. /** 重置表单 */
  120. const resetForm = () => {
  121. // 重置选项
  122. treeNodeAll.value = false
  123. menuExpand.value = false
  124. // 重置表单
  125. formData.value = {
  126. id: 0,
  127. name: '',
  128. code: '',
  129. menuIds: []
  130. }
  131. treeRef.value?.setCheckedNodes([])
  132. formRef.value?.resetFields()
  133. }
  134. /** 全选/全不选 */
  135. const handleCheckedTreeNodeAll = () => {
  136. treeRef.value.setCheckedNodes(treeNodeAll.value ? menuOptions.value : [])
  137. }
  138. /** 展开/折叠全部 */
  139. const handleCheckedTreeExpand = () => {
  140. const nodes = treeRef.value?.store.nodesMap
  141. for (let node in nodes) {
  142. if (nodes[node].expanded === menuExpand.value) {
  143. continue
  144. }
  145. nodes[node].expanded = menuExpand.value
  146. }
  147. }
  148. </script>
  149. <style lang="scss" scoped>
  150. .cardHeight {
  151. width: 100%;
  152. max-height: 400px;
  153. overflow-y: scroll;
  154. }
  155. </style>