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
a8c199a0
Commit
a8c199a0
authored
Jun 11, 2026
by
张宏
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
111
parent
93ea6fa5
Show whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
565 additions
and
321 deletions
+565
-321
room_bo.dart
lib/models/bo/room_bo.dart
+240
-0
alert_dashboard_repository.dart
lib/repositories/alert_dashboard_repository.dart
+1
-0
room_repository.dart
lib/repositories/room_repository.dart
+9
-0
room_service.dart
lib/services/room_service.dart
+9
-0
device_list_view.dart
lib/views/report/device/device_list_view.dart
+1
-1
energy_detail_view.dart
lib/views/report/energy/energy_detail_view.dart
+1
-1
room_report_cubit.dart
lib/views/report/room/cubit/room_report_cubit.dart
+23
-102
room_report_state.dart
lib/views/report/room/cubit/room_report_state.dart
+27
-123
room_detail_view.dart
lib/views/report/room/room_detail_view.dart
+63
-7
room_detail_card.dart
lib/views/report/room/widget/room_detail_card.dart
+29
-46
room_grid.dart
lib/views/report/room/widget/room_grid.dart
+39
-28
status_stats.dart
lib/views/report/room/widget/status_stats.dart
+24
-10
rule_management_view.dart
lib/views/report/rule/rule_management_view.dart
+1
-1
对接方案.md
对接方案.md
+98
-2
No files found.
lib/models/bo/room_bo.dart
View file @
a8c199a0
import
'package:equatable/equatable.dart'
;
// ============================================================
// 客房状态总览-客房概览统计 BO 模型
// ============================================================
/// 概览统计(顶部统计区域)
class
RoomOverviewSummaryBO
extends
Equatable
{
final
int
occupiedCount
;
final
int
vacantCount
;
final
int
reservedCount
;
final
int
alarmCount
;
const
RoomOverviewSummaryBO
({
this
.
occupiedCount
=
0
,
this
.
vacantCount
=
0
,
this
.
reservedCount
=
0
,
this
.
alarmCount
=
0
,
});
factory
RoomOverviewSummaryBO
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
return
RoomOverviewSummaryBO
(
occupiedCount:
json
[
'occupiedCount'
]
as
int
?
??
0
,
vacantCount:
json
[
'vacantCount'
]
as
int
?
??
0
,
reservedCount:
json
[
'reservedCount'
]
as
int
?
??
0
,
alarmCount:
json
[
'alarmCount'
]
as
int
?
??
0
,
);
}
@override
List
<
Object
?>
get
props
=>
[
occupiedCount
,
vacantCount
,
reservedCount
,
alarmCount
];
}
/// 楼层Tab列表
class
RoomFloorBO
extends
Equatable
{
final
int
floorId
;
final
String
floorName
;
final
bool
isActive
;
const
RoomFloorBO
({
this
.
floorId
=
0
,
this
.
floorName
=
''
,
this
.
isActive
=
false
,
});
factory
RoomFloorBO
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
return
RoomFloorBO
(
floorId:
json
[
'floorId'
]
as
int
?
??
0
,
floorName:
json
[
'floorName'
]
as
String
?
??
''
,
isActive:
json
[
'isActive'
]
as
bool
?
??
false
,
);
}
@override
List
<
Object
?>
get
props
=>
[
floorId
,
floorName
,
isActive
];
}
/// 房间网格(房间卡片网格布局)
class
RoomGridBO
extends
Equatable
{
final
int
roomId
;
final
String
roomNumber
;
final
String
statusText
;
final
String
statusColor
;
final
bool
hasAlarmIcon
;
const
RoomGridBO
({
this
.
roomId
=
0
,
this
.
roomNumber
=
''
,
this
.
statusText
=
''
,
this
.
statusColor
=
''
,
this
.
hasAlarmIcon
=
false
,
});
factory
RoomGridBO
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
return
RoomGridBO
(
roomId:
json
[
'roomId'
]
as
int
?
??
0
,
roomNumber:
json
[
'roomNumber'
]
as
String
?
??
''
,
statusText:
json
[
'statusText'
]
as
String
?
??
''
,
statusColor:
json
[
'statusColor'
]
as
String
?
??
''
,
hasAlarmIcon:
json
[
'hasAlarmIcon'
]
as
bool
?
??
false
,
);
}
@override
List
<
Object
?>
get
props
=>
[
roomId
,
roomNumber
,
statusText
,
statusColor
,
hasAlarmIcon
];
}
/// 房间基础信息(客房概览统计)
class
RoomOverviewBasicInfoBO
extends
Equatable
{
final
int
roomId
;
final
String
roomNumber
;
final
String
roomStatusText
;
const
RoomOverviewBasicInfoBO
({
this
.
roomId
=
0
,
this
.
roomNumber
=
''
,
this
.
roomStatusText
=
''
,
});
factory
RoomOverviewBasicInfoBO
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
return
RoomOverviewBasicInfoBO
(
roomId:
json
[
'roomId'
]
as
int
?
??
0
,
roomNumber:
json
[
'roomNumber'
]
as
String
?
??
''
,
roomStatusText:
json
[
'roomStatusText'
]
as
String
?
??
''
,
);
}
@override
List
<
Object
?>
get
props
=>
[
roomId
,
roomNumber
,
roomStatusText
];
}
/// 状态标签
class
StatusTagsBO
extends
Equatable
{
final
bool
isOccupied
;
final
bool
powerSupplyStatus
;
final
bool
wifiNormalStatus
;
const
StatusTagsBO
({
this
.
isOccupied
=
false
,
this
.
powerSupplyStatus
=
false
,
this
.
wifiNormalStatus
=
false
,
});
factory
StatusTagsBO
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
return
StatusTagsBO
(
isOccupied:
json
[
'isOccupied'
]
as
bool
?
??
false
,
powerSupplyStatus:
json
[
'powerSupplyStatus'
]
as
bool
?
??
false
,
wifiNormalStatus:
json
[
'wifiNormalStatus'
]
as
bool
?
??
false
,
);
}
@override
List
<
Object
?>
get
props
=>
[
isOccupied
,
powerSupplyStatus
,
wifiNormalStatus
];
}
/// 房间设备项
class
DeviceItemBO
extends
Equatable
{
final
int
deviceId
;
final
String
deviceName
;
final
String
deviceTypeIcon
;
final
String
powerStatus
;
final
String
powerStatusText
;
final
String
temperatureDisplay
;
final
String
paramDisplayText
;
const
DeviceItemBO
({
this
.
deviceId
=
0
,
this
.
deviceName
=
''
,
this
.
deviceTypeIcon
=
''
,
this
.
powerStatus
=
''
,
this
.
powerStatusText
=
''
,
this
.
temperatureDisplay
=
''
,
this
.
paramDisplayText
=
''
,
});
factory
DeviceItemBO
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
return
DeviceItemBO
(
deviceId:
json
[
'deviceId'
]
as
int
?
??
0
,
deviceName:
json
[
'deviceName'
]
as
String
?
??
''
,
deviceTypeIcon:
json
[
'deviceTypeIcon'
]
as
String
?
??
''
,
powerStatus:
json
[
'powerStatus'
]
as
String
?
??
''
,
powerStatusText:
json
[
'powerStatusText'
]
as
String
?
??
''
,
temperatureDisplay:
json
[
'temperatureDisplay'
]
as
String
?
??
''
,
paramDisplayText:
json
[
'paramDisplayText'
]
as
String
?
??
''
,
);
}
@override
List
<
Object
?>
get
props
=>
[
deviceId
,
deviceName
,
deviceTypeIcon
,
powerStatus
,
powerStatusText
,
temperatureDisplay
,
paramDisplayText
,
];
}
/// 房间详情列表项
class
RoomDetailListBO
extends
Equatable
{
final
RoomOverviewBasicInfoBO
roomBasicInfo
;
final
StatusTagsBO
statusTags
;
final
List
<
DeviceItemBO
>
deviceList
;
const
RoomDetailListBO
({
required
this
.
roomBasicInfo
,
required
this
.
statusTags
,
this
.
deviceList
=
const
[],
});
factory
RoomDetailListBO
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
return
RoomDetailListBO
(
roomBasicInfo:
RoomOverviewBasicInfoBO
.
fromJson
(
json
[
'roomBasicInfo'
]
as
Map
<
String
,
dynamic
>?
??
{}),
statusTags:
StatusTagsBO
.
fromJson
(
json
[
'statusTags'
]
as
Map
<
String
,
dynamic
>?
??
{}),
deviceList:
(
json
[
'deviceList'
]
as
List
<
dynamic
>?)
?.
map
((
e
)
=>
DeviceItemBO
.
fromJson
(
e
as
Map
<
String
,
dynamic
>))
.
toList
()
??
[],
);
}
@override
List
<
Object
?>
get
props
=>
[
roomBasicInfo
,
statusTags
,
deviceList
];
}
/// 客房概览统计根对象
class
RoomOverviewBO
extends
Equatable
{
final
RoomOverviewSummaryBO
summary
;
final
List
<
RoomFloorBO
>
floorList
;
final
List
<
RoomGridBO
>
roomGrid
;
final
List
<
RoomDetailListBO
>
roomDetailList
;
const
RoomOverviewBO
({
required
this
.
summary
,
this
.
floorList
=
const
[],
this
.
roomGrid
=
const
[],
this
.
roomDetailList
=
const
[],
});
factory
RoomOverviewBO
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
return
RoomOverviewBO
(
summary:
RoomOverviewSummaryBO
.
fromJson
(
json
[
'summary'
]
as
Map
<
String
,
dynamic
>?
??
{}),
floorList:
(
json
[
'floorList'
]
as
List
<
dynamic
>?)
?.
map
((
e
)
=>
RoomFloorBO
.
fromJson
(
e
as
Map
<
String
,
dynamic
>))
.
toList
()
??
[],
roomGrid:
(
json
[
'roomGrid'
]
as
List
<
dynamic
>?)
?.
map
((
e
)
=>
RoomGridBO
.
fromJson
(
e
as
Map
<
String
,
dynamic
>))
.
toList
()
??
[],
roomDetailList:
(
json
[
'roomDetailList'
]
as
List
<
dynamic
>?)
?.
map
((
e
)
=>
RoomDetailListBO
.
fromJson
(
e
as
Map
<
String
,
dynamic
>))
.
toList
()
??
[],
);
}
@override
List
<
Object
?>
get
props
=>
[
summary
,
floorList
,
roomGrid
,
roomDetailList
];
}
int
_parseInt
(
dynamic
value
,
{
int
fallback
=
0
})
{
if
(
value
==
null
)
return
fallback
;
if
(
value
is
int
)
return
value
;
...
...
lib/repositories/alert_dashboard_repository.dart
View file @
a8c199a0
...
...
@@ -6,6 +6,7 @@ class AlertDashboardRepository {
Future
<
ResponseModel
<
AlertDashboardBO
>>
getDashboard
()
{
return
DioRequest
.
instance
.
get
<
AlertDashboardBO
>(
'/app/alert/dashboard'
,
queryParameters:
{
'alertStatus'
:
0
,
'pageNum'
:
1
,
'pageSize'
:
5
},
// 处理状态:0=待处理 1=处理中 2=已处理 3=已忽略 4=误报关闭
fromJsonT:
(
data
)
=>
AlertDashboardBO
.
fromJson
(
data
as
Map
<
String
,
dynamic
>),
);
...
...
lib/repositories/room_repository.dart
View file @
a8c199a0
...
...
@@ -81,6 +81,15 @@ class RoomRepository {
);
}
/// 客房状态总览-客房概览统计
Future
<
ResponseModel
<
RoomOverviewBO
>>
getOverview
(
int
?
floorId
)
{
return
DioRequest
.
instance
.
get
<
RoomOverviewBO
>(
'/app/room/overview'
,
queryParameters:
{
'floorId'
:
floorId
==
0
?
null
:
floorId
},
fromJsonT:
(
data
)
=>
RoomOverviewBO
.
fromJson
(
data
as
Map
<
String
,
dynamic
>),
);
}
/// 非客房-相关楼层区域设备
Future
<
ResponseModel
<
List
<
AreaDeviceBO
>>>
getAreaDevices
({
required
int
floorId
,
...
...
lib/services/room_service.dart
View file @
a8c199a0
...
...
@@ -73,6 +73,15 @@ class RoomService {
}
}
/// 客房状态总览-客房概览统计
Future
<
RoomOverviewBO
>
getOverview
(
int
?
floorId
)
async
{
final
result
=
await
_repository
.
getOverview
(
floorId
);
if
(
result
.
success
&&
result
.
data
!=
null
)
{
return
result
.
data
!;
}
throw
Exception
(
result
.
msg
);
}
/// 非客房-相关楼层区域设备
Future
<
List
<
AreaDeviceBO
>>
getAreaDevices
({
required
int
floorId
,
...
...
lib/views/report/device/device_list_view.dart
View file @
a8c199a0
...
...
@@ -69,7 +69,7 @@ class _ReportDeviceListBodyState extends State<_ReportDeviceListBody> {
backgroundColor:
Colors
.
white
,
elevation:
0
,
leading:
IconButton
(
icon:
const
Icon
(
Icons
.
arrow_back
,
color:
Colors
.
black
),
icon:
const
Icon
(
Icons
.
arrow_back
_ios
,
color:
Colors
.
black
),
onPressed:
()
{
context
.
popRoute
();
},
...
...
lib/views/report/energy/energy_detail_view.dart
View file @
a8c199a0
...
...
@@ -22,7 +22,7 @@ class ReportEnergyDetailView extends StatelessWidget {
backgroundColor:
Colors
.
white
,
elevation:
0
,
leading:
IconButton
(
icon:
const
Icon
(
Icons
.
arrow_back
,
color:
Colors
.
black87
),
icon:
const
Icon
(
Icons
.
arrow_back
_ios
,
color:
Colors
.
black87
),
onPressed:
()
{
context
.
popRoute
();
},
...
...
lib/views/report/room/cubit/room_report_cubit.dart
View file @
a8c199a0
import
'package:flutter/material.dart'
;
import
'package:flutter_bloc/flutter_bloc.dart'
;
import
'package:smart_hotel_app/repositories/room_repository.dart'
;
import
'package:smart_hotel_app/services/room_service.dart'
;
import
'package:smart_hotel_app/views/report/room/cubit/room_report_state.dart'
;
class
RoomReportCubit
extends
Cubit
<
RoomReportState
>
{
RoomReportCubit
()
:
super
(
const
RoomReportState
())
{
_initData
();
}
void
_initData
()
{
final
floors
=
[
FloorInfo
(
floorName:
'一楼'
,
rooms:
List
.
generate
(
10
,
(
index
)
=>
_createRoom
(
101
+
index
)),
),
FloorInfo
(
floorName:
'二楼'
,
rooms:
List
.
generate
(
10
,
(
index
)
=>
_createRoom
(
201
+
index
)),
),
FloorInfo
(
floorName:
'三楼'
,
rooms:
List
.
generate
(
10
,
(
index
)
=>
_createRoom
(
301
+
index
)),
),
FloorInfo
(
floorName:
'四楼'
,
rooms:
List
.
generate
(
10
,
(
index
)
=>
_createRoom
(
401
+
index
)),
),
FloorInfo
(
floorName:
'五楼'
,
rooms:
List
.
generate
(
10
,
(
index
)
=>
_createRoom
(
501
+
index
)),
),
FloorInfo
(
floorName:
'六楼'
,
rooms:
List
.
generate
(
10
,
(
index
)
=>
_createRoom
(
601
+
index
)),
),
];
final
RoomService
_service
;
final
statusStats
=
[
const
StatusStat
(
label:
'入住'
,
count:
45
,
color:
Color
.
fromRGBO
(
59
,
130
,
246
,
1
),
),
const
StatusStat
(
label:
'空闲'
,
count:
7
,
color:
Color
.
fromRGBO
(
156
,
163
,
175
,
1
),
),
const
StatusStat
(
label:
'预订'
,
count:
5
,
color:
Color
.
fromRGBO
(
147
,
197
,
253
,
1
),
),
const
StatusStat
(
label:
'告警'
,
count:
3
,
color:
Color
.
fromRGBO
(
251
,
191
,
36
,
1
),
),
];
final
detailRooms
=
[
RoomInfo
(
roomNumber:
'302'
,
status:
RoomStatus
.
occupied
,
statusText:
'入住中'
,
deviceStatus:
const
RoomDeviceStatus
(
hasPerson:
true
,
powerOn:
true
,
wifiNormal:
true
,
acOn:
true
,
acTemperature:
'24'
,
lightOn:
false
,
tvOn:
true
,
curtainOn:
false
,
),
),
];
RoomReportCubit
({
RoomService
?
service
})
:
_service
=
service
??
RoomService
(
repository:
RoomRepository
()),
super
(
const
RoomReportState
())
{
loadData
();
}
Future
<
void
>
loadData
({
int
?
floorId
})
async
{
emit
(
state
.
copyWith
(
isLoading:
true
,
error:
null
));
try
{
final
overview
=
await
_service
.
getOverview
(
floorId
);
final
activeIndex
=
overview
.
floorList
.
indexWhere
((
f
)
=>
f
.
isActive
);
emit
(
state
.
copyWith
(
floors:
floors
,
statusStats:
statusStats
,
detailRooms:
detailRooms
,
isLoading:
false
,
summary:
overview
.
summary
,
floors:
overview
.
floorList
,
selectedFloorIndex:
activeIndex
>=
0
?
activeIndex
:
0
,
roomGrid:
overview
.
roomGrid
,
detailRooms:
overview
.
roomDetailList
,
));
}
catch
(
e
)
{
emit
(
state
.
copyWith
(
isLoading:
false
,
error:
e
.
toString
()));
}
RoomInfo
_createRoom
(
int
number
)
{
RoomStatus
status
;
String
statusText
;
if
(
number
==
303
)
{
status
=
RoomStatus
.
warning
;
statusText
=
'告警'
;
}
else
if
(
number
==
309
)
{
status
=
RoomStatus
.
temperature
;
statusText
=
'温度'
;
}
else
if
([
302
,
304
,
308
].
contains
(
number
))
{
status
=
RoomStatus
.
occupied
;
statusText
=
number
==
308
?
'入住·勿扰'
:
'入住·在房'
;
}
else
if
(
number
==
307
)
{
status
=
RoomStatus
.
reserved
;
statusText
=
'预定'
;
}
else
{
status
=
RoomStatus
.
vacant
;
statusText
=
'空闲'
;
}
return
RoomInfo
(
roomNumber:
number
.
toString
(),
status:
status
,
statusText:
statusText
,
);
}
void
selectFloor
(
int
index
)
{
final
floorId
=
state
.
floors
[
index
].
floorId
;
emit
(
state
.
copyWith
(
selectedFloorIndex:
index
));
loadData
(
floorId:
floorId
);
}
}
lib/views/report/room/cubit/room_report_state.dart
View file @
a8c199a0
import
'package:equatable/equatable.dart'
;
import
'package:flutter/material.dart'
;
enum
RoomStatus
{
vacant
,
occupied
,
reserved
,
warning
,
temperature
,
}
class
RoomDeviceStatus
{
final
bool
hasPerson
;
final
bool
powerOn
;
final
bool
wifiNormal
;
final
bool
acOn
;
final
String
acTemperature
;
final
bool
lightOn
;
final
bool
tvOn
;
final
bool
curtainOn
;
const
RoomDeviceStatus
({
this
.
hasPerson
=
true
,
this
.
powerOn
=
true
,
this
.
wifiNormal
=
true
,
this
.
acOn
=
false
,
this
.
acTemperature
=
'24'
,
this
.
lightOn
=
false
,
this
.
tvOn
=
false
,
this
.
curtainOn
=
false
,
});
RoomDeviceStatus
copyWith
({
bool
?
hasPerson
,
bool
?
powerOn
,
bool
?
wifiNormal
,
bool
?
acOn
,
String
?
acTemperature
,
bool
?
lightOn
,
bool
?
tvOn
,
bool
?
curtainOn
,
})
{
return
RoomDeviceStatus
(
hasPerson:
hasPerson
??
this
.
hasPerson
,
powerOn:
powerOn
??
this
.
powerOn
,
wifiNormal:
wifiNormal
??
this
.
wifiNormal
,
acOn:
acOn
??
this
.
acOn
,
acTemperature:
acTemperature
??
this
.
acTemperature
,
lightOn:
lightOn
??
this
.
lightOn
,
tvOn:
tvOn
??
this
.
tvOn
,
curtainOn:
curtainOn
??
this
.
curtainOn
,
);
}
}
class
RoomInfo
{
final
String
roomNumber
;
final
RoomStatus
status
;
final
String
statusText
;
final
RoomDeviceStatus
deviceStatus
;
const
RoomInfo
({
required
this
.
roomNumber
,
required
this
.
status
,
required
this
.
statusText
,
this
.
deviceStatus
=
const
RoomDeviceStatus
(),
});
RoomInfo
copyWith
({
String
?
roomNumber
,
RoomStatus
?
status
,
String
?
statusText
,
RoomDeviceStatus
?
deviceStatus
,
})
{
return
RoomInfo
(
roomNumber:
roomNumber
??
this
.
roomNumber
,
status:
status
??
this
.
status
,
statusText:
statusText
??
this
.
statusText
,
deviceStatus:
deviceStatus
??
this
.
deviceStatus
,
);
}
}
class
FloorInfo
{
final
String
floorName
;
final
List
<
RoomInfo
>
rooms
;
const
FloorInfo
({
required
this
.
floorName
,
required
this
.
rooms
,
});
FloorInfo
copyWith
({
String
?
floorName
,
List
<
RoomInfo
>?
rooms
,
})
{
return
FloorInfo
(
floorName:
floorName
??
this
.
floorName
,
rooms:
rooms
??
this
.
rooms
,
);
}
}
class
StatusStat
{
final
String
label
;
final
int
count
;
final
Color
color
;
const
StatusStat
({
required
this
.
label
,
required
this
.
count
,
required
this
.
color
,
});
}
import
'package:smart_hotel_app/models/bo/room_bo.dart'
;
class
RoomReportState
extends
Equatable
{
final
List
<
FloorInfo
>
floors
;
final
bool
isLoading
;
final
String
?
error
;
final
RoomOverviewSummaryBO
summary
;
final
List
<
RoomFloorBO
>
floors
;
final
int
selectedFloorIndex
;
final
List
<
StatusStat
>
statusStats
;
final
List
<
Room
Info
>
detailRooms
;
final
List
<
RoomGridBO
>
roomGrid
;
final
List
<
Room
DetailListBO
>
detailRooms
;
const
RoomReportState
({
this
.
isLoading
=
false
,
this
.
error
,
this
.
summary
=
const
RoomOverviewSummaryBO
(),
this
.
floors
=
const
[],
this
.
selectedFloorIndex
=
2
,
this
.
statusStats
=
const
[],
this
.
selectedFloorIndex
=
0
,
this
.
roomGrid
=
const
[],
this
.
detailRooms
=
const
[],
});
RoomReportState
copyWith
({
List
<
FloorInfo
>?
floors
,
bool
?
isLoading
,
String
?
error
,
RoomOverviewSummaryBO
?
summary
,
List
<
RoomFloorBO
>?
floors
,
int
?
selectedFloorIndex
,
List
<
StatusStat
>?
statusStats
,
List
<
RoomInfo
>?
detailRooms
,
List
<
RoomGridBO
>?
roomGrid
,
List
<
RoomDetailListBO
>?
detailRooms
,
bool
clearError
=
false
,
})
{
return
RoomReportState
(
isLoading:
isLoading
??
this
.
isLoading
,
error:
clearError
?
null
:
(
error
??
this
.
error
),
summary:
summary
??
this
.
summary
,
floors:
floors
??
this
.
floors
,
selectedFloorIndex:
selectedFloorIndex
??
this
.
selectedFloorIndex
,
statusStats:
statusStats
??
this
.
statusStats
,
roomGrid:
roomGrid
??
this
.
roomGrid
,
detailRooms:
detailRooms
??
this
.
detailRooms
,
);
}
@override
List
<
Object
?>
get
props
=>
[
isLoading
,
error
,
summary
,
floors
,
selectedFloorIndex
,
statusStats
,
roomGrid
,
detailRooms
,
];
}
lib/views/report/room/room_detail_view.dart
View file @
a8c199a0
...
...
@@ -2,6 +2,8 @@ import 'package:auto_route/auto_route.dart';
import
'package:flutter/material.dart'
;
import
'package:flutter_bloc/flutter_bloc.dart'
;
import
'package:flutter_screenutil/flutter_screenutil.dart'
;
import
'package:smart_hotel_app/repositories/room_repository.dart'
;
import
'package:smart_hotel_app/services/room_service.dart'
;
import
'package:smart_hotel_app/views/report/room/cubit/room_report_cubit.dart'
;
import
'package:smart_hotel_app/views/report/room/cubit/room_report_state.dart'
;
import
'package:smart_hotel_app/views/report/room/widget/floor_selector.dart'
;
...
...
@@ -16,22 +18,72 @@ class ReportRoomDetailView extends StatelessWidget {
@override
Widget
build
(
BuildContext
context
)
{
return
BlocProvider
(
create:
(
_
)
=>
RoomReportCubit
(),
create:
(
_
)
=>
RoomReportCubit
(
service:
RoomService
(
repository:
RoomRepository
()),
),
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
()),
);
}
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
,
color:
Colors
.
black
),
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
(
backgroundColor:
Colors
.
white
,
elevation:
0
,
leading:
IconButton
(
icon:
const
Icon
(
Icons
.
arrow_back_ios
,
color:
Colors
.
black
),
onPressed:
()
{
context
.
popRoute
();
context
.
maybePop
();
},
),
title:
Text
(
'客房
详情
'
,
'客房
状态总览
'
,
style:
TextStyle
(
color:
Colors
.
black
,
fontSize:
32
.
sp
,
...
...
@@ -66,12 +118,16 @@ class ReportRoomDetailView extends StatelessWidget {
borderRadius:
BorderRadius
.
vertical
(
bottom:
Radius
.
circular
(
24
.
r
)),
),
padding:
EdgeInsets
.
only
(
bottom:
28
.
w
),
child:
RoomGrid
(
rooms:
state
.
floors
[
state
.
selectedFloorIndex
].
rooms
),
child:
RoomGrid
(
rooms:
state
.
roomGrid
),
),
SizedBox
(
height:
10
.
h
),
if
(
state
.
statusStats
.
isNotEmpty
)
StatusStats
(
stats:
state
.
statusStats
),
StatusStats
(
summary:
state
.
summary
),
SizedBox
(
height:
10
.
h
),
if
(
state
.
isLoading
)
const
Padding
(
padding:
EdgeInsets
.
all
(
16.0
),
child:
Center
(
child:
CircularProgressIndicator
()),
),
...
state
.
detailRooms
.
map
((
room
)
{
return
Padding
(
padding:
EdgeInsets
.
only
(
bottom:
10
.
h
),
...
...
lib/views/report/room/widget/room_detail_card.dart
View file @
a8c199a0
import
'package:flutter/material.dart'
;
import
'package:flutter_screenutil/flutter_screenutil.dart'
;
import
'package:smart_hotel_app/
views/report/room/cubit/room_report_state
.dart'
;
import
'package:smart_hotel_app/
models/bo/room_bo
.dart'
;
class
RoomDetailCard
extends
StatelessWidget
{
final
Room
Info
room
;
final
Room
DetailListBO
room
;
const
RoomDetailCard
({
super
.
key
,
required
this
.
room
});
...
...
@@ -41,7 +41,7 @@ class RoomDetailCard extends StatelessWidget {
Row
(
children:
[
Text
(
room
.
roomNumber
,
room
.
room
BasicInfo
.
room
Number
,
style:
TextStyle
(
fontSize:
36
.
sp
,
fontWeight:
FontWeight
.
bold
,
...
...
@@ -50,7 +50,7 @@ class RoomDetailCard extends StatelessWidget {
),
SizedBox
(
width:
16
.
w
),
Text
(
'入住中'
,
room
.
roomBasicInfo
.
roomStatusText
,
style:
TextStyle
(
fontSize:
28
.
sp
,
color:
const
Color
.
fromRGBO
(
59
,
130
,
246
,
1
),
...
...
@@ -63,17 +63,17 @@ class RoomDetailCard extends StatelessWidget {
children:
[
_StatusTag
(
label:
'有人'
,
isActive:
room
.
deviceStatus
.
hasPerson
,
isActive:
room
.
statusTags
.
isOccupied
,
),
SizedBox
(
width:
12
.
w
),
_StatusTag
(
label:
'通电中'
,
isActive:
room
.
deviceStatus
.
powerOn
,
isActive:
room
.
statusTags
.
powerSupplyStatus
,
),
SizedBox
(
width:
12
.
w
),
_StatusTag
(
label:
'wifi正常'
,
isActive:
room
.
deviceStatus
.
wifiNormal
,
isActive:
room
.
statusTags
.
wifiNormalStatus
,
),
],
),
...
...
@@ -93,32 +93,7 @@ class RoomDetailCard extends StatelessWidget {
crossAxisCount:
2
,
mainAxisSpacing:
16
.
h
,
crossAxisSpacing:
16
.
w
,
children:
[
_DeviceControl
(
icon:
Icons
.
air_outlined
,
label:
'空调'
,
value:
'
${room.deviceStatus.acTemperature}
°C·
${room.deviceStatus.acOn ? '开' : '关'}
'
,
isOn:
room
.
deviceStatus
.
acOn
,
),
_DeviceControl
(
icon:
Icons
.
light_outlined
,
label:
'照明'
,
value:
room
.
deviceStatus
.
lightOn
?
'开'
:
'关'
,
isOn:
room
.
deviceStatus
.
lightOn
,
),
_DeviceControl
(
icon:
Icons
.
tv_outlined
,
label:
'电视'
,
value:
room
.
deviceStatus
.
tvOn
?
'开'
:
'关'
,
isOn:
room
.
deviceStatus
.
tvOn
,
),
_DeviceControl
(
icon:
Icons
.
curtains_outlined
,
label:
'窗帘'
,
value:
room
.
deviceStatus
.
curtainOn
?
'开'
:
'关'
,
isOn:
room
.
deviceStatus
.
curtainOn
,
),
],
children:
room
.
deviceList
.
map
((
device
)
=>
_DeviceControl
(
device:
device
)).
toList
(),
),
],
),
...
...
@@ -169,20 +144,28 @@ class _StatusTag extends StatelessWidget {
}
class
_DeviceControl
extends
StatelessWidget
{
final
IconData
icon
;
final
String
label
;
final
String
value
;
final
bool
isOn
;
final
DeviceItemBO
device
;
const
_DeviceControl
({
required
this
.
icon
,
required
this
.
label
,
required
this
.
value
,
required
this
.
isOn
,
});
const
_DeviceControl
({
required
this
.
device
});
IconData
_icon
()
{
switch
(
device
.
deviceTypeIcon
)
{
case
'ac'
:
return
Icons
.
air_outlined
;
case
'light'
:
return
Icons
.
light_outlined
;
case
'tv'
:
return
Icons
.
tv_outlined
;
case
'curtain'
:
return
Icons
.
curtains_outlined
;
default
:
return
Icons
.
devices_other
;
}
}
@override
Widget
build
(
BuildContext
context
)
{
final
isOn
=
device
.
powerStatus
==
'1'
;
return
Container
(
padding:
EdgeInsets
.
symmetric
(
horizontal:
24
.
w
,
vertical:
24
.
h
),
decoration:
BoxDecoration
(
...
...
@@ -192,13 +175,13 @@ class _DeviceControl extends StatelessWidget {
child:
Row
(
children:
[
Icon
(
icon
,
_icon
()
,
color:
const
Color
.
fromRGBO
(
59
,
130
,
246
,
1
),
size:
40
.
sp
,
),
SizedBox
(
width:
16
.
w
),
Text
(
label
,
device
.
deviceName
,
style:
TextStyle
(
fontSize:
28
.
sp
,
color:
const
Color
.
fromRGBO
(
59
,
130
,
246
,
1
),
...
...
@@ -207,7 +190,7 @@ class _DeviceControl extends StatelessWidget {
),
const
Spacer
(),
Text
(
value
,
device
.
paramDisplayText
.
isNotEmpty
?
device
.
paramDisplayText
:
device
.
powerStatusText
,
style:
TextStyle
(
fontSize:
26
.
sp
,
color:
isOn
...
...
lib/views/report/room/widget/room_grid.dart
View file @
a8c199a0
import
'package:flutter/material.dart'
;
import
'package:flutter_screenutil/flutter_screenutil.dart'
;
import
'package:smart_hotel_app/
views/report/room/cubit/room_report_state
.dart'
;
import
'package:smart_hotel_app/
models/bo/room_bo
.dart'
;
class
RoomGrid
extends
StatelessWidget
{
final
List
<
Room
Info
>
rooms
;
final
List
<
Room
GridBO
>
rooms
;
const
RoomGrid
({
super
.
key
,
required
this
.
rooms
});
...
...
@@ -18,53 +18,64 @@ class RoomGrid extends StatelessWidget {
crossAxisCount:
5
,
mainAxisSpacing:
20
.
h
,
crossAxisSpacing:
16
.
w
,
children:
rooms
.
map
((
room
)
=>
RoomItem
(
room:
room
)).
toList
(),
children:
rooms
.
map
((
room
)
=>
_
RoomItem
(
room:
room
)).
toList
(),
),
);
}
}
class
RoomItem
extends
StatelessWidget
{
final
Room
Info
room
;
class
_
RoomItem
extends
StatelessWidget
{
final
Room
GridBO
room
;
const
RoomItem
({
super
.
key
,
required
this
.
room
});
const
_RoomItem
({
required
this
.
room
});
Color
get
_bgColor
{
switch
(
room
.
status
)
{
case
RoomStatus
.
vacant
:
return
const
Color
.
fromRGBO
(
229
,
231
,
235
,
1
);
case
RoomStatus
.
occupied
:
case
RoomStatus
.
reserved
:
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
RoomStatus
.
warning
:
case
RoomStatus
.
temperature
:
case
'dnd'
:
return
const
Color
.
fromRGBO
(
254
,
243
,
199
,
1
);
case
'checkout'
:
case
'idle'
:
default
:
return
const
Color
.
fromRGBO
(
229
,
231
,
235
,
1
);
}
}
Color
get
_textColor
{
switch
(
room
.
status
)
{
case
RoomStatus
.
vacant
:
return
const
Color
.
fromRGBO
(
156
,
163
,
175
,
1
);
case
RoomStatus
.
occupied
:
case
RoomStatus
.
reserved
:
switch
(
room
.
statusColor
)
{
case
'occupied'
:
return
const
Color
.
fromRGBO
(
59
,
130
,
246
,
1
);
case
RoomStatus
.
warning
:
case
RoomStatus
.
temperature
:
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'
:
default
:
return
const
Color
.
fromRGBO
(
156
,
163
,
175
,
1
);
}
}
Color
?
get
_borderColor
{
switch
(
room
.
status
)
{
case
RoomStatus
.
vacant
:
switch
(
room
.
statusColor
)
{
case
'idle'
:
case
'checkout'
:
return
null
;
case
RoomStatus
.
occupied
:
case
RoomStatus
.
reserved
:
case
'occupied'
:
case
'reserved'
:
return
const
Color
.
fromRGBO
(
59
,
130
,
246
,
1
);
case
RoomStatus
.
warning
:
case
RoomStatus
.
temperature
:
case
'alarm'
:
case
'dnd'
:
return
const
Color
.
fromRGBO
(
245
,
158
,
11
,
1
);
default
:
return
null
;
}
}
...
...
@@ -91,9 +102,9 @@ class RoomItem extends StatelessWidget {
),
),
SizedBox
(
height:
4
.
h
),
if
(
room
.
status
==
RoomStatus
.
warning
||
room
.
status
==
RoomStatus
.
temperature
)
if
(
room
.
hasAlarmIcon
)
Icon
(
Icons
.
warning_amber_outlined
,
color:
_textColor
,
size:
20
.
sp
),
if
(
room
.
status
!=
RoomStatus
.
warning
&&
room
.
status
!=
RoomStatus
.
temperature
)
if
(
!
room
.
hasAlarmIcon
)
Text
(
room
.
statusText
,
style:
TextStyle
(
...
...
lib/views/report/room/widget/status_stats.dart
View file @
a8c199a0
import
'package:flutter/material.dart'
;
import
'package:flutter_screenutil/flutter_screenutil.dart'
;
import
'package:smart_hotel_app/
views/report/room/cubit/room_report_state
.dart'
;
import
'package:smart_hotel_app/
models/bo/room_bo
.dart'
;
class
StatusStats
extends
StatelessWidget
{
final
List
<
StatusStat
>
stats
;
final
RoomOverviewSummaryBO
summary
;
const
StatusStats
({
super
.
key
,
required
this
.
s
tats
});
const
StatusStats
({
super
.
key
,
required
this
.
s
ummary
});
@override
Widget
build
(
BuildContext
context
)
{
final
items
=
[
_StatItemData
(
label:
'入住'
,
count:
summary
.
occupiedCount
,
color:
const
Color
(
0xFF4A7CF7
)),
_StatItemData
(
label:
'空闲'
,
count:
summary
.
vacantCount
,
color:
const
Color
(
0xFF4CAF50
)),
_StatItemData
(
label:
'预定'
,
count:
summary
.
reservedCount
,
color:
const
Color
(
0xFF9C27B0
)),
_StatItemData
(
label:
'告警'
,
count:
summary
.
alarmCount
,
color:
const
Color
(
0xFFFF9800
)),
];
return
Container
(
width:
double
.
infinity
,
padding:
EdgeInsets
.
all
(
28
.
w
),
...
...
@@ -18,32 +25,39 @@ class StatusStats extends StatelessWidget {
),
child:
Row
(
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
children:
stats
.
map
((
stat
)
=>
StatItem
(
stat:
stat
)).
toList
(),
children:
items
.
map
((
item
)
=>
_StatItem
(
item:
item
)).
toList
(),
),
);
}
}
class
StatItem
extends
StatelessWidget
{
final
StatusStat
stat
;
class
_StatItemData
{
final
String
label
;
final
int
count
;
final
Color
color
;
const
_StatItemData
({
required
this
.
label
,
required
this
.
count
,
required
this
.
color
});
}
class
_StatItem
extends
StatelessWidget
{
final
_StatItemData
item
;
const
StatItem
({
super
.
key
,
required
this
.
stat
});
const
_StatItem
({
required
this
.
item
});
@override
Widget
build
(
BuildContext
context
)
{
return
Column
(
children:
[
Text
(
stat
.
count
.
toString
(),
item
.
count
.
toString
(),
style:
TextStyle
(
fontSize:
40
.
sp
,
fontWeight:
FontWeight
.
bold
,
color:
stat
.
color
,
color:
item
.
color
,
),
),
SizedBox
(
height:
4
.
h
),
Text
(
stat
.
label
,
item
.
label
,
style:
TextStyle
(
fontSize:
24
.
sp
,
color:
const
Color
.
fromRGBO
(
156
,
163
,
175
,
1
),
...
...
lib/views/report/rule/rule_management_view.dart
View file @
a8c199a0
...
...
@@ -22,7 +22,7 @@ class ReportRuleManagementView extends StatelessWidget {
backgroundColor:
Colors
.
white
,
elevation:
0
,
leading:
IconButton
(
icon:
const
Icon
(
Icons
.
arrow_back
,
color:
Colors
.
black
),
icon:
const
Icon
(
Icons
.
arrow_back
_ios
,
color:
Colors
.
black
),
onPressed:
()
{
context
.
popRoute
();
},
...
...
对接方案.md
View file @
a8c199a0
# 智慧酒
店 App 接口对接方案
# 智慧酒
店 App 接口对接方案
...
...
@@ -241,13 +241,109 @@ class xxxBO extends Equatable {
|
客房服务看板-统计 |GET | /app/room/statistics | 已对接 | 客房服务看板-统计 | |
|
非客房-相关楼层区域设备 |GET | /app/room/area/devices | 未对接 | 非客房-相关楼层区域设备 | |
|
非客房-相关楼层区域设备 |GET | /app/room/area/devices | 已对接 | 非客房-相关楼层区域设备 | |
|
客房状态总览-客房概览统计 |GET | /app/room/overview | 已对接 | 客房状态总览-客房概览统计 | |
#
## 3.2 接口详细定义
#
### 客房状态总览-客房概览统计
*
*GET /app/room/overview**
`
``
请
求体:
{
"floorId":0
}
响
应 data:
{
"code": 0,
"msg": "string",
"data": {
"summary": { // 顶部统计区域(页面中间一排数字卡片:45入住 7空闲 5披订 3告警)
"occupiedCount": 0, // 入住数量
"vacantCount": 0, // 空闲数量
"reservedCount": 0, // 预定数量
"alarmCount": 0 // 告警数量
},
"floorList": [ // 楼层Tab列表
{
"floorId": 0,
"floorName": "string",
"isActive": true
}
],
"roomGrid": [ // 房间网格区域(页面主体上半部分的房间卡片网格布局,每格显示房间号+状态文字+颜色背景),按楼层分组展示
{
"roomId": 0,
"roomNumber": "string",
"statusText": "string", // 房间状态文字,显示在房间号下方小字,由 hotel_room.sell_status 和 hotel_room.occupancy_status 综合判断映射:sell_status='0'且occupancy_status='0'→'空闲';sell_status='3'且occupancy_status='1'→'入住·在房';sell_status='4'→'维修/告警'(黄色);sell_status='2'→'预定';dnd_status='1'→'勿扰';checkout_status='1'→'退房'
"statusColor": "string", // 状态颜色类型:idle=灰色(green/默认) occupied=蓝色(blue) alarm=黄色(yellow/orange) reserved=紫色(purple) dnd=橙色(orange) checkout=灰色(gray),根据 statusText 对应,用于渲染格子背景色
"hasAlarmIcon": true // 是否显示告警图标(⚠️):true=显示黄色警告图标 false=不显示
}
],
"roomDetailList": [ // 房间详情列表
{
"roomBasicInfo": { // 房间基础信息区
"roomId": 0,
"roomNumber": "string",
"roomStatusText": "string"
},
"statusTags": { // 状态标签区(一行绿色勾选标签:✓有人 ✓通电中 ✓wif正常)
"isOccupied": true, // 是否已入住
"powerSupplyStatus": true, // 供电状态(true=供电中,false=已断电)
"wifiNormalStatus": true // WiFi是否正常(true=正常,false=异常)
},
"deviceList": [ // 房间设备列表
{
"deviceId": 0,
"deviceName": "string",
"deviceTypeIcon": "string", // 设备图标标识:ac=❄️空调 light=💡照明 tv=📺电视 curtain=🪟窗帘
"powerStatus": "string", // 电源开关状态码:0=关闭 1=开启
"powerStatusText": "string", // 开关状态文字:'开'(绿色)/'关'(灰色),根据 power_status 映射:'1'→'开' '0'→'关'
"temperatureDisplay": "string", // 温度显示文本
"paramDisplayText": "string" // 完整参数显示文本,最终展示值,拼接规则:有温度→'{temperature}°c·{powerStatusText}'(如'24°C·开');无温度→直接显示 powerStatusText(如'开'/'关')
}
]
}
]
}
}
`
``
#
### 客房状态总览
*
*GET /app/hotel/floor/list**
`
``
请
求体:
{
}
响
应 data:
{
"code": 0,
"msg": "string",
"data": {
"total": 0,
"floors": [
{
"floorId": 0,
"floorName": "string"
}
]
}
}
`
``
#
### 非客房-相关楼层区域设备
*
*GET /app/room/area/devices**
...
...
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