| | |
| | | NSpace, |
| | | NTabPane, |
| | | NTabs, |
| | | NTree, |
| | | NDataTable, |
| | | } from 'naive-ui' |
| | | import { |
| | |
| | | updatePermissionGroup, |
| | | getMenuList |
| | | } from '@/service' |
| | | |
| | | // 用于生成唯一的key |
| | | let uniqueIdCounter = 0 |
| | | function generateUniqueKey() { |
| | | return `node_${uniqueIdCounter++}` |
| | | } |
| | | |
| | | // 节点映射表,用于通过 key 查找节点信息 |
| | | const nodeMap = reactive(new Map<string, any>()) |
| | | |
| | | const route = useRoute() |
| | | const router = useRouter() |
| | |
| | | const activeTab = ref<'management' | 'operation'>('management') |
| | | const groupType = computed(() => (activeTab.value === 'management' ? 1 : 2)) |
| | | |
| | | // 处理标签页切换 |
| | | // 处理标签页切换 |
| | | function handleTabChange(value: 'management' | 'operation') { |
| | | if (mode.value === 'view') { |
| | | activeTab.value = value |
| | |
| | | } |
| | | |
| | | const currentTab = activeTab.value |
| | | const hasSelected = Object.values(selectedPermissions.value[currentTab] || {}).some(val => val) |
| | | const hasSelected = selectedPermissions[currentTab].checkedKeys.length > 0 || selectedPermissions[currentTab].indeterminateKeys.length > 0 |
| | | |
| | | if (hasSelected) { |
| | | window.$dialog?.warning({ |
| | |
| | | positiveText: '确认切换', |
| | | negativeText: '取消', |
| | | onPositiveClick: () => { |
| | | selectedPermissions.value[currentTab] = {} |
| | | selectedPermissions[currentTab].checkedKeys = [] |
| | | selectedPermissions[currentTab].indeterminateKeys = [] |
| | | activeTab.value = value |
| | | }, |
| | | }) |
| | |
| | | // 虚拟权限数据 - 管理端 |
| | | const managementPermissions = ref<any[]>([]) |
| | | |
| | | |
| | | // 转换树数据,为每个节点生成唯一 key |
| | | function transformTreeData(nodes: any[]): any[] { |
| | | return nodes.map(node => { |
| | | const key = generateUniqueKey() |
| | | const newNode = { |
| | | ...node, |
| | | key, // 使用 key 属性作为唯一标识 |
| | | children: node.children ? transformTreeData(node.children) : undefined, |
| | | } |
| | | // 存储节点映射 |
| | | nodeMap.set(key, newNode) |
| | | return newNode |
| | | }) |
| | | } |
| | | |
| | | // 虚拟权限数据 - 运维APP |
| | | const operationPermissions = ref([ |
| | | const operationPermissions = ref(transformTreeData([ |
| | | { |
| | | id: '10', |
| | | menuName: '车辆管理', |
| | |
| | | }, |
| | | ], |
| | | }, |
| | | ]) |
| | | ])) |
| | | |
| | | // 当前权限数据 |
| | | const currentPermissions = computed(() => { |
| | | return activeTab.value === 'management' ? managementPermissions.value : operationPermissions.value |
| | | }) |
| | | |
| | | // 选中的权限 |
| | | const selectedPermissions = ref<Record<string, any>>({ |
| | | management: {}, |
| | | operation: {}, |
| | | // 详情模式下的权限数据(禁用所有节点,但允许展开查看) |
| | | const viewPermissions = computed(() => { |
| | | const disableNodes = (nodes: any[]): any[] => { |
| | | return nodes.map(node => ({ |
| | | ...node, |
| | | disabled: true, |
| | | children: node.children ? disableNodes(node.children) : undefined, |
| | | })) |
| | | } |
| | | return disableNodes(currentPermissions.value) |
| | | }) |
| | | |
| | | // 选中的权限 |
| | | const selectedPermissions = reactive({ |
| | | management: { |
| | | checkedKeys: [] as Array<string | number>, |
| | | indeterminateKeys: [] as Array<string | number>, |
| | | }, |
| | | operation: { |
| | | checkedKeys: [] as Array<string | number>, |
| | | indeterminateKeys: [] as Array<string | number>, |
| | | }, |
| | | }) |
| | | |
| | | // 过滤出叶子节点的ID,用于回显 |
| | | function filterLeafKeys(menuIds: (string | number)[], treeData: any[]) { |
| | | const leafKeys: string[] = [] |
| | | const idSet = new Set(menuIds.map(String)) |
| | | |
| | | function traverse(nodes: any[]) { |
| | | nodes.forEach((node) => { |
| | | if (!node.children || node.children.length === 0) { |
| | | if (idSet.has(String(node.id))) { |
| | | // 如果节点ID匹配,使用生成的唯一 key |
| | | leafKeys.push(node.key) |
| | | } |
| | | } |
| | | else { |
| | | traverse(node.children) |
| | | } |
| | | }) |
| | | } |
| | | traverse(treeData) |
| | | return leafKeys |
| | | } |
| | | |
| | | // 计算树节点的选中状态(全选/半选) |
| | | function getTreeStatus(leafKeys: string[], treeData: any[]) { |
| | | const checkedKeys: string[] = [] |
| | | const indeterminateKeys: string[] = [] |
| | | const leafSet = new Set(leafKeys) |
| | | |
| | | function processNode(node: any): number { |
| | | if (!node.children || node.children.length === 0) { |
| | | const isChecked = leafSet.has(node.key) |
| | | if (isChecked) { |
| | | checkedKeys.push(node.key) |
| | | return 1 |
| | | } |
| | | return 0 |
| | | } |
| | | |
| | | let hasChecked = false |
| | | let hasUnchecked = false |
| | | let hasIndeterminate = false |
| | | |
| | | for (const child of node.children) { |
| | | const status = processNode(child) |
| | | if (status === 1) hasChecked = true |
| | | else if (status === 0) hasUnchecked = true |
| | | else hasIndeterminate = true |
| | | } |
| | | |
| | | if (hasIndeterminate || (hasChecked && hasUnchecked)) { |
| | | indeterminateKeys.push(node.key) |
| | | return 2 |
| | | } |
| | | else if (hasChecked) { |
| | | checkedKeys.push(node.key) |
| | | return 1 |
| | | } |
| | | return 0 |
| | | } |
| | | |
| | | treeData.forEach(node => processNode(node)) |
| | | return { checkedKeys, indeterminateKeys } |
| | | } |
| | | |
| | | // 表单验证规则 |
| | | const rules: FormRules = { |
| | |
| | | // 回显选中的权限 |
| | | if (data.menuIds && Array.isArray(data.menuIds)) { |
| | | const currentTab = activeTab.value |
| | | if (!selectedPermissions.value[currentTab]) { |
| | | selectedPermissions.value[currentTab] = {} |
| | | const leafKeys = filterLeafKeys(data.menuIds, currentPermissions.value) |
| | | |
| | | if (mode.value === 'view') { |
| | | // View 模式下,手动计算全选和半选状态 |
| | | const { checkedKeys, indeterminateKeys } = getTreeStatus(leafKeys, currentPermissions.value) |
| | | selectedPermissions[currentTab].checkedKeys = checkedKeys |
| | | selectedPermissions[currentTab].indeterminateKeys = indeterminateKeys |
| | | } else { |
| | | // Edit/Add 模式下,仅设置叶子节点,依赖 NTree 的 cascade 自动推导 |
| | | selectedPermissions[currentTab].checkedKeys = leafKeys |
| | | } |
| | | data.menuIds.forEach((menuId: string | number) => { |
| | | selectedPermissions.value[currentTab][menuId.toString()] = true |
| | | }) |
| | | } |
| | | } |
| | | } |
| | |
| | | loading.value = true |
| | | try { |
| | | const currentTab = activeTab.value |
| | | const menuIds = Object.keys(selectedPermissions.value[currentTab] || {}) |
| | | .filter(key => selectedPermissions.value[currentTab][key]) |
| | | const { checkedKeys, indeterminateKeys } = selectedPermissions[currentTab] |
| | | |
| | | // 从 keys 中还原出原始 ID |
| | | const allKeys = new Set([...checkedKeys, ...indeterminateKeys]) |
| | | const menuIds = Array.from(allKeys).map(key => { |
| | | const node = nodeMap.get(key as string) |
| | | return node ? node.id : null |
| | | }).filter(id => id !== null) |
| | | |
| | | // 去重,防止同一个 ID 对应多个 key 的情况导致重复提交 |
| | | const uniqueMenuIds = Array.from(new Set(menuIds)) |
| | | |
| | | const params: any = { |
| | | groupName: formModel.value.groupName, |
| | | groupType: groupType.value, |
| | | menuIds, |
| | | menuIds: uniqueMenuIds, |
| | | remarks: formModel.value.remarks, |
| | | } |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | // 检查模块是否全选 |
| | | function isModuleChecked(moduleKey: string) { |
| | | const currentTab = activeTab.value |
| | | const moduleData = currentPermissions.value.find(m => m.id === moduleKey) |
| | | if (!moduleData || !moduleData.children || moduleData.children.length === 0) |
| | | return false |
| | | |
| | | // 检查模块本身是否被选中 |
| | | const moduleIsSelected = selectedPermissions.value[currentTab]?.[moduleData.id] |
| | | |
| | | // 检查模块下所有页面(二级菜单)和权限(三级菜单)是否都选中 |
| | | const allPagesAndPermsSelected = moduleData.children.every(page => { |
| | | // 检查页面本身是否被选中 |
| | | const pageIsSelected = selectedPermissions.value[currentTab]?.[page.id] |
| | | |
| | | // 如果页面下有子权限,还需要检查子权限是否全部被选中 |
| | | if (page.children && page.children.length > 0) { |
| | | const allChildrenSelected = page.children.every(perm => |
| | | selectedPermissions.value[currentTab]?.[perm.id], |
| | | ) |
| | | // 页面本身和所有子权限都需要被选中 |
| | | return pageIsSelected && allChildrenSelected |
| | | } |
| | | |
| | | // 如果页面下没有子权限,只检查页面本身是否被选中 |
| | | return pageIsSelected |
| | | }) |
| | | |
| | | // 模块本身、所有页面和权限都需要被选中 |
| | | return moduleIsSelected && allPagesAndPermsSelected |
| | | } |
| | | |
| | | // 检查模块是否部分选中 |
| | | function isModuleIndeterminate(moduleKey: string) { |
| | | const currentTab = activeTab.value |
| | | const moduleData = currentPermissions.value.find(m => m.id === moduleKey) |
| | | if (!moduleData || !moduleData.children || moduleData.children.length === 0) |
| | | return false |
| | | |
| | | // 计算模块下所有数据的选中状态(包括模块本身、页面和权限) |
| | | let selectedCount = 0 |
| | | let totalCount = 1 // 模块本身算一个 |
| | | |
| | | // 检查模块本身是否被选中 |
| | | if (selectedPermissions.value[currentTab]?.[moduleData.id]) { |
| | | selectedCount++ |
| | | } |
| | | |
| | | for (const page of moduleData.children) { |
| | | totalCount++ // 页面本身算一个 |
| | | |
| | | // 检查页面本身是否被选中 |
| | | if (selectedPermissions.value[currentTab]?.[page.id]) { |
| | | selectedCount++ |
| | | } |
| | | |
| | | // 检查页面下的子权限 |
| | | if (page.children && page.children.length > 0) { |
| | | totalCount += page.children.length |
| | | selectedCount += page.children.filter(perm => |
| | | selectedPermissions.value[currentTab]?.[perm.id], |
| | | ).length |
| | | } |
| | | } |
| | | |
| | | return selectedCount > 0 && selectedCount < totalCount |
| | | } |
| | | |
| | | // 切换模块 |
| | | function toggleModule(moduleKey: string, checked: boolean) { |
| | | const currentTab = activeTab.value |
| | | const moduleData = currentPermissions.value.find(m => m.id === moduleKey) |
| | | if (!moduleData || !moduleData.children || moduleData.children.length === 0) |
| | | return |
| | | |
| | | if (!selectedPermissions.value[currentTab]) { |
| | | selectedPermissions.value[currentTab] = {} |
| | | } |
| | | |
| | | // 设置模块本身的状态 |
| | | selectedPermissions.value[currentTab][moduleData.id] = checked |
| | | |
| | | // 切换模块下所有页面(二级菜单)和权限(三级菜单)的状态 |
| | | moduleData.children.forEach((page) => { |
| | | // 设置页面本身的状态 |
| | | selectedPermissions.value[currentTab][page.id] = checked |
| | | |
| | | // 如果页面下有子权限,也要设置它们的状态 |
| | | if (page.children && page.children.length > 0) { |
| | | page.children.forEach((perm) => { |
| | | selectedPermissions.value[currentTab][perm.id] = checked |
| | | }) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | // 检查页面是否全选 |
| | | function isPageChecked(moduleKey: string, pageKey: string) { |
| | | const currentTab = activeTab.value |
| | | const moduleData = currentPermissions.value.find(m => m.id === moduleKey) |
| | | const pageData = moduleData?.children.find(p => p.id === pageKey) |
| | | if (!pageData) |
| | | return false |
| | | |
| | | // 检查页面本身是否被选中,如果有权限ID的话 |
| | | const pageIsSelected = selectedPermissions.value[currentTab]?.[pageData.id] |
| | | |
| | | // 如果页面下有子权限,还需要检查子权限是否全部被选中 |
| | | if (pageData.children && pageData.children.length > 0) { |
| | | const allChildrenSelected = pageData.children.every(perm => |
| | | selectedPermissions.value[currentTab]?.[perm.id], |
| | | ) |
| | | // 如果页面本身被选中,且所有子权限也被选中,则返回true |
| | | // 或者只有子权限全选而页面本身未选中(这种情况取决于业务逻辑) |
| | | return pageIsSelected || allChildrenSelected |
| | | } |
| | | |
| | | // 如果页面下没有子权限,只检查页面本身是否被选中 |
| | | return pageIsSelected || false |
| | | } |
| | | |
| | | // 检查页面是否部分选中 |
| | | function isPageIndeterminate(moduleKey: string, pageKey: string) { |
| | | const currentTab = activeTab.value |
| | | const moduleData = currentPermissions.value.find(m => m.id === moduleKey) |
| | | const pageData = moduleData?.children.find(p => p.id === pageKey) |
| | | if (!pageData) |
| | | return false |
| | | |
| | | // 检查页面本身是否被选中 |
| | | const pageIsSelected = selectedPermissions.value[currentTab]?.[pageData.id] |
| | | |
| | | // 检查页面下的子权限 |
| | | if (pageData.children && pageData.children.length > 0) { |
| | | const checkedCount = pageData.children.filter(perm => |
| | | selectedPermissions.value[currentTab]?.[perm.id], |
| | | ).length |
| | | |
| | | // 如果页面本身被选中,但不是所有子权限都被选中,则为部分选中 |
| | | if (pageIsSelected && checkedCount < pageData.children.length) { |
| | | return true |
| | | } |
| | | // 如果页面本身未被选中,但有部分子权限被选中,则为部分选中 |
| | | if (!pageIsSelected && checkedCount > 0 && checkedCount < pageData.children.length) { |
| | | return true |
| | | } |
| | | } |
| | | |
| | | return false |
| | | } |
| | | |
| | | // 切换页面 |
| | | function togglePage(moduleKey: string, pageKey: string, checked: boolean) { |
| | | const currentTab = activeTab.value |
| | | const moduleData = currentPermissions.value.find(m => m.id === moduleKey) |
| | | const pageData = moduleData?.children.find(p => p.id === pageKey) |
| | | if (!pageData) |
| | | return |
| | | |
| | | if (!selectedPermissions.value[currentTab]) { |
| | | selectedPermissions.value[currentTab] = {} |
| | | } |
| | | |
| | | // 设置页面本身的状态 |
| | | selectedPermissions.value[currentTab][pageData.id] = checked |
| | | |
| | | // 如果页面下有子权限,也要设置它们的状态 |
| | | if (pageData.children && pageData.children.length > 0) { |
| | | pageData.children.forEach((perm) => { |
| | | selectedPermissions.value[currentTab][perm.id] = checked |
| | | }) |
| | | } |
| | | } |
| | | |
| | | // 切换单个权限 |
| | | function togglePermission(moduleKey: string, pageKey: string, permKey: string, checked: boolean) { |
| | | const currentTab = activeTab.value |
| | | if (!selectedPermissions.value[currentTab]) { |
| | | selectedPermissions.value[currentTab] = {} |
| | | } |
| | | selectedPermissions.value[currentTab][permKey] = checked |
| | | } |
| | | |
| | | // 检查权限是否选中 |
| | | function isPermissionChecked(moduleKey: string, pageKey: string, permKey: string) { |
| | | const currentTab = activeTab.value |
| | | return selectedPermissions.value[currentTab]?.[permKey] || false |
| | | } |
| | | |
| | | const getMenuTree = async () => { |
| | | getMenuList().then((res) => { |
| | | return getMenuList().then((res) => { |
| | | if (res.code === 200) { |
| | | managementPermissions.value = res.data |
| | | managementPermissions.value = transformTreeData(res.data || []) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | onMounted(() => { |
| | | getPermissionGroupDetail() |
| | | getMenuTree() |
| | | onMounted(async () => { |
| | | await getMenuTree() |
| | | await getPermissionGroupDetail() |
| | | }) |
| | | </script> |
| | | |
| | |
| | | <!-- 详情模式下的 Tab: 权限信息 / 关联用户 --> |
| | | <NTabs v-if="mode === 'view'" v-model:value="mainTab" type="line" animated> |
| | | <NTabPane name="permission" tab="权限信息"> |
| | | <div class="permission-table"> |
| | | <div class="table-header"> |
| | | <div class="col-module"> |
| | | 板块 |
| | | </div> |
| | | <div class="col-page"> |
| | | 页面 |
| | | </div> |
| | | <div class="col-permissions"> |
| | | 权限 |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="table-body"> |
| | | <div v-for="module in currentPermissions" :key="module.id" class="module-row"> |
| | | <div class="col-module"> |
| | | <NCheckbox :checked="isModuleChecked(module.id)" |
| | | :indeterminate="isModuleIndeterminate(module.id)" :disabled="true"> |
| | | {{ module.menuName }} |
| | | </NCheckbox> |
| | | </div> |
| | | <div class="col-page-permissions"> |
| | | <div v-for="page in module.children" :key="page.id" class="page-row"> |
| | | <div class="col-page"> |
| | | <NCheckbox :checked="isPageChecked(module.id, page.id)" |
| | | :indeterminate="isPageIndeterminate(module.id, page.id)" :disabled="true"> |
| | | {{ page.menuName }} |
| | | </NCheckbox> |
| | | </div> |
| | | <div class="col-permissions"> |
| | | <NSpace :size="[16, 8]" :wrap="true"> |
| | | <NCheckbox v-for="perm in page.children" :key="perm.id" |
| | | :checked="isPermissionChecked(module.id, page.id, perm.id)" :disabled="true"> |
| | | {{ perm.menuName }} |
| | | </NCheckbox> |
| | | </NSpace> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="tree-container"> |
| | | <NTree |
| | | block-line |
| | | :cascade="false" |
| | | checkable |
| | | :data="viewPermissions" |
| | | key-field="key" |
| | | label-field="menuName" |
| | | children-field="children" |
| | | :default-expand-all="false" |
| | | :checked-keys="selectedPermissions[activeTab].checkedKeys" |
| | | :indeterminate-keys="selectedPermissions[activeTab].indeterminateKeys" |
| | | /> |
| | | </div> |
| | | </NTabPane> |
| | | <NTabPane name="accounts" tab="关联用户"> |
| | |
| | | <!-- 新增/编辑模式下的 Tab: 管理端 / 运维APP --> |
| | | <NTabs v-else :value="activeTab" type="line" animated @update:value="handleTabChange"> |
| | | <NTabPane name="management" tab="管理端"> |
| | | <div class="permission-table"> |
| | | <div class="table-header"> |
| | | <div class="col-module"> |
| | | 板块 |
| | | </div> |
| | | <div class="col-page"> |
| | | 页面 |
| | | </div> |
| | | <div class="col-permissions"> |
| | | 权限 |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="table-body"> |
| | | <div v-for="module in managementPermissions" :key="module.id" class="module-row"> |
| | | <div class="col-module"> |
| | | <NCheckbox :checked="isModuleChecked(module.id)" |
| | | :indeterminate="isModuleIndeterminate(module.id)" |
| | | @update:checked="(checked) => toggleModule(module.id, checked)"> |
| | | {{ module.menuName }} |
| | | </NCheckbox> |
| | | </div> |
| | | <div class="col-page-permissions"> |
| | | <div v-for="page in module.children" :key="page.id" class="page-row"> |
| | | <div class="col-page"> |
| | | <NCheckbox :checked="isPageChecked(module.id, page.id)" |
| | | :indeterminate="isPageIndeterminate(module.id, page.id)" |
| | | @update:checked="(checked) => togglePage(module.id, page.id, checked)"> |
| | | {{ page.menuName }} |
| | | </NCheckbox> |
| | | </div> |
| | | <div class="col-permissions"> |
| | | <NSpace :size="[16, 8]" :wrap="true"> |
| | | <NCheckbox v-for="perm in page.children" :key="perm.id" |
| | | :checked="isPermissionChecked(module.id, page.id, perm.id)" |
| | | @update:checked="(checked) => togglePermission(module.id, page.id, perm.id, checked)"> |
| | | {{ perm.menuName }} |
| | | </NCheckbox> |
| | | </NSpace> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="tree-container"> |
| | | <NTree |
| | | block-line |
| | | cascade |
| | | checkable |
| | | :data="currentPermissions" |
| | | key-field="key" |
| | | label-field="menuName" |
| | | children-field="children" |
| | | :default-expand-all="false" |
| | | :checked-keys="selectedPermissions[activeTab].checkedKeys" |
| | | :indeterminate-keys="selectedPermissions[activeTab].indeterminateKeys" |
| | | @update:checked-keys="(keys) => selectedPermissions[activeTab].checkedKeys = keys" |
| | | @update:indeterminate-keys="(keys) => selectedPermissions[activeTab].indeterminateKeys = keys" |
| | | /> |
| | | </div> |
| | | </NTabPane> |
| | | <!-- <NTabPane name="operation" tab="运维APP"> |
| | | <div class="permission-table"> |
| | | <div class="table-header"> |
| | | <div class="col-module"> |
| | | 板块 |
| | | </div> |
| | | <div class="col-page"> |
| | | 页面 |
| | | </div> |
| | | <div class="col-permissions"> |
| | | 权限 |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="table-body"> |
| | | <div |
| | | v-for="module in operationPermissions" |
| | | :key="module.id" |
| | | class="module-row" |
| | | > |
| | | <div class="col-module"> |
| | | <NCheckbox |
| | | :checked="isModuleChecked(module.id)" |
| | | :indeterminate="isModuleIndeterminate(module.id)" |
| | | @update:checked="(checked) => toggleModule(module.id, checked)" |
| | | > |
| | | {{ module.menuName }} |
| | | </NCheckbox> |
| | | </div> |
| | | <div class="col-page-permissions"> |
| | | <div |
| | | v-for="page in module.children" |
| | | :key="page.id" |
| | | class="page-row" |
| | | > |
| | | <div class="col-page"> |
| | | <NCheckbox |
| | | :checked="isPageChecked(module.id, page.id)" |
| | | :indeterminate="isPageIndeterminate(module.id, page.id)" |
| | | @update:checked="(checked) => togglePage(module.id, page.id, checked)" |
| | | > |
| | | {{ page.menuName }} |
| | | </NCheckbox> |
| | | </div> |
| | | <div class="col-permissions"> |
| | | <NSpace :size="[16, 8]" :wrap="true"> |
| | | <NCheckbox |
| | | v-for="perm in page.children" |
| | | :key="perm.id" |
| | | :checked="isPermissionChecked(module.id, page.id, perm.id)" |
| | | @update:checked="(checked) => togglePermission(module.id, page.id, perm.id, checked)" |
| | | > |
| | | {{ perm.menuName }} |
| | | </NCheckbox> |
| | | </NSpace> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <NTabPane name="operation" tab="运维APP"> |
| | | <div class="tree-container"> |
| | | <NTree |
| | | block-line |
| | | cascade |
| | | checkable |
| | | :data="currentPermissions" |
| | | key-field="key" |
| | | label-field="menuName" |
| | | children-field="children" |
| | | :default-expand-all="false" |
| | | :checked-keys="selectedPermissions[activeTab].checkedKeys" |
| | | :indeterminate-keys="selectedPermissions[activeTab].indeterminateKeys" |
| | | @update:checked-keys="(keys) => selectedPermissions[activeTab].checkedKeys = keys" |
| | | @update:indeterminate-keys="(keys) => selectedPermissions[activeTab].indeterminateKeys = keys" |
| | | /> |
| | | </div> |
| | | </NTabPane> --> |
| | | </NTabPane> |
| | | </NTabs> |
| | | </div> |
| | | </NFormItem> |
| | |
| | | width: 100%; |
| | | } |
| | | |
| | | .permission-table { |
| | | width: 100%; |
| | | border: 1px solid #e8e8e8; |
| | | border-radius: 2px; |
| | | overflow: hidden; |
| | | background-color: #fff; |
| | | } |
| | | |
| | | .table-header { |
| | | display: flex; |
| | | background-color: #fafafa; |
| | | border-bottom: 1px solid #e8e8e8; |
| | | font-weight: 500; |
| | | padding: 12px 16px; |
| | | color: #333; |
| | | text-align: center; |
| | | } |
| | | |
| | | .table-header .col-module { |
| | | width: 150px; |
| | | flex-shrink: 0; |
| | | text-align: center; |
| | | } |
| | | |
| | | .table-header .col-page { |
| | | width: 180px; |
| | | flex-shrink: 0; |
| | | text-align: center; |
| | | } |
| | | |
| | | .table-header .col-permissions { |
| | | flex: 1; |
| | | text-align: center; |
| | | } |
| | | |
| | | .table-body { |
| | | background-color: #fff; |
| | | } |
| | | |
| | | .module-row { |
| | | display: flex; |
| | | border-bottom: 1px solid #e8e8e8; |
| | | } |
| | | |
| | | .module-row:last-child { |
| | | border-bottom: none; |
| | | } |
| | | |
| | | .module-row .col-module { |
| | | width: 150px; |
| | | flex-shrink: 0; |
| | | padding: 16px; |
| | | border-right: 1px solid #e8e8e8; |
| | | display: flex; |
| | | align-items: flex-start; |
| | | background-color: #fafafa; |
| | | } |
| | | |
| | | .module-row .col-page-permissions { |
| | | flex: 1; |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .page-row { |
| | | display: flex; |
| | | border-bottom: 1px solid #e8e8e8; |
| | | min-height: 48px; |
| | | } |
| | | |
| | | .page-row:last-child { |
| | | border-bottom: none; |
| | | } |
| | | |
| | | .page-row .col-page { |
| | | width: 180px; |
| | | flex-shrink: 0; |
| | | padding: 12px 16px; |
| | | border-right: 1px solid #e8e8e8; |
| | | display: flex; |
| | | align-items: center; |
| | | background-color: #fafafa; |
| | | } |
| | | |
| | | .page-row .col-permissions { |
| | | flex: 1; |
| | | padding: 12px 16px; |
| | | display: flex; |
| | | align-items: center; |
| | | .tree-container { |
| | | height: 400px; |
| | | overflow-y: auto; |
| | | border: 1px solid #eee; |
| | | padding: 12px; |
| | | border-radius: 4px; |
| | | } |
| | | </style> |