Commit dd39c5b7 authored by 张宏's avatar 张宏

重构设备控制页面。

parent 6106b9fe
This diff is collapsed.
...@@ -4,6 +4,7 @@ import 'package:auto_route/auto_route.dart'; ...@@ -4,6 +4,7 @@ import 'package:auto_route/auto_route.dart';
import 'package:laki_icu_app/views/login/login_view.dart'; import 'package:laki_icu_app/views/login/login_view.dart';
import 'package:laki_icu_app/views/layout/default_view.dart'; import 'package:laki_icu_app/views/layout/default_view.dart';
import 'package:laki_icu_app/views/monitoring/index/monitoring_index_view.dart'; import 'package:laki_icu_app/views/monitoring/index/monitoring_index_view.dart';
import 'package:laki_icu_app/views/monitoring/device_control/device_control_index_view.dart';
import 'package:laki_icu_app/views/blood_pressure/blood_pressure_index_view.dart'; import 'package:laki_icu_app/views/blood_pressure/blood_pressure_index_view.dart';
import 'package:laki_icu_app/views/vital_signs/vital_signs_index_view.dart'; import 'package:laki_icu_app/views/vital_signs/vital_signs_index_view.dart';
import 'package:laki_icu_app/views/ecg_system/ecg_system_index_view.dart'; import 'package:laki_icu_app/views/ecg_system/ecg_system_index_view.dart';
...@@ -28,7 +29,10 @@ class AppRouter extends $AppRouter { ...@@ -28,7 +29,10 @@ class AppRouter extends $AppRouter {
initial: true, initial: true,
children: [ children: [
// MonitoringIndexRoute / MonitoringIndexView(ICU中控 - Tab 0) // MonitoringIndexRoute / MonitoringIndexView(ICU中控 - Tab 0)
AutoRoute(page: MonitoringIndexRoute.page, initial: true), AutoRoute(
page: MonitoringIndexRoute.page,
initial: true,
),
// BloodPressureIndexRoute / BloodPressureIndexView(血压监测 - Tab 1) // BloodPressureIndexRoute / BloodPressureIndexView(血压监测 - Tab 1)
AutoRoute(page: BloodPressureIndexRoute.page), AutoRoute(page: BloodPressureIndexRoute.page),
// VitalSignsIndexRoute / VitalSignsIndexView(体征仪 - Tab 2) // VitalSignsIndexRoute / VitalSignsIndexView(体征仪 - Tab 2)
...@@ -41,7 +45,10 @@ class AppRouter extends $AppRouter { ...@@ -41,7 +45,10 @@ class AppRouter extends $AppRouter {
AutoRoute(page: RecordIndexRoute.page), AutoRoute(page: RecordIndexRoute.page),
// SettingsIndexRoute / SettingsIndexView(设置 - Tab 6) // SettingsIndexRoute / SettingsIndexView(设置 - Tab 6)
AutoRoute(page: SettingsIndexRoute.page), AutoRoute(page: SettingsIndexRoute.page),
AutoRoute(page: DeviceControlIndexRoute.page),
], ],
), ),
]; ];
} }
This diff is collapsed.
...@@ -17,6 +17,7 @@ const _menuIdToIndex = <String, int>{ ...@@ -17,6 +17,7 @@ const _menuIdToIndex = <String, int>{
'tsaas': 4, 'tsaas': 4,
'record': 5, 'record': 5,
'settings': 6, 'settings': 6,
'device_control': 7
}; };
const _indexToMenuId = <int, String>{ const _indexToMenuId = <int, String>{
...@@ -27,6 +28,7 @@ const _indexToMenuId = <int, String>{ ...@@ -27,6 +28,7 @@ const _indexToMenuId = <int, String>{
4: 'tsaas', 4: 'tsaas',
5: 'record', 5: 'record',
6: 'settings', 6: 'settings',
7: 'device_control',
}; };
/// 将 Tab 索引转换为菜单 ID /// 将 Tab 索引转换为菜单 ID
......
...@@ -74,6 +74,7 @@ class _DefaultLayoutContent extends StatelessWidget { ...@@ -74,6 +74,7 @@ class _DefaultLayoutContent extends StatelessWidget {
TsaasIndexRoute(), TsaasIndexRoute(),
RecordIndexRoute(), RecordIndexRoute(),
SettingsIndexRoute(), SettingsIndexRoute(),
DeviceControlIndexRoute(), // index=7,该路由实际不在菜单栏。
], ],
appBarBuilder: (context, tabsRouter) { appBarBuilder: (context, tabsRouter) {
return PreferredSize( return PreferredSize(
......
import 'package:flutter_bloc/flutter_bloc.dart';
import 'controller_index_state.dart';
class ControllerIndexCubit extends Cubit<ControllerIndexState> {
ControllerIndexCubit({
required String selectedMetricLabel,
}) : super(ControllerIndexState(selectedMetricLabel: selectedMetricLabel));
void selectMetric(String label) {
emit(state.copyWith(selectedMetricLabel: label));
}
}
import 'package:equatable/equatable.dart';
class ControllerIndexState extends Equatable {
final String selectedMetricLabel;
const ControllerIndexState({
required this.selectedMetricLabel,
});
ControllerIndexState copyWith({
String? selectedMetricLabel,
}) {
return ControllerIndexState(
selectedMetricLabel: selectedMetricLabel ?? this.selectedMetricLabel,
);
}
@override
List<Object?> get props => [selectedMetricLabel];
}
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:laki_icu_app/models/bo/monitoring_bo.dart';
import 'cubit/controller_index_cubit.dart';
import 'cubit/controller_index_state.dart';
import 'widgets/monitoring_environment_detail_view.dart';
class MonitoringControllerView extends StatelessWidget {
const MonitoringControllerView({
super.key,
required this.metrics,
required this.selectedMetricLabel,
required this.onBack,
});
final List<MonitoringMetricBO> metrics;
final String selectedMetricLabel;
final VoidCallback onBack;
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => ControllerIndexCubit(
selectedMetricLabel: selectedMetricLabel,
),
child: ControllerIndexContent(
metrics: metrics,
onBack: onBack,
),
);
}
}
class ControllerIndexContent extends StatelessWidget {
const ControllerIndexContent({
super.key,
required this.metrics,
required this.onBack,
});
final List<MonitoringMetricBO> metrics;
final VoidCallback onBack;
@override
Widget build(BuildContext context) {
return BlocBuilder<ControllerIndexCubit, ControllerIndexState>(
builder: (context, state) {
return MonitoringEnvironmentDetailView(
metrics: metrics,
selectedLabel: state.selectedMetricLabel,
onBack: onBack,
onMetricSelected: context.read<ControllerIndexCubit>().selectMetric,
);
},
);
}
}
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:laki_icu_app/models/bo/monitoring_bo.dart';
import 'package:laki_icu_app/services/monitoring_service.dart';
import 'device_control_index_state.dart';
class DeviceControlIndexCubit extends Cubit<DeviceControlIndexState> {
final MonitoringService _monitoringService;
DeviceControlIndexCubit({
MonitoringService? monitoringService,
}) : _monitoringService = monitoringService ?? MonitoringService(),
super(DeviceControlIndexState()) {
loadMetrics();
}
void selectMetric(String label) {
emit(state.copyWith(selectedMetricLabel: label));
}
Future<void> loadMetrics() async {
emit(state.copyWith(
status: DeviceControlIndexStatus.loading,
isLoading: true,
error: null,
));
try {
final allMetrics = await _monitoringService.getMetrics();
final envMetrics = _environmentMetrics(allMetrics);
emit(state.copyWith(
status: DeviceControlIndexStatus.success,
isLoading: false,
metrics: envMetrics,
error: null,
));
} catch (e) {
emit(state.copyWith(
status: DeviceControlIndexStatus.failure,
isLoading: false,
error: e.toString(),
));
}
}
/// 过滤出环境指标(温度/湿度/氧气/二氧化碳),最多取4个
List<MonitoringMetricBO> _environmentMetrics(
List<MonitoringMetricBO> metrics) {
final matched = metrics
.where((item) =>
item.label.contains('温度') ||
item.label.contains('湿度') ||
item.label.contains('氧气') ||
item.label.contains('二氧化碳') ||
item.label.toUpperCase().contains('CO'))
.take(4)
.toList();
if (matched.length == 4) return matched;
return const [
MonitoringMetricBO(value: '--', unit: '℃', label: '舱内温度'),
MonitoringMetricBO(value: '--', unit: 'RH', label: '舱内湿度'),
MonitoringMetricBO(value: '--', unit: '%', label: '氧气浓度'),
MonitoringMetricBO(value: '--', unit: '%', label: '二氧化碳'),
];
}
}
import 'package:equatable/equatable.dart';
import 'package:laki_icu_app/models/bo/monitoring_bo.dart';
enum DeviceControlIndexStatus { initial, loading, success, failure }
class DeviceControlIndexState extends Equatable {
final DeviceControlIndexStatus status;
final bool isLoading;
final String? error;
final List<MonitoringMetricBO> metrics;
final String selectedMetricLabel;
const DeviceControlIndexState({
this.status = DeviceControlIndexStatus.initial,
this.isLoading = false,
this.error,
this.metrics = const [],
this.selectedMetricLabel = '',
});
DeviceControlIndexState copyWith({
DeviceControlIndexStatus? status,
bool? isLoading,
String? error,
List<MonitoringMetricBO>? metrics,
String? selectedMetricLabel,
}) {
return DeviceControlIndexState(
status: status ?? this.status,
isLoading: isLoading ?? this.isLoading,
error: error ?? this.error,
metrics: metrics ?? this.metrics,
selectedMetricLabel: selectedMetricLabel ?? this.selectedMetricLabel,
);
}
@override
List<Object?> get props => [
status,
isLoading,
error,
metrics,
selectedMetricLabel,
];
}
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:laki_icu_app/models/bo/monitoring_bo.dart';
import 'cubit/device_control_index_cubit.dart';
import 'cubit/device_control_index_state.dart';
import 'widgets/device_control_care_panel.dart';
import 'widgets/device_control_info_panel.dart';
import 'widgets/device_control_metric_card.dart';
import 'widgets/device_control_mode_panel.dart';
import 'widgets/device_control_oxygen_timer_panel.dart';
import 'widgets/device_control_time_panel.dart';
import 'widgets/device_control_wind_panel.dart';
@RoutePage()
class DeviceControlIndexView extends StatelessWidget {
const DeviceControlIndexView({
super.key,
// this.selectedMetricLabel = '',
});
// final String selectedMetricLabel;
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => DeviceControlIndexCubit(),
child: const DeviceControlIndexContent(),
);
}
}
class DeviceControlIndexContent extends StatelessWidget {
const DeviceControlIndexContent({super.key});
@override
Widget build(BuildContext context) {
return BlocBuilder<DeviceControlIndexCubit, DeviceControlIndexState>(
builder: (context, state) {
if (state.isLoading && state.metrics.isEmpty) {
return const Scaffold(
backgroundColor: Color(0xFF010B1D),
body: Center(
child: CircularProgressIndicator(color: Colors.white),
),
);
}
if (state.status == DeviceControlIndexStatus.failure &&
state.metrics.isEmpty) {
return Scaffold(
backgroundColor: const Color(0xFF010B1D),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'加载失败: ${state.error ?? "未知错误"}',
style: TextStyle(color: Colors.white, fontSize: 22.sp),
),
SizedBox(height: 22.h),
ElevatedButton(
onPressed: () =>
context.read<DeviceControlIndexCubit>().loadMetrics(),
child: const Text('重试'),
),
],
),
),
);
}
return _DeviceControlLayout(
metrics: state.metrics,
selectedLabel: state.selectedMetricLabel,
onBack: () => context.router.popForced(),
onMetricSelected:
context.read<DeviceControlIndexCubit>().selectMetric,
);
},
);
}
}
/// 设备控制主布局
class _DeviceControlLayout extends StatelessWidget {
const _DeviceControlLayout({
required this.metrics,
required this.selectedLabel,
required this.onBack,
required this.onMetricSelected,
});
final List<MonitoringMetricBO> metrics;
final String selectedLabel;
final VoidCallback onBack;
final ValueChanged<String> onMetricSelected;
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFF010B1D),
body: SafeArea(
maintainBottomViewPadding: true,
child: Row(
children: [
Expanded(
flex: 74,
child: Column(
children: [
Expanded(
flex: 42,
child: Row(
children: [
for (int index = 0;
index < metrics.length;
index++) ...[
Expanded(
child: DeviceControlMetricCard(
metric: metrics[index],
),
),
if (index < metrics.length - 1)
SizedBox(width: 24.w),
],
],
),
),
SizedBox(height: 24.h),
Expanded(
flex: 58,
child: Row(
children: [
const Expanded(
flex: 48,
child: DeviceControlModePanel(),
),
SizedBox(width: 24.w),
const Expanded(
flex: 23,
child: DeviceControlWindPanel(),
),
SizedBox(width: 24.w),
const Expanded(
flex: 29,
child: DeviceControlTimePanel(),
),
],
),
),
],
),
),
SizedBox(width: 24.w),
Expanded(
flex: 26,
child: Column(
children: [
const Expanded(flex: 21, child: DeviceControlCarePanel()),
SizedBox(height: 24.h),
const Expanded(
flex: 20,
child: DeviceControlInfoPanel(
title: '新风进化',
actionText: 'Auto',
icon: Icons.cyclone,
body: 'CO₂超过设定值时自动启动外循环\n净化,当降至2000ppm后,本\n功能自动关闭',
),
),
SizedBox(height: 24.h),
const Expanded(
flex: 20,
child: DeviceControlInfoPanel(
title: '开放供氧',
actionText: 'Off',
icon: Icons.air,
body: '开启后风扇打开、制冷、加热和\n循环等设备暂停。',
warning: '*保持设备周围空气相对禁止,严禁烟火',
),
),
SizedBox(height: 24.h),
const Expanded(
flex: 26,
child: DeviceControlOxygenTimerPanel(),
),
],
),
),
],
),
),
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'device_control_common.dart';
/// 护理等级面板
class DeviceControlCarePanel extends StatelessWidget {
const DeviceControlCarePanel({super.key});
@override
Widget build(BuildContext context) {
return DeviceControlPanelFrame(
backgroundAsset: assetPanelWide,
padding: EdgeInsets.fromLTRB(30.w, 26.h, 30.w, 24.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const DeviceControlPanelTitle('护理等级'),
const Spacer(),
Container(
height: 20.h,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.r),
gradient: const LinearGradient(
colors: [
Color(0xFFDCEFFC),
Color(0xFF71FFCF),
Color(0xFFE4CF1D),
Color(0xFFFFA11B),
Color(0xFFFF5945),
Color(0xFFFFFFFF),
],
),
),
),
SizedBox(height: 24.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
for (final item in const ['关闭', '三级', '二级', '一级', '特级'])
Text(
item,
style: TextStyle(color: Colors.white, fontSize: 24.sp),
),
],
),
],
),
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
// -- 资源常量 --
const String assetMetricBlue = 'lib/assets/monitoring/box_bg_325x372.png';
const String assetMetricGreen =
'lib/assets/monitoring/frame_card_green_325x372.png';
const String assetPanelWide = 'lib/assets/monitoring/frame_button_324x164.png';
const String assetPanelLarge = 'lib/assets/monitoring/frame_panel_674x509.png';
const String assetPanelTall = 'lib/assets/monitoring/box_bg_460x905.png';
const String assetButton = 'lib/assets/monitoring/frame_button_324x164.png';
const String assetArrowButton =
'lib/assets/monitoring/frame_button_arrows_561x637.png';
/// 面板外框容器
class DeviceControlPanelFrame extends StatelessWidget {
const DeviceControlPanelFrame({
super.key,
required this.child,
required this.padding,
this.active = false,
this.backgroundAsset,
});
final Widget child;
final EdgeInsetsGeometry padding;
final bool active;
final String? backgroundAsset;
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
height: double.infinity,
padding: padding,
decoration: BoxDecoration(
color: backgroundAsset == null
? active
? const Color(0xFF003F14).withValues(alpha: 0.92)
: const Color(0xFF061C34).withValues(alpha: 0.92)
: null,
image: backgroundAsset == null
? null
: DecorationImage(
image: AssetImage(backgroundAsset!),
fit: BoxFit.fill,
),
border: backgroundAsset == null
? Border.all(
color: active
? const Color(0xFF3EFF5F)
: const Color(0xFF89DFFF),
width: 3.w,
)
: null,
borderRadius: BorderRadius.circular(8.r),
boxShadow: [
BoxShadow(
color: (active
? const Color(0xFF3EFF5F)
: const Color(0xFF4FC8FF))
.withValues(alpha: 0.32),
blurRadius: 18.r,
spreadRadius: 2.r,
),
],
),
child: child,
);
}
}
/// 面板标题
class DeviceControlPanelTitle extends StatelessWidget {
const DeviceControlPanelTitle(this.text, {super.key});
final String text;
@override
Widget build(BuildContext context) {
return Text(
text,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Colors.white,
fontSize: 28.sp,
fontWeight: FontWeight.w700,
),
);
}
}
/// 圆角标签
class DeviceControlPill extends StatelessWidget {
const DeviceControlPill({
super.key,
required this.text,
this.active = false,
});
final String text;
final bool active;
@override
Widget build(BuildContext context) {
return Container(
height: 40.h,
constraints: BoxConstraints(minWidth: 90.w),
padding: EdgeInsets.symmetric(horizontal: 18.w),
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: active ? 0.14 : 0.06),
border: Border.all(
color: active ? const Color(0xFF52FFBB) : const Color(0xFF75C1DD),
width: 1.w,
),
borderRadius: BorderRadius.circular(20.r),
),
child: Text(
text,
style: TextStyle(
color: active ? const Color(0xFF52FFBB) : const Color(0xFF75C1DD),
fontSize: 22.sp,
fontWeight: FontWeight.w700,
),
),
);
}
}
/// 加减调节文字
class DeviceControlAdjustText extends StatelessWidget {
const DeviceControlAdjustText(this.text, {super.key, this.active = false});
final String text;
final bool active;
@override
Widget build(BuildContext context) {
return Text(
text,
style: TextStyle(
color: active ? const Color(0xFF20E5FF) : const Color(0xFF4B7191),
fontSize: 58.sp,
height: 1,
fontWeight: FontWeight.w700,
),
);
}
}
/// 大号操作按钮
class DeviceControlBigActionButton extends StatelessWidget {
const DeviceControlBigActionButton({super.key, required this.text});
final String text;
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
height: 64.h,
decoration: deviceControlButtonDecoration(backgroundAsset: assetButton),
alignment: Alignment.center,
child: Text(
'《《 $text 》》',
style: TextStyle(
color: const Color(0xFFC9F8FF),
fontSize: 28.sp,
fontWeight: FontWeight.w700,
),
),
);
}
}
/// 数字值展示框
class DeviceControlDigitalValueBox extends StatelessWidget {
const DeviceControlDigitalValueBox({
super.key,
required this.value,
required this.unit,
this.active = false,
});
final String value;
final String unit;
final bool active;
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
decoration: deviceControlButtonDecoration(
active: active,
backgroundAsset: assetButton,
),
alignment: Alignment.center,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
value,
style: TextStyle(
color: active ? const Color(0xFFC8FFFF) : Colors.white,
fontSize: 46.sp,
letterSpacing: 2,
fontWeight: FontWeight.w500,
),
),
SizedBox(width: 28.w),
Text(
unit,
style: TextStyle(
color: Colors.white,
fontSize: 30.sp,
fontWeight: FontWeight.w600,
),
),
],
),
);
}
}
/// 按钮装饰(公共样式)
BoxDecoration deviceControlButtonDecoration({
bool active = false,
String? backgroundAsset,
}) {
return BoxDecoration(
color: backgroundAsset == null
? active
? const Color(0xFF063F1A).withValues(alpha: 0.96)
: const Color(0xFF0A243F).withValues(alpha: 0.96)
: null,
image: backgroundAsset == null
? null
: DecorationImage(
image: AssetImage(backgroundAsset),
fit: BoxFit.fill,
),
border: backgroundAsset == null
? Border.all(
color: active ? const Color(0xFF2DF765) : const Color(0xFF66BDE8),
width: 2,
)
: null,
borderRadius: BorderRadius.circular(6),
);
}
/// 指标图标资源映射
String deviceControlIconAsset(String label) {
if (label.contains('温度')) {
return 'lib/assets/monitoring/cabin_temperature.png';
}
if (label.contains('湿度')) {
return 'lib/assets/monitoring/cabin_humidity.png';
}
if (label.contains('氧气')) {
return 'lib/assets/monitoring/oxygen_concentration.png';
}
return 'lib/assets/monitoring/carbon_dioxide_concentration.png';
}
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'device_control_common.dart';
/// 信息展示面板(新风进化/开放供氧 等)
class DeviceControlInfoPanel extends StatelessWidget {
const DeviceControlInfoPanel({
super.key,
required this.title,
required this.actionText,
required this.icon,
required this.body,
this.warning,
});
final String title;
final String actionText;
final IconData icon;
final String body;
final String? warning;
@override
Widget build(BuildContext context) {
return DeviceControlPanelFrame(
backgroundAsset: assetPanelWide,
padding: EdgeInsets.fromLTRB(30.w, 22.h, 30.w, 24.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(child: DeviceControlPanelTitle(title)),
DeviceControlPill(text: actionText),
],
),
const Spacer(),
Row(
children: [
Icon(icon, color: const Color(0xFF6AC6F2), size: 54.w),
SizedBox(width: 28.w),
Expanded(
child: Text(
body,
style: TextStyle(
color: const Color(0xFFC9F8FF),
fontSize: 20.sp,
height: 1.35,
),
),
),
],
),
if (warning != null) ...[
SizedBox(height: 8.h),
Text(
warning!,
style: TextStyle(
color: const Color(0xFFFFC400),
fontSize: 16.sp,
),
),
],
],
),
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:laki_icu_app/models/bo/monitoring_bo.dart';
import 'device_control_common.dart';
/// 环境指标控制卡片(温度/湿度/氧气/二氧化碳)
class DeviceControlMetricCard extends StatelessWidget {
const DeviceControlMetricCard({super.key, required this.metric});
final MonitoringMetricBO metric;
@override
Widget build(BuildContext context) {
final isOxygen = metric.label.contains('氧气');
final isCo2 = metric.label.contains('二氧化碳') ||
metric.label.toUpperCase().contains('CO');
return DeviceControlPanelFrame(
active: isOxygen,
backgroundAsset: isOxygen ? assetMetricGreen : assetMetricBlue,
padding: EdgeInsets.fromLTRB(32.w, 26.h, 32.w, 28.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: DeviceControlPanelTitle(_title(metric.label)),
),
DeviceControlPill(
text: isOxygen
? 'ON'
: isCo2
? 'PPM'
: 'Off',
active: isOxygen,
),
],
),
const Spacer(),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
metric.value,
style: TextStyle(
color: Colors.white,
fontSize: isCo2 ? 60.sp : 82.sp,
height: 0.9,
fontWeight: FontWeight.bold,
),
),
SizedBox(width: 12.w),
Padding(
padding: EdgeInsets.only(bottom: 8.h),
child: Text(
isCo2 ? '' : metric.unit,
style: TextStyle(
color: Colors.white,
fontSize: 28.sp,
fontWeight: FontWeight.w600,
),
),
),
const Spacer(),
Image.asset(
deviceControlIconAsset(metric.label),
width: 72.w,
height: 72.w,
fit: BoxFit.contain,
),
],
),
Divider(color: Colors.white.withValues(alpha: 0.18), height: 28.h),
Text(
isCo2
? '告警阈值设定'
: metric.label.contains('温度')
? '温度设定'
: '湿度设定',
style: TextStyle(
color: Colors.white.withValues(alpha: 0.78),
fontSize: 26.sp,
),
),
const Spacer(),
Row(
children: [
DeviceControlAdjustText('-', active: isOxygen || !isCo2),
const Spacer(),
Text(
isCo2
? '4000'
: isOxygen
? '90'
: metric.label.contains('湿度')
? '10'
: '24',
style: TextStyle(
color: isCo2
? const Color(0xFFFFC400)
: const Color(0xFF00B7F4),
fontSize: 64.sp,
height: 1,
fontWeight: FontWeight.bold,
),
),
const Spacer(),
const DeviceControlAdjustText('+', active: true),
],
),
],
),
);
}
static String _title(String label) {
if (label.contains('二氧化碳')) return '二氧化碳浓度';
return label;
}
}
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'device_control_common.dart';
/// 模式切换面板(外循环/检查灯/照明灯/内循环/蓝光/红光)
class DeviceControlModePanel extends StatelessWidget {
const DeviceControlModePanel({super.key});
@override
Widget build(BuildContext context) {
const items = [
_ModeItem(label: '外循环', icon: Icons.sync, active: true),
_ModeItem(label: '检查灯', icon: Icons.light),
_ModeItem(label: '照明灯', icon: Icons.lightbulb),
_ModeItem(label: '内循环', icon: Icons.autorenew),
_ModeItem(label: '蓝光', icon: Icons.wb_sunny_outlined),
_ModeItem(label: '红光', icon: Icons.wb_sunny),
];
return DeviceControlPanelFrame(
backgroundAsset: assetPanelLarge,
padding: EdgeInsets.all(28.w),
child: GridView.count(
crossAxisCount: 3,
crossAxisSpacing: 24.w,
mainAxisSpacing: 24.h,
physics: const NeverScrollableScrollPhysics(),
childAspectRatio: 1.1,
children: [
for (final item in items)
_ModeButton(
label: item.label,
icon: item.icon,
active: item.active,
),
],
),
);
}
}
class _ModeButton extends StatelessWidget {
const _ModeButton({
required this.label,
required this.icon,
this.active = false,
});
final String label;
final IconData icon;
final bool active;
@override
Widget build(BuildContext context) {
final color = active ? const Color(0xFF45F36F) : const Color(0xFF7DC4EA);
return Container(
decoration: deviceControlButtonDecoration(
active: active,
backgroundAsset: assetArrowButton,
),
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 18.h),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, color: color, size: 56.w),
SizedBox(height: 20.h),
Text(
label,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: color,
fontSize: 26.sp,
fontWeight: FontWeight.w700,
),
),
],
),
);
}
}
class _ModeItem {
final String label;
final IconData icon;
final bool active;
const _ModeItem({
required this.label,
required this.icon,
this.active = false,
});
}
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'device_control_common.dart';
/// 供氧计时 + ICU重置面板
class DeviceControlOxygenTimerPanel extends StatelessWidget {
const DeviceControlOxygenTimerPanel({super.key});
@override
Widget build(BuildContext context) {
return DeviceControlPanelFrame(
backgroundAsset: assetPanelWide,
padding: EdgeInsets.fromLTRB(30.w, 26.h, 30.w, 28.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const DeviceControlPanelTitle('供氧计时'),
const Spacer(),
Row(
children: [
Text(
'00:00:00',
style: TextStyle(
color: Colors.white,
fontSize: 42.sp,
letterSpacing: 2,
fontWeight: FontWeight.w500,
),
),
const Spacer(),
const DeviceControlPill(text: '清零'),
],
),
Divider(color: Colors.white.withValues(alpha: 0.16), height: 38.h),
Text(
'ICU舱设置重置',
style: TextStyle(
color: Colors.white,
fontSize: 28.sp,
fontWeight: FontWeight.w600,
),
),
const Spacer(),
const DeviceControlBigActionButton(text: '初始化设置'),
],
),
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'device_control_common.dart';
/// 设置雾化/消毒时间面板
class DeviceControlTimePanel extends StatelessWidget {
const DeviceControlTimePanel({super.key});
@override
Widget build(BuildContext context) {
return DeviceControlPanelFrame(
backgroundAsset: assetPanelTall,
padding: EdgeInsets.fromLTRB(34.w, 28.h, 34.w, 28.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const DeviceControlPanelTitle('设置雾化时间'),
SizedBox(height: 18.h),
const Expanded(
child: DeviceControlDigitalValueBox(value: '00', unit: '分钟'),
),
Divider(color: Colors.white.withValues(alpha: 0.16), height: 36.h),
const DeviceControlPanelTitle('设置消毒时间'),
SizedBox(height: 18.h),
const Expanded(
child: DeviceControlDigitalValueBox(
value: '14',
unit: '分钟',
active: true,
),
),
],
),
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'device_control_common.dart';
/// 风速调节面板
class DeviceControlWindPanel extends StatelessWidget {
const DeviceControlWindPanel({super.key});
@override
Widget build(BuildContext context) {
return DeviceControlPanelFrame(
backgroundAsset: assetPanelTall,
padding: EdgeInsets.fromLTRB(34.w, 28.h, 34.w, 28.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const DeviceControlPanelTitle('风速调节'),
SizedBox(height: 24.h),
const Expanded(
child: _WideModeButton(
label: '睡眠模式',
icon: Icons.nightlight_round,
),
),
SizedBox(height: 18.h),
const Expanded(
child: _WideModeButton(
label: '舒适模式',
icon: Icons.favorite_border,
),
),
SizedBox(height: 18.h),
const Expanded(
child: _WideModeButton(label: '强劲模式', icon: Icons.air),
),
],
),
);
}
}
class _WideModeButton extends StatelessWidget {
const _WideModeButton({
required this.label,
required this.icon,
});
final String label;
final IconData icon;
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
decoration: deviceControlButtonDecoration(backgroundAsset: assetButton),
padding: EdgeInsets.symmetric(horizontal: 28.w),
child: Row(
children: [
Icon(icon, color: const Color(0xFF7DC4EA), size: 52.w),
SizedBox(width: 34.w),
Expanded(
child: Text(
label,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Colors.white,
fontSize: 26.sp,
fontWeight: FontWeight.w600,
),
),
),
],
),
);
}
}
...@@ -8,7 +8,7 @@ import 'package:laki_icu_app/enums/video_stream_mode_enum.dart'; ...@@ -8,7 +8,7 @@ import 'package:laki_icu_app/enums/video_stream_mode_enum.dart';
import 'package:laki_icu_app/models/bo/monitoring_bo.dart'; import 'package:laki_icu_app/models/bo/monitoring_bo.dart';
import 'package:laki_icu_app/utils/event_bus.dart'; import 'package:laki_icu_app/utils/event_bus.dart';
import '../controller/monitoring_controller_view.dart';
import 'cubit/monitoring_index_cubit.dart'; import 'cubit/monitoring_index_cubit.dart';
import 'cubit/monitoring_index_state.dart'; import 'cubit/monitoring_index_state.dart';
import 'widgets/bluetooth_bind_dialog.dart'; import 'widgets/bluetooth_bind_dialog.dart';
...@@ -40,7 +40,6 @@ class MonitoringIndexContent extends StatefulWidget { ...@@ -40,7 +40,6 @@ class MonitoringIndexContent extends StatefulWidget {
class _MonitoringIndexContentState extends State<MonitoringIndexContent> { class _MonitoringIndexContentState extends State<MonitoringIndexContent> {
late final MonitoringIndexCubit _monitoringIndexCubit; late final MonitoringIndexCubit _monitoringIndexCubit;
StreamSubscription<OpenBluetoothScanEvent>? _bluetoothScanEventSub; StreamSubscription<OpenBluetoothScanEvent>? _bluetoothScanEventSub;
String? _selectedEnvironmentMetricLabel;
@override @override
void initState() { void initState() {
...@@ -71,9 +70,6 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> { ...@@ -71,9 +70,6 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> {
state.metrics.isEmpty) { state.metrics.isEmpty) {
return _buildError(state.error); return _buildError(state.error);
} }
if (_selectedEnvironmentMetricLabel != null) {
return _buildEnvironmentDetail(state);
}
return _buildMainArea(state); return _buildMainArea(state);
}, },
); );
...@@ -116,7 +112,7 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> { ...@@ -116,7 +112,7 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> {
children: [ children: [
Expanded( Expanded(
flex: 32, flex: 32,
child: _buildMetrics(state.metrics), child: state.metrics.isEmpty ? _buildMetricsEmpty() : _buildMetrics(state.metrics),
), ),
SizedBox(height: 24.h), SizedBox(height: 24.h),
Expanded( Expanded(
...@@ -153,6 +149,10 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> { ...@@ -153,6 +149,10 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> {
); );
} }
Widget _buildMetricsEmpty() {
return Text("data");
}
Widget _buildMetrics(List<MonitoringMetricBO> metrics) { Widget _buildMetrics(List<MonitoringMetricBO> metrics) {
final displayMetrics = _environmentMetrics(metrics); final displayMetrics = _environmentMetrics(metrics);
final children = <Widget>[]; final children = <Widget>[];
...@@ -161,7 +161,7 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> { ...@@ -161,7 +161,7 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> {
Expanded( Expanded(
child: MonitoringMetricCard( child: MonitoringMetricCard(
metric: displayMetrics[i], metric: displayMetrics[i],
onTap: () => _openEnvironmentDetail(displayMetrics[i]), onTap: () => _openDeviceControl(displayMetrics[i]),
), ),
), ),
); );
...@@ -174,23 +174,8 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> { ...@@ -174,23 +174,8 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> {
); );
} }
Widget _buildEnvironmentDetail(MonitoringIndexState state) { void _openDeviceControl(MonitoringMetricBO metric) {
final metrics = _environmentMetrics(state.metrics); context.tabsRouter.setActiveIndex(7);
return MonitoringControllerView(
metrics: metrics,
selectedMetricLabel: _selectedEnvironmentMetricLabel!,
onBack: () {
setState(() {
_selectedEnvironmentMetricLabel = null;
});
},
);
}
void _openEnvironmentDetail(MonitoringMetricBO metric) {
setState(() {
_selectedEnvironmentMetricLabel = metric.label;
});
} }
/// 模式切换回调 /// 模式切换回调
......
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