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
59de0036
Commit
59de0036
authored
Jun 25, 2026
by
akari
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 软件设置子页面
parent
72d5f64e
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
801 additions
and
65 deletions
+801
-65
bluetooth_read_bloc.dart
lib/blocs/bluetooth_read/bluetooth_read_bloc.dart
+23
-3
device_control_bloc.dart
lib/blocs/device_control/device_control_bloc.dart
+47
-12
bluetooth_manager.dart
lib/utils/bluetooth/bluetooth_manager.dart
+0
-0
monitoring_index_cubit.dart
lib/views/monitoring/index/cubit/monitoring_index_cubit.dart
+34
-18
bluetooth_bind_dialog.dart
...views/monitoring/index/widgets/bluetooth_bind_dialog.dart
+7
-1
customer_service_index_state.dart
.../customer_service/cubit/customer_service_index_state.dart
+20
-2
customer_service_panel.dart
...ings/customer_service/widgets/customer_service_panel.dart
+72
-10
remote_control_index_state.dart
...ings/remote_control/cubit/remote_control_index_state.dart
+35
-2
remote_control_panel.dart
...settings/remote_control/widgets/remote_control_panel.dart
+228
-9
software_update_index_state.dart
...gs/software_update/cubit/software_update_index_state.dart
+68
-2
software_update_panel.dart
...ttings/software_update/widgets/software_update_panel.dart
+267
-6
No files found.
lib/blocs/bluetooth_read/bluetooth_read_bloc.dart
View file @
59de0036
...
...
@@ -45,8 +45,7 @@ class BluetoothReadBloc extends Bloc<BluetoothReadEvent, BluetoothReadState> {
for
(
int
i
=
0
;
i
<
maxRetries
;
i
++)
{
try
{
info
=
await
_storageService
.
getBluetoothReadInfo
();
Log
.
warn
(
'**************sn 第
${i + 1}
次获取************:
${info.sn}
'
);
Log
.
warn
(
'**************sn 第
${i + 1}
次获取************:
${info.sn}
'
);
if
(
info
.
hasSn
)
{
Log
.
warn
(
'**************sn 获取成功,停止重试************'
);
break
;
...
...
@@ -75,7 +74,28 @@ class BluetoothReadBloc extends Bloc<BluetoothReadEvent, BluetoothReadState> {
BluetoothReadUpdated
event
,
Emitter
<
BluetoothReadState
>
emit
,
)
{
emit
(
state
.
copyWith
(
info:
event
.
info
,
clearError:
true
));
emit
(
state
.
copyWith
(
info:
_resolveDisplayInfo
(
event
.
info
),
clearError:
true
));
}
BluetoothReadModel
_resolveDisplayInfo
(
BluetoothReadModel
next
)
{
final
currentSn
=
state
.
info
.
sn
;
final
nextSn
=
next
.
sn
;
if
(
currentSn
==
null
||
currentSn
.
isEmpty
||
nextSn
==
null
||
nextSn
.
isEmpty
||
currentSn
==
nextSn
)
{
return
next
;
}
final
currentSide
=
currentSn
[
currentSn
.
length
-
1
].
toUpperCase
();
if
((
currentSide
==
'L'
||
currentSide
==
'R'
)
&&
currentSn
.
substring
(
0
,
currentSn
.
length
-
1
)
==
nextSn
)
{
return
next
.
copyWith
(
sn:
currentSn
);
}
return
next
;
}
void
_onCleared
(
...
...
lib/blocs/device_control/device_control_bloc.dart
View file @
59de0036
...
...
@@ -588,10 +588,10 @@ class DeviceControlBloc
// ==================== 护理等级控制事件处理 ====================
/// [用户操作] 设置护理等级(发送给MCU)—— 持久化
void
_onCareLevelChanged
(
Future
<
void
>
_onCareLevelChanged
(
DeviceControlCareLevelChanged
event
,
Emitter
<
DeviceControlState
>
emit
,
)
{
)
async
{
final
newData
=
state
.
data
.
copyWith
(
careLevel:
state
.
data
.
careLevel
.
copyWith
(
currentLevel:
event
.
level
,
...
...
@@ -602,7 +602,12 @@ class DeviceControlBloc
userSettings:
state
.
userSettings
.
copyWith
(
careLevelValue:
event
.
level
),
clearError:
true
,
));
// TODO: 发送设置到MCU
await
_writeControlValueToBluetooth
(
identifier:
0xA8
,
setValue:
'
${_careLevelProtocolValue(event.level)}
'
,
errorPrefix:
'护理等级'
,
emit:
emit
,
);
}
// ==================== 新风进化控制事件处理 ====================
...
...
@@ -653,10 +658,10 @@ class DeviceControlBloc
// ==================== 雾化时间设置事件处理 ====================
/// [用户操作] 设置雾化时间(发送给MCU)—— 持久化
void
_onNebulizationTimeChanged
(
Future
<
void
>
_onNebulizationTimeChanged
(
DeviceControlNebulizationTimeChanged
event
,
Emitter
<
DeviceControlState
>
emit
,
)
{
)
async
{
final
newData
=
state
.
data
.
copyWith
(
nebulizationTime:
state
.
data
.
nebulizationTime
.
copyWith
(
setMinutes:
event
.
minutes
,
...
...
@@ -668,16 +673,21 @@ class DeviceControlBloc
state
.
userSettings
.
copyWith
(
nebulizationTimeMinutes:
event
.
minutes
),
clearError:
true
,
));
// TODO: 发送设置到MCU
await
_writeControlValueToBluetooth
(
identifier:
0x0C
,
setValue:
event
.
minutes
,
errorPrefix:
'雾化时间'
,
emit:
emit
,
);
}
// ==================== 消毒时间设置事件处理 ====================
/// [用户操作] 设置消毒时间(发送给MCU)—— 持久化
void
_onDisinfectionTimeChanged
(
Future
<
void
>
_onDisinfectionTimeChanged
(
DeviceControlDisinfectionTimeChanged
event
,
Emitter
<
DeviceControlState
>
emit
,
)
{
)
async
{
final
newData
=
state
.
data
.
copyWith
(
disinfectionTime:
state
.
data
.
disinfectionTime
.
copyWith
(
setMinutes:
event
.
minutes
,
...
...
@@ -689,7 +699,12 @@ class DeviceControlBloc
state
.
userSettings
.
copyWith
(
disinfectionTimeMinutes:
event
.
minutes
),
clearError:
true
,
));
// TODO: 发送设置到MCU
await
_writeControlValueToBluetooth
(
identifier:
0x0D
,
setValue:
event
.
minutes
,
errorPrefix:
'消毒时间'
,
emit:
emit
,
);
}
// ==================== 开放供氧控制事件处理 ====================
...
...
@@ -735,10 +750,10 @@ class DeviceControlBloc
}
/// [用户操作] 清零供氧计时
void
_onOxygenTimerCleared
(
Future
<
void
>
_onOxygenTimerCleared
(
DeviceControlOxygenTimerCleared
event
,
Emitter
<
DeviceControlState
>
emit
,
)
{
)
async
{
final
newData
=
state
.
data
.
copyWith
(
oxygenTimer:
const
OxygenTimerBO
(
hours:
'00'
,
...
...
@@ -747,7 +762,12 @@ class DeviceControlBloc
),
);
emit
(
state
.
copyWith
(
data:
newData
,
clearError:
true
));
// TODO: 发送清零指令到MCU
await
_writeControlValueToBluetooth
(
identifier:
0x29
,
setValue:
'0'
,
errorPrefix:
'供氧累计时间清零'
,
emit:
emit
,
);
}
// ==================== 循环模式控制事件处理 ====================
...
...
@@ -984,6 +1004,21 @@ class DeviceControlBloc
}
}
int
_careLevelProtocolValue
(
CareLevel
level
)
{
switch
(
level
)
{
case
CareLevel
.
off
:
return
0
;
case
CareLevel
.
level3
:
return
1
;
case
CareLevel
.
level2
:
return
2
;
case
CareLevel
.
level1
:
return
3
;
case
CareLevel
.
special
:
return
4
;
}
}
List
<
int
>?
_buildControlValueCommand
({
required
int
identifier
,
required
String
setValue
,
...
...
lib/utils/bluetooth/bluetooth_manager.dart
View file @
59de0036
This diff is collapsed.
Click to expand it.
lib/views/monitoring/index/cubit/monitoring_index_cubit.dart
View file @
59de0036
...
...
@@ -150,8 +150,7 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
).
copyWith
(
sn:
reportSn
.
isEmpty
?
null
:
reportSn
);
if
(
bluetoothReadInfo
.
hasSn
)
{
await
_storageService
.
saveBluetoothReadSn
(
bluetoothReadInfo
.
sn
!);
await
_connectMqttForDeviceSn
(
bluetoothReadInfo
.
sn
!);
await
_fetchCabinDetailAndConnectVideo
(
bluetoothReadInfo
.
sn
!);
unawaited
(
_fetchCabinDetailAndConnectVideo
(
bluetoothReadInfo
.
sn
!));
}
eventBus
.
emit
(
BluetoothReadInfoChangedEvent
(
bluetoothReadInfo
),
...
...
@@ -232,18 +231,24 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
final
boundDevice
=
await
_storageService
.
getBoundBluetoothDevice
();
if
(
isClosed
)
return
;
final
deviceId
=
boundDevice
[
'deviceId'
];
final
bluetoothReadInfo
=
await
_storageService
.
getBluetoothReadInfo
();
if
(
isClosed
)
return
;
debugPrint
(
'🔥🔥🔥 BLE_CONNECT_DEBUG load_bound_device '
'deviceId=
$deviceId
deviceName=
${boundDevice['deviceName']}
'
'has
Sn=
${bluetoothReadInfo.hasSn
}
'
,
'has
DeviceId=
${deviceId != null && deviceId.isNotEmpty
}
'
,
);
emit
(
state
.
copyWith
(
boundBluetoothDeviceId:
deviceId
==
null
||
deviceId
.
isEmpty
?
null
:
deviceId
,
boundBluetoothDeviceName:
boundDevice
[
'deviceName'
],
));
if
(
deviceId
!=
null
&&
deviceId
.
isNotEmpty
)
{
_isAutoReconnectEnabled
=
true
;
await
_connectBoundBluetoothDevice
();
if
(
isClosed
)
return
;
}
final
bluetoothReadInfo
=
await
_storageService
.
getBluetoothReadInfo
();
if
(
isClosed
)
return
;
if
(
bluetoothReadInfo
.
hasSn
)
{
final
sn
=
_appendBluetoothSideToSn
(
bluetoothReadInfo
.
sn
!);
if
(
sn
.
isNotEmpty
)
{
...
...
@@ -253,14 +258,9 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
BluetoothReadInfoChangedEvent
(
bluetoothReadInfo
.
copyWith
(
sn:
sn
)),
);
}
_connectMqttForDeviceSn
(
sn
);
_fetchCabinDetailAndConnectVideo
(
sn
);
}
}
if
(
deviceId
!=
null
&&
deviceId
.
isNotEmpty
)
{
_isAutoReconnectEnabled
=
true
;
_connectBoundBluetoothDevice
();
}
}
/// 页面初始化入口。
...
...
@@ -288,8 +288,10 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
final
cameraSn
=
state
.
cabinCameraSn
;
final
wifiPwd
=
state
.
cabinWifiPwd
;
if
(
cameraSn
==
null
||
cameraSn
.
isEmpty
||
wifiPwd
==
null
||
wifiPwd
.
isEmpty
)
{
if
(
cameraSn
==
null
||
cameraSn
.
isEmpty
||
wifiPwd
==
null
||
wifiPwd
.
isEmpty
)
{
debugPrint
(
'[MonitoringIndexCubit] WebRTC 凭证未就绪,跳过连接'
);
return
;
}
...
...
@@ -313,8 +315,10 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
final
cameraSn
=
state
.
cabinCameraSn
;
final
wifiPwd
=
state
.
cabinWifiPwd
;
if
(
cameraSn
==
null
||
cameraSn
.
isEmpty
||
wifiPwd
==
null
||
wifiPwd
.
isEmpty
)
{
if
(
cameraSn
==
null
||
cameraSn
.
isEmpty
||
wifiPwd
==
null
||
wifiPwd
.
isEmpty
)
{
debugPrint
(
'[MonitoringIndexCubit] P2P 凭证未就绪,跳过连接'
);
return
;
}
...
...
@@ -461,6 +465,18 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
}
}
Future
<
void
>
stopBluetoothScan
()
async
{
await
_bluetoothManager
.
stopScan
();
if
(!
isClosed
)
{
emit
(
state
.
copyWith
(
isBluetoothScanning:
false
));
}
}
Future
<
void
>
connectBoundBluetoothDevice
()
async
{
_isAutoReconnectEnabled
=
true
;
await
_connectBoundBluetoothDevice
();
}
Future
<
void
>
bindBluetoothDevice
(
BluetoothScanDevice
device
)
async
{
if
(
state
.
isBluetoothBinding
)
return
;
...
...
@@ -472,6 +488,7 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
));
try
{
await
stopBluetoothScan
();
await
_bluetoothManager
.
connectToDevice
(
device
);
if
(!
_bluetoothManager
.
isConnected
)
{
throw
StateError
(
'蓝牙连接未就绪'
);
...
...
@@ -573,16 +590,15 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
);
try
{
await
_bluetoothManager
.
connectToDeviceId
(
deviceId
,
prescan:
true
);
await
_bluetoothManager
.
connectToDeviceId
(
deviceId
);
debugPrint
(
'🔥🔥🔥 BLE_CONNECT_DEBUG auto_connect_success '
'deviceId=
$deviceId
isConnected=
${_bluetoothManager.isConnected}
'
,
);
if
(!
isClosed
)
{
emit
(
state
.
copyWith
(
bluetoothMessage:
_bluetoothManager
.
isConnected
?
'蓝牙设备已自动连接'
:
'蓝牙自动连接未就绪,等待重试'
,
bluetoothMessage:
_bluetoothManager
.
isConnected
?
'蓝牙设备已自动连接'
:
'蓝牙自动连接未就绪,等待重试'
,
));
}
if
(
_bluetoothManager
.
isConnected
)
{
...
...
lib/views/monitoring/index/widgets/bluetooth_bind_dialog.dart
View file @
59de0036
import
'dart:async'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_bloc/flutter_bloc.dart'
;
import
'package:flutter_screenutil/flutter_screenutil.dart'
;
...
...
@@ -91,7 +93,11 @@ class _BluetoothBindDialogState extends State<BluetoothBindDialog> {
),
),
IconButton
(
onPressed:
()
=>
Navigator
.
of
(
context
).
pop
(),
onPressed:
()
{
final
cubit
=
context
.
read
<
MonitoringIndexCubit
>();
Navigator
.
of
(
context
).
pop
();
unawaited
(
cubit
.
stopBluetoothScan
());
},
icon:
Icon
(
Icons
.
close
,
color:
Colors
.
white
,
size:
48
.
w
),
tooltip:
'关闭'
,
padding:
EdgeInsets
.
zero
,
...
...
lib/views/settings/customer_service/cubit/customer_service_index_state.dart
View file @
59de0036
import
'package:equatable/equatable.dart'
;
class
CustomerServiceIndexState
extends
Equatable
{
const
CustomerServiceIndexState
();
const
CustomerServiceIndexState
({
this
.
title
=
'联系我们'
,
this
.
hotlineLabel
=
'服务热线'
,
this
.
hotline
=
'400-0000-0000'
,
this
.
serviceTimeLabel
=
'服务时间'
,
this
.
serviceTime
=
'工作日 早上9:00 - 下午19:00'
,
});
final
String
title
;
final
String
hotlineLabel
;
final
String
hotline
;
final
String
serviceTimeLabel
;
final
String
serviceTime
;
@override
List
<
Object
?>
get
props
=>
[];
List
<
Object
?>
get
props
=>
[
title
,
hotlineLabel
,
hotline
,
serviceTimeLabel
,
serviceTime
,
];
}
lib/views/settings/customer_service/widgets/customer_service_panel.dart
View file @
59de0036
import
'package:flutter/material.dart'
;
import
'package:flutter_bloc/flutter_bloc.dart'
;
import
'package:flutter_screenutil/flutter_screenutil.dart'
;
import
'../../widgets/settings_panel_frame.dart'
;
import
'../cubit/customer_service_index_cubit.dart'
;
import
'../cubit/customer_service_index_state.dart'
;
class
CustomerServicePanel
extends
StatelessWidget
{
const
CustomerServicePanel
({
super
.
key
});
...
...
@@ -10,18 +13,77 @@ class CustomerServicePanel extends StatelessWidget {
Widget
build
(
BuildContext
context
)
{
return
SettingsPanelFrame
(
backgroundAsset:
settingsAssetPanelMain
,
padding:
EdgeInsets
.
all
(
42
.
w
),
child:
Align
(
alignment:
Alignment
.
topLeft
,
child:
Text
(
'客户服务'
,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
34
.
sp
,
fontWeight:
FontWeight
.
w800
,
padding:
EdgeInsets
.
fromLTRB
(
42
.
w
,
64
.
h
,
42
.
w
,
48
.
h
),
child:
BlocBuilder
<
CustomerServiceIndexCubit
,
CustomerServiceIndexState
>(
builder:
(
context
,
state
)
{
return
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
Text
(
state
.
title
,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
38
.
sp
,
fontWeight:
FontWeight
.
w800
,
),
),
SizedBox
(
height:
46
.
h
),
_ContactLine
(
label:
state
.
hotlineLabel
,
value:
state
.
hotline
,
),
SizedBox
(
height:
30
.
h
),
_ContactLine
(
label:
state
.
serviceTimeLabel
,
value:
state
.
serviceTime
,
),
SizedBox
(
height:
42
.
h
),
Divider
(
color:
const
Color
(
0xFF4CA7D2
).
withValues
(
alpha:
0.36
)),
],
);
},
),
);
}
}
class
_ContactLine
extends
StatelessWidget
{
const
_ContactLine
({
required
this
.
label
,
required
this
.
value
,
});
final
String
label
;
final
String
value
;
@override
Widget
build
(
BuildContext
context
)
{
return
Row
(
children:
[
SizedBox
(
width:
220
.
w
,
child:
Text
(
label
,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
29
.
sp
,
fontWeight:
FontWeight
.
w800
,
),
),
),
),
Expanded
(
child:
Text
(
value
,
maxLines:
1
,
overflow:
TextOverflow
.
ellipsis
,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
29
.
sp
,
fontWeight:
FontWeight
.
w800
,
),
),
),
],
);
}
}
lib/views/settings/remote_control/cubit/remote_control_index_state.dart
View file @
59de0036
import
'package:equatable/equatable.dart'
;
class
RemoteControlAccount
extends
Equatable
{
const
RemoteControlAccount
({
required
this
.
phone
,
});
final
String
phone
;
@override
List
<
Object
?>
get
props
=>
[
phone
];
}
class
RemoteControlIndexState
extends
Equatable
{
const
RemoteControlIndexState
();
const
RemoteControlIndexState
({
this
.
productName
=
'TSAAS ICU 动物医疗监护舱'
,
this
.
modelName
=
'730SE-CICU-A'
,
this
.
appName
=
'TSAAS 移动APP'
,
this
.
adminAccounts
=
const
[
RemoteControlAccount
(
phone:
'130****1233'
),
],
this
.
monitorAccounts
=
const
[
RemoteControlAccount
(
phone:
'130****1233'
),
],
});
final
String
productName
;
final
String
modelName
;
final
String
appName
;
final
List
<
RemoteControlAccount
>
adminAccounts
;
final
List
<
RemoteControlAccount
>
monitorAccounts
;
@override
List
<
Object
?>
get
props
=>
[];
List
<
Object
?>
get
props
=>
[
productName
,
modelName
,
appName
,
adminAccounts
,
monitorAccounts
,
];
}
lib/views/settings/remote_control/widgets/remote_control_panel.dart
View file @
59de0036
import
'package:flutter/material.dart'
;
import
'package:flutter_bloc/flutter_bloc.dart'
;
import
'package:flutter_screenutil/flutter_screenutil.dart'
;
import
'../../widgets/settings_panel_frame.dart'
;
import
'../cubit/remote_control_index_cubit.dart'
;
import
'../cubit/remote_control_index_state.dart'
;
class
RemoteControlPanel
extends
StatelessWidget
{
const
RemoteControlPanel
({
super
.
key
});
...
...
@@ -10,17 +13,233 @@ class RemoteControlPanel extends StatelessWidget {
Widget
build
(
BuildContext
context
)
{
return
SettingsPanelFrame
(
backgroundAsset:
settingsAssetPanelMain
,
padding:
EdgeInsets
.
all
(
42
.
w
),
child:
Align
(
alignment:
Alignment
.
topLeft
,
child:
Text
(
'远程控制'
,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
34
.
sp
,
fontWeight:
FontWeight
.
w800
,
padding:
EdgeInsets
.
fromLTRB
(
42
.
w
,
54
.
h
,
42
.
w
,
48
.
h
),
child:
BlocBuilder
<
RemoteControlIndexCubit
,
RemoteControlIndexState
>(
builder:
(
context
,
state
)
{
return
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
stretch
,
children:
[
_RemoteHeader
(
state:
state
),
SizedBox
(
height:
34
.
h
),
Divider
(
color:
const
Color
(
0xFF4CA7D2
).
withValues
(
alpha:
0.36
)),
SizedBox
(
height:
32
.
h
),
Expanded
(
child:
SingleChildScrollView
(
physics:
const
ClampingScrollPhysics
(),
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
_AccountSection
(
title:
'管理员'
,
description:
'可以设置更改设备各项参数,启动TSAAS等功能。'
,
accounts:
state
.
adminAccounts
,
),
SizedBox
(
height:
32
.
h
),
_AccountSection
(
title:
'监测员'
,
description:
'只能查看设备各项参数,记录诊疗状态等权限。'
,
accounts:
state
.
monitorAccounts
,
),
],
),
),
),
],
);
},
),
);
}
}
class
_RemoteHeader
extends
StatelessWidget
{
const
_RemoteHeader
({
required
this
.
state
});
final
RemoteControlIndexState
state
;
@override
Widget
build
(
BuildContext
context
)
{
return
Row
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
Expanded
(
child:
Padding
(
padding:
EdgeInsets
.
only
(
top:
20
.
h
),
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
Text
(
state
.
productName
,
maxLines:
1
,
overflow:
TextOverflow
.
ellipsis
,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
42
.
sp
,
fontWeight:
FontWeight
.
w800
,
),
),
SizedBox
(
height:
22
.
h
),
Text
(
state
.
modelName
,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
36
.
sp
,
fontWeight:
FontWeight
.
w800
,
),
),
],
),
),
),
SizedBox
(
width:
40
.
w
),
_AppQrCard
(
appName:
state
.
appName
),
],
);
}
}
class
_AppQrCard
extends
StatelessWidget
{
const
_AppQrCard
({
required
this
.
appName
});
final
String
appName
;
@override
Widget
build
(
BuildContext
context
)
{
return
SizedBox
(
width:
226
.
w
,
child:
Column
(
children:
[
Container
(
width:
190
.
w
,
height:
190
.
w
,
decoration:
BoxDecoration
(
color:
Colors
.
white
,
borderRadius:
BorderRadius
.
circular
(
18
.
r
),
),
),
SizedBox
(
height:
20
.
h
),
Text
(
appName
,
maxLines:
1
,
overflow:
TextOverflow
.
ellipsis
,
textAlign:
TextAlign
.
center
,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
24
.
sp
,
fontWeight:
FontWeight
.
w800
,
),
),
],
),
);
}
}
class
_AccountSection
extends
StatelessWidget
{
const
_AccountSection
({
required
this
.
title
,
required
this
.
description
,
required
this
.
accounts
,
});
final
String
title
;
final
String
description
;
final
List
<
RemoteControlAccount
>
accounts
;
@override
Widget
build
(
BuildContext
context
)
{
final
items
=
[
for
(
final
account
in
accounts
)
_AccountActionCard
(
text:
account
.
phone
,
icon:
Icons
.
manage_accounts_outlined
,
),
const
_AccountActionCard
(
text:
'新增账户'
,
icon:
Icons
.
person_add_alt_1_outlined
,
),
];
return
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
Row
(
crossAxisAlignment:
CrossAxisAlignment
.
end
,
children:
[
Text
(
title
,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
38
.
sp
,
fontWeight:
FontWeight
.
w800
,
),
),
SizedBox
(
width:
24
.
w
),
Expanded
(
child:
Padding
(
padding:
EdgeInsets
.
only
(
bottom:
6
.
h
),
child:
Text
(
description
,
maxLines:
1
,
overflow:
TextOverflow
.
ellipsis
,
style:
TextStyle
(
color:
const
Color
(
0xFFAAEFFF
),
fontSize:
17
.
sp
,
fontWeight:
FontWeight
.
w500
,
),
),
),
),
],
),
SizedBox
(
height:
28
.
h
),
Wrap
(
spacing:
20
.
w
,
runSpacing:
18
.
h
,
children:
items
,
),
],
);
}
}
class
_AccountActionCard
extends
StatelessWidget
{
const
_AccountActionCard
({
required
this
.
text
,
required
this
.
icon
,
});
final
String
text
;
final
IconData
icon
;
@override
Widget
build
(
BuildContext
context
)
{
return
Container
(
width:
235
.
w
,
height:
92
.
h
,
padding:
EdgeInsets
.
symmetric
(
horizontal:
22
.
w
),
decoration:
settingsButtonDecoration
(),
child:
Row
(
children:
[
Icon
(
icon
,
color:
const
Color
(
0xFF78C8F5
).
withValues
(
alpha:
0.78
),
size:
42
.
w
,
),
SizedBox
(
width:
16
.
w
),
Expanded
(
child:
Text
(
text
,
maxLines:
1
,
overflow:
TextOverflow
.
ellipsis
,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
22
.
sp
,
fontWeight:
FontWeight
.
w800
,
),
),
),
],
),
);
}
...
...
lib/views/settings/software_update/cubit/software_update_index_state.dart
View file @
59de0036
import
'package:equatable/equatable.dart'
;
class
SoftwareUpdateInfo
extends
Equatable
{
const
SoftwareUpdateInfo
({
required
this
.
title
,
required
this
.
currentVersion
,
required
this
.
latestVersion
,
required
this
.
updateNotes
,
required
this
.
actionText
,
required
this
.
canUpdate
,
this
.
footerText
,
});
final
String
title
;
final
String
currentVersion
;
final
String
latestVersion
;
final
List
<
String
>
updateNotes
;
final
String
actionText
;
final
bool
canUpdate
;
final
String
?
footerText
;
@override
List
<
Object
?>
get
props
=>
[
title
,
currentVersion
,
latestVersion
,
updateNotes
,
actionText
,
canUpdate
,
footerText
,
];
}
class
SoftwareUpdateIndexState
extends
Equatable
{
const
SoftwareUpdateIndexState
();
const
SoftwareUpdateIndexState
({
this
.
productName
=
'TSAAS ICU 动物医疗监护舱'
,
this
.
modelName
=
'730SE-CICU-A'
,
this
.
tabletSystem
=
const
SoftwareUpdateInfo
(
title:
'平板系统'
,
currentVersion:
'V2.32.4'
,
latestVersion:
'V2.32.5'
,
updateNotes:
[
'1.重置UI界面'
,
'2.优化已知bug'
,
],
actionText:
'立即更新'
,
canUpdate:
true
,
),
this
.
deviceFirmware
=
const
SoftwareUpdateInfo
(
title:
'设备固件OTA(实体按键)'
,
currentVersion:
'V2.32.4'
,
latestVersion:
'V2.32.4(当前版本)'
,
updateNotes:
[
'修复bug'
,
],
actionText:
'已是最新版本'
,
canUpdate:
false
,
footerText:
'*固件升级时间较长,请勿断电'
,
),
});
final
String
productName
;
final
String
modelName
;
final
SoftwareUpdateInfo
tabletSystem
;
final
SoftwareUpdateInfo
deviceFirmware
;
@override
List
<
Object
?>
get
props
=>
[];
List
<
Object
?>
get
props
=>
[
productName
,
modelName
,
tabletSystem
,
deviceFirmware
,
];
}
lib/views/settings/software_update/widgets/software_update_panel.dart
View file @
59de0036
import
'package:flutter/material.dart'
;
import
'package:flutter_bloc/flutter_bloc.dart'
;
import
'package:flutter_screenutil/flutter_screenutil.dart'
;
import
'../../widgets/settings_panel_frame.dart'
;
import
'../cubit/software_update_index_cubit.dart'
;
import
'../cubit/software_update_index_state.dart'
;
class
SoftwareUpdatePanel
extends
StatelessWidget
{
const
SoftwareUpdatePanel
({
super
.
key
});
...
...
@@ -10,17 +13,275 @@ class SoftwareUpdatePanel extends StatelessWidget {
Widget
build
(
BuildContext
context
)
{
return
SettingsPanelFrame
(
backgroundAsset:
settingsAssetPanelMain
,
padding:
EdgeInsets
.
all
(
42
.
w
),
child:
Align
(
alignment:
Alignment
.
topLeft
,
child:
Text
(
'软件更新'
,
padding:
EdgeInsets
.
fromLTRB
(
42
.
w
,
54
.
h
,
42
.
w
,
48
.
h
),
child:
BlocBuilder
<
SoftwareUpdateIndexCubit
,
SoftwareUpdateIndexState
>(
builder:
(
context
,
state
)
{
return
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
stretch
,
children:
[
_UpdateHeader
(
state:
state
),
SizedBox
(
height:
30
.
h
),
Divider
(
color:
const
Color
(
0xFF4CA7D2
).
withValues
(
alpha:
0.36
)),
SizedBox
(
height:
36
.
h
),
Expanded
(
child:
Row
(
crossAxisAlignment:
CrossAxisAlignment
.
stretch
,
children:
[
Expanded
(
child:
_UpdateColumn
(
info:
state
.
tabletSystem
)),
Container
(
width:
1
.
w
,
margin:
EdgeInsets
.
symmetric
(
horizontal:
38
.
w
),
color:
const
Color
(
0xFF3A9BD2
).
withValues
(
alpha:
0.42
),
),
Expanded
(
child:
_UpdateColumn
(
info:
state
.
deviceFirmware
)),
],
),
),
],
);
},
),
);
}
}
class
_UpdateHeader
extends
StatelessWidget
{
const
_UpdateHeader
({
required
this
.
state
});
final
SoftwareUpdateIndexState
state
;
@override
Widget
build
(
BuildContext
context
)
{
return
Row
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
Expanded
(
child:
Padding
(
padding:
EdgeInsets
.
only
(
top:
22
.
h
),
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
Text
(
state
.
productName
,
maxLines:
1
,
overflow:
TextOverflow
.
ellipsis
,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
42
.
sp
,
fontWeight:
FontWeight
.
w800
,
),
),
SizedBox
(
height:
22
.
h
),
Text
(
state
.
modelName
,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
36
.
sp
,
fontWeight:
FontWeight
.
w800
,
),
),
],
),
),
),
SizedBox
(
width:
34
.
w
),
const
_CheckUpdateButton
(),
],
);
}
}
class
_CheckUpdateButton
extends
StatelessWidget
{
const
_CheckUpdateButton
();
@override
Widget
build
(
BuildContext
context
)
{
return
Container
(
width:
245
.
w
,
height:
104
.
h
,
padding:
EdgeInsets
.
symmetric
(
horizontal:
24
.
w
),
decoration:
settingsButtonDecoration
(
active:
true
),
child:
Row
(
children:
[
Container
(
width:
52
.
w
,
height:
52
.
w
,
decoration:
BoxDecoration
(
shape:
BoxShape
.
circle
,
border:
Border
.
all
(
color:
const
Color
(
0xFF78C8F5
).
withValues
(
alpha:
0.86
),
width:
3
.
w
,
),
),
child:
Icon
(
Icons
.
arrow_upward
,
color:
const
Color
(
0xFF78C8F5
).
withValues
(
alpha:
0.9
),
size:
32
.
w
,
),
),
SizedBox
(
width:
22
.
w
),
Expanded
(
child:
Text
(
'检查更新'
,
maxLines:
1
,
overflow:
TextOverflow
.
ellipsis
,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
23
.
sp
,
fontWeight:
FontWeight
.
w800
,
),
),
),
],
),
);
}
}
class
_UpdateColumn
extends
StatelessWidget
{
const
_UpdateColumn
({
required
this
.
info
});
final
SoftwareUpdateInfo
info
;
@override
Widget
build
(
BuildContext
context
)
{
return
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
Text
(
info
.
title
,
maxLines:
1
,
overflow:
TextOverflow
.
ellipsis
,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
3
4
.
sp
,
fontSize:
3
8
.
sp
,
fontWeight:
FontWeight
.
w800
,
),
),
SizedBox
(
height:
42
.
h
),
_VersionLine
(
label:
'当前版本:'
,
value:
info
.
currentVersion
),
SizedBox
(
height:
20
.
h
),
_VersionLine
(
label:
'最新版本:'
,
value:
info
.
latestVersion
),
SizedBox
(
height:
22
.
h
),
Text
(
'更新内容:'
,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
27
.
sp
,
fontWeight:
FontWeight
.
w800
,
),
),
SizedBox
(
height:
18
.
h
),
for
(
final
note
in
info
.
updateNotes
)
Padding
(
padding:
EdgeInsets
.
only
(
bottom:
6
.
h
),
child:
Text
(
note
,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
20
.
sp
,
fontWeight:
FontWeight
.
w500
,
),
),
),
const
Spacer
(),
Center
(
child:
_UpdateActionButton
(
info:
info
)),
if
(
info
.
footerText
!=
null
)
...[
SizedBox
(
height:
18
.
h
),
Center
(
child:
Text
(
info
.
footerText
!,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
17
.
sp
,
fontWeight:
FontWeight
.
w700
,
),
),
),
]
else
SizedBox
(
height:
38
.
h
),
],
);
}
}
class
_VersionLine
extends
StatelessWidget
{
const
_VersionLine
({
required
this
.
label
,
required
this
.
value
,
});
final
String
label
;
final
String
value
;
@override
Widget
build
(
BuildContext
context
)
{
return
Text
.
rich
(
TextSpan
(
children:
[
TextSpan
(
text:
label
),
TextSpan
(
text:
value
),
],
),
maxLines:
1
,
overflow:
TextOverflow
.
ellipsis
,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
26
.
sp
,
fontWeight:
FontWeight
.
w800
,
),
);
}
}
class
_UpdateActionButton
extends
StatelessWidget
{
const
_UpdateActionButton
({
required
this
.
info
});
final
SoftwareUpdateInfo
info
;
@override
Widget
build
(
BuildContext
context
)
{
final
foreground
=
info
.
canUpdate
?
Colors
.
white
:
Colors
.
white
.
withValues
(
alpha:
0.42
);
return
Container
(
width:
345
.
w
,
height:
70
.
h
,
padding:
EdgeInsets
.
symmetric
(
horizontal:
28
.
w
),
decoration:
settingsButtonDecoration
(
active:
info
.
canUpdate
),
child:
Row
(
children:
[
Text
(
'《《'
,
style:
TextStyle
(
color:
const
Color
(
0xFF67C9F5
)
.
withValues
(
alpha:
info
.
canUpdate
?
0.72
:
0.34
),
fontSize:
26
.
sp
,
fontWeight:
FontWeight
.
w700
,
),
),
Expanded
(
child:
Text
(
info
.
actionText
,
maxLines:
1
,
overflow:
TextOverflow
.
ellipsis
,
textAlign:
TextAlign
.
center
,
style:
TextStyle
(
color:
foreground
,
fontSize:
26
.
sp
,
fontWeight:
FontWeight
.
w800
,
),
),
),
Text
(
'》》'
,
style:
TextStyle
(
color:
const
Color
(
0xFF67C9F5
)
.
withValues
(
alpha:
info
.
canUpdate
?
0.72
:
0.34
),
fontSize:
26
.
sp
,
fontWeight:
FontWeight
.
w700
,
),
),
],
),
);
}
...
...
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