React+Antd实现省、市区级联下拉多选组件(支持只选省不选市)

 1、效果

是你要的效果,咱们继续往下看,搜索面板实现省市区下拉,原本有antd的Cascader组件,但是级联组件必须选到子节点,不能只选省,满足不了页面的需求

2、环境准备

1、react18

2、antd 4+

3、功能实现

原理:封装一个受控组件,该组件就是两select基本组件

1、首先,导入需要的组件:

import { Select, Space, Tag } from 'antd';

 2、定义2个状态变量来存储选中省和市的下拉枚举

  const [firstOptions, setFirstOptions] = useState<any>([]);
  const [secondOptions, setSecondOptions] = useState<any>([]);

 3、组件可接收的props子属性 如下:

  •  options: 省市级联数据
  •  value: 已选中的值
  •  width:slect框的宽度
  •  firstPlaceholder 第一个select框的placeholder
  • secondPlaceholder第二个select框的placeholder
  •  onChange: 选中的值发生变化时回调

 4、创建handleFirstChange函数来处理第一个select框的change事件,更新第二个select框的下拉项和值

  // 第一个select生变化
  const handleFirstChange = (data: any) => {
    if (!isEmpty(data) && data.value) {
      let insertIndex = (options || []).findIndex((item: any) => {
        return item?.value === data?.value;
      });
      setSecondOptions(options?.[insertIndex]?.children || []);
      onChange({ first: [data] });
    } else {
      setSecondOptions([]);
      onChange(null);
    }
  };

 5、创建onSecondChange 函数来处理第二个select框的change事件,将选中的值回传给父组件

  // 第二个select发生变化
  const onSecondChange = (data: any) => {
    if (!isEmpty(value) && value.first) {
      if (!isEmpty(data)) {
        onChange({
          ...value,
          second: mode === 'multiple' ? (data || []).filter((item: any) => !isNil(item?.label)) : [data],
        });
      } else {
        onChange({ first: value.first, second: null });
      }
    } else {
      onChange(null);
    }
  };

 6、最后,使用2个select组件渲染,并将选中状态和change事件绑定到对应的属性上:

return (
    <>
      <Space wrap={false} direction="horizontal" size={12}>
        <Select
          defaultValue={firstOptions[0]}
          style={{ width: width }}
          onChange={handleFirstChange}
          placeholder={firstPlaceholder || '请选择'}
          value={value?.first}
          options={firstOptions}
          labelInValue
          allowClear
        />
        <Select
          style={{ width: width }}
          value={value?.second || []}
          onChange={onSecondChange}
          placeholder={secondPlaceholder || '请选择'}
          options={secondOptions}
          {...mode === "multiple" ? { mode: "multiple", maxTagCount: 'responsive', tagRender: tagRender } : {}}
          labelInValue
          allowClear
        />
      </Space>
    </>
)

 7、完整代码如下:

import { Select, Space, Tag } from 'antd';
import clsx from 'clsx';
import { isEmpty, isNil } from 'lodash';
import { useEffect, useState } from 'react';
import './index.less';

const MultipleCascaderSelect = (props: any) => {
  const {
    options,
    value,
    onChange,
    width = 160,
    firstPlaceholder,
    secondPlaceholder,
    mode = 'multiple'
  } = props;

  const [firstOptions, setFirstOptions] = useState<any>([]);
  const [secondOptions, setSecondOptions] = useState<any>();

  useEffect(() => {
    setFirstOptions(options || []);
    if (Array.isArray(value?.first) && value.first.length) {
      let findIndex = (options || []).findIndex((item: any) => {
        return item.value === value.first?.[0].value;
      });
      setSecondOptions(options[findIndex]?.children || []);
    } else {
      setSecondOptions([]);
    }
  }, [options, value]);

  // 第一个select生变化
  const handleFirstChange = (data: any) => {
    if (!isEmpty(data) && data.value) {
      let insertIndex = (options || []).findIndex((item: any) => {
        return item?.value === data?.value;
      });
      setSecondOptions(options?.[insertIndex]?.children || []);
      onChange({ first: [data] });
    } else {
      setSecondOptions([]);
      onChange(null);
    }
  };

  // 第二个select发生变化
  const onSecondChange = (data: any) => {
    if (!isEmpty(value) && value.first) {
      if (!isEmpty(data)) {
        onChange({
          ...value,
          second: mode === 'multiple' ? (data || []).filter((item: any) => !isNil(item?.label)) : [data],
        });
      } else {
        onChange({ first: value.first, second: null });
      }
    } else {
      onChange(null);
    }
  };
  const tagRender = ({ label, closable, onClose }: any) => {
    const isLongTag = `${label}`.length > 4;
    return (
      <Tag
        color={label.props?.color}
        closable={closable}
        onClose={onClose}
        className={clsx([
          'text-sky-400 bg-sky-400/10 text-sm font-normal leading-5',
          // 'border border-solid border-sky-400/50',
          'max-w-[110px] border-none',
          // 'whitespace-nowrap text-ellipsis overflow-hidden'
        ])}
      >
        <span>{isLongTag ? `${label.slice(0, 4)}...` : label}</span>
        {/* {isLongTag ? (
          <Tooltip
            title={label}
            key={label}
            rootClassName={clsx('toolTipCard')}
            placement="top"
          >
            <span>{label.slice(0, 4)}...</span>
          </Tooltip>
        ) : (
          <span>{label}</span>
        )} */}
      </Tag>
    );
  };
  return (
    <>
      <Space wrap={false} direction="horizontal" size={12}>
        <Select
          defaultValue={firstOptions[0]}
          style={{ width: width }}
          onChange={handleFirstChange}
          placeholder={firstPlaceholder || '请选择'}
          value={value?.first}
          options={firstOptions}
          labelInValue
          allowClear
        />
        <Select
          style={{ width: width }}
          value={value?.second || []}
          onChange={onSecondChange}
          placeholder={secondPlaceholder || '请选择'}
          options={secondOptions}
          {...mode === "multiple" ? { mode: "multiple", maxTagCount: 'responsive', tagRender: tagRender } : {}}
          labelInValue
          allowClear
        />
      </Space>
    </>
  );
};

