fix: 图片发送/医生加载/运动超时/用药黑屏/服药打卡

- sendImage: 本地预览→上传→远程URL替换
- doctorListProvider: 8s超时+mock医生fallback
- currentExercisePlanProvider: 8s超时→显示空状态
- 用药编辑: try-catch防黑屏+刷新列表
- 服药打卡: 接入后端confirm()接口
This commit is contained in:
MingNian
2026-06-03 20:03:17 +08:00
parent 95bf5732f6
commit e3b9716f7c
11 changed files with 916 additions and 393 deletions

View File

@@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'auth_provider.dart';
import 'data_providers.dart';
@@ -94,7 +95,7 @@ final conversationListProvider = FutureProvider<List<ConversationItem>>((ref) as
);
}).toList();
} catch (_) {
return _mockConversations;
return [];
}
});
@@ -110,30 +111,6 @@ ActiveAgent _parseAgent(String? type) {
}
}
final _mockConversations = [
ConversationItem(
id: '1',
title: '用药咨询',
lastMessage: '阿司匹林应该什么时候吃?',
updatedAt: DateTime.now().subtract(const Duration(hours: 2)),
agent: ActiveAgent.medication,
),
ConversationItem(
id: '2',
title: '血压偏高',
lastMessage: '血压145/90需要注意什么',
updatedAt: DateTime.now().subtract(const Duration(hours: 5)),
agent: ActiveAgent.health,
),
ConversationItem(
id: '3',
title: '饮食建议',
lastMessage: '今天吃了米饭和红烧肉',
updatedAt: DateTime.now().subtract(const Duration(days: 1)),
agent: ActiveAgent.diet,
),
];
class ChatNotifier extends Notifier<ChatState> {
StreamSubscription<Map<String, dynamic>>? _subscription;
@@ -142,7 +119,7 @@ class ChatNotifier extends Notifier<ChatState> {
void setAgent(ActiveAgent a) {
_subscription?.cancel();
state = state.activeAgent == a ? const ChatState() : ChatState(activeAgent: a);
state = state.copyWith(activeAgent: a);
}
void insertAgentWelcome(ActiveAgent agent) {
@@ -156,6 +133,49 @@ class ChatNotifier extends Notifier<ChatState> {
)]);
}
Future<void> sendImage(String imagePath, String text) async {
final file = File(imagePath);
if (!await file.exists()) return;
// 先显示用户消息(本地显示图片路径)
final userMsg = ChatMessage(
id: '${DateTime.now().millisecondsSinceEpoch}',
role: 'user',
content: text.isNotEmpty ? text : '[图片]',
createdAt: DateTime.now(),
metadata: {'localImagePath': imagePath},
);
state = state.copyWith(messages: [...state.messages, userMsg]);
// 异步上传图片
String? uploadedUrl;
try {
final api = ref.read(apiClientProvider);
uploadedUrl = await api.uploadFile('/api/upload', file);
} catch (_) {
// 上传失败:保留本地路径,仍然可以本地显示
}
// 更新消息元数据(上传成功则替换为远程 URL
final finalUrl = uploadedUrl ?? imagePath;
final updatedMsgs = state.messages.toList();
final idx = updatedMsgs.indexWhere((m) => m.id == userMsg.id);
if (idx >= 0) {
updatedMsgs[idx] = ChatMessage(
id: userMsg.id,
role: 'user',
content: userMsg.content,
createdAt: userMsg.createdAt,
metadata: {'imageUrl': finalUrl},
);
state = state.copyWith(messages: updatedMsgs);
}
// 将图片 URL 作为消息内容发送给 AI
final msgWithImage = text.isNotEmpty ? '$text\n[图片已上传]' : '[图片已上传]';
await _sendToAI(msgWithImage);
}
Future<void> sendMessage(String text) async {
if (text.trim().isEmpty || state.isStreaming) return;
@@ -168,6 +188,10 @@ class ChatNotifier extends Notifier<ChatState> {
state = state.copyWith(
messages: [...state.messages, userMsg], isStreaming: true);
await _sendToAI(text);
}
Future<void> _sendToAI(String text) async {
final aiMsg = ChatMessage(
id: '${DateTime.now().millisecondsSinceEpoch}_ai',
role: 'assistant',
@@ -175,6 +199,8 @@ class ChatNotifier extends Notifier<ChatState> {
createdAt: DateTime.now(),
);
state = state.copyWith(isStreaming: true);
try {
final token = await ref.read(apiClientProvider).accessToken;
if (token == null) {