适用情况
1.element ui中tree 如果设置check-strictly 为true 则是父节点和子节点不进行关联(即点击选中哪个就是哪个)不方便用户使用,如果设置为false 则父节点和子节点进行关联,但是后端数据要求需要提供选中子节点的父节点 就需要我们讲半选状态的菜单ID getHalfCheckedKeys()和选中的ID getCheckedKeys()合并
都给后端,后端详情返回父节点的ID,如果直接设置选中setCheckedKeys 因为父节点关联子节点 【因此就会选中在回显的树呈现父节点下面的子节点都进行了选中】因此可以想办法区别一下那个ID是半选的ID,哪些是选中的ID,进行设置,但是文档上提供了设置全选的方法,但是没有设置半选的方法;
2.下面是解决当前场景的方法(目前是个新增、编辑、简单的角色,里面的权限树,方式可以以上问题现象)
<template>
<div class="roleManagement" style="min-height:calc(100vh - 95px)">
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible" center width="750px">
<el-form ref="organizationData" :rules="rules" :model="organizationData">
<el-form-item label="角色名称" prop="roleName" :label-width="formLabelWidth">
<el-input v-model="organizationData.roleName" type="text" placeholder="请输入角色名称" maxlength="20" />
</el-form-item>
<el-form-item label="角色描述" :label-width="formLabelWidth">
<el-input v-model="organizationData.roleDesc" type="textarea" :rows="5" maxlength="200" @input="change" />
</el-form-item>
<el-form-item label="权限" :label-width="formLabelWidth">
<!-- <el-tree
ref="tree"
style="margin-top:5px"
:data="privilegeData"
:default-checked-keys="privilegeUpData"
:default-expanded-keys="privilegeUpData"
show-checkbox
node-key="id"
default-expanded
:props="defaultProps"
@check="handleCheckChangeAuth"
/> -->
<el-tree
ref="tree"
style="margin-top:5px"
:data="privilegeData"
:default-expanded-keys="privilegeUpData"
show-checkbox
node-key="id"
default-expanded
:props="defaultProps"
:check-strictly="checkStrictly"
@check-change="handleCheckChangeAuthNode"
/>
</el-form-item>
</el-form>
<div v-if="dialogStatus ==='Details'" slot="footer" class="dialog-footer">
<el-button type="primary" @click="dialogFormVisible = false">确定</el-button>
</div>
<div v-else slot="footer" class="dialog-footer">
<el-button @click="backData()">取消</el-button>
<el-button type="primary" @click="dialogStatus === 'Create' ? createData() : updateData()">提交</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { sysMenuInfo, roleListAdd, roleDetails, roleListEdit } from '@/api/vue-roleManagement'
export default {
name: 'RoleManagement',
data() {
const tagNameReg = (rule, value, callback) => {
if (value.length === 0) {
callback(new Error('请输入角色名称'))
} else if (value.length > 20) {
callback(new Error('角色名称必须要在20个字以内'))
} else {
callback()
}
}
return {
textMap: {
update: 'Edit',
create: 'Create',
detail: 'Details'
},
dialogFormVisible: false, // 新增、编辑弹出框是否展示
formLabelWidth: '120px', // 弹出框form表单宽度展示
dialogStatus: '',
readonly: false,
organizationData: {
roleName: '', // 角色名称
roleDesc: '' // 角色描述
},
rules: {
roleName: [
{ required: true, validator: tagNameReg, trigger: 'blur' }
]
},
privilegeData: [], // 菜单权限
privilegeUpData: null,
defaultProps: {
children: 'childMenus',
label: 'menuName'
},
menuIdsObj: [], // 菜单选中的权限限制
ids: '' // 编辑ID
}
},
created() {
this.getList()
},
methods: {
/**
* 选中的节点展示
*/
handleCheckChangeAuthNode() {
this.menuIdsObj = []
// this.menuIdsObj = this.$refs.tree.getCheckedNodes() // 全选
this.menuIdsObj = this.$refs.tree.getCheckedNodes().concat(this.$refs.tree.getHalfCheckedNodes())
},
/**
* 新增事件
*/
AddEvent() {
this.dialogStatus = 'Create'
this.dialogFormVisible = true
this.resetForm('organizationData')
Object.keys(this.organizationData).forEach((key) => {
this.organizationData[key] = ''
})
// 菜单权限接口
this.privilegeData = []
this.privilegeUpData = []
this.sysMenuInfo()
},
/**
* 菜单权限展示 用于新增
*/
sysMenuInfo() {
sysMenuInfo().then((res) => {
if (res.code === 0 || res.code === '0') {
this.privilegeData = res.data
} else {
this.$message({
type: 'error',
message: res.message
})
}
}).catch((err) => {
console.log(err)
})
},
/**
* 新增保存接口
*/
createData() {
this.$refs['organizationData'].validate((valid) => {
if (valid) {
const params = {
roleName: this.organizationData.roleName, // 角色名称
roleDesc: this.organizationData.roleDesc, // 角色描述
menuIds: [] // 选中的菜单数据
}
this.menuIdsObj.forEach(function(item) {
const obj = {
type: item.type,
parentId: item.parentId,
id: item.id
}
params.menuIds.push(obj)
})
roleListAdd(params).then((res) => {
if (res.code === '0' || res.code === 0) {
this.$message({
type: 'success',
message: '创建成功'
})
this.dialogFormVisible = false
this.pageNum = 1
this.pageSize = 10
this.getList()
} else {
this.$message({
type: 'error',
message: res.message
})
}
})
}
})
},
/**
* 点击编辑的时间
*/
handleUpdate(row) {
this.dialogStatus = 'Edit'
this.dialogFormVisible = true
this.resetForm('organizationData')
Object.keys(this.organizationData).forEach((key) => {
this.organizationData[key] = ''
})
this.ids = row.id
this.privilegeData = [] // 菜单接口
this.privilegeUpData = []
// 调用详情接口
this.roleDetailsList(row.id)
},
/**
* 菜单获取接口 用于编辑接口展示
*/
getPartsysMenuInfo() {
return new Promise(function(resolve) {
sysMenuInfo().then((res) => resolve(res))
})
},
/**
* 详情展示接口用于编辑展示
*/
getPartRoleDetails(id) {
return new Promise(function(resolve) {
roleDetails({ id: id }).then((res) => resolve(res))
})
},
roleDetailsList(id) {
if (id === '' || id === null || id === '{}' || id === undefined) {
return false
}
const _this = this
return Promise.all([this.getPartsysMenuInfo(), this.getPartRoleDetails(id)])
.then((results) => {
// 处理数据
if (results[0].code === '0' || results[0].code === 0) { // 菜单接口
_this.privilegeData = results[0].data
} else {
_this.$message({
message: results[0].message,
type: 'error'
})
_this.privilegeData = []
}
if (results[1].code === '0' || results[1].code === 0) { // 代表详情接口
_this.organizationData.roleName = results[1].data.roleName // 角色名称
_this.organizationData.roleDesc = results[1].data.roleDesc // 角色描述
if (results[1].data.roleMenus === null && results[1].data.roleButtons === null || results[1].data.roleMenus.length <= 0 && results[1].data.roleButtons.length <= 0) {
_this.privilegeUpData = null
} else {
const arr = []
const brr = [] // 用于渲染编辑回显的内容
// 菜单存放的数组
results[1].data.roleMenus.forEach(function(element) {
arr.push(element.menuId)
const brrObj1 = {
type: 1,
parentId: element.parentId,
id: element.menuId
}
brr.push(brrObj1)
})
results[1].data.roleButtons.forEach(function(element) {
arr.push(element.buttonId)
const brrObj2 = {
type: 2,
parentId: element.parentId,
id: element.buttonId
}
brr.push(brrObj2)
})
_this.menuIdsObj = brr // 渲染是否选中问题 [树]
const checkTreeNode = [] // 存放选中的节点
const parentNodes = []
_this.$nextTick(() => {
for (const item of arr) {
const node = _this.$refs.tree.getNode(item)
if (node && node.isLeaf) {
checkTreeNode.push(item)
} else if (node) {
parentNodes.push(node)
}
}
// 设置所有子节点选中,自动回填父节点
_this.$refs.tree.setCheckedKeys(checkTreeNode)
// 未回填的父节点单独设置
for (let node of parentNodes) {
do {
// 应当有状态的父节点在未选中时设置为半选中状态
if (!node.checked && !node.indeterminate) {
node.indeterminate = true
}
// node.indeterminate = true 仅对一个节点进行半选中状态设置,其父节点不能自动级联设置,所以这里循环设置级联父节点状态
node = node.parent
} while (node)
}
})
_this.privilegeUpData = arr // 用于需要渲染的ID数据
}
} else {
_this.$message({
message: results[1].message,
type: 'error'
})
}
})
},
/**
* 编辑接口
*/
updateData() {
this.$refs['organizationData'].validate((valid) => {
if (valid) {
const params = {
roleName: this.organizationData.roleName, // 角色名称
roleDesc: this.organizationData.roleDesc, // 角色描述
menuIds: [], // 选中的菜单数据
id: this.ids
}
// 处理选中需要的数据
this.menuIdsObj.forEach(function(item) {
const obj = {
type: item.type,
parentId: item.parentId,
id: item.id
}
params.menuIds.push(obj)
})
roleListEdit(params).then((res) => {
if (res.code === '0' || res.code === 0) {
this.$message({
type: 'success',
message: '编辑成功'
})
this.dialogFormVisible = false
this.pageNum = 1
this.pageSize = 10
this.getList()
} else {
this.$message({
type: 'error',
message: res.message
})
}
})
}
})
},
/**
* 点击返回
*/
backData() {
this.dialogFormVisible = false
}
}
}
</script>
<style lang="scss" scoped>
.roleManagement {
.header_btn {
width:100%;
height:50px;
}
.loading_btnInfo{font-size:36px;color:#409EFF;margin:auto;width:36px;height:36px;display:block;margin-top:200px}
.loading_p1{width:100%;text-align:center;height:50px;line-height:50px;font-size:14px;}
.box-card {
margin:20px;
}
.box-card_serch {
margin:20px 20px 0px 20px;
}
.search-button {
border-radius: 4px;
}
/**-----------------添加成员 成员编辑权限样式调整---------------------------; */
/deep/ .el-tree-node__content{
height:35px !important;
}
.main_app {
.main_left {
background: #FFFFFF;
border: 1px solid #DDDEE3;
border-radius: 4px;
width: 440px;
height: 430px;
float: left;
overflow-y:auto;
.div1 {
width: 440px;
height: 40px;
padding: 11px 19.9px 9px 20.5px;
color: #4A4A4A;
border-bottom: 1px solid #DDDEE3;
cursor: pointer;
}
.treeStyle {
padding: 20px;
.p1 {
font-size: 16px;
color: #2B3642;
margin-top: 15px;
margin-bottom: 15px;
img {
width: 4px;
height: 16px;
margin-right: 12px;
vertical-align: middle;
}
span {
vertical-align: middle;
}
time {
float: right;
}
}
}
}
}
}
</style>
