feat: 医生端完整实现 + 双角色登录 + 健康日历 + 饮食记录重做 + 配色系统重构

后端:
- User表加Role字段 + DoctorProfile表 + 废弃旧Doctor数据
- JWT加Role claim + 注册/登录拆分(/api/auth/register + /api/auth/login)
- 医生端点全量加JWT鉴权+Role校验(15个端点)
- 日历端点改造(用药/运动/随访按日期+星期匹配)
- 饮食记录PUT端点 + 健康记录DELETE端点
- 用户Profile端点返回Role

前端:
- 登录页重做(角色选择卡片 用户紫/医生蓝 + 审核码6666 + 注册返登录)
- App路由分流(userRole→患者端/医生端)
- 医生端: 工作台Dashboard + 患者管理(搜索分页) + 问诊/报告/随访CRUD
- 报告审核: AI预分析+指标表格+严重程度4级+模板多选+评语
- 随访编辑: 患者选择器+日期时间+打通患者端
- 健康日历: 可点击日历+当日安排列表+用药/运动/随访颜色标记
- 饮食记录: 热量趋势7/30天+日历(一周)+当日胶囊详情+侧滑编辑删除
- 健康档案: 日期选择器+手术可添加多条+白底渐变保存按钮
- 配色系统: 清理25个未使用色+新增doctorBlue/drawerGradient/pageGrey
- 患者端侧边栏: 新渐变+白底按钮+紫色汉堡图标
- AI对话: 用户气泡白底黑字+药管家改蓝色
- App启动闪屏: 防登录页闪烁

文档: color_design_system.md(20页面完整配色设计+图标底色对照表)
This commit is contained in:
MingNian
2026-06-14 23:28:14 +08:00
parent d102205b18
commit 01cd132476
35 changed files with 3473 additions and 1095 deletions

View File

