地市组件改为获取全部数据,不使用懒加载
This commit is contained in:
@ -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];
|
||||
}
|
||||
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 });
|
||||
getChild().then(res => {
|
||||
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',
|
||||
}));
|
||||
const tree = listToTree(res.data);
|
||||
setOptions(tree);
|
||||
}
|
||||
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>
|
||||
);
|
||||
};
|
||||
|
@ -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`);
|
@ -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>
|
||||
</>
|
||||
),
|
||||
|
Reference in New Issue
Block a user