Commit a8c199a0 authored by 张宏's avatar 张宏

111

parent 93ea6fa5
import 'package:equatable/equatable.dart';
// ============================================================
// 客房状态总览-客房概览统计 BO 模型
// ============================================================
/// 概览统计(顶部统计区域)
class RoomOverviewSummaryBO extends Equatable {
final int occupiedCount;
final int vacantCount;
final int reservedCount;
final int alarmCount;
const RoomOverviewSummaryBO({
this.occupiedCount = 0,
this.vacantCount = 0,
this.reservedCount = 0,
this.alarmCount = 0,
});
factory RoomOverviewSummaryBO.fromJson(Map<String, dynamic> json) {
return RoomOverviewSummaryBO(
occupiedCount: json['occupiedCount'] as int? ?? 0,
vacantCount: json['vacantCount'] as int? ?? 0,
reservedCount: json['reservedCount'] as int? ?? 0,
alarmCount: json['alarmCount'] as int? ?? 0,
);
}
@override
List<Object?> get props => [occupiedCount, vacantCount, reservedCount, alarmCount];
}
/// 楼层Tab列表
class RoomFloorBO extends Equatable {
final int floorId;
final String floorName;
final bool isActive;
const RoomFloorBO({
this.floorId = 0,
this.floorName = '',
this.isActive = false,
});
factory RoomFloorBO.fromJson(Map<String, dynamic> json) {
return RoomFloorBO(
floorId: json['floorId'] as int? ?? 0,
floorName: json['floorName'] as String? ?? '',
isActive: json['isActive'] as bool? ?? false,
);
}
@override
List<Object?> get props => [floorId, floorName, isActive];
}
/// 房间网格(房间卡片网格布局)
class RoomGridBO extends Equatable {
final int roomId;
final String roomNumber;
final String statusText;
final String statusColor;
final bool hasAlarmIcon;
const RoomGridBO({
this.roomId = 0,
this.roomNumber = '',
this.statusText = '',
this.statusColor = '',
this.hasAlarmIcon = false,
});
factory RoomGridBO.fromJson(Map<String, dynamic> json) {
return RoomGridBO(
roomId: json['roomId'] as int? ?? 0,
roomNumber: json['roomNumber'] as String? ?? '',
statusText: json['statusText'] as String? ?? '',
statusColor: json['statusColor'] as String? ?? '',
hasAlarmIcon: json['hasAlarmIcon'] as bool? ?? false,
);
}
@override
List<Object?> get props => [roomId, roomNumber, statusText, statusColor, hasAlarmIcon];
}
/// 房间基础信息(客房概览统计)
class RoomOverviewBasicInfoBO extends Equatable {
final int roomId;
final String roomNumber;
final String roomStatusText;
const RoomOverviewBasicInfoBO({
this.roomId = 0,
this.roomNumber = '',
this.roomStatusText = '',
});
factory RoomOverviewBasicInfoBO.fromJson(Map<String, dynamic> json) {
return RoomOverviewBasicInfoBO(
roomId: json['roomId'] as int? ?? 0,
roomNumber: json['roomNumber'] as String? ?? '',
roomStatusText: json['roomStatusText'] as String? ?? '',
);
}
@override
List<Object?> get props => [roomId, roomNumber, roomStatusText];
}
/// 状态标签
class StatusTagsBO extends Equatable {
final bool isOccupied;
final bool powerSupplyStatus;
final bool wifiNormalStatus;
const StatusTagsBO({
this.isOccupied = false,
this.powerSupplyStatus = false,
this.wifiNormalStatus = false,
});
factory StatusTagsBO.fromJson(Map<String, dynamic> json) {
return StatusTagsBO(
isOccupied: json['isOccupied'] as bool? ?? false,
powerSupplyStatus: json['powerSupplyStatus'] as bool? ?? false,
wifiNormalStatus: json['wifiNormalStatus'] as bool? ?? false,
);
}
@override
List<Object?> get props => [isOccupied, powerSupplyStatus, wifiNormalStatus];
}
/// 房间设备项
class DeviceItemBO extends Equatable {
final int deviceId;
final String deviceName;
final String deviceTypeIcon;
final String powerStatus;
final String powerStatusText;
final String temperatureDisplay;
final String paramDisplayText;
const DeviceItemBO({
this.deviceId = 0,
this.deviceName = '',
this.deviceTypeIcon = '',
this.powerStatus = '',
this.powerStatusText = '',
this.temperatureDisplay = '',
this.paramDisplayText = '',
});
factory DeviceItemBO.fromJson(Map<String, dynamic> json) {
return DeviceItemBO(
deviceId: json['deviceId'] as int? ?? 0,
deviceName: json['deviceName'] as String? ?? '',
deviceTypeIcon: json['deviceTypeIcon'] as String? ?? '',
powerStatus: json['powerStatus'] as String? ?? '',
powerStatusText: json['powerStatusText'] as String? ?? '',
temperatureDisplay: json['temperatureDisplay'] as String? ?? '',
paramDisplayText: json['paramDisplayText'] as String? ?? '',
);
}
@override
List<Object?> get props => [
deviceId,
deviceName,
deviceTypeIcon,
powerStatus,
powerStatusText,
temperatureDisplay,
paramDisplayText,
];
}
/// 房间详情列表项
class RoomDetailListBO extends Equatable {
final RoomOverviewBasicInfoBO roomBasicInfo;
final StatusTagsBO statusTags;
final List<DeviceItemBO> deviceList;
const RoomDetailListBO({
required this.roomBasicInfo,
required this.statusTags,
this.deviceList = const [],
});
factory RoomDetailListBO.fromJson(Map<String, dynamic> json) {
return RoomDetailListBO(
roomBasicInfo: RoomOverviewBasicInfoBO.fromJson(json['roomBasicInfo'] as Map<String, dynamic>? ?? {}),
statusTags: StatusTagsBO.fromJson(json['statusTags'] as Map<String, dynamic>? ?? {}),
deviceList: (json['deviceList'] as List<dynamic>?)
?.map((e) => DeviceItemBO.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
);
}
@override
List<Object?> get props => [roomBasicInfo, statusTags, deviceList];
}
/// 客房概览统计根对象
class RoomOverviewBO extends Equatable {
final RoomOverviewSummaryBO summary;
final List<RoomFloorBO> floorList;
final List<RoomGridBO> roomGrid;
final List<RoomDetailListBO> roomDetailList;
const RoomOverviewBO({
required this.summary,
this.floorList = const [],
this.roomGrid = const [],
this.roomDetailList = const [],
});
factory RoomOverviewBO.fromJson(Map<String, dynamic> json) {
return RoomOverviewBO(
summary: RoomOverviewSummaryBO.fromJson(json['summary'] as Map<String, dynamic>? ?? {}),
floorList: (json['floorList'] as List<dynamic>?)
?.map((e) => RoomFloorBO.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
roomGrid: (json['roomGrid'] as List<dynamic>?)
?.map((e) => RoomGridBO.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
roomDetailList: (json['roomDetailList'] as List<dynamic>?)
?.map((e) => RoomDetailListBO.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
);
}
@override
List<Object?> get props => [summary, floorList, roomGrid, roomDetailList];
}
int _parseInt(dynamic value, {int fallback = 0}) {
if (value == null) return fallback;
if (value is int) return value;
......
......@@ -6,6 +6,7 @@ class AlertDashboardRepository {
Future<ResponseModel<AlertDashboardBO>> getDashboard() {
return DioRequest.instance.get<AlertDashboardBO>(
'/app/alert/dashboard',
queryParameters: {'alertStatus': 0,'pageNum':1,'pageSize':5}, // 处理状态:0=待处理 1=处理中 2=已处理 3=已忽略 4=误报关闭
fromJsonT: (data) =>
AlertDashboardBO.fromJson(data as Map<String, dynamic>),
);
......
......@@ -81,6 +81,15 @@ class RoomRepository {
);
}
/// 客房状态总览-客房概览统计
Future<ResponseModel<RoomOverviewBO>> getOverview(int? floorId) {
return DioRequest.instance.get<RoomOverviewBO>(
'/app/room/overview',
queryParameters: {'floorId': floorId == 0 ? null : floorId},
fromJsonT: (data) => RoomOverviewBO.fromJson(data as Map<String, dynamic>),
);
}
/// 非客房-相关楼层区域设备
Future<ResponseModel<List<AreaDeviceBO>>> getAreaDevices({
required int floorId,
......
......@@ -73,6 +73,15 @@ class RoomService {
}
}
/// 客房状态总览-客房概览统计
Future<RoomOverviewBO> getOverview(int? floorId) async {
final result = await _repository.getOverview(floorId);
if (result.success && result.data != null) {
return result.data!;
}
throw Exception(result.msg);
}
/// 非客房-相关楼层区域设备
Future<List<AreaDeviceBO>> getAreaDevices({
required int floorId,
......
......@@ -69,7 +69,7 @@ class _ReportDeviceListBodyState extends State<_ReportDeviceListBody> {
backgroundColor: Colors.white,
elevation: 0,
leading: IconButton(
icon: const Icon(Icons.arrow_back, color: Colors.black),
icon: const Icon(Icons.arrow_back_ios, color: Colors.black),
onPressed: () {
context.popRoute();
},
......
......@@ -22,7 +22,7 @@ class ReportEnergyDetailView extends StatelessWidget {
backgroundColor: Colors.white,
elevation: 0,
leading: IconButton(
icon: const Icon(Icons.arrow_back, color: Colors.black87),
icon: const Icon(Icons.arrow_back_ios, color: Colors.black87),
onPressed: () {
context.popRoute();
},
......
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:smart_hotel_app/repositories/room_repository.dart';
import 'package:smart_hotel_app/services/room_service.dart';
import 'package:smart_hotel_app/views/report/room/cubit/room_report_state.dart';
class RoomReportCubit extends Cubit<RoomReportState> {
RoomReportCubit() : super(const RoomReportState()) {
_initData();
}
void _initData() {
final floors = [
FloorInfo(
floorName: '一楼',
rooms: List.generate(10, (index) => _createRoom(101 + index)),
),
FloorInfo(
floorName: '二楼',
rooms: List.generate(10, (index) => _createRoom(201 + index)),
),
FloorInfo(
floorName: '三楼',
rooms: List.generate(10, (index) => _createRoom(301 + index)),
),
FloorInfo(
floorName: '四楼',
rooms: List.generate(10, (index) => _createRoom(401 + index)),
),
FloorInfo(
floorName: '五楼',
rooms: List.generate(10, (index) => _createRoom(501 + index)),
),
FloorInfo(
floorName: '六楼',
rooms: List.generate(10, (index) => _createRoom(601 + index)),
),
];
final statusStats = [
const StatusStat(
label: '入住',
count: 45,
color: Color.fromRGBO(59, 130, 246, 1),
),
const StatusStat(
label: '空闲',
count: 7,
color: Color.fromRGBO(156, 163, 175, 1),
),
const StatusStat(
label: '预订',
count: 5,
color: Color.fromRGBO(147, 197, 253, 1),
),
const StatusStat(
label: '告警',
count: 3,
color: Color.fromRGBO(251, 191, 36, 1),
),
];
final RoomService _service;
final detailRooms = [
RoomInfo(
roomNumber: '302',
status: RoomStatus.occupied,
statusText: '入住中',
deviceStatus: const RoomDeviceStatus(
hasPerson: true,
powerOn: true,
wifiNormal: true,
acOn: true,
acTemperature: '24',
lightOn: false,
tvOn: true,
curtainOn: false,
),
),
];
emit(state.copyWith(
floors: floors,
statusStats: statusStats,
detailRooms: detailRooms,
));
RoomReportCubit({RoomService? service})
: _service = service ?? RoomService(repository: RoomRepository()),
super(const RoomReportState()) {
loadData();
}
RoomInfo _createRoom(int number) {
RoomStatus status;
String statusText;
if (number == 303) {
status = RoomStatus.warning;
statusText = '告警';
} else if (number == 309) {
status = RoomStatus.temperature;
statusText = '温度';
} else if ([302, 304, 308].contains(number)) {
status = RoomStatus.occupied;
statusText = number == 308 ? '入住·勿扰' : '入住·在房';
} else if (number == 307) {
status = RoomStatus.reserved;
statusText = '预定';
} else {
status = RoomStatus.vacant;
statusText = '空闲';
Future<void> loadData({int? floorId}) async {
emit(state.copyWith(isLoading: true, error: null));
try {
final overview = await _service.getOverview(floorId);
final activeIndex = overview.floorList.indexWhere((f) => f.isActive);
emit(state.copyWith(
isLoading: false,
summary: overview.summary,
floors: overview.floorList,
selectedFloorIndex: activeIndex >= 0 ? activeIndex : 0,
roomGrid: overview.roomGrid,
detailRooms: overview.roomDetailList,
));
} catch (e) {
emit(state.copyWith(isLoading: false, error: e.toString()));
}
return RoomInfo(
roomNumber: number.toString(),
status: status,
statusText: statusText,
);
}
void selectFloor(int index) {
final floorId = state.floors[index].floorId;
emit(state.copyWith(selectedFloorIndex: index));
loadData(floorId: floorId);
}
}
\ No newline at end of file
}
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
enum RoomStatus {
vacant,
occupied,
reserved,
warning,
temperature,
}
class RoomDeviceStatus {
final bool hasPerson;
final bool powerOn;
final bool wifiNormal;
final bool acOn;
final String acTemperature;
final bool lightOn;
final bool tvOn;
final bool curtainOn;
const RoomDeviceStatus({
this.hasPerson = true,
this.powerOn = true,
this.wifiNormal = true,
this.acOn = false,
this.acTemperature = '24',
this.lightOn = false,
this.tvOn = false,
this.curtainOn = false,
});
RoomDeviceStatus copyWith({
bool? hasPerson,
bool? powerOn,
bool? wifiNormal,
bool? acOn,
String? acTemperature,
bool? lightOn,
bool? tvOn,
bool? curtainOn,
}) {
return RoomDeviceStatus(
hasPerson: hasPerson ?? this.hasPerson,
powerOn: powerOn ?? this.powerOn,
wifiNormal: wifiNormal ?? this.wifiNormal,
acOn: acOn ?? this.acOn,
acTemperature: acTemperature ?? this.acTemperature,
lightOn: lightOn ?? this.lightOn,
tvOn: tvOn ?? this.tvOn,
curtainOn: curtainOn ?? this.curtainOn,
);
}
}
class RoomInfo {
final String roomNumber;
final RoomStatus status;
final String statusText;
final RoomDeviceStatus deviceStatus;
const RoomInfo({
required this.roomNumber,
required this.status,
required this.statusText,
this.deviceStatus = const RoomDeviceStatus(),
});
RoomInfo copyWith({
String? roomNumber,
RoomStatus? status,
String? statusText,
RoomDeviceStatus? deviceStatus,
}) {
return RoomInfo(
roomNumber: roomNumber ?? this.roomNumber,
status: status ?? this.status,
statusText: statusText ?? this.statusText,
deviceStatus: deviceStatus ?? this.deviceStatus,
);
}
}
class FloorInfo {
final String floorName;
final List<RoomInfo> rooms;
const FloorInfo({
required this.floorName,
required this.rooms,
});
FloorInfo copyWith({
String? floorName,
List<RoomInfo>? rooms,
}) {
return FloorInfo(
floorName: floorName ?? this.floorName,
rooms: rooms ?? this.rooms,
);
}
}
class StatusStat {
final String label;
final int count;
final Color color;
const StatusStat({
required this.label,
required this.count,
required this.color,
});
}
import 'package:smart_hotel_app/models/bo/room_bo.dart';
class RoomReportState extends Equatable {
final List<FloorInfo> floors;
final bool isLoading;
final String? error;
final RoomOverviewSummaryBO summary;
final List<RoomFloorBO> floors;
final int selectedFloorIndex;
final List<StatusStat> statusStats;
final List<RoomInfo> detailRooms;
final List<RoomGridBO> roomGrid;
final List<RoomDetailListBO> detailRooms;
const RoomReportState({
this.isLoading = false,
this.error,
this.summary = const RoomOverviewSummaryBO(),
this.floors = const [],
this.selectedFloorIndex = 2,
this.statusStats = const [],
this.selectedFloorIndex = 0,
this.roomGrid = const [],
this.detailRooms = const [],
});
RoomReportState copyWith({
List<FloorInfo>? floors,
bool? isLoading,
String? error,
RoomOverviewSummaryBO? summary,
List<RoomFloorBO>? floors,
int? selectedFloorIndex,
List<StatusStat>? statusStats,
List<RoomInfo>? detailRooms,
List<RoomGridBO>? roomGrid,
List<RoomDetailListBO>? detailRooms,
bool clearError = false,
}) {
return RoomReportState(
isLoading: isLoading ?? this.isLoading,
error: clearError ? null : (error ?? this.error),
summary: summary ?? this.summary,
floors: floors ?? this.floors,
selectedFloorIndex: selectedFloorIndex ?? this.selectedFloorIndex,
statusStats: statusStats ?? this.statusStats,
roomGrid: roomGrid ?? this.roomGrid,
detailRooms: detailRooms ?? this.detailRooms,
);
}
@override
List<Object?> get props => [
isLoading,
error,
summary,
floors,
selectedFloorIndex,
statusStats,
roomGrid,
detailRooms,
];
}
\ No newline at end of file
}
......@@ -2,6 +2,8 @@ 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:smart_hotel_app/repositories/room_repository.dart';
import 'package:smart_hotel_app/services/room_service.dart';
import 'package:smart_hotel_app/views/report/room/cubit/room_report_cubit.dart';
import 'package:smart_hotel_app/views/report/room/cubit/room_report_state.dart';
import 'package:smart_hotel_app/views/report/room/widget/floor_selector.dart';
......@@ -16,22 +18,72 @@ class ReportRoomDetailView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => RoomReportCubit(),
create: (_) => RoomReportCubit(
service: RoomService(repository: RoomRepository()),
),
child: BlocBuilder<RoomReportCubit, RoomReportState>(
builder: (context, state) {
if (state.isLoading && state.floors.isEmpty) {
return 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: Colors.black),
onPressed: () => context.maybePop(),
),
title: Text(
'客房状态总览',
style: TextStyle(
color: Colors.black,
fontSize: 32.sp,
fontWeight: FontWeight.bold,
),
),
centerTitle: true,
),
body: const Center(child: CircularProgressIndicator()),
);
}
if (state.error != null && state.floors.isEmpty) {
return 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: Colors.black),
onPressed: () => context.maybePop(),
),
title: Text(
'客房状态总览',
style: TextStyle(
color: Colors.black,
fontSize: 32.sp,
fontWeight: FontWeight.bold,
),
),
centerTitle: true,
),
body: Center(child: Text('加载失败: ${state.error}')),
);
}
return Scaffold(
backgroundColor: const Color.fromRGBO(242, 243, 245, 1),
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
leading: IconButton(
icon: const Icon(Icons.arrow_back, color: Colors.black),
icon: const Icon(Icons.arrow_back_ios, color: Colors.black),
onPressed: () {
context.popRoute();
context.maybePop();
},
),
title: Text(
'客房详情',
'客房状态总览',
style: TextStyle(
color: Colors.black,
fontSize: 32.sp,
......@@ -66,12 +118,16 @@ class ReportRoomDetailView extends StatelessWidget {
borderRadius: BorderRadius.vertical(bottom: Radius.circular(24.r)),
),
padding: EdgeInsets.only(bottom: 28.w),
child: RoomGrid(rooms: state.floors[state.selectedFloorIndex].rooms),
child: RoomGrid(rooms: state.roomGrid),
),
SizedBox(height: 10.h),
if (state.statusStats.isNotEmpty)
StatusStats(stats: state.statusStats),
StatusStats(summary: state.summary),
SizedBox(height: 10.h),
if (state.isLoading)
const Padding(
padding: EdgeInsets.all(16.0),
child: Center(child: CircularProgressIndicator()),
),
...state.detailRooms.map((room) {
return Padding(
padding: EdgeInsets.only(bottom: 10.h),
......
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:smart_hotel_app/views/report/room/cubit/room_report_state.dart';
import 'package:smart_hotel_app/models/bo/room_bo.dart';
class RoomDetailCard extends StatelessWidget {
final RoomInfo room;
final RoomDetailListBO room;
const RoomDetailCard({super.key, required this.room});
......@@ -41,7 +41,7 @@ class RoomDetailCard extends StatelessWidget {
Row(
children: [
Text(
room.roomNumber,
room.roomBasicInfo.roomNumber,
style: TextStyle(
fontSize: 36.sp,
fontWeight: FontWeight.bold,
......@@ -50,7 +50,7 @@ class RoomDetailCard extends StatelessWidget {
),
SizedBox(width: 16.w),
Text(
'入住中',
room.roomBasicInfo.roomStatusText,
style: TextStyle(
fontSize: 28.sp,
color: const Color.fromRGBO(59, 130, 246, 1),
......@@ -63,17 +63,17 @@ class RoomDetailCard extends StatelessWidget {
children: [
_StatusTag(
label: '有人',
isActive: room.deviceStatus.hasPerson,
isActive: room.statusTags.isOccupied,
),
SizedBox(width: 12.w),
_StatusTag(
label: '通电中',
isActive: room.deviceStatus.powerOn,
isActive: room.statusTags.powerSupplyStatus,
),
SizedBox(width: 12.w),
_StatusTag(
label: 'wifi正常',
isActive: room.deviceStatus.wifiNormal,
isActive: room.statusTags.wifiNormalStatus,
),
],
),
......@@ -93,32 +93,7 @@ class RoomDetailCard extends StatelessWidget {
crossAxisCount: 2,
mainAxisSpacing: 16.h,
crossAxisSpacing: 16.w,
children: [
_DeviceControl(
icon: Icons.air_outlined,
label: '空调',
value: '${room.deviceStatus.acTemperature}°C·${room.deviceStatus.acOn ? '开' : '关'}',
isOn: room.deviceStatus.acOn,
),
_DeviceControl(
icon: Icons.light_outlined,
label: '照明',
value: room.deviceStatus.lightOn ? '开' : '关',
isOn: room.deviceStatus.lightOn,
),
_DeviceControl(
icon: Icons.tv_outlined,
label: '电视',
value: room.deviceStatus.tvOn ? '开' : '关',
isOn: room.deviceStatus.tvOn,
),
_DeviceControl(
icon: Icons.curtains_outlined,
label: '窗帘',
value: room.deviceStatus.curtainOn ? '开' : '关',
isOn: room.deviceStatus.curtainOn,
),
],
children: room.deviceList.map((device) => _DeviceControl(device: device)).toList(),
),
],
),
......@@ -169,20 +144,28 @@ class _StatusTag extends StatelessWidget {
}
class _DeviceControl extends StatelessWidget {
final IconData icon;
final String label;
final String value;
final bool isOn;
final DeviceItemBO device;
const _DeviceControl({
required this.icon,
required this.label,
required this.value,
required this.isOn,
});
const _DeviceControl({required this.device});
IconData _icon() {
switch (device.deviceTypeIcon) {
case 'ac':
return Icons.air_outlined;
case 'light':
return Icons.light_outlined;
case 'tv':
return Icons.tv_outlined;
case 'curtain':
return Icons.curtains_outlined;
default:
return Icons.devices_other;
}
}
@override
Widget build(BuildContext context) {
final isOn = device.powerStatus == '1';
return Container(
padding: EdgeInsets.symmetric(horizontal: 24.w, vertical: 24.h),
decoration: BoxDecoration(
......@@ -192,13 +175,13 @@ class _DeviceControl extends StatelessWidget {
child: Row(
children: [
Icon(
icon,
_icon(),
color: const Color.fromRGBO(59, 130, 246, 1),
size: 40.sp,
),
SizedBox(width: 16.w),
Text(
label,
device.deviceName,
style: TextStyle(
fontSize: 28.sp,
color: const Color.fromRGBO(59, 130, 246, 1),
......@@ -207,7 +190,7 @@ class _DeviceControl extends StatelessWidget {
),
const Spacer(),
Text(
value,
device.paramDisplayText.isNotEmpty ? device.paramDisplayText : device.powerStatusText,
style: TextStyle(
fontSize: 26.sp,
color: isOn
......
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:smart_hotel_app/views/report/room/cubit/room_report_state.dart';
import 'package:smart_hotel_app/models/bo/room_bo.dart';
class RoomGrid extends StatelessWidget {
final List<RoomInfo> rooms;
final List<RoomGridBO> rooms;
const RoomGrid({super.key, required this.rooms});
......@@ -18,53 +18,64 @@ class RoomGrid extends StatelessWidget {
crossAxisCount: 5,
mainAxisSpacing: 20.h,
crossAxisSpacing: 16.w,
children: rooms.map((room) => RoomItem(room: room)).toList(),
children: rooms.map((room) => _RoomItem(room: room)).toList(),
),
);
}
}
class RoomItem extends StatelessWidget {
final RoomInfo room;
class _RoomItem extends StatelessWidget {
final RoomGridBO room;
const RoomItem({super.key, required this.room});
const _RoomItem({required this.room});
Color get _bgColor {
switch (room.status) {
case RoomStatus.vacant:
return const Color.fromRGBO(229, 231, 235, 1);
case RoomStatus.occupied:
case RoomStatus.reserved:
switch (room.statusColor) {
case 'occupied':
return const Color.fromRGBO(219, 234, 254, 1);
case 'alarm':
return const Color.fromRGBO(254, 243, 199, 1);
case 'reserved':
return const Color.fromRGBO(219, 234, 254, 1);
case RoomStatus.warning:
case RoomStatus.temperature:
case 'dnd':
return const Color.fromRGBO(254, 243, 199, 1);
case 'checkout':
case 'idle':
default:
return const Color.fromRGBO(229, 231, 235, 1);
}
}
Color get _textColor {
switch (room.status) {
case RoomStatus.vacant:
return const Color.fromRGBO(156, 163, 175, 1);
case RoomStatus.occupied:
case RoomStatus.reserved:
switch (room.statusColor) {
case 'occupied':
return const Color.fromRGBO(59, 130, 246, 1);
case RoomStatus.warning:
case RoomStatus.temperature:
case 'alarm':
return const Color.fromRGBO(245, 158, 11, 1);
case 'reserved':
return const Color.fromRGBO(59, 130, 246, 1);
case 'dnd':
return const Color.fromRGBO(245, 158, 11, 1);
case 'checkout':
case 'idle':
default:
return const Color.fromRGBO(156, 163, 175, 1);
}
}
Color? get _borderColor {
switch (room.status) {
case RoomStatus.vacant:
switch (room.statusColor) {
case 'idle':
case 'checkout':
return null;
case RoomStatus.occupied:
case RoomStatus.reserved:
case 'occupied':
case 'reserved':
return const Color.fromRGBO(59, 130, 246, 1);
case RoomStatus.warning:
case RoomStatus.temperature:
case 'alarm':
case 'dnd':
return const Color.fromRGBO(245, 158, 11, 1);
default:
return null;
}
}
......@@ -91,9 +102,9 @@ class RoomItem extends StatelessWidget {
),
),
SizedBox(height: 4.h),
if (room.status == RoomStatus.warning || room.status == RoomStatus.temperature)
if (room.hasAlarmIcon)
Icon(Icons.warning_amber_outlined, color: _textColor, size: 20.sp),
if (room.status != RoomStatus.warning && room.status != RoomStatus.temperature)
if (!room.hasAlarmIcon)
Text(
room.statusText,
style: TextStyle(
......
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:smart_hotel_app/views/report/room/cubit/room_report_state.dart';
import 'package:smart_hotel_app/models/bo/room_bo.dart';
class StatusStats extends StatelessWidget {
final List<StatusStat> stats;
final RoomOverviewSummaryBO summary;
const StatusStats({super.key, required this.stats});
const StatusStats({super.key, required this.summary});
@override
Widget build(BuildContext context) {
final items = [
_StatItemData(label: '入住', count: summary.occupiedCount, color: const Color(0xFF4A7CF7)),
_StatItemData(label: '空闲', count: summary.vacantCount, color: const Color(0xFF4CAF50)),
_StatItemData(label: '预定', count: summary.reservedCount, color: const Color(0xFF9C27B0)),
_StatItemData(label: '告警', count: summary.alarmCount, color: const Color(0xFFFF9800)),
];
return Container(
width: double.infinity,
padding: EdgeInsets.all(28.w),
......@@ -18,32 +25,39 @@ class StatusStats extends StatelessWidget {
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: stats.map((stat) => StatItem(stat: stat)).toList(),
children: items.map((item) => _StatItem(item: item)).toList(),
),
);
}
}
class StatItem extends StatelessWidget {
final StatusStat stat;
class _StatItemData {
final String label;
final int count;
final Color color;
const _StatItemData({required this.label, required this.count, required this.color});
}
class _StatItem extends StatelessWidget {
final _StatItemData item;
const StatItem({super.key, required this.stat});
const _StatItem({required this.item});
@override
Widget build(BuildContext context) {
return Column(
children: [
Text(
stat.count.toString(),
item.count.toString(),
style: TextStyle(
fontSize: 40.sp,
fontWeight: FontWeight.bold,
color: stat.color,
color: item.color,
),
),
SizedBox(height: 4.h),
Text(
stat.label,
item.label,
style: TextStyle(
fontSize: 24.sp,
color: const Color.fromRGBO(156, 163, 175, 1),
......
......@@ -22,7 +22,7 @@ class ReportRuleManagementView extends StatelessWidget {
backgroundColor: Colors.white,
elevation: 0,
leading: IconButton(
icon: const Icon(Icons.arrow_back, color: Colors.black),
icon: const Icon(Icons.arrow_back_ios, color: Colors.black),
onPressed: () {
context.popRoute();
},
......
# 智慧酒店 App 接口对接方案
# 智慧酒店 App 接口对接方案
......@@ -241,13 +241,109 @@ class xxxBO extends Equatable {
| 客房服务看板-统计 |GET | /app/room/statistics | 已对接 | 客房服务看板-统计 | |
| 非客房-相关楼层区域设备 |GET | /app/room/area/devices | 未对接 | 非客房-相关楼层区域设备 | |
| 非客房-相关楼层区域设备 |GET | /app/room/area/devices | 已对接 | 非客房-相关楼层区域设备 | |
| 客房状态总览-客房概览统计 |GET | /app/room/overview | 已对接 | 客房状态总览-客房概览统计 | |
### 3.2 接口详细定义
#### 客房状态总览-客房概览统计
**GET /app/room/overview**
```
求体:
{
"floorId":0
}
应 data:
{
"code": 0,
"msg": "string",
"data": {
"summary": { // 顶部统计区域(页面中间一排数字卡片:45入住 7空闲 5披订 3告警)
"occupiedCount": 0, // 入住数量
"vacantCount": 0, // 空闲数量
"reservedCount": 0, // 预定数量
"alarmCount": 0 // 告警数量
},
"floorList": [ // 楼层Tab列表
{
"floorId": 0,
"floorName": "string",
"isActive": true
}
],
"roomGrid": [ // 房间网格区域(页面主体上半部分的房间卡片网格布局,每格显示房间号+状态文字+颜色背景),按楼层分组展示
{
"roomId": 0,
"roomNumber": "string",
"statusText": "string", // 房间状态文字,显示在房间号下方小字,由 hotel_room.sell_status 和 hotel_room.occupancy_status 综合判断映射:sell_status='0'且occupancy_status='0'→'空闲';sell_status='3'且occupancy_status='1'→'入住·在房';sell_status='4'→'维修/告警'(黄色);sell_status='2'→'预定';dnd_status='1'→'勿扰';checkout_status='1'→'退房'
"statusColor": "string", // 状态颜色类型:idle=灰色(green/默认) occupied=蓝色(blue) alarm=黄色(yellow/orange) reserved=紫色(purple) dnd=橙色(orange) checkout=灰色(gray),根据 statusText 对应,用于渲染格子背景色
"hasAlarmIcon": true // 是否显示告警图标(⚠️):true=显示黄色警告图标 false=不显示
}
],
"roomDetailList": [ // 房间详情列表
{
"roomBasicInfo": { // 房间基础信息区
"roomId": 0,
"roomNumber": "string",
"roomStatusText": "string"
},
"statusTags": { // 状态标签区(一行绿色勾选标签:✓有人 ✓通电中 ✓wif正常)
"isOccupied": true, // 是否已入住
"powerSupplyStatus": true, // 供电状态(true=供电中,false=已断电)
"wifiNormalStatus": true // WiFi是否正常(true=正常,false=异常)
},
"deviceList": [ // 房间设备列表
{
"deviceId": 0,
"deviceName": "string",
"deviceTypeIcon": "string", // 设备图标标识:ac=❄️空调 light=💡照明 tv=📺电视 curtain=🪟窗帘
"powerStatus": "string", // 电源开关状态码:0=关闭 1=开启
"powerStatusText": "string", // 开关状态文字:'开'(绿色)/'关'(灰色),根据 power_status 映射:'1'→'开' '0'→'关'
"temperatureDisplay": "string", // 温度显示文本
"paramDisplayText": "string" // 完整参数显示文本,最终展示值,拼接规则:有温度→'{temperature}°c·{powerStatusText}'(如'24°C·开');无温度→直接显示 powerStatusText(如'开'/'关')
}
]
}
]
}
}
```
#### 客房状态总览
**GET /app/hotel/floor/list**
```
求体:
{
}
应 data:
{
"code": 0,
"msg": "string",
"data": {
"total": 0,
"floors": [
{
"floorId": 0,
"floorName": "string"
}
]
}
}
```
#### 非客房-相关楼层区域设备
**GET /app/room/area/devices**
......
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