export default MultipleCascaderSelect;

组件调用

 <MultipleCascaderSelect
     width={162}
     options={enumData|| []}
     firstPlaceholder="请选择"
     secondPlaceholder="请选择"
 />

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Win32 SDK Gui编程系列之--ListView自绘OwnerDraw(续)

通过所有者绘制的列表视图(2) 所有者绘制列表视图的基础已在前一页中说明。本页将展示如何在所有者绘制列表视图中显示数据库表数据。 1、访问日志 正如在另一个页面中所述,本网站的访问日志目前是通过SQLite3数据库管理的。 以下是上述程序执行的结果。为…

OpenCV-30 腐蚀操作

一、引入 腐蚀操作也是用卷积核扫描图像&#xff0c;只不过腐蚀操作的卷积核一般都是1&#xff08;卷积核内的每个数字都为1&#xff09;&#xff0c;如果卷积核内所有像素点都是白色&#xff0c;那么锚点&#xff08;中心点&#xff09;即为白色。 大部分时候腐蚀操作使用的都…

基于ESP-WROOM-32的双串口通信并显示到OLED显示屏上

目录 开发板引脚图 Arduino环境配置1.ESP32开发版下载2.Arduino开发板选择 -> ESP32 Dev Module3.安装驱动库 接线图Arduino代码现象演示 开发板 ESP-WROOM-32 引脚图 Arduino环境配置 1.ESP32开发版下载 选择 esp32 by Espressif Systems 2.Arduino开发板选择 -> E…

搜索引擎DuckDuckGo代理指南

DuckDuckGo作為一款搜索引擎&#xff0c;同時擁有自己的流覽器&#xff0c;高度保護用戶隱私&#xff0c;使其有別於其他收集和利用用戶數據進行定向廣告的搜索引擎。然而&#xff0c;單獨使用DuckDuckGo並不能保證線上完全匿名。如果你想進一步保護隱私&#xff0c;那就需要使…

寒假作业5

TCP 1&#xff1a;提供面向连接的&#xff0c;可靠的数据传输服务 2&#xff1a;传输过程中&#xff0c;数据无误、数据无丢失、数据无失序、数据无重复 3&#xff1a;数据传输效率低&#xff0c;耗费资源多 4&#xff1a;数据收发是不同步的 5&#xff1a;TCP的使用场景&#…

【计算机网络】【练习题及解答】【新加坡南洋理工大学】【Computer Control Network】【Exercise Solution】

说明&#xff1a; 个人资料&#xff0c;仅供学习使用&#xff0c;版权归校方所有。 一、题目描述 该问题接上期博文【练习题及解答】&#xff0c;描述网络通信中的链路效率&#xff08;Link Efficiency&#xff09;&#xff0c;即Link Utilization的计算。&#xff08;此处认…

go modules使用

创建项目 在使用GoLand创建项目的时候&#xff0c;会自动创建对应的go.mod文件。 创建完后 创建文件 创建一个main.go的文件&#xff0c;里面print一个hello world。 在运行时可以设置是否采取先生成文件再运行。 为空的话则不输出。 下面的Environment为设置运行的环境…

IDEA生成可执行jar包

1. 进入需要打包的项目&#xff0c;选择 最上方菜单栏的 File → Project Structure 2. 选择 左侧菜单栏 Artifacts → 加号 → JAR → from modules with dependencies 3. 选择入口类 Main Class&#xff08;点击文件夹图标可以快速选择&#xff09;&#xff0c;点击 OK&#…

