feat(system/role): 新增查询角色权限树列表接口(替换角色分配权限的菜单树列表接口)

This commit is contained in:
2025-07-27 12:44:23 +08:00
parent d7937e7905
commit 87e6deab4d
4 changed files with 48 additions and 44 deletions

View File

@@ -31,6 +31,11 @@ export function deleteRole(id: string) {
return http.del(`${BASE_URL}`, { ids: [id] }) return http.del(`${BASE_URL}`, { ids: [id] })
} }
/** @desc 查询角色权限树 */
export function listRolePermissionTree() {
return http.get<T.RolePermissionResp[]>(`${BASE_URL}/permission/tree`)
}
/** @desc 修改角色权限 */ /** @desc 修改角色权限 */
export function updateRolePermission(id: string, data: any) { export function updateRolePermission(id: string, data: any) {
return http.put(`${BASE_URL}/${id}/permission`, data) return http.put(`${BASE_URL}/${id}/permission`, data)

View File

@@ -63,6 +63,15 @@ export type RoleDetailResp = RoleResp & {
menuCheckStrictly: boolean menuCheckStrictly: boolean
deptCheckStrictly: boolean deptCheckStrictly: boolean
} }
export interface RolePermissionResp {
id: string
title: string
parentId: string
permission?: string
children?: RolePermissionResp[]
permissions?: RolePermissionResp[]
isChecked?: boolean
}
export interface RoleUserResp { export interface RoleUserResp {
id: string id: string
username: string username: string

View File

@@ -96,7 +96,7 @@
import type { TableInstance } from '@arco-design/web-vue' import type { TableInstance } from '@arco-design/web-vue'
import { Message, Modal } from '@arco-design/web-vue' import { Message, Modal } from '@arco-design/web-vue'
import MenuAddModal from './MenuAddModal.vue' import MenuAddModal from './MenuAddModal.vue'
import { type MenuQuery, type MenuResp, clearMenuCache, deleteMenu, listMenu } from '@/apis/system/menu' import { type MenuQuery, type MenuResp, clearMenuCache, deleteMenu, listMenuDictTree } from '@/apis/system/menu'
import type GiTable from '@/components/GiTable/index.vue' import type GiTable from '@/components/GiTable/index.vue'
import { useTable } from '@/hooks' import { useTable } from '@/hooks'
import { isMobile } from '@/utils' import { isMobile } from '@/utils'
@@ -111,7 +111,7 @@ const {
loading, loading,
search, search,
handleDelete, handleDelete,
} = useTable(() => listMenu(queryForm), { immediate: true }) } = useTable(() => listMenuDictTree({ description: queryForm.description || '' }), { immediate: true })
// 过滤树 // 过滤树
const searchData = (title: string, path: string, permission: string) => { const searchData = (title: string, path: string, permission: string) => {

View File

@@ -56,12 +56,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { nextTick, ref, watch } from 'vue' import { nextTick, ref, watch } from 'vue'
import { Message, type TableInstance } from '@arco-design/web-vue' import { Message, type TableInstance, type TreeNodeData } from '@arco-design/web-vue'
import { type MenuResp, listMenu } from '@/apis/system/menu'
import { isMobile } from '@/utils' import { isMobile } from '@/utils'
import type GiTable from '@/components/GiTable/index.vue' import type GiTable from '@/components/GiTable/index.vue'
import { useTable } from '@/hooks' import { useTable } from '@/hooks'
import { getRole, updateRolePermission } from '@/apis' import { type RolePermissionResp, getRole, listRolePermissionTree, updateRolePermission } from '@/apis/system/role'
import has from '@/utils/has' import has from '@/utils/has'
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
@@ -72,16 +71,7 @@ interface Props {
roleId: string roleId: string
} }
interface PermissionItem { interface ExtendedRolePermissionResp extends RolePermissionResp {
id: string | number
title: string
parentId: string | number
permission?: string
isChecked?: boolean
}
interface ExtendedMenuResp extends MenuResp {
permissions?: PermissionItem[]
checkedPermissions?: (string | number)[] checkedPermissions?: (string | number)[]
isChecked?: boolean isChecked?: boolean
disabled?: boolean disabled?: boolean
@@ -105,12 +95,12 @@ const onExpanded = () => {
* *
* @param menus 菜单数据 * @param menus 菜单数据
*/ */
const transformMenu = (menus: MenuResp[]): ExtendedMenuResp[] => { const transformMenu = (menus: RolePermissionResp[]): ExtendedRolePermissionResp[] => {
return menus.map((item): ExtendedMenuResp => { return menus.map((item): ExtendedRolePermissionResp => {
// 如果当前项有子项,递归处理子项 // 如果当前项有子项,递归处理子项
if (item.children && item.children.length > 0) { if (item.children && item.children.length > 0) {
// 过滤出 type 为 3 的按钮权限 // 过滤出 permission 不为空的子项
const permissions = item.children.filter((child) => child.type === 3 || child.permission).map((child): PermissionItem => ({ const permissions = item.children.filter((child) => child.permission).map((child): RolePermissionResp => ({
id: child.id, id: child.id,
title: child.title, title: child.title,
parentId: child.parentId, parentId: child.parentId,
@@ -118,13 +108,13 @@ const transformMenu = (menus: MenuResp[]): ExtendedMenuResp[] => {
isChecked: false, isChecked: false,
})) }))
// 过滤出 type 不为 3 的子项 // 过滤出 permission 为空的子项
item.children = item.children.filter((child) => child.type !== 3 && !child.permission) item.children = item.children.filter((child) => !child.permission)
// 如果有权限,将其添加到当前项的 permissions 属性中 // 如果有权限,将其添加到当前项的 permissions 属性中
if (permissions.length > 0) { if (permissions.length > 0) {
(item as ExtendedMenuResp).permissions = permissions (item as ExtendedRolePermissionResp).permissions = permissions
;(item as ExtendedMenuResp).checkedPermissions = permissions.filter((permission) => permission.isChecked).map((permission) => permission.id) ;(item as ExtendedRolePermissionResp).checkedPermissions = permissions.filter((permission) => permission.isChecked).map((permission) => permission.id)
} }
// 递归处理剩余的子项 // 递归处理剩余的子项
@@ -141,7 +131,7 @@ const transformMenu = (menus: MenuResp[]): ExtendedMenuResp[] => {
} }
// 更新表格数据的选中状态 // 更新表格数据的选中状态
const updateTableDataCheckedStatus = (data: ExtendedMenuResp[], selectedKeys: (string | number)[]) => { const updateTableDataCheckedStatus = (data: ExtendedRolePermissionResp[], selectedKeys: (string | number)[]) => {
data.forEach((item) => { data.forEach((item) => {
item.disabled = disabled.value item.disabled = disabled.value
// 设置菜单项的选中状态 // 设置菜单项的选中状态
@@ -157,23 +147,23 @@ const updateTableDataCheckedStatus = (data: ExtendedMenuResp[], selectedKeys: (s
} }
// 递归处理子菜单 // 递归处理子菜单
if (item.children) { if (item.children) {
updateTableDataCheckedStatus(item.children as ExtendedMenuResp[], selectedKeys) updateTableDataCheckedStatus(item.children as ExtendedRolePermissionResp[], selectedKeys)
} }
}) })
} }
// 查找指定菜单 - 使用 Map 缓存优化查找性能 // 查找指定菜单 - 使用 Map 缓存优化查找性能
const menuMap = ref<Map<string | number, ExtendedMenuResp>>(new Map()) const menuMap = ref<Map<string | number, ExtendedRolePermissionResp>>(new Map())
// 构建菜单映射缓存 // 构建菜单映射缓存
const buildMenuMap = (data: ExtendedMenuResp[]) => { const buildMenuMap = (data: ExtendedRolePermissionResp[]) => {
const map = new Map<string | number, ExtendedMenuResp>() const map = new Map<string | number, ExtendedRolePermissionResp>()
const traverse = (items: ExtendedMenuResp[]) => { const traverse = (items: ExtendedRolePermissionResp[]) => {
items.forEach((item) => { items.forEach((item) => {
map.set(item.id, item) map.set(item.id, item)
if (item.children?.length) { if (item.children?.length) {
traverse(item.children as ExtendedMenuResp[]) traverse(item.children as ExtendedRolePermissionResp[])
} }
}) })
} }
@@ -188,9 +178,9 @@ const {
tableData, tableData,
loading, loading,
search, search,
} = useTable(() => listMenu(), { } = useTable(() => listRolePermissionTree(), {
immediate: true, immediate: true,
formatResult(data: MenuResp[]) { formatResult(data: TreeNodeData[]) {
return transformMenu(data) return transformMenu(data)
}, },
onSuccess: () => { onSuccess: () => {
@@ -198,10 +188,10 @@ const {
tableRef.value?.tableRef?.expandAll(true) tableRef.value?.tableRef?.expandAll(true)
}) })
// 构建菜单映射缓存 // 构建菜单映射缓存
buildMenuMap(tableData.value as ExtendedMenuResp[]) buildMenuMap(tableData.value as ExtendedRolePermissionResp[])
// 初始加载时应用已选中的权限 // 初始加载时应用已选中的权限
if (selectedKeys.value.size > 0) { if (selectedKeys.value.size > 0) {
updateTableDataCheckedStatus(tableData.value as ExtendedMenuResp[], Array.from(selectedKeys.value)) updateTableDataCheckedStatus(tableData.value as ExtendedRolePermissionResp[], Array.from(selectedKeys.value))
} }
}, },
}) })
@@ -212,13 +202,13 @@ const columns: TableInstance['columns'] = [
] ]
// 级联选中子项 // 级联选中子项
const cascadeSelectChild = (record: ExtendedMenuResp, isCascade: boolean) => { const cascadeSelectChild = (record: ExtendedRolePermissionResp, isCascade: boolean) => {
if (!isCascade) return if (!isCascade) return
// 批量处理子菜单 // 批量处理子菜单
if (record.children && record.children.length > 0) { if (record.children && record.children.length > 0) {
record.children.forEach((child) => { record.children.forEach((child) => {
const extendedChild = child as ExtendedMenuResp const extendedChild = child as ExtendedRolePermissionResp
extendedChild.isChecked = record.isChecked extendedChild.isChecked = record.isChecked
tableRef.value?.tableRef?.select(child.id, extendedChild.isChecked) tableRef.value?.tableRef?.select(child.id, extendedChild.isChecked)
@@ -253,19 +243,19 @@ const cascadeSelectChild = (record: ExtendedMenuResp, isCascade: boolean) => {
} }
// 查找指定菜单 // 查找指定菜单
const findItem = (id: string | number): ExtendedMenuResp | null => { const findItem = (id: string | number): ExtendedRolePermissionResp | null => {
return menuMap.value.get(id) || null return menuMap.value.get(id) || null
} }
// 级联选中父项目 // 级联选中父项目
const cascadeSelectParent = (record: ExtendedMenuResp, isCascade: boolean) => { const cascadeSelectParent = (record: ExtendedRolePermissionResp, isCascade: boolean) => {
if (!isCascade || !record.parentId || record.parentId === '0') return if (!isCascade || !record.parentId || record.parentId === '0') return
const parent = findItem(record.parentId) const parent = findItem(record.parentId)
if (!parent) return if (!parent) return
// 检查父项目的所有子项是否有被选中的 // 检查父项目的所有子项是否有被选中的
const hasCheckedChildren = parent.children?.some((child) => (child as ExtendedMenuResp).isChecked) const hasCheckedChildren = parent.children?.some((child) => (child as ExtendedRolePermissionResp).isChecked)
parent.isChecked = hasCheckedChildren || false parent.isChecked = hasCheckedChildren || false
// 更新表格选中状态 // 更新表格选中状态
@@ -286,7 +276,7 @@ const cascadeSelectParent = (record: ExtendedMenuResp, isCascade: boolean) => {
// 选中 // 选中
const select: TableInstance['onSelect'] = (rowKeys, checked, record) => { const select: TableInstance['onSelect'] = (rowKeys, checked, record) => {
const extendedRecord = record as ExtendedMenuResp const extendedRecord = record as ExtendedRolePermissionResp
// 如果处于禁用状态,直接返回,不执行任何逻辑 // 如果处于禁用状态,直接返回,不执行任何逻辑
if (disabled.value || extendedRecord.disabled) { if (disabled.value || extendedRecord.disabled) {
@@ -312,7 +302,7 @@ const selectAll: TableInstance['onSelectAll'] = (checked) => {
} }
tableData.value.forEach((item) => { tableData.value.forEach((item) => {
const extendedItem = item as ExtendedMenuResp const extendedItem = item as ExtendedRolePermissionResp
extendedItem.isChecked = checked extendedItem.isChecked = checked
checked checked
? selectedKeys.value.add(item.id) ? selectedKeys.value.add(item.id)
@@ -322,7 +312,7 @@ const selectAll: TableInstance['onSelectAll'] = (checked) => {
} }
// 选中权限 // 选中权限
const selectPermission = (record: ExtendedMenuResp) => { const selectPermission = (record: ExtendedRolePermissionResp) => {
// 如果处于禁用状态,直接返回,不执行任何逻辑 // 如果处于禁用状态,直接返回,不执行任何逻辑
if (disabled.value || record.disabled) { if (disabled.value || record.disabled) {
return return
@@ -384,9 +374,9 @@ const fetchRole = async (id: string) => {
// 更新选中键集合 // 更新选中键集合
selectedKeys.value = new Set(data.menuIds) selectedKeys.value = new Set(data.menuIds)
// 重新构建菜单映射缓存 // 重新构建菜单映射缓存
buildMenuMap(tableData.value as ExtendedMenuResp[]) buildMenuMap(tableData.value as ExtendedRolePermissionResp[])
// 更新表格数据的选中状态 // 更新表格数据的选中状态
updateTableDataCheckedStatus(tableData.value as ExtendedMenuResp[], data.menuIds) updateTableDataCheckedStatus(tableData.value as ExtendedRolePermissionResp[], data.menuIds)
// 手动设置表格行的选中状态,确保组件响应 // 手动设置表格行的选中状态,确保组件响应
await nextTick(() => { await nextTick(() => {
tableRef.value?.tableRef?.selectAll(false) tableRef.value?.tableRef?.selectAll(false)