feat: 实现日历提醒完整功能(操作执行、通知服务重构、归档)

- 新增 ReminderActionExecutor 处理取消/稍后提醒操作
- 新增 ReminderOutboxStore 本地存储待处理操作
- 重构 LocalNotificationService 支持聚合提醒和交互操作
- 新增 event_color_resolver 工具类统一颜色解析
- 新增 CalendarService.archiveEvent 归档方法
- 增强 ModelTracking 支持缓存命中、推理token和成本追踪
- 添加 qwen3.5-35b-a3b 模型配置
- 更新 AndroidManifest 全屏intent权限
- 补充相关单元测试和文档
This commit is contained in:
qzl
2026-03-18 19:12:47 +08:00
parent 257cb0f5d5
commit 00f37d7e19
35 changed files with 2676 additions and 244 deletions
@@ -9,6 +9,7 @@ import '../../data/models/schedule_item_model.dart';
import '../../data/services/calendar_service.dart';
import '../calendar_state_manager.dart';
import '../calendar_time_utils.dart';
import '../utils/event_color_resolver.dart';
import '../dayweek/day_event_layout_engine.dart';
import '../dayweek/day_timeline_metrics.dart';
import '../dayweek/day_view_scale.dart';
@@ -614,7 +615,10 @@ class _CalendarDayWeekScreenState extends State<CalendarDayWeekScreen>
required DayEventLayout layout,
required double boardHeight,
}) {
final eventColor = _parseColor(layout.event.metadata?.color);
final eventColor = resolveEventColor(
status: layout.event.status,
colorHex: layout.event.metadata?.color,
);
final isCompact = layout.visualHeight < 20;
final tapHeight = layout.visualHeight < _minEventTapHeight
? _minEventTapHeight
@@ -687,17 +691,6 @@ class _CalendarDayWeekScreenState extends State<CalendarDayWeekScreen>
);
}
Color _parseColor(String? hex) {
if (hex == null || hex.isEmpty) {
return AppColors.blue600;
}
try {
return Color(int.parse(hex.replaceFirst('#', '0xFF')));
} catch (_) {
return AppColors.blue600;
}
}
Widget _buildBottomDock() {
return BottomDock(
activeTab: DockTab.calendar,
@@ -10,6 +10,7 @@ import '../../../../shared/widgets/detail_header_action_menu.dart';
import '../../../../shared/widgets/destructive_action_sheet.dart';
import '../../data/services/calendar_service.dart';
import '../../data/models/schedule_item_model.dart';
import '../utils/event_color_resolver.dart';
import '../widgets/create_event_sheet.dart';
import '../widgets/calendar_share_dialog.dart';
@@ -223,7 +224,7 @@ class _CalendarEventDetailScreenState extends State<CalendarEventDetailScreen> {
_formatReminderText(event.metadata?.reminderMinutes),
),
const SizedBox(height: 14),
_buildColorField(event.metadata?.color),
_buildColorField(event),
const SizedBox(height: 14),
if (event.metadata?.location != null) ...[
_buildDetailField('地点', event.metadata!.location!),
@@ -275,7 +276,10 @@ class _CalendarEventDetailScreenState extends State<CalendarEventDetailScreen> {
width: 4,
height: 20,
decoration: BoxDecoration(
color: _parseColor(event.metadata?.color),
color: resolveEventColor(
status: event.status,
colorHex: event.metadata?.color,
),
borderRadius: BorderRadius.circular(2),
),
),
@@ -343,8 +347,11 @@ class _CalendarEventDetailScreenState extends State<CalendarEventDetailScreen> {
);
}
Widget _buildColorField(String? colorHex) {
final color = _parseColor(colorHex);
Widget _buildColorField(ScheduleItemModel event) {
final color = resolveEventColor(
status: event.status,
colorHex: event.metadata?.color,
);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -400,15 +407,6 @@ class _CalendarEventDetailScreenState extends State<CalendarEventDetailScreen> {
);
}
Color _parseColor(String? hex) {
if (hex == null || hex.isEmpty) return AppColors.blue600;
try {
return Color(int.parse(hex.replaceFirst('#', '0xFF')));
} catch (_) {
return AppColors.blue600;
}
}
Widget _buildInputContainer() {
return Container(
height: 80,
@@ -7,6 +7,7 @@ import '../../../../core/theme/design_tokens.dart';
import '../../../../shared/widgets/app_pressable.dart';
import '../calendar_state_manager.dart';
import '../calendar_time_utils.dart';
import '../utils/event_color_resolver.dart';
import '../widgets/bottom_dock.dart';
import '../widgets/create_event_sheet.dart';
import '../../data/models/schedule_item_model.dart';
@@ -360,7 +361,10 @@ class _CalendarMonthScreenState extends State<CalendarMonthScreen>
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...displayEvents.map((event) {
final color = _parseColor(event.metadata?.color);
final color = resolveEventColor(
status: event.status,
colorHex: event.metadata?.color,
);
return AppPressable(
borderRadius: BorderRadius.circular(AppRadius.sm),
onTap: () {
@@ -415,15 +419,6 @@ class _CalendarMonthScreenState extends State<CalendarMonthScreen>
);
}
Color _parseColor(String? hex) {
if (hex == null || hex.isEmpty) return AppColors.blue600;
try {
return Color(int.parse(hex.replaceFirst('#', '0xFF')));
} catch (_) {
return AppColors.blue600;
}
}
void _showMonthPicker() {
var selectedYear = _currentMonth.year;
var selectedMonth = _currentMonth.month;
@@ -0,0 +1,23 @@
import 'package:flutter/material.dart';
import '../../../../core/theme/design_tokens.dart';
import '../../data/models/schedule_item_model.dart';
Color resolveEventColor({
required ScheduleStatus status,
required String? colorHex,
}) {
if (status == ScheduleStatus.archived) {
return AppColors.slate400;
}
if (colorHex == null || colorHex.isEmpty) {
return AppColors.blue600;
}
try {
return Color(int.parse(colorHex.replaceFirst('#', '0xFF')));
} catch (_) {
return AppColors.blue600;
}
}
@@ -669,9 +669,9 @@ class _CreateEventSheetState extends State<CreateEventSheet>
try {
final notificationService = sl<LocalNotificationService>();
await notificationService.upsertEventReminder(saved);
} catch (e) {
} catch (_) {
if (mounted) {
Toast.show(context, '提醒创建失败$e', type: ToastType.warning);
Toast.show(context, '提醒创建失败,请检查通知权限', type: ToastType.warning);
}
}