import 'package:aioa_mobile/features/form/application/leave_attachment_controller.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; class LeaveAttachmentSheet extends ConsumerStatefulWidget { const LeaveAttachmentSheet({required this.leaveRequestId, super.key}); final String leaveRequestId; @override ConsumerState createState() => _LeaveAttachmentSheetState(); } class _LeaveAttachmentSheetState extends ConsumerState { @override void initState() { super.initState(); Future.microtask( () => ref .read(leaveAttachmentProvider(widget.leaveRequestId).notifier) .load(), ); } @override Widget build(BuildContext context) { final state = ref.watch(leaveAttachmentProvider(widget.leaveRequestId)); return SafeArea( child: Padding( padding: EdgeInsets.fromLTRB( 20, 0, 20, 20 + MediaQuery.viewInsetsOf(context).bottom, ), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text( '添加附件', style: Theme.of( context, ).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.w700), ), const SizedBox(height: 6), const Text('支持 JPEG、PNG、PDF,单个文件不超过 10 MB。文件将直接上传至对象存储。'), if (state.error != null) ...[ const SizedBox(height: 10), Text( state.error!, style: TextStyle(color: Theme.of(context).colorScheme.error), ), ], if (state.uploading) ...[ const SizedBox(height: 14), LinearProgressIndicator(value: state.progress), const SizedBox(height: 6), Text('正在上传 ${(state.progress * 100).round()}%'), ], if (state.items.isNotEmpty) ...[ const SizedBox(height: 14), for (final item in state.items) ListTile( contentPadding: EdgeInsets.zero, leading: Icon( item.contentType == 'application/pdf' ? Icons.picture_as_pdf_outlined : Icons.image_outlined, ), title: Text( item.fileName, maxLines: 1, overflow: TextOverflow.ellipsis, ), subtitle: Text( '${_formatBytes(item.sizeBytes)} · ${item.status == 'READY' ? '已上传' : '处理中'}', ), trailing: item.status == 'READY' ? const Icon(Icons.check_circle, color: Colors.green) : null, ), ], const SizedBox(height: 14), OutlinedButton.icon( onPressed: state.uploading ? null : _pickAndUpload, icon: const Icon(Icons.attach_file), label: const Padding( padding: EdgeInsets.symmetric(vertical: 12), child: Text('选择附件'), ), ), const SizedBox(height: 8), FilledButton( onPressed: state.uploading ? null : () => Navigator.pop(context), child: const Text('完成'), ), ], ), ), ); } Future _pickAndUpload() async { final result = await FilePicker.platform.pickFiles( type: FileType.custom, allowedExtensions: const ['jpg', 'jpeg', 'png', 'pdf'], withData: true, ); if (result == null || !mounted) return; final file = result.files.single; final bytes = file.bytes; if (bytes == null) { ScaffoldMessenger.of( context, ).showSnackBar(const SnackBar(content: Text('无法读取所选文件'))); return; } final contentType = switch (file.extension?.toLowerCase()) { 'jpg' || 'jpeg' => 'image/jpeg', 'png' => 'image/png', 'pdf' => 'application/pdf', _ => null, }; if (contentType == null) return; await ref .read(leaveAttachmentProvider(widget.leaveRequestId).notifier) .upload(fileName: file.name, contentType: contentType, bytes: bytes); } String _formatBytes(int bytes) { if (bytes < 1024) return '$bytes B'; if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)} KB'; return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB'; } }