react+antd+CheckableTag实现Tag标签单选或多选功能
1、效果如下图
实现tag标签单选或多选功能

2、环境准备
1、react18
2、antd 4+
3、功能实现
原理: 封装一个受控组件,接受父组件的参数,数据发现变化后,回传给父组件
1、首先,引入CheckableTag组件和useEffect, useMemo, useState钩子:
import { Tag } from 'antd';
import { useEffect, useMemo, useState } from 'react';const { CheckableTag } = Tag;2、然后,定义一个状态变量来存储选中的tag:
const [tagsData, setTagsData] = useState<enumItem[]>();3、组件可接收的props子属性 如下:
- selectedTagsValues: 父组件传入的标签配置枚举列表
- value: 已选中的值
- startIndex:距离左右节点位置,用于是否将左右节点滑动到可视局域
- onChange: 选中的值发生变化时回调
4、创建一个函数来处理tag的选中和取消选中事件:
  // handelChange,找到所点击项的索引,并把那一项的checked设置为trueconst handleChange = (tag: any, checked: boolean, mode?: string) => {if (mode !== 'multiple') {onChange?.(checked ? tag : null);return;}const changeData = (value || []).filter((item: any) => item.value !== tag.value,);if (checked) {changeData.push(tag);}onChange?.(changeData);}; 5、最后,使用CheckableTag组件以及tagsData, value值的变化动态来渲染tag列表,并将选中状态和change事件绑定到对应的属性上:
  //遍历const dom = useMemo(() => {return (<div id={uniqueKey} className={clsx(['flex'])}>{(tagsData || []).map((tag: any, index: number) => {const isHasSelectedTag = isSelectedTag(tag?.value, value);return (// eslint-disable-next-line react/jsx-key<div className={clsx(['self-check-tag'])} key={index}><CheckableTagkey={tag.value}checked={tag.checked || isHasSelectedTag}onClick={(e: any) => {scrollIntoViewHandle(e.target?.parentElement?.parentElement?.parentElement?.childNodes,index,tagsData?.length || 0,);}}onChange={(checked) => {handleChange(tag, checked, mode);}}><div className={clsx([mode === 'multiple' ? 'cur' : ''])}>{tag.label}{tag.checked || isHasSelectedTag ? <i></i> : ''}</div></CheckableTag></div>);})}</div>);}, [tagsData, value]);6、完整代码如下:
