Commit 9474420e authored by huqu's avatar huqu

fix bugs

parent 7566c742
...@@ -97,6 +97,8 @@ class AlertItemBO extends Equatable { ...@@ -97,6 +97,8 @@ class AlertItemBO extends Equatable {
final String waitingDurationDesc; final String waitingDurationDesc;
final String alertTime; final String alertTime;
final String alertCategory; final String alertCategory;
final String deviceCode;
final String deviceLocation;
const AlertItemBO({ const AlertItemBO({
required this.alertId, required this.alertId,
...@@ -117,6 +119,8 @@ class AlertItemBO extends Equatable { ...@@ -117,6 +119,8 @@ class AlertItemBO extends Equatable {
required this.waitingDurationDesc, required this.waitingDurationDesc,
required this.alertTime, required this.alertTime,
required this.alertCategory, required this.alertCategory,
required this.deviceCode,
required this.deviceLocation,
}); });
factory AlertItemBO.fromJson(Map<String, dynamic> json) { factory AlertItemBO.fromJson(Map<String, dynamic> json) {
...@@ -139,6 +143,8 @@ class AlertItemBO extends Equatable { ...@@ -139,6 +143,8 @@ class AlertItemBO extends Equatable {
waitingDurationDesc: json['waitingDurationDesc'] as String? ?? '', waitingDurationDesc: json['waitingDurationDesc'] as String? ?? '',
alertTime: json['alertTime'] as String? ?? '', alertTime: json['alertTime'] as String? ?? '',
alertCategory: json['alertCategory'] as String? ?? '', alertCategory: json['alertCategory'] as String? ?? '',
deviceCode: json['deviceCode'] as String? ?? '',
deviceLocation: json['deviceLocation'] as String? ?? '',
); );
} }
...@@ -162,6 +168,8 @@ class AlertItemBO extends Equatable { ...@@ -162,6 +168,8 @@ class AlertItemBO extends Equatable {
waitingDurationDesc, waitingDurationDesc,
alertTime, alertTime,
alertCategory, alertCategory,
deviceCode,
deviceLocation,
]; ];
} }
...@@ -181,6 +189,8 @@ class DeviceItemBO extends Equatable { ...@@ -181,6 +189,8 @@ class DeviceItemBO extends Equatable {
final String current; final String current;
final String onlineStatus; final String onlineStatus;
final String lastHeartbeat; final String lastHeartbeat;
final String deviceCode;
final String deviceLocation;
const DeviceItemBO({ const DeviceItemBO({
required this.deviceId, required this.deviceId,
...@@ -196,6 +206,8 @@ class DeviceItemBO extends Equatable { ...@@ -196,6 +206,8 @@ class DeviceItemBO extends Equatable {
required this.current, required this.current,
required this.onlineStatus, required this.onlineStatus,
required this.lastHeartbeat, required this.lastHeartbeat,
required this.deviceCode,
required this.deviceLocation,
}); });
factory DeviceItemBO.fromJson(Map<String, dynamic> json) { factory DeviceItemBO.fromJson(Map<String, dynamic> json) {
...@@ -213,6 +225,8 @@ class DeviceItemBO extends Equatable { ...@@ -213,6 +225,8 @@ class DeviceItemBO extends Equatable {
current: json['current'] as String? ?? '', current: json['current'] as String? ?? '',
onlineStatus: json['onlineStatus'] as String? ?? '0', onlineStatus: json['onlineStatus'] as String? ?? '0',
lastHeartbeat: json['lastHeartbeat'] as String? ?? '', lastHeartbeat: json['lastHeartbeat'] as String? ?? '',
deviceCode: json['deviceCode'] as String? ?? '',
deviceLocation: json['deviceLocation'] as String? ?? '',
); );
} }
...@@ -231,5 +245,7 @@ class DeviceItemBO extends Equatable { ...@@ -231,5 +245,7 @@ class DeviceItemBO extends Equatable {
current, current,
onlineStatus, onlineStatus,
lastHeartbeat, lastHeartbeat,
deviceCode,
deviceLocation,
]; ];
} }
\ No newline at end of file
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
class InspectionRoomBO extends Equatable { class InspectionRoomBO extends Equatable {
final int roomId;
final String roomNumber;
final InspectionRoomRunStatusBO runStatus; final InspectionRoomRunStatusBO runStatus;
final List<InspectionRoomGroupsBO> roomGroups;
const InspectionRoomBO({ const InspectionRoomBO({
required this.roomId,
required this.roomNumber,
required this.runStatus, required this.runStatus,
required this.roomGroups,
}); });
factory InspectionRoomBO.fromJson(Map<String, dynamic> json) { factory InspectionRoomBO.fromJson(Map<String, dynamic> json) {
final roomGroups = json['roomGroups'] as List<dynamic>? ?? [];
return InspectionRoomBO( return InspectionRoomBO(
roomId: int.tryParse(json['roomId']?.toString() ?? '') ?? 0,
roomNumber: json['roomNumber'] as String? ?? '',
runStatus: InspectionRoomRunStatusBO.fromJson( runStatus: InspectionRoomRunStatusBO.fromJson(
json['runStatus'] as Map<String, dynamic>? ?? {}, json['runStatus'] as Map<String, dynamic>? ?? {},
), ),
roomGroups: roomGroups
.map((e) => InspectionRoomGroupsBO.fromJson(e as Map<String, dynamic>))
.toList(),
); );
} }
@override @override
List<Object?> get props => [roomId, roomNumber, runStatus]; List<Object?> get props => [runStatus, roomGroups];
} }
class InspectionRoomRunStatusBO extends Equatable { class InspectionRoomRunStatusBO extends Equatable {
...@@ -49,4 +49,50 @@ class InspectionRoomRunStatusBO extends Equatable { ...@@ -49,4 +49,50 @@ class InspectionRoomRunStatusBO extends Equatable {
@override @override
List<Object?> get props => [normal, warning, offline, fault]; List<Object?> get props => [normal, warning, offline, fault];
}
class InspectionRoomGroupsBO extends Equatable {
final String areaTypeName;
final List<InspectionRoomsBO> rooms;
const InspectionRoomGroupsBO({
required this.areaTypeName,
required this.rooms,
});
factory InspectionRoomGroupsBO.fromJson(Map<String, dynamic> json) {
final rooms = json['rooms'] as List<dynamic>? ?? [];
return InspectionRoomGroupsBO(
areaTypeName: json['areaTypeName']?.toString() ?? '',
rooms: rooms
.map((e) => InspectionRoomsBO.fromJson(e as Map<String, dynamic>))
.toList(),
);
}
@override
List<Object?> get props => [areaTypeName, rooms];
}
class InspectionRoomsBO extends Equatable {
final int roomId;
final String roomNumber;
final String areaTypeName;
const InspectionRoomsBO({
required this.roomId,
required this.roomNumber,
required this.areaTypeName,
});
factory InspectionRoomsBO.fromJson(Map<String, dynamic> json) {
return InspectionRoomsBO(
roomId: int.tryParse(json['roomId']?.toString() ?? '') ?? 0,
roomNumber: json['roomNumber']?.toString() ?? '',
areaTypeName: json['areaTypeName']?.toString() ?? '',
);
}
@override
List<Object?> get props => [roomId, roomNumber, areaTypeName];
} }
\ No newline at end of file
...@@ -4,7 +4,7 @@ import '../models/bo/alert_list_bo.dart'; ...@@ -4,7 +4,7 @@ import '../models/bo/alert_list_bo.dart';
class AlertListRepository { class AlertListRepository {
/// 获取告警列表 /// 获取告警列表
/// [alertStatus] 告警状态码:0=待处理 2=已处理 3=误报,不传则查询全部 /// [alertStatus] 告警状态码:0=待处理 2=已处理 4=误报,不传则查询全部
Future<ResponseModel<AlertListBO>> getList({ Future<ResponseModel<AlertListBO>> getList({
required int pageSize, required int pageSize,
required int pageNum, required int pageNum,
...@@ -20,8 +20,7 @@ class AlertListRepository { ...@@ -20,8 +20,7 @@ class AlertListRepository {
return DioRequest.instance.get<AlertListBO>( return DioRequest.instance.get<AlertListBO>(
'/app/alert/list', '/app/alert/list',
queryParameters: params, queryParameters: params,
fromJsonT: (data) => fromJsonT: (data) => AlertListBO.fromJson(data as Map<String, dynamic>),
AlertListBO.fromJson(data as Map<String, dynamic>),
); );
} }
} }
\ No newline at end of file
...@@ -4,15 +4,11 @@ import '../models/bo/inspection_room_bo.dart'; ...@@ -4,15 +4,11 @@ import '../models/bo/inspection_room_bo.dart';
class InspectionRoomRepository { class InspectionRoomRepository {
/// 获取设备巡检房间列表 /// 获取设备巡检房间列表
Future<ResponseModel<List<InspectionRoomBO>>> getRooms() { Future<ResponseModel<InspectionRoomBO>> getRooms() {
return DioRequest.instance.get<List<InspectionRoomBO>>( return DioRequest.instance.get<InspectionRoomBO>(
'/app/device/inspection/rooms', '/app/device/inspection/rooms',
fromJsonT: (data) { fromJsonT: (data) =>
final list = data as List<dynamic>; InspectionRoomBO.fromJson(data as Map<String, dynamic>),
return list
.map((e) => InspectionRoomBO.fromJson(e as Map<String, dynamic>))
.toList();
},
); );
} }
} }
\ No newline at end of file
...@@ -7,7 +7,7 @@ class InspectionRoomService { ...@@ -7,7 +7,7 @@ class InspectionRoomService {
InspectionRoomService({required InspectionRoomRepository repository}) InspectionRoomService({required InspectionRoomRepository repository})
: _repository = repository; : _repository = repository;
Future<List<InspectionRoomBO>> getRooms() async { Future<InspectionRoomBO> getRooms() async {
final result = await _repository.getRooms(); final result = await _repository.getRooms();
if (result.success && result.data != null) { if (result.success && result.data != null) {
return result.data!; return result.data!;
......
...@@ -54,6 +54,7 @@ class AbnormalDetailView extends StatelessWidget { ...@@ -54,6 +54,7 @@ class AbnormalDetailView extends StatelessWidget {
if (state.isLoading) { if (state.isLoading) {
return const Center(child: CircularProgressIndicator(color: Color.fromRGBO(66, 165, 245, 1.0))); return const Center(child: CircularProgressIndicator(color: Color.fromRGBO(66, 165, 245, 1.0)));
} }
if (state.error != null) { if (state.error != null) {
return Center( return Center(
child: Text( child: Text(
...@@ -65,10 +66,12 @@ class AbnormalDetailView extends StatelessWidget { ...@@ -65,10 +66,12 @@ class AbnormalDetailView extends StatelessWidget {
), ),
); );
} }
final alarmInfo = state.alarmInfo; final alarmInfo = state.alarmInfo;
if (alarmInfo == null) { if (alarmInfo == null) {
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
final cubit = context.read<AbnormalCubit>(); final cubit = context.read<AbnormalCubit>();
return SingleChildScrollView( return SingleChildScrollView(
padding: EdgeInsets.symmetric(horizontal: 28.w), padding: EdgeInsets.symmetric(horizontal: 28.w),
...@@ -94,11 +97,12 @@ class AbnormalDetailView extends StatelessWidget { ...@@ -94,11 +97,12 @@ class AbnormalDetailView extends StatelessWidget {
Widget _buildBottomButtons(BuildContext context, AbnormalCubit cubit) { Widget _buildBottomButtons(BuildContext context, AbnormalCubit cubit) {
return Container( return Container(
width: double.infinity, width: double.infinity,
padding: EdgeInsets.symmetric(vertical: 15.h), padding: EdgeInsets.symmetric(vertical: 20.h),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
spacing: 20.h,
children: [ children: [
if (alarmStatus != AlarmStatus.processed) ...[ if (alarmStatus != AlarmStatus.processed)
BlocBuilder<AbnormalCubit, AbnormalState>( BlocBuilder<AbnormalCubit, AbnormalState>(
builder: (context, state) { builder: (context, state) {
final isDisabled = state.isHandling || state.isHandled; final isDisabled = state.isHandling || state.isHandled;
...@@ -109,7 +113,7 @@ class AbnormalDetailView extends StatelessWidget { ...@@ -109,7 +113,7 @@ class AbnormalDetailView extends StatelessWidget {
color: isDisabled color: isDisabled
? const Color.fromRGBO(73, 149, 234, 0.4) ? const Color.fromRGBO(73, 149, 234, 0.4)
: const Color.fromRGBO(73, 149, 234, 1), : const Color.fromRGBO(73, 149, 234, 1),
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(40.r),
), ),
child: TextButton( child: TextButton(
onPressed: isDisabled onPressed: isDisabled
...@@ -127,7 +131,7 @@ class AbnormalDetailView extends StatelessWidget { ...@@ -127,7 +131,7 @@ class AbnormalDetailView extends StatelessWidget {
: Text( : Text(
state.isHandled ? '已处理' : '确认处理', state.isHandled ? '已处理' : '确认处理',
style: TextStyle( style: TextStyle(
fontSize: 28.sp, fontSize: 36.sp,
color: const Color.fromRGBO(237, 245, 255, 1), color: const Color.fromRGBO(237, 245, 255, 1),
), ),
), ),
...@@ -135,14 +139,12 @@ class AbnormalDetailView extends StatelessWidget { ...@@ -135,14 +139,12 @@ class AbnormalDetailView extends StatelessWidget {
); );
}, },
), ),
SizedBox(height: 20.h),
],
Container( Container(
width: double.infinity, width: double.infinity,
height: 88.h, height: 88.h,
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color.fromRGBO(100, 116, 139, 0.2), color: const Color.fromRGBO(100, 116, 139, 0.2),
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(40.r),
), ),
child: TextButton( child: TextButton(
onPressed: () { onPressed: () {
...@@ -156,15 +158,15 @@ class AbnormalDetailView extends StatelessWidget { ...@@ -156,15 +158,15 @@ class AbnormalDetailView extends StatelessWidget {
Text( Text(
'设备控制', '设备控制',
style: TextStyle( style: TextStyle(
fontSize: 28.sp, fontSize: 36.sp,
color: const Color.fromRGBO(100, 116, 139, 1), color: const Color.fromRGBO(100, 116, 139, 1),
), ),
), ),
SizedBox(width: 8.w), SizedBox(width: 8.w),
Image.asset( Image.asset(
"lib/assets/icon/right.png", "lib/assets/icon/right.png",
width: 24.w, width: 40.w,
height: 21.h, height: 24.h,
), ),
], ],
), ),
......
...@@ -39,8 +39,8 @@ class AbnormalCubit extends Cubit<AbnormalState> { ...@@ -39,8 +39,8 @@ class AbnormalCubit extends Cubit<AbnormalState> {
return AlarmInfo( return AlarmInfo(
alarmType: basic.alertTitle, alarmType: basic.alertTitle,
deviceInfo: basic.alertDeviceLocation, deviceInfo: basic.alertDeviceLocation,
alertLevelText: basic.alertLevelText, alertLevelText: basic.alertLevel,
urgency: _mapUrgency(basic.alertLevel), urgency: '',
currentTemp: info.alertValue, currentTemp: info.alertValue,
voltage: '', voltage: '',
current: '', current: '',
...@@ -60,20 +60,10 @@ class AbnormalCubit extends Cubit<AbnormalState> { ...@@ -60,20 +60,10 @@ class AbnormalCubit extends Cubit<AbnormalState> {
}).toList(), }).toList(),
alertDeviceId: basic.alertDeviceId, alertDeviceId: basic.alertDeviceId,
alertCount: basic.alertCount, alertCount: basic.alertCount,
alertIcon: basic.alertIcon,
); );
} }
String _mapUrgency(String level) {
switch (level) {
case 'danger':
return '高警';
case 'warning':
return '中警';
default:
return '普通';
}
}
Future<void> handleConfirm() async { Future<void> handleConfirm() async {
if (state.isHandling || state.isHandled) return; if (state.isHandling || state.isHandled) return;
emit(state.copyWith(isHandling: true)); emit(state.copyWith(isHandling: true));
......
...@@ -22,6 +22,7 @@ class AlarmInfo { ...@@ -22,6 +22,7 @@ class AlarmInfo {
final List<FlSpot> temperatureSpots; final List<FlSpot> temperatureSpots;
final String alertDeviceId; final String alertDeviceId;
final int alertCount; final int alertCount;
final String alertIcon;
const AlarmInfo({ const AlarmInfo({
required this.alarmType, required this.alarmType,
...@@ -45,6 +46,7 @@ class AlarmInfo { ...@@ -45,6 +46,7 @@ class AlarmInfo {
required this.temperatureSpots, required this.temperatureSpots,
required this.alertDeviceId, required this.alertDeviceId,
required this.alertCount, required this.alertCount,
required this.alertIcon,
}); });
} }
......
...@@ -33,7 +33,9 @@ class AlarmInfoBlock extends StatelessWidget { ...@@ -33,7 +33,9 @@ class AlarmInfoBlock extends StatelessWidget {
SizedBox(height: 25.h), SizedBox(height: 25.h),
_buildInfoRow( _buildInfoRow(
'告警级别', '告警级别',
alarmInfo.alertLevelText, alarmInfo.alertLevelText == '3' ? '高警(Level 3)' :
alarmInfo.alertLevelText == '2' ? '中警(Level 2)' :
alarmInfo.alertLevelText == '1' ? '低警(Level 1)' : '',
valueColor: const Color.fromRGBO(255, 100, 101, 1), valueColor: const Color.fromRGBO(255, 100, 101, 1),
), ),
SizedBox(height: 25.h), SizedBox(height: 25.h),
...@@ -65,7 +67,6 @@ class AlarmInfoBlock extends StatelessWidget { ...@@ -65,7 +67,6 @@ class AlarmInfoBlock extends StatelessWidget {
Widget _buildInfoRow(String label, String value, {Color? valueColor}) { Widget _buildInfoRow(String label, String value, {Color? valueColor}) {
final displayValue = _truncateText(value); final displayValue = _truncateText(value);
return Row( return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
label, label,
...@@ -74,12 +75,18 @@ class AlarmInfoBlock extends StatelessWidget { ...@@ -74,12 +75,18 @@ class AlarmInfoBlock extends StatelessWidget {
color: const Color.fromRGBO(100, 116, 139, 1), color: const Color.fromRGBO(100, 116, 139, 1),
), ),
), ),
Text( SizedBox(width: 20.w),
displayValue, Expanded(
style: TextStyle( child: Text(
fontSize: 24.sp, displayValue,
color: valueColor ?? const Color.fromRGBO(100, 116, 139, 1), style: TextStyle(
), fontSize: 24.sp,
color: valueColor ?? const Color.fromRGBO(100, 116, 139, 1),
),
textAlign: TextAlign.end,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
), ),
], ],
); );
......
...@@ -18,65 +18,53 @@ class TemperatureBlock extends StatelessWidget { ...@@ -18,65 +18,53 @@ class TemperatureBlock extends StatelessWidget {
color: Colors.white, color: Colors.white,
), ),
child: Column( child: Column(
spacing: 5.h,
children: [ children: [
Row( Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
spacing: 20.w,
children: [ children: [
Container( _buildIcon(),
width: 70.w,
height: 66.h,
decoration: BoxDecoration(
color: const Color.fromRGBO(219, 234, 254, 1),
borderRadius: BorderRadius.circular(8.r),
),
child: Icon(
Icons.thermostat_outlined,
color: const Color.fromRGBO(59, 130, 246, 1),
size: 40.sp,
),
),
SizedBox(width: 15.w),
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
spacing: 5.h,
children: [ children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Expanded(
alarmInfo.alarmType, child: Text(
style: TextStyle( alarmInfo.alarmType,
fontSize: 28.sp,
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
Row(
children: [
Text(
'${alarmInfo.alertLevelText} · ',
style: TextStyle( style: TextStyle(
fontSize: 24.sp, fontSize: 28.sp,
color: const Color.fromRGBO(255, 100, 101, 1), color: Colors.black,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
maxLines: 1,
overflow: TextOverflow.ellipsis,
), ),
Container(
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 5.h),
decoration: BoxDecoration(
color: const Color.fromRGBO(255, 158, 159, 0.5),
borderRadius: BorderRadius.circular(16.r),
),
child: Text(
alarmInfo.urgency,
style: TextStyle(
fontSize: 24.sp,
color: const Color.fromRGBO(255, 100, 101, 1),
),
),
),
],
), ),
Text(
alarmInfo.alertLevelText == '3' ? '高警' :
alarmInfo.alertLevelText == '2' ? '中警' :
alarmInfo.alertLevelText == '1' ? '低警' : '',
style: TextStyle(
fontSize: 24.sp,
color: const Color.fromRGBO(255, 100, 101, 1),
),
),
if (alarmInfo.alertLevelText == '3') ...[
Container(
width: 5.w,
height: 5.h,
margin: EdgeInsets.symmetric(horizontal: 5.w),
decoration: const BoxDecoration(
color: Color.fromRGBO(255, 100, 101, 1),
shape: BoxShape.circle
),
),
_buildLevelBadge()
],
], ],
), ),
Text( Text(
...@@ -91,21 +79,20 @@ class TemperatureBlock extends StatelessWidget { ...@@ -91,21 +79,20 @@ class TemperatureBlock extends StatelessWidget {
), ),
], ],
), ),
SizedBox(height: 5.h),
Row( Row(
children: [ children: [
SizedBox(width: 85.w), SizedBox(width: 85.w),
Text( Text(
'告警数量:', '告警数量:',
style: TextStyle( style: TextStyle(
fontSize: 28.sp, fontSize: 24.sp,
color: const Color.fromRGBO(100, 116, 139, 1), color: const Color.fromRGBO(100, 116, 139, 1),
), ),
), ),
Text( Text(
alarmInfo.alertCount.toString(), alarmInfo.alertCount.toString(),
style: TextStyle( style: TextStyle(
fontSize: 28.sp, fontSize: 24.sp,
color: const Color.fromRGBO(255, 100, 101, 1), color: const Color.fromRGBO(255, 100, 101, 1),
), ),
), ),
...@@ -115,4 +102,78 @@ class TemperatureBlock extends StatelessWidget { ...@@ -115,4 +102,78 @@ class TemperatureBlock extends StatelessWidget {
) )
); );
} }
Widget _buildIcon() {
Color? bgColor;
String icon = '';
double iconW = 0;
double iconH = 0;
switch (alarmInfo.alertIcon) {
case 'temperature'://温度
bgColor = const Color.fromRGBO(237, 245, 255, 1);
icon = 'lib/assets/icon/thermostat_bg.png';
iconW = 20.w;
iconH = 38.h;
break;
case 'voltage'://电压
bgColor = const Color.fromRGBO(255, 211, 213, 1);
icon = 'lib/assets/icon/voltage_bg.png';
iconW = 40.w;
iconH = 30.h;
break;
case 'current'://电流
bgColor = const Color.fromRGBO(255, 161, 72, 1);
icon = 'lib/assets/icon/current_bg.png';
iconW = 30.w;
iconH = 38.h;
break;
case 'alert'://漏电
bgColor = const Color.fromRGBO(255, 128, 114, 1);
icon = 'lib/assets/icon/alert_bg.png';
iconW = 46.w;
iconH = 32.h;
break;
case 'offline'://离线
bgColor = const Color.fromRGBO(229, 241, 255, 1);
icon = 'lib/assets/icon/offline_bg.png';
iconW = 44.w;
iconH = 32.h;
break;
}
return Container(
width: 64.w,
height: 53.h,
alignment: Alignment.center,
decoration: BoxDecoration(
color: bgColor,
borderRadius: BorderRadius.circular(8.r),
),
child: icon.isNotEmpty ? Image.asset(icon, width: iconW, height: iconH) :
const SizedBox.shrink(),
);
}
Widget _buildLevelBadge() {
return Container(
width: 73.w,
height: 32.h,
alignment: Alignment.center,
decoration: BoxDecoration(
color: const Color.fromRGBO(255, 100, 101, 0.2),
borderRadius: BorderRadius.circular(16.r),
border: Border.all(
color: const Color.fromRGBO(255, 158, 159, 0.5),
width: 1.w,
)
),
child: Text(
'紧急',
style: TextStyle(
fontSize: 24.sp,
color: const Color.fromRGBO(255, 100, 101, 1),
),
),
);
}
} }
\ No newline at end of file
...@@ -8,8 +8,8 @@ import 'package:smart_hotel_app/services/alert_list_service.dart'; ...@@ -8,8 +8,8 @@ import 'package:smart_hotel_app/services/alert_list_service.dart';
import 'package:smart_hotel_app/views/home/abnormal_list/cubit/abnormal_list_state.dart'; import 'package:smart_hotel_app/views/home/abnormal_list/cubit/abnormal_list_state.dart';
/// AlarmStatus → 告警状态码: 0=待处理 1=处理中 2=已确认 3=已关闭 4=误报关闭 /// AlarmStatus → 告警状态码: 0=待处理 1=处理中 2=已确认 3=已关闭 4=误报关闭
String? alarmStatusToApi(AlarmStatus filter) { String? alarmStatusToApi(AlarmStatus status) {
switch (filter) { switch (status) {
case AlarmStatus.all: case AlarmStatus.all:
return null; return null;
case AlarmStatus.pending: case AlarmStatus.pending:
...@@ -28,8 +28,7 @@ class AbnormalListCubit extends Cubit<AbnormalListState> { ...@@ -28,8 +28,7 @@ class AbnormalListCubit extends Cubit<AbnormalListState> {
PagingController<int, AlarmItem>? _pagingController; PagingController<int, AlarmItem>? _pagingController;
AbnormalListCubit() AbnormalListCubit(): _service = AlertListService(
: _service = AlertListService(
repository: AlertListRepository(), repository: AlertListRepository(),
), ),
super(const AbnormalListState()); super(const AbnormalListState());
...@@ -71,12 +70,14 @@ class AbnormalListCubit extends Cubit<AbnormalListState> { ...@@ -71,12 +70,14 @@ class AbnormalListCubit extends Cubit<AbnormalListState> {
/// 切换筛选 Tab,触发重新请求 /// 切换筛选 Tab,触发重新请求
void setFilter(AlarmStatus filter) { void setFilter(AlarmStatus filter) {
if (state.currentFilter == filter) return; if (state.currentFilter == filter) return;
emit(state.copyWith(currentFilter: filter)); emit(state.copyWith(currentFilter: filter));
_pagingController?.refresh(); _pagingController?.refresh();
} }
void onAlarmItemTap(AlarmItem item, BuildContext context) { void onAlarmItemTap(AlarmItem item, BuildContext context) {
final alertId = int.tryParse(item.id) ?? 0; final alertId = int.tryParse(item.id) ?? 0;
context.pushRoute(AbnormalDetailRoute(alertId: alertId, alarmStatus: item.status)); context.pushRoute(AbnormalDetailRoute(
alertId: alertId, alarmStatus: item.status));
} }
} }
...@@ -11,7 +11,7 @@ class AlarmItem extends Equatable { ...@@ -11,7 +11,7 @@ class AlarmItem extends Equatable {
final String processTime; final String processTime;
final AlarmStatus status; final AlarmStatus status;
final String level; final String level;
final String urgency; final String alertIcon;
const AlarmItem({ const AlarmItem({
required this.id, required this.id,
...@@ -21,7 +21,7 @@ class AlarmItem extends Equatable { ...@@ -21,7 +21,7 @@ class AlarmItem extends Equatable {
required this.processTime, required this.processTime,
required this.status, required this.status,
required this.level, required this.level,
required this.urgency, required this.alertIcon,
}); });
factory AlarmItem.fromAlertListItemBO(AlertListItemBO item) { factory AlarmItem.fromAlertListItemBO(AlertListItemBO item) {
...@@ -32,8 +32,8 @@ class AlarmItem extends Equatable { ...@@ -32,8 +32,8 @@ class AlarmItem extends Equatable {
alarmTime: item.alertTime.isEmpty ? '--' : item.alertTime, alarmTime: item.alertTime.isEmpty ? '--' : item.alertTime,
processTime: item.handleTime.isEmpty ? '--' : item.handleTime, processTime: item.handleTime.isEmpty ? '--' : item.handleTime,
status: _mapAlertStatus(item.alertStatus), status: _mapAlertStatus(item.alertStatus),
level: _mapAlertLevel(item.alertLevel), level: item.alertLevel,
urgency: '', alertIcon: item.alertIcon,
); );
} }
...@@ -51,28 +51,9 @@ class AlarmItem extends Equatable { ...@@ -51,28 +51,9 @@ class AlarmItem extends Equatable {
} }
} }
static String _mapAlertLevel(String alertLevel) {
switch (alertLevel) {
case '3':
case 'danger':
return '高警';
case '2':
case 'warning':
return '中警';
case '1':
case 'info':
return '普通';
default:
return '';
}
}
@override @override
List<Object?> get props => List<Object?> get props =>
[id, title, deviceInfo, alarmTime, processTime, status, level, urgency]; [id, title, deviceInfo, alarmTime, processTime, status, level, alertIcon];
} }
class AbnormalListState extends Equatable { class AbnormalListState extends Equatable {
......
...@@ -15,6 +15,7 @@ class AlarmListItem extends StatelessWidget { ...@@ -15,6 +15,7 @@ class AlarmListItem extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GestureDetector( return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => onTap(item), onTap: () => onTap(item),
child: Container( child: Container(
width: double.infinity, width: double.infinity,
...@@ -26,35 +27,52 @@ class AlarmListItem extends StatelessWidget { ...@@ -26,35 +27,52 @@ class AlarmListItem extends StatelessWidget {
), ),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
spacing: 20.w,
children: [ children: [
_buildIcon(), _buildIcon(),
SizedBox(width: 20.w),
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
spacing: 10.h,
children: [ children: [
_buildTitleRow(), _buildTitleRow(),
SizedBox(height: 15.h),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Expanded(
item.deviceInfo, child: Text(
style: TextStyle( item.deviceInfo,
fontSize: 24.sp, style: TextStyle(
color: const Color.fromRGBO(100, 116, 139, 1), fontSize: 24.sp,
), color: const Color.fromRGBO(100, 116, 139, 1),
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
), ),
SizedBox(width: 15.w),
Text( Text(
item.level, item.level == '3' ? '高警' :
item.level == '2' ? '中警' :
item.level == '1' ? '低警' : '',
style: TextStyle( style: TextStyle(
fontSize: 24.sp, fontSize: 24.sp,
color: const Color.fromRGBO(255, 100, 101, 1), color: const Color.fromRGBO(255, 100, 101, 1),
), ),
), ),
if (item.level == '3') ...[
Container(
width: 5.w,
height: 5.h,
margin: EdgeInsets.symmetric(horizontal: 5.w),
decoration: const BoxDecoration(
color: Color.fromRGBO(255, 100, 101, 1),
shape: BoxShape.circle
),
),
_buildLevelBadge()
],
], ],
), ),
SizedBox(height: 10.h),
Text( Text(
'告警时间: ${item.alarmTime}', '告警时间: ${item.alarmTime}',
style: TextStyle( style: TextStyle(
...@@ -62,7 +80,6 @@ class AlarmListItem extends StatelessWidget { ...@@ -62,7 +80,6 @@ class AlarmListItem extends StatelessWidget {
color: const Color.fromRGBO(100, 116, 139, 1), color: const Color.fromRGBO(100, 116, 139, 1),
), ),
), ),
SizedBox(height: 10.h),
Text( Text(
'处理时间: ${item.processTime}', '处理时间: ${item.processTime}',
style: TextStyle( style: TextStyle(
...@@ -80,89 +97,128 @@ class AlarmListItem extends StatelessWidget { ...@@ -80,89 +97,128 @@ class AlarmListItem extends StatelessWidget {
} }
Widget _buildIcon() { Widget _buildIcon() {
Color? bgColor;
String icon = '';
double iconW = 0;
double iconH = 0;
switch (item.alertIcon) {
case 'temperature'://温度
bgColor = const Color.fromRGBO(237, 245, 255, 1);
icon = 'lib/assets/icon/thermostat_bg.png';
iconW = 20.w;
iconH = 38.h;
break;
case 'voltage'://电压
bgColor = const Color.fromRGBO(255, 211, 213, 1);
icon = 'lib/assets/icon/voltage_bg.png';
iconW = 40.w;
iconH = 30.h;
break;
case 'current'://电流
bgColor = const Color.fromRGBO(255, 161, 72, 1);
icon = 'lib/assets/icon/current_bg.png';
iconW = 30.w;
iconH = 38.h;
break;
case 'alert'://漏电
bgColor = const Color.fromRGBO(255, 128, 114, 1);
icon = 'lib/assets/icon/alert_bg.png';
iconW = 46.w;
iconH = 32.h;
break;
case 'offline'://离线
bgColor = const Color.fromRGBO(229, 241, 255, 1);
icon = 'lib/assets/icon/offline_bg.png';
iconW = 44.w;
iconH = 32.h;
break;
}
return Container( return Container(
width: 96.w, width: 80.w,
height: 96.w, height: 64.h,
alignment: Alignment.center,
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color.fromRGBO(237, 245, 255, 1), color: bgColor,
borderRadius: BorderRadius.circular(8.r), borderRadius: BorderRadius.circular(8.r),
), ),
child: Icon( child: icon.isNotEmpty ? Image.asset(icon, width: iconW, height: iconH) :
Icons.thermostat, const SizedBox.shrink(),
size: 48.w,
color: const Color.fromRGBO(73, 149, 234, 1),
),
); );
} }
Widget _buildTitleRow() { Widget _buildTitleRow() {
return Row( return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Expanded(
item.title, child: Text(
style: TextStyle( item.title,
fontSize: 28.sp, style: TextStyle(
color: Colors.black, fontSize: 28.sp,
fontWeight: FontWeight.bold, color: Colors.black,
fontWeight: FontWeight.bold,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
), ),
), ),
_buildStatusBadge(), _buildStatusBadge(),
// Row(
// children: [
// _buildStatusBadge(),
// SizedBox(width: 12.w),
// _buildLevelBadge(),
// SizedBox(width: 8.w),
// _buildUrgencyBadge(),
// ],
// ),
], ],
); );
} }
Widget _buildStatusBadge() { Widget _buildStatusBadge() {
Color bgColor; Color? bgColor;
Color textColor; Color? textColor;
String text; String text = '';
IconData? icon; String icon = '';
double iconW = 0;
double iconH = 0;
switch (item.status) { switch (item.status) {
case AlarmStatus.pending: case AlarmStatus.pending:
bgColor = const Color.fromRGBO(254, 247, 217, 1); bgColor = const Color.fromRGBO(254, 247, 217, 1);
textColor = const Color.fromRGBO(250, 204, 21, 1); textColor = const Color.fromRGBO(250, 204, 21, 1);
text = '待处理'; text = '待处理';
icon = Icons.error_outline; icon = 'lib/assets/icon/info_dcl_bg.png';
iconW = 4.w;
iconH = 15.h;
break; break;
case AlarmStatus.processed: case AlarmStatus.processed:
bgColor = const Color.fromRGBO(84, 214, 120, 0.15); bgColor = const Color.fromRGBO(209, 250, 229, 1);
textColor = const Color.fromRGBO(84, 214, 120, 1); textColor = const Color.fromRGBO(20, 184, 166, 1);
text = '已处理'; text = '已处理';
icon = Icons.check_circle_outline; icon = 'lib/assets/icon/info_ycl_bg.png';
iconW = 18.w;
iconH = 11.h;
break; break;
case AlarmStatus.falseAlarm: case AlarmStatus.falseAlarm:
bgColor = const Color.fromRGBO(255, 100, 101, 0.15); bgColor = const Color.fromRGBO(255, 224, 224, 1);
textColor = const Color.fromRGBO(255, 100, 101, 1); textColor = const Color.fromRGBO(255, 100, 101, 1);
text = '误报'; text = '误报';
icon = Icons.cancel_outlined; icon = 'lib/assets/icon/info_wb_bg.png';
iconW = 14.w;
iconH = 11.h;
break; break;
default: default:
bgColor = const Color.fromRGBO(100, 116, 139, 0.0);//完全透明 break;
textColor = const Color.fromRGBO(100, 116, 139, 1); }
text = '';
if (text.isEmpty) {
return const SizedBox.shrink();
} }
return Container( return Container(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 4.h), width: 132.w,
height: 32.h,
decoration: BoxDecoration( decoration: BoxDecoration(
color: bgColor, color: bgColor,
borderRadius: BorderRadius.circular(40.r), borderRadius: BorderRadius.circular(40.r),
), ),
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center,
spacing: 6.w, spacing: 6.w,
children: [ children: [
Icon(icon, size: 30.w, color: textColor), Image.asset(icon, width: iconW, height: iconH),
Text( Text(
text, text,
style: TextStyle( style: TextStyle(
...@@ -178,35 +234,25 @@ class AlarmListItem extends StatelessWidget { ...@@ -178,35 +234,25 @@ class AlarmListItem extends StatelessWidget {
Widget _buildLevelBadge() { Widget _buildLevelBadge() {
return Container( return Container(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h), width: 73.w,
height: 32.h,
alignment: Alignment.center,
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color.fromRGBO(255, 100, 101, 0.15), color: const Color.fromRGBO(255, 100, 101, 0.2),
borderRadius: BorderRadius.circular(12.r), borderRadius: BorderRadius.circular(16.r),
border: Border.all(
color: const Color.fromRGBO(255, 158, 159, 0.5),
width: 1.w,
)
), ),
child: Text( child: Text(
item.level, '紧急',
style: TextStyle( style: TextStyle(
fontSize: 22.sp, fontSize: 24.sp,
color: const Color.fromRGBO(255, 100, 101, 1), color: const Color.fromRGBO(255, 100, 101, 1),
), ),
), ),
); );
} }
Widget _buildUrgencyBadge() {
return Container(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h),
decoration: BoxDecoration(
color: const Color.fromRGBO(255, 100, 101, 0.15),
borderRadius: BorderRadius.circular(12.r),
),
child: Text(
item.urgency,
style: TextStyle(
fontSize: 22.sp,
color: const Color.fromRGBO(255, 100, 101, 1),
),
),
);
}
} }
...@@ -48,8 +48,7 @@ class DeviceCubit extends Cubit<DeviceState> { ...@@ -48,8 +48,7 @@ class DeviceCubit extends Cubit<DeviceState> {
return DeviceInfo( return DeviceInfo(
deviceName: basic.deviceName, deviceName: basic.deviceName,
deviceId: basic.deviceCode, deviceCode: basic.deviceCode,
// location: basic.roomName,
location: basic.deviceLocation, location: basic.deviceLocation,
status: status, status: status,
voltage: params.voltage, voltage: params.voltage,
......
...@@ -2,7 +2,7 @@ import 'package:equatable/equatable.dart'; ...@@ -2,7 +2,7 @@ import 'package:equatable/equatable.dart';
class DeviceInfo extends Equatable { class DeviceInfo extends Equatable {
final String deviceName; final String deviceName;
final String deviceId; final String deviceCode;
final String location; final String location;
final String status; final String status;
final String voltage; final String voltage;
...@@ -15,7 +15,7 @@ class DeviceInfo extends Equatable { ...@@ -15,7 +15,7 @@ class DeviceInfo extends Equatable {
const DeviceInfo({ const DeviceInfo({
required this.deviceName, required this.deviceName,
required this.deviceId, required this.deviceCode,
required this.location, required this.location,
required this.status, required this.status,
required this.voltage, required this.voltage,
...@@ -30,7 +30,7 @@ class DeviceInfo extends Equatable { ...@@ -30,7 +30,7 @@ class DeviceInfo extends Equatable {
@override @override
List<Object?> get props => [ List<Object?> get props => [
deviceName, deviceName,
deviceId, deviceCode,
location, location,
status, status,
voltage, voltage,
......
...@@ -28,6 +28,7 @@ class PowerCharBlock extends StatelessWidget { ...@@ -28,6 +28,7 @@ class PowerCharBlock extends StatelessWidget {
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
spacing: 20.h,
children: [ children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
...@@ -39,25 +40,14 @@ class PowerCharBlock extends StatelessWidget { ...@@ -39,25 +40,14 @@ class PowerCharBlock extends StatelessWidget {
color: const Color.fromRGBO(100, 116, 139, 1), color: const Color.fromRGBO(100, 116, 139, 1),
), ),
), ),
Container( Row(
decoration: BoxDecoration( children: [
borderRadius: BorderRadius.circular(12.r), _buildTab('功率', 0),
color: Colors.transparent, _buildTab('温度', 1),
border: Border.all( ],
color: const Color.fromRGBO(73, 149, 234, 0.5),
width: 1.5.w,
),
),
child: Row(
children: [
_buildTab('功率', 0),
_buildTab('温度', 1),
],
),
), ),
], ],
), ),
SizedBox(height: 20.h),
SizedBox( SizedBox(
height: 250.h, height: 250.h,
child: BarChart( child: BarChart(
...@@ -93,13 +83,15 @@ class PowerCharBlock extends StatelessWidget { ...@@ -93,13 +83,15 @@ class PowerCharBlock extends StatelessWidget {
getTitlesWidget: (value, meta) { getTitlesWidget: (value, meta) {
return Text( return Text(
'${value.toInt()}', '${value.toInt()}',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle( style: TextStyle(
color: const Color.fromRGBO(156, 163, 175, 1), color: const Color.fromRGBO(156, 163, 175, 1),
fontSize: 24.sp, fontSize: 22.sp,
), ),
); );
}, },
reservedSize: 40, reservedSize: _leftReservedSize,
), ),
), ),
bottomTitles: AxisTitles( bottomTitles: AxisTitles(
...@@ -143,15 +135,20 @@ class PowerCharBlock extends StatelessWidget { ...@@ -143,15 +135,20 @@ class PowerCharBlock extends StatelessWidget {
Widget _buildTab(String title, int index) { Widget _buildTab(String title, int index) {
final isSelected = selectedTabIndex == index; final isSelected = selectedTabIndex == index;
return GestureDetector( return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => onTabChanged(index), onTap: () => onTabChanged(index),
child: Container( child: Container(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h), width: 72.w,
decoration: BoxDecoration( height: 34.h,
alignment: Alignment.center,
decoration: isSelected ? BoxDecoration(
borderRadius: BorderRadius.circular(8.r), borderRadius: BorderRadius.circular(8.r),
color: isSelected color: const Color.fromRGBO(219, 234, 254, 1),
? const Color.fromRGBO(219, 234, 254, 1) border: Border.all(
: Colors.transparent, width: 1.w,
), color: const Color.fromRGBO(73, 149, 234, 0.5),
)
) : null,
child: Text( child: Text(
title, title,
style: TextStyle( style: TextStyle(
...@@ -236,4 +233,10 @@ class PowerCharBlock extends StatelessWidget { ...@@ -236,4 +233,10 @@ class PowerCharBlock extends StatelessWidget {
return _calcMaxY(index) / 5; return _calcMaxY(index) / 5;
} }
/// 根据最大数值的位数动态计算左侧保留宽度
double get _leftReservedSize {
final maxLabel = _calcMaxY(selectedTabIndex).toInt().toString();
return maxLabel.length * 8;
}
} }
\ No newline at end of file
...@@ -20,8 +20,8 @@ class StateInfoBlock extends StatelessWidget { ...@@ -20,8 +20,8 @@ class StateInfoBlock extends StatelessWidget {
child: Row( child: Row(
children: [ children: [
Container( Container(
width: 80.w, width: 64.w,
height: 70.h, height: 53.h,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15.r), borderRadius: BorderRadius.circular(15.r),
color: const Color.fromRGBO(219, 234, 254, 1), color: const Color.fromRGBO(219, 234, 254, 1),
...@@ -29,25 +29,25 @@ class StateInfoBlock extends StatelessWidget { ...@@ -29,25 +29,25 @@ class StateInfoBlock extends StatelessWidget {
child: Icon( child: Icon(
Icons.power, Icons.power,
color: const Color.fromRGBO(59, 130, 246, 1), color: const Color.fromRGBO(59, 130, 246, 1),
size: 32.sp, size: 38.sp,
), ),
), ),
SizedBox(width: 15.w), SizedBox(width: 15.w),
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
spacing: 8.h,
children: [ children: [
Text( Text(
deviceInfo.deviceName, '${deviceInfo.location} ${deviceInfo.deviceName}',
style: TextStyle( style: TextStyle(
fontSize: 28.sp, fontSize: 28.sp,
color: Colors.black, color: Colors.black,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
SizedBox(height: 8.h),
Text( Text(
deviceInfo.location, '${deviceInfo.deviceCode}${deviceInfo.deviceName}·${deviceInfo.location}',
style: TextStyle( style: TextStyle(
fontSize: 24.sp, fontSize: 24.sp,
color: const Color.fromRGBO(100, 116, 139, 1), color: const Color.fromRGBO(100, 116, 139, 1),
......
...@@ -72,7 +72,7 @@ class HomeIndexCubit extends Cubit<HomeIndexState> { ...@@ -72,7 +72,7 @@ class HomeIndexCubit extends Cubit<HomeIndexState> {
EquipmentItem _mapDeviceToEquipment(DeviceItemBO d) { EquipmentItem _mapDeviceToEquipment(DeviceItemBO d) {
return EquipmentItem( return EquipmentItem(
icon: _mapDeviceIcon(d.deviceTypeIcon), icon: _mapDeviceIcon(d.deviceTypeIcon),
title: d.deviceName, title: d.deviceLocation + d.deviceName,
subtitle: _buildDeviceSubtitle(d), subtitle: _buildDeviceSubtitle(d),
// status: d.onlineStatus == '1' ? d.runStatusText : '离线', // status: d.onlineStatus == '1' ? d.runStatusText : '离线',
status: d.runStatusText, status: d.runStatusText,
...@@ -82,14 +82,16 @@ class HomeIndexCubit extends Cubit<HomeIndexState> { ...@@ -82,14 +82,16 @@ class HomeIndexCubit extends Cubit<HomeIndexState> {
IconData _mapDeviceIcon(String icon) { IconData _mapDeviceIcon(String icon) {
switch (icon) { switch (icon) {
case 'power': case 'ac':
return Icons.power; return Icons.air_outlined;
case 'light':
return Icons.light_outlined;
case 'tv':
return Icons.tv_outlined;
case 'curtain':
return Icons.blinds_outlined;
case 'wifi': case 'wifi':
return Icons.wifi; return Icons.wifi;
case 'thermostat':
return Icons.thermostat;
case 'light':
return Icons.light;
default: default:
return Icons.devices_other; return Icons.devices_other;
} }
......
...@@ -92,7 +92,7 @@ class _HomeViewState extends State<HomeView> with AutoRouteAwareStateMixin<HomeV ...@@ -92,7 +92,7 @@ class _HomeViewState extends State<HomeView> with AutoRouteAwareStateMixin<HomeV
List<Widget> _buildAlertBlocks(List<AlertItemBO> alerts) { List<Widget> _buildAlertBlocks(List<AlertItemBO> alerts) {
final blocks = <Widget>[]; final blocks = <Widget>[];
for (final alert in alerts) { for (final alert in alerts) {
final location = [alert.roomName, alert.deviceName] final location = [alert.deviceLocation, alert.deviceCode + alert.deviceName]
.where((s) => s.isNotEmpty) .where((s) => s.isNotEmpty)
.join('·'); .join('·');
......
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:smart_hotel_app/utils/deal_utils.dart';
import 'package:smart_hotel_app/views/home/index/cubit/home_index_cubit.dart'; import 'package:smart_hotel_app/views/home/index/cubit/home_index_cubit.dart';
import 'package:smart_hotel_app/utils/toast_utils.dart'; import 'package:smart_hotel_app/utils/toast_utils.dart';
...@@ -46,15 +47,13 @@ class TemperatureBlock extends StatelessWidget { ...@@ -46,15 +47,13 @@ class TemperatureBlock extends StatelessWidget {
Container( Container(
width: 66.w, width: 66.w,
height: 54.h, height: 54.h,
alignment: Alignment.center,
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color.fromRGBO(235, 244, 255, 1.0), color: const Color.fromRGBO(235, 244, 255, 1.0),
borderRadius: BorderRadius.circular(12.r), borderRadius: BorderRadius.circular(12.r),
), ),
child: Icon( child: Image.asset('lib/assets/icon/thermostat_bg.png',
Icons.thermostat_outlined, width: 16.w, height: 32.h),
color: const Color.fromRGBO(66, 165, 245, 1.0),
size: 40.sp,
),
), ),
SizedBox(width: 16.w), SizedBox(width: 16.w),
Expanded( Expanded(
...@@ -107,7 +106,8 @@ class TemperatureBlock extends StatelessWidget { ...@@ -107,7 +106,8 @@ class TemperatureBlock extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text( Text(
currentTemperature.isNotEmpty ? currentTemperature : '--℃', currentTemperature.isNotEmpty ? DealUtils.
cleanNumber(currentTemperature) : '--℃',
style: TextStyle( style: TextStyle(
fontSize: 40.sp, fontSize: 40.sp,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
...@@ -130,7 +130,8 @@ class TemperatureBlock extends StatelessWidget { ...@@ -130,7 +130,8 @@ class TemperatureBlock extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text( Text(
currentVoltage.isNotEmpty ? currentVoltage : '--V', currentVoltage.isNotEmpty ?
DealUtils.cleanNumber(currentVoltage) : '--V',
style: TextStyle( style: TextStyle(
fontSize: 40.sp, fontSize: 40.sp,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
...@@ -153,7 +154,8 @@ class TemperatureBlock extends StatelessWidget { ...@@ -153,7 +154,8 @@ class TemperatureBlock extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text( Text(
currentCurrent.isNotEmpty ? currentCurrent : '--A', currentCurrent.isNotEmpty ? DealUtils.
cleanNumber(currentCurrent) : '--A',
style: TextStyle( style: TextStyle(
fontSize: 40.sp, fontSize: 40.sp,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
...@@ -187,20 +189,12 @@ class TemperatureBlock extends StatelessWidget { ...@@ -187,20 +189,12 @@ class TemperatureBlock extends StatelessWidget {
), ),
SizedBox(width: 8.w), SizedBox(width: 8.w),
Text( Text(
waitTime, waitTime.isNotEmpty ? waitTime : '--',
style: TextStyle( style: TextStyle(
fontSize: 24.sp, fontSize: 24.sp,
color: const Color.fromRGBO(100, 116, 139, 1.0), color: const Color.fromRGBO(100, 116, 139, 1.0),
), ),
), ),
// Text(
// alarmLevel,
// style: TextStyle(
// fontSize: 24.sp,
// fontWeight: FontWeight.bold,
// color: const Color.fromRGBO(255, 77, 79, 1.0),
// ),
// ),
], ],
), ),
GestureDetector( GestureDetector(
......
...@@ -46,22 +46,15 @@ class VoltageOperateBlock extends StatelessWidget { ...@@ -46,22 +46,15 @@ class VoltageOperateBlock extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Container( Container(
width: 64.w, width: 66.w,
height: 54.h, height: 54.h,
alignment: Alignment.center,
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color.fromRGBO(235, 244, 255, 1.0), color: const Color.fromRGBO(235, 244, 255, 1.0),
borderRadius: BorderRadius.circular(12.r), borderRadius: BorderRadius.circular(12.r),
// border: Border.all(
// color: const Color.fromRGBO(66, 165, 245, 1.0),
// width: 2.w,
// style: BorderStyle.solid,
// ),
),
child: Icon(
Icons.thermostat_outlined,
color: const Color.fromRGBO(66, 165, 245, 1.0),
size: 40.sp,
), ),
child: Image.asset('lib/assets/icon/thermostat_bg.png',
width: 16.w, height: 32.h),
), ),
SizedBox(width: 16.w), SizedBox(width: 16.w),
Expanded( Expanded(
...@@ -103,7 +96,7 @@ class VoltageOperateBlock extends StatelessWidget { ...@@ -103,7 +96,7 @@ class VoltageOperateBlock extends StatelessWidget {
width: double.infinity, width: double.infinity,
margin: EdgeInsets.only(left: 80.w, bottom: 20.h), margin: EdgeInsets.only(left: 80.w, bottom: 20.h),
child: Text( child: Text(
'电压${voltageValue}V (正常≤${normalVoltage}V)·$voltageWaitTime', '电压${voltageValue} (正常≤${normalVoltage}V)·$voltageWaitTime',
style: TextStyle( style: TextStyle(
fontSize: 24.sp, fontSize: 24.sp,
color: const Color.fromRGBO(100, 116, 139, 1.0), color: const Color.fromRGBO(100, 116, 139, 1.0),
......
...@@ -12,9 +12,6 @@ class InspectionCubit extends Cubit<InspectionState> { ...@@ -12,9 +12,6 @@ class InspectionCubit extends Cubit<InspectionState> {
static const int pageSize = 20; static const int pageSize = 20;
/// 完整房间列表缓存,用于在切换 Tab 时按 runStatus 重新过滤
List<InspectionRoomBO> _allRoomsRaw = const [];
InspectionCubit({ InspectionCubit({
InspectionRoomService? roomService, InspectionRoomService? roomService,
InspectionDeviceService? deviceService, InspectionDeviceService? deviceService,
...@@ -30,21 +27,22 @@ class InspectionCubit extends Cubit<InspectionState> { ...@@ -30,21 +27,22 @@ class InspectionCubit extends Cubit<InspectionState> {
Future<void> initData() async { Future<void> initData() async {
try { try {
final rooms = await _roomService.getRooms(); final rooms = await _roomService.getRooms();
_allRoomsRaw = rooms; final tabCounts = _computeTabCounts(rooms.runStatus);
final roomGroups = rooms.roomGroups;
final tabCounts = _computeTabCounts(rooms);
final filteredRooms = _filterRooms(rooms, state.selectedTab);
// 仅当首次进入(之前没有选过房间)时,默认选中过滤后的第一个房间 // 默认选中第一个区域、第一个房间
final initialRoomIds = filteredRooms.isNotEmpty final defaultAreaIndex = roomGroups.isNotEmpty ? 0 : 0;
? [filteredRooms.entries.first.key] final defaultRoomId = roomGroups.isNotEmpty &&
: <int>[]; roomGroups[defaultAreaIndex].rooms.isNotEmpty
? roomGroups[defaultAreaIndex].rooms.first.roomId
: null;
emit(state.copyWith( emit(state.copyWith(
isLoading: false, isLoading: false,
error: null, error: null,
allRooms: filteredRooms, roomGroups: roomGroups,
selectedRoomIds: initialRoomIds, selectedAreaIndex: defaultAreaIndex,
selectedRoomId: defaultRoomId,
tabCounts: tabCounts, tabCounts: tabCounts,
)); ));
} catch (e) { } catch (e) {
...@@ -55,20 +53,30 @@ class InspectionCubit extends Cubit<InspectionState> { ...@@ -55,20 +53,30 @@ class InspectionCubit extends Cubit<InspectionState> {
} }
} }
/// 从顶层 runStatus 汇总计算各 tab 数量
Map<DeviceStatusTab, int> _computeTabCounts(
InspectionRoomRunStatusBO runStatus) {
final total =
runStatus.normal + runStatus.warning + runStatus.offline + runStatus.fault;
return {
DeviceStatusTab.total: total,
DeviceStatusTab.on: runStatus.normal,
DeviceStatusTab.off: 0,
DeviceStatusTab.offline: runStatus.offline,
DeviceStatusTab.alarm: runStatus.warning,
DeviceStatusTab.fault: runStatus.fault,
};
}
/// 供 PagingController.fetchPage 回调使用 /// 供 PagingController.fetchPage 回调使用
Future<List<InspectionDevice>> fetchPage(int pageKey) async { Future<List<InspectionDevice>> fetchPage(int pageKey) async {
final roomId = state.selectedRoomIds.length == 1
? state.selectedRoomIds.first
: null;
final result = await _deviceService.getList( final result = await _deviceService.getList(
pageSize: pageSize, pageSize: pageSize,
pageNum: pageKey, pageNum: pageKey,
roomId: roomId, roomId: state.selectedRoomId,
runStatus: state.selectedTab.runStatus, runStatus: state.selectedTab.runStatus,
); );
// 首页时记录总数,供翻页判断是否到底
if (pageKey == 1) { if (pageKey == 1) {
emit(state.copyWith(totalCount: result.total)); emit(state.copyWith(totalCount: result.total));
} }
...@@ -78,87 +86,20 @@ class InspectionCubit extends Cubit<InspectionState> { ...@@ -78,87 +86,20 @@ class InspectionCubit extends Cubit<InspectionState> {
.toList(); .toList();
} }
Map<DeviceStatusTab, int> _computeTabCounts(List<InspectionRoomBO> rooms) {
int normal = 0;
int warning = 0;
int offline = 0;
int fault = 0;
for (final room in rooms) {
normal += room.runStatus.normal;
warning += room.runStatus.warning;
offline += room.runStatus.offline;
fault += room.runStatus.fault;
}
return {
DeviceStatusTab.total: normal + warning + offline + fault,
DeviceStatusTab.on: normal,
DeviceStatusTab.off: 0,
DeviceStatusTab.offline: offline,
DeviceStatusTab.alarm: warning,
DeviceStatusTab.fault: fault,
};
}
/// 根据 Tab 的 runStatus 过滤房间:
/// - total / runStatus 为 null → 返回全部
/// - 其他 → 仅保留对应状态计数 > 0 的房间
Map<int, String> _filterRooms(
List<InspectionRoomBO> rooms, DeviceStatusTab tab) {
final result = <int, String>{};
for (final room in rooms) {
if (_roomMatchesTab(room, tab)) {
result[room.roomId] = room.roomNumber;
}
}
return result;
}
bool _roomMatchesTab(InspectionRoomBO room, DeviceStatusTab tab) {
switch (tab) {
case DeviceStatusTab.total:
return true;
case DeviceStatusTab.on:
return room.runStatus.normal > 0;
case DeviceStatusTab.off:
case DeviceStatusTab.offline:
return room.runStatus.offline > 0;
case DeviceStatusTab.alarm:
return room.runStatus.warning > 0;
case DeviceStatusTab.fault:
return room.runStatus.fault > 0;
}
}
void selectTab(DeviceStatusTab tab) { void selectTab(DeviceStatusTab tab) {
final filteredRooms = _filterRooms(_allRoomsRaw, tab); emit(state.copyWith(selectedTab: tab));
}
// 过滤后剔除已不存在的已选房间;若全被剔除则默认选第一个
final keptSelection = state.selectedRoomIds
.where(filteredRooms.containsKey)
.toList();
final newSelectedRoomIds = keptSelection.isNotEmpty
? keptSelection
: (filteredRooms.isNotEmpty
? [filteredRooms.entries.first.key]
: <int>[]);
void selectArea(int index) {
if (index < 0 || index >= state.roomGroups.length) return;
final rooms = state.roomGroups[index].rooms;
emit(state.copyWith( emit(state.copyWith(
selectedTab: tab, selectedAreaIndex: index,
allRooms: filteredRooms, selectedRoomId: rooms.isNotEmpty ? rooms.first.roomId : null,
selectedRoomIds: newSelectedRoomIds,
)); ));
} }
void toggleRoom(int roomId) { void selectRoom(int roomId) {
final List<int> newSelected = List.from(state.selectedRoomIds); emit(state.copyWith(selectedRoomId: roomId));
if (newSelected.contains(roomId)) {
newSelected.remove(roomId);
} else {
newSelected.clear();
newSelected.add(roomId);
}
emit(state.copyWith(selectedRoomIds: newSelected));
} }
} }
\ No newline at end of file
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:smart_hotel_app/models/bo/inspection_device_bo.dart'; import 'package:smart_hotel_app/models/bo/inspection_device_bo.dart';
import 'package:smart_hotel_app/models/bo/inspection_room_bo.dart';
enum DeviceStatusTab { total, on, off, offline, alarm, fault } enum DeviceStatusTab { total, on, off, offline, alarm, fault }
...@@ -61,6 +62,8 @@ class InspectionDevice { ...@@ -61,6 +62,8 @@ class InspectionDevice {
static IconData _mapIcon(String iconType) { static IconData _mapIcon(String iconType) {
switch (iconType) { switch (iconType) {
case 'curtain':
return Icons.blinds_outlined;
case 'gateway': case 'gateway':
return Icons.wifi; return Icons.wifi;
case 'sensor': case 'sensor':
...@@ -107,8 +110,9 @@ class InspectionState extends Equatable { ...@@ -107,8 +110,9 @@ class InspectionState extends Equatable {
final bool isLoading; final bool isLoading;
final String? error; final String? error;
final DeviceStatusTab selectedTab; final DeviceStatusTab selectedTab;
final List<int> selectedRoomIds; final int selectedAreaIndex;
final Map<int, String> allRooms; final int? selectedRoomId;
final List<InspectionRoomGroupsBO> roomGroups;
final Map<DeviceStatusTab, int> tabCounts; final Map<DeviceStatusTab, int> tabCounts;
final int totalCount; final int totalCount;
...@@ -116,8 +120,9 @@ class InspectionState extends Equatable { ...@@ -116,8 +120,9 @@ class InspectionState extends Equatable {
this.isLoading = false, this.isLoading = false,
this.error, this.error,
this.selectedTab = DeviceStatusTab.total, this.selectedTab = DeviceStatusTab.total,
this.selectedRoomIds = const [], this.selectedAreaIndex = 0,
this.allRooms = const {}, this.selectedRoomId,
this.roomGroups = const [],
this.tabCounts = const { this.tabCounts = const {
DeviceStatusTab.total: 0, DeviceStatusTab.total: 0,
DeviceStatusTab.on: 0, DeviceStatusTab.on: 0,
...@@ -133,8 +138,9 @@ class InspectionState extends Equatable { ...@@ -133,8 +138,9 @@ class InspectionState extends Equatable {
bool? isLoading, bool? isLoading,
String? error, String? error,
DeviceStatusTab? selectedTab, DeviceStatusTab? selectedTab,
List<int>? selectedRoomIds, int? selectedAreaIndex,
Map<int, String>? allRooms, int? selectedRoomId,
List<InspectionRoomGroupsBO>? roomGroups,
Map<DeviceStatusTab, int>? tabCounts, Map<DeviceStatusTab, int>? tabCounts,
int? totalCount, int? totalCount,
}) { }) {
...@@ -142,8 +148,9 @@ class InspectionState extends Equatable { ...@@ -142,8 +148,9 @@ class InspectionState extends Equatable {
isLoading: isLoading ?? this.isLoading, isLoading: isLoading ?? this.isLoading,
error: error, error: error,
selectedTab: selectedTab ?? this.selectedTab, selectedTab: selectedTab ?? this.selectedTab,
selectedRoomIds: selectedRoomIds ?? this.selectedRoomIds, selectedAreaIndex: selectedAreaIndex ?? this.selectedAreaIndex,
allRooms: allRooms ?? this.allRooms, selectedRoomId: selectedRoomId ?? this.selectedRoomId,
roomGroups: roomGroups ?? this.roomGroups,
tabCounts: tabCounts ?? this.tabCounts, tabCounts: tabCounts ?? this.tabCounts,
totalCount: totalCount ?? this.totalCount, totalCount: totalCount ?? this.totalCount,
); );
...@@ -154,8 +161,9 @@ class InspectionState extends Equatable { ...@@ -154,8 +161,9 @@ class InspectionState extends Equatable {
isLoading, isLoading,
error, error,
selectedTab, selectedTab,
selectedRoomIds, selectedAreaIndex,
allRooms, selectedRoomId,
roomGroups,
tabCounts, tabCounts,
totalCount, totalCount,
]; ];
......
...@@ -33,7 +33,7 @@ class _InspectionDetailBodyState extends State<_InspectionDetailBody> { ...@@ -33,7 +33,7 @@ class _InspectionDetailBodyState extends State<_InspectionDetailBody> {
late final PagingController<int, InspectionDevice> _pagingController; late final PagingController<int, InspectionDevice> _pagingController;
late final InspectionCubit _cubit; late final InspectionCubit _cubit;
DeviceStatusTab? _lastTab; DeviceStatusTab? _lastTab;
List<int>? _lastRoomIds; int? _lastRoomId;
@override @override
void initState() { void initState() {
...@@ -55,9 +55,9 @@ class _InspectionDetailBodyState extends State<_InspectionDetailBody> { ...@@ -55,9 +55,9 @@ class _InspectionDetailBodyState extends State<_InspectionDetailBody> {
void _checkFilterChanged(InspectionState state) { void _checkFilterChanged(InspectionState state) {
if (_lastTab != state.selectedTab || if (_lastTab != state.selectedTab ||
_lastRoomIds != state.selectedRoomIds) { _lastRoomId != state.selectedRoomId) {
_lastTab = state.selectedTab; _lastTab = state.selectedTab;
_lastRoomIds = state.selectedRoomIds; _lastRoomId = state.selectedRoomId;
_pagingController.refresh(); _pagingController.refresh();
} }
} }
...@@ -138,9 +138,11 @@ class _InspectionDetailBodyState extends State<_InspectionDetailBody> { ...@@ -138,9 +138,11 @@ class _InspectionDetailBodyState extends State<_InspectionDetailBody> {
selectedTab: state.selectedTab, selectedTab: state.selectedTab,
tabCounts: state.tabCounts, tabCounts: state.tabCounts,
onTabSelected: cubit.selectTab, onTabSelected: cubit.selectTab,
selectedRoomIds: state.selectedRoomIds, selectedAreaIndex: state.selectedAreaIndex,
allRooms: state.allRooms, selectedRoomId: state.selectedRoomId,
onRoomToggle: cubit.toggleRoom, roomGroups: state.roomGroups,
onAreaSelected: cubit.selectArea,
onRoomSelected: cubit.selectRoom,
), ),
), ),
), ),
......
...@@ -87,7 +87,7 @@ class DeviceListItem extends StatelessWidget { ...@@ -87,7 +87,7 @@ class DeviceListItem extends StatelessWidget {
), ),
SizedBox(width: 4.w), SizedBox(width: 4.w),
Text( Text(
device.time, device.time.isNotEmpty ? device.time : '--',
style: TextStyle( style: TextStyle(
fontSize: 24.sp, fontSize: 24.sp,
color: const Color.fromRGBO(100, 116, 139, 1.0), color: const Color.fromRGBO(100, 116, 139, 1.0),
......
...@@ -68,6 +68,7 @@ class InspectionDeviceView extends StatelessWidget { ...@@ -68,6 +68,7 @@ class InspectionDeviceView extends StatelessWidget {
child: BlocBuilder<InspectionDeviceCubit, InspectionDeviceState>( child: BlocBuilder<InspectionDeviceCubit, InspectionDeviceState>(
builder: (context, state) { builder: (context, state) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
DeviceOverviewCard( DeviceOverviewCard(
deviceName: state.deviceName, deviceName: state.deviceName,
...@@ -82,6 +83,15 @@ class InspectionDeviceView extends StatelessWidget { ...@@ -82,6 +83,15 @@ class InspectionDeviceView extends StatelessWidget {
), ),
if (state.inspectionHistory.isNotEmpty) ...[ if (state.inspectionHistory.isNotEmpty) ...[
SizedBox(height: 20.h), SizedBox(height: 20.h),
Text(
'巡检历史 (${state.inspectionHistory.length})',
style: TextStyle(
fontSize: 26.sp,
fontWeight: FontWeight.bold,
color: const Color.fromRGBO(10, 13, 20, 1.0),
),
),
SizedBox(height: 10.h),
InspectionHistoryCard( InspectionHistoryCard(
historyList: state.inspectionHistory, historyList: state.inspectionHistory,
), ),
......
...@@ -37,10 +37,11 @@ class DeviceOverviewCard extends StatelessWidget { ...@@ -37,10 +37,11 @@ class DeviceOverviewCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Row(
spacing: 20.w,
children: [ children: [
Container( Container(
width: 100.w, width: 90.w,
height: 100.h, height: 90.h,
decoration: const BoxDecoration( decoration: const BoxDecoration(
color: Color.fromRGBO(235, 244, 255, 1.0), color: Color.fromRGBO(235, 244, 255, 1.0),
shape: BoxShape.circle, shape: BoxShape.circle,
...@@ -48,13 +49,13 @@ class DeviceOverviewCard extends StatelessWidget { ...@@ -48,13 +49,13 @@ class DeviceOverviewCard extends StatelessWidget {
child: Icon( child: Icon(
icon, icon,
color: const Color.fromRGBO(66, 165, 245, 1.0), color: const Color.fromRGBO(66, 165, 245, 1.0),
size: 50.sp, size: 40.sp,
), ),
), ),
SizedBox(width: 20.w),
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
spacing: 5.h,
children: [ children: [
Text( Text(
deviceName, deviceName,
...@@ -64,7 +65,6 @@ class DeviceOverviewCard extends StatelessWidget { ...@@ -64,7 +65,6 @@ class DeviceOverviewCard extends StatelessWidget {
color: const Color.fromRGBO(10, 13, 20, 1.0), color: const Color.fromRGBO(10, 13, 20, 1.0),
), ),
), ),
SizedBox(height: 12.h),
Text( Text(
deviceType, deviceType,
style: TextStyle( style: TextStyle(
...@@ -72,31 +72,30 @@ class DeviceOverviewCard extends StatelessWidget { ...@@ -72,31 +72,30 @@ class DeviceOverviewCard extends StatelessWidget {
color: const Color.fromRGBO(100, 116, 139, 1.0), color: const Color.fromRGBO(100, 116, 139, 1.0),
), ),
), ),
SizedBox(height: 16.h),
Container(
padding: EdgeInsets.symmetric(horizontal: 24.w, vertical: 10.h),
decoration: BoxDecoration(
color: deviceStatus == '1'
? const Color.fromRGBO(225, 245, 238, 1.0)
: const Color.fromRGBO(255, 235, 238, 1.0),
borderRadius: BorderRadius.circular(20.r),
),
child: Text(
deviceStatus == '1' ? '在线' : '离线',
style: TextStyle(
fontSize: 28.sp,
color: deviceStatus == '1'
? const Color.fromRGBO(26, 188, 156, 1.0)
: const Color.fromRGBO(231, 76, 60, 1.0),
fontWeight: FontWeight.bold,
),
),
),
], ],
), ),
), ),
], ],
), ),
Container(
margin: EdgeInsets.only(left: 110.w, top: 5.h),
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 4.h),
decoration: BoxDecoration(
color: deviceStatus == '1'
? const Color.fromRGBO(225, 245, 238, 1.0)
: const Color.fromRGBO(255, 235, 238, 1.0),
borderRadius: BorderRadius.circular(20.r),
),
child: Text(
deviceStatus == '1' ? '在线' : '离线',
style: TextStyle(
fontSize: 24.sp,
color: deviceStatus == '1'
? const Color.fromRGBO(209, 250, 229, 1.0)
: const Color.fromRGBO(231, 76, 60, 1.0),
),
),
),
SizedBox(height: 32.h), SizedBox(height: 32.h),
Text( Text(
'基本信息', '基本信息',
......
...@@ -24,15 +24,6 @@ class InspectionHistoryCard extends StatelessWidget { ...@@ -24,15 +24,6 @@ class InspectionHistoryCard extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text(
'巡检历史 (${historyList.length})',
style: TextStyle(
fontSize: 26.sp,
fontWeight: FontWeight.bold,
color: const Color.fromRGBO(10, 13, 20, 1.0),
),
),
SizedBox(height: 20.h),
...historyList.map((item) => _buildHistoryItem(item)).toList(), ...historyList.map((item) => _buildHistoryItem(item)).toList(),
], ],
), ),
...@@ -77,7 +68,7 @@ class InspectionHistoryCard extends StatelessWidget { ...@@ -77,7 +68,7 @@ class InspectionHistoryCard extends StatelessWidget {
spacing: 4.h, spacing: 4.h,
children: [ children: [
Text( Text(
item.inspectorName, item.inspectorName.isNotEmpty ? item.inspectorName : '--',
style: TextStyle( style: TextStyle(
fontSize: 26.sp, fontSize: 26.sp,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
...@@ -93,7 +84,7 @@ class InspectionHistoryCard extends StatelessWidget { ...@@ -93,7 +84,7 @@ class InspectionHistoryCard extends StatelessWidget {
), ),
SizedBox(width: 4.w), SizedBox(width: 4.w),
Text( Text(
item.inspectionTime, item.inspectionTime.isNotEmpty ? item.inspectionTime : '--',
style: TextStyle( style: TextStyle(
fontSize: 24.sp, fontSize: 24.sp,
color: const Color.fromRGBO(100, 116, 139, 1.0), color: const Color.fromRGBO(100, 116, 139, 1.0),
...@@ -106,21 +97,24 @@ class InspectionHistoryCard extends StatelessWidget { ...@@ -106,21 +97,24 @@ class InspectionHistoryCard extends StatelessWidget {
], ],
), ),
Container( Container(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 4.h), padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 2.h),
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color.fromRGBO(225, 245, 238, 1.0), color: const Color.fromRGBO(225, 245, 238, 1.0),
borderRadius: BorderRadius.circular(40.r), borderRadius: BorderRadius.circular(40.r),
), ),
child: Row( child: Row(
spacing: 4.w,
children: [ children: [
Icon( Icon(
Icons.check, Icons.check,
color: const Color.fromRGBO(26, 188, 156, 1.0), color: const Color.fromRGBO(26, 188, 156, 1.0),
size: 24.sp, size: 24.sp,
), ),
SizedBox(width: 4.w),
Text( Text(
item.result, item.result == '0' ? '巡检中' :
item.result == '1' ? '不通过' :
item.result == '2' ? '通过' :
item.result == '3' ? '警告' : '',
style: TextStyle( style: TextStyle(
fontSize: 24.sp, fontSize: 24.sp,
color: const Color.fromRGBO(26, 188, 156, 1.0), color: const Color.fromRGBO(26, 188, 156, 1.0),
......
...@@ -79,7 +79,8 @@ class InspectionItemsCard extends StatelessWidget { ...@@ -79,7 +79,8 @@ class InspectionItemsCard extends StatelessWidget {
), ),
SizedBox(width: 4.w), SizedBox(width: 4.w),
Text( Text(
item.result, // item.result,
'通过',
style: TextStyle( style: TextStyle(
fontSize: 24.sp, fontSize: 24.sp,
color: const Color.fromRGBO(26, 188, 156, 1.0), color: const Color.fromRGBO(26, 188, 156, 1.0),
......
...@@ -49,8 +49,8 @@ class InspectionTopologyCubit extends Cubit<InspectionTopologyState> { ...@@ -49,8 +49,8 @@ class InspectionTopologyCubit extends Cubit<InspectionTopologyState> {
final rootNode = _buildTopologyTree(treeNodes); final rootNode = _buildTopologyTree(treeNodes);
emit(InspectionTopologyState( emit(InspectionTopologyState(
deviceName: info.deviceName, deviceName: '${info.deviceLocation} ${info.deviceName}',
deviceModel: '${info.deviceCode}·${info.deviceLocation}', deviceModel: '${info.deviceCode}${info.deviceName}·${info.deviceLocation}',
topologyNodes: [rootNode], topologyNodes: [rootNode],
deviceCount: _countAllNodes(rootNode), deviceCount: _countAllNodes(rootNode),
connectionCount: _countConnections(rootNode), connectionCount: _countConnections(rootNode),
......
...@@ -48,7 +48,7 @@ class DeviceDetailDialog extends StatelessWidget { ...@@ -48,7 +48,7 @@ class DeviceDetailDialog extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Container( Container(
margin: EdgeInsets.only(top: 20.h), margin: EdgeInsets.only(top: 40.h),
width: 120.w, width: 120.w,
height: 8.h, height: 8.h,
decoration: BoxDecoration( decoration: BoxDecoration(
...@@ -65,14 +65,14 @@ class DeviceDetailDialog extends StatelessWidget { ...@@ -65,14 +65,14 @@ class DeviceDetailDialog extends StatelessWidget {
children: [ children: [
Container( Container(
width: 80.w, width: 80.w,
height: 80.h, height: 64.h,
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color.fromRGBO(239, 246, 255, 1), color: const Color.fromRGBO(239, 246, 255, 1),
borderRadius: BorderRadius.circular(20.r), borderRadius: BorderRadius.circular(8.r),
), ),
child: Icon( child: Icon(
_getDeviceIcon(node.name), _getDeviceIcon(node.name),
size: 80.sp, size: 40.sp,
color: const Color.fromRGBO(59, 130, 246, 1), color: const Color.fromRGBO(59, 130, 246, 1),
), ),
), ),
...@@ -102,15 +102,15 @@ class DeviceDetailDialog extends StatelessWidget { ...@@ -102,15 +102,15 @@ class DeviceDetailDialog extends StatelessWidget {
), ),
Container( Container(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(
horizontal: 24.w, horizontal: 16.w,
vertical: 12.h, vertical: 2.h,
), ),
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color.fromRGBO(209, 250, 229, 1), color: const Color.fromRGBO(209, 250, 229, 1),
borderRadius: BorderRadius.circular(40.r), borderRadius: BorderRadius.circular(40.r),
), ),
child: Text( child: Text(
node.statusText?? '-', node.statusText?? '--',
style: TextStyle( style: TextStyle(
fontSize: 24.sp, fontSize: 24.sp,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
...@@ -145,7 +145,7 @@ class DeviceDetailDialog extends StatelessWidget { ...@@ -145,7 +145,7 @@ class DeviceDetailDialog extends StatelessWidget {
), ),
), ),
Text( Text(
'${node.id}', node.id,
style: TextStyle( style: TextStyle(
fontSize: 24.sp, fontSize: 24.sp,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
...@@ -192,7 +192,7 @@ class DeviceDetailDialog extends StatelessWidget { ...@@ -192,7 +192,7 @@ class DeviceDetailDialog extends StatelessWidget {
} }
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: const Color.fromRGBO(59, 130, 246, 1), backgroundColor: const Color.fromRGBO(73, 149, 234, 1),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(40.r), borderRadius: BorderRadius.circular(40.r),
), ),
......
...@@ -26,7 +26,7 @@ class DeviceHeaderWidget extends StatelessWidget { ...@@ -26,7 +26,7 @@ class DeviceHeaderWidget extends StatelessWidget {
children: [ children: [
Container( Container(
width: 64.w, width: 64.w,
height: 64.h, height: 53.h,
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color.fromRGBO(219, 234, 254, 1), color: const Color.fromRGBO(219, 234, 254, 1),
borderRadius: BorderRadius.circular(20.r), borderRadius: BorderRadius.circular(20.r),
......
...@@ -56,6 +56,7 @@ class DeviceListCubit extends Cubit<DeviceListState> { ...@@ -56,6 +56,7 @@ class DeviceListCubit extends Cubit<DeviceListState> {
: (row.onlineStatus == '1' ? '正常' : '离线'), : (row.onlineStatus == '1' ? '正常' : '离线'),
color: row.statusTag.color, color: row.statusTag.color,
lastUpdate: row.subtitle.text, lastUpdate: row.subtitle.text,
deviceTypeIcon: row.deviceTypeIcon,
); );
}).toList(); }).toList();
} }
......
...@@ -45,6 +45,7 @@ class DeviceInfo extends Equatable { ...@@ -45,6 +45,7 @@ class DeviceInfo extends Equatable {
final String status; final String status;
final String lastUpdate; final String lastUpdate;
final String color; final String color;
final String deviceTypeIcon;
const DeviceInfo({ const DeviceInfo({
this.deviceId = 0, this.deviceId = 0,
...@@ -55,11 +56,12 @@ class DeviceInfo extends Equatable { ...@@ -55,11 +56,12 @@ class DeviceInfo extends Equatable {
this.status = '', this.status = '',
this.lastUpdate = '', this.lastUpdate = '',
this.color = '', this.color = '',
this.deviceTypeIcon = '',
}); });
@override @override
List<Object?> get props => [deviceId, name, room, type, online, List<Object?> get props => [deviceId, name, room, type, online,
status, lastUpdate, color]; status, lastUpdate, color, deviceTypeIcon];
} }
class DeviceListState extends Equatable { class DeviceListState extends Equatable {
......
...@@ -10,29 +10,20 @@ class DeviceCard extends StatelessWidget { ...@@ -10,29 +10,20 @@ class DeviceCard extends StatelessWidget {
required this.device, required this.device,
}); });
IconData _getIconForType(DeviceTypeEnum type) { IconData _getIconForType(String deviceTypeIcon) {
switch (type) { switch (deviceTypeIcon) {
case DeviceTypeEnum.smartBreaker: case 'ac':
return Icons.power; return Icons.air_outlined;
case DeviceTypeEnum.guestControl: case 'light':
return Icons.settings_remote; return Icons.light_outlined;
case DeviceTypeEnum.network: case 'tv':
return Icons.tv_outlined;
case 'curtain':
return Icons.blinds_outlined;
case 'wifi':
return Icons.wifi; return Icons.wifi;
default: default:
return Icons.devices; return Icons.devices_other;
}
}
Color _getIconColor(DeviceTypeEnum type) {
switch (type) {
case DeviceTypeEnum.smartBreaker:
return const Color.fromRGBO(245, 158, 11, 1);
case DeviceTypeEnum.guestControl:
return const Color.fromRGBO(59, 130, 246, 1);
case DeviceTypeEnum.network:
return const Color.fromRGBO(16, 185, 129, 1);
default:
return const Color.fromRGBO(107, 114, 128, 1);
} }
} }
...@@ -62,14 +53,14 @@ class DeviceCard extends StatelessWidget { ...@@ -62,14 +53,14 @@ class DeviceCard extends StatelessWidget {
children: [ children: [
Container( Container(
width: 64.w, width: 64.w,
height: 58.h, height: 53.h,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.r), borderRadius: BorderRadius.circular(8.r),
color: _getIconColor(device.type).withOpacity(0.1), color: const Color.fromRGBO(229, 241, 255, 1),
), ),
child: Icon( child: Icon(
_getIconForType(device.type), _getIconForType(device.deviceTypeIcon),
color: _getIconColor(device.type), color: const Color.fromRGBO(73, 149, 234, 1),
size: 36.sp, size: 36.sp,
), ),
), ),
...@@ -93,14 +84,14 @@ class DeviceCard extends StatelessWidget { ...@@ -93,14 +84,14 @@ class DeviceCard extends StatelessWidget {
Text( Text(
_getTypeLabel(device.type), _getTypeLabel(device.type),
style: TextStyle( style: TextStyle(
fontSize: 20.sp, fontSize: 24.sp,
color: const Color.fromRGBO(107, 114, 128, 1), color: const Color.fromRGBO(107, 114, 128, 1),
), ),
), ),
Text( Text(
'房间 ${device.room}', '房间 ${device.room}',
style: TextStyle( style: TextStyle(
fontSize: 20.sp, fontSize: 24.sp,
color: const Color.fromRGBO(107, 114, 128, 1), color: const Color.fromRGBO(107, 114, 128, 1),
), ),
), ),
...@@ -122,38 +113,6 @@ class DeviceCard extends StatelessWidget { ...@@ -122,38 +113,6 @@ class DeviceCard extends StatelessWidget {
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
), ),
// Column(
// crossAxisAlignment: CrossAxisAlignment.end,
// children: [
// Container(
// padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h),
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(12.r),
// color: device.online
// ? const Color.fromRGBO(220, 252, 231, 1)
// : const Color.fromRGBO(254, 226, 226, 1),
// ),
// child: Text(
// device.status,
// style: TextStyle(
// fontSize: 20.sp,
// color: device.online
// ? const Color.fromRGBO(21, 128, 61, 1)
// : const Color.fromRGBO(220, 38, 38, 1),
// fontWeight: FontWeight.bold,
// ),
// ),
// ),
// SizedBox(height: 8.h),
// Text(
// device.lastUpdate,
// style: TextStyle(
// fontSize: 18.sp,
// color: const Color.fromRGBO(156, 163, 175, 1),
// ),
// ),
// ],
// ),
], ],
), ),
); );
......
...@@ -35,12 +35,13 @@ class DeviceFilterTab extends StatelessWidget { ...@@ -35,12 +35,13 @@ class DeviceFilterTab extends StatelessWidget {
color: Colors.white, color: Colors.white,
), ),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, // mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: tabs.map((tab) { children: tabs.map((tab) {
final isSelected = selectedType == tab['type']; final isSelected = selectedType == tab['type'];
return GestureDetector( return GestureDetector(
onTap: () => onSelect(tab['type'] as DeviceTypeEnum), onTap: () => onSelect(tab['type'] as DeviceTypeEnum),
child: Container( child: Container(
margin: EdgeInsets.only(right: 16.w),
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 8.h), padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 8.h),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.r), borderRadius: BorderRadius.circular(8.r),
......
...@@ -66,23 +66,24 @@ class EnergyStatsCardBlock extends StatelessWidget { ...@@ -66,23 +66,24 @@ class EnergyStatsCardBlock extends StatelessWidget {
Widget _buildCard(String value, String label, String? change, bool? isDown, Color valueColor) { Widget _buildCard(String value, String label, String? change, bool? isDown, Color valueColor) {
return Container( return Container(
padding: EdgeInsets.symmetric(vertical: 24.h), height: 128.h,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.white,
borderRadius: BorderRadius.circular(20.r), borderRadius: BorderRadius.circular(20.r),
), ),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 4.h,
children: [ children: [
Text( Text(
value, value,
style: TextStyle( style: TextStyle(
color: valueColor, color: valueColor,
fontSize: 40.sp, fontSize: 36.sp,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
SizedBox(height: 4.h),
Text( Text(
label, label,
style: TextStyle( style: TextStyle(
...@@ -90,7 +91,6 @@ class EnergyStatsCardBlock extends StatelessWidget { ...@@ -90,7 +91,6 @@ class EnergyStatsCardBlock extends StatelessWidget {
fontSize: 24.sp, fontSize: 24.sp,
), ),
), ),
SizedBox(height: 4.h),
if (change != null) if (change != null)
Row( Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
...@@ -113,8 +113,6 @@ class EnergyStatsCardBlock extends StatelessWidget { ...@@ -113,8 +113,6 @@ class EnergyStatsCardBlock extends StatelessWidget {
), ),
], ],
) )
else
SizedBox(height: 24.h),
], ],
), ),
); );
......
...@@ -25,20 +25,40 @@ class HourlyChartBlock extends StatelessWidget { ...@@ -25,20 +25,40 @@ class HourlyChartBlock extends StatelessWidget {
double get _chartInterval => _chartMaxY / 5; double get _chartInterval => _chartMaxY / 5;
double get _bottomInterval { Set<int> get _visibleLabelIndices {
final len = hourlyData.length; final length = hourlyData.length;
if (len <= 1) return 1; if (length <= 10) {
// 最多显示约 5 个标签,按间距计算 return Set.from(List.generate(length, (i) => i));
return (len / 5).ceilToDouble(); }
final isOdd = length % 2 == 1;
if (isOdd) {
// 奇数:7 个标签,首尾都显示,中间 5 个均分 6 段
final step = (length - 1) / 6;
final indices = <int>{};
for (int i = 0; i <= 6; i++) {
indices.add((step * i).round());
}
return indices;
} else {
// 偶数:6 个标签,显示首和倒数第二个,中间 4 个均分 5 段
final step = (length - 2) / 5;
final indices = <int>{};
for (int i = 0; i <= 5; i++) {
indices.add((step * i).round());
}
return indices;
}
} }
String _formatTimeLabel(int index) { String _formatTimeLabel(int index) {
if (index < 0 || index >= timeLabels.length) return ''; if (index < 0 || index >= timeLabels.length) return '';
final label = timeLabels[index]; final label = timeLabels[index];
return label;
// "00:00:00" / "00:00" → 只取整点小时,去掉前导 0 // "00:00:00" / "00:00" → 只取整点小时,去掉前导 0
final parts = label.split(':'); // final parts = label.split(':');
final hour = parts.isNotEmpty ? parts[0] : label; // final hour = parts.isNotEmpty ? parts[0] : label;
return '${int.tryParse(hour) ?? 0}'; // return '${int.tryParse(hour) ?? 0}';
} }
@override @override
...@@ -81,10 +101,14 @@ class HourlyChartBlock extends StatelessWidget { ...@@ -81,10 +101,14 @@ class HourlyChartBlock extends StatelessWidget {
bottomTitles: AxisTitles( bottomTitles: AxisTitles(
sideTitles: SideTitles( sideTitles: SideTitles(
showTitles: true, showTitles: true,
interval: _bottomInterval, interval: 1,
reservedSize: 30, reservedSize: 30,
getTitlesWidget: (value, meta) { getTitlesWidget: (value, meta) {
final label = _formatTimeLabel(value.toInt()); final i = value.toInt();
if (!_visibleLabelIndices.contains(i)) {
return const SizedBox.shrink();
}
final label = _formatTimeLabel(i);
return Padding( return Padding(
padding: EdgeInsets.only(top: 8.h), padding: EdgeInsets.only(top: 8.h),
child: Text( child: Text(
......
...@@ -45,6 +45,17 @@ class WeeklyPowerChart extends StatelessWidget { ...@@ -45,6 +45,17 @@ class WeeklyPowerChart extends StatelessWidget {
return _timeLabels[index]; return _timeLabels[index];
} }
String _formatLeftTitle(double value) {
return '${value.toInt()}';
}
/// 根据最大数值的位数动态计算左侧保留宽度
double get _leftReservedSize {
final maxLabel = _maxY.toInt().toString();
// 每位数字约 10px(22.sp 字号)
return maxLabel.length * 8;
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GestureDetector( return GestureDetector(
...@@ -142,14 +153,16 @@ class WeeklyPowerChart extends StatelessWidget { ...@@ -142,14 +153,16 @@ class WeeklyPowerChart extends StatelessWidget {
interval: _horizontalInterval, interval: _horizontalInterval,
getTitlesWidget: (value, meta) { getTitlesWidget: (value, meta) {
return Text( return Text(
'${value.toInt()}', _formatLeftTitle(value),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle( style: TextStyle(
color: const Color.fromRGBO(156, 163, 175, 1), color: const Color.fromRGBO(156, 163, 175, 1),
fontSize: 22.sp, fontSize: 22.sp,
), ),
); );
}, },
reservedSize: 50, reservedSize: _leftReservedSize,
), ),
), ),
), ),
......
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:smart_hotel_app/models/bo/room_bo.dart';
import 'package:smart_hotel_app/repositories/room_repository.dart'; import 'package:smart_hotel_app/repositories/room_repository.dart';
import 'package:smart_hotel_app/services/room_service.dart'; import 'package:smart_hotel_app/services/room_service.dart';
import 'package:smart_hotel_app/views/report/room/cubit/room_report_state.dart'; import 'package:smart_hotel_app/views/report/room/cubit/room_report_state.dart';
...@@ -17,13 +18,26 @@ class RoomReportCubit extends Cubit<RoomReportState> { ...@@ -17,13 +18,26 @@ class RoomReportCubit extends Cubit<RoomReportState> {
try { try {
final overview = await _service.getOverview(floorId); final overview = await _service.getOverview(floorId);
final activeIndex = overview.floorList.indexWhere((f) => f.isActive); final activeIndex = overview.floorList.indexWhere((f) => f.isActive);
// 过滤掉每个房间 deviceList 中名称含"分控"的设备
final filteredDetailRooms = overview.roomDetailList.map((room) {
return RoomDetailListBO(
roomBasicInfo: room.roomBasicInfo,
statusTags: room.statusTags,
deviceList: room.deviceList
.where((d) => !d.deviceName.contains('分控'))
.toList(),
);
}).toList();
emit(state.copyWith( emit(state.copyWith(
isLoading: false, isLoading: false,
summary: overview.summary, summary: overview.summary,
floors: overview.floorList, floors: overview.floorList,
selectedFloorIndex: activeIndex >= 0 ? activeIndex : 0, selectedFloorIndex: activeIndex >= 0 ? activeIndex : 0,
roomGrid: overview.roomGrid, roomGrid: overview.roomGrid,
detailRooms: overview.roomDetailList, detailRooms: filteredDetailRooms,
selectedRoomId: null,
)); ));
} catch (e) { } catch (e) {
emit(state.copyWith(isLoading: false, error: e.toString())); emit(state.copyWith(isLoading: false, error: e.toString()));
...@@ -34,7 +48,12 @@ class RoomReportCubit extends Cubit<RoomReportState> { ...@@ -34,7 +48,12 @@ class RoomReportCubit extends Cubit<RoomReportState> {
if (state.selectedFloorIndex == index) return; if (state.selectedFloorIndex == index) return;
final floorId = state.floors[index].floorId; final floorId = state.floors[index].floorId;
emit(state.copyWith(selectedFloorIndex: index)); emit(state.copyWith(selectedFloorIndex: index, selectedRoomId: null));
loadData(floorId: floorId); loadData(floorId: floorId);
} }
void selectRoom(int roomId) {
final newId = state.selectedRoomId == roomId ? null : roomId;
emit(state.copyWith(selectedRoomId: newId));
}
} }
...@@ -2,6 +2,8 @@ import 'package:equatable/equatable.dart'; ...@@ -2,6 +2,8 @@ import 'package:equatable/equatable.dart';
import 'package:smart_hotel_app/models/bo/room_bo.dart'; import 'package:smart_hotel_app/models/bo/room_bo.dart';
class RoomReportState extends Equatable { class RoomReportState extends Equatable {
static const _nothing = Object();
final bool isLoading; final bool isLoading;
final String? error; final String? error;
final RoomOverviewSummaryBO summary; final RoomOverviewSummaryBO summary;
...@@ -9,6 +11,7 @@ class RoomReportState extends Equatable { ...@@ -9,6 +11,7 @@ class RoomReportState extends Equatable {
final int selectedFloorIndex; final int selectedFloorIndex;
final List<RoomGridBO> roomGrid; final List<RoomGridBO> roomGrid;
final List<RoomDetailListBO> detailRooms; final List<RoomDetailListBO> detailRooms;
final int? selectedRoomId;
const RoomReportState({ const RoomReportState({
this.isLoading = false, this.isLoading = false,
...@@ -18,6 +21,7 @@ class RoomReportState extends Equatable { ...@@ -18,6 +21,7 @@ class RoomReportState extends Equatable {
this.selectedFloorIndex = 0, this.selectedFloorIndex = 0,
this.roomGrid = const [], this.roomGrid = const [],
this.detailRooms = const [], this.detailRooms = const [],
this.selectedRoomId,
}); });
RoomReportState copyWith({ RoomReportState copyWith({
...@@ -28,6 +32,7 @@ class RoomReportState extends Equatable { ...@@ -28,6 +32,7 @@ class RoomReportState extends Equatable {
int? selectedFloorIndex, int? selectedFloorIndex,
List<RoomGridBO>? roomGrid, List<RoomGridBO>? roomGrid,
List<RoomDetailListBO>? detailRooms, List<RoomDetailListBO>? detailRooms,
Object? selectedRoomId = _nothing,
bool clearError = false, bool clearError = false,
}) { }) {
return RoomReportState( return RoomReportState(
...@@ -38,6 +43,7 @@ class RoomReportState extends Equatable { ...@@ -38,6 +43,7 @@ class RoomReportState extends Equatable {
selectedFloorIndex: selectedFloorIndex ?? this.selectedFloorIndex, selectedFloorIndex: selectedFloorIndex ?? this.selectedFloorIndex,
roomGrid: roomGrid ?? this.roomGrid, roomGrid: roomGrid ?? this.roomGrid,
detailRooms: detailRooms ?? this.detailRooms, detailRooms: detailRooms ?? this.detailRooms,
selectedRoomId: identical(selectedRoomId, _nothing) ? this.selectedRoomId : selectedRoomId as int?,
); );
} }
...@@ -50,5 +56,6 @@ class RoomReportState extends Equatable { ...@@ -50,5 +56,6 @@ class RoomReportState extends Equatable {
selectedFloorIndex, selectedFloorIndex,
roomGrid, roomGrid,
detailRooms, detailRooms,
selectedRoomId,
]; ];
} }
...@@ -59,52 +59,63 @@ class ReportRoomDetailView extends StatelessWidget { ...@@ -59,52 +59,63 @@ class ReportRoomDetailView extends StatelessWidget {
} }
return SingleChildScrollView( return SingleChildScrollView(
child: Column( child: Padding(
children: [ padding: EdgeInsets.symmetric(horizontal: 28.w),
Padding( child: Column(
padding: EdgeInsets.symmetric(horizontal: 28.w), children: [
child: Column( SizedBox(height: 10.h),
children: [ Container(
SizedBox(height: 10.h), decoration: BoxDecoration(
if (state.floors.isNotEmpty) color: Colors.white,
Container( borderRadius: BorderRadius.circular(24.r),
decoration: BoxDecoration( ),
color: Colors.white, child: Column(
borderRadius: BorderRadius.vertical(top: Radius.circular(24.r)), children: [
), if (state.floors.isNotEmpty)
child: FloorSelector( FloorSelector(
floorNames: state.floors.map((f) => f.floorName).toList(), floorNames: state.floors.map((f) => f.floorName).toList(),
selectedIndex: state.selectedFloorIndex, selectedIndex: state.selectedFloorIndex,
), ),
), if (state.floors.isNotEmpty && state.selectedFloorIndex < state.floors.length)
if (state.floors.isNotEmpty && state.selectedFloorIndex < state.floors.length) Padding(
Container( padding: EdgeInsets.only(bottom: 28.h),
decoration: BoxDecoration( child: RoomGrid(
color: Colors.white, rooms: state.roomGrid,
borderRadius: BorderRadius.vertical(bottom: Radius.circular(24.r)), selectedRoomId: state.selectedRoomId,
onRoomTap: (roomId) {
context.read<RoomReportCubit>().selectRoom(roomId);
},
),
), ),
padding: EdgeInsets.only(bottom: 28.w), Divider(
child: RoomGrid(rooms: state.roomGrid), height: 1.h,
thickness: 0.5,
color: const Color.fromRGBO(229, 231, 235, 1),
indent: 28.w,
endIndent: 28.w,
), ),
SizedBox(height: 10.h), StatusStats(summary: state.summary),
StatusStats(summary: state.summary), if (state.isLoading)
SizedBox(height: 10.h), const Padding(
if (state.isLoading) padding: EdgeInsets.all(16.0),
const Padding( child: Center(child: CircularProgressIndicator(color: Color.fromRGBO(66, 165, 245, 1.0))),
padding: EdgeInsets.all(16.0), ),
child: Center(child: CircularProgressIndicator(color: Color.fromRGBO(66, 165, 245, 1.0))), ...state.detailRooms
), .where((room) =>
...state.detailRooms.map((room) { state.selectedRoomId == null ||
return Padding( room.roomBasicInfo.roomId == state.selectedRoomId)
padding: EdgeInsets.only(bottom: 10.h), .map((room) {
child: RoomDetailCard(room: room), return Padding(
); padding: EdgeInsets.only(bottom: 10.h),
}).toList(), child: RoomDetailCard(room: room),
SizedBox(height: 10.h), );
], }),
],
),
), ),
), SizedBox(height: 10.h),
], ],
),
), ),
); );
} }
......
...@@ -167,7 +167,9 @@ class _DeviceControl extends StatelessWidget { ...@@ -167,7 +167,9 @@ class _DeviceControl extends StatelessWidget {
case 'tv': case 'tv':
return Icons.tv_outlined; return Icons.tv_outlined;
case 'curtain': case 'curtain':
return Icons.curtains_outlined; return Icons.blinds_outlined;
case 'wifi':
return Icons.wifi;
default: default:
return Icons.devices_other; return Icons.devices_other;
} }
...@@ -191,7 +193,9 @@ class _DeviceControl extends StatelessWidget { ...@@ -191,7 +193,9 @@ class _DeviceControl extends StatelessWidget {
SizedBox(width: 16.w), SizedBox(width: 16.w),
Expanded( Expanded(
child: Text( child: Text(
device.deviceName, device.deviceName.contains('-')
? device.deviceName.split('-').last
: device.deviceName,
style: TextStyle( style: TextStyle(
fontSize: 28.sp, fontSize: 28.sp,
color: const Color.fromRGBO(59, 130, 246, 1), color: const Color.fromRGBO(59, 130, 246, 1),
......
...@@ -4,8 +4,15 @@ import 'package:smart_hotel_app/models/bo/room_bo.dart'; ...@@ -4,8 +4,15 @@ import 'package:smart_hotel_app/models/bo/room_bo.dart';
class RoomGrid extends StatelessWidget { class RoomGrid extends StatelessWidget {
final List<RoomGridBO> rooms; final List<RoomGridBO> rooms;
final int? selectedRoomId;
final Function(int) onRoomTap;
const RoomGrid({super.key, required this.rooms}); const RoomGrid({
super.key,
required this.rooms,
required this.selectedRoomId,
required this.onRoomTap,
});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
...@@ -18,7 +25,11 @@ class RoomGrid extends StatelessWidget { ...@@ -18,7 +25,11 @@ class RoomGrid extends StatelessWidget {
crossAxisCount: 5, crossAxisCount: 5,
mainAxisSpacing: 16.h, mainAxisSpacing: 16.h,
crossAxisSpacing: 16.w, crossAxisSpacing: 16.w,
children: rooms.map((room) => _RoomItem(room: room)).toList(), children: rooms.map((room) => _RoomItem(
room: room,
isSelected: room.roomId == selectedRoomId,
onTap: () => onRoomTap(room.roomId),
)).toList(),
), ),
); );
} }
...@@ -26,8 +37,14 @@ class RoomGrid extends StatelessWidget { ...@@ -26,8 +37,14 @@ class RoomGrid extends StatelessWidget {
class _RoomItem extends StatelessWidget { class _RoomItem extends StatelessWidget {
final RoomGridBO room; final RoomGridBO room;
final bool isSelected;
final VoidCallback onTap;
const _RoomItem({required this.room}); const _RoomItem({
required this.room,
required this.isSelected,
required this.onTap,
});
Color get _bgColor { Color get _bgColor {
switch (room.statusColor) { switch (room.statusColor) {
...@@ -55,34 +72,19 @@ class _RoomItem extends StatelessWidget { ...@@ -55,34 +72,19 @@ class _RoomItem extends StatelessWidget {
} }
} }
Color? get _borderColor {
switch (room.statusColor) {
case 'blue':
return const Color.fromRGBO(73, 149, 234, 1);
case 'yellow':
return const Color.fromRGBO(250, 204, 21, 1);
case 'default':
case 'gray':
default:
return null;
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GestureDetector( return GestureDetector(
behavior: HitTestBehavior.opaque, behavior: HitTestBehavior.opaque,
onTap: () { onTap: onTap,
},
child: Container( child: Container(
padding: EdgeInsets.symmetric(vertical: 12.h), padding: EdgeInsets.symmetric(vertical: 12.h),
decoration: BoxDecoration( decoration: BoxDecoration(
color: _bgColor, color: _bgColor,
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
// border: _borderColor != null border: isSelected
// ? Border.all(color: _borderColor!, width: 1.w) ? Border.all(color: const Color.fromRGBO(80, 162, 255, 1), width: 1.w)
// : null, : null,
), ),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
......
...@@ -64,7 +64,7 @@ class ServiceIndexCubit extends Cubit<ServiceIndexState> { ...@@ -64,7 +64,7 @@ class ServiceIndexCubit extends Cubit<ServiceIndexState> {
floors: floors, floors: floors,
)); ));
// 默认选中第一个楼层,加载其全部房间数据(不选区域类型) // 默认选中第一个楼层,加载其全部房间数据,再默认选中第一个区域和房间
if (floors.isNotEmpty) { if (floors.isNotEmpty) {
final firstFloor = floors.first; final firstFloor = floors.first;
final firstFloorId = firstFloor.keys.first; final firstFloorId = firstFloor.keys.first;
...@@ -72,6 +72,7 @@ class ServiceIndexCubit extends Cubit<ServiceIndexState> { ...@@ -72,6 +72,7 @@ class ServiceIndexCubit extends Cubit<ServiceIndexState> {
_updateAreasForFloor(firstFloorLabel); _updateAreasForFloor(firstFloorLabel);
selectFloor(firstFloorLabel); selectFloor(firstFloorLabel);
await _loadAllRooms(floorId: firstFloorId); await _loadAllRooms(floorId: firstFloorId);
_selectFirstTypeAndRoom();
} }
} catch (e) { } catch (e) {
emit(state.copyWith(isLoading: false, error: e.toString())); emit(state.copyWith(isLoading: false, error: e.toString()));
...@@ -204,7 +205,7 @@ class ServiceIndexCubit extends Cubit<ServiceIndexState> { ...@@ -204,7 +205,7 @@ class ServiceIndexCubit extends Cubit<ServiceIndexState> {
} }
/// UI 层切换楼层(切换时重置类型/房间/搜索,重新加载数据) /// UI 层切换楼层(切换时重置类型/房间/搜索,重新加载数据)
void toggleFloor(String floorName, int floorId) { Future<void> toggleFloor(String floorName, int floorId) async {
final isSameFloor = state.selectedFloor == floorName; final isSameFloor = state.selectedFloor == floorName;
final newFloor = isSameFloor ? null : floorName; final newFloor = isSameFloor ? null : floorName;
...@@ -227,17 +228,30 @@ class ServiceIndexCubit extends Cubit<ServiceIndexState> { ...@@ -227,17 +228,30 @@ class ServiceIndexCubit extends Cubit<ServiceIndexState> {
)); ));
if (newFloor != null) { if (newFloor != null) {
_loadAllRooms(floorId: floorId); await _loadAllRooms(floorId: floorId);
_selectFirstTypeAndRoom();
} }
} }
/// UI 层切换区域类型(点击切换,再点取消,重置房间) /// UI 层切换区域类型(点击切换,再点取消;切换后默认选中该类型第一个房间)
void toggleType(String type) { void toggleType(String type) {
final newType = state.selectedType != type ? type : null; final newType = state.selectedType != type ? type : null;
String? newRoom;
int? newRoomId;
if (newType != null) {
final matchedRoom = state.rooms.firstWhere(
(r) => r['areaType'] == newType,
orElse: () => <String, dynamic>{},
);
if (matchedRoom.isNotEmpty) {
newRoom = matchedRoom['number'] as String;
newRoomId = matchedRoom['roomId'] as int;
}
}
emit(state.copyWith( emit(state.copyWith(
selectedType: newType, selectedType: newType,
selectedRoom: null, selectedRoom: newRoom,
selectedRoomId: null, selectedRoomId: newRoomId,
)); ));
} }
...@@ -327,4 +341,22 @@ class ServiceIndexCubit extends Cubit<ServiceIndexState> { ...@@ -327,4 +341,22 @@ class ServiceIndexCubit extends Cubit<ServiceIndexState> {
} }
return null; return null;
} }
/// 默认选中第一个区域类型和该类型下的第一个房间
void _selectFirstTypeAndRoom() {
final areas = state.areas;
final rooms = state.rooms;
if (areas.isEmpty || rooms.isEmpty) return;
final defaultType = areas.first;
final defaultRoom = rooms.firstWhere(
(r) => r['areaType'] == defaultType,
orElse: () => rooms.first,
);
emit(state.copyWith(
selectedType: defaultType,
selectedRoom: defaultRoom['number'] as String,
selectedRoomId: defaultRoom['roomId'] as int,
));
}
} }
\ No newline at end of file
...@@ -185,46 +185,6 @@ class RoomManagementBlock extends StatelessWidget { ...@@ -185,46 +185,6 @@ class RoomManagementBlock extends StatelessWidget {
); );
} }
Widget _buildRoomDetail(ServiceIndexState state, ServiceIndexCubit cubit) {
final room = cubit.getSelectedRoomDetail();
if (room == null) return const SizedBox.shrink();
final items = [
('房间有无人', room['occupancyStatus'] == '1' ? '有人' : '无人'),
('房间状态', room['occupancyStatus'] == '1' ? '入住' : '空闲'),
('清洁灯', room['cleanStatus'] == '2' ? '已完成' : '待清洁'),
('设备开关', room['devicePowerStatus'] == '1' ? '开' : '关'),
];
return Column(
children: items.map((item) {
return Padding(
padding: EdgeInsets.only(bottom: 16.h),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
item.$1,
style: TextStyle(
fontSize: 24.sp,
color: const Color.fromRGBO(100, 116, 139, 1),
),
),
Text(
item.$2,
style: TextStyle(
fontSize: 24.sp,
color: const Color.fromRGBO(73, 149, 234, 1),
fontWeight: FontWeight.w500,
),
),
],
),
);
}).toList(),
);
}
Widget _buildRoomGrid(ServiceIndexState state, ServiceIndexCubit cubit) { Widget _buildRoomGrid(ServiceIndexState state, ServiceIndexCubit cubit) {
final filteredRooms = cubit.getFilteredRooms(); final filteredRooms = cubit.getFilteredRooms();
const crossAxisCount = 12; const crossAxisCount = 12;
...@@ -397,6 +357,46 @@ class RoomManagementBlock extends StatelessWidget { ...@@ -397,6 +357,46 @@ class RoomManagementBlock extends StatelessWidget {
); );
} }
Widget _buildRoomDetail(ServiceIndexState state, ServiceIndexCubit cubit) {
final room = cubit.getSelectedRoomDetail();
if (room == null) return const SizedBox.shrink();
final items = [
('房间有无人', room['occupancyStatus'] == '1' ? '有人' : '无人'),
('房间状态', room['occupancyStatus'] == '1' ? '入住' : '空闲'),
// ('清洁灯', room['cleanStatus'] == '2' ? '已完成' : '待清洁'),
// ('设备开关', room['devicePowerStatus'] == '1' ? '开' : '关'),
];
return Column(
children: items.map((item) {
return Padding(
padding: EdgeInsets.only(bottom: 16.h),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
item.$1,
style: TextStyle(
fontSize: 24.sp,
color: const Color.fromRGBO(100, 116, 139, 1),
),
),
Text(
item.$2,
style: TextStyle(
fontSize: 24.sp,
color: const Color.fromRGBO(73, 149, 234, 1),
fontWeight: FontWeight.w500,
),
),
],
),
);
}).toList(),
);
}
Widget _buildBottomButtons(ServiceIndexState state, ServiceIndexCubit cubit, BuildContext context) { Widget _buildBottomButtons(ServiceIndexState state, ServiceIndexCubit cubit, BuildContext context) {
final isRoomSelected = state.selectedRoom != null; final isRoomSelected = state.selectedRoom != null;
return Row( return Row(
......
...@@ -19,7 +19,7 @@ class ServiceStatsBlock extends StatelessWidget { ...@@ -19,7 +19,7 @@ class ServiceStatsBlock extends StatelessWidget {
width: double.infinity, width: double.infinity,
padding: EdgeInsets.only(top: 20.w, bottom: 20.w), padding: EdgeInsets.only(top: 20.w, bottom: 20.w),
child: Row( child: Row(
spacing: 12.w, spacing: 20.w,
children: [ children: [
_buildStatCard( _buildStatCard(
'待响应', '待响应',
...@@ -44,13 +44,12 @@ class ServiceStatsBlock extends StatelessWidget { ...@@ -44,13 +44,12 @@ class ServiceStatsBlock extends StatelessWidget {
Widget _buildStatCard(String label, String value, Color color) { Widget _buildStatCard(String label, String value, Color color) {
return Expanded( return Expanded(
child: Container( child: Container(
padding: EdgeInsets.symmetric(vertical: 16.h), height: 112.h,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.white,
borderRadius: BorderRadius.circular(24.r), borderRadius: BorderRadius.circular(24.r),
), ),
child: Column( child: Column(
spacing: 5.h,
children: [ children: [
Text( Text(
value, value,
......
...@@ -38,20 +38,34 @@ class ServiceRoomCubit extends Cubit<ServiceRoomState> { ...@@ -38,20 +38,34 @@ class ServiceRoomCubit extends Cubit<ServiceRoomState> {
wifiOnlineStatus: basic.wifiOnlineStatus, wifiOnlineStatus: basic.wifiOnlineStatus,
); );
final roomDevices = detail.deviceStatusList.map((d) { final roomDevices = detail.deviceStatusList
.where((d) => !d.deviceName.contains('分控'))
.map((d) {
return RoomDevice( return RoomDevice(
name: d.deviceName, name: d.deviceName.contains('-')
icon: _mapDeviceIcon(d.deviceTypeName, d.deviceTypeIcon), ? d.deviceName.split('-').last
: d.deviceName,
icon: _mapDeviceIcon(d.deviceTypeIcon),
isOn: d.powerStatus == '1' || d.powerStatus == 'on', isOn: d.powerStatus == '1' || d.powerStatus == 'on',
statusText: _buildDeviceStatusText(d), statusText: _buildDeviceStatusText(d),
); );
}).toList(); }).toList();
final controlDevices = detail.deviceControl.subDeviceControls.map((c) { // 从 deviceStatusList 建立 deviceId → deviceTypeIcon 的映射
final iconMap = <int, String>{};
for (final d in detail.deviceStatusList) {
iconMap[d.deviceId] = d.deviceTypeIcon;
}
final controlDevices = detail.deviceControl.subDeviceControls
.where((c) => !c.deviceName.contains('分控'))
.map((c) {
return ControlDevice( return ControlDevice(
deviceId: c.deviceId, deviceId: c.deviceId,
name: c.deviceName, name: c.deviceName.contains('-')
icon: _mapDeviceIcon(c.deviceTypeName, ''), ? c.deviceName.split('-').last
: c.deviceName,
icon: _mapDeviceIcon(iconMap[c.deviceId] ?? ''),
isOn: c.powerOn, isOn: c.powerOn,
detailText: c.currentParamDisplay.isNotEmpty ? c.currentParamDisplay : null, detailText: c.currentParamDisplay.isNotEmpty ? c.currentParamDisplay : null,
deviceTypeName: c.deviceTypeName, deviceTypeName: c.deviceTypeName,
...@@ -114,44 +128,30 @@ class ServiceRoomCubit extends Cubit<ServiceRoomState> { ...@@ -114,44 +128,30 @@ class ServiceRoomCubit extends Cubit<ServiceRoomState> {
} }
} }
IconData _mapDeviceIcon(String deviceTypeName, String deviceTypeIcon) { IconData _mapDeviceIcon(String deviceTypeIcon) {
// Try to match by deviceTypeName first
final name = deviceTypeName.toLowerCase();
if (name.contains('空调') || name.contains('air')) {
return Icons.air_outlined;
}
if (name.contains('照明') || name.contains('灯') || name.contains('light')) {
return Icons.light_outlined;
}
if (name.contains('电视') || name.contains('tv')) {
return Icons.tv_outlined;
}
if (name.contains('窗帘') || name.contains('curtain')) {
return Icons.curtains_closed_outlined;
}
// Fallback: match by deviceTypeIcon string
final icon = deviceTypeIcon.toLowerCase(); final icon = deviceTypeIcon.toLowerCase();
if (icon.contains('air') || icon.contains('ac')) { if (icon.contains('air-vent')) {
return Icons.air_outlined; return Icons.air_outlined;
} }
if (icon.contains('light')) { if (icon.contains('lightbulb')) {
return Icons.light_outlined; return Icons.light_outlined;
} }
if (icon.contains('tv')) { if (icon.contains('tv')) {
return Icons.tv_outlined; return Icons.tv_outlined;
} }
if (icon.contains('curtain')) { if (icon.contains('panel-top')) {
return Icons.curtains_closed_outlined; return Icons.curtains_closed_outlined;
} }
if (icon.contains('wifi')) {
return Icons.wifi;
}
return Icons.devices_other; return Icons.devices_other;
} }
/// 房间总电源开关 /// 房间总电源开关
Future<void> toggleMainPowerForSwitch(bool value) async { Future<void> toggleMainPowerForSwitch(bool value) async {
if (state.isPoweringOn || state.isPoweringOff) return; if (state.isPoweringOn || state.isPoweringOff) return;
final loadingKey = value ? 'isPoweringOn' : 'isPoweringOff'; // final loadingKey = value ? 'isPoweringOn' : 'isPoweringOff';
emit(state.copyWith( emit(state.copyWith(
isPoweringOn: value, isPoweringOn: value,
isPoweringOff: !value, isPoweringOff: !value,
......
...@@ -87,18 +87,44 @@ class ServiceRoomDetailView extends StatelessWidget { ...@@ -87,18 +87,44 @@ class ServiceRoomDetailView extends StatelessWidget {
return SingleChildScrollView( return SingleChildScrollView(
padding: EdgeInsets.symmetric(horizontal: 28.w), padding: EdgeInsets.symmetric(horizontal: 28.w),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
SizedBox(height: 20.h), SizedBox(height: 20.h),
RoomOverviewCard( Container(
roomNumber: state.roomNumber, decoration: BoxDecoration(
roomStatus: state.roomStatus, color: Colors.white,
statusTags: state.statusTags, borderRadius: BorderRadius.circular(24.r),
),
child: Column(
children: [
RoomOverviewCard(
roomNumber: state.roomNumber,
roomStatus: state.roomStatus,
statusTags: state.statusTags,
),
Divider(
height: 0.5.h,
color: const Color.fromRGBO(100, 116, 139, 0.2),
thickness: 0.5,
indent: 28.w,
endIndent: 28.w,
),
RoomDeviceStatusCard(
devices: state.roomDevices,
),
],
),
), ),
SizedBox(height: 20.h), SizedBox(height: 20.h),
RoomDeviceStatusCard( Text(
devices: state.roomDevices, '设备控制',
style: TextStyle(
fontSize: 32.sp,
fontWeight: FontWeight.bold,
color: const Color.fromRGBO(10, 13, 20, 1.0),
),
), ),
SizedBox(height: 20.h), SizedBox(height: 10.h),
DeviceControlCard( DeviceControlCard(
mainPowerOn: state.mainPowerOn, mainPowerOn: state.mainPowerOn,
controlDevices: state.controlDevices, controlDevices: state.controlDevices,
......
...@@ -30,17 +30,8 @@ class DeviceControlCard extends StatelessWidget { ...@@ -30,17 +30,8 @@ class DeviceControlCard extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text(
'设备控制',
style: TextStyle(
fontSize: 32.sp,
fontWeight: FontWeight.bold,
color: const Color.fromRGBO(10, 13, 20, 1.0),
),
),
SizedBox(height: 20.h),
_buildMainPowerControl(), _buildMainPowerControl(),
SizedBox(height: 24.h), SizedBox(height: 30.h),
Text( Text(
'分设备控制', '分设备控制',
style: TextStyle( style: TextStyle(
...@@ -49,14 +40,10 @@ class DeviceControlCard extends StatelessWidget { ...@@ -49,14 +40,10 @@ class DeviceControlCard extends StatelessWidget {
color: const Color.fromRGBO(10, 13, 20, 1.0), color: const Color.fromRGBO(10, 13, 20, 1.0),
), ),
), ),
SizedBox(height: 30.h),
...controlDevices.asMap().entries.map((entry) { ...controlDevices.asMap().entries.map((entry) {
final index = entry.key; final index = entry.key;
final device = entry.value; final device = entry.value;
return Padding( return _buildDeviceControl(index, device);
padding: EdgeInsets.only(bottom: index < controlDevices.length - 1 ? 20.h : 0),
child: _buildDeviceControl(index, device),
);
}).toList(), }).toList(),
], ],
), ),
...@@ -134,24 +121,25 @@ class DeviceControlCard extends StatelessWidget { ...@@ -134,24 +121,25 @@ class DeviceControlCard extends StatelessWidget {
Widget _buildDeviceControl(int index, ControlDevice device) { Widget _buildDeviceControl(int index, ControlDevice device) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Row( SizedBox(
mainAxisAlignment: MainAxisAlignment.spaceBetween, height: 80.h,
children: [ child: Row(
Expanded( mainAxisAlignment: MainAxisAlignment.spaceBetween,
child: Material( children: [
color: Colors.transparent, Expanded(
child: InkWell( child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: onDeviceTap != null ? () => onDeviceTap!(index, device) : null, onTap: onDeviceTap != null ? () => onDeviceTap!(index, device) : null,
child: Row( child: Row(
spacing: 8.w,
children: [ children: [
Icon( Icon(
device.icon, device.icon,
color: const Color.fromRGBO(66, 165, 245, 1.0), color: const Color.fromRGBO(66, 165, 245, 1.0),
size: 36.sp, size: 36.sp,
), ),
SizedBox(width: 8.w),
Text( Text(
device.name, device.name,
style: TextStyle( style: TextStyle(
...@@ -164,23 +152,22 @@ class DeviceControlCard extends StatelessWidget { ...@@ -164,23 +152,22 @@ class DeviceControlCard extends StatelessWidget {
), ),
), ),
), ),
), FlutterSwitch(
FlutterSwitch( width: 80.w,
width: 80.w, height: 35.h,
height: 35.h, valueFontSize: 20.sp,
valueFontSize: 20.sp, toggleSize: 25.h,
toggleSize: 25.h, borderRadius: 20.r,
borderRadius: 20.r, padding: 4.w,
padding: 4.w, value: device.isOn,
value: device.isOn, activeColor: const Color.fromRGBO(80, 162, 255, 1),
activeColor: const Color.fromRGBO(80, 162, 255, 1), inactiveColor: const Color.fromRGBO(210, 213, 218, 1),
inactiveColor: const Color.fromRGBO(210, 213, 218, 1), onToggle: (value) => onDeviceToggle(index, value),
onToggle: (value) => onDeviceToggle(index, value), ),
), ],
], ),
), ),
if (index < controlDevices.length - 1) ...[ if (index < controlDevices.length - 1) ...[
SizedBox(height: 30.h),
Divider( Divider(
height: 0.5.h, height: 0.5.h,
color: const Color.fromRGBO(100, 116, 139, 0.2), color: const Color.fromRGBO(100, 116, 139, 0.2),
......
...@@ -61,13 +61,17 @@ class RoomDeviceStatusCard extends StatelessWidget { ...@@ -61,13 +61,17 @@ class RoomDeviceStatusCard extends StatelessWidget {
color: const Color.fromRGBO(66, 165, 245, 1.0), color: const Color.fromRGBO(66, 165, 245, 1.0),
size: 36.sp, size: 36.sp,
), ),
Text( Expanded(
device.name, child: Text(
style: TextStyle( device.name,
fontSize: 28.sp, style: TextStyle(
color: const Color.fromRGBO(66, 165, 245, 1.0), fontSize: 28.sp,
fontWeight: FontWeight.bold, color: const Color.fromRGBO(66, 165, 245, 1.0),
), fontWeight: FontWeight.bold,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
), ),
], ],
), ),
......
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