@@ -1,70 +1,55 @@
import 'package:flutter/material.dart';
/// 健康管家 — 蚂蚁阿福风格 v2.0
/// 柔和淡紫 + 大量留白 + 紫色仅作点缀
/// 健康管家 — 色彩体系 v3.0
class AppColors {
AppColors._();
// ═══════════════════════════════════════════════════════════
// 主色 — 柔和淡紫(非浓烈深紫)
// ═══════════════════════════════════════════════════════════
// ── 主色 ──
static const Color primary = Color(0xFF8B5CF6);
static const Color primaryLight = Color(0xFFA78BFA);
static const Color primaryDark = Color(0xFF7C3AED);
static const Color primary = Color(0xFF8B5CF6); // 主紫(柔和不刺眼)
static const Color primaryLight = Color(0xFFA78BFA); // 浅紫(装饰/渐变)
static const Color primaryDark = Color(0xFF7C3AED); // 深紫(按压态)
static const Color primaryMid = Color(0xFFA78BFA); // 兼容旧名
// 渐变 — 柔和淡紫,仅垂直
static const LinearGradient primaryGradient = LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
begin: Alignment.topCenter, end: Alignment.bottomCenter,
colors: [Color(0xFF8B5CF6), Color(0xFFA78BFA)],
);
// ═══════════════════════════════════════════════════════════
// 背景色
// ═══════════════════════════════════════════════════════════
// ── 医生端主色 ──
static const Color doctorBlue = Color(0xFF6366F1);
static const LinearGradient doctorGradient = LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [
Color(0xFF38BDF8), Color(0xFF6366F1), Color(0xFF60A5FA), Color(0xFF818CF8)
]);
static const Color background = Color(0xFFF0ECFF); // 页面全局背景(淡紫)
static const Color backgroundSoft = Color(0xFFF0ECFF); // 输入框底色(淡紫,同页面背景)
static const Color cardBackground = Color(0xFFFFFFFF); // 卡片白色
static const Color cardInner = Color(0xFFF5F2FF); // 卡片内区域底色
// ── 背景 ──
static const Color background = Color(0xFFF0ECFF);
static const Color cardBackground = Color(0xFFFFFFFF);
static const Color cardInner = Color(0xFFF5F2FF);
static const Color pageGrey = Color(0xFFF5F5F5); // 医生端/设置页灰底
static const Color iconBg = Color(0xFFE8E0FA);
// 主背景渐变紫→蓝4:6比例
static const LinearGradient bgGradient = LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
begin: Alignment.centerLeft, end: Alignment.centerRight,
colors: [Color(0xFFF0ECFF), Color(0xFFEBF4FF)],
stops: [0.0, 0.4],
);
// 导航栏背景(极淡蓝紫
static const LinearGradient navGradient = LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0xFFE0E7FF), Color(0xFFC7D2FE)],
// 抽屉专用渐变(原色降低饱和度
static const LinearGradient drawerGradient = LinearGradient(
begin: Alignment.topLeft, end: Alignment.bottomRight,
colors: [Color(0xFFD4B5F0), Color(0xFFF3E8FF), Color(0xFFA8A0E8)],
stops: [0.0, 0.5, 1.0],
);
// ═══════════════════════════════════════════════════════════
// 文字颜色
// ═══════════════════════════════════════════════════════════
// ── 文字 ──
static const Color textPrimary = Color(0xFF1F2937);
static const Color textSecondary = Color(0xFF6B7280);
static const Color textHint = Color(0xFF9CA3AF);
static const Color textOnGradient = Color(0xFFFFFFFF);
// ═══════════════════════════════════════════════════════════
// 边框/分割线
// ═══════════════════════════════════════════════════════════
// ── 边框 ──
static const Color border = Color(0xFFE5E7EB);
static const Color borderLight = Color(0xFFF3F4F6);
// ═══════════════════════════════════════════════════════════
// 功能色
// ═══════════════════════════════════════════════════════════
// ── 功能色 ──
static const Color success = Color(0xFF10B981);
static const Color successLight = Color(0xFFD1FAE5);
static const Color error = Color(0xFFEF4444);
@@ -72,51 +57,9 @@ class AppColors {
static const Color warning = Color(0xFFF59E0B);
static const Color warningLight = Color(0xFFFEF3C7);
static const Color blueMeasure = Color(0xFF3B82F6);
static const Color avatarBg = Color(0xFFF0F0FF); // 头像底
// ═══════════════════════════════════════════════════════════
// 图标 / 小区域底色(极淡紫蓝)
// ═══════════════════════════════════════════════════════════
static const Color iconBg = Color(0xFFE8E0FA); // 卡片内底色,比页面背景稍深
// 兼容旧名字
static const Color iconBlueStart = Color(0xFF8B5CF6);
static const Color iconBlueEnd = Color(0xFFA78BFA);
static const Color iconCyanStart = Color(0xFF10B981);
static const Color iconCyanEnd = Color(0xFF34D399);
static const Color iconPurpleStart = Color(0xFF8B5CF6);
static const Color iconPurpleEnd = Color(0xFFA78BFA);
static const Color accentCyan = Color(0xFF10B981);
static const Color accentCyanDark = Color(0xFF059669);
// ═══════════════════════════════════════════════════════════
// 渐变 — 所有旧名字统一为柔和淡紫
// ═══════════════════════════════════════════════════════════
static LinearGradient get borderGradient => primaryGradient;
static LinearGradient get cyanGradient => primaryGradient;
static LinearGradient get purpleGradient => primaryGradient;
static LinearGradient get pageBgGradient => bgGradient;
static LinearGradient get cardLightGradient => bgGradient;
static LinearGradient get gWelcomeHeader => primaryGradient;
// 各种旧花渐变全改为极淡色或纯色
static LinearGradient get gPurplePink => primaryGradient;
static LinearGradient get gBluePurple => primaryGradient;
static LinearGradient get gPinkPurple => primaryGradient;
static LinearGradient get gBlueRed => primaryGradient;
static LinearGradient get gBlueGreen => primaryGradient;
static LinearGradient get gPurpleCyan => primaryGradient;
static LinearGradient get gRedPurple => primaryGradient;
static LinearGradient get gCyanBlue => primaryGradient;
static LinearGradient get gPinkCyan => primaryGradient;
static LinearGradient get gLightPurple => bgGradient;
static LinearGradient get gLightBlue => bgGradient;
// ═══════════════════════════════════════════════════════════
// 阴影 — 轻柔
// ═══════════════════════════════════════════════════════════
// ── 阴影 ──
static List<BoxShadow> get cardShadow => [
BoxShadow(color: Colors.black.withOpacity(0.06), blurRadius: 8, offset: const Offset(0, 2)),
];
@@ -130,18 +73,14 @@ class AppColors {
BoxShadow(color: Color(0xFF8B5CF6).withOpacity(0.30), blurRadius: 16, offset: const Offset(0, 6)),
];
// ═══════════════════════════════════════════════════════════
// 旧代码兼容别名chat_messages_view 原始代码引用)
// ═══════════════════════════════════════════════════════════
static const Color purpleGradientStart = Color(0xFF8B5CF6);
static const Color blueGradientEnd = Color(0xFF3B82F6);
// ── 兼容别名 ──
static Color get iconColor => Color(0xFF9B8AF7);
static LinearGradient get purpleBlueGradient => primaryGradient;
static const Color backgroundSoft = background;
static const Color textOnGradient = Color(0xFFFFFFFF);
static Color get primaryPurple => primary;
static Color get iconColor => Color(0xFF9B8AF7); // 淡紫图标色
static Color get iconBackground => iconBg;
static Color get primaryBlue => blueMeasure;
static LinearGradient get lightGradient => gLightPurple;
static Color get backgroundSecondary => background;
static LinearGradient get successButtonGradient => primaryGradient;
static LinearGradient get lightGradient => bgGradient;
}

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'navigation_provider.dart';
import '../pages/auth/login_page.dart';
import '../pages/home/home_page.dart';
@@ -17,60 +18,55 @@ import '../pages/diet/diet_capture_page.dart';
import '../pages/device/device_scan_page.dart';
import '../pages/device/device_management_page.dart';
import '../pages/remaining_pages.dart';
import '../pages/doctor/doctor_home_page.dart';
import '../pages/doctor/doctor_patient_detail_page.dart';
import '../pages/doctor/doctor_report_detail_page.dart';
import '../pages/doctor/doctor_followup_edit_page.dart';
import '../pages/doctor/doctor_settings_page.dart';
import '../pages/doctor/doctor_profile_page.dart';
import '../providers/auth_provider.dart' show userRoleProvider;
/// 根据路由信息返回对应页面
Widget buildPage(RouteInfo route) {
Widget buildPage(RouteInfo route, WidgetRef ref) {
final params = route.params;
switch (route.name) {
case 'login':
return const LoginPage();
case 'login': return const LoginPage();
case 'home':
return const HomePage();
case 'trend':
return TrendPage(metricType: params['type']?.isNotEmpty == true ? params['type'] : null);
case 'calendar':
return const HealthCalendarPage();
case 'medications':
return const MedicationListPage();
case 'medCheckIn':
return MedicationCheckInPage(medId: params['id']);
case 'medicationEdit':
return const MedicationEditPage();
case 'reports':
return const ReportListPage();
case 'aiAnalysis':
return AiAnalysisPage(id: params['id']!);
case 'consultation':
return DoctorChatPage(id: params['id']!);
case 'exercisePlan':
return const ExercisePlanPage();
case 'exerciseCreate':
return const ExercisePlanCreatePage();
case 'dietRecords':
return const DietRecordListPage();
case 'dietCapture':
return const DietCapturePage();
case 'profile':
return const ProfilePage();
case 'devices':
return const DeviceManagementPage();
case 'deviceScan':
return const DeviceScanPage();
case 'healthArchive':
return const HealthArchivePage();
case 'followups':
return const FollowUpListPage();
case 'settings':
return const SettingsPage();
case 'notificationPrefs':
return const NotificationPrefsPage();
case 'staticText':
return StaticTextPage(type: params['type']!);
case 'servicePackageDetail':
return ServicePackageDetailPage(packageId: params['id']!);
case 'dietDetail':
return DietRecordDetailPage(id: params['id']!);
default:
return const LoginPage();
final role = ref.watch(userRoleProvider);
return role == 'Doctor' ? const DoctorHomePage() : const HomePage();
case 'doctorHome': return const DoctorHomePage();
case 'trend': return TrendPage(metricType: params['type']?.isNotEmpty == true ? params['type'] : null);
case 'calendar': return const HealthCalendarPage();
case 'medications': return const MedicationListPage();
case 'medCheckIn': return MedicationCheckInPage(medId: params['id']);
case 'medicationEdit': return const MedicationEditPage();
case 'reports': return const ReportListPage();
case 'aiAnalysis': return AiAnalysisPage(id: params['id']!);
case 'consultation': return DoctorChatPage(id: params['id']!);
case 'exercisePlan': return const ExercisePlanPage();
case 'exerciseCreate': return const ExercisePlanCreatePage();
case 'dietRecords': return const DietRecordListPage();
case 'dietCapture': return const DietCapturePage();
case 'profile': return const ProfilePage();
case 'doctorProfile': return const DoctorProfileEditPage();
case 'doctorSettings': return const DoctorSettingsPage();
case 'doctorPatientDetail': return DoctorPatientDetailPage(id: params['id']!);
case 'doctorChat': return DoctorChatPage(id: params['id']!);
case 'doctorReportDetail': return DoctorReportDetailPage(id: params['id']!);
case 'doctorFollowUpEdit': return DoctorFollowUpEditPage(id: params['id']!);
case 'devices': return const DeviceManagementPage();
case 'deviceScan': return const DeviceScanPage();
case 'healthArchive': return const HealthArchivePage();
case 'followups': return const FollowUpListPage();
case 'settings': return const SettingsPage();
case 'notificationPrefs': return const NotificationPrefsPage();
case 'staticText': return StaticTextPage(type: params['type']!);
case 'servicePackageDetail': return ServicePackageDetailPage(packageId: params['id']!);
case 'dietDetail': return DietRecordDetailPage(id: params['id']!);
default: return const LoginPage();
}
}
// ── 医生端次级页面 Stubs逐步实现──

View File

@@ -34,13 +34,13 @@ class AppTheme {
static const Color primary = AppColors.primary;
static const Color primaryLight = AppColors.primaryLight;
static const Color primaryDark = AppColors.primaryDark;
static const Color primaryMid = AppColors.primaryMid;
static const Color primaryMid = AppColors.primaryLight;
static const Color accent = AppColors.success;
static const Color info = AppColors.blueMeasure;
// ── 中性色 ──
static const Color bg = AppColors.background;
static const Color bgSoft = AppColors.backgroundSoft;
static const Color bgSoft = AppColors.background;
static const Color surface = AppColors.cardBackground;
static const Color surfaceInner = AppColors.cardInner;
static const Color text = AppColors.textPrimary;