Commit 4b88b06f authored by 张宏's avatar 张宏

1

parent 97313115
import 'package:equatable/equatable.dart';
class InspectionDeviceBO extends Equatable {
final int deviceId;
final String deviceName;
final String deviceTypeIcon;
final String deviceTypeName;
final String runStatus;
final String runStatusText;
final String roomName;
final String lastHeartbeat;
final String lastHeartbeatDisplay;
final String onlineStatus;
final String powerStatus;
final int roomId;
const InspectionDeviceBO({
required this.deviceId,
required this.deviceName,
required this.deviceTypeIcon,
required this.deviceTypeName,
required this.runStatus,
required this.runStatusText,
required this.roomName,
required this.lastHeartbeat,
required this.lastHeartbeatDisplay,
required this.onlineStatus,
required this.powerStatus,
required this.roomId,
});
factory InspectionDeviceBO.fromJson(Map<String, dynamic> json) {
return InspectionDeviceBO(
deviceId: int.tryParse(json['deviceId']?.toString() ?? '') ?? 0,
deviceName: json['deviceName'] as String? ?? '',
deviceTypeIcon: json['deviceTypeIcon'] as String? ?? '',
deviceTypeName: json['deviceTypeName'] as String? ?? '',
runStatus: json['runStatus'] as String? ?? '',
runStatusText: json['runStatusText'] as String? ?? '',
roomName: json['roomName'] as String? ?? '',
lastHeartbeat: json['lastHeartbeat'] as String? ?? '',
lastHeartbeatDisplay: json['lastHeartbeatDisplay'] as String? ?? '',
onlineStatus: json['onlineStatus'] as String? ?? '',
powerStatus: json['powerStatus'] as String? ?? '',
roomId: int.tryParse(json['roomId']?.toString() ?? '') ?? 0,
);
}
@override
List<Object?> get props => [
deviceId,
deviceName,
deviceTypeIcon,
deviceTypeName,
runStatus,
runStatusText,
roomName,
lastHeartbeat,
lastHeartbeatDisplay,
onlineStatus,
powerStatus,
roomId,
];
}
class InspectionDeviceListBO extends Equatable {
final int total;
final List<InspectionDeviceBO> data;
const InspectionDeviceListBO({
required this.total,
required this.data,
});
factory InspectionDeviceListBO.fromJson(Map<String, dynamic> json) {
final list = json['rows'] as List<dynamic>? ?? [];
return InspectionDeviceListBO(
total: int.tryParse(json['total']?.toString() ?? '') ?? 0,
data: list
.map((e) => InspectionDeviceBO.fromJson(e as Map<String, dynamic>))
.toList(),
);
}
@override
List<Object?> get props => [total, data];
}
\ No newline at end of file
......@@ -13,7 +13,7 @@ class InspectionRoomBO extends Equatable {
factory InspectionRoomBO.fromJson(Map<String, dynamic> json) {
return InspectionRoomBO(
roomId: json['roomId'] as int? ?? 0,
roomId: int.tryParse(json['roomId']?.toString() ?? '') ?? 0,
roomNumber: json['roomNumber'] as String? ?? '',
runStatus: InspectionRoomRunStatusBO.fromJson(
json['runStatus'] as Map<String, dynamic>? ?? {},
......@@ -40,10 +40,10 @@ class InspectionRoomRunStatusBO extends Equatable {
factory InspectionRoomRunStatusBO.fromJson(Map<String, dynamic> json) {
return InspectionRoomRunStatusBO(
normal: json['normal'] as int? ?? 0,
warning: json['warning'] as int? ?? 0,
offline: json['offline'] as int? ?? 0,
fault: json['fault'] as int? ?? 0,
normal: int.tryParse(json['normal']?.toString() ?? '') ?? 0,
warning: int.tryParse(json['warning']?.toString() ?? '') ?? 0,
offline: int.tryParse(json['offline']?.toString() ?? '') ?? 0,
fault: int.tryParse(json['fault']?.toString() ?? '') ?? 0,
);
}
......
import '../../utils/http/response_model.dart';
import '../../utils/http/dio_request.dart';
import '../../models/bo/inspection_device_bo.dart';
class InspectionDeviceRepository {
Future<ResponseModel<InspectionDeviceListBO>> getList({
required int pageSize,
required int pageNum,
int? roomId
}) {
return DioRequest.instance.get<InspectionDeviceListBO>(
'/app/device/inspection',
queryParameters: {'pageSize': pageSize, 'pageNum': pageNum, "roomId": roomId},
fromJsonT: (data) =>
InspectionDeviceListBO.fromJson(data as Map<String, dynamic>),
);
}
}
\ No newline at end of file
import '../repositories/inspection_device_repository.dart';
import '../models/bo/inspection_device_bo.dart';
class InspectionDeviceService {
final InspectionDeviceRepository _repository;
InspectionDeviceService({required InspectionDeviceRepository repository})
: _repository = repository;
Future<InspectionDeviceListBO> getList({
required int pageSize,
required int pageNum,
int? roomId
}) async {
final result =
await _repository.getList(pageSize: pageSize, pageNum: pageNum, roomId: roomId);
if (result.success && result.data != null) {
return result.data!;
}
throw Exception(result.msg);
}
}
\ No newline at end of file
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:smart_hotel_app/models/bo/inspection_room_bo.dart';
import 'package:smart_hotel_app/repositories/inspection_device_repository.dart';
import 'package:smart_hotel_app/repositories/inspection_room_repository.dart';
import 'package:smart_hotel_app/services/inspection_device_service.dart';
import 'package:smart_hotel_app/services/inspection_room_service.dart';
import 'package:smart_hotel_app/views/home/inspection/cubit/inspection_state.dart';
class InspectionCubit extends Cubit<InspectionState> {
final InspectionRoomService _service;
final InspectionRoomService _roomService;
final InspectionDeviceService _deviceService;
static const List<InspectionDevice> allDevices = [
InspectionDevice(
id: '1',
icon: Icons.wifi,
name: '101主控网关',
type: '智能网关',
location: '101电视柜',
status: '正常',
time: '2024-01-15 14:30:00',
statusColor: Color.fromRGBO(26, 188, 156, 1.0),
),
InspectionDevice(
id: '2',
icon: Icons.thermostat,
name: '温湿度传感器-101',
type: '传感器',
location: '101角落',
status: '正常',
time: '2024-01-15 14:25:00',
statusColor: Color.fromRGBO(26, 188, 156, 1.0),
),
InspectionDevice(
id: '3',
icon: Icons.videocam,
name: '智能摄像头-102',
type: '摄像头',
location: '102门口',
status: '告警',
time: '2024-01-15 10:00:00',
statusColor: Color.fromRGBO(255, 77, 79, 1.0),
),
InspectionDevice(
id: '4',
icon: Icons.light,
name: '智能吸顶灯-102',
type: '传感器',
location: '102天花板',
status: '正常',
time: '2024-01-15 14:25:00',
statusColor: Color.fromRGBO(26, 188, 156, 1.0),
),
InspectionDevice(
id: '5',
icon: Icons.thermostat,
name: '温湿度传感器-103',
type: '传感器',
location: '103角落',
status: '正常',
time: '2024-01-15 14:25:00',
statusColor: Color.fromRGBO(26, 188, 156, 1.0),
),
InspectionDevice(
id: '6',
icon: Icons.light,
name: '103灯-智能',
type: '智能灯',
location: '103',
status: '开启',
time: '2024-01-15 14:20:00',
statusColor: Color.fromRGBO(26, 188, 156, 1.0),
),
InspectionDevice(
id: '7',
icon: Icons.sensor_door,
name: '门窗传感器-104',
type: '传感器',
location: '104门',
status: '离线',
time: '2024-01-15 12:00:00',
statusColor: Color.fromRGBO(102, 102, 102, 1.0),
),
InspectionDevice(
id: '8',
icon: Icons.kitchen,
name: '智能插座-105',
type: '插座',
location: '105台面',
status: '关闭',
time: '2024-01-15 14:15:00',
statusColor: Color.fromRGBO(100, 116, 139, 1.0),
),
InspectionDevice(
id: '9',
icon: Icons.smoke_free,
name: '烟雾报警器-105',
type: '报警器',
location: '105天花板',
status: '正常',
time: '2024-01-15 14:30:00',
statusColor: Color.fromRGBO(26, 188, 156, 1.0),
),
InspectionDevice(
id: '10',
icon: Icons.desk,
name: '智能台灯-106',
type: '智能灯',
location: '106书桌',
status: '关闭',
time: '2024-01-15 14:10:00',
statusColor: Color.fromRGBO(100, 116, 139, 1.0),
),
InspectionDevice(
id: '11',
icon: Icons.ac_unit,
name: '智能空调-101',
type: '空调',
location: '101',
status: '正常',
time: '2024-01-15 14:30:00',
statusColor: Color.fromRGBO(26, 188, 156, 1.0),
),
];
static const int _pageSize = 20;
InspectionCubit({InspectionRoomService? service})
: _service = service ??
InspectionRoomService(
repository: InspectionRoomRepository(),
),
InspectionCubit({
InspectionRoomService? roomService,
InspectionDeviceService? deviceService,
}) : _roomService = roomService ??
InspectionRoomService(repository: InspectionRoomRepository()),
_deviceService = deviceService ??
InspectionDeviceService(
repository: InspectionDeviceRepository()),
super(const InspectionState(isLoading: true)) {
initData();
}
Future<void> initData() async {
try {
final rooms = await _service.getRooms();
final roomNumbers = rooms.map((r) => r.roomNumber).toList();
final rooms = await _roomService.getRooms();
final roomsMap = <int, String>{};
for (final r in rooms) {
roomsMap[r.roomId] = r.roomNumber;
}
final tabCounts = _computeTabCounts(rooms);
final firstRoomIds = rooms.isNotEmpty ? [rooms.first.roomId] : <int>[];
emit(state.copyWith(
isLoading: false,
error: null,
allRooms: roomNumbers,
selectedRooms: roomNumbers.isNotEmpty ? [roomNumbers.first] : [],
allRooms: roomsMap,
selectedRoomIds: firstRoomIds,
tabCounts: tabCounts,
deviceList: _filterDevices(
selectedTab: DeviceStatusTab.total,
selectedRooms: roomNumbers.isNotEmpty ? [roomNumbers.first] : [],
),
));
} catch (e) {
emit(state.copyWith(
isLoading: false,
error: e.toString(),
deviceList: _filterDevices(),
));
}
}
/// 供 PagingController.fetchPage 回调使用
Future<List<InspectionDevice>> fetchPage(int pageKey) async {
final roomId = state.selectedRoomIds.length == 1
? state.selectedRoomIds.first
: null;
final result = await _deviceService.getList(
pageSize: _pageSize,
pageNum: pageKey,
roomId: roomId,
);
return result.data
.map((e) => InspectionDevice.fromInspectionDeviceBO(e))
.toList();
}
Map<DeviceStatusTab, int> _computeTabCounts(List<InspectionRoomBO> rooms) {
int normal = 0;
int warning = 0;
......@@ -180,60 +90,17 @@ class InspectionCubit extends Cubit<InspectionState> {
}
void selectTab(DeviceStatusTab tab) {
emit(state.copyWith(
selectedTab: tab,
deviceList: _filterDevices(selectedTab: tab),
));
emit(state.copyWith(selectedTab: tab));
}
void toggleRoom(String room) {
final List<String> newSelectedRooms = List.from(state.selectedRooms);
if (newSelectedRooms.contains(room)) {
newSelectedRooms.remove(room);
void toggleRoom(int roomId) {
final List<int> newSelected = List.from(state.selectedRoomIds);
if (newSelected.contains(roomId)) {
newSelected.remove(roomId);
} else {
newSelectedRooms.add(room);
}
emit(state.copyWith(
selectedRooms: newSelectedRooms,
deviceList: _filterDevices(selectedRooms: newSelectedRooms),
));
}
List<InspectionDevice> _filterDevices({
DeviceStatusTab? selectedTab,
List<String>? selectedRooms,
}) {
DeviceStatusTab tab = selectedTab ?? state.selectedTab;
List<String> rooms = selectedRooms ?? state.selectedRooms;
List<InspectionDevice> filtered = List.from(allDevices);
if (rooms.isNotEmpty) {
filtered = filtered.where((device) {
return rooms.any(
(room) => device.location.contains(room) || device.name.contains(room));
}).toList();
newSelected.clear();
newSelected.add(roomId);
}
if (tab != DeviceStatusTab.total) {
filtered = filtered.where((device) {
switch (tab) {
case DeviceStatusTab.on:
return device.status == '正常' || device.status == '开启';
case DeviceStatusTab.off:
return device.status == '关闭';
case DeviceStatusTab.offline:
return device.status == '离线';
case DeviceStatusTab.alarm:
return device.status == '告警';
case DeviceStatusTab.fault:
return device.status == '故障';
default:
return true;
}
}).toList();
}
return filtered;
emit(state.copyWith(selectedRoomIds: newSelected));
}
}
\ No newline at end of file
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:smart_hotel_app/models/bo/inspection_device_bo.dart';
enum DeviceStatusTab { total, on, off, offline, alarm, fault }
......@@ -23,24 +24,78 @@ class InspectionDevice {
required this.time,
required this.statusColor,
});
factory InspectionDevice.fromInspectionDeviceBO(InspectionDeviceBO bo) {
return InspectionDevice(
id: bo.deviceId.toString(),
icon: _mapIcon(bo.deviceTypeIcon),
name: bo.deviceName,
type: bo.deviceTypeName,
location: bo.roomName,
status: bo.runStatusText,
time: bo.lastHeartbeatDisplay,
statusColor: _mapStatusColor(bo.runStatus),
);
}
static IconData _mapIcon(String iconType) {
switch (iconType) {
case 'gateway':
return Icons.wifi;
case 'sensor':
return Icons.thermostat;
case 'camera':
return Icons.videocam;
case 'light':
return Icons.light;
case 'air_conditioner':
case 'ac':
return Icons.ac_unit;
case 'socket':
return Icons.kitchen;
case 'alarm':
return Icons.smoke_free;
case 'door_sensor':
return Icons.sensor_door;
default:
return Icons.devices_other;
}
}
static Color _mapStatusColor(String runStatus) {
switch (runStatus) {
case 'normal':
case 'online':
return const Color.fromRGBO(26, 188, 156, 1.0);
case 'warning':
case 'alarm':
return const Color.fromRGBO(255, 77, 79, 1.0);
case 'offline':
return const Color.fromRGBO(102, 102, 102, 1.0);
case 'fault':
return const Color.fromRGBO(239, 68, 68, 1.0);
case 'off':
return const Color.fromRGBO(100, 116, 139, 1.0);
default:
return const Color.fromRGBO(100, 116, 139, 1.0);
}
}
}
class InspectionState extends Equatable {
final bool isLoading;
final String? error;
final DeviceStatusTab selectedTab;
final List<String> selectedRooms;
final List<String> allRooms;
final List<InspectionDevice> deviceList;
final List<int> selectedRoomIds;
final Map<int, String> allRooms;
final Map<DeviceStatusTab, int> tabCounts;
const InspectionState({
this.isLoading = false,
this.error,
this.selectedTab = DeviceStatusTab.total,
this.selectedRooms = const [],
this.allRooms = const [],
this.deviceList = const [],
this.selectedRoomIds = const [],
this.allRooms = const {},
this.tabCounts = const {
DeviceStatusTab.total: 0,
DeviceStatusTab.on: 0,
......@@ -55,18 +110,16 @@ class InspectionState extends Equatable {
bool? isLoading,
String? error,
DeviceStatusTab? selectedTab,
List<String>? selectedRooms,
List<String>? allRooms,
List<InspectionDevice>? deviceList,
List<int>? selectedRoomIds,
Map<int, String>? allRooms,
Map<DeviceStatusTab, int>? tabCounts,
}) {
return InspectionState(
isLoading: isLoading ?? this.isLoading,
error: this.error,
error: error,
selectedTab: selectedTab ?? this.selectedTab,
selectedRooms: selectedRooms ?? this.selectedRooms,
selectedRoomIds: selectedRoomIds ?? this.selectedRoomIds,
allRooms: allRooms ?? this.allRooms,
deviceList: deviceList ?? this.deviceList,
tabCounts: tabCounts ?? this.tabCounts,
);
}
......@@ -76,9 +129,8 @@ class InspectionState extends Equatable {
isLoading,
error,
selectedTab,
selectedRooms,
selectedRoomIds,
allRooms,
deviceList,
tabCounts,
];
}
\ No newline at end of file
......@@ -2,10 +2,12 @@ import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
import 'package:smart_hotel_app/routes/app_router.gr.dart';
import 'package:smart_hotel_app/views/home/inspection/cubit/inspection_cubit.dart';
import 'package:smart_hotel_app/views/home/inspection/cubit/inspection_state.dart';
import 'package:smart_hotel_app/views/home/inspection/widget/filter_panel.dart';
import 'package:smart_hotel_app/views/home/inspection/widget/device_list.dart';
import 'package:smart_hotel_app/views/home/inspection/widget/filter_panel.dart';
@RoutePage()
class InspectionDetailView extends StatelessWidget {
......@@ -15,38 +17,78 @@ class InspectionDetailView extends StatelessWidget {
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => InspectionCubit(),
child: Scaffold(
backgroundColor: const Color.fromRGBO(242, 243, 245, 1),
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios,
color: Color.fromRGBO(100, 116, 139, 1)),
onPressed: () {
context.popRoute();
},
),
title: Text(
'设备巡检',
style: TextStyle(
color: const Color.fromRGBO(10, 13, 20, 1),
fontSize: 32.sp,
fontWeight: FontWeight.w200,
),
),
centerTitle: true,
),
body: BlocBuilder<InspectionCubit, InspectionState>(
builder: (context, state) {
final cubit = context.read<InspectionCubit>();
child: const _InspectionDetailBody(),
);
}
}
class _InspectionDetailBody extends StatefulWidget {
const _InspectionDetailBody();
@override
State<_InspectionDetailBody> createState() => _InspectionDetailBodyState();
}
if (state.isLoading) {
return const Center(child: CircularProgressIndicator());
}
class _InspectionDetailBodyState extends State<_InspectionDetailBody> {
late final PagingController<int, InspectionDevice> _pagingController;
DeviceStatusTab? _lastTab;
List<int>? _lastRoomIds;
if (state.error != null) {
return Center(
@override
void initState() {
super.initState();
_pagingController = PagingController<int, InspectionDevice>(
getNextPageKey: (state) {
if (state.keys == null || state.keys!.isEmpty) return 1;
return state.lastPageIsEmpty ? null : state.nextIntPageKey;
},
fetchPage: (pageKey) =>
context.read<InspectionCubit>().fetchPage(pageKey),
);
}
void _checkFilterChanged(InspectionState state) {
if (_lastTab != state.selectedTab ||
_lastRoomIds != state.selectedRoomIds) {
_lastTab = state.selectedTab;
_lastRoomIds = state.selectedRoomIds;
_pagingController.refresh();
}
}
@override
void dispose() {
_pagingController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final cubit = context.read<InspectionCubit>();
return BlocListener<InspectionCubit, InspectionState>(
listener: (context, state) {
if (_lastTab != null) {
_checkFilterChanged(state);
}
},
child: BlocBuilder<InspectionCubit, InspectionState>(
builder: (context, state) {
_checkFilterChanged(state);
if (state.isLoading) {
return Scaffold(
backgroundColor: const Color.fromRGBO(242, 243, 245, 1),
appBar: _buildAppBar(context),
body: const Center(child: CircularProgressIndicator()),
);
}
if (state.error != null) {
return Scaffold(
backgroundColor: const Color.fromRGBO(242, 243, 245, 1),
appBar: _buildAppBar(context),
body: Center(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 28.w),
child: Column(
......@@ -56,54 +98,90 @@ class InspectionDetailView extends StatelessWidget {
'加载失败: ${state.error}',
style: TextStyle(
fontSize: 28.sp,
color: const Color.fromRGBO(255, 100, 101, 1),
color:
const Color.fromRGBO(255, 100, 101, 1),
),
textAlign: TextAlign.center,
),
SizedBox(height: 24.h),
ElevatedButton(
onPressed: () => cubit.initData(),
child: Text('重新加载', style: TextStyle(fontSize: 28.sp)),
child: Text('重新加载',
style: TextStyle(fontSize: 28.sp)),
),
],
),
),
);
}
),
);
}
return SingleChildScrollView(
padding: EdgeInsets.symmetric(horizontal: 28.w, vertical: 20.h),
child: Column(
children: [
FilterPanel(
selectedTab: state.selectedTab,
tabCounts: state.tabCounts,
onTabSelected: (tab) => cubit.selectTab(tab),
selectedRooms: state.selectedRooms,
allRooms: state.allRooms,
onRoomToggle: (room) => cubit.toggleRoom(room),
),
SizedBox(height: 20.h),
if (state.deviceList.isEmpty)
Padding(
padding: EdgeInsets.only(top: 40.h),
child: Text(
'暂无设备数据',
style: TextStyle(
fontSize: 28.sp,
color: const Color.fromRGBO(100, 116, 139, 1),
return Scaffold(
backgroundColor: const Color.fromRGBO(242, 243, 245, 1),
appBar: _buildAppBar(context),
body: PagingListener<int, InspectionDevice>(
controller: _pagingController,
builder: (context, pagingState, fetchNextPage) {
return CustomScrollView(
slivers: [
SliverPadding(
padding: EdgeInsets.symmetric(
horizontal: 28.w, vertical: 20.h),
sliver: SliverToBoxAdapter(
child: FilterPanel(
selectedTab: state.selectedTab,
tabCounts: state.tabCounts,
onTabSelected: cubit.selectTab,
selectedRoomIds: state.selectedRoomIds,
allRooms: state.allRooms,
onRoomToggle: cubit.toggleRoom,
),
),
)
else
DeviceList(deviceList: state.deviceList),
SizedBox(height: 30.h),
],
),
);
},
),
SliverToBoxAdapter(
child: SizedBox(height: 20.h),
),
DeviceList(
pagingState: pagingState,
fetchNextPage: fetchNextPage,
onTap: (item, ctx) {
ctx.pushRoute(InspectionDeviceRoute(
deviceId: item.id));
},
),
SliverToBoxAdapter(
child: SizedBox(height: 30.h),
),
],
);
},
),
);
},
),
);
}
PreferredSizeWidget _buildAppBar(BuildContext context) {
return AppBar(
backgroundColor: Colors.white,
elevation: 0,
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios,
color: Color.fromRGBO(100, 116, 139, 1)),
onPressed: () {
context.popRoute();
},
),
title: Text(
'设备巡检',
style: TextStyle(
color: const Color.fromRGBO(10, 13, 20, 1),
fontSize: 32.sp,
fontWeight: FontWeight.w200,
),
),
centerTitle: true,
);
}
}
\ No newline at end of file
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:smart_hotel_app/routes/app_router.gr.dart';
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
import 'package:smart_hotel_app/views/home/inspection/cubit/inspection_state.dart';
import 'package:smart_hotel_app/views/home/inspection/widget/device_list_item.dart';
class DeviceList extends StatelessWidget {
final List<InspectionDevice> deviceList;
final PagingState<int, InspectionDevice> pagingState;
final VoidCallback fetchNextPage;
final void Function(InspectionDevice item, BuildContext context) onTap;
const DeviceList({
super.key,
required this.deviceList,
required this.pagingState,
required this.fetchNextPage,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'设备列表 (${deviceList.length})',
style: TextStyle(
fontSize: 28.sp,
fontWeight: FontWeight.bold,
color: const Color.fromRGBO(10, 13, 20, 1.0),
return SliverPadding(
padding: EdgeInsets.symmetric(horizontal: 28.w),
sliver: PagedSliverList<int, InspectionDevice>(
state: pagingState,
fetchNextPage: fetchNextPage,
builderDelegate: PagedChildBuilderDelegate<InspectionDevice>(
firstPageErrorIndicatorBuilder: (_) => Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'加载失败: ${pagingState.error}',
style: TextStyle(
fontSize: 28.sp,
color: const Color.fromRGBO(255, 100, 101, 1),
),
textAlign: TextAlign.center,
),
SizedBox(height: 24.h),
ElevatedButton(
onPressed: fetchNextPage,
child:
Text('重新加载', style: TextStyle(fontSize: 28.sp)),
),
],
),
),
),
SizedBox(height: 12.h),
ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: deviceList.length,
separatorBuilder: (context, index) => SizedBox(height: 12.h),
itemBuilder: (context, index) {
noItemsFoundIndicatorBuilder: (_) => Padding(
padding: EdgeInsets.only(top: 60.h),
child: Text(
'暂无设备数据',
style: TextStyle(
fontSize: 28.sp,
color: const Color.fromRGBO(100, 116, 139, 1),
),
),
),
newPageErrorIndicatorBuilder: (_) => Center(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 20.h),
child: Column(
children: [
Text(
'加载失败: ${pagingState.error}',
style: TextStyle(
fontSize: 24.sp,
color: const Color.fromRGBO(255, 100, 101, 1),
),
),
SizedBox(height: 12.h),
TextButton(
onPressed: fetchNextPage,
child: Text('重试', style: TextStyle(fontSize: 24.sp)),
),
],
),
),
),
itemBuilder: (_, item, __) {
return DeviceListItem(
device: deviceList[index],
onTap: () {
context.pushRoute(
InspectionDeviceRoute(deviceId: deviceList[index].id),
);
},
device: item,
onTap: () => onTap(item, context),
);
},
),
],
),
);
}
}
}
\ No newline at end of file
......@@ -6,16 +6,16 @@ class FilterPanel extends StatelessWidget {
final DeviceStatusTab selectedTab;
final Map<DeviceStatusTab, int> tabCounts;
final Function(DeviceStatusTab) onTabSelected;
final List<String> selectedRooms;
final List<String> allRooms;
final Function(String) onRoomToggle;
final List<int> selectedRoomIds;
final Map<int, String> allRooms;
final Function(int) onRoomToggle;
const FilterPanel({
super.key,
required this.selectedTab,
required this.tabCounts,
required this.onTabSelected,
required this.selectedRooms,
required this.selectedRoomIds,
required this.allRooms,
required this.onRoomToggle,
});
......@@ -126,6 +126,7 @@ class FilterPanel extends StatelessWidget {
}
Widget _buildRoomSelector() {
final roomEntries = allRooms.entries.toList();
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
......@@ -138,7 +139,7 @@ class FilterPanel extends StatelessWidget {
),
),
SizedBox(height: 16.h),
if (allRooms.isEmpty)
if (roomEntries.isEmpty)
Text(
'暂无房间数据',
style: TextStyle(
......@@ -150,10 +151,10 @@ class FilterPanel extends StatelessWidget {
Wrap(
spacing: 5.w,
runSpacing: 12.h,
children: allRooms.map((room) {
final isSelected = selectedRooms.contains(room);
children: roomEntries.map((entry) {
final isSelected = selectedRoomIds.contains(entry.key);
return GestureDetector(
onTap: () => onRoomToggle(room),
onTap: () => onRoomToggle(entry.key),
child: Container(
width: 120.w,
padding: EdgeInsets.symmetric(horizontal: 28.w, vertical: 12.h),
......@@ -170,7 +171,7 @@ class FilterPanel extends StatelessWidget {
),
),
child: Text(
room,
entry.value,
style: TextStyle(
fontSize: 24.sp,
color: isSelected
......
# 智慧酒店 App 接口对接方案
# 智慧酒店 App 接口对接方案
......@@ -195,11 +195,21 @@ class xxxBO extends Equatable {
| 设备巡检-房间 |GET | /app/device/inspection/rooms | 已对接 | 告警列表 | 相关页面:FilterPanel |
| 设备巡检-设备列表 |GET | /app/device/inspection | 未对接 | 告警列表 | 相关页面:DeviceList |
| 设备巡检-设备列表 |GET | /app/device/inspection | 已对接 | 设备巡检 | 相关页面:DeviceList |
| 设备拓扑图 |GET | /app/device/detail | 未对接 | 设备拓扑图 | 相关页面:InspectionDeviceView |
| 设备拓扑图-开始巡检 |POST | /app/device/inspection/start | 未对接 | 设备拓扑图-开始巡检 | 相关页面:InspectionDeviceView |
| 巡检记录 |GET | /app/device/inspection/list | 未对接 | 设备拓扑图 | 相关页面:InspectionHistoryView |
| 获取当前登录用户信息 |GET | /app/auth/user-info | 未对接 | 获取当前登录用户信息 | |
| 设备拓扑图 |GET | /app/device/detail | 未对接 | 设备拓扑图 | 相关页面:DeviceList |
| 设备拓扑图-开始巡检 |GET | /app/device/inspection/start | 未对接 | 设备拓扑图-开始巡检 | 相关页面:DeviceList |
......@@ -574,6 +584,7 @@ class xxxBO extends Equatable {
```
求体:
{
"roomId":0,
"pageSize": 20,
"pageNum": 1
}
......@@ -674,6 +685,87 @@ class xxxBO extends Equatable {
```
#### 巡检记录
**GET /app/device/inspection/list**
```
求体:
{
"pageSize": 10,
"pageNum": 1,
"deviceId": 0
}
应 data:
{
"code": 0,
"msg": "string",
"data": {
"total": 0,
"tabCount": { // 各Tab对应的巡检数量统计,用于顶部Tab标签右侧的数字角标(如「总计(6) 通过(3) 警告(1) 不通过(2)」),一次性返回4种状态的数量避免多次请求
"total": 0,
"passCount": 0,
"warnCount": 0,
"failCount": 0
},
"deviceInfo": { // 当前查询目标设备的基础信息
"deviceId": 0,
"deviceName": "string",
"deviceIcon": "string"
},
"rows": [ // 巡检记录行列表
{
"inspectId": 0,
"inspectorName": "string",
"inspectTime": "string",
"inspectStatus": "string",
"remark": "string",
"itemsDetail": [ // 巡检项明细列表
{
"name": "string",
"value": "string",
"status": "string"
}
]
}
]
}
}
```
#### 获取当前登录用户信息
**GET /app/auth/user-info**
```
求体:
{
}
应 data:
{
"code": 0,
"msg": "string",
"data": {
"userId": 0,
"userName": "string",
"nickName": "string",
"phonenumber": "string",
"email": "string",
"sex": "string",
"avatarUrl": "string",
"deptName": "string",
"tenantId": "string"
}
}
```
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment