Initial commit: 健康管家 AI 健康陪伴助手
- Backend: .NET 10 Minimal API + EF Core + PostgreSQL - Frontend: Flutter + Riverpod + GoRouter + Dio - AI: DeepSeek LLM + Qwen VLM (OpenAI-compatible) - Auth: SMS + JWT (access/refresh tokens) - Features: AI chat, health tracking, medication management, diet analysis, exercise plans, doctor consultations, report analysis
This commit is contained in:
80
health_app/lib/pages/medication/medication_list_page.dart
Normal file
80
health_app/lib/pages/medication/medication_list_page.dart
Normal file
@@ -0,0 +1,80 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import '../../providers/data_providers.dart';
|
||||
|
||||
/// 用药列表页
|
||||
class MedicationListPage extends ConsumerWidget {
|
||||
const MedicationListPage({super.key});
|
||||
@override Widget build(BuildContext context, WidgetRef ref) {
|
||||
final meds = ref.watch(medicationListProvider);
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('我的用药')),
|
||||
body: meds.when(
|
||||
data: (list) {
|
||||
if (list.isEmpty) return _empty(context);
|
||||
return ListView.builder(
|
||||
itemCount: list.length,
|
||||
itemBuilder: (ctx, i) {
|
||||
final m = list[i];
|
||||
final times = (m['timeOfDay'] as List?)?.cast<String>() ?? [];
|
||||
return Card(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||
child: ListTile(
|
||||
leading: const Icon(Icons.medication, color: Color(0xFF635BFF)),
|
||||
title: Text('${m['name']} ${m['dosage'] ?? ''}', style: const TextStyle(fontSize: 16)),
|
||||
subtitle: Text('每天 ${times.join("、")}', style: const TextStyle(fontSize: 14, color: Color(0xFF666666))),
|
||||
trailing: IconButton(icon: const Icon(Icons.check_circle_outline, color: Color(0xFF43A047)), onPressed: () async {
|
||||
await ref.read(medicationServiceProvider).confirm(m['id']);
|
||||
ref.invalidate(medicationListProvider);
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error: (_, _) => _empty(context),
|
||||
),
|
||||
floatingActionButton: FloatingActionButton.extended(
|
||||
onPressed: () => context.push('/medications/add').then((_) => ref.invalidate(medicationListProvider)),
|
||||
icon: const Icon(Icons.add), label: const Text('添加药品'),
|
||||
),
|
||||
);
|
||||
}
|
||||
Widget _empty(BuildContext context) => Center(child: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
Icon(Icons.medication, size: 64, color: Colors.grey[300]),
|
||||
const SizedBox(height: 12), Text('暂无用药计划', style: Theme.of(context).textTheme.bodyMedium),
|
||||
const SizedBox(height: 8), Text('可通过 AI 对话或手动添加', style: Theme.of(context).textTheme.labelMedium),
|
||||
]));
|
||||
}
|
||||
|
||||
/// 编辑用药页
|
||||
class MedicationEditPage extends ConsumerStatefulWidget {
|
||||
final String? id;
|
||||
const MedicationEditPage({super.key, this.id});
|
||||
@override ConsumerState<MedicationEditPage> createState() => _MedicationEditPageState();
|
||||
}
|
||||
class _MedicationEditPageState extends ConsumerState<MedicationEditPage> {
|
||||
final _nameCtrl = TextEditingController(); final _dosageCtrl = TextEditingController(); final _timeCtrl = TextEditingController();
|
||||
@override void dispose() { _nameCtrl.dispose(); _dosageCtrl.dispose(); _timeCtrl.dispose(); super.dispose(); }
|
||||
|
||||
Future<void> _save() async {
|
||||
await ref.read(medicationServiceProvider).create({
|
||||
'name': _nameCtrl.text, 'dosage': _dosageCtrl.text,
|
||||
'frequency': 'Daily', 'timeOfDay': [if (_timeCtrl.text.isNotEmpty) _timeCtrl.text],
|
||||
'source': 'Manual', 'startDate': DateTime.now().toIso8601String().substring(0, 10),
|
||||
});
|
||||
if (mounted) context.pop();
|
||||
}
|
||||
|
||||
@override Widget build(BuildContext context) => Scaffold(
|
||||
appBar: AppBar(title: const Text('添加药品')),
|
||||
body: ListView(padding: const EdgeInsets.all(16), children: [
|
||||
TextField(controller: _nameCtrl, decoration: const InputDecoration(labelText: '药品名称', hintText: '如:阿司匹林')),
|
||||
const SizedBox(height: 16), TextField(controller: _dosageCtrl, decoration: const InputDecoration(labelText: '剂量', hintText: '如:100mg')),
|
||||
const SizedBox(height: 16), TextField(controller: _timeCtrl, decoration: const InputDecoration(labelText: '服药时间', hintText: '如:08:00:00')),
|
||||
const SizedBox(height: 32), SizedBox(width: double.infinity, height: 48, child: ElevatedButton(onPressed: _save, child: const Text('保存'))),
|
||||
]),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user