Files
AI-Health/health_app/lib/core/app_theme.dart
MingNian 01cd132476 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页面完整配色设计+图标底色对照表)
2026-06-14 23:28:14 +08:00

217 lines
7.6 KiB
Dart
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.

import 'package:flutter/material.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import 'app_colors.dart';
/// 渐变背景 Scaffold紫→蓝 4:6 渐变)
class GradientScaffold extends StatelessWidget {
final PreferredSizeWidget? appBar;
final Widget? body;
final Widget? floatingActionButton;
final Widget? drawer;
final Widget? bottomNavigationBar;
final bool extendBody;
const GradientScaffold({super.key, this.appBar, this.body, this.floatingActionButton, this.drawer, this.bottomNavigationBar, this.extendBody = false});
@override Widget build(BuildContext context) => Container(
decoration: const BoxDecoration(gradient: AppColors.bgGradient),
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: appBar,
body: body,
floatingActionButton: floatingActionButton,
drawer: drawer,
bottomNavigationBar: bottomNavigationBar,
extendBody: extendBody,
),
);
}
/// 健康管家 — 设计规范 v1.0:紫色系 + 干净白灰 + 12px圆角
class AppTheme {
AppTheme._();
// ── 主色调 ──
static const Color primary = AppColors.primary;
static const Color primaryLight = AppColors.primaryLight;
static const Color primaryDark = AppColors.primaryDark;
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.background;
static const Color surface = AppColors.cardBackground;
static const Color surfaceInner = AppColors.cardInner;
static const Color text = AppColors.textPrimary;
static const Color textSub = AppColors.textSecondary;
static const Color textHint = AppColors.textHint;
static const Color border = AppColors.border;
static const Color divider = AppColors.borderLight;
// ── 语义色 ──
static const Color success = AppColors.success;
static const Color successLight = AppColors.successLight;
static const Color error = AppColors.error;
static const Color errorLight = AppColors.errorLight;
static const Color warning = AppColors.warning;
static const Color warningLight = AppColors.warningLight;
// ── 圆角规范12px 卡片)──
static const double rXs = 4;
static const double rSm = 8;
static const double rMd = 12;
static const double rLg = 16;
static const double rXl = 20;
static const double rPill = 999;
// ── 间距8px 网格)──
static const double sXs = 4;
static const double sSm = 8;
static const double sMd = 12;
static const double sLg = 16;
static const double sXl = 20;
static const double sXxl = 24;
// ── 阴影(轻阴影)──
static BoxShadow get shadowCard => BoxShadow(
color: Colors.black.withOpacity(0.06),
blurRadius: 8,
offset: const Offset(0, 2),
);
static BoxShadow get shadowLight => BoxShadow(
color: Colors.black.withOpacity(0.04),
blurRadius: 6,
offset: const Offset(0, 1),
);
static BoxShadow get shadowElevated => BoxShadow(
color: primary.withOpacity(0.08),
blurRadius: 12,
offset: const Offset(0, 4),
);
static BoxShadow get shadowButton => BoxShadow(
color: primary.withOpacity(0.25),
blurRadius: 12,
offset: const Offset(0, 4),
);
// ── 智能体配色 ──
static const Map<String, Color> agentColors = {
'default': Color(0xFFEDE9FE),
'consultation': Color(0xFFE0E7FF),
'health': Color(0xFFD1FAE5),
'diet': Color(0xFFFEF3C7),
'medication': Color(0xFFFEE2E2),
'report': Color(0xFFDBEAFE),
'exercise': Color(0xFFEDE9FE),
};
static Color agentLight(String? name) => agentColors[name] ?? primaryLight;
// ── Material 主题 ──
static ThemeData get lightTheme => ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: primary,
primary: primary,
primaryContainer: primaryLight,
onPrimary: Colors.white,
secondary: accent,
surface: surface,
brightness: Brightness.light,
),
scaffoldBackgroundColor: bg,
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
hoverColor: primaryLight.withOpacity(0.3),
fontFamily: null,
appBarTheme: AppBarTheme(
backgroundColor: surface,
foregroundColor: text,
elevation: 0,
centerTitle: true,
scrolledUnderElevation: 0,
titleTextStyle: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: text),
toolbarHeight: 44,
),
cardTheme: CardThemeData(
color: surface,
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(rMd)),
margin: EdgeInsets.zero,
),
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: bgSoft,
contentPadding: const EdgeInsets.symmetric(horizontal: sLg, vertical: 12),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide.none,
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide.none,
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: const BorderSide(color: primary, width: 1),
),
hintStyle: TextStyle(color: textHint, fontSize: 15),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: primary,
foregroundColor: Colors.white,
minimumSize: const Size(double.infinity, 52),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(rMd)),
textStyle: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
elevation: 0,
),
),
dialogTheme: DialogThemeData(shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(rLg))),
textTheme: const TextTheme(
headlineLarge: TextStyle(fontSize: 24, fontWeight: FontWeight.w700, color: text),
titleLarge: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: text),
titleMedium: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: text),
bodyLarge: TextStyle(fontSize: 15, color: text, height: 1.5),
bodyMedium: TextStyle(fontSize: 13, color: textSub, height: 1.4),
labelMedium: TextStyle(fontSize: 13, color: textSub),
labelSmall: TextStyle(fontSize: 12, color: textHint),
),
);
// ── Shadcn UI 主题 ──
static ShadThemeData get shadTheme => ShadThemeData(
brightness: Brightness.light,
colorScheme: ShadVioletColorScheme.light(
background: bg,
foreground: text,
card: surface,
cardForeground: text,
popover: surface,
popoverForeground: text,
primary: primary,
primaryForeground: Colors.white,
secondary: primaryLight,
secondaryForeground: primaryDark,
muted: bgSoft,
mutedForeground: textSub,
accent: accent,
accentForeground: Colors.white,
destructive: error,
destructiveForeground: Colors.white,
border: border,
input: border,
ring: primary,
selection: primaryLight,
),
radius: BorderRadius.circular(rMd),
);
}