万物皆可问 — 私有部署网易有道QAnything

什么是 QAnything&#xff1f; QAnything&#xff08;Question and Answer based on Anything&#xff09;是一个本地知识库问答系统&#xff0c;旨在支持多种文件格式和数据库&#xff0c;允许离线安装和使用。使用QAnything&#xff0c;您可以简单地删除本地存储的任何格式的…

人工智能|深度学习——使用多层级注意力机制和keras实现问题分类

代码下载 使用多层级注意力机制和keras实现问题分类资源-CSDN文库 1 准备工作 1.1 什么是词向量? ”词向量”&#xff08;词嵌入&#xff09;是将一类将词的语义映射到向量空间中去的自然语言处理技术。即将一个词用特定的向量来表示&#xff0c;向量之间的距离&#xff08;例…

Web 站点的欢迎页面

Web 站点的欢迎页面 一、JavaWeb 中的欢迎页1.Tomcat 的默认欢迎页2.局部配置欢迎页 二、SpringBoot 中的欢迎页1.默认欢迎页2.自定义欢迎页(1) 通过页面跳转控制器方式(2) controller 直接实现方式 一、JavaWeb 中的欢迎页 1.Tomcat 的默认欢迎页 当文件名设置为 index.html…

【学习笔记】TypeScript学习笔记1 --TypeScript中的类型

文章目录 TS总的变量类型References TS总的变量类型 备注&#xff1a; 如果一个变量设置为了any 类型之后相当于变量关闭了TS的类型检测 let d: any; d 10; d hello;//unknown表示的是未知类型&#xff0c;实际是上一个安全的any,unknown类型的变量不能直接赋值给其他变量le…

Avalonia学习(二十三)-大屏

弄一个大屏显示的界面例子&#xff0c;但是代码有点多&#xff0c;还有用户控件。 目前还有一点问题在解决&#xff0c;先看一下界面效果。

mybatis sql报错记录: Encountered “<EOF>“ at line 1, column 0.

报错日志&#xff1a; 解决问题&#xff1a; ***<if test" ">***AND T1.xxx_code in<foreach item"pollCode" collection"vo.pollCodeList" open"(" separator"," close")">#{pollCode}</foreac…

宝塔面板Node项目带参启动配置方法

宝塔面板Node项目带参启动配置方法 ​0x00 缘起​0x01 解决方法​0x02 系统信息​0x03 后记 ​0x00 缘起 编写的Nest项目根据启动时的环境变量加载不同的数据库配置, 在CentOS 7的终端中, 使用如下命令就可以启动成功: export NODE_ENVproduction; node dist/main可是使用宝塔…

宠物空气净化器适合养猫家庭吗?除猫毛好的猫用空气净化器推荐

宠物掉毛是一个普遍存在的问题&#xff0c;尤其在脱毛季节&#xff0c;毛发似乎无处不在。这给家中的小孩和老人带来了很多麻烦&#xff0c;他们容易流鼻涕、过敏等不适。此外&#xff0c;宠物有时还会不规矩地拉扯和撒尿&#xff0c;这股气味实在是难以忍受。家人们对宠物的存…

数据结构第十三天(树)

目录 前言 概述 树的基本概念&#xff1a; 树的相关操作 &#xff1a; 源码&#xff1a; 主函数&#xff1a; 运行结果&#xff1a; 往期精彩内容&#xff1a; 前言 2010年一部电影创造了奇迹&#xff0c;它是全球第一部票房到达 27 亿美 元&#xff0c;总票房历史 排名第…

Linux系统调试课:硬件断点

沉淀、分享、成长,让自己和他人都能有所收获!😄 📢在linux内核编程中,经常会遇到由于内存被篡改,例如 buffer overflow,野指针,write after free等。查找分析此类问题非常的麻烦。 一、什么是硬件断点 硬件断点,是Linux内核中是一种被ptrace和内核内调试器使用调试…

阿里云游戏服务器租用费用价格组成,费用详单

阿里云游戏服务器租用价格表&#xff1a;4核16G服务器26元1个月、146元半年&#xff0c;游戏专业服务器8核32G配置90元一个月、271元3个月&#xff0c;阿里云服务器网aliyunfuwuqi.com分享阿里云游戏专用服务器详细配置和精准报价&#xff1a; 阿里云游戏服务器租用价格表 阿…

架构学习(五):scrapy实现自定义代理中间件

scrapy实现自定义代理中间件 前言关卡&#xff1a;实现自定义代理中间件代理中间件源码解析代理池自定义代理中间件 结束 前言 ip检测是比较常规的反爬手段&#xff0c;一般站点会限制ip的访问频率&#xff0c;或者根据ip的访问规律和频率来识别异常访问&#xff0c;从而点对点…