style: 淡薰紫 Lavender Breeze + UI修复

- 主色改淡薰紫 #8B9CF7 + 白底 清新风格
- 每个智能体卡不同淡色调
- 删除欢迎卡底部"或直接对我说"
- 运动创建/打卡接入 API
- 全项目薄荷绿→淡紫替换
This commit is contained in:
MingNian
2026-06-03 20:46:39 +08:00
parent f46c30f8e7
commit f6c1ea7ec9
18 changed files with 306 additions and 254 deletions

View File

@@ -86,7 +86,7 @@ class _HomePageState extends ConsumerState<HomePage> {
return Scaffold(
drawer: const HealthDrawer(),
backgroundColor: const Color(0xFFF6F9FB),
backgroundColor: const Color(0xFFF8F9FC),
body: SafeArea(
child: Column(children: [
// ── 顶部栏 ──
@@ -110,11 +110,11 @@ class _HomePageState extends ConsumerState<HomePage> {
child: Row(children: [
Builder(builder: (ctx) => GestureDetector(
onTap: () => Scaffold.of(ctx).openDrawer(),
child: CircleAvatar(radius: 20, backgroundColor: const Color(0xFFE6FAF6), backgroundImage: user?.avatarUrl != null ? NetworkImage(user!.avatarUrl!) : null, child: user?.avatarUrl == null ? const Icon(Icons.person, size: 24, color: Color(0xFF14B8A6)) : null),
child: CircleAvatar(radius: 20, backgroundColor: const Color(0xFFF0F2FF), backgroundImage: user?.avatarUrl != null ? NetworkImage(user!.avatarUrl!) : null, child: user?.avatarUrl == null ? const Icon(Icons.person, size: 24, color: Color(0xFF8B9CF7)) : null),
)),
const SizedBox(width: 10),
Expanded(child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Row(mainAxisSize: MainAxisSize.min, children: [Icon(Icons.smart_toy_outlined, size: 16, color: const Color(0xFF14B8A6)), const SizedBox(width: 4), Text('AI 健康管家', style: TextStyle(fontSize: 13, fontWeight: FontWeight.w500, color: Colors.grey[600]))]),
Row(mainAxisSize: MainAxisSize.min, children: [Icon(Icons.smart_toy_outlined, size: 16, color: const Color(0xFF8B9CF7)), const SizedBox(width: 4), Text('AI 健康管家', style: TextStyle(fontSize: 13, fontWeight: FontWeight.w500, color: Colors.grey[600]))]),
const SizedBox(height: 2),
Text('${_getGreeting()}${user?.name ?? '张三'}', style: const TextStyle(fontSize: 17, fontWeight: FontWeight.bold, color: Color(0xFF1A1A1A))),
])),
@@ -151,11 +151,11 @@ class _HomePageState extends ConsumerState<HomePage> {
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [BoxShadow(color: const Color(0xFF14B8A6).withAlpha(8), blurRadius: 6, offset: const Offset(0, 1))]),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [BoxShadow(color: const Color(0xFF8B9CF7).withAlpha(8), blurRadius: 6, offset: const Offset(0, 1))]),
child: Row(children: [
const Text('今日任务', style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600, color: Color(0xFF1A1A1A))),
const Spacer(),
const Text('展开 ▾', style: TextStyle(fontSize: 12, color: Color(0xFF14B8A6))),
const Text('展开 ▾', style: TextStyle(fontSize: 12, color: Color(0xFF8B9CF7))),
]),
),
);
@@ -165,7 +165,7 @@ class _HomePageState extends ConsumerState<HomePage> {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [BoxShadow(color: const Color(0xFF14B8A6).withAlpha(8), blurRadius: 6, offset: const Offset(0, 1))]),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [BoxShadow(color: const Color(0xFF8B9CF7).withAlpha(8), blurRadius: 6, offset: const Offset(0, 1))]),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Row(children: [
const Text('今日任务', style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600, color: Color(0xFF1A1A1A))),
@@ -339,7 +339,7 @@ class _HomePageState extends ConsumerState<HomePage> {
borderRadius: BorderRadius.circular(8),
),
child: Row(crossAxisAlignment: CrossAxisAlignment.center, children: [
Container(width: 30, height: 30, decoration: BoxDecoration(color: const Color(0xFFF2FAF9), borderRadius: BorderRadius.circular(8)), child: Icon(icon, size: 15, color: const Color(0xFF14B8A6))),
Container(width: 30, height: 30, decoration: BoxDecoration(color: const Color(0xFFF0F2FF), borderRadius: BorderRadius.circular(8)), child: Icon(icon, size: 15, color: const Color(0xFF8B9CF7))),
const SizedBox(width: 10), Expanded(child: Text(label, style: const TextStyle(fontSize: 13, color: Color(0xFF333333)))),
Icon(icons[effectiveStatus], size: 18, color: colors[effectiveStatus] ?? Colors.grey),
]),
@@ -351,7 +351,7 @@ class _HomePageState extends ConsumerState<HomePage> {
void _handleMedicationCheck() async {
await ref.read(medicationServiceProvider).confirm('');
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('已记录服药 ✅'), backgroundColor: Color(0xFF14B8A6)));
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('已记录服药 ✅'), backgroundColor: Color(0xFF8B9CF7)));
}
// ═════════════════════ 智能体选择条(常驻) ═════════════════════
@@ -392,9 +392,9 @@ class _HomePageState extends ConsumerState<HomePage> {
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: isActive ? const Color(0xFF14B8A6) : Colors.white,
color: isActive ? const Color(0xFF8B9CF7) : Colors.white,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: isActive ? const Color(0xFF14B8A6) : const Color(0xFFE0E0E0)),
border: Border.all(color: isActive ? const Color(0xFF8B9CF7) : const Color(0xFFE0E0E0)),
),
child: Row(mainAxisSize: MainAxisSize.min, children: [
Icon(icon, size: 13, color: isActive ? Colors.white : const Color(0xFF666666)),
@@ -456,7 +456,7 @@ class _HomePageState extends ConsumerState<HomePage> {
decoration: const InputDecoration(hintText: '输入你想说的...', hintStyle: TextStyle(fontSize: 15, color: Color(0xFFBBBBBB)), contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 10), border: InputBorder.none),
onSubmitted: (_) => _sendMessage(),
)),
IconButton(icon: const Icon(Icons.send, size: 24, color: Color(0xFF14B8A6)), onPressed: _sendMessage),
IconButton(icon: const Icon(Icons.send, size: 24, color: Color(0xFF8B9CF7)), onPressed: _sendMessage),
]),
);
}

