- Flutter 3.x 项目初始化(pubspec.yaml + 目录结构) - 核心依赖:dio/go_router/provider/shared_preferences/hive/geolocator - 统一登录页 + Token管理(TokenService + AuthInterceptor) - 底部Tab三合一导航(供水/巡检/营收) - 供水管理Tab:监测数据列表(MonitorListPage) - 巡检Tab:任务列表+状态筛选+进度展示(PatrolTaskListPage) - 营收Tab:抄表页面+账单列表(MeterReadingPage/BillListPage) - 消息推送服务(PushService) - GPS定位服务(LocationService) - 拍照/相册服务(CameraService) - 离线缓存服务(CacheService) - Android/iOS打包配置+权限声明 - GoRouter路由+Auth认证守卫 - Provider状态管理
239 lines
8.4 KiB
Dart
239 lines
8.4 KiB
Dart
import 'package:flutter/material.dart';
|
|
import '../models/meter_reading_model.dart';
|
|
import 'bill_list_page.dart';
|
|
|
|
/// 抄表页面(营收 Tab 入口,包含抄表列表和账单入口)
|
|
class MeterReadingPage extends StatefulWidget {
|
|
const MeterReadingPage({super.key});
|
|
|
|
@override
|
|
State<MeterReadingPage> createState() => _MeterReadingPageState();
|
|
}
|
|
|
|
class _MeterReadingPageState extends State<MeterReadingPage>
|
|
with AutomaticKeepAliveClientMixin {
|
|
List<MeterReadingModel> _readings = [];
|
|
bool _isLoading = true;
|
|
|
|
@override
|
|
bool get wantKeepAlive => true;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_loadData();
|
|
}
|
|
|
|
void _loadData() {
|
|
Future.delayed(const Duration(milliseconds: 500), () {
|
|
setState(() {
|
|
_readings = _generateMockData();
|
|
_isLoading = false;
|
|
});
|
|
});
|
|
}
|
|
|
|
List<MeterReadingModel> _generateMockData() {
|
|
return [
|
|
MeterReadingModel(
|
|
id: '1', meterNo: 'WM-2024-001', userName: '张三',
|
|
address: '城东街道12号', previousReading: 1250.5,
|
|
status: 'pending', readingDate: DateTime.now(),
|
|
),
|
|
MeterReadingModel(
|
|
id: '2', meterNo: 'WM-2024-002', userName: '李四',
|
|
address: '城西大道88号', previousReading: 3420.0,
|
|
currentReading: 3456.0, status: 'completed',
|
|
readingDate: DateTime.now().subtract(const Duration(days: 1)),
|
|
reader: '王工',
|
|
),
|
|
MeterReadingModel(
|
|
id: '3', meterNo: 'WM-2024-003', userName: '王五',
|
|
address: '南区花园5栋', previousReading: 890.0,
|
|
status: 'pending', readingDate: DateTime.now(),
|
|
),
|
|
MeterReadingModel(
|
|
id: '4', meterNo: 'WM-2024-004', userName: '赵六',
|
|
address: '北区商铺16号', previousReading: 5600.0,
|
|
currentReading: 5780.0, status: 'completed',
|
|
readingDate: DateTime.now().subtract(const Duration(days: 2)),
|
|
reader: '李工',
|
|
),
|
|
MeterReadingModel(
|
|
id: '5', meterNo: 'WM-2024-005', userName: '孙七',
|
|
address: '中心路28号', previousReading: 2100.0,
|
|
status: 'abnormal', readingDate: DateTime.now(),
|
|
remark: '水表损坏',
|
|
),
|
|
];
|
|
}
|
|
|
|
Color _getStatusColor(String status) {
|
|
switch (status) {
|
|
case 'pending': return Colors.orange;
|
|
case 'completed': return Colors.green;
|
|
case 'abnormal': return Colors.red;
|
|
default: return Colors.grey;
|
|
}
|
|
}
|
|
|
|
String _getStatusText(String status) {
|
|
switch (status) {
|
|
case 'pending': return '待抄表';
|
|
case 'completed': return '已完成';
|
|
case 'abnormal': return '异常';
|
|
default: return status;
|
|
}
|
|
}
|
|
|
|
void _showReadingDialog(MeterReadingModel reading) {
|
|
final controller = TextEditingController();
|
|
showDialog(
|
|
context: context,
|
|
builder: (ctx) => AlertDialog(
|
|
title: Text('抄表 - ${reading.userName}'),
|
|
content: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text('表号: ${reading.meterNo}'),
|
|
Text('上次读数: ${reading.previousReading}'),
|
|
const SizedBox(height: 12),
|
|
TextField(
|
|
controller: controller,
|
|
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
|
decoration: const InputDecoration(
|
|
labelText: '当前读数',
|
|
hintText: '请输入当前水表读数',
|
|
),
|
|
),
|
|
],
|
|
),
|
|
actions: [
|
|
TextButton(onPressed: () => Navigator.pop(ctx), child: const Text('取消')),
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
// TODO: 提交抄表数据
|
|
Navigator.pop(ctx);
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(content: Text('抄表数据已提交')),
|
|
);
|
|
},
|
|
child: const Text('提交'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
super.build(context);
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: const Text('营收管理'),
|
|
actions: [
|
|
IconButton(
|
|
icon: const Icon(Icons.receipt_long),
|
|
tooltip: '账单列表',
|
|
onPressed: () {
|
|
Navigator.of(context).push(
|
|
MaterialPageRoute(builder: (_) => const BillListPage()),
|
|
);
|
|
},
|
|
),
|
|
],
|
|
),
|
|
body: _isLoading
|
|
? const Center(child: CircularProgressIndicator())
|
|
: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// 统计卡片
|
|
Padding(
|
|
padding: const EdgeInsets.all(12),
|
|
child: Row(
|
|
children: [
|
|
_buildStatCard('待抄表', '${_readings.where((r) => r.status == 'pending').length}', Colors.orange),
|
|
const SizedBox(width: 8),
|
|
_buildStatCard('已完成', '${_readings.where((r) => r.status == 'completed').length}', Colors.green),
|
|
const SizedBox(width: 8),
|
|
_buildStatCard('异常', '${_readings.where((r) => r.status == 'abnormal').length}', Colors.red),
|
|
],
|
|
),
|
|
),
|
|
const Padding(
|
|
padding: EdgeInsets.symmetric(horizontal: 12),
|
|
child: Text('抄表任务', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
|
),
|
|
// 抄表列表
|
|
Expanded(
|
|
child: ListView.builder(
|
|
padding: const EdgeInsets.all(12),
|
|
itemCount: _readings.length,
|
|
itemBuilder: (context, index) {
|
|
final reading = _readings[index];
|
|
return Card(
|
|
margin: const EdgeInsets.only(bottom: 8),
|
|
child: ListTile(
|
|
leading: CircleAvatar(
|
|
backgroundColor: _getStatusColor(reading.status).withOpacity(0.1),
|
|
child: Icon(
|
|
Icons.speed,
|
|
color: _getStatusColor(reading.status),
|
|
),
|
|
),
|
|
title: Text(reading.userName),
|
|
subtitle: Text('${reading.meterNo} · ${reading.address}'),
|
|
trailing: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
crossAxisAlignment: CrossAxisAlignment.end,
|
|
children: [
|
|
Text(
|
|
_getStatusText(reading.status),
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
color: _getStatusColor(reading.status),
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
if (reading.status == 'pending')
|
|
TextButton(
|
|
onPressed: () => _showReadingDialog(reading),
|
|
style: TextButton.styleFrom(
|
|
padding: EdgeInsets.zero,
|
|
minimumSize: const Size(0, 0),
|
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
),
|
|
child: const Text('抄表', style: TextStyle(fontSize: 12)),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildStatCard(String label, String count, Color color) {
|
|
return Expanded(
|
|
child: Card(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(12),
|
|
child: Column(
|
|
children: [
|
|
Text(count, style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: color)),
|
|
const SizedBox(height: 4),
|
|
Text(label, style: TextStyle(fontSize: 12, color: Colors.grey[600])),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|