fix: 用药模块重做 + 运动计划不限7天 + 打卡可切换 + 删除修复

- 用药: 重写列表页(标签筛选+详情弹窗+打卡切换+滑动删除)
- 用药: 重写编辑页(选择器替代输入+同列布局+频率改为每天几次)
- 用药: 加Notes字段+DELETE端点+修复重复端点
- 运动: 不限7天, startDate+索引推算当天
- 打卡: 药/运动打卡可切换(点✓取消)
- 删除: 修复滑动删除热区, 一次点击即删除
- 侧边栏: 一键清理历史对话
- 饮食/运动/用药/问诊 API路径加/api/前缀
- 通知时机修正: 新建计划不再提前弹窗
This commit is contained in:
MingNian
2026-06-04 21:15:23 +08:00
parent b944a31983
commit cd65bc4d65
11 changed files with 1069 additions and 1242 deletions

View File

@@ -175,11 +175,7 @@ class ChatMessagesView extends ConsumerWidget {
Widget _agentActionBtn(_AgentAction a, double screenWidth, BuildContext context, WidgetRef ref, _AgentColors colors) {
return InkWell(
onTap: () {
if (a.action == 'createPlan') {
_createExercisePlan(ref, context);
} else if (a.action == 'checkIn') {
_exerciseCheckIn(ref, context);
} else if (a.label == '服药打卡') {
if (a.label == '服药打卡') {
_medicationCheckIn(ref, context);
} else if (a.route != null) {
if (a.route == 'camera' || a.route == 'gallery') {
@@ -1200,73 +1196,6 @@ class ChatMessagesView extends ConsumerWidget {
}
}
static void _createExercisePlan(WidgetRef ref, BuildContext context) async {
try {
final service = ref.read(exerciseServiceProvider);
final today = DateTime.now();
final monday = today.subtract(Duration(days: today.weekday - 1));
final items = List.generate(7, (i) => {
'dayOfWeek': i,
'exerciseType': i == 2 || i == 5 ? '休息' : '散步',
'durationMinutes': i == 2 || i == 5 ? 0 : 30,
'isRestDay': i == 2 || i == 5,
});
await service.createPlan({
'weekStartDate': '${monday.year}-${monday.month.toString().padLeft(2, '0')}-${monday.day.toString().padLeft(2, '0')}',
'items': items,
});
ref.invalidate(currentExercisePlanProvider);
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('运动计划已创建'), backgroundColor: Color(0xFF43A047)),
);
pushRoute(ref, 'exercisePlan');
} catch (e) {
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('创建失败:$e'), backgroundColor: Colors.red),
);
}
}
static void _exerciseCheckIn(WidgetRef ref, BuildContext context) async {
try {
final plan = await ref.read(currentExercisePlanProvider.future);
if (plan == null || plan.isEmpty) {
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('请先创建运动计划'), backgroundColor: Color(0xFFFF9800)),
);
return;
}
final items = (plan['items'] as List?)?.cast<Map<String, dynamic>>() ?? [];
final today = DateTime.now().weekday - 1;
final todayItem = items.cast<Map<String, dynamic>?>().firstWhere(
(i) => i?['dayOfWeek'] == today && i?['isRestDay'] != true,
orElse: () => null,
);
if (todayItem == null) {
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('今天休息日'), backgroundColor: Color(0xFFFF9800)),
);
return;
}
final service = ref.read(exerciseServiceProvider);
await service.checkIn(todayItem['id']?.toString() ?? '');
ref.invalidate(currentExercisePlanProvider);
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('打卡成功'), backgroundColor: Color(0xFF43A047)),
);
} catch (e) {
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('打卡失败:$e'), backgroundColor: Colors.red),
);
}
}
static _AgentColors _agentColors(ActiveAgent agent) {
return switch (agent) {
ActiveAgent.consultation => _AgentColors(
@@ -1401,13 +1330,27 @@ class ChatMessagesView extends ConsumerWidget {
tasks.add(_taskRow(context, Icons.medication_rounded, '暂无用药提醒', status: 'pending'));
}
// 3. 运动
final exOverdue = now.hour >= 18;
tasks.add(_taskRow(
context, Icons.directions_run, '今日待运动:散步 30 分钟',
status: exOverdue ? 'overdue' : 'pending',
onTap: () => pushRoute(ref, 'exercisePlan'),
));
// 3. 运动 — 从所有计划中找今日待打卡项
final plans = ref.read(exerciseServiceProvider).getPlans();
plans.then((list) {
for (final p in list) {
final items = (p['items'] as List?)?.cast<Map<String, dynamic>>() ?? [];
final today = now.weekday - 1;
final todayItem = items.cast<Map<String, dynamic>?>().firstWhere(
(i) => i?['dayOfWeek'] == today, orElse: () => null);
if (todayItem != null) {
final done = todayItem['isCompleted'] == true;
final name = items.isNotEmpty ? (items.first['exerciseType']?.toString() ?? '运动') : '运动';
final dur = items.isNotEmpty ? (items.first['durationMinutes']?.toString() ?? '30') : '30';
tasks.add(_taskRow(context, Icons.directions_run,
'$name $dur分钟',
status: done ? 'done' : (now.hour >= 18 ? 'overdue' : 'pending'),
onTap: () => pushRoute(ref, 'exercisePlan'),
));
break;
}
}
});
// 4. 测量
tasks.add(_taskRow(
@@ -1529,8 +1472,7 @@ final _agentActions = <ActiveAgent, List<_AgentAction>>{
],
ActiveAgent.exercise: [
_AgentAction(label: '查看计划', icon: Icons.calendar_month_outlined, isWide: true, route: 'exercisePlan'),
_AgentAction(label: '新建计划', icon: Icons.add_task_outlined, isWide: true, action: 'createPlan'),
_AgentAction(label: '今日打卡', icon: Icons.fact_check_outlined, isWide: true, action: 'checkIn'),
_AgentAction(label: '新建计划', icon: Icons.add_task_outlined, isWide: true, route: 'exerciseCreate'),
],
};