ops-console v1.0.2 (versionCode 8) 源码入库
This commit is contained in:
145
lib/data/region_data.dart
Normal file
145
lib/data/region_data.dart
Normal file
@@ -0,0 +1,145 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/services.dart' show rootBundle;
|
||||
|
||||
/// 省市区节点(省 → 市 → 区三级树)。
|
||||
///
|
||||
/// 对应 `assets/data/MCityPiker/province.json` 结构:
|
||||
/// `{code, name, sub:[{code, name, sub:[{code, name}]}]}`。
|
||||
class RegionNode {
|
||||
const RegionNode({
|
||||
required this.code,
|
||||
required this.name,
|
||||
this.sub = const [],
|
||||
});
|
||||
|
||||
/// 行政区划代码(省 6 位、市 6 位、区 6 位)。
|
||||
final String code;
|
||||
|
||||
/// 行政区划名称(如「北京市」「朝阳区」)。
|
||||
final String name;
|
||||
|
||||
/// 下一级子节点(省含市,市含区,区无 sub)。
|
||||
final List<RegionNode> sub;
|
||||
|
||||
factory RegionNode.fromJson(Map<String, dynamic> json) {
|
||||
final subRaw = json['sub'];
|
||||
final List<RegionNode> children = subRaw is List
|
||||
? subRaw
|
||||
.whereType<Map<String, dynamic>>()
|
||||
.map(RegionNode.fromJson)
|
||||
.toList()
|
||||
: const <RegionNode>[];
|
||||
return RegionNode(
|
||||
code: json['code']?.toString() ?? '',
|
||||
name: json['name']?.toString() ?? '',
|
||||
sub: children,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 省市区数据源。
|
||||
///
|
||||
/// 统一由 [RegionData.load] 懒加载 `province.json` 并缓存为
|
||||
/// `List<RegionNode>` 树;组件禁止直接 `rootBundle` 读取,避免重复解析
|
||||
/// (文件约 284KB)。
|
||||
class RegionData {
|
||||
RegionData._();
|
||||
|
||||
/// 全局单例。
|
||||
static final RegionData instance = RegionData._();
|
||||
|
||||
/// 是否已加载。
|
||||
bool get isLoaded => _loaded;
|
||||
bool _loaded = false;
|
||||
|
||||
/// 省级节点树(已缓存)。
|
||||
List<RegionNode> get provinces => List.unmodifiable(_tree);
|
||||
final List<RegionNode> _tree = [];
|
||||
|
||||
/// 省份名称缓存(code → 名称),用于反查。
|
||||
final Map<String, String> _nameByCode = {};
|
||||
|
||||
/// 懒加载省市区数据。重复调用安全(仅首次解析)。
|
||||
Future<void> load() async {
|
||||
if (_loaded) return;
|
||||
final raw = await rootBundle
|
||||
.loadString('assets/data/MCityPiker/province.json');
|
||||
final decoded = jsonDecode(raw);
|
||||
final list = decoded is List
|
||||
? decoded.whereType<Map<String, dynamic>>().map(RegionNode.fromJson)
|
||||
: const <RegionNode>[];
|
||||
_tree
|
||||
..clear()
|
||||
..addAll(list);
|
||||
_buildNameIndex(_tree);
|
||||
_loaded = true;
|
||||
}
|
||||
|
||||
/// 构建 code → 名称 反查索引(省/市/区全部登记)。
|
||||
void _buildNameIndex(List<RegionNode> nodes) {
|
||||
for (final node in nodes) {
|
||||
if (node.code.isNotEmpty) {
|
||||
_nameByCode[node.code] = node.name;
|
||||
}
|
||||
if (node.sub.isNotEmpty) {
|
||||
_buildNameIndex(node.sub);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取某省下的市级节点;[provinceCode] 为空或不存在时返回空列表。
|
||||
List<RegionNode> citiesOf(String provinceCode) {
|
||||
for (final province in _tree) {
|
||||
if (province.code == provinceCode) return province.sub;
|
||||
}
|
||||
return const [];
|
||||
}
|
||||
|
||||
/// 获取某市下的区级节点;
|
||||
/// [provinceCode]/[cityCode] 任一为空或不存在时返回空列表。
|
||||
List<RegionNode> districtsOf(String provinceCode, String cityCode) {
|
||||
for (final province in _tree) {
|
||||
if (province.code != provinceCode) continue;
|
||||
for (final city in province.sub) {
|
||||
if (city.code == cityCode) return city.sub;
|
||||
}
|
||||
}
|
||||
return const [];
|
||||
}
|
||||
|
||||
/// 按 code 反查名称(省/市/区通用)。
|
||||
String nameOf(String code) => _nameByCode[code] ?? '';
|
||||
|
||||
/// 由历历史纯文本地址尽力映射省市区。
|
||||
///
|
||||
/// 当前实现:遍历所有省份/城市/区名称,若 [address] 包含该名称,
|
||||
/// 则按「省 → 市 → 区」优先级取最长命中,返回
|
||||
/// `[provinceCode, cityCode, districtCode]`;匹配不到的维度为空字符串。
|
||||
/// 由于区名可能重复,此映射仅作尽力回显,无法保证 100% 准确。
|
||||
List<String> tryMapAddress(String address) {
|
||||
final text = address.trim();
|
||||
if (text.isEmpty) return const ['', '', ''];
|
||||
|
||||
String provinceCode = '';
|
||||
String cityCode = '';
|
||||
String districtCode = '';
|
||||
|
||||
for (final province in _tree) {
|
||||
if (text.contains(province.name)) {
|
||||
provinceCode = province.code;
|
||||
for (final city in province.sub) {
|
||||
if (text.contains(city.name)) {
|
||||
cityCode = city.code;
|
||||
for (final district in city.sub) {
|
||||
if (text.contains(district.name)) {
|
||||
districtCode = district.code;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return [provinceCode, cityCode, districtCode];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user