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份实施文档(情况/问诊/报告/建档/日历/视觉统一)
This commit is contained in:
292
健康管家-视觉统一实施文档.md
Normal file
292
健康管家-视觉统一实施文档.md
Normal file
@@ -0,0 +1,292 @@
|
||||
# 视觉统一 — 实施文档
|
||||
|
||||
> 对照《页面设计文档》规范,逐项统一圆角、阴影、留白、颜色。
|
||||
|
||||
## 一、设计 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 组件
|
||||
Reference in New Issue
Block a user