地市组件改为获取全部数据,不使用懒加载
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 { Cascader, Spin } from 'antd';
|
||||||
import type { DefaultOptionType } from 'antd/es/cascader';
|
import type { DefaultOptionType } from 'antd/es/cascader';
|
||||||
import { getChild } from './services';
|
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 DictRegionSelect: React.FC<Partial<import('antd').CascaderProps<DefaultOptionType>>> = (props) => {
|
||||||
const [options, setOptions] = useState<DefaultOptionType[]>([]);
|
const [options, setOptions] = useState<DefaultOptionType[]>([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
// 处理回显的所有级联节点
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let destroyed = false;
|
setLoading(true);
|
||||||
// 只在有初始值时触发自动懒加载
|
getChild().then(res => {
|
||||||
async function loadAllLevels() {
|
if (res && res.code === 200 && Array.isArray(res.data)) {
|
||||||
setLoading(true);
|
const tree = listToTree(res.data);
|
||||||
// props.value 是 [省, 市, 区] 的 id 数组
|
setOptions(tree);
|
||||||
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) {
|
}).finally(() => setLoading(false));
|
||||||
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 [];
|
|
||||||
};
|
|
||||||
|
|
||||||
const cascaderProps = {
|
const cascaderProps = {
|
||||||
changeOnSelect: true,
|
changeOnSelect: true,
|
||||||
options,
|
options,
|
||||||
loadData: loadData as (selectedOptions: DefaultOptionType[]) => void,
|
|
||||||
placeholder: "请选择地区",
|
placeholder: "请选择地区",
|
||||||
...props,
|
...props,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Spin spinning={loading}>
|
<Spin spinning={loading}>
|
||||||
<Cascader {...cascaderProps as any} />
|
<Cascader {...(cascaderProps as any)} />
|
||||||
</Spin>
|
</Spin>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -8,4 +8,4 @@ interface getChildParams {
|
|||||||
pId: string | number;
|
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
|
<DictRegionSelect
|
||||||
onChange={(value: any) => handleAddressChange(value as string[], record)}
|
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>
|
</Form.Item>
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
|
Reference in New Issue
Block a user