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 集群搭建和简单使用教程

    Redis 集群搭建和简单使用教程

    集群技术是构建高性能网站架构的重要手段,下面这篇文章主要介绍了Redis 集群搭建和简单使用教程,文中通过示例代码和图片介绍的很想,对大家具有一定的参考价值,有需要的朋友们下面来一起看看吧。
    2017-02-02
  • 浅谈Redis中的缓存更新策略

    浅谈Redis中的缓存更新策略

    这篇文章主要介绍了浅谈Redis中的缓存更新策略,CacheAsidePatter是我们比较常用的缓存更新策略,其由缓存调用者在更新数据库时,在业务逻辑中设置缓存更新,需要的朋友可以参考下
    2023-08-08
  • Redis数据结构之intset整数集合使用学习

    Redis数据结构之intset整数集合使用学习

    这篇文章主要为大家介绍了Redis数据结构之整数集合使用学习,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-07-07
  • Redis入门基础常用操作命令整理

    Redis入门基础常用操作命令整理

    这篇文章主要为大家介绍了Redis入门基础常用操作命令的整理,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • linux安装配置及使用redis

    linux安装配置及使用redis

    本文主要跟大家讲解的是在Linux环境下,Redis的安装与部署,非常的简单实用,有需要的小伙伴可以参考下
    2018-04-04
  • Redis慢查询的实现

    Redis慢查询的实现

    本文主要介绍了Redis慢查询的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07
  • 利用Redis实现SQL伸缩的方法简介

    利用Redis实现SQL伸缩的方法简介

    这篇文章主要介绍了利用Redis实现SQL伸缩的方法,包括讲到了锁和时间序列等方面来提升传统数据库的性能,需要的朋友可以参考下
    2015-06-06
  • redis缓存预热的实现示例

    redis缓存预热的实现示例

    本文主要介绍了Java中实现缓存预热的多种策略,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-11-11
  • Redis配置外网可访问(redis远程连接不上)的方法

    Redis配置外网可访问(redis远程连接不上)的方法

    默认情况下,当我们在部署了redis服务之后,redis本身默认只允许本地访问。Redis服务端只允许它所在服务器上的客户端访问,如果Redis服务端和Redis客户端不在同一个机器上,就要进行配置。
    2022-12-12
  • 一文详解Redis在Ubuntu系统上的安装步骤

    一文详解Redis在Ubuntu系统上的安装步骤

    安装redis在Ubuntu上有多种方法,下面这篇文章主要给大家介绍了关于Redis在Ubuntu系统上安装的相关资料,文中通过图文以及代码介绍的非常详细,需要的朋友可以参考下
    2024-07-07

最新评论