当前位置: 首页 > 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. 前…...

三维GIS开发cesium智慧地铁教程(5)Cesium相机控制

一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点&#xff1a; 路径验证&#xff1a;确保相对路径.…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

Python爬虫(二):爬虫完整流程

爬虫完整流程详解&#xff08;7大核心步骤实战技巧&#xff09; 一、爬虫完整工作流程 以下是爬虫开发的完整流程&#xff0c;我将结合具体技术点和实战经验展开说明&#xff1a; 1. 目标分析与前期准备 网站技术分析&#xff1a; 使用浏览器开发者工具&#xff08;F12&…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解

本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

uniapp微信小程序视频实时流+pc端预览方案

方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度​WebSocket图片帧​定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐​RTMP推流​TRTC/即构SDK推流❌ 付费方案 &#xff08;部分有免费额度&#x…...

12.找到字符串中所有字母异位词

&#x1f9e0; 题目解析 题目描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义&#xff1a; 若两个字符串包含的字符种类和出现次数完全相同&#xff0c;顺序无所谓&#xff0c;则互为…...

微信小程序云开发平台MySQL的连接方式

注&#xff1a;微信小程序云开发平台指的是腾讯云开发 先给结论&#xff1a;微信小程序云开发平台的MySQL&#xff0c;无法通过获取数据库连接信息的方式进行连接&#xff0c;连接只能通过云开发的SDK连接&#xff0c;具体要参考官方文档&#xff1a; 为什么&#xff1f; 因为…...