145 lines
4.7 KiB
TypeScript
145 lines
4.7 KiB
TypeScript
import { regionData } from 'element-china-area-data';
|
|
|
|
export type RegionTree = Record<string, Record<string, string[]>>;
|
|
|
|
/** 由国家标准省市区数据构建三级树 */
|
|
function buildRegionTree(): RegionTree {
|
|
const tree: RegionTree = {};
|
|
for (const province of regionData) {
|
|
const cities: Record<string, string[]> = {};
|
|
for (const city of province.children ?? []) {
|
|
cities[city.label] = (city.children ?? []).map((district) => district.label);
|
|
}
|
|
tree[province.label] = cities;
|
|
}
|
|
return tree;
|
|
}
|
|
|
|
export const REGION_TREE: RegionTree = buildRegionTree();
|
|
|
|
export const PROVINCES = Object.keys(REGION_TREE);
|
|
|
|
/** 三级选择「全市」选项(省/市/区列表首项) */
|
|
export const REGION_ALL = '全市';
|
|
|
|
export function getCities(province: string): string[] {
|
|
if (province === REGION_ALL) return [];
|
|
return Object.keys(REGION_TREE[province] ?? {});
|
|
}
|
|
|
|
export function getDistricts(province: string, city: string): string[] {
|
|
if (province === REGION_ALL || city === REGION_ALL) return [];
|
|
return REGION_TREE[province]?.[city] ?? [];
|
|
}
|
|
|
|
/** 省份列表(含全市) */
|
|
export function getProvincesForPicker(): string[] {
|
|
return [...PROVINCES];
|
|
}
|
|
|
|
/** 城市列表(含全市) */
|
|
export function getCitiesForPicker(province: string): string[] {
|
|
if (province === REGION_ALL) return [REGION_ALL];
|
|
return [...getCities(province)];
|
|
}
|
|
|
|
/** 区县列表(含全市) */
|
|
export function getDistrictsForPicker(province: string, city: string): string[] {
|
|
if (province === REGION_ALL || city === REGION_ALL) return [REGION_ALL];
|
|
return [REGION_ALL, ...getDistricts(province, city)];
|
|
}
|
|
|
|
export function formatRegion(province: string, city: string, district: string): string {
|
|
if (!province) return '';
|
|
if (province === REGION_ALL) return REGION_ALL;
|
|
if (city === REGION_ALL) return `${province} ${REGION_ALL}`;
|
|
if (district === REGION_ALL) return `${province} ${city} ${REGION_ALL}`;
|
|
if (!city || !district) return '';
|
|
return `${province} ${city} ${district}`;
|
|
}
|
|
|
|
/** 仅展示省、市两级(门店列表等场景) */
|
|
export function formatRegionCity(province: string, city: string): string {
|
|
if (!province) return '';
|
|
if (province === REGION_ALL) return REGION_ALL;
|
|
if (city === REGION_ALL) return `${province} ${REGION_ALL}`;
|
|
if (!city) return province;
|
|
return `${province} ${city}`;
|
|
}
|
|
|
|
/** 门店筛选:固定为市级,不按区县过滤 */
|
|
export function toCityLevelRegion(selection: RegionSelection): RegionSelection {
|
|
const normalized = normalizeRegionSelection(selection);
|
|
return {
|
|
province: normalized.province,
|
|
city: normalized.city,
|
|
district: REGION_ALL,
|
|
};
|
|
}
|
|
|
|
export type RegionSelection = {
|
|
province: string;
|
|
city: string;
|
|
district: string;
|
|
};
|
|
|
|
export const DEFAULT_REGION: RegionSelection = {
|
|
province: '河南省',
|
|
city: '郑州市',
|
|
district: '金水区',
|
|
};
|
|
|
|
export const FALLBACK_CITY_REGION: RegionSelection = {
|
|
province: '河南省',
|
|
city: '郑州市',
|
|
district: REGION_ALL,
|
|
};
|
|
|
|
export function regionFromGeo(province: string, city: string, district?: string): RegionSelection {
|
|
const cityName = city.endsWith('市') ? city : `${city}市`;
|
|
const provinceInTree = PROVINCES.includes(province) ? province : DEFAULT_REGION.province;
|
|
const cities = getCities(provinceInTree);
|
|
const matchedCity = cities.includes(cityName)
|
|
? cityName
|
|
: cities.find((c) => c.replace(/市$/, '') === city.replace(/市$/, '')) ?? cityName;
|
|
const districts = getDistricts(provinceInTree, matchedCity);
|
|
const districtName =
|
|
district && districts.includes(district)
|
|
? district
|
|
: REGION_ALL;
|
|
return normalizeRegionSelection({
|
|
province: provinceInTree,
|
|
city: cities.includes(matchedCity) ? matchedCity : matchedCity,
|
|
district: districtName,
|
|
});
|
|
}
|
|
|
|
/** 校验已选地区是否仍存在于数据源中 */
|
|
export function normalizeRegionSelection(selection: RegionSelection): RegionSelection {
|
|
if (selection.province === REGION_ALL) {
|
|
return { province: REGION_ALL, city: REGION_ALL, district: REGION_ALL };
|
|
}
|
|
|
|
const province = PROVINCES.includes(selection.province)
|
|
? selection.province
|
|
: DEFAULT_REGION.province;
|
|
|
|
if (selection.city === REGION_ALL) {
|
|
return { province, city: REGION_ALL, district: REGION_ALL };
|
|
}
|
|
|
|
const cities = getCities(province);
|
|
const city = cities.includes(selection.city) ? selection.city : (cities[0] ?? DEFAULT_REGION.city);
|
|
|
|
if (selection.district === REGION_ALL) {
|
|
return { province, city, district: REGION_ALL };
|
|
}
|
|
|
|
const districts = getDistricts(province, city);
|
|
const district = districts.includes(selection.district)
|
|
? selection.district
|
|
: (districts[0] ?? DEFAULT_REGION.district);
|
|
|
|
return { province, city, district };
|
|
}
|