style: 统一患者端页面视觉并补齐入口跳转

- 调整全局颜色基调,提高灰色文字和边框对比度
- 新增企业级页面头部、统计块和卡片通用组件
- 优化用药管理、报告管理、个人信息、通知中心页面布局
- 新增多张页面插画和通用 UI 装饰素材
- 运动计划列表更紧凑,并新增运动计划详情路由
- 补齐医生端协议入口和原始报告查看入口
- 移除设置页未实现的字体大小和清除缓存入口
This commit is contained in:
MingNian
2026-06-25 22:13:42 +08:00
parent 53ce19e8c3
commit 18df2f3285
23 changed files with 1266 additions and 470 deletions

View File

@@ -7,6 +7,7 @@ import '../../providers/data_providers.dart';
import '../../widgets/app_empty_state.dart';
import '../../widgets/app_future_view.dart';
import '../../widgets/common_widgets.dart';
import '../../widgets/enterprise_widgets.dart';
class MedicationListPage extends ConsumerStatefulWidget {
const MedicationListPage({super.key});
@@ -14,35 +15,20 @@ class MedicationListPage extends ConsumerStatefulWidget {
ConsumerState<MedicationListPage> createState() => _MedicationListPageState();
}
class _MedicationListPageState extends ConsumerState<MedicationListPage>
with SingleTickerProviderStateMixin {
String _filter = '';
class _MedicationListPageState extends ConsumerState<MedicationListPage> {
static const _medBlue = Color(0xFF60A5FA);
static const _medCyan = Color(0xFF8B5CF6);
Future<List<Map<String, dynamic>>>? _future;
late final _tabCtrl = TabController(length: 3, vsync: this);
@override
void initState() {
super.initState();
_tabCtrl.addListener(() {
if (!_tabCtrl.indexIsChanging) {
setState(() {
_filter = ['', 'active', 'inactive'][_tabCtrl.index];
});
_load();
}
});
_load();
}
@override
void dispose() {
_tabCtrl.dispose();
super.dispose();
}
void _load() {
setState(() {
_future = ref.read(medicationServiceProvider).getMedications(_filter);
_future = ref.read(medicationServiceProvider).getMedications('');
});
}
@@ -52,8 +38,6 @@ class _MedicationListPageState extends ConsumerState<MedicationListPage>
_load();
}
static const _tabs = ['全部', '服用中', '已停药'];
@override
Widget build(BuildContext context) {
return GradientScaffold(
@@ -63,43 +47,59 @@ class _MedicationListPageState extends ConsumerState<MedicationListPage>
onPressed: () => popRoute(ref),
),
title: const Text('用药管理'),
bottom: PreferredSize(
preferredSize: const Size.fromHeight(48),
child: TabBar(
controller: _tabCtrl,
indicatorColor: AppColors.textPrimary,
labelColor: AppColors.textPrimary,
unselectedLabelColor: AppColors.textHint,
labelStyle: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
),
tabs: _tabs.map((t) => Tab(child: Text(t))).toList(),
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => pushRoute(ref, 'medicationEdit'),
backgroundColor: AppColors.primaryDark,
backgroundColor: _medBlue,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: const Icon(Icons.add, size: 28, color: Colors.white),
),
body: AppFutureView<List<Map<String, dynamic>>>(
future: _future,
onRetry: _load,
errorTitle: '用药信息加载失败',
onData: (ctx, list) {
if (list.isEmpty) {
return const AppEmptyState(
future: _future,
onRetry: _load,
errorTitle: '用药信息加载失败',
onData: (ctx, list) {
if (list.isEmpty) {
return const AppEmptyState(
icon: Icons.medication_outlined,
title: '暂无用药',
subtitle: '点击右下角➕添加药品',
);
}
final activeCount = list.where((m) => m['isActive'] == true).length;
final reminderCount = list.where((m) {
final times = m['timeOfDay'] as List?;
return times != null && times.isNotEmpty;
}).length;
return ListView(
padding: const EdgeInsets.fromLTRB(16, 12, 16, 88),
children: [
EnterpriseHeader(
title: '用药管理',
subtitle: '集中管理服药计划、剂量和每日打卡状态',
icon: Icons.medication_outlined,
title: '暂无用药',
subtitle: '点击右下角➕添加药品',
);
}
return ListView.builder(
padding: const EdgeInsets.fromLTRB(0, 8, 0, 80),
itemCount: list.length,
itemBuilder: (ctx, i) {
color: _medBlue,
accent: _medCyan,
stats: [
EnterpriseStat(
label: '药品总数',
value: '${list.length}',
icon: Icons.inventory_2_outlined,
),
EnterpriseStat(
label: '服用中',
value: '$activeCount',
icon: Icons.check_circle_outline,
),
EnterpriseStat(
label: '有提醒',
value: '$reminderCount',
icon: Icons.alarm_outlined,
),
],
),
const SizedBox(height: 10),
...List.generate(list.length, (i) {
final m = list[i];
final times =
(m['timeOfDay'] as List?)
@@ -112,22 +112,14 @@ class _MedicationListPageState extends ConsumerState<MedicationListPage>
key: Key(id),
onDelete: () => _delete(id),
onTap: () => pushRoute(ref, 'medCheckIn', params: {'id': id}),
margin: const EdgeInsets.symmetric(
horizontal: AppTheme.sLg,
vertical: 4,
),
margin: const EdgeInsets.symmetric(vertical: 5),
child: Container(
padding: const EdgeInsets.all(AppTheme.sLg),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(AppTheme.rLg),
boxShadow: [
BoxShadow(
color: const Color(0xFF101828).withValues(alpha: 0.08),
blurRadius: 18,
offset: const Offset(0, 6),
),
],
borderRadius: BorderRadius.circular(16),
border: Border.all(color: AppColors.border, width: 1.1),
boxShadow: [AppTheme.shadowLight],
),
child: Row(
children: [
@@ -136,9 +128,13 @@ class _MedicationListPageState extends ConsumerState<MedicationListPage>
height: 44,
decoration: BoxDecoration(
gradient: isActive
? AppColors.primaryGradient
? const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [_medBlue, _medCyan],
)
: AppColors.surfaceGradient,
borderRadius: BorderRadius.circular(AppTheme.rMd),
borderRadius: BorderRadius.circular(14),
border: Border.all(color: AppColors.borderLight),
),
child: Icon(
@@ -172,14 +168,16 @@ class _MedicationListPageState extends ConsumerState<MedicationListPage>
vertical: 1,
),
decoration: BoxDecoration(
color: AppTheme.bgSoft,
borderRadius: BorderRadius.circular(4),
color: _medBlue.withValues(alpha: 0.08),
borderRadius: BorderRadius.circular(
999,
),
),
child: Text(
'已停',
style: TextStyle(
fontSize: 13,
color: AppTheme.textSub,
color: _medBlue,
),
),
),
@@ -200,16 +198,17 @@ class _MedicationListPageState extends ConsumerState<MedicationListPage>
Icon(
Icons.chevron_right,
size: 21,
color: AppTheme.border,
color: AppColors.textHint,
),
],
),
),
);
},
);
},
),
}),
],
);
},
),
);
}
}