当前位置:   article > 正文

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

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

 1、效果

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

2、环境准备

1、react18

2、antd 4+

3、功能实现

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

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

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

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

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

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

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

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

  1. // 第一个select生变化
  2. const handleFirstChange = (data: any) => {
  3. if (!isEmpty(data) && data.value) {
  4. let insertIndex = (options || []).findIndex((item: any) => {
  5. return item?.value === data?.value;
  6. });
  7. setSecondOptions(options?.[insertIndex]?.children || []);
  8. onChange({ first: [data] });
  9. } else {
  10. setSecondOptions([]);
  11. onChange(null);
  12. }
  13. };

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

  1. // 第二个select发生变化
  2. const onSecondChange = (data: any) => {
  3. if (!isEmpty(value) && value.first) {
  4. if (!isEmpty(data)) {
  5. onChange({
  6. ...value,
  7. second: mode === 'multiple' ? (data || []).filter((item: any) => !isNil(item?.label)) : [data],
  8. });
  9. } else {
  10. onChange({ first: value.first, second: null });
  11. }
  12. } else {
  13. onChange(null);
  14. }
  15. };

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

  1. return (
  2. <>
  3. <Space wrap={false} direction="horizontal" size={12}>
  4. <Select
  5. defaultValue={firstOptions[0]}
  6. style={{ width: width }}
  7. onChange={handleFirstChange}
  8. placeholder={firstPlaceholder || '请选择'}
  9. value={value?.first}
  10. options={firstOptions}
  11. labelInValue
  12. allowClear
  13. />
  14. <Select
  15. style={{ width: width }}
  16. value={value?.second || []}
  17. onChange={onSecondChange}
  18. placeholder={secondPlaceholder || '请选择'}
  19. options={secondOptions}
  20. {...mode === "multiple" ? { mode: "multiple", maxTagCount: 'responsive', tagRender: tagRender } : {}}
  21. labelInValue
  22. allowClear
  23. />
  24. </Space>
  25. </>
  26. )

 7、完整代码如下:

  1. import { Select, Space, Tag } from 'antd';
  2. import clsx from 'clsx';
  3. import { isEmpty, isNil } from 'lodash';
  4. import { useEffect, useState } from 'react';
  5. import './index.less';
  6. const MultipleCascaderSelect = (props: any) => {
  7. const {
  8. options,
  9. value,
  10. onChange,
  11. width = 160,
  12. firstPlaceholder,
  13. secondPlaceholder,
  14. mode = 'multiple'
  15. } = props;
  16. const [firstOptions, setFirstOptions] = useState<any>([]);
  17. const [secondOptions, setSecondOptions] = useState<any>();
  18. useEffect(() => {
  19. setFirstOptions(options || []);
  20. if (Array.isArray(value?.first) && value.first.length) {
  21. let findIndex = (options || []).findIndex((item: any) => {
  22. return item.value === value.first?.[0].value;
  23. });
  24. setSecondOptions(options[findIndex]?.children || []);
  25. } else {
  26. setSecondOptions([]);
  27. }
  28. }, [options, value]);
  29. // 第一个select生变化
  30. const handleFirstChange = (data: any) => {
  31. if (!isEmpty(data) && data.value) {
  32. let insertIndex = (options || []).findIndex((item: any) => {
  33. return item?.value === data?.value;
  34. });
  35. setSecondOptions(options?.[insertIndex]?.children || []);
  36. onChange({ first: [data] });
  37. } else {
  38. setSecondOptions([]);
  39. onChange(null);
  40. }
  41. };
  42. // 第二个select发生变化
  43. const onSecondChange = (data: any) => {
  44. if (!isEmpty(value) && value.first) {
  45. if (!isEmpty(data)) {
  46. onChange({
  47. ...value,
  48. second: mode === 'multiple' ? (data || []).filter((item: any) => !isNil(item?.label)) : [data],
  49. });
  50. } else {
  51. onChange({ first: value.first, second: null });
  52. }
  53. } else {
  54. onChange(null);
  55. }
  56. };
  57. const tagRender = ({ label, closable, onClose }: any) => {
  58. const isLongTag = `${label}`.length > 4;
  59. return (
  60. <Tag
  61. color={label.props?.color}
  62. closable={closable}
  63. onClose={onClose}
  64. className={clsx([
  65. 'text-sky-400 bg-sky-400/10 text-sm font-normal leading-5',
  66. // 'border border-solid border-sky-400/50',
  67. 'max-w-[110px] border-none',
  68. // 'whitespace-nowrap text-ellipsis overflow-hidden'
  69. ])}
  70. >
  71. <span>{isLongTag ? `${label.slice(0, 4)}...` : label}</span>
  72. {/* {isLongTag ? (
  73. <Tooltip
  74. title={label}
  75. key={label}
  76. rootClassName={clsx('toolTipCard')}
  77. placement="top"
  78. >
  79. <span>{label.slice(0, 4)}...</span>
  80. </Tooltip>
  81. ) : (
  82. <span>{label}</span>
  83. )} */}
  84. </Tag>
  85. );
  86. };
  87. return (
  88. <>
  89. <Space wrap={false} direction="horizontal" size={12}>
  90. <Select
  91. defaultValue={firstOptions[0]}
  92. style={{ width: width }}
  93. onChange={handleFirstChange}
  94. placeholder={firstPlaceholder || '请选择'}
  95. value={value?.first}
  96. options={firstOptions}
  97. labelInValue
  98. allowClear
  99. />
  100. <Select
  101. style={{ width: width }}
  102. value={value?.second || []}
  103. onChange={onSecondChange}
  104. placeholder={secondPlaceholder || '请选择'}
  105. options={secondOptions}
  106. {...mode === "multiple" ? { mode: "multiple", maxTagCount: 'responsive', tagRender: tagRender } : {}}
  107. labelInValue
  108. allowClear
  109. />
  110. </Space>
  111. </>
  112. );
  113. };
  114. export default MultipleCascaderSelect;

组件调用

  1. <MultipleCascaderSelect
  2. width={162}
  3. options={enumData|| []}
  4. firstPlaceholder="请选择"
  5. secondPlaceholder="请选择"
  6. />

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/79690
推荐阅读
相关标签
  

闽ICP备14008679号