fix: 数据确认面板修复 - 显示实际数值+确认关闭+侧边栏刷新+清理旧数据

- Handler返回value/unit/isAbnormal到metadata
- DataConfirmCard显示实际数值和单位
- 确认按钮关闭卡片+刷新健康概览
- 健康Agent欢迎卡简化为'查看趋势'
- 清空87条旧健康测试数据
This commit is contained in:
MingNian
2026-06-04 21:57:02 +08:00
parent cd65bc4d65
commit 20a5ce1199
4 changed files with 69 additions and 161 deletions

View File

@@ -74,6 +74,11 @@ public static class HealthDataAgentHandler
db.HealthRecords.Add(record);
await db.SaveChangesAsync();
return new { success = true, record_id = record.Id, type = record.MetricType.ToString() };
var valStr = record.MetricType switch
{
HealthMetricType.BloodPressure => $"{record.Systolic}/{record.Diastolic}",
_ => record.Value?.ToString() ?? ""
};
return new { success = true, record_id = record.Id, type = record.MetricType.ToString(), value = valStr, unit = record.Unit, isAbnormal = record.IsAbnormal };
}
}

View File

@@ -408,8 +408,10 @@ public static class AiChatEndpoints
messageType = "data_confirm";
if (toolResult is IDictionary<string, object> resultDict)
{
if (resultDict.TryGetValue("type", out var type))
metadata["type"] = type.ToString();
if (resultDict.TryGetValue("type", out var type)) metadata["type"] = type.ToString();
if (resultDict.TryGetValue("value", out var val)) metadata["value"] = val.ToString();
if (resultDict.TryGetValue("unit", out var unit)) metadata["unit"] = unit.ToString();
if (resultDict.TryGetValue("isAbnormal", out var abn) && abn is bool b2) metadata["abnormal"] = b2;
if (resultDict.TryGetValue("success", out var success) && success is bool b && b)
metadata["success"] = true;
}

View File

