完成优化登录的优化
完成切换和切回APP数据的及时响应 完成服务下发退出账号的命令
This commit is contained in:
@@ -9,8 +9,8 @@ class DioClient {
|
||||
final dio = Dio(
|
||||
BaseOptions(
|
||||
baseUrl: HttpApiConsts.baseUrl,
|
||||
connectTimeout: const Duration(seconds: 10),
|
||||
receiveTimeout: const Duration(seconds: 10),
|
||||
connectTimeout: const Duration(seconds: 20),
|
||||
receiveTimeout: const Duration(seconds: 20),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
),
|
||||
);
|
||||
|
||||
@@ -14,6 +14,7 @@ import '../../../../core/network/tcp/tcp_client.dart';
|
||||
import '../../../../core/storage/user_storage.dart';
|
||||
import '../../../devices/domain/usecases/get_user_device_usecase.dart';
|
||||
import '../../../devices/domain/usecases/switch_device_usecase.dart';
|
||||
import '../../../devices/presentation/bloc/devices_cubit.dart';
|
||||
import '../../data/datasources/auth_tcp_datasource.dart';
|
||||
import '../../data/datasources/impl/auth_tcp_datasource_impl.dart';
|
||||
import 'auth_state.dart';
|
||||
@@ -47,12 +48,9 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
data: {'user': user, 'data': user}
|
||||
);
|
||||
if (user != null) {
|
||||
// print("App 启动时检查本地缓存user != null) ");
|
||||
// 1. print("App 启动时检查本地缓存user != null) ");
|
||||
// print("App 启动时检查本地缓存 tcp.connect) ");
|
||||
tcp.connect(host: TCPConsts.TCP_IP, port: TCPConsts.TCP_PORT);
|
||||
tcp.connect(host: TCPConsts.TCP_IP, port: TCPConsts.TCP_PORT);
|
||||
// await _authTcpDatasource.sendAuthPacket();//包括发送认证包和获取列表和切换函数
|
||||
tcp.startHeartbeat(interval: const Duration(seconds: 4));
|
||||
tcp.startHeartbeat(interval: const Duration(seconds: 4));
|
||||
// 2. 同步全局 App 状态
|
||||
appCubit.setAuth(user);
|
||||
// 3. 进入已登录状态
|
||||
@@ -67,9 +65,9 @@ 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(); //包括发送认证包和获取列表和切换函数
|
||||
// await _authTcpDatasource.sendAuthPacket(); //包括发送认证包和获取列表和切换函数
|
||||
//tcp.startHeartbeat(interval: const Duration(seconds: 4)); //启动心跳
|
||||
// appCubit.setAuth(user);
|
||||
appCubit.setAuth(user);
|
||||
emit(AuthAuthenticated(user));
|
||||
}
|
||||
|
||||
@@ -90,7 +88,11 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
_kickOutSub = dispatcher.onJsonMessage(0x12).listen((json) {
|
||||
// 如果后端发来指令确认需要退出
|
||||
// print('>>> [AUTH] 收到 0x12: $json'); //
|
||||
logout();
|
||||
// logout();
|
||||
final respond = json['respond'] ?? '';
|
||||
if (respond == 'have_logged_in') {
|
||||
logout(); // ✅ 只有特定消息才退出
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -51,16 +51,16 @@ class DeviceStatusBloc extends Bloc<DeviceStatusEvent, DeviceStatusState> {
|
||||
);
|
||||
|
||||
// 订阅 JSON 流 (0x12)
|
||||
_jsonSub = _dispatcher.onJsonMessage(0x12).listen(
|
||||
(jsonData) {
|
||||
debugPrint('📩 [Bloc] 收到 JSON 数据事件:$jsonData');
|
||||
if (!isClosed) {
|
||||
add(PushMessageReceived(jsonData));
|
||||
}
|
||||
},
|
||||
onDone: () => debugPrint('⚠️ [Bloc] JSON 流已结束 (onDone)'),
|
||||
onError: (e) => debugPrint('❌ [Bloc] JSON 流发生错误:$e'),
|
||||
);
|
||||
// _jsonSub = _dispatcher.onJsonMessage(0x12).listen(
|
||||
// (jsonData) {
|
||||
// debugPrint('📩 [Bloc] 收到 JSON 数据事件:$jsonData');
|
||||
// if (!isClosed) {
|
||||
// add(PushMessageReceived(jsonData));
|
||||
// }
|
||||
// },
|
||||
// onDone: () => debugPrint('⚠️ [Bloc] JSON 流已结束 (onDone)'),
|
||||
// onError: (e) => debugPrint('❌ [Bloc] JSON 流发生错误:$e'),
|
||||
// );
|
||||
|
||||
_isSubscribed = true;
|
||||
debugPrint('✅ [DeviceStatusBloc] 订阅建立完成,将持续监听数据流');
|
||||
|
||||
@@ -221,7 +221,7 @@ class DevicesCubit extends Cubit<DevicesState> {
|
||||
debugPrint('🛑 检测到已连接,先断开旧 TCP 连接...');
|
||||
_tcpClient.disconnects(forSwitch: true);
|
||||
// 稍微等待一下,确保底层 Socket 资源释放 (可选,但推荐)
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
// await Future.delayed(const Duration(milliseconds: 100));
|
||||
}
|
||||
|
||||
// 🔥 关键修复 2:发起新连接
|
||||
|
||||
@@ -2,81 +2,164 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:maibu_satabot_v2/core/app/app_user_cubit.dart';
|
||||
import 'package:maibu_satabot_v2/core/consts/tcp_consts.dart';
|
||||
import 'package:maibu_satabot_v2/core/network/tcp/tcp_client.dart';
|
||||
import 'package:maibu_satabot_v2/features/devices/presentation/bloc/devices_cubit.dart';
|
||||
import 'package:maibu_satabot_v2/features/devices/presentation/bloc/devices_state.dart'; // 导入设备状态类
|
||||
|
||||
import '../../../../core/consts/tcp_consts.dart';
|
||||
import '../../../../core/network/tcp/tcp_client.dart';
|
||||
import '../../../devices/domain/entities/device_entity.dart';
|
||||
import '../widgets/ImmersionHeader.dart';
|
||||
import '../widgets/quick_actions_grid.dart';
|
||||
import '../widgets/work_params_card.dart';
|
||||
|
||||
class HomePage extends StatelessWidget {
|
||||
class HomePage extends StatefulWidget {
|
||||
const HomePage({super.key});
|
||||
|
||||
/// 校验选中设备是否有效,无效则返回列表第一个设备
|
||||
DeviceEntity? _getValidCurrentDevice({required List<DeviceEntity> deviceList, required DeviceEntity? selectedDevice}) {
|
||||
if (deviceList.isEmpty) return null;
|
||||
@override
|
||||
State<HomePage> createState() => _HomePageState();
|
||||
}
|
||||
|
||||
// 用deviceName判断设备唯一性(如果有deviceId建议替换为deviceId)
|
||||
final bool isSelectedDeviceValid = deviceList.any((device) => device.deviceName == selectedDevice?.deviceName);
|
||||
class _HomePageState extends State<HomePage> {
|
||||
bool _isInitializing = false; // 标记是否正在初始化
|
||||
bool _hasLoadedDevices = false; // 标记是否已加载过设备列表
|
||||
|
||||
debugPrint("选中设备有效性:$isSelectedDeviceValid,选中设备名:${selectedDevice?.deviceName},设备列表:${deviceList.map((e) => e.deviceName).toList()}");
|
||||
/// 🔥 核心方法:页面显示前的初始化操作
|
||||
Future<void> _initializePage() async {
|
||||
if (_isInitializing) {
|
||||
debugPrint('[HomePage] 正在初始化,跳过重复调用');
|
||||
return;
|
||||
}
|
||||
|
||||
return isSelectedDeviceValid ? selectedDevice : deviceList.first;
|
||||
_isInitializing = true;
|
||||
|
||||
try {
|
||||
final userState = context.read<AppUserCubit>().state;
|
||||
final String? username = userState.user?.username;
|
||||
|
||||
if (username == null || username.isEmpty) {
|
||||
debugPrint('[HomePage] 用户未登录,跳过初始化');
|
||||
return;
|
||||
}
|
||||
|
||||
final devicesCubit = context.read<DevicesCubit>();
|
||||
|
||||
// 🔥 步骤 1: 先检查设备列表是否需要刷新
|
||||
if (!_hasLoadedDevices && (devicesCubit.state.devices == null || devicesCubit.state.devices!.isEmpty)) {
|
||||
debugPrint('[HomePage] 设备列表为空,触发获取...');
|
||||
|
||||
// ❌ 不要 await,让它后台执行
|
||||
devicesCubit.fetchAllDevices(username);
|
||||
|
||||
// 🔥 等待一小段时间让数据过来
|
||||
// await Future.delayed(const Duration(milliseconds: 500));
|
||||
|
||||
_hasLoadedDevices = true;
|
||||
}
|
||||
|
||||
// 🔥 步骤 2: 获取最新设备列表
|
||||
final deviceState = devicesCubit.state;
|
||||
final List<DeviceEntity> deviceList = deviceState.devices ?? [];
|
||||
|
||||
if (deviceList.isNotEmpty) {
|
||||
// 🔥 步骤 3: TCP 连接(异步执行,不阻塞 UI)
|
||||
final firstDevice = deviceState.selectedDevice ?? deviceList.first;
|
||||
debugPrint('🔌 [HomePage] 准备连接设备:${firstDevice.deviceName}');
|
||||
|
||||
final tcpClient = GetIt.instance<TcpClient>();
|
||||
var bool = tcpClient.isConnected;
|
||||
debugPrint('tcpClient,$bool');
|
||||
if (!tcpClient.isConnected) {
|
||||
// ❌ 异步执行,不阻塞
|
||||
tcpClient.connectBySwitch(
|
||||
host: TCPConsts.TCP_IP,
|
||||
port: TCPConsts.TCP_PORT,
|
||||
deviceName: firstDevice.deviceName
|
||||
).then((_) {
|
||||
debugPrint('✅ [HomePage] TCP 连接成功');
|
||||
tcpClient.startHeartbeat(interval: const Duration(seconds: 4));
|
||||
}).catchError((e) {
|
||||
debugPrint('❌ [HomePage] TCP 连接失败:$e');
|
||||
});
|
||||
} else {
|
||||
debugPrint('✅ [HomePage] TCP 已连接');
|
||||
}
|
||||
} else {
|
||||
debugPrint('⚠️ [HomePage] 设备列表仍为空');
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
debugPrint('❌ [HomePage] 初始化异常:$e');
|
||||
} finally {
|
||||
_isInitializing = false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
debugPrint('📄 [HomePage] initState - 页面开始初始化');
|
||||
|
||||
// 🔥 关键:在页面显示之前执行初始化
|
||||
// 使用 addPostFrameCallback 确保 build 完成后立即执行
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_initializePage();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// 获取用户状态(确保用户已登录)
|
||||
debugPrint('🎨 [HomePage] build - 渲染 UI');
|
||||
|
||||
// 同时监听用户和设备状态
|
||||
final userState = context.watch<AppUserCubit>().state;
|
||||
final String? username = userState.user?.username;
|
||||
final deviceState = context.watch<DevicesCubit>().state;
|
||||
final currentDevice = deviceState.selectedDevice;
|
||||
|
||||
// 页面首次渲染完成后,触发设备列表请求(避免同步调用导致的生命周期问题)
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (username != null && username.isNotEmpty) {
|
||||
final devicesCubit = context.read<DevicesCubit>();
|
||||
// 仅在设备列表为空或首次进入时请求(避免重复请求)
|
||||
devicesCubit.fetchAllDevices(username);
|
||||
//context.read<TcpClient>().connectBySwitch(host: TCPConsts.TCP_IP, port: TCPConsts.TCP_PORT, deviceName: device.deviceName);
|
||||
print("开始tcp连接。。。。");
|
||||
final tcpClient = GetIt.instance<TcpClient>();
|
||||
tcpClient.connectBySwitch(host: TCPConsts.TCP_IP, port: TCPConsts.TCP_PORT, deviceName: "MC700AIR-CN-JS-1762486208349-0000001A-FD");
|
||||
tcpClient.startHeartbeat(interval: const Duration(seconds: 4));
|
||||
}
|
||||
});
|
||||
final defaultDevice = DeviceEntity(
|
||||
deviceName: '暂未绑定设备',
|
||||
productId: 00000,
|
||||
productName: '',
|
||||
tenantId: 00000,
|
||||
tenantName: ''
|
||||
);
|
||||
|
||||
// 构建默认设备(无绑定设备时展示)
|
||||
final DeviceEntity defaultDevice = DeviceEntity(deviceName: '暂未绑定设备', productId: 00000, productName: '', tenantId: 00000, tenantName: '');
|
||||
// 如果没有绑定设备,展示未绑定设备的提示页面
|
||||
if (currentDevice == null) {
|
||||
debugPrint('ℹ️ [HomePage] 无选中设备,显示默认设备');
|
||||
|
||||
return BlocBuilder<DevicesCubit, DevicesState>(
|
||||
builder: (context, deviceState) {
|
||||
// 3. 正常状态:处理设备数据并渲染页面
|
||||
final List<DeviceEntity> deviceList = deviceState.devices ?? [];
|
||||
final DeviceEntity? selectedDevice = deviceState.selectedDevice;
|
||||
final DeviceEntity? currentDevice = _getValidCurrentDevice(deviceList: deviceList, selectedDevice: selectedDevice);
|
||||
return Scaffold(
|
||||
backgroundColor: const Color(0xFFF7F7F7),
|
||||
body: CustomScrollView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
slivers: [
|
||||
SliverToBoxAdapter(child: ImmersionHeader(device: defaultDevice)),
|
||||
const SliverToBoxAdapter(child: QuickActionsGrid()),
|
||||
const SliverToBoxAdapter(child: WorkParamsCard()),
|
||||
const SliverToBoxAdapter(child: SizedBox(height: 120)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: const Color(0xFFF7F7F7),
|
||||
body: CustomScrollView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
slivers: [
|
||||
// 1. 沉浸式头部(优先展示有效设备,无则展示默认设备)
|
||||
SliverToBoxAdapter(child: ImmersionHeader(device: currentDevice ?? defaultDevice)),
|
||||
// 如果有绑定设备,正常展示页面
|
||||
debugPrint('✅ [HomePage] 有选中设备:${currentDevice.deviceName}');
|
||||
|
||||
// 2. 功能网格
|
||||
const SliverToBoxAdapter(child: QuickActionsGrid()),
|
||||
|
||||
// 3. 作业参数卡片
|
||||
SliverToBoxAdapter(child: WorkParamsCard()),
|
||||
|
||||
// 4. 地图区域占位
|
||||
const SliverToBoxAdapter(child: SizedBox(height: 120)),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
return Scaffold(
|
||||
backgroundColor: const Color(0xFFF7F7F7),
|
||||
body: CustomScrollView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
slivers: [
|
||||
SliverToBoxAdapter(child: ImmersionHeader(device: currentDevice)),
|
||||
const SliverToBoxAdapter(child: QuickActionsGrid()),
|
||||
SliverToBoxAdapter(child: WorkParamsCard()),
|
||||
const SliverToBoxAdapter(child: SizedBox(height: 120)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
debugPrint('🗑️ [HomePage] dispose - 页面销毁');
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user