Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
S
smart_hotel_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
张宏
smart_hotel_app
Commits
7566c742
Commit
7566c742
authored
Jun 18, 2026
by
huqu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix bugs
parent
59e6cf59
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
36 changed files
with
705 additions
and
467 deletions
+705
-467
app_router.dart
lib/routes/app_router.dart
+2
-0
app_router.gr.dart
lib/routes/app_router.gr.dart
+0
-0
deal_utils.dart
lib/utils/deal_utils.dart
+9
-0
device_cubit.dart
lib/views/home/device/cubit/device_cubit.dart
+5
-0
device_state.dart
lib/views/home/device/cubit/device_state.dart
+7
-0
device_detail_view.dart
lib/views/home/device/device_detail_view.dart
+5
-1
device_parms_block.dart
lib/views/home/device/widget/device_parms_block.dart
+11
-10
power_char_block.dart
lib/views/home/device/widget/power_char_block.dart
+92
-68
home_index_cubit.dart
lib/views/home/index/cubit/home_index_cubit.dart
+19
-19
index_view.dart
lib/views/home/index/index_view.dart
+17
-10
temperature_block.dart
lib/views/home/index/widget/temperature_block.dart
+3
-3
voltage_operate_block.dart
lib/views/home/index/widget/voltage_operate_block.dart
+38
-36
device_overview_card.dart
...s/home/inspection_device/widget/device_overview_card.dart
+1
-1
inspection_topology_cubit.dart
.../inspection_topology/cubit/inspection_topology_cubit.dart
+10
-15
inspection_topology_view.dart
...ws/home/inspection_topology/inspection_topology_view.dart
+2
-2
device_detail_dialog.dart
...home/inspection_topology/widget/device_detail_dialog.dart
+4
-4
login_cubit.dart
lib/views/login/cubit/login_cubit.dart
+36
-9
btn_login.dart
lib/views/login/widget/btn_login.dart
+1
-1
about_view.dart
lib/views/profile/about/about_view.dart
+99
-0
profile_cubit.dart
lib/views/profile/index/cubit/profile_cubit.dart
+1
-1
index_view.dart
lib/views/profile/index/index_view.dart
+97
-2
profile_menu_block.dart
lib/views/profile/index/widget/profile_menu_block.dart
+5
-0
index_view.dart
lib/views/report/index/index_view.dart
+12
-9
report_metrics_block.dart
lib/views/report/index/widget/report_metrics_block.dart
+3
-1
room_report_cubit.dart
lib/views/report/room/cubit/room_report_cubit.dart
+2
-0
room_detail_view.dart
lib/views/report/room/room_detail_view.dart
+63
-99
room_grid.dart
lib/views/report/room/widget/room_grid.dart
+69
-68
status_stats.dart
lib/views/report/room/widget/status_stats.dart
+2
-2
service_index_cubit.dart
lib/views/service/index/cubit/service_index_cubit.dart
+0
-0
service_index_state.dart
lib/views/service/index/cubit/service_index_state.dart
+0
-11
index_view.dart
lib/views/service/index/index_view.dart
+12
-9
room_management_block.dart
lib/views/service/index/widget/room_management_block.dart
+24
-30
service_room_cubit.dart
lib/views/service/room/cubit/service_room_cubit.dart
+29
-10
service_room_state.dart
lib/views/service/room/cubit/service_room_state.dart
+10
-5
room_detail_view.dart
lib/views/service/room/room_detail_view.dart
+2
-1
room_bottom_buttons.dart
lib/views/service/room/widget/room_bottom_buttons.dart
+13
-40
No files found.
lib/routes/app_router.dart
View file @
7566c742
...
...
@@ -56,5 +56,7 @@ class AppRouter extends $AppRouter {
AutoRoute
(
page:
ReportDeviceListRoute
.
page
),
// InspectionTopologyRoute / InspectionTopologyView(巡检拓扑页)
AutoRoute
(
page:
InspectionTopologyRoute
.
page
),
// AboutRoute / AboutView(关于页)
AutoRoute
(
page:
AboutRoute
.
page
),
];
}
lib/routes/app_router.gr.dart
View file @
7566c742
This diff is collapsed.
Click to expand it.
lib/utils/deal_utils.dart
0 → 100644
View file @
7566c742
class
DealUtils
{
/// 去除多余0
static
String
cleanNumber
(
String
value
)
{
return
value
.
replaceAll
(
RegExp
(
r'\.?0+$'
),
''
);
}
}
\ No newline at end of file
lib/views/home/device/cubit/device_cubit.dart
View file @
7566c742
...
...
@@ -99,4 +99,8 @@ class DeviceCubit extends Cubit<DeviceState> {
void
clearOperationMessage
()
{
emit
(
state
.
copyWith
(
operationMessage:
null
));
}
void
selectChartTab
(
int
index
)
{
emit
(
state
.
copyWith
(
selectedChartTabIndex:
index
));
}
}
\ No newline at end of file
lib/views/home/device/cubit/device_state.dart
View file @
7566c742
...
...
@@ -53,6 +53,7 @@ class DeviceState extends Equatable {
final
bool
isPoweringOn
;
final
bool
isPoweringOff
;
final
String
?
operationMessage
;
final
int
selectedChartTabIndex
;
const
DeviceState
({
this
.
isLoading
=
false
,
...
...
@@ -61,6 +62,7 @@ class DeviceState extends Equatable {
this
.
isPoweringOn
=
false
,
this
.
isPoweringOff
=
false
,
this
.
operationMessage
,
this
.
selectedChartTabIndex
=
0
,
});
DeviceState
copyWith
({
...
...
@@ -70,6 +72,7 @@ class DeviceState extends Equatable {
bool
?
isPoweringOn
,
bool
?
isPoweringOff
,
Object
?
operationMessage
=
_unset
,
int
?
selectedChartTabIndex
,
})
{
return
DeviceState
(
isLoading:
isLoading
??
this
.
isLoading
,
...
...
@@ -82,6 +85,8 @@ class DeviceState extends Equatable {
operationMessage:
identical
(
operationMessage
,
_unset
)
?
this
.
operationMessage
:
operationMessage
as
String
?,
selectedChartTabIndex:
selectedChartTabIndex
??
this
.
selectedChartTabIndex
,
);
}
...
...
@@ -93,5 +98,6 @@ class DeviceState extends Equatable {
isPoweringOn
,
isPoweringOff
,
operationMessage
,
selectedChartTabIndex
,
];
}
\ No newline at end of file
lib/views/home/device/device_detail_view.dart
View file @
7566c742
...
...
@@ -78,7 +78,11 @@ class DeviceDetailView extends StatelessWidget {
SizedBox
(
height:
10
.
h
),
DeviceParmsBlock
(
deviceInfo:
deviceInfo
),
SizedBox
(
height:
10
.
h
),
PowerCharBlock
(
deviceInfo:
deviceInfo
),
PowerCharBlock
(
deviceInfo:
deviceInfo
,
selectedTabIndex:
state
.
selectedChartTabIndex
,
onTabChanged:
(
index
)
=>
cubit
.
selectChartTab
(
index
),
),
SizedBox
(
height:
10
.
h
),
_buildBottomButtons
(
context
,
cubit
,
state
),
SizedBox
(
height:
30
.
h
),
...
...
lib/views/home/device/widget/device_parms_block.dart
View file @
7566c742
import
'package:flutter/material.dart'
;
import
'package:flutter_screenutil/flutter_screenutil.dart'
;
import
'package:smart_hotel_app/utils/deal_utils.dart'
;
import
'package:smart_hotel_app/views/home/device/cubit/device_state.dart'
;
class
DeviceParmsBlock
extends
StatelessWidget
{
...
...
@@ -57,15 +58,15 @@ class DeviceParmsBlock extends StatelessWidget {
);
}
String
_formatValue
(
String
raw
)
{
final
d
=
double
.
tryParse
(
raw
);
if
(
d
==
null
)
return
raw
;
// 去掉末尾多余的 0,整数不显示小数点
final
formatted
=
d
.
toStringAsFixed
(
2
);
final
parts
=
formatted
.
split
(
'.'
);
if
(
parts
.
length
==
2
&&
parts
[
1
]
==
'00'
)
return
parts
[
0
];
return
formatted
.
replaceAll
(
RegExp
(
r'0+$'
),
''
).
replaceAll
(
RegExp
(
r'\.$'
),
''
);
}
//
String _formatValue(String raw) {
//
final d = double.tryParse(raw);
//
if (d == null) return raw;
//
// 去掉末尾多余的 0,整数不显示小数点
//
final formatted = d.toStringAsFixed(2);
//
final parts = formatted.split('.');
//
if (parts.length == 2 && parts[1] == '00') return parts[0];
//
return formatted.replaceAll(RegExp(r'0+$'), '').replaceAll(RegExp(r'\.$'), '');
//
}
Widget
_buildParamCard
(
String
value
,
String
label
,
String
unit
,
{
bool
isGreen
=
false
})
{
return
Container
(
...
...
@@ -81,7 +82,7 @@ class DeviceParmsBlock extends StatelessWidget {
TextSpan
(
children:
[
TextSpan
(
text:
_formatValue
(
value
),
text:
DealUtils
.
cleanNumber
(
value
),
style:
TextStyle
(
fontSize:
40
.
sp
,
color:
isGreen
?
const
Color
.
fromRGBO
(
20
,
184
,
166
,
1
)
...
...
lib/views/home/device/widget/power_char_block.dart
View file @
7566c742
...
...
@@ -4,17 +4,17 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
import
'package:fl_chart/fl_chart.dart'
;
import
'package:smart_hotel_app/views/home/device/cubit/device_state.dart'
;
class
PowerCharBlock
extends
State
ful
Widget
{
class
PowerCharBlock
extends
State
less
Widget
{
final
DeviceInfo
deviceInfo
;
final
int
selectedTabIndex
;
final
ValueChanged
<
int
>
onTabChanged
;
const
PowerCharBlock
({
super
.
key
,
required
this
.
deviceInfo
});
@override
State
<
PowerCharBlock
>
createState
()
=>
_PowerCharBlockState
();
}
class
_PowerCharBlockState
extends
State
<
PowerCharBlock
>
{
int
_selectedIndex
=
0
;
const
PowerCharBlock
({
super
.
key
,
required
this
.
deviceInfo
,
required
this
.
selectedTabIndex
,
required
this
.
onTabChanged
,
});
@override
Widget
build
(
BuildContext
context
)
{
...
...
@@ -61,17 +61,17 @@ class _PowerCharBlockState extends State<PowerCharBlock> {
SizedBox
(
height:
250
.
h
,
child:
BarChart
(
BarChartData
(
alignment:
BarChartAlignment
.
spaceBetween
,
maxY:
_calcMaxY
(
_selected
Index
),
minY:
0
,
groupsSpace:
10
,
BarChartData
(
alignment:
BarChartAlignment
.
spaceBetween
,
maxY:
_calcMaxY
(
selectedTab
Index
),
minY:
0
,
groupsSpace:
10
,
barTouchData:
BarTouchData
(
enabled:
false
),
gridData:
FlGridData
(
show:
true
,
drawHorizontalLine:
true
,
drawVerticalLine:
false
,
horizontalInterval:
_calcInterval
(
_selected
Index
),
horizontalInterval:
_calcInterval
(
selectedTab
Index
),
getDrawingHorizontalLine:
(
value
)
{
return
const
FlLine
(
color:
Color
.
fromRGBO
(
209
,
213
,
219
,
1
),
...
...
@@ -82,12 +82,14 @@ class _PowerCharBlockState extends State<PowerCharBlock> {
),
titlesData:
FlTitlesData
(
show:
true
,
rightTitles:
const
AxisTitles
(
sideTitles:
SideTitles
(
showTitles:
false
)),
topTitles:
const
AxisTitles
(
sideTitles:
SideTitles
(
showTitles:
false
)),
rightTitles:
const
AxisTitles
(
sideTitles:
SideTitles
(
showTitles:
false
)),
topTitles:
const
AxisTitles
(
sideTitles:
SideTitles
(
showTitles:
false
)),
leftTitles:
AxisTitles
(
sideTitles:
SideTitles
(
showTitles:
true
,
interval:
_calcInterval
(
_selected
Index
),
interval:
_calcInterval
(
selectedTab
Index
),
getTitlesWidget:
(
value
,
meta
)
{
return
Text
(
'
${value.toInt()}
'
,
...
...
@@ -103,15 +105,20 @@ class _PowerCharBlockState extends State<PowerCharBlock> {
bottomTitles:
AxisTitles
(
sideTitles:
SideTitles
(
showTitles:
true
,
interval:
_calcBottomInterval
()
,
interval:
1
,
getTitlesWidget:
(
value
,
meta
)
{
final
i
=
value
.
toInt
();
final
labels
=
widget
.
deviceInfo
.
timeLabels
;
final
label
=
i
>=
0
&&
i
<
labels
.
length
?
labels
[
i
]
:
''
;
final
labels
=
deviceInfo
.
timeLabels
;
if
(
i
<
0
||
i
>=
labels
.
length
)
{
return
const
SizedBox
.
shrink
();
}
if
(!
_visibleLabelIndices
.
contains
(
i
))
{
return
const
SizedBox
.
shrink
();
}
return
Padding
(
padding:
EdgeInsets
.
only
(
top:
8
.
h
),
child:
Text
(
label
,
label
s
[
i
]
,
style:
TextStyle
(
color:
const
Color
.
fromRGBO
(
156
,
163
,
175
,
1
),
fontSize:
20
.
sp
,
...
...
@@ -124,7 +131,7 @@ class _PowerCharBlockState extends State<PowerCharBlock> {
),
),
borderData:
FlBorderData
(
show:
false
),
barGroups:
_buildBarGroups
(
_selectedIndex
),
barGroups:
_buildBarGroups
(),
),
),
),
...
...
@@ -134,81 +141,99 @@ class _PowerCharBlockState extends State<PowerCharBlock> {
}
Widget
_buildTab
(
String
title
,
int
index
)
{
final
isSelected
=
_selected
Index
==
index
;
final
isSelected
=
selectedTab
Index
==
index
;
return
GestureDetector
(
onTap:
()
{
setState
(()
{
_selectedIndex
=
index
;
});
},
onTap:
()
=>
onTabChanged
(
index
),
child:
Container
(
padding:
EdgeInsets
.
symmetric
(
horizontal:
16
.
w
,
vertical:
8
.
h
),
decoration:
BoxDecoration
(
borderRadius:
BorderRadius
.
circular
(
8
.
r
),
color:
isSelected
?
const
Color
.
fromRGBO
(
219
,
234
,
254
,
1
)
:
Colors
.
transparent
,
color:
isSelected
?
const
Color
.
fromRGBO
(
219
,
234
,
254
,
1
)
:
Colors
.
transparent
,
),
child:
Text
(
title
,
style:
TextStyle
(
fontSize:
24
.
sp
,
color:
isSelected
?
const
Color
.
fromRGBO
(
73
,
149
,
234
,
1
)
:
const
Color
.
fromRGBO
(
100
,
116
,
139
,
1
),
color:
isSelected
?
const
Color
.
fromRGBO
(
73
,
149
,
234
,
1
)
:
const
Color
.
fromRGBO
(
100
,
116
,
139
,
1
),
),
),
),
);
}
List
<
BarChartGroupData
>
_buildBarGroups
(
int
index
)
{
final
data
=
index
==
0
?
widget
.
deviceInfo
.
powerData
:
widget
.
deviceInfo
.
tempData
;
final
Color
mainColor
=
index
==
0
?
const
Color
.
fromRGBO
(
59
,
130
,
246
,
1
)
List
<
BarChartGroupData
>
_buildBarGroups
()
{
final
data
=
selectedTabIndex
==
0
?
deviceInfo
.
powerData
:
deviceInfo
.
tempData
;
final
Color
mainColor
=
selectedTabIndex
==
0
?
const
Color
.
fromRGBO
(
80
,
162
,
255
,
1
)
:
const
Color
.
fromRGBO
(
251
,
191
,
36
,
1
);
return
List
.
generate
(
data
.
length
,
(
i
)
{
if
(
data
[
i
]
==
0
)
return
BarChartGroupData
(
x:
i
);
return
BarChartGroupData
(
x:
i
,
barRods:
[
BarChartRodData
(
toY:
data
[
i
].
toDouble
(),
color:
mainColor
,
width:
22
.
w
,
borderRadius:
BorderRadius
.
vertical
(
top:
Radius
.
circular
(
8
.
r
)),
),
],
);
},
).
where
((
group
)
=>
group
.
barRods
.
isNotEmpty
).
toList
();
return
List
.
generate
(
data
.
length
,
(
i
)
{
if
(
data
[
i
]
==
0
)
return
BarChartGroupData
(
x:
i
);
return
BarChartGroupData
(
x:
i
,
barRods:
[
BarChartRodData
(
toY:
data
[
i
].
toDouble
(),
color:
mainColor
,
width:
16
.
w
,
borderRadius:
BorderRadius
.
vertical
(
top:
Radius
.
circular
(
8
.
r
)),
),
],
);
}).
where
((
group
)
=>
group
.
barRods
.
isNotEmpty
).
toList
();
}
double
_calcMaxY
(
int
index
)
{
final
data
=
index
==
0
?
widget
.
deviceInfo
.
powerData
:
widget
.
deviceInfo
.
tempData
;
final
data
=
index
==
0
?
deviceInfo
.
powerData
:
deviceInfo
.
tempData
;
if
(
data
.
isEmpty
)
return
100
;
final
max
=
data
.
reduce
((
a
,
b
)
=>
a
>
b
?
a
:
b
).
toDouble
();
if
(
max
==
0
)
return
100
;
// 向上取整到最近的 nice number,并留 20% 余量
final
raw
=
max
*
1.2
;
final
magnitude
=
pow
(
10
,
(
log
(
raw
)
/
ln10
).
floor
()).
toDouble
();
final
normalized
=
raw
/
magnitude
;
final
nice
=
normalized
<=
1
?
1
:
normalized
<=
2
?
2
:
normalized
<=
5
?
5
:
10
;
final
nice
=
normalized
<=
1
?
1
:
normalized
<=
2
?
2
:
normalized
<=
5
?
5
:
10
;
return
nice
*
magnitude
;
}
Set
<
int
>
get
_visibleLabelIndices
{
final
length
=
deviceInfo
.
timeLabels
.
length
;
if
(
length
<=
10
)
{
return
Set
.
from
(
List
.
generate
(
length
,
(
i
)
=>
i
));
}
final
isOdd
=
length
%
2
==
1
;
if
(
isOdd
)
{
// 基数:7 个标签,首尾都显示,中间 5 个均分 6 段
final
step
=
(
length
-
1
)
/
6
;
final
indices
=
<
int
>{};
for
(
int
i
=
0
;
i
<=
6
;
i
++)
{
indices
.
add
((
step
*
i
).
round
());
}
return
indices
;
}
else
{
// 偶数:6 个标签,显示首和倒数第二个,中间 4 个均分 5 段
final
step
=
(
length
-
2
)
/
5
;
final
indices
=
<
int
>{};
for
(
int
i
=
0
;
i
<=
5
;
i
++)
{
indices
.
add
((
step
*
i
).
round
());
}
return
indices
;
}
}
double
_calcInterval
(
int
index
)
{
return
_calcMaxY
(
index
)
/
5
;
}
double
_calcBottomInterval
()
{
final
length
=
widget
.
deviceInfo
.
timeLabels
.
length
;
if
(
length
<=
6
)
return
1
;
if
(
length
<=
12
)
return
2
;
if
(
length
<=
24
)
return
3
;
return
(
length
/
6
).
ceilToDouble
();
}
}
\ No newline at end of file
lib/views/home/index/cubit/home_index_cubit.dart
View file @
7566c742
...
...
@@ -5,6 +5,7 @@ import 'package:smart_hotel_app/repositories/alert_dashboard_repository.dart';
import
'package:smart_hotel_app/repositories/alert_detail_repository.dart'
;
import
'package:smart_hotel_app/services/alert_dashboard_service.dart'
;
import
'package:smart_hotel_app/services/alert_detail_service.dart'
;
import
'package:smart_hotel_app/utils/deal_utils.dart'
;
import
'package:smart_hotel_app/views/home/index/cubit/home_index_state.dart'
;
class
HomeIndexCubit
extends
Cubit
<
HomeIndexState
>
{
...
...
@@ -73,8 +74,9 @@ class HomeIndexCubit extends Cubit<HomeIndexState> {
icon:
_mapDeviceIcon
(
d
.
deviceTypeIcon
),
title:
d
.
deviceName
,
subtitle:
_buildDeviceSubtitle
(
d
),
status:
d
.
onlineStatus
==
'1'
?
d
.
runStatusText
:
'离线'
,
statusColor:
_mapStatusColor
(
d
.
onlineStatus
,
d
.
runStatus
),
// status: d.onlineStatus == '1' ? d.runStatusText : '离线',
status:
d
.
runStatusText
,
statusColor:
_mapStatusColor
(
d
.
runStatus
),
);
}
...
...
@@ -94,28 +96,26 @@ class HomeIndexCubit extends Cubit<HomeIndexState> {
}
String
_buildDeviceSubtitle
(
DeviceItemBO
d
)
{
final
parts
=
<
String
>[];
if
(
d
.
roomName
.
isNotEmpty
)
parts
.
add
(
d
.
roomName
);
if
(
d
.
temperature
.
isNotEmpty
)
parts
.
add
(
'
${d.temperature}
°C'
);
if
(
d
.
voltage
.
isNotEmpty
)
parts
.
add
(
'
${d.voltage}
V'
);
if
(
d
.
power
.
isNotEmpty
)
parts
.
add
(
'
${d.power}
kW'
);
if
(
d
.
current
.
isNotEmpty
)
parts
.
add
(
'
${d.current}
A'
);
return
parts
.
isNotEmpty
?
parts
.
join
(
'·'
)
:
''
;
return
[
if
(
d
.
roomName
.
isNotEmpty
)
d
.
roomName
,
if
(
d
.
temperature
.
isNotEmpty
)
'
${DealUtils.cleanNumber(d.temperature)}
°C'
,
if
(
d
.
voltage
.
isNotEmpty
)
'
${DealUtils.cleanNumber(d.voltage)}
V'
,
if
(
d
.
power
.
isNotEmpty
)
'
${DealUtils.cleanNumber(d.power)}
kW'
,
if
(
d
.
current
.
isNotEmpty
)
'
${DealUtils.cleanNumber(d.current)}
A'
,
].
join
(
'·'
)
;
}
Color
_mapStatusColor
(
String
onlineStatus
,
String
runStatus
)
{
if
(
onlineStatus
==
'0'
)
{
return
const
Color
.
fromRGBO
(
102
,
102
,
102
,
1.0
);
}
Color
_mapStatusColor
(
String
runStatus
)
{
switch
(
runStatus
)
{
case
'alert'
:
case
'alarm'
:
return
const
Color
.
fromRGBO
(
255
,
77
,
79
,
1.0
);
case
'normal'
:
return
const
Color
.
fromRGBO
(
20
,
184
,
166
,
1.0
);
case
'warning'
:
case
'注意'
:
return
const
Color
.
fromRGBO
(
255
,
193
,
7
,
1.0
);
return
const
Color
.
fromRGBO
(
250
,
204
,
21
,
1.0
);
case
'fault'
:
return
const
Color
.
fromRGBO
(
255
,
77
,
79
,
1.0
);
case
'offline'
:
default
:
return
const
Color
.
fromRGBO
(
26
,
188
,
156
,
1.0
);
return
const
Color
.
fromRGBO
(
102
,
102
,
102
,
1.0
);
}
}
}
lib/views/home/index/index_view.dart
View file @
7566c742
...
...
@@ -20,26 +20,30 @@ class HomeView extends StatefulWidget {
}
class
_HomeViewState
extends
State
<
HomeView
>
with
AutoRouteAwareStateMixin
<
HomeView
>
{
TabsRouter
?
_tabsRouter
;
@override
void
didChangeDependencies
()
{
super
.
didChangeDependencies
();
// Subscribe to tab router events
final
tabsRouter
=
AutoTabsRouter
.
of
(
context
);
tabsRouter
.
addListener
(
_onTabChange
);
// 缓存引用,避免在 dispose 中通过 context 查找(此时树已不稳定)
final
newRouter
=
AutoTabsRouter
.
of
(
context
);
if
(
newRouter
!=
_tabsRouter
)
{
_tabsRouter
?.
removeListener
(
_onTabChange
);
_tabsRouter
=
newRouter
;
_tabsRouter
?.
addListener
(
_onTabChange
);
}
}
@override
void
dispose
()
{
final
tabsRouter
=
AutoTabsRouter
.
of
(
context
);
tabsRouter
.
removeListener
(
_onTabChange
);
_tabsRouter
?.
removeListener
(
_onTabChange
);
super
.
dispose
();
}
void
_onTabChange
()
{
final
tabsRouter
=
AutoTabsRouter
.
of
(
context
);
// Check if this tab is now active
if
(
tabsRouter
.
activeIndex
==
0
)
{
// Reload data when tab is activated
if
(!
mounted
)
return
;
if
(
_tabsRouter
?.
activeIndex
==
0
)
{
context
.
read
<
HomeIndexCubit
>().
reloadDashboard
();
}
}
...
...
@@ -88,7 +92,10 @@ class _HomeViewState extends State<HomeView> with AutoRouteAwareStateMixin<HomeV
List
<
Widget
>
_buildAlertBlocks
(
List
<
AlertItemBO
>
alerts
)
{
final
blocks
=
<
Widget
>[];
for
(
final
alert
in
alerts
)
{
final
location
=
'
${alert.roomName}
·
${alert.deviceName}
'
;
final
location
=
[
alert
.
roomName
,
alert
.
deviceName
]
.
where
((
s
)
=>
s
.
isNotEmpty
)
.
join
(
'·'
);
switch
(
alert
.
alertCategory
)
{
case
'temperature'
:
blocks
.
addAll
([
...
...
lib/views/home/index/widget/temperature_block.dart
View file @
7566c742
...
...
@@ -107,7 +107,7 @@ class TemperatureBlock extends StatelessWidget {
mainAxisSize:
MainAxisSize
.
min
,
children:
[
Text
(
currentTemperature
,
currentTemperature
.
isNotEmpty
?
currentTemperature
:
'--℃'
,
style:
TextStyle
(
fontSize:
40
.
sp
,
fontWeight:
FontWeight
.
bold
,
...
...
@@ -130,7 +130,7 @@ class TemperatureBlock extends StatelessWidget {
mainAxisSize:
MainAxisSize
.
min
,
children:
[
Text
(
currentVoltage
,
currentVoltage
.
isNotEmpty
?
currentVoltage
:
'--V'
,
style:
TextStyle
(
fontSize:
40
.
sp
,
fontWeight:
FontWeight
.
bold
,
...
...
@@ -153,7 +153,7 @@ class TemperatureBlock extends StatelessWidget {
mainAxisSize:
MainAxisSize
.
min
,
children:
[
Text
(
currentCurrent
,
currentCurrent
.
isNotEmpty
?
currentCurrent
:
'--A'
,
style:
TextStyle
(
fontSize:
40
.
sp
,
fontWeight:
FontWeight
.
bold
,
...
...
lib/views/home/index/widget/voltage_operate_block.dart
View file @
7566c742
...
...
@@ -177,7 +177,6 @@ class VoltageOperateBlock extends StatelessWidget {
borderRadius:
BorderRadius
.
circular
(
16
.
r
),
),
child:
Container
(
padding:
EdgeInsets
.
all
(
24
.
w
),
decoration:
BoxDecoration
(
color:
Colors
.
white
,
borderRadius:
BorderRadius
.
circular
(
16
.
r
),
...
...
@@ -186,33 +185,42 @@ class VoltageOperateBlock extends StatelessWidget {
mainAxisSize:
MainAxisSize
.
min
,
children:
[
Row
(
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
mainAxisAlignment:
MainAxisAlignment
.
end
,
children:
[
Text
(
voltageAlarmTitle
,
style:
TextStyle
(
fontSize:
30
.
sp
,
fontWeight:
FontWeight
.
bold
,
color:
Colors
.
black
,
),
),
GestureDetector
(
onTap:
()
=>
Navigator
.
of
(
dialogContext
).
pop
(),
child:
Icon
(
Icons
.
close
,
color:
const
Color
.
fromRGBO
(
100
,
116
,
139
,
1.0
),
size:
32
.
sp
,
),
onTap:
()
=>
Navigator
.
of
(
dialogContext
).
pop
(),
child:
Container
(
margin:
EdgeInsets
.
only
(
top:
20
.
h
,
right:
20
.
h
),
padding:
EdgeInsets
.
symmetric
(
horizontal:
2
.
w
,
vertical:
2
.
h
),
decoration:
BoxDecoration
(
shape:
BoxShape
.
circle
,
border:
Border
.
all
(
width:
2
.
w
,
color:
const
Color
.
fromRGBO
(
100
,
116
,
139
,
1.0
),
),
),
child:
Icon
(
Icons
.
close
,
color:
const
Color
.
fromRGBO
(
100
,
116
,
139
,
1.0
),
size:
32
.
sp
,
),
)
),
],
),
SizedBox
(
height:
16
.
h
),
Text
(
voltageAlarmTitle
,
style:
TextStyle
(
fontSize:
30
.
sp
,
fontWeight:
FontWeight
.
bold
,
color:
Colors
.
black
,
),
),
SizedBox
(
height:
20
.
h
),
Container
(
width:
double
.
infinity
,
padding:
EdgeInsets
.
symmetric
(
horizontal:
20
.
w
,
vertical:
20
.
h
,
),
margin:
EdgeInsets
.
symmetric
(
horizontal:
40
.
w
),
padding:
EdgeInsets
.
symmetric
(
horizontal:
30
.
w
,
vertical:
30
.
h
),
decoration:
BoxDecoration
(
color:
const
Color
.
fromRGBO
(
242
,
243
,
245
,
1
),
borderRadius:
BorderRadius
.
circular
(
12
.
r
),
...
...
@@ -230,7 +238,7 @@ class VoltageOperateBlock extends StatelessWidget {
),
SizedBox
(
height:
12
.
h
),
Text
(
'1.检查
输入电压是否正常;
\n
2.确认设备是否正常运行
;
\n
3.若异常,请联系电工。'
,
'1.检查
设备是否通风良好;
\n
2.确认设备负载是否正常
;
\n
3.若异常,请联系电工。'
,
style:
TextStyle
(
fontSize:
26
.
sp
,
color:
const
Color
.
fromRGBO
(
100
,
116
,
139
,
1.0
),
...
...
@@ -240,8 +248,8 @@ class VoltageOperateBlock extends StatelessWidget {
],
),
),
SizedBox
(
height:
20
.
h
),
GestureDetector
(
behavior:
HitTestBehavior
.
opaque
,
onTap:
()
async
{
Navigator
.
of
(
dialogContext
).
pop
();
try
{
...
...
@@ -256,19 +264,13 @@ class VoltageOperateBlock extends StatelessWidget {
}
},
child:
Container
(
width:
double
.
infinity
,
height:
72
.
h
,
decoration:
BoxDecoration
(
color:
const
Color
.
fromRGBO
(
66
,
165
,
245
,
1.0
),
borderRadius:
BorderRadius
.
circular
(
12
.
r
),
),
child:
Center
(
child:
Text
(
'确认'
,
style:
TextStyle
(
fontSize:
28
.
sp
,
color:
Colors
.
white
,
),
padding:
EdgeInsets
.
symmetric
(
horizontal:
30
.
w
,
vertical:
30
.
h
),
child:
Text
(
'确认'
,
style:
TextStyle
(
fontSize:
28
.
sp
,
color:
const
Color
.
fromRGBO
(
73
,
149
,
234
,
1.0
),
fontWeight:
FontWeight
.
bold
,
),
),
),
...
...
lib/views/home/inspection_device/widget/device_overview_card.dart
View file @
7566c742
...
...
@@ -133,7 +133,7 @@ class DeviceOverviewCard extends StatelessWidget {
),
),
Text
(
value
,
value
.
isNotEmpty
?
value
:
'--'
,
style:
TextStyle
(
fontSize:
28
.
sp
,
color:
const
Color
.
fromRGBO
(
100
,
116
,
139
,
1.0
),
...
...
lib/views/home/inspection_topology/cubit/inspection_topology_cubit.dart
View file @
7566c742
...
...
@@ -37,33 +37,28 @@ class InspectionTopologyCubit extends Cubit<InspectionTopologyState> {
void
_emitFromTopology
(
DeviceTopologyBO
topology
)
{
final
info
=
topology
.
deviceInfo
;
final
nodes
=
topology
.
treeNodes
;
final
rootNode
=
_buildTopologyTree
(
nodes
);
final
treeNodes
=
topology
.
treeNodes
;
if
(
treeNodes
.
isEmpty
)
{
emit
(
InspectionTopologyState
(
deviceName:
info
.
deviceName
,
deviceModel:
'
${info.deviceCode}
·
${info.deviceLocation}
'
,
));
return
;
}
final
rootNode
=
_buildTopologyTree
(
treeNodes
);
emit
(
InspectionTopologyState
(
deviceName:
info
.
deviceName
,
deviceModel:
'
${info.deviceCode}
·
${info.deviceLocation}
'
,
topologyNodes:
[
rootNode
],
deviceCount:
_countAllNodes
(
rootNode
),
connectionCount:
_countConnections
(
rootNode
),
// statusText:
));
}
/// 根据 gatewayId 将扁平节点列表构建为树结构,并自动计算布局位置
TopologyNode
_buildTopologyTree
(
List
<
TreeNodeBO
>
nodes
)
{
if
(
nodes
.
isEmpty
)
{
return
const
TopologyNode
(
id:
'0'
,
name:
'未知设备'
,
status:
'offline'
,
children:
[],
x:
0.5
,
y:
0.08
,
);
}
// 构建子节点映射表:parent deviceId -> [child TreeNodeBO]
final
childrenMap
=
<
int
,
List
<
TreeNodeBO
>>{};
final
nodeMap
=
<
int
,
TreeNodeBO
>{};
...
...
lib/views/home/inspection_topology/inspection_topology_view.dart
View file @
7566c742
...
...
@@ -55,7 +55,7 @@ class InspectionTopologyPage extends StatelessWidget {
builder:
(
context
,
state
)
{
if
(
state
.
isLoading
)
{
return
const
Center
(
child:
CircularProgressIndicator
(
color:
const
Color
.
fromRGBO
(
66
,
165
,
245
,
1.0
)),
child:
CircularProgressIndicator
(
color:
Color
.
fromRGBO
(
66
,
165
,
245
,
1.0
)),
);
}
...
...
@@ -124,7 +124,7 @@ class InspectionTopologyPage extends StatelessWidget {
if
(
state
.
selectedNode
!=
null
)
DeviceDetailDialog
(
node:
state
.
selectedNode
!,
curentState:
state
,
cur
r
entState:
state
,
onClose:
()
=>
context
.
read
<
InspectionTopologyCubit
>().
selectNode
(
null
),
),
...
...
lib/views/home/inspection_topology/widget/device_detail_dialog.dart
View file @
7566c742
...
...
@@ -6,13 +6,13 @@ import '../cubit/inspection_topology_state.dart';
class
DeviceDetailDialog
extends
StatelessWidget
{
final
TopologyNode
node
;
final
InspectionTopologyState
curentState
;
final
InspectionTopologyState
cur
r
entState
;
final
VoidCallback
onClose
;
const
DeviceDetailDialog
({
super
.
key
,
required
this
.
node
,
required
this
.
curentState
,
required
this
.
cur
r
entState
,
required
this
.
onClose
,
});
...
...
@@ -26,7 +26,7 @@ class DeviceDetailDialog extends StatelessWidget {
child:
GestureDetector
(
onTap:
onClose
,
child:
Container
(
color:
Colors
.
black
.
with
Opacity
(
0.5
),
color:
Colors
.
black
.
with
Values
(
alpha:
0.5
),
),
),
),
...
...
@@ -91,7 +91,7 @@ class DeviceDetailDialog extends StatelessWidget {
),
SizedBox
(
height:
12
.
h
),
Text
(
curentState
.
deviceModel
,
cur
r
entState
.
deviceModel
,
style:
TextStyle
(
fontSize:
24
.
sp
,
color:
const
Color
.
fromRGBO
(
148
,
163
,
184
,
1
),
...
...
lib/views/login/cubit/login_cubit.dart
View file @
7566c742
import
'dart:async'
;
import
'package:flutter_bloc/flutter_bloc.dart'
;
import
'package:smart_hotel_app/blocs/auth/auth_bloc.dart'
;
import
'package:smart_hotel_app/blocs/auth/auth_event.dart'
;
...
...
@@ -8,6 +10,7 @@ import 'package:smart_hotel_app/views/login/cubit/login_state.dart';
class
LoginCubit
extends
Cubit
<
LoginState
>
{
final
AuthBloc
authBloc
;
final
StorageService
storageService
;
late
final
StreamSubscription
_authSubscription
;
LoginCubit
({
required
this
.
authBloc
,
...
...
@@ -17,18 +20,32 @@ class LoginCubit extends Cubit<LoginState> {
}
void
_listenAuthState
()
{
authBloc
.
stream
.
listen
((
authState
)
{
if
(
authState
is
AuthFailure
)
{
emit
(
state
.
copyWith
(
isLoading:
false
,
error:
authState
.
error
));
}
else
if
(
authState
is
AuthLoading
)
{
emit
(
state
.
copyWith
(
isLoading:
true
,
error:
null
));
}
else
if
(
authState
is
AuthSuccess
)
{
emit
(
state
.
copyWith
(
isLoading:
false
,
error:
null
));
}
});
_authSubscription
=
authBloc
.
stream
.
listen
(
_onAuthStateChanged
,
onError:
_onAuthError
,
);
}
void
_onAuthStateChanged
(
AuthState
authState
)
{
// 检查 Cubit 是否已关闭
if
(
isClosed
)
return
;
if
(
authState
is
AuthFailure
)
{
emit
(
state
.
copyWith
(
isLoading:
false
,
error:
authState
.
error
));
}
else
if
(
authState
is
AuthLoading
)
{
emit
(
state
.
copyWith
(
isLoading:
true
,
error:
null
));
}
else
if
(
authState
is
AuthSuccess
)
{
emit
(
state
.
copyWith
(
isLoading:
false
,
error:
null
));
}
}
void
_onAuthError
(
error
)
{
if
(
isClosed
)
return
;
emit
(
state
.
copyWith
(
isLoading:
false
,
error:
error
.
toString
()));
}
void
toggleRememberPassword
(
bool
value
)
{
if
(
isClosed
)
return
;
emit
(
state
.
copyWith
(
rememberPassword:
value
));
}
...
...
@@ -53,6 +70,8 @@ class LoginCubit extends Cubit<LoginState> {
}
Future
<
void
>
loadRememberCredentials
()
async
{
if
(
isClosed
)
return
;
final
credentials
=
await
storageService
.
getRememberCredentials
();
if
(
credentials
[
'username'
]
!=
null
)
{
emit
(
state
.
copyWith
(
...
...
@@ -66,4 +85,11 @@ class LoginCubit extends Cubit<LoginState> {
void
forgotPwd
()
{
// TODO: 跳转到忘记密码页面
}
@override
Future
<
void
>
close
()
{
_authSubscription
.
cancel
();
return
super
.
close
();
}
}
\ No newline at end of file
lib/views/login/widget/btn_login.dart
View file @
7566c742
...
...
@@ -17,7 +17,7 @@ class BtnLogin extends StatelessWidget {
width:
double
.
infinity
,
height:
104
.
h
,
decoration:
BoxDecoration
(
color:
Color
.
fromRGBO
(
73
,
149
,
234
,
1
),
color:
const
Color
.
fromRGBO
(
73
,
149
,
234
,
1
),
borderRadius:
BorderRadius
.
circular
(
40
.
w
),
),
child:
ElevatedButton
(
...
...
lib/views/profile/about/about_view.dart
0 → 100644
View file @
7566c742
import
'package:auto_route/auto_route.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_screenutil/flutter_screenutil.dart'
;
@RoutePage
()
class
AboutView
extends
StatelessWidget
{
const
AboutView
({
super
.
key
});
Widget
_buildRichInfo
()
{
TextStyle
highlightStyle
=
TextStyle
(
fontSize:
30
.
sp
,
color:
const
Color
.
fromRGBO
(
15
,
23
,
42
,
1
),
fontWeight:
FontWeight
.
w600
,
);
return
RichText
(
text:
TextSpan
(
style:
TextStyle
(
fontSize:
30
.
sp
,
color:
const
Color
.
fromRGBO
(
100
,
116
,
139
,
1
),
),
children:
[
TextSpan
(
text:
'湖北洲语科技有限公司'
,
style:
highlightStyle
),
const
TextSpan
(
text:
'成立于2020年06月30日,'
'注册地位于武汉市汉阳区龙阳大道98号武汉惠誉大厦6层(1)商号'
),
TextSpan
(
text:
'601-603'
,
style:
highlightStyle
),
const
TextSpan
(
text:
',''法定代表人为'
),
TextSpan
(
text:
'严高波'
,
style:
highlightStyle
),
const
TextSpan
(
text:
'。经营范围包括光电、信息、网络技术开发、技术咨询、'
'技术服务、技术转让;电子产品、通讯设施(不含无线发射装置及卫星地面接收装置)、'
'光机电一体化设备技术研发、批发、零售;数据处理及存储服务;计算机软硬件技术开发、'
'技术咨询、技术服务;计算机系统集成;商务信息咨询(不含商务调查);'
'企业管理咨询;市场营销策划;策划创意服务;展示展览服务;会议会展服务;'
'文化艺术交流活动策划;货物或技术进出口(国家禁止或涉及行政审批的货物和技术进出口除外);'
'对农业项目的投资;对生态旅游项目的投资;安防设备、电子产品安装;'
'网络工程、弱电工程、通信工程、楼宇智能化工程、道路工程、园林绿化工程、建筑工程施工;'
'为建筑工程提供劳务服务(涉及许可经营项目,应取得相关部门许可后方可经营)。'
),
],
),
);
}
@override
Widget
build
(
BuildContext
context
)
{
return
Scaffold
(
backgroundColor:
const
Color
.
fromRGBO
(
242
,
243
,
245
,
1
),
appBar:
AppBar
(
backgroundColor:
Colors
.
white
,
elevation:
0
,
leading:
IconButton
(
icon:
const
Icon
(
Icons
.
arrow_back_ios
,
color:
Color
.
fromRGBO
(
100
,
116
,
139
,
1
)),
onPressed:
()
=>
context
.
maybePop
(),
),
title:
Text
(
'关于'
,
style:
TextStyle
(
color:
const
Color
.
fromRGBO
(
15
,
23
,
42
,
1
),
fontSize:
32
.
sp
,
fontWeight:
FontWeight
.
w600
,
),
),
centerTitle:
true
,
),
body:
SingleChildScrollView
(
padding:
EdgeInsets
.
all
(
28
.
w
),
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
Container
(
width:
double
.
infinity
,
padding:
EdgeInsets
.
all
(
28
.
w
),
decoration:
BoxDecoration
(
color:
Colors
.
white
,
borderRadius:
BorderRadius
.
circular
(
24
.
r
),
),
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
Text
(
'公司简介'
,
style:
TextStyle
(
fontSize:
30
.
sp
,
fontWeight:
FontWeight
.
w600
,
color:
const
Color
.
fromRGBO
(
15
,
23
,
42
,
1
),
),
),
SizedBox
(
height:
20
.
h
),
_buildRichInfo
(),
],
),
),
],
),
),
);
}
}
\ No newline at end of file
lib/views/profile/index/cubit/profile_cubit.dart
View file @
7566c742
...
...
@@ -28,7 +28,7 @@ class ProfileCubit extends Cubit<ProfileState> {
const
MenuItem
(
icon:
'lib/assets/icon/help_bg.png'
,
title:
'帮助'
),
const
MenuItem
(
icon:
'lib/assets/icon/about_bg.png'
,
title:
'关于'
),
const
MenuItem
(
icon:
'lib/assets/icon/exit_login_bg.png'
,
title:
'退出登录'
),
const
MenuItem
(
icon:
'lib/assets/icon/logout_account_bg.png'
,
title:
'注销账号'
),
//
const MenuItem(icon: 'lib/assets/icon/logout_account_bg.png', title: '注销账号'),
],
));
}
...
...
lib/views/profile/index/index_view.dart
View file @
7566c742
...
...
@@ -55,8 +55,7 @@ class _ProfileContent extends StatelessWidget {
child:
ProfileMenuBlock
(
menuList:
state
.
menuList
,
onLogout:
()
{
context
.
read
<
AuthBloc
>()
.
add
(
const
AuthLogoutRequestedEvent
());
_showHandleDialog
(
context
);
},
),
),
...
...
@@ -70,4 +69,100 @@ class _ProfileContent extends StatelessWidget {
},
);
}
void
_showHandleDialog
(
BuildContext
context
)
{
showDialog
(
context:
context
,
barrierDismissible:
true
,
builder:
(
dialogContext
)
{
return
Dialog
(
shape:
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
circular
(
24
.
r
),
),
child:
Container
(
padding:
EdgeInsets
.
symmetric
(
horizontal:
28
.
w
,
vertical:
28
.
h
),
decoration:
BoxDecoration
(
color:
Colors
.
white
,
borderRadius:
BorderRadius
.
circular
(
24
.
r
),
),
child:
Column
(
mainAxisSize:
MainAxisSize
.
min
,
children:
[
Text
(
'温馨提示'
,
style:
TextStyle
(
fontSize:
34
.
sp
,
fontWeight:
FontWeight
.
bold
,
color:
Colors
.
black
,
),
),
SizedBox
(
height:
30
.
h
),
Text
(
'确定退出登录吗?'
,
style:
TextStyle
(
fontSize:
32
.
sp
,
color:
Colors
.
black
,
),
),
SizedBox
(
height:
50
.
h
),
Row
(
spacing:
28
.
w
,
children:
[
Expanded
(
child:
GestureDetector
(
behavior:
HitTestBehavior
.
opaque
,
onTap:
()
{
Navigator
.
of
(
dialogContext
).
pop
();
},
child:
Container
(
height:
80
.
h
,
alignment:
Alignment
.
center
,
decoration:
BoxDecoration
(
color:
const
Color
.
fromRGBO
(
229
,
241
,
255
,
1
),
borderRadius:
BorderRadius
.
circular
(
16
.
r
),
),
child:
Text
(
'取消'
,
style:
TextStyle
(
fontSize:
32
.
sp
,
color:
const
Color
.
fromRGBO
(
100
,
116
,
139
,
1
),
),
),
),
)
),
Expanded
(
child:
GestureDetector
(
behavior:
HitTestBehavior
.
opaque
,
onTap:
()
{
Navigator
.
of
(
dialogContext
).
pop
();
context
.
read
<
AuthBloc
>().
add
(
const
AuthLogoutRequestedEvent
());
},
child:
Container
(
height:
80
.
h
,
alignment:
Alignment
.
center
,
decoration:
BoxDecoration
(
color:
const
Color
.
fromRGBO
(
73
,
149
,
234
,
1
),
borderRadius:
BorderRadius
.
circular
(
16
.
r
),
),
child:
Text
(
'确定'
,
style:
TextStyle
(
fontSize:
32
.
sp
,
color:
Colors
.
white
,
),
),
),
)
),
],
),
],
),
),
);
},
);
}
}
lib/views/profile/index/widget/profile_menu_block.dart
View file @
7566c742
...
...
@@ -73,6 +73,11 @@ class ProfileMenuBlock extends StatelessWidget {
case
'告警信息'
:
context
.
pushRoute
(
const
AbnormalListRoute
());
break
;
case
'帮助'
:
break
;
case
'关于'
:
context
.
pushRoute
(
const
AboutRoute
());
break
;
case
'退出登录'
:
onLogout
?.
call
();
break
;
...
...
lib/views/report/index/index_view.dart
View file @
7566c742
...
...
@@ -20,26 +20,29 @@ class ReportIndexView extends StatefulWidget {
}
class
_ReportIndexViewState
extends
State
<
ReportIndexView
>
with
AutoRouteAwareStateMixin
<
ReportIndexView
>
{
TabsRouter
?
_tabsRouter
;
@override
void
didChangeDependencies
()
{
super
.
didChangeDependencies
();
// Subscribe to tab router events
final
tabsRouter
=
AutoTabsRouter
.
of
(
context
);
tabsRouter
.
addListener
(
_onTabChange
);
final
newRouter
=
AutoTabsRouter
.
of
(
context
);
if
(
newRouter
!=
_tabsRouter
)
{
_tabsRouter
?.
removeListener
(
_onTabChange
);
_tabsRouter
=
newRouter
;
_tabsRouter
?.
addListener
(
_onTabChange
);
}
}
@override
void
dispose
()
{
final
tabsRouter
=
AutoTabsRouter
.
of
(
context
);
tabsRouter
.
removeListener
(
_onTabChange
);
_tabsRouter
?.
removeListener
(
_onTabChange
);
super
.
dispose
();
}
void
_onTabChange
()
{
final
tabsRouter
=
AutoTabsRouter
.
of
(
context
);
// Check if this tab is now active (index 2)
if
(
tabsRouter
.
activeIndex
==
2
)
{
// Reload data when tab is activated
if
(!
mounted
)
return
;
if
(
_tabsRouter
?.
activeIndex
==
2
)
{
context
.
read
<
ReportIndexCubit
>().
reloadData
();
}
}
...
...
lib/views/report/index/widget/report_metrics_block.dart
View file @
7566c742
import
'package:flutter/material.dart'
;
import
'package:flutter_screenutil/flutter_screenutil.dart'
;
import
'package:smart_hotel_app/utils/deal_utils.dart'
;
class
ReportMetricsBlock
extends
StatelessWidget
{
final
Map
<
String
,
dynamic
>
reportMetrics
;
...
...
@@ -16,7 +17,7 @@ class ReportMetricsBlock extends StatelessWidget {
Expanded
(
child:
_buildMetricCard
(
title:
'今日用电'
,
value:
powerUsage
[
'value'
]
,
value:
DealUtils
.
cleanNumber
(
powerUsage
[
'value'
]
as
String
)
,
unit:
powerUsage
[
'unit'
]
as
String
,
changeText:
powerUsage
[
'changeText'
]
as
String
,
changeColor:
const
Color
.
fromRGBO
(
20
,
184
,
166
,
1
),
...
...
@@ -86,6 +87,7 @@ class ReportMetricsBlock extends StatelessWidget {
children:
[
Text
(
value
,
// cleanNumber(value),
style:
TextStyle
(
fontSize:
40
.
sp
,
fontWeight:
FontWeight
.
bold
,
...
...
lib/views/report/room/cubit/room_report_cubit.dart
View file @
7566c742
...
...
@@ -31,6 +31,8 @@ class RoomReportCubit extends Cubit<RoomReportState> {
}
void
selectFloor
(
int
index
)
{
if
(
state
.
selectedFloorIndex
==
index
)
return
;
final
floorId
=
state
.
floors
[
index
].
floorId
;
emit
(
state
.
copyWith
(
selectedFloorIndex:
index
));
loadData
(
floorId:
floorId
);
...
...
lib/views/report/room/room_detail_view.dart
View file @
7566c742
...
...
@@ -23,54 +23,6 @@ class ReportRoomDetailView extends StatelessWidget {
),
child:
BlocBuilder
<
RoomReportCubit
,
RoomReportState
>(
builder:
(
context
,
state
)
{
if
(
state
.
isLoading
&&
state
.
floors
.
isEmpty
)
{
return
Scaffold
(
backgroundColor:
const
Color
.
fromRGBO
(
242
,
243
,
245
,
1
),
appBar:
AppBar
(
backgroundColor:
Colors
.
white
,
elevation:
0
,
leading:
IconButton
(
icon:
const
Icon
(
Icons
.
arrow_back_ios
,
color:
Colors
.
black
),
onPressed:
()
=>
context
.
maybePop
(),
),
title:
Text
(
'客房状态总览'
,
style:
TextStyle
(
color:
Colors
.
black
,
fontSize:
32
.
sp
,
fontWeight:
FontWeight
.
bold
,
),
),
centerTitle:
true
,
),
body:
const
Center
(
child:
CircularProgressIndicator
(
color:
Color
.
fromRGBO
(
66
,
165
,
245
,
1.0
))),
);
}
if
(
state
.
error
!=
null
&&
state
.
floors
.
isEmpty
)
{
return
Scaffold
(
backgroundColor:
const
Color
.
fromRGBO
(
242
,
243
,
245
,
1
),
appBar:
AppBar
(
backgroundColor:
Colors
.
white
,
elevation:
0
,
leading:
IconButton
(
icon:
const
Icon
(
Icons
.
arrow_back_ios
,
color:
Colors
.
black
),
onPressed:
()
=>
context
.
maybePop
(),
),
title:
Text
(
'客房状态总览'
,
style:
TextStyle
(
color:
Colors
.
black
,
fontSize:
32
.
sp
,
fontWeight:
FontWeight
.
bold
,
),
),
centerTitle:
true
,
),
body:
Center
(
child:
Text
(
'加载失败:
${state.error}
'
)),
);
}
return
Scaffold
(
backgroundColor:
const
Color
.
fromRGBO
(
242
,
243
,
245
,
1
),
appBar:
AppBar
(
...
...
@@ -78,9 +30,7 @@ class ReportRoomDetailView extends StatelessWidget {
elevation:
0
,
leading:
IconButton
(
icon:
const
Icon
(
Icons
.
arrow_back_ios
,
color:
Colors
.
black
),
onPressed:
()
{
context
.
maybePop
();
},
onPressed:
()
=>
context
.
maybePop
(),
),
title:
Text
(
'客房状态总览'
,
...
...
@@ -92,58 +42,71 @@ class ReportRoomDetailView extends StatelessWidget {
),
centerTitle:
true
,
),
body:
SingleChildScrollView
(
child:
Column
(
children:
[
Padding
(
padding:
EdgeInsets
.
symmetric
(
horizontal:
28
.
w
),
child:
Column
(
children:
[
SizedBox
(
height:
10
.
h
),
if
(
state
.
floors
.
isNotEmpty
)
Container
(
decoration:
BoxDecoration
(
color:
Colors
.
white
,
borderRadius:
BorderRadius
.
vertical
(
top:
Radius
.
circular
(
24
.
r
)),
),
child:
FloorSelector
(
floorNames:
state
.
floors
.
map
((
f
)
=>
f
.
floorName
).
toList
(),
selectedIndex:
state
.
selectedFloorIndex
,
),
),
if
(
state
.
floors
.
isNotEmpty
&&
state
.
selectedFloorIndex
<
state
.
floors
.
length
)
Container
(
decoration:
BoxDecoration
(
color:
Colors
.
white
,
borderRadius:
BorderRadius
.
vertical
(
bottom:
Radius
.
circular
(
24
.
r
)),
),
padding:
EdgeInsets
.
only
(
bottom:
28
.
w
),
child:
RoomGrid
(
rooms:
state
.
roomGrid
),
),
SizedBox
(
height:
10
.
h
),
StatusStats
(
summary:
state
.
summary
),
SizedBox
(
height:
10
.
h
),
if
(
state
.
isLoading
)
const
Padding
(
padding:
EdgeInsets
.
all
(
16.0
),
child:
Center
(
child:
CircularProgressIndicator
(
color:
const
Color
.
fromRGBO
(
66
,
165
,
245
,
1.0
))),
),
...
state
.
detailRooms
.
map
((
room
)
{
return
Padding
(
padding:
EdgeInsets
.
only
(
bottom:
10
.
h
),
child:
RoomDetailCard
(
room:
room
),
);
}).
toList
(),
SizedBox
(
height:
10
.
h
),
],
body:
_buildBody
(
context
,
state
),
);
},
),
);
}
Widget
_buildBody
(
BuildContext
context
,
RoomReportState
state
)
{
if
(
state
.
isLoading
&&
state
.
floors
.
isEmpty
)
{
return
const
Center
(
child:
CircularProgressIndicator
(
color:
Color
.
fromRGBO
(
66
,
165
,
245
,
1
)));
}
if
(
state
.
error
!=
null
&&
state
.
floors
.
isEmpty
)
{
return
Center
(
child:
Text
(
'加载失败:
${state.error}
'
));
}
return
SingleChildScrollView
(
child:
Column
(
children:
[
Padding
(
padding:
EdgeInsets
.
symmetric
(
horizontal:
28
.
w
),
child:
Column
(
children:
[
SizedBox
(
height:
10
.
h
),
if
(
state
.
floors
.
isNotEmpty
)
Container
(
decoration:
BoxDecoration
(
color:
Colors
.
white
,
borderRadius:
BorderRadius
.
vertical
(
top:
Radius
.
circular
(
24
.
r
)),
),
child:
FloorSelector
(
floorNames:
state
.
floors
.
map
((
f
)
=>
f
.
floorName
).
toList
(),
selectedIndex:
state
.
selectedFloorIndex
,
),
),
],
),
if
(
state
.
floors
.
isNotEmpty
&&
state
.
selectedFloorIndex
<
state
.
floors
.
length
)
Container
(
decoration:
BoxDecoration
(
color:
Colors
.
white
,
borderRadius:
BorderRadius
.
vertical
(
bottom:
Radius
.
circular
(
24
.
r
)),
),
padding:
EdgeInsets
.
only
(
bottom:
28
.
w
),
child:
RoomGrid
(
rooms:
state
.
roomGrid
),
),
SizedBox
(
height:
10
.
h
),
StatusStats
(
summary:
state
.
summary
),
SizedBox
(
height:
10
.
h
),
if
(
state
.
isLoading
)
const
Padding
(
padding:
EdgeInsets
.
all
(
16.0
),
child:
Center
(
child:
CircularProgressIndicator
(
color:
Color
.
fromRGBO
(
66
,
165
,
245
,
1.0
))),
),
...
state
.
detailRooms
.
map
((
room
)
{
return
Padding
(
padding:
EdgeInsets
.
only
(
bottom:
10
.
h
),
child:
RoomDetailCard
(
room:
room
),
);
}).
toList
(),
SizedBox
(
height:
10
.
h
),
],
),
)
;
}
,
)
,
]
,
),
);
}
}
\ No newline at end of file
lib/views/report/room/widget/room_grid.dart
View file @
7566c742
...
...
@@ -16,7 +16,7 @@ class RoomGrid extends StatelessWidget {
shrinkWrap:
true
,
physics:
const
NeverScrollableScrollPhysics
(),
crossAxisCount:
5
,
mainAxisSpacing:
20
.
h
,
mainAxisSpacing:
16
.
h
,
crossAxisSpacing:
16
.
w
,
children:
rooms
.
map
((
room
)
=>
_RoomItem
(
room:
room
)).
toList
(),
),
...
...
@@ -31,49 +31,38 @@ class _RoomItem extends StatelessWidget {
Color
get
_bgColor
{
switch
(
room
.
statusColor
)
{
case
'occupied'
:
return
const
Color
.
fromRGBO
(
219
,
234
,
254
,
1
);
case
'alarm'
:
return
const
Color
.
fromRGBO
(
254
,
243
,
199
,
1
);
case
'reserved'
:
return
const
Color
.
fromRGBO
(
219
,
234
,
254
,
1
);
case
'dnd'
:
return
const
Color
.
fromRGBO
(
254
,
243
,
199
,
1
);
case
'checkout'
:
case
'idle'
:
case
'blue'
:
return
const
Color
.
fromRGBO
(
229
,
241
,
255
,
1
);
case
'yellow'
:
return
const
Color
.
fromRGBO
(
255
,
251
,
237
,
1
);
case
'default'
:
case
'gray'
:
default
:
return
const
Color
.
fromRGBO
(
2
29
,
231
,
235
,
1
);
return
const
Color
.
fromRGBO
(
2
38
,
238
,
238
,
1
);
}
}
Color
get
_textColor
{
switch
(
room
.
statusColor
)
{
case
'occupied'
:
return
const
Color
.
fromRGBO
(
59
,
130
,
246
,
1
);
case
'alarm'
:
return
const
Color
.
fromRGBO
(
245
,
158
,
11
,
1
);
case
'reserved'
:
return
const
Color
.
fromRGBO
(
59
,
130
,
246
,
1
);
case
'dnd'
:
return
const
Color
.
fromRGBO
(
245
,
158
,
11
,
1
);
case
'checkout'
:
case
'idle'
:
case
'blue'
:
return
const
Color
.
fromRGBO
(
74
,
149
,
234
,
1
);
case
'yellow'
:
return
const
Color
.
fromRGBO
(
250
,
204
,
21
,
1
);
case
'default'
:
case
'gray'
:
default
:
return
const
Color
.
fromRGBO
(
1
56
,
163
,
175
,
1
);
return
const
Color
.
fromRGBO
(
1
00
,
116
,
139
,
1
);
}
}
Color
?
get
_borderColor
{
switch
(
room
.
statusColor
)
{
case
'idle'
:
case
'checkout'
:
return
null
;
case
'occupied'
:
case
'reserved'
:
return
const
Color
.
fromRGBO
(
59
,
130
,
246
,
1
);
case
'alarm'
:
case
'dnd'
:
return
const
Color
.
fromRGBO
(
245
,
158
,
11
,
1
);
case
'blue'
:
return
const
Color
.
fromRGBO
(
73
,
149
,
234
,
1
);
case
'yellow'
:
return
const
Color
.
fromRGBO
(
250
,
204
,
21
,
1
);
case
'default'
:
case
'gray'
:
default
:
return
null
;
}
...
...
@@ -81,44 +70,56 @@ class _RoomItem extends StatelessWidget {
@override
Widget
build
(
BuildContext
context
)
{
return
Container
(
padding:
EdgeInsets
.
symmetric
(
vertical:
12
.
h
,
horizontal:
8
.
w
),
decoration:
BoxDecoration
(
color:
_bgColor
,
borderRadius:
BorderRadius
.
circular
(
16
.
r
),
border:
_borderColor
!=
null
?
Border
.
all
(
color:
_borderColor
!,
width:
2
.
w
)
:
null
,
),
child:
Column
(
mainAxisAlignment:
MainAxisAlignment
.
center
,
children:
[
Text
(
room
.
roomNumber
,
style:
TextStyle
(
fontSize:
28
.
sp
,
fontWeight:
FontWeight
.
w500
,
color:
_textColor
,
return
GestureDetector
(
behavior:
HitTestBehavior
.
opaque
,
onTap:
()
{
},
child:
Container
(
padding:
EdgeInsets
.
symmetric
(
vertical:
12
.
h
),
decoration:
BoxDecoration
(
color:
_bgColor
,
borderRadius:
BorderRadius
.
circular
(
16
.
r
),
// border: _borderColor != null
// ? Border.all(color: _borderColor!, width: 1.w)
// : null,
),
child:
Column
(
mainAxisAlignment:
MainAxisAlignment
.
center
,
spacing:
4
.
h
,
children:
[
Text
(
room
.
roomNumber
,
style:
TextStyle
(
fontSize:
28
.
sp
,
fontWeight:
FontWeight
.
w500
,
color:
_textColor
,
),
maxLines:
1
,
overflow:
TextOverflow
.
ellipsis
,
),
maxLines:
1
,
overflow:
TextOverflow
.
ellipsis
,
),
SizedBox
(
height:
4
.
h
),
Row
(
mainAxisAlignment:
MainAxisAlignment
.
center
,
children:
[
if
(
room
.
hasAlarmIcon
)
Icon
(
Icons
.
warning_amber_outlined
,
color:
_textColor
,
size:
24
.
sp
),
Text
(
room
.
statusText
,
style:
TextStyle
(
fontSize:
24
.
sp
,
color:
_textColor
,
Row
(
mainAxisAlignment:
MainAxisAlignment
.
center
,
children:
[
if
(
room
.
hasAlarmIcon
)
Icon
(
Icons
.
warning_amber_outlined
,
color:
_textColor
,
size:
24
.
sp
),
Flexible
(
fit:
FlexFit
.
loose
,
//让文本只占实际需要的宽度
child:
Text
(
room
.
statusText
,
style:
TextStyle
(
fontSize:
24
.
sp
,
color:
_textColor
,
),
textAlign:
TextAlign
.
center
,
maxLines:
1
,
overflow:
TextOverflow
.
ellipsis
,
),
),
)
,
]
,
)
,
]
,
]
,
)
,
]
,
)
,
),
);
}
...
...
lib/views/report/room/widget/status_stats.dart
View file @
7566c742
...
...
@@ -49,7 +49,7 @@ class _StatItem extends StatelessWidget {
child:
Container
(
padding:
EdgeInsets
.
symmetric
(
vertical:
12
.
h
,
horizontal:
8
.
w
),
decoration:
BoxDecoration
(
color:
const
Color
.
fromRGBO
(
2
29
,
231
,
235
,
1
),
color:
const
Color
.
fromRGBO
(
2
38
,
238
,
238
,
1
),
borderRadius:
BorderRadius
.
circular
(
24
.
r
),
),
child:
Column
(
...
...
@@ -67,7 +67,7 @@ class _StatItem extends StatelessWidget {
item
.
label
,
style:
TextStyle
(
fontSize:
24
.
sp
,
color:
const
Color
.
fromRGBO
(
1
56
,
163
,
175
,
1
),
color:
const
Color
.
fromRGBO
(
1
00
,
116
,
139
,
1
),
),
),
],
...
...
lib/views/service/index/cubit/service_index_cubit.dart
View file @
7566c742
This diff is collapsed.
Click to expand it.
lib/views/service/index/cubit/service_index_state.dart
View file @
7566c742
import
'package:equatable/equatable.dart'
;
import
'package:smart_hotel_app/models/bo/room_bo.dart'
;
class
ServiceIndexState
extends
Equatable
{
/// copyWith sentinel: 区分「未传参」和「显式传 null」
...
...
@@ -16,9 +15,7 @@ class ServiceIndexState extends Equatable {
final
int
?
selectedRoomId
;
final
String
searchText
;
final
List
<
Map
<
String
,
dynamic
>>
rooms
;
final
List
<
AreaDeviceBO
>
areaDevices
;
final
bool
isLoading
;
final
bool
isLoadingAreaDevices
;
final
String
?
error
;
final
bool
isPoweringOn
;
final
bool
isPoweringOff
;
...
...
@@ -35,9 +32,7 @@ class ServiceIndexState extends Equatable {
this
.
selectedRoomId
,
this
.
searchText
=
''
,
this
.
rooms
=
const
[],
this
.
areaDevices
=
const
[],
this
.
isLoading
=
false
,
this
.
isLoadingAreaDevices
=
false
,
this
.
error
,
this
.
isPoweringOn
=
false
,
this
.
isPoweringOff
=
false
,
...
...
@@ -55,9 +50,7 @@ class ServiceIndexState extends Equatable {
Object
?
selectedRoomId
=
_nothing
,
String
?
searchText
,
List
<
Map
<
String
,
dynamic
>>?
rooms
,
List
<
AreaDeviceBO
>?
areaDevices
,
bool
?
isLoading
,
bool
?
isLoadingAreaDevices
,
Object
?
error
=
_nothing
,
bool
?
isPoweringOn
,
bool
?
isPoweringOff
,
...
...
@@ -74,9 +67,7 @@ class ServiceIndexState extends Equatable {
selectedRoomId:
identical
(
selectedRoomId
,
_nothing
)
?
this
.
selectedRoomId
:
selectedRoomId
as
int
?,
searchText:
searchText
??
this
.
searchText
,
rooms:
rooms
??
this
.
rooms
,
areaDevices:
areaDevices
??
this
.
areaDevices
,
isLoading:
isLoading
??
this
.
isLoading
,
isLoadingAreaDevices:
isLoadingAreaDevices
??
this
.
isLoadingAreaDevices
,
error:
identical
(
error
,
_nothing
)
?
this
.
error
:
error
as
String
?,
isPoweringOn:
isPoweringOn
??
this
.
isPoweringOn
,
isPoweringOff:
isPoweringOff
??
this
.
isPoweringOff
,
...
...
@@ -96,9 +87,7 @@ class ServiceIndexState extends Equatable {
selectedRoomId
,
searchText
,
rooms
,
areaDevices
,
isLoading
,
isLoadingAreaDevices
,
error
,
isPoweringOn
,
isPoweringOff
,
...
...
lib/views/service/index/index_view.dart
View file @
7566c742
...
...
@@ -17,26 +17,29 @@ class ServiceIndexView extends StatefulWidget {
}
class
_ServiceIndexViewState
extends
State
<
ServiceIndexView
>
with
AutoRouteAwareStateMixin
<
ServiceIndexView
>
{
TabsRouter
?
_tabsRouter
;
@override
void
didChangeDependencies
()
{
super
.
didChangeDependencies
();
// Subscribe to tab router events
final
tabsRouter
=
AutoTabsRouter
.
of
(
context
);
tabsRouter
.
addListener
(
_onTabChange
);
final
newRouter
=
AutoTabsRouter
.
of
(
context
);
if
(
newRouter
!=
_tabsRouter
)
{
_tabsRouter
?.
removeListener
(
_onTabChange
);
_tabsRouter
=
newRouter
;
_tabsRouter
?.
addListener
(
_onTabChange
);
}
}
@override
void
dispose
()
{
final
tabsRouter
=
AutoTabsRouter
.
of
(
context
);
tabsRouter
.
removeListener
(
_onTabChange
);
_tabsRouter
?.
removeListener
(
_onTabChange
);
super
.
dispose
();
}
void
_onTabChange
()
{
final
tabsRouter
=
AutoTabsRouter
.
of
(
context
);
// Check if this tab is now active (index 1)
if
(
tabsRouter
.
activeIndex
==
1
)
{
// Reload data when tab is activated
if
(!
mounted
)
return
;
if
(
_tabsRouter
?.
activeIndex
==
1
)
{
context
.
read
<
ServiceIndexCubit
>().
reloadAll
();
}
}
...
...
lib/views/service/index/widget/room_management_block.dart
View file @
7566c742
...
...
@@ -7,7 +7,6 @@ import 'package:smart_hotel_app/views/service/index/cubit/service_index_cubit.da
import
'package:flutter_bloc/flutter_bloc.dart'
;
import
'package:smart_hotel_app/routes/app_router.gr.dart'
;
import
'package:smart_hotel_app/views/service/index/cubit/service_index_state.dart'
;
import
'package:smart_hotel_app/views/service/index/widget/area_device_list.dart'
;
import
'package:smart_hotel_app/utils/toast_utils.dart'
;
class
RoomManagementBlock
extends
StatelessWidget
{
...
...
@@ -39,28 +38,19 @@ class RoomManagementBlock extends StatelessWidget {
SizedBox
(
height:
24
.
h
),
_buildTypeSelector
(
state
,
cubit
),
SizedBox
(
height:
24
.
h
),
if
(
state
.
selectedType
==
null
||
state
.
selectedType
==
'客房'
)
...[
_buildSearchBox
(
state
,
cubit
),
SizedBox
(
height:
32
.
h
),
_buildLine
(),
SizedBox
(
height:
24
.
h
),
_buildRoomGrid
(
state
,
cubit
),
SizedBox
(
height:
24
.
h
),
if
(
state
.
selectedRoom
!=
null
)
...[
_buildLine
(),
SizedBox
(
height:
24
.
h
),
_buildRoomDetail
(
state
,
cubit
),
SizedBox
(
height:
10
.
h
),
],
_buildBottomButtons
(
state
,
cubit
,
context
),
]
else
...[
_buildSearchBox
(
state
,
cubit
),
SizedBox
(
height:
32
.
h
),
_buildLine
(),
SizedBox
(
height:
24
.
h
),
_buildRoomGrid
(
state
,
cubit
),
SizedBox
(
height:
24
.
h
),
if
(
state
.
selectedRoom
!=
null
)
...[
_buildLine
(),
SizedBox
(
height:
24
.
h
),
AreaDeviceList
(
devices:
state
.
areaDevices
,
isLoading:
state
.
isLoadingAreaDevices
,
),
_buildRoomDetail
(
state
,
cubit
),
SizedBox
(
height:
10
.
h
),
],
_buildBottomButtons
(
state
,
cubit
,
context
),
],
),
);
...
...
@@ -78,8 +68,7 @@ class RoomManagementBlock extends StatelessWidget {
floorName
==
'3'
?
'三'
:
floorName
==
'4'
?
'四'
:
floorName
==
'5'
?
'五'
:
floorName
==
'6'
?
'六'
:
floorName
==
'7'
?
'七'
:
''
;
floorName
==
'6'
?
'六'
:
''
;
final
floorId
=
floorMap
.
keys
.
first
;
final
isSelected
=
state
.
selectedFloor
==
floorName
;
return
Expanded
(
...
...
@@ -241,7 +230,10 @@ class RoomManagementBlock extends StatelessWidget {
const
crossAxisCount
=
12
;
final
floorPart
=
state
.
selectedFloor
?.
replaceAll
(
'楼'
,
''
)
??
''
;
final
filterLabel
=
floorPart
.
isNotEmpty
?
'客房
$floorPart
楼·左右滑动选择'
:
'左右滑动选择'
;
final
areaLabel
=
state
.
selectedType
??
'全部'
;
final
filterLabel
=
floorPart
.
isNotEmpty
?
'
$areaLabel
·
$floorPart
楼·左右滑动选择'
:
'
$areaLabel
·左右滑动选择'
;
return
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
...
...
@@ -386,6 +378,8 @@ class RoomManagementBlock extends StatelessWidget {
width:
24
.
sp
,
height:
30
.
sp
,
);
// return Icon(Icons.cleaning_services_rounded, size: 26.w,
// color: const Color.fromRGBO(255, 138, 139, 1));
}
else
{
return
Text
(
' '
,
...
...
@@ -405,7 +399,6 @@ class RoomManagementBlock extends StatelessWidget {
Widget
_buildBottomButtons
(
ServiceIndexState
state
,
ServiceIndexCubit
cubit
,
BuildContext
context
)
{
final
isRoomSelected
=
state
.
selectedRoom
!=
null
;
final
isProcessing
=
state
.
isPoweringOn
||
state
.
isPoweringOff
;
return
Row
(
spacing:
16
.
w
,
children:
[
...
...
@@ -421,7 +414,7 @@ class RoomManagementBlock extends StatelessWidget {
),
elevation:
0
,
),
onPressed:
isRoomSelected
&&
!
isProcessing
onPressed:
isRoomSelected
&&
!
state
.
isPoweringOn
?
()
async
{
try
{
await
cubit
.
powerOnRoom
(
state
.
selectedRoom
!);
...
...
@@ -453,14 +446,14 @@ class RoomManagementBlock extends StatelessWidget {
padding:
EdgeInsets
.
zero
,
shape:
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
circular
(
16
.
r
),
side:
BorderSide
(
color:
const
Color
.
fromRGBO
(
73
,
149
,
234
,
1
),
width:
1
.
w
,
),
//
side: BorderSide(
//
color: const Color.fromRGBO(73, 149, 234, 1),
//
width: 1.w,
//
),
),
elevation:
0
,
),
onPressed:
isRoomSelected
&&
!
isProcessing
onPressed:
isRoomSelected
&&
!
state
.
isPoweringOff
?
()
async
{
try
{
await
cubit
.
powerOffRoom
(
state
.
selectedRoom
!);
...
...
@@ -515,6 +508,7 @@ class RoomManagementBlock extends StatelessWidget {
],
);
}
}
...
...
lib/views/service/room/cubit/service_room_cubit.dart
View file @
7566c742
...
...
@@ -150,36 +150,55 @@ class ServiceRoomCubit extends Cubit<ServiceRoomState> {
/// 房间总电源开关
Future
<
void
>
toggleMainPowerForSwitch
(
bool
value
)
async
{
if
(
state
.
isTogglingPower
)
return
;
emit
(
state
.
copyWith
(
isTogglingPower:
true
));
if
(
state
.
isPoweringOn
||
state
.
isPoweringOff
)
return
;
final
loadingKey
=
value
?
'isPoweringOn'
:
'isPoweringOff'
;
emit
(
state
.
copyWith
(
isPoweringOn:
value
,
isPoweringOff:
!
value
,
));
try
{
if
(
value
)
{
await
_service
.
roomPowerOn
(
roomId:
_roomId
);
}
else
{
await
_service
.
roomPowerOff
(
roomId:
_roomId
);
}
emit
(
state
.
copyWith
(
isTogglingPower:
false
,
mainPowerOn:
value
));
emit
(
state
.
copyWith
(
isPoweringOn:
false
,
isPoweringOff:
false
,
mainPowerOn:
value
,
));
}
catch
(
e
)
{
emit
(
state
.
copyWith
(
isTogglingPower:
false
));
// Re-throw so the view can show error snackbar
emit
(
state
.
copyWith
(
isPoweringOn:
false
,
isPoweringOff:
false
,
));
rethrow
;
}
}
/// 房间总电源开关
Future
<
void
>
toggleMainPower
(
bool
value
)
async
{
if
(
state
.
isTogglingPower
)
return
;
emit
(
state
.
copyWith
(
isTogglingPower:
true
));
if
(
state
.
isPoweringOn
||
state
.
isPoweringOff
)
return
;
emit
(
state
.
copyWith
(
isPoweringOn:
value
,
isPoweringOff:
!
value
,
));
try
{
if
(
value
)
{
await
_service
.
powerOn
(
roomId:
_roomId
);
}
else
{
await
_service
.
powerOff
(
roomId:
_roomId
);
}
emit
(
state
.
copyWith
(
isTogglingPower:
false
,
mainPowerOn:
value
));
emit
(
state
.
copyWith
(
isPoweringOn:
false
,
isPoweringOff:
false
,
mainPowerOn:
value
,
));
}
catch
(
e
)
{
emit
(
state
.
copyWith
(
isTogglingPower:
false
));
// Re-throw so the view can show error snackbar
emit
(
state
.
copyWith
(
isPoweringOn:
false
,
isPoweringOff:
false
,
));
rethrow
;
}
}
...
...
lib/views/service/room/cubit/service_room_state.dart
View file @
7566c742
...
...
@@ -45,7 +45,8 @@ class ServiceRoomState extends Equatable {
final
List
<
RoomDevice
>
roomDevices
;
final
bool
mainPowerOn
;
final
List
<
ControlDevice
>
controlDevices
;
final
bool
isTogglingPower
;
final
bool
isPoweringOn
;
final
bool
isPoweringOff
;
const
ServiceRoomState
({
this
.
isLoading
=
false
,
...
...
@@ -57,7 +58,8 @@ class ServiceRoomState extends Equatable {
this
.
roomDevices
=
const
[],
this
.
mainPowerOn
=
false
,
this
.
controlDevices
=
const
[],
this
.
isTogglingPower
=
false
,
this
.
isPoweringOn
=
false
,
this
.
isPoweringOff
=
false
,
});
ServiceRoomState
copyWith
({
...
...
@@ -70,7 +72,8 @@ class ServiceRoomState extends Equatable {
List
<
RoomDevice
>?
roomDevices
,
bool
?
mainPowerOn
,
List
<
ControlDevice
>?
controlDevices
,
bool
?
isTogglingPower
,
bool
?
isPoweringOn
,
bool
?
isPoweringOff
,
})
{
return
ServiceRoomState
(
isLoading:
isLoading
??
this
.
isLoading
,
...
...
@@ -82,7 +85,8 @@ class ServiceRoomState extends Equatable {
roomDevices:
roomDevices
??
this
.
roomDevices
,
mainPowerOn:
mainPowerOn
??
this
.
mainPowerOn
,
controlDevices:
controlDevices
??
this
.
controlDevices
,
isTogglingPower:
isTogglingPower
??
this
.
isTogglingPower
,
isPoweringOn:
isPoweringOn
??
this
.
isPoweringOn
,
isPoweringOff:
isPoweringOff
??
this
.
isPoweringOff
,
);
}
...
...
@@ -97,6 +101,7 @@ class ServiceRoomState extends Equatable {
roomDevices
,
mainPowerOn
,
controlDevices
,
isTogglingPower
,
isPoweringOn
,
isPoweringOff
,
];
}
lib/views/service/room/room_detail_view.dart
View file @
7566c742
...
...
@@ -124,7 +124,8 @@ class ServiceRoomDetailView extends StatelessWidget {
),
SizedBox
(
height:
20
.
h
),
RoomBottomButtons
(
isProcessing:
state
.
isTogglingPower
,
isPoweringOn:
state
.
isPoweringOn
,
isPoweringOff:
state
.
isPoweringOff
,
onPowerOn:
()
async
{
try
{
await
cubit
.
toggleMainPower
(
true
);
...
...
lib/views/service/room/widget/room_bottom_buttons.dart
View file @
7566c742
...
...
@@ -4,15 +4,15 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
class
RoomBottomButtons
extends
StatelessWidget
{
final
VoidCallback
?
onPowerOn
;
final
VoidCallback
?
onPowerOff
;
// final VoidCallback? onDeviceControl
;
final
bool
isP
rocessing
;
final
bool
isPoweringOn
;
final
bool
isP
oweringOff
;
const
RoomBottomButtons
({
super
.
key
,
this
.
onPowerOn
,
this
.
onPowerOff
,
// this.onDeviceControl
,
this
.
isP
rocessing
=
false
,
this
.
isPoweringOn
=
false
,
this
.
isP
oweringOff
=
false
,
});
@override
...
...
@@ -22,11 +22,11 @@ class RoomBottomButtons extends StatelessWidget {
children:
[
Expanded
(
child:
GestureDetector
(
onTap:
(
onPowerOn
!=
null
&&
!
isP
rocessing
)
?
onPowerOn
:
null
,
onTap:
(
onPowerOn
!=
null
&&
!
isP
oweringOn
)
?
onPowerOn
:
null
,
child:
Container
(
height:
70
.
h
,
decoration:
BoxDecoration
(
color:
(
onPowerOn
!=
null
&&
!
isP
rocessing
)
color:
(
onPowerOn
!=
null
&&
!
isP
oweringOn
)
?
const
Color
.
fromRGBO
(
73
,
149
,
234
,
1
)
:
const
Color
.
fromRGBO
(
204
,
204
,
204
,
1
),
borderRadius:
BorderRadius
.
circular
(
16
.
r
),
...
...
@@ -46,26 +46,20 @@ class RoomBottomButtons extends StatelessWidget {
),
Expanded
(
child:
GestureDetector
(
onTap:
(
onPowerOff
!=
null
&&
!
isP
rocessing
)
?
onPowerOff
:
null
,
onTap:
(
onPowerOff
!=
null
&&
!
isP
oweringOff
)
?
onPowerOff
:
null
,
child:
Container
(
height:
70
.
h
,
decoration:
BoxDecoration
(
color:
const
Color
.
fromRGBO
(
229
,
241
,
255
,
1
),
color:
(
onPowerOff
!=
null
&&
!
isPoweringOff
)
?
const
Color
.
fromRGBO
(
229
,
241
,
255
,
1
)
:
const
Color
.
fromRGBO
(
204
,
204
,
204
,
1
),
borderRadius:
BorderRadius
.
circular
(
16
.
r
),
border:
Border
.
all
(
color:
(
onPowerOff
!=
null
&&
!
isProcessing
)
?
const
Color
.
fromRGBO
(
73
,
149
,
234
,
1
)
:
const
Color
.
fromRGBO
(
204
,
204
,
204
,
1
),
width:
1
.
w
,
),
),
child:
Center
(
child:
Text
(
'断电'
,
style:
TextStyle
(
color:
(
onPowerOff
!=
null
&&
!
isProcessing
)
?
const
Color
.
fromRGBO
(
66
,
165
,
245
,
1.0
)
:
const
Color
.
fromRGBO
(
100
,
116
,
139
,
1
),
color:
const
Color
.
fromRGBO
(
100
,
116
,
139
,
1
),
fontSize:
28
.
sp
,
fontWeight:
FontWeight
.
w500
,
),
...
...
@@ -74,29 +68,7 @@ class RoomBottomButtons extends StatelessWidget {
),
),
),
// Expanded(
// child: GestureDetector(
// onTap: onDeviceControl,
// child: Container(
// height: 70.h,
// decoration: BoxDecoration(
// color: const Color.fromRGBO(100, 116, 139, 0.2),
// borderRadius: BorderRadius.circular(16.r),
// ),
// child: Center(
// child: Text(
// '设备控制',
// style: TextStyle(
// color: const Color.fromRGBO(100, 116, 139, 1.0),
// fontSize: 28.sp,
// fontWeight: FontWeight.w500,
// ),
// ),
// ),
// ),
// ),
// ),
],
);
}
}
}
\ 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