AIR版的功能添加

This commit is contained in:
2026-09-20 16:31:15 +08:00
parent c8b597f683
commit 32397f8899
3 changed files with 58 additions and 36 deletions

View File

@@ -20,7 +20,16 @@ class BleDeviceDetailPage extends StatefulWidget {
final BluetoothDevice device; final BluetoothDevice device;
final ScanResult? scanResult; final ScanResult? scanResult;
const BleDeviceDetailPage({super.key, required this.device, this.scanResult}); /// 后端设备长名称(如 MC700PLUS-CN-JS-1760408682847-0000002A-A9),
/// 由列表页跳转时传入;为空时回退到蓝牙广播名
final String? displayName;
const BleDeviceDetailPage({
super.key,
required this.device,
this.scanResult,
this.displayName,
});
@override @override
State<BleDeviceDetailPage> createState() => _BleDeviceDetailPageState(); State<BleDeviceDetailPage> createState() => _BleDeviceDetailPageState();
@@ -515,7 +524,9 @@ class _BleDeviceDetailPageState extends State<BleDeviceDetailPage> {
final advData = widget.scanResult?.advertisementData; final advData = widget.scanResult?.advertisementData;
// 优先使用广告数据中的名称(含扫描响应),手机蓝牙列表也是这样显示的 // 优先使用广告数据中的名称(含扫描响应),手机蓝牙列表也是这样显示的
final name = final name =
_resolvedName ?? (widget.displayName?.isNotEmpty == true
? widget.displayName!
: _resolvedName) ??
(advData?.advName.isNotEmpty == true (advData?.advName.isNotEmpty == true
? advData!.advName ? advData!.advName
: (device.platformName.isNotEmpty : (device.platformName.isNotEmpty
@@ -603,14 +614,6 @@ class _BleDeviceDetailPageState extends State<BleDeviceDetailPage> {
color: context.appColors.textPrimary, color: context.appColors.textPrimary,
), ),
), ),
const SizedBox(height: 4),
Text(
'${widget.device.remoteId}',
style: TextStyle(
fontSize: 12,
color: context.appColors.textTertiary,
),
),
], ],
), ),
), ),
@@ -621,7 +624,6 @@ class _BleDeviceDetailPageState extends State<BleDeviceDetailPage> {
Divider(height: 1, color: context.appColors.divider), Divider(height: 1, color: context.appColors.divider),
const SizedBox(height: 12), const SizedBox(height: 12),
_buildInfoRow('设备名称', name), _buildInfoRow('设备名称', name),
_buildInfoRow('设备ID', '${widget.device.remoteId}'),
if (scanResult != null) ...[ if (scanResult != null) ...[
_buildInfoRow('信号强度', '${scanResult.rssi} dBm'), _buildInfoRow('信号强度', '${scanResult.rssi} dBm'),
_buildInfoRow( _buildInfoRow(
@@ -1237,7 +1239,7 @@ class _BleDeviceDetailPageState extends State<BleDeviceDetailPage> {
children: [ children: [
Expanded( Expanded(
child: Text( child: Text(
'设备配置 (0x05)', '设备配置',
style: TextStyle( style: TextStyle(
fontSize: 15, fontSize: 15,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,

View File

@@ -74,6 +74,10 @@ class _DeviceStatusViewState extends State<DeviceStatusView> {
/// 当前缓存的 siteId,用于判断是否真的发生了变化 /// 当前缓存的 siteId,用于判断是否真的发生了变化
int? _currentSiteId; int? _currentSiteId;
/// 🔥 下拉刷新计数器:变更时联动重建"全部"标签下的机器人/无人机场内嵌列表,
/// 使其与统计卡片(DeviceStatusBloc)同步刷新,避免统计数字与实际卡片对不上
int _refreshTick = 0;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@@ -616,9 +620,16 @@ class _DeviceStatusViewState extends State<DeviceStatusView> {
return RefreshIndicator( return RefreshIndicator(
onRefresh: () async { onRefresh: () async {
// 🔥 同步刷新:统计卡片(DeviceStatusBloc) + 下方机器人列表(RobotListBloc)
// + 无人机场列表(DroneStationBloc)。三者数据源相互独立,只刷统计会导致
// 数字与实际卡片对不上,故用 _refreshTick 变更内嵌列表的 ValueKey 强制
// 重建对应 BlocProvider,使其重新拉取数据。
context.read<DeviceListBloc.DeviceStatusBloc>().add( context.read<DeviceListBloc.DeviceStatusBloc>().add(
const DeviceListEvent.DeviceStatusRefresh(), const DeviceListEvent.DeviceStatusRefresh(),
); );
if (state.selectedType == 'all') {
setState(() => _refreshTick++);
}
}, },
color: context.appColors.primary, color: context.appColors.primary,
child: ListView( child: ListView(
@@ -639,7 +650,7 @@ class _DeviceStatusViewState extends State<DeviceStatusView> {
_buildDroneStationList( _buildDroneStationList(
context, context,
embedded: true, embedded: true,
key: ValueKey('embedded_drone_station_$_currentSiteId'), key: ValueKey('embedded_drone_station_${_currentSiteId}_$_refreshTick'),
), ),
// 🔥 普通设备列表(仅类型 tab 生效,"全部"模式下 filteredByType 为空) // 🔥 普通设备列表(仅类型 tab 生效,"全部"模式下 filteredByType 为空)
...filteredDevices ...filteredDevices
@@ -674,7 +685,7 @@ class _DeviceStatusViewState extends State<DeviceStatusView> {
return BlocProvider( return BlocProvider(
// 🔥 电站切换时通过 key 变化强制重建 BlocProvider,重新用新 siteId 加载 // 🔥 电站切换时通过 key 变化强制重建 BlocProvider,重新用新 siteId 加载
key: ValueKey('embedded_robot_$siteId'), key: ValueKey('embedded_robot_${siteId}_$_refreshTick'),
create: (_) => create: (_) =>
sl<RobotListBloc>()..add(RobotListLoadData(siteId: siteId)), sl<RobotListBloc>()..add(RobotListLoadData(siteId: siteId)),
child: BlocConsumer<RobotListBloc, RobotListState>( child: BlocConsumer<RobotListBloc, RobotListState>(
@@ -757,7 +768,7 @@ class _DeviceStatusViewState extends State<DeviceStatusView> {
debugPrint('❌ [全部-EmbeddedRobotList] 未提取到时间戳'); debugPrint('❌ [全部-EmbeddedRobotList] 未提取到时间戳');
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( const SnackBar(
content: Text('设备名称格式错误,无法提取时间戳'), content: Text('设备名称异常,暂时无法连接'),
duration: Duration(seconds: 2), duration: Duration(seconds: 2),
), ),
); );
@@ -967,12 +978,12 @@ class _DeviceStatusViewState extends State<DeviceStatusView> {
BuildContext context, BuildContext context,
DeviceListState.DeviceStatusLoaded state, DeviceListState.DeviceStatusLoaded state,
) { ) {
// 从实际设备列表计算统计数据(不再使用硬编码的假数据) // 从实际设备列表计算统计数据(设备状态仅有"在线/离线"两种,接口不会返回"异常",
// 故不再统计恒为 0 的"告警"项)
final devices = state.devices; final devices = state.devices;
final totalCount = devices.length; final totalCount = devices.length;
final onlineCount = devices.where((d) => d.status == '在线').length; final onlineCount = devices.where((d) => d.status == '在线').length;
final exceptionCount = devices.where((d) => d.status == '异常').length; final offlineCount = totalCount - onlineCount;
final offlineCount = totalCount - onlineCount - exceptionCount;
return Container( return Container(
margin: const EdgeInsets.symmetric(horizontal: 16.0), margin: const EdgeInsets.symmetric(horizontal: 16.0),
@@ -1008,15 +1019,6 @@ class _DeviceStatusViewState extends State<DeviceStatusView> {
textColor: context.appColors.success, textColor: context.appColors.success,
), ),
Container(width: 1, height: 40, color: context.appColors.divider), Container(width: 1, height: 40, color: context.appColors.divider),
_buildStatItem(
value: exceptionCount.toString(),
label: AppLocalizations.of(
context,
).translate('device_list_v2.alarm'),
dotColor: context.appColors.warning,
textColor: context.appColors.warning,
),
Container(width: 1, height: 40, color: context.appColors.divider),
_buildStatItem( _buildStatItem(
value: offlineCount.toString(), value: offlineCount.toString(),
label: AppLocalizations.of( label: AppLocalizations.of(
@@ -1356,7 +1358,10 @@ class _DeviceStatusViewState extends State<DeviceStatusView> {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (_) => BleDeviceDetailPage(device: connectedDevice), builder: (_) => BleDeviceDetailPage(
device: connectedDevice,
displayName: deviceName,
),
), ),
); );
return; return;
@@ -1418,7 +1423,10 @@ class _DeviceStatusViewState extends State<DeviceStatusView> {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (_) => BleDeviceDetailPage(device: device), builder: (_) => BleDeviceDetailPage(
device: device,
displayName: deviceName,
),
), ),
); );
} else { } else {
@@ -1449,7 +1457,7 @@ class _DeviceStatusViewState extends State<DeviceStatusView> {
dialogShown = true; dialogShown = true;
return AlertDialog( return AlertDialog(
title: const Text('未找到设备'), title: const Text('未找到设备'),
content: const Text('请确认机器已上线并打开蓝牙了'), content: const Text('暂未匹配到该设备,稍后重试'),
actions: [ actions: [
TextButton( TextButton(
onPressed: () { onPressed: () {
@@ -1495,7 +1503,10 @@ class _DeviceStatusViewState extends State<DeviceStatusView> {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (_) => BleDeviceDetailPage(device: result.device), builder: (_) => BleDeviceDetailPage(
device: result.device,
displayName: deviceName,
),
), ),
); );
break; break;

View File

@@ -669,7 +669,7 @@ class _RobotListViewState extends State<RobotListView> {
debugPrint('❌ [RobotListPage] 无法从设备名中提取时间戳: ${robot.name}'); debugPrint('❌ [RobotListPage] 无法从设备名中提取时间戳: ${robot.name}');
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text('设备名格式异常,无法进行蓝牙连接'), content: Text('设备名称异常,暂时无法连接'),
backgroundColor: context.appColors.warning, backgroundColor: context.appColors.warning,
), ),
); );
@@ -767,7 +767,10 @@ class _RobotListViewState extends State<RobotListView> {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (_) => BleDeviceDetailPage(device: connectedDevice), builder: (_) => BleDeviceDetailPage(
device: connectedDevice,
displayName: deviceName,
),
), ),
); );
return; return;
@@ -848,7 +851,10 @@ class _RobotListViewState extends State<RobotListView> {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (_) => BleDeviceDetailPage(device: device), builder: (_) => BleDeviceDetailPage(
device: device,
displayName: deviceName,
),
), ),
); );
} else { } else {
@@ -881,7 +887,7 @@ class _RobotListViewState extends State<RobotListView> {
dialogShown = true; dialogShown = true;
return AlertDialog( return AlertDialog(
title: const Text('未找到设备'), title: const Text('未找到设备'),
content: const Text('请确认机器已上线并打开蓝牙了'), content: const Text('暂未匹配到该设备,稍后重试'),
actions: [ actions: [
TextButton( TextButton(
onPressed: () { onPressed: () {
@@ -933,7 +939,10 @@ class _RobotListViewState extends State<RobotListView> {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (_) => BleDeviceDetailPage(device: dev!), builder: (_) => BleDeviceDetailPage(
device: dev!,
displayName: deviceName,
),
), ),
); );
break; break;