253 lines
10 KiB
Dart
253 lines
10 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
import 'package:maibu_satabot_v2/features/v2/home/presentation/bloc/home_v2_bloc.dart';
|
|
import 'package:maibu_satabot_v2/features/v2/home/presentation/bloc/home_v2_event.dart';
|
|
import 'package:maibu_satabot_v2/features/v2/home/presentation/bloc/home_v2_state.dart';
|
|
import 'package:maibu_satabot_v2/features/v2/home/presentation/widgets/power_card.dart';
|
|
import 'package:maibu_satabot_v2/features/v2/home/presentation/widgets/stats_grid.dart';
|
|
import 'package:maibu_satabot_v2/features/v2/home/presentation/widgets/work_order_card.dart';
|
|
import 'package:maibu_satabot_v2/features/v2/home/presentation/widgets/quick_entry_card.dart';
|
|
import 'package:maibu_satabot_v2/features/v2/home/presentation/widgets/power_trend_chart.dart';
|
|
import 'package:maibu_satabot_v2/features/v2/home/presentation/widgets/plant_overview_card.dart';
|
|
|
|
class HomeV2Page extends StatefulWidget {
|
|
const HomeV2Page({super.key});
|
|
|
|
@override
|
|
State<HomeV2Page> createState() => _HomeV2PageState();
|
|
}
|
|
|
|
class _HomeV2PageState extends State<HomeV2Page> {
|
|
late HomeV2Bloc _bloc;
|
|
String _selectedPlant = '示例光伏电站';
|
|
final List<String> _plantList = [
|
|
'示例光伏电站',
|
|
'一号光伏电站',
|
|
'二号光伏电站',
|
|
'三号光伏电站',
|
|
];
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_bloc = context.read<HomeV2Bloc>();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
backgroundColor: const Color(0xFFF5F7FA),
|
|
body: BlocBuilder<HomeV2Bloc, HomeV2State>(
|
|
builder: (context, state) {
|
|
if (state is HomeV2Error) {
|
|
return Center(
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
const Icon(Icons.error_outline, size: 48, color: Color(0xFF86909C)),
|
|
const SizedBox(height: 16),
|
|
Text(state.message),
|
|
const SizedBox(height: 16),
|
|
ElevatedButton(
|
|
onPressed: () => _bloc.add(const HomeV2LoadData()),
|
|
child: const Text('重试'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
if (state is HomeV2Loaded) {
|
|
return RefreshIndicator(
|
|
onRefresh: () async {
|
|
_bloc.add(const HomeV2Refresh());
|
|
},
|
|
child: CustomScrollView(
|
|
slivers: [
|
|
SliverAppBar(
|
|
pinned: true,
|
|
backgroundColor: Colors.white,
|
|
elevation: 0,
|
|
automaticallyImplyLeading: false,
|
|
flexibleSpace: FlexibleSpaceBar(
|
|
background: Container(
|
|
padding: const EdgeInsets.fromLTRB(16, 40, 16, 16),
|
|
color: Colors.white,
|
|
child: Row(
|
|
children: [
|
|
Expanded(
|
|
child: InkWell(
|
|
onTap: _showPlantSelector,
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Flexible(
|
|
child: Text(
|
|
_selectedPlant,
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: const TextStyle(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.bold,
|
|
color: Color(0xFF1D2129),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: 4),
|
|
const Icon(Icons.arrow_drop_down, size: 20, color: Color(0xFF1D2129)),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
IconButton(
|
|
icon: const Icon(Icons.qr_code_scanner, size: 24, color: Color(0xFF1D2129)),
|
|
onPressed: () => print('点击扫码'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
SliverPadding(
|
|
padding: const EdgeInsets.fromLTRB(16, 8, 16, 40),
|
|
sliver: SliverList(
|
|
delegate: SliverChildListDelegate([
|
|
const SizedBox(height: 16),
|
|
// 天气和告警
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFFF2F3F5),
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
const Icon(Icons.cloud_queue, size: 16, color: Color(0xFF165DFF)),
|
|
const SizedBox(width: 6),
|
|
const Text(
|
|
'多云 28°C',
|
|
style: TextStyle(
|
|
fontSize: 13,
|
|
color: Color(0xFF4E5969),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
Expanded(
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFFFFF0F0),
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
const Icon(Icons.warning_amber_rounded, size: 16, color: Color(0xFFF53F3F)),
|
|
const SizedBox(width: 6),
|
|
const Expanded(
|
|
child: Text(
|
|
'紧急告警',
|
|
style: TextStyle(
|
|
fontSize: 13,
|
|
color: Color(0xFFF53F3F),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: 4),
|
|
const Icon(Icons.arrow_forward_ios, size: 12, color: Color(0xFFF53F3F)),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 16),
|
|
PowerCard(data: state.homeData),
|
|
const SizedBox(height: 10),
|
|
StatsGrid(data: state.homeData),
|
|
const SizedBox(height: 10),
|
|
PlantOverviewCard(overview: state.homeData.plantOverview),
|
|
const SizedBox(height: 10),
|
|
WorkOrderCard(stats: state.homeData.workOrderStats),
|
|
const SizedBox(height: 10),
|
|
QuickEntryCard(),
|
|
const SizedBox(height: 10),
|
|
PowerTrendChart(
|
|
data: state.homeData.powerTrendData,
|
|
trendType: state.trendType,
|
|
onToggleType: (type) => _bloc.add(HomeV2ToggleTrendType(type)),
|
|
),
|
|
const SizedBox(height: 40),
|
|
]),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
return const SizedBox();
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
void didChangeDependencies() {
|
|
super.didChangeDependencies();
|
|
// 首次进入时自动加载数据,但不显示转圈
|
|
if (_bloc.state is HomeV2Initial) {
|
|
_bloc.add(const HomeV2LoadData());
|
|
}
|
|
}
|
|
|
|
void _showPlantSelector() {
|
|
showModalBottomSheet(
|
|
context: context,
|
|
backgroundColor: Colors.white,
|
|
shape: const RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
|
|
),
|
|
builder: (context) {
|
|
return Container(
|
|
padding: const EdgeInsets.all(14),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
const Text(
|
|
'选择电站',
|
|
style: TextStyle(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.bold,
|
|
color: Color(0xFF1D2129),
|
|
),
|
|
),
|
|
const SizedBox(height: 14),
|
|
..._plantList.map((plant) => ListTile(
|
|
title: Text(plant),
|
|
trailing: _selectedPlant == plant
|
|
? const Icon(Icons.check, color: Color(0xFF165DFF))
|
|
: null,
|
|
onTap: () {
|
|
setState(() {
|
|
_selectedPlant = plant;
|
|
});
|
|
Navigator.pop(context);
|
|
},
|
|
)),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|