import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../core/app_colors.dart'; import '../../providers/data_providers.dart' show adminServiceProvider; import '../../utils/backoffice_formatters.dart'; import '../../widgets/backoffice_ui.dart'; class AdminPatientsPage extends ConsumerStatefulWidget { const AdminPatientsPage({super.key}); @override ConsumerState createState() => _AdminPatientsPageState(); } class _AdminPatientsPageState extends ConsumerState { final _searchCtrl = TextEditingController(); List> _patients = []; int _total = 0; int _page = 1; bool _loading = true; bool _loadingMore = false; String? _error; @override void initState() { super.initState(); _load(); } @override void dispose() { _searchCtrl.dispose(); super.dispose(); } Future _load({bool reset = true}) async { if (reset) { _page = 1; setState(() { _loading = true; _patients = []; _error = null; }); } try { final res = await ref .read(adminServiceProvider) .getPatients(search: _searchCtrl.text.trim(), page: _page); if (!mounted) return; if (res['code'] == 0) { final data = res['data'] as Map? ?? {}; setState(() { _total = data['total'] ?? 0; final list = List>.from(data['patients'] ?? []); if (reset) { _patients = list; } else { _patients.addAll(list); } _loading = false; _loadingMore = false; }); } else { setState(() { _loading = false; _loadingMore = false; _error = res['message']?.toString() ?? '患者列表加载失败'; }); } } catch (e) { if (mounted) { setState(() { _loading = false; _loadingMore = false; _error = '患者列表加载失败'; }); } } } void _loadMore() { if (!_loadingMore && _patients.length < _total) { setState(() { _page++; _loadingMore = true; }); _load(reset: false); } } @override Widget build(BuildContext context) { return Column( children: [ // 搜索栏 Padding( padding: const EdgeInsets.all(16), child: TextField( controller: _searchCtrl, decoration: InputDecoration( hintText: '搜索患者姓名或手机号', prefixIcon: const Icon(Icons.search, color: AppColors.textHint), suffixIcon: _searchCtrl.text.isNotEmpty ? IconButton( icon: const Icon(Icons.clear), onPressed: () { _searchCtrl.clear(); _load(); }, ) : null, filled: true, fillColor: Colors.white, border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: AppColors.border), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: AppColors.border), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: AppColors.primary), ), contentPadding: const EdgeInsets.symmetric( horizontal: 16, vertical: 12, ), ), onSubmitted: (_) => _load(), onChanged: (v) => setState(() {}), ), ), // 患者列表 Expanded( child: _loading ? const BackofficeLoadingState(message: '正在加载患者') : _error != null ? BackofficeErrorState(message: _error!, onRetry: _load) : _patients.isEmpty ? const BackofficeEmptyState( icon: Icons.people_outline, title: '暂无患者', description: '注册患者会显示在这里', ) : NotificationListener( onNotification: (n) { if (n is ScrollEndNotification && n.metrics.pixels >= n.metrics.maxScrollExtent - 50) { _loadMore(); } return false; }, child: ListView.builder( padding: const EdgeInsets.symmetric(horizontal: 16), itemCount: _patients.length + (_loadingMore ? 1 : 0), itemBuilder: (_, i) { if (i >= _patients.length) { return const Center( child: Padding( padding: EdgeInsets.all(16), child: CircularProgressIndicator(), ), ); } final p = _patients[i]; return BackofficeSectionCard( margin: const EdgeInsets.only(bottom: 8), padding: EdgeInsets.zero, child: ListTile( leading: CircleAvatar( backgroundColor: AppColors.iconBg, child: Text( backofficeInitial(p['name']), style: const TextStyle(color: AppColors.primary), ), ), title: Text( p['name'] ?? '', style: const TextStyle(fontWeight: FontWeight.w500), ), subtitle: Text( p['phone'] ?? '', style: const TextStyle( fontSize: 13, color: AppColors.textSecondary, ), ), trailing: p['doctorName'] != null ? Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 4, ), decoration: BoxDecoration( color: AppColors.successLight, borderRadius: BorderRadius.circular(6), ), child: Text( p['doctorName']!, style: const TextStyle( fontSize: 12, color: AppColors.success, ), ), ) : null, ), ); }, ), ), ), ], ); } }