Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
L
laki_icu_app
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
张宏
laki_icu_app
Commits
d2db426e
Commit
d2db426e
authored
Jun 24, 2026
by
张宏
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
11111
parent
74d50b40
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
1207 additions
and
60 deletions
+1207
-60
dev.json
env/dev.json
+1
-1
bluetooth_read_bloc.dart
lib/blocs/bluetooth_read/bluetooth_read_bloc.dart
+28
-3
cabin_detail_response.dart
lib/models/bo/cabin_detail_response.dart
+529
-0
cabin_repository.dart
lib/repositories/cabin_repository.dart
+38
-0
cabin_mock_data.dart
lib/repositories/mock/cabin_mock_data.dart
+84
-0
cabin_service.dart
lib/services/cabin_service.dart
+20
-0
monitoring_index_cubit.dart
lib/views/monitoring/index/cubit/monitoring_index_cubit.dart
+136
-6
monitoring_index_state.dart
lib/views/monitoring/index/cubit/monitoring_index_state.dart
+21
-0
monitoring_index_view.dart
lib/views/monitoring/index/monitoring_index_view.dart
+31
-11
monitoring_alert_card.dart
...views/monitoring/index/widgets/monitoring_alert_card.dart
+45
-39
monitoring_alert_detail_dialog.dart
...itoring/index/widgets/monitoring_alert_detail_dialog.dart
+274
-0
No files found.
env/dev.json
View file @
d2db426e
{
"APP_ENV"
:
"dev"
,
"SERVICE_URL"
:
"http://1
21.41.173.189/laki-merchant-service
"
"SERVICE_URL"
:
"http://1
92.168.112.129:8089
"
}
lib/blocs/bluetooth_read/bluetooth_read_bloc.dart
View file @
d2db426e
...
...
@@ -38,9 +38,34 @@ class BluetoothReadBloc extends Bloc<BluetoothReadEvent, BluetoothReadState> {
)
async
{
emit
(
state
.
copyWith
(
isLoading:
true
,
clearError:
true
));
try
{
final
info
=
await
_storageService
.
getBluetoothReadInfo
();
Log
.
warn
(
'**************sn************:
${info.sn}
'
);
emit
(
state
.
copyWith
(
info:
info
,
isLoading:
false
,
clearError:
true
));
BluetoothReadModel
?
info
;
const
maxRetries
=
5
;
const
retryInterval
=
Duration
(
seconds:
10
);
for
(
int
i
=
0
;
i
<
maxRetries
;
i
++)
{
try
{
info
=
await
_storageService
.
getBluetoothReadInfo
();
Log
.
warn
(
'**************sn 第
${i + 1}
次获取************:
${info.sn}
'
);
if
(
info
.
hasSn
)
{
Log
.
warn
(
'**************sn 获取成功,停止重试************'
);
break
;
}
}
catch
(
e
)
{
Log
.
warn
(
'**************sn 第
${i + 1}
次获取失败:
$e
************'
);
}
// 如果还没获取到sn且不是最后一次,则等待后重试
if
(
i
<
maxRetries
-
1
&&
(
info
==
null
||
!
info
.
hasSn
))
{
await
Future
.
delayed
(
retryInterval
);
}
}
emit
(
state
.
copyWith
(
info:
info
??
BluetoothReadModel
.
empty
,
isLoading:
false
,
clearError:
true
,
));
}
catch
(
e
)
{
emit
(
state
.
copyWith
(
isLoading:
false
,
error:
e
.
toString
()));
}
...
...
lib/models/bo/cabin_detail_response.dart
0 → 100644
View file @
d2db426e
/// 舱详情接口响应模型
///
/// 接口:GET /cabin/detail?carbinSn=xxx
/// 来源:从 laki_biz_app 拷贝并适配为本项目手写 fromJson 风格
class
CabinDetailResponse
{
final
String
?
pageTitle
;
final
int
?
deviceId
;
final
String
?
deviceName
;
final
int
?
cabinId
;
final
String
?
cabinName
;
final
String
?
cabinType
;
final
String
?
carbinSn
;
final
String
?
cameraSn
;
final
String
?
wifiPwd
;
final
String
?
wifiName
;
final
PetInfoResponse
?
petInfo
;
final
CameraInfoResponse
?
cameraInfo
;
final
MonitoringDataResponse
?
monitoringData
;
final
CabinControlsResponse
?
cabinControls
;
final
int
?
currentPlanId
;
final
int
?
currentPetId
;
final
String
?
status
;
// 舱状态
final
String
?
state
;
// 舱位物理状态
CabinDetailResponse
({
this
.
pageTitle
,
this
.
deviceId
,
this
.
deviceName
,
this
.
cabinId
,
this
.
cabinName
,
this
.
cabinType
,
this
.
carbinSn
,
this
.
cameraSn
,
this
.
wifiPwd
,
this
.
wifiName
,
this
.
petInfo
,
this
.
cameraInfo
,
this
.
monitoringData
,
this
.
cabinControls
,
this
.
currentPlanId
,
this
.
currentPetId
,
this
.
status
,
this
.
state
,
});
factory
CabinDetailResponse
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
return
CabinDetailResponse
(
pageTitle:
json
[
'pageTitle'
]
as
String
?,
deviceId:
json
[
'deviceId'
]
as
int
?,
deviceName:
json
[
'deviceName'
]
as
String
?,
cabinId:
json
[
'cabinId'
]
as
int
?,
cabinName:
json
[
'cabinName'
]
as
String
?,
cabinType:
json
[
'cabinType'
]
as
String
?,
carbinSn:
json
[
'carbinSn'
]
as
String
?,
cameraSn:
json
[
'cameraSn'
]
as
String
?,
wifiPwd:
json
[
'wifiPwd'
]
as
String
?,
wifiName:
json
[
'wifiName'
]
as
String
?,
petInfo:
json
[
'petInfo'
]
!=
null
?
PetInfoResponse
.
fromJson
(
json
[
'petInfo'
]
as
Map
<
String
,
dynamic
>)
:
null
,
cameraInfo:
json
[
'cameraInfo'
]
!=
null
?
CameraInfoResponse
.
fromJson
(
json
[
'cameraInfo'
]
as
Map
<
String
,
dynamic
>)
:
null
,
monitoringData:
json
[
'monitoringData'
]
!=
null
?
MonitoringDataResponse
.
fromJson
(
json
[
'monitoringData'
]
as
Map
<
String
,
dynamic
>)
:
null
,
cabinControls:
json
[
'cabinControls'
]
!=
null
?
CabinControlsResponse
.
fromJson
(
json
[
'cabinControls'
]
as
Map
<
String
,
dynamic
>)
:
null
,
currentPlanId:
json
[
'currentPlanId'
]
as
int
?,
currentPetId:
json
[
'currentPetId'
]
as
int
?,
status:
json
[
'status'
]
as
String
?,
state:
json
[
'state'
]
as
String
?,
);
}
CabinDetailResponse
copyWith
({
String
?
pageTitle
,
int
?
deviceId
,
String
?
deviceName
,
int
?
cabinId
,
String
?
cabinName
,
String
?
cabinType
,
String
?
carbinSn
,
String
?
cameraSn
,
String
?
wifiPwd
,
String
?
wifiName
,
PetInfoResponse
?
petInfo
,
CameraInfoResponse
?
cameraInfo
,
MonitoringDataResponse
?
monitoringData
,
CabinControlsResponse
?
cabinControls
,
int
?
currentPlanId
,
int
?
currentPetId
,
String
?
status
,
String
?
state
,
})
{
return
CabinDetailResponse
(
pageTitle:
pageTitle
??
this
.
pageTitle
,
deviceId:
deviceId
??
this
.
deviceId
,
deviceName:
deviceName
??
this
.
deviceName
,
cabinId:
cabinId
??
this
.
cabinId
,
cabinName:
cabinName
??
this
.
cabinName
,
cabinType:
cabinType
??
this
.
cabinType
,
carbinSn:
carbinSn
??
this
.
carbinSn
,
cameraSn:
cameraSn
??
this
.
cameraSn
,
wifiPwd:
wifiPwd
??
this
.
wifiPwd
,
wifiName:
wifiName
??
this
.
wifiName
,
petInfo:
petInfo
??
this
.
petInfo
,
cameraInfo:
cameraInfo
??
this
.
cameraInfo
,
monitoringData:
monitoringData
??
this
.
monitoringData
,
cabinControls:
cabinControls
??
this
.
cabinControls
,
currentPlanId:
currentPlanId
??
this
.
currentPlanId
,
currentPetId:
currentPetId
??
this
.
currentPetId
,
status:
status
??
this
.
status
,
state:
state
??
this
.
state
,
);
}
}
/// 宠物信息(舱详情接口子模型)
class
PetInfoResponse
{
final
int
?
petId
;
final
String
?
name
;
final
String
?
avatar
;
final
String
?
type
;
final
String
?
breedTag
;
final
String
?
disease
;
final
String
?
ownerName
;
final
String
?
ownerPhone
;
final
String
?
basicInfo
;
final
String
?
petName
;
final
String
?
petType
;
final
String
?
monitoringStatus
;
PetInfoResponse
({
this
.
petId
,
this
.
name
,
this
.
avatar
,
this
.
type
,
this
.
breedTag
,
this
.
disease
,
this
.
ownerName
,
this
.
ownerPhone
,
this
.
basicInfo
,
this
.
petName
,
this
.
petType
,
this
.
monitoringStatus
,
});
factory
PetInfoResponse
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
return
PetInfoResponse
(
petId:
json
[
'petId'
]
as
int
?,
name:
json
[
'name'
]
as
String
?,
avatar:
json
[
'avatar'
]
as
String
?,
type:
json
[
'type'
]
as
String
?,
breedTag:
json
[
'breedTag'
]
as
String
?,
disease:
json
[
'disease'
]
as
String
?,
ownerName:
json
[
'ownerName'
]
as
String
?,
ownerPhone:
json
[
'ownerPhone'
]
as
String
?,
basicInfo:
json
[
'basicInfo'
]
as
String
?,
petName:
json
[
'petName'
]
as
String
?,
petType:
json
[
'petType'
]
as
String
?,
monitoringStatus:
json
[
'monitoringStatus'
]
as
String
?,
);
}
PetInfoResponse
copyWith
({
int
?
petId
,
String
?
name
,
String
?
avatar
,
String
?
type
,
String
?
breedTag
,
String
?
disease
,
String
?
ownerName
,
String
?
ownerPhone
,
String
?
basicInfo
,
String
?
petName
,
String
?
petType
,
String
?
monitoringStatus
,
})
{
return
PetInfoResponse
(
petId:
petId
??
this
.
petId
,
name:
name
??
this
.
name
,
avatar:
avatar
??
this
.
avatar
,
type:
type
??
this
.
type
,
breedTag:
breedTag
??
this
.
breedTag
,
disease:
disease
??
this
.
disease
,
ownerName:
ownerName
??
this
.
ownerName
,
ownerPhone:
ownerPhone
??
this
.
ownerPhone
,
basicInfo:
basicInfo
??
this
.
basicInfo
,
petName:
petName
??
this
.
petName
,
petType:
petType
??
this
.
petType
,
monitoringStatus:
monitoringStatus
??
this
.
monitoringStatus
,
);
}
}
/// 摄像头信息(舱详情接口子模型)
class
CameraInfoResponse
{
final
int
?
cameraId
;
final
String
?
cameraName
;
final
String
?
sn
;
final
String
?
cameraPwd
;
final
String
?
status
;
final
String
?
streamUrl
;
CameraInfoResponse
({
this
.
cameraId
,
this
.
cameraName
,
this
.
sn
,
this
.
cameraPwd
,
this
.
status
,
this
.
streamUrl
,
});
factory
CameraInfoResponse
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
return
CameraInfoResponse
(
cameraId:
json
[
'cameraId'
]
as
int
?,
cameraName:
json
[
'cameraName'
]
as
String
?,
sn:
json
[
'sn'
]
as
String
?,
cameraPwd:
json
[
'cameraPwd'
]
as
String
?,
status:
json
[
'status'
]
as
String
?,
streamUrl:
json
[
'streamUrl'
]
as
String
?,
);
}
CameraInfoResponse
copyWith
({
int
?
cameraId
,
String
?
cameraName
,
String
?
sn
,
String
?
cameraPwd
,
String
?
status
,
String
?
streamUrl
,
})
{
return
CameraInfoResponse
(
cameraId:
cameraId
??
this
.
cameraId
,
cameraName:
cameraName
??
this
.
cameraName
,
sn:
sn
??
this
.
sn
,
cameraPwd:
cameraPwd
??
this
.
cameraPwd
,
status:
status
??
this
.
status
,
streamUrl:
streamUrl
??
this
.
streamUrl
,
);
}
}
/// 监控数据(舱详情接口子模型)
class
MonitoringDataResponse
{
final
int
?
totalDuration
;
final
int
?
elapsedDuration
;
final
String
?
status
;
final
String
?
careLevel
;
final
int
?
planId
;
final
String
?
planName
;
final
String
?
planType
;
final
int
?
temperatureSetting
;
final
int
?
mainTemperature
;
final
int
?
humidity
;
final
int
?
humiditySetting
;
final
int
?
oxygenConcentration
;
final
int
?
oxygenSetting
;
final
int
?
openO2
;
final
int
?
noOpenO2SettingMax
;
final
int
?
openO2SettingMax
;
final
int
?
co2Measurement
;
final
int
?
co2Setting
;
final
int
?
co2FastClearSwitch
;
final
int
?
internalExternalCycleState
;
final
int
?
floodLight
;
final
int
?
inspectionLamp
;
final
int
?
blueLight
;
final
int
?
redLight
;
final
int
?
airConditioner
;
final
int
?
levelLight
;
final
int
?
windSet
;
final
int
?
rightAtomizerTreatmentTime
;
final
int
?
rightInfraredPhysiotherapyTime
;
final
int
?
wifible
;
final
int
?
totalTime
;
final
int
?
temperatureAlarm
;
final
int
?
humidityAlarm
;
final
int
?
oxygenAlarm
;
final
int
?
co2Alarm
;
MonitoringDataResponse
({
this
.
totalDuration
,
this
.
elapsedDuration
,
this
.
status
,
this
.
careLevel
,
this
.
planId
,
this
.
planName
,
this
.
planType
,
this
.
temperatureSetting
,
this
.
mainTemperature
,
this
.
humidity
,
this
.
humiditySetting
,
this
.
oxygenConcentration
,
this
.
oxygenSetting
,
this
.
openO2
,
this
.
noOpenO2SettingMax
,
this
.
openO2SettingMax
,
this
.
co2Measurement
,
this
.
co2Setting
,
this
.
co2FastClearSwitch
,
this
.
internalExternalCycleState
,
this
.
floodLight
,
this
.
inspectionLamp
,
this
.
blueLight
,
this
.
redLight
,
this
.
airConditioner
,
this
.
levelLight
,
this
.
windSet
,
this
.
rightAtomizerTreatmentTime
,
this
.
rightInfraredPhysiotherapyTime
,
this
.
wifible
,
this
.
totalTime
,
this
.
temperatureAlarm
,
this
.
humidityAlarm
,
this
.
oxygenAlarm
,
this
.
co2Alarm
,
});
factory
MonitoringDataResponse
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
return
MonitoringDataResponse
(
totalDuration:
json
[
'totalDuration'
]
as
int
?,
elapsedDuration:
json
[
'elapsedDuration'
]
as
int
?,
status:
json
[
'status'
]
as
String
?,
careLevel:
json
[
'careLevel'
]
as
String
?,
planId:
json
[
'planId'
]
as
int
?,
planName:
json
[
'planName'
]
as
String
?,
planType:
json
[
'planType'
]
as
String
?,
temperatureSetting:
json
[
'temperatureSetting'
]
as
int
?,
mainTemperature:
json
[
'mainTemperature'
]
as
int
?,
humidity:
json
[
'humidity'
]
as
int
?,
humiditySetting:
json
[
'humiditySetting'
]
as
int
?,
oxygenConcentration:
json
[
'oxygenConcentration'
]
as
int
?,
oxygenSetting:
json
[
'oxygenSetting'
]
as
int
?,
openO2:
json
[
'openO2'
]
as
int
?,
noOpenO2SettingMax:
json
[
'noOpenO2SettingMax'
]
as
int
?,
openO2SettingMax:
json
[
'openO2SettingMax'
]
as
int
?,
co2Measurement:
json
[
'co2Measurement'
]
as
int
?,
co2Setting:
json
[
'co2Setting'
]
as
int
?,
co2FastClearSwitch:
json
[
'co2FastClearSwitch'
]
as
int
?,
internalExternalCycleState:
json
[
'internalExternalCycleState'
]
as
int
?,
floodLight:
json
[
'floodLight'
]
as
int
?,
inspectionLamp:
json
[
'inspectionLamp'
]
as
int
?,
blueLight:
json
[
'blueLight'
]
as
int
?,
redLight:
json
[
'redLight'
]
as
int
?,
airConditioner:
json
[
'airConditioner'
]
as
int
?,
levelLight:
json
[
'levelLight'
]
as
int
?,
windSet:
json
[
'windSet'
]
as
int
?,
rightAtomizerTreatmentTime:
json
[
'rightAtomizerTreatmentTime'
]
as
int
?,
rightInfraredPhysiotherapyTime:
json
[
'rightInfraredPhysiotherapyTime'
]
as
int
?,
wifible:
json
[
'wifible'
]
as
int
?,
totalTime:
json
[
'totalTime'
]
as
int
?,
temperatureAlarm:
json
[
'temperatureAlarm'
]
as
int
?,
humidityAlarm:
json
[
'humidityAlarm'
]
as
int
?,
oxygenAlarm:
json
[
'oxygenAlarm'
]
as
int
?,
co2Alarm:
json
[
'co2Alarm'
]
as
int
?,
);
}
MonitoringDataResponse
copyWith
({
int
?
totalDuration
,
int
?
elapsedDuration
,
String
?
status
,
String
?
careLevel
,
int
?
planId
,
String
?
planName
,
String
?
planType
,
int
?
temperatureSetting
,
int
?
mainTemperature
,
int
?
humidity
,
int
?
humiditySetting
,
int
?
oxygenConcentration
,
int
?
oxygenSetting
,
int
?
openO2
,
int
?
noOpenO2SettingMax
,
int
?
openO2SettingMax
,
int
?
co2Measurement
,
int
?
co2Setting
,
int
?
co2FastClearSwitch
,
int
?
internalExternalCycleState
,
int
?
floodLight
,
int
?
inspectionLamp
,
int
?
blueLight
,
int
?
redLight
,
int
?
airConditioner
,
int
?
levelLight
,
int
?
windSet
,
int
?
rightAtomizerTreatmentTime
,
int
?
rightInfraredPhysiotherapyTime
,
int
?
wifible
,
int
?
totalTime
,
int
?
temperatureAlarm
,
int
?
humidityAlarm
,
int
?
oxygenAlarm
,
int
?
co2Alarm
,
})
{
return
MonitoringDataResponse
(
totalDuration:
totalDuration
??
this
.
totalDuration
,
elapsedDuration:
elapsedDuration
??
this
.
elapsedDuration
,
status:
status
??
this
.
status
,
careLevel:
careLevel
??
this
.
careLevel
,
planId:
planId
??
this
.
planId
,
planName:
planName
??
this
.
planName
,
planType:
planType
??
this
.
planType
,
temperatureSetting:
temperatureSetting
??
this
.
temperatureSetting
,
mainTemperature:
mainTemperature
??
this
.
mainTemperature
,
humidity:
humidity
??
this
.
humidity
,
humiditySetting:
humiditySetting
??
this
.
humiditySetting
,
oxygenConcentration:
oxygenConcentration
??
this
.
oxygenConcentration
,
oxygenSetting:
oxygenSetting
??
this
.
oxygenSetting
,
openO2:
openO2
??
this
.
openO2
,
noOpenO2SettingMax:
noOpenO2SettingMax
??
this
.
noOpenO2SettingMax
,
openO2SettingMax:
openO2SettingMax
??
this
.
openO2SettingMax
,
co2Measurement:
co2Measurement
??
this
.
co2Measurement
,
co2Setting:
co2Setting
??
this
.
co2Setting
,
co2FastClearSwitch:
co2FastClearSwitch
??
this
.
co2FastClearSwitch
,
internalExternalCycleState:
internalExternalCycleState
??
this
.
internalExternalCycleState
,
floodLight:
floodLight
??
this
.
floodLight
,
inspectionLamp:
inspectionLamp
??
this
.
inspectionLamp
,
blueLight:
blueLight
??
this
.
blueLight
,
redLight:
redLight
??
this
.
redLight
,
airConditioner:
airConditioner
??
this
.
airConditioner
,
levelLight:
levelLight
??
this
.
levelLight
,
windSet:
windSet
??
this
.
windSet
,
rightAtomizerTreatmentTime:
rightAtomizerTreatmentTime
??
this
.
rightAtomizerTreatmentTime
,
rightInfraredPhysiotherapyTime:
rightInfraredPhysiotherapyTime
??
this
.
rightInfraredPhysiotherapyTime
,
wifible:
wifible
??
this
.
wifible
,
totalTime:
totalTime
??
this
.
totalTime
,
temperatureAlarm:
temperatureAlarm
??
this
.
temperatureAlarm
,
humidityAlarm:
humidityAlarm
??
this
.
humidityAlarm
,
oxygenAlarm:
oxygenAlarm
??
this
.
oxygenAlarm
,
co2Alarm:
co2Alarm
??
this
.
co2Alarm
,
);
}
}
/// 舱控制参数(舱详情接口子模型)
class
CabinControlsResponse
{
final
double
?
temperature
;
final
double
?
oxygen
;
final
double
?
humidity
;
final
double
?
co2
;
final
int
?
nebulizer
;
final
int
?
sterilize
;
final
String
?
fanMode
;
final
String
?
airMode
;
final
bool
?
newFan
;
final
bool
?
openOxygen
;
final
bool
?
checkLight
;
final
bool
?
light
;
final
bool
?
blueLight
;
final
bool
?
redLight
;
CabinControlsResponse
({
this
.
temperature
,
this
.
oxygen
,
this
.
humidity
,
this
.
co2
,
this
.
nebulizer
,
this
.
sterilize
,
this
.
fanMode
,
this
.
airMode
,
this
.
newFan
,
this
.
openOxygen
,
this
.
checkLight
,
this
.
light
,
this
.
blueLight
,
this
.
redLight
,
});
factory
CabinControlsResponse
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
return
CabinControlsResponse
(
temperature:
(
json
[
'temperature'
]
as
num
?)?.
toDouble
(),
oxygen:
(
json
[
'oxygen'
]
as
num
?)?.
toDouble
(),
humidity:
(
json
[
'humidity'
]
as
num
?)?.
toDouble
(),
co2:
(
json
[
'co2'
]
as
num
?)?.
toDouble
(),
nebulizer:
json
[
'nebulizer'
]
as
int
?,
sterilize:
json
[
'sterilize'
]
as
int
?,
fanMode:
json
[
'fanMode'
]
as
String
?,
airMode:
json
[
'airMode'
]
as
String
?,
newFan:
json
[
'newFan'
]
as
bool
?,
openOxygen:
json
[
'openOxygen'
]
as
bool
?,
checkLight:
json
[
'checkLight'
]
as
bool
?,
light:
json
[
'light'
]
as
bool
?,
blueLight:
json
[
'blueLight'
]
as
bool
?,
redLight:
json
[
'redLight'
]
as
bool
?,
);
}
CabinControlsResponse
copyWith
({
double
?
temperature
,
double
?
oxygen
,
double
?
humidity
,
double
?
co2
,
int
?
nebulizer
,
int
?
sterilize
,
String
?
fanMode
,
String
?
airMode
,
bool
?
newFan
,
bool
?
openOxygen
,
bool
?
checkLight
,
bool
?
light
,
bool
?
blueLight
,
bool
?
redLight
,
})
{
return
CabinControlsResponse
(
temperature:
temperature
??
this
.
temperature
,
oxygen:
oxygen
??
this
.
oxygen
,
humidity:
humidity
??
this
.
humidity
,
co2:
co2
??
this
.
co2
,
nebulizer:
nebulizer
??
this
.
nebulizer
,
sterilize:
sterilize
??
this
.
sterilize
,
fanMode:
fanMode
??
this
.
fanMode
,
airMode:
airMode
??
this
.
airMode
,
newFan:
newFan
??
this
.
newFan
,
openOxygen:
openOxygen
??
this
.
openOxygen
,
checkLight:
checkLight
??
this
.
checkLight
,
light:
light
??
this
.
light
,
blueLight:
blueLight
??
this
.
blueLight
,
redLight:
redLight
??
this
.
redLight
,
);
}
}
lib/repositories/cabin_repository.dart
0 → 100644
View file @
d2db426e
import
'mock/cabin_mock_data.dart'
;
import
'package:laki_icu_app/models/bo/cabin_detail_response.dart'
;
import
'package:laki_icu_app/utils/constants.dart'
;
import
'package:laki_icu_app/utils/http/dio_request.dart'
;
import
'package:laki_icu_app/utils/http/response_model.dart'
;
class
CabinRepository
{
CabinRepository
.
_
();
static
final
CabinRepository
instance
=
CabinRepository
.
_
();
/// 获取舱详情
///
/// [carbinSn] 舱序列号(设备 SN)
Future
<
ResponseModel
<
CabinDetailResponse
>>
getCabinDetail
(
String
carbinSn
,
)
{
return
DioRequest
.
instance
.
get
<
CabinDetailResponse
>(
'/icu/detail'
,
queryParameters:
{
'carbinSn'
:
carbinSn
},
fromJsonT:
(
data
)
=>
CabinDetailResponse
.
fromJson
(
data
as
Map
<
String
,
dynamic
>),
);
}
Future
<
ResponseModel
<
CabinDetailResponse
>>
_mockCabinDetail
()
async
{
await
Future
.
delayed
(
const
Duration
(
milliseconds:
300
));
return
ResponseModel
<
CabinDetailResponse
>(
code:
200
,
msg:
'获取舱详情成功(mock)'
,
data:
CabinMockData
.
getMockCabinDetail
(),
uuid:
'mock_uuid_
${DateTime.now().millisecondsSinceEpoch}
'
,
success:
true
,
timestamp:
DateTime
.
now
().
millisecondsSinceEpoch
,
);
}
}
lib/repositories/mock/cabin_mock_data.dart
0 → 100644
View file @
d2db426e
import
'../../models/bo/cabin_detail_response.dart'
;
/// 舱详情模块本地 mock 数据。
///
/// 所有 mock 数据直接使用 models 层构造函数创建,
/// 确保数据结构与 models 定义完全一致。
/// 后续接入真实接口时,只需修改 Repository 层开关即可。
class
CabinMockData
{
CabinMockData
.
_
();
static
CabinDetailResponse
getMockCabinDetail
()
{
return
CabinDetailResponse
(
pageTitle:
'ICU 监护舱'
,
deviceId:
1001
,
deviceName:
'TSAAS ICU 设备'
,
cabinId:
1
,
cabinName:
'1号舱'
,
cabinType:
'ICU'
,
carbinSn:
'VE10085871QOXG'
,
cameraSn:
'VE10085871QOXG'
,
wifiPwd:
'143548'
,
wifiName:
'TSAAS-ICU-WiFi'
,
petInfo:
PetInfoResponse
(
petId:
1
,
name:
'妮蔻'
,
avatar:
''
,
type:
'猫'
,
breedTag:
'长毛三花'
,
disease:
'胃炎'
,
ownerName:
'张先生'
,
ownerPhone:
'130-1111-3333'
,
basicInfo:
'3岁 雌性 4.5kg'
,
petName:
'妮蔻'
,
petType:
'猫'
,
monitoringStatus:
'monitoring'
,
),
cameraInfo:
CameraInfoResponse
(
cameraId:
1
,
cameraName:
'主摄像头'
,
sn:
'VE10085871QOXG'
,
cameraPwd:
'143548'
,
status:
'online'
,
streamUrl:
''
,
),
monitoringData:
MonitoringDataResponse
(
totalDuration:
7200
,
elapsedDuration:
3600
,
status:
'monitoring'
,
careLevel:
'特级'
,
planId:
1
,
planName:
'标准监护方案'
,
planType:
'standard'
,
temperatureSetting:
25
,
mainTemperature:
24
,
humidity:
55
,
humiditySetting:
50
,
oxygenConcentration:
21
,
oxygenSetting:
20
,
co2Measurement:
400
,
co2Setting:
500
,
),
cabinControls:
CabinControlsResponse
(
temperature:
25.0
,
oxygen:
21.0
,
humidity:
55.0
,
co2:
400.0
,
nebulizer:
0
,
sterilize:
0
,
fanMode:
'auto'
,
airMode:
'cycle'
,
newFan:
true
,
openOxygen:
false
,
checkLight:
false
,
light:
true
,
blueLight:
false
,
redLight:
false
,
),
currentPlanId:
1
,
currentPetId:
1
,
status:
'occupied'
,
state:
'normal'
,
);
}
}
lib/services/cabin_service.dart
0 → 100644
View file @
d2db426e
import
'package:laki_icu_app/models/bo/cabin_detail_response.dart'
;
import
'package:laki_icu_app/repositories/cabin_repository.dart'
;
class
CabinService
{
final
CabinRepository
_repository
;
CabinService
()
:
_repository
=
CabinRepository
.
instance
;
/// 获取舱详情
///
/// [carbinSn] 舱序列号(设备 SN)
/// 返回 [CabinDetailResponse],包含 cameraSn、wifiPwd、petInfo 等
Future
<
CabinDetailResponse
>
getCabinDetail
(
String
carbinSn
)
async
{
final
result
=
await
_repository
.
getCabinDetail
(
carbinSn
);
if
(
result
.
success
&&
result
.
data
!=
null
)
{
return
result
.
data
!;
}
throw
Exception
(
result
.
msg
);
}
}
lib/views/monitoring/index/cubit/monitoring_index_cubit.dart
View file @
d2db426e
...
...
@@ -3,7 +3,9 @@ import 'dart:async';
import
'package:flutter/foundation.dart'
;
import
'package:flutter_bloc/flutter_bloc.dart'
;
import
'package:laki_icu_app/enums/video_stream_mode_enum.dart'
;
import
'package:laki_icu_app/models/bo/cabin_detail_response.dart'
;
import
'package:laki_icu_app/models/bo/monitoring_bo.dart'
;
import
'package:laki_icu_app/services/cabin_service.dart'
;
import
'package:laki_icu_app/services/monitoring_service.dart'
;
import
'package:laki_icu_app/services/p2p_video_service.dart'
;
import
'package:laki_icu_app/services/webrtc_service.dart'
;
...
...
@@ -16,11 +18,11 @@ import 'package:laki_icu_app/utils/storage/storage_service.dart';
import
'monitoring_index_state.dart'
;
/// 设备凭据 —— Mock 阶段使用占位值,后续接口接入后替换
/// TODO: 从舱详情接口获取真实的 cameraSn 和 wifiPwd
/// 设备凭据 —— 从舱详情接口获取 cameraSn 和 wifiPwd
class
MonitoringIndexCubit
extends
Cubit
<
MonitoringIndexState
>
{
final
MonitoringService
_monitoringService
;
final
CabinService
_cabinService
;
final
WebrtcService
_webrtcService
;
final
P2pVideoService
_p2pVideoService
;
final
StorageService
_storageService
;
...
...
@@ -47,12 +49,17 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
bool
_isMqttConnecting
=
false
;
String
?
_mqttDeviceSn
;
/// 记录已请求过舱详情的 SN,避免每个 BLE 数据帧都重复请求接口
String
?
_lastFetchedCabinSn
;
bool
_isFetchingCabinDetail
=
false
;
/// 连接代际计数器 —— 每次切换模式时 +1,
/// 防止旧连接的异步结果污染当前模式的状态。
int
_switchGen
=
0
;
MonitoringIndexCubit
()
:
_monitoringService
=
MonitoringService
(),
_cabinService
=
CabinService
(),
_webrtcService
=
WebrtcService
(),
_p2pVideoService
=
P2pVideoService
(),
_storageService
=
StorageService
(),
...
...
@@ -141,6 +148,7 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
if
(
bluetoothReadInfo
.
hasSn
)
{
await
_storageService
.
saveBluetoothReadSn
(
bluetoothReadInfo
.
sn
!);
await
_connectMqttForDeviceSn
(
bluetoothReadInfo
.
sn
!);
await
_fetchCabinDetailAndConnectVideo
(
bluetoothReadInfo
.
sn
!);
}
eventBus
.
emit
(
BluetoothReadInfoChangedEvent
(
bluetoothReadInfo
),
...
...
@@ -216,6 +224,7 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
);
}
_connectMqttForDeviceSn
(
sn
);
_fetchCabinDetailAndConnectVideo
(
sn
);
}
}
if
(
deviceId
!=
null
&&
deviceId
.
isNotEmpty
)
{
...
...
@@ -270,10 +279,22 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
}
/// 初始化 WebRTC 视频连接
///
/// 使用从舱详情接口获取的 [cabinCameraSn] 和 [cabinWifiPwd],
/// 凭证未就绪时跳过连接。
Future
<
void
>
_initWebrtc
()
async
{
final
cameraSn
=
state
.
cabinCameraSn
;
final
wifiPwd
=
state
.
cabinWifiPwd
;
if
(
cameraSn
==
null
||
cameraSn
.
isEmpty
||
wifiPwd
==
null
||
wifiPwd
.
isEmpty
)
{
debugPrint
(
'[MonitoringIndexCubit] WebRTC 凭证未就绪,跳过连接'
);
return
;
}
await
_webrtcService
.
connect
(
deviceNo:
'VE10085871QOXG'
,
password:
'143548'
,
deviceNo:
cameraSn
,
password:
wifiPwd
,
);
}
...
...
@@ -283,11 +304,73 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
}
/// 初始化 P2P 视频连接
///
/// 使用从舱详情接口获取的 [cabinCameraSn] 和 [cabinWifiPwd],
/// 凭证未就绪时跳过连接。
Future
<
void
>
_initP2p
()
async
{
final
cameraSn
=
state
.
cabinCameraSn
;
final
wifiPwd
=
state
.
cabinWifiPwd
;
if
(
cameraSn
==
null
||
cameraSn
.
isEmpty
||
wifiPwd
==
null
||
wifiPwd
.
isEmpty
)
{
debugPrint
(
'[MonitoringIndexCubit] P2P 凭证未就绪,跳过连接'
);
return
;
}
await
_p2pVideoService
.
connect
(
deviceId:
'VE10085871QOXG'
,
deviceId:
cameraSn
,
username:
'admin'
,
password:
'143548'
,
password:
wifiPwd
,
);
}
/// 根据舱 SN 获取舱详情,并将摄像头凭证和宠物信息同步到 State
///
/// 成功后自动触发视频连接(按当前 [VideoStreamMode] 方式)。
/// 同一 SN 只会请求一次,避免 BLE 数据帧高频回调导致重复调用接口。
Future
<
void
>
_fetchCabinDetailAndConnectVideo
(
String
carbinSn
)
async
{
// 相同 SN 已请求过则跳过,避免每个 BLE 数据帧都触发一次接口调用
if
(
_lastFetchedCabinSn
==
carbinSn
)
return
;
// 上一次请求尚未完成则跳过
if
(
_isFetchingCabinDetail
)
return
;
_isFetchingCabinDetail
=
true
;
_lastFetchedCabinSn
=
carbinSn
;
try
{
final
detail
=
await
_cabinService
.
getCabinDetail
(
carbinSn
);
if
(
isClosed
)
return
;
emit
(
state
.
copyWith
(
cabinCameraSn:
detail
.
cameraSn
,
cabinWifiPwd:
detail
.
wifiPwd
,
patientInfo:
detail
.
petInfo
!=
null
?
_mapPetInfoToPatientInfo
(
detail
.
petInfo
!)
:
state
.
patientInfo
,
));
// 凭证就绪后发起视频连接
_initVideoByMode
();
}
catch
(
e
)
{
if
(
isClosed
)
return
;
debugPrint
(
'[MonitoringIndexCubit] 获取舱详情失败:
$e
'
);
}
finally
{
_isFetchingCabinDetail
=
false
;
}
}
/// 将 [PetInfoResponse] 映射为 [PatientInfoBO]
PatientInfoBO
_mapPetInfoToPatientInfo
(
PetInfoResponse
pet
)
{
return
PatientInfoBO
(
name:
(
pet
.
petName
??
pet
.
name
)
??
''
,
type:
(
pet
.
petType
??
pet
.
type
)
??
''
,
phone:
pet
.
ownerPhone
??
''
,
breed:
pet
.
breedTag
??
''
,
doctor:
''
,
avatarUrl:
pet
.
avatar
??
''
,
disease:
pet
.
disease
??
''
,
checkInDate:
''
,
careLevel:
''
,
);
}
...
...
@@ -440,6 +523,7 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
await
_storageService
.
deleteBluetoothReadInfo
();
_isMqttReconnectEnabled
=
false
;
_mqttDeviceSn
=
null
;
_lastFetchedCabinSn
=
null
;
await
_disposeMqttClient
();
eventBus
.
emit
(
const
BluetoothReadInfoClearedEvent
());
_mcuReportDecoder
.
clear
();
...
...
@@ -766,6 +850,52 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
/// 获取 P2P 视频播放控制器供 UI 层使用
P2pVideoService
get
p2pVideoService
=>
_p2pVideoService
;
/// 选中告警(用于打开详情弹窗)
void
selectAlert
(
AlertInfoBO
alert
)
{
if
(
isClosed
)
return
;
emit
(
state
.
copyWith
(
selectedAlert:
alert
));
}
/// 清除选中的告警(用于关闭弹窗)
void
clearSelectedAlert
()
{
if
(
isClosed
)
return
;
emit
(
state
.
copyWith
(
selectedAlert:
null
));
}
/// 标记告警为已处理
void
markAlertAsProcessed
(
AlertInfoBO
alert
)
{
if
(
isClosed
)
return
;
final
updatedAlerts
=
state
.
alerts
.
map
((
item
)
{
if
(
item
.
title
==
alert
.
title
&&
item
.
time
==
alert
.
time
)
{
return
AlertInfoBO
(
title:
item
.
title
,
description:
item
.
description
,
time:
item
.
time
,
status:
'已处理'
,
);
}
return
item
;
}).
toList
();
emit
(
state
.
copyWith
(
alerts:
updatedAlerts
,
selectedAlert:
null
));
}
/// 标记告警为误报
void
markAlertAsFalse
(
AlertInfoBO
alert
)
{
if
(
isClosed
)
return
;
final
updatedAlerts
=
state
.
alerts
.
map
((
item
)
{
if
(
item
.
title
==
alert
.
title
&&
item
.
time
==
alert
.
time
)
{
return
AlertInfoBO
(
title:
item
.
title
,
description:
item
.
description
,
time:
item
.
time
,
status:
'误报'
,
);
}
return
item
;
}).
toList
();
emit
(
state
.
copyWith
(
alerts:
updatedAlerts
,
selectedAlert:
null
));
}
@override
Future
<
void
>
close
()
{
_cancelBluetoothReconnect
();
...
...
lib/views/monitoring/index/cubit/monitoring_index_state.dart
View file @
d2db426e
...
...
@@ -71,6 +71,15 @@ class MonitoringIndexState extends Equatable {
/// 当前 MQTT 连接使用的设备 SN
final
String
?
mqttDeviceSn
;
/// 设备摄像头序列号(从舱详情接口 /cabin/detail 获取)
final
String
?
cabinCameraSn
;
/// 设备 WiFi 密码(从舱详情接口 /cabin/detail 获取)
final
String
?
cabinWifiPwd
;
/// 当前选中的告警(用于弹窗显示)
final
AlertInfoBO
?
selectedAlert
;
const
MonitoringIndexState
({
this
.
status
=
MonitoringIndexStatus
.
initial
,
this
.
isLoading
=
false
,
...
...
@@ -95,6 +104,9 @@ class MonitoringIndexState extends Equatable {
this
.
mqttConnectionState
=
LakiMqttConnectionState
.
disconnected
,
this
.
mqttMessage
,
this
.
mqttDeviceSn
,
this
.
cabinCameraSn
,
this
.
cabinWifiPwd
,
this
.
selectedAlert
,
});
MonitoringIndexState
copyWith
({
...
...
@@ -121,6 +133,9 @@ class MonitoringIndexState extends Equatable {
LakiMqttConnectionState
?
mqttConnectionState
,
String
?
mqttMessage
,
String
?
mqttDeviceSn
,
String
?
cabinCameraSn
,
String
?
cabinWifiPwd
,
AlertInfoBO
?
selectedAlert
,
bool
clearBoundBluetoothDevice
=
false
,
bool
clearLatestMcuReport
=
false
,
bool
clearPatientInfo
=
false
,
...
...
@@ -159,6 +174,9 @@ class MonitoringIndexState extends Equatable {
mqttMessage:
mqttMessage
??
this
.
mqttMessage
,
mqttDeviceSn:
clearMqttDeviceSn
?
null
:
mqttDeviceSn
??
this
.
mqttDeviceSn
,
cabinCameraSn:
cabinCameraSn
??
this
.
cabinCameraSn
,
cabinWifiPwd:
cabinWifiPwd
??
this
.
cabinWifiPwd
,
selectedAlert:
selectedAlert
??
this
.
selectedAlert
,
);
}
...
...
@@ -187,5 +205,8 @@ class MonitoringIndexState extends Equatable {
mqttConnectionState
,
mqttMessage
,
mqttDeviceSn
,
cabinCameraSn
,
cabinWifiPwd
,
selectedAlert
,
];
}
lib/views/monitoring/index/monitoring_index_view.dart
View file @
d2db426e
...
...
@@ -13,6 +13,7 @@ import 'cubit/monitoring_index_cubit.dart';
import
'cubit/monitoring_index_state.dart'
;
import
'widgets/bluetooth_bind_dialog.dart'
;
import
'widgets/monitoring_alert_card.dart'
;
import
'widgets/monitoring_alert_detail_dialog.dart'
;
import
'widgets/monitoring_metric_card.dart'
;
import
'widgets/monitoring_pet_info_card.dart'
;
import
'widgets/video_player_widget.dart'
;
...
...
@@ -59,19 +60,26 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> {
@override
Widget
build
(
BuildContext
context
)
{
return
BlocBuilder
<
MonitoringIndexCubit
,
MonitoringIndexState
>(
builder:
(
context
,
state
)
{
if
(
state
.
isLoading
&&
state
.
metrics
.
isEmpty
)
{
return
const
Center
(
child:
CircularProgressIndicator
(
color:
Colors
.
white
),
);
return
BlocListener
<
MonitoringIndexCubit
,
MonitoringIndexState
>(
listener:
(
context
,
state
)
{
if
(
state
.
selectedAlert
!=
null
)
{
_showAlertDetailDialog
(
state
.
selectedAlert
!);
}
if
(
state
.
status
==
MonitoringIndexStatus
.
failure
&&
state
.
metrics
.
isEmpty
)
{
return
_buildError
(
state
.
error
);
}
return
_buildMainArea
(
state
);
},
child:
BlocBuilder
<
MonitoringIndexCubit
,
MonitoringIndexState
>(
builder:
(
context
,
state
)
{
if
(
state
.
isLoading
&&
state
.
metrics
.
isEmpty
)
{
return
const
Center
(
child:
CircularProgressIndicator
(
color:
Colors
.
white
),
);
}
if
(
state
.
status
==
MonitoringIndexStatus
.
failure
&&
state
.
metrics
.
isEmpty
)
{
return
_buildError
(
state
.
error
);
}
return
_buildMainArea
(
state
);
},
),
);
}
...
...
@@ -243,6 +251,18 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> {
);
}
void
_showAlertDetailDialog
(
AlertInfoBO
alert
)
{
showDialog
<
void
>(
context:
context
,
builder:
(
_
)
=>
BlocProvider
.
value
(
value:
_monitoringIndexCubit
,
child:
MonitoringAlertDetailDialog
(
alert:
alert
),
),
).
then
((
_
)
{
_monitoringIndexCubit
.
clearSelectedAlert
();
});
}
List
<
MonitoringMetricBO
>
_environmentMetrics
(
List
<
MonitoringMetricBO
>
metrics
)
{
final
matched
=
metrics
...
...
lib/views/monitoring/index/widgets/monitoring_alert_card.dart
View file @
d2db426e
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
'package:laki_icu_app/views/monitoring/index/cubit/monitoring_index_cubit.dart'
;
class
MonitoringAlertCard
extends
StatelessWidget
{
final
List
<
AlertInfoBO
>
alerts
;
...
...
@@ -73,53 +75,57 @@ class MonitoringAlertCard extends StatelessWidget {
itemBuilder:
(
context
,
index
)
{
final
item
=
alerts
[
index
];
final
pending
=
item
.
status
.
contains
(
'待处理'
);
return
_buildAlertItem
(
item
,
pending
);
return
_buildAlertItem
(
context
,
item
,
pending
);
},
);
}
Widget
_buildAlertItem
(
AlertInfoBO
item
,
bool
pending
)
{
return
IntrinsicHeight
(
child:
Row
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
_buildIconBlock
(
pending
),
SizedBox
(
width:
20
.
w
),
Expanded
(
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
mainAxisAlignment:
MainAxisAlignment
.
start
,
children:
[
Text
(
item
.
title
,
style:
TextStyle
(
color:
pending
?
const
Color
(
0xFFF26522
)
:
const
Color
(
0xFFFFC14A
),
fontSize:
28
.
sp
,
fontWeight:
FontWeight
.
bold
,
Widget
_buildAlertItem
(
BuildContext
context
,
AlertInfoBO
item
,
bool
pending
)
{
return
InkWell
(
onTap:
()
=>
context
.
read
<
MonitoringIndexCubit
>().
selectAlert
(
item
),
borderRadius:
BorderRadius
.
circular
(
16
.
r
),
child:
IntrinsicHeight
(
child:
Row
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
_buildIconBlock
(
pending
),
SizedBox
(
width:
20
.
w
),
Expanded
(
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
mainAxisAlignment:
MainAxisAlignment
.
start
,
children:
[
Text
(
item
.
title
,
style:
TextStyle
(
color:
pending
?
const
Color
(
0xFFF26522
)
:
const
Color
(
0xFFFFC14A
),
fontSize:
28
.
sp
,
fontWeight:
FontWeight
.
bold
,
),
),
),
SizedBox
(
height:
8
.
h
),
Text
(
item
.
description
,
maxLines:
2
,
overflow:
TextOverflow
.
ellipsis
,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
23
.
sp
,
SizedBox
(
height:
8
.
h
),
Text
(
item
.
description
,
maxLines:
2
,
overflow:
TextOverflow
.
ellipsis
,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
23
.
sp
,
)
,
),
),
// SizedBox(height: 10.h),
Text
(
item
.
time
,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
20
.
sp
,
// SizedBox(height: 10.h
),
Text
(
item
.
time
,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
20
.
sp
,
)
,
),
)
,
]
,
]
,
)
,
),
)
,
]
,
]
,
)
,
),
);
}
...
...
lib/views/monitoring/index/widgets/monitoring_alert_detail_dialog.dart
0 → 100644
View file @
d2db426e
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
'package:laki_icu_app/views/monitoring/index/cubit/monitoring_index_cubit.dart'
;
class
MonitoringAlertDetailDialog
extends
StatelessWidget
{
final
AlertInfoBO
alert
;
const
MonitoringAlertDetailDialog
({
super
.
key
,
required
this
.
alert
,
});
@override
Widget
build
(
BuildContext
context
)
{
return
Dialog
(
backgroundColor:
Colors
.
transparent
,
insetPadding:
EdgeInsets
.
zero
,
child:
_buildDialogContent
(
context
),
);
}
Widget
_buildDialogContent
(
BuildContext
context
)
{
return
Container
(
margin:
EdgeInsets
.
fromLTRB
(
100
.
w
,
100
.
h
,
100
.
w
,
100
.
h
),
decoration:
BoxDecoration
(
color:
const
Color
(
0xFF0A1929
),
border:
Border
.
all
(
color:
const
Color
(
0xFF00D4FF
),
width:
3
.
w
,
),
borderRadius:
BorderRadius
.
circular
(
20
.
r
),
boxShadow:
[
BoxShadow
(
color:
const
Color
(
0xFF00D4FF
).
withValues
(
alpha:
77
),
blurRadius:
20
.
r
,
offset:
const
Offset
(
0
,
0
),
),
],
),
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
_buildHeader
(),
SizedBox
(
height:
30
.
h
),
_buildContent
(
context
),
SizedBox
(
height:
40
.
h
),
_buildBottomButtons
(
context
),
],
),
);
}
Widget
_buildHeader
()
{
return
Container
(
padding:
EdgeInsets
.
fromLTRB
(
40
.
w
,
24
.
h
,
40
.
w
,
24
.
h
),
decoration:
const
BoxDecoration
(
border:
Border
(
bottom:
BorderSide
(
color:
Color
(
0xFF00D4FF
),
width:
2
,
),
),
),
child:
Row
(
mainAxisAlignment:
MainAxisAlignment
.
center
,
children:
[
Text
(
'告警详情'
,
style:
TextStyle
(
color:
const
Color
(
0xFF00D4FF
),
fontSize:
36
.
sp
,
fontWeight:
FontWeight
.
bold
,
),
),
],
),
);
}
Widget
_buildContent
(
BuildContext
context
)
{
return
Expanded
(
child:
Padding
(
padding:
EdgeInsets
.
symmetric
(
horizontal:
40
.
w
),
child:
Row
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
_buildImagePlaceholder
(),
SizedBox
(
width:
30
.
w
),
Expanded
(
child:
_buildAlertInfo
(),
),
],
),
),
);
}
Widget
_buildImagePlaceholder
()
{
return
Container
(
width:
280
.
w
,
height:
200
.
h
,
decoration:
BoxDecoration
(
color:
const
Color
(
0xFF1A2332
),
borderRadius:
BorderRadius
.
circular
(
12
.
r
),
),
);
}
Widget
_buildAlertInfo
()
{
final
isPending
=
alert
.
status
.
contains
(
'待处理'
);
return
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
Row
(
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
children:
[
Text
(
alert
.
title
,
style:
TextStyle
(
color:
const
Color
(
0xFFFFC14A
),
fontSize:
34
.
sp
,
fontWeight:
FontWeight
.
bold
,
),
),
Text
(
alert
.
time
,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
24
.
sp
,
),
),
],
),
SizedBox
(
height:
20
.
h
),
_buildInfoRow
(
'处理状态'
,
isPending
?
'待处理'
:
'已处理'
,
isPending
),
_buildInfoRow
(
'处理状态'
,
isPending
?
'待处理'
:
'已处理'
,
isPending
),
_buildInfoRow
(
'处理时间'
,
''
,
false
),
SizedBox
(
height:
30
.
h
),
Expanded
(
child:
SingleChildScrollView
(
child:
Text
(
alert
.
description
,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
24
.
sp
,
height:
1.5
,
),
),
),
),
],
);
}
Widget
_buildInfoRow
(
String
label
,
String
value
,
bool
isHighlight
)
{
return
Padding
(
padding:
EdgeInsets
.
symmetric
(
vertical:
8
.
h
),
child:
Row
(
children:
[
SizedBox
(
width:
140
.
w
,
child:
Text
(
label
,
style:
TextStyle
(
color:
Colors
.
white
.
withValues
(
alpha:
153
),
fontSize:
26
.
sp
,
),
),
),
SizedBox
(
width:
20
.
w
),
Text
(
value
,
style:
TextStyle
(
color:
isHighlight
?
const
Color
(
0xFFFFC14A
)
:
Colors
.
white
,
fontSize:
26
.
sp
,
fontWeight:
isHighlight
?
FontWeight
.
bold
:
FontWeight
.
normal
,
),
),
],
),
);
}
Widget
_buildBottomButtons
(
BuildContext
context
)
{
return
Container
(
padding:
EdgeInsets
.
fromLTRB
(
40
.
w
,
20
.
h
,
40
.
w
,
30
.
h
),
child:
Row
(
mainAxisAlignment:
MainAxisAlignment
.
center
,
children:
[
_buildButton
(
'关闭窗口'
,
const
Color
(
0xFF2A2E3B
),
Colors
.
white
,
const
Color
(
0xFF4A4E5B
),
()
=>
Navigator
.
of
(
context
).
pop
(),
),
SizedBox
(
width:
30
.
w
),
_buildButton
(
'标记误报'
,
const
Color
(
0xFF2A2E3B
),
Colors
.
white
,
const
Color
(
0xFF4A4E5B
),
()
=>
_handleMarkAsFalse
(
context
),
),
SizedBox
(
width:
30
.
w
),
_buildButton
(
'已处理'
,
const
Color
(
0xFF00D4FF
),
Colors
.
black
,
const
Color
(
0xFF00D4FF
),
()
=>
_handleMarkAsProcessed
(
context
),
isActive:
true
,
),
],
),
);
}
Widget
_buildButton
(
String
text
,
Color
backgroundColor
,
Color
textColor
,
Color
borderColor
,
VoidCallback
onPressed
,
{
bool
isActive
=
false
,
})
{
return
Container
(
width:
220
.
w
,
height:
72
.
h
,
decoration:
BoxDecoration
(
color:
backgroundColor
,
border:
Border
.
all
(
color:
borderColor
,
width:
2
.
w
,
),
borderRadius:
BorderRadius
.
circular
(
8
.
r
),
boxShadow:
isActive
?
[
BoxShadow
(
color:
borderColor
.
withValues
(
alpha:
128
),
blurRadius:
10
.
r
,
offset:
const
Offset
(
0
,
0
),
),
]
:
null
,
),
child:
TextButton
(
onPressed:
onPressed
,
child:
Text
(
text
,
style:
TextStyle
(
color:
textColor
,
fontSize:
28
.
sp
,
fontWeight:
FontWeight
.
bold
,
),
),
),
);
}
void
_handleMarkAsFalse
(
BuildContext
context
)
{
context
.
read
<
MonitoringIndexCubit
>().
markAlertAsFalse
(
alert
);
Navigator
.
of
(
context
).
pop
();
}
void
_handleMarkAsProcessed
(
BuildContext
context
)
{
context
.
read
<
MonitoringIndexCubit
>().
markAlertAsProcessed
(
alert
);
Navigator
.
of
(
context
).
pop
();
}
}
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment