Vue3 + TS + Element-Plus 封装Tree组件 《亲测可用》

news2024/11/26 0:44:19

9a69fede8b2044a79dd834e3e48f20b4.png前期回顾f8e3cc1a0f694ac2b665ca2ad14c49d7.png

Vite + Vue3 + Ts 《企业级项目》二次封装 el-table、el-pagination、el-tooltip、el-dialog_vue后台管理系统需要二次封装的组件有哪些_彩色之外的博客-CSDN博客封装的功能有哪些?分页、表格排序、文字居中、溢出隐藏、操作列、开关、宽、最小宽、type类型(selection/index/expand)、格式化 、不同页面不同操作列、vuex、vue持久化插件、(此处没有接口所以用到,还涉及了query与params传值区别)子组件说思路:data数据请求接口拿到,表头数据一般也是后台接口,如没,前台可自定义自己写......_vue后台管理系统需要二次封装的组件有哪些https://blog.csdn.net/m0_57904695/article/details/125613767?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168906559516800227493706%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=168906559516800227493706&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-5-125613767-null-null.268^v1^koosearch&utm_term=%E5%B0%81%E8%A3%85&spm=1018.2226.3001.4450

目录

😷  子组件:

😎  父组件:

🥰😉  谢谢观看

 


 

 为了全局使用类型,可以新建

// tree
declare interface Tree {
	id: number;
	label: string;
	children?: Tree[];
	[key: string]: any;
}

😷  子组件:

<template>
	<div>
		<el-button @click="addNode" type="primary">添加节点</el-button>
		<el-button @click="removeNode" type="danger">删除节点</el-button>
		<div class="tree-container">
			<el-tree
				class="tree-line"
				ref="treeRef"
				:indent="0"
				node-key="id"
				:data="treeData"
				:props="defaultProps"
				:check-strictly="checkStrictly"
				:show-checkbox="isShowCheckbox"
				:check-on-click-node="checkOnClickNode"
				:default-expand-all="defaultExpandAll"
				:draggable="isDraggable"
				:allow-drag="allowDrag"
				:allow-drop="allowDrop"
				@node-drag-end="handleDragEnd"
				@node-click="handleNodeClick"
				@node-contextmenu="editNode"
				@check-change="getCheckedAllNodes"
			>
				<template #default="{ node }">
					<i :class="checkIconByNodeLevel(node)" />
					<input
						v-if="showIpt && node.label === curNodLabel"
						ref="inputRef"
						type="text"
						:value="node.label"
						@blur="showIpt = false"
						@keyup.enter="updateNodeLabel($event, node)"
					/>
					<span v-else>{{ node.label }}</span>
				</template>
			</el-tree>
		</div>
	</div>
</template>

<script setup lang="ts">
import { nextTick, ref } from 'vue';
import type Node from 'element-plus/es/components/tree/src/model/node';
const showIpt = ref<boolean>(false); // 是否显示输入框
const curNodLabel = ref<string>(); // 记录右键点击的节点
const inputRef = ref(); // 输入框实例

const treeRef = ref(); // 树实例
// 默认配置
const defaultProps = {
	children: 'children',
	label: 'label',
};
// 判断节点能否被放置 如果返回 false ,节点不能被放置
const allowDrop = () => true;
// 判断节点能否被拖拽 如果返回 false ,节点不能被拖动
const allowDrag = () => true;

// 子组件事件发送
const emits = defineEmits(['eCurNode', 'eCheckedNodes', 'eSaveNodes']);

// 接受父组件传递过来的数据
const props = defineProps({
	// 树型数据
	treeData: {
		type: Array,
		default: () => [],
	},
	// 在显示复选框的情况下,是否严格的遵循父子不互相关联的做法
	checkStrictly: {
		type: Boolean,
		default: () => false,
	},
	// 是否显示复选框
	isShowCheckbox: {
		type: Boolean,
		default: () => true,
	},
	// 选中节点时是否选中复选框
	checkOnClickNode: {
		type: Boolean,
		default: () => true,
	},
	// 是否默认展开所有节点
	defaultExpandAll: {
		type: Boolean,
		default: () => true,
	},
	// 是否开启拖拽节点功能
	isDraggable: {
		type: Boolean,
		default: () => false,
	},
});

