当前位置: 首页 > news >正文

vue3+vant 实现树状多选组件

vue3+vant 实现树状多选组件

  • 需求描述
  • 效果图
  • 代码
    • 父组件引用
    • selectTree组件
  • tree组件
    • 数据格式

需求描述

在这里插入图片描述

移动端需要复刻Pc端如上图的功能组件,但vant无组件可用,所以自己封装一个。

效果图

在这里插入图片描述

代码

父组件引用

import TreeSelect from "/selectTree.vue"<treeSelectref="treeSelectRef"v-model:show="showAera":modelValue="modelValue":listData="options":multiple='true'placeholder="请选择"@changeModelValue="changeModelValue"></treeSelect>

selectTree组件

<template><van-popup v-model:show="showPicker" round position="bottom"  @click-overlay="onClickOverlay" ><div class="tree-box"><div class="tree-container"><div class="tree-data"><TreeSelectref="treeSelectRef":list="data.list":listObj="data.listObj"@confirm="onConfirm"></TreeSelect></div></div></div><div class="tree-confirm"><van-button type="primary" block @click="handleConfirm">确定</van-button></div></van-popup>
</template><script setup>
import { reactive, watch, ref, nextTick, onMounted } from "vue";
import TreeSelect from "./tree.vue";
import { showLoadingToast, closeToast } from "vant";const emits = defineEmits(["changeModelValue", "update:show", "confirm"]);
const props = defineProps({show: {type: Boolean,default: false,},// 绑定值modelValue: {type: Array,default() {return [];},},listData: {type: Array,default() {return [];},},
});watch(() => props.show,() => {showPicker.value = props.show;initData(props.listData);}
);const showPicker = ref(props.show);
const data = reactive({list: props.listData, // 树数组listObj: {}, // 数组对象selectList: [], // 选中的数据canCheckList: [], // 能够选择的数据集合canCheckListFixed: [], // 固定的能够选择的数据集合
});const treeSelectRef = ref(null);const init = (type) => {data.canCheckList = [];data.canCheckListFixed = [];
};
const initData = (options) => {if (options && options.length) {options[0].first=truedata.list = options;init();data.listObj = setListObj(options);}
};// 将树形数据转为扁平对象
const setListObj = (list) => {let listObj = {};list.forEach((itm) => {if(props.modelValue&&props.modelValue.indexOf(itm.id)!==-1){itm.checked=true}data.canCheckList.push(itm);data.canCheckListFixed.push(itm);listObj[itm.id] = itm;if (itm.children && itm.children.length) {listObj = {...listObj,...setListObj(itm.children),};}});return listObj;
};const onClickOverlay = () => {emits("update:show", false);
};
// 确认
const handleConfirm = () => {emits("changeModelValue", data.selectList);emits("update:show", false);
};const onConfirm = (e) => {const showSelectList = filterData(e);data.selectList = showSelectList.map((itm) => itm.id);
};// 过滤数据
const filterData = (selectList) => {// 过滤出展示中,且打勾的数据const showSelectList = selectList.filter((itm) => {return !itm.isHide && !itm.isShowChildren;});return showSelectList;
};const sendWordShow = ref(false);defineExpose({init,setListObj,
});
</script><style lang="less" scoped>
.tree-box {--van-search-content-background-color: #eeeeee;--van-search-content-background: #eeeeee;
}.tree-container {width: 100%;padding: 32px 32px 0;
}.tree-data {height: 60vh;overflow-y: auto;
}.tree-btns {width: 100%;margin-bottom: 24px;display: flex;align-items: center;
}.tree-confirm {width: 100%;padding: 12px 32px;
}
</style>

tree组件

