fix: 管理员系统 + 注册流程重构 + UI改版 + 全链路修复

- 新增管理员角色(手机号12345678910),管理员可增删医生、查看患者
- 注册页重构:去掉角色选择+审核码,改为选医生+填姓名
- 医生端点按DoctorId过滤患者,Patient↔Doctor关系建立
- Doctor/DoctorProfile/User实体新增关联字段
- JSON循环引用修复(IgnoreCycles)
- /api/doctors改为公开接口
- 登录闪屏修复+原生Android启动页
- 输入框全局白底+灰框
- 蓝牙重连同步修复
- Web端(doctor_web+health_app/web)全部删除
- 全局UI改版:白底企业风,新配色和组件系统
- 新品牌图标和启动图
This commit is contained in:
MingNian
2026-06-16 15:16:44 +08:00
parent b635e9d25f
commit c70d5d4be6
103 changed files with 12284 additions and 13049 deletions

View File

@@ -2,7 +2,6 @@ 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;
@@ -10,9 +9,19 @@ class GradientScaffold extends StatelessWidget {
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(
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,
@@ -26,30 +35,26 @@ class GradientScaffold extends StatelessWidget {
);
}
/// 健康管家 — 设计规范 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 primaryMid = AppColors.blueMeasure;
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 bgSoft = AppColors.backgroundSoft;
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 divider = AppColors.divider;
// ── 语义色 ──
static const Color success = AppColors.success;
static const Color successLight = AppColors.successLight;
static const Color error = AppColors.error;
@@ -57,7 +62,6 @@ class AppTheme {
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;
@@ -65,7 +69,6 @@ class AppTheme {
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;
@@ -73,144 +76,181 @@ class AppTheme {
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),
);
color: const Color(0xFF101828).withValues(alpha: 0.07),
blurRadius: 22,
offset: const Offset(0, 10),
);
static BoxShadow get shadowLight => BoxShadow(
color: const Color(0xFF101828).withValues(alpha: 0.045),
blurRadius: 14,
offset: const Offset(0, 6),
);
static BoxShadow get shadowElevated => BoxShadow(
color: primary.withValues(alpha: 0.12),
blurRadius: 18,
offset: const Offset(0, 8),
);
static BoxShadow get shadowButton => BoxShadow(
color: primary.withValues(alpha: 0.22),
blurRadius: 16,
offset: const Offset(0, 8),
);
// ── 智能体配色 ──
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),
'default': Color(0xFFF3F0FF),
'consultation': Color(0xFFEFF5FF),
'health': Color(0xFFF3F0FF),
'diet': Color(0xFFFFF7E6),
'medication': Color(0xFFEFF5FF),
'report': Color(0xFFF6F3FF),
'exercise': Color(0xFFEFF8FF),
};
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,
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: primary,
primary: primary,
primaryContainer: primaryLight,
onPrimary: Colors.white,
secondary: AppColors.blueMeasure,
surface: surface,
brightness: Brightness.light,
),
scaffoldBackgroundColor: bg,
splashColor: primary.withValues(alpha: 0.06),
highlightColor: Colors.transparent,
hoverColor: primaryLight.withValues(alpha: 0.55),
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,
),
appBarTheme: const AppBarTheme(
backgroundColor: surface,
foregroundColor: text,
elevation: 0,
centerTitle: true,
scrolledUnderElevation: 0,
surfaceTintColor: Colors.transparent,
titleTextStyle: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w700,
color: text,
),
toolbarHeight: 48,
),
cardTheme: CardThemeData(
color: surface,
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(rMd)),
margin: EdgeInsets.zero,
),
cardTheme: CardThemeData(
color: surface,
elevation: 0,
surfaceTintColor: Colors.transparent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(rLg),
side: const BorderSide(color: border),
),
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),
),
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: Colors.white,
contentPadding: const EdgeInsets.symmetric(horizontal: sLg, vertical: 13),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(rMd),
borderSide: const BorderSide(color: border),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(rMd),
borderSide: const BorderSide(color: border),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(rMd),
borderSide: const BorderSide(color: primary, width: 1.2),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(rMd),
borderSide: const BorderSide(color: error),
),
hintStyle: const 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,
),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: primary,
foregroundColor: Colors.white,
minimumSize: const Size(double.infinity, 50),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(rMd)),
textStyle: const TextStyle(fontSize: 16, fontWeight: FontWeight.w700),
elevation: 0,
),
),
dialogTheme: DialogThemeData(shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(rLg))),
outlinedButtonTheme: OutlinedButtonThemeData(
style: OutlinedButton.styleFrom(
foregroundColor: text,
side: const BorderSide(color: border),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(rMd)),
textStyle: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600),
),
),
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),
),
);
dialogTheme: DialogThemeData(
backgroundColor: surface,
surfaceTintColor: Colors.transparent,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(rLg)),
),
textTheme: const TextTheme(
headlineLarge: TextStyle(
fontSize: 24,
fontWeight: FontWeight.w800,
color: text,
),
titleLarge: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w700,
color: text,
),
titleMedium: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
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),
);
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: AppColors.blueMeasure,
accentForeground: Colors.white,
destructive: error,
destructiveForeground: Colors.white,
border: border,
input: border,
ring: primary,
selection: primaryLight,
),
radius: BorderRadius.circular(rMd),
);
}