基于 antd 的 Cascader 封装的省市区街道地区下拉选择器

根据 antd 的 Cascader 和 Select 组件进行进一步的封装,按需处理好所需要的省、市、区和街道的数据,然后给到组件就好了。这里用到的技术栈是:react,hooks 和 ts;

使用方法如下

import AreaCascader from '@/components/AreaCascader';
import type { areaItemType } from '@/components/AreaCascader';
import { useState, useMemo } from 'react';

type AddressInfoType = {
  // ... 一些别的信息 
  provinceName: string; // 省 
  cityName: string; // 市
  countyName: string; // 区 
  streetName: string; // 街道
  nationalCode: string; // // 地区码
};

export default function TestPage() {
  // 需要的一些地址信息(其中包括省、市、区、街道、地区码等)
  const [addressInfo, setAddressInfo] = useState<AddressInfoType>({
    // ... 一些别的信息
    provinceName: '',cityName: '',countyName: '',streetName: '',nationalCode: ''
  });

  const { provinceName, cityName, countyName, streetName } = addressInfo;
  
  // 选中的值  使用的时候用 useMemo 记录一下
  const areaValue = useMemo(
    () => [provinceName, cityName, countyName, streetName],
    [provinceName, cityName, countyName, streetName],
  );

  // onChange 的回调方法
  const areaChange = (e?: string[], e2?: areaItemType[]) => {
    console.log(e, e2);
    setAddressInfo((v) => ({
      ...v,
      provinceName: e?.[0] ?? '',
      cityName: e?.[1] ?? '',
      countyName: e?.[2] ?? '',
      streetName: e?.[3] ?? '',
      // 由于业务需求,我这里是获取到区的地区码即可。
      nationalCode: e2?.[2]?.code ?? '',
    }));
  };

  return (
    <div style={{width: '300px'}}>
      <AreaCascader
        type="street"
        value={areaValue}
        onChange={areaChange}
      />
    </div>
  );
}

props说明

type CascaderType = 'province' | 'city' | 'area' | 'street';
type PropsType = {
  type?: CascaderType;
  value: string[] | string;
  placeholder?: string;
  onChange: (e?: string[], option?: areaItemType[]) => void;
};

type:

  • province:只有省
  • city:只有省市
  • area:只有省市区(不传或传其它,默认选中该值)
  • street:省市区街道都有

value: 当前选中的值,当只有 type 为 province 时传入字符串即可,其它情况传入对应的地区字符串数组。

onChange: (value, selectedOptions) => void,第一个参数是返回选中的值(一个字符串数组),第二个参数返回选中的值还有地区码。

组件源码

import { Cascader, Select } from 'antd';
import { useEffect, useState } from 'react';
const { Option } = Select;

export type areaItemType = {
  code: string;
  name: string;
  children?: areaItemType[];
};

type CascaderType = 'province' | 'city' | 'area' | 'street';
type PropsType = {
  type?: CascaderType;
  value: string[] | string;
  placeholder?: string;
  onChange: (e?: string[], option?: areaItemType[]) => void;
};

export default ({
  value,
  type = 'area',
  placeholder = '请选择省市区',
  onChange,
}: PropsType) => {
  const [options, setOptions] = useState<areaItemType[]>();

  useEffect(() => {
    const getData = async () => {
      let data;
      if (type === 'street') {
        // 省市区街道 该文件1.9m~~
        data = (await import('./areas-streets.json')).default;
      } else {
        data = (await import('./areas.json')).default;
      }
      switch (type) {
        // 省
        case 'province': {
          const list = JSON.parse(JSON.stringify(data)).map(
            (item: areaItemType) => {
              delete item.children;
              return item;
            },
          );
          setOptions(list);
          break;
        }
        // 省市
        case 'city': {
          const list = JSON.parse(JSON.stringify(data)).map(
            (item: areaItemType) => {
              item.children?.forEach((item2: areaItemType) => {
                if (item2.children) {
                  delete item2.children;
                }
              });
              return item;
            },
          );
          setOptions(list);
          break;
        }
        default: {
          setOptions(data);
        }
      }
    };
    if (type) {
      getData();
    }
  }, [type]);

  if (type == 'province') {
    // 只有省的下拉框
    return (
      <Select
        value={value as string}
        onChange={(e, option) => {
          const o = option as DefaultOptionType;
          onChange([e], [{ code: o.key, name: o.value as string }]);
        }}
      >
        {options?.map(({ name, code }: areaItemType) => (
          <Option key={code} value={name}>
            {name}
          </Option>
        ))}
      </Select>
    );
  } else {
    // 省市区街道的下拉框
    const val = value as string[];
    return (
      <Cascader
        value={val?.some((v) => v) ? val : ['']}
        fieldNames={{ label: 'name', value: 'name', children: 'children' }}
        expandTrigger="hover"
        options={options}
        placeholder={placeholder}
        onChange={(e, option) => {
          onChange(e as string[], option as unknown as areaItemType[]);
        }}
        style={{width: '100%'}}
      />
    );
  }
};

数据来源:https://github.com/modood/Administrative-divisions-of-China

省市区 json 数据:https://github.com/modood/Administrative-divisions-of-China/blob/master/dist/pca-code.json

省市区街道 json 数据:https://github.com/modood/Administrative-divisions-of-China/blob/master/dist/pcas-code.json

主要是这两份数据,文件比较大,特别是包括街道那个,差不多有1.9m,建议不要学我丢本地项目里面了。

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值