适用情况
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>