From 1bb06cc107c4e1b42449b8c35133ce406558f714 Mon Sep 17 00:00:00 2001 From: liwei1dao Date: Mon, 23 Mar 2026 18:15:35 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E7=A7=BB=E5=8A=A8cmei?= =?UTF-8?q?=E7=A0=81=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/data/models/user_Info.dart | 4 + lib/modules/devices/views/devices_view.dart | 571 ++++++++++++++------ {ui => preview}/yidong.html | 0 3 files changed, 423 insertions(+), 152 deletions(-) rename {ui => preview}/yidong.html (100%) diff --git a/lib/data/models/user_Info.dart b/lib/data/models/user_Info.dart index 2467ba7bd..e6125b672 100644 --- a/lib/data/models/user_Info.dart +++ b/lib/data/models/user_Info.dart @@ -217,6 +217,7 @@ class UserDevice { String devicename; String devicemac; String license; + String cmei; UserDevice({ required this.id, required this.uid, @@ -225,6 +226,7 @@ class UserDevice { required this.devicename, required this.devicemac, required this.license, + this.cmei = '', }); factory UserDevice.fromJson(Map json) => UserDevice( @@ -235,6 +237,7 @@ class UserDevice { devicename: json['devicename'] as String, devicemac: json['devicemac'] as String, license: json['license'] as String, + cmei: json['cmei'] as String? ?? '', ); toJson() => { @@ -245,5 +248,6 @@ class UserDevice { 'devicename': devicename, 'devicemac': devicemac, 'license': license, + 'cmei': cmei, }; } diff --git a/lib/modules/devices/views/devices_view.dart b/lib/modules/devices/views/devices_view.dart index 7a69dd5b0..30b5570ee 100644 --- a/lib/modules/devices/views/devices_view.dart +++ b/lib/modules/devices/views/devices_view.dart @@ -1,12 +1,10 @@ -import 'dart:ui'; - import '../../../routes/app_routes.dart'; -import '/modules/devices/controllers/devices_controller.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import '/core/theme/app_colors.dart'; import '/data/models/user_Info.dart'; +import '/data/services/ble_manager.dart'; import '../controllers/devices_controller.dart'; // 设备类型枚举 @@ -20,7 +18,6 @@ enum DeviceType { const DeviceType(this.value); final int value; - // 使用getter获取多语言显示名称 String get displayName { switch (this) { case DeviceType.Unknown: @@ -61,112 +58,240 @@ enum DeviceType { class DevicesView extends GetView { const DevicesView({super.key}); + bool _isConnected(UserDevice device) { + try { + final bleManager = Get.find(); + return bleManager.currentDeviceAddress != null && + bleManager.currentDeviceAddress == device.devicemac; + } catch (_) { + return false; + } + } + @override Widget build(BuildContext context) { - final isDarkMode = Theme.of(context).brightness == Brightness.dark; + final isDark = Theme.of(context).brightness == Brightness.dark; + final bg = isDark ? const Color(0xFF0A0A0F) : const Color(0xFFF5F6FA); + final topBg = isDark ? const Color(0xFF0F1018) : const Color(0xFFF0F1F6); return Scaffold( - backgroundColor: isDarkMode ? AppColors.darkBackground : Colors.white, + backgroundColor: bg, body: Column( children: [ - // 顶部标题栏 - Container( - padding: EdgeInsets.only( - top: MediaQuery.of(context).padding.top + 10.h, - left: 20.w, - right: 20.w, - bottom: 20.h, - ), - decoration: BoxDecoration( - color: isDarkMode ? Colors.black : Colors.white, - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.05), - blurRadius: 10, - offset: const Offset(0, 2), - ), - ], - ), - child: Row( - children: [ - GestureDetector( - onTap: () => Get.back(), + // 顶部区域 + _buildTopArea(context, isDark, bg, topBg), + // 设备列表 + Expanded(child: _buildDevicesList(isDark, topBg)), + ], + ), + ); + } + + Widget _buildTopArea( + BuildContext context, bool isDark, Color bg, Color topBg) { + return Container( + padding: EdgeInsets.only( + top: MediaQuery.of(context).padding.top + 8.h, + left: 20.w, + right: 20.w, + bottom: 24.h, + ), + decoration: BoxDecoration(color: bg), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // 导航栏 + Row( + children: [ + GestureDetector( + onTap: () => Get.back(), + child: Container( + width: 40.w, + height: 40.w, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: isDark + ? Colors.white.withOpacity(0.06) + : Colors.black.withOpacity(0.04), + ), child: Icon( - Icons.arrow_back_ios, - color: isDarkMode ? Colors.white : Colors.black, - size: 24.sp, + Icons.arrow_back_ios_new, + size: 18.sp, + color: isDark ? Colors.white : const Color(0xFF222222), ), ), - SizedBox(width: 16.w), - Text( - "deviceManagement".tr, - style: TextStyle( - fontSize: 20.sp, - fontWeight: FontWeight.w600, - color: isDarkMode ? Colors.white : Colors.black, - ), + ), + SizedBox(width: 16.w), + Text( + "deviceManagement".tr, + style: TextStyle( + fontSize: 26.sp, + fontWeight: FontWeight.w800, + color: isDark ? Colors.white : const Color(0xFF111111), + letterSpacing: -0.5, ), - ], - ), - ), - // 设备列表内容 - Expanded( - child: _buildDevicesList(isDarkMode), + ), + ], ), + SizedBox(height: 20.h), + // 摘要卡片 + Obx(() { + final totalDevices = controller.devices.values + .fold(0, (sum, list) => sum + list.length); + final connectedCount = controller.devices.values + .expand((list) => list) + .where((d) => _isConnected(d)) + .length; + return Row( + children: [ + _buildSummaryCard( + number: totalDevices.toString(), + label: "boundDevices".tr, + isActive: true, + isDark: isDark, + ), + SizedBox(width: 12.w), + _buildSummaryCard( + number: connectedCount.toString(), + label: "currentConnection".tr, + isActive: false, + isDark: isDark, + ), + ], + ); + }), ], ), ); } - Widget _buildDevicesList(bool isDarkMode) { + Widget _buildSummaryCard({ + required String number, + required String label, + required bool isActive, + required bool isDark, + }) { + final activeGradient = isDark + ? [ + const Color(0xFF6366F1).withOpacity(0.15), + const Color(0xFF8B5CF6).withOpacity(0.1), + ] + : [const Color(0xFFEEF2FF), const Color(0xFFF5F3FF)]; + + return Expanded( + child: Container( + padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 14.h), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16.r), + gradient: isActive ? LinearGradient(colors: activeGradient) : null, + color: isActive + ? null + : (isDark + ? Colors.white.withOpacity(0.04) + : Colors.white), + border: Border.all( + color: isActive + ? (isDark + ? const Color(0xFF6366F1).withOpacity(0.2) + : const Color(0xFF6366F1).withOpacity(0.15)) + : (isDark + ? Colors.white.withOpacity(0.06) + : Colors.black.withOpacity(0.04)), + ), + boxShadow: isDark + ? null + : [ + BoxShadow( + color: Colors.black.withOpacity(0.04), + blurRadius: 4, + offset: const Offset(0, 1), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + number, + style: TextStyle( + fontSize: 24.sp, + fontWeight: FontWeight.w700, + color: isActive + ? (isDark + ? const Color(0xFFA5B4FC) + : const Color(0xFF6366F1)) + : (isDark ? Colors.white : const Color(0xFF222222)), + ), + ), + SizedBox(height: 2.h), + Text( + label, + style: TextStyle( + fontSize: 11.sp, + fontWeight: FontWeight.w500, + color: isActive + ? (isDark + ? const Color(0xFFA5B4FC).withOpacity(0.6) + : const Color(0xFF818CF8)) + : (isDark + ? Colors.white.withOpacity(0.35) + : const Color(0xFF999999)), + ), + ), + ], + ), + ), + ); + } + + Widget _buildDevicesList(bool isDark, Color topBg) { return Obx(() { - // 使用控制器的响应式数据,这会触发控制器的实例化 final groupedDevices = controller.devices; if (groupedDevices.isEmpty) { - return _buildEmptyState(isDarkMode); + return _buildEmptyState(isDark); } return ListView( - padding: EdgeInsets.all(20.w), + padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 20.h), children: [ - // 遍历每个设备类型 ...groupedDevices.entries.map((entry) { - final deviceTypeValue = entry.key; + final deviceType = DeviceType.fromValue(entry.key); final deviceList = entry.value; - final deviceType = DeviceType.fromValue(deviceTypeValue); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - // 设备类型标题 + // 分类标题 Padding( - padding: EdgeInsets.only(bottom: 12.h), + padding: EdgeInsets.only(left: 8.w, bottom: 12.h), child: Text( - deviceType.displayName, + deviceType.displayName.toUpperCase(), style: TextStyle( - fontSize: 16.sp, + fontSize: 12.sp, fontWeight: FontWeight.w600, - color: isDarkMode ? Colors.white : Colors.black87, + color: isDark + ? Colors.white.withOpacity(0.25) + : const Color(0xFFAAAAAA), + letterSpacing: 1.5, ), ), ), - // 该类型下的设备列表 ...deviceList.map((device) => _buildDeviceCard( device: device, deviceType: deviceType, - isDarkMode: isDarkMode, + isDark: isDark, )), - SizedBox(height: 20.h), + SizedBox(height: 16.h), ], ); - }).toList(), + }), ], ); }); } - Widget _buildEmptyState(bool isDarkMode) { + Widget _buildEmptyState(bool isDark) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, @@ -174,14 +299,14 @@ class DevicesView extends GetView { Icon( Icons.devices_other, size: 80.sp, - color: isDarkMode ? Colors.white54 : Colors.grey[400], + color: isDark ? Colors.white54 : Colors.grey[400], ), SizedBox(height: 20.h), Text( "noDevicesBound".tr, style: TextStyle( fontSize: 16.sp, - color: isDarkMode ? Colors.white70 : Colors.grey[600], + color: isDark ? Colors.white70 : Colors.grey[600], ), ), SizedBox(height: 12.h), @@ -189,7 +314,7 @@ class DevicesView extends GetView { "pleaseBindDeviceFirst".tr, style: TextStyle( fontSize: 14.sp, - color: isDarkMode ? Colors.white54 : Colors.grey[500], + color: isDark ? Colors.white54 : Colors.grey[500], ), ), ], @@ -200,121 +325,263 @@ class DevicesView extends GetView { Widget _buildDeviceCard({ required UserDevice device, required DeviceType deviceType, - required bool isDarkMode, + required bool isDark, }) { + final connected = _isConnected(device); + final connectedColor = const Color(0xFF22C55E); + return GestureDetector( onTap: () => Get.toNamed(Routes.eqSettings), child: Container( - margin: EdgeInsets.only(bottom: 12.h), - padding: EdgeInsets.all(16.w), + margin: EdgeInsets.only(bottom: 10.h), + padding: EdgeInsets.all(20.w), decoration: BoxDecoration( - color: isDarkMode ? AppColors.cardBackground : Colors.white, - borderRadius: BorderRadius.circular(12.r), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.05), - blurRadius: 8, - offset: const Offset(0, 2), - ), - ], + borderRadius: BorderRadius.circular(20.r), + color: isDark + ? (connected + ? connectedColor.withOpacity(0.04) + : Colors.white.withOpacity(0.03)) + : (connected + ? connectedColor.withOpacity(0.03) + : Colors.white), + border: Border.all( + color: connected + ? connectedColor.withOpacity(0.2) + : (isDark + ? Colors.white.withOpacity(0.05) + : Colors.black.withOpacity(0.04)), + ), + boxShadow: isDark + ? null + : [ + BoxShadow( + color: Colors.black.withOpacity(0.03), + blurRadius: 12, + offset: const Offset(0, 2), + ), + ], ), - child: Row( + child: Column( children: [ - // 设备图标 - Container( - width: 48.w, - height: 48.w, - decoration: BoxDecoration( - color: isDarkMode - ? Colors.white.withOpacity(0.1) - : Colors.grey[100], - borderRadius: BorderRadius.circular(12.r), - ), - child: Icon( - deviceType.icon, - color: isDarkMode ? Colors.white : Colors.black87, - size: 24.sp, - ), - ), - SizedBox(width: 16.w), - // 设备信息 - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - device.devicename.isNotEmpty - ? device.devicename - : "deviceTypeUnknown".tr, - style: TextStyle( - fontSize: 16.sp, - fontWeight: FontWeight.w500, - color: isDarkMode ? Colors.white : Colors.black87, + // 上部:图标 + 名称/MAC + 解绑 + Row( + children: [ + // 设备图标 + Container( + width: 46.w, + height: 46.w, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(14.r), + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: connected + ? [connectedColor, const Color(0xFF16A34A)] + : [const Color(0xFF6366F1), const Color(0xFF8B5CF6)], ), + boxShadow: [ + BoxShadow( + color: (connected ? connectedColor : const Color(0xFF6366F1)) + .withOpacity(0.3), + blurRadius: 16, + offset: const Offset(0, 4), + ), + ], ), - SizedBox(height: 4.h), - Text( - device.devicemac.isNotEmpty - ? device.devicemac - : "noMacAddress".tr, - style: TextStyle( - fontSize: 14.sp, - color: isDarkMode ? Colors.white70 : Colors.grey[600], - ), + child: Icon( + deviceType.icon, + color: Colors.white, + size: 22.sp, ), - if (device.license.isNotEmpty) ...[ - SizedBox(height: 4.h), - Text( - "${"licenseCode".tr}: ${device.license}", + ), + SizedBox(width: 14.w), + // 设备名 + MAC + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text( + device.devicename.isNotEmpty + ? device.devicename + : "deviceTypeUnknown".tr, + style: TextStyle( + fontSize: 16.sp, + fontWeight: FontWeight.w700, + color: isDark + ? Colors.white + : const Color(0xFF1A1A2E), + ), + ), + if (connected) ...[ + SizedBox(width: 8.w), + Container( + width: 6.w, + height: 6.w, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: connectedColor, + boxShadow: [ + BoxShadow( + color: connectedColor.withOpacity(0.6), + blurRadius: 8, + ), + ], + ), + ), + SizedBox(width: 6.w), + Container( + padding: EdgeInsets.symmetric( + horizontal: 8.w, vertical: 3.h), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(6.r), + color: connectedColor.withOpacity(0.1), + ), + child: Text( + "connected".tr, + style: TextStyle( + fontSize: 10.sp, + fontWeight: FontWeight.w600, + color: connectedColor, + ), + ), + ), + ], + ], + ), + SizedBox(height: 3.h), + Text( + device.devicemac.isNotEmpty + ? device.devicemac + : "noMacAddress".tr, + style: TextStyle( + fontSize: 12.sp, + fontWeight: FontWeight.w500, + color: isDark + ? Colors.white.withOpacity(0.3) + : const Color(0xFF999999), + fontFamily: 'Menlo', + letterSpacing: 0.5, + ), + ), + ], + ), + ), + // 解绑按钮 + GestureDetector( + onTap: () => _showUnbindDialog(device, isDark), + child: Container( + padding: EdgeInsets.symmetric( + horizontal: 14.w, vertical: 8.h), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10.r), + border: Border.all( + color: isDark + ? Colors.white.withOpacity(0.08) + : Colors.black.withOpacity(0.08), + ), + ), + child: Text( + "unbind".tr, style: TextStyle( fontSize: 12.sp, - color: isDarkMode ? Colors.white54 : Colors.grey[500], + fontWeight: FontWeight.w600, + color: isDark + ? Colors.white.withOpacity(0.4) + : const Color(0xFF999999), ), ), - ], - ], - ), - ), - // 解绑按钮 - GestureDetector( - onTap: () => _showUnbindDialog(device, isDarkMode), - child: Container( - padding: EdgeInsets.symmetric( - horizontal: 12.w, - vertical: 6.h, + ), ), + ], + ), + // 下部:授权码 / CMEI + if (device.license.isNotEmpty || device.cmei.isNotEmpty) ...[ + SizedBox(height: 16.h), + Container( + padding: EdgeInsets.all(12.w), decoration: BoxDecoration( - color: Colors.red.withOpacity(0.1), - borderRadius: BorderRadius.circular(6.r), - border: Border.all( - color: Colors.red.withOpacity(0.3), - width: 1, - ), + borderRadius: BorderRadius.circular(12.r), + color: isDark + ? Colors.white.withOpacity(0.03) + : const Color(0xFFF7F7FA), ), - child: Text( - "unbind".tr, - style: TextStyle( - fontSize: 12.sp, - color: Colors.red, - fontWeight: FontWeight.w500, - ), + child: Column( + children: [ + if (device.license.isNotEmpty) + _buildFieldRow( + label: "licenseCode".tr, + value: device.license, + isDark: isDark, + ), + if (device.license.isNotEmpty && device.cmei.isNotEmpty) + SizedBox(height: 8.h), + if (device.cmei.isNotEmpty) + _buildFieldRow( + label: "CMEI", + value: device.cmei, + isDark: isDark, + ), + ], ), ), - ), + ], ], ), ), ); } - void _showUnbindDialog(UserDevice device, bool isDarkMode) { + Widget _buildFieldRow({ + required String label, + required String value, + required bool isDark, + }) { + return Row( + children: [ + SizedBox( + width: 44.w, + child: Text( + label, + style: TextStyle( + fontSize: 10.sp, + fontWeight: FontWeight.w600, + color: isDark + ? Colors.white.withOpacity(0.2) + : const Color(0xFFBBBBBB), + letterSpacing: 0.5, + ), + ), + ), + SizedBox(width: 8.w), + Expanded( + child: Text( + value, + style: TextStyle( + fontSize: 11.sp, + fontWeight: FontWeight.w500, + color: isDark + ? Colors.white.withOpacity(0.5) + : const Color(0xFF666666), + fontFamily: 'Menlo', + letterSpacing: 0.3, + ), + overflow: TextOverflow.ellipsis, + ), + ), + ], + ); + } + + void _showUnbindDialog(UserDevice device, bool isDark) { Get.dialog( AlertDialog( - backgroundColor: isDarkMode ? AppColors.cardBackground : Colors.white, + backgroundColor: isDark ? AppColors.cardBackground : Colors.white, title: Text( "confirmUnbind".tr, style: TextStyle( - color: isDarkMode ? Colors.white : Colors.black87, + color: isDark ? Colors.white : Colors.black87, ), ), content: Text( @@ -322,7 +589,7 @@ class DevicesView extends GetView { .tr .replaceAll('{deviceName}', device.devicename), style: TextStyle( - color: isDarkMode ? Colors.white70 : Colors.black54, + color: isDark ? Colors.white70 : Colors.black54, ), ), actions: [ @@ -331,7 +598,7 @@ class DevicesView extends GetView { child: Text( "cancel".tr, style: TextStyle( - color: isDarkMode ? Colors.white70 : Colors.grey[600], + color: isDark ? Colors.white70 : Colors.grey[600], ), ), ), diff --git a/ui/yidong.html b/preview/yidong.html similarity index 100% rename from ui/yidong.html rename to preview/yidong.html