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="请选择"
 />

相关推荐

  1. 【bootstrap-multiselect】

    2024-02-08 07:34:01       11 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-02-08 07:34:01       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-02-08 07:34:01       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-02-08 07:34:01       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-02-08 07:34:01       20 阅读

热门阅读

  1. LeetCode动态规划的解题思路

    2024-02-08 07:34:01       30 阅读
  2. HTML系列 -> <meta charset=“utf-8“>

    2024-02-08 07:34:01       33 阅读
  3. Spark的timestamp 数据时间问题

    2024-02-08 07:34:01       41 阅读
  4. ORACLE的 软 软 软 解析!

    2024-02-08 07:34:01       37 阅读
  5. 【大数据面试题】005 谈一谈 Flink Watermark 水印

    2024-02-08 07:34:01       34 阅读
  6. FolkMQ “单线程“消息中间件(开源) v1.0.32 发布

    2024-02-08 07:34:01       39 阅读
  7. [AIGC] 开源流程引擎哪个好,如何选型?

    2024-02-08 07:34:01       33 阅读
  8. 1.2 Verilog 简介及发展历史

    2024-02-08 07:34:01       41 阅读
  9. visual studio注册码

    2024-02-08 07:34:01       37 阅读
  10. pydantic了解学习

    2024-02-08 07:34:01       34 阅读
  11. ThreadLocal在项目中的简单使用

    2024-02-08 07:34:01       33 阅读