feat: 用药打卡全面重构 - 按次追踪 + 独立打卡页 + 任务区汇总
- 后端用药提醒改为按次粒度(每个服药时间独立判断) - 新增 per-dose 打卡/取消打卡 API - 新增 MedicationCheckInPage 独立打卡页(每药每时间单独toggle) - 用药列表删底部弹窗(编辑/停药),点药品直接进该药打卡页 - 任务区用药汇总为一行(过期待服已服) - 支持隔天(EveryOtherDay)/每周频率判断 - 删历史对话功能(ConversationItem + conversationListProvider + UI)
This commit is contained in:
@@ -13,7 +13,7 @@ class ChatMessage {
|
||||
String content;
|
||||
final DateTime createdAt;
|
||||
MessageType type;
|
||||
final Map<String, dynamic>? metadata;
|
||||
Map<String, dynamic>? metadata;
|
||||
bool confirmed;
|
||||
ChatMessage({
|
||||
required this.id,
|
||||
@@ -53,22 +53,6 @@ class ChatState {
|
||||
);
|
||||
}
|
||||
|
||||
class ConversationItem {
|
||||
final String id;
|
||||
final String title;
|
||||
final String lastMessage;
|
||||
final DateTime updatedAt;
|
||||
final ActiveAgent agent;
|
||||
|
||||
ConversationItem({
|
||||
required this.id,
|
||||
required this.title,
|
||||
required this.lastMessage,
|
||||
required this.updatedAt,
|
||||
required this.agent,
|
||||
});
|
||||
}
|
||||
|
||||
class SelectedAgentNotifier extends Notifier<ActiveAgent?> {
|
||||
@override
|
||||
ActiveAgent? build() => null;
|
||||
@@ -78,28 +62,6 @@ class SelectedAgentNotifier extends Notifier<ActiveAgent?> {
|
||||
final selectedAgentProvider =
|
||||
NotifierProvider<SelectedAgentNotifier, ActiveAgent?>(SelectedAgentNotifier.new);
|
||||
final chatProvider = NotifierProvider<ChatNotifier, ChatState>(ChatNotifier.new);
|
||||
final conversationListProvider = FutureProvider<List<ConversationItem>>((ref) async {
|
||||
final api = ref.watch(apiClientProvider);
|
||||
final token = await api.accessToken;
|
||||
if (token == null) return [];
|
||||
|
||||
try {
|
||||
final res = await api.get('/api/ai/conversations');
|
||||
final list = res.data['data'] as List? ?? [];
|
||||
return list.map((item) {
|
||||
final data = item as Map<String, dynamic>;
|
||||
return ConversationItem(
|
||||
id: data['id']?.toString() ?? '',
|
||||
title: data['title']?.toString() ?? '对话',
|
||||
lastMessage: data['lastMessage']?.toString() ?? '',
|
||||
updatedAt: DateTime.parse(data['updatedAt']?.toString() ?? DateTime.now().toIso8601String()),
|
||||
agent: _parseAgent(data['agentType']?.toString()),
|
||||
);
|
||||
}).toList();
|
||||
} catch (_) {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
ActiveAgent _parseAgent(String? type) {
|
||||
switch (type?.toLowerCase()) {
|
||||
@@ -138,6 +100,8 @@ class ChatNotifier extends Notifier<ChatState> {
|
||||
}
|
||||
|
||||
void setAgent(ActiveAgent a) {
|
||||
// 流式回复中忽略胶囊切换,防止状态混乱
|
||||
if (state.isStreaming) return;
|
||||
_subscription?.cancel();
|
||||
state = state.copyWith(activeAgent: a);
|
||||
ref.read(selectedAgentProvider.notifier).select(a);
|
||||
@@ -190,16 +154,12 @@ class ChatNotifier extends Notifier<ChatState> {
|
||||
);
|
||||
}).toList();
|
||||
|
||||
// 从对话列表中找到对应的 agent 类型
|
||||
final convList = ref.read(conversationListProvider).value ?? [];
|
||||
final conv = convList.firstWhere((c) => c.id == convId, orElse: () => ConversationItem(id: convId, title: '', lastMessage: '', updatedAt: DateTime.now(), agent: ActiveAgent.default_));
|
||||
|
||||
state = state.copyWith(
|
||||
messages: messages,
|
||||
conversationId: convId,
|
||||
activeAgent: conv.agent,
|
||||
activeAgent: ActiveAgent.default_,
|
||||
);
|
||||
ref.read(selectedAgentProvider.notifier).select(conv.agent);
|
||||
ref.read(selectedAgentProvider.notifier).select(ActiveAgent.default_);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
@@ -331,6 +291,10 @@ class ChatNotifier extends Notifier<ChatState> {
|
||||
aiMsg.content += (j['data'] as String?) ?? '';
|
||||
final messageType = j['type'] as String? ?? 'text';
|
||||
aiMsg.type = _parseMessageType(messageType);
|
||||
// 从 SSE 中获取元数据(用药/健康数据的确认信息)
|
||||
if (j['metadata'] is Map) {
|
||||
aiMsg.metadata = Map<String, dynamic>.from(j['metadata']);
|
||||
}
|
||||
state = state.copyWith(thinkingText: null);
|
||||
_update(aiMsg);
|
||||
case 'notice':
|
||||
|
||||
Reference in New Issue
Block a user