Files
my-bigScreen/screen-vue/src/views/system/role/role.vue
2026-03-07 12:13:00 +08:00

260 lines
6.0 KiB
Vue

<template>
<div class="role-auth-container">
<el-form
:model="formData"
class="role-info-form"
label-width="80px"
inline
>
<el-form-item label="角色编号:" class="form-col">
<el-input
v-model="formData.roleId"
disabled
class="info-input"
/>
</el-form-item>
<el-form-item label="角色名称:" class="form-col">
<el-input
v-model="formData.roleName"
class="info-input"
/>
</el-form-item>
</el-form>
<div class="menu-tree-wrapper">
<div class="tree-content">
<el-skeleton v-if="loading" :rows="10" animated />
<el-tree
v-else
ref="menuTreeRef"
:data="menuTreeData"
:props="treeProps"
:show-checkbox="true"
:default-expand-all="false"
node-key="id"
@check-change="handleCheckChange"
@check="handleCheck"
/>
</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, defineProps, defineExpose, watch } from 'vue'
import { ElMessage } from 'element-plus'
import { getTreeMenuList } from '@/api/bizMenu'
import { getHomeRoleMenus } from '@/api/bizRole'
const props = defineProps({
formData: {
type: Object,
required: true,
default: () => ({
roleId: '',
roleName: '',
menuIds: []
})
}
})
const menuTreeData = ref([])
const loading = ref(false)
const menuTreeRef = ref(null)
const selectedMenuIds = ref([])
const getLeafNodeIds = (treeData, leafIds = []) => {
treeData.forEach(node => {
if (!node.children || node.children.length === 0) {
leafIds.push(String(node.id))
} else {
getLeafNodeIds(node.children, leafIds)
}
})
return leafIds
}
const filterOnlyLeafIds = (allIds, treeData) => {
const allLeafIds = getLeafNodeIds(treeData)
return allIds.filter(id => allLeafIds.includes(id))
}
const getTreeListData = async () => {
try {
const res = await getTreeMenuList()
menuTreeData.value = res ?? []
return menuTreeData.value
} catch (error) {
console.error('加载菜单树失败:', error)
menuTreeData.value = []
return []
}
}
const getRoleMenuIds = async () => {
try {
const reqParams = { roleId: props.formData.roleId }
const res = await getHomeRoleMenus(reqParams)
return (res ?? []).map(item => String(item?.menuId)).filter(id => id)
} catch (error) {
console.error('加载角色菜单权限失败:', error)
return []
}
}
const treeProps = reactive({
label: 'label',
children: 'children',
disabled: 'disabled'
})
function getIndeterminateKeys(node) {
let keys = []
if (node.indeterminate) {
keys.push(node.key)
}
if (node.childNodes) {
node.childNodes.forEach(c => keys.push(...getIndeterminateKeys(c)))
}
return keys
}
function getCheckedAndIndeterminateKeys() {
if (!menuTreeRef.value) return []
const checked = menuTreeRef.value.getCheckedKeys(false)
const indeterminate = getIndeterminateKeys(menuTreeRef.value.root)
return [...new Set([...checked, ...indeterminate])]
}
onMounted(async () => {
loading.value = true
try {
const treeData = await getTreeListData()
const roleMenuIds = await getRoleMenuIds()
const propsMenuIds = (props.formData?.menuIds ?? []).map(id => String(id)).filter(id => id)
const allMenuIds = roleMenuIds.length > 0 ? roleMenuIds : propsMenuIds
const leafMenuIds = filterOnlyLeafIds(allMenuIds, treeData)
if (leafMenuIds.length > 0) {
selectedMenuIds.value = [...leafMenuIds]
setTimeout(() => {
if (menuTreeRef.value && treeData.length) {
menuTreeRef.value.setCheckedKeys(leafMenuIds, false)
}
}, 300)
}
} catch (error) {
console.error('初始化权限数据失败:', error)
} finally {
loading.value = false
}
})
watch(() => props.formData.menuIds, (newVal) => {
if (!newVal || !newVal.length || !menuTreeRef.value || !menuTreeData.value.length) return
const allMenuIds = newVal.map(id => String(id)).filter(id => id)
const leafMenuIds = filterOnlyLeafIds(allMenuIds, menuTreeData.value)
if (leafMenuIds.length) {
selectedMenuIds.value = [...leafMenuIds]
menuTreeRef.value.setCheckedKeys(leafMenuIds, false)
}
}, { deep: true })
const handleCheck = () => {
if (!menuTreeRef.value) return
selectedMenuIds.value = menuTreeRef.value.getCheckedKeys(false)
}
const handleCheckChange = () => {
handleCheck()
}
const validate = async () => {
if (selectedMenuIds.value.length === 0) {
ElMessage.warning('请至少选择一个菜单权限!')
return false
}
return true
}
const resetForm = () => {
if (menuTreeRef.value) {
menuTreeRef.value.setCheckedKeys([])
selectedMenuIds.value = []
}
}
defineExpose({
validate,
resetForm,
getSelectedMenuIds: () => getCheckedAndIndeterminateKeys(),
getLeafMenuIds: () => menuTreeRef.value ? menuTreeRef.value.getCheckedKeys(true) : []
})
</script>
<style scoped>
.role-auth-container {
width: 100%;
padding: 16px;
border: 1px solid rgba(64, 158, 255, 0.15);
border-radius: 8px;
box-sizing: border-box;
background: #fff;
}
.role-info-form {
margin-bottom: 16px;
padding-bottom: 12px;
border-bottom: 1px solid #e5e7eb;
width: 100%;
display: flex;
gap: 20px;
}
.form-col {
flex: 1;
min-width: 200px;
}
.info-input {
width: 100%;
}
:deep(.role-info-form .el-form-item) {
margin-bottom: 0;
width: 100%;
}
:deep(.role-info-form .el-input.is-disabled .el-input__inner) {
background-color: #f5f7fa;
color: #666;
}
.menu-tree-wrapper {
width: 100%;
}
.tree-content {
width: 100%;
height: 400px;
overflow-y: auto;
border: 1px solid #e5e7eb;
border-radius: 4px;
padding: 8px;
box-sizing: border-box;
}
:deep(.el-tree) {
--el-tree-node-content-hover-bg-color: #f5f7fa;
}
:deep(.el-tree-node__content) {
height: 36px;
line-height: 36px;
}
.tree-content::-webkit-scrollbar {
width: 6px;
}
.tree-content::-webkit-scrollbar-thumb {
background-color: #dcdfe6;
border-radius: 3px;
}
</style>