vue3 递归无限分类树型菜单+搜索功能

2023-10-26

我们先来看一下大致实现效果,数据可以无限向下增加,搜索关键字会自动展开数据

vue3树形结构菜单+搜索

首先我这个需要自己设计数据源,一定要先搞清楚数据是什么结构才能顺利开展下一步(有接口的同学可以忽略这一步~)

其中children顾名思义,子节点,相当于树的分叉,就这样一级一级的生成,就是递归生成后的树形结构数据

[{
	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',
		}]
	}]
}]

明白数据结构之后,我们就正式开始啦~

1、在components中创建xx.vue文件,通过props来传值

<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>
	export default {
		name: 'DefaultCompoent',
		props: {
			data: { type: Array }
		}
	}
</script>

2、在app.vue或者其他你想使用的页面引入刚刚定义的组件

<template>
	<section>
		<DefaultCompoent :data="tree.Arr"></DefaultCompoent>
	</section>
</template>

<script setup>
    import {ref,reactive} from "vue";
    import DefaultCompoent from '/src/components/DefaultTree.vue';//引入自定义文件,路径一定要引对!!!

    //数据
    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',
					}]
				}]
			}
		],
	});
</script>

目前,我们的数据已经引好,就是下面的效果

 3、接下来,我们需要使用Pinia,关于Pinia的介绍以及引用,请移步我另一篇博客

https://blog.csdn.net/m0_64397933/article/details/125185496?spm=1001.2014.3001.5502

给页面的数据添加点击事件,传本条数据对应的id

// 改变状态
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);
    }
};

4、搜索功能

<template>
	<!-- 搜索 -->
	<el-input v-model="input" placeholder="请输入搜索内容" @change="changes" />
	<router-view />
</template>

<script setup>
// 查找字段
	var num = 1;
	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 != "") {
			finds(arr, input.value, arr);
		} else {
			tree.arr = JSON.parse(JSON.stringify(tree.Arr));
			pinia.closeList = [];
			pinia.searchs = [];
		}
	};
    //监听输入框的值,当输入框的值为空时,数据显示默认状态
	watch(input, (newName, oldName) => {
	  console.log(newName);
	  if(newName==''){
		  pinia.closeList = [];
		  pinia.searchs = [];
	  }
	});
</script>

下面页面完整代码给大家~

components文件下xx.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>

app.vue源码

<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 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);
		}
	};
    //监听输入框的值,当输入框的值为空时,数据显示默认状态
	watch(input, (newName, oldName) => {
	  console.log(newName);
	  if(newName==''){
		  pinia.closeList = [];
		  pinia.searchs = [];
	  }
	});
</script>

<style>

</style>

如有不对,或者更好的方法,欢迎指出~

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

