import { Cascader as AntdCascader, CascaderProps, ConfigProvider, SelectProps, } from 'antd'; import React, { useCallback, useContext, useEffect, useMemo, useState, } from 'react'; import './index.less'; import classNames from 'classnames'; import { DefaultOptionType } from 'antd/lib/cascader'; import { CascaderRef } from 'antd/lib/cascader'; import { Icon } from '../Icon'; import { AOP } from '../utils/AOP'; import { translate } from '../utils/index'; interface CascaderExtraProps { regionType?: 'province' | 'city' | 'country'; regionCodes?: (number | string)[]; } interface Option { value: string; label: string; children?: Option[]; isLeaf?: boolean; loading?: boolean; } const Cascader = ( props: React.PropsWithChildren> & { ref?: React.Ref | undefined; } & CascaderExtraProps, ) => { const { locale } = useContext(ConfigProvider.ConfigContext); const [_value, setValue] = useState(props.defaultValue); const [regionOptions, setRegionOptions] = useState(); const Region = require('china-region'); const regionLoop = { province: { getFn: Region.getPrefectures, type: 'city' }, city: { getFn: Region.getCounties, type: 'country' }, }; const FormatOption = useCallback( ( option: { name: string; code: string }[], type: string, selectOption?: any, ) => { const derictCity: any = { 110000: '110100', 120000: '120100', 500000: '500100', 310000: '310100', }; const defaultAllRegionMap = { province: { label: translate( 'packages.base.src.Cascader.index.全国', locale?.locale, ), value: '100000', isLeaf: true, type: 'all', }, city: { label: translate( 'packages.base.src.Cascader.index.全省', locale?.locale, ), value: selectOption?.value, isLeaf: true, type: 'all', }, country: { label: translate( 'packages.base.src.Cascader.index.全市', locale?.locale, ), value: selectOption?.value, isLeaf: true, type: 'all', }, }; let res = []; if (option.length === 0) { res = [ { label: Region.info(selectOption?.value).name, value: derictCity[selectOption?.value], isLeaf: props.regionType === type, type, }, ]; } else { res = option.map((item) => ({ label: item.name, value: item.code, isLeaf: props.regionType === type, type, })); } if (props.regionCodes) { res = res.filter((item) => { return ( props.regionCodes.indexOf(item.value) > -1 || props.regionCodes.indexOf(Number(item.value)) > -1 ); }); } res.unshift(defaultAllRegionMap[type]); return res; }, [props.regionCodes], ); useEffect(() => { if (props.regionType) { const provinceOptions = FormatOption(Region.getProvinces(), 'province'); setRegionOptions(provinceOptions); } }, [FormatOption]); useEffect(() => { setValue(props.value); if (props.value && props.regionType) { setValue(props.value?.map((code) => String(code))); const typeMap = { 0: 'province', 1: 'city', 2: 'country', }; let children = []; let provinceCode = ''; props.value.forEach((code, i) => { if (i < props.value.length - 1) { if (i === 1) { const child = FormatOption( regionLoop[typeMap[i]].getFn?.(String(code)), regionLoop[typeMap[i]]?.type, { value: String(code) }, ); children = children.map((item) => { if (item.value == code) { return { ...item, children: child, }; } return item; }); setRegionOptions((pre) => { return pre.map((item) => { if (item.value == provinceCode) { return { ...item, children, }; } return item; }); }); } if (i === 0) { children = FormatOption( regionLoop[typeMap[i]].getFn?.(String(code)), regionLoop[typeMap[i]]?.type, { value: String(code) }, ); provinceCode = String(code); setRegionOptions((pre) => { return pre.map((item) => { if (item.value == code) { return { ...item, children, }; } return item; }); }); } } }); } }, [props.value]); // 为了与 antd 的生态保持兼容性,我们要求必须要使用 `.@{ant-prefix}` 变量来生成类名 const { getPrefixCls } = useContext(ConfigProvider.ConfigContext); const prefixCls = getPrefixCls('btri-cascader'); const { dropdownMenuColumnStyle, showSearch = false, multiple } = props; const [_dropdownVisible, _setDropdownVisible] = useState(false); const _onDropdownVisibleChange = new AOP( props.onDropdownVisibleChange, ) .before((v) => { _setDropdownVisible(v); }) .getFunction(); const _onLoadData = new AOP(props.loadData) .before((selectedOptions) => { if (props.regionType) { const targetOption = selectedOptions[selectedOptions.length - 1]; targetOption.children = FormatOption( regionLoop[(targetOption as any).type].getFn?.(targetOption.value), regionLoop[(targetOption as any).type]?.type, targetOption, ); setRegionOptions((pre) => [...pre]); } }) .getFunction(); const _onChange = new AOP(props.onChange) .before((res) => { setValue([...new Set(res)]); }) .getFunction(); const _suffixIcon = useMemo(() => { if (props.suffixIcon) { return props.suffixIcon; } const icons = { search: ( ), direction: ( ), }; return icons[showSearch ? 'search' : 'direction']; }, [_dropdownVisible, showSearch]); const _dropdownMenuColumnStyle = useMemo(() => { return { padding: '5px 12px', ...dropdownMenuColumnStyle, }; }, [dropdownMenuColumnStyle]); return (
{ return label.pop(); }) } changeOnSelect={props.regionType ? true : false} {...props} className={classNames(`${prefixCls}-box`, props.className)} onDropdownVisibleChange={_onDropdownVisibleChange} suffixIcon={_suffixIcon} style={{ width: '100%', ...props.style }} dropdownClassName={classNames( `${prefixCls}-dropdown`, props.dropdownClassName, )} dropdownMenuColumnStyle={_dropdownMenuColumnStyle} options={props.regionType ? regionOptions : props.options} loadData={_onLoadData} value={_value} onChange={_onChange} > {props.children} {multiple && ( )}
); }; Cascader.SHOW_CHILD = AntdCascader.SHOW_CHILD; Cascader.SHOW_PARENT = AntdCascader.SHOW_PARENT; export { Cascader };