// 点击节点时触发
const handleNodeClick = (data: Tree) => {
	// console.log('点击节点时触发 🚀 ==>:', data);
	emits('eCurNode', data);
};

// 删除节点
const removeNode = () => {
	const checkedNodes = treeRef.value.getCheckedNodes();
	if (checkedNodes.length === 0) return alert('请至少勾选一项才能删除节点');
	for (const node of checkedNodes) {
		// 根据节点的id删除节点
		nextTick(() => {
			treeRef.value.remove(node.id, false);
			// 根据接口重新获取树型数据
		});
	}
};

// 右击节点时触发
const editNode = (event: MouseEvent, node: Node) => {
	event.preventDefault();
	curNodLabel.value = node.label;
	showIpt.value = true;
	nextTick(() => {
		inputRef.value.focus();
	});
};

// 更新节点的label
const updateNodeLabel = (e: Event, node: Tree) => {
	const target = e.target as HTMLInputElement;
	// 递归树 如果target.value有重复的label,就不允许修改
	if (isValueInTree(props.treeData, target.value)) return alert('该节点已存在');
	// 浅拷贝只会影响引用类型的属性,而不会影响基本类型的属性。当浅拷贝一个对象时,基本类型的属性会被复制而不是引用
	// 浅拷贝只有是引用类型才会 两个对象相互影响,如果是基本类型不会互相影响
	node = Object.assign({}, node);
	node.data.label = target.value;
	showIpt.value = false;
};
function isValueInTree(data: string | any[], value: string) {
	for (let i = 0; i < data.length; i++) {
		if (data[i].label === value) {
			return true; // 如果找到匹配项,则返回 true
		}
		// 如果当前节点有子节点,则递归调用遍历子节点
		if (Array.isArray(data[i].children)) {
			if (isValueInTree(data[i].children, value)) {
				return true; // 如果在子节点中找到匹配项,则返回 true
			}
		}
	}
	return false; // 如果遍历完所有节点都没有找到匹配项,则返回 false
}

// 新增节点
const addNode = () => {
	const checkedNodes = treeRef.value.getCheckedNodes();

	if (checkedNodes.length === 0) return alert('请至少勾选一项才能添加节点');

	const nodeName = prompt('请输入节点名称');
	if (!nodeName) return;

	if (isValueInTree(props.treeData, nodeName)) return alert('该节点已存在');

	for (const parentNode of checkedNodes) {
		const newNode = {
			id: props.treeData.length + 1,
			label: nodeName,
		};
		if (!parentNode.children) {
			parentNode.children = [];
		}
		parentNode.children.push(newNode);
	}
};

// 结束拖拽
const handleDragEnd = (dropNode: Node) => {
	if (!dropNode) return;
	if (props.isDraggable === false) return;
	// 保存节点
	saveNode();
};

function saveNode() {
	emits('eSaveNodes', props.treeData);
}

// 复选框改变
const getCheckedAllNodes = (data: Tree, isSelected: boolean) => {
	if (!props.isShowCheckbox) return;
	// 获取所有选中的节点
	const checkedNodes = treeRef.value.getCheckedNodes();
	// 获取所有半选中的节点
	const halfCheckedNodes = treeRef.value.getHalfCheckedNodes();
	// data: 当前节点的数据
	// isSelected: 当前节点是否被选中
	// checkedNodes: 所有选中的节点
	// halfCheckedNodes: 所有半选中的节点
	emits('eCheckedNodes', data, isSelected, checkedNodes, halfCheckedNodes);
};

// 根据节点层级显示不同的图标
const checkIconByNodeLevel = (node: {
	childNodes: [];
	expanded: boolean;
	data: { id: number };
}) => {
	if (node.childNodes.length === 0) return 'iconfont icon-24gl-fileEmpty';
	return node.expanded ? 'iconfont icon-wenjianzhankai' : 'iconfont icon-jian';
};

