60 lines
1.6 KiB
TypeScript
60 lines
1.6 KiB
TypeScript
/**
|
||||
|
|
* 地区数据模块级单例缓存。
|
|||
|
|
* 首次调用时 fetch /region.csv 并构建 idMap 和 parentMap,
|
|||
|
|
* 后续调用直接返回缓存,SPA 生命周期内只加载一次。
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
interface RegionItem {
|
|||
|
|
id: number;
|
|||
|
|
parentId: number;
|
|||
|
|
level: number;
|
|||
|
|
name: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
interface RegionCache {
|
|||
|
|
/** id → RegionItem,用于表格列的 id→name 转换 */
|
|||
|
|
idMap: Map<number, RegionItem>;
|
|||
|
|
/** parentId → children[],用于省/市/区下拉联动 O(1) 查找 */
|
|||
|
|
parentMap: Map<number, RegionItem[]>;
|
|||
|
|
/** 所有地区平铺数组 */
|
|||
|
|
list: RegionItem[];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let _cache: RegionCache | null = null;
|
|||
|
|
|
|||
|
|
function parseCSV(text: string): RegionItem[] {
|
|||
|
|
const lines = text.split("\n").filter((l) => l.trim());
|
|||
|
|
const headers = lines[0].split(",").map((h) => h.trim());
|
|||
|
|
return lines.slice(1).map((line) => {
|
|||
|
|
const cols = line.split(",").map((c) => c.trim());
|
|||
|
|
const obj: any = {};
|
|||
|
|
headers.forEach((h, i) => {
|
|||
|
|
obj[h] = h === "name" ? cols[i] : Number(cols[i]);
|
|||
|
|
});
|
|||
|
|
return obj as RegionItem;
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export async function useRegionCache(): Promise<RegionCache> {
|
|||
|
|
if (_cache) return _cache;
|
|||
|
|
|
|||
|
|
const res = await fetch("/region.csv");
|
|||
|
|
if (!res.ok) throw new Error("地区数据加载失败");
|
|||
|
|
const text = await res.text();
|
|||
|
|
const list = parseCSV(text);
|
|||
|
|
|
|||
|
|
const idMap = new Map<number, RegionItem>();
|
|||
|
|
const parentMap = new Map<number, RegionItem[]>();
|
|||
|
|
|
|||
|
|
for (const item of list) {
|
|||
|
|
idMap.set(item.id, item);
|
|||
|
|
if (!parentMap.has(item.parentId)) {
|
|||
|
|
parentMap.set(item.parentId, []);
|
|||
|
|
}
|
|||
|
|
parentMap.get(item.parentId)!.push(item);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_cache = { idMap, parentMap, list };
|
|||
|
|
return _cache;
|
|||
|
|
}
|