175 lines
5.8 KiB
Dart
175 lines
5.8 KiB
Dart
import 'package:flutter/material.dart';
|
|
import '../models/record.dart';
|
|
import '../services/database_service.dart';
|
|
|
|
class WeeklySpendingChart extends StatelessWidget {
|
|
final List<Record> records;
|
|
late final DatabaseService _dbService;
|
|
|
|
WeeklySpendingChart({
|
|
Key? key,
|
|
required this.records,
|
|
}) : super(key: key) {
|
|
_dbService = DatabaseService();
|
|
}
|
|
|
|
/// 获取最近7天的数据
|
|
Future<List<DailySpending>> _getWeeklyData() async {
|
|
final now = DateTime.now();
|
|
final startDate = DateTime(now.year, now.month, now.day - 6);
|
|
final endDate = DateTime(now.year, now.month, now.day, 23, 59, 59);
|
|
|
|
// 直接从数据库获取最近7天的数据
|
|
final weekRecords = await _dbService.getRecordsByDateRange(startDate, endDate);
|
|
|
|
final List<DailySpending> weeklyData = [];
|
|
for (int i = 6; i >= 0; i--) {
|
|
final date = DateTime(now.year, now.month, now.day - i);
|
|
final dayRecords = weekRecords.where((record) =>
|
|
record.createTime.year == date.year &&
|
|
record.createTime.month == date.month &&
|
|
record.createTime.day == date.day &&
|
|
record.type == RecordType.expense
|
|
);
|
|
|
|
final dailyTotal = dayRecords.fold(0.0, (sum, record) => sum + record.amount);
|
|
weeklyData.add(DailySpending(
|
|
date: date,
|
|
amount: dailyTotal,
|
|
));
|
|
}
|
|
|
|
return weeklyData;
|
|
}
|
|
|
|
String _formatAmount(double amount) {
|
|
if (amount >= 10000) {
|
|
return '${(amount / 10000).toStringAsFixed(2)}w';
|
|
} else if (amount >= 1000) {
|
|
return '${(amount / 1000).toStringAsFixed(2)}k';
|
|
}
|
|
return amount.toStringAsFixed(2);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return FutureBuilder<List<DailySpending>>(
|
|
future: _getWeeklyData(),
|
|
builder: (context, snapshot) {
|
|
if (!snapshot.hasData) {
|
|
return const Center(child: CircularProgressIndicator());
|
|
}
|
|
|
|
final weeklyData = snapshot.data!;
|
|
final totalSpending = weeklyData.fold(0.0, (sum, day) => sum + day.amount);
|
|
final maxDailySpending = weeklyData.fold(0.0, (max, day) => day.amount > max ? day.amount : max);
|
|
|
|
return Container(
|
|
padding: const EdgeInsets.all(16),
|
|
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(12),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.grey.withOpacity(0.1),
|
|
spreadRadius: 1,
|
|
blurRadius: 4,
|
|
offset: const Offset(0, 2),
|
|
),
|
|
],
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
const Text(
|
|
'最近7日支出',
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
Text(
|
|
'总计: ${totalSpending.toStringAsFixed(2)}',
|
|
style: const TextStyle(
|
|
fontSize: 14,
|
|
color: Colors.grey,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 16),
|
|
SizedBox(
|
|
height: 140,
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
crossAxisAlignment: CrossAxisAlignment.end,
|
|
children: weeklyData.map((day) {
|
|
final barHeight = maxDailySpending > 0
|
|
? (day.amount / maxDailySpending) * 80
|
|
: 0.0;
|
|
|
|
return SizedBox(
|
|
width: 40,
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
mainAxisAlignment: MainAxisAlignment.end,
|
|
children: [
|
|
Text(
|
|
_formatAmount(day.amount),
|
|
style: const TextStyle(
|
|
fontSize: 11,
|
|
height: 1.2,
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
const SizedBox(height: 4),
|
|
Container(
|
|
width: 24,
|
|
height: barHeight,
|
|
decoration: BoxDecoration(
|
|
color: Theme.of(context).primaryColor.withOpacity(0.8),
|
|
borderRadius: const BorderRadius.vertical(
|
|
top: Radius.circular(4),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 4),
|
|
Text(
|
|
_getWeekdayText(day.date.weekday),
|
|
style: const TextStyle(
|
|
fontSize: 12,
|
|
height: 1.2,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}).toList(),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
String _getWeekdayText(int weekday) {
|
|
const weekdays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
|
|
return weekdays[weekday - 1];
|
|
}
|
|
}
|
|
|
|
class DailySpending {
|
|
final DateTime date;
|
|
final double amount;
|
|
|
|
DailySpending({
|
|
required this.date,
|
|
required this.amount,
|
|
});
|
|
} |