defineExpose({
	treeRef,
	removeNode,
	addNode,
});
</script>

<style lang="scss" scoped>
@import url('/@/myIcon/iconfont.css');
.tree-container {
	width: 20%;
	height: calc(100vh - 130px);
	background-color: #fff;
	overflow-y: auto;
}
// 树样式
.tree-line {
	::v-deep(.el-tree-node) {
		position: relative;
		// padding-left: 10px; // 缩进量
	}
	::v-deep(.el-tree-node__children) {
		padding-left: 16px; // 缩进量
	}
	// 竖线
	::v-deep(.el-tree-node::before) {
		content: '';
		width: 22px;
		height: 20px;
		position: absolute;
		left: -3px;
		top: -28px;
		border-width: 1px;
		border-left: 1px dashed #ccc;
	}
	// 当前层最后⼀个节点的竖线⾼度固定
	::v-deep(.el-tree-node:last-child::before) {
		height: 38px; // 可以⾃⼰调节到合适数值
	}
	// 横线
	::v-deep(.el-tree-node::after) {
		content: '';
		width: 22px;
		height: 20px;
		position: absolute;
		left: -3px;
		top: 11px;
		border-width: 1px;
		border-top: 1px dashed #ccc;
	}
	// 去掉最顶层的虚线,放最下⾯样式才不会被上⾯的覆盖了
	& > ::v-deep(.el-tree-node::after) {
		border-top: none;
	}
	& > ::v-deep(.el-tree-node::before) {
		border-left: none;
	}
	// 展开关闭的icon
	::v-deep(.el-tree-node__expand-icon) {
		font-size: 16px;
		// 叶⼦节点(⽆⼦节点)
		::v-deep(&.is-leaf) {
			color: transparent;
			display: none;
		}
	}
}
</style>

😎  父组件:

<template>
	<div>
		<zw-tree :treeData="state.treeData" isDraggable @eSaveNodes="onSaveNodes" />
	</div>
</template>

<script setup lang="ts">
import { reactive } from 'vue';
const state = reactive({
	// 树型数据
	treeData: [
		{
			id: 1,
			label: '一级 1',
			children: [
				{
					id: 4,
					label: '二级 1-1',
					children: [
						{
							id: 9,
							label: '三级 1-1-1',
						},
						{
							id: 10,
							label: '三级 1-1-2',
						},
					],
				},
			],
		},
		{
			id: 2,
			label: '一级 2',
			children: [
				{
					id: 5,
					label: '二级 2-1',
				},
				{
					id: 6,
					label: '二级 2-2',
				},
			],
		},
		{
			id: 3,
			label: '一级 3',
			children: [
				{
					id: 7,
					label: '二级 3-1',
				},
				{
					id: 8,
					label: '二级 3-2',
				},
			],
		},
	],
});

function onSaveNodes(data: Tree) {
	console.log(data);
}
</script>

更多的el-tree看这里 🤺👉  自定义《element-UI》el-tree 的样式 、亲测管用_自定义《element-UI》el-tree 的样式 、亲测管用_https://blog.csdn.net/m0_57904695/article/details/123514519?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168906618216800211567162%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=168906618216800211567162&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-123514519-null-null.268^v1^koosearch&utm_term=el-tree&spm=1018.2226.3001.4450彩色之外的博客-csdn博客<>

更多的el-table看这里  😂 

点击《el-table》让选中的行变色,亲测实用_彩色之外的博客-CSDN博客公司各种需求又来了,直接看下面文吧,一看就懂就不在说需求了,此时我觉得我的表情包是【我就像是一个小朋友站在路标下满头的问号】亲测管用,希望可以帮助到大家https://blog.csdn.net/m0_57904695/article/details/123722382?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168906621616782425128470%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=168906621616782425128470&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-9-123722382-null-null.268^v1^koosearch&utm_term=%E8%A1%A8%E6%A0%BC&spm=1018.2226.3001.4450

🥰😉  谢谢观看

7730e2bd39d64179909767e1967da702.jpeg

 _______________________________  期待再见  _______________________________ 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/742975.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Vue H5项目实战】从0到1的自助点餐系统—— 搭建脚手架(Vue3.2 + Vite + TS + Vant + Pinia + Node.js)

