树形结构功能
思路,采用递归组件, 子组件emit 给父组件改变半选,权限状态
本页面启用了 reactivityTransform: true
父组件
Demo.vue
vue
<script lang="ts" setup>
import { ref } from 'vue'
import Row from './Row.vue'
const isSelectAll = ref(false)
const isIndeterminate = ref(false)
const nodeTree = ref([])
function handleAllStatus() {
isSelectAll.value = nodeTree.value.length && nodeTree.value.every(e => e.active)
isIndeterminate.value
= !(isSelectAll.value || nodeTree.value.every(e => !e.active))
|| nodeTree.value.some(e => e.indeterminate) // 子类有半选设置父级半选;
}
</script>
<template>
<Row :items="nodeTree" @change="handleAllStatus" />
</template>
递归组件
Row.vue
文件
vue
<script setup lang="ts">
const { items = [] } = defineProps<{
items: Record<string, string>[]
}>()
const emit = defineEmits<{
(e: 'change', val: [boolean, boolean]): void
}>()
/**
* 递归改变子类状态
*/
function recursion(nodeItem, nodeActive) {
if (nodeItem.length === 0)
return
nodeItem.forEach((item) => {
item.active = nodeActive
item.indeterminate = false
if (item.child && item.child.length)
recursion(item.child, item.active)
})
}
function handleChange(val, node) {
const [active, isAllChild] = Array.isArray(val) ? val : [val]
if (node) {
handleSetStatus(node)
if (!isAllChild && node.child && node.child.length) {
node.active = active
node.indeterminate = false
recursion(node.child, active)
emit('change', [active, true])
return
}
}
emit('change', [active, true])
}
/**
* 修改状态
*/
function handleSetStatus(node) {
node.active = node.child.every(e => e.active)
node.indeterminate
= !(node.active || node.child.every(e => !e.active))
|| node.child.some(e => e.indeterminate) // 子类有半选设置父级半选
// vue2 使用
// this.$forceUpdate()
}
</script>
<template>
<div class="node-wrap">
<template v-for="nodeItem in items">
<div
v-if="nodeItem.child && nodeItem.child.length"
:key="nodeItem.node_id"
class="node-row"
>
<div class="node-main">
<el-checkbox
v-model="nodeItem.active"
:indeterminate="nodeItem.indeterminate"
@change="handleChange($event, nodeItem)"
>
{{ nodeItem.node_name }}
</el-checkbox>
</div>
<row
class="node-child"
:items="nodeItem.child"
@change="handleChange($event, nodeItem)"
/>
</div>
<div v-else :key="nodeItem.node_id" class="node-item">
<el-checkbox
v-model="nodeItem.active"
@change="handleChange($event)"
>
{{ nodeItem.node_name }}
</el-checkbox>
</div>
</template>
</div>
</template>
<style lang="less" scoped>
.node-wrap :not(.node-row).node-child {
flex-direction: row;
flex-wrap: wrap;
align-items: center;
height: 100%;
}
.node-row {
width: 100%;
display: flex;
align-items: center;
border-top: 1px solid #ebeef5;
.node-row:first-of-type {
border-top: none;
}
}
.node-main {
flex: 1;
min-width: 130px;
height: 100%;
display: flex;
justify-content: left;
padding: 6px 20px 6px 20px;
line-height: 50px;
& + .node-child {
display: flex;
flex: 9;
flex-direction: column;
border-left: 1px solid #ebeef5;
}
}
.node-item {
display: inline-block;
padding: 6px 20px 6px 20px;
line-height: 50px;
}
</style>