Files
tuiche/lib/pages/mh_page/people_info.dart
2026-01-07 15:19:16 +08:00

1287 lines
82 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:async';
import 'package:ef/ef.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:flutterflow_ui/flutterflow_ui.dart';
import 'package:vbvs_app/common/color/appConstants.dart';
import 'package:vbvs_app/common/color/app_uri_status.dart';
import 'package:vbvs_app/common/pojo/city.dart';
import 'package:vbvs_app/common/util/FitTool.dart';
import 'package:vbvs_app/common/util/MyUtils.dart';
import 'package:vbvs_app/component/tool/CustomCard.dart';
import 'package:vbvs_app/component/tool/TopSlideNotification.dart';
import 'package:vbvs_app/controller/mh_controller/people_info_controller.dart';
import 'package:vbvs_app/pages/common/selectDialog.dart';
import 'package:vbvs_app/pages/mh_page/homepage/controller/mht_home_controller.dart';
import 'package:vbvs_app/pages/mh_page/test/WebviewTestModel.dart';
import 'package:vbvs_app/pages/person/select_city.dart';
import 'package:vbvs_app/pages/person/select_disease.dart';
//更新人员信息
class PeopleInfoPage extends GetView<PeopleInfoController> {
Map data;
PeopleInfoPage({required this.data});
getLine() {
return Divider(
color: Color(0XFF929699),
thickness: 0.5.rpx,
height: 0,
);
}
PeopleInfoController controller = Get.put(PeopleInfoController());
final CityModelController cityController = Get.find<CityModelController>();
@override
Widget build(BuildContext context) {
controller.getDiseaseData().then((apiResponse) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (apiResponse.code != HttpStatusCodes.ok) {
TopSlideNotification.show(
context,
text: apiResponse.msg ?? '',
textColor: themeController.currentColor.sc9,
);
}
});
});
getPersonData();
controller.cityDataFuture =
cityController.loadAndSetCityData().then((success) {
return cityController.cityList;
});
return LayoutBuilder(
builder: (context, boxConstraints) => GestureDetector(
onTap: () {
// 触摸收起键盘
FocusScope.of(context).requestFocus(FocusNode());
},
child: Container(
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/new_background.png'), // 本地图片
fit: BoxFit.fill, // 填满整个 Container
),
),
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
elevation: 0,
surfaceTintColor: Colors.transparent,
backgroundColor: Colors.transparent,
automaticallyImplyLeading: false,
iconTheme: IconThemeData(color: Colors.white),
titleSpacing: 0,
title: SizedBox(
width: double.infinity,
height: 180.rpx,
child: Stack(
alignment: Alignment.center,
children: [
// 中间居中的标题
Text(
'人员资料'.tr,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 30.rpx,
),
),
// 左侧图标
Positioned(
left: 0.rpx,
child: returnIconButtomNew(),
),
Positioned(
right: 30.rpx,
child: CustomCard(
borderRadius: 16.rpx,
gradientDirection: GradientDirection.vertical,
onTap: () async {
bool isValid = true;
// 数据验证
for (int i = 0;
i < controller.model.peopleList.length;
i++) {
var d = controller.model.peopleList[i];
// String prefix = "人员信息${i == 0 ? "A" : "B"}";
if (d['height'] != null &&
d['height'].toString().isNotEmpty &&
int.tryParse(d['height'].toString()) ==
null) {
TopSlideNotification.show(context,
text: "请选择身高".tr,
textColor: Color(0xFFFF7159));
isValid = false;
break;
}
if (d['weight'] != null &&
d['weight'].toString().isNotEmpty &&
int.tryParse(d['weight'].toString()) ==
null) {
TopSlideNotification.show(context,
text: "请选择体重".tr,
textColor: Color(0xFFFF7159));
isValid = false;
break;
}
if (d['contact'] != null &&
d['contact'].toString().isNotEmpty &&
!MyUtils.isValidPhoneNumber(
d['contact'].toString())) {
TopSlideNotification.show(context,
text: "请输入正确的联系人电话".tr,
textColor: Color(0xFFFF7159));
isValid = false;
break;
}
}
// 所有数据合法,开始保存
if (isValid) {
for (var data in controller.model.peopleList) {
try {
await controller.savePeoples(data, context);
MHTHomeController mhtHomeController =
Get.find();
await Future.delayed(Duration(seconds: 1));
await mhtHomeController.getPersonList();
WebviewTestController
webviewTestController = Get.find();
webviewTestController
.queryPersonInfoByMac();
} catch (e) {
print("保存失败:$e");
TopSlideNotification.show(context,
text: "更新失败".tr,
textColor: Color(0xFFFF7159));
}
}
}
},
colors: const [
Color(0xFFFCFCFC),
Color(0xFFF8FAF9),
Color(0XFFECF6F3),
Color(0XFFD9F0E9),
Color(0xFFCEECE3)
],
child: Container(
width: 120.rpx,
height: 60.rpx,
alignment: Alignment.center,
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(5),
// color: stringToColor("#182B7C"),
// ),
child: Text(
"保存".tr,
style: TextStyle(
fontFamily: 'Readex Pro',
color: Color(0XFF011D33),
letterSpacing: 0,
fontSize: 30.rpx,
),
),
),
))
],
),
),
centerTitle: false,
),
body: SafeArea(
top: true,
child: Container(
padding: EdgeInsets.only(left: 30.rpx, right: 30.rpx),
width: MediaQuery.sizeOf(context).width,
height: MediaQuery.sizeOf(context).height * 1.123,
// decoration: BoxDecoration(
// color: Colors.transparent,
// image: DecorationImage(
// image: AssetImage("assets/images/background.png"),
// fit: BoxFit.cover,
// ),
// ),
child: SingleChildScrollView(
child: Obx(() => Column(
children: [
if (controller.model.peopleList.isNotEmpty)
...List.generate(
controller.model.peopleList.length,
(index) {
final person =
controller.model.peopleList[index];
String location_ = '';
// if ("${data["bindMacB"]}".length > 6 &&
// (person["direction"] == 1 ||
// person["direction"] == 2)) {
// location_ = person["direction"] == 1 ? '左侧' : '右侧';
// }
return Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Container(
margin: EdgeInsets.only(
left: 18.rpx,
top: index == 0 ? 30.rpx : 90.rpx,
bottom: 20.rpx),
child: Text(
index == 0
? 'person_info_A'.tr
: 'person_info_B'.tr,
style: TextStyle(
color: Colors.white,
fontSize: 30.rpx),
)),
Container(
child: Column(
children: [
getLine(),
Obx(
() => Container(
width: double.infinity,
height: 90.rpx,
margin: EdgeInsets.only(
left: 40.rpx,
right: 35.rpx),
decoration: BoxDecoration(),
child: Row(
mainAxisSize:
MainAxisSize.max,
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(
'姓名'.tr,
style: TextStyle(
fontFamily:
'Readex Pro',
color:
Color(0xFF9EA4B7),
fontSize: 30.rpx,
letterSpacing: 0,
),
),
Container(
width: 300.rpx,
child: TextField(
obscureText: false,
textAlign:
TextAlign.right,
style: TextStyle(
fontSize: 30.rpx,
color:
Colors.white),
decoration:
InputDecoration(
fillColor: Colors
.transparent,
filled: true,
hintText:
"请输入姓名".tr,
hintStyle:
TextStyle(
color: themeController
.currentColor
.sc4,
),
border:
InputBorder
.none,
contentPadding:
EdgeInsets
.all(
0)),
onChanged: (value) {
controller.model
.peopleList[
index]
["name"] = value;
},
controller: controller.onReDraw(
TextEditingController(
text: controller
.model
.peopleList[index]
[
"name"] ??
""),
(textEditingController) {
textEditingController
.text = controller
.model
.peopleList[
index]["name"] ??
"";
}, "people_name_$index"),
),
),
],
),
),
),
getLine(),
Obx(
() => InkWell(
onTap: () {
// 延迟执行的代码
FocusScope.of(context)
.requestFocus(
FocusNode());
Future.delayed(
const Duration(
milliseconds: 250),
() {
showOneSelectionDialog(
context,
title: "选择性别".tr,
arr: ["".tr, "".tr],
checkIndex: controller
.model
.peopleList[
index]
['gender'] ??
0,
checkChange: (sindex) {
controller.model.peopleList[
index]['gender'] =
sindex; // 👈 保存为 0 / 1
controller.updateAll();
});
});
},
child: Container(
margin: EdgeInsets.only(
left: 40.rpx,
right: 35.rpx),
width: double.infinity,
height: 90.rpx,
decoration: BoxDecoration(),
child: Row(
mainAxisSize:
MainAxisSize.max,
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(
'性别'.tr,
style: TextStyle(
fontFamily:
'Readex Pro',
color:
Color(0xFF9EA4B7),
fontSize: 30.rpx,
letterSpacing: 0,
),
),
Row(
mainAxisSize:
MainAxisSize.max,
children: [
Container(
width: 200.rpx,
child: Text(
controller.model.peopleList[
index]
[
'gender'] ==
0
? ''.tr
: ''.tr,
textAlign:
TextAlign
.right,
style: TextStyle(
fontFamily:
'Readex Pro',
color: Colors
.white,
fontSize:
30.rpx,
letterSpacing:
0,
),
),
),
SizedBox(
width: 16.rpx,
),
Container(
height: 30.rpx,
width: 30.rpx,
child: SvgPicture
.asset(
'assets/img/icon/expand_more.svg',
color: Colors
.white,
))
],
),
],
),
),
),
),
getLine(),
InkWell(
onTap: () {
// Convert string height to int for initial value
final currentHeight = controller
.model.peopleList[index]
['height'];
final initialHeight =
currentHeight != null
? int.tryParse(
currentHeight
.toString()) ??
170
: 170;
FocusScope.of(context)
.requestFocus(FocusNode());
Future.delayed(
const Duration(
milliseconds: 250), () {
showHeightPickerDialog(
context,
title: "选择身高".tr,
initialHeight:
initialHeight,
onConfirm:
(int selectedHeight) {
controller.model.peopleList[
index]['height'] =
selectedHeight
.toString();
controller.updateAll();
print(
"身高: $selectedHeight cm");
},
);
});
},
child: Container(
width: double.infinity,
height: 90.rpx,
margin: EdgeInsets.only(
left: 40.rpx,
right: 35.rpx),
decoration: BoxDecoration(),
child: Row(
mainAxisSize:
MainAxisSize.max,
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(
'身高(cm)'.tr,
style: TextStyle(
fontFamily:
'Readex Pro',
color:
Color(0xFF9EA4B7),
fontSize: 30.rpx,
letterSpacing: 0,
),
),
Row(
children: [
Text(
controller.model.peopleList[
index]
[
'height'] !=
null
? "${controller.model.peopleList[index]['height']} cm"
: '',
style: TextStyle(
fontFamily:
'Readex Pro',
color: Colors.white,
fontSize: 30.rpx,
),
),
SizedBox(width: 16.rpx),
Container(
height: 30.rpx,
width: 30.rpx,
child: SvgPicture
.asset(
'assets/img/icon/expand_more.svg',
color:
Colors.white,
)),
],
),
],
),
),
),
getLine(),
Obx(
() => InkWell(
onTap: () {
FocusScope.of(context)
.requestFocus(
FocusNode());
Future.delayed(
const Duration(
milliseconds: 250),
() {
showWeightPickerDialog(
context,
title: "选择体重".tr,
initialWeight: controller
.model
.peopleList[
index]['weight'] ??
"",
onConfirm:
(int selectedWeight) {
controller.model
.peopleList[
index]['weight'] =
selectedWeight
.toString(); // ✅ 转成字符串
controller.updateAll();
},
);
});
},
child: Container(
width: double.infinity,
height: 90.rpx,
margin: EdgeInsets.only(
left: 40.rpx,
right: 35.rpx),
decoration: BoxDecoration(),
child: Row(
mainAxisSize:
MainAxisSize.max,
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(
'体重(kg)'.tr,
style: TextStyle(
fontFamily:
'Readex Pro',
color:
Color(0xFF9EA4B7),
fontSize: 30.rpx,
letterSpacing: 0,
),
),
Row(
children: [
Text(
controller.model.peopleList[
index]
[
'weight'] !=
null
? "${controller.model.peopleList[index]['weight']} kg"
: '',
style: TextStyle(
fontFamily:
'Readex Pro',
color:
Colors.white,
fontSize: 30.rpx,
),
),
SizedBox(
width: 16.rpx),
Container(
height: 30.rpx,
width: 30.rpx,
child: SvgPicture
.asset(
'assets/img/icon/expand_more.svg',
color: Colors
.white,
))
],
),
],
),
),
),
),
getLine(),
Obx(() => InkWell(
onTap: () {
// 延迟执行的代码
FocusScope.of(context)
.requestFocus(
FocusNode());
Future.delayed(
const Duration(
milliseconds: 250),
() {
showDateSelectionDialog(
context,
title: "选择生日".tr,
checkDate: controller
.model
.peopleList[index]
['birthday']
is DateTime
? controller.model
.peopleList[index]
['birthday']
: DateTime.tryParse(
controller.model.peopleList[index]['birthday'] ??
'') ??
DateTime.now(),
checkChange: (DateTime d) {
controller.model
.peopleList[
index]
['birthday'] = d;
controller.updateAll();
});
});
},
child: Container(
width: double.infinity,
height: 90.rpx,
margin: EdgeInsets.only(
left: 40.rpx,
right: 35.rpx),
decoration: BoxDecoration(),
child: Row(
mainAxisSize:
MainAxisSize.max,
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(
'生日'.tr,
style: TextStyle(
fontFamily:
'Readex Pro',
color: Color(
0xFF9EA4B7),
fontSize: 30.rpx,
letterSpacing: 0,
),
),
Row(
mainAxisSize:
MainAxisSize.max,
children: [
Container(
constraints:
BoxConstraints(
minWidth:
200.rpx),
child: Text(
controller.model.peopleList[index]['birthday'] !=
null
? time_08_Formatter_pattern(
controller.model.peopleList[index]
[
'birthday'],
"yyyy年MM月dd日"
.tr)
: '',
textAlign:
TextAlign
.right,
style:
TextStyle(
fontFamily:
'Readex Pro',
color: Colors
.white,
fontSize:
30.rpx,
letterSpacing:
0,
),
),
),
SizedBox(
width: 16.rpx,
),
Container(
height: 30.rpx,
width: 30.rpx,
child:
SvgPicture
.asset(
'assets/img/icon/expand_more.svg',
color: Colors
.white,
))
],
),
],
),
),
)),
getLine(),
Container(
width: double.infinity,
height: 90.rpx,
margin: EdgeInsets.only(
left: 40.rpx, right: 35.rpx),
decoration: BoxDecoration(),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(
'联系方式'.tr,
style: TextStyle(
fontFamily: 'Readex Pro',
color: Color(0xFF9EA4B7),
fontSize: 30.rpx,
letterSpacing: 0,
),
),
Container(
width: 300.rpx,
child: TextField(
obscureText: false,
keyboardType:
TextInputType.number,
textInputAction:
TextInputAction.done,
textAlign:
TextAlign.right,
style: TextStyle(
fontSize: 30.rpx,
color: Colors.white),
decoration:
InputDecoration(
fillColor: Colors
.transparent,
filled: true,
hintText:
"请输入联系方式".tr,
hintStyle:
TextStyle(
color: themeController
.currentColor
.sc4,
),
border:
InputBorder
.none,
contentPadding:
EdgeInsets
.all(0)),
onChanged: (value) {
controller.model
.peopleList[
index]
['contact'] = value;
},
controller: controller.onReDraw(
TextEditingController(
text: controller
.model
.peopleList[index]
[
'contact'] ??
""),
(textEditingController) {
textEditingController
.text = controller
.model
.peopleList[
index]['contact'] ??
"";
}, "contact_$index"),
),
),
],
),
),
getLine(),
Container(
height: 90.rpx,
margin: EdgeInsets.only(
left: 40.rpx, right: 35.rpx),
child: InkWell(
onTap: () {
// if (widget.status ==
// BindType.share.code) {
// TopSlideNotification.show(
// context,
// text: "被分享用户只能修改用户名称",
// textColor:
// themeController
// .currentColor
// .sc9);
// return;
// }
FocusScope.of(context)
.requestFocus(
FocusNode());
Future.delayed(
Duration(
milliseconds: 250),
() {
// 使用当前选中的城市数据,如果没有则创建默认
CityModel? currentCity;
if (controller
.model
.cityModels
.isNotEmpty &&
index <
controller
.model
.cityModels
.length) {
currentCity = controller
.model
.cityModels[index];
} else {
currentCity = CityModel();
}
showCitySelectionDialog(
context,
selectedCity: currentCity,
onCityChanged:
(CityModel newCity) {
final list = controller
.model.cityModels;
if (index <
list.length) {
// 替换
list[index] = newCity;
} else {
// 补齐并追加
list.add(newCity);
}
// controller.model
// .peopleList[
// index]['UTC'] =
// list[index].UTC;
controller.model
.peopleList[
index]
['city_id'] = list[
index]
.id;
controller.updateAll();
},
title: "选择城市".tr,
cityDataFuture: controller
.cityDataFuture, // 传入预加载的数据
colors:
CitySelectionColors(
pickerBackgroundColor:
stringToColor(
"#003058"),
confirmTextColor:
stringToColor(
"#84F5FF"),
selectedCityColor:
stringToColor(
"#84F5FF"),
selectedTextColor:
stringToColor(
"#011D33"),
),
);
});
},
child: Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(
'请选择城市'.tr,
style: TextStyle(
fontFamily:
'Readex Pro',
color:
Color(0xFF9EA4B7),
fontSize: 30.rpx,
letterSpacing: 0,
),
),
Row(
children: [
Text(
getCityModel(index) !=
null
? MyUtils
.getDetailedCityDisplayText(
getCityModel(
index))
: "请选择城市".tr,
style: TextStyle(
color: getCityModel(
index) !=
null &&
getCityModel(
index)!
.id !=
null
? themeController
.currentColor
.sc3
: Color(
0xFF9EA4B7),
fontSize: AppConstants()
.title_text_fontSize,
),
),
SizedBox(width: 16.rpx),
Container(
height: 30.rpx,
width: 30.rpx,
child: SvgPicture
.asset(
'assets/img/icon/expand_more.svg',
color:
Colors.white,
)),
],
),
],
),
),
),
getLine(),
Container(
height: 90.rpx,
margin: EdgeInsets.only(
left: 40.rpx,
right: 35.rpx,
),
child: InkWell(
onTap: () {
// if (widget.status ==
// BindType.share.code) {
// TopSlideNotification.show(
// context,
// text: "被分享用户只能修改用户名称",
// textColor:
// themeController
// .currentColor
// .sc9);
// return;
// }
FocusScope.of(context)
.requestFocus(
FocusNode());
Future.delayed(
Duration(
milliseconds: 250),
() {
// 获取当前时区
String? currentTimeZone;
if (controller
.model
.peopleList
.isNotEmpty &&
index <
controller
.model
.peopleList
.length &&
controller.model
.peopleList[
index]['UTC'] !=
null) {
currentTimeZone =
controller.model
.peopleList[
index]['UTC'];
}
showTimeMHTZonePickerDialog(
context,
title: "请选择时区".tr,
initialTimeZone:
currentTimeZone ?? "",
onConfirm: (String
selectedTimeZone) {
controller.model
.peopleList[
index]['UTC'] =
selectedTimeZone;
},
);
});
},
child: Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(
'请选择时区'.tr,
style: TextStyle(
fontFamily:
'Readex Pro',
color:
Color(0xFF9EA4B7),
fontSize: 30.rpx,
letterSpacing: 0,
),
),
Row(
children: [
Text(
controller
.model
.peopleList
.isNotEmpty &&
index <
controller
.model
.peopleList
.length &&
controller.model
.peopleList[index]
[
'UTC'] !=
null &&
controller
.model
.peopleList[
index]
[
'UTC']!
.isNotEmpty
? controller.model
.peopleList[
index]['UTC']!
: "请选择时区".tr,
style: TextStyle(
color: controller
.model
.peopleList
.isNotEmpty &&
index <
controller
.model
.peopleList
.length &&
controller.model
.peopleList[index]
[
'UTC'] !=
null &&
controller
.model
.peopleList[
index]
[
'UTC']!
.isNotEmpty
? themeController
.currentColor
.sc3
: Color(
0xFF9EA4B7),
fontSize: AppConstants()
.title_text_fontSize,
),
),
SizedBox(width: 16.rpx),
Container(
height: 30.rpx,
width: 30.rpx,
child:
SvgPicture.asset(
'assets/img/icon/expand_more.svg',
color: Colors.white,
),
),
],
),
],
),
),
),
getLine(),
Container(
height: 90.rpx,
margin: EdgeInsets.only(
left: 40.rpx, right: 35.rpx),
child: InkWell(
onTap: () {
FocusScope.of(context)
.requestFocus(
FocusNode());
Future.delayed(
Duration(
milliseconds: 250),
() {
// 获取当前已选择的慢病ID列表
final currentDiseaseIds = List<
String>.from(controller
.model
.peopleList[
index]
['disease_ids'] ??
[]);
showDiseaseSelectionDialog(
context,
selectedIds:
currentDiseaseIds,
onDiseasesChanged:
(List<String>
newDiseaseIds) {
// 更新模型数据
controller.model.peopleList[
index][
'disease_ids'] =
newDiseaseIds;
controller.updateAll();
},
title: "选择慢病".tr,
colors:
DiseaseSelectionColors(
pickerBackgroundColor:
stringToColor(
"#003058"),
confirmTextColor:
stringToColor(
"#84F5FF"),
selectedDiseaseColor:
stringToColor(
"#84F5FF"),
),
diseaseList: controller
.diseaseList,
);
});
},
child: Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(
'慢病管理'.tr,
style: TextStyle(
fontFamily:
'Readex Pro',
color:
Color(0xFF9EA4B7),
fontSize: 30.rpx,
letterSpacing: 0,
),
),
Obx(() {
final selectedIds = controller
.model
.peopleList[
index]
['disease_ids'] ??
[];
final diseases =
controller
.diseaseList;
return Row(
children: [
Text(
getSelectedDiseaseNames(
diseases,
List<String>.from(
selectedIds)),
style: TextStyle(
color: selectedIds.isNotEmpty
? themeController
.currentColor
.sc3
: themeController
.currentColor
.sc4,
fontSize:
AppConstants()
.title_text_fontSize,
),
),
SizedBox(
width: 16.rpx,
),
Container(
height: 30.rpx,
width: 30.rpx,
child: SvgPicture
.asset(
'assets/img/icon/expand_more.svg',
color: Colors
.white,
))
],
);
}),
],
),
),
),
getLine(),
],
),
)
],
);
}),
SizedBox(
height: 100.rpx,
),
],
)),
)),
),
),
)),
);
}
String time_08_Formatter_pattern(dynamic date, String pattern) {
if (date == null || date.toString().isEmpty) return "-";
try {
String normalized = date.toString().replaceAll("/", "-");
DateTime dt = DateTime.parse(normalized);
return DateFormat(pattern).format(dt);
} catch (e) {
return "-";
}
}
Future<void> getPersonData() async {
PeopleInfoController peopleInfoController = Get.find();
await peopleInfoController
.getPeoples(Get.arguments['mac'.tr]); // 控制器创建时立即执行
}
CityModel? getCityModel(int index) {
final list = controller.model.cityModels;
if (list.isEmpty || index >= list.length) return null;
return list[index];
}
}