diff --git a/assets/langs/en_US.json b/assets/langs/en_US.json index fbec5d3..08c92a2 100644 --- a/assets/langs/en_US.json +++ b/assets/langs/en_US.json @@ -614,5 +614,14 @@ "详情": "Detail", "更新成功": "Update Success", "原邮箱号": "Original Email", - "请输入邮箱号":"Please enter email" + "请输入邮箱号": "Please enter email", + "中国": "China", + "香港": "Hong Kong", + "选择区号": "Select Country Code", + "输入邮箱号码": " Enter Email", + "通知设置": "Notification Settings", + "注意!关闭后将无法接受任何消息": "Note! After closing, you will not receive any messages", + "手机号登录": "Phone Login", + "邮箱登录": "Email Login", + "输入邮箱": "Enter Email" } \ No newline at end of file diff --git a/assets/langs/zh_CN.json b/assets/langs/zh_CN.json index 3cd8dcc..b51bc25 100644 --- a/assets/langs/zh_CN.json +++ b/assets/langs/zh_CN.json @@ -615,5 +615,16 @@ "原邮箱号": "原邮箱号", "用户拒绝授权": "用户拒绝授权", "用户取消授权": "用户取消授权", - "请输入邮箱号":"请输入邮箱号" + "请输入邮箱号":"请输入邮箱号", + "中国":"中国", + "香港":"香港", + "选择区号":"选择区号", + "输入邮箱号码":"输入邮箱号码", + "通知设置":"通知设置", + "注意!关闭后将无法接受任何消息":"注意!关闭后将无法接受任何消息", + "手机号登录":"手机号登录", + "邮箱登录":"邮箱登录", + "输入邮箱":"输入邮箱" + + } \ No newline at end of file diff --git a/assets/langs/zh_TW.json b/assets/langs/zh_TW.json index 319a383..7b09cbe 100644 --- a/assets/langs/zh_TW.json +++ b/assets/langs/zh_TW.json @@ -612,5 +612,14 @@ "原邮箱号": "原郵箱號", "用户拒绝授权": "用戶拒絕授權", "用户取消授权": "用戶取消授權", - "请输入邮箱号":"請輸入郵箱號" + "请输入邮箱号": "請輸入郵箱號", + "中国": "中國", + "香港": "香港", + "选择区号": "选择區號", + "输入邮箱号码": "輸入郵箱號碼", + "通知设置": "通知設置", + "注意!关闭后将无法接受任何消息": "注意!關閉將無法接受任何消息", + "手机号登录": "手機登入", + "邮箱登录": "電子郵件登入", + "输入邮箱": "輸入電子郵件" } \ No newline at end of file diff --git a/lib/controller/device/body_device_controller.dart b/lib/controller/device/body_device_controller.dart index 7814d76..7285976 100644 --- a/lib/controller/device/body_device_controller.dart +++ b/lib/controller/device/body_device_controller.dart @@ -226,6 +226,11 @@ class BodyDeviceController extends GetControllerEx { if (res.code == HttpStatusCodes.ok) { // bindDeviceNum.value = res.total!; deviceList.value = res.data!; + deviceList.value.sort((a, b) { + final int at = a['create_time'] ?? 0; + final int bt = b['create_time'] ?? 0; + return bt.compareTo(at); + }); updateAll(); return res; } diff --git a/lib/controller/login/login_controller.dart b/lib/controller/login/login_controller.dart index 8a1b935..fc65edb 100644 --- a/lib/controller/login/login_controller.dart +++ b/lib/controller/login/login_controller.dart @@ -164,8 +164,10 @@ class LoginController extends GetControllerEx { queryUrl += "?lang=$language"; } } + UserInfoController userInfoController = Get.find(); var data = { "userName": model.phone, + "ccode":userInfoController.select_country_code.value, }; if (AppConstants().ent_type == APPPackageType.HUANSHUI.code) { data['code'] = "hzhskj"; @@ -232,9 +234,11 @@ class LoginController extends GetControllerEx { queryUrl += "?lang=$language"; } } + UserInfoController userInfoController = Get.find(); var data = { "userName": model.updatePhone, "type": 5, + "ccode":userInfoController.select_country_code.value, }; if (AppConstants().ent_type == APPPackageType.HUANSHUI.code) { data['code'] = "hzhskj"; diff --git a/lib/controller/user_info_controller.dart b/lib/controller/user_info_controller.dart index b9b6523..59dc03a 100644 --- a/lib/controller/user_info_controller.dart +++ b/lib/controller/user_info_controller.dart @@ -74,6 +74,20 @@ class UserInfoController extends GetControllerEx { FluwxCancelable? fluwxCancelable; final Fluwx fluwx = Fluwx(); + List> country_code = [ + { + "name": "中国", + "id": "+86", + }, + { + "name": "香港", + "id": "+852", + }, + ]; + + RxString select_country_code = "+86".obs; + RxInt select_login_method = 1.obs;//1手机 2邮箱 + Future uploadImg() async { EasyDartModule.logger.info("请求上传图片"); DailyLogUtils.writeLog("请求上传图片"); diff --git a/lib/pages/device/BodyDeviceWidget.dart b/lib/pages/device/BodyDeviceWidget.dart index 5ce2e5e..3622eef 100644 --- a/lib/pages/device/BodyDeviceWidget.dart +++ b/lib/pages/device/BodyDeviceWidget.dart @@ -207,6 +207,15 @@ class _BodyDevicePageState extends State { _pageController.animateToPage(index, duration: const Duration(milliseconds: 300), curve: Curves.easeInOut); BodyDeviceController deviceController = Get.find(); + + if (index == 0) { + deviceController.model.type = 1; + homeController.model.type = 1; + } else if (index == 1) { + deviceController.model.type = 2; + homeController.model.type = 2; + } + deviceController.updateAll(); await deviceController.getDeviceList(); await deviceController.getSleepReport(); } diff --git a/lib/pages/device/component/DeviceDataComponentWidget.dart b/lib/pages/device/component/DeviceDataComponentWidget.dart index 49814b6..8bb0827 100644 --- a/lib/pages/device/component/DeviceDataComponentWidget.dart +++ b/lib/pages/device/component/DeviceDataComponentWidget.dart @@ -25,6 +25,7 @@ import 'package:vbvs_app/enum/BindType.dart'; import 'package:vbvs_app/model/api_response.dart'; import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart'; + class DeviceDataComponentWidget extends StatefulWidget { final Map device; diff --git a/lib/pages/login/other_login.dart b/lib/pages/login/other_login.dart index ed8dd02..e8a45d2 100644 --- a/lib/pages/login/other_login.dart +++ b/lib/pages/login/other_login.dart @@ -21,6 +21,8 @@ import 'package:vbvs_app/controller/time/countdown_controller.dart'; import 'package:vbvs_app/controller/user_info_controller.dart'; import 'package:vbvs_app/controller/weather/weather_controller.dart'; import 'package:vbvs_app/model/api_response.dart'; +import 'package:vbvs_app/pages/person/select_time.dart'; +import 'dart:ui' as ui; class OtherLoginPage extends StatefulWidget { const OtherLoginPage({super.key}); @@ -106,7 +108,7 @@ class _OtherLoginPageState extends State { // 登录成功移出网络检查监听 Checknetwork.subscription?.cancel(); if (Get.currentRoute != '/mianPageBottomChange') { - Get.offAndToNamed("/mianPageBottomChange"); + Get.offAndToNamed("/mianPageBottomChange"); } } // TODO 操作全部跳转页面前成功以后移除监听,防止重复监听,其他方式登录成功也需要移出监听 @@ -122,7 +124,6 @@ class _OtherLoginPageState extends State { } } }); - } @override @@ -255,6 +256,22 @@ class _OtherLoginPageState extends State { child: Padding( padding: EdgeInsetsDirectional.fromSTEB( 0, 95.rpx, 0, 0), + child: Container( + width: double.infinity, + height: bodysize.maxHeight * 0.056, + constraints: BoxConstraints( + minHeight: 90.rpx, + ), + // ✅ 在这里塞进去 + child: _buildLoginMethodSelector(bodysize), + ), + ), + ), + Align( + alignment: AlignmentDirectional(-1, 0), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0, 26.rpx, 0, 0), child: Container( width: double.infinity, height: bodysize.maxHeight * 0.056, @@ -279,128 +296,161 @@ class _OtherLoginPageState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Padding( - padding: EdgeInsetsDirectional.fromSTEB( - 26.rpx, 0, 0, 0), - child: Row( - mainAxisSize: MainAxisSize.max, - children: [ - InkWell( - onTap: () async {}, - child: Container( - alignment: Alignment.center, - // constraints: BoxConstraints( - // minWidth: 150.rpx, - // ), - child: Text( - "+86", - style: TextStyle( - fontFamily: 'Readex Pro', + Obx(() { + if (userInfoController + .select_login_method.value == + 2) { + return Container(); + } + return Padding( + padding: + EdgeInsetsDirectional.fromSTEB( + 26.rpx, 0, 0, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + InkWell( + onTap: () async { + await showCountryCodePickerDialog( + context, + initialCode: userInfoController + .select_country_code + .value, // ✅ 来自 controller + onConfirm: (String code) { + print("选中的区号:$code"); + userInfoController + .select_country_code + .value = code; + userInfoController + .updateAll(); + // setState / controller.update + }, + title: "选择区号".tr, + ); + }, + child: Obx(() { + return Container( + width: 80.rpx, + alignment: Alignment.center, + child: Text( + "${userInfoController.select_country_code.value}", + style: TextStyle( + fontFamily: + 'Readex Pro', + color: themeController + .currentColor.sc2, + fontSize: AppConstants() + .middler_text_fontSize, + ), + ), + ); + }), + ), + SizedBox( + height: 30.rpx, + child: VerticalDivider( + thickness: 2.rpx, + color: themeController + .currentColor.sc4, + ), + ), + ].divide(SizedBox(width: 10.rpx)), + ), + ); + }), + Obx(() { + return Expanded( + child: Container( + child: Align( + alignment: + AlignmentDirectional(-1, 0), + child: TextFormField( + onChanged: (value) { + loginController.model.phone = + value; + }, + autofocus: false, + obscureText: false, + decoration: InputDecoration( + isDense: true, + labelStyle: TextStyle( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + hintText: userInfoController + .select_login_method + .value == + 2 + ? '输入邮箱'.tr + : "输入手机号码".tr, + hintStyle: TextStyle( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, color: themeController .currentColor.sc4, - fontSize: AppConstants() - .middler_text_fontSize, - letterSpacing: 0, ), + enabledBorder: + OutlineInputBorder( + borderSide: BorderSide( + color: Color(0x00000000), + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular( + 8.rpx), + ), + focusedBorder: + OutlineInputBorder( + borderSide: BorderSide( + color: Color(0x00000000), + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular( + 8.rpx), + ), + errorBorder: + OutlineInputBorder( + borderSide: BorderSide( + color: Colors.red, + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular( + 8.rpx), + ), + focusedErrorBorder: + OutlineInputBorder( + borderSide: BorderSide( + color: Colors.red, + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular( + 8.rpx), + ), + filled: false, + fillColor: Colors.white, ), - ), - ), - SizedBox( - height: 30.rpx, - child: VerticalDivider( - thickness: 2.rpx, - color: themeController - .currentColor.sc4, - ), - ), - ].divide(SizedBox(width: 10.rpx)), - ), - ), - Expanded( - child: Container( - child: Align( - alignment: - AlignmentDirectional(-1, 0), - child: TextFormField( - onChanged: (value) { - loginController.model.phone = - value; - }, - autofocus: false, - obscureText: false, - decoration: InputDecoration( - isDense: true, - labelStyle: TextStyle( - fontFamily: 'Inter', - fontSize: 26.rpx, - letterSpacing: 0.0, - ), - hintText: '其他手机登录页.输入内容'.tr, - hintStyle: TextStyle( + style: TextStyle( fontFamily: 'Inter', fontSize: 26.rpx, letterSpacing: 0.0, color: themeController - .currentColor.sc4, + .currentColor.sc3, ), - enabledBorder: - OutlineInputBorder( - borderSide: BorderSide( - color: Color(0x00000000), - width: 1.rpx, - ), - borderRadius: - BorderRadius.circular( - 8.rpx), - ), - focusedBorder: - OutlineInputBorder( - borderSide: BorderSide( - color: Color(0x00000000), - width: 1.rpx, - ), - borderRadius: - BorderRadius.circular( - 8.rpx), - ), - errorBorder: OutlineInputBorder( - borderSide: BorderSide( - color: Colors.red, - width: 1.rpx, - ), - borderRadius: - BorderRadius.circular( - 8.rpx), - ), - focusedErrorBorder: - OutlineInputBorder( - borderSide: BorderSide( - color: Colors.red, - width: 1.rpx, - ), - borderRadius: - BorderRadius.circular( - 8.rpx), - ), - filled: false, - fillColor: Colors.white, - ), - style: TextStyle( - fontFamily: 'Inter', - fontSize: 26.rpx, - letterSpacing: 0.0, - color: themeController + cursorColor: themeController .currentColor.sc3, + // validator: _model + // .textControllerValidator + // .asValidator(context), ), - cursorColor: themeController - .currentColor.sc3, - // validator: _model - // .textControllerValidator - // .asValidator(context), ), ), - ), - ), + ); + }), ], ), ), @@ -629,7 +679,6 @@ class _OtherLoginPageState extends State { ), ), ), - Padding( padding: EdgeInsetsDirectional.fromSTEB(0, 26.rpx, 0, 0), @@ -1071,4 +1120,179 @@ class _OtherLoginPageState extends State { )), ); } + + Widget _buildLoginMethodSelector(BoxConstraints bodysize) { + final userInfoController = Get.find(); + + // 文本样式 + final textStyle = TextStyle( + fontSize: 28.rpx, + fontWeight: FontWeight.w600, + ); + + // 两个 tab 之间的间距 + final double gap = 60.rpx; + // 左侧内边距 + final double leftPadding = 40.rpx; + // 下划线和文字的垂直间距 + final double underlineGap = 20.rpx; + // 下划线高度 + final double underlineHeight = 6.rpx; + + return Align( + alignment: AlignmentDirectional(-1, 0), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 0.rpx, 0, 0), + child: Container( + width: double.infinity, + height: bodysize.maxHeight * 0.056, + constraints: const BoxConstraints(minHeight: 90), + child: Obx(() { + final selectedIndex = + userInfoController.select_login_method.value; // 1 or 2 + + // --- 计算文字宽高 --- + final tp1 = TextPainter( + text: TextSpan(text: '手机号登录'.tr, style: textStyle), + textDirection: ui.TextDirection.ltr, + )..layout(); + + final tp2 = TextPainter( + text: TextSpan(text: '邮箱登录'.tr, style: textStyle), + textDirection: ui.TextDirection.ltr, + )..layout(); + + final double w1 = tp1.width; + final double w2 = tp2.width; + final double textHeight = tp1.height; + + // --- 每个 tab 的起始 X --- + final double startX1 = leftPadding; + final double startX2 = leftPadding + w1 + gap; + + // --- 下划线宽度(和文字等宽)--- + final double underlineWidth1 = w1; + final double underlineWidth2 = w2; + + // --- 下划线 left(始终居中文字)--- + final double underlineLeft1 = startX1; + final double underlineLeft2 = startX2; + + final double underlineLeft = + selectedIndex == 1 ? underlineLeft1 : underlineLeft2; + final double underlineWidth = + selectedIndex == 1 ? underlineWidth1 : underlineWidth2; + + return Stack( + clipBehavior: Clip.none, + children: [ + // ===== 文字行 ===== + Positioned( + left: 0, + top: 0, + child: Row( + children: [ + SizedBox(width: leftPadding), + + // 手机号登录 + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () => + userInfoController.select_login_method.value = 1, + child: Padding( + padding: EdgeInsets.symmetric(vertical: 6.rpx), + child: Obx(() { + final selected = + userInfoController.select_login_method.value == + 1; + return Text( + '手机号登录'.tr, + style: textStyle.copyWith( + color: selected + ? themeController.currentColor.sc2 + : themeController.currentColor.sc4, + ), + ); + }), + ), + ), + + SizedBox(width: gap), + + // 邮箱登录 + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () => + userInfoController.select_login_method.value = 2, + child: Padding( + padding: EdgeInsets.symmetric(vertical: 6.rpx), + child: Obx(() { + final selected = + userInfoController.select_login_method.value == + 2; + return Text( + '邮箱登录'.tr, + style: textStyle.copyWith( + color: selected + ? themeController.currentColor.sc2 + : themeController.currentColor.sc4, + ), + ); + }), + ), + ), + ], + ), + ), + + // ===== 下划线(文字正下方) ===== + AnimatedPositioned( + duration: const Duration(milliseconds: 280), + curve: Curves.easeOutCubic, + left: underlineLeft, + top: textHeight + underlineGap, + child: AnimatedContainer( + duration: const Duration(milliseconds: 280), + curve: Curves.easeOutCubic, + width: underlineWidth, + height: underlineHeight, + decoration: BoxDecoration( + color: themeController.currentColor.sc2, + borderRadius: BorderRadius.circular(underlineHeight / 2), + ), + ), + ), + ], + ); + }), + ), + ), + ); + } + + Widget _buildLoginTab({ + required String text, + required int index, + required UserInfoController controller, + }) { + return GestureDetector( + onTap: () { + controller.select_login_method.value = index; + }, + child: Obx(() { + final selected = controller.select_login_method.value == index; + + return Text( + text, + style: TextStyle( + fontSize: 28.rpx, + fontWeight: FontWeight.w600, + color: selected + ? themeController.currentColor.sc2 + : themeController.currentColor.sc4, + ), + ); + }), + ); + } } diff --git a/lib/pages/login/th_bind_tel_page.dart b/lib/pages/login/th_bind_tel_page.dart index b5c0d03..5b9c69d 100644 --- a/lib/pages/login/th_bind_tel_page.dart +++ b/lib/pages/login/th_bind_tel_page.dart @@ -15,13 +15,14 @@ import 'package:vbvs_app/controller/time/countdown_controller.dart'; import 'package:vbvs_app/controller/user_info_controller.dart'; import 'package:vbvs_app/model/api_response.dart'; import 'package:vbvs_app/pages/mh_page/user/controller/bind_tel_controller.dart'; -import 'package:vbvs_app/pages/mh_page/user/controller/mht_login_controller.dart'; +import 'package:vbvs_app/pages/person/select_time.dart'; class THBindTelWidget extends GetView { BoxConstraints? bodysize; Map img; final scaffoldKey = GlobalKey(); + UserInfoController userInfoController = Get.find(); THBindTelWidget({super.key, required this.img}); @@ -182,26 +183,42 @@ class THBindTelWidget extends GetView { 0.rpx, 0, 0, 0), child: Row( mainAxisSize: MainAxisSize.max, - children: [ + children: [ InkWell( - onTap: () async {}, - child: Container( - alignment: Alignment.center, - // constraints: BoxConstraints( - // minWidth: 150.rpx, - // ), - child: Text( - "+86", - style: TextStyle( - fontFamily: 'Readex Pro', - color: themeController - .currentColor.sc4, - fontSize: AppConstants() - .middler_text_fontSize, - letterSpacing: 0, + onTap: () async { + await showCountryCodePickerDialog( + context, + initialCode: userInfoController + .select_country_code + .value, // ✅ 来自 controller + onConfirm: (String code) { + print("选中的区号:$code"); + userInfoController + .select_country_code + .value = code; + userInfoController + .updateAll(); + // setState / controller.update + }, + title: "选择区号".tr, + ); + }, + child: Obx(() { + return Container( + width: 80.rpx, + alignment: Alignment.center, + child: Text( + "${userInfoController.select_country_code.value}", + style: TextStyle( + fontFamily: 'Readex Pro', + color: themeController + .currentColor.sc2, + fontSize: AppConstants() + .middler_text_fontSize, + ), ), - ), - ), + ); + }), ), SizedBox( height: 30.rpx, diff --git a/lib/pages/mh_page/user/controller/bind_tel_controller.dart b/lib/pages/mh_page/user/controller/bind_tel_controller.dart index 3bc3915..c44ff9f 100644 --- a/lib/pages/mh_page/user/controller/bind_tel_controller.dart +++ b/lib/pages/mh_page/user/controller/bind_tel_controller.dart @@ -10,6 +10,7 @@ import 'package:vbvs_app/common/util/DailyLogUtils.dart'; import 'package:vbvs_app/common/util/MyUtils.dart'; import 'package:vbvs_app/common/util/requestWithLog.dart'; import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; +import 'package:vbvs_app/controller/user_info_controller.dart'; import 'package:vbvs_app/model/api_response.dart'; import 'package:vbvs_app/model/user_data.dart'; @@ -112,9 +113,11 @@ class AuthBindTelController extends GetControllerEx { String serviceName = ServiceConstant.server_service; String serviceApi = ServiceConstant.send_code; String queryUrl = "${serviceAddress}${serviceName}${serviceApi}"; + UserInfoController userInfoController = Get.find(); var data = { "userName": model.phone!, 'type': 5, + "ccode":userInfoController.select_country_code.value, }; if (img != null) { if (img!['code'] != null) { diff --git a/lib/pages/mh_page/user/page/bind_tel_page.dart b/lib/pages/mh_page/user/page/bind_tel_page.dart index f4acd0b..9075959 100644 --- a/lib/pages/mh_page/user/page/bind_tel_page.dart +++ b/lib/pages/mh_page/user/page/bind_tel_page.dart @@ -143,8 +143,8 @@ class BindTelWidget extends GetView { ), ), Padding( - padding: - EdgeInsetsDirectional.fromSTEB(0, 16, 0, 0), + padding: EdgeInsetsDirectional.fromSTEB( + 0, 16.rpx, 0, 0), child: Container( width: bodysize!.maxWidth, height: bodysize!.maxHeight * 0.06, @@ -469,8 +469,10 @@ class BindTelWidget extends GetView { text: message); return; } - if (authBindTelController.model.code == null || - authBindTelController.model.code!.isEmpty) { + if (authBindTelController.model.code == + null || + authBindTelController + .model.code!.isEmpty) { message = "请输入验证码".tr; TopSlideNotification.show(context, text: message); diff --git a/lib/pages/person/select_time.dart b/lib/pages/person/select_time.dart index 03b42ed..71938a9 100644 --- a/lib/pages/person/select_time.dart +++ b/lib/pages/person/select_time.dart @@ -11,6 +11,7 @@ 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/controller/theme_controller/ThemeController.dart'; +import 'package:vbvs_app/controller/user_info_controller.dart'; import 'package:vbvs_app/language/AppLanguage.dart'; import 'package:vbvs_app/pages/common/selectDialog.dart'; @@ -1154,3 +1155,154 @@ Future showIntervalPickerDialog( }, ); } + + +Future showCountryCodePickerDialog( + BuildContext context, { + required String initialCode, // 初始区号,如 "+86" + required Function(String selectedCode) onConfirm, + String title = "选择区号", +}) async { + UserInfoController controller = Get.find(); + ThemeController themeController = Get.find(); + + final List> list = controller.country_code; + + int selectedIndex = + list.indexWhere((e) => e["id"] == initialCode); + if (selectedIndex < 0) selectedIndex = 0; + + final RxInt tempIndex = RxInt(selectedIndex); + + await showDialog( + context: context, + barrierDismissible: true, + builder: (BuildContext context) { + return Stack( + children: [ + Positioned( + bottom: 0, + left: 0, + right: 0, + child: Material( + color: Colors.transparent, + child: Dialog( + backgroundColor: themeController.currentColor.sc17, + insetPadding: EdgeInsets.zero, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.zero, + ), + child: Container( + padding: EdgeInsets.fromLTRB(0, 0, 0, 90.rpx), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + /// 顶部栏 + Container( + padding: + EdgeInsets.symmetric(horizontal: 30.rpx), + color: themeController.currentColor.sc5, + height: 80.rpx, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ClickableContainer( + backgroundColor: Colors.transparent, + highlightColor: Colors.transparent, + onTap: Get.back, + padding: EdgeInsets.all(0), + child: SizedBox( + width: 110.rpx, + height: 60.rpx, + child: Center( + child: Text( + "取消".tr, + style: const TextStyle( + color: Colors.white), + ), + ), + ), + ), + Text( + title.tr, + style: TextStyle( + fontFamily: 'Readex Pro', + color: + themeController.currentColor.sc3, + fontSize: 30.rpx, + ), + ), + ClickableContainer( + padding: EdgeInsets.all(0), + backgroundColor: Colors.transparent, + highlightColor: Colors.transparent, + onTap: () { + onConfirm( + list[tempIndex.value]["id"]!); + Get.back(); + }, + child: SizedBox( + width: 110.rpx, + height: 60.rpx, + child: Center( + child: Text( + "确定".tr, + style: TextStyle( + color: themeController + .currentColor.sc2, + ), + ), + ), + ), + ), + ], + ), + ), + + SizedBox(height: 20.rpx), + + /// Picker + Stack( + children: [ + Positioned.fill( + child: IgnorePointer( + child: Center( + child: Container( + height: 90.rpx, + margin: EdgeInsets.symmetric( + horizontal: 95.rpx), + decoration: BoxDecoration( + color: themeController + .currentColor.sc2, + borderRadius: + BorderRadius.circular( + 16.rpx), + ), + ), + ), + ), + ), + SizedBox( + height: 240.rpx, + child: getOnePickers( + context, + list + .map((e) => + "${e['name']}".tr+"(${e['id']})") + .toList(), + tempIndex, + ), + ), + ], + ), + ], + ), + ), + ), + ), + ), + ], + ); + }, + ); +} diff --git a/lib/pages/setting/common_mesaage_setting.dart b/lib/pages/setting/common_mesaage_setting.dart index 4f57843..5541990 100644 --- a/lib/pages/setting/common_mesaage_setting.dart +++ b/lib/pages/setting/common_mesaage_setting.dart @@ -9,6 +9,7 @@ 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/NewTopSlideNotification.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'; @@ -204,6 +205,14 @@ class _CommonMessageSettingPageState extends State { ? true : false, onChanged: (val) { + if (val == false) { + NewTopSlideNotification.show( + text: "注意!关闭后将无法接受任何消息".tr, + textColor: themeController + .currentColor.sc9, + duration: Duration(seconds: 3), + ); + } String serviceAddress = ServiceConstant.service_address; String serviceName = diff --git a/lib/pages/sleep_report/chart/SnoreWaveform.dart b/lib/pages/sleep_report/chart/SnoreWaveform.dart index 904e9ce..e19a5ef 100644 --- a/lib/pages/sleep_report/chart/SnoreWaveform.dart +++ b/lib/pages/sleep_report/chart/SnoreWaveform.dart @@ -1,280 +1,3 @@ -// 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").withOpacity(0.8) -// ..strokeWidth = 1.5 -// ..style = PaintingStyle.stroke; - -// final Path upperPath = Path(); -// final Path lowerPath = Path(); - -// // ✅ 获取最大值用于自适应比例 -// double maxValue = snoreValues.fold(0, (prev, e) { -// final value = e["value"]?.toDouble() ?? 0; -// return value > prev ? value : prev; -// }); - -// // ✅ 自适应缩放比例,限制波形最大高度为 height * 0.45 -// final double maxWaveHeight = height * 1; -// final double scaleY = maxValue > 0 ? (maxWaveHeight / maxValue) : 1; - -// 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.withOpacity(0.6) -// ..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 = t == startTime -// ? DateFormat('HH:mm').format(dt) -// : DateFormat('h').format(dt); // 12小时制 - -// textPainter.text = TextSpan( -// text: label, -// style: TextStyle(fontSize: 10, color: Colors.grey), -// ); -// textPainter.layout(); -// textPainter.paint( -// canvas, -// Offset(x - textPainter.width / 2, height + 2), // 标签显示在底部 -// ); -// } - -// // ✅ 画终点时间 -// { -// 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 + 2), -// ); -// } -// } - -// @override -// bool shouldRepaint(covariant CustomPainter oldDelegate) => true; -// } - import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; @@ -300,6 +23,11 @@ class SnoreChartContainer extends StatelessWidget { @override Widget build(BuildContext context) { + // barData.add({ + // "type": 6, + // "et": 1765291778963, + // "st": 1765289341000, + // }); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -378,6 +106,7 @@ class SnoreBarPainter extends CustomPainter { final int st = item['st']; final int et = item['et']; final int type = item['type']; + int heightInit = 1; final match = showLabel.firstWhere( (e) => e['type'] == type, @@ -400,7 +129,17 @@ class SnoreBarPainter extends CustomPainter { final double rightX = (et - startTime) * pixelPerMs; final double barWidth = rightX - leftX; - final double barHeight = (type + 5).toDouble() * 8; + //rem 深睡 中 + //浅睡 低 + //其他 高 + if (type == 1) { + heightInit = 1; + } else if (type == 2 || type == 6) { + heightInit = 2; + } else { + heightInit = 3; + } + final double barHeight = (heightInit + 5).toDouble() * 8; final double top = height - barHeight; final rect = Rect.fromLTWH(leftX, top, barWidth, barHeight); diff --git a/lib/pages/sleep_report/component/DailyDataWidget.dart b/lib/pages/sleep_report/component/DailyDataWidget.dart index 0d7cc81..977746c 100644 --- a/lib/pages/sleep_report/component/DailyDataWidget.dart +++ b/lib/pages/sleep_report/component/DailyDataWidget.dart @@ -25,11 +25,10 @@ Widget DailyDataWidget( GlobalKey breatheCardKey, dynamic data, ) { - List _buildSectionList() { EdgeInsetsDirectional padding = EdgeInsetsDirectional.fromSTEB(30.rpx, 0, 30.rpx, 25.rpx); - + return [ SleepScoreWidget(sleepReport: sleepReport), SleepViewWidget(sleepReport: sleepReport), @@ -47,7 +46,7 @@ Widget DailyDataWidget( sleepReport: sleepReport, highlightItem: data['itemName'], ), - HeartChangeWidget(sleepReport: sleepReport),//心率变异性 + HeartChangeWidget(sleepReport: sleepReport), //心率变异性 BreatheStandardWidget(sleepReport: sleepReport), BreatheCard( key: breatheCardKey, diff --git a/lib/pages/sleep_report/component/SleepView.dart b/lib/pages/sleep_report/component/SleepView.dart index 09f17e6..fbfad22 100644 --- a/lib/pages/sleep_report/component/SleepView.dart +++ b/lib/pages/sleep_report/component/SleepView.dart @@ -65,6 +65,12 @@ class _SleepViewWidgetState extends State { int minutes = time['minutes']; List stages = widget.sleepReport['sleepData']['stages']; + // showLabel.add({ + // "type": 6, + // "name": "rem", + // "color": "#FFC0CB", + // }); + return Container( width: double.infinity, decoration: BoxDecoration( @@ -95,7 +101,8 @@ class _SleepViewWidgetState extends State { 14.rpx, 10.rpx, 14.rpx, 10.rpx), // borderRadius: 0.rpx, // 圆形点击区域 onTap: () { - if (AppConstants().ent_type == APPPackageType.MHT.code) { + if (AppConstants().ent_type == + APPPackageType.MHT.code) { showTipDialog( context, Container( diff --git a/lib/pages/sleep_report/component/new_sleep_view.dart b/lib/pages/sleep_report/component/new_sleep_view.dart index 8d1c6e0..410d0f5 100644 --- a/lib/pages/sleep_report/component/new_sleep_view.dart +++ b/lib/pages/sleep_report/component/new_sleep_view.dart @@ -11,7 +11,7 @@ import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart'; import 'package:vbvs_app/pages/mh_page/homepage/controller/mht_home_controller.dart'; import 'package:vbvs_app/pages/sleep_report/chart/SnoreWaveform.dart'; -//睡眠规律性 +//睡眠规律性 眠花糖 class NewSleepViewWidget extends StatefulWidget { var sleepReport; final VoidCallback? onRefresh; // 添加回调函数 diff --git a/lib/pages/user/update_user_tel.dart b/lib/pages/user/update_user_tel.dart index 3c90536..2cd5384 100644 --- a/lib/pages/user/update_user_tel.dart +++ b/lib/pages/user/update_user_tel.dart @@ -23,6 +23,7 @@ import 'package:vbvs_app/controller/time/countdown_controller.dart'; import 'package:vbvs_app/controller/user_info_controller.dart'; import 'package:vbvs_app/model/api_response.dart'; import 'package:vbvs_app/model/user_data.dart'; +import 'package:vbvs_app/pages/person/select_time.dart'; class UpdateUserTelPage extends StatefulWidget { const UpdateUserTelPage({super.key}); @@ -231,25 +232,43 @@ class _UpdateUserTelPageState extends State { mainAxisSize: MainAxisSize.max, children: [ InkWell( - onTap: () async {}, - child: Container( - alignment: Alignment.center, - // constraints: BoxConstraints( - // minWidth: 150.rpx, - // ), - child: Text( - "+86", - style: TextStyle( - fontFamily: 'Readex Pro', - color: themeController - .currentColor.sc4, - fontSize: AppConstants() - .middler_text_fontSize, - letterSpacing: 0, + onTap: () async { + await showCountryCodePickerDialog( + context, + initialCode: userInfoController + .select_country_code + .value, // ✅ 来自 controller + onConfirm: (String code) { + print("选中的区号:$code"); + userInfoController + .select_country_code + .value = code; + userInfoController + .updateAll(); + // setState / controller.update + }, + title: "选择区号".tr, + ); + }, + child: Obx(() { + return Container( + width: 80.rpx, + alignment: Alignment.center, + child: Text( + "${userInfoController.select_country_code.value}", + style: TextStyle( + fontFamily: + 'Readex Pro', + color: themeController + .currentColor.sc2, + fontSize: AppConstants() + .middler_text_fontSize, + ), ), - ), - ), + ); + }), ), + SizedBox( height: 30.rpx, child: VerticalDivider(