uniapp+vue3中 实现无限递归树形组件,可配置单选、多选,可配置允许选择任意节点或只允许选择最底层节点
·
注释很详细开箱即用
可配置单选、多选,可配置允许选择任意节点或只允许选择最底层节点

<template>
<view class="container1">
<scroll-view :scroll-y="true">
<view class="container1-page" v-for="(item, index) in arrays" :key="item.id">
<view v-if="item.children?.length" class="department">
<view class="department-header" @click="toggleExpand(item)">
<view class="department-header-left">
<up-icon v-if="isExpanded(item)" name="arrow-down-fill" size="12" color="#bfbfbf" class="icon-expand" />
<up-icon v-else name="play-right-fill" size="12" color="#bfbfbf" class="icon-expand" />
<span class="node-title">{{ item.name }}</span>
</view>
<view v-if="!props.onlyLeaf" class="checkbox checkbox-parent" :class="{ 'checkbox-checked': isSelected(item) }" @click.stop="toggleSelection(item)">
<view class="checkbox-inner"></view>
</view>
</view>
<jcTree v-if="isExpanded(item)" class="indent-child" :arrays="item.children" @toggleSelection="getData" />
</view>
<view v-else class="employee">
<view class="employee-content">
<up-icon name="tags" size="19" color="#bfbfbf" class="icon-tags" />
<span class="node-title">{{ item.name }}</span>
</view>
<view class="checkbox" :class="{ 'checkbox-checked': isSelected(item) }" @click="toggleSelection(item)">
<view class="checkbox-inner"></view>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script lang="ts" setup>
import { ref, defineEmits } from "vue";
// 组件配置(解决递归组件名称问题)
defineOptions({
name: "jcTree",
});
const props = defineProps({
arrays: {
// 树形数据结构,格式示例:[{id:'',name:'',children:[]}]
type: Array,
default: () => [],
} as any,
isCheckbox: {
// 选择模式控制:true=多选模式,false=单选模式
type: Boolean,
default: false,
},
onlyLeaf: {
//是否可以选择任意层级:true=只允许选择叶子节点,false=允许选择任意层级
type: Boolean,
default: false, // 默认只允许选择叶子节点
},
});
const selectedEmployees = ref<any[]>([]); // 已选节点集合
const expandedNodes = ref<any[]>([]); // 展开节点ID集合
const emits = defineEmits(["toggleSelection"]);
/**
* 切换节点选中状态
* @param node - 当前操作的节点对象
*
* 逻辑说明:
* - 多选模式:切换节点的选中状态
* - 单选模式:始终只保留最后一个选中节点
*/
const toggleSelection = (node: any) => {
if (props.isCheckbox) {
const index = selectedEmployees.value.findIndex((emp) => emp.id === node.id);
if (index !== -1) {
selectedEmployees.value.splice(index, 1);
} else {
selectedEmployees.value.push({ id: node.id, name: node.name });
}
} else {
selectedEmployees.value = [{ id: node.id, name: node.name }];
}
emits("toggleSelection", selectedEmployees.value);
};
/**
* 处理子节点事件
* @param item - 来自子组件的事件数据
*
* 设计说明:
* - 多选模式:操作最后一个节点保持多选特性
* - 单选模式:始终操作第一个节点保持单选特性
*/
const getData = (item: any) => {
props.isCheckbox ? toggleSelection(item[item.length - 1]) : toggleSelection(item[0]);
};
/**
* 判断节点是否被选中
* @param node - 需要检测的节点对象
*/
const isSelected = (node: any) => {
return selectedEmployees.value.some((emp) => emp.id === node.id);
};
/**
* 切换节点展开状态
* @param node - 需要切换展开状态的节点
*
* 实现方式:
* - 通过维护expandedNodes数组记录已展开节点ID
*/
const toggleExpand = (node: any) => {
const index = expandedNodes.value.indexOf(node.id);
index === -1 ? expandedNodes.value.push(node.id) : expandedNodes.value.splice(index, 1);
};
/**
* 判断节点是否展开
* @param node - 需要检测的节点对象
*/
const isExpanded = (node: any) => {
return expandedNodes.value.includes(node.id);
};
</script>
<style lang="scss" scoped>
.container1 {
overflow-y: auto;
&-page {
.department {
font-weight: bold;
&-header {
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
color: #747272;
margin: 12px 0;
&-left {
display: flex;
align-items: center;
.icon-expand {
margin-right: 5px;
}
}
.checkbox-parent {
margin-left: 10px;
}
}
}
.employee {
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
color: #747272;
margin: 12px 0;
&-content {
display: flex;
align-items: center;
.icon-tags {
margin-right: 5px;
}
}
}
.node-title {
font-size: 14px;
}
.indent-child {
margin-left: 10px;
}
}
}
.checkbox {
width: 18px;
height: 18px;
border: 1px solid #3c9cff;
border-radius: 2px;
display: flex;
align-items: center;
justify-content: center;
&-inner {
width: 11px;
height: 11px;
border-radius: 1px;
}
&-checked {
.checkbox-inner {
background-color: #3c9cff;
}
}
}
</style>
在父组件中使用
<tyTree style="max-height: 500px" :arrays="data" @toggleSelection="toggleSelection" />
const data = ref([
{
id: "1",
name: "A",
children: [
{
id: "1-1",
name: "A-1",
children: [],
},
{
id: "1-2",
name: "A-2",
children: [],
},
],
},
{
id: "2",
name: "B",
children: [
{
id: "2-1",
name: "B-1",
children: [],
},
{
id: "2-2",
name: "B-2",
children: [],
},
],
},
{
id: "3",
name: "C",
children: [
{
id: "3-1",
name: "C-1",
children: [
{
id: "3-1-1",
name: "C-1-1",
children: [],
},
{
id: "3-1-2",
name: "C-1-2",
children: [],
}
],
},
{
id: "3-2",
name: "C-2",
children: [],
},
],
},
{ id: "4", name: "d", children: [] },
]);
const toggleSelection = (data: any) => {
//处理数据
};
更多推荐


所有评论(0)