From 98cd7f4e6a07285bcadad7d00be5b9fdf8512b1b Mon Sep 17 00:00:00 2001 From: wyf <494641114@qq.com> Date: Tue, 27 May 2025 23:09:31 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=9D=A1=E7=9C=A0=E6=8A=A5?= =?UTF-8?q?=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/img/icon/calendar.svg | 1 + assets/img/moon.png | Bin 0 -> 1979 bytes assets/img/sun.png | Bin 0 -> 1919 bytes assets/langs/zh_CN.json | 30 +- lib/common/color/ServiceConstant.dart | 2 + lib/common/util/MyUtils.dart | 21 + lib/component/base/GradientSwitch.dart | 90 ++ .../home_page/DynamicReportDetailWidget.dart | 18 +- .../home_page/SleepDataModuleWidget.dart | 91 +- .../device/blueteeth_bind_controller.dart | 2 +- .../common_message_setting_controller.dart | 38 + .../common_message_setting_controller.g.dart | 28 + .../message/message_setting_controller.dart | 34 + .../message/message_setting_controller.g.dart | 21 + .../sleep/sleep_report_controller.dart | 1 + lib/main.dart | 4 + .../component/DeviceDataComponentWidget.dart | 3 +- .../device/component/MessageSetting.dart | 574 +++++++++ .../component/ReviewMessageWidgetWidget.dart | 2 +- lib/pages/device/message_review_page.dart | 87 +- .../SingleBlueteethDeviceCompoentWidget.dart | 11 +- lib/pages/device_bind/device_calibration.dart | 10 +- lib/pages/device_bind/device_share_page.dart | 99 +- lib/pages/login/login.dart | 31 +- lib/pages/login/other_login.dart | 133 ++- lib/pages/main_bottom/mine_page.dart | 6 +- lib/pages/setting/common_mesaage_setting.dart | 896 ++++++++++++++ .../sleep_report/chart/DataShowWidget.dart | 2 +- lib/pages/sleep_report/chart/DotBarChart.dart | 173 ++- .../sleep_report/chart/GradientLine.dart | 48 + .../chart/SegmentedCirclePainter.dart | 24 +- .../sleep_report/chart/SleepRadarChart.dart | 131 +-- .../sleep_report/chart/SnoreWaveform.dart | 273 +++++ .../component/AIAdviceWidget.dart | 39 +- .../component/BreathPauseWidget.dart | 59 +- .../sleep_report/component/BreatheCard.dart | 69 ++ .../component/BreathePauseNewWidget.dart | 166 +++ .../component/BreatheStandardWidget.dart | 37 +- .../component/CompareSleepWidget.dart | 55 +- .../component/DiseasePercentsWidget.dart | 120 +- .../component/HeartChangeWidget.dart | 155 +-- .../component/HeartHealthWidget.dart | 62 +- .../component/HeartPointWidget.dart | 49 +- .../sleep_report/component/HeartRateCard.dart | 69 ++ .../component/HeartRateStandardWidget.dart | 30 +- .../component/SkinPercentWidget.dart | 41 +- .../sleep_report/component/SleepCard.dart | 69 ++ .../component/SleepScoreWidget.dart | 105 +- .../sleep_report/component/SleepView.dart | 388 ++++++ .../component/SnoreViewWidget.dart | 66 +- .../component/ZiZhuShenJingPercentWidget.dart | 38 +- .../sleep_report/new_sleep_report_page.dart | 1041 +++++++++-------- lib/pages/user/setting_page.dart | 51 + lib/routers/routers.dart | 17 +- 54 files changed, 4450 insertions(+), 1160 deletions(-) create mode 100644 assets/img/icon/calendar.svg create mode 100644 assets/img/moon.png create mode 100644 assets/img/sun.png create mode 100644 lib/component/base/GradientSwitch.dart create mode 100644 lib/controller/message/common_message_setting_controller.dart create mode 100644 lib/controller/message/common_message_setting_controller.g.dart create mode 100644 lib/controller/message/message_setting_controller.dart create mode 100644 lib/controller/message/message_setting_controller.g.dart create mode 100644 lib/pages/device/component/MessageSetting.dart create mode 100644 lib/pages/setting/common_mesaage_setting.dart create mode 100644 lib/pages/sleep_report/chart/GradientLine.dart create mode 100644 lib/pages/sleep_report/chart/SnoreWaveform.dart create mode 100644 lib/pages/sleep_report/component/BreatheCard.dart create mode 100644 lib/pages/sleep_report/component/BreathePauseNewWidget.dart create mode 100644 lib/pages/sleep_report/component/HeartRateCard.dart create mode 100644 lib/pages/sleep_report/component/SleepCard.dart create mode 100644 lib/pages/sleep_report/component/SleepView.dart diff --git a/assets/img/icon/calendar.svg b/assets/img/icon/calendar.svg new file mode 100644 index 0000000..74d3b59 --- /dev/null +++ b/assets/img/icon/calendar.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/img/moon.png b/assets/img/moon.png new file mode 100644 index 0000000000000000000000000000000000000000..7a1b2146c174bf78cd94baf094ddeb303174dbc8 GIT binary patch literal 1979 zcmX|A3s4hh5I#glph8a&hls&gwHTFpC@RL8_CO*;G)6#2c^=5i5a~&vT3c&;L9J9H z4@C%gf)E7JAmw2}IUcPNd>rBf%2*>7FvWIGrLD9Hb}#8Pv$xsXfA`yOzdt!9lLk6E z%yK{w#8DCy5CY$U++$}8J}$M&3qd9|%Ql3G1IZ0hg(k2Vjd=%Ujz4dXE#WNgoS7&# zCznq^1}vp=0u1s=7bX;yoWT*)q-ZB13r&Qj1p+Gq_=Hk(@`VZT&;>0}nOMg4S~Kt} zub7A~GC{y)v>Y-_i7Dphf&moZ0}l9|U=`dHYZ(MqdR7`h8v@7x+a2f4zy!L$xS^H+ zU?Bh-Hia8=>8*JeHmWoMCVRC21Xn88Dia;h4aHVk%sX3osH>8Po*BP#A-&bt;0w)g+@*1Y zFA4Aq+jg}4Ou`o~elyl-)hq42W(Z~9xYkfd8)Pj_&HsOL)$uvbDcKD{&yv{N@xAqD zUDH1KU2#`Ad|g@BtlHXG_@ujKAn*H2y3? zJ8C;!7A7V2DP|Wt53VVlBAOYz^wq}Wqs@CYHwGgU9C@b9Tu(Sn zdEUP?JZQ{5BfG;`(BzXneNOSpk58Sbo#mQan3?g@C(}QQd^5gRI4?Wv8%a=m)Jgf= z@P0AF6Az`o=i%|{>(k@iq;J{KHUExur;>!t)yTq87wyZ7er;p+B}0FP*EGb5uivos z%j$T=_}WMH_2|31y1M$MJ+UE~H@h@#FO!F2rIc@7&PK+2pJ2YS%48UwJSngsf6XPQ zqzLrxT;=2e2VdT~JqIMhwRWq})wc524X$s_otQxHQ%H$G-h#?&rHd zJqA@-n|<>Z1xtDDleQEz`0%%3A&#C!Im?!ivt=DS*SdLSKJTkM^rochIr$)@O(;nf z4K_U~da&D0IFH}hT;m7vMP$nCt!mH>T089KIs>PW+ePxdQPeFJPfgBK-SI2`@U{h`%Y=7%Qw#x$oS zw{AK+^2c!C4BLA{8LWF>2LIUAxILF2kKVa=Wist<% zKHg!9E&Xe_KJ@4H{g2r{oq|Lr5wjL#jaUU-BE=af`s zA6hj0+1fPyZ_!6SZp?c$I+k^4*M5^p>~-W~Q(#TRsvA6aclUSCT>c6UlOd`DFJy`r TWZT7c@RvsbWQ%4F>{*b%+Qxf^ZswAO79hk@7X+u!HMOH}21#lQMS(zW&<-p# z@qq-WP()2UxJMK<=+tmVqkwz)ED0%xpj3XK7VL}$;tyg#Qu?zr_}UqdcE!ggFpLCA zVkC|;DYk(y60`!RF`B6`k~EED7-nSBIFleTN-BygHZlb8Vt z5t5Q5CB+Q{MPh6=W}%c89K#9Pptn#Ya6%6@n;?}InrXq5G+|`xNuz$x3UN_-14SEgj4&wJ3O21In0mHBL9?ZV(nuL;AXOTnGp463dMT;YA0%F! z25S=(QOP?Qj46TW9Vd?G&xC<`eajqyixn9sPnPTmL##+J%5nIP442SrT8}99e>z9E3orW`GDzn z{qv)Pp-4>abJ;SIg_Df3dn;Fo)DT?iS$U*6lDubUN2Bo3cQQJs=AZJs>~BdZHEn=ZUmXXCbalP4aZ zQ>5*EXpsA@9di5DC99>o-b40!gSxj_XnwmWgR34mSe5Cb@?Tssk}7&U|HOgDipyKg zO`_b~RO_i_RsMbF1a0eYsz3Z`>J&{CilXLr%#|c&t}86Ob@(a=6!zO{md!Z4o1SBJp+3zsbo!$i|Vdd zWUh<+g+j(4e4I_W~cR47eYEGids|rK>cdIiWrkgQ52yzZsg}-@iLk(;BujpqG{vNFnyg#sd#LbO#=g|zu=SbJW7w)&&f>BGn?!r@x^G(NFIDSwcT1JHSDfHO zLFRhzN6A@bADC&cy7 diffHoursMinutesMap(int startMillis, int endMillis) { + final duration = Duration(milliseconds: endMillis - startMillis); + final hours = duration.inHours; + final minutes = duration.inMinutes % 60; + return { + "hours": hours, + "minutes": minutes, + }; + } + + static String formatToHHmm(int timestampMillis) { + final dateTime = DateTime.fromMillisecondsSinceEpoch(timestampMillis); + final twoDigits = (int n) => n.toString().padLeft(2, '0'); + return '${twoDigits(dateTime.hour)}:${twoDigits(dateTime.minute)}'; + } + static ApiResponse formatResponse( ApiResponse res, String successMsg, diff --git a/lib/component/base/GradientSwitch.dart b/lib/component/base/GradientSwitch.dart new file mode 100644 index 0000000..40b8f69 --- /dev/null +++ b/lib/component/base/GradientSwitch.dart @@ -0,0 +1,90 @@ +import 'package:flutter/material.dart'; + +class GradientSwitch extends StatefulWidget { + final bool value; + final ValueChanged onChanged; + final Gradient activeGradient; + final Color inactiveColor; + final Color activeThumbColor; + final Color inactiveThumbColor; + + const GradientSwitch({ + super.key, + required this.value, + required this.onChanged, + required this.activeGradient, + this.inactiveColor = Colors.grey, + this.activeThumbColor = Colors.white, + this.inactiveThumbColor = Colors.white, + }); + + @override + State createState() => _GradientSwitchState(); +} + +class _GradientSwitchState extends State + with SingleTickerProviderStateMixin { + late AnimationController _controller; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + vsync: this, duration: const Duration(milliseconds: 200)); + _controller.value = widget.value ? 1.0 : 0.0; + } + + @override + void didUpdateWidget(covariant GradientSwitch oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.value != oldWidget.value) { + widget.value ? _controller.forward() : _controller.reverse(); + } + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () => widget.onChanged(!widget.value), + child: AnimatedBuilder( + animation: _controller, + builder: (context, child) { + final align = Alignment.lerp( + Alignment.centerLeft, Alignment.centerRight, _controller.value)!; + + // 动态计算小球颜色(线性过渡) + final thumbColor = Color.lerp(widget.inactiveThumbColor, + widget.activeThumbColor, _controller.value)!; + + return Container( + width: 60, + height: 30, + padding: const EdgeInsets.all(3), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30), + gradient: widget.value ? widget.activeGradient : null, + color: widget.value ? null : widget.inactiveColor, + ), + child: Align( + alignment: align, + child: Container( + width: 24, + height: 24, + decoration: BoxDecoration( + color: thumbColor, + shape: BoxShape.circle, + boxShadow: const [ + BoxShadow( + color: Colors.black26, + blurRadius: 2, + offset: Offset(0, 1)), + ], + ), + ), + ), + ); + }, + ), + ); + } +} diff --git a/lib/component/home_page/DynamicReportDetailWidget.dart b/lib/component/home_page/DynamicReportDetailWidget.dart index 2fadefe..5b00bea 100644 --- a/lib/component/home_page/DynamicReportDetailWidget.dart +++ b/lib/component/home_page/DynamicReportDetailWidget.dart @@ -16,6 +16,7 @@ class DynamicReportDetailWidget extends StatelessWidget { final List sleepDataModuleWidgets; final ThemeController themeController = Get.find(); final Map targetDevice; + late ScrollController _scrollController; DynamicReportDetailWidget({ required this.sleepDateWidgets, @@ -25,6 +26,18 @@ class DynamicReportDetailWidget extends StatelessWidget { @override Widget build(BuildContext context) { + _scrollController = ScrollController(); + WidgetsBinding.instance.addPostFrameCallback((_) { + if (_scrollController.hasClients) { + // _scrollController.jumpTo(_scrollController.position.maxScrollExtent); + // 如果你希望有动画,用下面这句替代 jumpTo: + _scrollController.animateTo( + _scrollController.position.maxScrollExtent, + duration: Duration(milliseconds: 300), + curve: Curves.easeOut, + ); + } + }); return Padding( padding: EdgeInsetsDirectional.fromSTEB(0, 25.rpx, 0, 25.rpx), child: Container( @@ -40,7 +53,7 @@ class DynamicReportDetailWidget extends StatelessWidget { child: Column( mainAxisSize: MainAxisSize.max, children: [ - _buildHeader(context,targetDevice), + _buildHeader(context, targetDevice), SizedBox( height: 33.rpx, ), @@ -67,7 +80,7 @@ class DynamicReportDetailWidget extends StatelessWidget { borderRadius: 0, padding: EdgeInsets.zero, onTap: () async { - await Get.toNamed("/bodyDevice",arguments: targetDevice); + await Get.toNamed("/bodyDevice", arguments: targetDevice); }, child: Text( '${targetDevice['person']?['name'] == null ? '未命名'.tr : targetDevice['person']['name']}', @@ -132,6 +145,7 @@ class DynamicReportDetailWidget extends StatelessWidget { width: double.infinity, decoration: BoxDecoration(), child: SingleChildScrollView( + controller: _scrollController, // ⭐️ 关键点 scrollDirection: Axis.horizontal, child: Row( mainAxisSize: MainAxisSize.max, diff --git a/lib/component/home_page/SleepDataModuleWidget.dart b/lib/component/home_page/SleepDataModuleWidget.dart index dde4a6d..934bae6 100644 --- a/lib/component/home_page/SleepDataModuleWidget.dart +++ b/lib/component/home_page/SleepDataModuleWidget.dart @@ -101,76 +101,51 @@ class _SleepDataModuleWidgetState extends State { ), ], ), - // Container( - // width: MediaQuery.sizeOf(context).width * 0.07, - // height: MediaQuery.sizeOf(context).height * 0.014, - // constraints: BoxConstraints( - // minWidth: 43.rpx, - // minHeight: 36.rpx, - // ), - // child: FFButtonWidget( - // onPressed: () { - // print('Button pressed ...'); - // }, - // // text: '${widget.data['level']}', - // text: '${widget.data['level']}', - // options: FFButtonOptions( - // height: 40.rpx, - // padding: EdgeInsets.zero, - // // color: themeController.currentColor.sc14, - // color: stringToColor('${widget.data['color']}'), - // textStyle: - // FlutterFlowTheme.of(context).titleSmall.override( - // fontFamily: 'Inter Tight', - // color: themeController.currentColor.sc3, - // // color: stringToColor('${widget.data['color']}'), - // letterSpacing: 0.0, - // fontSize: 15.rpx, - // ), - // elevation: 0, - // borderRadius: BorderRadius.circular(8.rpx), - // ), - // ), - // ), - - ClickableContainer( - backgroundColor: stringToColor('${widget.data['color']}'), - highlightColor: themeController.currentColor.sc3, - padding: EdgeInsets.symmetric( - horizontal: 0.rpx, - vertical: 0.rpx, - ), - borderRadius: 8.rpx, - onTap: () { - print('Button pressed ...'); - }, - child: Container( - alignment: Alignment.center, - constraints: BoxConstraints( - minWidth: 43.rpx, - minHeight: 36.rpx, + if (widget.data['level'] != null) + ClickableContainer( + backgroundColor: (widget.data['color'] == null || + widget.data['color'].toString().isEmpty) + ? Colors.transparent + : stringToColor(widget.data['color']), + highlightColor: themeController.currentColor.sc3, + padding: EdgeInsets.symmetric( + horizontal: 0.rpx, + vertical: 0.rpx, ), - child: Text( - '${widget.data['level']}', - style: FlutterFlowTheme.of(context).titleSmall.override( - fontFamily: 'Inter Tight', - color: themeController.currentColor.sc3, - letterSpacing: 0.0, - fontSize: 15.rpx, - ), + borderRadius: 8.rpx, + onTap: () { + print('Button pressed ...'); + }, + child: Container( + alignment: Alignment.center, + constraints: BoxConstraints( + minWidth: 43.rpx, + minHeight: 36.rpx, + ), + child: Text( + '${widget.data['level']}', + style: FlutterFlowTheme.of(context).titleSmall.override( + fontFamily: 'Inter Tight', + color: themeController.currentColor.sc3, + letterSpacing: 0.0, + fontSize: 15.rpx, + ), + ), ), ), - ), ].divide(SizedBox(width: 0.rpx)), ), Text( - '${widget.data['range']}', + "正常值".tr + + '${(widget.data['range'] ?? '').toString().isEmpty ? '未知数据'.tr : widget.data['range']}', style: FlutterFlowTheme.of(context).bodyMedium.override( fontFamily: 'Inter', fontSize: AppConstants().small_text_fontSize, letterSpacing: 0.0, color: themeController.currentColor.sc4, ), + maxLines: 1, + overflow: TextOverflow.ellipsis, ), ], ), diff --git a/lib/controller/device/blueteeth_bind_controller.dart b/lib/controller/device/blueteeth_bind_controller.dart index 67ba02c..08f3ef0 100644 --- a/lib/controller/device/blueteeth_bind_controller.dart +++ b/lib/controller/device/blueteeth_bind_controller.dart @@ -26,7 +26,7 @@ class BlueteethBindModel { @JsonKey(ignore: true) List? betDevicelist = []; //网络状态 @JsonKey(ignore: true) - List? blelist = []; + List? blelist = [];//蓝牙扫描的原始数据 List bindArr = ["", "", ""]; String connectedWifiName = ""; diff --git a/lib/controller/message/common_message_setting_controller.dart b/lib/controller/message/common_message_setting_controller.dart new file mode 100644 index 0000000..26ed352 --- /dev/null +++ b/lib/controller/message/common_message_setting_controller.dart @@ -0,0 +1,38 @@ +import 'package:ef/ef.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'common_message_setting_controller.g.dart'; // 由json_serializable自动生成的部分 + +@JsonSerializable() +class CommonMessageSettingModel { + int? setting = 0;//总设置 0 关闭 1 开启 + int? appSetting = 0;//app消息设置 + int? serviceSetting = 0;//服务号消息 + int? tipSetting = 0;//设备放置说明 + int? deviceUpgradeSetting = 0;//设备升级提示 + int? deviceIssueSetting = 0;//设备故障提示 + + + CommonMessageSettingModel(); + + // 从JSON反序列化时的异常处理 + + factory CommonMessageSettingModel.fromJson(Map json) { + try { + return _$CommonMessageSettingModelFromJson(json); + } catch (e) { + // 在实际应用中,应该有更细致的异常处理策略和错误日志 + return CommonMessageSettingModel(); // 或者返回一个带有错误信息的特定DeviceInfoModel实例 + } + } + + // 序列化为JSON时的异常处理 + Map toJson() => _$CommonMessageSettingModelToJson(this); +} + +class CommonMessageSettingController extends GetControllerEx { + CommonMessageSettingController() { + attr = GetModel(CommonMessageSettingModel()).obs; + } + +} diff --git a/lib/controller/message/common_message_setting_controller.g.dart b/lib/controller/message/common_message_setting_controller.g.dart new file mode 100644 index 0000000..6559b16 --- /dev/null +++ b/lib/controller/message/common_message_setting_controller.g.dart @@ -0,0 +1,28 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'common_message_setting_controller.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +CommonMessageSettingModel _$CommonMessageSettingModelFromJson( + Map json) => + CommonMessageSettingModel() + ..setting = (json['setting'] as num?)?.toInt() + ..appSetting = (json['appSetting'] as num?)?.toInt() + ..serviceSetting = (json['serviceSetting'] as num?)?.toInt() + ..tipSetting = (json['tipSetting'] as num?)?.toInt() + ..deviceUpgradeSetting = (json['deviceUpgradeSetting'] as num?)?.toInt() + ..deviceIssueSetting = (json['deviceIssueSetting'] as num?)?.toInt(); + +Map _$CommonMessageSettingModelToJson( + CommonMessageSettingModel instance) => + { + 'setting': instance.setting, + 'appSetting': instance.appSetting, + 'serviceSetting': instance.serviceSetting, + 'tipSetting': instance.tipSetting, + 'deviceUpgradeSetting': instance.deviceUpgradeSetting, + 'deviceIssueSetting': instance.deviceIssueSetting, + }; diff --git a/lib/controller/message/message_setting_controller.dart b/lib/controller/message/message_setting_controller.dart new file mode 100644 index 0000000..19fa89a --- /dev/null +++ b/lib/controller/message/message_setting_controller.dart @@ -0,0 +1,34 @@ +import 'package:ef/ef.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'message_setting_controller.g.dart'; // 由json_serializable自动生成的部分 + +@JsonSerializable() +class MessageSettingModel { + int? setting = 0;//总设置 0 关闭 1 开启 + int? appSetting = 0;//app消息设置 + int? serviceSetting = 0;//服务号消息 + + MessageSettingModel(); + + // 从JSON反序列化时的异常处理 + + factory MessageSettingModel.fromJson(Map json) { + try { + return _$MessageSettingModelFromJson(json); + } catch (e) { + // 在实际应用中,应该有更细致的异常处理策略和错误日志 + return MessageSettingModel(); // 或者返回一个带有错误信息的特定DeviceInfoModel实例 + } + } + + // 序列化为JSON时的异常处理 + Map toJson() => _$MessageSettingModelToJson(this); +} + +class MessageSettingController extends GetControllerEx { + MessageSettingController() { + attr = GetModel(MessageSettingModel()).obs; + } + +} diff --git a/lib/controller/message/message_setting_controller.g.dart b/lib/controller/message/message_setting_controller.g.dart new file mode 100644 index 0000000..cf071b8 --- /dev/null +++ b/lib/controller/message/message_setting_controller.g.dart @@ -0,0 +1,21 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'message_setting_controller.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +MessageSettingModel _$MessageSettingModelFromJson(Map json) => + MessageSettingModel() + ..setting = (json['setting'] as num?)?.toInt() + ..appSetting = (json['appSetting'] as num?)?.toInt() + ..serviceSetting = (json['serviceSetting'] as num?)?.toInt(); + +Map _$MessageSettingModelToJson( + MessageSettingModel instance) => + { + 'setting': instance.setting, + 'appSetting': instance.appSetting, + 'serviceSetting': instance.serviceSetting, + }; diff --git a/lib/controller/sleep/sleep_report_controller.dart b/lib/controller/sleep/sleep_report_controller.dart index b44bd19..150b5a6 100644 --- a/lib/controller/sleep/sleep_report_controller.dart +++ b/lib/controller/sleep/sleep_report_controller.dart @@ -27,6 +27,7 @@ class SleepReportModel { class SleepReportController extends GetControllerEx { Rx selectedDate = Rx(null); + RxMap sleepReport = RxMap(); SleepReportController() { attr = GetModel(SleepReportModel()).obs; } diff --git a/lib/main.dart b/lib/main.dart index a00e383..43503b0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -27,8 +27,10 @@ import 'package:vbvs_app/controller/home/home_controller.dart'; import 'package:vbvs_app/controller/login/login_controller.dart'; import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; import 'package:vbvs_app/controller/main_bottom/main_page_controller.dart'; +import 'package:vbvs_app/controller/message/common_message_setting_controller.dart'; import 'package:vbvs_app/controller/message/message_controller.dart'; import 'package:vbvs_app/controller/message/message_review_controller.dart'; +import 'package:vbvs_app/controller/message/message_setting_controller.dart'; import 'package:vbvs_app/controller/person/person_controller.dart'; import 'package:vbvs_app/controller/repair/repair_controller.dart'; import 'package:vbvs_app/controller/setting/language/language_controller.dart'; @@ -247,6 +249,8 @@ class MyApp extends StatelessWidget { Get.lazyPut(() => CalendarController()), Get.lazyPut(() => SleepReportController()), Get.lazyPut(() => MessageReviewController()), + Get.lazyPut(() => MessageSettingController()), + Get.lazyPut(() => CommonMessageSettingController()), ])); }); } diff --git a/lib/pages/device/component/DeviceDataComponentWidget.dart b/lib/pages/device/component/DeviceDataComponentWidget.dart index b017d9c..7cab3a1 100644 --- a/lib/pages/device/component/DeviceDataComponentWidget.dart +++ b/lib/pages/device/component/DeviceDataComponentWidget.dart @@ -328,7 +328,8 @@ class _DeviceDataComponentWidgetState extends State { _popupEntry?.remove(); _popupEntry = null; // Get.toNamed("/deviceDetail", arguments: widget.device); - TopSlideNotification.show(context, text: "待开发功能".tr); + Get.toNamed("/messageSettingPage", arguments: widget.device); + // TopSlideNotification.show(context, text: "待开发功能".tr); }, ), ]); diff --git a/lib/pages/device/component/MessageSetting.dart b/lib/pages/device/component/MessageSetting.dart new file mode 100644 index 0000000..e8444e8 --- /dev/null +++ b/lib/pages/device/component/MessageSetting.dart @@ -0,0 +1,574 @@ +import 'dart:async'; + +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; +import 'package:vbvs_app/common/color/ServiceConstant.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/common/util/requestWithLog.dart'; +import 'package:vbvs_app/component/base/GradientSwitch.dart'; +import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; +import 'package:vbvs_app/controller/message/common_message_setting_controller.dart'; +import 'package:vbvs_app/controller/message/message_setting_controller.dart'; +import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; + +class MessageSettingPage extends StatefulWidget { + var data; + MessageSettingPage({super.key, required this.data}); + + @override + State createState() => _MessageSettingPageState(); +} + +class _MessageSettingPageState extends State { + final ThemeController themeController = Get.find(); + MessageSettingController messageSettingController = Get.find(); + CommonMessageSettingController commonMessageSettingController = Get.find(); + + @override + void initState() { + super.initState(); + _fetchCommonMessageSetting(); + _fetchDeviceMessageSetting(); + } + + Future _fetchCommonMessageSetting() async { + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.user_setting; + String type = "user_message_setting"; + String queryUrl = "${serviceAddress}${serviceName}${serviceApi}?type=$type"; + requestWithLog( + logTitle: "查询用户消息配置", + method: MyHttpMethod.get, + queryUrl: queryUrl, + onSuccess: (res) { + if (res.data == null || res.data.isEmpty) { + var data = { + "type": type, + "setting": 1, + "appSetting": 1, + "serviceSetting": 1, + "tipSetting": 1, + "deviceUpgradeSetting": 1, + "deviceIssueSetting": 1, + }; + requestWithLog( + logTitle: "更新用户消息配置", + method: MyHttpMethod.put, + queryUrl: queryUrl, + data: data, + onSuccess: (res) { + requestWithLog( + logTitle: "查询用户消息配置", + method: MyHttpMethod.get, + queryUrl: queryUrl, + onSuccess: (res) { + if (res.data != null && res.data.isNotEmpty) { + var datalist = res.data; + commonMessageSettingController.model.setting = + datalist['setting']; + commonMessageSettingController.model.appSetting = + datalist['appSetting']; + commonMessageSettingController.model.serviceSetting = + datalist['serviceSetting']; + commonMessageSettingController.model.tipSetting = + datalist['tipSetting']; + commonMessageSettingController.model.deviceUpgradeSetting = + datalist['deviceUpgradeSetting']; + commonMessageSettingController.model.deviceIssueSetting = + datalist['deviceIssueSetting']; + commonMessageSettingController.updateAll(); + } + }, + ); + + // + }, + ); + } else { + var datalist = res.data; + commonMessageSettingController.model.setting = datalist['setting']; + commonMessageSettingController.model.appSetting = + datalist['appSetting']; + commonMessageSettingController.model.serviceSetting = + datalist['serviceSetting']; + commonMessageSettingController.model.tipSetting = + datalist['tipSetting']; + commonMessageSettingController.model.deviceUpgradeSetting = + datalist['deviceUpgradeSetting']; + commonMessageSettingController.model.deviceIssueSetting = + datalist['deviceIssueSetting']; + commonMessageSettingController.updateAll(); + } + }, + onFailure: (res) { + print(res); + }, + ); + } + + Future _fetchDeviceMessageSetting() async { + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.user_setting; + String mac = widget.data['mac']; + String type = "user_device_message_setting_$mac"; + String queryUrl = "${serviceAddress}${serviceName}${serviceApi}?type=$type"; + requestWithLog( + logTitle: "查询用户消息配置", + method: MyHttpMethod.get, + queryUrl: queryUrl, + onSuccess: (res) { + if (res.data == null || res.data.isEmpty) { + var data = { + "type": type, + "setting": 1, + "appSetting": 1, + "serviceSetting": 1, + }; + requestWithLog( + logTitle: "更新用户消息配置", + method: MyHttpMethod.put, + queryUrl: queryUrl, + data: data, + onSuccess: (res) { + requestWithLog( + logTitle: "查询用户消息配置", + method: MyHttpMethod.get, + queryUrl: queryUrl, + onSuccess: (res) { + if (res.data != null && res.data.isNotEmpty) { + var datalist = res.data; + messageSettingController.model.setting = + datalist['setting']; + messageSettingController.model.appSetting = + datalist['appSetting']; + messageSettingController.model.serviceSetting = + datalist['serviceSetting']; + messageSettingController.updateAll(); + } + }, + ); + + // + }, + ); + } else { + var datalist = res.data; + messageSettingController.model.setting = datalist['setting']; + messageSettingController.model.appSetting = datalist['appSetting']; + messageSettingController.model.serviceSetting = + datalist['serviceSetting']; + messageSettingController.updateAll(); + } + }, + onFailure: (res) { + print(res); + }, + ); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, bodysize) => GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/img/bgNoImg.png'), + fit: BoxFit.fill, + ), + ), + child: Scaffold( + backgroundColor: Colors.transparent, + appBar: AppBar( + backgroundColor: themeController.currentColor.sc17, + 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: 'ReadexPro', + color: themeController.currentColor.sc3, + letterSpacing: 0, + fontSize: 30.rpx, + ), + ), + Positioned( + left: 0, + child: returnIconButtomAddCallback(() {}), + ), + ], + ), + ), + actions: [], + centerTitle: false, + ), + body: GestureDetector( + child: SafeArea( + top: true, + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(0.rpx, 0, 0.rpx, 0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 30.rpx, 0, 0), + child: Container( + width: double.infinity, + constraints: BoxConstraints( + minHeight: 90.rpx, + ), + decoration: BoxDecoration( + color: themeController.currentColor.sc5), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 15.rpx, 30.rpx, 15.rpx), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + "消息提醒设置".tr, + style: TextStyle( + color: themeController.currentColor.sc3, + fontSize: + AppConstants().title_text_fontSize), + ), + ), + Obx(() { + return GradientSwitch( + value: commonMessageSettingController + .model.setting == + 1 + ? (messageSettingController + .model.setting == + 1 + ? true + : false) + : false, + onChanged: (val) { + if (commonMessageSettingController + .model.setting == + 0) { + TopSlideNotification.show(context, + text: "请先在设置里的消息通知打开全部消息配置".tr, + textColor: themeController + .currentColor.sc9); + return; + } + String serviceAddress = + ServiceConstant.service_address; + String serviceName = + ServiceConstant.server_service; + String serviceApi = + ServiceConstant.user_setting; + String mac = widget.data['mac']; + String type = + "user_device_message_setting_$mac"; + String queryUrl = + "${serviceAddress}${serviceName}${serviceApi}"; + var data = { + "type": type, + "setting": val == true ? 1 : 0, + "appSetting": messageSettingController + .model.appSetting, + "serviceSetting": + messageSettingController + .model.serviceSetting, + }; + requestWithLog( + logTitle: "更新消息推送状态", + method: MyHttpMethod.put, + queryUrl: queryUrl, + data: data, + onSuccess: (res) { + _fetchDeviceMessageSetting(); + messageSettingController + .updateAll(); + }); + }, + activeGradient: LinearGradient( + colors: [ + themeController.currentColor.sc1, + themeController.currentColor.sc2 + ], + ), + activeThumbColor: Colors.white, + inactiveThumbColor: + stringToColor("#A2A4A9"), + inactiveColor: stringToColor("#161B28"), + ); + }), + ], + ), + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 21.rpx, 30.rpx, 0), + child: Container( + decoration: BoxDecoration( + color: themeController.currentColor.sc5, + borderRadius: BorderRadius.circular( + AppConstants().normal_container_radius), + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 40.rpx, 30.rpx, 54.rpx), + child: Container( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + "APP消息".tr, + style: TextStyle( + color: themeController + .currentColor.sc3, + fontSize: AppConstants() + .title_text_fontSize), + ), + ), + Obx(() { + return GradientSwitch( + value: commonMessageSettingController + .model.setting == + 1 + ? (messageSettingController + .model.setting == + 1 + ? (messageSettingController + .model + .appSetting == + 1 + ? true + : false) + : false) + : false, + onChanged: (val) { + if (commonMessageSettingController + .model.setting == + 0) { + TopSlideNotification.show(context, + text: + "请先在设置里的消息通知打开全部消息配置".tr, + textColor: themeController + .currentColor.sc9); + return; + } + if (messageSettingController + .model.setting == + 0) { + TopSlideNotification.show(context, + text: "请先打开消息提醒设置".tr, + textColor: themeController + .currentColor.sc9); + return; + } + String serviceAddress = + ServiceConstant.service_address; + String serviceName = + ServiceConstant.server_service; + String serviceApi = + ServiceConstant.user_setting; + String mac = widget.data['mac']; + String type = + "user_device_message_setting_$mac"; + String queryUrl = + "${serviceAddress}${serviceName}${serviceApi}"; + var data = { + "type": type, + "setting": + messageSettingController + .model.setting, + "appSetting": val == true ? 1 : 0, + "serviceSetting": + messageSettingController + .model.serviceSetting, + }; + requestWithLog( + logTitle: "更新消息推送状态", + method: MyHttpMethod.put, + queryUrl: queryUrl, + data: data, + onSuccess: (res) { + _fetchDeviceMessageSetting(); + messageSettingController + .updateAll(); + }); + }, + activeGradient: LinearGradient( + colors: [ + themeController.currentColor.sc1, + themeController.currentColor.sc2 + ], + ), + activeThumbColor: Colors.white, + inactiveThumbColor: + stringToColor("#A2A4A9"), + inactiveColor: + stringToColor("#161B28"), + ); + }), + ], + ), + Text( + "APP消息介绍".tr, + style: TextStyle( + color: themeController.currentColor.sc4, + fontSize: AppConstants() + .normal_text_fontSize), + ), + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + "服务号消息".tr, + style: TextStyle( + color: themeController + .currentColor.sc3, + fontSize: AppConstants() + .title_text_fontSize), + ), + ), + Obx(() { + return GradientSwitch( + value: commonMessageSettingController + .model.setting == + 1 + ? (messageSettingController + .model.setting == + 1 + ? (messageSettingController + .model + .serviceSetting == + 1 + ? true + : false) + : false) + : false, + onChanged: (val) { + if (commonMessageSettingController + .model.setting == + 0) { + TopSlideNotification.show(context, + text: + "请先在设置里的消息通知打开全部消息配置".tr, + textColor: themeController + .currentColor.sc9); + return; + } + if (messageSettingController + .model.setting == + 0) { + TopSlideNotification.show(context, + text: "请先打开消息提醒设置".tr, + textColor: themeController + .currentColor.sc9); + return; + } + String serviceAddress = + ServiceConstant.service_address; + String serviceName = + ServiceConstant.server_service; + String serviceApi = + ServiceConstant.user_setting; + String mac = widget.data['mac']; + String type = + "user_device_message_setting_$mac"; + String queryUrl = + "${serviceAddress}${serviceName}${serviceApi}"; + var data = { + "type": type, + "setting": + messageSettingController + .model.setting, + "appSetting": + messageSettingController + .model.appSetting, + "serviceSetting": + val == true ? 1 : 0, + }; + requestWithLog( + logTitle: "更新消息推送状态", + method: MyHttpMethod.put, + queryUrl: queryUrl, + data: data, + onSuccess: (res) { + _fetchDeviceMessageSetting(); + messageSettingController + .updateAll(); + }); + }, + activeGradient: LinearGradient( + colors: [ + themeController.currentColor.sc1, + themeController.currentColor.sc2 + ], + ), + activeThumbColor: Colors.white, + inactiveThumbColor: + stringToColor("#A2A4A9"), + inactiveColor: + stringToColor("#161B28"), + ); + }), + ], + ), + Text( + "服务号消息介绍".tr, + style: TextStyle( + color: themeController.currentColor.sc4, + fontSize: AppConstants() + .normal_text_fontSize), + ), + ].divide(SizedBox( + height: 49.rpx, + )), + ), + ), + ), + ), + ), + ], + ), + ), + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/pages/device/component/ReviewMessageWidgetWidget.dart b/lib/pages/device/component/ReviewMessageWidgetWidget.dart index 819a78b..21f55e1 100644 --- a/lib/pages/device/component/ReviewMessageWidgetWidget.dart +++ b/lib/pages/device/component/ReviewMessageWidgetWidget.dart @@ -28,7 +28,7 @@ class _ReviewMessageWidgetWidgetState extends State { var messageInfo = widget.data; return ClickableContainer( backgroundColor: themeController.currentColor.sc5, - highlightColor: themeController.currentColor.sc3, + highlightColor: themeController.currentColor.sc21, borderRadius: 20.rpx, padding: EdgeInsetsDirectional.fromSTEB(31.rpx, 33.rpx, 0.rpx, 33.rpx), onTap: () {}, diff --git a/lib/pages/device/message_review_page.dart b/lib/pages/device/message_review_page.dart index 07bc4c5..e0101a3 100644 --- a/lib/pages/device/message_review_page.dart +++ b/lib/pages/device/message_review_page.dart @@ -92,7 +92,7 @@ class _MessageReviewPageState extends State { child: ClickableContainer( backgroundColor: themeController.currentColor.sc5, highlightColor: - themeController.currentColor.sc5, // 或你希望的点击水波纹颜色 + themeController.currentColor.sc21, // 或你希望的点击水波纹颜色 borderRadius: AppConstants() .normal_container_radius, // 如果你想加圆角可以设置 eg. 12.rpx padding: EdgeInsets.zero, @@ -288,48 +288,49 @@ class _MessageReviewPageState extends State { // RepairController repairController = Get.find(); // repairController.repairHistory.value = apiResponse.data; // repairController.updateAll(); - messageReviewController.messageList.value = [ - { - "data": { - "title": "实时监测结果通知", - 'val': [ - {'k': '消息类型', 'v': '心率异常'}, - {'k': '检测数值', 'v': '106'}, - {'k': '发生时间', 'v': '2024-07-30 01:15'}, - ], - } - }, - { - "data": { - "title": "实时监测结果通知", - 'val': [ - {'k': '消息类型', 'v': '心率异常'}, - {'k': '检测数值', 'v': '106'}, - {'k': '发生时间', 'v': '2024-07-30 01:15'}, - ], - } - }, - { - "data": { - "title": "实时监测结果通知", - 'val': [ - {'k': '消息类型', 'v': '心率异常'}, - {'k': '检测数值', 'v': '106'}, - {'k': '发生时间', 'v': '2024-07-30 01:15'}, - ], - } - }, - { - "data": { - "title": "实时监测结果通知", - 'val': [ - {'k': '消息类型', 'v': '心率异常'}, - {'k': '检测数值', 'v': '106'}, - {'k': '发生时间', 'v': '2024-07-30 01:15'}, - ], - } - }, - ]; + + // messageReviewController.messageList.value = [ + // { + // "data": { + // "title": "实时监测结果通知", + // 'val': [ + // {'k': '消息类型', 'v': '心率异常'}, + // {'k': '检测数值', 'v': '106'}, + // {'k': '发生时间', 'v': '2024-07-30 01:15'}, + // ], + // } + // }, + // { + // "data": { + // "title": "实时监测结果通知", + // 'val': [ + // {'k': '消息类型', 'v': '心率异常'}, + // {'k': '检测数值', 'v': '106'}, + // {'k': '发生时间', 'v': '2024-07-30 01:15'}, + // ], + // } + // }, + // { + // "data": { + // "title": "实时监测结果通知", + // 'val': [ + // {'k': '消息类型', 'v': '心率异常'}, + // {'k': '检测数值', 'v': '106'}, + // {'k': '发生时间', 'v': '2024-07-30 01:15'}, + // ], + // } + // }, + // { + // "data": { + // "title": "实时监测结果通知", + // 'val': [ + // {'k': '消息类型', 'v': '心率异常'}, + // {'k': '检测数值', 'v': '106'}, + // {'k': '发生时间', 'v': '2024-07-30 01:15'}, + // ], + // } + // }, + // ]; } Widget _buildMessageListView(List dataList) { diff --git a/lib/pages/device_bind/componnet/SingleBlueteethDeviceCompoentWidget.dart b/lib/pages/device_bind/componnet/SingleBlueteethDeviceCompoentWidget.dart index 01fc768..7c7a9b6 100644 --- a/lib/pages/device_bind/componnet/SingleBlueteethDeviceCompoentWidget.dart +++ b/lib/pages/device_bind/componnet/SingleBlueteethDeviceCompoentWidget.dart @@ -44,10 +44,13 @@ class _SingleBlueteethDeviceCompoentWidgetState deviceData.mac = deviceData.deviceId.replaceAll(':', ''); BleDeviceData device = deviceData; // blueteethBindController.currentDeviceMac = device.mac; - device = blueteethBindController.model.betDevicelist!.firstWhere( - (d) => d.mac == device.mac, - orElse: () => device, - ); + final match = blueteethBindController.model.betDevicelist! + .firstWhereOrNull((d) => d.mac == device.mac); + + if (match == null) { + return Container(); + } + device = match; return ClickableContainer( backgroundColor: themeController.currentColor.sc5, highlightColor: themeController.currentColor.sc21, diff --git a/lib/pages/device_bind/device_calibration.dart b/lib/pages/device_bind/device_calibration.dart index 7331c90..923a4a2 100644 --- a/lib/pages/device_bind/device_calibration.dart +++ b/lib/pages/device_bind/device_calibration.dart @@ -28,8 +28,8 @@ class CalibrationPage extends StatefulWidget { class _CalibrationPageState extends State { DeviceCalibrationController deviceCalibrationController = Get.find(); BlueteethBindController blueteethBindController = Get.find(); - int flag = 0; //默认没有开始校准过 - bool exit = false; + int flag = 0; //默认没有开始校准过 0没有开始 + bool exit = false; @override void initState() { super.initState(); @@ -86,7 +86,6 @@ class _CalibrationPageState extends State { Positioned( left: 0, child: returnIconButtomAddCallback(() { - if (flag != 0) { try { showConfirmDialog( @@ -114,7 +113,7 @@ class _CalibrationPageState extends State { if (exit) { Get.back(); } - },enableBack:exit ), + }, enableBack: exit), ), if (widget.type == 1) Positioned( @@ -561,11 +560,11 @@ class _CalibrationPageState extends State { borderRadius: AppConstants().button_container_radius, // 圆角半径 onTap: () async { - flag = 1; if (deviceCalibrationController.complete) { showConfirmDialog( context, Container(), "校准已经完成,是否重新开始校准?", onConfirm: () { + flag = 1; BlueteethBindController blueteethBindController = Get.find(); deviceCalibrationController.process.value = 0; @@ -723,6 +722,7 @@ class _CalibrationPageState extends State { return; }); } else { + flag = 1; BlueteethBindController blueteethBindController = Get.find(); String serviceAddress = diff --git a/lib/pages/device_bind/device_share_page.dart b/lib/pages/device_bind/device_share_page.dart index ad1bcf4..b025764 100644 --- a/lib/pages/device_bind/device_share_page.dart +++ b/lib/pages/device_bind/device_share_page.dart @@ -369,55 +369,56 @@ class _DeviceSharePageState extends State { ), ), ), - Padding( - padding: - EdgeInsetsDirectional.fromSTEB(0, 379.rpx, 0, 0), - child: CustomCard( - borderRadius: - AppConstants().button_container_radius, // 圆角半径 - onTap: () async { - // TopSlideNotification.show(context, - // text: "待开发功能".tr); - LoginController loginController = Get.find(); - // loginController.fluwx.share(WeChatShareTextModel( - // "太和e护分享链接", - // scene: WeChatScene.session)); - final Uint8List data = await rootBundle.load('assets/img/camera.png').then((bd) => bd.buffer.asUint8List()); - loginController.fluwx.share(WeChatShareWebPageModel("taihecare://goods?id=123",title: "标题",description: "描述",thumbData: data)); - }, - colors: [ - // 渐变色 - themeController.currentColor.sc1, - themeController.currentColor.sc2, - ], - child: Container( - width: MediaQuery.sizeOf(context).width * 0.66, - height: MediaQuery.sizeOf(context).height * 0.055, - constraints: BoxConstraints( - minWidth: 500.rpx, - minHeight: 90.rpx, - ), - child: Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - '微信好友一键分享'.tr, - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - color: themeController.currentColor.sc3, - fontFamily: 'Inter', - fontSize: AppConstants() - .normal_text_fontSize, // 自定义字体大小 - letterSpacing: 0.0, - ), - ), - ].divide(SizedBox(width: 17.rpx)), - ), - ), - ), - ), + + // Padding( + // padding: + // EdgeInsetsDirectional.fromSTEB(0, 379.rpx, 0, 0), + // child: CustomCard( + // borderRadius: + // AppConstants().button_container_radius, // 圆角半径 + // onTap: () async { + // // TopSlideNotification.show(context, + // // text: "待开发功能".tr); + // LoginController loginController = Get.find(); + // // loginController.fluwx.share(WeChatShareTextModel( + // // "太和e护分享链接", + // // scene: WeChatScene.session)); + // final Uint8List data = await rootBundle.load('assets/img/camera.png').then((bd) => bd.buffer.asUint8List()); + // loginController.fluwx.share(WeChatShareWebPageModel("taihecare://goods?id=123",title: "标题",description: "描述",thumbData: data)); + // }, + // colors: [ + // // 渐变色 + // themeController.currentColor.sc1, + // themeController.currentColor.sc2, + // ], + // child: Container( + // width: MediaQuery.sizeOf(context).width * 0.66, + // height: MediaQuery.sizeOf(context).height * 0.055, + // constraints: BoxConstraints( + // minWidth: 500.rpx, + // minHeight: 90.rpx, + // ), + // child: Row( + // mainAxisSize: MainAxisSize.max, + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // Text( + // '微信好友一键分享'.tr, + // style: FlutterFlowTheme.of(context) + // .bodyMedium + // .override( + // color: themeController.currentColor.sc3, + // fontFamily: 'Inter', + // fontSize: AppConstants() + // .normal_text_fontSize, // 自定义字体大小 + // letterSpacing: 0.0, + // ), + // ), + // ].divide(SizedBox(width: 17.rpx)), + // ), + // ), + // ), + // ), ], ), ), diff --git a/lib/pages/login/login.dart b/lib/pages/login/login.dart index 1e6c102..3224bbc 100644 --- a/lib/pages/login/login.dart +++ b/lib/pages/login/login.dart @@ -96,6 +96,7 @@ class _EPageState extends State { ), ), child: Scaffold( + resizeToAvoidBottomInset: false, backgroundColor: Colors.transparent, body: SafeArea( top: true, @@ -506,22 +507,20 @@ class _EPageState extends State { // }, onTap: () async { //loginController.model.isIos == true && - if (loginController - .model.register_agree == - null || - loginController - .model.register_agree != - true) { - TopSlideNotification.show( - context, - text: "登录页.未同意协议".tr, - textColor: - themeController.currentColor.sc9, - ); - return; - } - await loginController - .wxLoginSendAuth(context); + if (loginController.model.register_agree == + null || + loginController.model.register_agree != + true) { + TopSlideNotification.show( + context, + text: "登录页.未同意协议".tr, + textColor: + themeController.currentColor.sc9, + ); + return; + } + await loginController + .wxLoginSendAuth(context); }, child: Container( width: 91.rpx, diff --git a/lib/pages/login/other_login.dart b/lib/pages/login/other_login.dart index f2f7fc4..1aa9b51 100644 --- a/lib/pages/login/other_login.dart +++ b/lib/pages/login/other_login.dart @@ -3,8 +3,11 @@ 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:fluwx/fluwx.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/CheckNetwork.dart'; +import 'package:vbvs_app/common/util/CommonVariables.dart'; import 'package:vbvs_app/common/util/DailyLogUtils.dart'; import 'package:vbvs_app/common/util/FitTool.dart'; import 'package:vbvs_app/common/util/MyUtils.dart'; @@ -32,6 +35,60 @@ class _OtherLoginPageState extends State { CountdownController countdownController = Get.find(); LoginController loginController = Get.find(); + @override + void initState() { + // TODO: implement initState + super.initState(); + super.initState(); + LoginController loginController = Get.find(); + Fluwx fluwx = loginController.fluwx; + if (isiOS) { + loginController.model.isIos = true; + fluwx.isWeChatInstalled.then((isWeChatInstalled) { + debugPrint('is wechat installed: $isWeChatInstalled'); + if (!isWeChatInstalled) { + // TODO ios未安装微信 隐藏微信一键登录按钮 + loginController.model.isWeChatNotInstalled = false; + } else { + loginController.model.isWeChatNotInstalled = true; + } + loginController.updateAll(); + }); + } + // 微信监听回调 + loginController.fluwxCancelable = fluwx.addSubscriber((response) async { + if (response is WeChatAuthResponse) { + debugPrint('state :${response.state} \n code:${response.code}'); + int errCode = response.errCode ?? -9999; + if (errCode == 0) { + // TODO 微信登录成功 传递code给后台 再操作逻辑 + String code = response.code ?? ""; + //把微信登录返回的code传给后台,剩下的事就交给后台处理 + //首次未注册的用户引导去手机号填写页面 + //已注册的用户直接跳转首页 + if (CommonVariables.isNetWorkOn == false) { + showToast("网络未连接,请开启设备网络后重试"); + return; + } + ApiResponse msg = await loginController.loginByWechatCode(code); + if (msg.code == HttpStatusCodes.ok) { + // TODO 操作全部跳转页面前成功以后移除监听,防止重复监听,其他方式登录成功也需要移出监听 + loginController.fluwxCancelable?.cancel(); + // 登录成功移出网络检查监听 + Checknetwork.subscription?.cancel(); + Get.offAndToNamed("/mianPageBottomChange"); + } + // TODO 操作全部跳转页面前成功以后移除监听,防止重复监听,其他方式登录成功也需要移出监听 + // fluwxCancelable?.cancel(); + } else if (errCode == -4) { + showToast("用户拒绝授权"); + } else if (errCode == -2) { + showToast("用户取消授权"); + } + } + }); + } + @override Widget build(BuildContext context) { return LayoutBuilder( @@ -45,6 +102,7 @@ class _OtherLoginPageState extends State { ), ), child: Scaffold( + resizeToAvoidBottomInset: false, backgroundColor: Colors.transparent, body: SafeArea( top: true, @@ -55,32 +113,27 @@ class _OtherLoginPageState extends State { mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Column( - children: [ - SizedBox( - height: 66.rpx, + SizedBox( + height: 66.rpx, + ), + ClickableContainer( + backgroundColor: Colors.transparent, // 容器背景色 + highlightColor: + themeController.currentColor.sc21, // 点击时的背景色 + padding: EdgeInsets.zero, // 这里去掉外部的 padding,避免影响点击范围 + onTap: () { + Get.back(); + }, + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 16.rpx, 10.rpx, 16.rpx, 10.rpx), + child: SvgPicture.asset( + 'assets/img/icon/arrow_left.svg', + width: 25.rpx, + height: 25.rpx, // 如果 SVG 中没有固定颜色,使用 color 设置 + color: themeController.currentColor.sc3, ), - ClickableContainer( - backgroundColor: Colors.transparent, // 容器背景色 - highlightColor: - themeController.currentColor.sc21, // 点击时的背景色 - padding: - EdgeInsets.zero, // 这里去掉外部的 padding,避免影响点击范围 - onTap: () { - Get.back(); - }, - child: Padding( - padding: EdgeInsetsDirectional.fromSTEB( - 16.rpx, 10.rpx, 16.rpx, 10.rpx), - child: SvgPicture.asset( - 'assets/img/icon/arrow_left.svg', - width: 25.rpx, - height: 25.rpx, // 如果 SVG 中没有固定颜色,使用 color 设置 - color: themeController.currentColor.sc3, - ), - ), - ), - ], + ), ), Expanded( child: SingleChildScrollView( @@ -827,22 +880,20 @@ class _OtherLoginPageState extends State { padding: EdgeInsets.zero, onTap: () async { //loginController.model.isIos == true && - if (loginController - .model.register_agree == - null || - loginController - .model.register_agree != - true) { - TopSlideNotification.show( - context, - text: "登录页.未同意协议".tr, - textColor: - themeController.currentColor.sc9, - ); - return; - } - await loginController - .wxLoginSendAuth(context); + if (loginController.model.register_agree == + null || + loginController.model.register_agree != + true) { + TopSlideNotification.show( + context, + text: "登录页.未同意协议".tr, + textColor: + themeController.currentColor.sc9, + ); + return; + } + await loginController + .wxLoginSendAuth(context); }, child: Container( width: 91.rpx, diff --git a/lib/pages/main_bottom/mine_page.dart b/lib/pages/main_bottom/mine_page.dart index c18dc60..36c5022 100644 --- a/lib/pages/main_bottom/mine_page.dart +++ b/lib/pages/main_bottom/mine_page.dart @@ -650,8 +650,8 @@ class _MinePageState extends State { // themeController.currentColor.sc2, // ); Get.toNamed("/newSleepReportPage", - arguments: DateTime.now() - .millisecondsSinceEpoch); + arguments: {'date':DateTime.now() + .millisecondsSinceEpoch,"mac":'aaaaaaeeeeeq','type':1}); } }, child: Container( @@ -693,7 +693,7 @@ class _MinePageState extends State { mainAxisSize: MainAxisSize.max, children: [ Text( - 'V1.0.2505.20', + 'V1.0.2505.26', style: FlutterFlowTheme.of(context) .bodyMedium diff --git a/lib/pages/setting/common_mesaage_setting.dart b/lib/pages/setting/common_mesaage_setting.dart new file mode 100644 index 0000000..d3b528c --- /dev/null +++ b/lib/pages/setting/common_mesaage_setting.dart @@ -0,0 +1,896 @@ +import 'dart:async'; + +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; +import 'package:vbvs_app/common/color/ServiceConstant.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/common/util/requestWithLog.dart'; +import 'package:vbvs_app/component/base/GradientSwitch.dart'; +import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; +import 'package:vbvs_app/controller/message/common_message_setting_controller.dart'; +import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; + +class CommonMessageSettingPage extends StatefulWidget { + CommonMessageSettingPage({super.key}); + + @override + State createState() => + _CommonMessageSettingPageState(); +} + +class _CommonMessageSettingPageState extends State { + final ThemeController themeController = Get.find(); + CommonMessageSettingController commonMessageSettingController = Get.find(); + + @override + void initState() { + super.initState(); + _fetchDeviceList(); + } + + Future _fetchDeviceList() async { + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.user_setting; + String type = "user_message_setting"; + String queryUrl = "${serviceAddress}${serviceName}${serviceApi}?type=$type"; + requestWithLog( + logTitle: "查询用户消息配置", + method: MyHttpMethod.get, + queryUrl: queryUrl, + onSuccess: (res) { + if (res.data == null || res.data.isEmpty) { + var data = { + "type": type, + "setting": 1, + "appSetting": 1, + "serviceSetting": 1, + "tipSetting": 1, + "deviceUpgradeSetting": 1, + "deviceIssueSetting": 1, + }; + requestWithLog( + logTitle: "更新用户消息配置", + method: MyHttpMethod.put, + queryUrl: queryUrl, + data: data, + onSuccess: (res) { + requestWithLog( + logTitle: "查询用户消息配置", + method: MyHttpMethod.get, + queryUrl: queryUrl, + onSuccess: (res) { + if (res.data != null && res.data.isNotEmpty) { + var datalist = res.data; + commonMessageSettingController.model.setting = + datalist['setting']; + commonMessageSettingController.model.appSetting = + datalist['appSetting']; + commonMessageSettingController.model.serviceSetting = + datalist['serviceSetting']; + commonMessageSettingController.model.tipSetting = + datalist['tipSetting']; + commonMessageSettingController.model.deviceUpgradeSetting = + datalist['deviceUpgradeSetting']; + commonMessageSettingController.model.deviceIssueSetting = + datalist['deviceIssueSetting']; + commonMessageSettingController.updateAll(); + } + }, + ); + + // + }, + ); + } else { + var datalist = res.data; + commonMessageSettingController.model.setting = datalist['setting']; + commonMessageSettingController.model.appSetting = + datalist['appSetting']; + commonMessageSettingController.model.serviceSetting = + datalist['serviceSetting']; + commonMessageSettingController.model.tipSetting = + datalist['tipSetting']; + commonMessageSettingController.model.deviceUpgradeSetting = + datalist['deviceUpgradeSetting']; + commonMessageSettingController.model.deviceIssueSetting = + datalist['deviceIssueSetting']; + commonMessageSettingController.updateAll(); + } + }, + onFailure: (res) { + print(res); + }, + ); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, bodysize) => GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/img/bgNoImg.png'), + fit: BoxFit.fill, + ), + ), + child: Scaffold( + backgroundColor: Colors.transparent, + appBar: AppBar( + backgroundColor: themeController.currentColor.sc17, + 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: 'ReadexPro', + color: themeController.currentColor.sc3, + letterSpacing: 0, + fontSize: 30.rpx, + ), + ), + Positioned( + left: 0, + child: returnIconButtomAddCallback(() {}), + ), + ], + ), + ), + actions: [], + centerTitle: false, + ), + body: GestureDetector( + child: SafeArea( + top: true, + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(0.rpx, 0, 0.rpx, 0), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 30.rpx, 0, 0), + child: Container( + width: double.infinity, + constraints: BoxConstraints( + minHeight: 90.rpx, + ), + decoration: BoxDecoration( + color: themeController.currentColor.sc5), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 15.rpx, 30.rpx, 15.rpx), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + "全部消息".tr, + style: TextStyle( + color: + themeController.currentColor.sc3, + fontSize: AppConstants() + .title_text_fontSize), + ), + ), + Obx(() { + return GradientSwitch( + value: commonMessageSettingController + .model.setting == + 1 + ? true + : false, + onChanged: (val) { + String serviceAddress = + ServiceConstant.service_address; + String serviceName = + ServiceConstant.server_service; + String serviceApi = + ServiceConstant.user_setting; + String type = "user_message_setting"; + String queryUrl = + "${serviceAddress}${serviceName}${serviceApi}"; + var data = { + "type": type, + "setting": val == true ? 1 : 0, + "appSetting": + commonMessageSettingController + .model.appSetting, + "serviceSetting": + commonMessageSettingController + .model.serviceSetting, + "tipSetting": + commonMessageSettingController + .model.tipSetting, + "deviceUpgradeSetting": + commonMessageSettingController + .model.deviceUpgradeSetting, + "deviceIssueSetting": + commonMessageSettingController + .model.deviceIssueSetting, + }; + requestWithLog( + logTitle: "更新消息推送状态", + method: MyHttpMethod.put, + queryUrl: queryUrl, + data: data, + onSuccess: (res) { + _fetchDeviceList(); + commonMessageSettingController + .updateAll(); + }); + }, + activeGradient: LinearGradient( + colors: [ + themeController.currentColor.sc1, + themeController.currentColor.sc2 + ], + ), + activeThumbColor: Colors.white, + inactiveThumbColor: + stringToColor("#A2A4A9"), + inactiveColor: stringToColor("#161B28"), + ); + }), + ], + ), + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 21.rpx, 30.rpx, 0), + child: Container( + decoration: BoxDecoration( + color: themeController.currentColor.sc5, + borderRadius: BorderRadius.circular( + AppConstants().normal_container_radius), + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 40.rpx, 30.rpx, 54.rpx), + child: Container( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + "APP消息".tr, + style: TextStyle( + color: themeController + .currentColor.sc3, + fontSize: AppConstants() + .title_text_fontSize), + ), + ), + Obx(() { + return GradientSwitch( + value: commonMessageSettingController + .model.setting == + 1 + ? (commonMessageSettingController + .model.appSetting == + 1 + ? true + : false) + : false, + onChanged: (val) { + if (commonMessageSettingController + .model.setting == + 0) { + TopSlideNotification.show( + context, + text: "请先打开全部消息配置".tr, + textColor: themeController + .currentColor.sc9); + return; + } + String serviceAddress = + ServiceConstant + .service_address; + String serviceName = + ServiceConstant + .server_service; + String serviceApi = + ServiceConstant.user_setting; + String type = + "user_message_setting"; + String queryUrl = + "${serviceAddress}${serviceName}${serviceApi}"; + var data = { + "type": type, + "setting": + commonMessageSettingController + .model.setting, + "appSetting": + val == true ? 1 : 0, + "serviceSetting": + commonMessageSettingController + .model.serviceSetting, + "tipSetting": + commonMessageSettingController + .model.tipSetting, + "deviceUpgradeSetting": + commonMessageSettingController + .model + .deviceUpgradeSetting, + "deviceIssueSetting": + commonMessageSettingController + .model + .deviceIssueSetting, + }; + requestWithLog( + logTitle: "更新消息推送状态", + method: MyHttpMethod.put, + queryUrl: queryUrl, + data: data, + onSuccess: (res) { + _fetchDeviceList(); + commonMessageSettingController + .updateAll(); + }); + }, + activeGradient: LinearGradient( + colors: [ + themeController + .currentColor.sc1, + themeController.currentColor.sc2 + ], + ), + activeThumbColor: Colors.white, + inactiveThumbColor: + stringToColor("#A2A4A9"), + inactiveColor: + stringToColor("#161B28"), + ); + }), + ], + ), + Text( + "通用APP消息介绍".tr, + style: TextStyle( + color: + themeController.currentColor.sc4, + fontSize: AppConstants() + .normal_text_fontSize), + ), + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + "服务号消息".tr, + style: TextStyle( + color: themeController + .currentColor.sc3, + fontSize: AppConstants() + .title_text_fontSize), + ), + ), + Obx(() { + return GradientSwitch( + value: commonMessageSettingController + .model.setting == + 1 + ? (commonMessageSettingController + .model + .serviceSetting == + 1 + ? true + : false) + : false, + onChanged: (val) { + if (commonMessageSettingController + .model.setting == + 0) { + TopSlideNotification.show( + context, + text: "请先打开全部消息配置".tr, + textColor: themeController + .currentColor.sc9); + return; + } + String serviceAddress = + ServiceConstant + .service_address; + String serviceName = + ServiceConstant + .server_service; + String serviceApi = + ServiceConstant.user_setting; + String type = + "user_message_setting"; + String queryUrl = + "${serviceAddress}${serviceName}${serviceApi}"; + var data = { + "type": type, + "setting": + commonMessageSettingController + .model.setting, + "appSetting": + commonMessageSettingController + .model.appSetting, + "serviceSetting": + val == true ? 1 : 0, + "tipSetting": + commonMessageSettingController + .model.tipSetting, + "deviceUpgradeSetting": + commonMessageSettingController + .model + .deviceUpgradeSetting, + "deviceIssueSetting": + commonMessageSettingController + .model + .deviceIssueSetting, + }; + requestWithLog( + logTitle: "更新消息推送状态", + method: MyHttpMethod.put, + queryUrl: queryUrl, + data: data, + onSuccess: (res) { + _fetchDeviceList(); + commonMessageSettingController + .updateAll(); + }); + }, + activeGradient: LinearGradient( + colors: [ + themeController + .currentColor.sc1, + themeController.currentColor.sc2 + ], + ), + activeThumbColor: Colors.white, + inactiveThumbColor: + stringToColor("#A2A4A9"), + inactiveColor: + stringToColor("#161B28"), + ); + }), + ], + ), + Text( + "通用服务号消息介绍".tr, + style: TextStyle( + color: + themeController.currentColor.sc4, + fontSize: AppConstants() + .normal_text_fontSize), + ), + ].divide(SizedBox( + height: 49.rpx, + )), + ), + ), + ), + ), + ), + // Padding( + // padding: + // EdgeInsetsDirectional.fromSTEB(0, 30.rpx, 0, 0), + // child: Container( + // width: double.infinity, + // constraints: BoxConstraints( + // minHeight: 90.rpx, + // ), + // decoration: BoxDecoration( + // color: themeController.currentColor.sc5), + // child: Padding( + // padding: EdgeInsetsDirectional.fromSTEB( + // 30.rpx, 15.rpx, 30.rpx, 15.rpx), + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Row( + // mainAxisSize: MainAxisSize.max, + // mainAxisAlignment: + // MainAxisAlignment.spaceBetween, + // children: [ + // Expanded( + // child: Text( + // "绑定操作提示".tr, + // style: TextStyle( + // color: themeController + // .currentColor.sc3, + // fontSize: AppConstants() + // .title_text_fontSize), + // ), + // ), + // Obx(() { + // return GradientSwitch( + // value: commonMessageSettingController + // .model.setting == + // 1 + // ? (commonMessageSettingController + // .model.tipSetting == + // 1 + // ? true + // : false) + // : false, + // onChanged: (val) { + // if (commonMessageSettingController + // .model.setting == + // 0) { + // TopSlideNotification.show( + // context, + // text: "请先打开全部消息配置".tr, + // textColor: themeController + // .currentColor.sc9); + // return; + // } + // String serviceAddress = + // ServiceConstant + // .service_address; + // String serviceName = + // ServiceConstant + // .server_service; + // String serviceApi = + // ServiceConstant.user_setting; + // String type = + // "user_message_setting"; + // String queryUrl = + // "${serviceAddress}${serviceName}${serviceApi}"; + // var data = { + // "type": type, + // "setting": + // commonMessageSettingController + // .model.setting, + // "appSetting": + // commonMessageSettingController + // .model.appSetting, + // "serviceSetting": + // commonMessageSettingController + // .model.serviceSetting, + // "tipSetting": + // val == true ? 1 : 0, + // "deviceUpgradeSetting": + // commonMessageSettingController + // .model + // .deviceUpgradeSetting, + // "deviceIssueSetting": + // commonMessageSettingController + // .model + // .deviceIssueSetting, + // }; + // requestWithLog( + // logTitle: "更新消息推送状态", + // method: MyHttpMethod.put, + // queryUrl: queryUrl, + // data: data, + // onSuccess: (res) { + // _fetchDeviceList(); + // commonMessageSettingController + // .updateAll(); + // }); + // }, + // activeGradient: LinearGradient( + // colors: [ + // themeController + // .currentColor.sc1, + // themeController.currentColor.sc2 + // ], + // ), + // activeThumbColor: Colors.white, + // inactiveThumbColor: + // stringToColor("#A2A4A9"), + // inactiveColor: + // stringToColor("#161B28"), + // ); + // }), + // ], + // ), + // // Text( + // // "绑定操作提示介绍".tr, + // // style: TextStyle( + // // color: + // // themeController.currentColor.sc4, + // // fontSize: AppConstants() + // // .normal_text_fontSize), + // // ), + // ].divide(SizedBox( + // height: 49.rpx, + // )), + // )), + // ), + // ), + + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 30.rpx, 0, 0), + child: Container( + width: double.infinity, + constraints: BoxConstraints( + minHeight: 90.rpx, + ), + decoration: BoxDecoration( + color: themeController.currentColor.sc5), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 15.rpx, 30.rpx, 15.rpx), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + "设备故障通知".tr, + style: TextStyle( + color: themeController + .currentColor.sc3, + fontSize: AppConstants() + .title_text_fontSize), + ), + ), + Obx(() { + return GradientSwitch( + value: commonMessageSettingController + .model.setting == + 1 + ? (commonMessageSettingController + .model + .deviceUpgradeSetting == + 1 + ? true + : false) + : false, + onChanged: (val) { + if (commonMessageSettingController + .model.setting == + 0) { + TopSlideNotification.show( + context, + text: "请先打开全部消息配置".tr, + textColor: themeController + .currentColor.sc9); + return; + } + String serviceAddress = + ServiceConstant + .service_address; + String serviceName = + ServiceConstant + .server_service; + String serviceApi = + ServiceConstant.user_setting; + String type = + "user_message_setting"; + String queryUrl = + "${serviceAddress}${serviceName}${serviceApi}"; + var data = { + "type": type, + "setting": + commonMessageSettingController + .model.setting, + "appSetting": + commonMessageSettingController + .model.appSetting, + "serviceSetting": + commonMessageSettingController + .model.serviceSetting, + "tipSetting": + commonMessageSettingController + .model.tipSetting, + "deviceUpgradeSetting": + val == true ? 1 : 0, + "deviceIssueSetting": + commonMessageSettingController + .model + .deviceIssueSetting, + }; + requestWithLog( + logTitle: "更新消息推送状态", + method: MyHttpMethod.put, + queryUrl: queryUrl, + data: data, + onSuccess: (res) { + _fetchDeviceList(); + commonMessageSettingController + .updateAll(); + }); + }, + activeGradient: LinearGradient( + colors: [ + themeController + .currentColor.sc1, + themeController.currentColor.sc2 + ], + ), + activeThumbColor: Colors.white, + inactiveThumbColor: + stringToColor("#A2A4A9"), + inactiveColor: + stringToColor("#161B28"), + ); + }), + ], + ), + // Text( + // "绑定操作提示介绍".tr, + // style: TextStyle( + // color: + // themeController.currentColor.sc4, + // fontSize: AppConstants() + // .normal_text_fontSize), + // ), + ].divide(SizedBox( + height: 49.rpx, + )), + )), + ), + ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 30.rpx, 0, 0), + child: Container( + width: double.infinity, + constraints: BoxConstraints( + minHeight: 90.rpx, + ), + decoration: BoxDecoration( + color: themeController.currentColor.sc5), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 15.rpx, 30.rpx, 15.rpx), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + "设备升级通知".tr, + style: TextStyle( + color: themeController + .currentColor.sc3, + fontSize: AppConstants() + .title_text_fontSize), + ), + ), + Obx(() { + return GradientSwitch( + value: commonMessageSettingController + .model.setting == + 1 + ? (commonMessageSettingController + .model + .deviceIssueSetting == + 1 + ? true + : false) + : false, + onChanged: (val) { + if (commonMessageSettingController + .model.setting == + 0) { + TopSlideNotification.show( + context, + text: "请先打开全部消息配置".tr, + textColor: themeController + .currentColor.sc9); + return; + } + String serviceAddress = + ServiceConstant + .service_address; + String serviceName = + ServiceConstant + .server_service; + String serviceApi = + ServiceConstant.user_setting; + String type = + "user_message_setting"; + String queryUrl = + "${serviceAddress}${serviceName}${serviceApi}"; + var data = { + "type": type, + "setting": + commonMessageSettingController + .model.setting, + "appSetting": + commonMessageSettingController + .model.appSetting, + "serviceSetting": + commonMessageSettingController + .model.serviceSetting, + "tipSetting": + commonMessageSettingController + .model.tipSetting, + "deviceUpgradeSetting": + commonMessageSettingController + .model + .deviceUpgradeSetting, + "deviceIssueSetting": + val == true ? 1 : 0, + }; + requestWithLog( + logTitle: "更新消息推送状态", + method: MyHttpMethod.put, + queryUrl: queryUrl, + data: data, + onSuccess: (res) { + _fetchDeviceList(); + commonMessageSettingController + .updateAll(); + }); + }, + activeGradient: LinearGradient( + colors: [ + themeController + .currentColor.sc1, + themeController.currentColor.sc2 + ], + ), + activeThumbColor: Colors.white, + inactiveThumbColor: + stringToColor("#A2A4A9"), + inactiveColor: + stringToColor("#161B28"), + ); + }), + ], + ), + // Text( + // "绑定操作提示介绍".tr, + // style: TextStyle( + // color: + // themeController.currentColor.sc4, + // fontSize: AppConstants() + // .normal_text_fontSize), + // ), + ].divide(SizedBox( + height: 49.rpx, + )), + )), + ), + ), + ], + ), + ), + ), + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/pages/sleep_report/chart/DataShowWidget.dart b/lib/pages/sleep_report/chart/DataShowWidget.dart index c55b465..c022d07 100644 --- a/lib/pages/sleep_report/chart/DataShowWidget.dart +++ b/lib/pages/sleep_report/chart/DataShowWidget.dart @@ -50,7 +50,7 @@ class _DataShowWidgetState extends State { children: [ // 放入传入的 widget1 Container( - width: MediaQuery.sizeOf(context).width * 0.4, // 固定宽度 + width: MediaQuery.sizeOf(context).width * 0.35, // 固定宽度 decoration: BoxDecoration(), child: Align( alignment: widget.alignment == MainAxisAlignment.start diff --git a/lib/pages/sleep_report/chart/DotBarChart.dart b/lib/pages/sleep_report/chart/DotBarChart.dart index 1ab0216..2406850 100644 --- a/lib/pages/sleep_report/chart/DotBarChart.dart +++ b/lib/pages/sleep_report/chart/DotBarChart.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:vbvs_app/common/util/FitTool.dart'; import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'dart:ui' as ui; class DotBarChart extends StatefulWidget { final List> showLabel; @@ -39,7 +40,6 @@ class _DotBarChartState extends State { void _showTooltip(BuildContext context, Map data, Offset position, double dotY) { _removeOverlay(); - final RenderBox renderBox = context.findRenderObject() as RenderBox; final Offset globalPosition = renderBox.localToGlobal(position); @@ -55,11 +55,6 @@ class _DotBarChartState extends State { color: themeController.currentColor.sc5, borderRadius: BorderRadius.circular(8.rpx), boxShadow: [ - // BoxShadow( - // color: Colors.black.withOpacity(0.6), - // blurRadius: 10.rpx, - // offset: Offset(0, 4.rpx), - // ), BoxShadow( color: Colors.black.withOpacity(0.5), blurRadius: 12.rpx, @@ -92,7 +87,6 @@ class _DotBarChartState extends State { ), ), ); - Overlay.of(context)?.insert(_overlayEntry!); } @@ -103,7 +97,6 @@ class _DotBarChartState extends State { int maxTimes = widget.showLabel .map((e) => e['times'] ?? 0) .reduce((a, b) => a > b ? a : b); - int yMax = (maxTimes / 10).ceil() * 10; if (yMax == 0) yMax = 10; @@ -113,23 +106,35 @@ class _DotBarChartState extends State { int displayMax = step * maxSteps; List yLabels = List.generate(maxSteps + 1, (index) => step * index); - DateFormat fullFormat = DateFormat('HH:mm'); - DateFormat hourFormat = DateFormat('H'); + DateTime startDate = DateTime.fromMillisecondsSinceEpoch(widget.startTime); + DateTime endDate = DateTime.fromMillisecondsSinceEpoch(widget.endTime); - int maxXLabels = 11; - int totalPoints = widget.showLabel.length; - List xLabelIndices = []; + // Generate hourly timestamps + List hourlyTimestamps = []; + DateTime currentHour = DateTime( + startDate.year, startDate.month, startDate.day, startDate.hour); + while (currentHour.isBefore(endDate) || + currentHour.isAtSameMomentAs(endDate)) { + hourlyTimestamps.add(currentHour.millisecondsSinceEpoch); + currentHour = currentHour.add(const Duration(hours: 1)); + } - if (totalPoints <= maxXLabels) { - xLabelIndices = List.generate(totalPoints, (i) => i); - } else { - xLabelIndices.add(0); - int middleCount = maxXLabels - 2; - double stepX = (totalPoints - 1) / (middleCount + 1); - for (int i = 1; i <= middleCount; i++) { - xLabelIndices.add((stepX * i).round()); + // Calculate positions for hourly labels + List> hourLabels = []; + if (widget.showLabel.isNotEmpty) { + int firstDataTime = widget.showLabel.first['time']; + int lastDataTime = widget.showLabel.last['time']; + double totalDuration = (lastDataTime - firstDataTime).toDouble(); + + for (int timestamp in hourlyTimestamps) { + if (timestamp >= firstDataTime && timestamp <= lastDataTime) { + double position = (timestamp - firstDataTime) / totalDuration; + hourLabels.add({ + 'time': timestamp, + 'position': position, + }); + } } - xLabelIndices.add(totalPoints - 1); } double yAxisWidth = 36.rpx; @@ -225,19 +230,14 @@ class _DotBarChartState extends State { drawableHeight * (1 - times / displayMax); return Positioned( - left: x - 20.rpx, // Increase touch area - top: y - 20.rpx, // Increase touch area + left: x - 20.rpx, + top: y - 20.rpx, child: GestureDetector( onTap: () { setState(() { selectedIndex = index; }); - _showTooltip( - context, - data, - Offset(x, y), - y, - ); + _showTooltip(context, data, Offset(x, y), y); }, child: Container( width: 40.rpx, @@ -259,33 +259,15 @@ class _DotBarChartState extends State { padding: EdgeInsets.only(left: yAxisWidth), child: SizedBox( height: xAxisHeight, - child: Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: List.generate(widget.showLabel.length, (index) { - String label = ''; - if (xLabelIndices.contains(index)) { - DateTime dt = DateTime.fromMillisecondsSinceEpoch( - widget.showLabel[index]['time']); - if (index == 0 || index == totalPoints - 1) { - label = fullFormat.format(dt); - } else { - label = dt.hour.toString(); - } - } - return Expanded( - child: Padding( - padding: EdgeInsets.only(top: 14.rpx), - child: Text( - label, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 18.rpx, - color: themeController.currentColor.sc4, - ), - ), - ), - ); - }), + child: CustomPaint( + size: Size(double.infinity, xAxisHeight), + painter: _XAxisPainter( + hourLabels: hourLabels, + textColor: themeController.currentColor.sc4, + fontSize: 18.rpx, + startTime: widget.startTime, + endTime: widget.endTime, + ), ), ), ), @@ -328,7 +310,6 @@ class _DotBarChartPainter extends CustomPainter { double thresholdY = yAxisTopPadding + drawableHeight * (1 - threshold / yMax); - drawDashedLine( canvas, Offset(0, thresholdY), @@ -339,7 +320,6 @@ class _DotBarChartPainter extends CustomPainter { ); final double dotRadius = 13.rpx; - for (int i = 0; i < data.length; i++) { int times = data[i]['times'] ?? 0; double x = horizontalPadding + i * xStep; @@ -357,10 +337,8 @@ class _DotBarChartPainter extends CustomPainter { ..style = PaintingStyle.stroke ..color = Colors.white ..strokeWidth = 3.rpx; - canvas.drawCircle(Offset(x, y), dotRadius + 1.rpx, borderPaint); } - canvas.drawCircle(Offset(x, y), dotRadius, dotPaint); } @@ -376,7 +354,6 @@ class _DotBarChartPainter extends CustomPainter { for (int i = 0; i < yLabelsCount; i++) { double y = yAxisTopPadding + i * (drawableHeight / (yLabelsCount - 1)); - if (i == yLabelsCount - 1) { canvas.drawLine(Offset(0, y), Offset(size.width, y), solidLinePaint); } else { @@ -392,8 +369,14 @@ class _DotBarChartPainter extends CustomPainter { } } - void drawDashedLine(Canvas canvas, Offset start, Offset end, Paint paint, - double dashWidth, double gapWidth) { + void drawDashedLine( + Canvas canvas, + Offset start, + Offset end, + Paint paint, + double dashWidth, + double gapWidth, + ) { double dx = start.dx; final double y = start.dy; while (dx < end.dx) { @@ -406,3 +389,65 @@ class _DotBarChartPainter extends CustomPainter { @override bool shouldRepaint(covariant CustomPainter oldDelegate) => true; } + +class _XAxisPainter extends CustomPainter { + final List> hourLabels; + final Color textColor; + final double fontSize; + final int startTime; + final int endTime; + + _XAxisPainter({ + required this.hourLabels, + required this.textColor, + required this.fontSize, + required this.startTime, + required this.endTime, + }); + + @override + void paint(Canvas canvas, Size size) { + final textStyle = TextStyle( + color: textColor, + fontSize: fontSize, + ); + final textPainter = TextPainter( + textDirection: ui.TextDirection.ltr, + textAlign: TextAlign.center, + ); + + // Draw start time (leftmost) + final startText = DateFormat('HH:mm') + .format(DateTime.fromMillisecondsSinceEpoch(startTime)); + final startTextSpan = TextSpan(text: startText, style: textStyle); + textPainter.text = startTextSpan; + textPainter.layout(); + textPainter.paint(canvas, Offset(0, 14.rpx)); + + // Draw end time (rightmost) + final endText = DateFormat('HH:mm') + .format(DateTime.fromMillisecondsSinceEpoch(endTime)); + final endTextSpan = TextSpan(text: endText, style: textStyle); + textPainter.text = endTextSpan; + textPainter.layout(); + textPainter.paint(canvas, Offset(size.width - textPainter.width, 14.rpx)); + + // Draw hourly labels in between + for (var label in hourLabels) { + final position = label['position'] * size.width; + final time = DateTime.fromMillisecondsSinceEpoch(label['time']); + final hourText = DateFormat('h').format(time); + final textSpan = TextSpan(text: hourText, style: textStyle); + textPainter.text = textSpan; + textPainter.layout(); + final offset = Offset( + position - textPainter.width / 2, + 14.rpx, // Padding from bottom + ); + textPainter.paint(canvas, offset); + } + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => true; +} diff --git a/lib/pages/sleep_report/chart/GradientLine.dart b/lib/pages/sleep_report/chart/GradientLine.dart new file mode 100644 index 0000000..e5ad6b3 --- /dev/null +++ b/lib/pages/sleep_report/chart/GradientLine.dart @@ -0,0 +1,48 @@ +import 'package:flutter/material.dart'; + +class GradientLine extends StatelessWidget { + final double height; + final Color color; + + const GradientLine({ + Key? key, + this.height = 4.0, + required this.color, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, constraints) { + return CustomPaint( + size: Size(constraints.maxWidth, height), + painter: _GradientLinePainter(color: color), + ); + }, + ); + } +} + +class _GradientLinePainter extends CustomPainter { + final Color color; + + _GradientLinePainter({required this.color}); + + @override + void paint(Canvas canvas, Size size) { + final Paint paint = Paint() + ..shader = LinearGradient( + colors: [ + color.withOpacity(0.0), // 左端透明 + color.withOpacity(1.0), // 中间最深 + color.withOpacity(0.0), // 右端透明 + ], + stops: [0.0, 0.5, 1.0], + ).createShader(Rect.fromLTWH(0, 0, size.width, size.height)); + + canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), paint); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => false; +} diff --git a/lib/pages/sleep_report/chart/SegmentedCirclePainter.dart b/lib/pages/sleep_report/chart/SegmentedCirclePainter.dart index d6cb1b8..79c3abe 100644 --- a/lib/pages/sleep_report/chart/SegmentedCirclePainter.dart +++ b/lib/pages/sleep_report/chart/SegmentedCirclePainter.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:vbvs_app/common/util/FitTool.dart'; -import 'package:vbvs_app/common/util/MyUtils.dart'; class SegmentData { final Color color; @@ -63,6 +62,7 @@ class SegmentedCircleWithCenterWidget extends StatelessWidget { final double strokeWidth; final double gapAngle; final Widget centerWidget; + final int trend; const SegmentedCircleWithCenterWidget({ Key? key, @@ -70,6 +70,7 @@ class SegmentedCircleWithCenterWidget extends StatelessWidget { this.strokeWidth = 6.0, this.gapAngle = 4.0, required this.centerWidget, + required this.trend, }) : super(key: key); @override @@ -87,16 +88,27 @@ class SegmentedCircleWithCenterWidget extends StatelessWidget { ), centerWidget, // 放置自定义的中心 Widget Positioned( + bottom: 200.rpx, right: 60.rpx, // 放置在右侧 child: SvgPicture.asset( - 'assets/img/icon/score_down.svg', - width: 14.rpx, - height: 22.rpx, - color: themeController.currentColor.sc9, + _getTrendIcon(trend), + width: trend != 0 ? 14.rpx : 18.rpx, + height: trend != 0 ? 22.rpx : 6.rpx, + // color: themeController.currentColor.sc9, ), ), - ], ); } + + String _getTrendIcon(int? trend) { + switch (trend) { + case 0: + return 'assets/img/icon/score_equal.svg'; + case 1: + return 'assets/img/icon/score_up.svg'; + default: + return 'assets/img/icon/score_down.svg'; + } + } } diff --git a/lib/pages/sleep_report/chart/SleepRadarChart.dart b/lib/pages/sleep_report/chart/SleepRadarChart.dart index dd5b1c8..78782fe 100644 --- a/lib/pages/sleep_report/chart/SleepRadarChart.dart +++ b/lib/pages/sleep_report/chart/SleepRadarChart.dart @@ -1,17 +1,20 @@ -import 'package:flutter/material.dart'; +import 'package:ef/ef.dart'; import 'package:fl_chart/fl_chart.dart'; +import 'package:flutter/material.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'; class SleepRadarChart extends StatelessWidget { - final Map today; - final Map yesterday; +final List> data; - const SleepRadarChart({ - Key? key, - required this.today, - required this.yesterday, - }) : super(key: key); +const SleepRadarChart({Key? key, required this.data}) : super(key: key); + + // const SleepRadarChart({ + // Key? key, + // required this.today, + // required this.yesterday, + // }) : super(key: key); @override Widget build(BuildContext context) { @@ -27,74 +30,48 @@ class SleepRadarChart extends StatelessWidget { } Widget _buildRadarChart() { - return AspectRatio( - aspectRatio: 1.3, - child: RadarChart( - RadarChartData( - dataSets: [ - // 今日数据 - RadarDataSet( - dataEntries: [ - RadarEntry(value: today['type1']!), // 呼吸暂停 - RadarEntry(value: today['type2']!), // 入睡时间 - RadarEntry(value: today['type3']!), // 离床次数 - RadarEntry(value: today['type4']!), // 深睡比例 - RadarEntry(value: today['type5']!), // 睡眠时长 - ], - borderColor: stringToColor("#00C1AA"), - borderWidth: 2, - fillColor: Colors.transparent, - entryRadius: 0, - ), - // 昨日数据 - RadarDataSet( - dataEntries: [ - RadarEntry(value: yesterday['type1']!), // 呼吸暂停 - RadarEntry(value: yesterday['type2']!), // 入睡时间 - RadarEntry(value: yesterday['type3']!), // 离床次数 - RadarEntry(value: yesterday['type4']!), // 深睡比例 - RadarEntry(value: yesterday['type5']!), // 睡眠时长 - ], - borderColor: stringToColor("#FFD251"), - borderWidth: 2, - fillColor: Colors.transparent, - entryRadius: 0, - ), - ], - radarBackgroundColor: stringToColor("#343844"), - radarBorderData: - BorderSide(color: themeController.currentColor.sc4, width: 1), - radarShape: RadarShape.polygon, - titlePositionPercentageOffset: 0.2, - titleTextStyle: TextStyle( - fontSize: AppConstants().normal_text_fontSize, - color: themeController.currentColor.sc3), - getTitle: (index, angle) { - switch (index) { - case 0: - return RadarChartTitle(text: '呼吸暂停'); - case 1: - return RadarChartTitle(text: '入睡时间'); - case 2: - return RadarChartTitle(text: '离床次数'); - case 3: - return RadarChartTitle(text: '深睡比例'); - case 4: - return RadarChartTitle(text: '睡眠时长'); - default: - return const RadarChartTitle(text: ''); - } - }, - tickCount: 5, - ticksTextStyle: - const TextStyle(color: Colors.transparent, fontSize: 10), - // ticksColor: Colors.grey.shade300, - gridBorderData: BorderSide(color: Colors.transparent, width: 1), - tickBorderData: - BorderSide(color: themeController.currentColor.sc4, width: 1), - ), - swapAnimationDuration: const Duration(milliseconds: 400), + return AspectRatio( + aspectRatio: 1.3, + child: RadarChart( + RadarChartData( + dataSets: [ + // 今日数据 + RadarDataSet( + dataEntries: data.map((e) => RadarEntry(value: (e['t'] as num).toDouble())).toList(), + borderColor: stringToColor("#00C1AA"), + borderWidth: 2, + fillColor: Colors.transparent, + entryRadius: 0, + ), + // 昨日数据 + RadarDataSet( + dataEntries: data.map((e) => RadarEntry(value: (e['y'] as num).toDouble())).toList(), + borderColor: stringToColor("#FFD251"), + borderWidth: 2, + fillColor: Colors.transparent, + entryRadius: 0, + ), + ], + radarBackgroundColor: stringToColor("#343844"), + radarBorderData: BorderSide( + color: themeController.currentColor.sc4, width: 0.5.rpx), + radarShape: RadarShape.polygon, + titlePositionPercentageOffset: 0.2, + titleTextStyle: TextStyle( + fontSize: AppConstants().normal_text_fontSize, + color: themeController.currentColor.sc3), + getTitle: (index, angle) { + return RadarChartTitle(text: data[index]['name'] ?? '未知'.tr); + }, + tickCount: 5, + ticksTextStyle: const TextStyle(color: Colors.transparent, fontSize: 10), + gridBorderData: BorderSide(color: Colors.transparent, width: 1), + tickBorderData: BorderSide( + color: themeController.currentColor.sc4, width: 0.5.rpx), ), - ); - } + swapAnimationDuration: const Duration(milliseconds: 400), + ), + ); +} + } diff --git a/lib/pages/sleep_report/chart/SnoreWaveform.dart b/lib/pages/sleep_report/chart/SnoreWaveform.dart new file mode 100644 index 0000000..8320ea6 --- /dev/null +++ b/lib/pages/sleep_report/chart/SnoreWaveform.dart @@ -0,0 +1,273 @@ +import 'dart:ui' as ui; +import 'package:flutter/material.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; +import 'package:vbvs_app/common/util/FitTool.dart'; +import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:intl/intl.dart'; + +class SnoreChartContainer extends StatelessWidget { + final List snoreValues; + final List barData; + final List showLabel; + final int startTime; + final int endTime; + + const SnoreChartContainer({ + required this.snoreValues, + required this.barData, + required this.showLabel, + required this.startTime, + required this.endTime, + super.key, + }); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SnoreBarOverlay( + barData: barData, + showLabel: showLabel, + startTime: startTime, + endTime: endTime, + ), + Container(height: 32.rpx), + Container( + height: 23.rpx, + child: SnoreWaveform( + snoreValues: snoreValues, + startTime: startTime, + endTime: endTime, + ), + ), + ], + ); + } +} + +class SnoreBarOverlay extends StatelessWidget { + final List barData; + final List showLabel; // ✅ 加入 + final int startTime; + final int endTime; + + const SnoreBarOverlay({ + required this.barData, + required this.showLabel, // ✅ 加入 + required this.startTime, + required this.endTime, + super.key, + }); + + @override + Widget build(BuildContext context) { + const double barHeight = 50; + return SizedBox( + height: barHeight, + child: CustomPaint( + size: Size(double.infinity, barHeight), + painter: SnoreBarPainter( + barData: barData, + showLabel: showLabel, // ✅ 加入 + startTime: startTime, + endTime: endTime, + ), + ), + ); + } +} + +class SnoreBarPainter extends CustomPainter { + final List barData; + final List showLabel; // ✅ 加入 + final int startTime; + final int endTime; + + SnoreBarPainter({ + required this.barData, + required this.showLabel, // ✅ 加入 + required this.startTime, + required this.endTime, + }); + + @override + void paint(Canvas canvas, Size size) { + final double width = size.width; + final double height = size.height; + final double pixelPerMs = width / (endTime - startTime); + + for (var item in barData) { + final int st = item['st']; + final int et = item['et']; + final int type = item['type']; + + // 查找匹配的颜色 + final match = showLabel.firstWhere( + (e) => e['type'] == type, + orElse: () => null, + ); + + Color barColor = Colors.transparent; + if (match != null) { + final dynamic colorStr = match['color']; + if (colorStr != null && colorStr.toString().isNotEmpty) { + barColor = stringToColor(colorStr); + } + } + + final Paint barPaint = Paint() + ..color = barColor + ..style = PaintingStyle.fill; + + final double leftX = (st - startTime) * pixelPerMs; + final double rightX = (et - startTime) * pixelPerMs; + final double barWidth = rightX - leftX; + + final double barHeight = (type + 5).toDouble() * 8; + final double top = height - barHeight; + + final rect = Rect.fromLTWH(leftX, top, barWidth, barHeight); + canvas.drawRect(rect, barPaint); + } + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => true; +} + +class SnoreWaveform extends StatelessWidget { + final List snoreValues; + final int startTime; + final int endTime; + + const SnoreWaveform({ + required this.snoreValues, + required this.startTime, + required this.endTime, + super.key, + }); + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 150, + child: CustomPaint( + size: Size(double.infinity, 150), + painter: SnoreWaveformPainter( + snoreValues: snoreValues, + startTime: startTime, + endTime: endTime, + ), + ), + ); + } +} + +class SnoreWaveformPainter extends CustomPainter { + final List snoreValues; + final int startTime; + final int endTime; + + SnoreWaveformPainter({ + required this.snoreValues, + required this.startTime, + required this.endTime, + }); + + @override + void paint(Canvas canvas, Size size) { + final double width = size.width; + final double height = size.height; + final double centerY = height / 2; + final double totalDuration = (endTime - startTime).toDouble(); + final double pixelPerMs = width / totalDuration; + + final Paint wavePaint = Paint() + ..color = stringToColor("#8E7DEF") + ..strokeWidth = 1.5 + ..style = PaintingStyle.stroke; + + final Path upperPath = Path(); + final Path lowerPath = Path(); + const double scaleY = 0.5; //波形图比例 + + for (int i = 0; i < snoreValues.length; i++) { + final timestamp = snoreValues[i]["st"]; + final value = snoreValues[i]["value"]?.toDouble() ?? 0; + + final x = (timestamp - startTime) * pixelPerMs; + final y = centerY - value * scaleY; + final yMirror = centerY + value * scaleY; + + if (i == 0) { + upperPath.moveTo(x, y); + lowerPath.moveTo(x, yMirror); + } else { + upperPath.lineTo(x, y); + lowerPath.lineTo(x, yMirror); + } + } + + canvas.drawPath(upperPath, wavePaint); + canvas.drawPath(lowerPath, wavePaint); + + final Paint axisPaint = Paint() + ..color = Colors.grey + ..strokeWidth = 0.5; + + // 画中心线 + canvas.drawLine(Offset(0, centerY), Offset(width, centerY), axisPaint); + + // 时间刻度绘制 + final textPainter = TextPainter( + textAlign: TextAlign.center, + textDirection: ui.TextDirection.ltr, + ); + + final int hourMs = 60 * 60 * 1000; + + // 循环绘制整点小时标签(不包含终点) + for (int t = startTime; t < endTime; t += hourMs) { + double x = (t - startTime) * pixelPerMs; + + DateTime dt = DateTime.fromMillisecondsSinceEpoch(t); + String label; + if (t == startTime) { + label = DateFormat('HH:mm').format(dt); // 起点显示 HH:mm + } else { + label = DateFormat('h').format(dt); // 中间显示小时,不带前导0 + } + + textPainter.text = TextSpan( + text: label, + style: TextStyle(fontSize: 10, color: Colors.grey), + ); + textPainter.layout(); + textPainter.paint( + canvas, + Offset(x - textPainter.width / 2, height + 20.rpx), + ); + } + + // 单独绘制终点时间标签,确保显示具体时分 + { + double x = (endTime - startTime) * pixelPerMs; + DateTime dt = DateTime.fromMillisecondsSinceEpoch(endTime); + String label = DateFormat('HH:mm').format(dt); + + textPainter.text = TextSpan( + text: label, + style: TextStyle(fontSize: 10, color: Colors.grey), + ); + textPainter.layout(); + textPainter.paint( + canvas, + Offset(x - textPainter.width / 2, height + 20.rpx), + ); + } + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => true; +} diff --git a/lib/pages/sleep_report/component/AIAdviceWidget.dart b/lib/pages/sleep_report/component/AIAdviceWidget.dart index 67d125f..1e5d83d 100644 --- a/lib/pages/sleep_report/component/AIAdviceWidget.dart +++ b/lib/pages/sleep_report/component/AIAdviceWidget.dart @@ -9,7 +9,8 @@ import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart'; import 'package:vbvs_app/pages/sleep_report/chart/AdviceComponnetWidget.dart'; class AIAdviceWidget extends StatefulWidget { - AIAdviceWidget({super.key}); + var sleepReport; + AIAdviceWidget({super.key, required this.sleepReport}); @override State createState() => _AIAdviceWidgetState(); @@ -33,32 +34,12 @@ class _AIAdviceWidgetState extends State { @override Widget build(BuildContext context) { - List advices = [ - { - "title": "调整作息时间", - "description": "确保每天在相同的时间上床并醒来。保持规律的作息可以帮助调整你的生物钟,改善睡眠质量。", - }, - { - "title": "减少睡前刺激", - "description": "避免在睡前使用电子设备,如手机、电脑等,减少屏幕时间,以防影响你的睡眠质量。", - }, - { - "title": "创造理想睡眠环境", - "description": "确保卧室安静、黑暗且舒适。调节室温,并避免过于嘈杂或明亮的环境,帮助你快速入睡。", - }, - { - "title": "避免摄入咖啡因和酒精", - "description": "避免在睡前几小时内摄入咖啡、茶、酒精等饮品,因为这些物质可能会干扰你的睡眠。", - }, - { - "title": "增加日间活动", - "description": "适量的日间运动可以帮助提高睡眠质量,但要避免睡前剧烈运动,以免影响入睡。", - }, - { - "title": "放松身心", - "description": "睡前可以进行一些放松活动,如深呼吸、冥想或听轻音乐,这有助于减轻压力并促进良好的睡眠。", - }, - ]; + if (widget.sleepReport == null || + widget.sleepReport['sugges'] == null || + widget.sleepReport['sugges'].isEmpty) { + return Container(); + } + List advices = widget.sleepReport['sugges']; return Container( width: double.infinity, @@ -126,8 +107,8 @@ class _AIAdviceWidgetState extends State { child: Column( children: advices.map((advice) { return AdviceComponnetWidget( - title: advice["title"], - description: advice["description"], + title: advice["q"], + description: advice["s"], ).paddingOnly(bottom: 0.rpx); // 在每个组件下方添加间隔 }).toList(), ), diff --git a/lib/pages/sleep_report/component/BreathPauseWidget.dart b/lib/pages/sleep_report/component/BreathPauseWidget.dart index 5c20ced..6231075 100644 --- a/lib/pages/sleep_report/component/BreathPauseWidget.dart +++ b/lib/pages/sleep_report/component/BreathPauseWidget.dart @@ -9,7 +9,8 @@ import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart'; import 'package:vbvs_app/pages/sleep_report/chart/DotBarChart.dart'; class BreathPauseWidget extends StatefulWidget { - BreathPauseWidget({super.key}); + var sleepReport; + BreathPauseWidget({super.key, required this.sleepReport}); @override State createState() => _BreathPauseWidgetState(); @@ -33,33 +34,30 @@ class _BreathPauseWidgetState extends State { @override Widget build(BuildContext context) { + if (widget.sleepReport == null || + widget.sleepReport['asp'] == null || + widget.sleepReport['asp'].isEmpty) { + return Container(); + } + + // List data = widget.sleepReport['asp']; // var showLabel = [ - // {"time": 1744547251000, "times": 20}, - // {"time": 1744550851000, "times": 50}, - // {"time": 1744554451000, "times": 20}, - // {"time": 1744558051000, "times": 30}, - // {"time": 1744561651000, "times": 20}, - // {"time": 1744565251000, "times": 10}, - // {"time": 1744568851000, "times": 20}, - // {"time": 1744572451000, "times": 20}, - // {"time": 1744583251000, "times": 100}, - // {"time": 1744586851000, "times": 20}, + // {"time": 1744547251000, "times": 25}, + // {"time": 1744550851000, "times": 27}, + // {"time": 1744554451000, "times": 40}, + // {"time": 1744558051000, "times": 28}, + // {"time": 1744561651000, "times": 15}, + // {"time": 1744565251000, "times": 48}, + // {"time": 1744568851000, "times": 25}, + // {"time": 1744572451000, "times": 17}, + // {"time": 1744583251000, "times": 35}, + // {"time": 1744586851000, "times": 40}, // ]; - var showLabel = [ - {"time": 1744547251000, "times": 25}, - {"time": 1744550851000, "times": 27}, - {"time": 1744554451000, "times": 40}, - {"time": 1744558051000, "times": 28}, - {"time": 1744561651000, "times": 15}, - {"time": 1744565251000, "times": 48}, - {"time": 1744568851000, "times": 25}, - {"time": 1744572451000, "times": 17}, - {"time": 1744583251000, "times": 35}, - {"time": 1744586851000, "times": 40}, - ]; - var threshold = 40; - var startTime = 1744641151000; - var endTime = 1744677151000; + List data = widget.sleepReport['asp']; + var showLabel = convertAspData(data); + var threshold = 30; + var startTime = widget.sleepReport['startTime']; + var endTime = widget.sleepReport['endTime']; return Container( width: double.infinity, decoration: BoxDecoration( @@ -138,4 +136,13 @@ class _BreathPauseWidgetState extends State { ), ); } + + List> convertAspData(List data) { + return data.map>((item) { + return { + 'time': item['st'], + 'times': item['value'], + }; + }).toList(); + } } diff --git a/lib/pages/sleep_report/component/BreatheCard.dart b/lib/pages/sleep_report/component/BreatheCard.dart new file mode 100644 index 0000000..ee753ca --- /dev/null +++ b/lib/pages/sleep_report/component/BreatheCard.dart @@ -0,0 +1,69 @@ +import 'package:flutter/material.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/home_page/SleepDataModuleWidget.dart'; + +class BreatheCard extends StatefulWidget { + var sleepReport; + BreatheCard({super.key, required this.sleepReport}); + + @override + State createState() => _BreatheCardState(); +} + +class _BreatheCardState 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) { + if (widget.sleepReport == null || + widget.sleepReport is! Map || + widget.sleepReport.isEmpty) { + return Container(); + } + + List data = widget.sleepReport['brs'] ?? []; + + 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: Wrap( + spacing: 23.rpx, // 横向间距(左右间距如需加可设置) + runSpacing: 25.rpx, // 每行之间的垂直间距 + children: List.generate(data.length, (index) { + final item = data[index]; + return SizedBox( + width: (MediaQuery.of(context).size.width - 160.rpx) / 3, + child: SleepDataModuleWidget(data: item), + // child: Container( + // width: 20, + // height: 20, + // color: Colors.red, + // ), + ); + }), + ), + ), + ); + } +} diff --git a/lib/pages/sleep_report/component/BreathePauseNewWidget.dart b/lib/pages/sleep_report/component/BreathePauseNewWidget.dart new file mode 100644 index 0000000..535d46a --- /dev/null +++ b/lib/pages/sleep_report/component/BreathePauseNewWidget.dart @@ -0,0 +1,166 @@ +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/LineChartByRange.dart'; + +class BreathePauseNewWidget extends StatefulWidget { + var sleepReport; + BreathePauseNewWidget({super.key, required this.sleepReport}); + + @override + State createState() => _SnoreViewWidgetWidgetState(); +} + +class _SnoreViewWidgetWidgetState 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) { + if (widget.sleepReport == null || + widget.sleepReport is! Map || + widget.sleepReport.isEmpty) { + return Container(); + } + + List> data = + (widget.sleepReport['asp'] as List).cast>(); + List> showLabel = convertToShowLabel(data); + var startTime = widget.sleepReport['startTime']; + var endTime = widget.sleepReport['endTime']; + 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, 0.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( + "呼吸暂停监测介绍。", + style: TextStyle( + fontSize: 26.rpx, + color: themeController.currentColor.sc3, + ), + ), + ), + ); + }, + 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, + ), + ), + ), + ], + ), + ), + SizedBox( + height: 32.rpx, + ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0.rpx, 0.rpx, 0.rpx, 0.rpx), + child: LineChartByRange( + showLabel: showLabel, + startTime: startTime, + endTime: endTime, + ), + ), + SizedBox( + height: 52.rpx, + ), + ], + ), + ), + ); + } + + List> convertToShowLabel( + List> data) { + if (data.isEmpty) return []; + + data.sort((a, b) => a['st'].compareTo(b['st'])); // 确保时间有序 + + List> result = []; + + int startTime = data[0]['st']; + int endTime = data[0]['st']; + int currentValue = data[0]['value']; + + for (int i = 1; i < data.length; i++) { + final item = data[i]; + final st = item['st']; + final value = item['value']; + + if (value == currentValue) { + endTime = st; + } else { + result.add({ + "startTime": startTime, + "endTime": endTime, + "times": currentValue, + }); + startTime = st; + endTime = st; + currentValue = value; + } + } + + // 添加最后一段 + result.add({ + "startTime": startTime, + "endTime": endTime, + "times": currentValue, + }); + + return result; + } +} diff --git a/lib/pages/sleep_report/component/BreatheStandardWidget.dart b/lib/pages/sleep_report/component/BreatheStandardWidget.dart index 58ab714..98e7af8 100644 --- a/lib/pages/sleep_report/component/BreatheStandardWidget.dart +++ b/lib/pages/sleep_report/component/BreatheStandardWidget.dart @@ -10,7 +10,8 @@ import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart'; import 'package:vbvs_app/pages/sleep_report/chart/TimeSeriesChart.dart'; class BreatheStandardWidget extends StatefulWidget { - BreatheStandardWidget({super.key}); + var sleepReport; + BreatheStandardWidget({super.key, required this.sleepReport}); @override State createState() => _BreatheStandardWidgetState(); @@ -34,17 +35,29 @@ class _BreatheStandardWidgetState extends State { @override Widget build(BuildContext context) { - final now = DateTime.now(); - final startTime = now.subtract(Duration(hours: 5)).millisecondsSinceEpoch; - final endTime = now.millisecondsSinceEpoch; - final dataPoints = [ - TimeSeriesPoint(startTime + Duration(minutes: 10).inMilliseconds, 50), - TimeSeriesPoint(startTime + Duration(hours: 1).inMilliseconds, 120), - TimeSeriesPoint(startTime + Duration(hours: 2).inMilliseconds, 80), - TimeSeriesPoint(startTime + Duration(hours: 3).inMilliseconds, 180), - TimeSeriesPoint(startTime + Duration(hours: 4).inMilliseconds, 30), - TimeSeriesPoint(endTime - Duration(minutes: 10).inMilliseconds, 150), - ]; + if (widget.sleepReport == null || + widget.sleepReport is! Map || + widget.sleepReport.isEmpty) { + return Container(); + } + + final startTime = widget.sleepReport['startTime']; + final endTime = widget.sleepReport['endTime']; + List> data = + (widget.sleepReport['brbc'] as List).cast>(); + // final dataPoints = [ + // TimeSeriesPoint(12121, 50), + // TimeSeriesPoint(1212, 120), + // TimeSeriesPoint(121, 80), + // TimeSeriesPoint(1212, 180), + // TimeSeriesPoint(1212, 30), + // TimeSeriesPoint(1212, 150), + // ]; + final dataPoints = data.map((item) { + final x = item['st'] as int; + final y = (item['value'] as num).toDouble(); // 安全地转换为 double + return TimeSeriesPoint(x, y); + }).toList(); return Container( width: double.infinity, diff --git a/lib/pages/sleep_report/component/CompareSleepWidget.dart b/lib/pages/sleep_report/component/CompareSleepWidget.dart index fded756..0fae301 100644 --- a/lib/pages/sleep_report/component/CompareSleepWidget.dart +++ b/lib/pages/sleep_report/component/CompareSleepWidget.dart @@ -7,12 +7,11 @@ 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/AdviceComponnetWidget.dart'; -import 'package:vbvs_app/pages/sleep_report/chart/RadarChart.dart'; import 'package:vbvs_app/pages/sleep_report/chart/SleepRadarChart.dart'; class CompareSleepWidget extends StatefulWidget { - CompareSleepWidget({super.key}); + var sleepReport; + CompareSleepWidget({super.key, required this.sleepReport}); @override State createState() => _CompareSleepWidgetState(); @@ -36,20 +35,37 @@ class _CompareSleepWidgetState extends State { @override Widget build(BuildContext context) { - var today = { - "type1": 40.0, - "type2": 80.0, - "type3": 60.0, - "type4": 70.0, - "type5": 100.0 - }; - var yesterday = { - "type1": 40.0, - "type2": 90.0, - "type3": 50.0, - "type4": 70.0, - "type5": 30.0 - }; + if (widget.sleepReport == null || + widget.sleepReport['yc'] == null || + widget.sleepReport['yc'].isEmpty) { + return Container(); + } + List> data = (widget.sleepReport['yc'] as List) + .map((e) => e as Map) + .toList(); + + // var today = { + // "type1": 40.0, + // "type2": 80.0, + // "type3": 60.0, + // "type4": 70.0, + // "type5": 100.0 + // }; + // var yesterday = { + // "type1": 40.0, + // "type2": 90.0, + // "type3": 50.0, + // "type4": 70.0, + // "type5": 30.0 + // }; + Map today = {}; + Map yesterday = {}; + + for (var item in data) { + String typeKey = 'type${item['id']}'; + today[typeKey] = (item['t'] as num).toDouble(); + yesterday[typeKey] = (item['y'] as num).toDouble(); + } return Container( width: double.infinity, decoration: BoxDecoration( @@ -128,8 +144,9 @@ class _CompareSleepWidgetState extends State { padding: EdgeInsetsDirectional.fromSTEB( 30.rpx, 0.rpx, 0.rpx, 0.rpx), child: SleepRadarChart( - today: today, - yesterday: yesterday, + // today: today, + // yesterday: yesterday, + data: data, ), ), // 在左侧添加一个 Text diff --git a/lib/pages/sleep_report/component/DiseasePercentsWidget.dart b/lib/pages/sleep_report/component/DiseasePercentsWidget.dart index f37fe16..5c310a3 100644 --- a/lib/pages/sleep_report/component/DiseasePercentsWidget.dart +++ b/lib/pages/sleep_report/component/DiseasePercentsWidget.dart @@ -9,57 +9,59 @@ import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart'; import 'package:vbvs_app/pages/sleep_report/chart/HorizontalBarChart.dart'; class DiseasePercentsWidget extends StatefulWidget { - DiseasePercentsWidget({super.key}); + var sleepReport; + DiseasePercentsWidget({super.key, required this.sleepReport}); @override State createState() => _DiseasePercentsWidgetState(); } class _DiseasePercentsWidgetState extends State { - var showLabel = [ - { - "key": 1, - "name": "心脏病", - "color": stringToColor("#00C1AA"), - "percent": 45, - "explain": "心脏病是指心脏的结构或功能异常,可能导致心脏无法有效地泵血。" - }, - { - "key": 2, - "name": "高血压", - "color": stringToColor("#00C1AA"), - "percent": 32, - "explain": "高血压是指血液在动脉中流动时对血管壁施加的压力过高。" - }, - { - "key": 3, - "name": "糖尿病", - "color": stringToColor("#00C1AA"), - "percent": 50, - "explain": "糖尿病是一种代谢性疾病,导致血糖水平异常升高。" - }, - { - "key": 4, - "name": "甲亢", - "color": stringToColor("#FF7159"), - "percent": 80, - "explain": "甲亢是指甲状腺分泌过多的甲状腺激素,导致新陈代谢加速。" - }, - { - "key": 5, - "name": "消化系统", - "color": stringToColor("#00C1AA"), - "percent": 12, - "explain": "消化系统是身体中处理食物的机构,是造成疾病和疾病症状的来源。", - }, - { - "key": 6, - "name": "呼吸系统", - "color": stringToColor("#00C1AA"), - "percent": 62, - "explain": "呼吸系统是负责气体交换的器官系统,包括鼻、喉、气管和肺等。", - }, - ]; + // var showLabel = [ + // { + // "key": 1, + // "name": "心脏病", + // "color": stringToColor("#00C1AA"), + // "percent": 45, + // "explain": "心脏病是指心脏的结构或功能异常,可能导致心脏无法有效地泵血。" + // }, + // { + // "key": 2, + // "name": "高血压", + // "color": stringToColor("#00C1AA"), + // "percent": 32, + // "explain": "高血压是指血液在动脉中流动时对血管壁施加的压力过高。" + // }, + // { + // "key": 3, + // "name": "糖尿病", + // "color": stringToColor("#00C1AA"), + // "percent": 50, + // "explain": "糖尿病是一种代谢性疾病,导致血糖水平异常升高。" + // }, + // { + // "key": 4, + // "name": "甲亢", + // "color": stringToColor("#FF7159"), + // "percent": 80, + // "explain": "甲亢是指甲状腺分泌过多的甲状腺激素,导致新陈代谢加速。" + // }, + // { + // "key": 5, + // "name": "消化系统", + // "color": stringToColor("#00C1AA"), + // "percent": 12, + // "explain": "消化系统是身体中处理食物的机构,是造成疾病和疾病症状的来源。", + // }, + // { + // "key": 6, + // "name": "呼吸系统", + // "color": stringToColor("#00C1AA"), + // "percent": 62, + // "explain": "呼吸系统是负责气体交换的器官系统,包括鼻、喉、气管和肺等。", + // }, + // ]; + @override void setState(VoidCallback callback) { super.setState(callback); @@ -77,6 +79,13 @@ class _DiseasePercentsWidgetState extends State { @override Widget build(BuildContext context) { + if (widget.sleepReport == null || + widget.sleepReport['cdri'] == null || + widget.sleepReport['cdri'].isEmpty) { + return Container(); + } + List diseaseData = widget.sleepReport['cdri']; + var showLabel = convertDiseaseData(diseaseData); return Container( width: double.infinity, decoration: BoxDecoration( @@ -107,7 +116,7 @@ class _DiseasePercentsWidgetState extends State { borderRadius: 0.rpx, // 圆形点击区域 onTap: () { // 你的点击逻辑 - showTipDialog( + showTipDialog( context, Container( child: Text( @@ -149,4 +158,23 @@ class _DiseasePercentsWidgetState extends State { ), ); } + + List> convertDiseaseData(List data) { + return data.asMap().entries.map>((entry) { + final index = entry.key; + final item = entry.value; + + return { + "key": item["id"], + "name": item["name"], + "color": item["id"] == 40004 + ? stringToColor("#FF7159") // 特殊颜色处理 + : stringToColor("#00C1AA"), + "percent": item["value"], + "explain": (item["tips"] != null && (item["tips"] as String).trim().isNotEmpty) + ? item["tips"] + : '未知数据'.tr, + }; + }).toList(); +} } diff --git a/lib/pages/sleep_report/component/HeartChangeWidget.dart b/lib/pages/sleep_report/component/HeartChangeWidget.dart index 76c67eb..57a0423 100644 --- a/lib/pages/sleep_report/component/HeartChangeWidget.dart +++ b/lib/pages/sleep_report/component/HeartChangeWidget.dart @@ -9,7 +9,8 @@ import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart'; import 'package:vbvs_app/pages/sleep_report/chart/DataShowWidget.dart'; class HeartChangeWidget extends StatefulWidget { - HeartChangeWidget({super.key}); + var sleepReport; + HeartChangeWidget({super.key, required this.sleepReport}); @override State createState() => _HeartChangeWidgetState(); @@ -33,72 +34,80 @@ class _HeartChangeWidgetState extends State { @override Widget build(BuildContext context) { + if (widget.sleepReport == null || + widget.sleepReport['hrvs'] == null || + widget.sleepReport['hrvs'].isEmpty) { + return Container(); + } + + List dataList = widget.sleepReport['hrvs']; //0上升 1下降 2持平 - List data = [ - { - "name": "心脏总能量", - "value": 5262, - "range": "2055-6000", - "change": 0, - "desc": "心脏总能量介绍" - }, - { - "name": "心率减速力", - "value": 5262, - "range": "2055-6000", - "change": 1, - "desc": "心率减速力介绍" - }, - { - "name": "迷走神经张力指数", - "value": 5262, - "range": "2055-6000", - "change": 2, - "desc": "迷走神经张力指数介绍" - }, - { - "name": "交感神经张力指数", - "value": 5262, - "range": "2055-6000", - "change": 0, - "desc": "交感神经张力指数介绍" - }, - { - "name": "自主神经张力指数", - "value": 5262, - "range": "2055-6000", - "change": 2, - "desc": "自主神经张力指数介绍" - }, - { - "name": "血管舒张指数", - "value": 5262, - "range": "2055-6000", - "change": 1, - "desc": "血管舒张指数介绍" - }, - { - "name": "SDNN", - "value": 5262, - "range": "2055-6000", - "change": 0, - "desc": "SDNN介绍" - }, - { - "name": "PNN50", - "value": 5262, - "range": "2055-6000", - "change": 1, - "desc": "PNN50介绍" - }, - { - "name": "RMSSD", - "value": 5262, - "range": "2055-6000", - "change": 2, - "desc": "RMSSD介绍" - }, - ]; + // List data = [ + // { + // "name": "心脏总能量", + // "value": 5262, + // "range": "2055-6000", + // "change": 0, + // "desc": "心脏总能量介绍" + // }, + // { + // "name": "心率减速力", + // "value": 5262, + // "range": "2055-6000", + // "change": 1, + // "desc": "心率减速力介绍" + // }, + // { + // "name": "迷走神经张力指数", + // "value": 5262, + // "range": "2055-6000", + // "change": 2, + // "desc": "迷走神经张力指数介绍" + // }, + // { + // "name": "交感神经张力指数", + // "value": 5262, + // "range": "2055-6000", + // "change": 0, + // "desc": "交感神经张力指数介绍" + // }, + // { + // "name": "自主神经张力指数", + // "value": 5262, + // "range": "2055-6000", + // "change": 2, + // "desc": "自主神经张力指数介绍" + // }, + // { + // "name": "血管舒张指数", + // "value": 5262, + // "range": "2055-6000", + // "change": 1, + // "desc": "血管舒张指数介绍" + // }, + // { + // "name": "SDNN", + // "value": 5262, + // "range": "2055-6000", + // "change": 0, + // "desc": "SDNN介绍" + // }, + // { + // "name": "PNN50", + // "value": 5262, + // "range": "2055-6000", + // "change": 1, + // "desc": "PNN50介绍" + // }, + // { + // "name": "RMSSD", + // "value": 5262, + // "range": "2055-6000", + // "change": 2, + // "desc": "RMSSD介绍" + // }, + // ]; + List> data = transformHrvData(dataList); return Container( width: double.infinity, decoration: BoxDecoration( @@ -313,4 +322,18 @@ class _HeartChangeWidgetState extends State { ), ); } + + List> transformHrvData(List originalData) { + return originalData.map((item) { + return { + "name": item["name"] ?? "未知指标".tr, + "value": (item["value"] is num) ? item["value"].toDouble() : 0.0, + "range": item["range"] ?? "0-0", + "change": item["trend"] ?? 0, + "desc": item["tips"]?.toString().isNotEmpty == true + ? item["tips"].toString() + : "未知数据".tr + }; + }).toList(); + } } diff --git a/lib/pages/sleep_report/component/HeartHealthWidget.dart b/lib/pages/sleep_report/component/HeartHealthWidget.dart index 3123da4..8e26fb2 100644 --- a/lib/pages/sleep_report/component/HeartHealthWidget.dart +++ b/lib/pages/sleep_report/component/HeartHealthWidget.dart @@ -10,7 +10,8 @@ import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart'; import 'package:vbvs_app/pages/sleep_report/chart/FatigueCircleIndicator.dart'; class HeartHealthWidget extends StatefulWidget { - HeartHealthWidget({super.key}); + var sleepReport; + HeartHealthWidget({super.key, required this.sleepReport}); @override State createState() => _HeartHealthWidgetState(); @@ -34,20 +35,14 @@ class _HeartHealthWidgetState extends State { @override Widget build(BuildContext context) { - var showLabel = [ - { - "name": "焦虑抑郁", - "color": Color(0xFF4CAF50), - "percent": "7%", - "explain": "低风险" - }, - { - "name": "过度疲劳", - "color": stringToColor("#FF7159"), - "percent": "69%", - "explain": "高风险" - }, - ]; + if (widget.sleepReport == null || + widget.sleepReport['mha'] == null || + widget.sleepReport['mha'].isEmpty) { + return Container(); + } + + List data = widget.sleepReport['mha']; + var showLabel = convertMentalHealthData(data); return Container( width: double.infinity, decoration: BoxDecoration( @@ -115,22 +110,10 @@ class _HeartHealthWidgetState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ FatigueCircleIndicator( - data: { - "name": "焦虑抑郁", - "color": stringToColor("#00C1AA"), - "percent": 7, - "explain": "低风险", - "bottomColor": Colors.grey, - }, + data: showLabel[0], ), FatigueCircleIndicator( - data: { - "name": "过度疲劳", - "color": stringToColor("#FF7159"), - "percent": 69, - "explain": "高风险", - "bottomColor": Colors.grey, - }, + data: showLabel[1], ), ].divide(SizedBox( width: 110.rpx, @@ -145,4 +128,25 @@ class _HeartHealthWidgetState extends State { ), ); } + + List> convertMentalHealthData(List data) { + return data.map>((item) { + final String? colorStr = item['color']; + final int value = item['value'] ?? 0; + final String explain = + (item['tips'] != null && (item['tips'] as String).trim().isNotEmpty) + ? item['tips'] + : '高风险'; + + return { + 'name': item['name'], + 'color': colorStr != null && colorStr.isNotEmpty + ? stringToColor(colorStr) + : stringToColor("#00C1AA"), // 默认红色 + 'percent': value, + 'explain': explain, + "bottomColor": Colors.grey, + }; + }).toList(); + } } diff --git a/lib/pages/sleep_report/component/HeartPointWidget.dart b/lib/pages/sleep_report/component/HeartPointWidget.dart index b9812c4..f31ff8c 100644 --- a/lib/pages/sleep_report/component/HeartPointWidget.dart +++ b/lib/pages/sleep_report/component/HeartPointWidget.dart @@ -12,7 +12,8 @@ import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart'; import 'package:vbvs_app/pages/sleep_report/chart/ScatterPlotChart.dart'; class HeartPointWidget extends StatefulWidget { - HeartPointWidget({super.key}); + var sleepReport; + HeartPointWidget({super.key, required this.sleepReport}); @override State createState() => _HeartPointWidgetState(); @@ -36,17 +37,39 @@ class _HeartPointWidgetState extends State { @override Widget build(BuildContext context) { - List data = List.generate(200, (index) { - // 随机生成 x 和 y 值,范围都在 0-1400 之间 - double x = Random().nextDouble() * 1400; // x 值在 0-1400 范围 - double y = Random().nextDouble() * 1400; // y 值也在 0-1400 范围 + if (widget.sleepReport == null || + widget.sleepReport['hrsp'] == null || + widget.sleepReport['hrsp'].isEmpty) { + return Container(); + } + // List rawData = widget.sleepReport['hrsp']; + // List data = List.generate(200, (index) { + // // 随机生成 x 和 y 值,范围都在 0-1400 之间 + // double x = Random().nextDouble() * 1400; // x 值在 0-1400 范围 + // double y = Random().nextDouble() * 1400; // y 值也在 0-1400 范围 - // 返回 ScatterSpot,使用圆点绘制器自定义大小和颜色 - return ScatterSpot( - x, - y, - ); - }); + // // 返回 ScatterSpot,使用圆点绘制器自定义大小和颜色 + // return ScatterSpot( + // x, + // y, + // ); + // }); + double maxX = 0; + double maxY = 0; + + List data = []; + List rawData = widget.sleepReport['hrsp']; + try { + data = rawData.map((item) { + double x = (item['st'] ?? 0).toDouble(); + double y = (item['value'] ?? 0).toDouble(); + if (x > maxX) maxX = x; + if (y > maxY) maxY = y; + return ScatterSpot(x, y); + }).toList(); + } catch (e) { + print(e); + } return Container( width: double.infinity, @@ -120,8 +143,8 @@ class _HeartPointWidgetState extends State { ), child: ScatterPlotChart( points: data, - xMax: 1400, // x轴最大值 - yMax: 1400, // y轴最大值 + xMax: maxX.toInt(), // x轴最大值 + yMax: maxY.toInt(), // y轴最大值 pointColor: stringToColor("#00C1AA"), // 点的颜色 divisions: 7, // 刻度分割数量 ), diff --git a/lib/pages/sleep_report/component/HeartRateCard.dart b/lib/pages/sleep_report/component/HeartRateCard.dart new file mode 100644 index 0000000..ba35c8d --- /dev/null +++ b/lib/pages/sleep_report/component/HeartRateCard.dart @@ -0,0 +1,69 @@ +import 'package:flutter/material.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/home_page/SleepDataModuleWidget.dart'; + +class HeartRateCard extends StatefulWidget { + var sleepReport; + HeartRateCard({super.key, required this.sleepReport}); + + @override + State createState() => _HeartRateCardState(); +} + +class _HeartRateCardState 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) { + if (widget.sleepReport == null || + widget.sleepReport is! Map || + widget.sleepReport.isEmpty) { + return Container(); + } + + List data = widget.sleepReport['hrs'] ?? []; + + 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: Wrap( + spacing: 23.rpx, // 横向间距(左右间距如需加可设置) + runSpacing: 25.rpx, // 每行之间的垂直间距 + children: List.generate(data.length, (index) { + final item = data[index]; + return SizedBox( + width: (MediaQuery.of(context).size.width - 160.rpx) / 3, + child: SleepDataModuleWidget(data: item), + // child: Container( + // width: 20, + // height: 20, + // color: Colors.red, + // ), + ); + }), + ), + ), + ); + } +} diff --git a/lib/pages/sleep_report/component/HeartRateStandardWidget.dart b/lib/pages/sleep_report/component/HeartRateStandardWidget.dart index 9760fdd..687d0b5 100644 --- a/lib/pages/sleep_report/component/HeartRateStandardWidget.dart +++ b/lib/pages/sleep_report/component/HeartRateStandardWidget.dart @@ -10,7 +10,8 @@ import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart'; import 'package:vbvs_app/pages/sleep_report/chart/TimeSeriesChart.dart'; class HeartRateStandardWidget extends StatefulWidget { - HeartRateStandardWidget({super.key}); + var sleepReport; + HeartRateStandardWidget({super.key, required this.sleepReport}); @override State createState() => @@ -35,17 +36,22 @@ class _HeartRateStandardWidgetState extends State { @override Widget build(BuildContext context) { - final now = DateTime.now(); - final startTime = now.subtract(Duration(hours: 5)).millisecondsSinceEpoch; - final endTime = now.millisecondsSinceEpoch; - final dataPoints = [ - TimeSeriesPoint(startTime + Duration(minutes: 10).inMilliseconds, 50), - TimeSeriesPoint(startTime + Duration(hours: 1).inMilliseconds, 120), - TimeSeriesPoint(startTime + Duration(hours: 2).inMilliseconds, 80), - TimeSeriesPoint(startTime + Duration(hours: 3).inMilliseconds, 180), - TimeSeriesPoint(startTime + Duration(hours: 4).inMilliseconds, 30), - TimeSeriesPoint(endTime - Duration(minutes: 10).inMilliseconds, 150), - ]; + if (widget.sleepReport == null || + widget.sleepReport is! Map || + widget.sleepReport.isEmpty) { + return Container(); + } + + final startTime = widget.sleepReport['startTime']; + final endTime = widget.sleepReport['endTime']; + List> data = + (widget.sleepReport['hrbc'] as List).cast>(); + final dataPoints = data.map((item) { + final x = item['st'] as int; + final y = (item['value'] as num).toDouble(); // 安全地转换为 double + return TimeSeriesPoint(x, y); +}).toList(); + return Container( width: double.infinity, diff --git a/lib/pages/sleep_report/component/SkinPercentWidget.dart b/lib/pages/sleep_report/component/SkinPercentWidget.dart index e4ab1db..6c719dd 100644 --- a/lib/pages/sleep_report/component/SkinPercentWidget.dart +++ b/lib/pages/sleep_report/component/SkinPercentWidget.dart @@ -9,7 +9,8 @@ import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart'; import 'package:vbvs_app/pages/sleep_report/chart/StatusBarWithIndicator.dart'; class SkinPercentWidget extends StatefulWidget { - SkinPercentWidget({super.key}); + var sleepReport; + SkinPercentWidget({super.key, required this.sleepReport}); @override State createState() => _SkinPercentWidgetState(); @@ -32,7 +33,32 @@ class _SkinPercentWidgetState extends State { } @override - Widget build(BuildContext context) { + Widget build(BuildContext context) { + if (widget.sleepReport == null || + widget.sleepReport['sicp'] == null || + widget.sleepReport['sicp'].isEmpty) { + return Container(); + } + int id = 100001; + List data = widget.sleepReport['sicp']; + final target = data.firstWhere( + (element) => element['id'] == id, + orElse: () => null, // 如果没有找到,返回 null(你也可以抛异常或用 {} 替代) + ); + + if (target == null) { + return Container(); + } + List> showLabel = []; + if (target != null && target['type'] is List) { + showLabel = (target['type'] as List).map>((item) { + return { + 'key': item['type'], + 'name': item['name'], + 'color': stringToColor(item['color']), + }; + }).toList(); + } return Container( width: double.infinity, decoration: BoxDecoration( @@ -62,7 +88,7 @@ class _SkinPercentWidgetState extends State { 14.rpx, 0.rpx, 14.rpx, 0), // borderRadius: 0.rpx, // 圆形点击区域 onTap: () { - showTipDialog( + showTipDialog( context, Container( child: Text( @@ -97,13 +123,8 @@ class _SkinPercentWidgetState extends State { padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 0.rpx, 30.rpx, 0.rpx), child: StatusBarWithIndicator( - selectKey: 2, - showLabel: [ - {"key": 1, "name": "正常", "color": Color(0xFF4CAF50)}, - {"key": 2, "name": "一般", "color": Color(0xFF8BC34A)}, - {"key": 3, "name": "注意", "color": Color(0xFFFFC107)}, - {"key": 4, "name": "警告", "color": Color(0xFFF44336)}, - ], + selectKey: target['value'], + showLabel: showLabel, ), ), SizedBox( diff --git a/lib/pages/sleep_report/component/SleepCard.dart b/lib/pages/sleep_report/component/SleepCard.dart new file mode 100644 index 0000000..42ee152 --- /dev/null +++ b/lib/pages/sleep_report/component/SleepCard.dart @@ -0,0 +1,69 @@ +import 'package:flutter/material.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/home_page/SleepDataModuleWidget.dart'; + +class SleepCard extends StatefulWidget { + var sleepReport; + SleepCard({super.key, required this.sleepReport}); + + @override + State createState() => _SleepCardState(); +} + +class _SleepCardState 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) { + if (widget.sleepReport == null || + widget.sleepReport is! Map || + widget.sleepReport.isEmpty) { + return Container(); + } + + List data = widget.sleepReport['bs'] ?? []; + + 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: Wrap( + spacing: 23.rpx, // 横向间距(左右间距如需加可设置) + runSpacing: 25.rpx, // 每行之间的垂直间距 + children: List.generate(data.length, (index) { + final item = data[index]; + return SizedBox( + width: (MediaQuery.of(context).size.width - 160.rpx) / 3, + child: SleepDataModuleWidget(data: item), + // child: Container( + // width: 20, + // height: 20, + // color: Colors.red, + // ), + ); + }), + ), + ), + ); + } +} diff --git a/lib/pages/sleep_report/component/SleepScoreWidget.dart b/lib/pages/sleep_report/component/SleepScoreWidget.dart index d12c1c8..fb33a0b 100644 --- a/lib/pages/sleep_report/component/SleepScoreWidget.dart +++ b/lib/pages/sleep_report/component/SleepScoreWidget.dart @@ -1,3 +1,4 @@ +import 'package:ef/ef.dart'; import 'package:flutter/material.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:vbvs_app/common/color/appConstants.dart'; @@ -6,7 +7,8 @@ import 'package:vbvs_app/common/util/MyUtils.dart'; import 'package:vbvs_app/pages/sleep_report/chart/SegmentedCirclePainter.dart'; class SleepScoreWidget extends StatefulWidget { - const SleepScoreWidget({super.key}); + var sleepReport; + SleepScoreWidget({super.key, required this.sleepReport}); @override State createState() => _SleepScoreWidgetState(); @@ -30,12 +32,21 @@ class _SleepScoreWidgetState extends State { @override Widget build(BuildContext context) { - List> showLabel = [ - {"level": 1, "name": "优秀(≥85)", "color": Color(0xFF4CAF50)}, - {"level": 2, "name": "良好(75~84)", "color": Color(0xFF8BC34A)}, - {"level": 3, "name": "合格(60~74)", "color": Color(0xFFFFC107)}, - {"level": 4, "name": "注意(<60)", "color": Color(0xFFF44336)}, - ]; + if (widget.sleepReport == null || + widget.sleepReport is! Map || + widget.sleepReport.isEmpty) { + return Container(); + } + // List> showLabel = [ + // {"level": 1, "name": "优秀(≥85)", "color": Color(0xFF4CAF50)}, + // {"level": 2, "name": "良好(75~84)", "color": Color(0xFF8BC34A)}, + // {"level": 3, "name": "合格(60~74)", "color": Color(0xFFFFC107)}, + // {"level": 4, "name": "注意(<60)", "color": Color(0xFFF44336)}, + // ]; + List showLabel = widget.sleepReport['score']['type']; + List stages = widget.sleepReport['score']['stages']; + List segments = parseSegments(widget.sleepReport); + return Container( width: double.infinity, // decoration: BoxDecoration(color: Colors.green), @@ -57,7 +68,7 @@ class _SleepScoreWidgetState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - '30天平均分', + '30天平均分'.tr, style: TextStyle( color: Color(0xFFD3D3D3), fontSize: 26.rpx, @@ -65,7 +76,7 @@ class _SleepScoreWidgetState extends State { ), ), Text( - '79', + '${widget.sleepReport['score']?['avg']}', style: TextStyle( color: Colors.white, fontSize: 48.rpx, @@ -79,12 +90,13 @@ class _SleepScoreWidgetState extends State { child: Container( decoration: BoxDecoration(), 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: [ + // 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: 8, gapAngle: 8, centerWidget: Container( @@ -92,14 +104,14 @@ class _SleepScoreWidgetState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - "睡眠评分", + "睡眠评分".tr, style: TextStyle( color: stringToColor("#FFFFFF"), fontSize: AppConstants().normal_text_fontSize), ), Text( - "65", + '${widget.sleepReport['score']?['score']}', style: TextStyle( color: stringToColor("#FF9F66"), fontSize: 100.rpx), @@ -107,6 +119,7 @@ class _SleepScoreWidgetState extends State { ], ), ), + trend: widget.sleepReport['score']?['trend'], ), ), ), @@ -117,7 +130,7 @@ class _SleepScoreWidgetState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - '睡眠等级', + '睡眠等级'.tr, style: TextStyle( color: Color(0xFFD3D3D3), fontSize: 26.rpx, @@ -125,7 +138,7 @@ class _SleepScoreWidgetState extends State { ), ), Text( - '合格', + '${getSleepLevel(widget.sleepReport)}', style: TextStyle( color: stringToColor("#FF9F66"), fontSize: 48.rpx, @@ -153,13 +166,13 @@ class _SleepScoreWidgetState extends State { width: 20.rpx, height: 20.rpx, decoration: BoxDecoration( - color: item["color"], + color: stringToColor("${item["color"]}"), borderRadius: BorderRadius.circular(10.rpx), ), ), SizedBox(width: 17.rpx), Text( - item["name"], + item["name"] + '(' + item['range'] + ")", style: TextStyle( color: Colors.white, fontSize: 24.rpx, @@ -175,4 +188,54 @@ class _SleepScoreWidgetState extends State { ), ); } + + //获取睡眠等级 + String getSleepLevel(sleepReport) { + if (sleepReport == null || + sleepReport['score'] == null || + sleepReport['score']['level'] == null || + sleepReport['score']['type'] == null) { + return "未知数据".tr; + } + + int level = sleepReport['score']['level']; + List typeList = sleepReport['score']['type']; + + // 在 typeList 中查找 level 匹配的项 + var matched = typeList.firstWhere( + (item) => item['level'] == level, + orElse: () => null, + ); + + if (matched != null && matched['name'] != null) { + return matched['name']; + } + return "未知数据".tr; + } + + List parseSegments(Map sleepReport) { + try { + List stages = sleepReport['score']['stages'] ?? []; + List typeList = sleepReport['sleepData']['type'] ?? []; + + Map typeColorMap = { + for (var item in typeList) + if (item['type'] != null && item['color'] != null) + item['type']: stringToColor(item['color']) + }; + + // 构造 segments 列表 + List segments = stages.map((stage) { + int type = stage['type']; + double value = (stage['per'] ?? 0).toDouble(); + Color color = typeColorMap[type] ?? Colors.grey; // fallback 灰色 + + return SegmentData(color: color, value: value); + }).toList(); + + return segments; + } catch (e) { + return []; + } + } } diff --git a/lib/pages/sleep_report/component/SleepView.dart b/lib/pages/sleep_report/component/SleepView.dart new file mode 100644 index 0000000..0e80ad9 --- /dev/null +++ b/lib/pages/sleep_report/component/SleepView.dart @@ -0,0 +1,388 @@ +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'; + +//睡眠规律性 +class SleepViewWidget extends StatefulWidget { + var sleepReport; + SleepViewWidget({super.key, required this.sleepReport}); + + @override + State createState() => _SleepViewWidgetState(); +} + +class _SleepViewWidgetState 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) { + if (widget.sleepReport == null || + widget.sleepReport is! Map || + widget.sleepReport.isEmpty) { + return Container(); + } + + // List showLabel = widget.sleepReport['sleepData']['type']; + List showLabel = widget.sleepReport['sleepData']['type'] + .where((item) => item['show'] != false) + .toList(); + + // final snoreValues = generateSnoreValues( + // widget.sleepReport['startTime'], + // widget.sleepReport['endTime'], + // ); + List snoreValues = widget.sleepReport['ssp']; + Map time = MyUtils.diffHoursMinutesMap( + widget.sleepReport['startTime'], widget.sleepReport['endTime']); + int hour = time['hours']; + int minutes = time['minutes']; + // List stages = [ + // {"et": 1748011592746, "st": 1748010870326, "type": 1}, + // {"et": 1748013519534, "st": 1748011593746, "type": 2}, + // {"et": 1748013639534, "st": 1748013520534, "type": 1}, + // {"et": 1748014183143, "st": 1748013640534, "type": 2}, + // {"et": 1748014363143, "st": 1748014184143, "type": 1}, + // {"et": 1748014423143, "st": 1748014364143, "type": 2}, + // {"et": 1748014541143, "st": 1748014424143, "type": 1}, + // {"et": 1748015439477, "st": 1748014542143, "type": 0}, + // {"et": 1748018470415, "st": 1748015440477, "type": 1}, + // {"et": 1748019755726, "st": 1748018471415, "type": 2}, + // {"et": 1748020123225, "st": 1748019756726, "type": 1}, + // {"et": 1748021138809, "st": 1748020124225, "type": 2}, + // {"et": 1748021535809, "st": 1748021139809, "type": 1}, + // {"et": 1748021776809, "st": 1748021536809, "type": 2}, + // {"et": 1748022140117, "st": 1748021777809, "type": 1}, + // {"et": 1748022320117, "st": 1748022141117, "type": 2}, + // {"et": 1748022996627, "st": 1748022321117, "type": 1}, + // {"et": 1748023439627, "st": 1748022997627, "type": 2}, + // {"et": 1748023812173, "st": 1748023440627, "type": 1}, + // {"et": 1748023895173, "st": 1748023813173, "type": 2}, + // {"et": 1748024692483, "st": 1748023896173, "type": 1}, + // {"et": 1748024960483, "st": 1748024693483, "type": 2}, + // {"et": 1748025678983, "st": 1748024961483, "type": 1}, + // {"et": 1748026351585, "st": 1748025679983, "type": 2}, + // {"et": 1748027131585, "st": 1748026352585, "type": 1}, + // {"et": 1748027209585, "st": 1748027132585, "type": 2}, + // {"et": 1748027487864, "st": 1748027210585, "type": 1}, + // {"et": 1748027967864, "st": 1748027488864, "type": 3}, + // {"et": 1748028182371, "st": 1748027968864, "type": 1}, + // {"et": 1748028372371, "st": 1748028183371, "type": 2}, + // {"et": 1748029109981, "st": 1748028373371, "type": 1}, + // {"et": 1748029958223, "st": 1748029110981, "type": 2}, + // {"et": 1748030792223, "st": 1748029959223, "type": 1}, + // {"et": 1748030874723, "st": 1748030793223, "type": 2}, + // {"et": 1748032042305, "st": 1748030875723, "type": 1}, + // {"et": 1748032170305, "st": 1748032043305, "type": 2}, + // {"et": 1748033387611, "st": 1748032171305, "type": 1}, + // {"et": 1748033967118, "st": 1748033388611, "type": 2}, + // {"et": 1748034087118, "st": 1748033968118, "type": 1}, + // {"et": 1748034147118, "st": 1748034088118, "type": 2}, + // {"et": 1748034327118, "st": 1748034148118, "type": 1}, + // {"et": 1748034930672, "st": 1748034328118, "type": 2}, + // {"et": 1748035230672, "st": 1748034931672, "type": 1}, + // {"et": 1748035353974, "st": 1748035231672, "type": 2}, + // {"et": 1748036710471, "st": 1748035354974, "type": 1}, + // {"et": 1748037126471, "st": 1748036711471, "type": 2}, + // {"et": 1748037310051, "st": 1748037127471, "type": 1}, + // {"et": 1748037380051, "st": 1748037311051, "type": 2}, + // {"et": 1748038881358, "st": 1748037381051, "type": 1}, + // {"et": 1748038962867, "st": 1748038882358, "type": 0}, + // {"et": 1748039291867, "st": 1748038963867, "type": 1}, + // {"et": 1748039684867, "st": 1748039292867, "type": 0} + // ]; + 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( + "睡眠规律性介绍。", + style: TextStyle( + fontSize: 26.rpx, + color: themeController.currentColor.sc3, + ), + ), + ), + ); + }, + 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, + ), + ), + ), + ], + ), + ), + SizedBox( + height: 83.rpx, + ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(30.rpx, 0.rpx, 30.rpx, 0.rpx), + child: Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + // 左侧 - 入睡时间 + Column( + mainAxisSize: MainAxisSize.min, + children: [ + Image.asset( + "assets/img/moon.png", + width: 43.rpx, + height: 43.rpx, + fit: BoxFit.cover, + ), + SizedBox(height: 33.rpx), + Container( + height: 40.rpx, + child: Text( + "${MyUtils.formatToHHmm(widget.sleepReport['startTime'])}", + style: TextStyle( + color: themeController.currentColor.sc3, + fontSize: AppConstants().normal_text_fontSize, + ), + ), + ), + SizedBox(height: 20.rpx), + Text( + "入睡时间".tr, + style: TextStyle( + color: themeController.currentColor.sc4, + fontSize: AppConstants().normal_text_fontSize, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ], + ), + + // 中间 - 渐变线 + 时间 + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + height: 43.rpx, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + GradientLine( + height: 1, + color: Colors.white60, + ), + ], + )), + SizedBox(height: 33.rpx), // 与图标对齐 + Text.rich( + TextSpan( + children: [ + TextSpan( + text: "$hour", + style: TextStyle( + color: themeController + .currentColor.sc2, // 小时数字颜色 + fontSize: 36.rpx, // 小时数字字号 + ), + ), + TextSpan( + text: "小时", + style: TextStyle( + color: themeController.currentColor.sc3, + fontSize: AppConstants().title_text_fontSize, + ), + ), + TextSpan( + text: "$minutes", + style: TextStyle( + color: themeController + .currentColor.sc2, // 小时数字颜色 + fontSize: 36.rpx, // 分钟数字字号 + ), + ), + TextSpan( + text: "分钟", + style: TextStyle( + color: themeController.currentColor.sc3, + fontSize: AppConstants().title_text_fontSize, + ), + ), + ], + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + SizedBox(height: 20.rpx), + Text( + "睡眠时长".tr, + style: TextStyle( + color: themeController.currentColor.sc4, + fontSize: AppConstants().normal_text_fontSize, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + + // 右侧 - 起床时间图标 + Column( + mainAxisSize: MainAxisSize.min, + children: [ + Image.asset( + "assets/img/sun.png", + width: 43.rpx, + height: 43.rpx, + fit: BoxFit.cover, + ), + SizedBox(height: 33.rpx), + Container( + height: 40.rpx, + child: Text( + "${MyUtils.formatToHHmm(widget.sleepReport['endTime'])}", + style: TextStyle( + color: themeController.currentColor.sc3, + fontSize: AppConstants().normal_text_fontSize, + ), + ), + ), + SizedBox(height: 20.rpx), + Text( + "起床时间".tr, + style: TextStyle( + color: themeController.currentColor.sc4, + fontSize: AppConstants().normal_text_fontSize, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ], + ), + ], + ), + ), + 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(), + ), + ], + ), + ), + ); + } + + List> generateSnoreValues(int startTime, int endTime) { + final int count = 100; + final int interval = ((endTime - startTime) / count).floor(); + + return List.generate(count, (index) { + final timestamp = startTime + interval * index; + final value = + (2 + 1 * (index % 7) / 6.0) * (index % 2 == 0 ? 1 : -1); // 示例上下对称波动 + return { + "timestamp": timestamp, + "value": double.parse(value.toStringAsFixed(2)), + }; + }); + } +} diff --git a/lib/pages/sleep_report/component/SnoreViewWidget.dart b/lib/pages/sleep_report/component/SnoreViewWidget.dart index bdc277e..7fa8234 100644 --- a/lib/pages/sleep_report/component/SnoreViewWidget.dart +++ b/lib/pages/sleep_report/component/SnoreViewWidget.dart @@ -9,7 +9,8 @@ import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart'; import 'package:vbvs_app/pages/sleep_report/chart/LineChartByRange.dart'; class SnoreViewWidgetWidget extends StatefulWidget { - SnoreViewWidgetWidget({super.key}); + var sleepReport; + SnoreViewWidgetWidget({super.key, required this.sleepReport}); @override State createState() => _SnoreViewWidgetWidgetState(); @@ -33,17 +34,17 @@ class _SnoreViewWidgetWidgetState extends State { @override Widget build(BuildContext context) { - var showLabel = [ - {"startTime": 1744644751000, "endTime": 1744648351000, "times": 25}, - {"startTime": 1744650031000, "endTime": 1744653631000, "times": 27}, - {"startTime": 1744655011000, "endTime": 1744662211000, "times": 60}, - // {"startTime": 1744657291000, "endTime": 1744657411000, "times": 28}, - // {"startTime": 1744661011000, "endTime": 1744661131000, "times": 15}, - // {"startTime": 1744668331000, "endTime": 1744668511000, "times": 48}, - // {"startTime": 1744673431000, "endTime": 1744673551000, "times": 25}, - ]; - var startTime = 1744641151000; - var endTime = 1744677151000; + if (widget.sleepReport == null || + widget.sleepReport is! Map || + widget.sleepReport.isEmpty) { + return Container(); + } + + List> data = + (widget.sleepReport['ssp'] as List).cast>(); + List> showLabel = convertToShowLabel(data); + var startTime = widget.sleepReport['startTime']; + var endTime = widget.sleepReport['endTime']; return Container( width: double.infinity, decoration: BoxDecoration( @@ -121,4 +122,45 @@ class _SnoreViewWidgetWidgetState extends State { ), ); } + + List> convertToShowLabel( + List> data) { + if (data.isEmpty) return []; + + data.sort((a, b) => a['st'].compareTo(b['st'])); // 确保时间有序 + + List> result = []; + + int startTime = data[0]['st']; + int endTime = data[0]['st']; + int currentValue = data[0]['value']; + + for (int i = 1; i < data.length; i++) { + final item = data[i]; + final st = item['st']; + final value = item['value']; + + if (value == currentValue) { + endTime = st; + } else { + result.add({ + "startTime": startTime, + "endTime": endTime, + "times": currentValue, + }); + startTime = st; + endTime = st; + currentValue = value; + } + } + + // 添加最后一段 + result.add({ + "startTime": startTime, + "endTime": endTime, + "times": currentValue, + }); + + return result; + } } diff --git a/lib/pages/sleep_report/component/ZiZhuShenJingPercentWidget.dart b/lib/pages/sleep_report/component/ZiZhuShenJingPercentWidget.dart index f09a8e4..e264949 100644 --- a/lib/pages/sleep_report/component/ZiZhuShenJingPercentWidget.dart +++ b/lib/pages/sleep_report/component/ZiZhuShenJingPercentWidget.dart @@ -9,7 +9,8 @@ import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart'; import 'package:vbvs_app/pages/sleep_report/chart/StatusBarWithIndicator.dart'; class ZiZhuShenJingPercentWidget extends StatefulWidget { - ZiZhuShenJingPercentWidget({super.key}); + var sleepReport; + ZiZhuShenJingPercentWidget({super.key, required this.sleepReport}); @override State createState() => @@ -35,6 +36,32 @@ class _ZiZhuShenJingPercentWidgetState @override Widget build(BuildContext context) { + if (widget.sleepReport == null || + widget.sleepReport['sicp'] == null || + widget.sleepReport['sicp'].isEmpty) { + return Container(); + } + int id = 100000; + List data = widget.sleepReport['sicp']; + final target = data.firstWhere( + (element) => element['id'] == id, + orElse: () => null, // 如果没有找到,返回 null(你也可以抛异常或用 {} 替代) + ); + + if (target == null) { + return Container(); + } + List> showLabel = []; + if (target != null && target['type'] is List) { + showLabel = (target['type'] as List).map>((item) { + return { + 'key': item['type'], + 'name': item['name'], + 'color': stringToColor(item['color']), + }; + }).toList(); + } + return Container( width: double.infinity, decoration: BoxDecoration( @@ -99,13 +126,8 @@ class _ZiZhuShenJingPercentWidgetState padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 0.rpx, 30.rpx, 0.rpx), child: StatusBarWithIndicator( - selectKey: 3, - showLabel: [ - {"key": 1, "name": "正常", "color": Color(0xFF4CAF50)}, - {"key": 2, "name": "一般", "color": Color(0xFF8BC34A)}, - {"key": 3, "name": "注意", "color": Color(0xFFFFC107)}, - {"key": 4, "name": "警告", "color": Color(0xFFF44336)}, - ], + selectKey: target['value'], + showLabel: showLabel, ), ), SizedBox( diff --git a/lib/pages/sleep_report/new_sleep_report_page.dart b/lib/pages/sleep_report/new_sleep_report_page.dart index 3a6df3d..f921f70 100644 --- a/lib/pages/sleep_report/new_sleep_report_page.dart +++ b/lib/pages/sleep_report/new_sleep_report_page.dart @@ -5,27 +5,32 @@ import 'package:flutterflow_ui/flutterflow_ui.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/common/util/requestWithLog.dart'; import 'package:vbvs_app/component/tool/ClickableContainer.dart'; import 'package:vbvs_app/controller/date/CalendarController.dart'; import 'package:vbvs_app/controller/sleep/sleep_report_controller.dart'; import 'package:vbvs_app/pages/common/selectDialog.dart'; import 'package:vbvs_app/pages/sleep_report/component/AIAdviceWidget.dart'; -import 'package:vbvs_app/pages/sleep_report/component/BreathPauseWidget.dart'; +import 'package:vbvs_app/pages/sleep_report/component/BreatheCard.dart'; +import 'package:vbvs_app/pages/sleep_report/component/BreathePauseNewWidget.dart'; import 'package:vbvs_app/pages/sleep_report/component/BreatheStandardWidget.dart'; import 'package:vbvs_app/pages/sleep_report/component/CompareSleepWidget.dart'; import 'package:vbvs_app/pages/sleep_report/component/DiseasePercentsWidget.dart'; import 'package:vbvs_app/pages/sleep_report/component/HeartChangeWidget.dart'; import 'package:vbvs_app/pages/sleep_report/component/HeartHealthWidget.dart'; import 'package:vbvs_app/pages/sleep_report/component/HeartPointWidget.dart'; +import 'package:vbvs_app/pages/sleep_report/component/HeartRateCard.dart'; import 'package:vbvs_app/pages/sleep_report/component/HeartRateStandardWidget.dart'; import 'package:vbvs_app/pages/sleep_report/component/SkinPercentWidget.dart'; +import 'package:vbvs_app/pages/sleep_report/component/SleepCard.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/SnoreViewWidget.dart'; import 'package:vbvs_app/pages/sleep_report/component/ZiZhuShenJingPercentWidget.dart'; class NewSleepReportPage extends StatefulWidget { - var date; - NewSleepReportPage({super.key, required this.date}); + var data; + NewSleepReportPage({super.key, required this.data}); @override State createState() => _NewSleepReportPageState(); @@ -37,14 +42,32 @@ class _NewSleepReportPageState extends State { @override void initState() { - if (widget == null) { - widget.date = DateTime.now(); + if (widget.data['date'] == null) { + widget.data['date'] = DateTime.now(); } calendarController.selectedDate.value = - DateTime.fromMillisecondsSinceEpoch(widget.date); + DateTime.fromMillisecondsSinceEpoch(widget.data['date']); sleepReportController.selectedDate.value = - DateTime.fromMillisecondsSinceEpoch(widget.date); - sleepReportController.model.type = 1; + DateTime.fromMillisecondsSinceEpoch(widget.data['date']); + if (widget.data['type'] != null) { + sleepReportController.model.type = widget.data['type']; + } else { + sleepReportController.model.type = 1; + } + String date = MyUtils.formatToDate(widget.data['date']); + requestWithLog( + logTitle: "查询睡眠报告", + method: MyHttpMethod.get, + queryUrl: + "http://192.168.1.80:9898/api/analysis/sleep/analysis?mac=${widget.data['mac']}&time=${date}&type=${sleepReportController.model.type}", + onSuccess: (res) { + print(res); + sleepReportController.sleepReport.value = res.data; + sleepReportController.updateAll(); + }, + onFailure: (res) { + print(res); + }); super.initState(); } @@ -101,516 +124,574 @@ class _NewSleepReportPageState extends State { body: SafeArea( top: true, child: SingleChildScrollView( - child: Column( - children: [ - Padding( - padding: EdgeInsetsDirectional.fromSTEB(0, 30.rpx, 0, 0), - child: Container( - width: double.infinity, - constraints: BoxConstraints( - minHeight: 90.rpx, + child: Obx(() { + var sleepReport = sleepReportController.sleepReport; + print(sleepReport); + return Column( + children: [ + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 30.rpx, 0, 0), + child: Container( + width: double.infinity, + constraints: BoxConstraints( + minHeight: 90.rpx, + ), + decoration: BoxDecoration( + color: themeController.currentColor.sc5), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 15.rpx, 30.rpx, 15.rpx), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + 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 { + sleepReportController.model.type = + 1; + sleepReportController.updateAll(); + }, + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Container( + width: + 115.rpx, // 固定宽度为 160.rpx + alignment: + Alignment.center, // 文字居中 + child: Text( + '日报'.tr, + style: FlutterFlowTheme.of( + context) + .bodyMedium + .override( + 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 { + // sleepReportController.model.type = + // 2; + // sleepReportController.updateAll(); + // }, + // child: Column( + // mainAxisSize: MainAxisSize.max, + // children: [ + // Container( + // width: 115.rpx, // 固定宽度为 160.rpx + // alignment: + // Alignment.center, // 文字居中 + // child: Text( + // '周报'.tr, + // style: FlutterFlowTheme.of( + // context) + // .bodyMedium + // .override( + // 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 { + // sleepReportController.model.type = + // 3; + // sleepReportController.updateAll(); + // }, + // child: Column( + // mainAxisSize: MainAxisSize.max, + // children: [ + // Container( + // width: 115.rpx, // 固定宽度为 160.rpx + // alignment: + // Alignment.center, // 文字居中 + // child: Text( + // '月报'.tr, + // style: FlutterFlowTheme.of( + // context) + // .bodyMedium + // .override( + // 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), + // ], + // ), + // ); + // }), + ], + ), + Obx(() { + double lineWidth = 115.rpx; + return AnimatedPositioned( + duration: Duration(milliseconds: 300), + curve: Curves.easeInOut, + bottom: 0, + left: + sleepReportController.model.type == + 1 + ? 0 + : sleepReportController + .model.type == + 2 + ? 115.rpx + : 230.rpx, + child: Container( + width: lineWidth, + height: 4.rpx, + decoration: BoxDecoration( + color: themeController + .currentColor.sc2, + borderRadius: + BorderRadius.circular(2.rpx), + ), + ), + ); + }), + ], + ), + // Padding( + // padding: EdgeInsetsDirectional.fromSTEB( + // 0, 0.rpx, 0.rpx, 0), + // child: Container( + // width: 28.rpx, + // height: 28.rpx, + // // width: double.infinity, + // decoration: BoxDecoration(), + // child: SvgPicture.asset( + // 'assets/img/icon/share.svg', + // fit: BoxFit.cover, + // color: themeController.currentColor.sc3, + // ), + // ), + // ), + ], + ), + ), ), - decoration: BoxDecoration( - color: themeController.currentColor.sc5), + ), + Container( + width: double.infinity, child: Padding( padding: EdgeInsetsDirectional.fromSTEB( - 30.rpx, 15.rpx, 30.rpx, 15.rpx), + 30.rpx, 32.rpx, 30.rpx, 32.rpx), + child: Obx(() { + var date = sleepReportController.selectedDate; + return getTimeWidget(); + }), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 0.rpx, 30.rpx, 58.rpx), + child: ClickableContainer( + backgroundColor: themeController.currentColor.sc5, + highlightColor: + themeController.currentColor.sc5, // 或你希望的点击水波纹颜色 + borderRadius: AppConstants() + .normal_container_radius, // 如果你想加圆角可以设置 eg. 12.rpx + padding: EdgeInsets.zero, + onTap: () {}, child: Row( mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - 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 { - sleepReportController.model.type = - 1; - sleepReportController.updateAll(); - }, - child: Column( - mainAxisSize: MainAxisSize.max, - children: [ - Container( - width: 115.rpx, // 固定宽度为 160.rpx - alignment: - Alignment.center, // 文字居中 - child: Text( - '日报'.tr, - style: FlutterFlowTheme.of( - context) + Expanded( + flex: 1, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Column( + crossAxisAlignment: + CrossAxisAlignment.end, + children: [ + Text( + '实时体征.姓名'.tr, + style: + FlutterFlowTheme.of(context) .bodyMedium .override( fontFamily: 'Inter', - fontSize: AppConstants() - .title_text_fontSize, + fontSize: 26.rpx, letterSpacing: 0.0, - color: - sleepReportController - .model - .type == - 1 - ? themeController - .currentColor - .sc2 - : themeController - .currentColor - .sc3, + color: themeController + .currentColor.sc4, ), - ), - ), - SizedBox(height: 10.rpx), - ], - ), - ); - }), - Obx(() { - return ClickableContainer( - backgroundColor: Colors.transparent, - highlightColor: - themeController.currentColor.sc3, - borderRadius: 8.rpx, - padding: EdgeInsets.all(0), - onTap: () async { - sleepReportController.model.type = - 2; - sleepReportController.updateAll(); - }, - child: Column( - mainAxisSize: MainAxisSize.max, - children: [ - Container( - width: 115.rpx, // 固定宽度为 160.rpx - alignment: - Alignment.center, // 文字居中 - child: Text( - '周报'.tr, - style: FlutterFlowTheme.of( - context) + ), + Text( + '实时体征.年龄'.tr, + style: + FlutterFlowTheme.of(context) .bodyMedium .override( fontFamily: 'Inter', - fontSize: AppConstants() - .title_text_fontSize, + fontSize: 26.rpx, letterSpacing: 0.0, - color: - sleepReportController - .model - .type == - 2 - ? themeController - .currentColor - .sc2 - : themeController - .currentColor - .sc3, + color: themeController + .currentColor.sc4, ), - ), - ), - SizedBox(height: 10.rpx), - ], - ), - ); - }), - Obx(() { - return ClickableContainer( - backgroundColor: Colors.transparent, - highlightColor: - themeController.currentColor.sc3, - borderRadius: 8.rpx, - padding: EdgeInsets.all(0), - onTap: () async { - sleepReportController.model.type = - 3; - sleepReportController.updateAll(); - }, - child: Column( - mainAxisSize: MainAxisSize.max, - children: [ - Container( - width: 115.rpx, // 固定宽度为 160.rpx - alignment: - Alignment.center, // 文字居中 - child: Text( - '月报'.tr, - style: FlutterFlowTheme.of( - context) - .bodyMedium - .override( - 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), - ], - ), - ); - }), - ], - ), - Obx(() { - double lineWidth = 115.rpx; - return AnimatedPositioned( - duration: Duration(milliseconds: 300), - curve: Curves.easeInOut, - bottom: 0, - left: sleepReportController.model.type == - 1 - ? 0 - : sleepReportController.model.type == - 2 - ? 115.rpx - : 230.rpx, - child: Container( - width: lineWidth, - height: 4.rpx, - decoration: BoxDecoration( - color: - themeController.currentColor.sc2, - borderRadius: - BorderRadius.circular(2.rpx), + ), + ].divide(SizedBox(height: 34.rpx)), ), - ), - ); - }), - ], + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + '传感器1号', + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc3, + ), + ), + Text( + '2022-02-02', + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc3, + ), + ), + ].divide(SizedBox(height: 34.rpx)), + ), + ] + .divide(SizedBox(width: 33.rpx)) + .addToStart(SizedBox(width: 37.rpx)), + ), + ] + .addToStart(SizedBox(height: 36.rpx)) + .addToEnd(SizedBox(height: 36.rpx)), + ), ), - Padding( - padding: EdgeInsetsDirectional.fromSTEB( - 0, 0.rpx, 0.rpx, 0), - child: Container( - width: 28.rpx, - height: 28.rpx, - // width: double.infinity, - decoration: BoxDecoration(), - child: SvgPicture.asset( - 'assets/img/icon/share.svg', - fit: BoxFit.cover, - color: themeController.currentColor.sc3, - ), + Expanded( + flex: 1, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Column( + crossAxisAlignment: + CrossAxisAlignment.end, + children: [ + Text( + '实时体征.设备ID'.tr, + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc4, + ), + ), + Text( + '实时体征.体重'.tr, + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc4, + ), + ), + ].divide(SizedBox(height: 34.rpx)), + ), + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + '1231212', + // "D11250300003", + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc3, + ), + ), + Text( + '55kg', + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc3, + ), + ), + ].divide(SizedBox(height: 34.rpx)), + ), + ] + .divide(SizedBox(width: 33.rpx)) + .addToStart(SizedBox(width: 37.rpx)), + ), + ] + .addToStart(SizedBox(height: 36.rpx)) + .addToEnd(SizedBox(height: 36.rpx)), ), ), ], ), ), ), - ), - Container( - width: double.infinity, - child: Padding( + Padding( padding: EdgeInsetsDirectional.fromSTEB( - 30.rpx, 32.rpx, 30.rpx, 32.rpx), - child: Obx(() { - var date = sleepReportController.selectedDate; - return getTimeWidget(); - }), - ), - ), - Padding( - padding: EdgeInsetsDirectional.fromSTEB( - 30.rpx, 0.rpx, 30.rpx, 58.rpx), - child: ClickableContainer( - backgroundColor: themeController.currentColor.sc5, - highlightColor: - themeController.currentColor.sc5, // 或你希望的点击水波纹颜色 - borderRadius: AppConstants() - .normal_container_radius, // 如果你想加圆角可以设置 eg. 12.rpx - padding: EdgeInsets.zero, - onTap: () { - print('点击了体征卡片'); - }, - child: Row( - mainAxisSize: MainAxisSize.max, - children: [ - Expanded( - flex: 1, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Column( - crossAxisAlignment: - CrossAxisAlignment.end, - children: [ - Text( - '实时体征.姓名'.tr, - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: 'Inter', - fontSize: 26.rpx, - letterSpacing: 0.0, - color: themeController - .currentColor.sc4, - ), - ), - Text( - '实时体征.年龄'.tr, - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: 'Inter', - fontSize: 26.rpx, - letterSpacing: 0.0, - color: themeController - .currentColor.sc4, - ), - ), - ].divide(SizedBox(height: 34.rpx)), - ), - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - '传感器1号', - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: 'Inter', - fontSize: 26.rpx, - letterSpacing: 0.0, - color: themeController - .currentColor.sc3, - ), - ), - Text( - '2022-02-02', - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: 'Inter', - fontSize: 26.rpx, - letterSpacing: 0.0, - color: themeController - .currentColor.sc3, - ), - ), - ].divide(SizedBox(height: 34.rpx)), - ), - ] - .divide(SizedBox(width: 33.rpx)) - .addToStart(SizedBox(width: 37.rpx)), - ), - ] - .addToStart(SizedBox(height: 36.rpx)) - .addToEnd(SizedBox(height: 36.rpx)), - ), - ), - Expanded( - flex: 1, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Column( - crossAxisAlignment: - CrossAxisAlignment.end, - children: [ - Text( - '实时体征.设备ID'.tr, - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: 'Inter', - fontSize: 26.rpx, - letterSpacing: 0.0, - color: themeController - .currentColor.sc4, - ), - ), - Text( - '实时体征.体重'.tr, - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: 'Inter', - fontSize: 26.rpx, - letterSpacing: 0.0, - color: themeController - .currentColor.sc4, - ), - ), - ].divide(SizedBox(height: 34.rpx)), - ), - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - '1231212', - // "D11250300003", - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: 'Inter', - fontSize: 26.rpx, - letterSpacing: 0.0, - color: themeController - .currentColor.sc3, - ), - ), - Text( - '55kg', - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: 'Inter', - fontSize: 26.rpx, - letterSpacing: 0.0, - color: themeController - .currentColor.sc3, - ), - ), - ].divide(SizedBox(height: 34.rpx)), - ), - ] - .divide(SizedBox(width: 33.rpx)) - .addToStart(SizedBox(width: 37.rpx)), - ), - ] - .addToStart(SizedBox(height: 36.rpx)) - .addToEnd(SizedBox(height: 36.rpx)), - ), - ), - ], + 30.rpx, 0.rpx, 30.rpx, 0), + child: Container( + width: double.infinity, + child: + SleepScoreWidget(sleepReport: sleepReport.value), ), ), - ), - Padding( - padding: EdgeInsetsDirectional.fromSTEB( - 30.rpx, 0.rpx, 30.rpx, 0), - child: Container( - width: double.infinity, - child: SleepScoreWidget(), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 0.rpx, 30.rpx, 0), + child: Container( + width: double.infinity, + child: SleepViewWidget( + sleepReport: sleepReport, + ), + ), ), - ), - Padding( - padding: EdgeInsetsDirectional.fromSTEB( - 30.rpx, 0.rpx, 30.rpx, 0), - child: Container( - width: double.infinity, - child: CompareSleepWidget(), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 0.rpx, 30.rpx, 0), + child: Container( + width: double.infinity, + child: SleepCard( + sleepReport: sleepReport, + ), + ), ), - ), - Padding( - padding: EdgeInsetsDirectional.fromSTEB( - 30.rpx, 0.rpx, 30.rpx, 0), - child: Container( - width: double.infinity, - child: HeartPointWidget(), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 0.rpx, 30.rpx, 0), + child: Container( + width: double.infinity, + child: CompareSleepWidget(sleepReport: sleepReport), + ), ), - ), - Padding( - padding: EdgeInsetsDirectional.fromSTEB( - 30.rpx, 0.rpx, 30.rpx, 0), - child: Container( - // color: stringToColor("#242835"), - width: double.infinity, - child: AIAdviceWidget(), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 0.rpx, 30.rpx, 0), + child: Container( + width: double.infinity, + child: HeartPointWidget( + sleepReport: sleepReport, + ), + ), ), - ), - Padding( - padding: EdgeInsetsDirectional.fromSTEB( - 30.rpx, 0.rpx, 30.rpx, 0), - child: Container( - width: double.infinity, - child: HeartRateStandardWidget(), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 0.rpx, 30.rpx, 0), + child: Container( + width: double.infinity, + child: AIAdviceWidget(sleepReport: sleepReport), + ), ), - ), - Padding( - padding: EdgeInsetsDirectional.fromSTEB( - 30.rpx, 0.rpx, 30.rpx, 0), - child: Container( - width: double.infinity, - child: HeartChangeWidget(), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 0.rpx, 30.rpx, 0), + child: Container( + width: double.infinity, + child: + HeartRateStandardWidget(sleepReport: sleepReport), + ), ), - ), - Padding( - padding: EdgeInsetsDirectional.fromSTEB( - 30.rpx, 0.rpx, 30.rpx, 0), - child: Container( - width: double.infinity, - child: BreatheStandardWidget(), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 0.rpx, 30.rpx, 0), + child: Container( + width: double.infinity, + child: HeartRateCard(sleepReport: sleepReport), + ), ), - ), - Padding( - padding: EdgeInsetsDirectional.fromSTEB( - 30.rpx, 0.rpx, 30.rpx, 0), - child: Container( - width: double.infinity, - child: SnoreViewWidgetWidget(), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 0.rpx, 30.rpx, 0), + child: Container( + width: double.infinity, + child: HeartChangeWidget(sleepReport: sleepReport), + ), ), - ), - Padding( - padding: EdgeInsetsDirectional.fromSTEB( - 30.rpx, 0.rpx, 30.rpx, 0), - child: Container( - width: double.infinity, - child: BreathPauseWidget(), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 0.rpx, 30.rpx, 0), + child: Container( + width: double.infinity, + child: + BreatheStandardWidget(sleepReport: sleepReport), + ), ), - ), - Padding( - padding: EdgeInsetsDirectional.fromSTEB( - 30.rpx, 0.rpx, 30.rpx, 0), - child: Container( - width: double.infinity, - child: HeartHealthWidget(), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 0.rpx, 30.rpx, 0), + child: Container( + width: double.infinity, + child: BreatheCard(sleepReport: sleepReport), + ), ), - ), - Padding( - padding: EdgeInsetsDirectional.fromSTEB( - 30.rpx, 0.rpx, 30.rpx, 0), - child: Container( - width: double.infinity, - child: DiseasePercentsWidget(), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 0.rpx, 30.rpx, 0), + child: Container( + width: double.infinity, + child: + SnoreViewWidgetWidget(sleepReport: sleepReport), + ), ), - ), - Padding( - padding: EdgeInsetsDirectional.fromSTEB( - 30.rpx, 0.rpx, 30.rpx, 0), - child: Container( - width: double.infinity, - child: SkinPercentWidget(), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 0.rpx, 30.rpx, 0), + child: Container( + width: double.infinity, + child: + BreathePauseNewWidget(sleepReport: sleepReport), + ), ), - ), - Padding( - padding: EdgeInsetsDirectional.fromSTEB( - 30.rpx, 0.rpx, 30.rpx, 0), - child: Container( - width: double.infinity, - child: ZiZhuShenJingPercentWidget(), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 0.rpx, 30.rpx, 0), + child: Container( + width: double.infinity, + child: HeartHealthWidget(sleepReport: sleepReport), + ), ), - ), - ].divide(SizedBox( - height: 25.rpx, - )), - ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 0.rpx, 30.rpx, 0), + child: Container( + width: double.infinity, + child: + DiseasePercentsWidget(sleepReport: sleepReport), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 0.rpx, 30.rpx, 0), + child: Container( + width: double.infinity, + child: SkinPercentWidget(sleepReport: sleepReport), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 0.rpx, 30.rpx, 0), + child: Container( + width: double.infinity, + child: ZiZhuShenJingPercentWidget( + sleepReport: sleepReport), + ), + ), + ].divide(SizedBox( + height: 25.rpx, + )), + ); + }), ), ), ), @@ -733,7 +814,7 @@ class _NewSleepReportPageState extends State { backgroundColor: Colors.transparent, highlightColor: themeController.currentColor.sc3, padding: EdgeInsets.zero, - borderRadius: 0, + borderRadius: 8, onTap: () { showSleepCalendarBottomSheet( type: sleepReportController.model.type, @@ -751,7 +832,7 @@ class _NewSleepReportPageState extends State { width: 28.rpx, height: 28.rpx, child: SvgPicture.asset( - 'assets/img/icon/share.svg', + 'assets/img/icon/calendar.svg', fit: BoxFit.cover, color: themeController.currentColor.sc3, ), diff --git a/lib/pages/user/setting_page.dart b/lib/pages/user/setting_page.dart index cde09a6..680cb6c 100644 --- a/lib/pages/user/setting_page.dart +++ b/lib/pages/user/setting_page.dart @@ -255,6 +255,57 @@ class _SettingPageState extends State { ), ), ), + ClickableContainer( + backgroundColor: Colors.transparent, // 容器背景色 + highlightColor: themeController + .currentColor.sc21, // 点击时的背景色 + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 0.rpx, 0.rpx, 0.rpx), + onTap: () { + print('点击了容器'); + Get.toNamed("/commonMessageSettingPage"); + }, + child: Container( + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 40.rpx, 30.rpx, 40.rpx, 30.rpx), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Text( + '消息通知'.tr, + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + color: themeController + .currentColor.sc3, + fontSize: AppConstants() + .title_text_fontSize, + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox(width: 22.rpx)), + ), + SvgPicture.asset( + 'assets/img/icon/arrow_right.svg', + width: 8.rpx, + height: + 14.rpx, // 如果 SVG 中没有固定颜色,可以这样设置 + color: themeController + .currentColor.sc3, + ), + ], + ), + ), + ), + ), ClickableContainer( backgroundColor: Colors.transparent, // 容器背景色 highlightColor: themeController diff --git a/lib/routers/routers.dart b/lib/routers/routers.dart index 5498cb9..cfc3f7f 100644 --- a/lib/routers/routers.dart +++ b/lib/routers/routers.dart @@ -4,6 +4,7 @@ import 'package:vbvs_app/common/util/MyUtils.dart'; import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; import 'package:vbvs_app/controller/user_info_controller.dart'; import 'package:vbvs_app/pages/device/BodyDeviceWidget.dart'; +import 'package:vbvs_app/pages/device/component/MessageSetting.dart'; import 'package:vbvs_app/pages/device/device_detail.dart'; import 'package:vbvs_app/pages/device/instant_body_page.dart'; import 'package:vbvs_app/pages/device/message_review_page.dart'; @@ -31,6 +32,7 @@ import 'package:vbvs_app/pages/repair/apply_repair_page.dart'; import 'package:vbvs_app/pages/repair/apply_repair_success.dart'; import 'package:vbvs_app/pages/repair/repair_detail_page.dart'; import 'package:vbvs_app/pages/repair/repair_list_page.dart'; +import 'package:vbvs_app/pages/setting/common_mesaage_setting.dart'; import 'package:vbvs_app/pages/setting/language_setting.dart'; import 'package:vbvs_app/pages/sleep_report/new_sleep_report_page.dart'; import 'package:vbvs_app/pages/sleep_report/sleep_report_page.dart'; @@ -48,13 +50,15 @@ var routes = { "/messagePage": (contxt) => MessagePage(), "/minePage": (contxt) => MinePage(), "/mianPageBottomChange": (contxt) => MainPageBottomChange(), - "/loginPage": (contxt) => LoginPage(), + // "/loginPage": (contxt) => LoginPage(), + // "/otherLoginPage": (contxt) => OtherLoginPage(), + "/loginPage": (contxt) => OtherLoginPage(), + // "/otherLoginPage": (contxt) => OtherLoginPage(), "/deviceType": (contxt, {arguments}) => DeviceTypePage(), "/blueteethDevice": (contxt) => BlueteethDevicePage(), "/personPage": (contxt) => PersonPage(), "/bindDeviceSuccess": (contxt) => BindDeviceSuccess(), "/wifiPage": (contxt, {arguments}) => WifiPage(type: arguments), - "/otherLoginPage": (contxt) => OtherLoginPage(), "/updateUserPage": (contxt) => UpdateUserPage(), "/settingPage": (contxt) => SettingPage(), "/aboutUsPage": (contxt) => AboutUsPage(), @@ -77,13 +81,18 @@ var routes = { "/followPage": (contxt) => FollowPage(), "/repairListPage": (contxt) => RepairListPage(), "/newSleepReportPage": (contxt, {arguments}) => - NewSleepReportPage(date: arguments), + NewSleepReportPage(data: arguments), "/userSchemePage": (contxt) => UserSchemePage(), "/privacyPage": (contxt) => PrivacySchemePage(), "/repairDetailPage": (contxt, {arguments}) => RepairDetailPage(data: arguments), "/applyRepairSuccess": (contxt) => ApplyRepairSuccess(), - "/messageReviewPage": (contxt,{arguments}) => MessageReviewPage(data: arguments), + "/messageReviewPage": (contxt, {arguments}) => + MessageReviewPage(data: arguments), + "/messageSettingPage": (contxt, {arguments}) => + MessageSettingPage(data: arguments), + "/commonMessageSettingPage": (contxt) => + CommonMessageSettingPage(), }; var onGenerateRoute = (RouteSettings settings) {