Initial commit: 健康管家 AI 健康陪伴助手

- Backend: .NET 10 Minimal API + EF Core + PostgreSQL
- Frontend: Flutter + Riverpod + GoRouter + Dio
- AI: DeepSeek LLM + Qwen VLM (OpenAI-compatible)
- Auth: SMS + JWT (access/refresh tokens)
- Features: AI chat, health tracking, medication management, diet analysis, exercise plans, doctor consultations, report analysis
This commit is contained in:
MingNian
2026-06-02 11:11:29 +08:00
commit 14d7c30d3d
144 changed files with 11436 additions and 0 deletions

View File

@@ -0,0 +1,88 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../providers/chat_provider.dart';
/// 对话消息列表
class ChatMessagesView extends ConsumerWidget {
final ScrollController scrollCtrl;
final List<ChatMessage> messages;
const ChatMessagesView({super.key, required this.scrollCtrl, required this.messages});
@override
Widget build(BuildContext context, WidgetRef ref) {
final chatState = ref.watch(chatProvider);
if (messages.isEmpty) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.chat_bubble_outline, size: 48, color: Colors.grey[300]),
const SizedBox(height: 12),
Text('开始和 AI 健康管家对话吧', style: Theme.of(context).textTheme.bodyMedium),
],
),
);
}
return ListView.builder(
controller: scrollCtrl,
reverse: true,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
itemCount: messages.length,
itemBuilder: (context, index) {
final msg = messages[messages.length - 1 - index];
return _buildMessageBubble(context, msg, chatState);
},
);
}
Widget _buildMessageBubble(BuildContext context, ChatMessage msg, ChatState chatState) {
final isUser = msg.isUser;
return Align(
alignment: isUser ? Alignment.centerRight : Alignment.centerLeft,
child: Container(
margin: const EdgeInsets.only(bottom: 12),
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.78),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: isUser ? const Color(0xFF635BFF) : Colors.white,
borderRadius: BorderRadius.circular(16),
border: isUser ? null : const Border(left: BorderSide(color: Color(0xFF635BFF), width: 3)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (!isUser && chatState.isStreaming && msg.content.isEmpty)
_buildThinkingIndicator()
else
Text(
msg.content.isEmpty && !isUser ? '...' : msg.content,
style: TextStyle(fontSize: 16, color: isUser ? Colors.white : const Color(0xFF1A1A1A)),
),
if (!isUser && msg.content.isNotEmpty && !chatState.isStreaming)
Padding(
padding: const EdgeInsets.only(top: 8),
child: Text(
'AI 健康管家 · 仅供参考',
style: TextStyle(fontSize: 12, color: Colors.grey[400]),
),
),
],
),
),
);
}
Widget _buildThinkingIndicator() {
return const Row(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(width: 14, height: 14, child: CircularProgressIndicator(strokeWidth: 2)),
SizedBox(width: 8),
Text('思考中...', style: TextStyle(fontSize: 14, color: Color(0xFF999999))),
],
);
}
}