前言 H5 项目基于 Web 技术&#xff0c;可以在智能手机、平板电脑等移动设备上的浏览器中运行&#xff0c;无需下载和安装任何应用程序&#xff0c;且H5 项目的代码和资源可以集中在服务器端进行管理&#xff0c;只需更新服务器上的代码&#xff0c;即可让所有顾客访问到最新的…

6.2Java EE——Spring的入门程序

下面通过一个简单的入门程序演示Spring框架的使用&#xff0c;要求在控制台打印“张三&#xff0c;欢迎来到Spring”&#xff0c;实现步骤具体如下。 1、在IDEA中创建名称为chapter06的Maven项目&#xff0c;然后在pom.xml文件中加载需使用到的Spring四个基础包以及Spring依赖…

【域渗透篇】渗透域环境下的windows7与2008-r2

目录 前言 域环境搭建测试 主机发现&&端口服务扫描&&漏洞脚本扫描 主机发现 全端口扫描 漏洞脚本扫描 永恒之蓝获取shell 主机信息收集 查看当前所在域及当前域用户 找到域控制器 收集域成员的IP msf后渗透阶段 Hashdump获取本地密码信息 破解ha…

cesium学习文档

文章目录 1. 简易的cesium安装依赖修改 vite.config.js申请token创建简单的cesium 2. 修改查看器3. 修改摄像头4. 设置纽约城市模型5. 划分城市区域并且着色6. 地图标记显示7. 实现无人机巡城 1. 简易的cesium 安装依赖 yarn add -D cesium vite vite-plugin-cesium修改 vite…

Openlayers实战:小物块运动轨迹动画

