实现功能:
思路:
1、tab切换为一级菜单栏,根据点击不同的tab获取该底下的二三级菜单,实际操作为二三级,但最终传值,如果一级底下有勾选项也要将一级id传给后台
2、实现勾选与半勾选,用于回显页面的数据结构为树结构
全部源码:
<template>
<div class="newSubPage warp">
<div class="bg-f2 bg-fff" style="padding-bottom: 45px">
<el-form ref="addRoleForm" class="pAll" style="padding-bottom: 60px !important"
:rules="addRoleRules" :model="addRole" label-width="80px">
<el-form-item label="分配权限:" class="distribute">
<!-- 一级菜单tab -->
<el-tabs v-model="activeRoleId" type="card" @tab-click="handleClick">
<el-tab-pane v-for="item in firstmenuList" :key="item.id"
:label="item.menuName" :name="item.id.toString()">
</el-tab-pane>
</el-tabs>
<div class="table-body" v-for="secondMenu in roleList" :key="secondMenu.id">
<!-- 二级菜单 -->
<el-checkbox :indeterminate="secondMenu.indeterminate"
v-model="secondMenu.selected" @change="handleTwoLevelMenuhange(secondMenu, $event)">
{{ secondMenu.menuName }}
</el-checkbox>
<div v-for="thirdMenu in secondMenu.childList" :key="thirdMenu.id" class="juxtaposition">
<!-- 三级菜单 -->
<el-checkbox :indeterminate="thirdMenu.indeterminate" v-model="thirdMenu.selected"
@change="handleThreeLevelMenuhange(thirdMenu, secondMenu, $event, true)" class="thirdLeve">
{{ thirdMenu.menuName }}
</el-checkbox>
<div class="thirdLeveLable">
<!-- 四级菜单 -->
<el-checkbox :indeterminate="fourthMenu.indeterminate" v-model="fourthMenu.selected"
@change="handleFourLevelMenuhange(fourthMenu, thirdMenu, secondMenu, $event)"
v-for="fourthMenu in thirdMenu.childList" :key="fourthMenu.id">
{{ fourthMenu.menuName }}
</el-checkbox>
</div>
</div>
</div>
</el-form-item>
</el-form>
<el-button @click="savaBtnSubmit()" type="primary" class="btnSizeStyle">确定</el-button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
title: '', // 编辑新增标题
addRole: {},
addRoleRules: { },
activeRoleId: '1', // 运维概况Id为1
roleLoading: false, // 权限列表加载
firstmenuList: [], // 一级菜单查询
roleList: [], // 二三级数据源
checkbox: [], // 勾选数据源
checkModelList: [], // 储存每个模块化的勾选id
}
},
created() {
this.getFirstLevelMenu() // 一级菜单查询
},
watch: {},
methods: {
// 一级菜单查询, 顶部横向排序
getFirstLevelMenu() {
this.firstmenuList = [
{id: 1, menuName: '运维概况'},
{id: 2, menuName: '巡检中心'},
]
this.getRoleMenu()
},
// 二三四级菜单查询
getRoleMenu() {
let list = []
if (this.activeRoleId == 1) {
list = [
{id: 3, menuName: '主机监视', parentId: 1, childList: [
{id: 21, menuName: '主机列表', parentId: 3, childList: [
{id: 52, menuName: '查看主机列表', parentId: 21},
{id: 53, menuName: '删除', parentId: 21},
]}
]},
{id: 4, menuName: '资产分布', parentId: 1, childList: [
{id: 22, menuName: '点位分布概览', parentId: 4, childList: [
{id: 61, menuName: '查看点位分布概览', parentId: 22},
]},
{id: 23, menuName: '资产分布概览', parentId: 4, childList: [
{id: 71, menuName: '查看点位分布概览', parentId: 22},
{id: 72, menuName: '查看资产分布概览', parentId: 23},
]}
]}
]
} else {
list = [
{id: 5, menuName: '人工巡检', parentId: 2, childList: [
{id: 8, menuName: '机房巡检', parentId: 5, childList: [
{id: 82, menuName: '查看', parentId: 8},
{id: 83, menuName: '添加', parentId: 8},
{id: 84, menuName: '编辑', parentId: 8},
{id: 85, menuName: '详情', parentId: 8},
{id: 86, menuName: '导出', parentId: 8},
{id: 87, menuName: '删除', parentId: 8},
]}
]},
]
}
this.roleList = this.getProductType(list)
},
// 根据勾选数据获得默认勾选状态
getProductType(data, type) {
data.forEach(parent1 => {
let allSelected1 = true;
let someSelected1 = false;
parent1.childList.forEach(parent2 => {
let allSelected2 = true // 默认全勾选
let someSelected2 = false
parent2.childList.forEach(child => {
console.log(this.checkModelList.indexOf(child.id) != -1, this.checkModelList,'---', child.id)
if (this.checkModelList.indexOf(child.id) != -1) { // 如果包含该四级菜单
child.selected = true // 该四级菜单状态为勾选
} else { // 否则不勾选
child.selected = false
}
if (!child.selected) { // 如果四级菜单有一个没勾
allSelected2 = false // 三级菜单全勾状态为false
} else {
someSelected2 = true // 三级菜单全勾状态改变,代表勾选一部分
}
parent2.selected = allSelected2 // 赋值三级菜单全勾状态
if (someSelected2 && !allSelected2) { // 勾选三级菜单一部分
parent2.indeterminate = true // 赋值三级菜单办勾状态
} else {
parent2.indeterminate = false
}
if (!parent2.selected) { // 如果三级菜单有一个没勾
allSelected1 = false // 二级菜单全勾状态为false
} else {
someSelected1 = true // 二级菜单全勾状态改变,代表勾选一部分
}
})
parent1.selected = allSelected1 // 赋值二级菜单全勾状态
if (someSelected1 && !allSelected1) { // 勾选二级菜单一部分
parent1.indeterminate = true // 赋值二级菜单办勾状态
} else {
parent1.indeterminate = false
}
})
})
return data
},
// 二级菜单点击
handleTwoLevelMenuhange(secondMenu, e) {
secondMenu.indeterminate = false // 去掉二级不确定状态
secondMenu.selected = e // 二级勾选后,全部勾选或者取消
if(secondMenu.childList) {
for(let item of secondMenu.childList) {
if (e) item.indeterminate = false // 取消半勾选换成全勾选
item.selected = e // 三级全部勾选或者取消
if (item.childList)
for(let el of item.childList)
el.selected = e // 四级全部勾选或取消
}
}
this.getCheckData() // 得到勾选数据
},
// 三级菜单点击
handleThreeLevelMenuhange(thirdMenu, secondMenu, e, isFourClick){
let twoData = secondMenu.childList // 二级下的所有三级
let tickCount = 0, unTickCount = 0
for (let i of twoData) {
// 四级勾选已在四级进行了操作,所以点击四级不进这里
if(thirdMenu.id == i.id && isFourClick) i.selected = e
if(i.selected) { // 勾选操作
i.indeterminate = false
tickCount++
}
else if (!i.indeterminate && !i.selected) { // 不勾选
unTickCount++
}
}
if(tickCount == twoData.length) { // 三级级全勾选
secondMenu.selected = true
secondMenu.indeterminate = false
} else if(unTickCount == twoData.length) { // 三级级全不勾选
secondMenu.selected = false
secondMenu.indeterminate = false
} else {
secondMenu.selected = false
secondMenu.indeterminate = true // 添加三级不确定状态
}
if(thirdMenu.childList && isFourClick) { // 四级渲染时不走这里
for(let item of thirdMenu.childList) {
item.selected = e // 四级全部勾选或者取消
}
}
this.getCheckData() // 得到勾选数据
this.$forceUpdate() // 强制更新
},
// 四级菜单点击
handleFourLevelMenuhange(fourthMenu, thirdMenu, secondMenu, e){
let threeData = thirdMenu.childList // 三级下所有的四级
let tickCount = 0, unTickCount = 0
for (let i of threeData) {
if(fourthMenu.id == i.id) i.selected = e
if(i.selected) tickCount++ // 勾选
if(!i.selected) unTickCount++ // 不勾选
}
if(tickCount == threeData.length) { // 三级级全勾选
thirdMenu.selected = true
thirdMenu.indeterminate = false
} else if(unTickCount == threeData.length) { // 三级级全不勾选
thirdMenu.selected = false
thirdMenu.indeterminate = false
} else {
thirdMenu.selected = false
thirdMenu.indeterminate = true // 添加三级不确定状态
}
// 关联三级点击--二级勾选
this.handleThreeLevelMenuhange(thirdMenu, secondMenu, e, false)
this.$forceUpdate() // 强制更新
},
// 得到勾选的所有数据,如果一级下的子级有勾选,一级也要储存
getCheckData() {
this.checkbox = []
this.traverseData(this.roleList)
// 合并数组,checkbox有个操作会清空勾选数据
let allId = this.checkModelList.concat(this.checkbox)
let uniqueSelected2 = new Set(allId) // 去重
this.checkModelList = [...uniqueSelected2]
},
// 根据是否勾选将添加到数据
traverseData(data) {
for (let item of data) {
if (item.selected || item.indeterminate) {
this.checkbox.push(item.id)
this.checkbox.push(item.parentId) // 主要是将第一级id添加进去
} else if (!item.selected && !item.indeterminate) {
this.checkModelList.filter((el, index) => {
if (el === item.id || el === item.parentId) { // 同时将自己与父级删除
this.checkModelList.splice(index, 1)
index--
}
})
}
if (item.childList) this.traverseData(item.childList)
}
return data
},
// tab切换获取新的数据
handleClick(event) {
this.activeRoleId = event._props.name
this.getRoleMenu()
},
// 递归方法,将所有数据赋值新字段
circulateCheck(data) {
for (let item of data) {
item.selected = false
if (item.childList) {
this.circulateCheck(item.childList)
}
}
return data
},
// 确定按钮提交事件
async savaBtnSubmit() {
console.log('勾选数据源', this.checkModelList)
},
}
}
</script>
<style lang="scss" scoped>
.table-body {
border: 1px solid #dfe4ed;
margin-bottom: 10px;
padding: 0 10px;
}
.juxtaposition {
display: flex;
border-bottom: 1px solid #dfe4ed;
padding: 5px 0px;
.thirdLeve {
flex-shrink: 0; // 当前元素就不会被其他元素挤压。
width: 165px !important;
}
.thirdLeveLable {
padding-left: 10px;
border-left: 1px solid #1890ff;
}
}
::v-deep .juxtaposition .el-checkbox__label {
font-weight: normal !important;
}
::v-deep .el-tabs--card > .el-tabs__header {
height: 40px;
border-bottom: none;
}
</style>
总结:
1、getProductType函数是用于编辑页面回显的问题,不需要可注释
2、勾选级数大的,级数小的总是和级数大的状态一致
3、勾选级数小的,判断父级的子级长度,再根据tickCount和unTickCount这两个纸判断父级是全勾选还是半勾选。如果为全勾选,半勾选状态一定要为false,不然会出先显示不正确的问题。勾选四级要判断三级和二级,往上推