View File

@@ -26,10 +26,10 @@ class ChatMessagesView extends ConsumerWidget {
width: 80,
height: 80,
decoration: BoxDecoration(
color: const Color(0xFFE6FAF6),
color: const Color(0xFFF0F2FF),
borderRadius: BorderRadius.circular(40),
),
child: const Icon(Icons.health_and_safety, size: 40, color: Color(0xFF14B8A6)),
child: const Icon(Icons.health_and_safety, size: 40, color: Color(0xFF8B9CF7)),
),
const SizedBox(height: 16),
Text('开始和 AI 健康管家对话吧', style: Theme.of(context).textTheme.bodyMedium),
@@ -100,7 +100,7 @@ class ChatMessagesView extends ConsumerWidget {
color: const Color(0xFFFFFFFF),
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(color: const Color(0xFF14B8A6).withAlpha(20), blurRadius: 16, offset: const Offset(0, 4)),
BoxShadow(color: const Color(0xFF8B9CF7).withAlpha(20), blurRadius: 16, offset: const Offset(0, 4)),
],
),
clipBehavior: Clip.antiAlias,
@@ -113,7 +113,7 @@ class ChatMessagesView extends ConsumerWidget {
padding: const EdgeInsets.fromLTRB(20, 24, 16, 20),
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [Color(0xFF2DC4B4), Color(0xFF14B8A6), Color(0xFF0E8071)],
colors: [Color(0xFFA8B5FA), Color(0xFF8B9CF7), Color(0xFF5C70D6)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
@@ -143,7 +143,7 @@ class ChatMessagesView extends ConsumerWidget {
color: Colors.white.withAlpha(25),
borderRadius: BorderRadius.circular(10),
),
child: Text(info.$3, style: const TextStyle(fontSize: 12, color: Color(0xFFC6EBE6))),
child: Text(info.$3, style: const TextStyle(fontSize: 12, color: Color(0xFFD8DCFD))),
),
],
),
@@ -157,7 +157,7 @@ class ChatMessagesView extends ConsumerWidget {
color: Colors.white.withAlpha(20),
shape: BoxShape.circle,
),
child: const Icon(Icons.close, size: 16, color: Color(0xFFC6EBE6)),
child: const Icon(Icons.close, size: 16, color: Color(0xFFD8DCFD)),
),
),
],
@@ -176,21 +176,7 @@ class ChatMessagesView extends ConsumerWidget {
),
),
// ── 底部提示 ──
Padding(
padding: const EdgeInsets.fromLTRB(0, 12, 0, 18),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(width: 24, height: 1, color: const Color(0xFFC6EBE6)),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Text('或直接对我说...', style: TextStyle(fontSize: 13, color: Color(0xFF9EDBD3))),
),
Container(width: 24, height: 1, color: const Color(0xFFC6EBE6)),
],
),
),
const SizedBox(height: 12),
],
),
),
@@ -200,14 +186,18 @@ class ChatMessagesView extends ConsumerWidget {
Widget _agentActionBtn(_AgentAction a, double screenWidth, BuildContext context, WidgetRef ref) {
return InkWell(
onTap: () {
if (a.route != null) {
if (a.action == 'createPlan') {
_createExercisePlan(ref, context);
} else if (a.action == 'checkIn') {
_exerciseCheckIn(ref, context);
} else if (a.label == '服药打卡') {
_medicationCheckIn(ref, context);
} else if (a.route != null) {
if (a.route == 'camera' || a.route == 'gallery') {
ref.read(cameraActionProvider.notifier).trigger(a.route!);
} else {
pushRoute(ref, a.route!);
}
} else if (a.label == '服药打卡') {
_medicationCheckIn(ref, context);
}
}, borderRadius: BorderRadius.circular(14),
child: Container(
@@ -216,7 +206,7 @@ class ChatMessagesView extends ConsumerWidget {
decoration: BoxDecoration(
color: const Color(0xFFF7F5FF),
borderRadius: BorderRadius.circular(14),
border: Border.all(color: const Color(0xFFD4EDE8), width: 1),
border: Border.all(color: const Color(0xFFD8DCFD), width: 1),
),
child: Column(
mainAxisSize: MainAxisSize.min,
@@ -228,7 +218,7 @@ class ChatMessagesView extends ConsumerWidget {
color: const Color(0xFFEDEAFF),
borderRadius: BorderRadius.circular(11),
),
child: Icon(a.icon, size: 20, color: const Color(0xFF14B8A6)),
child: Icon(a.icon, size: 20, color: const Color(0xFF8B9CF7)),
),
const SizedBox(height: 7),
Text(a.label, style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w500, color: Color(0xFF333333))),
@@ -257,13 +247,13 @@ class ChatMessagesView extends ConsumerWidget {
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(14),
border: Border.all(color: const Color(0xFFE6FAF6)),
border: Border.all(color: const Color(0xFFF0F2FF)),
),
child: Column(children: [
CircleAvatar(
radius: 24,
backgroundColor: const Color(0xFFE6FAF6),
child: Text(doc['name']![0], style: const TextStyle(fontSize: 20, color: Color(0xFF14B8A6))),
backgroundColor: const Color(0xFFF0F2FF),
child: Text(doc['name']![0], style: const TextStyle(fontSize: 20, color: Color(0xFF8B9CF7))),
),
const SizedBox(height: 8),
Text(doc['name']!, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600)),
@@ -272,10 +262,10 @@ class ChatMessagesView extends ConsumerWidget {
Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: const Color(0xFFF2FAF9),
color: const Color(0xFFF0F2FF),
borderRadius: BorderRadius.circular(4),
),
child: Text(doc['dept']!, style: const TextStyle(fontSize: 10, color: Color(0xFF14B8A6))),
child: Text(doc['dept']!, style: const TextStyle(fontSize: 10, color: Color(0xFF8B9CF7))),
),
const SizedBox(height: 6),
Text(
@@ -310,7 +300,7 @@ class ChatMessagesView extends ConsumerWidget {
decoration: BoxDecoration(
color: const Color(0xFFFFFFFF),
borderRadius: BorderRadius.circular(20),
boxShadow: [BoxShadow(color: const Color(0xFF14B8A6).withAlpha(15), blurRadius: 14, offset: const Offset(0, 4))],
boxShadow: [BoxShadow(color: const Color(0xFF8B9CF7).withAlpha(15), blurRadius: 14, offset: const Offset(0, 4))],
),
clipBehavior: Clip.antiAlias,
child: Column(
@@ -431,7 +421,7 @@ class ChatMessagesView extends ConsumerWidget {
width: 22,
height: h,
decoration: BoxDecoration(
color: e.key == trend.length - 1 ? const Color(0xFF14B8A6) : const Color(0xFFD5D0FF),
color: e.key == trend.length - 1 ? const Color(0xFF8B9CF7) : const Color(0xFFD5D0FF),
borderRadius: BorderRadius.circular(5),
),
),
@@ -482,7 +472,7 @@ class ChatMessagesView extends ConsumerWidget {
decoration: BoxDecoration(
color: const Color(0xFFFFFFFF),
borderRadius: BorderRadius.circular(20),
boxShadow: [BoxShadow(color: const Color(0xFF14B8A6).withAlpha(15), blurRadius: 14, offset: const Offset(0, 4))],
boxShadow: [BoxShadow(color: const Color(0xFF8B9CF7).withAlpha(15), blurRadius: 14, offset: const Offset(0, 4))],
),
clipBehavior: Clip.antiAlias,
child: Column(
@@ -493,7 +483,7 @@ class ChatMessagesView extends ConsumerWidget {
width: double.infinity,
padding: const EdgeInsets.fromLTRB(18, 20, 18, 16),
decoration: const BoxDecoration(
gradient: LinearGradient(colors: [Color(0xFFE8F0FE), Color(0xFFF2FAF9)]),
gradient: LinearGradient(colors: [Color(0xFFE8F0FE), Color(0xFFF0F2FF)]),
),
child: Row(
children: [
@@ -537,7 +527,7 @@ class ChatMessagesView extends ConsumerWidget {
children: [
const Text('剩余药量', style: TextStyle(fontSize: 13, color: Color(0xFF666666))),
const Spacer(),
Text('${(remaining * 100).toInt()}%', style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w600, color: Color(0xFF14B8A6))),
Text('${(remaining * 100).toInt()}%', style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w600, color: Color(0xFF8B9CF7))),
],
),
const SizedBox(height: 8),
@@ -547,7 +537,7 @@ class ChatMessagesView extends ConsumerWidget {
value: remaining,
minHeight: 8,
backgroundColor: const Color(0xFFEFEDFF),
valueColor: const AlwaysStoppedAnimation<Color>(Color(0xFF14B8A6)),
valueColor: const AlwaysStoppedAnimation<Color>(Color(0xFF8B9CF7)),
),
),
@@ -601,7 +591,7 @@ class ChatMessagesView extends ConsumerWidget {
decoration: BoxDecoration(
color: const Color(0xFFFFFFFF),
borderRadius: BorderRadius.circular(20),
boxShadow: [BoxShadow(color: const Color(0xFF14B8A6).withAlpha(15), blurRadius: 14, offset: const Offset(0, 4))],
boxShadow: [BoxShadow(color: const Color(0xFF8B9CF7).withAlpha(15), blurRadius: 14, offset: const Offset(0, 4))],
),
clipBehavior: Clip.antiAlias,
child: Column(
@@ -657,7 +647,7 @@ class ChatMessagesView extends ConsumerWidget {
] else ...[
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(color: const Color(0xFFF2FAF9), borderRadius: BorderRadius.circular(12)),
decoration: BoxDecoration(color: const Color(0xFFF0F2FF), borderRadius: BorderRadius.circular(12)),
child: const Row(mainAxisAlignment: MainAxisAlignment.center, children: [
Icon(Icons.hourglass_empty, size: 18, color: Color(0xFF999999)),
SizedBox(width: 8),
@@ -672,7 +662,7 @@ class ChatMessagesView extends ConsumerWidget {
const SizedBox(height: 6),
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(color: const Color(0xFFF2FAF9), borderRadius: BorderRadius.circular(10)),
decoration: BoxDecoration(color: const Color(0xFFF0F2FF), borderRadius: BorderRadius.circular(10)),
child: Text(advice, style: const TextStyle(fontSize: 13, height: 1.6, color: Color(0xFF555555))),
),
]),
@@ -702,7 +692,7 @@ class ChatMessagesView extends ConsumerWidget {
decoration: BoxDecoration(
color: const Color(0xFFFFFFFF),
borderRadius: BorderRadius.circular(20),
boxShadow: [BoxShadow(color: const Color(0xFF14B8A6).withAlpha(15), blurRadius: 14, offset: const Offset(0, 4))],
boxShadow: [BoxShadow(color: const Color(0xFF8B9CF7).withAlpha(15), blurRadius: 14, offset: const Offset(0, 4))],
),
clipBehavior: Clip.antiAlias,
child: Column(
@@ -839,9 +829,9 @@ class ChatMessagesView extends ConsumerWidget {
children: [
const Row(
children: [
Icon(Icons.auto_awesome, size: 16, color: Color(0xFF14B8A6)),
Icon(Icons.auto_awesome, size: 16, color: Color(0xFF8B9CF7)),
SizedBox(width: 6),
Text('AI 解读摘要', style: TextStyle(fontSize: 13, fontWeight: FontWeight.w600, color: Color(0xFF14B8A6))),
Text('AI 解读摘要', style: TextStyle(fontSize: 13, fontWeight: FontWeight.w600, color: Color(0xFF8B9CF7))),
],
),
const SizedBox(height: 8),
@@ -883,7 +873,7 @@ class ChatMessagesView extends ConsumerWidget {
decoration: BoxDecoration(
color: const Color(0xFFFFFFFF),
borderRadius: BorderRadius.circular(20),
boxShadow: [BoxShadow(color: const Color(0xFF14B8A6).withAlpha(15), blurRadius: 14, offset: const Offset(0, 4))],
boxShadow: [BoxShadow(color: const Color(0xFF8B9CF7).withAlpha(15), blurRadius: 14, offset: const Offset(0, 4))],
),
child: Padding(
padding: const EdgeInsets.fromLTRB(18, 16, 18, 14),
@@ -897,8 +887,8 @@ class ChatMessagesView extends ConsumerWidget {
return ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFF2FAF9),
foregroundColor: const Color(0xFF14B8A6),
backgroundColor: const Color(0xFFF0F2FF),
foregroundColor: const Color(0xFF8B9CF7),
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 11),
@@ -926,8 +916,8 @@ class ChatMessagesView extends ConsumerWidget {
decoration: BoxDecoration(
color: const Color(0xFFFEFEFF),
borderRadius: const BorderRadius.only(topLeft: Radius.circular(4), topRight: Radius.circular(20), bottomLeft: Radius.circular(20), bottomRight: Radius.circular(20)),
border: Border.all(color: const Color(0xFFD4EDE8), width: 1.5),
boxShadow: [BoxShadow(color: const Color(0xFF14B8A6).withAlpha(12), blurRadius: 10, offset: const Offset(0, 3))],
border: Border.all(color: const Color(0xFFD8DCFD), width: 1.5),
boxShadow: [BoxShadow(color: const Color(0xFF8B9CF7).withAlpha(12), blurRadius: 10, offset: const Offset(0, 3))],
),
child: Row(
mainAxisSize: MainAxisSize.min,
@@ -937,10 +927,10 @@ class ChatMessagesView extends ConsumerWidget {
height: 26,
padding: const EdgeInsets.all(5),
decoration: BoxDecoration(
color: const Color(0xFFE6FAF6),
color: const Color(0xFFF0F2FF),
borderRadius: BorderRadius.circular(13),
),
child: const CircularProgressIndicator(strokeWidth: 2.2, color: Color(0xFF14B8A6)),
child: const CircularProgressIndicator(strokeWidth: 2.2, color: Color(0xFF8B9CF7)),
),
const SizedBox(width: 10),
Text(thinkingText?.isNotEmpty == true ? thinkingText! : '正在分析...', style: const TextStyle(fontSize: 14, color: Color(0xFF999999))),
@@ -963,15 +953,15 @@ class ChatMessagesView extends ConsumerWidget {
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.82),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
decoration: BoxDecoration(
color: isUser ? const Color(0xFF14B8A6) : const Color(0xFFFEFEFF),
color: isUser ? const Color(0xFF8B9CF7) : const Color(0xFFFEFEFF),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(isUser ? 20 : 4),
topRight: Radius.circular(isUser ? 4 : 20),
bottomLeft: const Radius.circular(20),
bottomRight: const Radius.circular(20),
),
border: isUser ? null : Border.all(color: const Color(0xFFD4EDE8), width: 1.5),
boxShadow: isUser ? [] : [BoxShadow(color: const Color(0xFF14B8A6).withAlpha(12), blurRadius: 10, offset: const Offset(0, 3))],
border: isUser ? null : Border.all(color: const Color(0xFFD8DCFD), width: 1.5),
boxShadow: isUser ? [] : [BoxShadow(color: const Color(0xFF8B9CF7).withAlpha(12), blurRadius: 10, offset: const Offset(0, 3))],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -998,14 +988,14 @@ class ChatMessagesView extends ConsumerWidget {
p: const TextStyle(fontSize: 16, color: Color(0xFF1A1A1A), height: 1.5),
h1: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: Color(0xFF1A1A1A)),
h2: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xFF1A1A1A)),
code: const TextStyle(fontSize: 14, backgroundColor: Color(0xFFF2FAF9)),
code: const TextStyle(fontSize: 14, backgroundColor: Color(0xFFF0F2FF)),
),
),
if (!isUser && msg.content.isNotEmpty)
Padding(
padding: const EdgeInsets.only(top: 10),
child: Row(children: [
const CircleAvatar(radius: 10, backgroundColor: Color(0xFFE6FAF6), child: Icon(Icons.chat_bubble_outline, size: 14, color: Color(0xFF14B8A6))),
const CircleAvatar(radius: 10, backgroundColor: Color(0xFFF0F2FF), child: Icon(Icons.chat_bubble_outline, size: 14, color: Color(0xFF8B9CF7))),
const SizedBox(width: 6),
const Text('健康管家', style: TextStyle(fontSize: 12, color: Color(0xFF9E9E9E))),
const SizedBox(width: 4),
@@ -1038,7 +1028,7 @@ class ChatMessagesView extends ConsumerWidget {
return ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF14B8A6),
backgroundColor: const Color(0xFF8B9CF7),
foregroundColor: Colors.white,
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
@@ -1056,8 +1046,8 @@ class ChatMessagesView extends ConsumerWidget {
return OutlinedButton(
onPressed: () {},
style: OutlinedButton.styleFrom(
foregroundColor: const Color(0xFF14B8A6),
side: const BorderSide(color: Color(0xFF14B8A6), width: 1.2),
foregroundColor: const Color(0xFF8B9CF7),
side: const BorderSide(color: Color(0xFF8B9CF7), width: 1.2),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
padding: const EdgeInsets.symmetric(vertical: 11),
),
@@ -1149,6 +1139,73 @@ 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 (_AgentIcon, String, String) _agentInfo(ActiveAgent agent) {
return switch (agent) {
ActiveAgent.health => (Icons.favorite_border, '记数据', '录入血压、血糖、心率等日常指标'),
@@ -1195,9 +1252,9 @@ class ChatMessagesView extends ConsumerWidget {
return Container(
margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [BoxShadow(color: const Color(0xFF14B8A6).withAlpha(10), blurRadius: 8, offset: const Offset(0, 2))]),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [BoxShadow(color: const Color(0xFF8B9CF7).withAlpha(10), blurRadius: 8, offset: const Offset(0, 2))]),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Row(children: [Icon(Icons.today, size: 18, color: const Color(0xFF14B8A6)), const SizedBox(width: 8), const Text('今日任务', style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600, color: Color(0xFF1A1A1A)))]),
Row(children: [Icon(Icons.today, size: 18, color: const Color(0xFF8B9CF7)), const SizedBox(width: 8), const Text('今日任务', style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600, color: Color(0xFF1A1A1A)))]),
const SizedBox(height: 10),
Row(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [
_miniMetric('血压', bpText, Icons.favorite),
@@ -1211,7 +1268,7 @@ class ChatMessagesView extends ConsumerWidget {
Widget _miniMetric(String label, String value, IconData icon) {
return Column(children: [
Icon(icon, size: 20, color: const Color(0xFF14B8A6)),
Icon(icon, size: 20, color: const Color(0xFF8B9CF7)),
const SizedBox(height: 4),
Text(value, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Color(0xFF1A1A1A))),
Text(label, style: const TextStyle(fontSize: 10, color: Color(0xFF999999))),
@@ -1230,8 +1287,9 @@ class _AgentAction {
final IconData icon;
final bool isWide;
final String? route;
final String? action;
const _AgentAction({required this.label, required this.icon, this.isWide = false, this.route});
const _AgentAction({required this.label, required this.icon, this.isWide = false, this.route, this.action});
}
final _agentActions = <ActiveAgent, List<_AgentAction>>{
@@ -1256,9 +1314,9 @@ final _agentActions = <ActiveAgent, List<_AgentAction>>{
_AgentAction(label: '查看历史', icon: Icons.history_outlined, isWide: true, route: 'reports'),
],
ActiveAgent.exercise: [
_AgentAction(label: '本周计划', icon: Icons.calendar_month_outlined, isWide: true, route: 'exercisePlan'),
_AgentAction(label: '新建计划', icon: Icons.add_task_outlined, isWide: true, route: 'exercisePlan'),
_AgentAction(label: '今日打卡', icon: Icons.fact_check_outlined, isWide: true, route: 'exercisePlan'),
_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'),
],
};
@@ -1298,9 +1356,9 @@ class _ExpandableAdviceState extends State<_ExpandableAdvice> {
children: [
Row(
children: [
const Icon(Icons.lightbulb_outline, size: 16, color: Color(0xFF14B8A6)),
const Icon(Icons.lightbulb_outline, size: 16, color: Color(0xFF8B9CF7)),
const SizedBox(width: 6),
const Text('AI 建议', style: TextStyle(fontSize: 13, fontWeight: FontWeight.w600, color: Color(0xFF14B8A6))),
const Text('AI 建议', style: TextStyle(fontSize: 13, fontWeight: FontWeight.w600, color: Color(0xFF8B9CF7))),
const Spacer(),
Icon(_expanded ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down, size: 18, color: const Color(0xFFAAAAAA)),
],