React Antd Cascader组件地区选择方式

 更新时间:2025年08月30日 11:56:09   作者:水果摊见  
文章介绍了在表单中实现地区选择功能,使用Cascader组件动态加载数据,需通过loadData和递归处理实现增删改查,存储和回显需完整id数组以支持多级地区展示,强调后端设计对数据关联(id/pid)的重要性

前言

表单中添加一个地区选择功能,要求支持增删改查功能。

Cascader

使用Cascader组件动态加载地区选项。使用 loadData 实现动态加载选项,(loadData 与 showSearch 无法一起使用)。

这里使用了Form.Item组件。

<Form.Item
    label={'地区'}
    name={'region'}
    rules={[
        {
            required: true,
        },
    ]}
>
    <Cascader
        options={regionOptions}
        loadData={loadRegionData}
        changeOnSelect
    />
</Form.Item>

这里的options在每次选择后,通过loadData加载新的选项。
Cascader组件的选项每次存储的值为option中的value,展示的值为label,并通过isLeaf判断是否有子菜单

{
    value: 'zhejiang',
    label: 'Zhejiang',
    isLeaf: false
}

新增

点击新增按钮时,会获取地区接口,并根据用户选择加载地区。

地区在数据库中是根据id和pid进行关联的。如果pid是0,代表是1级地区;pid是1,代表当前地区是id为1的地区的子地区;同理,pid是2,代表当前地区是id为2的地区的子地区。


const [regionOptions, setRegionOptions] = useState([]);

// 获取初始化的地区
// 这个接口参数为空时会返回地区的一级菜单,不为空时传递pid,返回该pid下面的子菜单
const getRegionList = async params => {
    try {
        const res = await selectCityInfoApi(params)
        const newRes = res.map(item => {
            return {
                ...item,  //返回的值是例如{value: value, label: label, level: 1}
                isLeaf: item.level === '3',  //判断level, 如果是三级菜单就没有子菜单
            }
        })
        if (!params) {
            setRegionOptions(newRes);
        }
        return newRes;
    } catch (error) {
        console.log(error);
    }
}

useEffect(() => {
    if (isAdd === false && editId) {
        // 编辑
    } else {
        // 新增
        getRegionList();   // 接口参数为空时会返回地区的一级菜单
    }
}, [isAdd]);

const loadRegionData = (selectedOptions) => {
    const targetOption = selectedOptions[selectedOptions.length - 1];  // 取选择的最后一个值

    getRegionList({ pid: targetOption.value }).then(res => {  // 用pid去调用接口
        targetOption.children = res;  //  把返回的值放到children中
        setRegionOptions([...regionOptions]);  // 更新regionOptions
    })
}

回显

表单保存之后,存储的值是选择的地区的id,这就有一个问题,回显时拿到的也是这个id。

例如如果选择:[北京、北京、东城区],那么存储的时候只会存东城的id:3,但是回显需要他们的id:[1, 2, 3] 才能回显地区,并且在用户点击时展开时还需要相关的option数据。

useEffect(() => {
    if (isAdd === false && editId) {
        // 编辑
        setLoading(true);
        getGroupResourceById(editId)  // 获取当前id的数据
            .then(data => {  // data中有region的id和Pid
                setLoading(false);
                    getRegionList();
                    getRegionDetails(data.regionPid, [String(data.regionPid), String(data.region)]).then((res) => {
                        form.setFieldsValue({ ...data, region: res });
                        buildRegionSelectOptions(res).then(selectRegions => {
                            setRegionOptions(selectRegions);
                        })
                    });
            })
            .catch(err => {
                setLoading(false);
            });
    } else {
        // 新增
        getRegionList();
    }
}, [isAdd]);

const getRegionDetails = async (regionId, regions = []) => {  // 收集region的id
    try {
        const res = await selectCityInfo({ id: regionId })  // 这个接口入参是region的id,返回当前id的具体信息
        if (!!res.length && res[0]?.pid && res[0].pid !== 0) {
            regions.unshift(String(res[0].pid));
        }
        return regions;
    } catch (error) {
        console.error('获取地区列表失败:', error);
        return regions;
    }
};

// 通过一级菜单的pid去递归调用,获取regionOption
const buildRegionSelectOptions = async (regionIds) => {
    const result = [...regionOptions];
    if (!regionIds || regionIds.length === 0) return result;

    const processRegion = async (ids, parentNode = null) => {
        if (!ids || ids.length === 0) return;

        const [currentId, ...restIds] = ids;

        try {
            const regionData = await getRegionList({ pid: currentId });
            const currentNode = regionData.map(item => ({...item, isLeaf: item.level === '3'}));
            parentNode.children = currentNode;

            if (restIds.length > 0) {
                const currentIndex = parentNode.children.findIndex(item => item.value === restIds[0]);
                await processRegion(restIds, parentNode.children[currentIndex]);
            }
        } catch (error) {
            console.error(`获取地区 ${currentId} 失败:`, error);
        }
    };

    let currentRegion = regionOptions.find(item => item.value === regionIds[0]);
    await processRegion(regionIds, currentRegion);
    return result;
};

总结

  1. 新增时通过pid依次获取下一级菜单
  2. 回显时通过递归获取菜单
  3. 后端还是要好好设计一下,这太麻烦了~

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Redis Subscribe timeout 报错的问题解决

    Redis Subscribe timeout 报错的问题解决

    最近系统偶尔报出org.redisson.client.RedisTimeoutException: Subscribe timeout: (7500ms)的错误,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-08-08
  • 浅谈Redis变慢的原因及排查方法

    浅谈Redis变慢的原因及排查方法

    本文主要介绍了浅谈Redis变慢的原因及排查方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-06-06
  • Redis位图bitmap操作

    Redis位图bitmap操作

    本文主要介绍了Redis位图bitmap操作,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-08-08
  • AOP Redis自定义注解实现细粒度接口IP访问限制

    AOP Redis自定义注解实现细粒度接口IP访问限制

    这篇文章主要为大家介绍了AOP Redis自定义注解实现细粒度接口IP访问限制,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-10-10
  • 防止redis内存溢出优化方法

    防止redis内存溢出优化方法

    本文主要介绍了防止redis内存溢出优化方法,包括使用maxmemory-policy选项、设置数据过期时间和配置Redis集群等,具有一定的参考价值,感兴趣的可以了解一下
    2025-03-03
  • 深入了解Redis的性能

    深入了解Redis的性能

    这篇文章主要介绍了Redis的性能,作者通过Visual Studio上的C#程序对其进行了分析,需要的朋友可以参考下
    2015-06-06
  • Redis 删除策略的三种实现

    Redis 删除策略的三种实现

    本文主要介绍了Redis 删除策略的三种实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-06-06
  • Redis实现多人多聊天室功能

    Redis实现多人多聊天室功能

    这篇文章主要为大家详细介绍了Redis实现多人多聊天室功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-11-11
  • Redis如何清理过期的key以及对应的解决方法分析

    Redis如何清理过期的key以及对应的解决方法分析

    这篇文章主要介绍了Redis如何清理过期的key以及对应的解决方法的相关资料,Redis提供了多种过期删除策略和内存淘汰策略,以管理缓存和临时数据,需要的朋友可以参考下
    2025-03-03
  • Redis 中spark参数executor-cores引起的异常解决办法

    Redis 中spark参数executor-cores引起的异常解决办法

    这篇文章主要介绍了Redis 中spark参数executor-cores引起的异常解决办法的相关资料,需要的朋友可以参考下
    2017-03-03

最新评论