167 lines
5.4 KiB
TypeScript
167 lines
5.4 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 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 };
|
||
}
|
||
|
||
export const DEFAULT_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,
|
||
});
|
||
}
|
||
|
||
function normalizeCityName(name: string) {
|
||
return name.replace(/市$/, '').trim();
|
||
}
|
||
|
||
type RegionFilterStore = {
|
||
province?: string;
|
||
cityName?: string;
|
||
district?: string;
|
||
};
|
||
|
||
/** 门店列表按省市区筛选(支持 REGION_ALL) */
|
||
export function matchesRegionFilter(store: RegionFilterStore, region: RegionSelection): boolean {
|
||
const normalized = normalizeRegionSelection(region);
|
||
if (normalized.province !== REGION_ALL) {
|
||
if ((store.province ?? '') !== normalized.province) return false;
|
||
}
|
||
if (normalized.city !== REGION_ALL) {
|
||
const storeCity = store.cityName ?? '';
|
||
const cityNorm = normalizeCityName(normalized.city);
|
||
if (
|
||
storeCity !== normalized.city &&
|
||
normalizeCityName(storeCity) !== cityNorm
|
||
) {
|
||
return false;
|
||
}
|
||
}
|
||
if (normalized.district !== REGION_ALL) {
|
||
if ((store.district ?? '') !== normalized.district) return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
export function formatRegionLabel(region: RegionSelection): string {
|
||
const normalized = normalizeRegionSelection(region);
|
||
if (normalized.province === REGION_ALL) return REGION_ALL;
|
||
if (normalized.city === REGION_ALL) return normalized.province;
|
||
if (normalized.district === REGION_ALL) return `${normalized.city}`;
|
||
return normalized.district;
|
||
}
|