1172 lines
58 KiB
Dart
1172 lines
58 KiB
Dart
import 'dart:async';
|
||
import 'dart:io';
|
||
|
||
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/BluetoothHelper.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/NewTopSlideNotification.dart';
|
||
import 'package:vbvs_app/component/tool/TopSlideNotification.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/enum/APPDeviceUpgrade.dart';
|
||
import 'package:vbvs_app/pages/common/selectDialog.dart';
|
||
import 'package:vbvs_app/pages/mh_page/device/component/UpgradeDevice.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';
|
||
import 'package:vbvs_app/pages/mh_page/device/upgrade/BatchUpgradeManager.dart';
|
||
import 'package:vbvs_app/pages/mh_page/device/upgrade/device_upgrade_controller.dart';
|
||
import 'package:vbvs_app/pages/mh_page/device/upgrade/tool/device_upgrade_tool.dart';
|
||
import 'package:flutter/services.dart';
|
||
|
||
class DeviceUpgrade extends StatefulWidget {
|
||
DeviceUpgrade({super.key});
|
||
|
||
@override
|
||
State<DeviceUpgrade> createState() => _DeviceUpgradeState();
|
||
}
|
||
|
||
class _DeviceUpgradeState extends State<DeviceUpgrade> {
|
||
MHTBlueToothController mhtBlueToothController = Get.find();
|
||
GlobalController globalController = Get.find();
|
||
UserInfoController userInfoController = Get.find();
|
||
ThemeController themeController = Get.find();
|
||
late FlutterBluePlus flutterBlue;
|
||
List<ScanResult> scanResults = [];
|
||
bool isScanning = false;
|
||
Timer? _timer;
|
||
bool _isDialogShowing = false;
|
||
|
||
var currentConnectedDeviceProp;
|
||
var connectDeviceCurrent = null;
|
||
List bleDevice = [];
|
||
String currentMsg = "寻找设备中...";
|
||
Timer? connectTimer;
|
||
bool isFind = false;
|
||
StreamSubscription<List<ScanResult>>? _scanSubscription;
|
||
var formFieldController = FormFieldController<String>(null);
|
||
|
||
MHTDeviceUpgradeController mhtDeviceUpgradeController = Get.find();
|
||
final BatchUpgradeManager _batchManager = BatchUpgradeManager();
|
||
bool _isPopupOpen = false;
|
||
|
||
// 新增状态:控制展开/收起
|
||
bool _isExpanded = false;
|
||
bool _showFilterPanel = false;
|
||
|
||
// 开始批量升级
|
||
Future<void> _startBatchUpgrade() async {
|
||
final selectedDevices = mhtDeviceUpgradeController.model.blueRawData!
|
||
.where((device) => device.selected == true)
|
||
.toList();
|
||
|
||
if (selectedDevices.isEmpty) {
|
||
TopSlideNotification.show(context,
|
||
text: "请选择要升级的设备".tr, textColor: Colors.red);
|
||
return;
|
||
}
|
||
|
||
try {
|
||
await _batchManager.startBatchUpgrade(selectedDevices);
|
||
} catch (e) {
|
||
TopSlideNotification.show(context,
|
||
text: "批量升级启动失败: $e".tr, textColor: Colors.red);
|
||
}
|
||
}
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
mhtBlueToothController.allSelect.value = false;
|
||
flutterBlue = FlutterBluePlus();
|
||
checkBluetoothPermission();
|
||
mhtBlueToothController.search.value = "";
|
||
mhtBlueToothController.currentDeviceMac?.value = "";
|
||
BluetoothHelper.listenBluetoothState((isOn) {
|
||
mhtBlueToothController.model.bluetooth = isOn;
|
||
mhtBlueToothController.updateAll();
|
||
if (!isOn && !_isDialogShowing) {
|
||
_isDialogShowing = true;
|
||
mhtDeviceUpgradeController.model.blueRawData = [];
|
||
mhtBlueToothController.model.deviceDataStatus = [];
|
||
mhtBlueToothController.updateAll();
|
||
showBluetoothNotEnabledDialog(context).then((_) {
|
||
_isDialogShowing = false;
|
||
});
|
||
}
|
||
});
|
||
initFirmwareVersions(formFieldController);
|
||
initDeviceUpgradeType();
|
||
}
|
||
|
||
//扫描设备
|
||
Future<void> checkBluetoothPermission() async {
|
||
PermissionStatus bluetoothStatus = await Permission.bluetooth.status;
|
||
PermissionStatus locationStatus = await Permission.location.status;
|
||
|
||
if (bluetoothStatus.isGranted && locationStatus.isGranted) {
|
||
_startScanning();
|
||
_startPeriodicScan();
|
||
} else {
|
||
_requestBluetoothPermission();
|
||
}
|
||
}
|
||
|
||
Future<void> _requestBluetoothPermission() async {
|
||
Map<Permission, PermissionStatus> statuses = {};
|
||
bool dialogShown = false; // 标记是否弹过权限提示弹窗
|
||
if (Platform.isIOS) {
|
||
PermissionStatus isBleGranted = await Permission.bluetooth.request();
|
||
print('checkBlePermissions-ios, isBleGranted=$isBleGranted');
|
||
if (isBleGranted.isGranted) {
|
||
// startBluetoothScanning();
|
||
_startScanning();
|
||
_startPeriodicScan();
|
||
} else {
|
||
try {
|
||
_startScanning();
|
||
_startPeriodicScan();
|
||
} 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();
|
||
_startPeriodicScan();
|
||
} 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);
|
||
}
|
||
|
||
void _startScanning() async {
|
||
try {
|
||
if (!mounted || isScanning || !mhtBlueToothController.shouldScan.value)
|
||
return;
|
||
|
||
_scanSubscription?.cancel();
|
||
mhtBlueToothController.updateAll();
|
||
|
||
if (!mhtBlueToothController.model.bluetooth!) 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.map((r) {
|
||
Map<String, dynamic> 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<int, List<int>> m_d = r.advertisementData.manufacturerData;
|
||
String? deviceId;
|
||
|
||
m_d.forEach((key, value) {
|
||
if (value == null || value.isEmpty) return;
|
||
|
||
if (key == 65517) {
|
||
List<int> a = [0, 0, ...value];
|
||
advertisDataFormatter(a, d); // 你原来的处理
|
||
if (d['adData']?['deviceId'] != null)
|
||
deviceId = d['adData']['deviceId'];
|
||
} else if (key == 11125 && value.length == 8) {
|
||
deviceId = ab2str(value.sublist(2, 8)).toUpperCase();
|
||
}
|
||
});
|
||
|
||
d['id'] = deviceId ?? r.device.remoteId.str; // fallback UUID
|
||
return BlueToothDataModel.fromScanResult(
|
||
r,
|
||
1,
|
||
bind: false,
|
||
name: d['name'],
|
||
mac: d['id'],
|
||
version: d['adData']?['version'],
|
||
rssi: d['rssi'],
|
||
selected: mhtBlueToothController.allSelect.value,
|
||
);
|
||
}).where((d) {
|
||
// 信号强度过滤(用 d.scanResult.rssi 或 d.rssi 都可以)
|
||
if (d.scanResult.rssi <= signalThreshold) return false;
|
||
|
||
// 搜索关键字过滤
|
||
if (searchKey.isNotEmpty) {
|
||
String name = d.name.toLowerCase();
|
||
String mac = d.mac.toLowerCase();
|
||
if (!name.contains(searchKey) && !mac.contains(searchKey))
|
||
return false;
|
||
}
|
||
|
||
// 名称过滤规则,必须包含 deviceType['reg'] 列表中的某个字符串
|
||
List<String> regList = [];
|
||
if (regList.isNotEmpty) {
|
||
String lowerName = d.name.toLowerCase();
|
||
bool match =
|
||
regList.any((reg) => lowerName.contains(reg.toLowerCase()));
|
||
if (!match) return false;
|
||
}
|
||
|
||
// 修正:不要使用外部的 r,改从 d.scanResult 读取广告数据
|
||
final adv = d.scanResult.advertisementData;
|
||
final isTarget = (d.rssi ?? d.scanResult.rssi) > signalThreshold &&
|
||
(adv.localName == "AITH-V2" || adv.localName == "SLAVE") &&
|
||
adv.manufacturerData.containsKey(0xFFED);
|
||
|
||
if (!isTarget) return false;
|
||
|
||
return true;
|
||
}).toList();
|
||
|
||
final currentDevices =
|
||
mhtDeviceUpgradeController.model.blueRawData ?? [];
|
||
final newDevices = <BlueToothDataModel>[];
|
||
|
||
// for (var newDevice in filteredResults) {
|
||
// final existingIndex =
|
||
// currentDevices.indexWhere((d) => d.mac == newDevice.mac);
|
||
// if (existingIndex >= 0) {
|
||
// currentDevices[existingIndex] = newDevice;
|
||
// } else {
|
||
// newDevices.add(newDevice);
|
||
// }
|
||
// }
|
||
|
||
for (var newDevice in filteredResults) {
|
||
final existingIndex =
|
||
currentDevices.indexWhere((d) => d.mac == newDevice.mac);
|
||
|
||
if (existingIndex >= 0) {
|
||
// 如果设备已存在,更新信号强度rssi
|
||
currentDevices[existingIndex].rssi = newDevice.rssi;
|
||
} else {
|
||
// 如果设备不存在,添加到新设备列表
|
||
newDevice.upgradeStatus = APPDeviceUpgrade.nothing.value;
|
||
newDevices.add(newDevice);
|
||
}
|
||
}
|
||
|
||
setState(() {
|
||
mhtDeviceUpgradeController.model.blueRawData = [
|
||
...currentDevices,
|
||
...newDevices
|
||
];
|
||
mhtBlueToothController.updateAll();
|
||
});
|
||
});
|
||
|
||
await Future.delayed(Duration(seconds: 10));
|
||
await FlutterBluePlus.stopScan();
|
||
|
||
if (mounted) {
|
||
setState(() {
|
||
isScanning = false;
|
||
});
|
||
}
|
||
}
|
||
} catch (e) {
|
||
ef.log("$e");
|
||
} finally {
|
||
setState(() {
|
||
isScanning = false;
|
||
});
|
||
}
|
||
}
|
||
|
||
void _startPeriodicScan() {
|
||
_timer = Timer.periodic(Duration(seconds: 3), (timer) {
|
||
if (mhtBlueToothController.shouldScan.value && !isScanning) {
|
||
removeOldDevices(); // 先清理老旧设备
|
||
_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();
|
||
mhtDeviceUpgradeController.model.blueRawData = [];
|
||
mhtBlueToothController.model.deviceDataStatus = [];
|
||
BluetoothHelper.cancelListener();
|
||
super.dispose();
|
||
}
|
||
|
||
bool isTargetDevice(String? name, List<String> keywords) {
|
||
if (name == null) return false;
|
||
return keywords.any((k) => name.contains(k));
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return LayoutBuilder(
|
||
builder: (context, boxConstraints) => GestureDetector(
|
||
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(
|
||
systemOverlayStyle: SystemUiOverlayStyle(
|
||
statusBarColor: Colors.transparent, // 状态栏背景色
|
||
statusBarIconBrightness: Brightness.light, // 图标颜色(Android)
|
||
statusBarBrightness: Brightness.light, // 图标颜色(iOS)
|
||
),
|
||
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: TextStyle(
|
||
fontFamily: 'Readex Pro',
|
||
color: themeController.currentColor.sc3,
|
||
letterSpacing: 0,
|
||
fontSize: 30.rpx,
|
||
),
|
||
),
|
||
Positioned(
|
||
left: 0,
|
||
child: returnIconButtom,
|
||
),
|
||
Positioned(
|
||
right: 0.rpx,
|
||
child: ClickableContainer(
|
||
backgroundColor: Colors.transparent,
|
||
highlightColor: Colors.transparent,
|
||
padding:
|
||
EdgeInsets.fromLTRB(30.rpx, 30.rpx, 50.rpx, 30.rpx),
|
||
onTap: () {
|
||
// 切换筛选条件显示状态
|
||
setState(() {
|
||
_showFilterPanel = !_showFilterPanel;
|
||
});
|
||
},
|
||
child: Stack(
|
||
children: [
|
||
SizedBox(
|
||
width: 34.rpx,
|
||
height: 24.rpx,
|
||
child: SvgPicture.asset(
|
||
'assets/img/icon/upgrade.svg',
|
||
fit: BoxFit.cover,
|
||
color: Colors.white,
|
||
),
|
||
),
|
||
// 使用 Obx 监听升级设备数量
|
||
Obx(() {
|
||
final upgradingCount = mhtDeviceUpgradeController
|
||
.model.upgradingDevices?.length ??
|
||
0;
|
||
return upgradingCount > 0
|
||
? Positioned(
|
||
right: 0,
|
||
top: -2.rpx,
|
||
child: Container(
|
||
padding: EdgeInsets.all(4.rpx),
|
||
decoration: BoxDecoration(
|
||
color: Colors.red,
|
||
shape: BoxShape.circle,
|
||
),
|
||
child: Text(
|
||
upgradingCount.toString(),
|
||
style: TextStyle(
|
||
color: Colors.white,
|
||
fontSize: 12.rpx,
|
||
fontWeight: FontWeight.bold,
|
||
),
|
||
),
|
||
),
|
||
)
|
||
: SizedBox.shrink();
|
||
}),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
actions: [],
|
||
centerTitle: false,
|
||
),
|
||
body: Stack(
|
||
children: [
|
||
// 主内容区域
|
||
SafeArea(
|
||
top: true,
|
||
child: Padding(
|
||
padding:
|
||
EdgeInsetsDirectional.fromSTEB(30.rpx, 0, 30.rpx, 0),
|
||
child: Column(
|
||
mainAxisSize: MainAxisSize.max,
|
||
children: [
|
||
Expanded(
|
||
child: Column(
|
||
children: [
|
||
Container(
|
||
width: double.infinity,
|
||
decoration: BoxDecoration(
|
||
gradient: LinearGradient(
|
||
colors: [
|
||
Colors.transparent,
|
||
Colors.transparent,
|
||
],
|
||
begin: Alignment.topCenter,
|
||
end: Alignment.bottomCenter,
|
||
),
|
||
borderRadius: BorderRadius.circular(20.rpx),
|
||
),
|
||
child: Padding(
|
||
padding: EdgeInsetsDirectional.fromSTEB(
|
||
21.rpx, 5.rpx, 21.rpx, 0.rpx),
|
||
child: Row(
|
||
mainAxisSize: MainAxisSize.max,
|
||
children: [
|
||
Text(
|
||
'最小信号强度'.tr,
|
||
style: TextStyle(
|
||
fontFamily: 'Inter',
|
||
color: stringToColor("#FFFFFF"),
|
||
fontSize: 26.rpx,
|
||
letterSpacing: 0.0,
|
||
),
|
||
),
|
||
Expanded(
|
||
child: Obx(() {
|
||
return Slider(
|
||
activeColor: Color(0xFF1FCC9B),
|
||
inactiveColor: Colors.white,
|
||
thumbColor: Colors.white,
|
||
min: -100,
|
||
max: 50,
|
||
value: mhtBlueToothController
|
||
.model.singal!,
|
||
onChanged: (newValue) {
|
||
newValue = double.parse(
|
||
newValue.toStringAsFixed(0));
|
||
mhtBlueToothController
|
||
.model.singal = newValue;
|
||
mhtDeviceUpgradeController
|
||
.model.blueRawData =
|
||
mhtDeviceUpgradeController
|
||
.model.blueRawData!
|
||
?.where((device) =>
|
||
device.rssi != null &&
|
||
device.rssi! >=
|
||
mhtBlueToothController
|
||
.model
|
||
.singal!)
|
||
.toList();
|
||
mhtBlueToothController
|
||
.updateAll();
|
||
},
|
||
);
|
||
}),
|
||
),
|
||
Obx(() {
|
||
return Text(
|
||
'${mhtBlueToothController.model.singal!.toInt()}',
|
||
style: TextStyle(
|
||
fontFamily: 'Inter',
|
||
color: stringToColor("#FFFFFF"),
|
||
fontSize: 26.rpx,
|
||
letterSpacing: 0.0,
|
||
),
|
||
);
|
||
}),
|
||
].divide(SizedBox(width: 30.rpx)),
|
||
),
|
||
),
|
||
),
|
||
Container(
|
||
width: double.infinity,
|
||
child: Padding(
|
||
padding: EdgeInsetsDirectional.fromSTEB(
|
||
21.rpx, 0.rpx, 0.rpx, 5.rpx),
|
||
child: Row(
|
||
mainAxisAlignment:
|
||
MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
Row(
|
||
mainAxisSize: MainAxisSize.max,
|
||
children: [
|
||
Theme(
|
||
data: Theme.of(context).copyWith(
|
||
splashColor:
|
||
stringToColor("#011D33"),
|
||
highlightColor:
|
||
stringToColor("#011D33"),
|
||
),
|
||
child: ScrollbarTheme(
|
||
data: ScrollbarThemeData(
|
||
thumbColor:
|
||
MaterialStateProperty.all(
|
||
Colors.transparent),
|
||
trackColor:
|
||
MaterialStateProperty.all(
|
||
Colors.transparent),
|
||
trackBorderColor:
|
||
MaterialStateProperty.all(
|
||
Colors.transparent),
|
||
),
|
||
child: ValueListenableBuilder(
|
||
valueListenable:
|
||
formFieldController,
|
||
builder: (c, a, s) =>
|
||
FlutterFlowDropDown<String>(
|
||
controller:
|
||
formFieldController,
|
||
options:
|
||
mhtDeviceUpgradeController
|
||
.firmwareList!
|
||
.map<String>((d) =>
|
||
"${d.version}")
|
||
.toList(),
|
||
optionLabels:
|
||
mhtDeviceUpgradeController
|
||
.firmwareList!
|
||
.map<String>((d) =>
|
||
"${d.version}")
|
||
.toList(),
|
||
onChanged: (val) {
|
||
var selectedVersion =
|
||
mhtDeviceUpgradeController
|
||
.firmwareList!
|
||
.firstWhere(
|
||
(d) => d.version == val,
|
||
);
|
||
if (selectedVersion !=
|
||
null) {
|
||
mhtDeviceUpgradeController
|
||
.selectVerison =
|
||
selectedVersion;
|
||
final MHTBlueToothController
|
||
_btController =
|
||
Get.find();
|
||
_btController
|
||
.currentUpgradeVersion =
|
||
mhtDeviceUpgradeController
|
||
.selectVerison!
|
||
.version!;
|
||
_btController
|
||
.currentUpgradeName =
|
||
mhtDeviceUpgradeController
|
||
.selectVerison!
|
||
.fileName!;
|
||
_btController
|
||
.currentUpgradeUrl =
|
||
mhtDeviceUpgradeController
|
||
.selectVerison!
|
||
.downloadUrl!;
|
||
}
|
||
},
|
||
width: 300.rpx,
|
||
height: 81.rpx,
|
||
maxHeight: 300.rpx,
|
||
textStyle: TextStyle(
|
||
fontSize: 28.rpx,
|
||
overflow:
|
||
TextOverflow.ellipsis,
|
||
color: Colors.black,
|
||
),
|
||
hintText: '选择固件版本'.tr,
|
||
icon: Icon(
|
||
Icons
|
||
.keyboard_arrow_down_rounded,
|
||
color: Color(0xFF929699),
|
||
size: 30.rpx,
|
||
),
|
||
fillColor: Colors.white,
|
||
elevation: 2,
|
||
borderColor:
|
||
Colors.transparent,
|
||
borderWidth: 2,
|
||
borderRadius: AppConstants()
|
||
.normal_container_radius,
|
||
margin: EdgeInsetsDirectional
|
||
.fromSTEB(32.rpx, 8.rpx,
|
||
22.rpx, 8.rpx),
|
||
hidesUnderline: true,
|
||
isOverButton: false,
|
||
isSearchable: false,
|
||
isMultiSelect: false,
|
||
),
|
||
),
|
||
),
|
||
),
|
||
ClickableContainer(
|
||
backgroundColor:
|
||
Colors.transparent,
|
||
highlightColor:
|
||
Colors.transparent,
|
||
padding: EdgeInsets.all(0),
|
||
onTap: () {
|
||
mhtBlueToothController
|
||
.autoUpgrade.value =
|
||
!mhtBlueToothController
|
||
.autoUpgrade.value;
|
||
mhtBlueToothController
|
||
.updateAll();
|
||
},
|
||
child: Obx(() {
|
||
double h = 33.rpx;
|
||
bool check =
|
||
mhtBlueToothController
|
||
.autoUpgrade.value;
|
||
return Container(
|
||
height: 33.rpx,
|
||
child: Row(
|
||
children: [
|
||
AspectRatio(
|
||
aspectRatio: 1,
|
||
child: Center(
|
||
child: Container(
|
||
height: h,
|
||
width: h,
|
||
decoration:
|
||
BoxDecoration(
|
||
borderRadius:
|
||
BorderRadius
|
||
.circular(
|
||
h / 2),
|
||
border:
|
||
Border.all(
|
||
width: check
|
||
? 1
|
||
: 0.5,
|
||
color: Color(
|
||
0xFFC8CBD2),
|
||
),
|
||
),
|
||
child: check
|
||
? Center(
|
||
child:
|
||
ClipOval(
|
||
child:
|
||
Container(
|
||
width:
|
||
h * 0.6,
|
||
height:
|
||
h * 0.6,
|
||
color:
|
||
const Color(0xFF6BFDAC),
|
||
),
|
||
),
|
||
)
|
||
: null,
|
||
),
|
||
),
|
||
),
|
||
Text(
|
||
"自动升级".tr,
|
||
style: TextStyle(
|
||
fontFamily: 'Inter',
|
||
color:
|
||
stringToColor(
|
||
"#FFFFFF"),
|
||
fontSize: 26.rpx,
|
||
letterSpacing: 0.0,
|
||
),
|
||
),
|
||
].divide(SizedBox(
|
||
width: 10.rpx)),
|
||
));
|
||
})),
|
||
].divide(SizedBox(width: 30.rpx)),
|
||
),
|
||
ClickableContainer(
|
||
backgroundColor: Colors.transparent,
|
||
highlightColor: Colors.transparent,
|
||
padding: EdgeInsets.fromLTRB(
|
||
0.rpx, 0.rpx, 20.rpx, 0.rpx),
|
||
onTap: () {
|
||
//刷新信号强度以及重置设备录入强度排行
|
||
},
|
||
child: SizedBox(
|
||
width: 28.rpx,
|
||
height: 28.rpx,
|
||
child: SvgPicture.asset(
|
||
'assets/img/icon/refresh.svg',
|
||
fit: BoxFit.cover,
|
||
color: Colors.white,
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
Padding(
|
||
padding: EdgeInsetsDirectional.fromSTEB(
|
||
0, 0.rpx, 0, 0.rpx),
|
||
child: Container(
|
||
width: double.infinity,
|
||
decoration: BoxDecoration(),
|
||
child: Padding(
|
||
padding: EdgeInsetsDirectional.fromSTEB(
|
||
19.rpx, 0, 0, 0),
|
||
child: Obx(() {
|
||
return Row(
|
||
mainAxisAlignment:
|
||
MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
Text(
|
||
'匹配出的外围设备'.tr +
|
||
"(${getRemainingDeviceCount()})",
|
||
style: TextStyle(
|
||
fontFamily: 'Inter',
|
||
fontSize: 30.rpx,
|
||
letterSpacing: 0.0,
|
||
color: themeController
|
||
.currentColor.sc3,
|
||
),
|
||
),
|
||
Row(
|
||
children: [
|
||
ClickableContainer(
|
||
backgroundColor:
|
||
Colors.transparent,
|
||
highlightColor:
|
||
Colors.transparent,
|
||
padding: EdgeInsets.all(0),
|
||
onTap: () {
|
||
mhtBlueToothController
|
||
.allSelect.value =
|
||
!mhtBlueToothController
|
||
.allSelect.value;
|
||
|
||
if (mhtBlueToothController
|
||
.allSelect.value) {
|
||
for (var device
|
||
in mhtDeviceUpgradeController
|
||
.model
|
||
.blueRawData!) {
|
||
device.selected = true;
|
||
}
|
||
} else {
|
||
for (var device
|
||
in mhtDeviceUpgradeController
|
||
.model
|
||
.blueRawData!) {
|
||
device.selected = false;
|
||
}
|
||
}
|
||
mhtBlueToothController
|
||
.updateAll();
|
||
},
|
||
child: Obx(() {
|
||
double h = 33.rpx;
|
||
bool check =
|
||
mhtBlueToothController
|
||
.allSelect.value;
|
||
return Container(
|
||
height: 33.rpx,
|
||
child: Row(
|
||
children: [
|
||
Text(
|
||
mhtBlueToothController
|
||
.allSelect
|
||
.value ==
|
||
false
|
||
? "全选".tr
|
||
: "取消全选".tr,
|
||
style: TextStyle(
|
||
fontFamily:
|
||
'Inter',
|
||
color:
|
||
stringToColor(
|
||
"#85F5FF"),
|
||
fontSize: 26.rpx,
|
||
letterSpacing:
|
||
0.0,
|
||
),
|
||
),
|
||
].divide(SizedBox(
|
||
width: 10.rpx)),
|
||
));
|
||
}),
|
||
),
|
||
],
|
||
)
|
||
],
|
||
);
|
||
}),
|
||
),
|
||
),
|
||
),
|
||
//设备列表
|
||
// Obx(() {
|
||
// if (mhtDeviceUpgradeController
|
||
// .model.blueRawData!.isNotEmpty) {
|
||
// final allDevices = mhtDeviceUpgradeController
|
||
// .model.blueRawData!;
|
||
// final upgradingDevices =
|
||
// mhtDeviceUpgradeController
|
||
// .model.upgradingDevices ??
|
||
// [];
|
||
// final upgradingDevicesMacs = upgradingDevices
|
||
// .map((device) => device.mac)
|
||
// .toList();
|
||
|
||
// // 当前选择的升级版本
|
||
// final selectedVersion =
|
||
// mhtDeviceUpgradeController.selectVerison;
|
||
|
||
// // 过滤条件:
|
||
// // 1. 不在正在升级列表中
|
||
// // 2. 且版本号不等于当前选择的版本(同版本不需要升级)
|
||
// var filteredDevices =
|
||
// allDevices.where((device) {
|
||
// // bool notUpgrading = !upgradingDevicesMacs
|
||
// // .contains(device.mac);
|
||
// bool notUpgrading = true;
|
||
// //todo wyf 恢复
|
||
// // bool needUpgrade =
|
||
// // device.version.toString() !=
|
||
// // selectedVersion!.version;
|
||
// bool needUpgrade = true;
|
||
// return notUpgrading && needUpgrade;
|
||
// }).toList();
|
||
|
||
// // 若需要过滤设备类型,可继续使用你的方法
|
||
// var deviceList =
|
||
// filterDeviceType(filteredDevices);
|
||
|
||
// // 按信号强度排序
|
||
// final sortedList = deviceList
|
||
// ..sort(
|
||
// (a, b) => b.rssi!.compareTo(a.rssi!));
|
||
|
||
// return Expanded(
|
||
// child: Container(
|
||
// width: double.infinity,
|
||
// child: SingleChildScrollView(
|
||
// child: Column(
|
||
// mainAxisSize: MainAxisSize.max,
|
||
// children: [
|
||
// ...sortedList
|
||
// .map((device) {
|
||
// return UpgradeDevice(
|
||
// bleDevice: device,
|
||
// deviceType: 1,
|
||
// );
|
||
// })
|
||
// .toList()
|
||
// .divide(
|
||
// SizedBox(height: 30.rpx))
|
||
// .addToEnd(
|
||
// SizedBox(height: 30.rpx)),
|
||
// ],
|
||
// ),
|
||
// ),
|
||
// ),
|
||
// );
|
||
// }
|
||
// return Container();
|
||
// })
|
||
|
||
Obx(() {
|
||
final allDevices = mhtDeviceUpgradeController
|
||
.model.blueRawData ??
|
||
[];
|
||
final upgradingDevices =
|
||
mhtDeviceUpgradeController
|
||
.model.upgradingDevices ??
|
||
[];
|
||
|
||
// 合并列表显示
|
||
// List<BlueToothDataModel> displayDevices = [
|
||
// ...allDevices.map((d) {
|
||
// final upgrading =
|
||
// upgradingDevices.firstWhereOrNull(
|
||
// (u) => u.mac == d.mac);
|
||
// return upgrading ?? d;
|
||
// }),
|
||
// // 如果有升级设备不在 rawData 中,也加上
|
||
// ...upgradingDevices
|
||
// .where((u) => !allDevices
|
||
// .any((d) => d.mac == u.mac))
|
||
// .toList(),
|
||
// ];
|
||
List<BlueToothDataModel> displayDevices = [
|
||
// 用 upgrading 覆盖 raw 中的相同 mac
|
||
...allDevices.map((d) {
|
||
final u = upgradingDevices.firstWhereOrNull(
|
||
(x) => x.mac == d.mac);
|
||
return u ?? d;
|
||
}),
|
||
// 加入那些只在 upgradingDevices 中但不在 raw 的
|
||
...upgradingDevices.where((u) =>
|
||
!allDevices.any((d) => d.mac == u.mac)),
|
||
];
|
||
|
||
// 可以按信号强度排序
|
||
displayDevices.sort((a, b) =>
|
||
(b.rssi ?? 0).compareTo(a.rssi ?? 0));
|
||
|
||
return Expanded(
|
||
child: Container(
|
||
width: double.infinity,
|
||
child: SingleChildScrollView(
|
||
child: Column(
|
||
mainAxisSize: MainAxisSize.max,
|
||
children: [
|
||
...displayDevices
|
||
.map((device) {
|
||
return UpgradeDevice(
|
||
bleDevice: device,
|
||
deviceType: 1,
|
||
);
|
||
})
|
||
.toList()
|
||
.divide(SizedBox(height: 30.rpx))
|
||
.addToEnd(
|
||
SizedBox(height: 30.rpx)),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}),
|
||
].divide(SizedBox(height: 30.rpx)),
|
||
),
|
||
),
|
||
Padding(
|
||
padding: EdgeInsetsDirectional.fromSTEB(
|
||
0, 0.rpx, 0, 30.rpx),
|
||
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: EdgeInsets.only(bottom: 0.rpx),
|
||
child: CustomCard(
|
||
borderRadius: 16.rpx,
|
||
gradientDirection: GradientDirection.vertical,
|
||
onTap: () {
|
||
String deviceCountFlag =
|
||
judgeUpgradeDeviceCount();
|
||
if (deviceCountFlag != null &&
|
||
deviceCountFlag != "") {
|
||
NewTopSlideNotification.show(
|
||
text: deviceCountFlag,
|
||
textColor:
|
||
themeController.currentColor.sc9);
|
||
return;
|
||
}
|
||
mhtDeviceUpgradeController.startUpgrade();
|
||
},
|
||
colors: const [
|
||
Color(0xFFFCFCFC),
|
||
Color(0xFFF8FAF9),
|
||
Color(0XFFECF6F3),
|
||
Color(0XFFD9F0E9),
|
||
Color(0xFFCEECE3)
|
||
],
|
||
child: Container(
|
||
width: double.infinity,
|
||
height: 90.rpx,
|
||
alignment: Alignment.center,
|
||
decoration: BoxDecoration(
|
||
borderRadius: BorderRadius.circular(6),
|
||
),
|
||
child: Text("升级".tr,
|
||
style: TextStyle(
|
||
color: const Color(0xFF003058),
|
||
fontSize: 26.rpx)),
|
||
),
|
||
)),
|
||
),
|
||
),
|
||
].divide(SizedBox(height: 30.rpx)),
|
||
),
|
||
),
|
||
),
|
||
|
||
// 筛选条件面板 - 从AppBar下方开始显示,遮住主内容
|
||
if (_showFilterPanel)
|
||
Positioned.fill(
|
||
top: 0.rpx, // 从AppBar下方开始
|
||
child: Container(
|
||
color: Colors.black.withOpacity(0.5), // 半透明遮罩
|
||
child: GestureDetector(
|
||
onTap: () {
|
||
// 点击遮罩区域关闭筛选面板
|
||
setState(() {
|
||
_showFilterPanel = false;
|
||
});
|
||
},
|
||
child: Container(
|
||
color: Colors.transparent,
|
||
child: Column(
|
||
children: [
|
||
// 筛选条件内容
|
||
Container(
|
||
width: double.infinity,
|
||
margin:
|
||
EdgeInsets.symmetric(horizontal: 30.rpx),
|
||
decoration: BoxDecoration(
|
||
color: stringToColor("#003058"),
|
||
borderRadius: BorderRadius.only(
|
||
bottomLeft: Radius.circular(12.rpx),
|
||
bottomRight: Radius.circular(12.rpx),
|
||
topLeft: Radius.circular(12.rpx),
|
||
topRight: Radius.circular(12.rpx),
|
||
),
|
||
boxShadow: [
|
||
BoxShadow(
|
||
color: Colors.black26,
|
||
blurRadius: 8.rpx,
|
||
offset: Offset(0, 4.rpx),
|
||
),
|
||
],
|
||
),
|
||
child: getQueryList(), // 使用原来的筛选条件内容
|
||
),
|
||
// 剩余空间可以点击关闭
|
||
Expanded(
|
||
child: GestureDetector(
|
||
onTap: () {
|
||
setState(() {
|
||
_showFilterPanel = false;
|
||
});
|
||
},
|
||
child: Container(
|
||
color: Colors.transparent,
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
}
|