feat: 饮食记录逻辑重构 + 通知管理分隔线修复 + 多页面 UI 调整

- 饮食: 新增 DietCommentaryPolicy + diet_record_logic, 饮食记录逻辑抽取复用
- 通知管理: 分隔线改为仿通知中心样式, 跳过图标区域只留文字下方
- 侧边栏: 对话记录操作栏浮层修复 + 常用功能间距收紧
- 智能体: 欢迎卡片去 400ms 延迟 + 胶囊去阴影
- 后端: AI 会话仓库新增方法 + 饮食评论策略 + 健康记录规则调整
- 其他: api_client IP 适配 + 多页面 UI 微调 + 新增测试
This commit is contained in:
MingNian
2026-07-12 22:49:38 +08:00
parent d82e006cf4
commit 335a3e6440
33 changed files with 2056 additions and 1283 deletions

View File

@@ -342,20 +342,24 @@ class _UnreadHint extends StatelessWidget {
margin: const EdgeInsets.fromLTRB(0, 2, 0, 14),
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 11),
decoration: BoxDecoration(
color: const Color(0xFFFFFBEB),
color: AppColors.notificationLight,
borderRadius: AppRadius.mdBorder,
border: Border.all(color: const Color(0xFFFDE68A)),
border: Border.all(color: AppColors.notificationBorder),
),
child: Row(
children: [
const Icon(LucideIcons.bellRing, size: 18, color: Color(0xFFF97316)),
const Icon(
LucideIcons.bellRing,
size: 18,
color: AppColors.notification,
),
const SizedBox(width: 9),
Text(
'你有 $unreadCount 条未读消息',
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w800,
color: Color(0xFF92400E),
color: AppColors.notification,
),
),
],
@@ -370,23 +374,14 @@ class _SectionTitle extends StatelessWidget {
@override
Widget build(BuildContext context) => _SectionShell(
child: Row(
children: [
const Icon(
LucideIcons.calendarDays,
size: 18,
color: AppColors.primary,
),
const SizedBox(width: 8),
Text(
text,
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w900,
color: AppColors.textPrimary,
),
),
],
child: Text(
text,
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.w700,
color: AppColors.textHint,
letterSpacing: 0,
),
),
);
}
@@ -421,34 +416,29 @@ class _CollapsibleSectionTitle extends StatelessWidget {
child: _SectionShell(
child: Row(
children: [
const Icon(
LucideIcons.history,
size: 18,
color: AppColors.textSecondary,
),
const SizedBox(width: 8),
Text(
'$text · $count',
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w900,
color: AppColors.textPrimary,
fontSize: 13,
fontWeight: FontWeight.w700,
color: AppColors.textHint,
letterSpacing: 0,
),
),
const Spacer(),
Text(
expanded ? '收起' : '展开',
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.w800,
color: AppColors.textSecondary,
fontSize: 12,
fontWeight: FontWeight.w700,
color: AppColors.textHint,
),
),
const SizedBox(width: 8),
const SizedBox(width: 4),
Icon(
expanded ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down,
size: 22,
color: AppColors.textSecondary,
size: 18,
color: AppColors.textHint,
),
],
),
@@ -483,50 +473,17 @@ class _NotificationRow extends StatelessWidget {
padding: const EdgeInsets.fromLTRB(14, 13, 12, 12),
child: Row(
children: [
Stack(
clipBehavior: Clip.none,
children: [
Container(
width: 48,
height: 48,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
visual.color.withValues(alpha: 0.18),
visual.lightColor,
],
),
borderRadius: AppRadius.mdBorder,
border: Border.all(
color: visual.color.withValues(alpha: 0.22),
),
),
child: Icon(
visual.icon,
size: 24,
color: visual.color,
),
Container(
width: 48,
height: 48,
decoration: BoxDecoration(
gradient: visual.gradient,
borderRadius: AppRadius.mdBorder,
border: Border.all(
color: visual.borderColor.withValues(alpha: 0.85),
),
if (!item.isRead)
Positioned(
top: -2,
right: -2,
child: Container(
width: 10,
height: 10,
decoration: BoxDecoration(
color: AppColors.error,
shape: BoxShape.circle,
border: Border.all(
color: Colors.white,
width: 1.5,
),
),
),
),
],
),
child: Icon(visual.icon, size: 24, color: Colors.white),
),
const SizedBox(width: 13),
Expanded(
@@ -594,14 +551,22 @@ class _NotificationRow extends StatelessWidget {
],
),
),
if (item.actionType != null) ...[
const SizedBox(width: 6),
const Icon(
Icons.chevron_right_rounded,
size: 22,
color: AppColors.textSecondary,
const SizedBox(width: 10),
SizedBox(
width: 12,
child: Center(
child: item.isRead
? const SizedBox.shrink()
: Container(
width: 8,
height: 8,
decoration: const BoxDecoration(
color: AppColors.error,
shape: BoxShape.circle,
),
),
),
],
),
],
),
),
@@ -641,21 +606,33 @@ class _NotificationVisual {
final IconData icon;
final Color color;
final Color lightColor;
final Color borderColor;
final LinearGradient gradient;
final String label;
const _NotificationVisual(this.icon, this.color, this.lightColor, this.label);
const _NotificationVisual(
this.icon,
this.color,
this.lightColor,
this.borderColor,
this.gradient,
this.label,
);
factory _NotificationVisual.fromModule(AppModuleVisual visual) {
return _NotificationVisual(
visual.icon,
visual.color,
visual.lightColor,
visual.borderColor,
visual.gradient,
visual.label,
);
}
factory _NotificationVisual.of(InAppNotification item) {
switch (item.actionType) {
final kind = item.actionType ?? item.type;
switch (kind) {
case 'exercise':
return _NotificationVisual.fromModule(AppModuleVisuals.exercise);
case 'health':
@@ -664,6 +641,12 @@ class _NotificationVisual {
LucideIcons.triangleAlert,
Color(0xFFEF4444),
Color(0xFFFEE2E2),
Color(0xFFFECACA),
LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Color(0xFFFF7A7A), Color(0xFFEF4444)],
),
'紧急',
)
: _NotificationVisual.fromModule(AppModuleVisuals.health);
@@ -673,11 +656,21 @@ class _NotificationVisual {
LucideIcons.fileWarning,
Color(0xFFEF4444),
Color(0xFFFEE2E2),
Color(0xFFFECACA),
LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Color(0xFFFF7A7A), Color(0xFFEF4444)],
),
'报告',
)
: _NotificationVisual.fromModule(AppModuleVisuals.report);
default:
case 'medication':
return _NotificationVisual.fromModule(AppModuleVisuals.medication);
case 'diet':
return _NotificationVisual.fromModule(AppModuleVisuals.diet);
default:
return _NotificationVisual.fromModule(AppModuleVisuals.notification);
}
}
}