Commit 0f9cb2e3 authored by akari's avatar akari

feat: 中控页功能联调修复

parent 4dff6ccd
export 'device_calibration_config_bloc.dart';
export 'device_calibration_config_event.dart';
export 'device_calibration_config_state.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:laki_icu_app/models/bo/device_calibration_config_bo.dart';
import 'device_calibration_config_event.dart';
import 'device_calibration_config_state.dart';
class DeviceCalibrationConfigBloc extends HydratedBloc<
DeviceCalibrationConfigEvent, DeviceCalibrationConfigState> {
DeviceCalibrationConfigBloc() : super(const DeviceCalibrationConfigState()) {
on<DeviceCalibrationConfigLoaded>(_onLoaded);
on<DeviceCalibrationConfigItemUpdated>(_onItemUpdated);
on<DeviceCalibrationConfigUpdated>(_onConfigUpdated);
on<DeviceCalibrationConfigResetToDefault>(_onResetToDefault);
}
Future<void> _onLoaded(
DeviceCalibrationConfigLoaded event,
Emitter<DeviceCalibrationConfigState> emit,
) async {
emit(state.copyWith(isLoading: true, clearError: true));
try {
emit(state.copyWith(isLoading: false, clearError: true));
} catch (e) {
emit(state.copyWith(isLoading: false, error: e.toString()));
}
}
void _onItemUpdated(
DeviceCalibrationConfigItemUpdated event,
Emitter<DeviceCalibrationConfigState> emit,
) {
emit(state.copyWith(
config: state.config.updateItem(event.key, event.value),
clearError: true,
));
}
void _onConfigUpdated(
DeviceCalibrationConfigUpdated event,
Emitter<DeviceCalibrationConfigState> emit,
) {
emit(state.copyWith(
config: event.config,
clearError: true,
));
}
void _onResetToDefault(
DeviceCalibrationConfigResetToDefault event,
Emitter<DeviceCalibrationConfigState> emit,
) {
emit(const DeviceCalibrationConfigState(
config: DeviceCalibrationConfigBO.defaultConfig,
));
}
void updateItem(DeviceCalibrationItemKey key, int value) {
add(DeviceCalibrationConfigItemUpdated(key: key, value: value));
}
void updateConfig(DeviceCalibrationConfigBO config) {
add(DeviceCalibrationConfigUpdated(config));
}
void resetToDefault() {
add(const DeviceCalibrationConfigResetToDefault());
}
@override
DeviceCalibrationConfigState? fromJson(Map<String, dynamic> json) {
try {
return DeviceCalibrationConfigState(
config: DeviceCalibrationConfigBO.fromJson(json),
);
} catch (e) {
return null;
}
}
@override
Map<String, dynamic>? toJson(DeviceCalibrationConfigState state) {
return state.config.toJson();
}
}
/// 全局设备校准配置 Bloc 引用。
///
/// 由 main.dart 在创建时写入,页面外需要直接读取校准参数时可通过此引用获取。
DeviceCalibrationConfigBloc? globalDeviceCalibrationConfigBloc;
import 'package:equatable/equatable.dart';
import 'package:laki_icu_app/models/bo/device_calibration_config_bo.dart';
abstract class DeviceCalibrationConfigEvent extends Equatable {
const DeviceCalibrationConfigEvent();
@override
List<Object?> get props => [];
}
class DeviceCalibrationConfigLoaded extends DeviceCalibrationConfigEvent {
const DeviceCalibrationConfigLoaded();
}
class DeviceCalibrationConfigItemUpdated extends DeviceCalibrationConfigEvent {
const DeviceCalibrationConfigItemUpdated({
required this.key,
required this.value,
});
final DeviceCalibrationItemKey key;
final int value;
@override
List<Object?> get props => [key, value];
}
class DeviceCalibrationConfigUpdated extends DeviceCalibrationConfigEvent {
const DeviceCalibrationConfigUpdated(this.config);
final DeviceCalibrationConfigBO config;
@override
List<Object?> get props => [config];
}
class DeviceCalibrationConfigResetToDefault
extends DeviceCalibrationConfigEvent {
const DeviceCalibrationConfigResetToDefault();
}
import 'package:equatable/equatable.dart';
import 'package:laki_icu_app/models/bo/device_calibration_config_bo.dart';
class DeviceCalibrationConfigState extends Equatable {
const DeviceCalibrationConfigState({
this.config = DeviceCalibrationConfigBO.defaultConfig,
this.isLoading = false,
this.error,
});
final DeviceCalibrationConfigBO config;
final bool isLoading;
final String? error;
DeviceCalibrationConfigState copyWith({
DeviceCalibrationConfigBO? config,
bool? isLoading,
String? error,
bool clearError = false,
}) {
return DeviceCalibrationConfigState(
config: config ?? this.config,
isLoading: isLoading ?? this.isLoading,
error: clearError ? null : error ?? this.error,
);
}
@override
List<Object?> get props => [config, isLoading, error];
}
......@@ -129,6 +129,7 @@ class DeviceControlBloc
DateTime? _pendingCareLevelSince;
late final StreamSubscription<McuReportChangedEvent> _mcuReportSub;
late final StreamSubscription<BluetoothDataPacket> _bluetoothDataSub;
Timer? _oxygenTimer;
/// 上次设备告警分发时间(30s 防抖)
DateTime? _lastAlarmDispatchTime;
......@@ -269,6 +270,7 @@ class DeviceControlBloc
),
);
emit(state.copyWith(data: newData, clearError: true));
_syncLocalOxygenTimer(report.oxygenConcentration);
// 异步分发:触发设备异常告警(MQTT 上行 + UI 通知)
_dispatchDeviceAlarm(newData);
......@@ -903,6 +905,11 @@ class DeviceControlBloc
DeviceControlOxygenTimerUpdated event,
Emitter<DeviceControlState> emit,
) {
final totalSeconds = _oxygenTimerSecondsFromParts(
event.hours,
event.minutes,
event.seconds,
);
final newData = state.data.copyWith(
oxygenTimer: state.data.oxygenTimer.copyWith(
hours: event.hours,
......@@ -910,7 +917,14 @@ class DeviceControlBloc
seconds: event.seconds,
),
);
emit(state.copyWith(data: newData, clearError: true));
final newUserSettings = state.userSettings.copyWith(
oxygenTimerSeconds: totalSeconds,
);
emit(state.copyWith(
data: newData,
userSettings: newUserSettings,
clearError: true,
));
}
/// [用户操作] 清零供氧计时
......@@ -925,7 +939,14 @@ class DeviceControlBloc
seconds: '00',
),
);
emit(state.copyWith(data: newData, clearError: true));
final newUserSettings = state.userSettings.copyWith(
oxygenTimerSeconds: 0,
);
emit(state.copyWith(
data: newData,
userSettings: newUserSettings,
clearError: true,
));
await _writeControlValueToBluetooth(
identifier: 0x29,
setValue: '0',
......@@ -1045,13 +1066,128 @@ class DeviceControlBloc
emit(state.copyWith(data: newData, clearError: true));
}
/// [用户操作] 重置设备设置为默认值 —— 清除持久化
void _onResetSettings(
/// [用户操作] 关闭所有可关闭控制并按 300ms 间隔下发
Future<void> _onResetSettings(
DeviceControlResetSettings event,
Emitter<DeviceControlState> emit,
) {
emit(const DeviceControlState());
// TODO: 发送重置指令到MCU
) async {
final newData = state.data.copyWith(
cabinTemp: state.data.cabinTemp.copyWith(isOn: false),
cabinHumidity: state.data.cabinHumidity.copyWith(isOn: false),
oxygenConcentration: state.data.oxygenConcentration.copyWith(
isOn: false,
setValue: _clampIntString(
state.data.oxygenConcentration.setValue,
0,
60,
),
),
openOxygen: state.data.openOxygen.copyWith(isOn: false),
windSpeed: state.data.windSpeed.copyWith(currentMode: WindSpeedMode.off),
nebulizationTime: state.data.nebulizationTime.copyWith(setMinutes: '0'),
disinfectionTime: state.data.disinfectionTime.copyWith(setMinutes: '0'),
careLevel: state.data.careLevel.copyWith(currentLevel: CareLevel.off),
lightControl: state.data.lightControl.copyWith(
checkLightOn: false,
illuminationOn: false,
blueLightOn: false,
redLightOn: false,
),
);
final newUserSettings = state.userSettings.copyWith(
cabinTempIsOn: false,
cabinHumidityIsOn: false,
oxygenConcentrationIsOn: false,
oxygenConcentrationSetValue: newData.oxygenConcentration.setValue,
openOxygenIsOn: false,
windSpeedModeValue: WindSpeedMode.off,
nebulizationTimeMinutes: '0',
disinfectionTimeMinutes: '0',
careLevelValue: CareLevel.off,
checkLightOn: false,
illuminationOn: false,
blueLightOn: false,
redLightOn: false,
);
emit(state.copyWith(
data: newData,
userSettings: newUserSettings,
clearError: true,
));
await _writeResetCommandWithDelay(
identifier: 0x02,
setValue: '10',
errorPrefix: '舱内温度关闭',
emit: emit,
);
await _writeResetCommandWithDelay(
identifier: 0x04,
setValue: '100',
errorPrefix: '舱内湿度关闭',
emit: emit,
);
await _writeResetCommandWithDelay(
identifier: 0x05,
setValue: '20',
errorPrefix: '氧气浓度关闭',
emit: emit,
);
await _writeResetCommandWithDelay(
identifier: 0x26,
setValue: '0',
errorPrefix: '开放供氧关闭',
emit: emit,
);
await _writeResetCommandWithDelay(
identifier: 0xB3,
setValue: '${_windSpeedProtocolValue(WindSpeedMode.off)}',
errorPrefix: '风速关闭',
emit: emit,
);
await _writeResetCommandWithDelay(
identifier: 0x0C,
setValue: '0',
errorPrefix: '雾化关闭',
emit: emit,
);
await _writeResetCommandWithDelay(
identifier: 0x0D,
setValue: '0',
errorPrefix: '消毒关闭',
emit: emit,
);
await _writeResetCommandWithDelay(
identifier: 0xA8,
setValue: '${_careLevelProtocolValue(CareLevel.off)}',
errorPrefix: '护理等级关闭',
emit: emit,
);
await _writeResetCommandWithDelay(
identifier: _lightIdentifier(_LightTarget.check),
setValue: '0',
errorPrefix: '检查灯关闭',
emit: emit,
);
await _writeResetCommandWithDelay(
identifier: _lightIdentifier(_LightTarget.illumination),
setValue: '0',
errorPrefix: '照明灯关闭',
emit: emit,
);
await _writeResetCommandWithDelay(
identifier: _lightIdentifier(_LightTarget.blue),
setValue: '0',
errorPrefix: '蓝光关闭',
emit: emit,
);
await _writeResetCommandWithDelay(
identifier: _lightIdentifier(_LightTarget.red),
setValue: '0',
errorPrefix: '红光关闭',
emit: emit,
delayAfter: false,
);
}
int get _oxygenSetValueMax => state.data.openOxygen.isOn ? 80 : 60;
......@@ -1099,6 +1235,60 @@ class DeviceControlBloc
return '${parsed.clamp(min, max)}';
}
void _syncLocalOxygenTimer(int oxygenConcentration) {
if (oxygenConcentration >= 30) {
_oxygenTimer ??= Timer.periodic(const Duration(seconds: 1), (_) {
if (isClosed) return;
final totalSeconds = _currentOxygenTimerSeconds() + 1;
final timer = _oxygenTimerFromSeconds(totalSeconds);
add(DeviceControlOxygenTimerUpdated(
timer.hours,
timer.minutes,
timer.seconds,
));
});
return;
}
_stopLocalOxygenTimer();
}
int _currentOxygenTimerSeconds() {
return state.userSettings.oxygenTimerSeconds ??
_oxygenTimerSecondsFromParts(
state.data.oxygenTimer.hours,
state.data.oxygenTimer.minutes,
state.data.oxygenTimer.seconds,
);
}
int _oxygenTimerSecondsFromParts(
String hours,
String minutes,
String seconds,
) {
final parsedHours = int.tryParse(hours) ?? 0;
final parsedMinutes = int.tryParse(minutes) ?? 0;
final parsedSeconds = int.tryParse(seconds) ?? 0;
return parsedHours * 3600 + parsedMinutes * 60 + parsedSeconds;
}
OxygenTimerBO _oxygenTimerFromSeconds(int totalSeconds) {
final safeSeconds = totalSeconds < 0 ? 0 : totalSeconds;
final hours = safeSeconds ~/ 3600;
final minutes = (safeSeconds % 3600) ~/ 60;
final seconds = safeSeconds % 60;
return OxygenTimerBO(
hours: hours.toString().padLeft(2, '0'),
minutes: minutes.toString().padLeft(2, '0'),
seconds: seconds.toString().padLeft(2, '0'),
);
}
void _stopLocalOxygenTimer() {
_oxygenTimer?.cancel();
_oxygenTimer = null;
}
Future<void> _setExclusiveLight({
required _LightTarget target,
required bool isOn,
......@@ -1306,6 +1496,24 @@ class DeviceControlBloc
);
}
Future<void> _writeResetCommandWithDelay({
required int identifier,
required String setValue,
required String errorPrefix,
required Emitter<DeviceControlState> emit,
bool delayAfter = true,
}) async {
await _writeControlValueToBluetooth(
identifier: identifier,
setValue: setValue,
errorPrefix: errorPrefix,
emit: emit,
);
if (delayAfter && !isClosed) {
await Future<void>.delayed(const Duration(milliseconds: 300));
}
}
int _windSpeedProtocolValue(WindSpeedMode mode) {
switch (mode) {
case WindSpeedMode.off:
......@@ -1421,6 +1629,7 @@ class DeviceControlBloc
@override
Future<void> close() async {
_stopLocalOxygenTimer();
await _mcuReportSub.cancel();
await _bluetoothDataSub.cancel();
return super.close();
......
......@@ -29,10 +29,18 @@ import 'package:flutter_ume_kit_dio_plus/flutter_ume_kit_dio_plus.dart';
import 'package:flutter_ume_kit_ui_plus/flutter_ume_kit_ui_plus.dart';
import 'package:laki_icu_app/blocs/device_control/device_control_bloc.dart';
import 'package:laki_icu_app/blocs/device_calibration_config/device_calibration_config.dart';
import 'package:laki_icu_app/blocs/device_threshold_config/device_threshold_config.dart';
import 'package:marionette_flutter/marionette_flutter.dart';
Future<void> main(List<String> args) async {
if (kDebugMode) {
// MarionetteBinding 继承自 WidgetsFlutterBinding,必须最先且唯一初始化
MarionetteBinding.ensureInitialized();
} else {
WidgetsFlutterBinding.ensureInitialized();
}
// 初始化 HydratedBloc 存储(用于持久化设备控制用户设置等)
final storageDirectory = await getApplicationDocumentsDirectory();
HydratedBloc.storage = await HydratedStorage.build(
......@@ -41,9 +49,6 @@ Future<void> main(List<String> args) async {
// UME 调试工具(仅 Debug 模式启用)
if (kDebugMode) {
// MarionetteBinding 继承自 WidgetsFlutterBinding,必须最先且唯一初始化
MarionetteBinding.ensureInitialized();
PluginManager.instance
..register(WidgetInfoInspector())
..register(WidgetDetailInspector())
......@@ -54,8 +59,6 @@ Future<void> main(List<String> args) async {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
runApp(UMEWidget(enable: true, child: const MyApp()));
} else {
WidgetsFlutterBinding.ensureInitialized();
// 全局全屏:隐藏系统状态栏和底部导航栏
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
runApp(const MyApp());
......@@ -78,6 +81,7 @@ class _MyAppState extends State<MyApp> {
late final AuthBloc _authBloc;
BluetoothReadBloc? _bluetoothReadBloc;
DeviceControlBloc? _deviceControlBloc;
DeviceCalibrationConfigBloc? _deviceCalibrationConfigBloc;
DeviceThresholdConfigBloc? _deviceThresholdConfigBloc;
StreamSubscription? _tokenExpiredSubscription;
bool _isInitializing = true;
......@@ -101,6 +105,15 @@ class _MyAppState extends State<MyApp> {
return _deviceThresholdConfigBloc!;
}
DeviceCalibrationConfigBloc get _deviceCalibrationConfigBlocInstance {
if (_deviceCalibrationConfigBloc != null) {
return _deviceCalibrationConfigBloc!;
}
_deviceCalibrationConfigBloc = DeviceCalibrationConfigBloc();
globalDeviceCalibrationConfigBloc = _deviceCalibrationConfigBloc;
return _deviceCalibrationConfigBloc!;
}
@override
void initState() {
super.initState();
......@@ -169,6 +182,7 @@ class _MyAppState extends State<MyApp> {
_authBloc.close();
_bluetoothReadBloc?.close();
_deviceControlBloc?.close();
_deviceCalibrationConfigBloc?.close();
_deviceThresholdConfigBloc?.close();
super.dispose();
}
......@@ -187,6 +201,9 @@ class _MyAppState extends State<MyApp> {
BlocProvider<DeviceControlBloc>.value(
value: _deviceControlBlocInstance,
),
BlocProvider<DeviceCalibrationConfigBloc>.value(
value: _deviceCalibrationConfigBlocInstance,
),
BlocProvider<DeviceThresholdConfigBloc>.value(
value: _deviceThresholdConfigBlocInstance,
),
......@@ -200,8 +217,8 @@ class _MyAppState extends State<MyApp> {
debugShowCheckedModeBanner: false,
builder: (context, child) {
// 每次页面重建时重新隐藏状态栏,防止路由切换后状态栏重新出现
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual, overlays: []);
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
overlays: []);
return child ?? const SizedBox.shrink();
},
),
......
This diff is collapsed.
......@@ -801,6 +801,9 @@ class DeviceControlUserSettings extends Equatable {
/// 开放供氧开关
final bool? openOxygenIsOn;
/// 本地供氧计时秒数
final int? oxygenTimerSeconds;
/// 循环模式
final CirculationMode? circulationModeValue;
......@@ -831,6 +834,7 @@ class DeviceControlUserSettings extends Equatable {
this.nebulizationTimeMinutes,
this.disinfectionTimeMinutes,
this.openOxygenIsOn,
this.oxygenTimerSeconds,
this.circulationModeValue,
this.checkLightOn,
this.illuminationOn,
......@@ -853,6 +857,7 @@ class DeviceControlUserSettings extends Equatable {
String? nebulizationTimeMinutes,
String? disinfectionTimeMinutes,
bool? openOxygenIsOn,
int? oxygenTimerSeconds,
CirculationMode? circulationModeValue,
bool? checkLightOn,
bool? illuminationOn,
......@@ -879,6 +884,7 @@ class DeviceControlUserSettings extends Equatable {
disinfectionTimeMinutes:
disinfectionTimeMinutes ?? this.disinfectionTimeMinutes,
openOxygenIsOn: openOxygenIsOn ?? this.openOxygenIsOn,
oxygenTimerSeconds: oxygenTimerSeconds ?? this.oxygenTimerSeconds,
circulationModeValue: circulationModeValue ?? this.circulationModeValue,
checkLightOn: checkLightOn ?? this.checkLightOn,
illuminationOn: illuminationOn ?? this.illuminationOn,
......@@ -926,6 +932,9 @@ class DeviceControlUserSettings extends Equatable {
openOxygen: openOxygenIsOn != null
? bo.openOxygen.copyWith(isOn: openOxygenIsOn)
: null,
oxygenTimer: oxygenTimerSeconds != null
? _oxygenTimerFromSeconds(oxygenTimerSeconds!)
: null,
circulationMode: circulationModeValue != null
? bo.circulationMode.copyWith(currentMode: circulationModeValue)
: null,
......@@ -970,6 +979,7 @@ class DeviceControlUserSettings extends Equatable {
nebulizationTimeMinutes: json['nebulizationTimeMinutes'] as String?,
disinfectionTimeMinutes: json['disinfectionTimeMinutes'] as String?,
openOxygenIsOn: json['openOxygenIsOn'] as bool?,
oxygenTimerSeconds: json['oxygenTimerSeconds'] as int?,
circulationModeValue: json['circulationModeValue'] != null
? CirculationMode.values.firstWhere(
(e) => e.name == json['circulationModeValue'] as String,
......@@ -1006,6 +1016,7 @@ class DeviceControlUserSettings extends Equatable {
if (disinfectionTimeMinutes != null)
'disinfectionTimeMinutes': disinfectionTimeMinutes,
if (openOxygenIsOn != null) 'openOxygenIsOn': openOxygenIsOn,
if (oxygenTimerSeconds != null) 'oxygenTimerSeconds': oxygenTimerSeconds,
if (circulationModeValue != null)
'circulationModeValue': circulationModeValue!.name,
if (checkLightOn != null) 'checkLightOn': checkLightOn,
......@@ -1031,6 +1042,7 @@ class DeviceControlUserSettings extends Equatable {
nebulizationTimeMinutes,
disinfectionTimeMinutes,
openOxygenIsOn,
oxygenTimerSeconds,
circulationModeValue,
checkLightOn,
illuminationOn,
......@@ -1038,3 +1050,15 @@ class DeviceControlUserSettings extends Equatable {
redLightOn,
];
}
OxygenTimerBO _oxygenTimerFromSeconds(int totalSeconds) {
final safeSeconds = totalSeconds < 0 ? 0 : totalSeconds;
final hours = safeSeconds ~/ 3600;
final minutes = (safeSeconds % 3600) ~/ 60;
final seconds = safeSeconds % 60;
return OxygenTimerBO(
hours: hours.toString().padLeft(2, '0'),
minutes: minutes.toString().padLeft(2, '0'),
seconds: seconds.toString().padLeft(2, '0'),
);
}
......@@ -18,6 +18,8 @@ class NumericKeyboard extends StatefulWidget {
super.key,
required this.initialValue,
this.decimalEnabled = false,
this.showCloseKey = false,
this.closeKeyValue,
this.onChanged,
this.onConfirm,
});
......@@ -28,6 +30,12 @@ class NumericKeyboard extends StatefulWidget {
/// 是否允许输入小数点(温度模式为 true)
final bool decimalEnabled;
/// 是否在键盘右下角显示关闭按钮
final bool showCloseKey;
/// 点击关闭按钮时返回的值;为空则只关闭弹窗
final String? closeKeyValue;
/// 输入变化回调(可选)
final ValueChanged<String>? onChanged;
......@@ -45,6 +53,8 @@ class NumericKeyboard extends StatefulWidget {
BuildContext context, {
String initialValue = '',
bool decimalEnabled = false,
bool showCloseKey = false,
String? closeKeyValue,
}) {
return showModalBottomSheet<String>(
context: context,
......@@ -54,6 +64,8 @@ class NumericKeyboard extends StatefulWidget {
builder: (_) => NumericKeyboard(
initialValue: initialValue,
decimalEnabled: decimalEnabled,
showCloseKey: showCloseKey,
closeKeyValue: closeKeyValue,
),
);
}
......@@ -92,6 +104,9 @@ class _NumericKeyboardState extends State<NumericKeyboard> {
Navigator.of(context).pop(_text);
}
return; // 不触发 onChanged 和后续 setState 中的 pop
case '关闭':
Navigator.of(context).pop(widget.closeKeyValue);
return;
case '.':
if (!widget.decimalEnabled || _hasDecimal) return;
if (_text.isEmpty) {
......@@ -249,7 +264,7 @@ class _NumericKeyboardState extends State<NumericKeyboard> {
Widget _buildKeypad() {
// 按键定义:行列表,每行包含(标签, 宽度比例, 颜色类型)
final keys = <List<_KeyDef>>[
final topRows = <List<_KeyDef>>[
[
const _KeyDef('1', 1),
const _KeyDef('2', 1),
......@@ -262,23 +277,13 @@ class _NumericKeyboardState extends State<NumericKeyboard> {
const _KeyDef('6', 1),
const _KeyDef('C', 1, isAction: true),
],
[
const _KeyDef('7', 1),
const _KeyDef('8', 1),
const _KeyDef('9', 1),
const _KeyDef('', 1),
],
[
if (widget.decimalEnabled) const _KeyDef('.', 1),
_KeyDef('0', widget.decimalEnabled ? 2 : 3),
const _KeyDef('', 1),
],
];
return Padding(
padding: const EdgeInsets.only(top: 2),
child: Column(
children: keys.map((row) {
children: [
...topRows.map((row) {
return Row(
children: row.map((keyDef) {
if (keyDef.label.isEmpty) {
......@@ -293,15 +298,73 @@ class _NumericKeyboardState extends State<NumericKeyboard> {
);
}).toList(),
);
}),
Row(
children: [
Expanded(
flex: 3,
child: Column(
children: [
Row(
children: [
const _KeyDef('7', 1),
const _KeyDef('8', 1),
const _KeyDef('9', 1),
].map((keyDef) {
return Expanded(
flex: keyDef.flex,
child: Padding(
padding: const EdgeInsets.all(5),
child: _buildKeyButton(keyDef),
),
);
}).toList(),
),
Row(
children: [
if (widget.decimalEnabled) const _KeyDef('.', 1),
_KeyDef('0', widget.decimalEnabled ? 2 : 3),
].map((keyDef) {
return Expanded(
flex: keyDef.flex,
child: Padding(
padding: const EdgeInsets.all(5),
child: _buildKeyButton(keyDef),
),
);
}).toList(),
),
],
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(5),
child: widget.showCloseKey
? _buildKeyButton(
const _KeyDef(
'关闭',
1,
isAction: true,
isDanger: true,
),
height: 130,
)
: const SizedBox.shrink(),
),
),
],
),
],
),
);
}
Widget _buildKeyButton(_KeyDef keyDef) {
Widget _buildKeyButton(_KeyDef keyDef, {double height = 60}) {
final isAction = keyDef.isAction;
final isDanger = keyDef.isDanger;
return SizedBox(
height: 60,
height: height,
child: Material(
color: Colors.transparent,
borderRadius: BorderRadius.circular(8),
......@@ -310,12 +373,16 @@ class _NumericKeyboardState extends State<NumericKeyboard> {
onTap: () => _onKeyTap(keyDef.label),
child: Ink(
decoration: BoxDecoration(
color: isAction
color: isDanger
? const Color(0xFF7A1D1D)
: isAction
? const Color(0xFF10385D)
: const Color(0xFF092849),
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: isAction
color: isDanger
? const Color(0xFFFF6B6B)
: isAction
? const Color(0xFF5D9EC5)
: const Color(0xFF3F9ED2),
width: 1,
......@@ -334,7 +401,9 @@ class _NumericKeyboardState extends State<NumericKeyboard> {
style: TextStyle(
fontSize: isAction ? 20 : 25,
fontWeight: isAction ? FontWeight.w600 : FontWeight.w700,
color: isAction
color: isDanger
? const Color(0xFFFFE7E7)
: isAction
? const Color(0xFFB8E7FF)
: const Color(0xFFEAF9FF),
),
......@@ -352,5 +421,11 @@ class _KeyDef {
final String label;
final int flex;
final bool isAction;
const _KeyDef(this.label, this.flex, {this.isAction = false});
final bool isDanger;
const _KeyDef(
this.label,
this.flex, {
this.isAction = false,
this.isDanger = false,
});
}
......@@ -61,15 +61,22 @@ class DeviceControlCarePanel extends StatelessWidget {
}
}
class _CareLevelBar extends StatelessWidget {
class _CareLevelBar extends StatefulWidget {
const _CareLevelBar({required this.currentLevel});
final CareLevel currentLevel;
@override
State<_CareLevelBar> createState() => _CareLevelBarState();
}
class _CareLevelBarState extends State<_CareLevelBar> {
int? _lastDragIndex;
@override
Widget build(BuildContext context) {
final activeIndex = _careLevelItems.indexWhere(
(item) => item.level == currentLevel,
(item) => item.level == widget.currentLevel,
);
return LayoutBuilder(
......@@ -83,7 +90,23 @@ class _CareLevelBar extends StatelessWidget {
segmentWidth -
knobSize / 2;
return SizedBox(
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTapDown: (details) => _setLevelFromDx(
context,
details.localPosition.dx,
constraints.maxWidth,
activeIndex,
),
onHorizontalDragUpdate: (details) => _setLevelFromDx(
context,
details.localPosition.dx,
constraints.maxWidth,
activeIndex,
),
onHorizontalDragEnd: (_) => _lastDragIndex = null,
onHorizontalDragCancel: () => _lastDragIndex = null,
child: SizedBox(
height: knobSize,
child: Stack(
clipBehavior: Clip.none,
......@@ -99,13 +122,6 @@ class _CareLevelBar extends StatelessWidget {
index < _careLevelItems.length;
index++) ...[
Expanded(
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => context.read<DeviceControlBloc>().add(
DeviceControlCareLevelChanged(
_careLevelItems[index].level,
),
),
child: Container(
height: 20.h,
decoration: BoxDecoration(
......@@ -126,7 +142,6 @@ class _CareLevelBar extends StatelessWidget {
),
),
),
),
if (index < _careLevelItems.length - 1)
SizedBox(width: segmentGap),
],
......@@ -152,11 +167,32 @@ class _CareLevelBar extends StatelessWidget {
),
],
),
),
);
},
);
}
void _setLevelFromDx(
BuildContext context,
double dx,
double width,
int activeIndex,
) {
if (width <= 0) return;
final index = (dx / width * _careLevelItems.length)
.floor()
.clamp(0, _careLevelItems.length - 1)
.toInt();
if (index == activeIndex || index == _lastDragIndex) return;
_lastDragIndex = index;
context.read<DeviceControlBloc>().add(
DeviceControlCareLevelChanged(_careLevelItems[index].level),
);
}
BorderRadius _segmentRadius(int index) {
final radius = Radius.circular(12.r);
if (index == 0) {
......
......@@ -155,13 +155,21 @@ class DeviceControlAdjustText extends StatelessWidget {
/// 大号操作按钮
class DeviceControlBigActionButton extends StatelessWidget {
const DeviceControlBigActionButton({super.key, required this.text});
const DeviceControlBigActionButton({
super.key,
required this.text,
this.onTap,
});
final String text;
final VoidCallback? onTap;
@override
Widget build(BuildContext context) {
return Container(
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: onTap,
child: Container(
width: double.infinity,
height: 64.h,
decoration: deviceControlButtonDecoration(backgroundAsset: assetButton),
......@@ -174,6 +182,7 @@ class DeviceControlBigActionButton extends StatelessWidget {
fontWeight: FontWeight.w700,
),
),
),
);
}
}
......
......@@ -89,16 +89,31 @@ class DeviceControlMetricCard extends StatelessWidget {
isHumidity: isHumidity,
isOxygen: isOxygen,
);
final displaySetValue = !isCo2 && !isSwitchOn ? '--' : setValue;
final displaySetValue = setValue;
final VoidCallback? switchTap = switchEventBuilder == null
? null
: () {
confirmDeviceControlChange(
context,
() => context.read<DeviceControlBloc>().add(
switchEventBuilder(!isSwitchOn),
),
);
};
final isCardActive = !isCo2 && isSwitchOn;
return DeviceControlPanelFrame(
active: isOxygen,
backgroundAsset: isOxygen ? assetMetricGreen : assetMetricBlue,
active: isCardActive,
backgroundAsset: isCardActive ? assetMetricGreen : assetMetricBlue,
padding: EdgeInsets.fromLTRB(32.w, 26.h, 32.w, 28.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: switchTap,
child: Row(
children: [
Expanded(
child: DeviceControlPanelTitle(_title(metric.label)),
......@@ -106,21 +121,22 @@ class DeviceControlMetricCard extends StatelessWidget {
DeviceControlPill(
text: isCo2 ? 'PPM' : (isSwitchOn ? 'ON' : 'Off'),
active: !isCo2 && isSwitchOn,
onTap: switchEventBuilder == null
? null
: () {
confirmDeviceControlChange(
context,
() => context.read<DeviceControlBloc>().add(
switchEventBuilder(!isSwitchOn),
),
);
},
onTap: switchTap,
),
],
),
const Spacer(),
Row(
),
Expanded(
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: switchTap,
child: const SizedBox.expand(),
),
),
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: switchTap,
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
......@@ -153,6 +169,7 @@ class DeviceControlMetricCard extends StatelessWidget {
),
],
),
),
Divider(
color: Colors.white.withValues(alpha: 0.18), height: 28.h),
Text(
......
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:laki_icu_app/blocs/device_control/device_control_bloc.dart';
import 'package:laki_icu_app/blocs/device_control/device_control_event.dart';
import 'package:laki_icu_app/blocs/device_control/device_control_state.dart';
import 'device_control_common.dart';
......@@ -9,6 +13,14 @@ class DeviceControlOxygenTimerPanel extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<DeviceControlBloc, DeviceControlState>(
buildWhen: (previous, current) =>
previous.data.oxygenTimer != current.data.oxygenTimer,
builder: (context, state) {
final oxygenTimer = state.data.oxygenTimer;
final timeText =
'${oxygenTimer.hours}:${oxygenTimer.minutes}:${oxygenTimer.seconds}';
return DeviceControlPanelFrame(
backgroundAsset: assetPanelWide,
padding: EdgeInsets.fromLTRB(30.w, 26.h, 30.w, 28.h),
......@@ -20,7 +32,7 @@ class DeviceControlOxygenTimerPanel extends StatelessWidget {
Row(
children: [
Text(
'00:00:00',
timeText,
style: TextStyle(
color: Colors.white,
fontSize: 42.sp,
......@@ -29,10 +41,17 @@ class DeviceControlOxygenTimerPanel extends StatelessWidget {
),
),
const Spacer(),
const DeviceControlPill(text: '清零'),
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => context
.read<DeviceControlBloc>()
.add(const DeviceControlOxygenTimerCleared()),
child: const DeviceControlPill(text: '清零'),
),
],
),
Divider(color: Colors.white.withValues(alpha: 0.16), height: 38.h),
Divider(
color: Colors.white.withValues(alpha: 0.16), height: 38.h),
Text(
'ICU舱设置重置',
style: TextStyle(
......@@ -42,9 +61,16 @@ class DeviceControlOxygenTimerPanel extends StatelessWidget {
),
),
const Spacer(),
const DeviceControlBigActionButton(text: '初始化设置'),
DeviceControlBigActionButton(
text: '初始化设置',
onTap: () => context
.read<DeviceControlBloc>()
.add(const DeviceControlResetSettings()),
),
],
),
);
},
);
}
}
......@@ -76,6 +76,8 @@ class DeviceControlTimePanel extends StatelessWidget {
context,
initialValue: initialValue == '--' ? '' : initialValue,
decimalEnabled: false,
showCloseKey: true,
closeKeyValue: '0',
);
if (!context.mounted || result == null) return;
......
......@@ -651,6 +651,18 @@ class MonitoringIndexCubit extends HydratedCubit<MonitoringIndexState> {
try {
await stopBluetoothScan();
final boundDeviceId = state.boundBluetoothDeviceId;
if (boundDeviceId != null &&
boundDeviceId.isNotEmpty &&
boundDeviceId != device.remoteId) {
await unbindBluetoothDevice();
if (isClosed) return;
emit(state.copyWith(
isBluetoothBinding: true,
bindingBluetoothDeviceId: device.remoteId,
bluetoothMessage: '正在绑定蓝牙设备: ${device.name}',
));
}
await _bluetoothManager.connectToDevice(device);
if (!_bluetoothManager.isConnected) {
throw StateError('蓝牙连接未就绪');
......
......@@ -4,6 +4,8 @@ import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:laki_icu_app/blocs/device_control/device_control_bloc.dart';
import 'package:laki_icu_app/blocs/device_control/device_control_state.dart';
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/utils/event_bus.dart';
......@@ -214,13 +216,22 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> {
Widget _buildMetrics(List<MonitoringMetricBO> metrics) {
final displayMetrics = _environmentMetrics(metrics);
return BlocBuilder<DeviceControlBloc, DeviceControlState>(
buildWhen: (previous, current) =>
previous.data.cabinTemp.isOn != current.data.cabinTemp.isOn ||
previous.data.cabinHumidity.isOn != current.data.cabinHumidity.isOn ||
previous.data.oxygenConcentration.isOn !=
current.data.oxygenConcentration.isOn,
builder: (context, controlState) {
final children = <Widget>[];
for (int i = 0; i < displayMetrics.length; i++) {
final metric = displayMetrics[i];
children.add(
Expanded(
child: MonitoringMetricCard(
metric: displayMetrics[i],
onTap: () => _openDeviceControl(displayMetrics[i]),
metric: metric,
switchValue: _metricSwitchValue(metric, controlState),
onTap: () => _openDeviceControl(metric),
),
),
);
......@@ -231,6 +242,24 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> {
return Row(
children: children,
);
},
);
}
bool _metricSwitchValue(
MonitoringMetricBO metric,
DeviceControlState controlState,
) {
if (metric.label.contains('温度')) {
return controlState.data.cabinTemp.isOn;
}
if (metric.label.contains('湿度')) {
return controlState.data.cabinHumidity.isOn;
}
if (metric.label.contains('氧气')) {
return controlState.data.oxygenConcentration.isOn;
}
return false;
}
void _openDeviceControl(MonitoringMetricBO metric) {
......
......@@ -139,11 +139,12 @@ class _BluetoothBindDialogState extends State<BluetoothBindDialog> {
}
Widget _buildPanelStatus(BuildContext context, MonitoringIndexState state) {
final devices = _cnaDevices(state);
final message = state.isBluetoothScanning
? '自动发现附近可配对的监护舱设备,搜索中...'
: state.bluetoothDevices.isEmpty
: devices.isEmpty
? '未发现设备,可重新搜索附近蓝牙设备'
: '蓝牙扫描结束,共发现 ${state.bluetoothDevices.length} 个设备';
: '蓝牙扫描结束,共发现 ${devices.length} 个设备';
return Row(
children: [
......@@ -187,7 +188,9 @@ class _BluetoothBindDialogState extends State<BluetoothBindDialog> {
}
Widget _buildDeviceList(BuildContext context, MonitoringIndexState state) {
if (state.bluetoothDevices.isEmpty) {
final devices = _sortDevicesForDisplay(state);
if (devices.isEmpty) {
return Center(
child: Text(
state.isBluetoothScanning ? '正在扫描附近设备...' : '暂无设备',
......@@ -199,8 +202,6 @@ class _BluetoothBindDialogState extends State<BluetoothBindDialog> {
);
}
final devices = _sortDevicesForDisplay(state);
return ListView.separated(
padding: EdgeInsets.zero,
itemCount: devices.length,
......@@ -227,12 +228,13 @@ class _BluetoothBindDialogState extends State<BluetoothBindDialog> {
}
List<BluetoothScanDevice> _sortDevicesForDisplay(MonitoringIndexState state) {
final devices = _cnaDevices(state);
final boundDeviceId = state.boundBluetoothDeviceId;
if (boundDeviceId == null || boundDeviceId.isEmpty) {
return state.bluetoothDevices;
return devices;
}
return List<BluetoothScanDevice>.from(state.bluetoothDevices)
return devices
..sort((left, right) {
final leftIsBound = left.remoteId == boundDeviceId;
final rightIsBound = right.remoteId == boundDeviceId;
......@@ -243,6 +245,14 @@ class _BluetoothBindDialogState extends State<BluetoothBindDialog> {
});
}
bool _isCnaDevice(BluetoothScanDevice device) {
return device.name.trim().toUpperCase().startsWith('CNA');
}
List<BluetoothScanDevice> _cnaDevices(MonitoringIndexState state) {
return state.bluetoothDevices.where(_isCnaDevice).toList();
}
Future<void> _confirmUnbindBluetoothDevice(
BuildContext context,
BluetoothScanDevice device,
......
......@@ -76,16 +76,22 @@ class MonitoringMetricCard extends StatelessWidget {
height: 41.h,
alignment: AlignmentDirectional.center,
decoration: BoxDecoration(
color: Color(0xFF0A1B34),
color: switchValue
? const Color(0xFF063F1A)
: const Color(0xFF0A1B34),
border: BoxBorder.all(
width: 1.w,
color: Color(0xFF5A8BB0),
color: switchValue
? const Color(0xFF3EFF5F)
: const Color(0xFF5A8BB0),
),
borderRadius: BorderRadius.circular(20)),
child: Text(
"ON",
switchValue ? "ON" : "Off",
style: TextStyle(
color: Color(0xFF3C6082),
color: switchValue
? const Color(0xFF52FFBB)
: const Color(0xFF3C6082),
fontSize: 24.sp,
fontWeight: FontWeight.bold),
),
......
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:laki_icu_app/blocs/device_threshold_config/device_threshold_config.dart';
import 'package:laki_icu_app/models/bo/device_threshold_config_bo.dart';
import 'package:laki_icu_app/utils/toast_utils.dart';
import 'factory_settings_index_state.dart';
......@@ -14,6 +15,10 @@ class FactorySettingsIndexCubit extends Cubit<FactorySettingsIndexState> {
final DeviceThresholdConfigBloc _thresholdConfigBloc;
void _saveEditingConfig(DeviceThresholdConfigBO config) {
_thresholdConfigBloc.updateConfig(config);
}
void showCalibrationPage() {
emit(state.copyWith(isCalibrationPage: true));
}
......@@ -23,66 +28,92 @@ class FactorySettingsIndexCubit extends Cubit<FactorySettingsIndexState> {
}
void editTempMax(int value) {
final editingConfig = state.editingConfig.copyWith(tempMax: value);
emit(state.copyWith(
editingConfig: state.editingConfig.copyWith(tempMax: value),
isDirty: true,
editingConfig: editingConfig,
isDirty: false,
));
_saveEditingConfig(editingConfig);
}
void editTempMin(int value) {
final editingConfig = state.editingConfig.copyWith(tempMin: value);
emit(state.copyWith(
editingConfig: state.editingConfig.copyWith(tempMin: value),
isDirty: true,
editingConfig: editingConfig,
isDirty: false,
));
_saveEditingConfig(editingConfig);
}
void editOxygenMax(int value) {
final editingConfig = state.editingConfig.copyWith(oxygenMax: value);
emit(state.copyWith(
editingConfig: state.editingConfig.copyWith(oxygenMax: value),
isDirty: true,
editingConfig: editingConfig,
isDirty: false,
));
_saveEditingConfig(editingConfig);
}
void editOxygenMin(int value) {
final editingConfig = state.editingConfig.copyWith(oxygenMin: value);
emit(state.copyWith(
editingConfig: state.editingConfig.copyWith(oxygenMin: value),
isDirty: true,
editingConfig: editingConfig,
isDirty: false,
));
_saveEditingConfig(editingConfig);
}
void editHumidityMax(int value) {
final editingConfig = state.editingConfig.copyWith(humidityMax: value);
emit(state.copyWith(
editingConfig: state.editingConfig.copyWith(humidityMax: value),
isDirty: true,
editingConfig: editingConfig,
isDirty: false,
));
_saveEditingConfig(editingConfig);
}
void editHumidityMin(int value) {
final editingConfig = state.editingConfig.copyWith(humidityMin: value);
emit(state.copyWith(
editingConfig: state.editingConfig.copyWith(humidityMin: value),
isDirty: true,
editingConfig: editingConfig,
isDirty: false,
));
_saveEditingConfig(editingConfig);
}
void editUvTimeMax(int value) {
final editingConfig = state.editingConfig.copyWith(uvTimeMax: value);
emit(state.copyWith(
editingConfig: state.editingConfig.copyWith(uvTimeMax: value),
isDirty: true,
editingConfig: editingConfig,
isDirty: false,
));
_saveEditingConfig(editingConfig);
}
void editIrTimeMax(int value) {
final editingConfig = state.editingConfig.copyWith(irTimeMax: value);
emit(state.copyWith(
editingConfig: state.editingConfig.copyWith(irTimeMax: value),
isDirty: true,
editingConfig: editingConfig,
isDirty: false,
));
_saveEditingConfig(editingConfig);
}
void editAtomizeTimeMax(int value) {
final editingConfig = state.editingConfig.copyWith(atomizeTimeMax: value);
emit(state.copyWith(
editingConfig: editingConfig,
isDirty: false,
));
_saveEditingConfig(editingConfig);
}
void resetToDefault() {
emit(state.copyWith(
editingConfig: state.editingConfig.copyWith(atomizeTimeMax: value),
isDirty: true,
editingConfig: DeviceThresholdConfigBO.defaultConfig,
isDirty: false,
));
_saveEditingConfig(DeviceThresholdConfigBO.defaultConfig);
}
void applyChanges() {
......
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:laki_icu_app/blocs/device_calibration_config/device_calibration_config.dart';
import 'package:laki_icu_app/models/bo/device_calibration_config_bo.dart';
import 'package:laki_icu_app/utils/numeric_keyboard.dart';
import 'package:laki_icu_app/utils/toast_utils.dart';
import '../../widgets/settings_panel_frame.dart';
import '../cubit/factory_settings_index_cubit.dart';
......@@ -20,42 +23,57 @@ class FactoryDeviceCalibrationPanel extends StatefulWidget {
class _FactoryDeviceCalibrationPanelState
extends State<FactoryDeviceCalibrationPanel> {
late List<_CalibrationSection> _sections;
late DeviceCalibrationConfigBO _config;
@override
void initState() {
super.initState();
_sections = _defaultSections();
_config = context.read<DeviceCalibrationConfigBloc>().state.config;
}
Future<void> _editItem(_CalibrationItem item) async {
Future<void> _editItem(DeviceCalibrationItemBO item) async {
final result = await NumericKeyboard.show(
context,
initialValue: item.value,
initialValue: '${item.value}',
decimalEnabled: false,
);
if (!mounted || result == null) return;
final value = result.trim();
if (value.isEmpty) return;
final parsedValue = int.tryParse(value);
if (parsedValue == null) return;
setState(() {
item.value = value;
_config = _config.updateItem(item.key, parsedValue);
});
_saveConfig();
}
void _resetDefaults() {
setState(() {
_sections = _defaultSections();
_config = DeviceCalibrationConfigBO.defaultConfig;
});
_saveConfig();
}
void _saveConfig() {
context.read<DeviceCalibrationConfigBloc>().updateConfig(_config);
}
void _applyChanges() {
// UI placeholder only. Real calibration persistence will be wired later.
try {
_saveConfig();
showToast('操作成功');
} catch (e) {
showToast('操作失败');
}
}
@override
Widget build(BuildContext context) {
final sections = _config.sections;
return Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
......@@ -66,12 +84,12 @@ class _FactoryDeviceCalibrationPanelState
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_CalibrationSectionView(
section: _sections[0],
section: sections[0],
onItemTap: _enableCalibrationEditing ? _editItem : null,
),
SizedBox(height: 28.h),
_CalibrationSectionView(
section: _sections[1],
section: sections[1],
onItemTap: _enableCalibrationEditing ? _editItem : null,
),
],
......@@ -88,7 +106,7 @@ class _FactoryDeviceCalibrationPanelState
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_CalibrationSectionView(
section: _sections[2],
section: sections[2],
onItemTap: _enableCalibrationEditing ? _editItem : null,
),
const Spacer(),
......@@ -142,8 +160,8 @@ class _CalibrationSectionView extends StatelessWidget {
required this.onItemTap,
});
final _CalibrationSection section;
final ValueChanged<_CalibrationItem>? onItemTap;
final DeviceCalibrationSectionBO section;
final ValueChanged<DeviceCalibrationItemBO>? onItemTap;
@override
Widget build(BuildContext context) {
......@@ -179,7 +197,7 @@ class _CalibrationRow extends StatelessWidget {
required this.onTap,
});
final _CalibrationItem item;
final DeviceCalibrationItemBO item;
final VoidCallback? onTap;
@override
......@@ -202,7 +220,7 @@ class _CalibrationRow extends StatelessWidget {
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: onTap,
child: _CalibrationValueBox(value: item.value),
child: _CalibrationValueBox(value: '${item.value}'),
),
],
);
......@@ -277,58 +295,3 @@ class _CalibrationButton extends StatelessWidget {
);
}
}
class _CalibrationSection {
const _CalibrationSection({
required this.title,
required this.items,
});
final String title;
final List<_CalibrationItem> items;
}
class _CalibrationItem {
_CalibrationItem({
required this.label,
required this.value,
});
final String label;
String value;
}
List<_CalibrationSection> _defaultSections() {
return [
_CalibrationSection(
title: '主温度调节系数',
items: [
_CalibrationItem(label: '温度系数%S1(上):', value: '17'),
_CalibrationItem(label: '温度系数%S1(下):', value: '34'),
_CalibrationItem(label: '环境温度冬夏临界值T0:', value: '17'),
_CalibrationItem(label: '摄像头循环重启(分钟):', value: '34'),
_CalibrationItem(label: '摄像头定时重启(小时):', value: '17'),
],
),
_CalibrationSection(
title: 'CO₂报警及净化修正值',
items: [
_CalibrationItem(label: 'CO₂最低设置值C1:', value: '4000'),
_CalibrationItem(label: 'CO₂差值C2:', value: '2000'),
_CalibrationItem(label: 'CO₂基准值C3:', value: '1500'),
_CalibrationItem(label: 'CO₂系数C4:', value: '34'),
],
),
_CalibrationSection(
title: '密闭舱氧气修正值',
items: [
_CalibrationItem(label: '密闭舱氧气修正点X0:', value: '17'),
_CalibrationItem(label: '密闭舱氧气修正幅度X1:', value: '34'),
_CalibrationItem(label: '密闭舱氧气修正步进值X4:', value: '17'),
_CalibrationItem(label: '开放式供氧氧气修正点S0:', value: '34'),
_CalibrationItem(label: '开放式供氧氧气修正幅度S1:', value: '17'),
_CalibrationItem(label: '开放式供氧氧气修正步进值S4:', value: '17'),
],
),
];
}
......@@ -143,7 +143,10 @@ class _FactoryActionColumn extends StatelessWidget {
if (_showFactoryCalibrationEntry && _showFactoryInitEntry)
SizedBox(height: 28.h),
if (_showFactoryInitEntry)
const _WideFactoryButton(text: '初始化设置'),
_WideFactoryButton(
text: '初始化设置',
onPressed: () => cubit.resetToDefault(),
),
],
),
),
......
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