<!--
* @Author: yeminglong
* @Date: 2024-09-27 10:14:30
* @LastEditors: yeminglong
* @LastEditTime: 2024-09-27 16:49:05
* @Description:
-->
<script>
import TreeItem from '@/views/test/TreeItem.vue'
export default {
name: 'TreeList',
components: { TreeItem },
props: {
data: Array,
props: {
label: 'name',
children: 'children'
},
nodeKey: String
},
provide: {
// 当前选中的节点
currentNode: {
data: null
}
},
watch: {
data: {
handler(data) {
const { props } = this
const treeList = []
const deep = (list = [], parent = null) => {
for (const item of list) {
const node = {
checked: false,
isHalf: false,
data: item,
children: []
}
if (!parent) {
treeList.push(node)
} else {
parent.children.push(node)
}
if (
item[props.children] &&
item[props.children].length
) {
deep(item.children, node)
}
}
}
deep(data, null)
this.treeList = treeList
},
deep: true,
immediate: true
}
},
data() {
return {
treeList: []
}
},
methods: {
change({ checked }) {
const children = this?.children ?? []
const count = children.filter(v => v.checked).length
if (!checked && count > 0) {
return false
}
this.checked = checked
}
}
}
</script>
<template>
<div>
<div
class="root-item"
v-for="(item, index) in treeList"
:key="`children-${(nodeKey && item.data[nodeKey]) || index}`"
>
<TreeItem
:half-value.sync="item.isHalf"
:value="item.checked"
@input="value => (treeList[index].checked = value)"
:node-key="nodeKey"
:props="props"
:data="item.data"
:label="item.data[props.label]"
:children="item.children"
@change="change"
>
<template #label="{ data, label }">
<slot name="label" v-bind="{ data, label }">
{{ label }}
</slot>
</template>
</TreeItem>
</div>
</div>
</template>
<style scoped lang="scss">
.root-item {
//background: #f5f8fd;
//border-radius: 12px;
//padding: 38px 50px;
margin: 20px 0 20px 0;
}
</style>
<!--
* @Author: yeminglong
* @Date: 2024-09-25 09:57:50
* @LastEditors: yeminglong
* @LastEditTime: 2024-09-27 18:14:55
* @Description:
-->
<script>
export default {
name: 'TreeItem',
inject: ['currentNode'],
props: {
level: {
type: Number,
default: 0
},
halfValue: Boolean,
value: Boolean,
nodeKey: String,
props: {
label: 'name',
children: 'children'
},
data: Object,
label: String,
children: Array
},
computed: {
isHalf() {
const children = this.children || []
const checkedList = children.filter(v => v.checked && !v.isHalf)
if (!children.length) {
return false
}
return (
children.length !== checkedList.length ||
children.filter(v => v.isHalf).length > 0
)
// if (children.length && checkedList.length) {
// return children.length !== checkedList.length
// }
// return false
}
},
watch: {
value: {
handler(checked) {
this.checked = checked
},
immediate: true
},
isHalf(halfValue) {
this.$emit('update:halfValue', halfValue)
},
checked(checked) {
this.$emit('input', checked)
this.$emit('change', { checked, data: this.data })
if (this.level >= this.currentNode.data?.level) {
this.checkedChildren(checked)
}
}
},
data() {
return {
checked: false,
foldUp: false
}
},
methods: {
handleClick() {
const checked = !this.checked
this.currentNode.data = { checked, ...this.$props }
this.checked = checked
},
checkedChildren(checked) {
const children = this?.children
if (children && children.length) {
for (const child of children) {
child.checked = checked
}
}
},
change({ checked }) {
const children = this?.children ?? []
const count = children.filter(v => v.checked).length
if (!checked && count > 0) {
return false
}
this.checked = checked
}
}
}
</script>
<template>
<div class="tree-item" :class="`tree-item-${level}`">
<div class="tree-item-header">
<!--<van-checkbox-->
<!-- v-model="checked"-->
<!-- shape="square"-->
<!-- @click.native="handleClick"-->
<!-->-->
<!-- <slot name="label" v-bind="{ data, label }">-->
<!-- {{ label }}-->
<!-- </slot>-->
<!--</van-checkbox>-->
<div class="checkbox-box">
<span
class="checkbox-box-toggle"
:class="{ 'is-foldUp': foldUp }"
:style="{
visibility: children.length ? 'visible' : 'hidden'
}"
@click="
() => {
foldUp = !foldUp
}
"
>
<van-icon name="arrow" v-if="children.length" />
</span>
<span @click="handleClick">
<span
class="checkbox-box-input"
:class="{ 'is-half': isHalf, 'is-checked': checked }"
>
<span class="half-line" v-if="isHalf"></span>
<svg-icon
class="checkbox-box-input-icon"
icon-class="common-checked"
v-if="checked && !isHalf"
/>
</span>
<slot name="label" v-bind="{ data, label }">
{{ label }}
</slot>
</span>
</div>
</div>
<div
class="tree-item-sub"
v-if="children && children.length"
v-show="foldUp"
>
<TreeItem
v-for="(item, index) in children"
:key="`children-${(nodeKey && item.data[nodeKey]) || index}`"
:level="level + 1"
:half-value.sync="item.isHalf"
:value="item.checked"
@input="value => (children[index].checked = value)"
:node-key="nodeKey"
:props="props"
:data="item.data"
:label="item.data[props.label]"
:children="item.children"
@change="change"
>
<template #label="{ data }">
<slot name="label" v-bind="{ data, label }">
{{ label }}
</slot>
</template>
</TreeItem>
</div>
</div>
</template>
<style scoped lang="scss">
.tree-item {
//margin: 5px 0 5px 0;
}
.tree-item-header {
//margin-bottom: 20px;
}
.checkbox-box {
display: inline-flex;
align-items: center;
> span {
display: inline-flex;
align-items: center;
}
.checkbox-box-toggle {
width: 20px;
height: 20px;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 15px;
&.is-foldUp {
transform: rotate(90deg);
}
}
.checkbox-box-input {
display: inline-flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
border-radius: 5px;
border: 1px solid rgb(200, 201, 204);
background: #fff;
color: #fff;
margin-right: 10px;
cursor: pointer;
overflow: hidden;
:deep(.checkbox-box-input-icon) {
width: 15px;
height: 15px;
overflow: hidden;
}
&.is-checked {
background: #0262f1;
}
.half-line {
height: 2px;
width: 60%;
background: #fff;
}
}
}
.tree-item-sub {
margin-left: 30px;
}
</style>
调用组件
<template>
<TreeList :data="treeData" node-key="id" :props="{
label: 'itemName',
children: 'children'
}" @change="change">
<template #label="{ data }">
{{ data.itemName }}({{ data.count }})
</template>
</TreeList>
</template>
<script>
export default {
name: 'index',
data() {
return {
treeData: [{
"id": "JDBF",
"itemName": "测试节点-01",
"itemLevel": 1,
"parentItem": "ALL",
"itemIndex": "1",
"itemClass": "1",
"isParent": "1",
"count": 16680,
"children": [{
"id": "AIR",
"itemName": "测试节点-01",
"itemLevel": 1,
"parentItem": "JDBF",
"itemIndex": "4",
"itemClass": "1",
"isParent": "1",
"count": 4169,
"children": [{
"id": "AIR_04",
"itemName": "测试节点-02",
"itemLevel": 2,
"parentItem": "AIR",
"itemIndex": "32",
"itemClass": "1",
"isParent": "1",
"count": 0,
"children": [{
"id": "AIR_04_01",
"itemName": "测试节点-03",
"itemLevel": 3,
"parentItem": "AIR_04",
"itemIndex": "45",
"itemClass": "1",
"isParent": "0",
"count": 0,
"children": []
},
{
"id": "AIR_04_02",
"itemName": "测试节点-03",
"itemLevel": 3,
"parentItem": "AIR_04",
"itemIndex": "46",
"itemClass": "1",
"isParent": "0",
"count": 0,
"children": []
}
]
},
{
"id": "AIR_03",
"itemName": "测试节点-02",
"itemLevel": 2,
"parentItem": "AIR",
"itemIndex": "31",
"itemClass": "1",
"isParent": "1",
"count": 0,
"children": [{
"id": "AIR_03_01",
"itemName": "测试节点-03",
"itemLevel": 3,
"parentItem": "AIR_03",
"itemIndex": "42",
"itemClass": "1",
"isParent": "0",
"count": 0,
"children": []
}]
},
{
"id": "AIR_02",
"itemName": "测试节点-02",
"itemLevel": 2,
"parentItem": "AIR",
"itemIndex": "30",
"itemClass": "1",
"isParent": "1",
"count": 174,
"children": [{
"id": "AIR_02_05",
"itemName": "测试节点-03",
"itemLevel": 3,
"parentItem": "AIR_02",
"itemIndex": "41",
"itemClass": "1",
"isParent": "0",
"count": 0,
"children": []
},
{
"id": "AIR_02_04",
"itemName": "测试节点-03",
"itemLevel": 3,
"parentItem": "AIR_02",
"itemIndex": "40",
"itemClass": "1",
"isParent": "0",
"count": 119,
"children": []
},
{
"id": "AIR_02_03",
"itemName": "测试节点-03",
"itemLevel": 3,
"parentItem": "AIR_02",
"itemIndex": "39",
"itemClass": "1",
"isParent": "0",
"count": 36,
"children": []
},
{
"id": "AIR_02_02",
"itemName": "测试节点-03",
"itemLevel": 3,
"parentItem": "AIR_02",
"itemIndex": "38",
"itemClass": "1",
"isParent": "0",
"count": 19,
"children": []
},
{
"id": "AIR_02_01",
"itemName": "测试节点-03",
"itemLevel": 3,
"parentItem": "AIR_02",
"itemIndex": "37",
"itemClass": "1",
"isParent": "0",
"count": 0,
"children": []
}
]
},
{
"id": "AIR_01",
"itemName": "测试节点-02",
"itemLevel": 2,
"parentItem": "AIR",
"itemIndex": "29",
"itemClass": "1",
"isParent": "1",
"count": 3995,
"children": [{
"id": "AIR_01_05",
"itemName": "测试节点-03",
"itemLevel": 3,
"parentItem": "AIR_01",
"itemIndex": "48",
"itemClass": "1",
"isParent": "0",
"count": 0,
"children": []
},
{
"id": "AIR_01_04",
"itemName": "测试节点-03",
"itemLevel": 3,
"parentItem": "AIR_01",
"itemIndex": "36",
"itemClass": "1",
"isParent": "0",
"count": 77,
"children": []
},
{
"id": "AIR_01_03",
"itemName": "测试节点-03",
"itemLevel": 3,
"parentItem": "AIR_01",
"itemIndex": "35",
"itemClass": "1",
"isParent": "0",
"count": 3852,
"children": []
},
{
"id": "AIR_01_02",
"itemName": "测试节点-03",
"itemLevel": 3,
"parentItem": "AIR_01",
"itemIndex": "34",
"itemClass": "1",
"isParent": "0",
"count": 0,
"children": []
},
{
"id": "AIR_01_01",
"itemName": "测试节点-03",
"itemLevel": 3,
"parentItem": "AIR_01",
"itemIndex": "33",
"itemClass": "1",
"isParent": "0",
"count": 66,
"children": []
}
]
}
]
},
{
"id": "PWXK",
"itemName": "测试节点-01",
"itemLevel": 1,
"parentItem": "JDBF",
"itemIndex": "2",
"itemClass": "1",
"isParent": "1",
"count": 5236,
"children": [{
"id": "PWXK_002",
"itemName": "测试节点-02",
"itemLevel": 2,
"parentItem": "PWXK",
"itemIndex": "6",
"itemClass": "1",
"isParent": "0",
"count": 124,
"children": []
},
{
"id": "PWXK_004",
"itemName": "测试节点-02",
"itemLevel": 2,
"parentItem": "PWXK",
"itemIndex": "8",
"itemClass": "1",
"isParent": "0",
"count": 2172,
"children": []
},
{
"id": "PWXK_003",
"itemName": "测试节点-02",
"itemLevel": 2,
"parentItem": "PWXK",
"itemIndex": "7",
"itemClass": "1",
"isParent": "0",
"count": 0,
"children": []
},
{
"id": "PWXK_001",
"itemName": "测试节点-02",
"itemLevel": 2,
"parentItem": "PWXK",
"itemIndex": "5",
"itemClass": "1",
"isParent": "0",
"count": 158,
"children": []
},
{
"id": "PWXK_005",
"itemName": "测试节点-02",
"itemLevel": 2,
"parentItem": "PWXK",
"itemIndex": "9",
"itemClass": "1",
"isParent": "0",
"count": 2782,
"children": []
}
]
},
{
"id": "WATER",
"itemName": "测试节点-01",
"itemLevel": 1,
"parentItem": "JDBF",
"itemIndex": "3",
"itemClass": "1",
"isParent": "1",
"count": 7275,
"children": [{
"id": "WATER_03",
"itemName": "测试节点-02",
"itemLevel": 2,
"parentItem": "WATER",
"itemIndex": "12",
"itemClass": "1",
"isParent": "1",
"count": 151,
"children": [{
"id": "WATER_03_01",
"itemName": "测试节点-03",
"itemLevel": 3,
"parentItem": "WATER_03",
"itemIndex": "26",
"itemClass": "1",
"isParent": "0",
"count": 151,
"children": []
}]
},
{
"id": "WATER_04",
"itemName": "测试节点-02",
"itemLevel": 2,
"parentItem": "WATER",
"itemIndex": "13",
"itemClass": "1",
"isParent": "1",
"count": 0,
"children": [{
"id": "WATER_04_02",
"itemName": "测试节点-03",
"itemLevel": 3,
"parentItem": "WATER_04",
"itemIndex": "28",
"itemClass": "1",
"isParent": "0",
"count": 0,
"children": []
},
{
"id": "WATER_04_01",
"itemName": "测试节点-03",
"itemLevel": 3,
"parentItem": "WATER_04",
"itemIndex": "27",
"itemClass": "1",
"isParent": "0",
"count": 0,
"children": []
}
]
},
{
"id": "WATER_02",
"itemName": "测试节点-02",
"itemLevel": 2,
"parentItem": "WATER",
"itemIndex": "11",
"itemClass": "1",
"isParent": "1",
"count": 333,
"children": [{
"id": "WATER_02_05",
"itemName": "测试节点-03",
"itemLevel": 3,
"parentItem": "WATER_02",
"itemIndex": "24",
"itemClass": "1",
"isParent": "0",
"count": 133,
"children": []
},
{
"id": "WATER_02_06",
"itemName": "测试节点-03",
"itemLevel": 3,
"parentItem": "WATER_02",
"itemIndex": "25",
"itemClass": "1",
"isParent": "0",
"count": 0,
"children": []
},
{
"id": "WATER_02_03",
"itemName": "测试节点-03",
"itemLevel": 3,
"parentItem": "WATER_02",
"itemIndex": "22",
"itemClass": "1",
"isParent": "0",
"count": 32,
"children": []
},
{
"id": "WATER_02_04",
"itemName": "测试节点-03",
"itemLevel": 3,
"parentItem": "WATER_02",
"itemIndex": "23",
"itemClass": "1",
"isParent": "0",
"count": 160,
"children": []
},
{
"id": "WATER_02_02",
"itemName": "测试节点-03",
"itemLevel": 3,
"parentItem": "WATER_02",
"itemIndex": "21",
"itemClass": "1",
"isParent": "0",
"count": 8,
"children": []
},
{
"id": "WATER_02_01",
"itemName": "测试节点-03",
"itemLevel": 3,
"parentItem": "WATER_02",
"itemIndex": "20",
"itemClass": "1",
"isParent": "0",
"count": 0,
"children": []
}
]
},
{
"id": "WATER_01",
"itemName": "测试节点-02",
"itemLevel": 2,
"parentItem": "WATER",
"itemIndex": "10",
"itemClass": "1",
"isParent": "1",
"count": 6791,
"children": [{
"id": "WATER_01_08",
"itemName": "测试节点-03",
"itemLevel": 3,
"parentItem": "WATER_01",
"itemIndex": "47",
"itemClass": "1",
"isParent": "0",
"count": 0,
"children": []
},
{
"id": "WATER_01_03",
"itemName": "测试节点-03",
"itemLevel": 3,
"parentItem": "WATER_01",
"itemIndex": "16",
"itemClass": "1",
"isParent": "0",
"count": 5129,
"children": []
},
{
"id": "WATER_01_07",
"itemName": "测试节点-03",
"itemLevel": 3,
"parentItem": "WATER_01",
"itemIndex": "19",
"itemClass": "1",
"isParent": "0",
"count": 1,
"children": []
},
{
"id": "WATER_01_05",
"itemName": "测试节点-03",
"itemLevel": 3,
"parentItem": "WATER_01",
"itemIndex": "18",
"itemClass": "1",
"isParent": "0",
"count": 0,
"children": []
},
{
"id": "WATER_01_04",
"itemName": "测试节点-03",
"itemLevel": 3,
"parentItem": "WATER_01",
"itemIndex": "17",
"itemClass": "1",
"isParent": "0",
"count": 1643,
"children": []
},
{
"id": "WATER_01_02",
"itemName": "测试节点-03",
"itemLevel": 3,
"parentItem": "WATER_01",
"itemIndex": "15",
"itemClass": "1",
"isParent": "0",
"count": 3,
"children": []
},
{
"id": "WATER_01_01",
"itemName": "测试节点-03",
"itemLevel": 3,
"parentItem": "WATER_01",
"itemIndex": "14",
"itemClass": "1",
"isParent": "1",
"count": 15,
"children": []
}
]
}
]
}
]
},
{
"id": "RCZL",
"itemName": "测试节点-01",
"itemLevel": 1,
"parentItem": "ALL",
"itemIndex": "1",
"itemClass": "2",
"isParent": "1",
"count": 0,
"children": []
}
]
}
},
methods: {
change({ checked, data }) {}
}
}
</script>
放出来一下效果图
用到的图标资源
标签:count,itemLevel,tree,isParent,联动,组件,itemName,children,parentItem From: https://www.cnblogs.com/yeminglong/p/18436400