Files
tuiche/lib/pages/mh_page/device/mht_people_info.dart
2025-12-17 14:55:51 +08:00

1008 lines
47 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/controller/mh_controller/people_info_controller.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';
import 'package:vbvs_app/pages/person/select_disease.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的城市数据
List<List<String>> diseaseIdsList = []; // 存储每个person的疾病ID列表
bool isLoading = true;
late Future<List<CityModel>> cityDataFuture;
// 添加疾病数据(这里需要根据实际情况获取,可以先设为空列表)
List diseaseList = [];
@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());
// 初始化疾病ID列表
diseaseIdsList = List.generate(peopleList.length, (index) => []);
// 加载疾病数据(这里需要实现获取疾病数据的方法)
await _loadDiseaseData();
// Initialize controllers after data is loaded
_initializeControllers();
setState(() => isLoading = false);
}
// 加载疾病数据的方法
Future<void> _loadDiseaseData() async {
try {
PeopleInfoController controller = Get.find();
await controller.getDiseaseData();
diseaseList = controller.diseaseList.value;
} catch (e) {
print("加载疾病数据失败: $e");
diseaseList = [];
}
}
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 "-";
}
}
// 获取已选择的慢病名称
String getSelectedDiseaseNames(List<String> selectedIds) {
if (selectedIds.isEmpty) return "请选择慢病".tr;
final selectedNames = diseaseList
.where((disease) => selectedIds.contains(disease['_id'].toString()))
.map((disease) => disease['disease_type_name'].toString())
.toList();
String displayText = selectedNames.join('');
if (displayText.length > 10) {
displayText = '${displayText.substring(0, 10)}...';
}
return displayText;
}
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'],
'disease_ids': personData['disease_ids'], // 添加疾病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];
final diseaseIds = diseaseIdsList[i];
// 添加城市信息到person数据
person['UTC'] = cityModel.UTC;
person['city_id'] = cityModel.id;
person['disease_ids'] = diseaseIds; // 添加疾病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(),
// 慢病管理部分
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(
diseaseIdsList[index]);
showDiseaseSelectionDialog(
context,
selectedIds: currentDiseaseIds,
diseaseList: diseaseList,
onDiseasesChanged:
(List<String> newDiseaseIds) {
setState(() {
diseaseIdsList[index] =
newDiseaseIds;
});
},
title: "选择慢病".tr,
colors: DiseaseSelectionColors(
pickerBackgroundColor:
stringToColor("#003058"),
confirmTextColor:
stringToColor("#84F5FF"),
selectedDiseaseColor:
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(
getSelectedDiseaseNames(
diseaseIdsList[index]),
style: TextStyle(
color: diseaseIdsList[index]
.isNotEmpty
? 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),
],
),
),
),
),
),
),
),
);
}
}