<template><div class="list"><div class="item" v-for="item in props.list" :key="item.key" v-show="!item.isHide"><div class="title"><div class="checkbox-box"><van-checkbox  icon-size="16px" shape="square" @click.stop="checkChange(item)" v-model="item.checked"><span style="font-size: 15px;">{{ item.name}}</span></van-checkbox></div><div @click.stop="itemClick(item)" :class="item.first?'arrow':'arrowlast'"><van-icon v-if="item.children && item.children.length" :name="item.isShowChildren ? 'arrow-up' : 'arrow-down'" /></div></div><div class="tree" v-show="item.first||item.isShowChildren"><tree  :isLink="data.isLink"v-if="item.children && item.children.length" :list="item.children" :listObj="props.listObj":isFirstFloor="false" :multiple="data.multiple" @confirm="onConfirm" :defaultId="defaultId"></tree></div></div></div>
</template>
<script setup>
import { reactive, watch } from 'vue'
import tree from './tree.vue'
const emits = defineEmits(["change","confirm"])
const props = defineProps({// 是否是第一层isFirstFloor: {type: Boolean,default() {return true;},},// 树形结构list: {type: Array,default() {return [];},},// 树形扁平化数据listObj: {type: Object,default() {return {};},},// 单选默认值defaultId : String
})const data = reactive({firstLoad: true,checkboxValue1: [],showList: [],isLink:true,multiple:true,isOutData: true, // 需要将数据抛出
})watch(() => props.list, () => {if (data.firstLoad) {outDataBuffer();data.firstLoad = false;}// 判断 是第一层树 且 不是进行显示隐藏操作时,进行数据的抛出if (props.isFirstFloor && data.isOutData) {if(data.multiple){outCheckedData();}}
}, { deep: true })// 展开
const itemClick = (item) => {outDataBuffer();item.isShowChildren = !item.isShowChildren}
// 数据抛出缓冲(在list数据变化时,不想抛出选择的数据时,调用该方法)
const outDataBuffer = () => {data.isOutData = false;setTimeout(() => {data.isOutData = true;}, 500);
}
// 获取选中对象
const getCheckData = (list) => {let deptList = [];list.forEach((itm) => {if (itm.checked) {deptList.push(itm);}if (itm.children && itm.children.length) {deptList = deptList.concat(getCheckData(itm.children));}});return deptList;
}
// 单项checked改变
const checkChange = (item) => {// 多选if (data.multiple) {// item.checked = !item.checkedif (data.isLink) {// 展开所有可以展开的节点if (item.checked) {expandAll(item);}// 判断父级是否需要勾选checkParent(item);// 勾选子级if (item.children && item.children.length) {checkChidren(item.children, item.checked);outCheckedData();}}return}// 单选if(item.children && item.children.length) returntoggleAllSelectData(props.list)outCheckedData();}// 获取全部可选择数据,进行全选/取消
const toggleAllSelectData = (list) => {list.forEach((itm) => {itm.checked = falseif (itm.children && itm.children.length) {toggleAllSelectData(itm.children)}});
}// 展开所有可以展开的节点
const expandAll = (item) => {if (item.children?.length) {item.isShowChildren = trueitem.children.forEach(itm => {expandAll(itm);})}
}
// 判断父级是否需要勾选
const checkParent = (item) => {// 父级不存在不再往下走if (!props.listObj[item[props.pidKey]]) {return;}let someDataCount = 0; // 同级的相同父级数据量let checkedDataCount = 0; // 同级已勾选的数据量for (const id in props.listObj) {const itm = props.listObj[id];if (itm[props.pidKey] === item[props.pidKey] && !itm.isHide) {someDataCount++;if (itm.checked) {checkedDataCount++;}}}const isEqual = someDataCount === checkedDataCount;if (props.listObj[item[props.pidKey]].checked != isEqual) {props.listObj[item[props.pidKey]].checked = isEqualcheckParent(props.listObj[item[props.pidKey]]);}
}
// 根据父级统一取消勾选或勾选
const checkChidren = (list, isChecked) => {list.forEach((itm) => {itm.checked = isCheckedif (itm.children && itm.children.length) {checkChidren(itm.children, isChecked);}});
}
// 抛出选中的数据
const outCheckedData = () => {const checkedList = getCheckData(props.list);emits("change", checkedList);onConfirm(checkedList)
}const onConfirm = (e) => {emits("confirm", e);
}defineExpose({itemClick,outDataBuffer,getCheckData,checkChange,expandAll,checkParent,checkChidren,outCheckedData,})
</script><style lang="less" scoped>.list {.item {margin-bottom: 10px;.title {display: flex;align-items: center;justify-content: space-between;margin-bottom: 10px;.checkbox-box {display: flex;align-items: center;cursor: pointer;padding: 10px 0;}.arrow{width: 80px;display: flex;justify-content: flex-end;}}.tree {margin-left: 50px;}}.arrow{display: none !important;}
}
</style>

数据格式

[{"name": "1","key": 0,"children": [{"name": "2","key": 1,"children": []},{"name": "3","key": 1,"children": [{"name": "4","key": 3,"children": []}]}]}
]

相关文章:

vue3+vant 实现树状多选组件

vue3vant 实现树状多选组件 需求描述效果图代码父组件引用selectTree组件 tree组件数据格式 需求描述 移动端需要复刻Pc端如上图的功能组件&#xff0c;但vant无组件可用&#xff0c;所以自己封装一个。 效果图 代码 父组件引用 import TreeSelect from "/selectTree.vu…...

Git安装与常用命令

Git简介&#xff1a; Git是一个开源的分布式版本控制系统&#xff0c;用于敏捷高效地处理任何或大或小的项目。Git是Linus Torvalds为了帮助管理Linux内核开发而开发的一个开放源代码的版本控制软件。Git与常用的版本控制工具CVS、Subversion等不同&#xff0c;它采用了分布式…...

uni-app 使用vscode开发uni-app

安装插件 uni-create-view 用于快速创建页面 配置插件 创建页面 输入页面名称&#xff0c;空格&#xff0c;顶部导航的标题&#xff0c;回车 自动生成页面并在pages.json中注册了路由 pages\login\login.vue <template><div class"login">login</d…...

单线程的JS中Vue导致的“线程安全”问题

目录 现象分析原因 浏览器中Js是单线程的&#xff0c;当然不可能出现线程安全问题。只是遇到的问题的现象与多线程的情况十分相似&#xff0c;导致对不了解Vue实现的我怀疑起了人生… 现象 项目中用到了element-plus中的加载组件&#xff0c;简单封装了一下&#xff0c;用来保…...

vue2 - SuperMap3D加载基于Nginx服务生成的3DTileset模型切片服务地址

文章目录 🍍开发环境🍉1:nginx发布3Dtileset模型切片服务🍍1.1:准备3DTileset文件🍍1.2:安装nginx服务,配置相关文件1.2.1:下载nginx1.2.2:下载完解压文件如下1.2.3:将3Dtileset模型文件放置 nginx-1.24.0/html/gc 新建文件中如下:1.2.4:配置nginx服务🍉2:…...

新版本Spring Security 2.7 + 用法,直接旧正版粘贴

一、以前的用法&#xff1a; Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter {Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}Overrideprotected void configure(HttpSecurity http) throws Exceptio…...

JVM——类加载器(JDK8及之前,双亲委派机制)

目录 1.类加载器的分类1.实现方式分类1.虚拟机底层实现2.JDK中默认提供或者自定义 2.类加载器的分类-启动类加载器3.类加载器的分类-Java中的默认类加载器4.类加载器的分类-扩展类加载器5.类加载器的分类-类加载器的继承 2.类加载器的双亲委派机制 类加载器&#xff08;ClassLo…...

(七)什么是Vite——vite优劣势、命令

vite分享ppt&#xff0c;感兴趣的可以下载&#xff1a; ​​​​​​​Vite分享、原理介绍ppt 什么是vite系列目录&#xff1a; &#xff08;一&#xff09;什么是Vite——vite介绍与使用-CSDN博客 &#xff08;二&#xff09;什么是Vite——Vite 和 Webpack 区别&#xff0…...

vue之Error: Unknown option: .devServer.

背景 在使用内网穿透工具时&#xff0c;加入对应的配置&#xff0c;启动出现报错。 一、遇到的问题 报错&#xff1a; Error: Unknown option: .devServer. Check out https://babeljs.io/docs/en/babel-core/#options for more information about options. Error: Unknown …...

基于ssm的房屋租售网站(有报告)。Javaee项目,ssm项目。

演示视频&#xff1a; 基于ssm的房屋租售网站(有报告)。Javaee项目&#xff0c;ssm项目。 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 项目介绍&#xff1a; 采用M&#xff08;mode…...

LeeCode AutoX-4 计算几何

题意 传送门 LeeCode AutoX-4 蚂蚁爬行 题解 枚举每一对几何图形&#xff0c;判断相交性&#xff0c;用并查集维护连通性即可。总时间复杂度 O ( n 2 m ) O(n^2 m) O(n2m)&#xff0c;其中 n n n 为几何图形数量&#xff0c; m m m 为查询数量。 根据几何图形性质分类讨…...

Vue3 动态设置 ref

介绍 在一些场景&#xff0c;ref设置是未知的需要根据动态数据来决定&#xff0c;如表格中的input框需要我们主动聚焦&#xff0c;就需要给每一个input设置一个ref&#xff0c;进而进行聚焦操作。 Demo 点击下面截图中的编辑按钮&#xff0c;自动聚焦到相应的输入框中。 &…...

fast lio 2 保存每一帧的点云PCD和里程计矩阵 Odom 在txt文件

修改了源代码的 laserMapping.cpp 文件,替换为下面的代码就可以保存了,注意里面有一个路径,需要修改为你的电脑的路径 // This is an advanced implementation of the algorithm described in the // following paper: // J. Zhang and S. Singh. LOAM: Lidar Odometry an…...

当前主流DDos方式有哪几类

随着互联网的普及和技术的进步&#xff0c;网络安全问题日益凸显。DDoS攻击作为其中一种常见且具破坏性的攻击方式&#xff0c;受到了广泛关注。小德将带领大家一起来了解当前流行的三种DDoS攻击方式。 1. 容量耗尽攻击 容量耗尽攻击是最常见也是最直接的DDoS攻击方式。攻击者通…...

神经网络常见评价指标AUROC(AUC-ROC)、AUPR(AUC-PR)

神经网络的性能可以通过多个评价指标进行衡量&#xff0c;具体选择哪些指标取决于任务的性质。以下是神经网络中常见的评价指标&#xff1a; 准确性&#xff08;Accuracy&#xff09;&#xff1a; 准确性是最常见的分类任务评价指标&#xff0c;表示模型正确预测的样本数占总样…...

Apache Doris安装部署

Apache Doris安装部署 版本&#xff1a; CentOS 7.6 Apache Doris 0.14.0 编译 选择合适的版本进行下载&#xff0c;此次选择0.14.0版本 下载 | Apache Doris 一、CentOS编译 1 安装依赖 sudo yum groupinstall Development Tools && sudo yum install maven c…...

Excel查询时用vlookup或者xlookup时,虽然用的参数选择的是精确匹配,但是发现不能区分大小写,应该如何解决?

Excel查询时用vlookup或者xlookup时&#xff0c;虽然用的参数选择的是精确匹配&#xff0c;但是发现不能区分大小写&#xff0c;应该如何解决&#xff1f; Index函数解决 INDEX([excel1.xlsx]Sheet1!$E:$E,MATCH(1,EXACT(G5,[excel1.xlsx]Sheet1!$E:$E)*1,0))重点说明&#x…...

4种经典的限流算法

0、基础知识 1000毫秒内&#xff0c;允许2个请求&#xff0c;其他请求全部拒绝。 不拒绝就可能往db打请求&#xff0c;把db干爆~ interval 1000 rate 2&#xff1b; 一、固定窗口限流 固定窗口限流算法&#xff08;Fixed Window Rate Limiting Algorithm&#xff09;是…...

<MySQL> 什么是数据库事务?事务该如何使用?

目录 一、事务的概念 二、事务的核心特性 三、事务操作中的常见BUG 3.1 脏读 3.2 不可重复读 3.3 幻读 四、隔离级别 五、使用事务 一、事务的概念 “事务”是指一组操作&#xff0c;在逻辑上是不可分割的&#xff0c;组成这组操作的各个语句&#xff0c;或者全部执行成…...

Linux 网络:PMTUD 简介

文章目录 1. 前言2. Path MTU Discovery(PMTUD) 协议2.1 PMTUD 发现最小 MTU 的过程 3. Linux 的 PMTUD 简析3.1 创建 socket 时初始化 PMTUD 模式3.2 数据发送时 PMTUD 相关处理3.2.1 源头主机发送过程中 PMTU 处理3.2.2 转发过程中 PMTUD 处理 4. PMTUD 观察5. 参考链接 1. 前…...

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…...

css实现圆环展示百分比,根据值动态展示所占比例

代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...

大数据零基础学习day1之环境准备和大数据初步理解

学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 &#xff08;1&#xff09;设置网关 打开VMware虚拟机&#xff0c;点击编辑…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

CSS设置元素的宽度根据其内容自动调整

width: fit-content 是 CSS 中的一个属性值&#xff0c;用于设置元素的宽度根据其内容自动调整&#xff0c;确保宽度刚好容纳内容而不会超出。 效果对比 默认情况&#xff08;width: auto&#xff09;&#xff1a; 块级元素&#xff08;如 <div>&#xff09;会占满父容器…...

免费PDF转图片工具

免费PDF转图片工具 一款简单易用的PDF转图片工具&#xff0c;可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件&#xff0c;也不需要在线上传文件&#xff0c;保护您的隐私。 工具截图 主要特点 &#x1f680; 快速转换&#xff1a;本地转换&#xff0c;无需等待上…...

push [特殊字符] present

push &#x1f19a; present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中&#xff0c;push 和 present 是两种不同的视图控制器切换方式&#xff0c;它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...

TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?

在工业自动化持续演进的今天&#xff0c;通信网络的角色正变得愈发关键。 2025年6月6日&#xff0c;为期三天的华南国际工业博览会在深圳国际会展中心&#xff08;宝安&#xff09;圆满落幕。作为国内工业通信领域的技术型企业&#xff0c;光路科技&#xff08;Fiberroad&…...

Rust 开发环境搭建

环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行&#xff1a; rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu ​ 2、Hello World fn main() { println…...