vue3 递归无限分类树型菜单+搜索功能 的相关文章

  • 有什么方法可以快速确定浏览器是否支持启用 CORS 的图像而不污染浏览器?

    是否有一个快速测试来确定浏览器是否支持启用 CORS 的图像 并且在其上绘制时不会污染画布 我知道 Chrome 15 支持此功能 Firefox 9Beta 但不支持 Firefox 8 Safari 不支持 IE9 不支持 但必须有一个
  • 如何将正文中的数字替换为波斯数字?

    我想将 html 内容中的每个数字转换为波斯数字 而不会对页面元素产生其他影响 例如 div style color c2c2c2 text number 1 span text number 2 span div text number
  • 如何在 Angular JS 中显示以字节数组形式接收的图像

    我有一个将返回图像的服务器端应用程序 这些是响应标头 Content Disposition attachment filename 8822a009 944e 43f4 999b d297198d302a 1 0 low res Cont
  • React-Native 博览会 POST Blob

    我正在使用 React Native 和 expo 并尝试通过 fetch api 发布 blob 图像 我对正文使用表单数据格式 并且我有下一个代码 const blob await response blob const form ne
  • 在 javascript/jquery 中获取图像的完整尺寸

    我在页面上有一个图像 该图像已调整大小以适合 div 例如 400x300 如何在 jQuery 中获取图像的完整尺寸 4000x3000 width 和 height 似乎只返回图像的当前大小 图像有naturalWidth and na
  • Javascript RegEx 替换所有不在 HTML 标签内的字符

    寻求一些帮助 我的正则表达式有点生锈 我试图用一个字符替换 javascript 中 HTML 标签之外的所有字符 例如 用破折号 替换这些字符 div class test Lorem Ipsum br Dolor Sit Amet di
  • 在 php 中进行 AES 加密,然后用 Javascript (cryptojs) 解密

    我正在寻找一种对简单文本 5 到 6 个数字和 或字符 进行双向加密的方法 问题是我想在 php 中进行加密 然后通过 Javascript 解密 对于 php 我已经使用 mcrypt encode 进行了测试并使其正常工作 因此当我尝试
  • 如何让 Rails 资产管道生成源地图?

    我想让 Rails 与编译后的 CoffeeScript 缩小的 JS 一起生成源映射 以便更好地记录错误 不过 网上似乎还没有关于如何执行此操作的全面文档 有人这样做过吗 我使用 Rails 3 2 和 Heroku Rails 支持缩小
  • 如何在 React JS 组件中预加载图像?

    我目前正在渲染一个子组件signInError发生 这signInError存储在父组件中 如果不为 null 则渲染
  • 规则和夏令时

    我在用着rrule https github com jakubroztocil rrule在我的数据库中创建和存储事件 一切正常 直到我发现我的重复事件与 3 月 31 日之后有一个小时的差异 在法国 这一天我们进行夏令时更改 实际上 我
  • jQuery:评估 ajax 响应中的脚本

    来自我的 web 应用程序的 XML 响应既有要添加到页面的 HTML 也有要运行的脚本 我正在尝试从我的网络应用程序发回 XML 例如
  • 计算网站上多个文件的下载次数的最佳方法[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 问题是 计算网站上多个文件的下载次数的最佳方法 我正在尝试做的事情 跟踪并统计多个文件的下载数量 对于具有不同扩展名的文件 foo z
  • 解决多个 jQuery 文件之间的冲突

    我的项目中有多个 jquery 文件 我正在使用jquery1 4 2使用facebox 但我也需要原型和scriptacolous脚本 我用过 jQuery noconflict 在我的代码中 但它不起作用 这是网址http mlep c
  • 对大数据块进行反应非阻塞渲染

    最近我开始学习反应并想知道是否有某种模式可以用于大数据的非阻塞 UI 线程渲染 比方说 我们采取这个例子 https www mendix com tech blog making react reactive pursuit high p
  • 现已弃用使用 Google Places API 获取多种类型

    谷歌最近宣布 自 2016 年 2 月 16 日起 types 参数已被弃用 取而代之的是新的类型参数 每个搜索请求仅支持一种类型 我的问题是 现在有什么方法 不使用已弃用的参数 从单个 API 调用中获取多个地点类型吗 谢谢 None
  • Nodejs Base64 中的读取文件

    我正在尝试从客户端读取以 base64 编码的图像 如何使用nodejs进行阅读 My code add to buffer base64 image var encondedImage new Buffer image name base
  • datatables.search 函数修改后的奇怪行为

    这个问题是后续问题这个问题 https stackoverflow com questions 54671211 overriding datatables js search behavior 我已经创建了这个 JSFiddle http
  • 过早退出 Qualtrics 中的循环和合并块

    我目前正在进行一项 Qualtrics 调查 受访者必须解决一长串字谜问题 然后回答一些人口统计问题 为了使变位词部分更容易 我使用了循环和合并块 第一个字段是要解决的变位词 第二个字段是变位词的解决方案 因此调查可以根据受访者的答案来检查
  • 让 hashchange 事件在所有浏览器(包括 IE7)中工作

    我有一些代码 由另一位开发人员编写 在 WordPress 内部进行 AJAX 页面加载 例如 没有页面重新加载 当您单击导航项时 AJAX 会刷新主要内容区域 我的问题是它在 IE7 中被破坏了 我不知道从哪里开始调试 最初的开场白是 v
  • 如何在 JavaScript 中将样式属性重置为其 CSS 默认值?

    在 php 生成的页面上有几个这样的元素 td class defaultTDStyle style color userDefinedCustomColor td 因此有一个默认样式 我应用了几个额外的样式来覆盖 CSS 中定义的样式 有

随机推荐

  • CSAPP malloclab实验

    书本配套实验地址 构造一个分配器是一件富有挑战的任务 设计空间很大 有多种块格式 空闲链表格式 以及放置 分割和合并策略可供选择 另一个挑战就是我们经常被迫在类型系统的安全和熟悉的限定之外编程 依赖于容易出错的指针强制类型转换和指针运算 这
  • FreeRTOS记录(五、FreeRTOS任务通知)

    在前面几篇文章我们已经对FreeRTOS任务API和任务调度原理进行了相对深入的分析 这篇文章主要针对任务与任务之间的交互 信息传递相关的API组件进行分析 目录 一 任务通知基本介绍 1 FreeRTOS 任务通知函数 2 CMSIS封装
  • Android调用打印机

    打印机其实和Android没有什么大的关系 和linux内核关联才是比较强的 最终的结果是要在Android实现驱动打印机 但是一般调试一个新的驱动的流程是这样的 1 先在linux PC上进行测试 2 在标准嵌入式linux上进行调试 3
  • MFC原理与方法(二)

    MFC原理与方法 二 一 前言 二 类向导的使用 三 MFC消息管理 1 MFC消息映射机制 2 消息处理 四 结语 一 前言 时间过得好快啊 又是一个星期过去了 我又回来啦 每个星期保持写博客的习惯 及时消化上课的知识 不仅仅对我有帮助和
  • MySQL进阶篇之存储过程(procedure)

    04 视图 存储过程 触发器 4 1 视图 view 4 2 存储过程 procedure 4 2 1 介绍 1 介绍 存储过程是事先经过编译并存储在数据库中的一段SQL语句的集合 调用存储过程可以简化应用开发人员的很多工作 减少数据在数据
  • 异常处理的返回

    异常处理的返回 异常可以分为四类 中断 interrupt 陷阱 trap 故障 fault 和终止 abort 这几种异常处理之后又有不同的返回方式 总的来讲 类别 原因 异步 同步 返回行为 中断 来自I O设备的信号 异步 总是返回到
  • 面试3个月拿下多家大厂的P7技术专家Offer,来看我面试复盘!

    一 概述 之前写过两篇文章 工作10年我面试过上百个程序员 真想对他们说 在公司里写代码天天摸鱼偷懒 出去面试又该怎么写简历 通过这两篇文章 我们给大家聊了聊国内中大型互联网公司 在Java面试时一些高频的技术问题 本文我们通过一篇真实的一
  • 【状态估计】用于非标量系统估计的最优卡尔曼滤波(Matlab代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码实现 1 概述 考虑了最优卡尔曼滤波的例子 假设一些非标量
  • vscode运行命令是报错:标记“&&”不是此版本中的有效语句分隔符。

    问题截图 问题原因 这个问题的原因和你运行的什么脚本语言没关系 即与 py c cpp无关 和你在那个终端运行的有关 解决方法 第一步 点击向下箭头 并选择 选择默认配置文件 第二步 选择 Windows PowerShell 第三步 关闭
  • 数字IC手撕代码-边沿检测(上升沿、下降沿、双边沿)

    前言 本专栏旨在记录高频笔面试手撕代码题 以备数字前端秋招 本专栏所有文章提供原理分析 代码及波形 所有代码均经过本人验证 目录如下 1 数字IC手撕代码 分频器 任意偶数分频 2 数字IC手撕代码 分频器 任意奇数分频 3 数字IC手撕代
  • 2021羊城杯CTF wp

    2021羊城杯 部分 wp Web web1 only 4 web2 EasyCurl web3 Checkin Go web4 Cross The Side Re Pwn BabyRop Crypto Miss bigrsa Misc M
  • FISCO-BCOS如何把WEBASE部署通过的合约方法由api在前端调用

    参考文章 fisco bcos官方文档第五章部分 通过POST请求 数据格式要为json 调用hello合约中的get方法 按要求填写需要的信息
  • 决策树的学习

    决策树 从名字上看 就知道其模型的结构为树结构 决策树既可以用于分类 也可以用于回归之中 在分类问题中 我们可以认为其是if then规则的集合 也可以认为是定义在特征空间与类空间上的条件概率分布 在学习过程中 利用训练数据和损失函数最小化
  • 因果推理的do算子

    在因果推理中 我们一般都需要首先构建一个因果图 这是后续进行因果分析的基础 但是在现阶段笔者的知识看来 因果图的构建其实是一个比较主观的过程 但偏偏又是后续分析的基础 所以略感头疼 在构建因果图前 我们有必要明白 什么是因果关系 通俗来说
  • 【JUC并发编程】

    本笔记内容为狂神说JUC并发编程部分 目录 一 什么是JUC 二 线程和进程 1 概述 2 并发 并行 3 线程有几个状态 4 wait sleep 区别 三 Lock锁 重点 四 生产者和消费者问题 五 八锁现象 六 集合类不安全 七 C
  • 统计字符串中,中文字符、英文字符和数字字符的数量

    package com suanfa public class ZYSTotal 统计字符串中 中文字符 英文字符和数字字符的数量 public static void main String args int englishCount 0
  • 指针和数组的相关练习题

    目录 一 一维数组 二 字符数组 三 二维数组 注意 假设本练习题所用的VS编译器是64位平台下的 首先要明白数组名的意义 1 sizeof 数组名 这里的数组名表示整个数组 计算的是整个数组的大小 2 数组名 这里的数组名表示整个数组 取
  • 帆软之图表详解

    帆软之图表详解 饼图 饼图 玫瑰图 玫瑰图和饼图类似 仅选择不同的图例即可 多分类饼图 注 标题居中不是直接显示标题居中 而是隐藏标题偶按照下面的方法将标题加上去 柱状图 柱状图设置柱子宽度 boby 样式 系列 固定柱宽 注意事项 问题描
  • 4.3寸串口屏在智能炒菜机上应用分享

    现代人追求高效品质生活的美好愿望以及社会科技的不断发展持续推动着一种新兴经济形态的出现 即懒人经济 懒人经济的崛起也成为智能家电行业新的增长引擎 自动炒菜机便是这一经济形态下的产物 对于很多居住于快节奏生活的一二线城市人来说 在辛苦工作一整
  • vue3 递归无限分类树型菜单+搜索功能

    我们先来看一下大致实现效果 数据可以无限向下增加 搜索关键字会自动展开数据 vue3树形结构菜单 搜索 首先我这个需要自己设计数据源 一定要先搞清楚数据是什么结构才能顺利开展下一步 有接口的同学可以忽略这一步 其中children顾名思义