/*** 公共组件:标签组件*/
import { Tag } from 'antd';
import clsx from 'clsx';
import { useEffect, useMemo, useState } from 'react';
import './index.less';const { CheckableTag } = Tag;type enumItem = {label: string;value: string;
};
// 距离左右节点的位置的默认值,决定开始、结束节点是否滚到可视区域
const START_INDEX = 3;/*** 标签属性配置* selectedTagsValues: 可选中的标签配置选项* uniqueKey:组件唯一标识key* value: 已选中的值* startIndex:距离左右节点位置,用于是否将左右节点滑动到可视局域* onChange: 选中的值发生变化时回调*/
interface SelectTagProps {selectedTagsValues: enumItem[];uniqueKey?: string;value?: any;startIndex?: number;mode?: string;onChange?: (values: any) => void;
}const SelectTag = (props: SelectTagProps) => {const { selectedTagsValues, uniqueKey, value, startIndex, mode, onChange } =props;const [tagsData, setTagsData] = useState<enumItem[]>();// 点击tag 跳到对应的可视区域const scrollIntoViewHandle = (node: any, index: number, tagLen: number) => {const scrollIndex =startIndex || Math.min(START_INDEX, Math.floor(tagLen / 2));if (tagLen > 0 && index < scrollIndex) {node?.[0]?.scrollIntoView({behavior: 'smooth',block: 'start',inline: 'start',});return;}if (tagLen > 0 && tagLen - index <= scrollIndex) {node?.[tagLen - 1]?.scrollIntoView({behavior: 'smooth',block: 'start',inline: 'start',});return;}node?.[index]?.scrollIntoView({behavior: 'smooth',block: 'center',inline: 'center',});};useEffect(() => {setTagsData(selectedTagsValues);}, [selectedTagsValues]);useEffect(() => {// 若枚举过长,可考虑传入一个uniqueKey,自动调到指定位置if (uniqueKey && value) {const index: number = (tagsData || []).findIndex((item: any) => item.value && item?.value === value?.value,);if (index > -1 && document.getElementById(uniqueKey)) {setTimeout(() => {scrollIntoViewHandle(document.getElementById(uniqueKey)?.childNodes,index,tagsData?.length || 0,);}, 100);}}}, [value]);// handelChange,找到所点击项的索引,并把那一项的checked设置为trueconst handleChange = (tag: any, checked: boolean, mode?: string) => {if (mode !== 'multiple') {onChange?.(checked ? tag : null);return;}const changeData = (value || []).filter((item: any) => item.value !== tag.value,);if (checked) {changeData.push(tag);}onChange?.(changeData);};// tag是否选中const isSelectedTag = (tagValue: string, value: any) => {if (mode === 'multiple') {if (Array.isArray(value) && value.length) {const findIndex = value.findIndex((item) => item?.value === tagValue);return findIndex > -1;}return false;}return tagValue === value?.value;};//遍历const dom = useMemo(() => {return (<div id={uniqueKey} className={clsx(['flex'])}>{(tagsData || []).map((tag: any, index: number) => {const isHasSelectedTag = isSelectedTag(tag?.value, value);return (// eslint-disable-next-line react/jsx-key<div className={clsx(['self-check-tag'])} key={index}><CheckableTagkey={tag.value}checked={tag.checked || isHasSelectedTag}onClick={(e: any) => {scrollIntoViewHandle(e.target?.parentElement?.parentElement?.parentElement?.childNodes,index,tagsData?.length || 0,);}}onChange={(checked) => {handleChange(tag, checked, mode);}}><div className={clsx([mode === 'multiple' ? 'cur' : ''])}>{tag.label}{tag.checked || isHasSelectedTag ? <i></i> : ''}</div></CheckableTag></div>);})}</div>);}, [tagsData, value]);return <>{dom}</>;
};export default SelectTag;
样式文件:
.self-check-tag {display: flex;.ant-tag {display: inline-block;height: 32px;font-size: 14px;font-family: 'Microsoft YaHei';color: #fff;line-height: 32px;border-radius: 3px;padding-left: 10px;padding-right: 10px;}div.cur {position: relative;// padding: 0 12px;}div.cur > i {display: block;position: absolute;border-bottom: 16px solid #1890ff;border-left: 16px solid transparent;width: 0;height: 0;bottom: 1px;right: -8px;content: '';}div.cur > i::before {content: '';position: absolute;top: -1px;right: -2px;border: 16px solid #1890ff;border-top-color: transparent;border-left-color: transparent;}div.cur > i::after {content: '';width: 6px;height: 10px;position: absolute;right: 0;top: 3px;border: 1px solid #fff;border-top-color: transparent;border-left-color: transparent;transform: rotate(40deg);}.ant-tag-checkable-checked {color: #2eb3ff;background-color: rgba(46, 179, 255, 10%);}.ant-tag-checkable:hover {color: #2eb3ff;background-color: rgba(46, 179, 255, 10%);}
}
组件调用:
const selectedTagsValues: any[] = [{ label: '特大型', value: '1' },{ label: '大型2级', value: '2' },{ label: '大型3级', value: '3' },{ label: '中型4级', value: '4' },{ label: '中型5级', value: '5' },{ label: '小型', value: '6' },
]<SelectTag selectedTagsValues={selectedTagsValues} />下一节将分享多层级的标签选中功能,同时支持多选和单选功能

相关文章:
 
react+antd+CheckableTag实现Tag标签单选或多选功能
1、效果如下图 实现tag标签单选或多选功能 2、环境准备 1、react18 2、antd 4 3、功能实现 原理: 封装一个受控组件,接受父组件的参数,数据发现变化后,回传给父组件 1、首先,引入CheckableTag组件和useEffect, useMemo, use…...
 
UUID和雪花(Snowflake)算法该如何选择?
UUID和雪花(Snowflake)算法该如何选择? UUID 和 Snowflake 都可以生成唯一标识,在分布式系统中可以说是必备利器,那么我们该如何对不同的场景进行不同算法的选择呢,UUID 简单无序十分适合生成 requestID, Snowflake 里…...
 
Jetpack Compose之进度条介绍(ProgressIndicator)
JetPack Compose系列(12)—进度条介绍 Compose自带进度条控件有两个,分别是:CircularProgressIndicator(圆形进度条)和LinearProgressIndicator(线性进度条)。 CircularProgressIn…...
 
【Qt基本功修炼】Qt线程的两种运行模式
1. 前言 QThread是Qt中的线程类,用于实现多线程运行。 QThread有两种工作模式,即 消息循环模式无消息循环模式 两种模式分别适用于不同的场景。下面我们将从多个方面,讲解QThread两种工作模式的区别。 2. 消息循环模式 2.1 实现原理 Q…...
 
三、设计模式相关理论总结
一、面向对象编程 1.1 概述 简称Object Oriented Program(OOP),指以类或对象作为基础组织单元,遵循封装、继承、多态以及抽象等特性,进行编程。其中面向对象不一定遵循封装、继承、封装和多态等特性,只是前人总结的套路规范&…...
鸿蒙 WiFi 连接 流程
那当界面上显示扫描到的所有Ap时,我们选择其中的一个Ap发起连接,看下代码流程是怎样的。 // applications/standard/settings/product/phone/src/main/ets/model/wifiImpl/WifiModel.tsconnectWiFi(password: string) {let apInfo this.userSelectedAp…...
golang 创建unix socket http服务端
服务端 package mainimport ("fmt""net""net/http""os" )func main() {http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {w.Write([]byte("hello"))})http.HandleFunc("/world", …...
 
annaconda如何切换当前python环境
annaconda默认的python环境是base: 把各种项目的依赖都安装到base环境中不是一个好的习惯,比如说我们做爬虫项目和做自动化测试项目等所需要的依赖是不一样的,我们可以将为每个项目创建自己的环境,在各自的环境中安装自己的依赖&…...
gtkmm 与 Cambalache 与 Gtk::Builder (新手向)_
文章目录 前言Cambalache检查Xml.cpp文件如何写才能显示UI首先creat获取ui里的对象显示 前言 新手刚刚使用时的笔记 Cambalache检查Xml 窗口右键inspect UI Definition切换到Xml视图, 可以全选复制粘贴到你的ui文件里, Cambalache 只能保存为.cmb工程文件, 导出也不知道导出…...
 
uniapp小程序端使用计算属性动态绑定style样式踩坑
踩坑点: 使用uniapp编译小程序端动态绑定复杂style使用计算属性方式,return必须返回json字符串格式,不能返回object,否则会不起作用。 代码总览 视图层 逻辑层(注意这里是使用的计算属性哈) 这里我封装成了一个个性化…...
 
计算机网络概念、组成、功能和分类
文章目录 概要1.怎么学习计算机网络2.概念3.功能、组成4.工作方式、功能组成5.分类 概要 概念、组成、功能和分类 1.怎么学习计算机网络 2.概念 通信设备:比如路由器、路由器 线路:将系统和通信设备两者联系的介质之类的 计算机网络是互连的、自治的的计…...
 
MyBatisPlus基础操作之增删改查
目录 一、基本使用 1.1 插入数据 1.2 删除操作 1.3 更新操作 二、条件构造器Wrapper 2.1 常用AbstractWrapper方法 2.1.1 示例一 2.2.2 示例二 2.2.3 示例三 2.2 常用QueryWrapper方法 2.2.1 示例一 2.2.2 示例二 2.2.3 示例三(常用) 2.3 常…...
视频处理学习笔记1:YUYV422、NV12和h264
最近因为工作关系在恶补视频相关知识点,在此做一记录便于日后复习。 以下均是个人学习经验总结,可能存在错误和坑,欢迎大佬指教。 工作中用到的是YUYV422存储格式。存储的就是裸流YUYV422格式文件。 YUYV422是两个像素点共用一个UV分量&am…...
 
CTFshow web(命令执行29-36)
?ceval($_GET[shy]);­passthru(cat flag.php); #逃逸过滤 ?cinclude%09$_GET[shy]?>­php://filter/readconvert.base64-encode/resourceflag.php #文件包含 ?cinclude%0a$_GET[cmd]?>&cmdphp://filter/readconvert.base64-encode/…...
 
PyTorch深度学习实战(23)——从零开始实现SSD目标检测
PyTorch深度学习实战(23)——从零开始实现SSD目标检测 0. 前言1. SSD 目标检测模型1.1 SSD 网络架构1.2 利用不同网络层执行边界框和类别预测1.3 不同网络层中默认框的尺寸和宽高比1.4 数据准备1.5 模型训练 2. 实现 SSD 目标检测2.1 SSD300 架构2.2 Mul…...
 
【附代码】NumPy加速库NumExpr(大数据)
文章目录 相关文献测试电脑配置数组加减乘除数组乘方Pandas加减乘除总结 作者:小猪快跑 基础数学&计算数学,从事优化领域5年,主要研究方向:MIP求解器、整数规划、随机规划、智能优化算法 如有错误,欢迎指正。如有…...
4、安全开发-Python-蓝队项目流量攻击分析文件动态监控图片隐写技术
用途:个人学习笔记,有所借鉴,欢迎指正! 总结: (1)使用python脚本Scapy库实现指定网卡的流量抓包分析 (2)使用python脚本Watchdog实现指定目录文件行为监控 (…...
 
MySQL 日志管理
4.6)日志管理 MySQL 支持丰富的日志类型,如下: 事务日志:transaction log 事务日志的写入类型为 "追加",因此其操作为 "顺序IO"; 通常也被称为:预写式日志 write ahead…...
 
Python CSV文件读取和写入
本文主要为Python 实现CSV文件读取和写入操作。 CSV文件写入和读取 因为没有现成的csv文件,所以csv的顺序为先写入后读取。 写入 创建csv文件并把数据写入,有两种实现方式:直接插入所有行和插入单行。 示例如下: import csv i…...
 
如何使用C#调用LabVIEW算法
新建一个工程 这是必须的; 创建项目 项目 点击完成; 将项目另存为;方便后续的使用; 创建 一个测试VI 功能很简单,用的一个加法;将加数A,B设置为输入,和C设置为输出,…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...
 
Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...
 
《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)
CSI-2 协议详细解析 (一) 1. CSI-2层定义(CSI-2 Layer Definitions) 分层结构 :CSI-2协议分为6层: 物理层(PHY Layer) : 定义电气特性、时钟机制和传输介质(导线&#…...
 
SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...
 
第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...
 
听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...
