fix: VLM识别修复 + 饮食CRUD + 侧边栏美化 + UI优化

- VLM: ChatMessage.Content string→object, 修复视觉content双重序列化
- 饮食: diet/medication端点 record→手动JSON解析, 修复循环引用
- 饮食记录: 左滑删除 + 去评分 + AI饮食评语(DeepSeek)
- 侧边栏: 功能区统一Row+Expanded, 服务包compact模式
- 历史对话: 点击加载历史消息
- 登录页: 居中布局, 去底部空白
- UI: 主色加深#6C5CE7, maxWidth:1024防图片超大
This commit is contained in:
MingNian
2026-06-04 16:27:03 +08:00
parent c44917b8e9
commit b944a31983
12 changed files with 413 additions and 305 deletions

View File

@@ -156,14 +156,109 @@ final List<ServicePackage> servicePackages = [
),
];
/// 服务包卡片 —— 展示在"我的"页面中
/// 服务包卡片
class ServicePackageCard extends ConsumerWidget {
const ServicePackageCard({super.key});
final bool compact;
const ServicePackageCard({super.key, this.compact = false});
@override
Widget build(BuildContext context, WidgetRef ref) {
final package = servicePackages.first;
// 构建服务图标网格
final services = package.services.take(8).toList();
final serviceRows = <Widget>[];
for (var i = 0; i < services.length; i += 4) {
final rowItems = services.skip(i).take(4).toList();
serviceRows.add(Padding(
padding: EdgeInsets.only(top: i > 0 ? 12 : 0),
child: Row(
children: rowItems.map((item) {
return Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 40,
height: 40,
decoration: const BoxDecoration(
color: Color(0xFFFFF8EE),
shape: BoxShape.circle,
),
child: Icon(item.icon, size: 20, color: const Color(0xFFF5A623)),
),
const SizedBox(height: 4),
Text(item.label,
style: const TextStyle(fontSize: 11, color: AppTheme.textSub),
textAlign: TextAlign.center,
),
],
),
);
}).toList(),
),
));
}
Widget card = Material(
color: Colors.transparent,
child: InkWell(
onTap: () => pushRoute(ref, 'servicePackageDetail', params: {'id': package.id}),
borderRadius: BorderRadius.circular(16),
child: Padding(
padding: const EdgeInsets.fromLTRB(18, 16, 18, 14),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 标题行 + 详情入口
Row(children: [
Text(package.title, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: AppTheme.text)),
const Spacer(),
GestureDetector(
onTap: () => pushRoute(ref, 'servicePackageDetail', params: {'id': package.id}),
child: Row(mainAxisSize: MainAxisSize.min, children: [
Text('详情', style: TextStyle(fontSize: 13, color: AppTheme.textSub)),
const SizedBox(width: 2),
Icon(Icons.chevron_right, size: 18, color: AppTheme.textHint),
]),
),
]),
const SizedBox(height: 12),
// VIP 标签
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
gradient: LinearGradient(colors: [const Color(0xFFF5A623).withAlpha(200), const Color(0xFFE8930C)]),
borderRadius: BorderRadius.circular(6),
),
child: const Row(mainAxisSize: MainAxisSize.min, children: [
Text('VIP', style: TextStyle(fontSize: 12, fontWeight: FontWeight.w700, color: Colors.white)),
SizedBox(width: 4),
Text('VIP 产品权益', style: TextStyle(fontSize: 11, fontWeight: FontWeight.w500, color: Colors.white)),
]),
),
const SizedBox(height: 14),
// 服务图标网格
...serviceRows,
const SizedBox(height: 12),
// 查看更多
Center(
child: GestureDetector(
onTap: () => pushRoute(ref, 'servicePackageDetail', params: {'id': package.id}),
child: Row(mainAxisSize: MainAxisSize.min, children: [
Text('查看更多服务包', style: TextStyle(fontSize: 13, color: AppTheme.primary, fontWeight: FontWeight.w500)),
Icon(Icons.chevron_right, size: 16, color: AppTheme.primary),
]),
),
),
],
),
),
),
);
if (compact) return card;
return Container(
margin: const EdgeInsets.symmetric(horizontal: 24),
decoration: BoxDecoration(
@@ -177,161 +272,7 @@ class ServicePackageCard extends ConsumerWidget {
),
],
),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: () => pushRoute(ref, 'servicePackageDetail', params: {'id': package.id}),
borderRadius: BorderRadius.circular(16),
child: Padding(
padding: const EdgeInsets.fromLTRB(18, 16, 18, 14),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 标题行 + 详情入口
Row(
children: [
Text(
package.title,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppTheme.text,
),
),
const Spacer(),
GestureDetector(
onTap: () => pushRoute(ref, 'servicePackageDetail', params: {'id': package.id}),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'详情',
style: TextStyle(
fontSize: 13,
color: AppTheme.textSub,
),
),
const SizedBox(width: 2),
Icon(
Icons.chevron_right,
size: 18,
color: AppTheme.textHint,
),
],
),
),
],
),
const SizedBox(height: 12),
// VIP 标签
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
const Color(0xFFF5A623).withAlpha(200),
const Color(0xFFE8930C),
],
),
borderRadius: BorderRadius.circular(6),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'VIP',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w700,
color: Colors.white,
),
),
const SizedBox(width: 4),
Text(
package.subtitle,
style: const TextStyle(
fontSize: 11,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
],
),
),
const SizedBox(height: 14),
// 服务图标网格
Wrap(
spacing: 0,
runSpacing: 12,
children: package.services.take(8).map((item) {
return SizedBox(
width: (MediaQuery.of(context).size.width - 48 - 36) / 4,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: const Color(0xFFFFF8EE),
shape: BoxShape.circle,
),
child: Icon(
item.icon,
size: 20,
color: const Color(0xFFF5A623),
),
),
const SizedBox(height: 4),
Text(
item.label,
style: const TextStyle(
fontSize: 11,
color: AppTheme.textSub,
),
textAlign: TextAlign.center,
),
],
),
);
}).toList(),
),
const SizedBox(height: 12),
// 查看更多
Center(
child: GestureDetector(
onTap: () => pushRoute(ref, 'servicePackageDetail', params: {'id': package.id}),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'查看更多服务包',
style: TextStyle(
fontSize: 13,
color: AppTheme.primary,
fontWeight: FontWeight.w500,
),
),
Icon(
Icons.chevron_right,
size: 16,
color: AppTheme.primary,
),
],
),
),
),
],
),
),
),
),
child: card,
);
}
}