Openlayers地图在做轨迹的时候,除了标注各个位置点,连线,还经常会用到轨迹动画。 本实战就是这样示例,一个物块在轨迹上移动。其实质是用setInterval,每个一小段时间,重新计算定位一下小物块位置,整体串起来就是在移动。 效果图 源代码 /* * @Author: 大剑师兰特(xia…

TextMining Day3 基于信息抽取的文本挖掘

TextMining Day3 基于信息抽取的文本挖掘 1. 简介2. 背景:文本挖掘与信息提取3. 数据挖掘与信息提取相结合3.1 DISCOTEX系统3.2 评价 4. 使用挖掘规则改进IE4.1 算法4.2 评价 7. 结论 1. 简介 图1:基于IE&#xff08;信息抽取&#xff09;的文本挖掘框架概述 本文报告了计算机…

stm32 使用CubeIDE 移植RTX5

STM32 使用st的官方开发环境 cubeide &#xff08;eclipse gcc&#xff09;移植 cmsis rtos2 RTX5 实时操作系统 这套环境的主要优势是免费。cubeide免费使用。RTX5 免商业版税&#xff08;已从原keil中剥离出来&#xff0c;现在完全开源免费&#xff09;。 一&#xff0c;环…

微软开源社区上线,能够给微软Win95等“上古系统”打补丁

日前一个基于社区的项目“Windows Update Restored”上线&#xff0c;据了解该项目的目的是为老系统重新提供对Windows Update的支持&#xff0c;可为 Windows 95 / NT 4.0/98(包括 SE)/ME/ 2000 SP2 等“上古时期”的微软操作系统提供升级补丁、修复 bug 或安全漏洞。 据悉&a…

Python+Requests+Excel接口测试实战

1、EXCEL文件接口保存方式&#xff0c;如图。 2、然后就是读取EXCEL文件中的数据方法&#xff0c;如下&#xff1a; 1 import xlrd2 3 4 class readExcel(object):5 def __init__(self, path):6 self.path path7 8 property9 def getSheet(self): 10 …

设计模式【创建型】-- 原型模式

原型模式&#xff08;Prototype&#xff09; 原型模式是指原型实例指定创建对象的种类&#xff0c;并且通过拷贝这些原型创建新的对象。调用者不需要知道任何创建细节&#xff0c;不调用构造函数 主要应用&#xff1a; 浅拷贝深拷贝 原型模式&#xff1a; 抽象原型类&#xf…

【Redis】—— Redis的AOF持久化机制

&#x1f4a7; 【 R e d i s 】—— R e d i s 的 A O F 持久化机制 \color{#FF1493}{【Redis】 —— Redis的AOF持久化机制} 【Redis】——Redis的AOF持久化机制&#x1f4a7; &#x1f337; 仰望天空&#xff0c;妳我亦是行人.✨ &#x1f984; 个人主页——微风撞…

如何搭建一个优秀的移动商城?

移动商城是一种新兴的购物方式。随着移动设备的普及和人们对线上购物的需求不断增加&#xff0c;移动商城已经成为了现代电子商务的重要组成部分。 对于想要在这个领域中获得成功的人来说&#xff0c;建立一个优秀的移动商城非常重要。但在搭建移动商城的过程中&#xff0c;有很…

【监控系统】Prometheus架构相关概念及源码部署实战

上篇我们介绍了业界主流监控框架的对比&#xff0c;我们监控系统这块主要是采用Prometheus。 那么&#xff0c;什么是Prometheus 官网&#xff1a;https://prometheus.io/docs/introduction/overview/ Prometheus是一个开源的系统监控和报警系统&#xff0c;现在已经加入到CN…

Android Java代码与JNI交互 JNI方法Java类字段 (六)

🔥 Android Studio 版本 🔥 🔥 Java 基础类型数据对应 jni 中的字母 🔥 通过 jni 查找java某个类中相应字段对应的数据类型 , 需要使用到 jni 中的 GetFieldID() 函数 jfieldID GetFieldID(jclass clazz, const char* name, const char* sig){ return functions-&g…

MySQL 如何处理 慢查询

如何定位慢查询? 方式一&#xff1a; 可以使用开源工具&#xff0c;比如&#xff1a; 调试工具&#xff1a;Arthas运维工具&#xff1a;Prometheus 、Skywalking 方式二&#xff1a; 使用MySQL自带慢日志 慢查询日志记录了所有执行时间超过指定参数&#xff08;long_qu…

bsub 的用法和意义

bsub -R “rusage [mem40960]” -Is -XF 执行脚本的方法 bsub <run 几条有用的命令 bqueues 查询所有queue的状态 4. 常用命令之bhosts 显示各节点作业相关情况 bhosts hostname 常用命令之bjobs 查看提交作业运行情况; bjobs –r 显示正在运行的作业 bjobs –a 显示正在…

微信小程序,左上脚返回点击直接到首页

我们做小程序时就有很多这种情况&#xff0c;根据不同情况处理方式不同 第一种情况&#xff1a;小程序有多个tab onUnload(event){ //多层级跳转之后&#xff0c;监听左上角返回事件&#xff0c;直接退回到indexuni.switchTab({url:"/pages/index/index"})}, 第二种…

pandas 笔记:高亮内容

1 高亮缺失值 1.0 数据 import pandas as pd import numpy as npdata[{a:1,b:2},{a:3,c:4},{a:10,b:-2,c:5}]df1pd.DataFrame(data) df1 1.1 highlight_null df.style.highlight_null(color: str red,subset: Subset | None None,props: str | None None, ) 1.1.1 默认情…

【Elasticsearch】RestClient操作文档

目录 5.RestClient操作文档 5.1.新增文档 5.1.1.索引库实体类 5.1.2.语法说明 5.1.3.完整代码 5.2.查询文档 5.2.1.语法说明 5.2.2.完整代码 5.3.删除文档 5.4.修改文档 5.4.1.语法说明 5.4.2.完整代码 5.5.批量导入文档 5.5.1.语法说明 5.5.2.完整代码 5.6.小…

java 整合 Elastic 8.

1. 准备工作 使用docker 快速搭建的环境,官网docker-compose 方式搭建的集群 设置了密码登录 elastic elastic 需要给jdk 导入证书 找到 证书对应目录&#xff0c;复制到桌面。主要导入下面2个证书,执行如下命令 keytool -importcert -alias "修改成你的证书名"…