diff --git a/backend/src/Health.WebApi/uploads/a46936e4-892a-4c7c-8a47-44b04d64e12f_΢��ͼƬ_20260603102503_4528_320.jpg b/backend/src/Health.WebApi/uploads/a46936e4-892a-4c7c-8a47-44b04d64e12f_΢��ͼƬ_20260603102503_4528_320.jpg new file mode 100644 index 0000000..2d81464 Binary files /dev/null and b/backend/src/Health.WebApi/uploads/a46936e4-892a-4c7c-8a47-44b04d64e12f_΢��ͼƬ_20260603102503_4528_320.jpg differ diff --git a/backend/src/Health.WebApi/uploads/compressed_a46936e4-892a-4c7c-8a47-44b04d64e12f_΢��ͼƬ_20260603102503_4528_320.jpg b/backend/src/Health.WebApi/uploads/compressed_a46936e4-892a-4c7c-8a47-44b04d64e12f_΢��ͼƬ_20260603102503_4528_320.jpg new file mode 100644 index 0000000..2d81d1f Binary files /dev/null and b/backend/src/Health.WebApi/uploads/compressed_a46936e4-892a-4c7c-8a47-44b04d64e12f_΢��ͼƬ_20260603102503_4528_320.jpg differ diff --git a/health_app/lib/pages/home/home_page.dart b/health_app/lib/pages/home/home_page.dart index 6720586..ee4fe37 100644 --- a/health_app/lib/pages/home/home_page.dart +++ b/health_app/lib/pages/home/home_page.dart @@ -18,15 +18,11 @@ class _HomePageState extends ConsumerState { final _textCtrl = TextEditingController(); final _scrollCtrl = ScrollController(); bool _taskCardsExpanded = true; - bool _showExpandButton = false; @override void initState() { super.initState(); _scrollCtrl.addListener(_onScroll); } @override void dispose() { _textCtrl.dispose(); _scrollCtrl.dispose(); super.dispose(); } - void _onScroll() { - if (_scrollCtrl.offset > 50 && !_showExpandButton) setState(() => _showExpandButton = true); - else if (_scrollCtrl.offset <= 50 && _showExpandButton) setState(() => _showExpandButton = false); - } + void _onScroll() {} void _sendMessage() { final text = _textCtrl.text.trim(); @@ -39,43 +35,53 @@ class _HomePageState extends ConsumerState { final chatState = ref.watch(chatProvider); final auth = ref.watch(authProvider); final user = auth.user; - final bottomInset = MediaQuery.of(context).viewInsets.bottom; final selectedAgent = ref.watch(selectedAgentProvider); return Scaffold( drawer: const HealthDrawer(), backgroundColor: const Color(0xFFF8F7FF), body: SafeArea( - child: Stack(children: [ - Column(children: [ - _buildHeader(user), - if (_taskCardsExpanded) _buildTaskCards(), - Expanded(child: ChatMessagesView(scrollCtrl: _scrollCtrl, messages: chatState.messages)), - if (selectedAgent != null) _buildAgentPanel(context, selectedAgent), - _buildInputBar(context), - SizedBox(height: bottomInset > 0 ? bottomInset : 0), - ]), - _buildExpandButton(), + bottom: false, + child: Column(children: [ + // ── 顶部栏 ── + _buildHeader(user), + + // ── 今日任务(可折叠) ── + _buildTaskCardsArea(), + + // ── 聊天区域(弹性填充剩余空间) ── + Expanded(child: ChatMessagesView(scrollCtrl: _scrollCtrl, messages: chatState.messages)), + + // ── 智能体选择器(常驻显示) ── + _buildAgentBar(selectedAgent), + + // ── 选中智能体的操作面板 ── + if (selectedAgent != null) _buildAgentPanel(context, selectedAgent), + + // ── 输入框 ── + _buildInputBar(context), ]), ), ); } + // ═════════════════════ 顶部栏 ═════════════════════ + Widget _buildHeader(dynamic user) { return Container( - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12), + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), child: Row(children: [ Builder(builder: (ctx) => GestureDetector( onTap: () => Scaffold.of(ctx).openDrawer(), - child: CircleAvatar(radius: 22, backgroundColor: const Color(0xFFEDEBFF), backgroundImage: user?.avatarUrl != null ? NetworkImage(user!.avatarUrl!) : null, child: user?.avatarUrl == null ? const Icon(Icons.person, size: 26, color: Color(0xFF635BFF)) : null), + child: CircleAvatar(radius: 20, backgroundColor: const Color(0xFFEDEBFF), backgroundImage: user?.avatarUrl != null ? NetworkImage(user!.avatarUrl!) : null, child: user?.avatarUrl == null ? const Icon(Icons.person, size: 24, color: Color(0xFF635BFF)) : null), )), - const SizedBox(width: 12), + const SizedBox(width: 10), Expanded(child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row(mainAxisSize: MainAxisSize.min, children: [Icon(Icons.smart_toy_outlined, size: 18, color: const Color(0xFF635BFF)), const SizedBox(width: 4), Text('AI 健康管家', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: Colors.grey[600]))]), + Row(mainAxisSize: MainAxisSize.min, children: [Icon(Icons.smart_toy_outlined, size: 16, color: const Color(0xFF635BFF)), 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: 18, fontWeight: FontWeight.bold, color: Color(0xFF1A1A1A))), + Text('${_getGreeting()},${user?.name ?? '张三'}!', style: const TextStyle(fontSize: 17, fontWeight: FontWeight.bold, color: Color(0xFF1A1A1A))), ])), - Icon(Icons.notifications_none, size: 24, color: Colors.grey[600]), + Icon(Icons.notifications_none, size: 22, color: Colors.grey[600]), ]), ); } @@ -88,35 +94,56 @@ class _HomePageState extends ConsumerState { return '晚上好'; } - Widget _buildExpandButton() { - if (!_showExpandButton || _taskCardsExpanded) return const SizedBox.shrink(); - return Positioned(top: 60, right: 16, child: AnimatedOpacity(opacity: _showExpandButton ? 1.0 : 0.0, duration: const Duration(milliseconds: 300), child: FloatingActionButton(onPressed: () => setState(() => _taskCardsExpanded = true), mini: true, backgroundColor: const Color(0xFF635BFF), child: const Icon(Icons.keyboard_arrow_down, size: 20)))); - } + // ═════════════════════ 今日任务(可折叠/展开) ═════════════════════ - Widget _buildTaskCards() { + Widget _buildTaskCardsArea() { final latestHealth = ref.watch(latestHealthProvider); - return latestHealth.when( - data: (data) => Container( - margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - padding: const EdgeInsets.all(16), - decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20), boxShadow: [BoxShadow(color: const Color(0xFF635BFF).withAlpha(10), blurRadius: 8, offset: const Offset(0, 2))]), - child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row(children: [Text('今日任务', style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xFF1A1A1A))), const Spacer(), GestureDetector(onTap: () => setState(() => _taskCardsExpanded = false), child: Text('全部展开', style: TextStyle(fontSize: 13, color: const Color(0xFF635BFF))))]), - const SizedBox(height: 12), ..._getTodayTasks(data), + if (_taskCardsExpanded) { + return latestHealth.when( + data: (data) => _taskCardContent(data), + loading: () => _taskCardContent({}), + error: (_, __) => _taskCardContent({}), + ); + } + + // 折叠状态:只显示一行可点击的标题栏 + return GestureDetector( + onTap: () => setState(() => _taskCardsExpanded = true), + behavior: HitTestBehavior.opaque, + child: Container( + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6), + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), + decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [BoxShadow(color: const Color(0xFF635BFF).withAlpha(8), blurRadius: 6, offset: const Offset(0, 1))]), + child: Row(children: [ + Icon(Icons.assignment_turned_in_outlined, size: 18, color: const Color(0xFF635BFF)), + const SizedBox(width: 8), + const Text('今日任务', style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600, color: Color(0xFF1A1A1A))), + const Spacer(), + Text('点击展开', style: TextStyle(fontSize: 12, color: const Color(0xFF635BFF))), + Icon(Icons.keyboard_arrow_right, size: 18, color: const Color(0xFF635BFF)), ]), ), - loading: () => const SizedBox.shrink(), - error: (_, __) => Container( - margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - padding: const EdgeInsets.all(16), - decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20), boxShadow: [BoxShadow(color: const Color(0xFF635BFF).withAlpha(10), blurRadius: 8, offset: const Offset(0, 2))]), - child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row(children: [const Text('今日任务', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xFF1A1A1A))), const Spacer(), GestureDetector(onTap: () => setState(() => _taskCardsExpanded = false), child: Text('全部展开', style: TextStyle(fontSize: 13, color: const Color(0xFF635BFF))))]), - const SizedBox(height: 12), ..._getTodayTasks({}), - ], - ), - ), + ); + } + + Widget _taskCardContent(Map healthData) { + 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(0xFF635BFF).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))), + const Spacer(), + GestureDetector(onTap: () => setState(() => _taskCardsExpanded = false), child: Row(mainAxisSize: MainAxisSize.min, children: [ + Text('收起', style: TextStyle(fontSize: 12, color: const Color(0xFF999999))), + Icon(Icons.keyboard_arrow_up, size: 18, color: const Color(0xFF999999)), + ])), + ]), + const SizedBox(height: 10), + ..._getTodayTasks(healthData), + ]), ); } @@ -140,14 +167,14 @@ class _HomePageState extends ConsumerState { final colors = {'done': const Color(0xFF43A047), 'warning': const Color(0xFFFF9800), 'pending': const Color(0xFF9E9E9E)}; final icons = {'done': Icons.check_circle, 'warning': Icons.warning, 'pending': Icons.circle_outlined}; return Padding( - padding: const EdgeInsets.only(bottom: 12), + padding: const EdgeInsets.only(bottom: 10), child: GestureDetector( behavior: HitTestBehavior.opaque, onTap: onTap, child: Row(children: [ - Container(width: 32, height: 32, decoration: BoxDecoration(color: const Color(0xFFF5F3FF), borderRadius: BorderRadius.circular(8)), child: Icon(icon, size: 16, color: const Color(0xFF635BFF))), - const SizedBox(width: 10), Expanded(child: Text(label, style: const TextStyle(fontSize: 14, color: Color(0xFF333333)))), - Icon(icons[status], size: 20, color: colors[status] ?? Colors.grey), + Container(width: 30, height: 30, decoration: BoxDecoration(color: const Color(0xFFF5F3FF), borderRadius: BorderRadius.circular(8)), child: Icon(icon, size: 15, color: const Color(0xFF635BFF))), + const SizedBox(width: 10), Expanded(child: Text(label, style: const TextStyle(fontSize: 13, color: Color(0xFF333333)))), + Icon(icons[status], size: 18, color: colors[status] ?? Colors.grey), ]), ), ); @@ -155,73 +182,139 @@ class _HomePageState extends ConsumerState { void _handleMedicationCheck() async { await ref.read(medicationServiceProvider).confirm(''); + if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('已记录服药 ✅'), backgroundColor: Color(0xFF635BFF))); } + // ═════════════════════ 智能体选择条(常驻) ═════════════════════ + + static final _agentDefs = [ + (ActiveAgent.consultation, '问诊', Icons.chat_bubble_outline), + (ActiveAgent.health, '记数据', Icons.favorite_border), + (ActiveAgent.diet, '拍饮食', Icons.restaurant_outlined), + (ActiveAgent.medication, '药管家', Icons.medication_outlined), + (ActiveAgent.report, '看报告', Icons.description_outlined), + (ActiveAgent.exercise, '运动', Icons.directions_run_outlined), + ]; + + Widget _buildAgentBar(ActiveAgent? selected) { + return Container( + height: 44, + padding: const EdgeInsets.symmetric(horizontal: 12), + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: _agentDefs.length, + separatorBuilder: (_, __) => const SizedBox(width: 6), + itemBuilder: (_, i) { + final (agent, label, icon) = _agentDefs[i]; + final isActive = selected == agent; + return GestureDetector( + onTap: () { + final notifier = ref.read(selectedAgentProvider.notifier); + notifier.select(isActive ? null : agent); + // 切换智能体时清空聊天 + if (!isActive) ref.read(chatProvider.notifier).setAgent(agent); + }, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), + decoration: BoxDecoration( + color: isActive ? const Color(0xFF635BFF) : Colors.white, + borderRadius: BorderRadius.circular(20), + border: Border.all(color: isActive ? const Color(0xFF635BFF) : const Color(0xFFE0E0E0)), + ), + child: Row(mainAxisSize: MainAxisSize.min, children: [ + Icon(icon, size: 14, color: isActive ? Colors.white : const Color(0xFF666666)), + const SizedBox(width: 4), + Text(label, style: TextStyle(fontSize: 12, fontWeight: isActive ? FontWeight.w600 : FontWeight.w500, color: isActive ? Colors.white : const Color(0xFF666666))), + ]), + ), + ); + }, + ), + ); + } + + // ═════════════════════ 智能体操作面板(选中后显示) ═════════════════════ + Widget _buildAgentPanel(BuildContext context, ActiveAgent agent) { final titles = {ActiveAgent.consultation: 'AI 问诊', ActiveAgent.health: '记数据', ActiveAgent.diet: '拍饮食', ActiveAgent.medication: '药管家', ActiveAgent.report: '看报告', ActiveAgent.exercise: '运动计划'}; final tips = {ActiveAgent.consultation: '或直接对我说你的症状', ActiveAgent.health: '或直接对我说:"血压 135/85"', ActiveAgent.diet: '或直接对我说:"中午吃了牛肉面"', ActiveAgent.medication: '或直接对我说:"医生让我吃阿托伐他汀 20mg"', ActiveAgent.report: '或直接上传报告图片', ActiveAgent.exercise: '或直接对我说:"每周一三五散步 30 分钟"'}; - return AnimatedContainer(duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, padding: const EdgeInsets.all(16), decoration: BoxDecoration(color: Colors.white, borderRadius: const BorderRadius.vertical(top: Radius.circular(24)), boxShadow: [BoxShadow(color: const Color(0xFF635BFF).withAlpha(15), blurRadius: 12, offset: const Offset(0, -4))]), child: Column(mainAxisSize: MainAxisSize.min, children: [ - Text(titles[agent] ?? '', style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xFF1A1A1A))), - const SizedBox(height: 4), Text(tips[agent] ?? '', style: TextStyle(fontSize: 12, color: Colors.grey[500])), - const SizedBox(height: 12), Wrap(spacing: 8, runSpacing: 8, children: _getAgentButtons(agent)), - ])); + return Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), + decoration: BoxDecoration(color: Colors.white, border: Border(top: BorderSide(color: const Color(0xFFEEEEEE)))), + child: Column(mainAxisSize: MainAxisSize.min, children: [ + Row(children: [ + Text(titles[agent] ?? '', style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: Color(0xFF1A1A1A))), + const SizedBox(width: 6), + Expanded(child: Text(tips[agent] ?? '', style: TextStyle(fontSize: 11, color: Colors.grey[500]))), + GestureDetector(onTap: () => ref.read(selectedAgentProvider.notifier).select(null), child: Icon(Icons.close, size: 18, color: Colors.grey[400])), + ]), + const SizedBox(height: 8), + SingleChildScrollView(scrollDirection: Axis.horizontal, child: Row(children: _getAgentButtons(agent))), + ])); } List _getAgentButtons(ActiveAgent agent) { switch (agent) { - case ActiveAgent.health: return [_agentBtn('手动录入血压', Icons.favorite), _agentBtn('手动录入血糖', Icons.bloodtype), _agentBtn('手动录入心率', Icons.monitor_heart), _agentBtn('手动录入血氧', Icons.air), _agentBtn('手动录入体重', Icons.monitor_weight)]; - case ActiveAgent.diet: return [_agentBtn('拍照', Icons.camera_alt), _agentBtn('上传照片', Icons.photo_library)]; + case ActiveAgent.health: return [_agentBtn('录入血压', Icons.favorite), _agentBtn('录入血糖', Icons.bloodtype), _agentBtn('录入心率', Icons.monitor_heart), _agentBtn('录入血氧', Icons.air), _agentBtn('录入体重', Icons.monitor_weight)]; + case ActiveAgent.diet: return [_agentBtn('拍照识别', Icons.camera_alt), _agentBtn('上传照片', Icons.photo_library)]; case ActiveAgent.medication: return [_agentBtn('用药管理', Icons.medication), _agentBtn('用药提醒', Icons.alarm)]; case ActiveAgent.consultation: return [_agentBtn('找医生', Icons.person_search)]; - case ActiveAgent.exercise: return [_agentBtn('本周计划', Icons.calendar_view_week), _agentBtn('创建新计划', Icons.add_circle_outline)]; + case ActiveAgent.exercise: return [_agentBtn('本周计划', Icons.calendar_view_week), _agentBtn('新建计划', Icons.add_circle_outline)]; default: return []; } } Widget _agentBtn(String label, IconData icon) { - return ElevatedButton.icon( - onPressed: () => _onAgentAction(label), - icon: Icon(icon, size: 16), - label: Text(label, style: const TextStyle(fontSize: 13)), - style: ElevatedButton.styleFrom(backgroundColor: const Color(0xFFF5F3FF), foregroundColor: const Color(0xFF635BFF), elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(18)), padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 14)), + return Padding( + padding: const EdgeInsets.only(right: 8), + child: ElevatedButton.icon( + onPressed: () => _onAgentAction(label), + icon: Icon(icon, size: 14), + label: Text(label, style: const TextStyle(fontSize: 12)), + style: ElevatedButton.styleFrom(backgroundColor: const Color(0xFFF5F3FF), foregroundColor: const Color(0xFF635BFF), elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12)), + ), ); } void _onAgentAction(String label) { switch (label) { - case '拍照': case '上传照片': pushRoute(ref, 'dietCapture'); - case '手动录入血压': _textCtrl.text = '血压 '; - case '手动录入血糖': _textCtrl.text = '血糖 '; - case '手动录入心率': _textCtrl.text = '心率 '; - case '手动录入血氧': _textCtrl.text = '血氧 '; - case '手动录入体重': _textCtrl.text = '体重 '; + case '拍照识别': case '上传照片': pushRoute(ref, 'dietCapture'); + case '录入血压': _textCtrl.text = '血压 '; + case '录入血糖': _textCtrl.text = '血糖 '; + case '录入心率': _textCtrl.text = '心率 '; + case '录入血氧': _textCtrl.text = '血氧 '; + case '录入体重': _textCtrl.text = '体重 '; case '用药管理': pushRoute(ref, 'medications'); case '找医生': pushRoute(ref, 'doctors'); - case '本周计划': case '创建新计划': pushRoute(ref, 'exercisePlan'); + case '本周计划': case '新建计划': pushRoute(ref, 'exercisePlan'); } } Future _pickImage(ImageSource source) async { final picker = ImagePicker(); final picked = await picker.pickImage(source: source, imageQuality: 85); - if (picked != null) { final token = await ref.read(apiClientProvider).accessToken; if (token == null) return; _textCtrl.text = '[图片已上传]'; setState(() {}); } + if (picked != null) { final token = await ref.read(apiClientProvider).accessToken; if (token == null) return; _textCtrl.text = '[图片已上传]'; if (mounted) setState(() {}); } } void _showAttachmentPicker(BuildContext context) { showModalBottomSheet(context: context, builder: (ctx) => SafeArea(child: Wrap(children: [ ListTile(leading: const Icon(Icons.camera_alt), title: const Text('拍照'), onTap: () { Navigator.pop(ctx); _pickImage(ImageSource.camera); }), ListTile(leading: const Icon(Icons.photo_library), title: const Text('从相册选'), onTap: () { Navigator.pop(ctx); _pickImage(ImageSource.gallery); }), - ListTile(leading: const Icon(Icons.attach_file), title: const Text('传文件'), onTap: () async { Navigator.pop(ctx); final result = await FilePicker.platform.pickFiles(); if (result != null && result.files.isNotEmpty) { _textCtrl.text = '[文件已选择] ${result.files.first.name}'; setState(() {}); }}), + ListTile(leading: const Icon(Icons.attach_file), title: const Text('传文件'), onTap: () async { Navigator.pop(ctx); final result = await FilePicker.platform.pickFiles(); if (result != null && result.files.isNotEmpty) { _textCtrl.text = '[文件已选择] ${result.files.first.name}'; if (mounted) setState(() {}); }}), ]))); } Widget _buildInputBar(BuildContext context) { - return Container(padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration(color: Colors.white, border: Border(top: BorderSide(color: const Color(0xFFEEEEEE)))), child: Row(children: [ - IconButton(icon: const Icon(Icons.attach_file, size: 24, color: Color(0xFF666666)), onPressed: () => _showAttachmentPicker(context)), - Expanded(child: TextField(controller: _textCtrl, decoration: InputDecoration(hintText: '输入你想说的...', contentPadding: const EdgeInsets.symmetric(horizontal: 12), border: InputBorder.none), onSubmitted: (_) => _sendMessage())), - IconButton(icon: const Icon(Icons.send, size: 24, color: Color(0xFF635BFF)), onPressed: _sendMessage), - ])); + return Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), + decoration: BoxDecoration(color: Colors.white, border: Border(top: BorderSide(color: const Color(0xFFEEEEEE)))), + child: Row(children: [ + IconButton(icon: const Icon(Icons.attach_file, size: 22, color: Color(0xFF666666)), onPressed: () => _showAttachmentPicker(context)), + Expanded(child: TextField(controller: _textCtrl, decoration: InputDecoration(hintText: '输入你想说的...', contentPadding: const EdgeInsets.symmetric(horizontal: 10), border: InputBorder.none, isDense: true), onSubmitted: (_) => _sendMessage())), + IconButton(icon: const Icon(Icons.send, size: 22, color: Color(0xFF635BFF)), onPressed: _sendMessage), + ]), + ); } } diff --git a/health_app/lib/widgets/health_drawer.dart b/health_app/lib/widgets/health_drawer.dart index eb5ec08..755f97d 100644 --- a/health_app/lib/widgets/health_drawer.dart +++ b/health_app/lib/widgets/health_drawer.dart @@ -75,24 +75,22 @@ class HealthDrawer extends ConsumerWidget { TextButton(onPressed: () => ref.invalidate(conversationListProvider), child: const Text('刷新', style: TextStyle(fontSize: 12, color: Color(0xFF635BFF)))), ]), ), - SizedBox( - height: 200, - child: conversations.when( + conversations.when( data: (items) { if (items.isEmpty) { - return const Center(child: Text('暂无历史对话', style: TextStyle(color: Color(0xFF999999), fontSize: 14))); + return const Padding(padding: EdgeInsets.symmetric(vertical: 20), child: Center(child: Text('暂无历史对话', style: TextStyle(color: Color(0xFF999999), fontSize: 13)))); } - return ListView.builder( - shrinkWrap: true, + return Padding( padding: const EdgeInsets.symmetric(horizontal: 8), - itemCount: items.length, - itemBuilder: (ctx, i) => _ConversationItem(item: items[i], ref: ref), + child: Column( + mainAxisSize: MainAxisSize.min, + children: items.map((item) => _ConversationItem(item: item, ref: ref)).toList(), + ), ); }, loading: () => const Center(child: CircularProgressIndicator(strokeWidth: 2)), error: (_, __) => const Center(child: Text('加载失败', style: TextStyle(color: Color(0xFF999999), fontSize: 14))), ), - ), const Divider(), _DrawerItem(icon: Icons.logout, label: '退出登录', onTap: () async { @@ -149,30 +147,31 @@ class _ConversationItem extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return Container( - margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), decoration: BoxDecoration( color: const Color(0xFFF8F7FF), - borderRadius: BorderRadius.circular(12), + borderRadius: BorderRadius.circular(10), ), child: ListTile( leading: Container( - width: 40, - height: 40, + width: 36, + height: 36, decoration: BoxDecoration( color: const Color(0xFFEDEBFF), - borderRadius: BorderRadius.circular(10), + borderRadius: BorderRadius.circular(8), ), - child: Icon(_getAgentIcon(item.agent), size: 18, color: const Color(0xFF635BFF)), + child: Icon(_getAgentIcon(item.agent), size: 16, color: const Color(0xFF635BFF)), ), - title: Text(item.title, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500)), - subtitle: Text(item.lastMessage, maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle(fontSize: 12, color: Colors.grey[500])), + title: Text(item.title, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500)), + subtitle: Text(item.lastMessage, maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle(fontSize: 11, color: Colors.grey[500])), trailing: Column( mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, children: [ - Text(_formatTime(item.updatedAt), style: const TextStyle(fontSize: 10, color: Color(0xFFCCCCCC))), - const SizedBox(height: 4), + Text(_formatTime(item.updatedAt), style: const TextStyle(fontSize: 9, color: Color(0xFFCCCCCC))), + const SizedBox(height: 2), PopupMenuButton( - icon: const Icon(Icons.more_vert, size: 16, color: Color(0xFFCCCCCC)), + icon: const Icon(Icons.more_vert, size: 14, color: Color(0xFFCCCCCC)), itemBuilder: (_) => [ const PopupMenuItem(value: 1, child: Text('继续聊')), const PopupMenuItem(value: 2, child: Text('删除')),