我们来看一下大致实现效果,数据可以无限向下增加,搜索关键字会自动展开数据
vue3树形结构菜单+搜索
1,在components文件夹下创建vue文件,自定义递归组件
<template>
<ul v-for="(item) in data" :key="item.id">
<li>
<div @click="open(item.id)">
<p>{{item.name}}</p>
</div>
</li>
<DefaultCompoent v-if="item.children&&value.includes(item.id)" :data="item.children" :key="item.id"></DefaultCompoent>
</ul>
</template>
<script>
import {
computed
} from "vue";
import {
PiniaStore
} from "/src/store/index.js";
export default {
name: 'DefaultCompoent',
props: {
data: { type: Array }
},
setup(props) {
// console.log(props);
// 改变状态
const pinia = PiniaStore();
const checked = computed(() => {
return pinia.searchs.length ? pinia.searchs : [];
})
const value = computed(() => {
return pinia.closeList
})
// 打开关闭点击事件
const open = (id) => {
console.log(id);
if (pinia.closeList.includes(id)) {
pinia.closeList.splice(pinia.closeList.indexOf(id));
} else {
pinia.closeList.push(id);
}
};
return {
open,
event,
checked,
value
}
}
}
</script>
<style>
</style>
2、主页面js部分
<template>
<section>
<DefaultCompoent :data="tree.Arr"></DefaultCompoent>
</section>
<!-- 搜索框 -->
<el-input v-model="input" placeholder="请输入搜索内容" @change="changes" />
<router-view />
</template>
<script setup>
import {
ref,
reactive,
watch
} from "vue";
// import axios from "axios";
import DefaultCompoent from '/src/components/DefaultTree.vue';
import {
PiniaStore
} from "/src/store/index.js";
const pinia = PiniaStore();
const input = ref(''); //输入框
//模拟数据
const tree = reactive({
Arr: [{
id: '1',
name: '两栖',
children: [{
id: '11',
name: '鸟类',
children: [{
id: '111',
name: '飞行',
children: [{
id: '1111',
name: '小鸟',
}]
}]
}]
},
{
id: '2',
name: '2',
children: [{
id: '22',
name: '22',
children: [{
id: '222',
name: '222',
}]
}]
}
],
});
// 查找字段
var num = 1;
// console.log(data);
const finds = (arr, field, original) => {
console.log(arr);
//正则匹配,只要存在该字段就会匹配成功
let reg = new RegExp(field);
for (let i = 0; i < arr.length; i++) {
let res = field,
raw = original;
if (reg.test(arr[i].name)) {
pinia.searchs.push(arr[i].id);
num = 1; //每次匹配成功初始num
unfold(original, arr[i]);
if (arr[i].children) {
// 若有匹配成功的字段就继续
finds(arr[i].children, res, raw);
}
} else {
//若匹配不成功,并且还有下级则再次调用自己并将下级数组传过去
if (arr[i].children != undefined && arr[i].children.length != 0) {
finds(arr[i].children, res, raw);
}
}
}
};
// 将匹配到的数据id分割拿到这条数据的所有上级id
const unfold = (raw, data) => {
console.log(raw, data);
if (raw != undefined && raw.length != 0) {
// let news = [...raw];
let id = data.id.slice(0, num); //切割id
// console.log(id);
pinia.closeList.push(id); //全局状态就拿到这条数据上级的所有id包括自己
let res = raw.findIndex((val) => val.id == id);
if (res != -1 && raw[res].children != undefined) {
//这条数据还有下级则继续分割id
num += 1;
let obj = data;
unfold(raw[res].children, obj);
}
tree.arr = raw;
} else {
num = 1;
}
};
const changes = () => {
// console.log(input.value)
// 初始数据
tree.arr = [];
pinia.closeList = [];
pinia.searchs = [];
num = 1;
let arr = JSON.parse(JSON.stringify(tree.Arr));
console.log(arr);
if (input.value != "") {
console.log(arr);
finds(arr, input.value, arr);
} else {
tree.arr = JSON.parse(JSON.stringify(tree.Arr));
pinia.closeList = [];
pinia.searchs = [];
console.log(tree.arr);
}
};
//监听input输入的值,当输入框的值为空,树形结构自动闭合
watch(input, (newName, oldName) => {
console.log(newName);
if(newName==''){
pinia.closeList = [];
pinia.searchs = [];
}
});
</script>
<style>
</style>
ps:其中请求数据部分,我用的是模拟数据,大家可以根据自己的需求来换成接口数据;这只是一个小demo,没有过多修饰样式,有需要的小伙伴可自行调整下样式~
大致就是以上内容啦~如有不对,或者更好的解决的方案,可以一起交流