diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 67dc4a4f..f282b750 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -12,6 +12,10 @@ + + + + diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 49eff797..c51b485c 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -49,7 +49,8 @@ 需要访问您的位置以显示地图 NSCameraUsageDescription - 需要访问相机以扫描二维码 + 我们需要访问您的摄像头以扫描二维码绑定设备 + diff --git a/lib/core/di/injection.dart b/lib/core/di/injection.dart index e014f110..85a19f28 100644 --- a/lib/core/di/injection.dart +++ b/lib/core/di/injection.dart @@ -35,6 +35,7 @@ import '../../features/devices/data/repositories/route_planning_repository_impl. import '../../features/devices/domain/repositories/device_hostrity_work_repository.dart'; import '../../features/devices/domain/repositories/path_repository.dart'; import '../../features/devices/domain/repositories/route_planning_repository.dart'; +import '../../features/devices/domain/usecases/bind_device_usecase.dart'; import '../../features/devices/domain/usecases/delete_work_record_usecase.dart'; import '../../features/devices/domain/usecases/device_work_hostrirty_usecase.dart'; import '../../features/devices/domain/usecases/generate_path_usecase.dart'; @@ -130,6 +131,7 @@ Future init() async { sl.registerLazySingleton(() => DeviceWorkHostrirty(sl())); sl.registerLazySingleton(() => SelectWorkRecordUseCase(sl())); sl.registerLazySingleton(() => UnbindDeviceUseCase(sl())); + sl.registerLazySingleton(() => BindDeviceUseCase(sl())); sl.registerLazySingleton(() => UpdateDevicenameUsecase(sl())); sl.registerLazySingleton(() => SaveWorkRecordUseCase(sl())); sl.registerLazySingleton(() => SwitchDeviceUseCase(sl())); @@ -148,7 +150,7 @@ Future init() async { sl.registerLazySingleton(() => AppUserCubit()); // AuthCubit 依赖它,必须先注册 sl.registerLazySingleton(() => GetDeviceLocationUseCase(sl())); sl.registerLazySingleton( - () => DevicesCubit(sl(), sl(), sl(), sl(), sl(), sl(), sl(), sl(), sl(),sl(),sl()), + () => DevicesCubit(sl(), sl(), sl(), sl(), sl(), sl(), sl(), sl(), sl(),sl(),sl(), sl()), ); sl.registerFactory(() => RemoteControlCubit(sl())); /* // 工厂模式(留存,每次获取新实例) diff --git a/lib/features/devices/presentation/bloc/device_status_bloc.dart b/lib/features/devices/presentation/bloc/device_status_bloc.dart index b78b0b13..2ad34136 100644 --- a/lib/features/devices/presentation/bloc/device_status_bloc.dart +++ b/lib/features/devices/presentation/bloc/device_status_bloc.dart @@ -30,7 +30,7 @@ class DeviceStatusBloc extends Bloc { // 🔥 新增:模拟测试数据(调试用) - _testMockData(); + // _testMockData(); } void _testMockData() { // 用户提供的真实指令数据(字节数组) diff --git a/lib/features/devices/presentation/bloc/devices_cubit.dart b/lib/features/devices/presentation/bloc/devices_cubit.dart index dee10ce7..96482e2b 100644 --- a/lib/features/devices/presentation/bloc/devices_cubit.dart +++ b/lib/features/devices/presentation/bloc/devices_cubit.dart @@ -13,6 +13,7 @@ import 'package:maibu_satabot_v2/features/devices/domain/usecases/update_devicen import '../../data/models/device_add_path_point_model.dart'; import '../../data/models/device_work_area_param_model.dart'; +import '../../domain/usecases/bind_device_usecase.dart'; import '../../domain/usecases/delete_work_record_usecase.dart'; import '../../domain/usecases/get_device_location_usecase.dart'; import '../../domain/usecases/get_work_record_usecase.dart'; @@ -25,6 +26,7 @@ class DevicesCubit extends Cubit { final DeviceRepository repository; final GetWorkRecordUseCase _getWorkRecordUseCase; final DeleteWorkRecordUseCase _deleteWorkRecordUseCase; + final BindDeviceUseCase _bindDeviceUseCase; final UnbindDeviceUseCase _unbindDeviceUseCase; final UpdateDevicenameUsecase _updateDevicename; final SelectWorkRecordUseCase _selectWorkRecordUseCase; @@ -43,7 +45,7 @@ class DevicesCubit extends Cubit { this._selectWorkRecordUseCase, this._saveWorkRecordUseCase, this._generatePathUseCase, - this._routePlanningUseCase + this._routePlanningUseCase, this._bindDeviceUseCase ) : super(const DevicesState()); Future unbindDevice(String deviceId, String deviceName) async { @@ -176,6 +178,41 @@ class DevicesCubit extends Cubit { selectDevice(device); }, (r) => emit(state.copyWith(isLoading: false, selectedDevice: device))); } + /// 绑定设备 + Future bindDevice(String deviceId, String deviceAlias) async { + emit(state.copyWith(isLoading: true, errorMessage: '')); + try { + final params = BindDeviceParams(deviceId, deviceAlias); + final result = await _bindDeviceUseCase.call(params); + result.fold( + // 失败处理 + (failure) => emit(state.copyWith( + isLoading: false, + errorMessage: failure.message ?? '绑定设备失败', + )), + // 成功处理(返回 int 状态码) + (successCode) { + if (successCode == 1) { + emit(state.copyWith(isLoading: false, errorMessage: '')); + // 绑定成功后刷新设备列表 + //fetchAllDevices(state.?.username ?? ''); + } else { + emit(state.copyWith( + isLoading: false, + errorMessage: '绑定失败:状态码 $successCode', + )); + } + }, + ); + } catch (e) { + emit(state.copyWith( + isLoading: false, + errorMessage: '绑定异常:${e.toString()}', + )); + } + } + + /// 获取设备位置 Future getDeviceLocation(DeviceEntity device) async { diff --git a/lib/features/my/presentation/pages/my_page.dart b/lib/features/my/presentation/pages/my_page.dart index a515434d..de81b3dc 100644 --- a/lib/features/my/presentation/pages/my_page.dart +++ b/lib/features/my/presentation/pages/my_page.dart @@ -10,6 +10,7 @@ import '../../../auth/presentation/bloc/auth_state.dart'; import '../../../auth/presentation/pages/login_page.dart'; import '../../../web_view/web_view_page.dart'; import '../web_view/user_info_pages.dart'; +import 'package:mobile_scanner/mobile_scanner.dart'; class MyPage extends StatelessWidget { const MyPage({super.key}); diff --git a/lib/features/my/presentation/web_view/user_info_pages.dart b/lib/features/my/presentation/web_view/user_info_pages.dart index de7a44a5..1927c817 100644 --- a/lib/features/my/presentation/web_view/user_info_pages.dart +++ b/lib/features/my/presentation/web_view/user_info_pages.dart @@ -1,5 +1,10 @@ // user_info_card.dart import 'package:flutter/material.dart'; +import 'package:maibu_satabot_v2/features/devices/presentation/bloc/devices_cubit.dart'; +import 'package:mobile_scanner/mobile_scanner.dart'; // 1. 导入扫码库 +import 'package:get_it/get_it.dart'; // 用于获取 Cubit 实例 +// 2. 导入你的 Cubit 文件,请根据实际路径调整 +import '../../../auth/presentation/bloc/auth_cubit.dart'; class UserInfoCard extends StatelessWidget { final String userName; @@ -28,7 +33,7 @@ class UserInfoCard extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(userName, style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), - Text('用户id:$userId', style: TextStyle(fontSize: 12, color: Colors.grey)), + Text('用户 id:$userId', style: TextStyle(fontSize: 12, color: Colors.grey)), ], ), ), @@ -39,9 +44,75 @@ class UserInfoCard extends StatelessWidget { Spacer(), IconButton( icon: Icon(Icons.add), - onPressed: () {}, + // 3. 绑定点击事件 + onPressed: () => _handleScanAndBind(context), ), ], ); } + + // 4. 定义扫码与绑定逻辑 + Future _handleScanAndBind(BuildContext context) async { + // 跳转到扫码页面 + final String? qrCode = await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => MobileScanner( + onDetect: (capture) { + final List barcodes = capture.barcodes; + if (barcodes.isNotEmpty) { + debugPrint('识别到的数据: $barcodes'); + final String? code = barcodes.first.rawValue; + if (code != null && code.isNotEmpty) { + // 识别成功,立即返回结果并关闭页面 + debugPrint('扫码成功,识别到的数据: $code'); + Navigator.pop(context, code); + } + } + }, + ), + ), + ); + + // 如果用户取消或未扫到码,直接返回 + if (qrCode == null || qrCode.isEmpty) { + return; + } + + // 显示加载对话框 + showDialog( + context: context, + barrierDismissible: false, + builder: (context) => Center(child: CircularProgressIndicator()), + ); + + try { + // 5. 获取 Cubit 实例并调用绑定接口 + // 假设 AuthCubit 中已定义 bindDeviceByQr 方法 + final Cubit = GetIt.I(); + + // 执行绑定逻辑 (请确保 Cubit 层已实现此方法) + //await Cubit.bindDevice(qrCode); + + // 关闭加载框 + if (context.mounted) Navigator.pop(context); + + // 成功提示 + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('设备绑定成功!'), backgroundColor: Colors.green), + ); + } + } catch (e) { + // 关闭加载框 + if (context.mounted) Navigator.pop(context); + + // 失败提示 + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('绑定失败:$e'), backgroundColor: Colors.red), + ); + } + } + } } diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 291d5749..bf451370 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -10,6 +10,7 @@ import file_selector_macos import flutter_webrtc import geolocator_apple import isar_community_flutter_libs +import mobile_scanner import package_info_plus import sentry_flutter import shared_preferences_foundation @@ -21,6 +22,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FlutterWebRTCPlugin.register(with: registry.registrar(forPlugin: "FlutterWebRTCPlugin")) GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin")) IsarFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "IsarFlutterLibsPlugin")) + MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) SentryFlutterPlugin.register(with: registry.registrar(forPlugin: "SentryFlutterPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) diff --git a/pubspec.lock b/pubspec.lock index 6d50973f..7d8e3187 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -799,6 +799,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "2.0.0" + mobile_scanner: + dependency: "direct dev" + description: + name: mobile_scanner + sha256: "1b60b8f9d4ce0cb0e7d7bc223c955d083a0737bee66fa1fcfe5de48225e0d5b3" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.5.7" native_toolchain_c: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 0c7a5fdb..96c3df36 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -140,6 +140,8 @@ dev_dependencies: flutter_launcher_icons: ^0.13.1 + mobile_scanner: ^3.4.1 # 请根据实际需求选择最新版本 + # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec