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

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、功能实现 原理: 封装一个受控组件&#xff0c;接受父组件的参数&#xff0c;数据发现变化后&#xff0c;回传给父组件 1、首先&#xff0c;引入CheckableTag组件和useEffect, useMemo, use…...

UUID和雪花(Snowflake)算法该如何选择?

UUID和雪花(Snowflake)算法该如何选择&#xff1f; UUID 和 Snowflake 都可以生成唯一标识&#xff0c;在分布式系统中可以说是必备利器&#xff0c;那么我们该如何对不同的场景进行不同算法的选择呢&#xff0c;UUID 简单无序十分适合生成 requestID&#xff0c; Snowflake 里…...

Jetpack Compose之进度条介绍(ProgressIndicator)

JetPack Compose系列&#xff08;12&#xff09;—进度条介绍 Compose自带进度条控件有两个&#xff0c;分别是&#xff1a;CircularProgressIndicator&#xff08;圆形进度条&#xff09;和LinearProgressIndicator&#xff08;线性进度条&#xff09;。 CircularProgressIn…...

【Qt基本功修炼】Qt线程的两种运行模式

1. 前言 QThread是Qt中的线程类&#xff0c;用于实现多线程运行。 QThread有两种工作模式&#xff0c;即 消息循环模式无消息循环模式 两种模式分别适用于不同的场景。下面我们将从多个方面&#xff0c;讲解QThread两种工作模式的区别。 2. 消息循环模式 2.1 实现原理 Q…...

三、设计模式相关理论总结

一、面向对象编程 1.1 概述 简称Object Oriented Program(OOP)&#xff0c;指以类或对象作为基础组织单元&#xff0c;遵循封装、继承、多态以及抽象等特性&#xff0c;进行编程。其中面向对象不一定遵循封装、继承、封装和多态等特性&#xff0c;只是前人总结的套路规范&…...

鸿蒙 WiFi 连接 流程

那当界面上显示扫描到的所有Ap时&#xff0c;我们选择其中的一个Ap发起连接&#xff0c;看下代码流程是怎样的。 // 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&#xff1a; 把各种项目的依赖都安装到base环境中不是一个好的习惯&#xff0c;比如说我们做爬虫项目和做自动化测试项目等所需要的依赖是不一样的&#xff0c;我们可以将为每个项目创建自己的环境&#xff0c;在各自的环境中安装自己的依赖&…...

gtkmm 与 Cambalache 与 Gtk::Builder (新手向)_

文章目录 前言Cambalache检查Xml.cpp文件如何写才能显示UI首先creat获取ui里的对象显示 前言 新手刚刚使用时的笔记 Cambalache检查Xml 窗口右键inspect UI Definition切换到Xml视图, 可以全选复制粘贴到你的ui文件里, Cambalache 只能保存为.cmb工程文件, 导出也不知道导出…...

uniapp小程序端使用计算属性动态绑定style样式踩坑

踩坑点: 使用uniapp编译小程序端动态绑定复杂style使用计算属性方式&#xff0c;return必须返回json字符串格式&#xff0c;不能返回object&#xff0c;否则会不起作用。 代码总览 视图层 逻辑层&#xff08;注意这里是使用的计算属性哈&#xff09; 这里我封装成了一个个性化…...

计算机网络概念、组成、功能和分类

文章目录 概要1.怎么学习计算机网络2.概念3.功能、组成4.工作方式、功能组成5.分类 概要 概念、组成、功能和分类 1.怎么学习计算机网络 2.概念 通信设备&#xff1a;比如路由器、路由器 线路&#xff1a;将系统和通信设备两者联系的介质之类的 计算机网络是互连的、自治的的计…...

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 示例三&#xff08;常用&#xff09; 2.3 常…...

视频处理学习笔记1:YUYV422、NV12和h264

最近因为工作关系在恶补视频相关知识点&#xff0c;在此做一记录便于日后复习。 以下均是个人学习经验总结&#xff0c;可能存在错误和坑&#xff0c;欢迎大佬指教。 工作中用到的是YUYV422存储格式。存储的就是裸流YUYV422格式文件。 YUYV422是两个像素点共用一个UV分量&am…...

CTFshow web(命令执行29-36)

?ceval($_GET[shy]);&shypassthru(cat flag.php); #逃逸过滤 ?cinclude%09$_GET[shy]?>&shyphp://filter/readconvert.base64-encode/resourceflag.php #文件包含 ?cinclude%0a$_GET[cmd]?>&cmdphp://filter/readconvert.base64-encode/…...

