feat: UI 系统中心化 + 趋势图重构 + 法律文档 H5 上线

- UI 系统: 新增 design_tokens / module_visuals / app_buttons / app_status_badge / app_toast / ai_content 六个共享模块; 28 个页面接入, SnackBar 全部替换为 AppToast
- 趋势图: 直线折线 + 渐变填充 + 阴影; 去网格线; X 轴日+月份分隔; Y 轴整数刻度最多 4 个; 点击数据点/录入记录显示上方浮层, 选中点变实心
- 法律文档 H5: 后端 wwwroot 托管隐私政策/服务协议/关于/个人信息收集清单/第三方 SDK 清单 + 索引页; Program.cs 启用 UseDefaultFiles + UseStaticFiles
- 其他: auth_provider 登录流程调整; api_client IP 适配; 主页智能体栏间距优化
This commit is contained in:
MingNian
2026-07-08 21:25:07 +08:00
parent 7a93237069
commit 1c020b8ae5
45 changed files with 3480 additions and 1000 deletions

View File

@@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../core/app_colors.dart';
import '../../core/app_design_tokens.dart';
import '../../core/app_module_visuals.dart';
import '../../core/app_theme.dart';
import '../../core/navigation_provider.dart';
import '../../providers/auth_provider.dart';
@@ -36,10 +38,10 @@ class ProfilePage extends ConsumerWidget {
),
const SizedBox(height: 14),
_ActionTile(
icon: Icons.folder_shared_outlined,
icon: AppModuleVisuals.health.icon,
title: '健康档案',
subtitle: '维护个人资料、病史、手术和过敏信息',
color: const Color(0xFF8B5CF6),
visual: AppModuleVisuals.health,
onTap: () => pushRoute(ref, 'healthArchive'),
),
const SizedBox(height: 14),
@@ -54,9 +56,7 @@ class ProfilePage extends ConsumerWidget {
final ok = await showDialog<bool>(
context: context,
builder: (ctx) => AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(AppTheme.rLg),
),
shape: RoundedRectangleBorder(borderRadius: AppRadius.lgBorder),
title: const Text('退出登录'),
content: const Text('确定退出当前账号?'),
actions: [
@@ -91,12 +91,12 @@ class _AccountCard extends StatelessWidget {
@override
Widget build(BuildContext context) => Container(
padding: const EdgeInsets.all(16),
padding: AppSpacing.panel,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
borderRadius: AppRadius.lgBorder,
border: Border.all(color: AppColors.border, width: 1.1),
boxShadow: [AppTheme.shadowLight],
boxShadow: AppShadows.soft,
),
child: Row(
children: [
@@ -110,21 +110,14 @@ class _AccountCard extends StatelessWidget {
name,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.w800,
color: AppColors.textPrimary,
),
style: AppTextStyles.summaryTitle.copyWith(fontSize: 20),
),
const SizedBox(height: 5),
Text(
phone,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 16,
color: AppColors.textSecondary,
),
style: AppTextStyles.listSubtitle,
),
],
),
@@ -144,18 +137,13 @@ class _Avatar extends StatelessWidget {
width: 58,
height: 58,
decoration: BoxDecoration(
color: AppColors.iconBg,
borderRadius: BorderRadius.circular(18),
border: Border.all(color: AppColors.borderLight),
gradient: AppModuleVisuals.health.gradient,
borderRadius: AppRadius.lgBorder,
),
clipBehavior: Clip.antiAlias,
child: avatarUrl != null && avatarUrl!.isNotEmpty
? Image.network(avatarUrl!, fit: BoxFit.cover)
: const Icon(
Icons.person_rounded,
color: AppColors.blueMeasure,
size: 34,
),
: const Icon(Icons.person_rounded, color: Colors.white, size: 34),
);
}
@@ -163,30 +151,30 @@ class _ActionTile extends StatelessWidget {
final IconData icon;
final String title;
final String subtitle;
final Color color;
final AppModuleVisual visual;
final VoidCallback onTap;
const _ActionTile({
required this.icon,
required this.title,
required this.subtitle,
required this.color,
required this.visual,
required this.onTap,
});
@override
Widget build(BuildContext context) => Material(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
borderRadius: AppRadius.lgBorder,
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(16),
borderRadius: AppRadius.lgBorder,
child: Container(
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
borderRadius: AppRadius.lgBorder,
border: Border.all(color: AppColors.border, width: 1.1),
boxShadow: [AppTheme.shadowLight],
boxShadow: AppShadows.soft,
),
child: Row(
children: [
@@ -194,42 +182,28 @@ class _ActionTile extends StatelessWidget {
width: 46,
height: 46,
decoration: BoxDecoration(
color: color.withValues(alpha: 0.10),
borderRadius: BorderRadius.circular(14),
gradient: visual.gradient,
borderRadius: AppRadius.mdBorder,
),
child: Icon(icon, color: color, size: 24),
child: Icon(icon, color: Colors.white, size: 24),
),
const SizedBox(width: 13),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.w800,
color: AppColors.textPrimary,
),
),
Text(title, style: AppTextStyles.listTitle),
const SizedBox(height: 4),
Text(
subtitle,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 14,
color: AppColors.textSecondary,
),
style: AppTextStyles.listSubtitle,
),
],
),
),
const Icon(
Icons.chevron_right_rounded,
size: 22,
color: AppColors.textHint,
),
const Icon(Icons.chevron_right_rounded, color: AppColors.textHint),
],
),
),
@@ -243,16 +217,13 @@ class _LogoutButton extends StatelessWidget {
const _LogoutButton({required this.onPressed});
@override
Widget build(BuildContext context) => OutlinedButton.icon(
Widget build(BuildContext context) => TextButton(
onPressed: onPressed,
icon: const Icon(Icons.logout_rounded, size: 19),
label: const Text('退出登录'),
style: OutlinedButton.styleFrom(
style: TextButton.styleFrom(
foregroundColor: AppColors.error,
side: BorderSide(color: AppColors.error.withValues(alpha: 0.34)),
minimumSize: const Size.fromHeight(52),
textStyle: const TextStyle(fontSize: 16, fontWeight: FontWeight.w800),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
padding: const EdgeInsets.symmetric(vertical: 14),
textStyle: AppTextStyles.button,
),
child: const Text('退出登录'),
);
}