Commit 4f547179 authored by 张宏's avatar 张宏

智能酒店页面开发

parent 165f6cbb
{
"flutter": "3.38.10"
}
\ No newline at end of file
......@@ -5,9 +5,11 @@
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/
# IntelliJ related
......@@ -42,3 +44,6 @@ app.*.map.json
/android/app/debug
/android/app/profile
/android/app/release
# FVM Version Cache
.fvm/
\ No newline at end of file
......@@ -4,7 +4,7 @@
# This file should be version controlled and should not be manually edited.
version:
revision: "67457e669f79e9f8d13d7a68fe09775fefbb79f4"
revision: "c6f67dede3d4aa1aa7a69dd56a3494a5cde6cc80"
channel: "stable"
project_type: app
......@@ -13,26 +13,11 @@ project_type: app
migration:
platforms:
- platform: root
create_revision: 67457e669f79e9f8d13d7a68fe09775fefbb79f4
base_revision: 67457e669f79e9f8d13d7a68fe09775fefbb79f4
- platform: android
create_revision: 67457e669f79e9f8d13d7a68fe09775fefbb79f4
base_revision: 67457e669f79e9f8d13d7a68fe09775fefbb79f4
create_revision: c6f67dede3d4aa1aa7a69dd56a3494a5cde6cc80
base_revision: c6f67dede3d4aa1aa7a69dd56a3494a5cde6cc80
- platform: ios
create_revision: 67457e669f79e9f8d13d7a68fe09775fefbb79f4
base_revision: 67457e669f79e9f8d13d7a68fe09775fefbb79f4
- platform: linux
create_revision: 67457e669f79e9f8d13d7a68fe09775fefbb79f4
base_revision: 67457e669f79e9f8d13d7a68fe09775fefbb79f4
- platform: macos
create_revision: 67457e669f79e9f8d13d7a68fe09775fefbb79f4
base_revision: 67457e669f79e9f8d13d7a68fe09775fefbb79f4
- platform: web
create_revision: 67457e669f79e9f8d13d7a68fe09775fefbb79f4
base_revision: 67457e669f79e9f8d13d7a68fe09775fefbb79f4
- platform: windows
create_revision: 67457e669f79e9f8d13d7a68fe09775fefbb79f4
base_revision: 67457e669f79e9f8d13d7a68fe09775fefbb79f4
create_revision: c6f67dede3d4aa1aa7a69dd56a3494a5cde6cc80
base_revision: c6f67dede3d4aa1aa7a69dd56a3494a5cde6cc80
# User provided section
......
{
"dart.flutterSdkPath": ".fvm/versions/3.38.10"
}
\ No newline at end of file
This diff is collapsed.
plugins {
id("com.android.application")
id("kotlin-android")
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id("dev.flutter.flutter-gradle-plugin")
}
android {
namespace = "com.example.smart_hotel_app"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.example.smart_hotel_app"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.getByName("debug")
}
}
}
flutter {
source = "../.."
}
allprojects {
repositories {
google()
mavenCentral()
}
}
val newBuildDir: Directory =
rootProject.layout.buildDirectory
.dir("../../build")
.get()
rootProject.layout.buildDirectory.value(newBuildDir)
subprojects {
val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
project.layout.buildDirectory.value(newSubprojectBuildDir)
}
subprojects {
project.evaluationDependsOn(":app")
}
tasks.register<Delete>("clean") {
delete(rootProject.layout.buildDirectory)
}
pluginManagement {
val flutterSdkPath =
run {
val properties = java.util.Properties()
file("local.properties").inputStream().use { properties.load(it) }
val flutterSdkPath = properties.getProperty("flutter.sdk")
require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
flutterSdkPath
}
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.11.1" apply false
id("org.jetbrains.kotlin.android") version "2.2.20" apply false
}
include(":app")
......@@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>12.0</string>
<string>13.0</string>
</dict>
</plist>
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
FLTEnableImpeller=NO
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
FLTEnableImpeller=NO
# Uncomment this line to define a global platform for your project
# platform :ios, '12.0'
# platform :ios, '13.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
......
......@@ -31,12 +31,12 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
SPEC CHECKSUMS:
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13
fluttertoast: eaafdac812d1d69931004e881c87a8643b1c9111
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e
PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796
PODFILE CHECKSUM: 4f1c12611da7338d21589c0b2ecd6bd20b109694
COCOAPODS: 1.16.2
This diff is collapsed.
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
......@@ -26,6 +26,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
......@@ -54,11 +55,13 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
enableGPUValidationMode = "1"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
......
import UIKit
import Flutter
@UIApplicationMain
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
......
......@@ -101,10 +101,10 @@ class _MyAppState extends State<MyApp> {
backgroundColor: Colors.red,
),
);
_appRouter.replaceAll([const LoginRoute()]);
_appRouter.replaceAll([LoginRoute()]);
}
if (state is AuthLoggedOut) {
_appRouter.replaceAll([const LoginRoute()]);
_appRouter.replaceAll([LoginRoute()]);
}
},
),
......
......@@ -25,5 +25,9 @@ class AppRouter extends $AppRouter {
AutoRoute(page: ServiceRoomDetailRoute.page),
AutoRoute(page: ReportEnergyDetailRoute.page),
AutoRoute(page: ReportRoomDetailRoute.page),
AutoRoute(page: ServiceDeviceDetailRoute.page),
AutoRoute(page: InspectionDetailRoute.page),
AutoRoute(page: InspectionDeviceRoute.page),
AutoRoute(page: InspectionHistoryRoute.page),
];
}
This diff is collapsed.
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/views/home/abnormal/child/alarm_info_block.dart';
import 'package:smart_hotel_app/views/home/abnormal/child/temperature_block.dart';
import 'package:smart_hotel_app/views/home/abnormal/child/temperature_char_block.dart';
import 'package:smart_hotel_app/views/home/abnormal/controller.dart';
import 'package:smart_hotel_app/routes/app_router.gr.dart';
import 'package:smart_hotel_app/views/home/abnormal/cubit/abnormal_cubit.dart';
import 'package:smart_hotel_app/views/home/abnormal/cubit/abnormal_state.dart';
import 'package:smart_hotel_app/views/home/abnormal/widget/alarm_info_block.dart';
import 'package:smart_hotel_app/views/home/abnormal/widget/temperature_block.dart';
import 'package:smart_hotel_app/views/home/abnormal/widget/temperature_char_block.dart';
@RoutePage()
class AbnormalDetailView extends StatefulWidget {
class AbnormalDetailView extends StatelessWidget {
const AbnormalDetailView({super.key});
@override
State<AbnormalDetailView> createState() => _AbnormalDetailViewState();
}
class _AbnormalDetailViewState extends State<AbnormalDetailView> {
late final HomeAbnormalController _controller;
late final AlarmInfo _alarmInfo;
@override
void initState() {
super.initState();
_controller = HomeAbnormalController(context);
_alarmInfo = _controller.getAlarmInfo();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color.fromRGBO(242, 243, 245, 1),
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
leading: IconButton(
icon: Icon(Icons.arrow_back_ios, color: Color.fromRGBO(100, 116, 139, 1),),
onPressed: () {
context.popRoute();
},
),
title: Text(
'告警详情',
style: TextStyle(
color: Color.fromRGBO(10, 13, 20, 1),
fontSize: 32.sp,
fontWeight: FontWeight.w200,
return BlocProvider(
create: (_) => AbnormalCubit(),
child: 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.popRoute();
},
),
title: Text(
'告警详情',
style: TextStyle(
color: const Color.fromRGBO(10, 13, 20, 1),
fontSize: 32.sp,
fontWeight: FontWeight.w200,
),
),
centerTitle: true,
),
centerTitle: true,
),
body: SingleChildScrollView(
padding: EdgeInsets.symmetric(horizontal: 28.w),
child: Column(
children: [
TemperatureBlock(alarmInfo: _alarmInfo),
SizedBox(height: 10.h),
TemperatureCharBlock(alarmInfo: _alarmInfo),
SizedBox(height: 10.h),
AlarmInfoBlock(alarmInfo: _alarmInfo),
SizedBox(height: 10.h),
_buildBottomButtons(),
SizedBox(height: 30.h),
],
body: SingleChildScrollView(
padding: EdgeInsets.symmetric(horizontal: 28.w),
child: BlocBuilder<AbnormalCubit, AbnormalState>(
builder: (context, state) {
final cubit = context.read<AbnormalCubit>();
return Column(
children: [
TemperatureBlock(alarmInfo: state.alarmInfo),
SizedBox(height: 10.h),
TemperatureCharBlock(alarmInfo: state.alarmInfo),
SizedBox(height: 10.h),
AlarmInfoBlock(alarmInfo: state.alarmInfo),
SizedBox(height: 10.h),
_buildBottomButtons(context, cubit),
SizedBox(height: 30.h),
],
);
},
),
),
),
);
}
Widget _buildBottomButtons() {
Widget _buildBottomButtons(BuildContext context, AbnormalCubit cubit) {
return Container(
width: double.infinity,
padding: EdgeInsets.symmetric(vertical: 15.h),
......@@ -77,16 +74,16 @@ class _AbnormalDetailViewState extends State<AbnormalDetailView> {
width: double.infinity,
height: 88.h,
decoration: BoxDecoration(
color: Color.fromRGBO(73, 149, 234, 1),
color: const Color.fromRGBO(73, 149, 234, 1),
borderRadius: BorderRadius.circular(16.r),
),
child: TextButton(
onPressed: _controller.handleConfirm,
onPressed: () => cubit.handleConfirm(),
child: Text(
'确认处理',
style: TextStyle(
fontSize: 28.sp,
color: Color.fromRGBO(237, 245, 255, 1),
color: const Color.fromRGBO(237, 245, 255, 1),
),
),
),
......@@ -96,23 +93,29 @@ class _AbnormalDetailViewState extends State<AbnormalDetailView> {
width: double.infinity,
height: 88.h,
decoration: BoxDecoration(
color: Color.fromRGBO(100, 116, 139, 0.2),
color: const Color.fromRGBO(100, 116, 139, 0.2),
borderRadius: BorderRadius.circular(16.r),
),
child: TextButton(
onPressed: _controller.handleViewDevice,
onPressed: () {
context.pushRoute(const DeviceDetailRoute());
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'查看关联设备',
'设备控制',
style: TextStyle(
fontSize: 28.sp,
color: Color.fromRGBO(100, 116, 139, 1),
color: const Color.fromRGBO(100, 116, 139, 1),
),
),
SizedBox(width: 8.w),
Image.asset("lib/assets/icon/right.png", width: 24.w, height: 21.h,),
Image.asset(
"lib/assets/icon/right.png",
width: 24.w,
height: 21.h,
),
],
),
),
......
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:smart_hotel_app/views/home/abnormal/cubit/abnormal_state.dart';
class AbnormalCubit extends Cubit<AbnormalState> {
AbnormalCubit() : super(AbnormalState(alarmInfo: _getAlarmInfo()));
static AlarmInfo _getAlarmInfo() {
return const AlarmInfo(
alarmType: '温度异常告警',
deviceInfo: 'A-301智能空开·客房301',
alarmLevel: '高警',
urgency: '紧急',
currentTemp: '85℃',
voltage: '22V',
current: '3.2A',
triggerTime: '2026-05-09 14:23:18',
threshold: '温度>80℃触发',
duration: '2小时37分钟',
peak: '89.2℃ (15:02)',
linkAction: '自动断电保护',
date: '5月9日',
temperatureSpots: [
FlSpot(0, 40),
FlSpot(4, 40),
FlSpot(8, 15),
FlSpot(12, 55),
FlSpot(16, 50),
FlSpot(18, 55),
FlSpot(20, 75),
FlSpot(22, 82),
FlSpot(24, 85),
],
);
}
void handleConfirm() {
print('确认处理');
}
}
\ No newline at end of file
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:smart_hotel_app/routes/app_router.gr.dart';
class AlarmInfo {
final String alarmType;
......@@ -19,7 +16,7 @@ class AlarmInfo {
final String date;
final List<FlSpot> temperatureSpots;
AlarmInfo({
const AlarmInfo({
required this.alarmType,
required this.deviceInfo,
required this.alarmLevel,
......@@ -37,49 +34,10 @@ class AlarmInfo {
});
}
class HomeAbnormalController {
final BuildContext context;
class AbnormalState {
final AlarmInfo alarmInfo;
HomeAbnormalController(this.context);
void navigateToDeviceDetail() {
context.pushRoute(const DeviceDetailRoute());
}
AlarmInfo getAlarmInfo() {
return AlarmInfo(
alarmType: '温度异常告警',
deviceInfo: 'A-301智能空开·客房301',
alarmLevel: '高警',
urgency: '紧急',
currentTemp: '85℃',
voltage: '22V',
current: '3.2A',
triggerTime: '2026-05-09 14:23:18',
threshold: '温度>80℃触发',
duration: '2小时37分钟',
peak: '89.2℃ (15:02)',
linkAction: '自动断电保护',
date: '5月9日',
temperatureSpots: [
FlSpot(0, 40),
FlSpot(4, 40),
FlSpot(8, 15),
FlSpot(12, 55),
FlSpot(16, 50),
FlSpot(18, 55),
FlSpot(20, 75),
FlSpot(22, 82),
FlSpot(24, 85),
],
);
}
void handleConfirm() {
print('确认处理');
}
void handleViewDevice() {
navigateToDeviceDetail();
}
const AbnormalState({
required this.alarmInfo,
});
}
\ No newline at end of file
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:smart_hotel_app/views/home/abnormal/controller.dart';
import 'package:smart_hotel_app/views/home/abnormal/cubit/abnormal_state.dart';
class AlarmInfoBlock extends StatelessWidget {
final AlarmInfo alarmInfo;
......
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:smart_hotel_app/views/home/abnormal/controller.dart';
import 'package:smart_hotel_app/views/home/abnormal/cubit/abnormal_state.dart';
class TemperatureBlock extends StatelessWidget {
final AlarmInfo alarmInfo;
......
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:smart_hotel_app/views/home/abnormal/controller.dart';
import 'package:smart_hotel_app/views/home/abnormal/cubit/abnormal_state.dart';
class TemperatureCharBlock extends StatelessWidget {
final AlarmInfo alarmInfo;
......
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:smart_hotel_app/views/home/device/cubit/device_state.dart';
class DeviceCubit extends Cubit<DeviceState> {
DeviceCubit() : super(DeviceState(deviceInfo: _getDeviceInfo()));
static DeviceInfo _getDeviceInfo() {
return const DeviceInfo(
deviceName: '客房305 智能空开',
deviceId: 'A-305',
location: '3楼客房区',
status: '在线',
voltage: '220V',
current: '2.36A',
power: '520W',
temperature: '38°C',
powerData: [
40, 60, 85, 98, 85, 72, 60, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0
],
tempData: [
28, 30, 32, 35, 33, 31, 30, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0
],
);
}
void remotePowerOn() {}
void remotePowerOff() {}
}
\ No newline at end of file
import 'package:flutter/material.dart';
import 'package:fl_chart/fl_chart.dart';
class DeviceInfo {
final String deviceName;
final String deviceId;
......@@ -13,7 +10,7 @@ class DeviceInfo {
final List<int> powerData;
final List<int> tempData;
DeviceInfo({
const DeviceInfo({
required this.deviceName,
required this.deviceId,
required this.location,
......@@ -27,31 +24,8 @@ class DeviceInfo {
});
}
class HomeDeviceController {
final BuildContext context;
HomeDeviceController(this.context);
void remotePowerOn() {
// 远程送电
}
void remotePowerOff() {
// 远程断电
}
class DeviceState {
final DeviceInfo deviceInfo;
DeviceInfo getDeviceInfo() {
return DeviceInfo(
deviceName: '客房305 智能空开',
deviceId: 'A-305',
location: '3楼客房区',
status: '在线',
voltage: '220V',
current: '2.36A',
power: '520W',
temperature: '38°C',
powerData: [40, 60, 85, 98, 85, 72, 60, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
tempData: [28, 30, 32, 35, 33, 31, 30, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
);
}
const DeviceState({required this.deviceInfo});
}
\ No newline at end of file
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/views/home/device/child/device_parms_block.dart';
import 'package:smart_hotel_app/views/home/device/child/power_char_block.dart';
import 'package:smart_hotel_app/views/home/device/child/state_info_block.dart';
import 'package:smart_hotel_app/views/home/device/controller.dart';
import 'package:smart_hotel_app/views/home/device/cubit/device_cubit.dart';
import 'package:smart_hotel_app/views/home/device/cubit/device_state.dart';
import 'package:smart_hotel_app/views/home/device/widget/device_parms_block.dart';
import 'package:smart_hotel_app/views/home/device/widget/power_char_block.dart';
import 'package:smart_hotel_app/views/home/device/widget/state_info_block.dart';
@RoutePage()
class DeviceDetailView extends StatefulWidget {
class DeviceDetailView extends StatelessWidget {
const DeviceDetailView({super.key});
@override
State<DeviceDetailView> createState() => _DeviceDetailViewState();
}
class _DeviceDetailViewState extends State<DeviceDetailView> {
late final HomeDeviceController _controller;
late final DeviceInfo _deviceInfo;
@override
void initState() {
super.initState();
_controller = HomeDeviceController(context);
_deviceInfo = _controller.getDeviceInfo();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color.fromRGBO(242, 243, 245, 1),
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
leading: IconButton(
icon: Icon(Icons.arrow_back_ios, color: Color.fromRGBO(100, 116, 139, 1),),
onPressed: () {
context.popRoute();
},
),
title: Text(
'设备详情',
style: TextStyle(
color: Color.fromRGBO(10, 13, 20, 1),
fontSize: 32.sp,
fontWeight: FontWeight.w200,
return BlocProvider(
create: (_) => DeviceCubit(),
child: 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.popRoute();
},
),
title: Text(
'设备控制',
style: TextStyle(
color: const Color.fromRGBO(10, 13, 20, 1),
fontSize: 32.sp,
fontWeight: FontWeight.w200,
),
),
centerTitle: true,
),
centerTitle: true,
),
body: SingleChildScrollView(
padding: EdgeInsets.symmetric(horizontal: 28.w),
child: Column(
children: [
StateInfoBlock(deviceInfo: _deviceInfo),
SizedBox(height: 10.h),
DeviceParmsBlock(deviceInfo: _deviceInfo),
SizedBox(height: 10.h),
PowerCharBlock(deviceInfo: _deviceInfo),
SizedBox(height: 10.h),
_buildBottomButtons(),
SizedBox(height: 30.h),
],
body: SingleChildScrollView(
padding: EdgeInsets.symmetric(horizontal: 28.w),
child: BlocBuilder<DeviceCubit, DeviceState>(
builder: (context, state) {
final cubit = context.read<DeviceCubit>();
return Column(
children: [
StateInfoBlock(deviceInfo: state.deviceInfo),
SizedBox(height: 10.h),
DeviceParmsBlock(deviceInfo: state.deviceInfo),
SizedBox(height: 10.h),
PowerCharBlock(deviceInfo: state.deviceInfo),
SizedBox(height: 10.h),
_buildBottomButtons(cubit),
SizedBox(height: 30.h),
],
);
},
),
),
),
);
}
Widget _buildBottomButtons() {
Widget _buildBottomButtons(DeviceCubit cubit) {
return Container(
width: double.infinity,
padding: EdgeInsets.symmetric(vertical: 15.h),
......@@ -78,34 +74,38 @@ class _DeviceDetailViewState extends State<DeviceDetailView> {
width: 300.w,
height: 88.h,
decoration: BoxDecoration(
color: Color.fromRGBO(73, 149, 234, 1),
color: const Color.fromRGBO(73, 149, 234, 1),
borderRadius: BorderRadius.circular(16.r),
),
child: TextButton(
onPressed: _controller.remotePowerOn,
onPressed: () => cubit.remotePowerOn(),
child: Text(
"远程送电",
style: TextStyle(fontSize: 28.sp, color: Color.fromRGBO(237, 245, 255, 1)),
style: TextStyle(
fontSize: 28.sp,
color: const Color.fromRGBO(237, 245, 255, 1)),
),
),
),
SizedBox(width: 30.w,),
SizedBox(width: 30.w),
Container(
width: 300.w,
height: 88.h,
decoration: BoxDecoration(
color: Color.fromRGBO(229, 241, 255, 1.0),
color: const Color.fromRGBO(229, 241, 255, 1.0),
border: Border.all(
color: Color.fromRGBO(73, 149, 234, 1),
color: const Color.fromRGBO(73, 149, 234, 1),
width: 1.w,
),
borderRadius: BorderRadius.circular(16.r),
),
child: TextButton(
onPressed: _controller.remotePowerOff,
onPressed: () => cubit.remotePowerOff(),
child: Text(
"远程断电",
style: TextStyle(fontSize: 28.sp, color: Color.fromRGBO(100, 116, 139, 1)),
style: TextStyle(
fontSize: 28.sp,
color: const Color.fromRGBO(100, 116, 139, 1)),
),
),
),
......
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:smart_hotel_app/views/home/device/controller.dart';
import 'package:smart_hotel_app/views/home/device/cubit/device_state.dart';
class DeviceParmsBlock extends StatelessWidget {
final DeviceInfo deviceInfo;
......
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:smart_hotel_app/views/home/device/controller.dart';
import 'package:smart_hotel_app/views/home/device/cubit/device_state.dart';
class PowerCharBlock extends StatefulWidget {
final DeviceInfo deviceInfo;
......
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:smart_hotel_app/views/home/device/controller.dart';
import 'package:smart_hotel_app/views/home/device/cubit/device_state.dart';
class StateInfoBlock extends StatelessWidget {
final DeviceInfo deviceInfo;
......
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
// import 'package:smart_hotel_app/routes/app_router.dart';
import 'package:smart_hotel_app/routes/app_router.gr.dart';
class EquipmentItem {
final IconData icon;
final String title;
final String subtitle;
final String status;
final Color statusColor;
EquipmentItem({
required this.icon,
required this.title,
required this.subtitle,
required this.status,
required this.statusColor,
});
}
class HomeIndexController {
final BuildContext context;
HomeIndexController(this.context);
void handleToAbnormalDetail() {
context.pushRoute(const AbnormalDetailRoute());
}
int getPendingTaskCount() {
return 3;
}
int getHighAlarmCount() {
return 1;
}
int getMediumAlarmCount() {
return 1;
}
int getOnlineDeviceCount() {
return 48;
}
String getTemperatureAlarmTitle() {
return '温度异常告警';
}
String getTemperatureAlarmLocation() {
return '客房301·A-301智能空开';
}
String getCurrentTemperature() {
return '85';
}
String getCurrentVoltage() {
return '220';
}
String getCurrentCurrent() {
return '3.2';
}
String getWaitTime() {
return '2h37m';
}
String getAlarmLevel() {
return '紧急';
}
String getVoltageAlarmTitle() {
return '电压波动告警';
}
String getVoltageAlarmLocation() {
return '会议室2F-01';
}
String getVoltageValue() {
return '248';
}
String getNormalVoltage() {
return '235';
}
String getVoltageWaitTime() {
return '1h19m';
}
String getVoltageAlarmLevel() {
return '中警';
}
List<EquipmentItem> getEquipmentList() {
return [
EquipmentItem(
icon: Icons.power,
title: '客房305空开',
subtitle: '38°C·220V·520W',
status: '正常',
statusColor: Color.fromRGBO(26, 188, 156, 1.0),
),
EquipmentItem(
icon: Icons.power,
title: '大堂总空开L-01',
subtitle: '380V·42A·28.6kW',
status: '正常',
statusColor: Color.fromRGBO(26, 188, 156, 1.0),
),
EquipmentItem(
icon: Icons.wifi,
title: '招牌AP-03',
subtitle: '信号弱·RSSI-78dBm',
status: '注意',
statusColor: Color.fromRGBO(255, 193, 7, 1.0),
),
EquipmentItem(
icon: Icons.power,
title: '客房201空开',
subtitle: '25°C·220V·380W',
status: '正常',
statusColor: Color.fromRGBO(26, 188, 156, 1.0),
),
EquipmentItem(
icon: Icons.power,
title: '水泵控制箱',
subtitle: '运行中·2.2kW',
status: '正常',
statusColor: Color.fromRGBO(26, 188, 156, 1.0),
),
EquipmentItem(
icon: Icons.thermostat,
title: '冷库温控器',
subtitle: '-18°C·异常波动',
status: '告警',
statusColor: Color.fromRGBO(255, 77, 79, 1.0),
),
EquipmentItem(
icon: Icons.light,
title: '走廊照明回路',
subtitle: '12盏·功率240W',
status: '正常',
statusColor: Color.fromRGBO(26, 188, 156, 1.0),
),
EquipmentItem(
icon: Icons.wifi,
title: '会议室AP-01',
subtitle: '连接数12·信号强',
status: '正常',
statusColor: Color.fromRGBO(26, 188, 156, 1.0),
),
EquipmentItem(
icon: Icons.power,
title: '电梯控制柜',
subtitle: '运行中·电压稳定',
status: '正常',
statusColor: Color.fromRGBO(26, 188, 156, 1.0),
),
EquipmentItem(
icon: Icons.thermostat,
title: '空调外机',
subtitle: '压力偏高·需检查',
status: '注意',
statusColor: Color.fromRGBO(255, 193, 7, 1.0),
),
EquipmentItem(
icon: Icons.wifi,
title: '楼道AP-05',
subtitle: '离线·需重启',
status: '离线',
statusColor: Color.fromRGBO(102, 102, 102, 1.0),
),
EquipmentItem(
icon: Icons.power,
title: '厨房消毒柜',
subtitle: '消毒中·温度120°C',
status: '正常',
statusColor: Color.fromRGBO(26, 188, 156, 1.0),
),
];
}
}
\ No newline at end of file
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:smart_hotel_app/views/home/index/cubit/home_index_state.dart';
import 'package:flutter/material.dart';
class HomeIndexCubit extends Cubit<HomeIndexState> {
HomeIndexCubit() : super(const HomeIndexState()) {
initData();
}
void initData() {
emit(state.copyWith(
highAlarmCount: 1,
mediumAlarmCount: 1,
onlineDeviceCount: 48,
temperatureAlarmTitle: '温度异常告警',
temperatureAlarmLocation: '客房301·A-301智能空开',
currentTemperature: '85',
currentVoltage: '220',
currentCurrent: '3.2',
waitTime: '2h37m',
alarmLevel: '紧急',
voltageAlarmTitle: '电压波动告警',
voltageAlarmLocation: '会议室2F-01',
voltageValue: '248',
normalVoltage: '235',
voltageWaitTime: '1h19m',
voltageAlarmLevel: '中警',
equipmentList: const [
EquipmentItem(icon: Icons.power, title: '客房305空开', subtitle: '38°C·220V·520W', status: '正常', statusColor: Color.fromRGBO(26, 188, 156, 1.0)),
EquipmentItem(icon: Icons.power, title: '大堂总空开L-01', subtitle: '380V·42A·28.6kW', status: '正常', statusColor: Color.fromRGBO(26, 188, 156, 1.0)),
EquipmentItem(icon: Icons.wifi, title: '招牌AP-03', subtitle: '信号弱·RSSI-78dBm', status: '注意', statusColor: Color.fromRGBO(255, 193, 7, 1.0)),
EquipmentItem(icon: Icons.power, title: '客房201空开', subtitle: '25°C·220V·380W', status: '正常', statusColor: Color.fromRGBO(26, 188, 156, 1.0)),
EquipmentItem(icon: Icons.power, title: '水泵控制箱', subtitle: '运行中·2.2kW', status: '正常', statusColor: Color.fromRGBO(26, 188, 156, 1.0)),
EquipmentItem(icon: Icons.thermostat, title: '冷库温控器', subtitle: '-18°C·异常波动', status: '告警', statusColor: Color.fromRGBO(255, 77, 79, 1.0)),
EquipmentItem(icon: Icons.light, title: '走廊照明回路', subtitle: '12盏·功率240W', status: '正常', statusColor: Color.fromRGBO(26, 188, 156, 1.0)),
EquipmentItem(icon: Icons.wifi, title: '会议室AP-01', subtitle: '连接数12·信号强', status: '正常', statusColor: Color.fromRGBO(26, 188, 156, 1.0)),
EquipmentItem(icon: Icons.power, title: '电梯控制柜', subtitle: '运行中·电压稳定', status: '正常', statusColor: Color.fromRGBO(26, 188, 156, 1.0)),
EquipmentItem(icon: Icons.thermostat, title: '空调外机', subtitle: '压力偏高·需检查', status: '注意', statusColor: Color.fromRGBO(255, 193, 7, 1.0)),
EquipmentItem(icon: Icons.wifi, title: '楼道AP-05', subtitle: '离线·需重启', status: '离线', statusColor: Color.fromRGBO(102, 102, 102, 1.0)),
EquipmentItem(icon: Icons.power, title: '厨房消毒柜', subtitle: '消毒中·温度120°C', status: '正常', statusColor: Color.fromRGBO(26, 188, 156, 1.0)),
],
));
}
}
\ No newline at end of file
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
class EquipmentItem {
final IconData icon;
final String title;
final String subtitle;
final String status;
final Color statusColor;
const EquipmentItem({
required this.icon,
required this.title,
required this.subtitle,
required this.status,
required this.statusColor,
});
}
class HomeIndexState extends Equatable {
final int highAlarmCount;
final int mediumAlarmCount;
final int onlineDeviceCount;
final String temperatureAlarmTitle;
final String temperatureAlarmLocation;
final String currentTemperature;
final String currentVoltage;
final String currentCurrent;
final String waitTime;
final String alarmLevel;
final String voltageAlarmTitle;
final String voltageAlarmLocation;
final String voltageValue;
final String normalVoltage;
final String voltageWaitTime;
final String voltageAlarmLevel;
final List<EquipmentItem> equipmentList;
const HomeIndexState({
this.highAlarmCount = 0,
this.mediumAlarmCount = 0,
this.onlineDeviceCount = 0,
this.temperatureAlarmTitle = '',
this.temperatureAlarmLocation = '',
this.currentTemperature = '',
this.currentVoltage = '',
this.currentCurrent = '',
this.waitTime = '',
this.alarmLevel = '',
this.voltageAlarmTitle = '',
this.voltageAlarmLocation = '',
this.voltageValue = '',
this.normalVoltage = '',
this.voltageWaitTime = '',
this.voltageAlarmLevel = '',
this.equipmentList = const [],
});
HomeIndexState copyWith({
int? highAlarmCount,
int? mediumAlarmCount,
int? onlineDeviceCount,
String? temperatureAlarmTitle,
String? temperatureAlarmLocation,
String? currentTemperature,
String? currentVoltage,
String? currentCurrent,
String? waitTime,
String? alarmLevel,
String? voltageAlarmTitle,
String? voltageAlarmLocation,
String? voltageValue,
String? normalVoltage,
String? voltageWaitTime,
String? voltageAlarmLevel,
List<EquipmentItem>? equipmentList,
}) {
return HomeIndexState(
highAlarmCount: highAlarmCount ?? this.highAlarmCount,
mediumAlarmCount: mediumAlarmCount ?? this.mediumAlarmCount,
onlineDeviceCount: onlineDeviceCount ?? this.onlineDeviceCount,
temperatureAlarmTitle: temperatureAlarmTitle ?? this.temperatureAlarmTitle,
temperatureAlarmLocation: temperatureAlarmLocation ?? this.temperatureAlarmLocation,
currentTemperature: currentTemperature ?? this.currentTemperature,
currentVoltage: currentVoltage ?? this.currentVoltage,
currentCurrent: currentCurrent ?? this.currentCurrent,
waitTime: waitTime ?? this.waitTime,
alarmLevel: alarmLevel ?? this.alarmLevel,
voltageAlarmTitle: voltageAlarmTitle ?? this.voltageAlarmTitle,
voltageAlarmLocation: voltageAlarmLocation ?? this.voltageAlarmLocation,
voltageValue: voltageValue ?? this.voltageValue,
normalVoltage: normalVoltage ?? this.normalVoltage,
voltageWaitTime: voltageWaitTime ?? this.voltageWaitTime,
voltageAlarmLevel: voltageAlarmLevel ?? this.voltageAlarmLevel,
equipmentList: equipmentList ?? this.equipmentList,
);
}
@override
List<Object?> get props => [
highAlarmCount,
mediumAlarmCount,
onlineDeviceCount,
temperatureAlarmTitle,
temperatureAlarmLocation,
currentTemperature,
currentVoltage,
currentCurrent,
waitTime,
alarmLevel,
voltageAlarmTitle,
voltageAlarmLocation,
voltageValue,
normalVoltage,
voltageWaitTime,
voltageAlarmLevel,
equipmentList,
];
}
\ No newline at end of file
import 'dart:math';
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:smart_hotel_app/routes/app_router.gr.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:smart_hotel_app/views/home/index/child/equipment_list_block.dart';
import 'package:smart_hotel_app/views/home/index/child/task_head_block.dart';
import 'package:smart_hotel_app/views/home/index/child/task_total_block.dart';
import 'package:smart_hotel_app/views/home/index/child/temperature_block.dart';
import 'package:smart_hotel_app/views/home/index/child/voltage_operate_block.dart';
import 'package:smart_hotel_app/views/home/index/controller.dart';
import 'package:smart_hotel_app/views/home/index/cubit/home_index_cubit.dart';
import 'package:smart_hotel_app/views/home/index/cubit/home_index_state.dart';
import 'package:smart_hotel_app/views/home/index/widget/equipment_list_block.dart';
import 'package:smart_hotel_app/views/home/index/widget/task_head_block.dart';
import 'package:smart_hotel_app/views/home/index/widget/task_total_block.dart';
import 'package:smart_hotel_app/views/home/index/widget/temperature_block.dart';
import 'package:smart_hotel_app/views/home/index/widget/voltage_operate_block.dart';
@RoutePage()
class HomeView extends StatefulWidget {
class HomeView extends StatelessWidget {
const HomeView({super.key});
@override
State<HomeView> createState() => _HomeViewState();
}
class _HomeViewState extends State<HomeView> {
late final HomeIndexController _controller;
@override
void initState() {
super.initState();
_controller = HomeIndexController(context);
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight,
),
child: Container(
width: double.infinity,
color: Color.fromRGBO(242, 243, 245, 1),
padding: EdgeInsets.symmetric(horizontal: 28.w),
child: Column(
children: [
TaskHeadBlock(controller: _controller),
SizedBox(height: 20.h),
TaskTotalBlock(controller: _controller),
SizedBox(height: 20.h),
TemperatureBlock(controller: _controller),
SizedBox(height: 20.h),
VoltageOperateBlock(controller: _controller),
SizedBox(height: 20.h),
EquipmentListBlock(controller: _controller),
SizedBox(height: 20.h),
],
return BlocProvider(
create: (_) => HomeIndexCubit(),
child: LayoutBuilder(
builder: (context, constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight,
),
child: Container(
width: double.infinity,
color: const Color.fromRGBO(242, 243, 245, 1),
padding: EdgeInsets.symmetric(horizontal: 28.w),
child: BlocBuilder<HomeIndexCubit, HomeIndexState>(
builder: (context, state) {
return Column(
children: [
const TaskHeadBlock(),
SizedBox(height: 20.h),
TaskTotalBlock(
highAlarmCount: state.highAlarmCount,
mediumAlarmCount: state.mediumAlarmCount,
onlineDeviceCount: state.onlineDeviceCount,
),
SizedBox(height: 20.h),
TemperatureBlock(
temperatureAlarmTitle: state.temperatureAlarmTitle,
temperatureAlarmLocation: state.temperatureAlarmLocation,
currentTemperature: state.currentTemperature,
currentVoltage: state.currentVoltage,
currentCurrent: state.currentCurrent,
waitTime: state.waitTime,
alarmLevel: state.alarmLevel,
),
SizedBox(height: 20.h),
VoltageOperateBlock(
voltageAlarmTitle: state.voltageAlarmTitle,
voltageAlarmLocation: state.voltageAlarmLocation,
voltageValue: state.voltageValue,
normalVoltage: state.normalVoltage,
voltageWaitTime: state.voltageWaitTime,
voltageAlarmLevel: state.voltageAlarmLevel,
),
SizedBox(height: 20.h),
EquipmentListBlock(
equipmentList: state.equipmentList,
),
SizedBox(height: 20.h),
],
);
},
),
),
),
),
);
},
);
},
),
);
}
}
}
\ No newline at end of file
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:smart_hotel_app/views/home/index/controller.dart';
import 'package:smart_hotel_app/views/home/index/cubit/home_index_state.dart';
import 'package:smart_hotel_app/routes/app_router.gr.dart';
class EquipmentListBlock extends StatefulWidget {
final HomeIndexController controller;
const EquipmentListBlock({super.key, required this.controller});
class EquipmentListBlock extends StatelessWidget {
final List<EquipmentItem> equipmentList;
@override
State<EquipmentListBlock> createState() => _EquipmentListBlockState();
}
const EquipmentListBlock({
super.key,
required this.equipmentList,
});
class _EquipmentListBlockState extends State<EquipmentListBlock> {
Widget _buildEquipmentItem(EquipmentItem item) {
return Container(
padding: EdgeInsets.symmetric(vertical: 16.h),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Color.fromRGBO(235, 235, 235, 1.0),
color: const Color.fromRGBO(235, 235, 235, 1.0),
width: 1.w,
),
),
......@@ -29,12 +29,17 @@ class _EquipmentListBlockState extends State<EquipmentListBlock> {
width: 64.w,
height: 66.h,
decoration: BoxDecoration(
color: Color.fromRGBO(235, 244, 255, 1.0),
color: const Color.fromRGBO(235, 244, 255, 1.0),
borderRadius: BorderRadius.circular(12.r),
border: Border.all(
color: const Color.fromRGBO(66, 165, 245, 1.0),
width: 2.w,
style: BorderStyle.solid,
),
),
child: Icon(
item.icon,
color: Color.fromRGBO(66, 165, 245, 1.0),
color: const Color.fromRGBO(66, 165, 245, 1.0),
size: 32.sp,
),
),
......@@ -59,7 +64,7 @@ class _EquipmentListBlockState extends State<EquipmentListBlock> {
item.subtitle,
style: TextStyle(
fontSize: 24.sp,
color: Color.fromRGBO(100, 116, 139, 1.0),
color: const Color.fromRGBO(100, 116, 139, 1.0),
),
),
],
......@@ -80,11 +85,8 @@ class _EquipmentListBlockState extends State<EquipmentListBlock> {
@override
Widget build(BuildContext context) {
final equipmentList = widget.controller.getEquipmentList();
return Container(
width: double.infinity,
// margin: EdgeInsets.symmetric(horizontal: 14.w, vertical: 24.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
......@@ -100,28 +102,38 @@ class _EquipmentListBlockState extends State<EquipmentListBlock> {
color: Colors.black,
),
),
Text(
'查看全部>',
style: TextStyle(
fontSize: 28.sp,
fontWeight: FontWeight.bold,
color: Color.fromRGBO(73, 149, 234, 1.0),
GestureDetector(
onTap: () {
context.pushRoute(const InspectionDetailRoute());
},
child: Row(
children: [
Text(
'查看全部',
style: TextStyle(
fontSize: 24.sp,
color: const Color.fromRGBO(66, 165, 245, 1.0),
),
),
Icon(
Icons.chevron_right,
color: const Color.fromRGBO(66, 165, 245, 1.0),
size: 24.sp,
),
],
),
),
],
),
SizedBox(height: 12.h),
Container(
width: double.infinity,
padding: EdgeInsets.only(top: 20.w,bottom: 20.w,left: 42.w,right: 42.w),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20.r),
color: Colors.white,
borderRadius: BorderRadius.circular(20.r),
),
padding: EdgeInsets.symmetric(horizontal: 20.w),
child: Column(
children: [
...equipmentList.map((item) => _buildEquipmentItem(item)).toList(),
],
children: equipmentList.take(3).map((item) => _buildEquipmentItem(item)).toList(),
),
),
],
......
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:smart_hotel_app/views/home/index/controller.dart';
class TaskHeadBlock extends StatefulWidget {
final HomeIndexController controller;
const TaskHeadBlock({super.key, required this.controller});
class TaskHeadBlock extends StatelessWidget {
const TaskHeadBlock({super.key});
@override
State<TaskHeadBlock> createState() => _TaskHeadBlockState();
}
class _TaskHeadBlockState extends State<TaskHeadBlock> {
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
// height: 140.h,
padding: EdgeInsets.only(top: ScreenUtil().statusBarHeight),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'设备告警看板',
style: TextStyle(
fontSize: 28.sp,
fontWeight: FontWeight.bold,
color: Color.fromRGBO(51, 51, 51, 1.0),
),
),
SizedBox(height: 8.h),
Text(
'工程运维视角·今日${widget.controller.getPendingTaskCount()}条待处理',
style: TextStyle(
fontSize: 16.sp,
color: Color.fromRGBO(102, 102, 102, 1.0),
),
),
],
child: Text(
'设备告警看板',
style: TextStyle(
fontSize: 28.sp,
fontWeight: FontWeight.bold,
color: const Color.fromRGBO(51, 51, 51, 1.0),
),
),
);
}
......
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:smart_hotel_app/views/home/index/controller.dart';
class TaskTotalBlock extends StatefulWidget {
final HomeIndexController controller;
const TaskTotalBlock({super.key, required this.controller});
class TaskTotalBlock extends StatelessWidget {
final int highAlarmCount;
final int mediumAlarmCount;
final int onlineDeviceCount;
@override
State<TaskTotalBlock> createState() => _TaskTotalBlockState();
}
const TaskTotalBlock({
super.key,
required this.highAlarmCount,
required this.mediumAlarmCount,
required this.onlineDeviceCount,
});
class _TaskTotalBlockState extends State<TaskTotalBlock> {
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
// height: 180.h,
child: Flex(
direction: Axis.horizontal,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// 高警待处理
Expanded(
child: Container(
margin: EdgeInsets.only(right: 10.w),
......@@ -35,21 +34,20 @@ class _TaskTotalBlockState extends State<TaskTotalBlock> {
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'${widget.controller.getHighAlarmCount()}',
'$highAlarmCount',
style: TextStyle(
fontSize: 48.sp,
fontWeight: FontWeight.bold,
color: Color.fromRGBO(255, 77, 79, 1.0),
),
color: const Color.fromRGBO(255, 77, 79, 1.0),
),
// SizedBox(height: 5.h),
),
Transform.translate(
offset: Offset(0, -8.h),
child: Text(
'高警待处理',
style: TextStyle(
fontSize: 24.sp,
color: Color.fromRGBO(100, 116, 139, 1.0),
color: const Color.fromRGBO(100, 116, 139, 1.0),
),
),
),
......@@ -57,8 +55,6 @@ class _TaskTotalBlockState extends State<TaskTotalBlock> {
),
),
),
// 中警待处理
Expanded(
child: Container(
margin: EdgeInsets.symmetric(horizontal: 10.w),
......@@ -71,21 +67,20 @@ class _TaskTotalBlockState extends State<TaskTotalBlock> {
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'${widget.controller.getMediumAlarmCount()}',
'$mediumAlarmCount',
style: TextStyle(
fontSize: 48.sp,
fontWeight: FontWeight.bold,
color: Color.fromRGBO(100, 116, 139, 1.0),
color: const Color.fromRGBO(100, 116, 139, 1.0),
),
),
// SizedBox(height: 8.h),
Transform.translate(
offset: Offset(0, -8.h),
child: Text(
'中警待处理',
style: TextStyle(
fontSize: 24.sp,
color: Color.fromRGBO(100, 116, 139, 1.0),
color: const Color.fromRGBO(100, 116, 139, 1.0),
),
),
),
......@@ -93,8 +88,6 @@ class _TaskTotalBlockState extends State<TaskTotalBlock> {
),
),
),
// 设备在线
Expanded(
child: Container(
margin: EdgeInsets.only(left: 10.w),
......@@ -107,21 +100,20 @@ class _TaskTotalBlockState extends State<TaskTotalBlock> {
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'${widget.controller.getOnlineDeviceCount()}',
'$onlineDeviceCount',
style: TextStyle(
fontSize: 48.sp,
fontWeight: FontWeight.bold,
color: Color.fromRGBO(26, 188, 156, 1.0),
color: const Color.fromRGBO(34, 197, 94, 1.0),
),
),
// SizedBox(height: 8.h),
Transform.translate(
offset: Offset(0, -8.h),
child: Text(
'设备在线',
style: TextStyle(
fontSize: 24.sp,
color: Color.fromRGBO(100, 116, 139, 1.0),
color: const Color.fromRGBO(100, 116, 139, 1.0),
),
),
),
......
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:smart_hotel_app/views/home/index/controller.dart';
import 'package:smart_hotel_app/routes/app_router.gr.dart';
class VoltageOperateBlock extends StatefulWidget {
final HomeIndexController controller;
const VoltageOperateBlock({super.key, required this.controller});
class VoltageOperateBlock extends StatelessWidget {
final String voltageAlarmTitle;
final String voltageAlarmLocation;
final String voltageValue;
final String normalVoltage;
final String voltageWaitTime;
final String voltageAlarmLevel;
@override
State<VoltageOperateBlock> createState() => _VoltageOperateBlockState();
}
const VoltageOperateBlock({
super.key,
required this.voltageAlarmTitle,
required this.voltageAlarmLocation,
required this.voltageValue,
required this.normalVoltage,
required this.voltageWaitTime,
required this.voltageAlarmLevel,
});
class _VoltageOperateBlockState extends State<VoltageOperateBlock> {
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
// margin: EdgeInsets.symmetric(horizontal: 14.w, vertical: 24.h),
padding: EdgeInsets.all(20.w),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20.r),
......@@ -25,7 +33,6 @@ class _VoltageOperateBlockState extends State<VoltageOperateBlock> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 描述信息
Container(
width: double.infinity,
margin: EdgeInsets.only(bottom: 16.h),
......@@ -36,12 +43,17 @@ class _VoltageOperateBlockState extends State<VoltageOperateBlock> {
width: 64.w,
height: 66.h,
decoration: BoxDecoration(
color: Color.fromRGBO(235, 244, 255, 1.0),
color: const Color.fromRGBO(235, 244, 255, 1.0),
borderRadius: BorderRadius.circular(12.r),
border: Border.all(
color: const Color.fromRGBO(66, 165, 245, 1.0),
width: 2.w,
style: BorderStyle.solid,
),
),
child: Icon(
Icons.thermostat_outlined,
color: Color.fromRGBO(66, 165, 245, 1.0),
color: const Color.fromRGBO(66, 165, 245, 1.0),
size: 32.sp,
),
),
......@@ -51,7 +63,7 @@ class _VoltageOperateBlockState extends State<VoltageOperateBlock> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.controller.getVoltageAlarmTitle(),
voltageAlarmTitle,
style: TextStyle(
fontSize: 28.sp,
fontWeight: FontWeight.bold,
......@@ -60,88 +72,82 @@ class _VoltageOperateBlockState extends State<VoltageOperateBlock> {
),
SizedBox(height: 4.h),
Text(
widget.controller.getVoltageAlarmLocation(),
voltageAlarmLocation,
style: TextStyle(
fontSize: 24.sp,
color: Color.fromRGBO(100, 116, 139, 1.0),
color: const Color.fromRGBO(100, 116, 139, 1.0),
),
),
],
),
),
Text(
widget.controller.getVoltageAlarmLevel(),
voltageAlarmLevel,
style: TextStyle(
fontSize: 24.sp,
fontWeight: FontWeight.bold,
color: Color.fromRGBO(255, 204, 21, 1.0),
color: const Color.fromRGBO(255, 204, 21, 1.0),
),
),
],
),
),
// 数据行
Container(
width: double.infinity,
margin: EdgeInsets.only(left: 80.w,bottom: 10.h),
margin: EdgeInsets.only(left: 80.w, bottom: 20.h),
child: Text(
'电压${widget.controller.getVoltageValue()}V (正常≤${widget.controller.getNormalVoltage()}V)·已等待${widget.controller.getVoltageWaitTime()}',
'电压${voltageValue}V (正常≤${normalVoltage}V)·已等待$voltageWaitTime',
style: TextStyle(
fontSize: 24.sp,
color: Color.fromRGBO(100, 116, 139, 1.0),
color: const Color.fromRGBO(100, 116, 139, 1.0),
),
),
),
// 操作行
Container(
width: double.infinity,
child: Row(
children: [
Expanded(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Color.fromRGBO(73, 149, 234, 1.0),
minimumSize: Size(0, 60.h),
padding: EdgeInsets.symmetric(vertical: 16.h),
shape: RoundedRectangleBorder(
child: GestureDetector(
onTap: () {},
child: Container(
height: 88.h,
decoration: BoxDecoration(
color: const Color.fromRGBO(66, 165, 245, 1.0),
borderRadius: BorderRadius.circular(16.r),
),
),
onPressed: () {
print("确认处理");
},
child: Text(
"确认处理",
style: TextStyle(
fontSize: 24.sp,
color: Colors.white,
fontWeight: FontWeight.w200,
child: Center(
child: Text(
'确认处理',
style: TextStyle(
fontSize: 28.sp,
color: Colors.white,
),
),
),
),
),
),
SizedBox(width: 16.w),
SizedBox(width: 20.w),
Expanded(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Color.fromRGBO(229, 241, 255, 1.0),
minimumSize: Size(0, 60.h),
padding: EdgeInsets.symmetric(vertical: 16.h),
shape: RoundedRectangleBorder(
child: GestureDetector(
onTap: () {
context.pushRoute(const AbnormalDetailRoute());
},
child: Container(
height: 88.h,
decoration: BoxDecoration(
color: const Color.fromRGBO(235, 244, 255, 1.0),
borderRadius: BorderRadius.circular(16.r),
),
),
onPressed: () {
widget.controller.handleToAbnormalDetail();
},
child: Text(
"查看详情",
style: TextStyle(
fontSize: 24.sp,
color: Color.fromRGBO(100, 116, 139, 1.0),
fontWeight: FontWeight.bold,
child: Center(
child: Text(
'查看详情',
style: TextStyle(
fontSize: 28.sp,
color: const Color.fromRGBO(100, 116, 139, 1.0),
),
),
),
),
),
......
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:smart_hotel_app/views/home/inspection/cubit/inspection_state.dart';
class InspectionCubit extends Cubit<InspectionState> {
static const List<String> allRooms = [
'客厅',
'主卧',
'玄关',
'厨房',
'书房',
'阳台',
];
static final List<InspectionDevice> allDevices = [
InspectionDevice(
id: '1',
icon: Icons.wifi,
name: '客厅主控网关',
type: '智能网关',
location: '客厅电视柜',
status: '正常',
time: '2024-01-15 14:30:00',
statusColor: const Color.fromRGBO(26, 188, 156, 1.0),
),
InspectionDevice(
id: '2',
icon: Icons.thermostat,
name: '温湿度传感器-客厅',
type: '传感器',
location: '客厅角落',
status: '正常',
time: '2024-01-15 14:25:00',
statusColor: const Color.fromRGBO(26, 188, 156, 1.0),
),
InspectionDevice(
id: '3',
icon: Icons.videocam,
name: '智能摄像头-门口',
type: '摄像头',
location: '入户门口',
status: '告警',
time: '2024-01-15 10:00:00',
statusColor: const Color.fromRGBO(255, 77, 79, 1.0),
),
InspectionDevice(
id: '4',
icon: Icons.light,
name: '智能吸顶灯-主卧',
type: '传感器',
location: '主卧天花板',
status: '正常',
time: '2024-01-15 14:25:00',
statusColor: const Color.fromRGBO(26, 188, 156, 1.0),
),
InspectionDevice(
id: '5',
icon: Icons.thermostat,
name: '温湿度传感器-主卧',
type: '传感器',
location: '主卧角落',
status: '正常',
time: '2024-01-15 14:25:00',
statusColor: const Color.fromRGBO(26, 188, 156, 1.0),
),
InspectionDevice(
id: '6',
icon: Icons.light,
name: '玄关灯-智能',
type: '智能灯',
location: '玄关',
status: '开启',
time: '2024-01-15 14:20:00',
statusColor: const Color.fromRGBO(26, 188, 156, 1.0),
),
InspectionDevice(
id: '7',
icon: Icons.sensor_door,
name: '门窗传感器-阳台',
type: '传感器',
location: '阳台门',
status: '离线',
time: '2024-01-15 12:00:00',
statusColor: const Color.fromRGBO(102, 102, 102, 1.0),
),
InspectionDevice(
id: '8',
icon: Icons.kitchen,
name: '智能插座-厨房',
type: '插座',
location: '厨房台面',
status: '关闭',
time: '2024-01-15 14:15:00',
statusColor: const Color.fromRGBO(100, 116, 139, 1.0),
),
InspectionDevice(
id: '9',
icon: Icons.smoke_free,
name: '烟雾报警器-厨房',
type: '报警器',
location: '厨房天花板',
status: '正常',
time: '2024-01-15 14:30:00',
statusColor: const Color.fromRGBO(26, 188, 156, 1.0),
),
InspectionDevice(
id: '10',
icon: Icons.desk,
name: '智能台灯-书房',
type: '智能灯',
location: '书房书桌',
status: '关闭',
time: '2024-01-15 14:10:00',
statusColor: const Color.fromRGBO(100, 116, 139, 1.0),
),
InspectionDevice(
id: '11',
icon: Icons.ac_unit,
name: '智能空调-客厅',
type: '空调',
location: '客厅',
status: '正常',
time: '2024-01-15 14:30:00',
statusColor: const Color.fromRGBO(26, 188, 156, 1.0),
),
];
InspectionCubit() : super(const InspectionState()) {
initData();
}
void initData() {
emit(state.copyWith(deviceList: _filterDevices()));
}
void selectTab(DeviceStatusTab tab) {
emit(state.copyWith(selectedTab: tab, deviceList: _filterDevices(selectedTab: tab)));
}
void toggleRoom(String room) {
final List<String> newSelectedRooms = List.from(state.selectedRooms);
if (newSelectedRooms.contains(room)) {
newSelectedRooms.remove(room);
} else {
newSelectedRooms.add(room);
}
emit(state.copyWith(
selectedRooms: newSelectedRooms,
deviceList: _filterDevices(selectedRooms: newSelectedRooms),
));
}
List<InspectionDevice> _filterDevices({
DeviceStatusTab? selectedTab,
List<String>? selectedRooms,
}) {
DeviceStatusTab tab = selectedTab ?? state.selectedTab;
List<String> rooms = selectedRooms ?? state.selectedRooms;
List<InspectionDevice> filtered = List.from(allDevices);
if (rooms.isNotEmpty) {
filtered = filtered.where((device) {
return rooms.any((room) => device.location.contains(room) || device.name.contains(room));
}).toList();
}
if (tab != DeviceStatusTab.total) {
filtered = filtered.where((device) {
switch (tab) {
case DeviceStatusTab.on:
return device.status == '正常' || device.status == '开启';
case DeviceStatusTab.off:
return device.status == '关闭';
case DeviceStatusTab.offline:
return device.status == '离线';
case DeviceStatusTab.alarm:
return device.status == '告警';
case DeviceStatusTab.fault:
return device.status == '故障';
default:
return true;
}
}).toList();
}
return filtered;
}
}
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
enum DeviceStatusTab { total, on, off, offline, alarm, fault }
class InspectionDevice {
final String id;
final IconData icon;
final String name;
final String type;
final String location;
final String status;
final String time;
final Color statusColor;
const InspectionDevice({
required this.id,
required this.icon,
required this.name,
required this.type,
required this.location,
required this.status,
required this.time,
required this.statusColor,
});
}
class InspectionState extends Equatable {
final DeviceStatusTab selectedTab;
final List<String> selectedRooms;
final List<InspectionDevice> deviceList;
final Map<DeviceStatusTab, int> tabCounts;
const InspectionState({
this.selectedTab = DeviceStatusTab.total,
this.selectedRooms = const ['客厅'],
this.deviceList = const [],
this.tabCounts = const {
DeviceStatusTab.total: 11,
DeviceStatusTab.on: 7,
DeviceStatusTab.off: 2,
DeviceStatusTab.offline: 1,
DeviceStatusTab.alarm: 1,
DeviceStatusTab.fault: 0,
},
});
InspectionState copyWith({
DeviceStatusTab? selectedTab,
List<String>? selectedRooms,
List<InspectionDevice>? deviceList,
Map<DeviceStatusTab, int>? tabCounts,
}) {
return InspectionState(
selectedTab: selectedTab ?? this.selectedTab,
selectedRooms: selectedRooms ?? this.selectedRooms,
deviceList: deviceList ?? this.deviceList,
tabCounts: tabCounts ?? this.tabCounts,
);
}
@override
List<Object?> get props => [
selectedTab,
selectedRooms,
deviceList,
tabCounts,
];
}
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/views/home/inspection/cubit/inspection_cubit.dart';
import 'package:smart_hotel_app/views/home/inspection/cubit/inspection_state.dart';
import 'package:smart_hotel_app/views/home/inspection/widget/filter_panel.dart';
import 'package:smart_hotel_app/views/home/inspection/widget/device_list.dart';
@RoutePage()
class InspectionDetailView extends StatelessWidget {
const InspectionDetailView({super.key});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => InspectionCubit(),
child: 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.popRoute();
},
),
title: Text(
'设备巡检',
style: TextStyle(
color: const Color.fromRGBO(10, 13, 20, 1),
fontSize: 32.sp,
fontWeight: FontWeight.w200,
),
),
centerTitle: true,
),
body: SingleChildScrollView(
padding: EdgeInsets.symmetric(horizontal: 28.w, vertical: 20.h),
child: BlocBuilder<InspectionCubit, InspectionState>(
builder: (context, state) {
final cubit = context.read<InspectionCubit>();
return Column(
children: [
FilterPanel(
selectedTab: state.selectedTab,
tabCounts: state.tabCounts,
onTabSelected: (tab) => cubit.selectTab(tab),
selectedRooms: state.selectedRooms,
onRoomToggle: (room) => cubit.toggleRoom(room),
),
SizedBox(height: 20.h),
DeviceList(deviceList: state.deviceList),
SizedBox(height: 30.h),
],
);
},
),
),
),
);
}
}
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:smart_hotel_app/routes/app_router.gr.dart';
import 'package:smart_hotel_app/views/home/inspection/cubit/inspection_state.dart';
import 'package:smart_hotel_app/views/home/inspection/widget/device_list_item.dart';
class DeviceList extends StatelessWidget {
final List<InspectionDevice> deviceList;
const DeviceList({
super.key,
required this.deviceList,
});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'设备列表 (${deviceList.length})',
style: TextStyle(
fontSize: 28.sp,
fontWeight: FontWeight.bold,
color: const Color.fromRGBO(10, 13, 20, 1.0),
),
),
SizedBox(height: 12.h),
ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: deviceList.length,
separatorBuilder: (context, index) => SizedBox(height: 12.h),
itemBuilder: (context, index) {
return DeviceListItem(
device: deviceList[index],
onTap: () {
context.pushRoute(
InspectionDeviceRoute(deviceId: deviceList[index].id),
);
},
);
},
),
],
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:smart_hotel_app/views/home/inspection/cubit/inspection_state.dart';
class DeviceListItem extends StatelessWidget {
final InspectionDevice device;
final VoidCallback? onTap;
const DeviceListItem({
super.key,
required this.device,
this.onTap,
});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20.r),
),
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 16.h),
child: Row(
children: [
Container(
width: 64.w,
height: 66.h,
decoration: BoxDecoration(
color: const Color.fromRGBO(235, 244, 255, 1.0),
borderRadius: BorderRadius.circular(12.r),
border: Border.all(
color: const Color.fromRGBO(66, 165, 245, 1.0),
width: 1.w,
style: BorderStyle.solid,
),
),
child: Icon(
device.icon,
color: const Color.fromRGBO(66, 165, 245, 1.0),
size: 32.sp,
),
),
SizedBox(width: 16.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
device.name,
style: TextStyle(
fontSize: 28.sp,
fontWeight: FontWeight.bold,
color: const Color.fromRGBO(10, 13, 20, 1.0),
),
),
SizedBox(width: 8.w),
Text(
device.status,
style: TextStyle(
fontSize: 24.sp,
fontWeight: FontWeight.bold,
color: device.statusColor,
),
),
],
),
SizedBox(height: 4.h),
Text(
'${device.type} | ${device.location}',
style: TextStyle(
fontSize: 24.sp,
color: const Color.fromRGBO(100, 116, 139, 1.0),
),
),
SizedBox(height: 4.h),
Row(
children: [
Icon(
Icons.access_time,
color: const Color.fromRGBO(100, 116, 139, 1.0),
size: 20.sp,
),
SizedBox(width: 4.w),
Text(
device.time,
style: TextStyle(
fontSize: 20.sp,
color: const Color.fromRGBO(100, 116, 139, 1.0),
),
),
],
),
],
),
),
Container(
width: 48.w,
height: 48.h,
decoration: BoxDecoration(
color: const Color.fromRGBO(242, 243, 245, 1.0),
borderRadius: BorderRadius.circular(8.r),
),
child: Icon(
Icons.chevron_right,
color: const Color.fromRGBO(100, 116, 139, 1.0),
size: 24.sp,
),
),
],
),
),
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../cubit/inspection_state.dart';
class FilterPanel extends StatelessWidget {
final DeviceStatusTab selectedTab;
final Map<DeviceStatusTab, int> tabCounts;
final Function(DeviceStatusTab) onTabSelected;
final List<String> selectedRooms;
final Function(String) onRoomToggle;
const FilterPanel({
super.key,
required this.selectedTab,
required this.tabCounts,
required this.onTabSelected,
required this.selectedRooms,
required this.onRoomToggle,
});
static const List<String> allRooms = [
'客厅',
'主卧',
'玄关',
'厨房',
'书房',
'阳台',
];
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20.r),
),
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 24.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildStatusTabs(),
SizedBox(height: 32.h),
_buildRoomSelector(),
],
),
);
}
Widget _buildStatusTabs() {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: DeviceStatusTab.values.map((tab) {
final isSelected = selectedTab == tab;
final count = tabCounts[tab] ?? 0;
return Expanded(
child: GestureDetector(
onTap: () => onTabSelected(tab),
child: Stack(
clipBehavior: Clip.none,
alignment: Alignment.center,
children: [
Padding(
padding: EdgeInsets.only(top: 8.h),
child: Text(
_getTabLabel(tab),
style: TextStyle(
fontSize: 36.sp,
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
color: isSelected
? const Color.fromRGBO(66, 165, 245, 1.0)
: const Color.fromRGBO(100, 116, 139, 1.0),
),
),
),
Positioned(
top: 0,
right: -8.w,
child: Container(
width: 56.w,
height: 56.h,
decoration: BoxDecoration(
color: _getTabColor(tab),
borderRadius: BorderRadius.circular(28.r),
),
child: Center(
child: Text(
count.toString(),
style: TextStyle(
fontSize: 24.sp,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
),
),
],
),
),
);
}).toList(),
),
SizedBox(height: 12.h),
Container(
height: 4.h,
width: double.infinity,
color: const Color.fromRGBO(242, 243, 245, 1),
child: Stack(
children: [
LayoutBuilder(
builder: (context, constraints) {
final segmentWidth = constraints.maxWidth / DeviceStatusTab.values.length;
final selectedIndex = DeviceStatusTab.values.indexOf(selectedTab);
return AnimatedPositioned(
duration: const Duration(milliseconds: 200),
left: selectedIndex * segmentWidth,
width: segmentWidth,
child: Container(
height: 4.h,
color: const Color.fromRGBO(66, 165, 245, 1.0),
),
);
},
),
],
),
),
],
);
}
Widget _buildRoomSelector() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'全部区域',
style: TextStyle(
fontSize: 28.sp,
fontWeight: FontWeight.bold,
color: const Color.fromRGBO(100, 116, 139, 1.0),
),
),
SizedBox(height: 16.h),
Wrap(
spacing: 5.w,
runSpacing: 12.h,
children: allRooms.map((room) {
final isSelected = selectedRooms.contains(room);
return GestureDetector(
onTap: () => onRoomToggle(room),
child: Container(
width: 120.w,
padding: EdgeInsets.symmetric(horizontal: 28.w, vertical: 12.h),
decoration: BoxDecoration(
color: isSelected
? const Color.fromRGBO(235, 244, 255, 1.0)
: Colors.white,
borderRadius: BorderRadius.circular(12.r),
border: Border.all(
color: isSelected
? const Color.fromRGBO(66, 165, 245, 1.0)
: const Color.fromRGBO(200, 208, 220, 1.0),
width: 2.w,
),
),
child: Text(
room,
style: TextStyle(
fontSize: 28.sp,
color: isSelected
? const Color.fromRGBO(66, 165, 245, 1.0)
: const Color.fromRGBO(100, 116, 139, 1.0),
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
),
),
),
);
}).toList(),
),
],
);
}
String _getTabLabel(DeviceStatusTab tab) {
switch (tab) {
case DeviceStatusTab.total:
return '总计';
case DeviceStatusTab.on:
return '开启';
case DeviceStatusTab.off:
return '关闭';
case DeviceStatusTab.offline:
return '离线';
case DeviceStatusTab.alarm:
return '告警';
case DeviceStatusTab.fault:
return '故障';
}
}
Color _getTabColor(DeviceStatusTab tab) {
switch (tab) {
case DeviceStatusTab.total:
return const Color.fromRGBO(66, 165, 245, 1.0);
case DeviceStatusTab.on:
return const Color.fromRGBO(26, 188, 156, 1.0);
case DeviceStatusTab.off:
return const Color.fromRGBO(200, 208, 220, 1.0);
case DeviceStatusTab.offline:
return const Color.fromRGBO(100, 116, 139, 1.0);
case DeviceStatusTab.alarm:
return const Color.fromRGBO(247, 184, 75, 1.0);
case DeviceStatusTab.fault:
return const Color.fromRGBO(239, 68, 68, 1.0);
}
}
}
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'inspection_device_state.dart';
class InspectionDeviceCubit extends Cubit<InspectionDeviceState> {
InspectionDeviceCubit({required String deviceId})
: super(InspectionDeviceState(
deviceId: deviceId,
deviceName: '',
deviceType: '',
deviceStatus: '',
deviceModel: '',
installLocation: '',
area: '',
responsiblePerson: '',
lastInspectionTime: '',
inspectionHistory: [],
inspectionItems: [],
icon: Icons.devices,
)) {
_loadDeviceData(deviceId);
}
void _loadDeviceData(String deviceId) {
// TODO: 从API或本地数据库加载设备数据
// 这里使用模拟数据
final mockState = InspectionDeviceState(
deviceId: deviceId,
deviceName: '客厅主控网关',
deviceType: '智能网关',
deviceStatus: '正常',
deviceModel: 'SmartHub Pro V2',
installLocation: '客厅电视柜',
area: '客厅',
responsiblePerson: '张师傅',
lastInspectionTime: '2024-01-15 14:30:00',
inspectionHistory: [
InspectionHistoryItem(
inspectorName: '张师傅',
inspectionTime: '2024-01-15 14:30:00',
result: '通过',
remark: '设备运行正常,各项指标正常',
),
],
inspectionItems: [
InspectionItem(
name: '电源状态',
value: '正常',
result: '通过',
),
InspectionItem(
name: '网络连接',
value: '稳定',
result: '通过',
),
InspectionItem(
name: 'CPU使用率',
value: '23%',
result: '通过',
),
InspectionItem(
name: '内存占用',
value: '45%',
result: '通过',
),
],
icon: Icons.wifi,
);
emit(mockState);
}
}
import 'package:flutter/material.dart';
import 'package:equatable/equatable.dart';
class InspectionDeviceState extends Equatable {
final String deviceId;
final String deviceName;
final String deviceType;
final String deviceStatus;
final String deviceModel;
final String installLocation;
final String area;
final String responsiblePerson;
final String lastInspectionTime;
final List<InspectionHistoryItem> inspectionHistory;
final List<InspectionItem> inspectionItems;
final IconData icon;
const InspectionDeviceState({
required this.deviceId,
required this.deviceName,
required this.deviceType,
required this.deviceStatus,
required this.deviceModel,
required this.installLocation,
required this.area,
required this.responsiblePerson,
required this.lastInspectionTime,
required this.inspectionHistory,
required this.inspectionItems,
required this.icon,
});
InspectionDeviceState copyWith({
String? deviceId,
String? deviceName,
String? deviceType,
String? deviceStatus,
String? deviceModel,
String? installLocation,
String? area,
String? responsiblePerson,
String? lastInspectionTime,
List<InspectionHistoryItem>? inspectionHistory,
List<InspectionItem>? inspectionItems,
IconData? icon,
}) {
return InspectionDeviceState(
deviceId: deviceId ?? this.deviceId,
deviceName: deviceName ?? this.deviceName,
deviceType: deviceType ?? this.deviceType,
deviceStatus: deviceStatus ?? this.deviceStatus,
deviceModel: deviceModel ?? this.deviceModel,
installLocation: installLocation ?? this.installLocation,
area: area ?? this.area,
responsiblePerson: responsiblePerson ?? this.responsiblePerson,
lastInspectionTime: lastInspectionTime ?? this.lastInspectionTime,
inspectionHistory: inspectionHistory ?? this.inspectionHistory,
inspectionItems: inspectionItems ?? this.inspectionItems,
icon: icon ?? this.icon,
);
}
@override
List<Object?> get props => [
deviceId,
deviceName,
deviceType,
deviceStatus,
deviceModel,
installLocation,
area,
responsiblePerson,
lastInspectionTime,
inspectionHistory,
inspectionItems,
icon,
];
}
class InspectionHistoryItem extends Equatable {
final String inspectorName;
final String inspectionTime;
final String result;
final String remark;
const InspectionHistoryItem({
required this.inspectorName,
required this.inspectionTime,
required this.result,
required this.remark,
});
@override
List<Object?> get props => [
inspectorName,
inspectionTime,
result,
remark,
];
}
class InspectionItem extends Equatable {
final String name;
final String value;
final String result;
const InspectionItem({
required this.name,
required this.value,
required this.result,
});
@override
List<Object?> get props => [
name,
value,
result,
];
}
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 'cubit/inspection_device_cubit.dart';
import 'cubit/inspection_device_state.dart';
import 'widget/device_overview_card.dart';
import 'widget/inspection_history_card.dart';
import 'widget/inspection_items_card.dart';
import 'widget/start_inspection_button.dart';
@RoutePage()
class InspectionDeviceView extends StatelessWidget {
final String deviceId;
const InspectionDeviceView({super.key, @pathParam required this.deviceId});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => InspectionDeviceCubit(deviceId: deviceId),
child: 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.popRoute();
},
),
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'设备拓扑图',
style: TextStyle(
color: const Color.fromRGBO(100, 116, 139, 1),
fontSize: 32.sp,
fontWeight: FontWeight.w200,
),
),
Icon(
Icons.arrow_forward,
color: const Color.fromRGBO(100, 116, 139, 1),
),
],
),
centerTitle: true,
),
body: SingleChildScrollView(
padding: EdgeInsets.symmetric(horizontal: 28.w, vertical: 20.h),
child: BlocBuilder<InspectionDeviceCubit, InspectionDeviceState>(
builder: (context, state) {
return Column(
children: [
DeviceOverviewCard(
deviceName: state.deviceName,
deviceType: state.deviceType,
deviceStatus: state.deviceStatus,
deviceModel: state.deviceModel,
installLocation: state.installLocation,
area: state.area,
responsiblePerson: state.responsiblePerson,
lastInspectionTime: state.lastInspectionTime,
icon: state.icon,
),
SizedBox(height: 20.h),
InspectionHistoryCard(
historyList: state.inspectionHistory,
),
SizedBox(height: 20.h),
InspectionItemsCard(
items: state.inspectionItems,
),
SizedBox(height: 30.h),
StartInspectionButton(deviceId: state.deviceId),
SizedBox(height: 30.h),
],
);
},
),
),
),
);
}
}
\ No newline at end of file
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class DeviceOverviewCard extends StatelessWidget {
final String deviceName;
final String deviceType;
final String deviceStatus;
final String deviceModel;
final String installLocation;
final String area;
final String responsiblePerson;
final String lastInspectionTime;
final IconData icon;
const DeviceOverviewCard({
super.key,
required this.deviceName,
required this.deviceType,
required this.deviceStatus,
required this.deviceModel,
required this.installLocation,
required this.area,
required this.responsiblePerson,
required this.lastInspectionTime,
required this.icon,
});
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20.r),
),
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 24.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
width: 100.w,
height: 100.h,
decoration: BoxDecoration(
color: const Color.fromRGBO(235, 244, 255, 1.0),
shape: BoxShape.circle,
),
child: Icon(
icon,
color: const Color.fromRGBO(66, 165, 245, 1.0),
size: 50.sp,
),
),
SizedBox(width: 20.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
deviceName,
style: TextStyle(
fontSize: 36.sp,
fontWeight: FontWeight.bold,
color: const Color.fromRGBO(10, 13, 20, 1.0),
),
),
SizedBox(height: 12.h),
Text(
deviceType,
style: TextStyle(
fontSize: 28.sp,
color: const Color.fromRGBO(100, 116, 139, 1.0),
),
),
SizedBox(height: 16.h),
Container(
padding: EdgeInsets.symmetric(horizontal: 24.w, vertical: 10.h),
decoration: BoxDecoration(
color: const Color.fromRGBO(225, 245, 238, 1.0),
borderRadius: BorderRadius.circular(20.r),
),
child: Text(
deviceStatus,
style: TextStyle(
fontSize: 28.sp,
color: const Color.fromRGBO(26, 188, 156, 1.0),
fontWeight: FontWeight.bold,
),
),
),
],
),
),
],
),
SizedBox(height: 32.h),
Text(
'基本信息',
style: TextStyle(
fontSize: 32.sp,
fontWeight: FontWeight.bold,
color: const Color.fromRGBO(10, 13, 20, 1.0),
),
),
SizedBox(height: 24.h),
_buildInfoItem('设备型号', deviceModel),
SizedBox(height: 24.h),
_buildInfoItem('安装位置', installLocation),
SizedBox(height: 24.h),
_buildInfoItem('所属区域', area),
SizedBox(height: 24.h),
_buildInfoItem('责任人', responsiblePerson),
SizedBox(height: 24.h),
_buildInfoItem('最后巡检', lastInspectionTime),
],
),
);
}
Widget _buildInfoItem(String label, String value) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
label,
style: TextStyle(
fontSize: 28.sp,
color: const Color.fromRGBO(100, 116, 139, 1.0),
),
),
Text(
value,
style: TextStyle(
fontSize: 28.sp,
color: const Color.fromRGBO(100, 116, 139, 1.0),
),
),
],
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../cubit/inspection_device_state.dart';
class InspectionHistoryCard extends StatelessWidget {
final List<InspectionHistoryItem> historyList;
const InspectionHistoryCard({
super.key,
required this.historyList,
});
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20.r),
),
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'巡检历史 (${historyList.length})',
style: TextStyle(
fontSize: 26.sp,
fontWeight: FontWeight.bold,
color: const Color.fromRGBO(10, 13, 20, 1.0),
),
),
SizedBox(height: 20.h),
...historyList.map((item) => _buildHistoryItem(item)).toList(),
],
),
);
}
Widget _buildHistoryItem(InspectionHistoryItem item) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Container(
width: 60.w,
height: 60.h,
decoration: BoxDecoration(
color: const Color.fromRGBO(242, 243, 245, 1.0),
borderRadius: BorderRadius.circular(8.r),
),
child: Icon(
Icons.person,
color: const Color.fromRGBO(100, 116, 139, 1.0),
size: 32.sp,
),
),
SizedBox(width: 12.w),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.inspectorName,
style: TextStyle(
fontSize: 26.sp,
fontWeight: FontWeight.bold,
color: const Color.fromRGBO(10, 13, 20, 1.0),
),
),
SizedBox(height: 4.h),
Row(
children: [
Icon(
Icons.access_time,
color: const Color.fromRGBO(100, 116, 139, 1.0),
size: 20.sp,
),
SizedBox(width: 4.w),
Text(
item.inspectionTime,
style: TextStyle(
fontSize: 22.sp,
color: const Color.fromRGBO(100, 116, 139, 1.0),
),
),
],
),
],
),
],
),
Container(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 6.h),
decoration: BoxDecoration(
color: const Color.fromRGBO(225, 245, 238, 1.0),
borderRadius: BorderRadius.circular(10.r),
),
child: Row(
children: [
Icon(
Icons.check,
color: const Color.fromRGBO(26, 188, 156, 1.0),
size: 20.sp,
),
SizedBox(width: 4.w),
Text(
item.result,
style: TextStyle(
fontSize: 22.sp,
color: const Color.fromRGBO(26, 188, 156, 1.0),
fontWeight: FontWeight.bold,
),
),
],
),
),
],
),
SizedBox(height: 16.h),
Container(
width: double.infinity,
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 12.h),
decoration: BoxDecoration(
color: const Color.fromRGBO(242, 243, 245, 1.0),
borderRadius: BorderRadius.circular(12.r),
),
child: Text(
item.remark,
style: TextStyle(
fontSize: 22.sp,
color: const Color.fromRGBO(100, 116, 139, 1.0),
),
),
),
SizedBox(height: 20.h),
],
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../cubit/inspection_device_state.dart';
class InspectionItemsCard extends StatelessWidget {
final List<InspectionItem> items;
const InspectionItemsCard({
super.key,
required this.items,
});
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20.r),
),
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'巡检项目',
style: TextStyle(
fontSize: 26.sp,
fontWeight: FontWeight.bold,
color: const Color.fromRGBO(10, 13, 20, 1.0),
),
),
SizedBox(height: 20.h),
...items.map((item) => _buildInspectionItem(item)).toList(),
],
),
);
}
Widget _buildInspectionItem(InspectionItem item) {
return Padding(
padding: EdgeInsets.only(bottom: 20.h),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
item.name,
style: TextStyle(
fontSize: 26.sp,
fontWeight: FontWeight.bold,
color: const Color.fromRGBO(10, 13, 20, 1.0),
),
),
),
Row(
children: [
Text(
item.value,
style: TextStyle(
fontSize: 24.sp,
color: const Color.fromRGBO(100, 116, 139, 1.0),
),
),
SizedBox(width: 12.w),
Container(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 6.h),
decoration: BoxDecoration(
color: const Color.fromRGBO(225, 245, 238, 1.0),
borderRadius: BorderRadius.circular(10.r),
),
child: Row(
children: [
Icon(
Icons.check,
color: const Color.fromRGBO(26, 188, 156, 1.0),
size: 20.sp,
),
SizedBox(width: 4.w),
Text(
item.result,
style: TextStyle(
fontSize: 22.sp,
color: const Color.fromRGBO(26, 188, 156, 1.0),
fontWeight: FontWeight.bold,
),
),
],
),
),
],
),
],
),
);
}
}
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:smart_hotel_app/routes/app_router.gr.dart';
class StartInspectionButton extends StatelessWidget {
final String deviceId;
const StartInspectionButton({super.key, required this.deviceId});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
context.pushRoute(
InspectionHistoryRoute(deviceId: deviceId),
);
},
child: Container(
width: double.infinity,
height: 88.h,
decoration: BoxDecoration(
color: const Color.fromRGBO(66, 165, 245, 1.0),
borderRadius: BorderRadius.circular(24.r),
),
child: Center(
child: Text(
'开始巡检',
style: TextStyle(
color: Colors.white,
fontSize: 32.sp,
fontWeight: FontWeight.bold,
),
),
),
),
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'inspection_history_state.dart';
class InspectionHistoryCubit extends Cubit<InspectionHistoryState> {
InspectionHistoryCubit({required String deviceId})
: super(InspectionHistoryState(
deviceId: deviceId,
totalCount: 0,
passCount: 0,
warningCount: 0,
failCount: 0,
records: [],
)) {
_loadHistoryData(deviceId);
}
void _loadHistoryData(String deviceId) {
// TODO: 从API或本地数据库加载巡检历史数据
// 这里使用模拟数据
final mockRecords = [
InspectionRecord(
deviceName: '客厅主控网关',
inspectorName: '张师傅',
inspectionTime: '2024-01-15 14:30:00',
result: '通过',
remark: '设备运行正常,各项指标正常',
items: [
const InspectionItemRecord(name: '电源状态', result: '通过'),
const InspectionItemRecord(name: '网络连接', result: '通过'),
const InspectionItemRecord(name: 'CPU使用率', result: '通过'),
const InspectionItemRecord(name: '内存占用', result: '通过'),
],
icon: Icons.wifi,
),
InspectionRecord(
deviceName: '智能摄像头-门口',
inspectorName: '王师傅',
inspectionTime: '2024-01-15 10:00:00',
result: '警告',
remark: '画面有轻微噪点,可能需要清洁镜头',
items: [
const InspectionItemRecord(name: '画面清晰度', result: '警告'),
const InspectionItemRecord(name: '夜视功能', result: '通过'),
const InspectionItemRecord(name: '录像存储', result: '通过'),
const InspectionItemRecord(name: '云台控制', result: '通过'),
],
icon: Icons.videocam,
),
InspectionRecord(
deviceName: '厨房智能插座',
inspectorName: '李师傅',
inspectionTime: '2024-01-15 10:00:00',
result: '不通过',
remark: '插座过热保护触发,需更换',
items: [
const InspectionItemRecord(name: '温度检测', result: '不通过'),
const InspectionItemRecord(name: '负载功率', result: '警告'),
const InspectionItemRecord(name: '过流保护', result: '通过'),
const InspectionItemRecord(name: '电源开关', result: '通过'),
],
icon: Icons.power,
),
];
final mockState = InspectionHistoryState(
deviceId: deviceId,
totalCount: mockRecords.length,
passCount: mockRecords.where((r) => r.result == '通过').length,
warningCount: mockRecords.where((r) => r.result == '警告').length,
failCount: mockRecords.where((r) => r.result == '不通过').length,
records: mockRecords,
);
emit(mockState);
}
}
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
class InspectionHistoryState extends Equatable {
final String deviceId;
final int totalCount;
final int passCount;
final int warningCount;
final int failCount;
final List<InspectionRecord> records;
const InspectionHistoryState({
required this.deviceId,
required this.totalCount,
required this.passCount,
required this.warningCount,
required this.failCount,
required this.records,
});
InspectionHistoryState copyWith({
String? deviceId,
int? totalCount,
int? passCount,
int? warningCount,
int? failCount,
List<InspectionRecord>? records,
}) {
return InspectionHistoryState(
deviceId: deviceId ?? this.deviceId,
totalCount: totalCount ?? this.totalCount,
passCount: passCount ?? this.passCount,
warningCount: warningCount ?? this.warningCount,
failCount: failCount ?? this.failCount,
records: records ?? this.records,
);
}
@override
List<Object?> get props => [
deviceId,
totalCount,
passCount,
warningCount,
failCount,
records,
];
}
class InspectionRecord extends Equatable {
final String deviceName;
final String inspectorName;
final String inspectionTime;
final String result;
final String remark;
final List<InspectionItemRecord> items;
final IconData icon;
const InspectionRecord({
required this.deviceName,
required this.inspectorName,
required this.inspectionTime,
required this.result,
required this.remark,
required this.items,
required this.icon,
});
@override
List<Object?> get props => [
deviceName,
inspectorName,
inspectionTime,
result,
remark,
items,
icon,
];
}
class InspectionItemRecord extends Equatable {
final String name;
final String result;
const InspectionItemRecord({
required this.name,
required this.result,
});
@override
List<Object?> get props => [name, result];
}
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 'cubit/inspection_history_cubit.dart';
import 'cubit/inspection_history_state.dart';
import 'widget/statistics_card.dart';
import 'widget/inspection_record_list.dart';
@RoutePage()
class InspectionHistoryView extends StatelessWidget {
final String deviceId;
const InspectionHistoryView({super.key, @pathParam required this.deviceId});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => InspectionHistoryCubit(deviceId: deviceId),
child: 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.popRoute();
},
),
title: Text(
'巡检记录',
style: TextStyle(
color: const Color.fromRGBO(10, 13, 20, 1),
fontSize: 32.sp,
fontWeight: FontWeight.w200,
),
),
centerTitle: true,
),
body: SingleChildScrollView(
padding: EdgeInsets.symmetric(horizontal: 28.w, vertical: 20.h),
child: BlocBuilder<InspectionHistoryCubit, InspectionHistoryState>(
builder: (context, state) {
return Column(
children: [
StatisticsCard(
totalCount: state.totalCount,
passCount: state.passCount,
warningCount: state.warningCount,
failCount: state.failCount,
),
SizedBox(height: 20.h),
InspectionRecordList(
records: state.records,
),
SizedBox(height: 30.h),
],
);
},
),
),
),
);
}
}
\ No newline at end of file
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../cubit/inspection_history_state.dart';
class InspectionRecordItem extends StatelessWidget {
final InspectionRecord record;
const InspectionRecordItem({
super.key,
required this.record,
});
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20.r),
),
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Container(
width: 60.w,
height: 60.h,
decoration: BoxDecoration(
color: const Color.fromRGBO(242, 243, 245, 1.0),
borderRadius: BorderRadius.circular(8.r),
),
child: Icon(
record.icon,
color: const Color.fromRGBO(100, 116, 139, 1.0),
size: 32.sp,
),
),
SizedBox(width: 12.w),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
record.deviceName,
style: TextStyle(
fontSize: 26.sp,
fontWeight: FontWeight.bold,
color: const Color.fromRGBO(10, 13, 20, 1.0),
),
),
SizedBox(height: 8.h),
Row(
children: [
Icon(
Icons.person,
color: const Color.fromRGBO(66, 165, 245, 1.0),
size: 18.sp,
),
SizedBox(width: 4.w),
Text(
record.inspectorName,
style: TextStyle(
fontSize: 22.sp,
color: const Color.fromRGBO(100, 116, 139, 1.0),
),
),
],
),
],
),
],
),
Container(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 6.h),
decoration: BoxDecoration(
color: _getResultColor(record.result).withOpacity(0.1),
borderRadius: BorderRadius.circular(10.r),
border: Border.all(
color: _getResultColor(record.result),
width: 1.w,
),
),
child: Row(
children: [
Icon(
_getResultIcon(record.result),
color: _getResultColor(record.result),
size: 18.sp,
),
SizedBox(width: 4.w),
Text(
record.result,
style: TextStyle(
fontSize: 22.sp,
color: _getResultColor(record.result),
fontWeight: FontWeight.bold,
),
),
],
),
),
],
),
SizedBox(height: 12.h),
Row(
children: [
Icon(
Icons.access_time,
color: const Color.fromRGBO(100, 116, 139, 1.0),
size: 20.sp,
),
SizedBox(width: 4.w),
Text(
record.inspectionTime,
style: TextStyle(
fontSize: 22.sp,
color: const Color.fromRGBO(100, 116, 139, 1.0),
),
),
],
),
SizedBox(height: 16.h),
Container(
width: double.infinity,
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 12.h),
decoration: BoxDecoration(
color: const Color.fromRGBO(242, 243, 245, 1.0),
borderRadius: BorderRadius.circular(12.r),
),
child: Text(
record.remark,
style: TextStyle(
fontSize: 22.sp,
color: const Color.fromRGBO(100, 116, 139, 1.0),
),
),
),
SizedBox(height: 16.h),
Divider(
color: const Color.fromRGBO(242, 243, 245, 1.0),
height: 1.h,
),
SizedBox(height: 16.h),
Text(
'巡检项目',
style: TextStyle(
fontSize: 24.sp,
fontWeight: FontWeight.bold,
color: const Color.fromRGBO(71, 85, 105, 1.0),
),
),
SizedBox(height: 12.h),
Wrap(
spacing: 8.w,
runSpacing: 8.h,
children: [
...record.items.map((item) => _buildItemTag(item)).toList(),
if (record.items.length > 3)
Container(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h),
decoration: BoxDecoration(
color: const Color.fromRGBO(242, 243, 245, 1.0),
borderRadius: BorderRadius.circular(16.r),
),
child: Text(
'+${record.items.length - 3}',
style: TextStyle(
fontSize: 20.sp,
color: const Color.fromRGBO(100, 116, 139, 1.0),
),
),
),
],
),
],
),
);
}
Widget _buildItemTag(InspectionItemRecord item) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h),
decoration: BoxDecoration(
color: _getResultColor(item.result).withOpacity(0.1),
borderRadius: BorderRadius.circular(16.r),
border: Border.all(
color: _getResultColor(item.result),
width: 1.w,
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
_getResultIcon(item.result),
color: _getResultColor(item.result),
size: 16.sp,
),
SizedBox(width: 4.w),
Text(
item.name,
style: TextStyle(
fontSize: 20.sp,
color: _getResultColor(item.result),
),
),
],
),
);
}
Color _getResultColor(String result) {
switch (result) {
case '通过':
return const Color.fromRGBO(26, 188, 156, 1.0);
case '警告':
return const Color.fromRGBO(247, 184, 75, 1.0);
case '不通过':
return const Color.fromRGBO(239, 68, 68, 1.0);
default:
return const Color.fromRGBO(100, 116, 139, 1.0);
}
}
IconData _getResultIcon(String result) {
switch (result) {
case '通过':
return Icons.check;
case '警告':
return Icons.warning_amber_outlined;
case '不通过':
return Icons.close;
default:
return Icons.help;
}
}
}
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../cubit/inspection_history_state.dart';
import 'inspection_record_item.dart';
class InspectionRecordList extends StatelessWidget {
final List<InspectionRecord> records;
const InspectionRecordList({
super.key,
required this.records,
});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'巡检历史 (${records.length})',
style: TextStyle(
fontSize: 28.sp,
fontWeight: FontWeight.bold,
color: const Color.fromRGBO(10, 13, 20, 1.0),
),
),
SizedBox(height: 16.h),
ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: records.length,
separatorBuilder: (context, index) => SizedBox(height: 16.h),
itemBuilder: (context, index) {
return InspectionRecordItem(
record: records[index],
);
},
),
],
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class StatisticsCard extends StatelessWidget {
final int totalCount;
final int passCount;
final int warningCount;
final int failCount;
const StatisticsCard({
super.key,
required this.totalCount,
required this.passCount,
required this.warningCount,
required this.failCount,
});
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20.r),
),
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 16.h),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildStatItem('总计', totalCount, const Color.fromRGBO(66, 165, 245, 1.0)),
_buildStatItem('通过', passCount, const Color.fromRGBO(26, 188, 156, 1.0)),
_buildStatItem('警告', warningCount, const Color.fromRGBO(247, 184, 75, 1.0)),
_buildStatItem('不通过', failCount, const Color.fromRGBO(239, 68, 68, 1.0)),
],
),
);
}
Widget _buildStatItem(String label, int count, Color color) {
return Column(
children: [
Text(
count.toString(),
style: TextStyle(
fontSize: 32.sp,
fontWeight: FontWeight.bold,
color: color,
),
),
SizedBox(height: 4.h),
Text(
label,
style: TextStyle(
fontSize: 22.sp,
color: const Color.fromRGBO(100, 116, 139, 1.0),
),
),
],
);
}
}
import 'package:flutter/material.dart';
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';
import 'package:smart_hotel_app/utils/storage/storage_service.dart';
import 'package:smart_hotel_app/views/login/cubit/login_state.dart';
class LoginController {
class LoginCubit extends Cubit<LoginState> {
final AuthBloc authBloc;
final TextEditingController usernameController;
final TextEditingController pwdController;
final GlobalKey<FormState> frmGlobalKey;
final StorageService storageService;
LoginController({
LoginCubit({
required this.authBloc,
required this.usernameController,
required this.pwdController,
required this.frmGlobalKey,
required this.storageService,
});
}) : super(const LoginState());
void login(GlobalKey<FormState> frmKey, {bool rememberPassword = false}) {
if (frmKey.currentState!.validate()) {
final username = usernameController.text.trim();
final password = pwdController.text.trim();
if (rememberPassword) {
storageService.saveRememberCredentials(
username: username,
password: password,
);
} else {
storageService.clearRememberCredentials();
}
void toggleRememberPassword(bool value) {
emit(state.copyWith(rememberPassword: value));
}
authBloc.add(AuthLoginRequested(username: username, password: password));
void login({
required String username,
required String password,
bool rememberPassword = false,
}) {
if (rememberPassword) {
storageService.saveRememberCredentials(
username: username,
password: password,
);
} else {
storageService.clearRememberCredentials();
}
authBloc.add(AuthLoginRequested(username: username, password: password));
}
Future<void> loadRememberCredentials() async {
final credentials = await storageService.getRememberCredentials();
if (credentials['username'] != null) {
usernameController.text = credentials['username']!;
}
if (credentials['password'] != null) {
pwdController.text = credentials['password']!;
emit(state.copyWith(
rememberPassword: true,
savedUsername: credentials['username'],
savedPassword: credentials['password'],
));
}
}
void forgotPwd() {
// TODO: 跳转到忘记密码页面
}
void resetFrm() {
usernameController.clear();
pwdController.clear();
frmGlobalKey.currentState?.reset();
}
}
\ No newline at end of file
import 'package:equatable/equatable.dart';
class LoginState extends Equatable {
final bool rememberPassword;
final bool isLoading;
final String? savedUsername;
final String? savedPassword;
const LoginState({
this.rememberPassword = false,
this.isLoading = false,
this.savedUsername,
this.savedPassword,
});
LoginState copyWith({
bool? rememberPassword,
bool? isLoading,
String? savedUsername,
String? savedPassword,
}) {
return LoginState(
rememberPassword: rememberPassword ?? this.rememberPassword,
isLoading: isLoading ?? this.isLoading,
savedUsername: savedUsername ?? this.savedUsername,
savedPassword: savedPassword ?? this.savedPassword,
);
}
@override
List<Object?> get props => [
rememberPassword,
isLoading,
savedUsername,
savedPassword,
];
}
\ No newline at end of file
This diff is collapsed.
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter/material.dart';
import 'package:smart_hotel_app/views/profile/index/cubit/profile_state.dart';
class ProfileCubit extends Cubit<ProfileState> {
ProfileCubit() : super(const ProfileState()) {
initData();
}
void initData() {
emit(state.copyWith(
phoneNumber: '15391514233',
welcomeText: '欢迎来到智能酒店',
menuList: [
const MenuItem(icon: Icons.list, title: '设备列表'),
const MenuItem(icon: Icons.warning_rounded, title: '告警信息'),
const MenuItem(icon: Icons.info_outline, title: '帮助'),
const MenuItem(icon: Icons.person_outline, title: '关于'),
const MenuItem(icon: Icons.logout, title: '退出登录'),
const MenuItem(icon: Icons.logout, title: '注销账号'),
],
));
}
}
\ No newline at end of file
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
class MenuItem {
final IconData icon;
final String title;
final VoidCallback? onTap;
const MenuItem({
required this.icon,
required this.title,
this.onTap,
});
}
class ProfileState extends Equatable {
final String phoneNumber;
final String welcomeText;
final List<MenuItem> menuList;
const ProfileState({
this.phoneNumber = '',
this.welcomeText = '',
this.menuList = const [],
});
ProfileState copyWith({
String? phoneNumber,
String? welcomeText,
List<MenuItem>? menuList,
}) {
return ProfileState(
phoneNumber: phoneNumber ?? this.phoneNumber,
welcomeText: welcomeText ?? this.welcomeText,
menuList: menuList ?? this.menuList,
);
}
@override
List<Object?> get props => [phoneNumber, welcomeText, menuList];
}
\ No newline at end of file
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/views/profile/index/cubit/profile_cubit.dart';
import 'package:smart_hotel_app/views/profile/index/cubit/profile_state.dart';
import 'package:smart_hotel_app/views/profile/index/widget/profile_header_block.dart';
import 'package:smart_hotel_app/views/profile/index/widget/profile_menu_block.dart';
@RoutePage()
class ProfileView extends StatelessWidget {
const ProfileView({super.key});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => ProfileCubit(),
child: LayoutBuilder(
builder: (context, constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight,
),
child: Container(
width: double.infinity,
color: const Color.fromRGBO(242, 243, 245, 1),
child: BlocBuilder<ProfileCubit, ProfileState>(
builder: (context, state) {
return Column(
children: [
ProfileHeaderBlock(
phoneNumber: state.phoneNumber,
welcomeText: state.welcomeText,
),
SizedBox(height: 10.h),
Padding(
padding: EdgeInsets.symmetric(horizontal: 28.w),
child: ProfileMenuBlock(menuList: state.menuList),
),
],
);
},
),
),
),
);
},
),
);
}
}
\ No newline at end of file
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class ProfileHeaderBlock extends StatelessWidget {
final String phoneNumber;
final String welcomeText;
const ProfileHeaderBlock({
super.key,
required this.phoneNumber,
required this.welcomeText,
});
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
color: Colors.white,
padding: EdgeInsets.symmetric(horizontal: 28.w, vertical: 32.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'我的',
style: TextStyle(
fontSize: 36.sp,
fontWeight: FontWeight.w500,
color: Colors.black,
),
),
SizedBox(height: 40.h),
Row(
children: [
Container(
width: 100.w,
height: 100.h,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Color.fromRGBO(235, 244, 255, 1.0),
),
child: ClipOval(
child: Image.network(
'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=200&h=200&fit=crop&crop=face',
width: 100.w,
height: 100.h,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return Icon(
Icons.person,
size: 48.sp,
color: const Color.fromRGBO(66, 165, 245, 1.0),
);
},
),
),
),
SizedBox(width: 24.w),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
phoneNumber,
style: TextStyle(
fontSize: 32.sp,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
SizedBox(height: 12.h),
Text(
welcomeText,
style: TextStyle(
fontSize: 26.sp,
color: const Color.fromRGBO(100, 116, 139, 1.0),
),
),
],
),
],
),
],
),
);
}
}
\ No newline at end of file
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:smart_hotel_app/views/profile/index/cubit/profile_state.dart';
class ProfileMenuBlock extends StatelessWidget {
final List<MenuItem> menuList;
const ProfileMenuBlock({super.key, required this.menuList});
Widget _buildMenuItem(MenuItem item) {
return GestureDetector(
onTap: () => item.onTap?.call(),
child: Container(
width: double.infinity,
padding: EdgeInsets.symmetric(horizontal: 24.w, vertical: 28.h),
margin: EdgeInsets.only(bottom: 16.h),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(24.r),
),
child: Row(
children: [
Container(
width: 96.w,
height: 96.h,
decoration: BoxDecoration(
color: const Color.fromRGBO(229, 241, 255, 1),
borderRadius: BorderRadius.circular(28.r),
),
child: Icon(
item.icon,
color: const Color.fromRGBO(66, 165, 245, 1.0),
size: 40.sp,
),
),
SizedBox(width: 24.w),
Expanded(
child: Text(
item.title,
style: TextStyle(
fontSize: 30.sp,
fontWeight: FontWeight.w500,
color: const Color.fromRGBO(100, 116, 139, 1.0),
),
),
),
Icon(
Icons.chevron_right,
color: const Color.fromRGBO(100, 116, 139, 1.0),
size: 32.sp,
),
],
),
),
);
}
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
child: Column(
children: [
...menuList.map((item) => _buildMenuItem(item)).toList(),
],
),
);
}
}
\ No newline at end of file
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
@RoutePage()
class ProfileView extends StatefulWidget {
const ProfileView({super.key});
@override
State<ProfileView> createState() => _ProfileViewState();
}
class _ProfileViewState extends State<ProfileView> {
@override
Widget build(BuildContext context) {
return const Center(
child: Text('ProfileView'),
);
}
}
class ReportEnergyController {
static const double todayEnergy = 1284;
static const double todayEnergyChange = -8;
static const double monthEnergy = 38420;
static const double monthEnergyChange = -5;
static const double monthBill = 6982;
static const List<double> hourlyData = [
45, 38, 32, 28, 25, 30, 42, 78, 112, 145, 168, 175,
162, 148, 132, 115, 98, 82, 70, 58, 48, 42, 38, 35
];
static const List<ZoneData> zoneData = [
ZoneData(name: '客房区域', percentage: 68, energy: 873),
ZoneData(name: '公共区域', percentage: 18, energy: 231),
ZoneData(name: '餐饮区域', percentage: 10, energy: 128),
ZoneData(name: '其他', percentage: 4, energy: 52),
];
}
class ZoneData {
final String name;
final double percentage;
final double energy;
const ZoneData({
required this.name,
required this.percentage,
required this.energy,
});
}
\ No newline at end of file
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:smart_hotel_app/views/report/energy/cubit/energy_state.dart';
class EnergyCubit extends Cubit<EnergyState> {
EnergyCubit() : super(const EnergyState());
}
\ No newline at end of file
import 'package:equatable/equatable.dart';
class ZoneData {
final String name;
final double percentage;
final double energy;
const ZoneData({
required this.name,
required this.percentage,
required this.energy,
});
}
class EnergyState extends Equatable {
final double todayEnergy;
final double todayEnergyChange;
final double monthEnergy;
final double monthEnergyChange;
final double monthBill;
final List<double> hourlyData;
final List<ZoneData> zoneData;
const EnergyState({
this.todayEnergy = 1284,
this.todayEnergyChange = -8,
this.monthEnergy = 38420,
this.monthEnergyChange = -5,
this.monthBill = 6982,
this.hourlyData = const [
45, 38, 32, 28, 25, 30, 42, 78, 112, 145, 168, 175,
162, 148, 132, 115, 98, 82, 70, 58, 48, 42, 38, 35,
],
this.zoneData = const [
ZoneData(name: '客房区域', percentage: 68, energy: 873),
ZoneData(name: '公共区域', percentage: 18, energy: 231),
ZoneData(name: '餐饮区域', percentage: 10, energy: 128),
ZoneData(name: '其他', percentage: 4, energy: 52),
],
});
EnergyState copyWith({
double? todayEnergy,
double? todayEnergyChange,
double? monthEnergy,
double? monthEnergyChange,
double? monthBill,
List<double>? hourlyData,
List<ZoneData>? zoneData,
}) {
return EnergyState(
todayEnergy: todayEnergy ?? this.todayEnergy,
todayEnergyChange: todayEnergyChange ?? this.todayEnergyChange,
monthEnergy: monthEnergy ?? this.monthEnergy,
monthEnergyChange: monthEnergyChange ?? this.monthEnergyChange,
monthBill: monthBill ?? this.monthBill,
hourlyData: hourlyData ?? this.hourlyData,
zoneData: zoneData ?? this.zoneData,
);
}
@override
List<Object?> get props => [
todayEnergy,
todayEnergyChange,
monthEnergy,
monthEnergyChange,
monthBill,
hourlyData,
zoneData,
];
}
\ No newline at end of file
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/views/report/energy/controller.dart';
import 'package:smart_hotel_app/views/report/energy/child/energy_stats_card_block.dart';
import 'package:smart_hotel_app/views/report/energy/child/hourly_chart_block.dart';
import 'package:smart_hotel_app/views/report/energy/child/zone_chart_block.dart';
import 'package:smart_hotel_app/views/report/energy/cubit/energy_cubit.dart';
import 'package:smart_hotel_app/views/report/energy/cubit/energy_state.dart';
import 'package:smart_hotel_app/views/report/energy/widget/energy_stats_card_block.dart';
import 'package:smart_hotel_app/views/report/energy/widget/hourly_chart_block.dart';
import 'package:smart_hotel_app/views/report/energy/widget/zone_chart_block.dart';
@RoutePage()
class ReportEnergyDetailView extends StatefulWidget {
class ReportEnergyDetailView extends StatelessWidget {
const ReportEnergyDetailView({super.key});
@override
State<ReportEnergyDetailView> createState() => _ReportEnergyDetailViewState();
}
class _ReportEnergyDetailViewState extends State<ReportEnergyDetailView> {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color.fromRGBO(15, 23, 42, 1),
appBar: AppBar(
backgroundColor: Color.fromRGBO(15, 23, 42, 1),
elevation: 0,
leading: IconButton(
icon: Icon(Icons.arrow_back, color: Colors.white),
onPressed: () {
context.popRoute();
},
return BlocProvider(
create: (_) => EnergyCubit(),
child: Scaffold(
backgroundColor: const Color.fromRGBO(15, 23, 42, 1),
appBar: AppBar(
backgroundColor: const Color.fromRGBO(15, 23, 42, 1),
elevation: 0,
leading: IconButton(
icon: const Icon(Icons.arrow_back, color: Colors.white),
onPressed: () {
context.popRoute();
},
),
title: Text(
'能耗分析',
style: TextStyle(
color: Colors.white,
fontSize: 18.sp,
fontWeight: FontWeight.bold,
),
),
centerTitle: true,
),
title: Text(
'能耗分析',
style: TextStyle(
color: Colors.white,
fontSize: 18.sp,
fontWeight: FontWeight.bold,
body: SingleChildScrollView(
child: BlocBuilder<EnergyCubit, EnergyState>(
builder: (context, state) {
return Column(
children: [
SizedBox(height: 10.h),
EnergyStatsCardBlock(
todayEnergy: state.todayEnergy,
todayEnergyChange: state.todayEnergyChange,
monthEnergy: state.monthEnergy,
monthEnergyChange: state.monthEnergyChange,
monthBill: state.monthBill,
),
SizedBox(height: 20.h),
HourlyChartBlock(hourlyData: state.hourlyData),
SizedBox(height: 20.h),
ZoneChartBlock(zoneData: state.zoneData),
SizedBox(height: 20.h),
],
);
},
),
),
centerTitle: true,
),
body: SingleChildScrollView(
child: Column(
children: [
SizedBox(height: 10.h),
const EnergyStatsCardBlock(),
SizedBox(height: 20.h),
const HourlyChartBlock(),
SizedBox(height: 20.h),
const ZoneChartBlock(),
SizedBox(height: 20.h),
],
),
),
);
}
}
\ No newline at end of file
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:smart_hotel_app/views/report/energy/controller.dart';
class EnergyStatsCardBlock extends StatelessWidget {
const EnergyStatsCardBlock({super.key});
final double todayEnergy;
final double todayEnergyChange;
final double monthEnergy;
final double monthEnergyChange;
final double monthBill;
const EnergyStatsCardBlock({
super.key,
required this.todayEnergy,
required this.todayEnergyChange,
required this.monthEnergy,
required this.monthEnergyChange,
required this.monthBill,
});
@override
Widget build(BuildContext context) {
......@@ -13,25 +25,25 @@ class EnergyStatsCardBlock extends StatelessWidget {
children: [
Expanded(
child: _buildCard(
'${ReportEnergyController.todayEnergy.toInt()}',
'${todayEnergy.toInt()}',
'今日(kWh)',
'${ReportEnergyController.todayEnergyChange.toInt()}%',
'${todayEnergyChange.toInt()}%',
true,
),
),
SizedBox(width: 12.w),
Expanded(
child: _buildCard(
'${ReportEnergyController.monthEnergy.toInt()}',
'${monthEnergy.toInt()}',
'本月(kWh)',
'${ReportEnergyController.monthEnergyChange.toInt()}%',
'${monthEnergyChange.toInt()}%',
true,
),
),
SizedBox(width: 12.w),
Expanded(
child: _buildCard(
${ReportEnergyController.monthBill.toInt()}',
${monthBill.toInt()}',
'本月电费预估',
null,
null,
......@@ -74,13 +86,17 @@ class EnergyStatsCardBlock extends StatelessWidget {
children: [
Icon(
isDown! ? Icons.arrow_drop_down : Icons.arrow_drop_up,
color: isDown ? const Color.fromRGBO(34, 197, 94, 1) : const Color.fromRGBO(239, 68, 68, 1),
color: isDown
? const Color.fromRGBO(34, 197, 94, 1)
: const Color.fromRGBO(239, 68, 68, 1),
size: 16.sp,
),
Text(
change,
style: TextStyle(
color: isDown ? const Color.fromRGBO(34, 197, 94, 1) : const Color.fromRGBO(239, 68, 68, 1),
color: isDown
? const Color.fromRGBO(34, 197, 94, 1)
: const Color.fromRGBO(239, 68, 68, 1),
fontSize: 12.sp,
),
),
......@@ -92,4 +108,4 @@ class EnergyStatsCardBlock extends StatelessWidget {
),
);
}
}
}
\ No newline at end of file
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:smart_hotel_app/views/report/energy/controller.dart';
class HourlyChartBlock extends StatelessWidget {
const HourlyChartBlock({super.key});
final List<double> hourlyData;
const HourlyChartBlock({super.key, required this.hourlyData});
@override
Widget build(BuildContext context) {
......@@ -34,33 +35,21 @@ class HourlyChartBlock extends StatelessWidget {
alignment: BarChartAlignment.spaceBetween,
maxY: 200,
barTouchData: BarTouchData(enabled: false),
titlesData: FlTitlesData(
titlesData: const FlTitlesData(
show: true,
rightTitles: const AxisTitles(
rightTitles: AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
topTitles: const AxisTitles(
topTitles: AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
getTitlesWidget: (value, meta) {
if (value % 4 == 0) {
return Text(
'${value.toInt()}',
style: TextStyle(
color: const Color.fromRGBO(148, 163, 184, 1),
fontSize: 10.sp,
),
);
}
return const SizedBox.shrink();
},
reservedSize: 20,
),
),
leftTitles: const AxisTitles(
leftTitles: AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
),
......@@ -76,7 +65,7 @@ class HourlyChartBlock extends StatelessWidget {
}
List<BarChartGroupData> _buildBarGroups() {
return ReportEnergyController.hourlyData.asMap().entries.map((entry) {
return hourlyData.asMap().entries.map((entry) {
final index = entry.key;
final value = entry.value;
return BarChartGroupData(
......@@ -88,13 +77,13 @@ class HourlyChartBlock extends StatelessWidget {
? const Color.fromRGBO(59, 130, 246, 1)
: const Color.fromRGBO(37, 99, 235, 1),
width: 10.w,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(4.r),
topRight: Radius.circular(4.r),
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(4),
topRight: Radius.circular(4),
),
),
],
);
}).toList();
}
}
}
\ No newline at end of file
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:smart_hotel_app/views/report/energy/controller.dart';
import 'package:smart_hotel_app/views/report/energy/cubit/energy_state.dart';
class ZoneChartBlock extends StatelessWidget {
const ZoneChartBlock({super.key});
final List<ZoneData> zoneData;
const ZoneChartBlock({super.key, required this.zoneData});
@override
Widget build(BuildContext context) {
......@@ -26,11 +28,12 @@ class ZoneChartBlock extends StatelessWidget {
),
),
SizedBox(height: 16.h),
...ReportEnergyController.zoneData.asMap().entries.map((entry) {
...zoneData.asMap().entries.map((entry) {
final index = entry.key;
final data = entry.value;
return Padding(
padding: EdgeInsets.only(bottom: index < ReportEnergyController.zoneData.length - 1 ? 16.h : 0),
padding: EdgeInsets.only(
bottom: index < zoneData.length - 1 ? 16.h : 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
......@@ -85,4 +88,4 @@ class ZoneChartBlock extends StatelessWidget {
),
);
}
}
}
\ No newline at end of file
import 'package:auto_route/auto_route.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter/material.dart';
import 'package:smart_hotel_app/routes/app_router.gr.dart';
import 'package:smart_hotel_app/views/report/index/cubit/report_index_state.dart';
class ReportIndexController {
final BuildContext context;
ReportIndexController(this.context);
Map<String, dynamic> getReportHeader() {
return {
'title': '经营概览看板',
'subtitle': '管理层视角 · 关键指标一览',
};
class ReportIndexCubit extends Cubit<ReportIndexState> {
ReportIndexCubit() : super(const ReportIndexState()) {
initData();
}
Map<String, dynamic> getReportMetrics() {
return {
'powerUsage': {
'value': 1284,
void initData() {
emit(state.copyWith(
reportHeader: {
'title': '经营概览看板',
'subtitle': '管理层视角 · 关键指标一览',
},
reportMetrics: {
'powerUsage': {
'value': 1284,
'unit': 'kWh',
'change': -8,
'changeText': '↓ 8%',
'progress': 0.6,
},
'pendingAlerts': {
'value': 3,
'unit': '条',
'change': null,
'changeText': '需关注',
'progress': 0.3,
},
},
weeklyPowerUsage: {
'title': '本周用电趋势',
'unit': 'kWh',
'change': -8,
'changeText': '↓ 8%',
'progress': 0.6,
'dateRange': '5.7 - 5.13',
'data': [
{'day': '一', 'value': 120},
{'day': '二', 'value': 180},
{'day': '三', 'value': 150},
{'day': '四', 'value': 200},
{'day': '五', 'value': 250},
{'day': '六', 'value': 220},
{'day': '日', 'value': 164},
],
},
'pendingAlerts': {
'value': 3,
'unit': '条',
'change': null,
'changeText': '需关注',
'progress': 0.3,
roomStatusDistribution: {
'title': '客房状态分布',
'totalRooms': 52,
'statuses': [
{'name': '入住中', 'count': 45, 'color': const Color.fromRGBO(59, 130, 246, 1)},
{'name': '空闲', 'count': 7, 'color': const Color.fromRGBO(148, 163, 184, 1)},
{'name': '已预订', 'count': 5, 'color': const Color.fromRGBO(96, 165, 250, 1)},
{'name': '设备告警', 'count': 3, 'color': const Color.fromRGBO(249, 115, 22, 1)},
],
},
};
}
Map<String, dynamic> getWeeklyPowerUsage() {
return {
'title': '本周用电趋势',
'unit': 'kWh',
'dateRange': '5.7 - 5.13',
'data': [
{'day': '一', 'value': 120},
{'day': '二', 'value': 180},
{'day': '三', 'value': 150},
{'day': '四', 'value': 200},
{'day': '五', 'value': 250},
{'day': '六', 'value': 220},
{'day': '日', 'value': 164},
],
};
}
Map<String, dynamic> getRoomStatusDistribution() {
return {
'title': '客房状态分布',
'totalRooms': 52,
'statuses': [
{'name': '入住中', 'count': 45, 'color': Color.fromRGBO(59, 130, 246, 1)},
{'name': '空闲', 'count': 7, 'color': Color.fromRGBO(148, 163, 184, 1)},
{'name': '已预订', 'count': 5, 'color': Color.fromRGBO(96, 165, 250, 1)},
{'name': '设备告警', 'count': 3, 'color': Color.fromRGBO(249, 115, 22, 1)},
],
};
}
Map<String, dynamic> getDeviceOnlineRate() {
return {
'title': '设备在线率',
'rate': 1.0,
'rateText': '100%',
'devices': [
{'name': '智能空开', 'count': 16},
{'name': '客控设备', 'count': 24},
{'name': '网络设备', 'count': 8},
],
};
}
Map<String, dynamic> getQuickActions() {
return {
'title': '快捷操作',
'actions': [
{
'name': '告警记录',
'icon': Icons.warning_amber,
'backgroundColor': Color.fromRGBO(125, 96, 87, 1),
'iconColor': Color.fromRGBO(255, 152, 0, 1),
},
],
};
}
void navigateToEnergyDetail() {
context.pushRoute(const ReportEnergyDetailRoute());
}
void navigateToRoomDetail() {
context.pushRoute(const ReportRoomDetailRoute());
deviceOnlineRate: {
'title': '设备在线率',
'rate': 1.0,
'rateText': '100%',
'devices': [
{'name': '智能空开', 'count': 16},
{'name': '客控设备', 'count': 24},
{'name': '网络设备', 'count': 8},
],
},
quickActions: {
'title': '快捷操作',
'actions': [
{'name': '告警记录', 'icon': Icons.warning_amber_outlined},
{'name': '联动规则', 'icon': Icons.tune},
],
},
));
}
}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment