import 'dart:async'; import 'dart:io'; import 'dart:typed_data'; 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:permission_handler/permission_handler.dart'; import 'package:vbvs_app/common/color/appConstants.dart'; import 'package:vbvs_app/common/util/BluetoothFirmwareUpdater.dart'; import 'package:vbvs_app/common/util/BluetoothHelper.dart'; import 'package:vbvs_app/common/util/CommonVariables.dart'; import 'package:vbvs_app/common/util/FirmwareVersionService.dart'; import 'package:vbvs_app/common/util/FitTool.dart'; import 'package:vbvs_app/common/util/MyUtils.dart'; import 'package:vbvs_app/component/NullDataComponentWidget.dart'; import 'package:vbvs_app/component/tool/ClickableContainer.dart'; import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; import 'package:vbvs_app/controller/mh_controller/device_list_controller.dart'; import 'package:vbvs_app/pages/common/selectDialog.dart'; import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart'; import 'package:vbvs_app/pages/mh_page/device/controller/mht_bluetooth_controller.dart'; import 'package:vbvs_app/pages/mh_page/device/mht_blueteeth_device_page.dart'; import 'package:vbvs_app/pages/mh_page/device/model/BlueToothDataModel.dart'; class VitalSignsSensorPage extends StatefulWidget { Map data; VitalSignsSensorPage({required this.data}); @override _VitalSignsSensorState createState() => _VitalSignsSensorState(); } class _VitalSignsSensorState extends State { BoxConstraints? bodysize; DeviceListController controller = Get.find(); Timer? _timer; late FlutterBluePlus flutterBlue; StreamSubscription>? _scanSubscription; MHTBlueToothController mhtBlueToothController = Get.find(); @override void initState() { //blog发送的时候关闭,发完时候进行开启,以便接受vota path="/root/mtd/update"的日志 //blog enable //控制每1s的数量 //前面的15s的速度每秒安卓50/苹果30帧,后面安卓150帧/苹果100帧 //苹果base64编码前每帧不超过100字节,安卓180字节 super.initState(); _fetchDeviceList(); _timer = Timer.periodic(Duration(seconds: 5), (timer) { _fetchDeviceList(); }); flutterBlue = FlutterBluePlus(); _fetchFirmwareInfo(); } _fetchDeviceList() async { await controller.getVitalList(widget.data['mac'.tr]); } @override void dispose() { _timer?.cancel(); // 页面销毁时取消定时器 super.dispose(); } @override Widget build(BuildContext context) { return LayoutBuilder(builder: (context, cc) { bodysize = cc; return GestureDetector( child: Container( decoration: const BoxDecoration( image: DecorationImage( image: AssetImage('assets/images/new_background.png'), // 本地图片 fit: BoxFit.fill, // 填满整个 Container ), ), child: Scaffold( backgroundColor: Colors.transparent, appBar: AppBar( backgroundColor: Colors.transparent, iconTheme: const IconThemeData(color: Colors.white), automaticallyImplyLeading: false, titleSpacing: 0, title: SizedBox( width: double.infinity, height: 180.rpx, child: Stack( alignment: Alignment.center, children: [ // 中间居中的标题 Text( "体征传感器".tr, textAlign: TextAlign.center, style: TextStyle( color: Colors.white, fontSize: 30.rpx, ), ), // 左侧图标 Positioned( left: 0.rpx, child: returnIconButtomNew(), ), Positioned( right: 0.rpx, child: ClickableContainer( backgroundColor: Colors.transparent, highlightColor: Colors.transparent, padding: EdgeInsets.symmetric( horizontal: 30.rpx, vertical: 30.rpx), onTap: () { // TopSlideNotification.show(context); //检测蓝牙是否开启,没有开启提示开启蓝牙 //如果已经开启,则进行x秒的蓝牙扫描 BluetoothHelper.listenBluetoothState((isOn) { mhtBlueToothController.model.bluetooth = isOn; mhtBlueToothController.updateAll(); if (!isOn) { mhtBlueToothController.model.blueRawData = []; mhtBlueToothController .model.deviceDataStatus = []; mhtBlueToothController.updateAll(); _showBluetoothNotEnabledDialog(); } }); _checkBluetoothPermission(); }, child: Obx(() { return SizedBox( width: 34.rpx, height: mhtBlueToothController.isScanning.value ? 34.rpx : 24.rpx, child: mhtBlueToothController.isScanning.value ? CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation( themeController.currentColor.sc3, ), ) : SvgPicture.asset( 'assets/img/icon/upgrade.svg', fit: BoxFit.cover, color: Colors.white, ), ); }), ), ), ], ), ), centerTitle: false, ), body: SafeArea( child: Obx(() { var list = controller.model.vitalList; return list.isEmpty ? const NullDataWidget() : _buildVitalListView(list); }), ), ))); }); } Widget _buildVitalListView(List dataList) { return Container( width: double.infinity, padding: EdgeInsets.symmetric(horizontal: 30.rpx), child: SingleChildScrollView( child: Column( children: [ SizedBox(height: 30.rpx), ...List.generate(dataList.length, (index) { final item = dataList[index]; final title = index == 0 ? '体征传感器A'.tr : '体征传感器B'.tr; // ✅ 只处理 A/B 场景 return VitalWidget( title: title, data: item, ); }).divide(SizedBox(height: 30.rpx)), SizedBox(height: 30.rpx), ], ), ), ); } Future _checkBluetoothPermission() async { PermissionStatus bluetoothStatus = await Permission.bluetooth.status; PermissionStatus locationStatus = await Permission.location.status; if (bluetoothStatus.isGranted && locationStatus.isGranted) { _startScanning(); } else { _requestBluetoothPermission(); } } void _startScanning() async { try { if (!mounted) return; _scanSubscription?.cancel(); mhtBlueToothController.isScanning.value = true; _scanSubscription = FlutterBluePlus.scanResults.listen((results) { if (!mounted) return; final filteredResults = results.map((r) { Map d = { "updateTime": DateTime.now().millisecondsSinceEpoch, "name": r.advertisementData.localName?.trim() ?? r.advertisementData.advName?.trim() ?? r.device.name ?? "", "rssi": r.rssi, "device": r.device, "connectable": r.advertisementData.connectable, }; // 从 manufacturerData 解析设备唯一 ID Map> m_d = r.advertisementData.manufacturerData; String? deviceId; int? version; m_d.forEach((key, value) { if (value == null || value.isEmpty) return; if (key == 65517) { List a = [0, 0, ...value]; advertisDataFormatter(a, d); // 你原来的处理 if (d['adData']?['deviceId'] != null) { deviceId = d['adData']['deviceId']; version = d['adData']['version']; } } else if (key == 11125 && value.length == 8) { deviceId = ab2str(value.sublist(2, 8)).toUpperCase(); } else if (value.length == 8 && isQuanShiDevice(d["name"])) { deviceId = ab2str(value.sublist(2, 8)).toUpperCase(); } else if ((value.length == 4 || value.length == 6) && isMHTSWES(d["name"])) { List a; if (value.length == 4) { ByteData bd = ByteData(2); bd.setUint16(0, key, Endian.little); a = [bd.getUint8(0), bd.getUint8(1), ...value]; } else { a = [...value]; } deviceId = ab2str(a).toUpperCase(); } }); d['id'] = deviceId ?? r.device.remoteId.str; if (version != null) { d['version'] = version; } return BlueToothDataModel.fromScanResult( r, 1, bind: false, name: d['name'], mac: d['id'], version: d['version'], ); }).where((d) { List macList = controller.model.vitalList .map((e) => e['mac']?.toString() ?? "") .toList(); //todo 过滤,跟远程的版本信息比对 // return macList.contains(d.mac) && d.version != null; if (formatString(d.mac) == "48CA43B2E0B4") { ef.log(""); } return macList.contains(formatString(d.mac)); }).toList(); print("$filteredResults"); for (var item in filteredResults) { // 如果不存在才加入,初始进度设为-1,代表未开始升级 mhtBlueToothController.localUpgradeMac.putIfAbsent( item.mac, () => {"progress": -1, "curversion": item.version, 'device': item}); } }); await FlutterBluePlus.startScan(timeout: Duration(seconds: 5)); FlutterBluePlus.isScanning.listen((scanning) { mhtBlueToothController.isScanning.value = scanning; }); } catch (e) { ef.log("$e"); } } Future _requestBluetoothPermission() async { Map statuses = {}; bool dialogShown = false; // 标记是否弹过权限提示弹窗 if (Platform.isIOS) { PermissionStatus isBleGranted = await Permission.bluetooth.request(); print('checkBlePermissions-ios, isBleGranted=$isBleGranted'); if (isBleGranted.isGranted) { // startBluetoothScanning(); _startScanning(); } else { // showToast("蓝牙开关或蓝牙权限未开启,请开启蓝牙开关与蓝牙权限".tr, closeTime: 7); try { _startScanning(); } catch (e) { TopSlideNotification.show(context, text: "蓝牙权限未开启,请在设置中开启蓝牙权限".tr, textColor: themeController.currentColor.sc9); } } } else if (Platform.isAndroid) { try { // 检查是否已授权 bool alreadyGranted = await Permission.bluetoothScan.isGranted && await Permission.bluetoothConnect.isGranted && await Permission.location.isGranted; if (!alreadyGranted) { // 弹出自定义提示 showPermissionInfoDialog( Get.context!, CommonVariables().bluetoothpermissionInfo); dialogShown = true; await Future.delayed(const Duration(milliseconds: 300)); // 请求权限 statuses = await [ Permission.bluetoothScan, Permission.bluetoothConnect, Permission.location, ].request(); ef.log("权限状态: $statuses"); } else { statuses = { Permission.bluetoothScan: PermissionStatus.granted, Permission.bluetoothConnect: PermissionStatus.granted, Permission.location: PermissionStatus.granted, }; } } catch (e) { ef.log("申请权限出错: $e"); } finally { // 只有真的弹过提示才关闭 if (dialogShown && Get.context != null) { Navigator.of(Get.context!, rootNavigator: true).pop(); } } bool allGranted = statuses[Permission.bluetoothScan]?.isGranted == true && statuses[Permission.bluetoothConnect]?.isGranted == true && statuses[Permission.location]?.isGranted == true; if (allGranted) { _startScanning(); } else { _showPermissionDeniedDialog(context); } } else { TopSlideNotification.show(context, text: "当前系统不支持蓝牙,无法使用此功能".tr, textColor: themeController.currentColor.sc9); } } void _showPermissionDeniedDialog(BuildContext context) { TopSlideNotification.show(context, text: "应用需要蓝牙和位置权限才能扫描设备。请授予权限。".tr, textColor: themeController.currentColor.sc9); } _showBluetoothNotEnabledDialog() async { await showTipDialog( colors: AppConstants().mhtNormalButton, 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"), ), ), ], )); } void _fetchFirmwareInfo() async { final firmwareInfo = await FirmwareVersionService.getLatestFirmwareVersion( baseUrl: "https://share.file.he-info.cn", firmwarePath: "/ota/aithv2/", ); if (firmwareInfo != null) { print("最新版本号: ${firmwareInfo.version}"); print("最新文件名: ${firmwareInfo.fileName}"); print("最新文件下载地址: ${firmwareInfo.downloadUrl}"); mhtBlueToothController.currentUpgradeVersion = firmwareInfo.version; mhtBlueToothController.currentUpgradeName = firmwareInfo.fileName; // 如果需要,也可以存储下载URL mhtBlueToothController.currentUpgradeUrl = firmwareInfo.downloadUrl; } else { print("未找到有效版本"); TopSlideNotification.show(context, text: "获取固件列表失败,请检查网络".tr, textColor: themeController.currentColor.sc9); } } String formatString(String input) { /// 格式化字符串:移除所有空格和冒号,并将小写字母转换为大写 return input.replaceAll(' ', '').replaceAll(':', '').toUpperCase(); } } // class VitalWidget extends StatelessWidget { // final String title; // final Map data; // const VitalWidget({ // super.key, // required this.title, // required this.data, // }); // @override // Widget build(BuildContext context) { // MHTBlueToothController mhtBlueToothController = Get.find(); // final String id = data['mac'.tr] ?? '--'; // final int? timestamp = data['status']['updateTime']; // final String time = (timestamp != null) // ? DateFormat('yyyy-MM-dd HH:mm') // .format(DateTime.fromMillisecondsSinceEpoch(timestamp)) // : '--'; // return ClickableContainer( // backgroundColor: Color(0xFF003058), // highlightColor: Color(0xFF055466), // borderRadius: 20.rpx, // padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 30.rpx, 30.rpx, 40.rpx), // onTap: () {}, // child: Column( // mainAxisSize: MainAxisSize.max, // children: [ // Container( // width: double.infinity, // // constraints: BoxConstraints( // // minHeight: 60.rpx, // // ), // child: Align( // alignment: AlignmentDirectional(-1, 0), // child: Text( // // '实时监测结果通知'.tr, // title, // style: TextStyle( // fontFamily: 'Inter', // fontSize: 30.rpx, // letterSpacing: 0.0, // color: themeController.currentColor.sc3, // ), // ), // ), // ), // Row( // mainAxisAlignment: MainAxisAlignment.start, // children: [ // Container( // width: MediaQuery.sizeOf(context).width * 0.14, // constraints: BoxConstraints(minWidth: 110.rpx), // child: Text( // "设备ID".tr, // style: TextStyle( // color: stringToColor("#929699"), // fontSize: 26.rpx, // ), // maxLines: 2, // overflow: TextOverflow.ellipsis, // ), // ), // Text( // id, // style: TextStyle(fontSize: 26.rpx, color: Color(0xFFFFFFFF)), // ), // ].divide(SizedBox(width: 33.rpx)), // ), // Row( // mainAxisAlignment: MainAxisAlignment.start, // children: [ // Container( // width: MediaQuery.sizeOf(context).width * 0.14, // constraints: BoxConstraints(minWidth: 110.rpx), // child: Text( // "更新时间".tr, // style: TextStyle( // color: stringToColor("#929699"), // fontSize: 26.rpx, // ), // maxLines: 2, // overflow: TextOverflow.ellipsis, // ), // ), // Text( // time, // style: TextStyle(fontSize: 26.rpx, color: Color(0xFFFFFFFF)), // ), // ].divide(SizedBox(width: 33.rpx)), // ), // Row( // mainAxisSize: MainAxisSize.max, // children: [ // Container( // width: MediaQuery.sizeOf(context).width * 0.14, // constraints: BoxConstraints(minWidth: 110.rpx), // child: Text( // "设备状态".tr, // style: TextStyle( // color: stringToColor("#929699"), // fontSize: 26.rpx, // ), // maxLines: 2, // overflow: TextOverflow.ellipsis, // ), // ), // Row( // mainAxisSize: MainAxisSize.max, // children: [ // if (data['status']['signal'] != null && // data['status']['signal'] != -1 && // data['status']['status'] != null && // data['status']['status'] == 1) // ClickableContainer( // backgroundColor: Colors.transparent, // highlightColor: Colors.grey, // 或根据你主题定制颜色 // padding: EdgeInsets.zero, // borderRadius: 0, // onTap: () { // // 点击事件 // showTipDialog( // context, // Container( // child: RichText( // text: TextSpan( // text: "信号强度".tr, // 文本前缀部分 // style: TextStyle( // color: Colors.black, // fontSize: // AppConstants().title_text_fontSize, // ), // children: [ // TextSpan( // text: getBedSignal( // data['status']['signal']), // 状态部分 // style: TextStyle( // color: themeController // .currentColor.sc2, // 同样颜色,也可改成其他颜色 // fontSize: // AppConstants().title_text_fontSize, // ), // ), // ], // ), // ), // ), // backgroundColor: Color(0xFFFFFFFF), // colors: [ // Color(0XFF1592AA), // Color(0xFF0C83A7), // Color(0xFF006FA3) // ], // ); // }, // child: Container( // width: 30.rpx, // height: 24.rpx, // child: Image.asset( // 'assets/img/signal${_getSignalLevel(data['status']['signal'])}.png'), // ), // ), // if (data['status']['inBed'] != null && // data['status']['status'] != null && // data['status']['status'] == 1) // ClickableContainer( // backgroundColor: Colors.transparent, // highlightColor: Colors.grey, // padding: EdgeInsetsDirectional.fromSTEB(0, 0.rpx, 0, 0), // borderRadius: 0, // onTap: () { // showTipDialog( // context, // Container( // child: RichText( // text: TextSpan( // text: "是否在床".tr, // 文本前缀部分 // style: TextStyle( // color: Colors.black, // fontSize: // AppConstants().title_text_fontSize, // ), // children: [ // TextSpan( // text: getBedStatus( // data['status']['inBed']), // 状态部分 // style: TextStyle( // color: themeController // .currentColor.sc2, // 同样颜色,也可改成其他颜色 // fontSize: // AppConstants().title_text_fontSize, // ), // ), // ], // ), // ), // ), // backgroundColor: Color(0xFFFFFFFF), // colors: [ // Color(0XFF1592AA), // Color(0xFF0C83A7), // Color(0xFF006FA3) // ], // ); // }, // child: SizedBox( // width: 16.rpx, // height: 36.rpx, // child: SvgPicture.asset( // data['status']['inBed'] == 0 // ? 'assets/img/icon/not_bed.svg' // : 'assets/img/icon/in_bed.svg', // fit: BoxFit.fill, // ), // ), // ), // if (data['status']['failure'] != 0 && // data['status']['status'] != null && // data['status']['status'] == 1) // ClickableContainer( // backgroundColor: Colors.transparent, // highlightColor: Colors.grey, // 可自定义点击水波纹颜色 // padding: EdgeInsetsDirectional.fromSTEB(0, 0.rpx, 0, 0), // borderRadius: 0, // onTap: () { // showTipDialog( // context, // Container( // child: RichText( // text: TextSpan( // text: "设备故障".tr, // 文本前缀部分 // style: TextStyle( // color: Colors.black, // fontSize: // AppConstants().title_text_fontSize, // ), // children: [ // // TextSpan( // // text:widget // // .device['status']['status'] == 1?"在线".tr:"离线".tr, // 状态部分 // // style: TextStyle( // // color: themeController.currentColor // // .sc2, // 同样颜色,也可改成其他颜色 // // fontSize: AppConstants() // // .title_text_fontSize, // // ), // // ), // ], // ), // ), // ), // backgroundColor: Color(0xFFFFFFFF), // colors: [ // Color(0XFF1592AA), // Color(0xFF0C83A7), // Color(0xFF006FA3) // ], // ); // }, // child: SizedBox( // width: 27.rpx, // height: 27.rpx, // child: SvgPicture.asset( // 'assets/img/icon/device_issue.svg', // fit: BoxFit.cover, // ), // ), // ), // if (data['status']['status'] != null && // data['status']['status'] == 0) // ClickableContainer( // backgroundColor: Colors.transparent, // highlightColor: Colors.grey, // 可替换为点击高亮色 // padding: EdgeInsetsDirectional.fromSTEB(0, 0.rpx, 0, 0), // borderRadius: 0, // onTap: () { // showTipDialog( // context, // Container( // child: RichText( // text: TextSpan( // text: "网络状态".tr, // 文本前缀部分 // style: TextStyle( // color: Colors.black, // fontSize: // AppConstants().title_text_fontSize, // ), // children: [ // TextSpan( // text: data['status']['status'] == 1 // ? "在线".tr // : "离线".tr, // 状态部分 // style: TextStyle( // color: themeController // .currentColor.sc2, // 同样颜色,也可改成其他颜色 // fontSize: // AppConstants().title_text_fontSize, // ), // ), // ], // ), // ), // ), // backgroundColor: Color(0xFFFFFFFF), // colors: [ // Color(0XFF1592AA), // Color(0xFF0C83A7), // Color(0xFF006FA3) // ], // ); // }, // child: SizedBox( // width: 27.rpx, // height: 27.rpx, // child: SvgPicture.asset( // // data['status']['status'] == 1 // // ? 'assets/img/icon/device_online.svg' // // : 'assets/img/icon/device_offline.svg', // 'assets/img/icon/device_issue.svg', // fit: BoxFit.cover, // ), // ), // ), // if (mhtBlueToothController.localUpgradeMac // .containsKey(data['mac'])) // ClickableContainer( // backgroundColor: Colors.transparent, // highlightColor: Colors.grey, // 可自定义点击效果颜色 // padding: EdgeInsetsDirectional.fromSTEB(0, 0.rpx, 0, 0), // borderRadius: 0, // onTap: () { // String curCersion = mhtBlueToothController // .localUpgradeMac[data['mac']]!['curversion'] // .toString(); // String newVersion = // mhtBlueToothController.currentUpgradeVersion ?? // ""; // if (mhtBlueToothController // .localUpgradeMac[data['mac']]!['progress'] == // -1) { // showConfirmUpDialog( // context, // Column( // children: [ // SizedBox( // width: 94.rpx, // height: 70.rpx, // child: SvgPicture.asset( // 'assets/img/icon/upgrade.svg', // fit: BoxFit.cover, // ), // ), // Text( // "设备升级".tr, // style: TextStyle( // color: stringToColor("#333333"), // fontSize: // AppConstants().title_text_fontSize, // ), // ), // Text( // "当前版本".tr + ":" + "${curCersion}", // style: TextStyle( // color: stringToColor("#333333"), // fontSize: // AppConstants().title_text_fontSize, // ), // ), // Text( // "最新版本".tr + ":" + "${newVersion}", // style: TextStyle( // color: stringToColor("#333333"), // fontSize: // AppConstants().title_text_fontSize, // ), // ), // ].divide(SizedBox(height: 37.rpx)), // ), // "".tr, onConfirm: () async { // final deviceData = mhtBlueToothController // .localUpgradeMac[data['mac']]!; // final firmwareUrl = // mhtBlueToothController.currentUpgradeUrl; // if (firmwareUrl == null) { // TopSlideNotification.show(context, // text: "未获取到固件下载地址".tr); // return; // } // try { // final scanResult = // deviceData['device'].scanResult; // final thapp = THapp(device: scanResult.device); // if (!thapp.isConnected) { // await thapp.device.connect(); // } // await MultiDeviceFirmwareUpdater().startUpgrade( // thapp: thapp, // mac: data['mac'], // firmwareUrl: firmwareUrl, // ); // } catch (e) { // print("升级失败: $e"); // TopSlideNotification.show(context, // text: "升级失败: ${e.toString()}".tr, // textColor: Colors.red); // } // }, // onCancel: () {}, // backgroundColor: Colors.white, // confirmText: "立即升级".tr, // cancelText: "下次再说".tr); // // return; // } else { // showConfirmCancelDialog( // context, // Column( // children: [ // SizedBox( // width: 94.rpx, // height: 70.rpx, // child: SvgPicture.asset( // 'assets/img/icon/upgrade.svg', // fit: BoxFit.cover, // // color: themeController.currentColor.sc3, // 若你想加颜色控制可取消注释 // ), // ), // Text( // "设备升级".tr, // style: TextStyle( // color: stringToColor("#333333"), // fontSize: // AppConstants().title_text_fontSize, // ), // ), // Text( // "当前版本".tr + // ":" + // "${mhtBlueToothController.localUpgradeMac[data['mac']]!['curversion']}", // style: TextStyle( // color: stringToColor("#333333"), // fontSize: // AppConstants().title_text_fontSize, // ), // ), // Text( // "最新版本".tr + ":" + "${newVersion}", // style: TextStyle( // color: stringToColor("#333333"), // fontSize: // AppConstants().title_text_fontSize, // ), // ), // Text( // "升级进度".tr + // ":" + // "${mhtBlueToothController.localUpgradeMac[data['mac']]!['progress']}" + // "%", // style: TextStyle( // color: stringToColor("#333333"), // fontSize: // AppConstants().title_text_fontSize, // ), // ), // ].divide(SizedBox(height: 37.rpx)), // ), // "".tr, onConfirm: () async { // //todo 取消升级 将进度改为-1 // }, // onCancel: () {}, // backgroundColor: Colors.white, // confirmText: "取消升级".tr, // cancelText: "返回".tr, // confirmButtonColor: stringToColor("#FF7159")); // } // }, // child: Obx(() { // var aa = mhtBlueToothController.isScanning; // print(aa); // return Row( // children: [ // SizedBox( // width: 34.rpx, // height: 24.rpx, // child: SvgPicture.asset( // 'assets/img/icon/upgrade.svg', // fit: BoxFit.cover, // // color: themeController.currentColor.sc3, // 若你想加颜色控制可取消注释 // ), // ), // if ((mhtBlueToothController.localUpgradeMac[ // data['mac']]!['progress']) != // -1) // Text( // "${mhtBlueToothController.localUpgradeMac[data['mac']]!['progress']}" + // "%", // style: TextStyle( // color: Colors.white, // fontSize: 26.rpx, // ), // ), // ].divide(SizedBox( // width: 10.rpx, // )), // ); // }), // ), // ].divide(SizedBox(width: 50.rpx)), // ), // ].divide(SizedBox(width: 34.rpx)), // ), // ].divide( // SizedBox(height: 20.rpx), // )), // ); // } // } class VitalWidget extends StatelessWidget { final String title; final Map data; const VitalWidget({ super.key, required this.title, required this.data, }); @override Widget build(BuildContext context) { MHTBlueToothController mhtBlueToothController = Get.find(); final String id = data['mac'.tr] ?? '--'; final int? timestamp = data['status']['updateTime']; final String time = (timestamp != null) ? DateFormat('yyyy-MM-dd HH:mm') .format(DateTime.fromMillisecondsSinceEpoch(timestamp)) : '--'; return ClickableContainer( backgroundColor: Color(0xFF003058), highlightColor: Color(0xFF055466), borderRadius: 20.rpx, padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 30.rpx, 30.rpx, 40.rpx), onTap: () {}, child: Column( mainAxisSize: MainAxisSize.max, children: [ Container( width: double.infinity, child: Align( alignment: AlignmentDirectional(-1, 0), child: Text( title, style: TextStyle( fontFamily: 'Inter', fontSize: 30.rpx, letterSpacing: 0.0, color: themeController.currentColor.sc3, ), ), ), ), Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Container( width: MediaQuery.sizeOf(context).width * 0.14, constraints: BoxConstraints(minWidth: 110.rpx), child: Text( "设备ID".tr, style: TextStyle( color: stringToColor("#929699"), fontSize: 26.rpx, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), ), Text( id, style: TextStyle(fontSize: 26.rpx, color: Color(0xFFFFFFFF)), ), ].divide(SizedBox(width: 33.rpx)), ), Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Container( width: MediaQuery.sizeOf(context).width * 0.14, constraints: BoxConstraints(minWidth: 110.rpx), child: Text( "更新时间".tr, style: TextStyle( color: stringToColor("#929699"), fontSize: 26.rpx, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), ), Text( time, style: TextStyle(fontSize: 26.rpx, color: Color(0xFFFFFFFF)), ), ].divide(SizedBox(width: 33.rpx)), ), Row( mainAxisSize: MainAxisSize.max, children: [ Container( width: MediaQuery.sizeOf(context).width * 0.14, constraints: BoxConstraints(minWidth: 110.rpx), child: Text( "设备状态".tr, style: TextStyle( color: stringToColor("#929699"), fontSize: 26.rpx, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), ), Expanded( child: Row( mainAxisSize: MainAxisSize.max, children: [ if (data['status']['signal'] != null && data['status']['signal'] != -1 && data['status']['status'] != null && data['status']['status'] == 1) ClickableContainer( backgroundColor: Colors.transparent, highlightColor: Colors.grey, padding: EdgeInsets.zero, borderRadius: 0, onTap: () { showTipDialog( context, Container( child: RichText( text: TextSpan( text: "信号强度".tr, style: TextStyle( color: Colors.black, fontSize: AppConstants().title_text_fontSize, ), children: [ TextSpan( text: getBedSignal( data['status']['signal']), style: TextStyle( color: themeController.currentColor.sc2, fontSize: AppConstants().title_text_fontSize, ), ), ], ), ), ), backgroundColor: Color(0xFFFFFFFF), colors: [ Color(0XFF1592AA), Color(0xFF0C83A7), Color(0xFF006FA3) ], ); }, child: Container( width: 30.rpx, height: 24.rpx, child: Image.asset( 'assets/img/signal${_getSignalLevel(data['status']['signal'])}.png'), ), ), if (data['status']['inBed'] != null && data['status']['status'] != null && data['status']['status'] == 1) ClickableContainer( backgroundColor: Colors.transparent, highlightColor: Colors.grey, padding: EdgeInsetsDirectional.fromSTEB(0, 0.rpx, 0, 0), borderRadius: 0, onTap: () { showTipDialog( context, Container( child: RichText( text: TextSpan( text: "是否在床".tr, style: TextStyle( color: Colors.black, fontSize: AppConstants().title_text_fontSize, ), children: [ TextSpan( text: getBedStatus(data['status']['inBed']), style: TextStyle( color: themeController.currentColor.sc2, fontSize: AppConstants().title_text_fontSize, ), ), ], ), ), ), backgroundColor: Color(0xFFFFFFFF), colors: [ Color(0XFF1592AA), Color(0xFF0C83A7), Color(0xFF006FA3) ], ); }, child: SizedBox( width: 16.rpx, height: 36.rpx, child: SvgPicture.asset( data['status']['inBed'] == 0 ? 'assets/img/icon/not_bed.svg' : 'assets/img/icon/in_bed.svg', fit: BoxFit.fill, ), ), ), if (data['status']['failure'] != 0 && data['status']['status'] != null && data['status']['status'] == 1) ClickableContainer( backgroundColor: Colors.transparent, highlightColor: Colors.grey, padding: EdgeInsetsDirectional.fromSTEB(0, 0.rpx, 0, 0), borderRadius: 0, onTap: () { showTipDialog( context, Container( child: RichText( text: TextSpan( text: "设备故障".tr, style: TextStyle( color: Colors.black, fontSize: AppConstants().title_text_fontSize, ), children: const [], ), ), ), backgroundColor: Color(0xFFFFFFFF), colors: [ Color(0XFF1592AA), Color(0xFF0C83A7), Color(0xFF006FA3) ], ); }, child: SizedBox( width: 27.rpx, height: 27.rpx, child: SvgPicture.asset( 'assets/img/icon/device_issue.svg', fit: BoxFit.cover, ), ), ), if (data['status']['status'] != null && data['status']['status'] == 0) ClickableContainer( backgroundColor: Colors.transparent, highlightColor: Colors.grey, padding: EdgeInsetsDirectional.fromSTEB(0, 0.rpx, 0, 0), borderRadius: 0, onTap: () { showTipDialog( context, Container( child: RichText( text: TextSpan( text: "网络状态".tr, style: TextStyle( color: Colors.black, fontSize: AppConstants().title_text_fontSize, ), children: [ TextSpan( text: data['status']['status'] == 1 ? "在线".tr : "离线".tr, style: TextStyle( color: themeController.currentColor.sc2, fontSize: AppConstants().title_text_fontSize, ), ), ], ), ), ), backgroundColor: Color(0xFFFFFFFF), colors: [ Color(0XFF1592AA), Color(0xFF0C83A7), Color(0xFF006FA3) ], ); }, child: SizedBox( width: 27.rpx, height: 27.rpx, child: SvgPicture.asset( 'assets/img/icon/device_issue.svg', fit: BoxFit.cover, ), ), ), Obx(() { final upgradeInfo = mhtBlueToothController.localUpgradeMac[data['mac']]; if (upgradeInfo == null) { return Container(); } final int progress = upgradeInfo?['progress'] ?? -1; final String status = upgradeInfo?['status'] ?? ''; // 只有在有升级信息时才显示 if (mhtBlueToothController.localUpgradeMac .containsKey(data['mac'])) { return ClickableContainer( backgroundColor: Colors.transparent, highlightColor: Colors.grey, padding: EdgeInsetsDirectional.fromSTEB(0, 0.rpx, 0, 0), borderRadius: 0, onTap: () { if (progress == -1) { _showUpgradeDialog(context, data['mac']); } else { _showCancelDialog(context, data['mac']); } }, child: Row( children: [ // 升级图标 SizedBox( width: 34.rpx, height: 24.rpx, child: SvgPicture.asset( 'assets/img/icon/upgrade.svg', fit: BoxFit.cover, color: _getUpgradeIconColor(status), ), ), SizedBox(width: 10.rpx), // 进度数字和状态指示 Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ // 进度百分比 if (progress >= 0 && progress <= 100) Text( "$progress%", style: TextStyle( color: _getProgressTextColor(progress), fontSize: 26.rpx, fontWeight: FontWeight.bold, ), ), ], ), ], ), ); } return Container(); }), ].divide(SizedBox(width: 50.rpx)), // 减少图标间距 ), ), ].divide(SizedBox(width: 34.rpx)), ), ].divide(SizedBox(height: 20.rpx)), ), ); } // 获取状态文本 String _getStatusText(String status) { switch (status) { case 'waiting': return '等待'.tr; case 'downloading': return '下载'.tr; case 'upgrading': return '升级'.tr; case 'completed': return '完成'.tr; case 'failed': return '失败'.tr; case 'cancelled': return '取消'.tr; default: return status; } } // 获取状态文字颜色 Color _getStatusTextColor(String status) { switch (status) { case 'completed': return Colors.green; case 'failed': return Colors.red; case 'cancelled': return Colors.orange; default: return Colors.blue; } } // 获取进度文字颜色 Color _getProgressTextColor(int progress) { if (progress == 100) return Colors.green; if (progress < 0) return Colors.red; return Colors.white; } // 获取升级图标颜色 Color _getUpgradeIconColor(String status) { switch (status) { case 'completed': return Colors.green; case 'failed': return Colors.red; case 'cancelled': return Colors.orange; default: return themeController.currentColor.sc3; } } // 显示升级对话框 void _showUpgradeDialog(BuildContext context, String mac) { MHTBlueToothController mhtBlueToothController = Get.find(); showConfirmUpDialog( context, Column( children: [ SizedBox( width: 94.rpx, height: 70.rpx, child: SvgPicture.asset( 'assets/img/icon/upgrade.svg', fit: BoxFit.cover, ), ), Text( "设备升级".tr, style: TextStyle( color: stringToColor("#333333"), fontSize: AppConstants().title_text_fontSize, ), ), ].divide(SizedBox(height: 37.rpx)), ), "", onConfirm: () async { final deviceData = mhtBlueToothController.localUpgradeMac[mac]!; final firmwareUrl = mhtBlueToothController.currentUpgradeUrl; if (firmwareUrl == null) { TopSlideNotification.show(context, text: "未获取到固件下载地址".tr); return; } try { final scanResult = deviceData['device'].scanResult; final thapp = THapp(device: scanResult.device); if (!thapp.isConnected) { await thapp.device.connect(); } await MultiDeviceFirmwareUpdater().startUpgrade( thapp: thapp, mac: mac, firmwareUrl: firmwareUrl, ); } catch (e) { TopSlideNotification.show(context, text: "升级失败: ${e.toString()}".tr, textColor: Colors.red); } }, onCancel: () {}, backgroundColor: Colors.white, confirmText: "立即升级".tr, cancelText: "下次再说".tr); } // 显示取消对话框 void _showCancelDialog(BuildContext context, String mac) { showConfirmCancelDialog( context, Column( children: [ Text( "确认取消升级".tr, style: TextStyle( color: stringToColor("#333333"), fontSize: AppConstants().title_text_fontSize, ), ), Text( "确定要取消设备 $mac 的升级吗?".tr, style: TextStyle( color: stringToColor("#333333"), fontSize: AppConstants().normal_text_fontSize, ), ), ], ), "", onConfirm: () { MultiDeviceFirmwareUpdater().cancelUpgrade(mac); }, onCancel: () {}, backgroundColor: Colors.white, confirmText: "确认取消".tr, cancelText: "继续升级".tr, confirmButtonColor: Colors.red); } } getBedSignal(signal) { if (signal <= 1) { return "较弱".tr; } if (signal > 1 && signal <= 2) { return "弱".tr; } if (signal > 2 && signal <= 3) { return "一般".tr; } if (signal > 3) { return "强".tr; } return "未知数据".tr; } String getBedStatus(int status) { if (status == 0) { return "离床".tr; } else if (status == 1) { return "在床".tr; } return ""; } int _getSignalLevel(int signal) { if (signal <= 25) { return 1; } else if (signal <= 50) { return 2; } else if (signal <= 75) { return 3; } else { return 4; } }