Files
AI-Health/健康管家-视觉统一实施文档.md
MingNian c2399b952f refactor: 4层架构重构 + 饮食VLM接入 + 多项修复
- 后端: remaining_endpoints拆分为6个独立文件
- 后端: AI Agent Handler从ai_chat_endpoints抽取为7个独立处理器
- 后端: 食物识别prompt改为输出结构化JSON
- 前端: 饮食识别从Mock替换为真实VLM API调用
- 前端: 首页图片上传URL修复(/api/upload→/api/files/upload)
- 前端: 拍饮食按钮导航到独立DietCapturePage
- 前端: 删除无用agent_bar.dart
- 前端: 修复widget_test.dart过期属性名
- 前端: 恢复ServicePackageCard和详情页
- 新增6份实施文档(情况/问诊/报告/建档/日历/视觉统一)
2026-06-03 23:17:37 +08:00

293 lines
9.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 视觉统一 — 实施文档
> 对照《页面设计文档》规范,逐项统一圆角、阴影、留白、颜色。
## 一、设计 Token 集中化
`AppTheme` 中新增静态 Token所有组件从 Token 取值,不再硬编码:
```dart
class AppTheme {
// ── 已有(不动)──
static const Color primary = Color(0xFF8B9CF7);
static const Color primaryLight = Color(0xFFF0F2FF);
static const Color bg = Color(0xFFF8F9FC);
static const Color surface = Color(0xFFFFFFFF);
static const Color text = Color(0xFF2D2B32);
static const Color textSub = Color(0xFF8A8892);
static const Color textHint = Color(0xFFBFBCC4);
static const Color success = Color(0xFF6ECF8A);
static const Color error = Color(0xFFF56C6C);
static const Color warning = Color(0xFFF5A623);
// ── 新增:圆角 Token ──
static const double radiusXs = 8; // 标签、小徽章
static const double radiusSm = 12; // 输入框、小按钮
static const double radiusMd = 16; // 列表项、菜单、弹窗内卡片
static const double radiusLg = 20; // 按钮
static const double radiusXl = 24; // 主卡片
static const double radiusPill = 999; // 胶囊
// ── 新增:间距 Token ──
static const double spaceXs = 4;
static const double spaceSm = 8;
static const double spaceMd = 12;
static const double spaceLg = 16;
static const double spaceXl = 20;
static const double space2xl = 24;
// ── 新增:阴影 Token ──
static BoxShadow shadowCard = BoxShadow(
color: primary.withAlpha(15),
blurRadius: 12,
offset: const Offset(0, 4),
);
static BoxShadow shadowBubble = BoxShadow(
color: primary.withAlpha(12),
blurRadius: 10,
offset: const Offset(0, 3),
);
static BoxShadow shadowNone = const BoxShadow(
color: Colors.transparent,
blurRadius: 0,
offset: Offset.zero,
);
}
```
## 二、ThemeData 统一调整
```dart
static ThemeData get lightTheme => ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: primary, primary: primary, surface: bg,
brightness: Brightness.light),
scaffoldBackgroundColor: bg,
// ── AppBar ── 不变
appBarTheme: const AppBarTheme(
backgroundColor: surface, foregroundColor: text,
elevation: 0, centerTitle: true, scrolledUnderElevation: 0,
titleTextStyle: TextStyle(
fontSize: 17, fontWeight: FontWeight.w600, color: text),
),
// ── Card: radius 16 → 24 ──
cardTheme: CardThemeData(
color: surface, elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radiusXl)), // 24
margin: EdgeInsets.zero,
),
// ── Input: radius 12 → 16, 内边距增加 ──
inputDecorationTheme: InputDecorationTheme(
filled: true, fillColor: const Color(0xFFF4F5FA),
contentPadding: const EdgeInsets.symmetric(
horizontal: 16, vertical: 14), // 更宽松
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(radiusMd), // 16
borderSide: BorderSide.none),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(radiusMd),
borderSide: BorderSide.none),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(radiusMd),
borderSide: const BorderSide(color: primary, width: 1.5)),
hintStyle: const TextStyle(color: textHint, fontSize: 15),
),
// ── Button: radius 14 → 20, 高度 48 → 52 ──
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: primary, foregroundColor: Colors.white,
minimumSize: const Size(double.infinity, 52), // 更高
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radiusLg)), // 20
textStyle: const TextStyle(
fontSize: 16, fontWeight: FontWeight.w600),
elevation: 0,
),
),
// ── Dialog: radius 保持 22略增 ──
dialogTheme: DialogThemeData(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radiusXl)), // 24
),
// BottomSheet 主题
bottomSheetTheme: const BottomSheetThemeData(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(radiusXl))), // 24
),
// 文字主题(不动)
textTheme: const TextTheme(...),
);
```
## 三、逐文件硬编码替换
### 3.1 health_drawer.dart
```dart
// 找BorderRadius.circular(16)
// 替换BorderRadius.circular(AppTheme.radiusXl) → 24
// _SectionCard
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(AppTheme.radiusXl), // was 16
boxShadow: [AppTheme.shadowCard], // 统一
)
// _MetricTile
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(AppTheme.radiusMd), // was 10
)
// _FeatureChip
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(AppTheme.radiusMd), // was 12
)
// _ConversationItem
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(AppTheme.radiusSm), // was 10
)
```
### 3.2 chat_messages_view.dart
```dart
// AI 气泡 — 不动radius 20 符合规范)
// 用户气泡 — 不动
// 按钮卡片 — 统一
// _cardFilledBtn
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(AppTheme.radiusLg)), // was 12
// _cardOutlineBtn
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(AppTheme.radiusLg)), // was 12
// DataConfirmCard 外层
borderRadius: BorderRadius.circular(AppTheme.radiusXl), // was 20
// 内部小卡片
borderRadius: BorderRadius.circular(AppTheme.radiusMd), // was 10-14
// 快捷选项按钮
borderRadius: BorderRadius.circular(AppTheme.radiusPill), // was 20
```
### 3.3 medication 相关页面
```dart
// medication_list_page.dart / medication_edit_page.dart
// 列表项卡片: was 12-16 → AppTheme.radiusMd (16)
// 按钮: was 14 → AppTheme.radiusLg (20)
```
### 3.4 remaining_pages.dart
```dart
// HealthCalendarPage 日期格子
borderRadius: BorderRadius.circular(AppTheme.radiusXs), // was 20错误值
// ExercisePlanPage 进度卡片
borderRadius: BorderRadius.circular(AppTheme.radiusXl), // was 20
// ExercisePlanItem 日条目
borderRadius: BorderRadius.circular(AppTheme.radiusMd), // was 16
// FollowUpItem 随访卡片
borderRadius: BorderRadius.circular(AppTheme.radiusXl), // was 16
```
### 3.5 diet_capture_page.dart
```dart
// 拍照大圆
borderRadius: BorderRadius.circular(90), // 不动
// 食物列表容器
borderRadius: BorderRadius.circular(AppTheme.radiusXl), // was 20
// 食物条目
borderRadius: BorderRadius.circular(AppTheme.radiusMd), // was 16
// 按钮
borderRadius: BorderRadius.circular(AppTheme.radiusPill), // was 24
```
### 3.6 report/consultation 等页面
```dart
// 搜索替换BorderRadius.circular(12) → AppTheme.radiusSm
// 搜索替换BorderRadius.circular(16) → AppTheme.radiusMd
// 卡片外框:→ AppTheme.radiusXl
// 按钮:→ AppTheme.radiusLg
```
## 四、阴影统一
全局替换:
```dart
// 旧boxShadow: [BoxShadow(color: Color(0xFF8B9CF7).withAlpha(15), blurRadius: 14, offset: Offset(0, 4))]
// 新boxShadow: [AppTheme.shadowCard]
// 旧boxShadow: [BoxShadow(color: Color(0xFF8B9CF7).withAlpha(12), blurRadius: 10, offset: Offset(0, 3))]
// 新boxShadow: [AppTheme.shadowBubble]
```
## 五、替换策略
| 原值 | Token | 用于 |
|------|-------|------|
| 8 | `radiusXs` | 标签、小圆点、Chip |
| 10-12 | `radiusSm` | 输入框、列表项、小图标容器 |
| 14-16 | `radiusMd` | 弹窗内部卡片、指标方块、食物条目 |
| 18-20 | `radiusLg` | 按钮 |
| 24+ | `radiusXl` | 主卡片、抽屉分区、对话框 |
| 999 | `radiusPill` | 胶囊按钮、快捷选项 |
## 六、验证方式
改完后跑 `flutter analyze` 确认无错误,然后运行 App 目测以下页面:
1. 侧滑抽屉 — 卡片圆角、阴影
2. 首页对话 — 气泡、按钮、任务卡片
3. 拍饮食 — 识别结果卡片、按钮
4. 登录页 — 输入框、按钮
5. 运动计划 — 日条目、进度卡片
## 七、文件改动清单
```
修改:
lib/core/app_theme.dart (+ 圆角/间距/阴影 Token, ThemeData 调整, ~50行)
lib/widgets/health_drawer.dart (~15处硬编码替换)
lib/pages/home/widgets/chat_messages_view.dart (~20处硬编码替换)
lib/pages/diet/diet_capture_page.dart (~8处硬编码替换)
lib/pages/auth/login_page.dart (~5处硬编码替换)
lib/pages/remaining_pages.dart (~10处硬编码替换)
lib/pages/medication/medication_list_page.dart (~5处硬编码替换)
lib/pages/medication/medication_edit_page.dart (~3处硬编码替换)
lib/pages/report/report_pages.dart (~5处硬编码替换)
lib/pages/report/ai_analysis_page.dart (~3处硬编码替换)
lib/pages/settings/*.dart (~3处硬编码替换)
lib/pages/profile/*.dart (~5处硬编码替换)
```
改动虽然文件多,但每个文件都是纯替换——把数字换成 AppTheme Token**不涉及逻辑变更**。
## 八、不做的事情
- 不改变颜色体系(淡薰紫主题保持)
- 不改变字体/字号
- 不改变布局结构
- 不引入新的 UI 组件