@@ -62,7 +62,7 @@ class ChatMessagesView extends ConsumerWidget {
case MessageType.taskCard:
return _buildTaskCardInChat(context, ref);
case MessageType.dataConfirm:
return _buildDataConfirmCard(context, msg);
return _buildDataConfirmCard(context, ref, msg);
case MessageType.medicationConfirm:
return _buildMedicationConfirmCard(context, msg);
case MessageType.dietAnalysis:
@@ -269,14 +269,14 @@ class ChatMessagesView extends ConsumerWidget {
// 2. DataConfirmCard — 增强版数据确认卡片
// ═══════════════════════════════════════════════════════════
Widget _buildDataConfirmCard(BuildContext context, ChatMessage msg) {
Widget _buildDataConfirmCard(BuildContext context, WidgetRef ref, ChatMessage msg) {
if (msg.confirmed) return const SizedBox.shrink();
final meta = msg.metadata;
final metricType = meta?['type'] as String? ?? '';
final value = meta?['value'] as String? ?? '';
final abnormal = meta?['abnormal'] as bool? ?? false;
final recordTime = meta?['recordTime'] as String? ?? '';
final unit = meta?['unit'] as String? ?? _getMetricUnit(metricType);
final trend = meta?['trend'] as List? ?? [0.6, 0.8, 0.5];
return Align(
alignment: Alignment.centerLeft,
@@ -286,154 +286,55 @@ class ChatMessagesView extends ConsumerWidget {
decoration: BoxDecoration(
color: const Color(0xFFFFFFFF),
borderRadius: BorderRadius.circular(20),
boxShadow: [BoxShadow(color: const Color(0xFF8B9CF7).withAlpha(15), blurRadius: 14, offset: const Offset(0, 4))],
boxShadow: [BoxShadow(color: const Color(0xFF6C5CE7).withAlpha(15), blurRadius: 14, offset: const Offset(0, 4))],
),
clipBehavior: Clip.antiAlias,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// ── 绿色勾选条 ──
child: Column(mainAxisSize: MainAxisSize.min, children: [
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 10),
decoration: const BoxDecoration(
gradient: LinearGradient(colors: [Color(0xFF4CAF50), Color(0xFF43A047)]),
),
child: const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.check_circle, size: 18, color: Colors.white),
SizedBox(width: 6),
Text('✓ 数据已记录', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: Colors.white)),
],
),
),
Padding(
padding: const EdgeInsets.all(18),
child: Column(
children: [
// 记录时间
Align(
alignment: Alignment.centerLeft,
child: Text(recordTime.isNotEmpty ? recordTime : _formatTime(msg.createdAt), style: const TextStyle(fontSize: 12, color: Color(0xFF9E9E9E))),
width: double.infinity, padding: const EdgeInsets.symmetric(vertical: 10),
decoration: const BoxDecoration(gradient: LinearGradient(colors: [Color(0xFF4CAF50), Color(0xFF43A047)])),
child: const Row(mainAxisAlignment: MainAxisAlignment.center, children: [
Icon(Icons.check_circle, size: 18, color: Colors.white), SizedBox(width: 6),
Text('数据已记录', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: Colors.white)),
]),
),
Padding(padding: const EdgeInsets.all(18), child: Column(children: [
Align(alignment: Alignment.centerLeft, child: Text(recordTime.isNotEmpty ? recordTime : _formatTime(msg.createdAt), style: const TextStyle(fontSize: 12, color: Color(0xFF9E9E9E)))),
const SizedBox(height: 14),
// 主要指标区域
Container(
padding: const EdgeInsets.all(18),
decoration: BoxDecoration(
color: const Color(0xFFF9F8FF),
borderRadius: BorderRadius.circular(16),
),
child: Row(
children: [
Container(
width: 52,
height: 52,
decoration: BoxDecoration(
color: const Color(0xFFEDEAFF),
borderRadius: BorderRadius.circular(14),
),
child: Center(child: Text(_getMetricIcon(metricType), style: const TextStyle(fontSize: 26))),
),
decoration: BoxDecoration(color: const Color(0xFFF9F8FF), borderRadius: BorderRadius.circular(16)),
child: Row(children: [
Container(width: 52, height: 52, decoration: BoxDecoration(color: const Color(0xFFEDEAFF), borderRadius: BorderRadius.circular(14)), child: Center(child: Text(_getMetricIcon(metricType), style: const TextStyle(fontSize: 26)))),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Text(_getMetricName(metricType), style: const TextStyle(fontSize: 13, color: Color(0xFF888888))),
const SizedBox(height: 4),
RichText(
text: TextSpan(
children: [
RichText(text: TextSpan(children: [
TextSpan(text: value, style: TextStyle(fontSize: 28, fontWeight: FontWeight.w800, color: abnormal ? const Color(0xFFE53935) : const Color(0xFF1A1A2E))),
TextSpan(text: ' $unit', style: TextStyle(fontSize: 14, color: abnormal ? const Color(0xFFE53970) : const Color(0xFF999999))),
],
])),
])),
]),
),
),
],
),
),
],
),
),
// 异常警告条
if (abnormal) ...[
const SizedBox(height: 12),
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
decoration: BoxDecoration(
color: const Color(0xFFFFF3F0),
borderRadius: BorderRadius.circular(10),
border: Border.all(color: const Color(0xFFFFDAD4), width: 1),
),
child: const Row(
children: [
Icon(Icons.warning_amber_rounded, size: 18, color: Color(0xFFE53935)),
SizedBox(width: 8),
Expanded(child: Text('⚠️ 数值偏高,建议关注', style: TextStyle(fontSize: 13, color: Color(0xFFE53935), fontWeight: FontWeight.w500))),
Container(width: double.infinity, padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), decoration: BoxDecoration(color: const Color(0xFFFFF3F0), borderRadius: BorderRadius.circular(10), border: Border.all(color: const Color(0xFFFFDAD4))),
child: const Row(children: [Icon(Icons.warning_amber_rounded, size: 18, color: Color(0xFFE53935)), SizedBox(width: 8), Expanded(child: Text('数值偏高,建议关注', style: TextStyle(fontSize: 13, color: Color(0xFFE53935), fontWeight: FontWeight.w500)))])),
],
),
),
],
// 迷你趋势图最近3次
const SizedBox(height: 16),
Row(
children: [
const Text('近期趋势', style: TextStyle(fontSize: 12, color: Color(0xFFAAAAAA))),
const Spacer(),
const Text('最近3次', style: TextStyle(fontSize: 11, color: Color(0xFFCCCCCC))),
],
),
const SizedBox(height: 8),
SizedBox(
height: 36,
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: trend.asMap().entries.map((e) {
final h = (e.value * 32).clamp(6.0, 32.0);
return Padding(
padding: EdgeInsets.only(right: e.key < trend.length - 1 ? 10 : 0),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 22,
height: h,
decoration: BoxDecoration(
color: e.key == trend.length - 1 ? const Color(0xFF8B9CF7) : const Color(0xFFD5D0FF),
borderRadius: BorderRadius.circular(5),
),
),
const SizedBox(height: 4),
Text('${e.key + 1}', style: const TextStyle(fontSize: 9, color: Color(0xFFBBBBBB))),
],
),
);
}).toList(),
),
),
// 底部操作按钮
const SizedBox(height: 18),
Row(children: [
Expanded(child: _cardOutlineBtn('编辑', Icons.edit_outlined)),
Expanded(child: _cardFilledBtn('确认', Icons.check, onTap: () {
msg.confirmed = true;
ref.invalidate(latestHealthProvider);
final notifier = ref.read(chatProvider.notifier);
notifier.markNeedsRebuild();
})),
const SizedBox(width: 8),
Expanded(child: _cardFilledBtn('确认', Icons.check)),
const SizedBox(width: 8),
Expanded(child: _cardOutlineBtn('查看详情', Icons.trending_up_outlined)),
Expanded(child: _cardOutlineBtn('查看详情', Icons.trending_up_outlined, onTap: () => pushRoute(ref, 'trend', params: {'type': metricType}))),
]),
])),
]),
],
),
),
],
),
),
);
}
@@ -1061,11 +962,11 @@ class ChatMessagesView extends ConsumerWidget {
// 公共组件:通用按钮
// ═══════════════════════════════════════════════════════════
Widget _cardFilledBtn(String label, IconData icon) {
Widget _cardFilledBtn(String label, IconData icon, {VoidCallback? onTap}) {
return ElevatedButton(
onPressed: () {},
onPressed: onTap ?? () {},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF8B9CF7),
backgroundColor: const Color(0xFF6C5CE7),
foregroundColor: Colors.white,
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
@@ -1079,12 +980,12 @@ class ChatMessagesView extends ConsumerWidget {
);
}
Widget _cardOutlineBtn(String label, IconData icon) {
Widget _cardOutlineBtn(String label, IconData icon, {VoidCallback? onTap}) {
return OutlinedButton(
onPressed: () {},
onPressed: onTap ?? () {},
style: OutlinedButton.styleFrom(
foregroundColor: const Color(0xFF8B9CF7),
side: const BorderSide(color: Color(0xFF8B9CF7), width: 1.2),
foregroundColor: const Color(0xFF6C5CE7),
side: const BorderSide(color: Color(0xFF6C5CE7), width: 1.2),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
padding: const EdgeInsets.symmetric(vertical: 11),
),
@@ -1451,11 +1352,7 @@ class _AgentAction {
final _agentActions = <ActiveAgent, List<_AgentAction>>{
ActiveAgent.health: [
_AgentAction(label: '录入血压', icon: Icons.monitor_heart_outlined, route: 'trend'),
_AgentAction(label: '录入血糖', icon: Icons.bloodtype_outlined, route: 'trend'),
_AgentAction(label: '录入心率', icon: Icons.favorite_border, route: 'trend'),
_AgentAction(label: '录入血氧', icon: Icons.air_outlined, route: 'trend'),
_AgentAction(label: '录入体重', icon: Icons.monitor_weight_outlined, route: 'trend'),
_AgentAction(label: '查看趋势', icon: Icons.trending_up, isWide: true, route: 'trend'),
],
ActiveAgent.diet: [
_AgentAction(label: '拍照识别', icon: Icons.camera_alt_outlined, isWide: true, route: 'dietCapture'),

View File

@@ -14,6 +14,7 @@ class ChatMessage {
final DateTime createdAt;
MessageType type;
final Map<String, dynamic>? metadata;
bool confirmed;
ChatMessage({
required this.id,
required this.role,
@@ -21,6 +22,7 @@ class ChatMessage {
required this.createdAt,
this.type = MessageType.text,
this.metadata,
this.confirmed = false,
});
bool get isUser => role == 'user';
}
@@ -114,6 +116,8 @@ ActiveAgent _parseAgent(String? type) {
class ChatNotifier extends Notifier<ChatState> {
StreamSubscription<Map<String, dynamic>>? _subscription;
void markNeedsRebuild() => state = state.copyWith();
@override
ChatState build() {
Future.microtask(() {