Browse Source

feat(设备图片): 实现动态加载设备产品图片功能

添加产品图片字段到DBProduct模型
在首页和OTA升级页面实现网络图片加载和缓存功能
添加图片加载状态处理和错误回退机制
weicu
lxm 1 year ago
parent
commit
5939ee1037
  1. 1
      lib/data/models/appconfig.dart
  2. 2
      lib/data/models/appconfig_model.dart
  3. 1
      lib/data/models/appconfig_model.g.dart
  4. 95
      lib/modules/home/controllers/home_controller.dart
  5. 48
      lib/modules/home/views/home_view.dart
  6. 52
      lib/modules/login/controllers/login_controller.dart
  7. 18
      lib/modules/ota_upgrade/controllers/ota_upgrade_controller.dart
  8. 33
      lib/modules/ota_upgrade/views/ota_upgrade_view.dart

1
lib/data/models/appconfig.dart

@ -47,6 +47,7 @@ class AppConfig {
return uuids;
}
//根据产品名称获取产品配置
static DBProduct? getproduct(String name) {
final foundProduct =
config.products.firstWhere((product) => product.devicename == name);

2
lib/data/models/appconfig_model.dart

@ -75,6 +75,7 @@ class DBProduct {
final int factoryid;
final int devicetype;
final String devicename;
final String? productimage;
final String scanuuid;
final String? version;
final String? updatedescription;
@ -85,6 +86,7 @@ class DBProduct {
required this.factoryid,
required this.devicetype,
required this.devicename,
required this.productimage,
required this.scanuuid,
required this.version,
required this.updatedescription,

1
lib/data/models/appconfig_model.g.dart

@ -76,6 +76,7 @@ DBProduct _$DBProductFromJson(Map<String, dynamic> json) => DBProduct(
factoryid: (json['factoryid'] as num).toInt(),
devicename: json['devicename'] as String,
devicetype: json['devicetype'] as int,
productimage: json['productimage'] as String?,
scanuuid: json['scanuuid'] as String,
version: json['version'] as String?,
updatedescription: json['updatedescription'] as String?,

95
lib/modules/home/controllers/home_controller.dart

@ -22,8 +22,12 @@ enum ConnectionStatus {
/// 主页控制器,管理设备连接状态和信息展示
class HomeController extends GetxController {
static const String _tag = 'HomeCtrl';
static const String _lastDeviceNameKey = 'last_connected_device_name'; // 存储键
static const String _lastDeviceImageKey =
'last_connected_device_image'; // 存储上一次设备图片的键
static HomeController get to => Get.find();
final GetStorage _storage = GetStorage();
String? _lastConnectedDeviceImage; // 新增:存储上一次连接设备的图片链接
final _isFullyConnected = false.obs;
String? _lastConnectedDeviceName;
@ -98,6 +102,7 @@ class HomeController extends GetxController {
void onInit() {
super.onInit();
_otaCheckService = Get.find<OtaCheckService>();
_loadLastConnectedDeviceName(); // 加载持久化的设备名称
_initWithCachedInfo();
// 移除了电池信息定时刷新的启动
}
@ -132,7 +137,7 @@ class HomeController extends GetxController {
_classicBluetoothWorker =
ever(_bluetoothManager.currentDeviceRx, (BluetoothDevice? device) {
if (device != null) {
_lastConnectedDeviceName = device.name;
_saveLastConnectedDeviceName(device.name); // 使用新的保存方法
}
_checkConnectionStatus();
});
@ -142,6 +147,28 @@ class HomeController extends GetxController {
});
}
/// 从本地存储加载最后连接的设备名称
void _loadLastConnectedDeviceName() {
_lastConnectedDeviceName = _storage.read(_lastDeviceNameKey);
Logger.d(_tag, '加载最后连接的设备名称: $_lastConnectedDeviceName');
}
/// 保存最后连接的设备名称到本地存储
void _saveLastConnectedDeviceName(String deviceName) {
_lastConnectedDeviceName = deviceName;
_storage.write(_lastDeviceNameKey, deviceName);
Logger.d(_tag, '保存最后连接的设备名称: $deviceName');
}
/// 保存上一次连接设备的图片链接到本地存储
void _saveLastConnectedDeviceImage(String? imageUrl) {
if (imageUrl != null && imageUrl.isNotEmpty) {
_lastConnectedDeviceImage = imageUrl;
_storage.write(_lastDeviceImageKey, imageUrl);
Logger.d(_tag, '保存上一次连接设备的图片: $imageUrl');
}
}
Future<void> refreshDeviceConnection() async {
await _checkConnectionStatus();
@ -235,6 +262,7 @@ class HomeController extends GetxController {
}
final deviceName = currentDevice?.name;
//final deviceName = '102';
if (deviceName != null && deviceName.isNotEmpty) {
return deviceName;
}
@ -404,4 +432,69 @@ class HomeController extends GetxController {
Map<String, dynamic> getOtaCheckStatus() {
return _otaCheckService.getCheckStatus();
}
/// 获取当前设备的产品图片路径
/// 如果没有配置产品图片或获取失败,返回null使用默认图片
String? getCurrentDeviceImage() {
try {
final deviceName = getDeviceName();
if (deviceName == 'unknownEquipment'.tr) {
Logger.d(_tag, '设备未知,使用默认图片');
return null;
}
// 通过AppConfig获取产品信息
if (!AppConfig.isInitialized()) {
Logger.w(_tag, 'AppConfig未初始化,使用默认图片');
return null;
}
final product = AppConfig.getproduct(deviceName);
if (product != null &&
product.productimage != null &&
product.productimage!.isNotEmpty) {
Logger.d(_tag, '获取到设备产品图片: ${product.productimage}');
_saveLastConnectedDeviceImage(product.productimage);
return product.productimage;
} else {
Logger.d(_tag, '设备 "$deviceName" 未配置产品图片,使用默认图片');
return null;
}
} catch (e) {
Logger.e(_tag, '获取设备产品图片失败: $e');
return null;
}
}
/// 获取设备图片的完整资源路径
/// 处理网络图片URL和本地资源路径
String getDeviceImagePath(bool isDarkMode) {
final productImage = getCurrentDeviceImage();
if (productImage != null) {
// 如果是网络图片URL
if (productImage.startsWith('http://') ||
productImage.startsWith('https://')) {
return productImage;
}
// 如果是本地资源路径,确保以assets/开头
else if (productImage.startsWith('assets/')) {
return productImage;
}
// 如果只是文件名,添加assets/images/前缀
else {
return 'assets/images/$productImage';
}
}
// 使用默认耳机图片
return isDarkMode
? 'assets/images/headphone_new3.png'
: 'assets/images/headphone_new4.png';
}
/// 判断是否为网络图片
bool isNetworkImage(String imagePath) {
return imagePath.startsWith('http://') || imagePath.startsWith('https://');
}
}

48
lib/modules/home/views/home_view.dart

@ -220,12 +220,58 @@ class HomeView extends GetView<HomeController> {
child: ClipRRect(
// 添加 ClipRRect
borderRadius: BorderRadius.circular(16.r),
child: Image.asset(
child: GetBuilder<HomeController>(
builder: (controller) {
final imagePath = controller.getDeviceImagePath(isDarkMode);
final isNetwork = controller.isNetworkImage(imagePath);
return isNetwork
? Image.network(
imagePath,
height: 220.h,
fit: BoxFit.contain,
errorBuilder: (context, error, stackTrace) {
// 网络图片加载失败时使用默认图片
return Image.asset(
isDarkMode
? 'assets/images/headphone_new3.png'
: 'assets/images/headphone_new4.png',
height: 220.h,
fit: BoxFit.contain,
);
},
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return SizedBox(
height: 220.h,
child: Center(
child: CircularProgressIndicator(
value: loadingProgress.expectedTotalBytes !=
null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
),
),
);
},
)
: Image.asset(
imagePath,
height: 220.h,
fit: BoxFit.contain,
errorBuilder: (context, error, stackTrace) {
// 本地图片加载失败时使用默认图片
return Image.asset(
isDarkMode
? 'assets/images/headphone_new3.png'
: 'assets/images/headphone_new4.png',
height: 220.h,
fit: BoxFit.contain,
);
},
);
},
),
),
),

52
lib/modules/login/controllers/login_controller.dart

@ -932,7 +932,59 @@ class LoginController extends GetxController {
Logger().d('getAppConfig:进来了');
// 根据API设计调整
final resp = UserGetAppConfigResp.fromJson(response);
// 详细记录解析后的配置信息
Logger().d('getAppConfig: 配置解析成功');
Logger().d('getAppConfig: 环境变量数量 = ${resp.env.length}');
Logger().d('getAppConfig: 智能体数量 = ${resp.agents.length}');
Logger().d('getAppConfig: MCP服务器数量 = ${resp.mcps.length}');
Logger().d('getAppConfig: 产品设备数量 = ${resp.products.length}');
// 详细记录产品设备信息
if (resp.products.isNotEmpty) {
Logger().d('getAppConfig: ========== 产品设备详细信息 ==========');
for (int i = 0; i < resp.products.length; i++) {
final product = resp.products[i];
Logger().d(
'getAppConfig: 产品[$i] ID=${product.id}, 设备名称="${product.devicename}"');
Logger().d(
'getAppConfig: 产品[$i] 厂商ID=${product.factoryid}, 设备类型=${product.devicetype}');
Logger().d('getAppConfig: 产品[$i] 扫描UUID="${product.scanuuid}"');
Logger().d(
'getAppConfig: 产品[$i] 产品图片="${product.productimage ?? "未设置"}"');
Logger().d('getAppConfig: 产品[$i] 版本="${product.version ?? "未设置"}"');
Logger().d(
'getAppConfig: 产品[$i] 更新描述="${product.updatedescription ?? "未设置"}"');
Logger().d(
'getAppConfig: 产品[$i] 更新包地址="${product.updatepackageaddress ?? "未设置"}"');
Logger().d('getAppConfig: ----------------------------------------');
}
Logger().d('getAppConfig: ========== 产品设备信息结束 ==========');
} else {
Logger().w('getAppConfig: 警告 - 未获取到任何产品设备信息');
}
// 记录环境变量信息
if (resp.env.isNotEmpty) {
Logger().d('getAppConfig: ========== 环境变量信息 ==========');
resp.env.forEach((key, value) {
Logger().d('getAppConfig: 环境变量 $key = "$value"');
});
Logger().d('getAppConfig: ========== 环境变量信息结束 ==========');
}
AppConfig.initialize(resp);
Logger().d(
'getAppConfig: AppConfig初始化完成,产品数量验证 = ${AppConfig.config.products.length}');
// 验证AppConfig中的产品数据
final productNames = AppConfig.allproductNames();
final productUuids = AppConfig.allproductUuids();
Logger().d('getAppConfig: 可用产品名称列表 = $productNames');
Logger().d('getAppConfig: 可用产品UUID列表 = $productUuids');
Logger().d('getAppConfig: 应用配置获取和初始化成功完成');
return true;
} catch (e) {
Logger().d('getAppConfig 取失败: $e');

18
lib/modules/ota_upgrade/controllers/ota_upgrade_controller.dart

@ -1,12 +1,14 @@
import 'package:classic_bluetooth/classic_bluetooth.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:logger/web.dart';
// import 'package:logger/web.dart';
import 'package:get_storage/get_storage.dart';
import 'package:ota/ota.dart';
import '../../../data/services/ble_manager.dart';
import 'ota_core_contropller.dart';
import 'ota_download_contropller.dart';
import '../views/ota_upgrade_popup_view.dart';
import '../../../core/utils/logger.dart';
/// OTA升级主控制器 - 重构后的简化版本
/// 负责协调各个服务,管理升级流程的整体状态
@ -33,13 +35,18 @@ class OtaUpgradeController extends GetxController {
final RxBool isFetchingDeviceInfo = false.obs;
final RxBool showDetailedStatus = false.obs;
final RxBool isBleConnected = true.obs;
final GetStorage _storage = GetStorage();
String _address = '';
String _updatePackageAddress = '';
String? _lastConnectedDeviceImage; // 新增:存储上一次连接设备的图片链接
get lastConnectedDeviceImage => _lastConnectedDeviceImage;
@override
void onInit() {
super.onInit();
_loadLastConnectedDeviceImage(); // 加载持久化的设备图片
_initializeController();
}
@ -51,6 +58,13 @@ class OtaUpgradeController extends GetxController {
super.onClose();
}
/// 从本地存储加载上一次连接设备的图片链接
void _loadLastConnectedDeviceImage() {
_lastConnectedDeviceImage = _storage.read('last_connected_device_name');
Logger.d(
'OtaUpgradeController', '加载上一次连接设备的图片: $_lastConnectedDeviceImage');
}
/// 初始化控制器
/// 设置事件监听并加载版本信息
Future<void> _initializeController() async {
@ -169,7 +183,7 @@ class OtaUpgradeController extends GetxController {
detailedStatus.value = '下载失败,请检查网络连接';
showDetailedStatus.value = false;
_handleError('下载失败', e.toString());
Logger().d('下载失败: $e');
Logger.d('OtaUpgradeController', '下载失败: $e');
}
}

33
lib/modules/ota_upgrade/views/ota_upgrade_view.dart

@ -185,12 +185,43 @@ class OtaUpgradeView extends GetView<OtaUpgradeController> {
width: 1,
),
),
child: Image.asset(
child: GetBuilder<OtaUpgradeController>(
builder: (controller) {
final imagePath = controller.lastConnectedDeviceImage;
// 网络图片
return Image.network(
imagePath,
height: 100.h,
fit: BoxFit.contain,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return SizedBox(
height: 100.h,
child: Center(
child: CircularProgressIndicator(
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
strokeWidth: 2,
color: isDarkMode ? Colors.white : Colors.blue,
),
),
);
},
errorBuilder: (context, error, stackTrace) {
// 网络图片加载失败时使用默认图片
return Image.asset(
isDarkMode
? 'assets/images/headphone_new3.png'
: 'assets/images/headphone_new4.png',
height: 100.h,
fit: BoxFit.contain,
);
},
);
},
),
);
}

Loading…
Cancel
Save