ops-console v1.0.2 (versionCode 8) 源码入库

This commit is contained in:
txh
2026-09-03 18:01:05 +08:00
commit 685fff27c8
170 changed files with 53049 additions and 0 deletions

145
lib/data/region_data.dart Normal file
View 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];
}
}