完成解决合并扫码绑定代码问题
完成优化tcp的重连和认证 完成登录响应慢和热加载时候tcp不能统一对象的重连
This commit is contained in:
@@ -9,8 +9,8 @@ class DioClient {
|
||||
final dio = Dio(
|
||||
BaseOptions(
|
||||
baseUrl: HttpApiConsts.baseUrl,
|
||||
connectTimeout: const Duration(seconds: 5),
|
||||
receiveTimeout: const Duration(seconds: 5),
|
||||
connectTimeout: const Duration(seconds: 10),
|
||||
receiveTimeout: const Duration(seconds: 10),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
),
|
||||
);
|
||||
|
||||
@@ -216,6 +216,7 @@ class TcpClient {
|
||||
debugPrint('tcp使用用户信息:$user');
|
||||
if (user == null || user.token == null) {
|
||||
debugPrint('❌ [TCP] 认证失败:用户未登录或 Token 为空,无法发送认证包');
|
||||
disconnect();
|
||||
return; // 直接返回,不要发送无效包
|
||||
}
|
||||
username = user.username;
|
||||
@@ -254,12 +255,14 @@ class TcpClient {
|
||||
(failure) {
|
||||
// 处理失败:打印日志或抛出异常
|
||||
debugPrint('❌ [AuthTcp] 获取设备列表失败:$failure');
|
||||
_sendAuthPacket();
|
||||
throw Exception('获取设备列表失败:$failure');
|
||||
},
|
||||
(devices) async {
|
||||
// 处理成功:devices 现在是真正的 List<DeviceEntity>
|
||||
if (devices.isEmpty) {
|
||||
debugPrint('⚠️ [AuthTcp] 当前用户无可用设备,跳过切换步骤');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -273,6 +276,7 @@ class TcpClient {
|
||||
await switchResult.fold(
|
||||
(failure) {
|
||||
debugPrint('❌ [AuthTcp] 切换设备失败:$failure');
|
||||
_sendAuthPacket();
|
||||
throw Exception('切换设备失败:$failure');
|
||||
},
|
||||
(success) {
|
||||
|
||||
@@ -53,8 +53,8 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
Future<void> loginSuccess(UserEntity user) async {
|
||||
await storage.saveUser(user);
|
||||
await tcp.connect(host: TCPConsts.TCP_IP, port: TCPConsts.TCP_PORT);
|
||||
await _authTcpDatasource.sendAuthPacket();//包括发送认证包和获取列表和切换函数
|
||||
tcp.startHeartbeat(interval: const Duration(seconds: 4)); //启动心跳
|
||||
//await _authTcpDatasource.sendAuthPacket();//包括发送认证包和获取列表和切换函数
|
||||
tcp.startHeartbeat(interval: const Duration(seconds: 4)); //启动心跳*/
|
||||
appCubit.setAuth(user);
|
||||
emit(AuthAuthenticated(user));
|
||||
}
|
||||
|
||||
@@ -180,16 +180,21 @@ class DevicesCubit extends Cubit<DevicesState> {
|
||||
}
|
||||
/// 绑定设备
|
||||
Future<void> 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 ?? '绑定设备失败',
|
||||
)),
|
||||
(failure) {
|
||||
emit(state.copyWith(
|
||||
isLoading: false,
|
||||
errorMessage: failure.message ?? '绑定设备失败',
|
||||
));
|
||||
// 抛出异常,携带后端返回的错误消息(如“设备不存在”)
|
||||
throw Exception(failure.message ?? '绑定设备失败');
|
||||
},
|
||||
// 成功处理(返回 int 状态码)
|
||||
(successCode) {
|
||||
if (successCode == 1) {
|
||||
@@ -201,14 +206,17 @@ class DevicesCubit extends Cubit<DevicesState> {
|
||||
isLoading: false,
|
||||
errorMessage: '绑定失败:状态码 $successCode',
|
||||
));
|
||||
throw Exception('绑定失败:状态码 $successCode');
|
||||
}
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
emit(state.copyWith(
|
||||
isLoading: false,
|
||||
errorMessage: '绑定异常:${e.toString()}',
|
||||
));
|
||||
// emit(state.copyWith(
|
||||
// isLoading: false,
|
||||
// errorMessage: '绑定异常:${e.toString()}',
|
||||
// ));
|
||||
// 🔥 重新抛出,传递给 UI
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -179,43 +179,132 @@ class _UserInfoCardState extends State<UserInfoCard> {
|
||||
|
||||
// 扫码绑定设备逻辑(保留)
|
||||
Future<void> _handleScanAndBind(BuildContext context) async {
|
||||
final String? qrCode = await Navigator.push<String>(
|
||||
bool isScanned = false;
|
||||
|
||||
// 1. 扫码逻辑 (保持不变)
|
||||
final String? deviceCode = await Navigator.push<String>(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => MobileScanner(
|
||||
onDetect: (capture) {
|
||||
final List<Barcode> barcodes = capture.barcodes;
|
||||
if (barcodes.isNotEmpty) {
|
||||
final String? code = barcodes.first.rawValue;
|
||||
if (code != null && code.isNotEmpty) {
|
||||
Navigator.pop(context, code);
|
||||
builder: (context) => Scaffold(
|
||||
backgroundColor: Colors.transparent,
|
||||
appBar: AppBar(
|
||||
title: const Text('扫码绑定设备'),
|
||||
backgroundColor: Colors.black54,
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
),
|
||||
body: MobileScanner(
|
||||
onDetect: (BarcodeCapture capture) {
|
||||
if (isScanned) return;
|
||||
final List<Barcode> barcodes = capture.barcodes;
|
||||
if (barcodes.isNotEmpty) {
|
||||
final String? code = barcodes.first.rawValue;
|
||||
if (code != null && code.isNotEmpty) {
|
||||
isScanned = true;
|
||||
// 使用 addPostFrameCallback 确保在帧渲染后执行,避免锁死
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (context.mounted && Navigator.canPop(context)) {
|
||||
Navigator.pop(context, code);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
if (qrCode == null || qrCode.isEmpty) return;
|
||||
if (deviceCode == null || deviceCode.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 确认对话框 (保持不变)
|
||||
final bool? confirmBind = await showDialog<bool>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('确认绑定设备'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text('设备信息:'),
|
||||
const SizedBox(height: 8),
|
||||
Text('设备编码:$deviceCode'),
|
||||
const Text('设备状态:未绑定'),
|
||||
const Text('设备类型:智能设备'),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, false),
|
||||
child: const Text('取消'),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () => Navigator.pop(context, true),
|
||||
child: const Text('确定绑定'),
|
||||
style: ElevatedButton.styleFrom(backgroundColor: Colors.blue),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (confirmBind != true) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 显示加载框
|
||||
// 保存 BuildContext 引用,防止后续 context 变化导致找不到正确的 Navigator
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (context) => const Center(child: CircularProgressIndicator(color: Color(0xFF1677FF), strokeWidth: 4)),
|
||||
builder: (loadingContext) => const Center(child: CircularProgressIndicator()),
|
||||
);
|
||||
|
||||
try {
|
||||
final devicesCubit = _getIt<DevicesCubit>();
|
||||
// await devicesCubit.bindDevice(qrCode);
|
||||
if (context.mounted) Navigator.pop(context);
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('设备绑定成功!'), backgroundColor: Colors.green));
|
||||
final devicesCubit = GetIt.I<DevicesCubit>();
|
||||
await devicesCubit.bindDevice(deviceCode, '');
|
||||
|
||||
// ✅ 成功:安全关闭加载框
|
||||
if (context.mounted && Navigator.canPop(context)) {
|
||||
Navigator.pop(context);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const 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));
|
||||
// ✅ 失败:安全关闭加载框 (关键修复点)
|
||||
// 即使发生了 Framework 错误,也要尝试关闭弹窗
|
||||
if (context.mounted && Navigator.canPop(context)) {
|
||||
Navigator.pop(context);
|
||||
|
||||
// 显示具体错误信息
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('绑定失败:${e.toString()}'),
|
||||
backgroundColor: Colors.red,
|
||||
duration: const Duration(seconds: 4),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// 极端情况:如果 Navigator 已经锁死无法 pop,尝试用其他方式提示用户
|
||||
// 或者等待下一帧再试
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (context.mounted) {
|
||||
// 尝试再次关闭,或者只显示错误提示
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('绑定失败:${e.toString()} (请手动返回)'),
|
||||
backgroundColor: Colors.orange,
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
debugPrint('设备绑定失败详情:$e');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user