本文的布局才用了叶落风尘大佬的一片文章,功能是自己加的 源代码
页面部分
<div class="h-100vh grid place-items-center">
<div>
<div class="h-455px flex flex-col border-1px border-solid border-#eee w-885px">
<div class="h-42px flex items-center bg-#F4F7FA pl-15px pr-25px">
<div class="flex items-center">
<el-checkbox v-model="checked" @change="changeAll">全选商品</el-checkbox>
({{getCheckAll}}/{{lastLevelTotalCount}})
</div>
<div class="ml-auto flex items-center cursor-pointer" @click="changeAll(false)">
<i class="el-icon-delete"></i>
<div class="text-14px ml-5px">清除</div>
</div>
</div>
<div class="flex-1 h-0">
<cascade-panel :datalist="dataList">
<template #list="slotProps">
<div class="min-w-120px pl-15px h-32px cursor-pointer flex items-center">
<el-checkbox :indeterminate="getCheckStatus(slotProps.data)" v-model="slotProps.data.isChecked" @change="(e) => getData(e, slotProps.data)"></el-checkbox>
<div class="ml-10px">{{ slotProps.data.label }}</div>
<div v-if="slotProps.data.level && slotProps.data.level == 1">({{ getLastCheck(slotProps.index)}})</div>
</div>
</template>
</cascade-panel>
</div>
</div>
</div>
</div>
逻辑部分
<script>
new Vue({
el: '#app',
components: {
cascadePanel: httpVueLoader('./components/cascadePanel.vue'),
},
data() {
return {
checked: false,
selectedItems: ['3', '4'],
dataList: [
{
value: '1',
label: '指南',
level: 1,
isChecked: false,
children: [
{
value: '2',
label: '设计原则',
isChecked: false,
parent: null,
children: [
{
value: '3',
label: '一致',
isChecked: false,
parent: null,
children: [],
},
{
value: '4',
label: '反馈',
isChecked: false,
parent: null,
children: [],
},
],
},
{
value: '5',
label: '导航',
isChecked: false,
parent: null,
children: [
{
value: '6',
label: '侧向导航',
isChecked: false,
parent: null,
children: [],
},
],
},
],
},
{
value: '10',
label: '测试',
level: 1,
isChecked: false,
children: [
{
value: '11',
label: '测试11',
isChecked: false,
parent: null,
children: [],
},
{
value: '12',
label: '测试22',
isChecked: false,
parent: null,
children: [],
},
],
},
],
}
},
created() {
for (let i of this.dataList) {
this.addParentRef(i)
this.echoData(i)
}
},
watch: {
dataList: {
deep: true,
handler(newVal, oldVal) {
this.selectedItems = []
this.collectSelectedNodes(newVal)
console.log(this.selectedItems, 'gouxun')
},
},
},
computed: {
getLastCheck() {
return (i) => {
let total = 0
function countLastLevelChecked(node) {
if (node.children?.length > 0) {
node.children.forEach((child) => {
countLastLevelChecked(child)
})
} else {
if (node.isChecked) {
total++
}
}
}
countLastLevelChecked(this.dataList[i])
return total
}
},
getCheckAll() {
let total = 0
for (let i in this.dataList) {
total += this.getLastCheck(i)
}
return total
},
lastLevelTotalCount() {
let total = 0
const countLastLevelNodes = (node, level) => {
if (node.children?.length > 0) {
node.children.forEach((child) => {
countLastLevelNodes(child, level + 1)
})
} else {
total++
}
}
this.dataList.forEach((node) => {
countLastLevelNodes(node, 0)
})
return total
},
},
methods: {
getCheckStatus(tree) {
if (tree.children && tree.children.length > 0) {
if (!tree.children.every((child) => child.isChecked)) {
for (const node of tree.children) {
if (node.isChecked) {
return true
}
if (node.children && node.children.length > 0) {
const childResult = this.getCheckStatus(node.children)
if (childResult) {
return true
}
}
}
} else {
return false
}
}
return false
},
getData(isChecked, data) {
if (data.children?.length > 0) {
data.isChecked = isChecked
data.children.forEach((item) => {
this.getData(isChecked, item)
})
if (data.parent) {
this.updateParent(data)
}
} else {
data.isChecked = isChecked
if (data.parent) {
this.updateParent(data)
}
}
},
updateParent(node) {
if (node.parent) {
const parent = node.parent
if (parent.children.every((child) => child.isChecked)) {
parent.isChecked = true
} else {
parent.isChecked = false
}
this.updateParent(parent) // 递归更新父节点的 isChecked 属性
}
},
// 操作勾选的vlaue
collectSelectedNodes(nodes) {
nodes.forEach((node) => {
if (node.isChecked && node.children.length === 0) {
this.selectedItems.push(node.value)
}
if (node.children && node.children.length > 0) {
this.collectSelectedNodes(node.children)
}
})
},
// 回显
echoData(node) {
if (this.selectedItems.includes(node.value)) {
node.isChecked = true // 如果 selectedItems 包含当前节点的 value,则将 isChecked 设置为 true
} else {
node.isChecked = false
}
if (node.parent) {
this.updateParent(node)
}
if (node.children && node.children.length > 0) {
node.children.forEach((child) => this.echoData(child)) // 递归处理子节点
}
},
// 添加父节点引用
addParentRef(node, parent) {
node.parent = parent
if (node.children) {
node.children.forEach((child) => this.addParentRef(child, node))
}
},
// 全选
changeAll(e) {
for (let i of this.dataList) {
this.getData(e, i)
}
if (!e) {
this.checked = false
}
},
},
})
</script>
组件部分
<template>
<div class="flex h-full">
<div :class="['level_' + propsIndex]" class="border-1px border-r-solid border-#eee overflow-y-auto min-w-180px">
<div v-for="(item, index) in datalist" :key="index" @click="change(index)"
:class="{ 'bg-#eff7ff text-#0FA3FF': Number(index) == childActiveId }"
class="flex items-center pr-10px">
<slot name="list" :data="item" :index="index"></slot>
<div v-if="Number(index) == childActiveId" class="el-icon-arrow-right text-14px ml-auto text-#2fafff"></div>
</div>
</div>
<div :class="['out_level_' + (propsIndex + 1)]"
v-if="datalist[childActiveId] && datalist[childActiveId].children != 0">
<cascade-panel ref="cascadeRef" :datalist="datalist[childActiveId].children" :propsIndex="propsIndex + 1">
<template #list="slotProps">
<slot name="list" :data="slotProps.data"></slot>
</template>
</cascade-panel>
</div>
</div>
</template>
<script>
module.exports = {
name: 'cascadePanel',
props: {
datalist: {
type: Array,
default: () => [],
},
propsIndex: {
default: 1,
type: Number
}
},
data() {
return {
childActiveId: 0,
};
},
methods: {
init() {
this.childActiveId = 0;
if (this.$refs.cascadeRef) {
this.$refs.cascadeRef.init()
}
},
change(index) {
this.init();
//点击之后下一个组件显示,activeId初始化0
this.childActiveId = index
},
}
};
</script>