diff --git a/assets/mhlangs/en_US.json b/assets/mhlangs/en_US.json index e181e86..9eccb2f 100644 --- a/assets/mhlangs/en_US.json +++ b/assets/mhlangs/en_US.json @@ -597,5 +597,5 @@ "去查看": "View now", "正常值": "range:", "绑定设备": "Bind Device", - "设备分享提醒": "Device Sharing Alert" + "设备分享提醒": "Device Sharing Alert","生命体征":"Vital signs" } \ No newline at end of file diff --git a/assets/mhlangs/zh_CN.json b/assets/mhlangs/zh_CN.json index 82a616c..3b9af7b 100644 --- a/assets/mhlangs/zh_CN.json +++ b/assets/mhlangs/zh_CN.json @@ -593,5 +593,5 @@ "该设备的历史数据将被清除": "该设备的历史数据将被清除","有一条新的设备分享消息":"有一条新的设备分享消息","去查看":"去查看", "正常值":"正常值:", "绑定设备":"绑定设备", - "设备分享提醒":"设备分享提醒" + "设备分享提醒":"设备分享提醒","生命体征":"生命体征" } \ No newline at end of file diff --git a/assets/mhlangs/zh_TW.json b/assets/mhlangs/zh_TW.json index 9481b29..04d5bcd 100644 --- a/assets/mhlangs/zh_TW.json +++ b/assets/mhlangs/zh_TW.json @@ -594,6 +594,7 @@ "有一条新的设备分享消息": "有一條新的設備分享消息", "去查看": "去查看", "正常值": "正常值:", - "绑定设备":"绑定设备", - "设备分享提醒":"設備分享提醒" + "绑定设备": "绑定设备", + "设备分享提醒": "設備分享提醒", + "生命体征": "生命體徵" } \ No newline at end of file diff --git a/lib/pages/mh_page/homepage/mht_sleep_report_page.dart b/lib/pages/mh_page/homepage/mht_sleep_report_page.dart index 13ef1f9..ee0cd80 100644 --- a/lib/pages/mh_page/homepage/mht_sleep_report_page.dart +++ b/lib/pages/mh_page/homepage/mht_sleep_report_page.dart @@ -2,12 +2,26 @@ import 'dart:ui'; 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/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/ClickableContainer.dart'; +import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; +import 'package:vbvs_app/controller/date/CalendarController.dart'; +import 'package:vbvs_app/controller/sleep/sleep_report_controller.dart'; import 'package:vbvs_app/controller/user_info_controller.dart'; +import 'package:vbvs_app/language/AppLanguage.dart'; +import 'package:vbvs_app/pages/common/selectDialog.dart'; +import 'package:vbvs_app/pages/main_bottom/component/main_page_b_bottom_change.dart'; import 'package:vbvs_app/pages/mh_page/homepage/controller/mht_home_controller.dart'; +import 'package:vbvs_app/pages/sleep_report/component/SleepScoreWidget.dart'; +import 'package:vbvs_app/pages/sleep_report/component/SleepView.dart'; +import 'package:vbvs_app/pages/sleep_report/component/Vital_signs.dart'; +import 'package:vbvs_app/pages/sleep_report/component/new_sleep_view.dart'; import 'package:vbvs_app/pages/sleep_report/new_sleep_report_page.dart'; class MhtSleepReportPage extends StatefulWidget { @@ -17,11 +31,14 @@ class MhtSleepReportPage extends StatefulWidget { } class _MhtSleepReportPageState extends State { + SleepReportController sleepReportController = Get.find(); + CalendarController calendarController = Get.find(); UserInfoController userInfoController = Get.find(); MHTHomeController deviceController = Get.find(); MHTHomeController homeController = Get.find(); double borderRadius = 16.rpx; - + late DateTime selectedDate; + final RxBool isRightLimit = false.obs; var formFieldController = FormFieldController(null); var personInfo = {}.obs; @@ -32,6 +49,15 @@ class _MhtSleepReportPageState extends State { //查询人员信息列表 deviceController.getPersonList(); } + + WidgetsBinding.instance.addPostFrameCallback((_) { + final selected = DateTime.fromMillisecondsSinceEpoch( + DateTime.now().millisecondsSinceEpoch); + + calendarController.selectedDate.value = selected; + sleepReportController.selectedDate.value = selected; + sleepReportController.model.type = 1; + }); } @override @@ -215,11 +241,11 @@ class _MhtSleepReportPageState extends State { final list = deviceController.personnelList.value; if (list.isNotEmpty) { WidgetsBinding.instance.addPostFrameCallback((_) { - formFieldController.value = list[0]["mac".tr]; + formFieldController.value = list[0]["mac"]; personInfo.value = list[0]; homeController.selectPerson.value = list[0]; homeController.selectDevcie.value = - list[0]["mac".tr]; + list[0]["mac"]; deviceController .getSleeps(formFieldController.value); homeController.updateAll(); @@ -227,7 +253,7 @@ class _MhtSleepReportPageState extends State { } return Padding( padding: EdgeInsetsDirectional.fromSTEB( - 0.rpx, 40.rpx, 30.rpx, 10.rpx), + 0.rpx, 40.rpx, 30.rpx, 40.rpx), child: Container( width: MediaQuery.sizeOf(context).width, constraints: BoxConstraints( @@ -239,7 +265,7 @@ class _MhtSleepReportPageState extends State { ), child: Padding( padding: EdgeInsetsDirectional.fromSTEB( - 30.rpx, 16.rpx, 16.rpx, 25.rpx), + 30.rpx, 16.rpx, 0.rpx, 25.rpx), child: Container( width: MediaQuery.sizeOf(context).width, child: Row( @@ -247,16 +273,16 @@ class _MhtSleepReportPageState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - '健康报告'.tr, - style: TextStyle( - fontFamily: 'Readex Pro', - color: - themeController.currentColor.sc3, - letterSpacing: 0, - fontSize: 30.rpx, - ), - ), + // Text( + // '健康报告'.tr, + // style: TextStyle( + // fontFamily: 'Readex Pro', + // color: + // themeController.currentColor.sc3, + // letterSpacing: 0, + // fontSize: 30.rpx, + // ), + // ), ScrollbarTheme( data: ScrollbarThemeData( thumbColor: MaterialStateProperty.all( @@ -313,7 +339,7 @@ class _MhtSleepReportPageState extends State { final selectedPerson = list.firstWhere( (element) => - element['mac'.tr] == + element['mac'] == val, orElse: () => null, ); @@ -386,6 +412,52 @@ class _MhtSleepReportPageState extends State { ), ))), ), + ClickableContainer( + backgroundColor: Colors.transparent, + highlightColor: + themeController.currentColor.sc3, + padding: EdgeInsets.zero, + borderRadius: 8, + onTap: () { + showSleepCalendarBottomSheet( + type: sleepReportController + .model.type, + timestamp: selectedDate + .millisecondsSinceEpoch, + context: context, + mac: homeController + .selectDevcie.value, + onDateSelected: (newDate) { + sleepReportController + .selectedDate + .value = newDate; + calendarController.selectedDate + .value = newDate; + String data = + MyUtils.formatDate( + calendarController + .selectedDate + .value!); + loadSleepReport(data); + sleepReportController + .updateAll(); + calendarController.updateAll(); + }, + ); + }, + child: Padding( + padding: EdgeInsets.all(10.rpx), + child: Container( + width: 36.rpx, + height: 36.rpx, + child: SvgPicture.asset( + 'assets/img/icon/calendar.svg', + fit: BoxFit.cover, + color: themeController + .currentColor.sc3, + ), + ), + )), ], ), ), @@ -399,22 +471,42 @@ class _MhtSleepReportPageState extends State { Obx(() { if (homeController.selectDevcie.value != null && homeController.selectDevcie.value!.isNotEmpty && - deviceController.personnelList.value.isNotEmpty) + deviceController.personnelList.value.isNotEmpty && + sleepReportController.sleepReport.value != null) { return Expanded( - child: NewSleepReportPage( - data: { - "tag": "123", - 'date': DateTime.now().millisecondsSinceEpoch, - 'mac'.tr: homeController.selectDevcie.value, - 'person': homeController.selectPerson.value, - 'backgroundImg': - 'assets/images/new_background.png', - 'arrow': false, - 'noBackImg': true, - 'person_show': false, - }, - ), - ); + child: SingleChildScrollView( + child: Column( + children: [ + getTimeWidget(), + SleepScoreWidget( + sleepReport: + sleepReportController.sleepReport.value, + showLabelWrap: false, + ), + NewSleepViewWidget( + sleepReport: + sleepReportController.sleepReport.value), + SizedBox(height: 25.rpx), + VitalSignsWidget( + sleepReport: + sleepReportController.sleepReport.value) + ], + + // child: NewSleepReportPage( + // data: { + // "tag": "123", + // 'date': DateTime.now().millisecondsSinceEpoch, + // 'mac'.tr: homeController.selectDevcie.value, + // 'person': homeController.selectPerson.value, + // 'backgroundImg': + // 'assets/images/new_background.png', + // 'arrow': false, + // 'noBackImg': true, + // 'person_show': false, + // }, + // ), + ))); + } return Container(); }), ], @@ -427,4 +519,164 @@ class _MhtSleepReportPageState extends State { ), ); } + + Widget getTimeWidget() { + selectedDate = sleepReportController.selectedDate.value!; + final type = sleepReportController.model.type; + bool isChinese = AppLanguage().isChinese(); + String displayText = ''; + if (isChinese) { + // 日报 + displayText = + MyUtils.getFormatChineseTime(selectedDate.millisecondsSinceEpoch); + } else { + // Daily Report + displayText = + MyUtils.getFormatEnglishDate(selectedDate.millisecondsSinceEpoch); + } + + void onLeftArrowTap() { + isRightLimit.value = false; + sleepReportController.selectedDate.value = + selectedDate.subtract(const Duration(days: 1)); + calendarController.selectedDate.value = + sleepReportController.selectedDate.value; + // String date = MyUtils.formatToDate(widget.data['date']); + String data = MyUtils.formatDate(calendarController.selectedDate.value!); + loadSleepReport(data); + sleepReportController.updateAll(); + calendarController.updateAll(); + } + + void onRightArrowTap() { + final now = DateTime.now(); + final today = DateTime(now.year, now.month, now.day, 23, 59, 59, 999); + DateTime targetDate = selectedDate; + final nextDate = selectedDate.add(const Duration(days: 1)); + if (!nextDate.isAfter(today)) { + targetDate = nextDate; + } else { + isRightLimit.value = true; + return; + } + // 👉 更新数据并请求 + sleepReportController.selectedDate.value = targetDate; + calendarController.selectedDate.value = targetDate; + + String data = MyUtils.formatDate(targetDate); + loadSleepReport(data); + + sleepReportController.updateAll(); + calendarController.updateAll(); + } + + void onChangeArrowTap() { + sleepReportController.selectedDate.value = + selectedDate.subtract(const Duration(days: 1)); + + calendarController.selectedDate.value = + sleepReportController.selectedDate.value; + // String date = MyUtils.formatToDate(widget.data['date']); + String data = MyUtils.formatDate(calendarController.selectedDate.value!); + loadSleepReport(data); + + sleepReportController.updateAll(); + calendarController.updateAll(); + } + + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row( + children: [ + ClickableContainer( + backgroundColor: Colors.transparent, + highlightColor: themeController.currentColor.sc3, + padding: EdgeInsets.all(10.rpx), + borderRadius: 8.rpx, + onTap: onLeftArrowTap, + child: Container( + width: 30.rpx, + height: 30.rpx, + child: SvgPicture.asset( + 'assets/img/icon/arrow_left1.svg', + color: themeController.currentColor.sc3, + ), + ), + ), + Padding( + padding: EdgeInsets.symmetric(horizontal: 20.rpx), + child: Text( + displayText, + style: TextStyle( + fontSize: AppConstants().normal_text_fontSize, + color: themeController.currentColor.sc3, + ), + ), + ), + Obx(() => ClickableContainer( + backgroundColor: Colors.transparent, + highlightColor: themeController.currentColor.sc3, + padding: EdgeInsets.all(10.rpx), + borderRadius: 8.rpx, + onTap: isRightLimit.value ? () {} : onRightArrowTap, + child: Container( + width: 30.rpx, + height: 30.rpx, + child: SvgPicture.asset('assets/img/icon/arrow_right1.svg', + color: isRightLimit.value + ? Color(0xFF929699) + : Color(0xFFFFFFFF)), + ), + )) + ], + ), + ], + ); + } + + void loadSleepReport(String data) { + requestWithLog( + logTitle: "查询睡眠报告", + method: MyHttpMethod.get, + queryUrl: + "https://sleepdata.he-info.com/api/analysis/sleep/analysis?mac=${homeController.selectDevcie.value}&time=${data}&type=${sleepReportController.model.type}", + onSuccess: (res) { + print(res); + sleepReportController.sleepReport.value = res.data; + sleepReportController.updateAll(); + }, + onFailure: (res) { + if (MainPageBBottomChange.getCurrentIndex() != null) { + if (MainPageBBottomChange.getCurrentIndex() == 1) { + TopSlideNotification.show(context, + text: res.msg!, textColor: themeController.currentColor.sc9); + } + } else { + TopSlideNotification.show(context, + text: res.msg!, textColor: themeController.currentColor.sc9); + } + sleepReportController.sleepReport.value = {}; + sleepReportController.updateAll(); + print(res); + }); + } + + static String _getEnglishMonthName(int month) { + const monthNames = [ + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December' + ]; + return monthNames[month - 1]; + } } diff --git a/lib/pages/sleep_report/component/SleepScoreWidget.dart b/lib/pages/sleep_report/component/SleepScoreWidget.dart index 5b3ae69..cf42493 100644 --- a/lib/pages/sleep_report/component/SleepScoreWidget.dart +++ b/lib/pages/sleep_report/component/SleepScoreWidget.dart @@ -9,7 +9,12 @@ import 'package:EasyDartModule/EasyDartModule.dart' as es; class SleepScoreWidget extends StatefulWidget { var sleepReport; - SleepScoreWidget({super.key, required this.sleepReport}); + final bool showLabelWrap; + SleepScoreWidget({ + super.key, + required this.sleepReport, + this.showLabelWrap = true, + }); @override State createState() => _SleepScoreWidgetState(); @@ -135,12 +140,6 @@ class _SleepScoreWidgetState extends State { width: 256.rpx, height: 256.rpx, child: SegmentedCircleWithCenterWidget( - // segments: [ - // SegmentData(color: Colors.red, value: 30), - // SegmentData(color: Colors.green, value: 20), - // SegmentData(color: Colors.blue, value: 40), - // SegmentData(color: Colors.orange, value: 10), - // ], segments: segments, strokeWidth: 12.rpx, gapAngle: 8, @@ -148,13 +147,6 @@ class _SleepScoreWidgetState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - // Text( - // "睡眠评分".tr, - // style: TextStyle( - // color: stringToColor("#FFFFFF"), - // fontSize: - // AppConstants().normal_text_fontSize), - // ), Padding( padding: EdgeInsets.only( top: 12.rpx), // 👈 向下偏移的关键 @@ -185,37 +177,42 @@ class _SleepScoreWidgetState extends State { ) ], )), - SizedBox(height: 50.rpx), - Wrap( - spacing: 32.rpx, - runSpacing: 20.rpx, - children: showLabel.map((item) { - return Container( - padding: EdgeInsets.all(5.rpx), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Container( - width: 20.rpx, - height: 20.rpx, - decoration: BoxDecoration( - color: stringToColor("${item["color"]}"), - borderRadius: BorderRadius.circular(10.rpx), + if (widget.showLabelWrap) + Column( + children: [ + SizedBox(height: 50.rpx), + Wrap( + spacing: 32.rpx, + runSpacing: 20.rpx, + children: showLabel.map((item) { + return Container( + padding: EdgeInsets.all(5.rpx), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 20.rpx, + height: 20.rpx, + decoration: BoxDecoration( + color: stringToColor("${item["color"]}"), + borderRadius: BorderRadius.circular(10.rpx), + ), + ), + SizedBox(width: 17.rpx), + Text( + item["name"] + '(' + item['range'] + ")", + style: TextStyle( + color: Colors.white, + fontSize: 24.rpx, + ), + ), + ], ), - ), - SizedBox(width: 17.rpx), - Text( - item["name"] + '(' + item['range'] + ")", - style: TextStyle( - color: Colors.white, - fontSize: 24.rpx, - ), - ), - ], + ); + }).toList(), ), - ); - }).toList(), - ), + ], + ), ], ), ), diff --git a/lib/pages/sleep_report/component/Vital_signs.dart b/lib/pages/sleep_report/component/Vital_signs.dart new file mode 100644 index 0000000..8b41d23 --- /dev/null +++ b/lib/pages/sleep_report/component/Vital_signs.dart @@ -0,0 +1,193 @@ +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:path/path.dart'; +import 'package:vbvs_app/common/color/appConstants.dart'; +import 'package:vbvs_app/common/util/FitTool.dart'; +import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/component/tool/ClickableContainer.dart'; +import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart'; +import 'package:vbvs_app/pages/sleep_report/chart/GradientLine.dart'; +import 'package:vbvs_app/pages/sleep_report/chart/SnoreWaveform.dart'; +import 'package:EasyDartModule/EasyDartModule.dart' as es; + +//睡眠规律性 +class VitalSignsWidget extends StatefulWidget { + var sleepReport; + VitalSignsWidget({super.key, required this.sleepReport}); + + @override + State createState() => _VitalSignsWidgetState(); +} + +class _VitalSignsWidgetState extends State { + @override + void setState(VoidCallback callback) { + super.setState(callback); + } + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + try { + if (widget.sleepReport == null || + widget.sleepReport is! Map || + widget.sleepReport.isEmpty) { + return Container(); + } + + List showLabel = widget.sleepReport['sleepData']['type'] + .where((item) => item['show'] != false) + .toList(); + final bsList = widget.sleepReport['bs'] as List; + + List snoreValues = []; + List lightSnore = widget.sleepReport['ssp']['data'][0]; + List heavySnore = widget.sleepReport['ssp']['data'][1]; + snoreValues = [...lightSnore, ...heavySnore]; + snoreValues.sort((a, b) { + return a['st'].compareTo(b['st']); + }); + Map time = MyUtils.diffHoursMinutesMap( + widget.sleepReport['startTime'], widget.sleepReport['endTime']); + int hour = time['hours']; + int minutes = time['minutes']; + + final matched = bsList.firstWhere( + (item) => item['id'] == 105, + orElse: () => {}, + ); + + List stages = widget.sleepReport['sleepData']['stages']; + return Container( + width: double.infinity, + decoration: BoxDecoration( + color: themeController.currentColor.sc5, + borderRadius: + BorderRadius.circular(AppConstants().normal_container_radius), + ), + child: Padding( + padding: + EdgeInsetsDirectional.fromSTEB(26.rpx, 29.rpx, 26.rpx, 45.rpx), + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "生命体征".tr, + style: TextStyle( + color: themeController.currentColor.sc3, + fontSize: AppConstants().title_text_fontSize), + ), + ClickableContainer( + backgroundColor: Colors.transparent, + highlightColor: Colors.white, // 或设置为你需要的水波纹颜色 + padding: EdgeInsetsDirectional.fromSTEB( + 14.rpx, 0.rpx, 14.rpx, 0), // + borderRadius: 0.rpx, // 圆形点击区域 + onTap: () { + showTipDialog( + context, + Container( + child: Text( + "睡眠规律性是指个体睡眠模式在时间、时长、环境等方面呈现出的稳定性和一致性,是衡量睡眠质量的重要指标之一。" + .tr, + style: TextStyle( + fontSize: 26.rpx, + color: Colors.black, + ), + ), + ), + backgroundColor: Color(0xFFFFFFFF), + colors: [ + Color(0XFF1592AA), + Color(0xFF0C83A7), + Color(0xFF006FA3) + ], + ); + }, + child: Container( + padding: EdgeInsetsDirectional.fromSTEB( + 0, 0.rpx, 0.rpx, 0), // 外部 padding 移到内部 + width: 28.rpx, + height: 28.rpx, + child: SvgPicture.asset( + 'assets/img/icon/explain.svg', + fit: BoxFit.cover, + color: themeController.currentColor.sc4, + ), + ), + ), + ], + ), + ), + Row( + children: [ + Column( + children: [ + Text( + "1", + style: TextStyle( + color: themeController.currentColor.sc3, + fontSize: AppConstants().title_text_fontSize), + ), + ], + ), + Column( + children: [ + Text( + "1", + style: TextStyle( + color: themeController.currentColor.sc3, + fontSize: AppConstants().title_text_fontSize), + ) + ], + ), + Column( + children: [ + Text( + "1", + style: TextStyle( + color: themeController.currentColor.sc3, + fontSize: AppConstants().title_text_fontSize), + ) + ], + ) + ], + ), + Row(mainAxisAlignment: MainAxisAlignment.end, children: [ + OutlinedButton( + onPressed: () {}, + style: OutlinedButton.styleFrom( + side: const BorderSide(color: Color(0XFF85F5FF)), + foregroundColor: Color(0XFF85F5FF), + minimumSize: Size(202.rpx, 62.rpx), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), + ), + child: Text('查看详情'.tr), + ), + ]) + ], + ), + ), + ); + } catch (e) { + es.EasyDartModule.logger.error("打鼾监测绘制异常${e}"); + return Container(); + } + } +} diff --git a/lib/pages/sleep_report/component/new_sleep_view.dart b/lib/pages/sleep_report/component/new_sleep_view.dart new file mode 100644 index 0000000..4c88693 --- /dev/null +++ b/lib/pages/sleep_report/component/new_sleep_view.dart @@ -0,0 +1,258 @@ +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:vbvs_app/common/color/appConstants.dart'; +import 'package:vbvs_app/common/util/FitTool.dart'; +import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/component/tool/ClickableContainer.dart'; +import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart'; +import 'package:vbvs_app/pages/sleep_report/chart/GradientLine.dart'; +import 'package:vbvs_app/pages/sleep_report/chart/SnoreWaveform.dart'; +import 'package:EasyDartModule/EasyDartModule.dart' as es; + +//睡眠规律性 +class NewSleepViewWidget extends StatefulWidget { + var sleepReport; + NewSleepViewWidget({super.key, required this.sleepReport}); + + @override + State createState() => _NewSleepViewWidgetState(); +} + +class _NewSleepViewWidgetState extends State { + @override + void setState(VoidCallback callback) { + super.setState(callback); + } + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + try { + if (widget.sleepReport == null || + widget.sleepReport is! Map || + widget.sleepReport.isEmpty) { + return Container(); + } + + List showLabel = widget.sleepReport['sleepData']['type'] + .where((item) => item['show'] != false) + .toList(); + final bsList = widget.sleepReport['bs'] as List; + + List snoreValues = []; + List lightSnore = widget.sleepReport['ssp']['data'][0]; + List heavySnore = widget.sleepReport['ssp']['data'][1]; + snoreValues = [...lightSnore, ...heavySnore]; + snoreValues.sort((a, b) { + return a['st'].compareTo(b['st']); + }); + Map time = MyUtils.diffHoursMinutesMap( + widget.sleepReport['startTime'], widget.sleepReport['endTime']); + int hour = time['hours']; + int minutes = time['minutes']; + + final matched = bsList.firstWhere( + (item) => item['id'] == 105, + orElse: () => {}, + ); + + List stages = widget.sleepReport['sleepData']['stages']; + return Container( + width: double.infinity, + decoration: BoxDecoration( + color: themeController.currentColor.sc5, + borderRadius: + BorderRadius.circular(AppConstants().normal_container_radius), + ), + child: Padding( + padding: + EdgeInsetsDirectional.fromSTEB(26.rpx, 29.rpx, 26.rpx, 45.rpx), + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "睡眠规律性".tr, + style: TextStyle( + color: themeController.currentColor.sc3, + fontSize: AppConstants().title_text_fontSize), + ), + ClickableContainer( + backgroundColor: Colors.transparent, + highlightColor: Colors.white, // 或设置为你需要的水波纹颜色 + padding: EdgeInsetsDirectional.fromSTEB( + 14.rpx, 0.rpx, 14.rpx, 0), // + borderRadius: 0.rpx, // 圆形点击区域 + onTap: () { + showTipDialog( + context, + Container( + child: Text( + "睡眠规律性是指个体睡眠模式在时间、时长、环境等方面呈现出的稳定性和一致性,是衡量睡眠质量的重要指标之一。" + .tr, + style: TextStyle( + fontSize: 26.rpx, + color: Colors.black, + ), + ), + ), + backgroundColor: Color(0xFFFFFFFF), + colors: [ + Color(0XFF1592AA), + Color(0xFF0C83A7), + Color(0xFF006FA3) + ], + ); + }, + child: Container( + padding: EdgeInsetsDirectional.fromSTEB( + 0, 0.rpx, 0.rpx, 0), // 外部 padding 移到内部 + width: 28.rpx, + height: 28.rpx, + child: SvgPicture.asset( + 'assets/img/icon/explain.svg', + fit: BoxFit.cover, + color: themeController.currentColor.sc4, + ), + ), + ), + ], + ), + ), + Container( + alignment: Alignment(-1, 0), + child: Text( + "在床时长 ${formatDecimalHoursWithTr(matched['value'])}", + style: TextStyle(color: Color(0xFF929699), fontSize: 20.rpx), + ), + ), + SizedBox( + height: 49.rpx, + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 26.rpx, 0.rpx, 26.rpx, 0.rpx), + child: SnoreChartContainer( + snoreValues: snoreValues, + barData: stages, + startTime: widget.sleepReport['startTime'], + endTime: widget.sleepReport['endTime'], + showLabel: showLabel, + ), + ), + SizedBox( + height: 70.rpx, + ), + Wrap( + spacing: 55.rpx, + runSpacing: 20.rpx, + children: showLabel.map((item) { + return Container( + padding: EdgeInsets.all(5.rpx), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 20.rpx, + height: 20.rpx, + decoration: BoxDecoration( + color: item["color"] == null || item["color"] == "" + ? Colors.transparent + : stringToColor(item["color"]), + borderRadius: BorderRadius.circular(10.rpx), + ), + ), + SizedBox(width: 17.rpx), + Text( + item["name"], + style: TextStyle( + color: Colors.white, + fontSize: 24.rpx, + ), + ), + ], + ), + ); + }).toList(), + ), + Row(mainAxisAlignment: MainAxisAlignment.end, children: [ + OutlinedButton( + onPressed: () {}, + style: OutlinedButton.styleFrom( + side: const BorderSide(color: Color(0XFF85F5FF)), + foregroundColor: Color(0XFF85F5FF), + minimumSize: Size(202.rpx, 62.rpx), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), + ), + child: Text('查看详情'.tr), + ), + ]) + ], + ), + ), + ); + } catch (e) { + es.EasyDartModule.logger.error("打鼾监测绘制异常${e}"); + return Container(); + } + } + + String formatDecimalHoursWithTr(double value) { + int hours = value.floor(); // 小时整数部分 + int minutes = ((value - hours) * 60).round(); // 小数转分钟 + return "$hours${'小时'.tr}$minutes${'分钟'.tr}"; + } + + List> formatSleepData(List bs) { + List targetIds = [114, 117, 111, 108, 116]; + + return bs.where((item) { + final id = item['id']; + return id != null && targetIds.contains(id); + }).map((item) { + try { + final rawValue = item['value']; + String valueStr = ''; + + if (rawValue is num) { + final double raw = rawValue.toDouble(); + int hours = raw.floor(); + int minutes = ((raw - hours) * 60).round(); + + if (hours > 0) valueStr += '$hours${'h'}'; + if (minutes > 0) valueStr += '$minutes${'m'}'; + if (valueStr.isEmpty) valueStr = '0${'m'}'; + } else { + valueStr = rawValue?.toString() ?? '0${'m'}'; + } + + return { + 'name': item['name'] ?? '', + 'value': valueStr, + 'color': item['color'], + }; + } catch (e) { + return { + 'name': item['name'] ?? '', + 'value': '0${'m'}', + 'color': item['color'], + }; + } + }).toList(); + } +} diff --git a/lib/pages/sleep_report/new_sleep_report_page.dart b/lib/pages/sleep_report/new_sleep_report_page.dart index 6549a23..71567c3 100644 --- a/lib/pages/sleep_report/new_sleep_report_page.dart +++ b/lib/pages/sleep_report/new_sleep_report_page.dart @@ -174,40 +174,7 @@ class _NewSleepReportPageState extends State { ), child: Scaffold( backgroundColor: Colors.transparent, // 背景透明 - // appBar: AppBar( - // backgroundColor: widget.data['backgroundColor'] != null - // ? widget.data['backgroundColor'].withOpacity(0.8) - // : themeController.currentColor.sc5, - // automaticallyImplyLeading: false, - // iconTheme: IconThemeData(color: themeController.currentColor.sc3), - // titleSpacing: 0, - // title: Container( - // width: double.infinity, - // height: 180.rpx, - // child: Stack( - // alignment: Alignment.center, - // children: [ - // /// 居中标题 - // Text( - // '健康报告'.tr, - // style: TextStyle( - // fontFamily: 'Readex Pro', - // color: themeController.currentColor.sc3, - // letterSpacing: 0, - // fontSize: 30.rpx, - // ), - // ), - // /// 左边返回按钮 - // if (widget.data['arrow'] == null || - // widget.data['arrow'] == true) - // Positioned( - // left: 0, - // child: returnIconButtomNew(), - // ), - // ], - // ), - // ), - // ), + appBar: (widget.data['arrow'] != null && widget.data['arrow'] == false) ? null @@ -688,144 +655,7 @@ class _NewSleepReportPageState extends State { Stack( alignment: Alignment.bottomLeft, children: [ - // Row( - // mainAxisSize: MainAxisSize.max, - // children: [ - // Obx(() { - // return ClickableContainer( - // backgroundColor: - // Colors.transparent, - // highlightColor: themeController - // .currentColor.sc3, - // borderRadius: 8.rpx, - // padding: EdgeInsets.all(0), - // onTap: () async { - // loadSleepReport(1); - // }, - // child: Column( - // mainAxisSize: - // MainAxisSize.max, - // children: [ - // Container( - // width: 115 - // .rpx, // 固定宽度为 160.rpx - // alignment: Alignment - // .center, // 文字居中 - // child: Text( - // '日报'.tr, - // style: TextStyle( - // fontFamily: 'Inter', - // fontSize: AppConstants() - // .title_text_fontSize, - // letterSpacing: 0.0, - // color: sleepReportController - // .model - // .type == - // 1 - // ? themeController - // .currentColor - // .sc2 - // : themeController - // .currentColor - // .sc3, - // ), - // ), - // ), - // SizedBox(height: 10.rpx), - // ], - // ), - // ); - // }), - // Obx(() { - // return ClickableContainer( - // backgroundColor: - // Colors.transparent, - // highlightColor: themeController - // .currentColor.sc3, - // borderRadius: 8.rpx, - // padding: EdgeInsets.all(0), - // onTap: () async { - // loadSleepReport(2); - // }, - // child: Column( - // mainAxisSize: - // MainAxisSize.max, - // children: [ - // Container( - // width: 115 - // .rpx, // 固定宽度为 160.rpx - // alignment: Alignment - // .center, // 文字居中 - // child: Text('周报'.tr, - // style: TextStyle( - // fontFamily: 'Inter', - // fontSize: AppConstants() - // .title_text_fontSize, - // letterSpacing: 0.0, - // color: sleepReportController - // .model - // .type == - // 2 - // ? themeController - // .currentColor - // .sc2 - // : themeController - // .currentColor - // .sc3, - // )), - // ), - // SizedBox(height: 10.rpx), - // ], - // ), - // ); - // }), - // Obx(() { - // return ClickableContainer( - // backgroundColor: - // Colors.transparent, - // highlightColor: themeController - // .currentColor.sc3, - // borderRadius: 8.rpx, - // padding: EdgeInsets.all(0), - // onTap: () async { - // loadSleepReport(3); - // }, - // child: Column( - // mainAxisSize: - // MainAxisSize.max, - // children: [ - // Container( - // width: 115 - // .rpx, // 固定宽度为 160.rpx - // alignment: Alignment - // .center, // 文字居中 - // child: Text( - // '月报'.tr, - // style: TextStyle( - // fontFamily: 'Inter', - // fontSize: AppConstants() - // .title_text_fontSize, - // letterSpacing: 0.0, - // color: sleepReportController - // .model - // .type == - // 3 - // ? themeController - // .currentColor - // .sc2 - // : themeController - // .currentColor - // .sc3, - // ), - // ), - // ), - // SizedBox(height: 10.rpx), - // ], - // ), - // ); - // }), - // ], - // ), + Row( mainAxisAlignment: MainAxisAlignment.spaceAround,