Commit 42fc065d authored by 张宏's avatar 张宏

告警、告警详情 接口对接

parent 695103c3
import 'package:equatable/equatable.dart';
// ==================== 设备告警看板 Dashboard ====================
class AlertDashboardBO extends Equatable {
final int total;
final AlertDashboardStatisticsBO statistics;
final List<AlertItemBO> alerts;
final List<DeviceItemBO> devices;
const AlertDashboardBO({
required this.total,
required this.statistics,
required this.alerts,
required this.devices,
});
factory AlertDashboardBO.fromJson(Map<String, dynamic> json) {
return AlertDashboardBO(
total: json['total'] as int? ?? 0,
statistics: AlertDashboardStatisticsBO.fromJson(
json['statistics'] as Map<String, dynamic>? ?? {},
),
alerts: (json['alerts'] as List<dynamic>?)
?.map((e) =>
AlertItemBO.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
devices: (json['devices'] as List<dynamic>?)
?.map((e) =>
DeviceItemBO.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
);
}
@override
List<Object?> get props => [total, statistics, alerts, devices];
}
// ==================== 统计数据 ====================
class AlertDashboardStatisticsBO extends Equatable {
final int highAlertCount;
final int mediumAlertCount;
final int onlineDeviceCount;
const AlertDashboardStatisticsBO({
required this.highAlertCount,
required this.mediumAlertCount,
required this.onlineDeviceCount,
});
factory AlertDashboardStatisticsBO.fromJson(Map<String, dynamic> json) {
return AlertDashboardStatisticsBO(
highAlertCount: json['highAlertCount'] as int? ?? 0,
mediumAlertCount: json['mediumAlertCount'] as int? ?? 0,
onlineDeviceCount: json['onlineDeviceCount'] as int? ?? 0,
);
}
@override
List<Object?> get props =>
[highAlertCount, mediumAlertCount, onlineDeviceCount];
}
// ==================== 告警信息 ====================
class AlertItemBO extends Equatable {
final int alertId;
final String alertNo;
final String alertTitle;
final String alertLevel;
final String alertLevelText;
final String alertStatus;
final String roomName;
final String deviceName;
final String alertValue;
final String thresholdValue;
final String alertContent;
final String voltage;
final String current;
final String temperature;
final int waitingDuration;
final String waitingDurationDesc;
final String alertTime;
const AlertItemBO({
required this.alertId,
required this.alertNo,
required this.alertTitle,
required this.alertLevel,
required this.alertLevelText,
required this.alertStatus,
required this.roomName,
required this.deviceName,
required this.alertValue,
required this.thresholdValue,
required this.alertContent,
required this.voltage,
required this.current,
required this.temperature,
required this.waitingDuration,
required this.waitingDurationDesc,
required this.alertTime,
});
factory AlertItemBO.fromJson(Map<String, dynamic> json) {
return AlertItemBO(
alertId: json['alertId'] as int? ?? 0,
alertNo: json['alertNo'] as String? ?? '',
alertTitle: json['alertTitle'] as String? ?? '',
alertLevel: json['alertLevel'] as String? ?? '',
alertLevelText: json['alertLevelText'] as String? ?? '',
alertStatus: json['alertStatus'] as String? ?? '0',
roomName: json['roomName'] as String? ?? '',
deviceName: json['deviceName'] as String? ?? '',
alertValue: json['alertValue'] as String? ?? '',
thresholdValue: json['thresholdValue'] as String? ?? '',
alertContent: json['alertContent'] as String? ?? '',
voltage: json['voltage'] as String? ?? '',
current: json['current'] as String? ?? '',
temperature: json['temperature'] as String? ?? '',
waitingDuration: json['waitingDuration'] as int? ?? 0,
waitingDurationDesc: json['waitingDurationDesc'] as String? ?? '',
alertTime: json['alertTime'] as String? ?? '',
);
}
@override
List<Object?> get props => [
alertId,
alertNo,
alertTitle,
alertLevel,
alertLevelText,
alertStatus,
roomName,
deviceName,
alertValue,
thresholdValue,
alertContent,
voltage,
current,
temperature,
waitingDuration,
waitingDurationDesc,
alertTime,
];
}
// ==================== 设备巡检列表 ====================
class DeviceItemBO extends Equatable {
final int deviceId;
final String deviceName;
final String deviceTypeIcon;
final String deviceTypeName;
final String runStatus;
final String runStatusText;
final String roomName;
final String temperature;
final String voltage;
final String power;
final String current;
final String onlineStatus;
final String lastHeartbeat;
const DeviceItemBO({
required this.deviceId,
required this.deviceName,
required this.deviceTypeIcon,
required this.deviceTypeName,
required this.runStatus,
required this.runStatusText,
required this.roomName,
required this.temperature,
required this.voltage,
required this.power,
required this.current,
required this.onlineStatus,
required this.lastHeartbeat,
});
factory DeviceItemBO.fromJson(Map<String, dynamic> json) {
return DeviceItemBO(
deviceId: json['deviceId'] as int? ?? 0,
deviceName: json['deviceName'] as String? ?? '',
deviceTypeIcon: json['deviceTypeIcon'] as String? ?? '',
deviceTypeName: json['deviceTypeName'] as String? ?? '',
runStatus: json['runStatus'] as String? ?? '',
runStatusText: json['runStatusText'] as String? ?? '',
roomName: json['roomName'] as String? ?? '',
temperature: json['temperature'] as String? ?? '',
voltage: json['voltage'] as String? ?? '',
power: json['power'] as String? ?? '',
current: json['current'] as String? ?? '',
onlineStatus: json['onlineStatus'] as String? ?? '0',
lastHeartbeat: json['lastHeartbeat'] as String? ?? '',
);
}
@override
List<Object?> get props => [
deviceId,
deviceName,
deviceTypeIcon,
deviceTypeName,
runStatus,
runStatusText,
roomName,
temperature,
voltage,
power,
current,
onlineStatus,
lastHeartbeat,
];
}
\ No newline at end of file
import 'package:equatable/equatable.dart';
// ==================== 告警详情 ====================
class AlertDetailBO extends Equatable {
final AlertBasicInfoBO alertBasicInfo;
final TrendChartBO trendChart;
final AlertDetailInfoBO alertDetailInfo;
const AlertDetailBO({
required this.alertBasicInfo,
required this.trendChart,
required this.alertDetailInfo,
});
factory AlertDetailBO.fromJson(Map<String, dynamic> json) {
return AlertDetailBO(
alertBasicInfo: AlertBasicInfoBO.fromJson(
json['alertBasicInfo'] as Map<String, dynamic>? ?? {},
),
trendChart: TrendChartBO.fromJson(
json['trendChart'] as Map<String, dynamic>? ?? {},
),
alertDetailInfo: AlertDetailInfoBO.fromJson(
json['alertDetailInfo'] as Map<String, dynamic>? ?? {},
),
);
}
@override
List<Object?> get props => [alertBasicInfo, trendChart, alertDetailInfo];
}
// ==================== 告警基本信息 ====================
class AlertBasicInfoBO extends Equatable {
final int alertId;
final String alertTitle;
final String alertLevel;
final String alertLevelText;
final String alertStatus;
final String alertDeviceLocation;
final int alertCount;
final String alertTime;
final String alertIcon;
const AlertBasicInfoBO({
required this.alertId,
required this.alertTitle,
required this.alertLevel,
required this.alertLevelText,
required this.alertStatus,
required this.alertDeviceLocation,
required this.alertCount,
required this.alertTime,
required this.alertIcon,
});
factory AlertBasicInfoBO.fromJson(Map<String, dynamic> json) {
return AlertBasicInfoBO(
alertId: json['alertId'] as int? ?? 0,
alertTitle: json['alertTitle'] as String? ?? '',
alertLevel: json['alertLevel'] as String? ?? '',
alertLevelText: json['alertLevelText'] as String? ?? '',
alertStatus: json['alertStatus'] as String? ?? '',
alertDeviceLocation: json['alertDeviceLocation'] as String? ?? '',
alertCount: json['alertCount'] as int? ?? 0,
alertTime: json['alertTime'] as String? ?? '',
alertIcon: json['alertIcon'] as String? ?? '',
);
}
@override
List<Object?> get props => [
alertId,
alertTitle,
alertLevel,
alertLevelText,
alertStatus,
alertDeviceLocation,
alertCount,
alertTime,
alertIcon,
];
}
// ==================== 趋势图 ====================
class TrendChartBO extends Equatable {
final String chartTitle;
final String chartDate;
final String unit;
final List<DataPointBO> dataPoints;
final int yaxisMax;
final int yaxisMin;
const TrendChartBO({
required this.chartTitle,
required this.chartDate,
required this.unit,
required this.dataPoints,
required this.yaxisMax,
required this.yaxisMin,
});
factory TrendChartBO.fromJson(Map<String, dynamic> json) {
return TrendChartBO(
chartTitle: json['chartTitle'] as String? ?? '',
chartDate: json['chartDate'] as String? ?? '',
unit: json['unit'] as String? ?? '',
dataPoints: (json['dataPoints'] as List<dynamic>?)
?.map(
(e) => DataPointBO.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
yaxisMax: json['yaxisMax'] as int? ?? 100,
yaxisMin: json['yaxisMin'] as int? ?? 0,
);
}
@override
List<Object?> get props =>
[chartTitle, chartDate, unit, dataPoints, yaxisMax, yaxisMin];
}
class DataPointBO extends Equatable {
final String time;
final double value;
final int recordId;
const DataPointBO({
required this.time,
required this.value,
required this.recordId,
});
factory DataPointBO.fromJson(Map<String, dynamic> json) {
return DataPointBO(
time: json['time'] as String? ?? '',
value: (json['value'] as num?)?.toDouble() ?? 0,
recordId: json['recordId'] as int? ?? 0,
);
}
@override
List<Object?> get props => [time, value, recordId];
}
// ==================== 告警详细信息 ====================
class AlertDetailInfoBO extends Equatable {
final String thresholdSetting;
final String duration;
final int durationMinutes;
final String peakRecord;
final String linkageAction;
final String alertValue;
final String alertContent;
const AlertDetailInfoBO({
required this.thresholdSetting,
required this.duration,
required this.durationMinutes,
required this.peakRecord,
required this.linkageAction,
required this.alertValue,
required this.alertContent,
});
factory AlertDetailInfoBO.fromJson(Map<String, dynamic> json) {
return AlertDetailInfoBO(
thresholdSetting: json['thresholdSetting'] as String? ?? '',
duration: json['duration'] as String? ?? '',
durationMinutes: json['durationMinutes'] as int? ?? 0,
peakRecord: json['peakRecord'] as String? ?? '',
linkageAction: json['linkageAction'] as String? ?? '',
alertValue: json['alertValue'] as String? ?? '',
alertContent: json['alertContent'] as String? ?? '',
);
}
@override
List<Object?> get props => [
thresholdSetting,
duration,
durationMinutes,
peakRecord,
linkageAction,
alertValue,
alertContent,
];
}
\ No newline at end of file
import '../utils/http/response_model.dart';
import '../utils/http/dio_request.dart';
import '../models/bo/alert_dashboard_bo.dart';
class AlertDashboardRepository {
Future<ResponseModel<AlertDashboardBO>> getDashboard() {
return DioRequest.instance.get<AlertDashboardBO>(
'/app/alert/dashboard',
fromJsonT: (data) =>
AlertDashboardBO.fromJson(data as Map<String, dynamic>),
);
}
}
\ No newline at end of file
import '../utils/http/response_model.dart';
import '../utils/http/dio_request.dart';
import '../models/bo/alert_detail_bo.dart';
class AlertDetailRepository {
/// 获取告警详情
Future<ResponseModel<AlertDetailBO>> getDetail({required int alertId}) {
return DioRequest.instance.get<AlertDetailBO>(
'/app/alert/detail',
queryParameters: {'alertId': alertId},
fromJsonT: (data) =>
AlertDetailBO.fromJson(data as Map<String, dynamic>),
);
}
}
\ No newline at end of file
...@@ -50,9 +50,13 @@ abstract class $AppRouter extends _i21.RootStackRouter { ...@@ -50,9 +50,13 @@ abstract class $AppRouter extends _i21.RootStackRouter {
@override @override
final Map<String, _i21.PageFactory> pagesMap = { final Map<String, _i21.PageFactory> pagesMap = {
AbnormalDetailRoute.name: (routeData) { AbnormalDetailRoute.name: (routeData) {
final args = routeData.argsAs<AbnormalDetailRouteArgs>();
return _i21.AutoRoutePage<dynamic>( return _i21.AutoRoutePage<dynamic>(
routeData: routeData, routeData: routeData,
child: const _i1.AbnormalDetailView(), child: _i1.AbnormalDetailView(
key: args.key,
alertId: args.alertId,
),
); );
}, },
AbnormalListRoute.name: (routeData) { AbnormalListRoute.name: (routeData) {
...@@ -200,16 +204,40 @@ abstract class $AppRouter extends _i21.RootStackRouter { ...@@ -200,16 +204,40 @@ abstract class $AppRouter extends _i21.RootStackRouter {
/// generated route for /// generated route for
/// [_i1.AbnormalDetailView] /// [_i1.AbnormalDetailView]
class AbnormalDetailRoute extends _i21.PageRouteInfo<void> { class AbnormalDetailRoute extends _i21.PageRouteInfo<AbnormalDetailRouteArgs> {
const AbnormalDetailRoute({List<_i21.PageRouteInfo>? children}) AbnormalDetailRoute({
: super( _i22.Key? key,
required int alertId,
List<_i21.PageRouteInfo>? children,
}) : super(
AbnormalDetailRoute.name, AbnormalDetailRoute.name,
args: AbnormalDetailRouteArgs(
key: key,
alertId: alertId,
),
initialChildren: children, initialChildren: children,
); );
static const String name = 'AbnormalDetailRoute'; static const String name = 'AbnormalDetailRoute';
static const _i21.PageInfo<void> page = _i21.PageInfo<void>(name); static const _i21.PageInfo<AbnormalDetailRouteArgs> page =
_i21.PageInfo<AbnormalDetailRouteArgs>(name);
}
class AbnormalDetailRouteArgs {
const AbnormalDetailRouteArgs({
this.key,
required this.alertId,
});
final _i22.Key? key;
final int alertId;
@override
String toString() {
return 'AbnormalDetailRouteArgs{key: $key, alertId: $alertId}';
}
} }
/// generated route for /// generated route for
......
import '../models/bo/alert_dashboard_bo.dart';
import '../repositories/alert_dashboard_repository.dart';
class AlertDashboardService {
final AlertDashboardRepository _repository;
AlertDashboardService({required AlertDashboardRepository repository})
: _repository = repository;
Future<AlertDashboardBO> getDashboard() async {
final result = await _repository.getDashboard();
if (result.success && result.data != null) {
return result.data!;
}
throw Exception(result.msg);
}
}
\ No newline at end of file
import '../models/bo/alert_detail_bo.dart';
import '../repositories/alert_detail_repository.dart';
class AlertDetailService {
final AlertDetailRepository _repository;
AlertDetailService({required AlertDetailRepository repository})
: _repository = repository;
Future<AlertDetailBO> getDetail({required int alertId}) async {
final result = await _repository.getDetail(alertId: alertId);
if (result.success && result.data != null) {
return result.data!;
}
throw Exception(result.msg);
}
}
\ No newline at end of file
...@@ -2,7 +2,9 @@ import 'package:auto_route/auto_route.dart'; ...@@ -2,7 +2,9 @@ import 'package:auto_route/auto_route.dart';
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/repositories/alert_detail_repository.dart';
import 'package:smart_hotel_app/routes/app_router.gr.dart'; import 'package:smart_hotel_app/routes/app_router.gr.dart';
import 'package:smart_hotel_app/services/alert_detail_service.dart';
import 'package:smart_hotel_app/views/home/abnormal/cubit/abnormal_cubit.dart'; import 'package:smart_hotel_app/views/home/abnormal/cubit/abnormal_cubit.dart';
import 'package:smart_hotel_app/views/home/abnormal/cubit/abnormal_state.dart'; import 'package:smart_hotel_app/views/home/abnormal/cubit/abnormal_state.dart';
import 'package:smart_hotel_app/views/home/abnormal/widget/alarm_info_block.dart'; import 'package:smart_hotel_app/views/home/abnormal/widget/alarm_info_block.dart';
...@@ -11,12 +13,18 @@ import 'package:smart_hotel_app/views/home/abnormal/widget/temperature_char_bloc ...@@ -11,12 +13,18 @@ import 'package:smart_hotel_app/views/home/abnormal/widget/temperature_char_bloc
@RoutePage() @RoutePage()
class AbnormalDetailView extends StatelessWidget { class AbnormalDetailView extends StatelessWidget {
const AbnormalDetailView({super.key}); final int alertId;
const AbnormalDetailView({super.key, required this.alertId});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (_) => AbnormalCubit(), create: (_) {
final repository = AlertDetailRepository();
final service = AlertDetailService(repository: repository);
return AbnormalCubit(service: service, alertId: alertId);
},
child: Scaffold( child: Scaffold(
backgroundColor: const Color.fromRGBO(242, 243, 245, 1), backgroundColor: const Color.fromRGBO(242, 243, 245, 1),
appBar: AppBar( appBar: AppBar(
...@@ -39,25 +47,43 @@ class AbnormalDetailView extends StatelessWidget { ...@@ -39,25 +47,43 @@ class AbnormalDetailView extends StatelessWidget {
), ),
centerTitle: true, centerTitle: true,
), ),
body: SingleChildScrollView( body: BlocBuilder<AbnormalCubit, AbnormalState>(
padding: EdgeInsets.symmetric(horizontal: 28.w), builder: (context, state) {
child: BlocBuilder<AbnormalCubit, AbnormalState>( if (state.isLoading) {
builder: (context, state) { return const Center(child: CircularProgressIndicator());
final cubit = context.read<AbnormalCubit>(); }
return Column( if (state.error != null) {
return Center(
child: Text(
'加载失败: ${state.error}',
style: TextStyle(
fontSize: 28.sp,
color: const Color.fromRGBO(255, 100, 101, 1),
),
),
);
}
final alarmInfo = state.alarmInfo;
if (alarmInfo == null) {
return const SizedBox.shrink();
}
final cubit = context.read<AbnormalCubit>();
return SingleChildScrollView(
padding: EdgeInsets.symmetric(horizontal: 28.w),
child: Column(
children: [ children: [
TemperatureBlock(alarmInfo: state.alarmInfo), TemperatureBlock(alarmInfo: alarmInfo),
SizedBox(height: 10.h), SizedBox(height: 10.h),
TemperatureCharBlock(alarmInfo: state.alarmInfo), TemperatureCharBlock(alarmInfo: alarmInfo),
SizedBox(height: 10.h), SizedBox(height: 10.h),
AlarmInfoBlock(alarmInfo: state.alarmInfo), AlarmInfoBlock(alarmInfo: alarmInfo),
SizedBox(height: 10.h), SizedBox(height: 10.h),
_buildBottomButtons(context, cubit), _buildBottomButtons(context, cubit),
SizedBox(height: 30.h), SizedBox(height: 30.h),
], ],
); ),
}, );
), },
), ),
), ),
); );
......
import 'package:fl_chart/fl_chart.dart'; import 'package:fl_chart/fl_chart.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:smart_hotel_app/models/bo/alert_detail_bo.dart';
import 'package:smart_hotel_app/services/alert_detail_service.dart';
import 'package:smart_hotel_app/views/home/abnormal/cubit/abnormal_state.dart'; import 'package:smart_hotel_app/views/home/abnormal/cubit/abnormal_state.dart';
class AbnormalCubit extends Cubit<AbnormalState> { class AbnormalCubit extends Cubit<AbnormalState> {
AbnormalCubit() : super(AbnormalState(alarmInfo: _getAlarmInfo())); final AlertDetailService _service;
final int _alertId;
static AlarmInfo _getAlarmInfo() { AbnormalCubit({
return const AlarmInfo( required AlertDetailService service,
alarmType: '温度异常告警', required int alertId,
deviceInfo: 'A-301智能空开·客房301', }) : _service = service,
alarmLevel: '高警', _alertId = alertId,
urgency: '紧急', super(const AbnormalState()) {
currentTemp: '85℃', loadDetail();
voltage: '22V', }
current: '3.2A',
triggerTime: '2026-05-09 14:23:18', Future<void> loadDetail() async {
threshold: '温度>80℃触发', emit(const AbnormalState(isLoading: true));
duration: '2小时37分钟', try {
peak: '89.2℃ (15:02)', final detail = await _service.getDetail(alertId: _alertId);
linkAction: '自动断电保护', emit(AbnormalState(alarmInfo: _mapToAlarmInfo(detail)));
date: '5月9日', } catch (e) {
temperatureSpots: [ emit(AbnormalState(error: e.toString()));
FlSpot(0, 40), }
FlSpot(4, 40), }
FlSpot(8, 15),
FlSpot(12, 55), AlarmInfo _mapToAlarmInfo(AlertDetailBO detail) {
FlSpot(16, 50), final basic = detail.alertBasicInfo;
FlSpot(18, 55), final trend = detail.trendChart;
FlSpot(20, 75), final info = detail.alertDetailInfo;
FlSpot(22, 82),
FlSpot(24, 85), return AlarmInfo(
], alarmType: basic.alertTitle,
deviceInfo: basic.alertDeviceLocation,
alarmLevel: basic.alertLevelText,
urgency: _mapUrgency(basic.alertLevel),
currentTemp: info.alertValue,
voltage: '',
current: '',
triggerTime: basic.alertTime,
threshold: info.thresholdSetting,
duration: info.duration,
peak: info.peakRecord,
linkAction: info.linkageAction,
date: trend.chartDate,
temperatureSpots: trend.dataPoints.asMap().entries.map((entry) {
return FlSpot(entry.key.toDouble(), entry.value.value);
}).toList(),
); );
} }
String _mapUrgency(String level) {
switch (level) {
case 'high':
return '紧急';
case 'medium':
return '一般';
default:
return '一般';
}
}
void handleConfirm() { void handleConfirm() {
print('确认处理'); print('确认处理');
} }
......
...@@ -35,9 +35,13 @@ class AlarmInfo { ...@@ -35,9 +35,13 @@ class AlarmInfo {
} }
class AbnormalState { class AbnormalState {
final AlarmInfo alarmInfo; final bool isLoading;
final String? error;
final AlarmInfo? alarmInfo;
const AbnormalState({ const AbnormalState({
required this.alarmInfo, this.isLoading = false,
this.error,
this.alarmInfo,
}); });
} }
\ No newline at end of file
...@@ -57,7 +57,13 @@ class AlarmInfoBlock extends StatelessWidget { ...@@ -57,7 +57,13 @@ class AlarmInfoBlock extends StatelessWidget {
); );
} }
String _truncateText(String text, {int maxLength = 20}) {
if (text.length <= maxLength) return text;
return '${text.substring(0, maxLength)}...';
}
Widget _buildInfoRow(String label, String value, {Color? valueColor}) { Widget _buildInfoRow(String label, String value, {Color? valueColor}) {
final displayValue = _truncateText(value);
return Row( return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
...@@ -69,7 +75,7 @@ class AlarmInfoBlock extends StatelessWidget { ...@@ -69,7 +75,7 @@ class AlarmInfoBlock extends StatelessWidget {
), ),
), ),
Text( Text(
value, displayValue,
style: TextStyle( style: TextStyle(
fontSize: 24.sp, fontSize: 24.sp,
color: valueColor ?? Color.fromRGBO(100, 116, 139, 1), color: valueColor ?? Color.fromRGBO(100, 116, 139, 1),
......
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:smart_hotel_app/models/bo/alert_dashboard_bo.dart';
import 'package:smart_hotel_app/services/alert_dashboard_service.dart';
import 'package:smart_hotel_app/views/home/index/cubit/home_index_state.dart'; import 'package:smart_hotel_app/views/home/index/cubit/home_index_state.dart';
import 'package:flutter/material.dart';
class HomeIndexCubit extends Cubit<HomeIndexState> { class HomeIndexCubit extends Cubit<HomeIndexState> {
HomeIndexCubit() : super(const HomeIndexState()) { final AlertDashboardService _service;
initData();
HomeIndexCubit({required AlertDashboardService service})
: _service = service,
super(const HomeIndexState()) {
loadDashboard();
} }
void initData() { Future<void> loadDashboard() async {
emit(state.copyWith(isLoading: true, error: null));
try {
final dashboard = await _service.getDashboard();
_emitFromDashboard(dashboard);
} catch (e) {
emit(state.copyWith(isLoading: false, error: e.toString()));
}
}
void _emitFromDashboard(AlertDashboardBO dashboard) {
emit(state.copyWith( emit(state.copyWith(
highAlarmCount: 1, isLoading: false,
mediumAlarmCount: 1, error: null,
onlineDeviceCount: 48, highAlarmCount: dashboard.statistics.highAlertCount,
temperatureAlarmTitle: '温度异常告警', mediumAlarmCount: dashboard.statistics.mediumAlertCount,
temperatureAlarmLocation: '客房301·A-301智能空开', onlineDeviceCount: dashboard.statistics.onlineDeviceCount,
currentTemperature: '85', temperatureAlarmTitle: dashboard.alerts.isNotEmpty
currentVoltage: '220', ? dashboard.alerts[0].alertTitle
currentCurrent: '3.2', : '',
waitTime: '2h37m', temperatureAlarmLocation: dashboard.alerts.isNotEmpty
alarmLevel: '紧急', ? '${dashboard.alerts[0].roomName}·${dashboard.alerts[0].deviceName}'
voltageAlarmTitle: '电压波动告警', : '',
voltageAlarmLocation: '会议室2F-01', currentTemperature: dashboard.alerts.isNotEmpty
voltageValue: '248', ? dashboard.alerts[0].temperature
normalVoltage: '235', : '',
voltageWaitTime: '1h19m', currentVoltage:
voltageAlarmLevel: '中警', dashboard.alerts.isNotEmpty ? dashboard.alerts[0].voltage : '',
equipmentList: const [ currentCurrent:
EquipmentItem(icon: Icons.power, title: '客房305空开', subtitle: '38°C·220V·520W', status: '正常', statusColor: Color.fromRGBO(26, 188, 156, 1.0)), dashboard.alerts.isNotEmpty ? dashboard.alerts[0].current : '',
EquipmentItem(icon: Icons.power, title: '大堂总空开L-01', subtitle: '380V·42A·28.6kW', status: '正常', statusColor: Color.fromRGBO(26, 188, 156, 1.0)), waitTime: dashboard.alerts.isNotEmpty
EquipmentItem(icon: Icons.wifi, title: '招牌AP-03', subtitle: '信号弱·RSSI-78dBm', status: '注意', statusColor: Color.fromRGBO(255, 193, 7, 1.0)), ? dashboard.alerts[0].waitingDurationDesc
EquipmentItem(icon: Icons.power, title: '客房201空开', subtitle: '25°C·220V·380W', status: '正常', statusColor: Color.fromRGBO(26, 188, 156, 1.0)), : '',
EquipmentItem(icon: Icons.power, title: '水泵控制箱', subtitle: '运行中·2.2kW', status: '正常', statusColor: Color.fromRGBO(26, 188, 156, 1.0)), alarmLevel: dashboard.alerts.isNotEmpty
EquipmentItem(icon: Icons.thermostat, title: '冷库温控器', subtitle: '-18°C·异常波动', status: '告警', statusColor: Color.fromRGBO(255, 77, 79, 1.0)), ? dashboard.alerts[0].alertLevelText
EquipmentItem(icon: Icons.light, title: '走廊照明回路', subtitle: '12盏·功率240W', status: '正常', statusColor: Color.fromRGBO(26, 188, 156, 1.0)), : '',
EquipmentItem(icon: Icons.wifi, title: '会议室AP-01', subtitle: '连接数12·信号强', status: '正常', statusColor: Color.fromRGBO(26, 188, 156, 1.0)), voltageAlarmTitle: dashboard.alerts.length > 1
EquipmentItem(icon: Icons.power, title: '电梯控制柜', subtitle: '运行中·电压稳定', status: '正常', statusColor: Color.fromRGBO(26, 188, 156, 1.0)), ? dashboard.alerts[1].alertTitle
EquipmentItem(icon: Icons.thermostat, title: '空调外机', subtitle: '压力偏高·需检查', status: '注意', statusColor: Color.fromRGBO(255, 193, 7, 1.0)), : '',
EquipmentItem(icon: Icons.wifi, title: '楼道AP-05', subtitle: '离线·需重启', status: '离线', statusColor: Color.fromRGBO(102, 102, 102, 1.0)), voltageAlarmLocation: dashboard.alerts.length > 1
EquipmentItem(icon: Icons.power, title: '厨房消毒柜', subtitle: '消毒中·温度120°C', status: '正常', statusColor: Color.fromRGBO(26, 188, 156, 1.0)), ? '${dashboard.alerts[1].roomName}·${dashboard.alerts[1].deviceName}'
], : '',
voltageValue: dashboard.alerts.length > 1
? dashboard.alerts[1].voltage
: '',
normalVoltage: '',
voltageWaitTime: dashboard.alerts.length > 1
? dashboard.alerts[1].waitingDurationDesc
: '',
voltageAlarmLevel: dashboard.alerts.length > 1
? dashboard.alerts[1].alertLevelText
: '',
voltageAlertId: dashboard.alerts.length > 1
? dashboard.alerts[1].alertId
: 0,
equipmentList: dashboard.devices
.map((d) => _mapDeviceToEquipment(d))
.toList(),
)); ));
} }
EquipmentItem _mapDeviceToEquipment(DeviceItemBO d) {
return EquipmentItem(
icon: _mapDeviceIcon(d.deviceTypeIcon),
title: d.deviceName,
subtitle: _buildDeviceSubtitle(d),
status: d.onlineStatus == '1' ? d.runStatusText : '离线',
statusColor: _mapStatusColor(d.onlineStatus, d.runStatus),
);
}
IconData _mapDeviceIcon(String icon) {
switch (icon) {
case 'power':
return Icons.power;
case 'wifi':
return Icons.wifi;
case 'thermostat':
return Icons.thermostat;
case 'light':
return Icons.light;
default:
return Icons.devices_other;
}
}
String _buildDeviceSubtitle(DeviceItemBO d) {
final parts = <String>[];
if (d.roomName.isNotEmpty) parts.add(d.roomName);
if (d.temperature.isNotEmpty) parts.add('${d.temperature}°C');
if (d.voltage.isNotEmpty) parts.add('${d.voltage}V');
if (d.power.isNotEmpty) parts.add('${d.power}kW');
if (d.current.isNotEmpty) parts.add('${d.current}A');
return parts.isNotEmpty ? parts.join('·') : '';
}
Color _mapStatusColor(String onlineStatus, String runStatus) {
if (onlineStatus == '0') {
return const Color.fromRGBO(102, 102, 102, 1.0);
}
switch (runStatus) {
case 'alert':
case 'alarm':
return const Color.fromRGBO(255, 77, 79, 1.0);
case 'warning':
case '注意':
return const Color.fromRGBO(255, 193, 7, 1.0);
default:
return const Color.fromRGBO(26, 188, 156, 1.0);
}
}
} }
\ No newline at end of file
...@@ -18,6 +18,8 @@ class EquipmentItem { ...@@ -18,6 +18,8 @@ class EquipmentItem {
} }
class HomeIndexState extends Equatable { class HomeIndexState extends Equatable {
final bool isLoading;
final String? error;
final int highAlarmCount; final int highAlarmCount;
final int mediumAlarmCount; final int mediumAlarmCount;
final int onlineDeviceCount; final int onlineDeviceCount;
...@@ -28,6 +30,7 @@ class HomeIndexState extends Equatable { ...@@ -28,6 +30,7 @@ class HomeIndexState extends Equatable {
final String currentCurrent; final String currentCurrent;
final String waitTime; final String waitTime;
final String alarmLevel; final String alarmLevel;
final int voltageAlertId;
final String voltageAlarmTitle; final String voltageAlarmTitle;
final String voltageAlarmLocation; final String voltageAlarmLocation;
final String voltageValue; final String voltageValue;
...@@ -37,6 +40,8 @@ class HomeIndexState extends Equatable { ...@@ -37,6 +40,8 @@ class HomeIndexState extends Equatable {
final List<EquipmentItem> equipmentList; final List<EquipmentItem> equipmentList;
const HomeIndexState({ const HomeIndexState({
this.isLoading = false,
this.error,
this.highAlarmCount = 0, this.highAlarmCount = 0,
this.mediumAlarmCount = 0, this.mediumAlarmCount = 0,
this.onlineDeviceCount = 0, this.onlineDeviceCount = 0,
...@@ -53,10 +58,13 @@ class HomeIndexState extends Equatable { ...@@ -53,10 +58,13 @@ class HomeIndexState extends Equatable {
this.normalVoltage = '', this.normalVoltage = '',
this.voltageWaitTime = '', this.voltageWaitTime = '',
this.voltageAlarmLevel = '', this.voltageAlarmLevel = '',
this.voltageAlertId = 0,
this.equipmentList = const [], this.equipmentList = const [],
}); });
HomeIndexState copyWith({ HomeIndexState copyWith({
bool? isLoading,
String? error,
int? highAlarmCount, int? highAlarmCount,
int? mediumAlarmCount, int? mediumAlarmCount,
int? onlineDeviceCount, int? onlineDeviceCount,
...@@ -67,6 +75,7 @@ class HomeIndexState extends Equatable { ...@@ -67,6 +75,7 @@ class HomeIndexState extends Equatable {
String? currentCurrent, String? currentCurrent,
String? waitTime, String? waitTime,
String? alarmLevel, String? alarmLevel,
int? voltageAlertId,
String? voltageAlarmTitle, String? voltageAlarmTitle,
String? voltageAlarmLocation, String? voltageAlarmLocation,
String? voltageValue, String? voltageValue,
...@@ -76,6 +85,8 @@ class HomeIndexState extends Equatable { ...@@ -76,6 +85,8 @@ class HomeIndexState extends Equatable {
List<EquipmentItem>? equipmentList, List<EquipmentItem>? equipmentList,
}) { }) {
return HomeIndexState( return HomeIndexState(
isLoading: isLoading ?? this.isLoading,
error: error,
highAlarmCount: highAlarmCount ?? this.highAlarmCount, highAlarmCount: highAlarmCount ?? this.highAlarmCount,
mediumAlarmCount: mediumAlarmCount ?? this.mediumAlarmCount, mediumAlarmCount: mediumAlarmCount ?? this.mediumAlarmCount,
onlineDeviceCount: onlineDeviceCount ?? this.onlineDeviceCount, onlineDeviceCount: onlineDeviceCount ?? this.onlineDeviceCount,
...@@ -92,12 +103,15 @@ class HomeIndexState extends Equatable { ...@@ -92,12 +103,15 @@ class HomeIndexState extends Equatable {
normalVoltage: normalVoltage ?? this.normalVoltage, normalVoltage: normalVoltage ?? this.normalVoltage,
voltageWaitTime: voltageWaitTime ?? this.voltageWaitTime, voltageWaitTime: voltageWaitTime ?? this.voltageWaitTime,
voltageAlarmLevel: voltageAlarmLevel ?? this.voltageAlarmLevel, voltageAlarmLevel: voltageAlarmLevel ?? this.voltageAlarmLevel,
voltageAlertId: voltageAlertId ?? this.voltageAlertId,
equipmentList: equipmentList ?? this.equipmentList, equipmentList: equipmentList ?? this.equipmentList,
); );
} }
@override @override
List<Object?> get props => [ List<Object?> get props => [
isLoading,
error,
highAlarmCount, highAlarmCount,
mediumAlarmCount, mediumAlarmCount,
onlineDeviceCount, onlineDeviceCount,
...@@ -114,6 +128,7 @@ class HomeIndexState extends Equatable { ...@@ -114,6 +128,7 @@ class HomeIndexState extends Equatable {
normalVoltage, normalVoltage,
voltageWaitTime, voltageWaitTime,
voltageAlarmLevel, voltageAlarmLevel,
voltageAlertId,
equipmentList, equipmentList,
]; ];
} }
\ No newline at end of file
...@@ -2,6 +2,8 @@ import 'package:auto_route/auto_route.dart'; ...@@ -2,6 +2,8 @@ import 'package:auto_route/auto_route.dart';
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/repositories/alert_dashboard_repository.dart';
import 'package:smart_hotel_app/services/alert_dashboard_service.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/views/home/index/cubit/home_index_state.dart'; import 'package:smart_hotel_app/views/home/index/cubit/home_index_state.dart';
import 'package:smart_hotel_app/views/home/index/widget/equipment_list_block.dart'; import 'package:smart_hotel_app/views/home/index/widget/equipment_list_block.dart';
...@@ -17,7 +19,11 @@ class HomeView extends StatelessWidget { ...@@ -17,7 +19,11 @@ class HomeView extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (_) => HomeIndexCubit(), create: (_) {
final repository = AlertDashboardRepository();
final service = AlertDashboardService(repository: repository);
return HomeIndexCubit(service: service);
},
child: LayoutBuilder( child: LayoutBuilder(
builder: (context, constraints) { builder: (context, constraints) {
return SingleChildScrollView( return SingleChildScrollView(
...@@ -52,6 +58,7 @@ class HomeView extends StatelessWidget { ...@@ -52,6 +58,7 @@ class HomeView extends StatelessWidget {
), ),
SizedBox(height: 20.h), SizedBox(height: 20.h),
VoltageOperateBlock( VoltageOperateBlock(
alertId: state.voltageAlertId,
voltageAlarmTitle: state.voltageAlarmTitle, voltageAlarmTitle: state.voltageAlarmTitle,
voltageAlarmLocation: state.voltageAlarmLocation, voltageAlarmLocation: state.voltageAlarmLocation,
voltageValue: state.voltageValue, voltageValue: state.voltageValue,
......
...@@ -200,7 +200,7 @@ class TemperatureBlock extends StatelessWidget { ...@@ -200,7 +200,7 @@ class TemperatureBlock extends StatelessWidget {
), ),
GestureDetector( GestureDetector(
onTap: () { onTap: () {
context.pushRoute(const AbnormalDetailRoute()); // context.pushRoute(const AbnormalDetailRoute());
}, },
child: Container( child: Container(
padding: EdgeInsets.symmetric(horizontal: 32.w, vertical: 20.h), padding: EdgeInsets.symmetric(horizontal: 32.w, vertical: 20.h),
......
...@@ -4,6 +4,7 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; ...@@ -4,6 +4,7 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:smart_hotel_app/routes/app_router.gr.dart'; import 'package:smart_hotel_app/routes/app_router.gr.dart';
class VoltageOperateBlock extends StatelessWidget { class VoltageOperateBlock extends StatelessWidget {
final int alertId;
final String voltageAlarmTitle; final String voltageAlarmTitle;
final String voltageAlarmLocation; final String voltageAlarmLocation;
final String voltageValue; final String voltageValue;
...@@ -13,6 +14,7 @@ class VoltageOperateBlock extends StatelessWidget { ...@@ -13,6 +14,7 @@ class VoltageOperateBlock extends StatelessWidget {
const VoltageOperateBlock({ const VoltageOperateBlock({
super.key, super.key,
required this.alertId,
required this.voltageAlarmTitle, required this.voltageAlarmTitle,
required this.voltageAlarmLocation, required this.voltageAlarmLocation,
required this.voltageValue, required this.voltageValue,
...@@ -132,7 +134,7 @@ class VoltageOperateBlock extends StatelessWidget { ...@@ -132,7 +134,7 @@ class VoltageOperateBlock extends StatelessWidget {
Expanded( Expanded(
child: GestureDetector( child: GestureDetector(
onTap: () { onTap: () {
context.pushRoute(const AbnormalDetailRoute()); context.pushRoute(AbnormalDetailRoute(alertId: alertId));
}, },
child: Container( child: Container(
height: 88.h, height: 88.h,
......
# 智慧酒店 App 接口对接方案 # 智慧酒店 App 接口对接方案
...@@ -178,9 +178,16 @@ class xxxBO extends Equatable { ...@@ -178,9 +178,16 @@ class xxxBO extends Equatable {
### 3.1 接口总览 ### 3.1 接口总览
| 页面序号 | 方法 | 路径 | 状态 | 所属模块 | | 页面序号 | 方法 | 路径 | 状态 | 所属模块 | 要求描述 |
|------|------|------|------|---------| |------|------|------|------|---------|
| 登录页 |POST | /app/auth/login/sms | 已对接 | 用户模块 | | 登录页 |POST | /app/auth/login/password | 已对接 | 用户模块 |
| 告警 |GET | /app/alert/dashboard | 已对接 | 设备告警看板 |
| 告警详情 |GET | /app/alert/detail | 已对接 | 告警详情 |
| 告警详情-确认处理 |POST | /app/alert/handle | 未对接 | 告警详情 | 在告警详情页面点击 确认处理 按钮,调用该接口。需要做下防重复提交,处理过程中不可点。处理完成之后弹出操作成功/操作失败提示。成功之后按钮不可再点击 |
| 设备控制 |GET | /app/device/control/detail | 未对接 | 设备控制 | |
| 设备控制 |POST | /app/device/control/powerOn | 未对接 | 设备控制-远程送电 | 需要做下防重复提交,处理过程中不可点。处理完成之后弹出操作成功/操作失败提示 |
| 设备控制 |POST | /app/device/control/powerOff | 未对接 | 设备控制-远程断电 | 需要做下防重复提交,处理过程中不可点。处理完成之后弹出操作成功/操作失败提示|
### 3.2 接口详细定义 ### 3.2 接口详细定义
...@@ -215,9 +222,218 @@ class xxxBO extends Equatable { ...@@ -215,9 +222,218 @@ class xxxBO extends Equatable {
} }
``` ```
#### 设备告警看板
--- **GET /app/alert/dashboard**
```
求体:
{
}
应 data:
{
"code": 0,
"msg": "string",
"data": {
"total": 0,
"statistics": {
"highAlertCount": 0, // 高警待处理数量
"mediumAlertCount": 0, // 中警待处理数量
"onlineDeviceCount": 0 // 在线设备数量
},
"alerts": [ //告警信息列表
{
"alertId": 0, // 告警ID
"alertNo": "string",
"alertTitle": "string", // 告警标题
"alertLevel": "string", // 告警级别编码
"alertLevelText": "string", // 告警级别中文描述
"alertStatus": "string",// 告警状态编码(0=待处理,1=处理中,2=已处理,3=忽略,4=误报)
"roomName": "string", // 所属房间名称
"deviceName": "string",// 告警设备名称
"alertValue": "string",
"thresholdValue": "string",
"alertContent": "string",
"voltage": "string", // 告警时电压(V)
"current": "string", // 告警时电流(A)
"temperature": "string", // 告警时温度(℃)
"waitingDuration": 0, // 告警持续时长(分钟)
"waitingDurationDesc": "string", // 告警持续时长的中文描述
"alertTime": "string"
}
],
"devices": [ // 设备巡检列表
{
"deviceId": 0, // 设备ID
"deviceName": "string", // 设备名称
"deviceTypeIcon": "string",
"deviceTypeName": "string",
"runStatus": "string",
"runStatusText": "string", // 运行状态中文描述
"roomName": "string", // 所属房间名称
"temperature": "string", // 告警时温度(℃)
"voltage": "string", // 设备当前电压(V)
"power": "string", // 设备当前功率(kW)
"current": "string", // 设备当前电流(A)
"onlineStatus": "string", // 在线状态(0=离线,1=在线)
"lastHeartbeat": "string",
"deviceTypeIcon": "string", // 设备类型图标标识
}
]
}
}
```
#### 告警详情
**GET /app/alert/detail**
```
求体:
{
"alertId": 0 // 告警ID
}
应 data:
{
"code": 0,
"msg": "string",
"data": {
"alertBasicInfo": { // 告警基本信息
"alertId": 0,
"alertTitle": "string", // 告警标题(如"温度异常告警")
"alertLevel": "string",
"alertLevelText": "string",
"alertStatus": "string",
"alertDeviceLocation": "string",
"alertCount": 0,
"alertTime": "string",
"alertIcon": "string"
},
"trendChart": { // 趋势图
"chartTitle": "string",
"chartDate": "string",
"unit": "string",
"dataPoints": [
{
"time": "string",
"value": 0,
"recordId": 0
}
],
"yaxisMax": 0,
"yaxisMin": 0
},
"alertDetailInfo": { //
"thresholdSetting": "string", // 预设阈值描述(如"温度>80°C触发")
"duration": "string", // 持续时长文本(如"2小时37分钟")
"durationMinutes": 0, // 持续时长(分钟)
"peakRecord": "string", // 峰值记录(如"88.2℃(15:02)")
"linkageAction": "string", // 联动动作描述(如"自动断电保护")
"alertValue": "string", // 告警触发值
"alertContent": "string" // 告警内容详情
}
}
}
```
#### 告警详情-确认处理
**POST /app/alert/handle**
```
求体:
{
"alertId": 0 // 告警ID
}
应 data:
{
"code": 0,
"msg": "string",
"data": {
"success": true
}
}
```
#### 设备控制
**GET /app/device/control/detail**
```
求体:
{
"deviceId": 0 // 设备ID
}
应 data:
{
"code": 0,
"msg": "string",
"data": {
"deviceBasicInfo": { // 设备基本信息
"deviceId": 0,
"deviceName": "string",
"deviceCode": "string",
"roomName": "string",
"deviceTypeIcon": "string",
"onlineStatus": "string"
},
"realtimeParams": { // 实时电气参数
"voltage": "string",
"current": "string",
"activePower": "string",
"deviceTemperature": "string"
},
"trendChart": { // 趋势图表数据
"chartTitle": "string",
"unit": "string",
"dataPoints": [
{
"time": "string",
"power": 0,
"temperature": 0
}
],
"yaxisMax": 0,
"yaxisMin": 0
}
}
}
```
---
## 6. 注意事项 ## 6. 注意事项
......
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