From acde8340a8207f3ccce1c720687efd7e4553e326 Mon Sep 17 00:00:00 2001 From: wyf <494641114@qq.com> Date: Mon, 16 Jun 2025 09:32:24 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=A3=89=E8=8A=B1=E7=B3=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/mhlangs/zh_CN.json | 21 +- lib/common/color/appConstants.dart | 11 +- lib/common/util/DailyLogUtils.dart | 5 + lib/component/tool/cmd.dart | 3 + lib/main.dart | 6 + lib/pages/main_bottom/home_page.dart | 2 - .../mh_page/component/mht_bind_dialog.dart | 1150 +++++++++++++ .../component/DeviceComponentWidget.dart | 616 +++++++ .../mht_device_calibration_controller.dart | 42 + .../mht_device_calibration_controller.g.dart | 15 + .../controller/mht_bluetooth_controller.dart | 264 +++ .../mht_bluetooth_controller.g.dart | 26 + .../mh_page/device/mht_bind_device_type.dart | 229 +++ .../device/mht_blueteeth_device_page.dart | 717 ++++++++ .../device/mht_device_calibration.dart | 1060 ++++++++++++ lib/pages/mh_page/device/mht_wifi_page.dart | 1119 +++++++++++++ .../device/model/BlueToothDataModel.dart | 36 + .../component/HomeDeviceStausWidget.dart | 103 ++ .../homepage/component/HomeDeviceWidget.dart | 53 + .../controller/mht_home_controller.dart | 330 ++++ .../controller/mht_home_controller.g.dart | 13 + lib/pages/mh_page/homepage/new_Home_page.dart | 1458 ++++++++++------- .../user/controller/mht_login_controller.dart | 45 +- .../controller/mht_register_controller.g.dart | 2 + .../mh_page/user/page/mht_login_page.dart | 45 +- lib/pages/person/person_page.dart | 31 +- lib/routers/mh_routers.dart | 18 +- 27 files changed, 6762 insertions(+), 658 deletions(-) create mode 100644 lib/pages/mh_page/component/mht_bind_dialog.dart create mode 100644 lib/pages/mh_page/device/component/DeviceComponentWidget.dart create mode 100644 lib/pages/mh_page/device/component/mht_device_calibration_controller.dart create mode 100644 lib/pages/mh_page/device/component/mht_device_calibration_controller.g.dart create mode 100644 lib/pages/mh_page/device/controller/mht_bluetooth_controller.dart create mode 100644 lib/pages/mh_page/device/controller/mht_bluetooth_controller.g.dart create mode 100644 lib/pages/mh_page/device/mht_bind_device_type.dart create mode 100644 lib/pages/mh_page/device/mht_blueteeth_device_page.dart create mode 100644 lib/pages/mh_page/device/mht_device_calibration.dart create mode 100644 lib/pages/mh_page/device/mht_wifi_page.dart create mode 100644 lib/pages/mh_page/device/model/BlueToothDataModel.dart create mode 100644 lib/pages/mh_page/homepage/component/HomeDeviceStausWidget.dart create mode 100644 lib/pages/mh_page/homepage/component/HomeDeviceWidget.dart create mode 100644 lib/pages/mh_page/homepage/controller/mht_home_controller.dart create mode 100644 lib/pages/mh_page/homepage/controller/mht_home_controller.g.dart diff --git a/assets/mhlangs/zh_CN.json b/assets/mhlangs/zh_CN.json index 4bdd041..25c0184 100644 --- a/assets/mhlangs/zh_CN.json +++ b/assets/mhlangs/zh_CN.json @@ -62,7 +62,26 @@ "输入新密码":"输入新密码", "确认验证码":"确认验证码", "输入手机号码":"输入手机号码", - "操作成功":"操作成功" + "操作成功":"操作成功", + "添加设备":"添加设备", + "扫描中":"扫描中...", + "检索设备":"检索设备", + "搜索":"搜索", + "匹配出的外围设备":"匹配出的外围设备", + "知道了":"知道了", + "蓝牙未开启":"蓝牙未开启", + "请先打开蓝牙在进行设备扫描":"请先打开蓝牙在进行设备扫描", + "等待扫描":"等待扫描", + "默认设备名称":"默认设备名称", + "连接异常":"连接异常", + "蓝牙绑定提示":"用手机进行设备添加时,请打开手机蓝牙搜索,靠近目标位置3米以内进行。", + "蓝牙连接成功":"蓝牙连接成功", + "蓝牙连接失败":"蓝牙连接失败,请重试", + "绑定失败请重试":"绑定失败请重试", + "无法绑定":"无法绑定!", + "无法绑定1":"检测到该设备", + "无法绑定2":"已被绑定", + "无法绑定3":",绑定前请先进行解绑,有疑问请联系客服" diff --git a/lib/common/color/appConstants.dart b/lib/common/color/appConstants.dart index 779fbe5..874fe5c 100644 --- a/lib/common/color/appConstants.dart +++ b/lib/common/color/appConstants.dart @@ -1,4 +1,7 @@ +import 'dart:ui'; + import 'package:vbvs_app/common/util/FitTool.dart'; +import 'package:vbvs_app/common/util/MyUtils.dart'; class AppConstants { // App-related constants @@ -28,5 +31,11 @@ class AppConstants { double dropdown_height = 90.rpx; //标题文字字号 double border_width = 1.rpx; //标题文字字号//border宽度 - + List mhtButtongradientColors = [ + stringToColor("FCFCFC"), // 浅蓝 + stringToColor("F8FAF9"), // 浅蓝 + stringToColor("ECF6F3"), // 浅蓝 + stringToColor("D9F0E9"), // 浅蓝 + stringToColor("CEECE3"), // 浅蓝 + ]; } diff --git a/lib/common/util/DailyLogUtils.dart b/lib/common/util/DailyLogUtils.dart index 7d1d5f7..ecb0276 100644 --- a/lib/common/util/DailyLogUtils.dart +++ b/lib/common/util/DailyLogUtils.dart @@ -44,6 +44,11 @@ class DailyLogUtils { await _writeLogWithLevel('DEBUG', content); } + static Future printLog(String content) async { + print("logger--->"+content); + // await writeLog(content); + } + // 读取当天日志 static Future readTodayLog() async { final file = await _getLogFile(); diff --git a/lib/component/tool/cmd.dart b/lib/component/tool/cmd.dart index 8b66941..6977bbe 100644 --- a/lib/component/tool/cmd.dart +++ b/lib/component/tool/cmd.dart @@ -1,8 +1,11 @@ //蓝牙指令 +import 'dart:typed_data'; + import 'package:EasyDartModule/EasyDartModule.dart' as edm; import 'package:easydevice/src/app/thapp.dart'; import 'package:vbvs_app/common/util/DailyLogUtils.dart'; +import 'package:vbvs_app/pages/mh_page/device/model/BlueToothDataModel.dart'; // wifi列表指令 getWifiList(THapp tHapp) async { diff --git a/lib/main.dart b/lib/main.dart index 6ddeae7..bb31a4b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -65,6 +65,9 @@ import 'package:vbvs_app/language/AppLanguage.dart'; import 'package:vbvs_app/model/CustomThemeColor.dart'; import 'package:vbvs_app/model/user_data.dart'; import 'package:vbvs_app/pages/mh_page/MattressControl.dart'; +import 'package:vbvs_app/pages/mh_page/device/component/mht_device_calibration_controller.dart'; +import 'package:vbvs_app/pages/mh_page/device/controller/mht_bluetooth_controller.dart'; +import 'package:vbvs_app/pages/mh_page/homepage/controller/mht_home_controller.dart'; import 'package:vbvs_app/pages/mh_page/user/controller/mht_login_controller.dart'; import 'package:vbvs_app/pages/mh_page/user/controller/mht_register_controller.dart'; import 'package:vbvs_app/pages/mh_page/user/controller/update_password_controller.dart'; @@ -335,6 +338,9 @@ class MyApp extends StatelessWidget { Get.lazyPut(() => RegisterController()), Get.lazyPut(() => UpdatePasswordController()), Get.lazyPut(() => DeviceListController()), + Get.lazyPut(() => MHTBlueToothController()), + Get.lazyPut(() => MHTHomeController()), + Get.lazyPut(() => MHTDeviceCalibrationController()), ])); } diff --git a/lib/pages/main_bottom/home_page.dart b/lib/pages/main_bottom/home_page.dart index 374ed37..34cc078 100644 --- a/lib/pages/main_bottom/home_page.dart +++ b/lib/pages/main_bottom/home_page.dart @@ -24,7 +24,6 @@ import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; import 'package:vbvs_app/controller/user_info_controller.dart'; import 'package:vbvs_app/controller/weather/weather_controller.dart'; import 'package:vbvs_app/enum/LoginStatus.dart'; -import 'package:vbvs_app/main.dart'; import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart'; class HomePage extends StatefulWidget { @@ -35,7 +34,6 @@ class HomePage extends StatefulWidget { } class _HomePageState extends State { - GlobalController globalController = Get.find(); UserInfoController userInfoController = Get.find(); ThemeController themeController = Get.find(); BodyDeviceController deviceController = Get.find(); diff --git a/lib/pages/mh_page/component/mht_bind_dialog.dart b/lib/pages/mh_page/component/mht_bind_dialog.dart new file mode 100644 index 0000000..1c22538 --- /dev/null +++ b/lib/pages/mh_page/component/mht_bind_dialog.dart @@ -0,0 +1,1150 @@ +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:loading_indicator/loading_indicator.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/component/tool/CustomCard.dart'; +import 'package:vbvs_app/component/tool/FrostedDialog.dart'; +import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; +import 'package:vbvs_app/controller/device/blueteeth_bind_controller.dart'; +import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; +import 'package:vbvs_app/model/BleDeviceData.dart'; +import 'package:vbvs_app/pages/device_bind/componnet/FancyCircleCheckbox.dart'; + +void showBindDoubleDialog( + BuildContext context, + List devices, { + required void Function(int selectedIndex) onConfirm, + required VoidCallback onCancel, +}) { + ThemeController themeController = Get.find(); + BlueteethBindController blueteethBindController = Get.find(); + + // 初始化默认选中 deviceIndex0 为 true + blueteethBindController.model.deviceIndex0 = true; + blueteethBindController.model.deviceIndex1 = false; + blueteethBindController.model.deviceIndex2 = false; + + showDialog( + context: context, + barrierDismissible: true, + barrierColor: Colors.black.withOpacity(0.5), + builder: (BuildContext context) { + return FrostedDialog( + blurSigma: 3.0, + child: Container( + decoration: BoxDecoration( + color: themeController.currentColor.sc17, + borderRadius: BorderRadius.circular(20.0), + ), + padding: EdgeInsetsDirectional.fromSTEB(0.rpx, 0, 0.rpx, 0), + child: Container( + width: double.infinity, + constraints: BoxConstraints( + maxHeight: MediaQuery.sizeOf(context).height * 0.656, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // 标题 + Align( + alignment: AlignmentDirectional(0, 0), + child: Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0.rpx, 93.rpx, 0, 0), + child: Text( + '蓝牙绑定.双人版绑定标题'.tr, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc3, + ), + ), + ), + ), + // 全选 + _buildCheckboxRow( + context, + title: '蓝牙绑定.绑定全部'.tr, + value: () => blueteethBindController.model.deviceIndex0!, + onChanged: (v) { + if (!blueteethBindController.model.deviceIndex0!) { + blueteethBindController.model.deviceIndex0 = true; + blueteethBindController.model.deviceIndex1 = false; + blueteethBindController.model.deviceIndex2 = false; + blueteethBindController.updateAll(); + } + }, + ), + // 主设备 + _buildCheckboxRow( + context, + title: '蓝牙绑定.主设备'.tr + + "${devices[0].mac}" + + (devices[0].bind == false ? "可绑定".tr : "已被绑定".tr), + value: () => blueteethBindController.model.deviceIndex1!, + onChanged: (v) { + if (!blueteethBindController.model.deviceIndex1!) { + blueteethBindController.model.deviceIndex0 = false; + blueteethBindController.model.deviceIndex1 = true; + blueteethBindController.model.deviceIndex2 = false; + blueteethBindController.updateAll(); + } + }, + ), + // 从设备 + _buildCheckboxRow( + context, + title: '蓝牙绑定.从设备'.tr + + "${devices[1].mac}" + + (devices[1].bind == false ? "可绑定".tr : "已被绑定".tr), + value: () => blueteethBindController.model.deviceIndex2!, + onChanged: (v) { + if (!blueteethBindController.model.deviceIndex2!) { + blueteethBindController.model.deviceIndex0 = false; + blueteethBindController.model.deviceIndex1 = false; + blueteethBindController.model.deviceIndex2 = true; + blueteethBindController.updateAll(); + } + }, + ), + + // 确定按钮 + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 60.rpx, 100.rpx, 60.rpx, 0), + child: _buildActionButton( + context, + text: '蓝牙绑定.确定'.tr, + onTap: () { + Get.back(); + + int selectedIndex = + blueteethBindController.model.deviceIndex0! + ? 0 + : (blueteethBindController.model.deviceIndex1! + ? 1 + : 2); + + onConfirm(selectedIndex); + }, + ), + ), + // 取消按钮 + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 60.rpx, 19.rpx, 60.rpx, 60.rpx), + child: _buildActionButton( + context, + text: '蓝牙绑定.取消'.tr, + onTap: () { + Get.back(); + onCancel(); + }, + ), + ), + ], + ), + ), + ), + ); + }, + ); +} + +Future showHaveBindDialog(BuildContext context) async { + ThemeController themeController = Get.find(); + + return showDialog( + context: context, + barrierDismissible: true, + barrierColor: Colors.black.withOpacity(0.5), + builder: (BuildContext context) { + return FrostedDialog( + blurSigma: 3.0, + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20.0), + ), + padding: EdgeInsetsDirectional.fromSTEB(60.rpx, 0, 60.rpx, 0), + child: Container( + width: double.infinity, + constraints: BoxConstraints( + maxHeight: MediaQuery.sizeOf(context).height * 0.656, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Align( + alignment: AlignmentDirectional(0, 0), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 93.rpx, 0.rpx, 0.rpx), + child: Text( + '无法绑定'.tr, + style: TextStyle( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + color: stringToColor("#333333"), + ), + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 70.rpx, 0.rpx, 56.rpx), + child: RichText( + text: TextSpan(children: [ + TextSpan( + text: "无法绑定1".tr, + style: TextStyle( + color: stringToColor("#333333"), + fontSize: AppConstants().normal_text_fontSize, + ), + ), + TextSpan( + text: "无法绑定2".tr, + style: TextStyle( + color: stringToColor("#333333"), + fontSize: AppConstants().normal_text_fontSize, + ), + ), + TextSpan( + text: "无法绑定3".tr, + style: TextStyle( + color: stringToColor("#333333"), + fontSize: AppConstants().normal_text_fontSize, + ), + ), + ]), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 19.rpx, 0, 60.rpx), + child: CustomCard( + borderRadius: AppConstants().button_container_radius, + onTap: () { + Get.back(result: true); + }, + colors: [ + themeController.currentColor.sc1, + themeController.currentColor.sc2, + ], + child: Container( + width: MediaQuery.sizeOf(context).width, + 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, + )), + ), + ), + ), + ), + ], + ), + ), + ), + ); + }, + ); +} + +void showLoadingDialog(BuildContext context, {String? title}) { + ThemeController themeController = Get.find(); + showDialog( + context: context, + barrierDismissible: true, + barrierColor: Colors.black.withOpacity(0.5), + builder: (BuildContext context) { + return FrostedDialog( + blurSigma: 3.0, + child: Container( + decoration: BoxDecoration( + color: Colors.transparent, + borderRadius: BorderRadius.circular(20.0), + ), + padding: EdgeInsetsDirectional.fromSTEB(60.rpx, 0, 60.rpx, 0), + child: Container( + width: double.infinity, + constraints: BoxConstraints( + maxHeight: MediaQuery.sizeOf(context).height * 0.656, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // Align( + // alignment: AlignmentDirectional(0, 0), + // child: Padding( + // padding: EdgeInsetsDirectional.fromSTEB( + // 0.rpx, 93.rpx, 0.rpx, 0.rpx), + // child: Text( + // '蓝牙绑定.无法绑定'.tr, + // style: FlutterFlowTheme.of(context).bodyMedium.override( + // fontFamily: 'Inter', + // fontSize: 30.rpx, + // letterSpacing: 0.0, + // color: themeController.currentColor.sc3, + // ), + // ), + // ), + // ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 93.rpx, 0.rpx, 0.rpx), + child: SizedBox( + width: 120.rpx, + height: 120.rpx, + child: LoadingIndicator( + indicatorType: Indicator.ballSpinFadeLoader, + colors: [ + themeController.currentColor.sc3, + ], + strokeWidth: 1, + backgroundColor: Colors.transparent, + pathBackgroundColor: themeController.currentColor.sc3), + ), + ), + + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 27.rpx, 0.rpx, 56.rpx), + child: RichText( + text: TextSpan(children: [ + TextSpan( + text: title ?? "连接中...".tr, + style: TextStyle( + color: themeController.currentColor.sc3, + fontSize: AppConstants().normal_text_fontSize, + ), + ), + ]), + ), + ), + ], + ), + ), + ), + ); + }, + ); +} + +Widget _buildCheckboxRow( + BuildContext context, { + required String title, + required bool Function() value, + required void Function(bool) onChanged, +}) { + ThemeController themeController = Get.find(); + return Padding( + padding: EdgeInsetsDirectional.fromSTEB(60.rpx, 64.rpx, 0.rpx, 0.rpx), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Obx(() => FancyCircleCheckbox( + borderColor: themeController.currentColor.sc3, + fillColor: themeController.currentColor.sc2, + value: value(), + onChanged: onChanged, + )), + Expanded( + child: Builder( + builder: (_) { + final bool isBindable = title.contains('可绑定'.tr); + final bool isBound = title.contains('已被绑定'.tr); + + // 提取主文本部分 + final String mainText = + title.replaceAll('可绑定'.tr, '').replaceAll('已被绑定'.tr, ''); + + return RichText( + text: TextSpan( + children: [ + TextSpan( + text: mainText, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + letterSpacing: 0.0, + fontSize: AppConstants().normal_text_fontSize, + color: themeController.currentColor.sc3, + ), + ), + if (isBindable) + TextSpan( + text: "(" + '可绑定'.tr + ")", + style: TextStyle( + fontSize: AppConstants().normal_text_fontSize, + color: themeController.currentColor.sc1, + ), + ), + if (isBound) + TextSpan( + text: "(" + '已被绑定'.tr + ")", + style: TextStyle( + fontSize: AppConstants().normal_text_fontSize, + color: themeController.currentColor.sc9, + ), + ), + ], + ), + ); + }, + ), + ), + ].divide(SizedBox(width: 21.rpx)), + ), + ); +} + +Widget _buildActionButton( + BuildContext context, { + required String text, + required VoidCallback onTap, +}) { + ThemeController themeController = Get.find(); + return CustomCard( + borderRadius: AppConstants().button_container_radius, + onTap: onTap, + colors: '蓝牙绑定.确定'.tr == text + ? [ + themeController.currentColor.sc1, + themeController.currentColor.sc2, + ] + : [Colors.transparent], + child: Container( + width: MediaQuery.sizeOf(context).width, + height: MediaQuery.sizeOf(context).height * 0.055, + constraints: BoxConstraints( + minWidth: 500.rpx, + minHeight: 90.rpx, + ), + decoration: '蓝牙绑定.确定'.tr == text + ? BoxDecoration() + : BoxDecoration( + border: Border.all( + color: themeController.currentColor.sc3, // 你可以换成指定颜色 + width: 1.0.rpx, + ), + borderRadius: BorderRadius.circular(50.rpx), // 可选:加圆角 + ), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + text, + 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)), + ), + ), + ); +} + +void showConfirmDialog( + BuildContext context, + Widget widget, + String title, { + required VoidCallback onConfirm, + required VoidCallback onCancel, +}) { + ThemeController themeController = Get.find(); + BlueteethBindController blueteethBindController = Get.find(); + try { + showDialog( + context: context, + barrierDismissible: true, + barrierColor: Colors.black.withOpacity(0.5), // 背景模糊色 + builder: (BuildContext context) { + return FrostedDialog( + blurSigma: 3.0, + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20.0), + ), + padding: EdgeInsetsDirectional.fromSTEB(31.rpx, 0, 31.rpx, 0), + child: Container( + width: double.infinity, + constraints: BoxConstraints( + maxHeight: MediaQuery.sizeOf(context).height * 0.656, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // 标题 + // Row( + // mainAxisAlignment: MainAxisAlignment.end, + // children: [ + // ClickableContainer( + // backgroundColor: Colors.transparent, // 容器背景色 + // highlightColor: + // themeController.currentColor.sc21, // 点击时的背景色 + // padding: EdgeInsets.zero, // 这里去掉外部的 padding,避免影响点击范围 + // onTap: () { + // Get.back(); + // onCancel(); + // }, + // child: Padding( + // // padding: + // // EdgeInsetsDirectional.fromSTEB(0, 33.rpx, 0, 0.rpx), + // padding: EdgeInsetsDirectional.fromSTEB( + // 33.rpx, 33.rpx, 33.rpx, 33.rpx), + // child: SvgPicture.asset( + // 'assets/img/icon/close.svg', + // width: 25.rpx, + // height: 25.rpx, // 如果 SVG 中没有固定颜色,使用 color 设置 + // color: themeController.currentColor.sc3, + // ), + // ), + // ), + // ], + // ), + + Align( + alignment: AlignmentDirectional(0, 0), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 33.rpx, 60.rpx, 33.rpx, 33.rpx), + child: Text( + title, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + color: stringToColor("#333333"), + fontWeight: FontWeight.w600, + ), + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 33.rpx, 0.rpx, 33.rpx, 0.rpx), + child: widget, + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 33.rpx, 58.rpx, 33.rpx, 60.rpx), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + CustomCard( + borderRadius: AppConstants().normal_container_radius, + onTap: () async { + Get.back(); + onCancel(); + }, + colors: [ + stringToColor("1592AA"), + stringToColor("006FA3"), + ], + child: Container( + width: MediaQuery.sizeOf(context).width * 0.115, + height: MediaQuery.sizeOf(context).height * 0.055, + constraints: BoxConstraints( + minWidth: 160.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)), + ), + ), + ), + CustomCard( + borderRadius: AppConstants().normal_container_radius, + onTap: () { + Get.back(); + onConfirm(); + // await Future.delayed(Duration(milliseconds: 300)); + }, + colors: [ + stringToColor("1592AA"), + stringToColor("006FA3"), + ], + child: Container( + width: MediaQuery.sizeOf(context).width * 0.215, + height: MediaQuery.sizeOf(context).height * 0.055, + constraints: BoxConstraints( + minWidth: 160.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)), + ), + ), + ), + ].divide(SizedBox( + width: 70.rpx, + )), + ), + ), + ], + ), + ), + ), + ); + }, + ); + } catch (e) { + print(e); + } +} + +void showSingleConfirmDialog( + BuildContext context, + Widget widget, + String title, { + required VoidCallback onConfirm, + required VoidCallback onCancel, + String confirmText = "确定", +}) { + ThemeController themeController = Get.find(); + BlueteethBindController blueteethBindController = Get.find(); + + showDialog( + context: context, + barrierDismissible: true, + barrierColor: Colors.black.withOpacity(0.5), // 背景模糊色 + builder: (BuildContext context) { + return FrostedDialog( + blurSigma: 3.0, + child: Container( + decoration: BoxDecoration( + color: themeController.currentColor.sc17, + borderRadius: BorderRadius.circular(20.0), + ), + padding: EdgeInsetsDirectional.fromSTEB(31.rpx, 0, 31.rpx, 0), + child: Container( + width: double.infinity, + constraints: BoxConstraints( + maxHeight: MediaQuery.sizeOf(context).height * 0.656, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // 标题 + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + ClickableContainer( + backgroundColor: Colors.transparent, // 容器背景色 + highlightColor: + themeController.currentColor.sc21, // 点击时的背景色 + padding: EdgeInsets.zero, // 这里去掉外部的 padding,避免影响点击范围 + onTap: () { + Get.back(); + onCancel(); + }, + child: Padding( + // padding: + // EdgeInsetsDirectional.fromSTEB(0, 33.rpx, 0, 0.rpx), + padding: EdgeInsetsDirectional.fromSTEB( + 33.rpx, 33.rpx, 33.rpx, 33.rpx), + child: SvgPicture.asset( + 'assets/img/icon/close.svg', + width: 25.rpx, + height: 25.rpx, // 如果 SVG 中没有固定颜色,使用 color 设置 + color: themeController.currentColor.sc3, + ), + ), + ), + ], + ), + Align( + alignment: AlignmentDirectional(0, 0), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 33.rpx, 60.rpx, 33.rpx, 33.rpx), + child: Text( + title, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc3, + ), + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 33.rpx, 0.rpx, 33.rpx, 0.rpx), + child: widget, + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 33.rpx, 58.rpx, 33.rpx, 60.rpx), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + CustomCard( + borderRadius: AppConstants().button_container_radius, + onTap: () { + Get.back(); + onConfirm(); + // await Future.delayed(Duration(milliseconds: 300)); + }, + colors: [ + themeController.currentColor.sc1, + themeController.currentColor.sc2, + ], + child: Container( + width: MediaQuery.sizeOf(context).width * 0.43, + height: MediaQuery.sizeOf(context).height * 0.055, + constraints: BoxConstraints( + minWidth: 160.rpx, + minHeight: 90.rpx, + ), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + confirmText, + 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)), + ), + ), + ), + ].divide(SizedBox( + width: 70.rpx, + )), + ), + ), + ], + ), + ), + ), + ); + }, + ); +} + +void showWifiDialog( + BuildContext context, + Widget widget, + String title, { + required VoidCallback onConfirm, +}) { + ThemeController themeController = Get.find(); + BlueteethBindController blueteethBindController = Get.find(); + + showDialog( + context: context, + barrierDismissible: true, + barrierColor: Colors.black.withOpacity(0.5), // 背景模糊色 + builder: (BuildContext context) { + return FrostedDialog( + blurSigma: 3.0, + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20.0), + ), + padding: EdgeInsetsDirectional.fromSTEB(64.rpx, 0, 64.rpx, 0), + child: Container( + width: double.infinity, + constraints: BoxConstraints( + maxHeight: MediaQuery.sizeOf(context).height * 0.656, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // 标题 + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + ClickableContainer( + backgroundColor: Colors.transparent, // 容器背景色 + highlightColor: + themeController.currentColor.sc21, // 点击时的背景色 + padding: EdgeInsets.zero, // 这里去掉外部的 padding,避免影响点击范围 + onTap: () { + Get.back(); + }, + child: Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 33.rpx, 0, 0.rpx), + child: SvgPicture.asset( + 'assets/img/icon/close.svg', + width: 25.rpx, + height: 25.rpx, // 如果 SVG 中没有固定颜色,使用 color 设置 + color: stringToColor("#878787"), + ), + ), + ), + ], + ), + + Align( + alignment: AlignmentDirectional(0, 0), + child: Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0.rpx, 40.rpx, 0, 0), + child: Text( + title, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + color: stringToColor("#333333"), + ), + ), + ), + ), + widget, + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 58.rpx, 0, 60.rpx), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + CustomCard( + borderRadius: AppConstants().button_container_radius, + onTap: () { + if (blueteethBindController.model.wifiPass == null || + blueteethBindController.model.wifiPass!.isEmpty) { + TopSlideNotification.show( + context, + text: "密码为空".tr, + textColor: themeController.currentColor.sc9, + ); + } else { + onConfirm(); + Get.back(); + } + }, + colors: [ + themeController.currentColor.sc1, + themeController.currentColor.sc2, + ], + child: Container( + width: MediaQuery.sizeOf(context).width * 0.115, + height: MediaQuery.sizeOf(context).height * 0.055, + constraints: BoxConstraints( + minWidth: 160.rpx, + minHeight: 90.rpx, + ), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "连接".tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + color: stringToColor("#333333"), + fontFamily: 'Inter', + fontSize: + AppConstants().normal_text_fontSize, + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox(width: 17.rpx)), + ), + ), + ), + ].divide(SizedBox( + width: 70.rpx, + )), + ), + ), + ], + ), + ), + ), + ); + }, + ); +} + +Future showTipDialog( + BuildContext context, + Widget widget, { + Color? backgroundColor, +}) { + ThemeController themeController = Get.find(); + BlueteethBindController blueteethBindController = Get.find(); + + return showDialog( + context: context, + barrierDismissible: true, + barrierColor: Colors.black.withOpacity(0.5), + builder: (BuildContext context) { + return FrostedDialog( + blurSigma: 3.0, + child: Container( + decoration: BoxDecoration( + color: backgroundColor ?? Colors.white, + borderRadius: BorderRadius.circular(20.0), + ), + padding: EdgeInsetsDirectional.fromSTEB(64.rpx, 0, 64.rpx, 0), + child: Container( + width: double.infinity, + constraints: BoxConstraints( + maxHeight: MediaQuery.sizeOf(context).height * 0.656, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Align( + alignment: AlignmentDirectional(0, 0), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 93.rpx, 0, 74.rpx), + child: widget, + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 19.rpx, 0, 60.rpx), + child: CustomCard( + gradientDirection: GradientDirection.vertical, + borderRadius: AppConstants().button_container_radius, + onTap: () { + Get.back(); // 关闭对话框 + }, + colors: [ + stringToColor("FCFCFC"), + stringToColor("CEECE3"), + ], + child: Container( + width: MediaQuery.sizeOf(context).width, + 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: stringToColor("#333333"), + fontFamily: 'Inter', + fontSize: AppConstants().normal_text_fontSize, + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox(width: 17.rpx)), + ), + ), + ), + ), + ], + ), + ), + ), + ); + }, + ); +} + +Future showUnBindTipDialog( + BuildContext context, + Widget widget, { + Color? backgroundColor, + VoidCallback? onConfirm, // “继续”按钮回调 + VoidCallback? onCancel, // “下次再说”按钮回调 +}) { + ThemeController themeController = Get.find(); + BlueteethBindController blueteethBindController = Get.find(); + + return showDialog( + context: context, + barrierDismissible: true, + barrierColor: Colors.black.withOpacity(0.5), + builder: (BuildContext context) { + return FrostedDialog( + blurSigma: 3.0, + child: Container( + decoration: BoxDecoration( + color: backgroundColor ?? themeController.currentColor.sc17, + borderRadius: BorderRadius.circular(20.0), + ), + padding: EdgeInsetsDirectional.fromSTEB(64.rpx, 0, 64.rpx, 0), + child: Container( + width: double.infinity, + constraints: BoxConstraints( + maxHeight: MediaQuery.sizeOf(context).height * 0.656, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Align( + alignment: AlignmentDirectional(0, 0), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 93.rpx, 0, 30.rpx), + child: widget, + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 0.rpx, 0, 10.rpx), + child: CustomCard( + borderRadius: AppConstants().button_container_radius, + onTap: () { + Get.back(); // 关闭对话框 + if (onConfirm != null) { + onConfirm(); // 调用确认回调 + } + }, + colors: [ + themeController.currentColor.sc1, + themeController.currentColor.sc2, + ], + child: Container( + width: MediaQuery.sizeOf(context).width, + 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, 19.rpx, 0, 60.rpx), + child: CustomCard( + borderRadius: AppConstants().button_container_radius, + onTap: () { + Get.back(); // 关闭对话框 + if (onCancel != null) { + onCancel(); // 调用取消回调 + } + }, + colors: [Colors.transparent], + child: Container( + width: MediaQuery.sizeOf(context).width, + height: MediaQuery.sizeOf(context).height * 0.055, + decoration: BoxDecoration( + border: Border.all( + color: Colors.white, + width: 0.5.rpx, + ), + borderRadius: BorderRadius.circular( + AppConstants().button_container_radius), + ), + 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/mh_page/device/component/DeviceComponentWidget.dart b/lib/pages/mh_page/device/component/DeviceComponentWidget.dart new file mode 100644 index 0000000..9582e67 --- /dev/null +++ b/lib/pages/mh_page/device/component/DeviceComponentWidget.dart @@ -0,0 +1,616 @@ +import 'dart:async'; + +import 'package:EasyDartModule/EasyDartModule.dart' as edm; +import 'package:EasyDartModule/base/logger/Logger.dart'; +import 'package:easydevice/easydevice.dart'; +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; +import 'package:vbvs_app/common/color/ServiceConstant.dart'; +import 'package:vbvs_app/common/color/app_uri_status.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'; +import 'package:vbvs_app/common/util/requestWithLog.dart'; +import 'package:vbvs_app/component/tool/ClickableContainer.dart'; +import 'package:vbvs_app/component/tool/CustomCard.dart'; +import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; +import 'package:vbvs_app/component/tool/cmd.dart'; +import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; +import 'package:vbvs_app/model/BleDeviceData.dart'; +import 'package:vbvs_app/model/api_response.dart'; +import 'package:vbvs_app/pages/mh_page/component/mht_bind_dialog.dart'; +import 'package:vbvs_app/pages/mh_page/device/controller/mht_bluetooth_controller.dart'; +import 'package:vbvs_app/pages/mh_page/device/model/BlueToothDataModel.dart'; + +class DeviceComponentWidget extends StatefulWidget { + BlueToothDataModel bleDevice; + + DeviceComponentWidget({ + super.key, + required this.bleDevice, + }); + + @override + State createState() => _DeviceComponentWidgetState(); +} + +class _DeviceComponentWidgetState extends State { + ThemeController themeController = Get.find(); + MHTBlueToothController blueteethBindController = Get.find(); + var lisObj; + + @override + Widget build(BuildContext context) { + Map device = { + "name": widget.bleDevice.name, + "mac": widget.bleDevice.mac, + "rssi": widget.bleDevice.scanResult.rssi, + "bind": widget.bleDevice.bind, + }; + return ClickableContainer( + backgroundColor: Colors.white, + highlightColor: Colors.white, + borderRadius: 20.rpx, + padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 36.rpx, 55.rpx, 52.rpx), + onTap: () async {}, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + Row( + children: [ + Text( + "${device['name']}", + style: TextStyle( + color: stringToColor("#333333"), fontSize: 30.rpx), + ), + SizedBox( + width: 10.rpx, + ), + Obx(() { + if (blueteethBindController.currentDeviceMac.value == + device['mac']) { + return SizedBox( + width: 24.rpx, + height: 24.rpx, + child: CircularProgressIndicator( + strokeWidth: 1, + valueColor: AlwaysStoppedAnimation( + stringToColor("#929699")), + ), + ); + } + return Container(); + }), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + width: MediaQuery.sizeOf(context).width * 0.14, + constraints: BoxConstraints(minWidth: 106.rpx), + child: Text( + "MAC".tr, + style: TextStyle( + color: stringToColor("#929699"), + fontSize: 26.rpx, + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ), + Text( + "${device['mac']}", + style: TextStyle( + fontSize: 26.rpx, + color: stringToColor("#333333")), + ), + ].divide(SizedBox(width: 33.rpx)), + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + width: MediaQuery.sizeOf(context).width * 0.14, + constraints: BoxConstraints(minWidth: 106.rpx), + child: Text( + "信号强度".tr, + style: TextStyle( + color: stringToColor("#929699"), + fontSize: 26.rpx, + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ), + Text( + "${device['rssi']}" + "dBm".tr, + style: TextStyle( + fontSize: 26.rpx, + color: stringToColor("#333333")), + ), + ].divide(SizedBox(width: 33.rpx)), + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + width: MediaQuery.sizeOf(context).width * 0.14, + constraints: BoxConstraints(minWidth: 106.rpx), + child: Text( + "设备状态".tr, + style: TextStyle( + color: stringToColor("#929699"), + fontSize: 26.rpx, + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ), + Text( + "${device['bind'] == true ? "已被绑定" : "可绑定"}", + style: TextStyle( + fontSize: 26.rpx, + color: device['bind'] == true + ? stringToColor("#FF7159") + : stringToColor("#6BFDAC"), + ), + ), + ].divide(SizedBox(width: 33.rpx)), + ), + ].divide(SizedBox(height: 37.rpx)), + ), + ), + CustomCard( + borderRadius: 16.rpx, + onTap: () async { + try { + // 连接前暂停扫描 + blueteethBindController.pauseScanning(); + if (blueteethBindController.currentDeviceMac?.value != + null && + blueteethBindController + .currentDeviceMac!.value.isNotEmpty) { + if (blueteethBindController.currentDeviceMac?.value != + device['mac']) { + showConfirmDialog( + context, Container(), "其他设备正在绑定中,是否终止其他设备绑定?".tr, + onConfirm: () { + blueteethBindController.currentDeviceMac.value = ""; + blueteethBindController.updateAll(); + }, onCancel: () { + blueteethBindController.resumeScanning(); + }); + } + } + + blueteethBindController.currentDeviceMac?.value = + device['mac']!; + blueteethBindController.updateAll(); + + if (device['bind'] == true) { + await showHaveBindDialog(context); + blueteethBindController.currentDeviceMac.value = ""; + blueteethBindController.resumeScanning(); + } else { + showConfirmDialog( + context, + Container(), + '是否确认绑定?'.tr, + onConfirm: () async { + //连接蓝牙 ,获取设备信息 + blueteethBindController.currentDeviceMac.value = + widget.bleDevice.mac; + blueteethBindController.updateAll(); + String mac = + await getBindTHMAC(context, widget.bleDevice); + if (mac != null && mac.isNotEmpty) { + bool flag = + await fillTHMac(mac, widget.bleDevice, context); + if (!flag) { + return; + } + } + blueteethBindController.currentFullDevice = + widget.bleDevice; + ApiResponse response = await blueteethBindController + .bindDeviceAndMAC(widget.bleDevice, context); + TopSlideNotification.show(context, + text: response.msg!); + if (response.code == HttpStatusCodes.ok) { + //更新设备绑定流程 + Get.toNamed("/mHTwifiPage",arguments: widget.bleDevice); + THapp bledevice = THapp( + device: widget.bleDevice.scanResult.device); + blueteethBindController.currentDevice = bledevice; + blueteethBindController.currentDeviceMac.value = ""; + } else { + blueteethBindController.resumeScanning(); + blueteethBindController.currentDeviceMac.value = ""; + blueteethBindController.updateAll(); + TopSlideNotification.show( + context, + text: response.msg ?? "蓝牙绑定.连接异常".tr, + textColor: themeController.currentColor.sc9, + ); + } + }, + onCancel: () { + print('用户点击了取消'); + blueteethBindController.currentDeviceMac.value = ""; + blueteethBindController.resumeScanning(); + blueteethBindController.updateAll(); + }, + ); + } + } catch (e) { + Navigator.pop(context); + TopSlideNotification.show( + context, + text: "连接异常".tr, + textColor: themeController.currentColor.sc9, + ); + edm.EasyDartModule.logger.info("连接异常: $e"); + DailyLogUtils.writeLog("连接异常: $e"); + } finally { + // 确保在任何情况下都恢复扫描 + if (blueteethBindController + .currentDeviceMac.value.isEmpty) { + blueteethBindController.resumeScanning(); + } + } + }, + colors: [stringToColor("1592AA"), stringToColor("006FA3")], + child: Container( + width: 150.rpx, + height: 90.rpx, + alignment: Alignment.center, + child: Text( + "添加".tr, + style: TextStyle(color: Colors.white, fontSize: 26.rpx), + ), + ), + ), + ], + ), + ].divide(SizedBox(height: 37.rpx)), + ), + ); + } + + //更新设备绑定状态 + void updateDeviceBindStatus(BleDeviceData device) { + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.user_setting; + String mac = device.mac!; + String type = "device_bind_status_${mac}"; + String queryUrl = "${serviceAddress}${serviceName}${serviceApi}"; + Map data = { + "type": type, + "mac": mac, + "wifi": false, + "celibration": false, + "person_info": false, + "time": DateTime.now().millisecondsSinceEpoch, + }; + requestWithLog( + logTitle: "更新用户绑定流程", + method: MyHttpMethod.put, + queryUrl: queryUrl, + data: data, + onSuccess: (res) {}, + onFailure: (res) {}, + ); + } + + Widget _buildDeviceInfoSection(device, BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0.rpx, 0.rpx, 30.rpx, 0.rpx), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + device.name ?? '蓝牙绑定.默认设备名称'.tr, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: const Color(0xFFF6FAFD), + fontSize: 30.rpx, + letterSpacing: 0.0, + ), + ), + Obx(() { + if (blueteethBindController.currentDeviceMac.value == + device.mac) { + return SizedBox( + width: 24.rpx, + height: 24.rpx, + child: CircularProgressIndicator( + strokeWidth: 1, + valueColor: AlwaysStoppedAnimation(Colors.white), + ), + ); + } + return Container(); + }), + ], + ), + ), + Row( + children: [ + Text( + "蓝牙绑定.信号强度".tr + ':${device.rssi ?? '-'}dBm', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: const Color(0xFFEBF2F8), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + SizedBox(width: 40.rpx), + Text( + "蓝牙绑定.SN".tr + ':${device.sn ?? '-'}', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: const Color(0xFFF5F9FD), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + ], + ), + Text( + "蓝牙绑定.蓝牙地址".tr + ':${device.deviceId ?? '-'}', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: const Color(0xFFF6FAFD), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + Text( + "蓝牙绑定.mac".tr + ':${device.mac ?? '-'}', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: const Color(0xFFF6FAFD), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + Row( + children: [ + Text( + "蓝牙绑定.网络".tr + + ':${device.isOnline == true ? '蓝牙绑定.在线'.tr : '蓝牙绑定.离线'.tr}', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: const Color(0xFFEBF2F8), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + SizedBox(width: 145.rpx), + Row( + children: [ + Text( + "蓝牙绑定.传感器".tr + ":", + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: const Color(0xFFEBF2F8), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + Text( + device.bind == false ? '蓝牙绑定.可绑定'.tr : '蓝牙绑定.已被绑定'.tr, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: device.bind == false + ? const Color(0xFF1AD2B5) + : themeController.currentColor.sc9, + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + ], + ), + ], + ), + Text( + "版本".tr + '${device.version ?? '-'}', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: const Color(0xFFF6FAFD), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox( + height: 37.rpx, + )), + ); + } + + //获取传感器mac + Future getBindTHMAC( + BuildContext context, BlueToothDataModel device) async { + const int maxRetries = 2; + const Duration timeout = Duration(seconds: 5); + String? macAddress; + + try { + // 连接设备 + THapp bledevice = THapp(device: device.scanResult.device); + await bledevice.connect(); + var res2 = bledevice.isConnected; + + if (!res2) { + edm.EasyDartModule.logger.error("蓝牙连接失败"); + DailyLogUtils.printLog("蓝牙连接失败"); + TopSlideNotification.show( + context, + text: "蓝牙连接失败".tr, + textColor: themeController.currentColor.sc9, + ); + throw Exception("蓝牙连接失败"); + } + + blueteethBindController.blueConnectFlag.value = 2; + blueteethBindController.currentDevice = bledevice; + + var read = bledevice.getresource('fff0/fff1'); + await read!.characteristic.setNotifyValue(true); + + var write = bledevice.getresource('fff0/fff2'); + + for (int attempt = 0; attempt < maxRetries; attempt++) { + var completer = Completer(); + + StreamSubscription? subscription; + subscription = read.characteristic.onValueReceived.listen((onData) { + if (onData.length >= 14) { + // 按照你提供的协议,返回数据长度至少 17字节,这里保险起见 14起步 + String parsedMac = parseMacFromBleResponse(onData); + completer.complete(parsedMac); + } + }); + + // 发送查询命令 + var order = [ + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0x00, + 0x03, + 0x40, + 0x01, + 0x01, + 0x00, + 0x45, + 0xFD + ]; + await write!.characteristic.write(order); + + try { + macAddress = await completer.future.timeout(timeout); + await subscription.cancel(); + break; // 成功拿到,跳出重试 + } catch (e) { + await subscription.cancel(); + if (attempt == maxRetries - 1) { + TopSlideNotification.show( + context, + text: "获取MAC地址超时,请重试".tr, + textColor: themeController.currentColor.sc9, + ); + throw Exception("获取MAC地址失败"); + } + } + } + + if (macAddress == null) { + throw Exception("未能获取到MAC地址"); + } + + // device.macA = macAddress; + print('MAC地址: $macAddress'); + return macAddress; + } catch (e) { + blueteethBindController.currentDeviceMac.value = ""; + edm.EasyDartModule.logger.error("蓝牙获取MAC失败:$e"); + DailyLogUtils.printLog("蓝牙获取MAC失败:$e"); + TopSlideNotification.show( + context, + text: "设备连接失败,请重试".tr, + textColor: themeController.currentColor.sc9, + ); + rethrow; + } + } + + fillTHMac( + String mac, BlueToothDataModel bleDevice, BuildContext context) async { + bool flag = false; + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.get_bluetooth_device_status; + String queryUrl = "$serviceAddress$serviceName$serviceApi"; + String macParam = + "mac=${Uri.encodeQueryComponent(mac.replaceAll(':', ''))}"; + if (queryUrl.contains('?')) { + queryUrl += '&$macParam'; + } else { + queryUrl += '?$macParam'; + } + + await requestWithLog( + logTitle: "获取设备状态", + method: MyHttpMethod.get, + queryUrl: queryUrl, + onSuccess: (res) { + flag = true; + if (res.code != HttpStatusCodes.ok) return; + + if (res.data != null && res.data is List) { + List responseList = res.data; + + // 查找当前MAC对应的数据 + String macKey = mac.replaceAll(':', '').toUpperCase(); + for (var item in responseList) { + if (item['mac'].toString().toUpperCase() == macKey) { + // 更新 bleDevice 的状态 + //如果传感器已经绑定 暂时不处理 + // bleDevice.bind = item['bind'] ?? bleDevice.bind; + bleDevice.macA = item['mac']; + if (item['bindMac'] != null && + item['bindMac'].toString().isNotEmpty) { + bleDevice.macB = item['bindMac']; + } + break; + } + } + } + }, + onFailure: (res) { + flag = false; + TopSlideNotification.show(context, + text: "获取设备状态失败".tr, textColor: themeController.currentColor.sc9); + }, + ); + return flag; + } +} + +String parseMacFromBleResponse(List data) { + // 先做简单的合法性判断 + if (data.length >= 17 && + data[0] == 0xFF && + data[1] == 0xFF && + data[2] == 0xFF && + data[3] == 0xFF && + data[6] == 0x40 && + data[7] == 0x01) { + // 取出 Byte8 ~ Byte13 + List macBytes = data.sublist(8, 14); + String macAddress = macBytes + .map((b) => b.toRadixString(16).padLeft(2, '0').toUpperCase()) + .join(''); + return macAddress; + } else { + throw Exception("BLE返回数据格式不正确"); + } +} diff --git a/lib/pages/mh_page/device/component/mht_device_calibration_controller.dart b/lib/pages/mh_page/device/component/mht_device_calibration_controller.dart new file mode 100644 index 0000000..80a4b26 --- /dev/null +++ b/lib/pages/mh_page/device/component/mht_device_calibration_controller.dart @@ -0,0 +1,42 @@ +import 'package:ef/ef.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'mht_device_calibration_controller.g.dart'; // 由json_serializable自动生成的部分 + +@JsonSerializable() +class MHTDeviceCalibrationModel { + MHTDeviceCalibrationModel(); + + factory MHTDeviceCalibrationModel.fromJson(Map json) { + try { + return _$MHTDeviceCalibrationModelFromJson(json); + } catch (e) { + return MHTDeviceCalibrationModel(); // 或者返回一个带有错误信息的特定DeviceInfoModel实例 + } + } + + // 序列化为JSON时的异常处理 + Map toJson() => _$MHTDeviceCalibrationModelToJson(this); +} + +class MHTDeviceCalibrationController + extends GetControllerEx { + MHTDeviceCalibrationController() { + attr = GetModel(MHTDeviceCalibrationModel()).obs; + } + + RxInt process = 0.obs; //校准流程 0.离床校准 1.位置校准 + RxInt bed_calibration = 0.obs; //0.未完成 1.完成 + RxInt position_calibration = 0.obs; //0.未完成 1.完成 + RxInt bed_type = 0.obs; //0.单人 1.双人 + RxString tips = "".obs; + RxInt flag = 0.obs; //0没有开始 1:校准中 2.校准完成 3.校准失败 + RxString statusContext = "".obs; + + + RxInt cd = 10000.obs; + bool forceStart = false; + + RxInt motionTips = 0.obs;//体动提示 + RxInt inBedTips = 0.obs;//在床提示 +} diff --git a/lib/pages/mh_page/device/component/mht_device_calibration_controller.g.dart b/lib/pages/mh_page/device/component/mht_device_calibration_controller.g.dart new file mode 100644 index 0000000..b01df51 --- /dev/null +++ b/lib/pages/mh_page/device/component/mht_device_calibration_controller.g.dart @@ -0,0 +1,15 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'mht_device_calibration_controller.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +MHTDeviceCalibrationModel _$MHTDeviceCalibrationModelFromJson( + Map json) => + MHTDeviceCalibrationModel(); + +Map _$MHTDeviceCalibrationModelToJson( + MHTDeviceCalibrationModel instance) => + {}; diff --git a/lib/pages/mh_page/device/controller/mht_bluetooth_controller.dart b/lib/pages/mh_page/device/controller/mht_bluetooth_controller.dart new file mode 100644 index 0000000..dd76dca --- /dev/null +++ b/lib/pages/mh_page/device/controller/mht_bluetooth_controller.dart @@ -0,0 +1,264 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:EasyDartModule/EasyDartModule.dart'; +import 'package:easydevice/src/app/thapp.dart'; +import 'package:ef/ef.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'package:vbvs_app/common/color/ServiceConstant.dart'; +import 'package:vbvs_app/common/color/app_uri_status.dart'; +import 'package:vbvs_app/common/util/DailyLogUtils.dart'; +import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; +import 'package:vbvs_app/model/api_response.dart'; +import 'package:vbvs_app/pages/mh_page/device/model/BlueToothDataModel.dart'; +import 'package:vbvs_app/pages/mh_page/user/controller/mht_register_controller.dart'; + +part 'mht_bluetooth_controller.g.dart'; + +@JsonSerializable() +class MHTBlueToothModel { + bool? bluetooth = false; //蓝牙开关 + double? singal = -100; + + @JsonKey(ignore: true) + List? blueRawData; //蓝牙原始数据 + @JsonKey(ignore: true) + List? deviceDataStatus; //已经请求过状态的数据 + + int? read = 0; + int? deviceType; //绑定设备类型 + + bool wifiPassShow = false; + String? wifiPass; + + MHTBlueToothModel(); + + static MHTBlueToothModel fromJson(Map json) => + _$MHTBlueToothModelFromJson(json); + Map toJson() => _$MHTBlueToothModelToJson(this); +} + +class MHTBlueToothController extends GetControllerEx { + MHTBlueToothController() { + attr = GetModel(MHTBlueToothModel()).obs; + } + + Timer? _statusTimer; + MHTRegisterController registerController = Get.find(); + + RxString search = "".obs; + + RxString currentDeviceMac = "".obs; //当前正在绑定的设备,用来显示loading + + THapp? currentDevice; //当前连接的设备 + BlueToothDataModel? currentFullDevice; //当前连接的设备 + + RxInt blueConnectFlag = 0.obs; //当前蓝牙连接状态 0.正在连接 1.未连接 2.已连接 + + RxBool bluetoothStatus = false.obs; //蓝牙开启状态 + RxInt connectStatus = 0.obs; //当前wifi连接状态 0:未连接 1:已连接 + RxInt netType = 0.obs; //当前网络类型 0.正在检测 1.wifi 2.4g设备 3.未知 + RxInt wifiConnectStatus = 1.obs; //获取wifi状态 0.正在检测 1.已检测完 + + RxMap selectWifi = {}.obs; //正在连接wifi信息 + + int returnPage = 0; //0返回首页 1.返回设备列表 + + RxInt wifiStatus = 0.obs; //wifi连接状态 0:未连接 1:已连接 + RxList wifiList = [].obs; + RxMap connect_wifi = {}.obs; + RxString? cid = "".obs; + + void startStatusPolling() { + updateDeviceStatus().then((res) { + if (res.code == HttpStatusCodes.ok) { + updateAll(); + } else { + safeShowNotification(res.msg ?? "获取设备状态异常"); + EasyDartModule.logger.info("获取设备状态异常: $res"); + DailyLogUtils.writeLog("获取设备状态异常: $res"); + } + }); + + if (_statusTimer == null) { + _statusTimer = Timer.periodic(Duration(seconds: 2), (timer) { + updateDeviceStatus().then((res) { + if (res.code == HttpStatusCodes.ok) { + updateAll(); + } else { + safeShowNotification(res.msg ?? "获取设备状态异常"); + EasyDartModule.logger.info("获取设备状态异常: $res"); + DailyLogUtils.writeLog("获取设备状态异常: $res"); + } + }).catchError((e, stack) { + print("updateDeviceStatus 执行异常: $e\n$stack"); + safeShowNotification("设备状态请求失败"); + EasyDartModule.logger.info("设备状态异常: $e"); + DailyLogUtils.writeLog("设备状态异常: $e"); + }); + }); + } + } + + void stopStatusPolling() { + _statusTimer?.cancel(); + _statusTimer = null; + } + + var shouldScan = true.obs; + + void pauseScanning() { + shouldScan.value = false; + update(); + } + + // 恢复扫描 + void resumeScanning() { + shouldScan.value = true; + update(); + } + + Future updateDeviceStatus() async { + try { + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.get_bluetooth_device_status; + String queryUrl = "$serviceAddress$serviceName$serviceApi"; + + if (model.blueRawData != null && model.blueRawData!.isNotEmpty) { + final macParams = model.blueRawData! + .map((device) => + "mac=${Uri.encodeQueryComponent(device.mac!.replaceAll(':', ''))}") + .join("&"); + + if (queryUrl.contains('?')) { + queryUrl += '&$macParams'; + } else { + queryUrl += '?$macParams'; + } + + String? language = ""; + if (languageController.selectLanguage != null) { + language = languageController.selectLanguage.value!.language_code; + } + if (language != null && language.isNotEmpty) { + if (queryUrl.contains("?")) { + queryUrl += "&lang=$language"; + } else { + queryUrl += "?lang=$language"; + } + } + + var response = await EasyDartModule.dio.get(queryUrl); + var responseData = + response.data is String ? jsonDecode(response.data) : response.data; + ApiResponse res = + ApiResponse.fromJson(responseData, (object) => object); + if (res.code != HttpStatusCodes.ok) return res; + + if (response.data['data'] != null && response.data['data'] is List) { + List responseList = response.data['data']; + + // 新建一个 Map,便于快速通过 mac 查找返回的设备状态 + final Map responseMap = { + for (var item in responseList) + item['mac'].toString().toUpperCase(): item + }; + + // 遍历 blueRawData,更新 bind 状态 + for (var device in model.blueRawData!) { + final macKey = device.mac!.replaceAll(':', '').toUpperCase(); + if (responseMap.containsKey(macKey)) { + var item = responseMap[macKey]; + // 更新 device 绑定状态等信息 + device.bind = item['bind'] ?? device.bind; + device.mac = item['bindMac'] ?? device.mac; + } + } + model.deviceDataStatus = + List.from(model.blueRawData!); + } else { + model.deviceDataStatus = []; + } + + updateAll(); + return res; + } else { + model.deviceDataStatus = []; + } + } catch (e) { + print("获取设备状态异常: $e"); + EasyDartModule.logger.info("获取设备状态异常: $e"); + DailyLogUtils.writeLog("获取设备状态异常: $e"); + return ApiResponse(code: -1, msg: "请求失败".tr); + } + return ApiResponse(code: -1, msg: "未知错误".tr); + } + + bindDeviceAndMAC(BlueToothDataModel bleDevice, BuildContext context) async { + try { + if ((bleDevice.macA == null || bleDevice.macA.isEmpty) && + (bleDevice.macB == null || bleDevice.macB.isEmpty)) { + TopSlideNotification.show(context, + text: "传感器mac读取失败".tr, textColor: themeController.currentColor.sc9); + currentDeviceMac.value = ""; + return; + } + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.device_bind; + String queryUrl = "$serviceAddress$serviceName$serviceApi"; + var data = { + "deviceType": model.deviceType, + "mac": bleDevice.mac, + "macA": bleDevice.macA, + if (bleDevice.macB != null && bleDevice.macB!.isNotEmpty) + "macB": bleDevice.macB, + }; + + var response = + await EasyDartModule.dio.post(queryUrl, data: jsonEncode(data)); + if (response != null) { + var responseData = + response.data is String ? jsonDecode(response.data) : response.data; + ApiResponse res = + ApiResponse.fromJson(responseData, (object) => object); + MyUtils.formatResponse(res, "绑定成功".tr, "绑定成功".tr); + + if (res.code == HttpStatusCodes.ok) { + // PersonController personController = Get.find(); + // personController.currentPersonId.value = res.data['id']; + currentDeviceMac.value = ""; + return res; + } else { + return res; + } + } else { + return ApiResponse(code: -1, msg: "服务器失败".tr); + } + } catch (e) { + EasyDartModule.logger.info("绑定异常: $e"); + DailyLogUtils.writeLog("蓝牙绑定: $e"); + } + return ApiResponse(code: -1, msg: "未知错误".tr); + } +} + +void safeShowNotification(String msg) { + try { + final ctx = Get.context; + if (ctx != null && ctx.mounted) { + TopSlideNotification.show( + ctx, + text: msg, + textColor: themeController.currentColor.sc9, + ); + } else { + print("TopSlideNotification 未显示:context 不可用或未挂载"); + } + } catch (e, stack) { + // print("TopSlideNotification 显示异常: $e\n$stack"); + } +} diff --git a/lib/pages/mh_page/device/controller/mht_bluetooth_controller.g.dart b/lib/pages/mh_page/device/controller/mht_bluetooth_controller.g.dart new file mode 100644 index 0000000..c90ec39 --- /dev/null +++ b/lib/pages/mh_page/device/controller/mht_bluetooth_controller.g.dart @@ -0,0 +1,26 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'mht_bluetooth_controller.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +MHTBlueToothModel _$MHTBlueToothModelFromJson(Map json) => + MHTBlueToothModel() + ..bluetooth = json['bluetooth'] as bool? + ..singal = (json['singal'] as num?)?.toDouble() + ..read = (json['read'] as num?)?.toInt() + ..deviceType = (json['deviceType'] as num?)?.toInt() + ..wifiPassShow = json['wifiPassShow'] as bool + ..wifiPass = json['wifiPass'] as String?; + +Map _$MHTBlueToothModelToJson(MHTBlueToothModel instance) => + { + 'bluetooth': instance.bluetooth, + 'singal': instance.singal, + 'read': instance.read, + 'deviceType': instance.deviceType, + 'wifiPassShow': instance.wifiPassShow, + 'wifiPass': instance.wifiPass, + }; diff --git a/lib/pages/mh_page/device/mht_bind_device_type.dart b/lib/pages/mh_page/device/mht_bind_device_type.dart new file mode 100644 index 0000000..887f66b --- /dev/null +++ b/lib/pages/mh_page/device/mht_bind_device_type.dart @@ -0,0 +1,229 @@ +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:vbvs_app/common/color/app_uri_status.dart'; +import 'package:vbvs_app/common/util/FitTool.dart'; +import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/component/tool/CustomCard.dart'; +import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; +import 'package:vbvs_app/controller/device/device_type_controller.dart'; +import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; +import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; +import 'package:vbvs_app/controller/user_info_controller.dart'; +import 'package:vbvs_app/pages/mh_page/device/controller/mht_bluetooth_controller.dart'; + +class MHTBindDeviceTypePage extends StatefulWidget { + int type = 0; //0绑定设备 1.查询我的设备 + MHTBindDeviceTypePage({super.key, this.type = 0}); + + @override + State createState() => _MHTBindDeviceTypePageState(); +} + +class _MHTBindDeviceTypePageState extends State { + GlobalController globalController = Get.find(); + UserInfoController userInfoController = Get.find(); + // BlueteethBindController blueteethBindController = Get.find(); + MHTBlueToothController blueteethBindController = Get.find(); + ThemeController themeController = Get.find(); + DeviceTypeController deviceTypeController = Get.find(); + + @override + void initState() { + super.initState(); + deviceTypeController.getDeviceType().then((response) { + if (response.code != HttpStatusCodes.ok) { + // 延迟到 widget 构建后再执行 show + TopSlideNotification.show( + context, // 用当前页面 context 替代 Get.context! + text: response.msg ?? "服务器.失败".tr, + textColor: themeController.currentColor.sc9, + ); + } + }); + } + + @override + Widget build(BuildContext context) { + int read = blueteethBindController.model.read!; + if (blueteethBindController.model.read == 1) { + //需要弹窗显示教程 + } + return LayoutBuilder( + builder: (context, bodySize) => GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/images/new_background.png'), // 本地图片 + fit: BoxFit.fill, // 填满整个 Container + ), + ), + child: Scaffold( + backgroundColor: Colors.transparent, // 加上这一行 + appBar: AppBar( + backgroundColor: Colors.transparent, + // backgroundColor: Colors.transparent, + automaticallyImplyLeading: false, + iconTheme: IconThemeData(color: themeController.currentColor.sc3), + titleSpacing: 0, + // leading: returnIconButtom, + title: Container( + width: double.infinity, + height: 180.rpx, + child: Stack( + alignment: Alignment.center, + children: [ + /// 居中标题 + Text( + '添加设备'.tr, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Readex Pro', + color: themeController.currentColor.sc3, + letterSpacing: 0, + fontSize: 30.rpx, + ), + ), + + /// 左边返回按钮 + Positioned( + left: 0, + child: returnIconButtom, + ), + ], + ), + ), + + actions: [], + centerTitle: false, + ), + + body: SafeArea( + top: true, + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 0, 30.rpx, 0), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // 使用 Obx 来监听 deviceTypeList 的变化 + SizedBox(height: 26.rpx), // 开始的间隔 + Text( + "选择类型", + style: TextStyle(color: Colors.white, fontSize: 30.rpx), + ), + SizedBox(height: 32.rpx), // 开始的间隔 + Obx(() { + return Column( + children: [ + ...deviceTypeController.deviceTypeList.value + .map((device) { + return Padding( + padding: EdgeInsets.only( + bottom: 26.rpx), // 添加每个设备之间的间隔 + child: _buildDeviceCard( + context, + title: device['name'], // 这里假设 device 是一个 Map + imageUrl: device['image'], + type: device['type'], + desc: device['desc'] ?? [], + reg: device['device_reg'] ?? [], + ), + ); + }).toList(), + SizedBox(height: 26.rpx), // 结束的间隔 + ], + ); + }), + ], + ), + ), + ), + ), + ), + ), + ), + ); + } + + Widget _buildDeviceCard(BuildContext context, + {required String title, + required String imageUrl, + required double type, + required List? desc, + required List? reg}) { + if (type == 1) { + return SizedBox.shrink(); + } + + Map data = { + "reg":reg, + "type":type, + }; + return CustomCard( + borderRadius: 20.rpx, + onTap: () { + MHTBlueToothController mhtBlueToothController = Get.find(); + mhtBlueToothController.model.deviceType = type.toInt(); + Get.toNamed("/mHTBlueteethDevicePage", arguments: data); + }, + colors: [Colors.white], + child: Container( + width: double.infinity, + height: MediaQuery.sizeOf(context).height * 0.135, + constraints: BoxConstraints(minHeight: 220.rpx), + padding: EdgeInsets.symmetric(horizontal: 30.rpx), + child: Row( + children: [ + SizedBox(width: 58.rpx), + ClipRRect( + borderRadius: BorderRadius.circular(8.rpx), + child: Image.network( + imageUrl, + width: 177.rpx, + height: 85.rpx, + fit: BoxFit.cover, + ), + ), + SizedBox(width: 30.rpx), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + title, + style: TextStyle( + fontFamily: 'Inter', + color: stringToColor("#333333"), + fontSize: 30.rpx, + letterSpacing: 0.0, + fontWeight: FontWeight.w500, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + if (desc != null && desc.isNotEmpty) ...[ + SizedBox(height: 10.rpx), + Text( + desc.join(' '), + style: TextStyle( + fontFamily: 'Inter', + color: stringToColor("#666666"), + fontSize: 24.rpx, + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.left, // 或 center 看你整体布局需求 + ), + ], + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/pages/mh_page/device/mht_blueteeth_device_page.dart b/lib/pages/mh_page/device/mht_blueteeth_device_page.dart new file mode 100644 index 0000000..c518bc0 --- /dev/null +++ b/lib/pages/mh_page/device/mht_blueteeth_device_page.dart @@ -0,0 +1,717 @@ +import 'dart:async'; + +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; +import 'package:permission_handler/permission_handler.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/controller/main_bottom/global_controller.dart'; +import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; +import 'package:vbvs_app/controller/user_info_controller.dart'; +import 'package:vbvs_app/pages/mh_page/component/mht_bind_dialog.dart'; +import 'package:vbvs_app/pages/mh_page/device/component/DeviceComponentWidget.dart'; +import 'package:vbvs_app/pages/mh_page/device/controller/mht_bluetooth_controller.dart'; +import 'package:vbvs_app/pages/mh_page/device/model/BlueToothDataModel.dart'; + +class MHTBlueteethDevicePage extends StatefulWidget { + var data; + MHTBlueteethDevicePage({super.key, required this.data}); + + @override + State createState() => _MHTBlueteethDevicePageState(); +} + +class _MHTBlueteethDevicePageState extends State { + MHTBlueToothController mhtBlueToothController = Get.find(); + GlobalController globalController = Get.find(); + UserInfoController userInfoController = Get.find(); + ThemeController themeController = Get.find(); + late FlutterBluePlus flutterBlue; + List scanResults = []; + bool isScanning = false; + Timer? _timer; + bool _isDialogShowing = false; + + var currentConnectedDeviceProp; + var connectDeviceCurrent = null; + List bleDevice = []; + String currentMsg = "寻找设备中..."; + Timer? connectTimer; + bool isFind = false; + List bindArrBackup = []; + List bindArr = ["", "", ""]; + StreamSubscription>? _scanSubscription; + + @override + void initState() { + super.initState(); + mhtBlueToothController.model.blueRawData = []; + mhtBlueToothController.model.deviceDataStatus = []; + flutterBlue = FlutterBluePlus(); + _checkBluetoothPermission(); + mhtBlueToothController.startStatusPolling(); + mhtBlueToothController.search.value = ""; + mhtBlueToothController.currentDeviceMac?.value = ""; + } + + Future _checkBluetoothPermission() async { + PermissionStatus bluetoothStatus = await Permission.bluetooth.status; + PermissionStatus locationStatus = await Permission.location.status; + + if (bluetoothStatus.isGranted && locationStatus.isGranted) { + _startScanning(); + _startPeriodicScan(); + } else { + _requestBluetoothPermission(); + } + } + + Future _requestBluetoothPermission() async { + Map statuses = await [ + Permission.bluetoothScan, + Permission.bluetoothConnect, + Permission.location, + ].request(); + + bool allGranted = statuses[Permission.bluetoothScan]?.isGranted == true && + statuses[Permission.bluetoothConnect]?.isGranted == true && + statuses[Permission.location]?.isGranted == true; + + if (allGranted) { + _startScanning(); + _startPeriodicScan(); + } else { + _showPermissionDeniedDialog(); + } + } + + void _showPermissionDeniedDialog() { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text("权限提示".tr), + content: Text("应用需要蓝牙和位置权限才能扫描设备。请授予权限。".tr), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text("确定".tr), + ), + ], + ); + }, + ); + } + + void _startScanning() async { + if (!mounted || isScanning || !mhtBlueToothController.shouldScan.value) return; + + _scanSubscription?.cancel(); + var bluetoothState = await FlutterBluePlus.isOn; + mhtBlueToothController.model.bluetooth = bluetoothState; + mhtBlueToothController.updateAll(); + + if (!bluetoothState && !_isDialogShowing) { + _isDialogShowing = true; + mhtBlueToothController.model.blueRawData = []; + mhtBlueToothController.model.deviceDataStatus = []; + mhtBlueToothController.updateAll(); + await _showBluetoothNotEnabledDialog(); + _isDialogShowing = false; + return; + } + + if (!isScanning) { + setState(() { + isScanning = true; + }); + + await FlutterBluePlus.startScan(timeout: Duration(seconds: 10)); + + _scanSubscription = FlutterBluePlus.scanResults.listen((results) { + if (!mounted) return; + + final signalThreshold = mhtBlueToothController.model.singal!; + final searchKey = mhtBlueToothController.search.value.trim().toLowerCase(); + + final filteredResults = results.where((r) { + final localName = r.advertisementData.localName; + final isTarget = r.rssi > signalThreshold && + isTargetDevice(localName, widget.data['reg'].cast()); + + if (!isTarget) return false; + + final name = r.advertisementData.advName.toLowerCase(); + String macAddress = r.device.remoteId.str; + final mac = macAddress.replaceAll(':', ''); + final search = searchKey.trim().replaceAll(':', '').toLowerCase(); + + if (search.isNotEmpty && + !name.contains(search) && + !mac.replaceAll(':', '').toLowerCase().contains(search)) { + return false; + } + + return true; + }).map((r) { + return BlueToothDataModel.fromScanResult( + r, widget.data['type']?.toInt(), + bind: false, + name: r.advertisementData.localName, + mac: r.device.remoteId.str.replaceAll(':', '')); + }).toList(); + + setState(() { + mhtBlueToothController.model.blueRawData = filteredResults; + }); + }); + + await Future.delayed(Duration(seconds: 10)); + await FlutterBluePlus.stopScan(); + + if (mounted) { + setState(() { + isScanning = false; + }); + } + } + } + + void _startPeriodicScan() { + _timer = Timer.periodic(Duration(seconds: 10), (timer) { + if (mhtBlueToothController.shouldScan.value && !isScanning) { + _startScanning(); + } + }); + } + + void _stopScanning() { + if (isScanning) { + FlutterBluePlus.stopScan(); + _scanSubscription?.cancel(); + if (mounted) { + setState(() { + isScanning = false; + }); + } + } + } + + void _stopPeriodicScan() { + _timer?.cancel(); + } + + @override + void dispose() { + _stopPeriodicScan(); + _stopScanning(); + _scanSubscription?.cancel(); + connectTimer?.cancel(); + mhtBlueToothController.stopStatusPolling(); + mhtBlueToothController.model.blueRawData = []; + mhtBlueToothController.model.deviceDataStatus = []; + super.dispose(); + } + + bool isTargetDevice(String? name, List keywords) { + if (name == null) return false; + return keywords.any((k) => name.contains(k)); + } + + _showBluetoothNotEnabledDialog() async { + await showTipDialog( + backgroundColor: Colors.white, + context, + Column( + children: [ + Text( + "蓝牙未开启".tr, + style: TextStyle( + fontSize: AppConstants().title_text_fontSize, + color: stringToColor("#333333"), + ), + ), + SizedBox( + height: 20.rpx, + ), + Text( + "请先打开蓝牙在进行设备扫描".tr, + style: TextStyle( + fontSize: AppConstants().normal_text_fontSize, + color: stringToColor("#333333"), + ), + ), + ], + )); + } + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, boxConstraints) => GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/images/new_background.png'), + fit: BoxFit.fill, + ), + ), + child: Scaffold( + resizeToAvoidBottomInset: false, + backgroundColor: Colors.transparent, + appBar: AppBar( + iconTheme: IconThemeData(color: themeController.currentColor.sc3), + backgroundColor: Colors.transparent, + automaticallyImplyLeading: false, + titleSpacing: 0, + title: Container( + width: double.infinity, + height: 180.rpx, + child: Stack( + alignment: Alignment.center, + children: [ + Text( + '添加设备'.tr, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Readex Pro', + color: themeController.currentColor.sc3, + letterSpacing: 0, + fontSize: 30.rpx, + ), + ), + Positioned( + left: 0, + child: returnIconButtom, + ), + ], + ), + ), + actions: [], + centerTitle: false, + ), + body: SafeArea( + top: true, + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 0, 30.rpx, 0), + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + child: Column( + children: [ + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 30.rpx, 0, 0), + child: Container( + width: double.infinity, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + stringToColor("FCFCFC"), + stringToColor("CEECE3") + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + borderRadius: BorderRadius.circular(20.rpx), + ), + child: Align( + alignment: AlignmentDirectional(0, 0), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0, 30.rpx, 0, 30.rpx), + child: Obx(() { + return Text( + (mhtBlueToothController.model.bluetooth == + null || + mhtBlueToothController + .model.bluetooth == + false) + ? "等待扫描".tr + : '扫描中'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + color: stringToColor("#003058"), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ); + }), + ), + ), + ), + ), + Container( + width: double.infinity, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + stringToColor("FCFCFC"), + stringToColor("CEECE3") + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + borderRadius: BorderRadius.circular(20.rpx), + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 21.rpx, 5.rpx, 21.rpx, 5.rpx), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Text( + '最小信号强度'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + color: stringToColor("#003058"), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + Expanded( + child: Obx(() { + return Slider( + activeColor: Color(0xFF1FCC9B), + inactiveColor: + FlutterFlowTheme.of(context) + .alternate, + min: -100, + max: 50, + value: + mhtBlueToothController.model.singal!, + onChanged: (newValue) { + newValue = double.parse( + newValue.toStringAsFixed(0)); + mhtBlueToothController.model.singal = + newValue; + _startScanning(); + mhtBlueToothController.updateAll(); + }, + ); + }), + ), + Obx(() { + return Text( + '${mhtBlueToothController.model.singal!.toInt()}', + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + color: stringToColor("#003058"), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ); + }), + ].divide(SizedBox(width: 30.rpx)), + ), + ), + ), + Container( + width: double.infinity, + decoration: BoxDecoration( + color: themeController.currentColor.sc3, + borderRadius: BorderRadius.circular(20.rpx), + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 35.rpx, 0, 35.rpx, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0, 0.rpx, 0, 0), + child: Container( + width: 25.rpx, + height: 25.rpx, + decoration: BoxDecoration(), + child: SvgPicture.asset( + 'assets/img/icon/query.svg', + fit: BoxFit.cover, + color: stringToColor("#333333"), + ), + ), + ), + Expanded( + child: Container( + width: 100.rpx, + height: 90.rpx, + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context) + .secondaryBackground, + ), + child: Align( + alignment: + AlignmentDirectional(-1, 0), + child: TextFormField( + initialValue: + mhtBlueToothController + .search.value, + onChanged: (Value) { + mhtBlueToothController + .search.value = Value; + }, + autofocus: false, + obscureText: false, + decoration: InputDecoration( + isDense: true, + labelStyle: + FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + hintText: '检索设备'.tr, + hintStyle: + FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.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: FlutterFlowTheme.of( + context) + .error, + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular( + 8.rpx), + ), + focusedErrorBorder: + OutlineInputBorder( + borderSide: BorderSide( + color: FlutterFlowTheme.of( + context) + .error, + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular( + 8.rpx), + ), + filled: false, + fillColor: themeController + .currentColor.sc22, + ), + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + cursorColor: + stringToColor("#003058"), + ), + ), + ), + ), + ].divide(SizedBox(width: 6.rpx)), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 26.rpx, 0, 0, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + SizedBox( + height: 50.rpx, + child: VerticalDivider( + thickness: 2.rpx, + color: stringToColor("#333333"), + ), + ), + ClickableContainer( + backgroundColor: Colors.transparent, + highlightColor: + themeController.currentColor.sc4, + borderRadius: 6.rpx, + padding: EdgeInsets.zero, + onTap: () async { + _startScanning(); + }, + child: Text( + '搜索'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + color: stringToColor("#333333"), + ), + ), + ), + ].divide(SizedBox(width: 26.rpx)), + ), + ), + ], + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0, 60.rpx, 0, 32.rpx), + child: Container( + width: double.infinity, + decoration: BoxDecoration(), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 19.rpx, 0, 0, 0), + child: Obx(() { + return Text( + '匹配出的外围设备'.tr + + "(${mhtBlueToothController.model.deviceDataStatus!.length})", + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc3, + ), + ); + }), + ), + ), + ), + Obx(() { + if (mhtBlueToothController + .model.deviceDataStatus!.isNotEmpty) { + return Expanded( + child: Container( + width: double.infinity, + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + ...mhtBlueToothController + .model.deviceDataStatus! + .map((device) { + return DeviceComponentWidget( + bleDevice: device, + ); + }) + .toList() + .divide(SizedBox(height: 30.rpx)) + .addToEnd(SizedBox(height: 30.rpx)), + ], + ), + ), + ), + ); + } + return Container(); + }), + ].divide(SizedBox( + height: 30.rpx, + )), + )), + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 52.rpx, 0, 0), + child: Container( + width: double.infinity, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20.rpx), + border: Border.all( + color: themeController.currentColor.sc4 + .withOpacity(0.5), + width: AppConstants().border_width, + ), + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 30.rpx, 30.rpx, 30.rpx), + child: Row( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0, 8.rpx, 0, 0), + child: Container( + width: 23.rpx, + height: 23.rpx, + decoration: BoxDecoration(), + child: SvgPicture.asset( + 'assets/img/icon/tips.svg', + fit: BoxFit.cover, + color: themeController.currentColor.sc4, + ), + ), + ), + Expanded( + child: Text( + '蓝牙绑定提示'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + color: themeController.currentColor.sc4, + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + ), + ].divide(SizedBox(width: 23.rpx)), + ), + ), + ), + ), + ].divide(SizedBox(height: 30.rpx)), + ), + ), + ), + ), + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/pages/mh_page/device/mht_device_calibration.dart b/lib/pages/mh_page/device/mht_device_calibration.dart new file mode 100644 index 0000000..e7f742d --- /dev/null +++ b/lib/pages/mh_page/device/mht_device_calibration.dart @@ -0,0 +1,1060 @@ +import 'dart:async'; + +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; +import 'package:vbvs_app/common/color/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/tool/ClickableContainer.dart'; +import 'package:vbvs_app/component/tool/CustomCard.dart'; +import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; +import 'package:vbvs_app/pages/device_bind/componnet/CalibrationProgressWidget.dart'; +import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart'; +import 'package:vbvs_app/pages/mh_page/device/component/mht_device_calibration_controller.dart'; +import 'package:vbvs_app/pages/mh_page/device/controller/mht_bluetooth_controller.dart'; + +class MHTCalibrationPage extends StatefulWidget { + MHTCalibrationPage({super.key}); + + @override + State createState() => _MHTCalibrationPageState(); +} + +class _MHTCalibrationPageState extends State { + MHTDeviceCalibrationController deviceCalibrationController = Get.find(); + MHTBlueToothController blueteethBindController = Get.find(); + final ValueNotifier progressNotifier = ValueNotifier(0.0); + final ValueNotifier failureNotifier = ValueNotifier(false); + Timer? _pollingTimer; + + bool exit = false; + @override + void initState() { + super.initState(); + deviceCalibrationController.process.value = 0; + deviceCalibrationController.cd.value = 10000; + deviceCalibrationController.flag.value = 0; + deviceCalibrationController.bed_calibration.value = 0; + deviceCalibrationController.position_calibration.value = 0; + blueteethBindController.cid!.value = ""; + deviceCalibrationController.motionTips.value = 0; + deviceCalibrationController.inBedTips.value = 0; + deviceCalibrationController.statusContext.value = ""; + deviceCalibrationController.bed_type.value = + (blueteethBindController.currentFullDevice?.macA?.isNotEmpty == true && + blueteethBindController.currentFullDevice?.macB?.isNotEmpty == + true) + ? 1 + : 0; + } + + @override + void dispose() { + _pollingTimer?.cancel(); + blueteethBindController.cid!.value = ""; + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return WillPopScope( + child: LayoutBuilder( + builder: (context, bodySize) => GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/images/new_background.png'), + fit: BoxFit.fill, + ), + ), + child: Scaffold( + backgroundColor: Colors.transparent, + appBar: AppBar( + backgroundColor: Colors.transparent, + automaticallyImplyLeading: false, + iconTheme: + IconThemeData(color: themeController.currentColor.sc3), + titleSpacing: 0, + title: Container( + width: double.infinity, + height: 180.rpx, + child: Stack( + alignment: Alignment.center, + children: [ + Text( + '设备校准'.tr, + style: TextStyle( + fontFamily: 'Readex Pro', + color: themeController.currentColor.sc3, + letterSpacing: 0, + fontSize: 30.rpx, + ), + ), + Positioned( + left: 0, + child: returnIconButtomAddCallback(() { + if (deviceCalibrationController.flag.value != 2) { + try { + showConfirmDialog( + context, Container(), "校准未完成提示".tr, + onConfirm: () async { + exit = true; + deviceCalibrationController.process.value = 0; + deviceCalibrationController + .bed_calibration.value = 0; + deviceCalibrationController + .position_calibration.value = 0; + blueteethBindController.cid!.value = ""; + Get.back(); + }, onCancel: () { + exit = false; + }); + } catch (e) { + print(e); + } + } else { + exit = true; + } + if (exit) { + Get.back(); + } + }, enableBack: exit), + ), + Positioned( + right: 20.rpx, + child: CustomCard( + borderRadius: 20.rpx, + onTap: () async { + if (deviceCalibrationController.flag.value != 2) { + showConfirmDialog( + context, Container(), "校准未完成提示".tr, + onConfirm: () async { + // await Get.toNamed("/personPage"); + Get.offNamed("/bindDeviceSuccess"); + deviceCalibrationController.process.value = 0; + deviceCalibrationController + .bed_calibration.value = 0; + deviceCalibrationController + .position_calibration.value = 0; + blueteethBindController.cid!.value = ""; + }, onCancel: () {}); + } else { + // await Get.toNamed("/personPage"); + Get.offNamed("/bindDeviceSuccess"); + deviceCalibrationController.process.value = 0; + deviceCalibrationController + .bed_calibration.value = 0; + deviceCalibrationController + .position_calibration.value = 0; + blueteethBindController.cid!.value = ""; + } + }, + colors: [ + stringToColor("#84F5FF"), + ], + child: Container( + width: 140.rpx, + height: 60.rpx, + alignment: Alignment.center, + padding: EdgeInsetsDirectional.fromSTEB( + 16.rpx, 0, 16.rpx, 0), + child: Text( + '下一步'.tr, + style: TextStyle( + fontFamily: 'Inter Tight', + color: stringToColor("#011D33"), + letterSpacing: 0.0, + ), + ), + ), + ), + ), + ], + ), + ), + actions: [], + centerTitle: false, + ), + body: SafeArea( + top: true, + // child: Container(), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 0.rpx, 30.rpx, 0.rpx), + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 30.rpx, 0, 0), + child: Container( + width: double.infinity, + decoration: BoxDecoration( + color: stringToColor("#003058"), + borderRadius: BorderRadius.circular(16.rpx), + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 10.rpx, 20.rpx, 34.rpx, 20.rpx), + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + ClickableContainer( + backgroundColor: Colors.transparent, + highlightColor: + themeController.currentColor.sc21, + borderRadius: 0, // 可按需设置圆角 + padding: EdgeInsets.symmetric( + vertical: 10.rpx), // 可自定义内边距 + onTap: () { + // deviceCalibrationController.process.value = 0; + // deviceCalibrationController.updateAll(); + }, + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Obx(() { + return Visibility( + maintainState: true, + maintainAnimation: true, + maintainSize: true, + visible: + deviceCalibrationController + .process.value == + 0, + child: SvgPicture.asset( + 'assets/img/icon/select_arrow.svg', + width: 17.rpx, + height: 17.rpx, + color: themeController + .currentColor.sc9, + ), + ); + }), + SizedBox(width: 8.rpx), + Text( + '离床校准'.tr, + style: TextStyle( + fontSize: 30.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc3, + ), + ), + ], + ), + Obx(() { + return Text( + deviceCalibrationController + .bed_calibration + .value == + 0 + ? '未完成'.tr + : "已完成".tr, + style: TextStyle( + fontSize: 26.rpx, + letterSpacing: 0.0, + color: deviceCalibrationController + .bed_calibration + .value == + 0 + ? themeController + .currentColor.sc3 + : themeController + .currentColor.sc1, + ), + ); + }), + ], + ), + ), + SizedBox(height: 41.rpx), + ClickableContainer( + backgroundColor: Colors.transparent, + highlightColor: + themeController.currentColor.sc21, + borderRadius: 0, // 可根据需要设为圆角 + padding: EdgeInsets.symmetric( + vertical: 10.rpx), // 可根据需要调整上下内边距 + onTap: () { + // deviceCalibrationController.process.value = 1; + // deviceCalibrationController.updateAll(); + }, + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Obx(() { + return Visibility( + maintainState: true, + maintainAnimation: true, + maintainSize: true, + visible: + deviceCalibrationController + .process.value == + 1, + child: SvgPicture.asset( + 'assets/img/icon/select_arrow.svg', + width: 17.rpx, + height: 17.rpx, + color: themeController + .currentColor.sc9, + ), + ); + }), + SizedBox(width: 8.rpx), + Text( + '位置校准'.tr, + style: TextStyle( + fontSize: 30.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc3, + ), + ), + ], + ), + Obx(() { + return Text( + deviceCalibrationController + .position_calibration + .value == + 0 + ? '未完成'.tr + : "已完成".tr, + style: TextStyle( + fontSize: 26.rpx, + letterSpacing: 0.0, + color: deviceCalibrationController + .position_calibration + .value == + 0 + ? themeController + .currentColor.sc3 + : themeController + .currentColor.sc1, + ), + ); + }), + ], + ), + ), + ], + ), + ), + ), + ), + Obx(() { + return Expanded( + child: SizedBox( + width: double.infinity, + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 100.rpx, 55.rpx, 100.rpx, 0), + child: Stack( + clipBehavior: Clip.none, + children: [ + Container( + width: MediaQuery.sizeOf(context).width * + 0.65, + height: + MediaQuery.sizeOf(context).height * + 0.3, + child: Stack( + children: [ + Image.asset( + deviceCalibrationController + .process.value == + 1 && + deviceCalibrationController + .position_calibration + .value == + 1 + ? (deviceCalibrationController + .bed_type.value == + 0 + ? "assets/img/single_person.png" + : "assets/img/double_person.png") + : (deviceCalibrationController + .bed_type.value == + 0 + ? "assets/img/single_pillow.png" + : "assets/img/double_pillow.png"), + width: double.infinity, + height: double.infinity, + fit: BoxFit.contain, + ), + + // 床头文字 + Positioned( + top: 23.rpx, + left: (MediaQuery.sizeOf(context) + .width * + 0.65) / + 2 - + 26.rpx, + child: Text( + '床头'.tr, + style: TextStyle( + fontSize: 26.rpx, + color: themeController + .currentColor.sc4, + ), + ), + ), + ], + ), + ), + Obx(() { + final double centerLeft = + (bodySize.maxWidth * 0.56) / 2; + if (deviceCalibrationController + .bed_type.value == + 0 && + deviceCalibrationController + .process.value == + 1) { + //单人床位置校准 + return Positioned( + top: -40.rpx, + left: centerLeft - 0.rpx, + child: Container( + width: bodySize.maxWidth * 0.087, + height: bodySize.maxHeight * 0.06, + constraints: BoxConstraints( + minWidth: 65.rpx, + minHeight: 76.rpx, + ), + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage( + 'assets/img/tip_arrow.gif'), + fit: BoxFit.cover, + ), + ), + ), + ); + } + if (deviceCalibrationController + .bed_type.value == + 1 && + deviceCalibrationController + .process.value == + 1) { + //双人人床位置校准 + return Positioned( + top: -40.rpx, + left: (MediaQuery.sizeOf(context) + .width * + 0.65) * + 0.22 - + 0.rpx, + child: Container( + width: bodySize.maxWidth * 0.087, + height: bodySize.maxHeight * 0.06, + constraints: BoxConstraints( + minWidth: 65.rpx, + minHeight: 76.rpx, + ), + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage( + 'assets/img/tip_arrow.gif'), + fit: BoxFit.cover, + ), + ), + ), + ); + } + + return Container(); + }), + Positioned( + top: MediaQuery.sizeOf(context).height * + 0.3 + + 82.rpx, + left: 0, + right: 0, + child: Obx(() { + return Column( + children: [ + getBodyMontion(), + SizedBox( + height: 10.rpx, + ), + getInBedMontion(), + SizedBox( + height: 10.rpx, + ), + Text( + (deviceCalibrationController + .cd.value == + 10000) + ? ((deviceCalibrationController + .process + .value == + 0 && + deviceCalibrationController + .bed_calibration + .value == + 1) || + (deviceCalibrationController + .process + .value == + 1 && + deviceCalibrationController + .position_calibration + .value == + 1) + ? (deviceCalibrationController + .flag.value == + 2 + ? '校准完成'.tr + : "校准失败".tr) + : deviceCalibrationController + .statusContext + .value + .isEmpty == + true + ? (deviceCalibrationController + .process + .value == + 0 + ? '离床校准提示'.tr + : '位置校准提示'.tr) + : deviceCalibrationController + .statusContext + .value) + : "${deviceCalibrationController.cd.value}" + + "s后超时".tr, + style: TextStyle( + fontSize: 38.rpx, + letterSpacing: 0.0, + color: ((deviceCalibrationController + .process + .value == + 0 && + deviceCalibrationController + .bed_calibration + .value == + 1) || + (deviceCalibrationController + .process + .value == + 1 && + deviceCalibrationController + .position_calibration + .value == + 1)) && + (deviceCalibrationController + .flag.value == + 2) + ? themeController + .currentColor.sc1 + : themeController + .currentColor.sc9, + ), + ), + ], + ); + }), + ), + ], + ), + ), + ), + ); + }), + Obx(() { + if (deviceCalibrationController.flag.value == 1) { + return Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 100.rpx, 0.rpx, 100.rpx, 60.rpx), + child: CalibrationProgressWidget( + progressNotifier: progressNotifier, + failureNotifier: failureNotifier, + ), + ); + } + return Container(); + }), + Obx(() { + if (deviceCalibrationController.flag.value != 1) { + return Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 100.rpx, 0.rpx, 100.rpx, 60.rpx), + child: CustomCard( + borderRadius: AppConstants() + .button_container_radius, // 圆角半径 + onTap: () async { + if (deviceCalibrationController.flag.value == + 2) { + deviceCalibrationController + .statusContext.value = ""; + deviceCalibrationController.flag.value = 1; + progressNotifier.value = 0; + failureNotifier.value = false; + deviceCalibrationController.process.value = + 0; + deviceCalibrationController + .bed_calibration.value = 0; + deviceCalibrationController + .position_calibration.value = 0; + + blueteethBindController.cid!.value = ""; + + deviceCalibrationController.process.value = + 0; + deviceCalibrationController + .bed_calibration.value = 0; + deviceCalibrationController + .position_calibration.value = 0; + blueteethBindController.cid!.value = ""; + String serviceAddress = + "https://caibration.he-info.cn"; + String calibrationApi = + ServiceConstant.start_calibration; + String progressApi = + ServiceConstant.calibration_process; + + String queryUrl = + "$serviceAddress$calibrationApi"; + + Timer? pollingTimer; + + Map data = { + "macA": blueteethBindController + .currentFullDevice!.macA, + }; + + // 是否是二次点击(有cid表示进行第二阶段) + bool isSecondStep = blueteethBindController + .cid?.value.isNotEmpty ?? + false; + + if (isSecondStep) { + data["id"] = + blueteethBindController.cid!.value; + } + + // 发起校准请求 + requestWithLog( + logTitle: "设备校准", + method: MyHttpMethod.post, + queryUrl: queryUrl, + data: data, + onSuccess: (res) { + if (!isSecondStep) { + // 保存第一次获取的 cid + String cid = + res.rawResponse.data['cid']; + blueteethBindController.cid!.value = + cid; + } + _startPollingProgress(serviceAddress, + progressApi, isSecondStep); + }, + onFailure: (res) { + deviceCalibrationController.flag.value = + 0; + blueteethBindController.cid!.value = ""; + blueteethBindController.updateAll(); + failureNotifier.value = true; + TopSlideNotification.show( + context, + text: res.msg ?? "服务器.失败".tr, + textColor: + themeController.currentColor.sc9, + ); + }, + ); + } else { + deviceCalibrationController.flag.value = 1; + progressNotifier.value = 0; + failureNotifier.value = false; + _pollingTimer?.cancel(); + String serviceAddress = + "https://caibration.he-info.cn"; + String calibrationApi = + ServiceConstant.start_calibration; + String progressApi = + ServiceConstant.calibration_process; + + String queryUrl = + "$serviceAddress$calibrationApi"; + + Map data = { + "macA": blueteethBindController + .currentFullDevice!.macA, + }; + + // 是否是二次点击(有cid表示进行第二阶段) + bool isSecondStep = blueteethBindController + .cid?.value.isNotEmpty ?? + false; + + if (isSecondStep) { + data["id"] = + blueteethBindController.cid!.value; + } else { + data["cancel"] = true; + } + + // 发起校准请求 + requestWithLog( + logTitle: "设备校准", + method: MyHttpMethod.post, + queryUrl: queryUrl, + data: data, + onSuccess: (res) { + if (!isSecondStep) { + // 保存第一次获取的 cid + String cid = + res.rawResponse.data['cid']; + blueteethBindController.cid!.value = + cid; + } + _startPollingProgress(serviceAddress, + progressApi, isSecondStep); + }, + onFailure: (res) { + deviceCalibrationController.flag.value = + 0; + blueteethBindController.cid!.value = ""; + blueteethBindController.updateAll(); + _pollingTimer?.cancel(); + failureNotifier.value = true; + TopSlideNotification.show( + context, + text: res.msg ?? "服务器.失败".tr, + textColor: + themeController.currentColor.sc9, + ); + }, + ); + } + }, + colors: [ + themeController.currentColor.sc1, + themeController.currentColor.sc2, + ], + child: Container( + width: bodySize.maxWidth, + height: + MediaQuery.sizeOf(context).height * 0.055, + constraints: BoxConstraints( + minWidth: 500.rpx, + minHeight: 90.rpx, + ), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Obx(() { + if (deviceCalibrationController + .flag.value == + 0) { + return Text( + '开始校准'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + color: themeController + .currentColor.sc3, + fontFamily: 'Inter', + fontSize: AppConstants() + .normal_text_fontSize, + letterSpacing: 0.0, + ), + ); + } + if (deviceCalibrationController + .flag.value == + 1) { + deviceCalibrationController + .statusContext.value = ""; + return Text( + '开始校准'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + color: themeController + .currentColor.sc3, + fontFamily: 'Inter', + fontSize: AppConstants() + .normal_text_fontSize, + letterSpacing: 0.0, + ), + ); + } + if (deviceCalibrationController + .flag.value == + 2) { + return Text( + '重新校准'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + color: themeController + .currentColor.sc3, + fontFamily: 'Inter', + fontSize: AppConstants() + .normal_text_fontSize, + letterSpacing: 0.0, + ), + ); + } + return Container(); + }), + ].divide(SizedBox( + width: 17.rpx, + )), + ), + ), + ), + ); + } + return Container(); + }), + ], + ), + ), + ), + ), + ), + ), + ), + onWillPop: () async { + bool exit = false; + if (deviceCalibrationController.flag.value != 2) { + showConfirmDialog(context, Container(), "校准未完成提示".tr, + onConfirm: () async { + exit = true; + // await Get.toNamed("/personPage"); + // print("object"); + deviceCalibrationController.process.value = 0; + deviceCalibrationController.bed_calibration.value = 0; + deviceCalibrationController.position_calibration.value = 0; + blueteethBindController.cid!.value = ""; + }, onCancel: () { + exit = false; + }); + } else { + exit = true; + } + return exit; + }); + } + + void _startPollingProgress( + String serviceAddress, String progressApi, bool isSecondStep) { + // 立即请求一次 + _requestProgress(serviceAddress, progressApi, isSecondStep); + + // 设置定时器每2秒请求一次 + _pollingTimer = Timer.periodic(Duration(seconds: 1), (_) { + _requestProgress(serviceAddress, progressApi, isSecondStep); + }); + } + + void _requestProgress( + String serviceAddress, String progressApi, bool isSecondStep) { + String cid = blueteethBindController.cid!.value; + String progressUrl = "$serviceAddress$progressApi?id=$cid"; + + requestWithLog( + logTitle: "设备校准进度", + method: MyHttpMethod.get, + queryUrl: progressUrl, + onSuccess: (res) { + final data = res.data; + double per = (data['per'] ?? 0).toDouble(); + int currStep = data['currStep'] ?? -1; + String tips = data['statusText'] ?? ''; + try { + if (data['mattress'] != null) { + deviceCalibrationController.inBedTips.value = + data['mattress']['inBed']; + deviceCalibrationController.motionTips.value = + data['mattress']['bm']; + } + } catch (e) { + print(e); + } + + if (data['status'] == null) { + //当前进程执行中,执行倒计时 + if (data['cd'] != null) { + int cd = data['cd'] ?? 0; + deviceCalibrationController.cd.value = cd; + deviceCalibrationController.statusContext.value = ""; + } + } else { + if (data['status'] == true) { + //当前步骤执行成功 + deviceCalibrationController.tips.value = ""; + deviceCalibrationController.cd.value = 10000; + if (isSecondStep) { + deviceCalibrationController.statusContext.value = + data['statusText']; + deviceCalibrationController.updateAll(); + } + } else { + deviceCalibrationController.statusContext.value = ""; + //当前步骤执行失败 + deviceCalibrationController.bed_calibration.value == 0; + _pollingTimer?.cancel(); + blueteethBindController.cid?.value = ""; + deviceCalibrationController.process.value = 0; + deviceCalibrationController.flag.value = 0; + deviceCalibrationController.tips.value = ""; + deviceCalibrationController.cd.value = 10000; + deviceCalibrationController.updateAll(); + TopSlideNotification.show( + context, + text: tips, + textColor: themeController.currentColor.sc9, + ); + return; + } + } + bool status = data['status'] ?? false; + + // 更新进度 + progressNotifier.value = per; + if (per != 100 && per != 20) { + deviceCalibrationController.tips.value = tips; + } else { + deviceCalibrationController.tips.value = ""; + } + deviceCalibrationController.updateAll(); + + if (!isSecondStep && per >= 20) { + // 第一步完成 + _pollingTimer?.cancel(); + progressNotifier.value = 100; + TopSlideNotification.show(context, text: tips); + deviceCalibrationController.process.value = 1; + deviceCalibrationController.bed_calibration.value = 1; + deviceCalibrationController.flag.value = 0; // 重置标志允许再次点击 + } else if (isSecondStep && per >= 100 && currStep == 5 && status) { + // 第二步完成 + _pollingTimer?.cancel(); + TopSlideNotification.show(context, text: "设备校准完成".tr); + deviceCalibrationController.bed_calibration.value = 1; + deviceCalibrationController.position_calibration.value = 1; + deviceCalibrationController.process.value = 1; + deviceCalibrationController.flag.value = 2; + progressNotifier.value = 0; // 重置进度条 + failureNotifier.value = false; // 重置失败状态 + updateDeviceBindStatus( + blueteethBindController.currentDeviceMac!.value); + } + }, + onFailure: (res) { + deviceCalibrationController.flag.value = 0; + _pollingTimer?.cancel(); + blueteethBindController.cid!.value = ""; + failureNotifier.value = true; + deviceCalibrationController.flag.value = 0; + deviceCalibrationController.cd.value = 10000; + TopSlideNotification.show( + context, + text: res.msg ?? "服务器.失败".tr, + textColor: themeController.currentColor.sc9, + ); + }, + ); + } + + getInBedMontion() { + if (deviceCalibrationController.cd.value == 10000) { + return Container(); + } + + if (deviceCalibrationController.process.value == 1) { + if (deviceCalibrationController.inBedTips.value == 0) { + return Text( + "请校准人员保持在床状态".tr, + style: TextStyle( + fontSize: AppConstants().normal_text_fontSize, + color: themeController.currentColor.sc9), + ); + } + } else { + if (deviceCalibrationController.inBedTips.value != 0) { + return Text( + "请校准人员暂时离开床铺".tr, + style: TextStyle( + fontSize: AppConstants().normal_text_fontSize, + color: themeController.currentColor.sc9), + ); + } + } + + return Container(); + } + + getBodyMontion() { + if (deviceCalibrationController.cd.value == 10000) { + return Container(); + } + if (deviceCalibrationController.process.value == 1) { + if (deviceCalibrationController.motionTips.value != 0) { + return Text( + "请保持身体静止".tr, + style: TextStyle( + fontSize: AppConstants().normal_text_fontSize, + color: themeController.currentColor.sc9), + ); + } + } + + return Container(); + } + + //更新设备绑定状态 + void updateDeviceBindStatus(String mac) { + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.user_setting; + String type = "device_bind_status_$mac"; + String queryUrl = + "${serviceAddress}${serviceName}${serviceApi}?type=${type}"; + requestWithLog( + logTitle: "查询绑定流程", + method: MyHttpMethod.get, + queryUrl: queryUrl, + onSuccess: (res) { + print(res); + Map data = { + "type": type, + "mac": mac, + "wifi": res.data['wifi'], + "celibration": true, + "person_info": false, + "time": DateTime.now().millisecondsSinceEpoch, + }; + requestWithLog( + logTitle: "更新绑定流程", + method: MyHttpMethod.put, + queryUrl: queryUrl, + data: data, + onSuccess: (res) {}, + onFailure: (res) {}, + ); + }, + onFailure: (res) {}, + ); + } +} diff --git a/lib/pages/mh_page/device/mht_wifi_page.dart b/lib/pages/mh_page/device/mht_wifi_page.dart new file mode 100644 index 0000000..77899b8 --- /dev/null +++ b/lib/pages/mh_page/device/mht_wifi_page.dart @@ -0,0 +1,1119 @@ +import 'dart:async'; + +import 'package:easydevice/easydevice.dart'; +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +import 'package:flutter_svg/svg.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/tool/ClickableContainer.dart'; +import 'package:vbvs_app/component/tool/CustomCard.dart'; +import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; +import 'package:vbvs_app/component/tool/cmd.dart'; +import 'package:vbvs_app/controller/device/blueteeth_bind_controller.dart'; +import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; +import 'package:vbvs_app/controller/person/person_controller.dart'; +import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; +import 'package:vbvs_app/controller/user_info_controller.dart'; +import 'package:vbvs_app/model/BleDeviceData.dart'; +import 'package:vbvs_app/pages/device_bind/blueteeth_device_page.dart'; +import 'package:vbvs_app/pages/mh_page/component/mht_bind_dialog.dart'; +import 'package:vbvs_app/pages/mh_page/device/model/BlueToothDataModel.dart'; + +class MHTWifiPage extends StatefulWidget { + BlueToothDataModel deviceInfo; + MHTWifiPage({super.key, required this.deviceInfo}); + + @override + State createState() => _MHTWifiPageState(); +} + +class _MHTWifiPageState extends State { + GlobalController globalController = Get.find(); + UserInfoController userInfoController = Get.find(); + BlueteethBindController blueteethBindController = Get.find(); + PersonController personController = Get.find(); + ThemeController themeController = Get.find(); + var lisObj; + Timer? _timeoutTimer; + StreamSubscription>? _scanSubscription; + bool _isDisposed = false; + + @override + void initState() { + super.initState(); + _initController(); + dealWifi(widget.deviceInfo.macA).then((aa) { + print("object"); + }); + } + + void _initController() { + blueteethBindController.netType.value = 0; + blueteethBindController.connectStatus.value = 0; + blueteethBindController.wifiList = [].obs; + blueteethBindController.wifiStatus = 0.obs; + blueteethBindController.connect_wifi.value = {}; + blueteethBindController.selectWifi.value = {}; + blueteethBindController.wifiStatus.value = 0; + blueteethBindController.blueConnectFlag.value = 0; + blueteethBindController.wifiConnectStatus.value = 1; + blueteethBindController.updateAll(); + } + + @override + void dispose() { + _isDisposed = true; + _cleanupResources(); + super.dispose(); + } + + void _cleanupResources() { + // 取消监听器 + lisObj?.cancel(); + lisObj = null; + + // 取消定时器 + _timeoutTimer?.cancel(); + _timeoutTimer = null; + + // 取消扫描订阅 + _scanSubscription?.cancel(); + _scanSubscription = null; + + // 断开蓝牙连接 + if (blueteethBindController.currentDevice != null) { + blueteethBindController.currentDevice!.disconnect(); + blueteethBindController.currentDevice = null; + } + + // 停止扫描 + FlutterBluePlus.stopScan(); + } + + Future _navigateToNextPage() async { + // 清理资源 + _cleanupResources(); + + if (_isDisposed) return; + + // 导航到下一页 + if (blueteethBindController.wifiStatus.value != 1) { + showConfirmDialog(context, Container(), "未配置网络提示".tr, onConfirm: () { + _cleanupResources(); + Get.toNamed("/calibrationPage"); + }, onCancel: () {}); + } else { + _cleanupResources(); + Get.toNamed("/calibrationPage"); + } + } + + @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/images/new_background.png'), + fit: BoxFit.fill, + ), + ), + child: Scaffold( + backgroundColor: Colors.transparent, + appBar: AppBar( + backgroundColor: Colors.transparent, + automaticallyImplyLeading: false, + iconTheme: IconThemeData(color: themeController.currentColor.sc3), + titleSpacing: 0, + title: Container( + width: double.infinity, + height: 180.rpx, + child: Stack( + alignment: Alignment.center, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'wifi配置'.tr, + style: + FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Readex Pro', + color: themeController.currentColor.sc3, + letterSpacing: 0, + fontSize: 30.rpx, + ), + ), + SizedBox( + width: 14.rpx, + ), + Obx(() { + if (!blueteethBindController.bluetoothStatus.value) { + return Container(); + } + if (blueteethBindController.blueConnectFlag.value == + 0) { + return SizedBox( + width: 24.rpx, + height: 24.rpx, + child: CircularProgressIndicator( + strokeWidth: 1, + valueColor: + AlwaysStoppedAnimation(Colors.white), + ), + ); + } + if (blueteethBindController.netType.value == 0) { + return SizedBox( + width: 24.rpx, + height: 24.rpx, + child: CircularProgressIndicator( + strokeWidth: 1, + valueColor: + AlwaysStoppedAnimation(Colors.white), + ), + ); + } + if (blueteethBindController.wifiConnectStatus.value == + 0) { + return SizedBox( + width: 24.rpx, + height: 24.rpx, + child: CircularProgressIndicator( + strokeWidth: 1, + valueColor: + AlwaysStoppedAnimation(Colors.white), + ), + ); + } + return Container(); + }), + ], + ), + Positioned( + left: 0, + child: returnIconButtom, + ), + Positioned( + right: 20.rpx, + child: CustomCard( + borderRadius: 20.rpx, + onTap: _navigateToNextPage, + colors: [ + stringToColor("#84F5FF"), + ], + child: Container( + width: 130.rpx, + height: 60.rpx, + alignment: Alignment.center, + padding: EdgeInsetsDirectional.fromSTEB( + 16.rpx, 0, 16.rpx, 0), + child: Text( + '下一步'.tr, + style: FlutterFlowTheme.of(context) + .titleSmall + .override( + fontFamily: 'Inter Tight', + color: stringToColor("#011D33"), + letterSpacing: 0.0, + ), + ), + ), + ), + ), + ], + ), + ), + actions: [], + centerTitle: false, + ), + body: SafeArea( + top: true, + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 0, 30.rpx, 0), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 30.rpx, 0, 0), + child: Container( + width: double.infinity, + decoration: BoxDecoration( + color: stringToColor("#003058"), + borderRadius: BorderRadius.circular(20.rpx), + ), + child: Column( + children: [ + Align( + alignment: AlignmentDirectional(0, 0), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 30.rpx, 30.rpx, 30.rpx), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + "WLAN".tr, + style: TextStyle( + color: themeController + .currentColor.sc3, + fontSize: AppConstants() + .title_text_fontSize, + ), + ), + Obx(() { + return Text( + blueteethBindController + .wifiStatus.value == + 0 + ? "未连接".tr + : "已连接".tr, + style: TextStyle( + color: blueteethBindController + .wifiStatus.value == + 0 + ? themeController + .currentColor.sc4 + : themeController + .currentColor.sc2, + fontSize: AppConstants() + .normal_text_fontSize, + ), + ); + }), + ], + ), + ), + ), + Obx(() { + if (blueteethBindController + .connect_wifi.value == + null || + blueteethBindController + .connect_wifi.value.isEmpty) { + return Container(); + } else { + return Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 0.rpx, 30.rpx, 30.rpx), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + blueteethBindController.connect_wifi + .value['ssid'] ?? + '未命名'.tr, + style: TextStyle( + color: themeController + .currentColor.sc3, + fontSize: AppConstants() + .title_text_fontSize, + ), + ), + getWifiIconByRsso( + blueteethBindController + .connect_wifi.value), + ], + ), + ); + } + }) + ], + )), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 25.rpx, 0.rpx, 0.rpx), + child: Container( + width: double.infinity, + decoration: BoxDecoration( + color: stringToColor("#003058"), + borderRadius: BorderRadius.circular(20.rpx), + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 30.rpx, 30.rpx, 30.rpx), + child: Container( + width: double.infinity, + decoration: BoxDecoration(), + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Text( + '可用WLAN'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc3, + ), + ), + ], + ), + Obx(() { + final sortedList = [ + ...blueteethBindController.wifiList.value + ]..sort((a, b) => + b['rssi'].compareTo(a['rssi'])); + + return Column( + mainAxisSize: MainAxisSize.max, + children: sortedList + .map((wifiItem) => ClickableContainer( + backgroundColor: + Colors.transparent, + highlightColor: themeController + .currentColor.sc3, + padding: EdgeInsets.symmetric( + vertical: 0.rpx, + horizontal: 0.rpx), + borderRadius: 16.rpx, + onTap: () { + showWifiDialog( + context, + Padding( + padding: + EdgeInsetsDirectional + .fromSTEB( + 0.rpx, + 41.rpx, + 0.rpx, + 0), + child: Container( + height: 80.rpx, + decoration: + BoxDecoration( + color: Colors.white, + borderRadius: + BorderRadius + .circular( + 12.rpx), + ), + child: Align( + alignment: + AlignmentDirectional( + -1, 0), + child: Obx(() { + return TextFormField( + onChanged: + (value) { + blueteethBindController + .model + .wifiPass = + value; + }, + autofocus: + false, + obscureText: + blueteethBindController + .model + .wifiPassShow, + decoration: + InputDecoration( + isDense: + true, + labelStyle: FlutterFlowTheme.of( + context) + .labelMedium + .override( + fontFamily: + 'Inter', + fontSize: + 26.rpx, + letterSpacing: + 0.0, + ), + hintText: + '输入wifi密码' + .tr, + hintStyle: FlutterFlowTheme.of(context).labelMedium.override( + fontFamily: + 'Inter', + fontSize: 26 + .rpx, + letterSpacing: + 0.0, + color: themeController + .currentColor + .sc4), + 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: FlutterFlowTheme.of(context) + .error, + width: 1 + .rpx, + ), + borderRadius: + BorderRadius.circular( + 8.rpx), + ), + focusedErrorBorder: + OutlineInputBorder( + borderSide: + BorderSide( + color: FlutterFlowTheme.of(context) + .error, + width: 1 + .rpx, + ), + borderRadius: + BorderRadius.circular( + 8.rpx), + ), + filled: + true, + fillColor: + stringToColor( + "#F3F5F6"), + suffixIcon: + InkWell( + onTap: + () { + blueteethBindController + .model + .wifiPassShow = !blueteethBindController.model.wifiPassShow; + blueteethBindController + .updateAll(); + }, + focusNode: + FocusNode( + skipTraversal: true), + child: + Icon( + blueteethBindController.model.wifiPassShow + ? Icons.visibility_outlined + : Icons.visibility_off_outlined, + size: 30 + .rpx, + ), + ), + ), + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: + 'Inter', + fontSize: 26 + .rpx, + letterSpacing: + 0.0, + color: Colors + .black), + cursorColor: + themeController + .currentColor + .sc3, + ); + })), + ), + ), + wifiItem['ssid'] ?? + '未命名'.tr, + onConfirm: () async { + blueteethBindController + .selectWifi + .value = wifiItem; + bool flag = await sendWifiSetting( + wifiItem, + blueteethBindController + .model.wifiPass!, + blueteethBindController + .currentDevice!); + if (flag) { + var aa = await getDeviceWifiStatus( + blueteethBindController + .currentDevice!, + 1); + blueteethBindController + .selectWifi + .value = {}; + if (aa != null && + aa is Map) { + blueteethBindController + .connect_wifi + .value = aa; + } else { + blueteethBindController + .connect_wifi + .value = {}; + } + blueteethBindController + .wifiStatus + .value = + aa != null ? 1 : 0; + if (aa != null) { + updateDeviceBindStatus( + blueteethBindController + .currentDeviceMac! + .value); + TopSlideNotification + .show( + context, + text: "wifi页.配网成功".tr, + textColor: + themeController + .currentColor + .sc2, + ); + } else { + TopSlideNotification + .show( + context, + text: "wifi页.配网失败".tr, + textColor: + themeController + .currentColor + .sc9, + ); + } + } else { + TopSlideNotification.show( + context, + text: "wifi页.配网失败".tr, + textColor: + themeController + .currentColor + .sc9, + ); + } + }); + }, + child: Row( + mainAxisSize: + MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment + .spaceBetween, + children: [ + Text( + wifiItem['ssid'] ?? '', + style: FlutterFlowTheme + .of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + color: + themeController + .currentColor + .sc3, + ), + ), + if (blueteethBindController + .selectWifi.value == + wifiItem) + SizedBox( + width: 32.rpx, + height: 32.rpx, + child: + CircularProgressIndicator( + strokeWidth: 1, + valueColor: + AlwaysStoppedAnimation< + Color>( + Colors.white), + ), + ) + else + getWifiIconByRsso( + wifiItem), + ], + ), + )) + .toList() + .divide(SizedBox(height: 67.rpx)), + ); + }), + ClickableContainer( + backgroundColor: Colors.transparent, + highlightColor: Colors.white, + padding: EdgeInsets.symmetric( + horizontal: 20.rpx, vertical: 10.rpx), + borderRadius: 20.rpx, + onTap: () async { + var bluetoothState = + await FlutterBluePlus.isOn; + blueteethBindController.bluetoothStatus + .value = bluetoothState; + if (!bluetoothState) { + await _showBluetoothNotEnabledDialog(); + return; + } + if (blueteethBindController + .blueConnectFlag.value == + 0 || + blueteethBindController + .blueConnectFlag.value == + 1) { + blueteethBindController + .blueConnectFlag.value = 0; + dealWifi(widget.deviceInfo.macA) + .then((aa) { + print("object"); + }); + return; + } + + if (blueteethBindController + .netType.value == + 0) { + return; + } + blueteethBindController.netType.value = 0; + blueteethBindController.updateAll(); + var aa = await getDeviceNetVersion( + blueteethBindController + .currentDevice!, + 1); + if (aa == "4g") { + updateDeviceBindStatus( + blueteethBindController + .currentDeviceMac!.value); + TopSlideNotification.show( + context, + text: "4g设备配置wifi提示".tr, + textColor: + themeController.currentColor.sc2, + ); + blueteethBindController.netType.value = + 2; + blueteethBindController + .connectStatus.value = 1; + blueteethBindController.updateAll(); + Future.delayed( + const Duration(seconds: 1), () { + _cleanupResources(); + Get.toNamed("/calibrationPage", + arguments: 1); + }); + } else if (aa == 'unknown') { + blueteethBindController.netType.value = + 3; + blueteethBindController.updateAll(); + WidgetsBinding.instance + .addPostFrameCallback((_) { + TopSlideNotification.show( + context, + text: "获取设备网络类型失败".tr, + textColor: themeController + .currentColor.sc9, + ); + }); + } else { + blueteethBindController.netType.value = + 1; + blueteethBindController.updateAll(); + await initWifiStatusAndWifiList(); + } + }, + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + Container( + width: 25.rpx, + height: 25.rpx, + decoration: BoxDecoration(), + child: SvgPicture.asset( + 'assets/img/icon/refresh.svg', + fit: BoxFit.cover, + color: Colors.white, + ), + ), + Text( + '刷新'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc3, + ), + ), + ].divide(SizedBox(width: 26.rpx)), + ), + ), + ].divide(SizedBox(height: 65.rpx)), + ), + ), + ), + ), + ), + ], + ), + ), + ), + ), + ), + ), + ), + ); + } + + Future initWifiStatusAndWifiList() async { + if (_isDisposed) return; + + if (lisObj != null) { + lisObj!.cancel(); + } + + bool wifiStatus = false; + var aa = + await getDeviceWifiStatus(blueteethBindController.currentDevice!, 0); + if (aa != null && aa is Map) { + updateDeviceBindStatus(blueteethBindController.currentDeviceMac!.value); + wifiStatus = true; + blueteethBindController.connect_wifi.value = aa; + } else { + wifiStatus = false; + } + blueteethBindController.wifiStatus.value = wifiStatus ? 1 : 0; + + List wifiList = []; + try { + final result = await getWifiList(blueteethBindController.currentDevice!); + blueteethBindController.wifiConnectStatus.value = 1; + blueteethBindController.updateAll(); + if (result is List) { + wifiList = result; + } + } catch (e) { + print("异常: $e"); + blueteethBindController.wifiConnectStatus.value = 0; + blueteethBindController.updateAll(); + } + + if (_isDisposed) return; + + if (wifiList.isNotEmpty) { + blueteethBindController.connectStatus.value = 1; + blueteethBindController.updateAll(); + TopSlideNotification.show( + context, + text: "获取wifi列表成功".tr, + textColor: themeController.currentColor.sc2, + ); + blueteethBindController.wifiList.value = wifiList; + blueteethBindController.updateAll(); + } else { + TopSlideNotification.show( + context, + text: "获取wifi列表失败".tr, + textColor: themeController.currentColor.sc9, + ); + } + + lisObj = blueteethBindController.currentDevice!.statusStream + .listen((onData) async { + if (onData.status == BleEventType.recvLineLog) { + final line = onData.val; + print("[bleee]:" + line); + } + }); + } + + Widget getWifiIconByRsso(wifiItem) { + int? rssi = int.tryParse(wifiItem['rssi'].toString()); + + if (rssi != null) { + if (rssi >= -30) { + return Container( + width: 40.rpx, + height: 40.rpx, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration(shape: BoxShape.circle), + child: Image.asset("assets/img/wifi4.png"), + ); + } else if (rssi >= -45) { + return Container( + width: 40.rpx, + height: 40.rpx, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration(shape: BoxShape.circle), + child: Image.asset("assets/img/wifi3.png"), + ); + } else if (rssi >= -60) { + return Container( + width: 40.rpx, + height: 40.rpx, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration(shape: BoxShape.circle), + child: Image.asset("assets/img/wifi3.png"), + ); + } else { + return Container( + width: 40.rpx, + height: 40.rpx, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration(shape: BoxShape.circle), + child: Image.asset("assets/img/wifi1.png"), + ); + } + } else { + return Container( + width: 40.rpx, + height: 40.rpx, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration(shape: BoxShape.circle), + child: Image.asset("assets/img/wifi1.png"), + ); + } + } + + Future dealWifi(String mac) async { + if (_isDisposed) return; + + final blueteethBindController = Get.find(); + final themeController = Get.find(); + + // 清理之前的资源 + _cleanupResources(); + + try { + // 检查蓝牙状态 + var bluetoothState = await FlutterBluePlus.isOn; + blueteethBindController.bluetoothStatus.value = bluetoothState; + blueteethBindController.updateAll(); + if (!bluetoothState) { + await _showBluetoothNotEnabledDialog(); + return; + } + + // 开始扫描 + await FlutterBluePlus.startScan(timeout: Duration(seconds: 10)); + + bool isConnected = false; + + // 设置超时定时器 + _timeoutTimer = Timer(Duration(seconds: 20), () { + if (!isConnected && !_isDisposed) { + blueteethBindController.blueConnectFlag.value = 1; + blueteethBindController.updateAll(); + TopSlideNotification.show( + context, + text: "设备连接超时,请重试".tr, + textColor: themeController.currentColor.sc9, + ); + FlutterBluePlus.stopScan(); + } + }); + + // 监听扫描结果 + _scanSubscription = FlutterBluePlus.scanResults.listen((results) async { + if (_isDisposed) return; + + ScanResult? targetDevice; + + for (var r in results) { + if (r.advertisementData.manufacturerData.containsKey(0xFFED)) { + List rawData = r.advertisementData.manufacturerData[0xFFED]!; + BleDeviceData deviceData = parseBleData(rawData); + String deviceMac = + deviceData.deviceId.replaceAll(':', '').toLowerCase(); + if (deviceMac == mac.toLowerCase()) { + targetDevice = r; + break; + } + } + } + + if (targetDevice != null && !isConnected) { + isConnected = true; + _timeoutTimer?.cancel(); + _scanSubscription?.cancel(); + + try { + THapp bledevice = THapp(device: targetDevice.device); + await bledevice.device.connect(); + var res2 = bledevice.isConnected; + + if (res2 && !_isDisposed) { + blueteethBindController.blueConnectFlag.value = 2; + TopSlideNotification.show( + context, + text: "蓝牙绑定.连接成功".tr, + textColor: themeController.currentColor.sc2, + ); + blueteethBindController.currentDevice = bledevice; + + lisObj = blueteethBindController.currentDevice!.statusStream + .listen((onData) async { + if (_isDisposed) return; + + if (onData.status == BleEventType.recvLineLog) { + final line = onData.val; + print("[bleee]:" + line); + } + if (onData.status == BleEventType.ready) { + var aa = await getDeviceNetVersion( + blueteethBindController.currentDevice!, 0); + if (aa == "4g") { + blueteethBindController.netType.value = 2; + blueteethBindController.updateAll(); + updateDeviceBindStatus( + blueteethBindController.currentDeviceMac!.value); + await showTipDialog( + context, + Text( + "4g设备配置wifi提示".tr, + style: TextStyle( + color: themeController.currentColor.sc3, + fontSize: AppConstants().title_text_fontSize), + )); + } else if (aa == 'unknown') { + blueteethBindController.netType.value = 3; + blueteethBindController.updateAll(); + TopSlideNotification.show( + context, + text: "获取设备网络类型失败".tr, + textColor: themeController.currentColor.sc9, + ); + } else { + blueteethBindController.netType.value = 1; + blueteethBindController.updateAll(); + blueteethBindController.wifiConnectStatus.value = 0; + blueteethBindController.updateAll(); + await initWifiStatusAndWifiList(); + } + } + }); + } else { + TopSlideNotification.show( + context, + text: "蓝牙绑定.连接失败".tr, + textColor: themeController.currentColor.sc9, + ); + } + } catch (e) { + TopSlideNotification.show( + context, + text: "设备连接失败".tr, + textColor: themeController.currentColor.sc9, + ); + } + } + }); + + await Future.delayed(Duration(seconds: 20)); + } catch (e) { + if (!_isDisposed) { + TopSlideNotification.show( + context, + text: "扫描过程中发生错误".tr, + textColor: themeController.currentColor.sc9, + ); + } + } finally { + _timeoutTimer?.cancel(); + await FlutterBluePlus.stopScan(); + } + } + + Future _showBluetoothNotEnabledDialog() async { + if (_isDisposed) return; + + await showTipDialog( + context, + Column( + children: [ + Text( + "蓝牙未开启".tr, + style: TextStyle( + fontSize: AppConstants().title_text_fontSize, + color: themeController.currentColor.sc3), + ), + SizedBox( + height: 20.rpx, + ), + Text( + "请先打开蓝牙在进行设备扫描".tr, + style: TextStyle( + fontSize: AppConstants().normal_text_fontSize, + color: themeController.currentColor.sc3), + ), + ], + )); + } + + void updateDeviceBindStatus(String mac) { + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.user_setting; + String type = "device_bind_status_$mac"; + String queryUrl = + "${serviceAddress}${serviceName}${serviceApi}?type=${type}"; + requestWithLog( + logTitle: "查询绑定流程", + method: MyHttpMethod.get, + queryUrl: queryUrl, + onSuccess: (res) { + print(res); + Map data = { + "type": type, + "mac": mac, + "wifi": true, + "celibration": res.data['celibration'], + "person_info": res.data['person_info'], + "time": DateTime.now().millisecondsSinceEpoch, + }; + requestWithLog( + logTitle: "更新绑定流程", + method: MyHttpMethod.put, + queryUrl: queryUrl, + data: data, + onSuccess: (res) {}, + onFailure: (res) {}, + ); + }, + onFailure: (res) {}, + ); + } +} diff --git a/lib/pages/mh_page/device/model/BlueToothDataModel.dart b/lib/pages/mh_page/device/model/BlueToothDataModel.dart new file mode 100644 index 0000000..546bce4 --- /dev/null +++ b/lib/pages/mh_page/device/model/BlueToothDataModel.dart @@ -0,0 +1,36 @@ +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; + +class BlueToothDataModel { + String name; + bool bind; + String mac; + ScanResult scanResult; + String macA; + String macB; + int type; + + BlueToothDataModel({ + this.name = '', + required this.bind, + required this.mac, + required this.scanResult, + required this.type, + this.macA = '', + this.macB = '', + }); + + factory BlueToothDataModel.fromScanResult(ScanResult result,int type, + {bool bind = false, String name = '', String mac = ''}) { + // 如果外部没有传入 name,则取 localName + String finalName = + name.isNotEmpty ? name : (result.advertisementData.localName ?? ''); + + return BlueToothDataModel( + name: finalName, + bind: bind, + mac: mac, + scanResult: result, + type:type + ); + } +} diff --git a/lib/pages/mh_page/homepage/component/HomeDeviceStausWidget.dart b/lib/pages/mh_page/homepage/component/HomeDeviceStausWidget.dart new file mode 100644 index 0000000..c4def6a --- /dev/null +++ b/lib/pages/mh_page/homepage/component/HomeDeviceStausWidget.dart @@ -0,0 +1,103 @@ +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'; +import 'package:vbvs_app/common/util/FitTool.dart'; +import 'package:vbvs_app/component/tool/CustomCard.dart'; + +class HomeDeviceStausWidget extends StatefulWidget { + final deviceStatus; + + const HomeDeviceStausWidget({super.key, required this.deviceStatus}); + + @override + State createState() => _HomeDeviceStausWidgetState(); +} + +class _HomeDeviceStausWidgetState extends State { + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + constraints: BoxConstraints(minHeight: 180.rpx), + decoration: BoxDecoration( + color: Color(0xFF003058), + borderRadius: BorderRadius.circular(16.rpx), + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(67.rpx, 0.rpx, 30.rpx, 0.rpx), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 170.rpx, + height: 81.rpx, + child: ClipRRect( + borderRadius: BorderRadius.circular(8.rpx), + child: Image.network( + widget.deviceStatus['device_image'], // 从DeviceStatus获取图片 + width: 200.rpx, + height: 200.rpx, + fit: BoxFit.cover, + ), + ), + ), + Text( + '${(widget.deviceStatus['name'] ?? '').isEmpty ? '未命名'.tr : widget.deviceStatus['name']}', + style: TextStyle( + color: Colors.white, + fontSize: 26.rpx, + letterSpacing: 0.0.rpx, + ), + ), + ].divide(SizedBox(height: 26.rpx)), + ), + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + // Text( + // widget.deviceStatus['status'] == 1 ? '在线'.tr : '离线'.tr, + // style: TextStyle( + // color: widget.deviceStatus['status'] == 1 + // ? stringToColor("#07BD60") + // : stringToColor("#F84B20"), + // fontSize: 26.rpx, + // letterSpacing: 0.0.rpx, + // ), + // ), + CustomCard( + gradientDirection: GradientDirection.vertical, + borderRadius: 10.rpx, + onTap: () { + // 点击回调逻辑放这里 + }, + colors: + AppConstants().mhtButtongradientColors, // 你原本没有渐变,单色即可 + enableGradient: true, // 关闭渐变 + enableAnimation: true, // 启用缩放动画 + child: SizedBox( + width: 150.rpx, + height: 61.rpx, + child: Center( + child: Text( + '控制'.tr, + style: TextStyle( + color: Color(0xFF003058), + fontSize: 26.rpx, + letterSpacing: 0.0.rpx, + ), + ), + ), + ), + ), + ].divide(SizedBox(height: 36.rpx)), + ), + ], + ), + ), + ); + } +} diff --git a/lib/pages/mh_page/homepage/component/HomeDeviceWidget.dart b/lib/pages/mh_page/homepage/component/HomeDeviceWidget.dart new file mode 100644 index 0000000..57f6d0c --- /dev/null +++ b/lib/pages/mh_page/homepage/component/HomeDeviceWidget.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; +import 'package:vbvs_app/common/util/FitTool.dart'; +import 'package:vbvs_app/pages/mh_page/homepage/component/HomeDeviceStausWidget.dart'; + +class HomeDeviceWidget extends StatefulWidget { + final String roomName; + final List deviceStatusList; + + const HomeDeviceWidget({ + super.key, + required this.roomName, + required this.deviceStatusList, + }); + + @override + State createState() => _HomeDeviceWidgetState(); +} + +class _HomeDeviceWidgetState extends State { + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + decoration: BoxDecoration(), + child: Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsetsDirectional.fromSTEB(16.rpx, 0.rpx, 0.rpx, 35.rpx), + child: Text( + widget.roomName, + maxLines: 2, + style: TextStyle( + color: Color(0xFFFBFDFF), + fontSize: 30.rpx, + letterSpacing: 0.0.rpx, + ), + ), + ), + Column( + mainAxisSize: MainAxisSize.max, + children: widget.deviceStatusList + .map((deviceStatus) => HomeDeviceStausWidget(deviceStatus: deviceStatus)) + .toList() + .divide(SizedBox(height: 30.rpx)), + ), + ], + ), + ); + } +} diff --git a/lib/pages/mh_page/homepage/controller/mht_home_controller.dart b/lib/pages/mh_page/homepage/controller/mht_home_controller.dart new file mode 100644 index 0000000..93011f9 --- /dev/null +++ b/lib/pages/mh_page/homepage/controller/mht_home_controller.dart @@ -0,0 +1,330 @@ +import 'dart:convert'; + +import 'package:EasyDartModule/EasyDartModule.dart'; +import 'package:ef/ef.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'package:vbvs_app/common/color/ServiceConstant.dart'; +import 'package:vbvs_app/common/color/app_uri_status.dart'; +import 'package:vbvs_app/common/util/DailyLogUtils.dart'; +import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/model/api_response.dart'; + +part 'mht_home_controller.g.dart'; // 由json_serializable自动生成的部分 + +@JsonSerializable() +class MHTHomeModel { + MHTHomeModel(); + + // 从JSON反序列化时的异常处理 + + factory MHTHomeModel.fromJson(Map json) { + try { + return _$MHTHomeModelFromJson(json); + } catch (e) { + // 在实际应用中,应该有更细致的异常处理策略和错误日志 + return MHTHomeModel(); // 或者返回一个带有错误信息的特定DeviceInfoModel实例 + } + } + + // 序列化为JSON时的异常处理 + Map toJson() => _$MHTHomeModelToJson(this); +} + +class MHTHomeController extends GetControllerEx { + MHTHomeController() { + attr = GetModel(MHTHomeModel()).obs; + } + + RxInt bindDeviceNum = 0.obs; //设备数量 + RxMap deviceList = {}.obs; //设备列表 + RxMap> sleepReportData = >{}.obs; + + RxString keyWord = "".obs; + + String wifiMac = ""; + + Future getDeviceNum() async { + try { + ApiResponse apiResponse = ApiResponse(code: -1, msg: "设备.设备列表请求失败".tr); + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.device_list; + String queryUrl = + "${serviceAddress}${serviceName}${serviceApi}?bindNum=1"; + String? language = ""; + if (languageController.selectLanguage != null) { + language = languageController.selectLanguage.value!.language_code; + } + if (language != null && language.isNotEmpty) { + if (queryUrl.contains("?")) { + queryUrl += "&lang=$language"; + } else { + queryUrl += "?lang=$language"; + } + } + var response = await EasyDartModule.dio.get(queryUrl); + if (response != null) { + var responseData = + response.data is String ? jsonDecode(response.data) : response.data; + ApiResponse res = + ApiResponse.fromJson(responseData, (object) => object); + MyUtils.formatResponse(res, "设备.设备列表请求成功".tr, "设备.设备列表请求失败".tr); + if (res.code == HttpStatusCodes.ok) { + bindDeviceNum.value = res.total!; + updateAll(); + return res; + } + } else { + return ApiResponse(code: -1, msg: "服务器.失败".tr); + } + return apiResponse; + } catch (e) { + EasyDartModule.logger.info("设备请求列表: $e"); + DailyLogUtils.writeLog("设备请求列表: $e"); + } + return ApiResponse(code: -1, msg: "未知错误".tr); // Default return statement + } + + Future getDeviceList( + {String? key, String? group, int? bindType}) async { + try { + ApiResponse apiResponse = ApiResponse(code: -1, msg: "设备.设备列表请求失败".tr); + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.device_list; + + // 初始URL + String queryUrl = "$serviceAddress$serviceName$serviceApi"; + + // 用Map统一管理query参数 + Map queryParams = {}; + + if (key != null && key.isNotEmpty) { + queryParams['key'] = key; + } + + if (group != null && group.isNotEmpty) { + queryParams['group'] = group; + } + + if (bindType != null) { + queryParams['bindType'] = bindType.toString(); + } + + String? language = ""; + if (languageController.selectLanguage != null) { + language = languageController.selectLanguage.value!.language_code; + } + if (language != null && language.isNotEmpty) { + queryParams['lang'] = language; + } + + // 拼接完整URL + if (queryParams.isNotEmpty) { + queryUrl += '?' + Uri(queryParameters: queryParams).query; + } + + var response = await EasyDartModule.dio.get(queryUrl); + if (response != null) { + var responseData = + response.data is String ? jsonDecode(response.data) : response.data; + ApiResponse res = + ApiResponse.fromJson(responseData, (object) => object); + MyUtils.formatResponse(res, "设备.设备列表请求成功".tr, "设备.设备列表请求失败".tr); + if (res.code == HttpStatusCodes.ok) { + deviceList.value = res.data!; + updateAll(); + return res; + } + } else { + return ApiResponse(code: -1, msg: "服务器.失败".tr); + } + return apiResponse; + } catch (e) { + EasyDartModule.logger.info("设备请求列表: $e"); + DailyLogUtils.writeLog("设备请求列表: $e"); + } + return ApiResponse(code: -1, msg: "未知错误".tr); // Default return statement + } + + Future deleteDevice(Map device) async { + try { + ApiResponse apiResponse = ApiResponse(code: -1, msg: "请求失败".tr); + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.device_bind; + String queryUrl = "${serviceAddress}${serviceName}${serviceApi}"; + String? language = ""; + if (languageController.selectLanguage != null) { + language = languageController.selectLanguage.value!.language_code; + } + if (language != null && language.isNotEmpty) { + if (queryUrl.contains("?")) { + queryUrl += "&lang=$language"; + } else { + queryUrl += "?lang=$language"; + } + } + final data = { + "mac": device['mac'], + }; + var response = + await EasyDartModule.dio.delete(queryUrl, data: jsonEncode(data)); + if (response != null) { + var responseData = + response.data is String ? jsonDecode(response.data) : response.data; + ApiResponse res = + ApiResponse.fromJson(responseData, (object) => object); + MyUtils.formatResponse(res, "请求成功".tr, "请求失败".tr); + if (res.code == HttpStatusCodes.ok) { + return res; + } + } else { + return ApiResponse(code: -1, msg: "服务器.失败".tr); + } + return apiResponse; + } catch (e) { + EasyDartModule.logger.info("解绑设备: $e"); + DailyLogUtils.writeLog("解绑设备: $e"); + } finally { + EasyDartModule.logger.info("用户操作:解绑设备"); + DailyLogUtils.writeLog("用户操作:解绑设备"); + } + return ApiResponse(code: -1, msg: "未知错误".tr); // Default return statement + } + + Future getSleepReport() async { + try { + sleepReportData.value = {}; + ApiResponse>> apiResponse = ApiResponse( + code: -1, + msg: "请求失败".tr, + ); + + if (deviceList.value.isEmpty) { + return ApiResponse( + code: HttpStatusCodes.ok, + msg: "请求成功".tr, + ); + } + + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.sleep_report; + + // for (var device in deviceList.value) { + // String mac = device['mac'] ?? ""; + // if (mac.isEmpty) continue; + + // sleepReportData[mac] = []; // 初始化当前设备的数据列表 + + // String queryUrl = + // "$serviceAddress$serviceName$serviceApi?mac=$mac&time=${DateTime.now().millisecondsSinceEpoch}"; + + // try { + // String? language = ""; + // if (languageController.selectLanguage != null) { + // language = languageController.selectLanguage.value!.language_code; + // } + // if (language != null && language.isNotEmpty) { + // if (queryUrl.contains("?")) { + // queryUrl += "&lang=$language"; + // } else { + // queryUrl += "?lang=$language"; + // } + // } + // var response = await EasyDartModule.dio.get(queryUrl); + // if (response != null) { + // var responseData = response.data is String + // ? jsonDecode(response.data) + // : response.data; + + // ApiResponse res = + // ApiResponse.fromJson(responseData, (object) => object); + + // if (res.code == HttpStatusCodes.ok && res.data != null) { + // // 确保数据是一个列表 + // if (res.data is List) { + // sleepReportData[mac] = List.from(res.data); + // } else { + // sleepReportData[mac] = [res.data]; + // } + // } + // } + // } catch (e) { + // EasyDartModule.logger.warning("请求设备 $mac 的睡眠数据失败: $e"); + // } + // } + if (sleepReportData.value.isNotEmpty) { + // 遍历 Map 中的每一个键值对 + sleepReportData.value.forEach((key, report) { + // 确保 report 列表不为空 + if (report.isNotEmpty) { + // 获取该列表的最后一个元素 + var lastElement = report.last; + + // 给最后一个元素添加 selected 属性 + lastElement['selected'] = true; // 假设每个元素是一个 Map 类型 + } + }); + } + + updateAll(); + + return ApiResponse( + code: HttpStatusCodes.ok, + msg: "请求成功".tr, + data: sleepReportData, + ); + } catch (e) { + EasyDartModule.logger.info("设备请求列表异常: $e"); + DailyLogUtils.writeLog("设备请求列表异常: $e"); + return ApiResponse(code: -1, msg: "未知错误".tr, data: {}); + } + } + + updateDeviceShow(device) async { + try { + ApiResponse apiResponse = ApiResponse(code: -1, msg: "操作失败".tr); + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.device_show; + String queryUrl = "${serviceAddress}${serviceName}${serviceApi}"; + String? language = ""; + if (languageController.selectLanguage != null) { + language = languageController.selectLanguage.value!.language_code; + } + if (language != null && language.isNotEmpty) { + if (queryUrl.contains("?")) { + queryUrl += "&lang=$language"; + } else { + queryUrl += "?lang=$language"; + } + } + var data = { + "id": device['_id'], + "show": !device['show'], + }; + var response = + await EasyDartModule.dio.put(queryUrl, data: jsonEncode(data)); + if (response != null) { + var responseData = + response.data is String ? jsonDecode(response.data) : response.data; + ApiResponse res = + ApiResponse.fromJson(responseData, (object) => object); + MyUtils.formatResponse(res, "操作成功".tr, "操作失败".tr); + if (res.code == HttpStatusCodes.ok) { + return res; + } + } else { + return ApiResponse(code: -1, msg: "服务器.失败".tr); + } + return apiResponse; + } catch (e) { + EasyDartModule.logger.info("更新显示异常: $e"); + DailyLogUtils.writeLog("更新显示异常: $e"); + } + return ApiResponse(code: -1, msg: "未知错误".tr); // Default return statement + } +} diff --git a/lib/pages/mh_page/homepage/controller/mht_home_controller.g.dart b/lib/pages/mh_page/homepage/controller/mht_home_controller.g.dart new file mode 100644 index 0000000..4763b38 --- /dev/null +++ b/lib/pages/mh_page/homepage/controller/mht_home_controller.g.dart @@ -0,0 +1,13 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'mht_home_controller.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +MHTHomeModel _$MHTHomeModelFromJson(Map json) => + MHTHomeModel(); + +Map _$MHTHomeModelToJson(MHTHomeModel instance) => + {}; diff --git a/lib/pages/mh_page/homepage/new_Home_page.dart b/lib/pages/mh_page/homepage/new_Home_page.dart index c3353ea..ca47b7f 100644 --- a/lib/pages/mh_page/homepage/new_Home_page.dart +++ b/lib/pages/mh_page/homepage/new_Home_page.dart @@ -3,15 +3,17 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:vbvs_app/common/color/appConstants.dart'; +import 'package:vbvs_app/common/color/app_uri_status.dart'; import 'package:vbvs_app/common/util/FitTool.dart'; import 'package:vbvs_app/common/util/MyUtils.dart'; import 'package:vbvs_app/component/tool/ClickableContainer.dart'; import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; -import 'package:vbvs_app/controller/device/body_device_controller.dart'; import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; import 'package:vbvs_app/controller/user_info_controller.dart'; import 'package:vbvs_app/controller/weather/weather_controller.dart'; import 'package:vbvs_app/pages/mh_page/FloatingSvgIcon.dart'; +import 'package:vbvs_app/pages/mh_page/homepage/component/HomeDeviceWidget.dart'; +import 'package:vbvs_app/pages/mh_page/homepage/controller/mht_home_controller.dart'; class NewHomePage extends StatefulWidget { const NewHomePage({super.key}); @@ -21,7 +23,8 @@ class NewHomePage extends StatefulWidget { class _NewHomePageState extends State { UserInfoController userInfoController = Get.find(); - BodyDeviceController deviceController = Get.find(); + MHTHomeController deviceController = Get.find(); + MHTHomeController homeController = Get.find(); double borderRadius = 16.rpx; var formFieldController = FormFieldController(null); @@ -29,8 +32,6 @@ class _NewHomePageState extends State { GlobalController gloablController = Get.find(); WeatherModelController weatherModelController = Get.find(); - // get userInfoController => Get.find(); - var sleepDays = [].obs; var sleep_mac = "".obs; @@ -54,6 +55,31 @@ class _NewHomePageState extends State { @override void initState() { super.initState(); + if (userInfoController.model.login == 1) { + //请求绑定设备列表 + homeController.getSleepReport(); + deviceController.getDeviceNum().then((apiResponse) { + if (apiResponse.code != HttpStatusCodes.ok) { + TopSlideNotification.show( + Get.context!, + text: apiResponse.msg!, + textColor: themeController.currentColor.sc9, + ); + } + }); + deviceController.getDeviceList(group: 'room').then((apiResponse) { + if (apiResponse.code != HttpStatusCodes.ok) { + TopSlideNotification.show( + Get.context!, + text: apiResponse.msg!, + textColor: themeController.currentColor.sc9, + ); + } else { + //请求睡眠报告 + deviceController.getSleepReport(); + } + }); + } } getWeekName(int i) { @@ -86,6 +112,18 @@ class _NewHomePageState extends State { @override Widget build(BuildContext context) { + deviceController.getDeviceList(group: 'room').then((apiResponse) { + if (apiResponse.code != HttpStatusCodes.ok) { + TopSlideNotification.show( + Get.context!, + text: apiResponse.msg!, + textColor: themeController.currentColor.sc9, + ); + } else { + //请求睡眠报告 + deviceController.getSleepReport(); + } + }); int login = userInfoController.model.login!; return GestureDetector( onTap: () => FocusScope.of(context).unfocus(), @@ -99,638 +137,824 @@ class _NewHomePageState extends State { child: Scaffold( backgroundColor: Colors.transparent, appBar: AppBar( - iconTheme: - IconThemeData(color: themeController.currentColor.sc3), - backgroundColor: Colors.transparent, - automaticallyImplyLeading: false, - titleSpacing: 0, - title: Container( - height: 180.rpx, - child: Row( - children: [ - // 左侧头像 - Obx(() { - return userInfo(userInfoController.model.login); - }), - const Spacer(), // 左右分隔 - FloatingSvgIcon( - assetPath: 'assets/img/icon/xiaoe.svg', - width: 60.rpx, - height: 60.rpx, - onTap: () { - print("点击了小鹅图标"); - }, - ), - SizedBox(width: 40.rpx), - ], - ), - )), - body: SafeArea( - child: Container( - width: MediaQuery.sizeOf(context).width, - height: MediaQuery.sizeOf(context).height * 1.123, - // decoration: BoxDecoration( - // color: AppColors.bg_color, - // image: DecorationImage( - // image: AssetImage("assets/images/background.png"), - // fit: BoxFit.cover, - // ), - // ), - decoration: BoxDecoration(), - child: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.max, + iconTheme: IconThemeData(color: themeController.currentColor.sc3), + backgroundColor: Colors.transparent, + automaticallyImplyLeading: false, + titleSpacing: 0, + title: Container( + height: 180.rpx, + child: Row( children: [ - Container( - padding: - EdgeInsets.fromLTRB(26.rpx, 10.rpx, 26.rpx, 40.rpx), - width: double.infinity, - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 0.rpx), - child: Column( - children: [ - Padding( - padding: - EdgeInsets.fromLTRB(17.rpx, 30.rpx, 0, 0), - child: Row( - children: [ - // Text('上海 22° 多云', - // style: TextStyle( - // fontSize: 26.rpx, - // color: Colors.white)), - // Icon(Icons.cloud, - // size: 30.rpx, color: Colors.white), - Obx(() { - return Row( - children: [ - Text( - "${weatherModelController.model.cityName ?? '未知数据'.tr}", - style: TextStyle( - color: Colors.white, - fontSize: AppConstants() - .normal_text_fontSize, - ), - ), - Text( - "${(weatherModelController.model.current_temperature != null && weatherModelController.model.current_temperature! > 0) ? weatherModelController.model.current_temperature : '未知数据'.tr}" + - "°C", - style: TextStyle( - color: Colors.white, - fontSize: AppConstants() - .normal_text_fontSize, - ), - ), - Text( - "${(weatherModelController.model.weather_info?.isNotEmpty ?? false) ? weatherModelController.model.weather_info : '未知数据'.tr}", - style: TextStyle( - color: Colors.white, - fontSize: AppConstants() - .normal_text_fontSize, - ), - ), - if (weatherModelController - .model.weatherIconurl != - null && - weatherModelController.model - .weatherIconurl!.isNotEmpty) - Container( - width: 35.rpx, - height: 26.rpx, - clipBehavior: Clip.antiAlias, - decoration: BoxDecoration( - shape: BoxShape.circle), - child: Image.network( - weatherModelController - .model.weatherIconurl!, - fit: BoxFit.cover, + // 左侧头像 + Obx(() { + return userInfo(userInfoController.model.login); + }), + const Spacer(), // 左右分隔 + FloatingSvgIcon( + assetPath: 'assets/img/icon/xiaoe.svg', + width: 60.rpx, + height: 60.rpx, + onTap: () { + print("点击了小鹅图标"); + }, + ), + SizedBox(width: 40.rpx), + ], + ), + ), + ), + body: SafeArea( + child: Container( + width: MediaQuery.sizeOf(context).width, + height: MediaQuery.sizeOf(context).height * 1.123, + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + //天气 + Container( + padding: + EdgeInsets.fromLTRB(26.rpx, 10.rpx, 26.rpx, 40.rpx), + width: double.infinity, + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 0.rpx), + child: Column( + children: [ + Padding( + padding: + EdgeInsets.fromLTRB(17.rpx, 30.rpx, 0, 0), + child: Row( + children: [ + Obx(() { + return Row( + children: [ + Text( + "${weatherModelController.model.cityName ?? '未知数据'.tr}", + style: TextStyle( + color: Colors.white, + fontSize: AppConstants() + .normal_text_fontSize, ), ), - ].divide(SizedBox( - width: 20.rpx, - )), - ); - }), - ], + Text( + "${(weatherModelController.model.current_temperature != null && weatherModelController.model.current_temperature! > 0) ? weatherModelController.model.current_temperature : '未知数据'.tr}" + + "°C", + style: TextStyle( + color: Colors.white, + fontSize: AppConstants() + .normal_text_fontSize, + ), + ), + Text( + "${(weatherModelController.model.weather_info?.isNotEmpty ?? false) ? weatherModelController.model.weather_info : '未知数据'.tr}", + style: TextStyle( + color: Colors.white, + fontSize: AppConstants() + .normal_text_fontSize, + ), + ), + if (weatherModelController + .model.weatherIconurl != + null && + weatherModelController.model + .weatherIconurl!.isNotEmpty) + Container( + width: 35.rpx, + height: 26.rpx, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + shape: BoxShape.circle), + child: Image.network( + weatherModelController + .model.weatherIconurl!, + fit: BoxFit.cover, + ), + ), + ].divide(SizedBox( + width: 20.rpx, + )), + ); + }), + ], + ), ), - ), - ], + ], + ), ), ), - ), - Obx(() { - return Container( - padding: EdgeInsets.only(left: 30.rpx, right: 30.rpx), - child: Column( - children: [ - if (gloablController.model.deviceList.length > 0) - Container( + // Obx(() { + // return Container( + // padding: EdgeInsets.only(left: 30.rpx, right: 30.rpx), + // child: Column( + // children: [ + // if (gloablController.model.deviceList.length > 0) + // Container( + // child: Column( + // children: [ + // Container( + // width: MediaQuery.sizeOf(context).width, + // decoration: BoxDecoration( + // color: Colors.white, + // borderRadius: BorderRadius.circular( + // borderRadius), + // ), + // child: Column( + // mainAxisSize: MainAxisSize.max, + // children: [ + // Padding( + // padding: EdgeInsetsDirectional + // .fromSTEB(30.rpx, 16.rpx, + // 16.rpx, 8.rpx), + // child: Container( + // width: + // MediaQuery.sizeOf(context) + // .width, + // height: + // MediaQuery.sizeOf(context) + // .height * + // 0.065, + // decoration: BoxDecoration( + // color: FlutterFlowTheme.of( + // context) + // .secondaryBackground, + // ), + // child: Row( + // mainAxisSize: + // MainAxisSize.max, + // mainAxisAlignment: + // MainAxisAlignment + // .spaceBetween, + // children: [ + // ValueListenableBuilder( + // valueListenable: + // formFieldController, + // builder: (c, a, s) => + // FlutterFlowDropDown< + // String>( + // controller: + // formFieldController, + // options: gloablController + // .model.JunheDevices + // .map((d) => + // "${d["mac"]}") + // .toList(), + // optionLabels: + // gloablController + // .model + // .JunheDevices + // .map( + // (d) { + // var s = d["name"] ?? + // d["mac"]; + // if (s == null) { + // return ""; + // } else { + // return "$s"; + // } + // }).toList(), + // onChanged: (val) { + // // print("$val"); + // // if (val == null) { + // // sleepDays.value = []; + // // } else { + // // getSleeps( + // // formFieldController + // // .value); + // // } + // // // sleep_mac.value = val!; + // }, + // width: 360.rpx, + // height: 72.rpx, + // maxHeight: 200.rpx, + // textStyle: TextStyle( + // fontSize: 28.rpx, + // overflow: + // TextOverflow + // .ellipsis), + // hintText: '', + // icon: Icon( + // Icons + // .keyboard_arrow_down_rounded, + // color: FlutterFlowTheme + // .of(context) + // .secondaryText, + // size: 48.rpx, + // ), + // fillColor: + // stringToColor( + // "#F3F5F6"), + // elevation: 2, + // borderColor: + // stringToColor( + // "#F3F5F6"), + // borderWidth: 2, + // borderRadius: 18, + // margin: + // EdgeInsetsDirectional + // .fromSTEB( + // 32.rpx, + // 8.rpx, + // 32.rpx, + // 8.rpx), + // hidesUnderline: true, + // isOverButton: false, + // isSearchable: false, + // isMultiSelect: false, + // ), + // ), + // InkWell( + // onTap: () { + // // if (formFieldController + // // .value != + // // null) { + // // Get.toNamed( + // // "/sleepWebview", + // // arguments: [ + // // formFieldController + // // .value + // // ]); + // // } + // }, + // child: Row( + // mainAxisSize: + // MainAxisSize.max, + // children: [ + // Text( + // '睡眠报告', + // style: FlutterFlowTheme + // .of(context) + // .bodyMedium + // .override( + // fontFamily: + // 'Readex Pro', + // letterSpacing: + // 0, + // fontSize: + // 30.rpx), + // ), + // SizedBox( + // width: 12.rpx, + // ), + // SvgPicture.asset( + // "assets/images/table.svg", + // width: 28.rpx, + // height: 28.rpx), + // SizedBox( + // width: 20.rpx, + // ), + // ], + // ), + // ), + // ], + // ), + // ), + // ), + // Container( + // padding: EdgeInsets.only( + // top: 0.rpx, + // bottom: 20.rpx, + // left: 16.rpx, + // right: 16.rpx), + // width: double.infinity, + // decoration: BoxDecoration(), + // child: Row( + // mainAxisSize: MainAxisSize.max, + // crossAxisAlignment: + // CrossAxisAlignment.start, + // children: [ + // ...List.generate( + // sleepDays.value.length, + // (index) { + // var day = sleepDays[index]; + // return Expanded( + // child: Container( + // padding: + // EdgeInsets.only( + // top: 10.rpx, + // bottom: 20.rpx), + // width: 100, + // // decoration: BoxDecoration( + // // color: index == 2 + // // ? stringToColor("#F3F5F6") + // // : Colors.white, + // // borderRadius: + // // BorderRadius.circular( + // // borderRadius), + // // ), + // child: Column( + // mainAxisSize: + // MainAxisSize.max, + // mainAxisAlignment: + // MainAxisAlignment + // .start, + // crossAxisAlignment: + // CrossAxisAlignment + // .center, + // children: [ + // Text( + // day['week'], + // style: FlutterFlowTheme + // .of(context) + // .bodyMedium + // .override( + // fontFamily: + // 'Readex Pro', + // fontSize: + // 30.rpx, + // letterSpacing: + // 0, + // ), + // ), + // Text( + // day['date'], + // style: FlutterFlowTheme + // .of(context) + // .bodyMedium + // .override( + // fontFamily: + // 'Readex Pro', + // letterSpacing: + // 0, + // fontSize: + // 24.rpx, + // ), + // ), + // SizedBox( + // height: 6.rpx, + // ), + // Column( + // children: [ + // Row( + // mainAxisSize: + // MainAxisSize + // .max, + // mainAxisAlignment: + // MainAxisAlignment + // .center, + // children: [ + // Text( + // '${day['score'] ?? "-"}', + // style: FlutterFlowTheme.of( + // context) + // .bodyMedium + // .override( + // fontFamily: + // 'Readex Pro', + // fontSize: + // 48.rpx, + // letterSpacing: + // 0, + // ), + // ), + // if (day['score'] != + // null) + // SizedBox( + // width: 2 + // .rpx, + // ), + // if (day['score'] != + // null) + // Text( + // '分', + // style: FlutterFlowTheme.of(context) + // .bodyMedium + // .override( + // fontFamily: 'Readex Pro', + // letterSpacing: 0, + // fontSize: 26.rpx, + // ), + // ), + // ], + // ), + // Container( + // width: + // 120.rpx, + // height: + // 52.rpx, + // decoration: + // BoxDecoration( + // color: stringToColor( + // day['scoreColor'] ?? + // "#f3f5f6"), + // borderRadius: + // BorderRadius.circular( + // 26.rpx), + // shape: BoxShape + // .rectangle, + // ), + // alignment: + // Alignment + // .center, + // child: Text( + // '${day['scoreType'] ?? "暂无"}', + // style: FlutterFlowTheme.of( + // context) + // .bodyMedium + // .override( + // fontFamily: + // 'Readex Pro', + // color: day['scoreType'] != null + // ? Colors.white + // : stringToColor("#ced1d7"), + // letterSpacing: + // 0, + // fontSize: + // 28.rpx, + // ), + // ), + // ) + // ], + // ) + // ], + // ), + // ), + // ); + // }) + // ], + // ), + // ), + // ], + // ), + // ), + // ...List.generate( + // gloablController.model.deviceList + // .length, (index) { + // var device = gloablController + // .model.deviceList[index]; + // String rname = device['roomName']; + // if (index != 0) { + // String lrname = gloablController + // .model.deviceList[index - 1] + // ["roomName"]; + // if (lrname == rname) { + // rname = ""; + // } + // } + // return Column(children: [ + // Padding( + // padding: + // EdgeInsetsDirectional.fromSTEB( + // 20.rpx, 20.rpx, 0, 0), + // child: Container( + // decoration: BoxDecoration(), + // alignment: + // AlignmentDirectional(-1, 0), + // child: rname.isNotEmpty + // ? Text( + // "$rname", + // style: FlutterFlowTheme + // .of(context) + // .bodyMedium + // .override( + // fontFamily: + // 'Readex Pro', + // color: Colors.white, + // fontSize: 32.rpx, + // letterSpacing: 0, + // ), + // ) + // : SizedBox( + // height: 10.rpx, + // ), + // ), + // ), + // // getDeviceList(context, device) + // ]); + // }), + // Padding( + // padding: EdgeInsetsDirectional.fromSTEB( + // 0, 40.rpx, 0, 100.rpx), + // child: Container( + // width: + // MediaQuery.sizeOf(context).width, + // height: 84.rpx, + // decoration: BoxDecoration(), + // child: FFButtonWidget( + // onPressed: () { + // // print('Button pressed ...'); + // // Get.toNamed("/homeDeviceType"); + // }, + // text: '添加新设备'.tr, + // icon: Icon( + // Icons.add, + // size: 60.rpx, + // ), + // options: FFButtonOptions( + // height: 80.rpx, + // padding: EdgeInsetsDirectional + // .fromSTEB( + // 48.rpx, 0, 48.rpx, 0), + // iconPadding: EdgeInsetsDirectional + // .fromSTEB(0, 0, 0, 0), + // color: stringToColor("#182B7C"), + // textStyle: + // FlutterFlowTheme.of(context) + // .titleSmall + // .override( + // fontFamily: + // 'Readex Pro', + // color: Colors.white, + // letterSpacing: 0, + // fontSize: 30.rpx), + // elevation: 3, + // borderSide: BorderSide( + // color: Colors.transparent, + // width: 1, + // ), + // borderRadius: + // BorderRadius.circular( + // borderRadius), + // ), + // ), + // ), + // ), + // ], + // ), + // ), + // if (gloablController.model.deviceList.length == 0) + // Padding( + // padding: EdgeInsetsDirectional.fromSTEB( + // 0, 0, 0, 80.rpx), + // child: ClickableContainer( + // backgroundColor: Colors.transparent, + // highlightColor: Colors.transparent, + // padding: EdgeInsets.all(0), + // onTap: () { + // if (userInfoController.model.login == + // null || + // userInfoController.model.login == 0) { + // TopSlideNotification.show(context, + // text: "请先登录".tr, + // textColor: themeController + // .currentColor.sc9); + // Get.toNamed("/loginPage"); + // } else { + // Get.toNamed("/mHTDeviceTypePage"); + // } + // }, + // child: Container( + // width: MediaQuery.sizeOf(context).width, + // height: 302.rpx, + // padding: EdgeInsets.only( + // top: 90.rpx, bottom: 80.rpx), + // decoration: BoxDecoration( + // borderRadius: + // BorderRadius.circular(borderRadius), + // border: Border.all( + // color: + // stringToColor("#85F5FF"), // 边框颜色 + // width: 1.rpx, // 边框宽度 + // ), + // ), + // child: Column( + // mainAxisSize: MainAxisSize.max, + // mainAxisAlignment: + // MainAxisAlignment.center, + // children: [ + // SvgPicture.asset( + // 'assets/images/icon/add.svg', + // width: 42.rpx, + // height: 42.rpx, + // ), + // SizedBox( + // height: 32.rpx, + // ), + // Text( + // '添加一台新设备'.tr, + // style: TextStyle( + // color: stringToColor("#85F5FF"), + // fontSize: AppConstants() + // .normal_text_fontSize, + // letterSpacing: 0, + // ), + // ), + // ], + // ), + // ), + // ), + // ), + // ], + // ), + // ); + // }), + + //未登录 + Obx(() { + if (userInfoController.model.login! == null || + userInfoController.model.login! == 0) { + return Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 0, 30.rpx, 80.rpx), + child: ClickableContainer( + backgroundColor: Colors.transparent, + highlightColor: Colors.transparent, + padding: EdgeInsets.all(0), + onTap: () { + if (userInfoController.model.login == null || + userInfoController.model.login == 0) { + TopSlideNotification.show(context, + text: "请先登录".tr, + textColor: + themeController.currentColor.sc9); + Get.toNamed("/loginPage"); + } else { + Get.toNamed("/mHTDeviceTypePage"); + } + }, + child: Container( + width: MediaQuery.sizeOf(context).width, + height: 302.rpx, + padding: EdgeInsets.only( + top: 90.rpx, bottom: 80.rpx), + decoration: BoxDecoration( + borderRadius: + BorderRadius.circular(borderRadius), + border: Border.all( + color: stringToColor("#85F5FF"), // 边框颜色 + width: 1.rpx, // 边框宽度 + ), + ), child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, children: [ - Container( - width: MediaQuery.sizeOf(context).width, - decoration: BoxDecoration( - color: Colors.white, - borderRadius: - BorderRadius.circular(borderRadius), - ), - child: Column( - mainAxisSize: MainAxisSize.max, - children: [ - Padding( - padding: - EdgeInsetsDirectional.fromSTEB( - 30.rpx, - 16.rpx, - 16.rpx, - 8.rpx), - child: Container( - width: MediaQuery.sizeOf(context) - .width, - height: MediaQuery.sizeOf(context) - .height * - 0.065, - decoration: BoxDecoration( - color: - FlutterFlowTheme.of(context) - .secondaryBackground, - ), - child: Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: - MainAxisAlignment - .spaceBetween, - children: [ - ValueListenableBuilder( - valueListenable: - formFieldController, - builder: (c, a, s) => - FlutterFlowDropDown< - String>( - controller: - formFieldController, - options: gloablController - .model.JunheDevices - .map((d) => - "${d["mac"]}") - .toList(), - optionLabels: - gloablController.model - .JunheDevices - .map((d) { - var s = d["name"] ?? - d["mac"]; - if (s == null) { - return ""; - } else { - return "$s"; - } - }).toList(), - onChanged: (val) { - // print("$val"); - // if (val == null) { - // sleepDays.value = []; - // } else { - // getSleeps( - // formFieldController - // .value); - // } - // // sleep_mac.value = val!; - }, - width: 360.rpx, - height: 72.rpx, - maxHeight: 200.rpx, - textStyle: TextStyle( - fontSize: 28.rpx, - overflow: TextOverflow - .ellipsis), - hintText: '', - icon: Icon( - Icons - .keyboard_arrow_down_rounded, - color: - FlutterFlowTheme.of( - context) - .secondaryText, - size: 48.rpx, - ), - fillColor: stringToColor( - "#F3F5F6"), - elevation: 2, - borderColor: - stringToColor( - "#F3F5F6"), - borderWidth: 2, - borderRadius: 18, - margin: - EdgeInsetsDirectional - .fromSTEB( - 32.rpx, - 8.rpx, - 32.rpx, - 8.rpx), - hidesUnderline: true, - isOverButton: false, - isSearchable: false, - isMultiSelect: false, - ), - ), - InkWell( - onTap: () { - // if (formFieldController - // .value != - // null) { - // Get.toNamed( - // "/sleepWebview", - // arguments: [ - // formFieldController - // .value - // ]); - // } - }, - child: Row( - mainAxisSize: - MainAxisSize.max, - children: [ - Text( - '睡眠报告', - style: FlutterFlowTheme - .of(context) - .bodyMedium - .override( - fontFamily: - 'Readex Pro', - letterSpacing: - 0, - fontSize: - 30.rpx), - ), - SizedBox( - width: 12.rpx, - ), - SvgPicture.asset( - "assets/images/table.svg", - width: 28.rpx, - height: 28.rpx), - SizedBox( - width: 20.rpx, - ), - ], - ), - ), - ], - ), - ), - ), - Container( - padding: EdgeInsets.only( - top: 0.rpx, - bottom: 20.rpx, - left: 16.rpx, - right: 16.rpx), - width: double.infinity, - decoration: BoxDecoration(), - child: Row( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - ...List.generate( - sleepDays.value.length, - (index) { - var day = sleepDays[index]; - return Expanded( - child: Container( - padding: EdgeInsets.only( - top: 10.rpx, - bottom: 20.rpx), - width: 100, - // decoration: BoxDecoration( - // color: index == 2 - // ? stringToColor("#F3F5F6") - // : Colors.white, - // borderRadius: - // BorderRadius.circular( - // borderRadius), - // ), - child: Column( - mainAxisSize: - MainAxisSize.max, - mainAxisAlignment: - MainAxisAlignment - .start, - crossAxisAlignment: - CrossAxisAlignment - .center, - children: [ - Text( - day['week'], - style: FlutterFlowTheme - .of(context) - .bodyMedium - .override( - fontFamily: - 'Readex Pro', - fontSize: - 30.rpx, - letterSpacing: - 0, - ), - ), - Text( - day['date'], - style: FlutterFlowTheme - .of(context) - .bodyMedium - .override( - fontFamily: - 'Readex Pro', - letterSpacing: - 0, - fontSize: - 24.rpx, - ), - ), - SizedBox( - height: 6.rpx, - ), - Column( - children: [ - Row( - mainAxisSize: - MainAxisSize - .max, - mainAxisAlignment: - MainAxisAlignment - .center, - children: [ - Text( - '${day['score'] ?? "-"}', - style: FlutterFlowTheme.of( - context) - .bodyMedium - .override( - fontFamily: - 'Readex Pro', - fontSize: - 48.rpx, - letterSpacing: - 0, - ), - ), - if (day['score'] != - null) - SizedBox( - width: - 2.rpx, - ), - if (day['score'] != - null) - Text( - '分', - style: FlutterFlowTheme.of( - context) - .bodyMedium - .override( - fontFamily: - 'Readex Pro', - letterSpacing: - 0, - fontSize: - 26.rpx, - ), - ), - ], - ), - Container( - width: 120.rpx, - height: 52.rpx, - decoration: - BoxDecoration( - color: stringToColor( - day['scoreColor'] ?? - "#f3f5f6"), - borderRadius: - BorderRadius - .circular( - 26.rpx), - shape: BoxShape - .rectangle, - ), - alignment: - Alignment - .center, - child: Text( - '${day['scoreType'] ?? "暂无"}', - style: FlutterFlowTheme.of( - context) - .bodyMedium - .override( - fontFamily: - 'Readex Pro', - color: day['scoreType'] != - null - ? Colors.white - : stringToColor("#ced1d7"), - letterSpacing: - 0, - fontSize: - 28.rpx, - ), - ), - ) - ], - ) - ], - ), - ), - ); - }) - ], - ), - ), - ], - ), + SvgPicture.asset( + 'assets/images/icon/add.svg', + width: 42.rpx, + height: 42.rpx, ), - ...List.generate( - gloablController - .model.deviceList.length, (index) { - var device = gloablController - .model.deviceList[index]; - String rname = device['roomName']; - if (index != 0) { - String lrname = gloablController.model - .deviceList[index - 1]["roomName"]; - if (lrname == rname) { - rname = ""; - } - } - return Column(children: [ - Padding( - padding: - EdgeInsetsDirectional.fromSTEB( - 20.rpx, 20.rpx, 0, 0), - child: Container( - decoration: BoxDecoration(), - alignment: - AlignmentDirectional(-1, 0), - child: rname.isNotEmpty - ? Text( - "$rname", - style: FlutterFlowTheme.of( - context) - .bodyMedium - .override( - fontFamily: - 'Readex Pro', - color: Colors.white, - fontSize: 32.rpx, - letterSpacing: 0, - ), - ) - : SizedBox( - height: 10.rpx, - ), - ), - ), - // getDeviceList(context, device) - ]); - }), - Padding( - padding: EdgeInsetsDirectional.fromSTEB( - 0, 40.rpx, 0, 100.rpx), - child: Container( - width: MediaQuery.sizeOf(context).width, - height: 84.rpx, - decoration: BoxDecoration(), - child: FFButtonWidget( - onPressed: () { - // print('Button pressed ...'); - // Get.toNamed("/homeDeviceType"); - }, - text: '添加新设备'.tr, - icon: Icon( - Icons.add, - size: 60.rpx, - ), - options: FFButtonOptions( - height: 80.rpx, - padding: - EdgeInsetsDirectional.fromSTEB( - 48.rpx, 0, 48.rpx, 0), - iconPadding: - EdgeInsetsDirectional.fromSTEB( - 0, 0, 0, 0), - color: stringToColor("#182B7C"), - textStyle: - FlutterFlowTheme.of(context) - .titleSmall - .override( - fontFamily: - 'Readex Pro', - color: Colors.white, - letterSpacing: 0, - fontSize: 30.rpx), - elevation: 3, - borderSide: BorderSide( - color: Colors.transparent, - width: 1, - ), - borderRadius: BorderRadius.circular( - borderRadius), - ), - ), + SizedBox( + height: 32.rpx, + ), + Text( + '添加一台新设备'.tr, + style: TextStyle( + color: stringToColor("#85F5FF"), + fontSize: + AppConstants().normal_text_fontSize, + letterSpacing: 0, ), ), ], ), ), - if (gloablController.model.deviceList.length == 0) - Padding( - padding: EdgeInsetsDirectional.fromSTEB( - 0, 0, 0, 80.rpx), - child: ClickableContainer( - backgroundColor: Colors.transparent, - highlightColor: Colors.transparent, - padding: EdgeInsets.all(0), - onTap: () { - if (userInfoController.model.login == - null || - userInfoController.model.login == 0) { - TopSlideNotification.show(context, - text: "请先登录".tr, - textColor: - themeController.currentColor.sc9); - Get.toNamed("/loginPage"); - } - }, - child: Container( - width: MediaQuery.sizeOf(context).width, - height: 302.rpx, - padding: EdgeInsets.only( - top: 90.rpx, bottom: 80.rpx), - decoration: BoxDecoration( - borderRadius: - BorderRadius.circular(borderRadius), - border: Border.all( - color: stringToColor("#85F5FF"), // 边框颜色 - width: 1.rpx, // 边框宽度 - ), - ), + ), + ); + } + return Container(); + }), + //已登录 + Obx(() { + if (userInfoController.model.login! != null && + userInfoController.model.login! == 1) { + return SingleChildScrollView( + child: Column( + children: [ + if (homeController.bindDeviceNum.value != 0) + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 40.rpx, 30.rpx, 100.rpx), child: Column( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - SvgPicture.asset( - 'assets/images/icon/add.svg', - width: 42.rpx, - height: 42.rpx, - ), - SizedBox( - height: 32.rpx, - ), - Text( - '添加一台新设备'.tr, - style: TextStyle( - color: stringToColor("#85F5FF"), - fontSize: AppConstants() - .normal_text_fontSize, - letterSpacing: 0, - ), - ), - ], + children: homeController + .deviceList.entries + .map((entry) => HomeDeviceWidget( + roomName: entry.key, + deviceStatusList: entry.value, + )) + .toList() + .divide(SizedBox(height: 30.rpx)), ), ), - ), - ), - ], - ), - ); - }) - ], + if (homeController.bindDeviceNum.value == 0) + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 0, 30.rpx, 80.rpx), + child: ClickableContainer( + backgroundColor: Colors.transparent, + highlightColor: Colors.transparent, + padding: EdgeInsets.all(0), + onTap: () { + if (userInfoController.model.login == + null || + userInfoController.model.login == + 0) { + TopSlideNotification.show(context, + text: "请先登录".tr, + textColor: themeController + .currentColor.sc9); + Get.toNamed("/loginPage"); + } else { + Get.toNamed("/mHTDeviceTypePage"); + } + }, + child: Container( + width: MediaQuery.sizeOf(context).width, + height: 302.rpx, + padding: EdgeInsets.only( + top: 90.rpx, bottom: 80.rpx), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular( + borderRadius), + border: Border.all( + color: stringToColor( + "#85F5FF"), // 边框颜色 + width: 1.rpx, // 边框宽度 + ), + ), + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + SvgPicture.asset( + 'assets/images/icon/add.svg', + width: 42.rpx, + height: 42.rpx, + ), + SizedBox( + height: 32.rpx, + ), + Text( + '添加一台新设备'.tr, + style: TextStyle( + color: stringToColor("#85F5FF"), + fontSize: AppConstants() + .normal_text_fontSize, + letterSpacing: 0, + ), + ), + ], + ), + ), + ), + ), + if (homeController.bindDeviceNum.value != 0) + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 0.rpx, 30.rpx, 100.rpx), + child: ClickableContainer( + onTap: () { + // 点击逻辑 + Get.toNamed("/mHTDeviceTypePage"); + }, + backgroundColor: Colors.transparent, + highlightColor: themeController + .currentColor + .sc21, // 这里可以自定义高亮色,透明就用 Colors.transparent + borderRadius: borderRadius, + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 0, 0.rpx, 0), + child: Container( + height: 92.rpx, + alignment: Alignment.center, + decoration: BoxDecoration( + border: Border.all( + color: stringToColor( + "#85F5FF"), // 边框颜色 + width: 1.rpx, // 边框宽度 + ), + borderRadius: BorderRadius.circular( + borderRadius), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.add, + size: 60.rpx, + color: stringToColor("#85F5FF"), + ), + SizedBox(width: 20.rpx), + Text( + '添加新设备'.tr, + style: + FlutterFlowTheme.of(context) + .titleSmall + .override( + fontFamily: + 'Readex Pro', + color: stringToColor( + "#85F5FF"), + letterSpacing: 0, + fontSize: 30.rpx, + ), + ), + ], + ), + ), + ), + ), + ], + ), + ); + } + return Container(); + }), + ], + ), ), ), - ) - - // Padding( - // padding: EdgeInsets.symmetric(horizontal: 30.rpx), - // child: Column( - // children: [ - // Padding( - // padding: EdgeInsets.fromLTRB(17.rpx, 30.rpx, 0, 0), - // child: Row( - // children: [ - // Text('上海 22° 多云', - // style: TextStyle( - // fontSize: 26.rpx, color: Colors.white)), - // Icon(Icons.cloud, - // size: 30.rpx, color: Colors.white), - // ], - // ), - // ), - // ], - // )) - ), + ), ), )); } diff --git a/lib/pages/mh_page/user/controller/mht_login_controller.dart b/lib/pages/mh_page/user/controller/mht_login_controller.dart index 6f95e62..9960278 100644 --- a/lib/pages/mh_page/user/controller/mht_login_controller.dart +++ b/lib/pages/mh_page/user/controller/mht_login_controller.dart @@ -19,7 +19,7 @@ part 'mht_login_controller.g.dart'; @JsonSerializable() class LoginModel { //版本id - int? loginStyle = 1; //1.密码登录 2.短信登录 + int? loginStyle = 1; //1.密码登录 2.短信登录(登录类型: 1:手机号/邮箱+验证码 2:微信一键登录 3:手机号+密码) String? account = '17649984946'; //账户 // String? account = '13953240733'; //账户 @@ -124,13 +124,20 @@ class MHTLoginController extends GetControllerEx { if (registerController.model.register_agree == null || registerController.model.register_agree != true) { message = "需要同意协议".tr; - showToast(message); + TopSlideNotification.show(context, + text: message, textColor: themeController.currentColor.sc9); return message; } + int type = 1; + if (model.loginStyle == 1) { + type = 3; + } else if (model.loginStyle == 2) { + type = 1; + } var data = { - "type": model.loginStyle == 1 ? 3 : 2, + "type": type, "userName": model.account, - "password": model.password, + "password": password, }; String serviceAddress = ServiceConstant.service_address; String serviceName = ServiceConstant.server_service; @@ -169,20 +176,42 @@ class MHTLoginController extends GetControllerEx { if (registerController.model.register_agree == null || registerController.model.register_agree != true) { message = "需要同意协议".tr; - showToast(message); + TopSlideNotification.show(context, + text: message, textColor: themeController.currentColor.sc9); return message; } if (model.phone == null || model.phone!.isEmpty) { message = "请输入手机号".tr; - showToast(message); + TopSlideNotification.show(context, + text: message, textColor: themeController.currentColor.sc9); return message; } if (!MyUtils.isValidPhoneNumber(model.phone!)) { message = '请输入正确的手机号'.tr; - showToast(message); + TopSlideNotification.show(context, + text: message, textColor: themeController.currentColor.sc9); return message; } - return ''; + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.send_code; + String queryUrl = "${serviceAddress}${serviceName}${serviceApi}"; + var data = {"userName": model.phone, "type": 1}; + await requestWithLog( + data: data, + logTitle: "发送验证码", + method: MyHttpMethod.post, + queryUrl: queryUrl, + onSuccess: (res) { + TopSlideNotification.show(context, text: "发送验证码成功".tr); + }, + onFailure: (res) { + message = res.msg!; + TopSlideNotification.show(context, + text: message, textColor: themeController.currentColor.sc9); + }, + ); + return message; } //微信登录 diff --git a/lib/pages/mh_page/user/controller/mht_register_controller.g.dart b/lib/pages/mh_page/user/controller/mht_register_controller.g.dart index dca2e2b..ff40a20 100644 --- a/lib/pages/mh_page/user/controller/mht_register_controller.g.dart +++ b/lib/pages/mh_page/user/controller/mht_register_controller.g.dart @@ -13,6 +13,7 @@ RegisterModel _$RegisterModelFromJson(Map json) => ..register_confirm_pd = json['register_confirm_pd'] as String? ..register_code = json['register_code'] as String? ..register_agree = json['register_agree'] as bool? + ..save_password = json['save_password'] as bool? ..pdshow = json['pdshow'] as bool? ..cpdshow = json['cpdshow'] as bool?; @@ -23,6 +24,7 @@ Map _$RegisterModelToJson(RegisterModel instance) => 'register_confirm_pd': instance.register_confirm_pd, 'register_code': instance.register_code, 'register_agree': instance.register_agree, + 'save_password': instance.save_password, 'pdshow': instance.pdshow, 'cpdshow': instance.cpdshow, }; diff --git a/lib/pages/mh_page/user/page/mht_login_page.dart b/lib/pages/mh_page/user/page/mht_login_page.dart index 3c51819..0ad3bdb 100644 --- a/lib/pages/mh_page/user/page/mht_login_page.dart +++ b/lib/pages/mh_page/user/page/mht_login_page.dart @@ -13,6 +13,7 @@ import 'package:vbvs_app/common/util/CheckNetwork.dart'; import 'package:vbvs_app/common/util/CommonVariables.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/component/tool/CustomCard.dart'; import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; import 'package:vbvs_app/controller/time/countdown_controller.dart'; @@ -947,7 +948,7 @@ class MHTLoginPage extends GetView { .countdown .value == 0 - ? '获取验证码' + ? '获取验证码'.tr : '${countdownController.countdown.value}' + '秒'.tr, style: TextStyle( @@ -2049,14 +2050,40 @@ class MHTLoginPage extends GetView { SizedBox( height: 30.rpx, ), - Container( - width: 90.rpx, - height: 90.rpx, - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage( - 'assets/images/wechat.png'), // 本地图片 - fit: BoxFit.fill, // 填满整个 Container + ClickableContainer( + backgroundColor: Colors.white, // 背景色 + highlightColor: Colors.grey, // 点击水波纹颜色 + borderRadius: 999.rpx, + 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); + TopSlideNotification.show(context,text: "测试阶段,暂不支持".tr); + }, + child: Container( + width: 90.rpx, + height: 90.rpx, + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage( + 'assets/images/wechat.png'), // 本地图片 + fit: BoxFit.fill, // 填满整个 Container + ), ), ), ), diff --git a/lib/pages/person/person_page.dart b/lib/pages/person/person_page.dart index dcd493b..d04063c 100644 --- a/lib/pages/person/person_page.dart +++ b/lib/pages/person/person_page.dart @@ -112,7 +112,8 @@ class _EPageState extends State { if (apiRespons.code == HttpStatusCodes.ok) { TopSlideNotification.show(context, text: apiRespons.msg!); - updateDeviceBindStatus(blueteethBindController.currentDeviceMac!.value); + updateDeviceBindStatus(blueteethBindController + .currentDeviceMac!.value); Get.offNamed("/bindDeviceSuccess"); } else { TopSlideNotification.show(context, @@ -772,21 +773,21 @@ class _EPageState extends State { mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Padding( - padding: EdgeInsetsDirectional.fromSTEB( - 0, 8.rpx, 0, 0), - child: Container( - width: 23.rpx, - height: 23.rpx, - // width: double.infinity, - decoration: BoxDecoration(), - child: SvgPicture.asset( - 'assets/img/icon/tips.svg', - fit: BoxFit.cover, - color: themeController.currentColor.sc4, - ), - ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0, 8.rpx, 0, 0), + child: Container( + width: 23.rpx, + height: 23.rpx, + // width: double.infinity, + decoration: BoxDecoration(), + child: SvgPicture.asset( + 'assets/img/icon/tips.svg', + fit: BoxFit.cover, + color: themeController.currentColor.sc4, ), + ), + ), Expanded( child: Text( '人员资料.提示'.tr, diff --git a/lib/routers/mh_routers.dart b/lib/routers/mh_routers.dart index 424d456..6e864eb 100644 --- a/lib/routers/mh_routers.dart +++ b/lib/routers/mh_routers.dart @@ -1,10 +1,9 @@ import 'package:ef/ef.dart'; import 'package:flutter/cupertino.dart'; -import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; import 'package:vbvs_app/controller/user_info_controller.dart'; -import 'package:vbvs_app/main.dart'; +import 'package:vbvs_app/pages/device_bind/bind_device_success.dart'; import 'package:vbvs_app/pages/main_bottom/component/main_page_b_bottom_change.dart'; import 'package:vbvs_app/pages/mh_page/HomeDeviceType.dart'; import 'package:vbvs_app/pages/mh_page/LanguagePage.dart'; @@ -12,6 +11,10 @@ import 'package:vbvs_app/pages/mh_page/address_list_page.dart'; import 'package:vbvs_app/pages/mh_page/apply_repair_page.dart'; import 'package:vbvs_app/pages/mh_page/book_info_page.dart'; import 'package:vbvs_app/pages/mh_page/book_success_page.dart'; +import 'package:vbvs_app/pages/mh_page/device/mht_bind_device_type.dart'; +import 'package:vbvs_app/pages/mh_page/device/mht_blueteeth_device_page.dart'; +import 'package:vbvs_app/pages/mh_page/device/mht_device_calibration.dart'; +import 'package:vbvs_app/pages/mh_page/device/mht_wifi_page.dart'; import 'package:vbvs_app/pages/mh_page/device_list.dart'; import 'package:vbvs_app/pages/mh_page/device_people_info.dart'; import 'package:vbvs_app/pages/mh_page/device_share_page.dart'; @@ -51,9 +54,8 @@ var mhroutes = { "/addressListPage": (contxt) => AddressListPage(), "/editAddressPage": (contxt) => EditAddressPage(), "/languagePage": (context) => LanguagePage(), - '/bluetoothPage': (context, {arguments}) => BluetoothPage(data: arguments), - "/devicePeopleInfo": (context, {arguments}) => - DevicePeopleInfo(data: arguments), + // '/bluetoothPage': (context) => BluetoothPage(), + // "/devicePeopleInfo": (context) => DevicePeopleInfo(), "/sleepHabitPage": (context) => SleepHabitPage(), '/syms': (context) => Smys(), "/applyRepairPage": (context) => ApplyRepairPage(), @@ -71,6 +73,12 @@ var mhroutes = { "/updatePasswordPage": (context) => UpdatePasswordPage(), "/deviceSharePage": (context) => ShareDeviceWidget(), "/deviceListPage": (context) => DeviceListPage(), + "/mHTDeviceTypePage": (context, {arguments}) => MHTBindDeviceTypePage(), + "/mHTBlueteethDevicePage": (context, {arguments}) => + MHTBlueteethDevicePage(data: arguments), + "/mHTwifiPage": (contxt, {arguments}) => MHTWifiPage(deviceInfo: arguments), + "/calibrationPage": (contxt) => MHTCalibrationPage(), + "/bindDeviceSuccess": (contxt) => BindDeviceSuccess(), }; var mhonGenerateRoute = (RouteSettings settings) { final String? name = settings.name; // 获取路由名称,如 /news 或 /search