Files
tuiche/lib/pages/mh_page/device/mht_people_info.dart
2025-12-17 11:01:30 +08:00

888 lines
41 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:flutterflow_ui/flutterflow_ui.dart';
import 'package:intl/intl.dart';
import 'package:vbvs_app/common/color/ServiceConstant.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/common/util/requestWithLog.dart';
import 'package:vbvs_app/component/tool/CustomCard.dart';
import 'package:vbvs_app/component/tool/TopSlideNotification.dart';
import 'package:vbvs_app/pages/common/selectDialog.dart';
import 'package:vbvs_app/pages/mh_page/device/controller/mht_bluetooth_controller.dart';
import 'package:vbvs_app/pages/mh_page/homepage/controller/mht_home_controller.dart';
import 'package:vbvs_app/pages/person/select_city.dart';
//保存人员信息
class MHTPeopleInfoPage extends StatefulWidget {
const MHTPeopleInfoPage({Key? key}) : super(key: key);
@override
State<MHTPeopleInfoPage> createState() => _MHTPeopleInfoPageState();
}
class _MHTPeopleInfoPageState extends State<MHTPeopleInfoPage> {
final MHTBlueToothController bluetoothController = Get.find();
final CityModelController cityController = Get.find<CityModelController>();
final List<TextEditingController> _nameControllers = [];
final List<TextEditingController> _contactControllers = [];
List<Map<String, dynamic>> peopleList = [];
List<CityModel> cityModels = []; // 存储每个person的城市数据
bool isLoading = true;
late Future<List<CityModel>> cityDataFuture;
@override
void initState() {
super.initState();
_initializeData();
}
void _initializeData() async {
// 先加载城市数据
cityDataFuture = cityController.loadAndSetCityData().then((success) {
return cityController.cityList;
});
// 等待城市数据加载完成后再初始化其他数据
await cityDataFuture;
final device = bluetoothController.currentFullDevice;
//todo 初始化传感器id
// Initialize person A
peopleList.add({
'mac': device?.macA,
'gender': 1,
'id': device!.macAID,
});
// Initialize person B if exists
if (device?.macB != null && device!.macB!.isNotEmpty) {
peopleList.add({
'mac': device.macB,
'gender': 1,
'id': device!.macBID,
});
}
// 初始化城市模型列表
cityModels = List.filled(peopleList.length, CityModel());
// Initialize controllers after data is loaded
_initializeControllers();
setState(() => isLoading = false);
}
void _initializeControllers() {
for (var person in peopleList) {
_nameControllers.add(TextEditingController(text: person["name"] ?? ""));
_contactControllers
.add(TextEditingController(text: person["contact"] ?? ""));
}
}
@override
void dispose() {
for (var controller in _nameControllers) {
controller.dispose();
}
for (var controller in _contactControllers) {
controller.dispose();
}
super.dispose();
}
Widget getLine() {
return Divider(
color: Color(0XFF929699),
thickness: 0.5.rpx,
height: 0,
);
}
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> _savePersonData(
Map<String, dynamic> personData, BuildContext context) async {
String serviceAddress = ServiceConstant.service_address;
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.personnel_info;
String queryUrl = "${serviceAddress}${serviceName}${serviceApi}";
try {
var body = {
'mac': personData['mac'],
'name': personData['name'],
'gender': personData['gender'],
'height': personData['height'],
'weight': personData['weight'],
'birthday': personData['birthday'] is DateTime
? DateFormat('yyyy-MM-dd').format(personData['birthday'])
: personData['birthday'],
'contact': personData['contact'],
'id': personData['id'],
'UTC': personData['UTC'],
'city_id': personData['city_id'],
};
await requestWithLog(
logTitle: "保存用户信息".tr,
method: MyHttpMethod.put,
queryUrl: queryUrl,
data: body,
onSuccess: (res) {
print(res);
},
onFailure: (res) {
TopSlideNotification.show(context,
text: res.msg!, textColor: themeController.currentColor.sc9);
print(res);
},
);
} catch (e) {
print("Error saving person data: $e");
}
}
// 根据城市ID查找完整的城市数据
CityModel? findCompleteCityDataById(int? id, List<CityModel> cityData) {
if (id == null) return null;
try {
// 遍历三级数据结构查找匹配的ID
for (var country in cityData) {
// 检查国家节点是否有ID匹配
if (country.id == id) {
return CityModel(
id: country.id,
value: country.value,
label: country.label,
country: country.value ?? country.country,
province: null,
city: null,
UTC: country.UTC,
children: country.children,
);
}
// 检查省份节点
for (var province in country.children ?? []) {
if (province.id == id) {
return CityModel(
id: province.id,
value: province.value,
label: province.label,
country: country.value ?? country.country,
province: province.value ?? province.province,
city: null,
UTC: province.UTC,
children: province.children,
);
}
// 检查城市节点
for (var city in province.children ?? []) {
if (city.id == id) {
return CityModel(
id: city.id,
value: city.value,
label: city.label,
country: country.value ?? country.country,
province: province.value ?? province.province,
city: city.value ?? city.city,
UTC: city.UTC,
children: city.children,
);
}
}
}
}
return null;
} catch (e) {
print("根据ID查找完整城市数据失败$e");
return null;
}
}
@override
Widget build(BuildContext context) {
if (isLoading) {
return Center(
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(
themeController.currentColor.sc1,
),
),
);
}
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,
),
),
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: 20.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 < peopleList.length; i++) {
var person = peopleList[i];
// 验证身高
if (person['height'] != null &&
person['height'].toString().isNotEmpty &&
int.tryParse(person['height'].toString()) ==
null) {
TopSlideNotification.show(context,
text: "请选择身高".tr,
textColor: Color(0xFFFF7159));
isValid = false;
break;
}
// 验证体重
if (person['weight'] != null &&
person['weight'].toString().isNotEmpty &&
int.tryParse(person['weight'].toString()) ==
null) {
TopSlideNotification.show(context,
text: "请选择体重".tr,
textColor: Color(0xFFFF7159));
isValid = false;
break;
}
// 验证联系方式
if (person['contact'] != null &&
person['contact'].toString().isNotEmpty &&
!MyUtils.isValidPhoneNumber(
person['contact'].toString())) {
TopSlideNotification.show(context,
text: "请输入正确的联系人电话".tr,
textColor: Color(0xFFFF7159));
isValid = false;
break;
}
// 验证城市选择
final cityModel = cityModels[i];
if (cityModel.id == null) {
TopSlideNotification.show(context,
text: "请选择城市".tr,
textColor: Color(0xFFFF7159));
isValid = false;
break;
}
}
if (isValid) {
// 保存所有人员数据
for (int i = 0; i < peopleList.length; i++) {
final person = peopleList[i];
final cityModel = cityModels[i];
// 添加城市信息到person数据
person['UTC'] = cityModel.UTC;
person['city_id'] = cityModel.id;
await _savePersonData(person, context);
}
TopSlideNotification.show(context, text: "保存成功".tr);
MHTHomeController mhtHomeController = Get.find();
mhtHomeController.getPersonList();
// 跳转到下一页
Map data = {};
final device =
bluetoothController.currentFullDevice;
data['_id'] = device!.deviceID;
data['isNextStep'] = true;
Get.toNamed("/editBedPage", arguments: data);
}
},
colors: const [
Color(0xFFFCFCFC),
Color(0xFFF8FAF9),
Color(0XFFECF6F3),
Color(0XFFD9F0E9),
Color(0xFFCEECE3)
],
child: Container(
width: 120.rpx,
height: 60.rpx,
alignment: Alignment.center,
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,
child: SingleChildScrollView(
child: Column(
children: [
...List.generate(peopleList.length, (index) {
final person = peopleList[index];
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(),
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(
controller: _nameControllers[index],
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: Colors.white),
border: InputBorder.none,
contentPadding:
EdgeInsets.all(0)),
onChanged: (value) {
setState(() {
peopleList[index]["name"] = value;
});
},
),
),
],
),
),
getLine(),
Container(
margin: EdgeInsets.only(
left: 40.rpx, right: 35.rpx),
width: double.infinity,
height: 90.rpx,
decoration: BoxDecoration(),
child: InkWell(
onTap: () {
FocusScope.of(context)
.requestFocus(FocusNode());
Future.delayed(
const Duration(milliseconds: 250),
() {
showOneSelectionDialog(context,
title: "选择性别".tr,
arr: ["".tr, "".tr],
checkIndex: peopleList[index]
['gender'],
checkChange: (sindex) {
setState(() {
peopleList[index]['gender'] =
sindex;
});
}).then((d) {});
});
},
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(
'${peopleList[index]['gender'] == 1 ? ''.tr : ''.tr}',
textAlign: TextAlign.right,
style: TextStyle(
fontFamily: 'Readex Pro',
color: Colors.white,
fontSize: 30.rpx,
letterSpacing: 0,
),
),
),
SizedBox(width: 16.rpx),
Icon(
Icons.expand_more,
color: Colors.white,
size: 48.rpx,
),
],
),
],
),
),
),
getLine(),
Container(
width: double.infinity,
height: 90.rpx,
margin: EdgeInsets.only(
left: 40.rpx, right: 35.rpx),
decoration: BoxDecoration(),
child: InkWell(
onTap: () {
FocusScope.of(context)
.requestFocus(FocusNode());
Future.delayed(
const Duration(milliseconds: 250),
() {
showHeightPickerDialog(
context,
title: "选择身高".tr,
initialHeight: int.tryParse(
peopleList[index]['height'] ??
'170') ??
170,
onConfirm: (int selectedHeight) {
setState(() {
peopleList[index]['height'] =
selectedHeight.toString();
});
},
);
});
},
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(
peopleList[index]['height'] !=
null
? "${peopleList[index]['height']} cm"
: '',
style: TextStyle(
fontFamily: 'Readex Pro',
color: Colors.white,
fontSize: 30.rpx,
),
),
SizedBox(width: 16.rpx),
Icon(Icons.expand_more,
color: Colors.white,
size: 48.rpx),
],
),
],
),
),
),
getLine(),
Container(
width: double.infinity,
height: 90.rpx,
margin: EdgeInsets.only(
left: 40.rpx, right: 35.rpx),
decoration: BoxDecoration(),
child: InkWell(
onTap: () {
FocusScope.of(context)
.requestFocus(FocusNode());
Future.delayed(
const Duration(milliseconds: 250),
() {
showWeightPickerDialog(
context,
title: "选择体重".tr,
initialWeight: "50",
onConfirm: (int selectedWeight) {
setState(() {
peopleList[index]['weight'] =
selectedWeight.toString();
});
},
);
});
},
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(
peopleList[index]['weight'] !=
null
? "${peopleList[index]['weight']} kg"
: '',
style: TextStyle(
fontFamily: 'Readex Pro',
color: Colors.white,
fontSize: 30.rpx,
),
),
SizedBox(width: 16.rpx),
Icon(Icons.expand_more,
color: Colors.white,
size: 48.rpx),
],
),
],
),
),
),
getLine(),
Container(
width: double.infinity,
height: 90.rpx,
margin: EdgeInsets.only(
left: 40.rpx, right: 35.rpx),
decoration: BoxDecoration(),
child: InkWell(
onTap: () {
FocusScope.of(context)
.requestFocus(FocusNode());
Future.delayed(
const Duration(milliseconds: 250),
() {
showDateSelectionDialog(context,
title: "选择生日".tr,
checkDate: peopleList[index]
['birthday'] is DateTime
? peopleList[index]['birthday']
: DateTime.tryParse(
peopleList[index]
['birthday'] ??
'') ??
DateTime.now(),
checkChange: (DateTime d) {
setState(() {
peopleList[index]['birthday'] = d;
});
}).then((d) {});
});
},
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(
peopleList[index]['birthday'] !=
null
? time_08_Formatter_pattern(
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),
Icon(
Icons.expand_more,
color: Colors.white,
size: 48.rpx,
),
],
),
],
),
),
),
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(
controller:
_contactControllers[index],
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: Colors.white),
border: InputBorder.none,
contentPadding:
EdgeInsets.all(0)),
onChanged: (value) {
setState(() {
peopleList[index]['contact'] =
value;
});
},
),
),
],
),
),
getLine(),
// 城市选择部分
Container(
height: 90.rpx,
margin: EdgeInsets.only(
left: 40.rpx, right: 35.rpx),
child: InkWell(
onTap: () {
FocusScope.of(context)
.requestFocus(FocusNode());
Future.delayed(
const Duration(milliseconds: 250),
() {
showCitySelectionDialog(
context,
selectedCity: cityModels[index],
onCityChanged: (CityModel newCity) {
setState(() {
cityModels[index] = newCity;
});
},
title: "选择城市".tr,
cityDataFuture: cityDataFuture,
colors: CitySelectionColors(
pickerBackgroundColor:
stringToColor("#003058"),
confirmTextColor:
stringToColor("#84F5FF"),
selectedCityColor:
stringToColor("#84F5FF"),
),
);
});
},
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
'请选择城市'.tr,
style: TextStyle(
fontFamily: 'Readex Pro',
color: Color(0xFF9EA4B7),
fontSize: 30.rpx,
letterSpacing: 0,
),
),
Row(
children: [
Text(
cityModels[index].id != null
? MyUtils
.getDetailedCityDisplayText(
cityModels[index])
: "请选择城市".tr,
style: TextStyle(
color:
cityModels[index].id != null
? Colors.white
: themeController
.currentColor.sc4,
fontSize: 30.rpx,
),
),
SizedBox(width: 16.rpx),
Icon(Icons.expand_more,
color: Colors.white,
size: 48.rpx),
],
),
],
),
),
),
getLine(),
],
))
],
);
}),
SizedBox(height: 100.rpx),
],
),
),
),
),
),
),
),
);
}
}