PyTorch深度学习实战(23)——从零开始实现SSD目标检测

PyTorch深度学习实战&#xff08;23&#xff09;——从零开始实现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加减乘除总结 作者&#xff1a;小猪快跑 基础数学&计算数学&#xff0c;从事优化领域5年&#xff0c;主要研究方向&#xff1a;MIP求解器、整数规划、随机规划、智能优化算法 如有错误&#xff0c;欢迎指正。如有…...

4、安全开发-Python-蓝队项目流量攻击分析文件动态监控图片隐写技术

用途&#xff1a;个人学习笔记&#xff0c;有所借鉴&#xff0c;欢迎指正&#xff01; 总结&#xff1a; &#xff08;1&#xff09;使用python脚本Scapy库实现指定网卡的流量抓包分析 &#xff08;2&#xff09;使用python脚本Watchdog实现指定目录文件行为监控 &#xff08;…...

MySQL 日志管理

4.6&#xff09;日志管理 MySQL 支持丰富的日志类型&#xff0c;如下&#xff1a; 事务日志&#xff1a;transaction log 事务日志的写入类型为 "追加"&#xff0c;因此其操作为 "顺序IO"&#xff1b; 通常也被称为&#xff1a;预写式日志 write ahead…...

Python CSV文件读取和写入

本文主要为Python 实现CSV文件读取和写入操作。 CSV文件写入和读取 因为没有现成的csv文件&#xff0c;所以csv的顺序为先写入后读取。 写入 创建csv文件并把数据写入&#xff0c;有两种实现方式&#xff1a;直接插入所有行和插入单行。 示例如下&#xff1a; import csv i…...

如何使用C#调用LabVIEW算法

新建一个工程 这是必须的&#xff1b; 创建项目 项目 点击完成&#xff1b; 将项目另存为&#xff1b;方便后续的使用&#xff1b; 创建 一个测试VI 功能很简单&#xff0c;用的一个加法&#xff1b;将加数A&#xff0c;B设置为输入&#xff0c;和C设置为输出&#xff0c;…...

谷歌浏览器插件

项目中有时候会用到插件 sync-cookie-extension1.0.0&#xff1a;开发环境同步测试 cookie 至 localhost&#xff0c;便于本地请求服务携带 cookie 参考地址&#xff1a;https://juejin.cn/post/7139354571712757767 里面有源码下载下来&#xff0c;加在到扩展即可使用FeHelp…...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

逻辑回归:给不确定性划界的分类大师

想象你是一名医生。面对患者的检查报告&#xff08;肿瘤大小、血液指标&#xff09;&#xff0c;你需要做出一个**决定性判断**&#xff1a;恶性还是良性&#xff1f;这种“非黑即白”的抉择&#xff0c;正是**逻辑回归&#xff08;Logistic Regression&#xff09;** 的战场&a…...

使用分级同态加密防御梯度泄漏

抽象 联邦学习 &#xff08;FL&#xff09; 支持跨分布式客户端进行协作模型训练&#xff0c;而无需共享原始数据&#xff0c;这使其成为在互联和自动驾驶汽车 &#xff08;CAV&#xff09; 等领域保护隐私的机器学习的一种很有前途的方法。然而&#xff0c;最近的研究表明&…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具

文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1

每日一言 生活的美好&#xff0c;总是藏在那些你咬牙坚持的日子里。 硬件&#xff1a;OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写&#xff0c;"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

Module Federation 和 Native Federation 的比较

前言 Module Federation 是 Webpack 5 引入的微前端架构方案&#xff0c;允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式&#xff1a; 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战

“&#x1f916;手搓TuyaAI语音指令 &#x1f60d;秒变表情包大师&#xff0c;让萌系Otto机器人&#x1f525;玩出智能新花样&#xff01;开整&#xff01;” &#x1f916; Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制&#xff08;TuyaAI…...

高防服务器能够抵御哪些网络攻击呢?

高防服务器作为一种有着高度防御能力的服务器&#xff0c;可以帮助网站应对分布式拒绝服务攻击&#xff0c;有效识别和清理一些恶意的网络流量&#xff0c;为用户提供安全且稳定的网络环境&#xff0c;那么&#xff0c;高防服务器一般都可以抵御哪些网络攻击呢&#xff1f;下面…...