地市组件改为获取全部数据,不使用懒加载

This commit is contained in:
孙景学
2025-08-06 08:20:44 +08:00
parent 0afdf4e64a
commit bf836ed1b7
3 changed files with 31 additions and 94 deletions

View File

@ -1,99 +1,53 @@
import React, { useState, useEffect } from 'react';
import React, { useEffect, useState } from 'react';
import { Cascader, Spin } from 'antd';
import type { DefaultOptionType } from 'antd/es/cascader';
import { getChild } from './services';
// 工具函数:将平铺数据转为树结构
function listToTree(data: any[]): DefaultOptionType[] {
const map: { [id: string]: any } = {};
data.forEach(item => map[item.id] = { ...item, value: item.id, label: item.name, children: [] });
const tree: DefaultOptionType[] = [];
data.forEach(item => {
if (item.pid === '0' || item.pid === 0) {
tree.push(map[item.id]);
} else if (map[item.pid]) {
map[item.pid].children.push(map[item.id]);
}
});
// 删除空 children
function prune(node: any) {
if (node.children && node.children.length === 0) delete node.children;
else if (node.children) node.children.forEach(prune);
}
tree.forEach(prune);
return tree;
}
const DictRegionSelect: React.FC<Partial<import('antd').CascaderProps<DefaultOptionType>>> = (props) => {
const [options, setOptions] = useState<DefaultOptionType[]>([]);
const [loading, setLoading] = useState(false);
// 处理回显的所有级联节点
useEffect(() => {
let destroyed = false;
// 只在有初始值时触发自动懒加载
async function loadAllLevels() {
setLoading(true);
// props.value 是 [省, 市, 区] 的 id 数组
const path = (props.value as string[]) || [];
let currentPid = '0';
let currentOptions: DefaultOptionType[] = [];
let optionLevel = [];
let parentOptions = currentOptions;
for (let i = 0; i < path.length; i++) {
// 拉取本级所有 children
// eslint-disable-next-line no-await-in-loop
const children = await fetchRegionOptions(currentPid);
// 新建 option 节点
const optionNodes = children.map(child => ({
...child,
children: undefined, // 只有下一步才补
}));
// 当前级别节点加入到 options 树
if (i === 0) {
// 顶级直接 set
currentOptions = optionNodes;
} else {
// 找到父节点,挂到父节点 children
let parent = parentOptions.find(opt => opt.value === path[i - 1]);
if (parent) parent.children = optionNodes;
}
// 记录本级 option下一轮用于找 parent
parentOptions = optionNodes;
currentPid = path[i];
setLoading(true);
getChild().then(res => {
if (res && res.code === 200 && Array.isArray(res.data)) {
const tree = listToTree(res.data);
setOptions(tree);
}
if (!destroyed) {
setOptions(currentOptions);
setLoading(false);
}
}
// 如果有 value并且是懒加载options 为空或只有顶级)
if (Array.isArray(props.value) && props.value.length > 0) {
loadAllLevels();
} else {
// 首次只拉顶级
setLoading(true);
fetchRegionOptions('0').then(data => {
setOptions(data);
setLoading(false);
});
}
return () => { destroyed = true; };
// eslint-disable-next-line
}, [props.value]);
const loadData = async (selectedOptions: DefaultOptionType[]) => {
const targetOption = selectedOptions[selectedOptions.length - 1];
targetOption.loading = true;
const children = await fetchRegionOptions(targetOption.value!);
targetOption.loading = false;
targetOption.children = children;
setOptions([...options]);
};
const fetchRegionOptions = async (pId: string | number): Promise<DefaultOptionType[]> => {
const res = await getChild({ pId });
if (res && res.code === 200 && Array.isArray(res.data)) {
return res.data.map((item: any) => ({
value: item.id,
label: item.name,
isLeaf: item.level === '2',
}));
}
return [];
};
}).finally(() => setLoading(false));
}, []);
const cascaderProps = {
changeOnSelect: true,
options,
loadData: loadData as (selectedOptions: DefaultOptionType[]) => void,
placeholder: "请选择地区",
...props,
};
return (
<Spin spinning={loading}>
<Cascader {...cascaderProps as any} />
<Cascader {...(cascaderProps as any)} />
</Spin>
);
};

View File

@ -8,4 +8,4 @@ interface getChildParams {
pId: string | number;
}
export const getChild = (params: getChildParams) => request.get(`/v1/dictRegion/getChild`, {params});
export const getChild = () => request.get(`/v1/dictRegion/all`);

View File

@ -593,23 +593,6 @@ export const BankAccountSection: React.FC<CommonFormSectionsProps> = ({ form, su
<DictRegionSelect
onChange={(value: any) => handleAddressChange(value as string[], record)}
/>
{/* <Cascader
options={addressOptions}
placeholder="请选择地址"
onChange={(value) => handleAddressChange(value, record)}
showSearch={{
filter: (inputValue, path) => {
return path.some((option) => {
if (typeof option.label === 'string') {
return (
option.label.toLowerCase().indexOf(inputValue.toLowerCase()) > -1
);
}
return false;
});
},
}}
/> */}
</Form.Item>
</>
),