import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:file_picker/file_picker.dart'; import 'dart:io'; import '../../../core/localization/app_localizations.dart'; import '../../../core/theme/app_theme.dart'; import '../../../shared/services/toast_service.dart'; import '../../../shared/widgets/common_button.dart'; import '../models/program.dart'; import '../providers/programs_provider.dart'; import '../widgets/program_form_dialog.dart'; import '../services/excel_import_service.dart'; import '../services/excel_template_service.dart'; /// 程序管理页面 class ProgramsPage extends ConsumerStatefulWidget { const ProgramsPage({super.key}); @override ConsumerState createState() => _ProgramsPageState(); } class _ProgramsPageState extends ConsumerState { final Set _selectedIds = {}; @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); final programsState = ref.watch(programsProvider); return Scaffold( body: Container( color: AppTheme.backgroundColor, child: Column( children: [ // 顶部导航栏 Container( height: 60, padding: const EdgeInsets.symmetric(horizontal: 24), decoration: BoxDecoration( color: Colors.white, boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.1), blurRadius: 4, offset: const Offset(0, 2), ), ], ), child: Row( children: [ Text( l10n?.programs ?? '程序管理', style: const TextStyle( fontSize: 20, fontWeight: FontWeight.w600, ), ), const Spacer(), // 新增按钮 CommonButton( text: l10n?.addProgram ?? '新增', icon: Icons.add, type: ButtonType.primary, onPressed: () => _showAddDialog(context, ref), ), const SizedBox(width: 12), // 下载模板按钮 CommonButton( text: l10n?.downloadTemplate ?? '下载模板', icon: Icons.file_download, type: ButtonType.secondary, onPressed: () => _downloadTemplate(context), ), const SizedBox(width: 12), // 导入按钮 CommonButton( text: l10n?.importProgram ?? '导入', icon: Icons.file_upload, type: ButtonType.secondary, onPressed: () => _importPrograms(context, ref), ), ], ), ), // 程序列表表格 Expanded( child: programsState.isLoading ? const Center(child: CircularProgressIndicator()) : programsState.programs.isEmpty ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.folder_open, size: 64, color: AppTheme.idleColor, ), const SizedBox(height: 16), Text( l10n?.noData ?? '暂无数据', style: TextStyle( color: AppTheme.textSecondary, fontSize: 16, ), ), ], ), ) : Container( margin: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.1), blurRadius: 8, offset: const Offset(2, 2), ), ], ), child: Column( children: [ // 表头 _buildTableHeader(l10n, programsState.programs), // 表格内容 Expanded( child: ListView.builder( itemCount: programsState.programs.length, itemBuilder: (context, index) { final program = programsState.programs[index]; final isSelected = _selectedIds.contains(program.id); return _buildTableRow( context, ref, l10n, program, isSelected, index == programsState.programs.length - 1, ); }, ), ), ], ), ), ), // 底部操作栏 if (programsState.programs.isNotEmpty) Container( height: 60, padding: const EdgeInsets.symmetric(horizontal: 24), decoration: BoxDecoration( color: Colors.white, boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.1), blurRadius: 4, offset: const Offset(0, -2), ), ], ), child: Row( children: [ Text( '${l10n?.selected ?? '已选择'}: ${_selectedIds.length}', style: TextStyle(color: AppTheme.textSecondary), ), const Spacer(), if (_selectedIds.isNotEmpty) CommonButton( text: l10n?.deleteProgram ?? '删除', icon: Icons.delete, type: ButtonType.danger, onPressed: () => _showDeleteConfirmDialog( context, ref, l10n, _selectedIds.toList(), ), ), ], ), ), ], ), ), ); } /// 表头 Widget _buildTableHeader(AppLocalizations? l10n, List programs) { final allSelected = _selectedIds.length == programs.length && programs.isNotEmpty; return Container( height: 50, padding: const EdgeInsets.symmetric(horizontal: 16), decoration: BoxDecoration( color: AppTheme.primaryColor.withValues(alpha: 0.1), borderRadius: const BorderRadius.vertical(top: Radius.circular(12)), ), child: Row( children: [ // 复选框 SizedBox( width: 50, child: Checkbox( value: allSelected, onChanged: (value) { setState(() { if (value == true) { _selectedIds.clear(); _selectedIds.addAll(programs.map((p) => p.id!)); } else { _selectedIds.clear(); } }); }, ), ), // 编号 SizedBox( width: 100, child: Text( l10n?.programCode ?? '编号', style: TextStyle( fontWeight: FontWeight.w600, color: AppTheme.textPrimary, ), ), ), // 名称 Expanded( flex: 2, child: Text( l10n?.programName ?? '名称', style: TextStyle( fontWeight: FontWeight.w600, color: AppTheme.textPrimary, ), ), ), // 创建时间 Expanded( child: Text( l10n?.createTime ?? '创建时间', style: TextStyle( fontWeight: FontWeight.w600, color: AppTheme.textPrimary, ), ), ), // 状态 SizedBox( width: 80, child: Text( l10n?.status ?? '状态', style: TextStyle( fontWeight: FontWeight.w600, color: AppTheme.textPrimary, ), ), ), // 操作 SizedBox( width: 150, child: Text( l10n?.detail ?? '操作', style: TextStyle( fontWeight: FontWeight.w600, color: AppTheme.textPrimary, ), textAlign: TextAlign.center, ), ), ], ), ); } /// 表格行 Widget _buildTableRow( BuildContext context, WidgetRef ref, AppLocalizations? l10n, Program program, bool isSelected, bool isLast, ) { return Container( height: 50, padding: const EdgeInsets.symmetric(horizontal: 16), decoration: BoxDecoration( color: isSelected ? AppTheme.primaryLight.withValues(alpha: 0.2) : null, border: isLast ? null : Border( bottom: BorderSide( color: AppTheme.idleColor.withValues(alpha: 0.2), ), ), ), child: Row( children: [ // 复选框 SizedBox( width: 50, child: Checkbox( value: isSelected, onChanged: (value) { setState(() { if (value == true) { _selectedIds.add(program.id!); } else { _selectedIds.remove(program.id!); } }); }, ), ), // 编号 SizedBox( width: 100, child: Text( program.code, style: TextStyle(color: AppTheme.textPrimary), ), ), // 名称 Expanded( flex: 2, child: Text( program.name, style: TextStyle(color: AppTheme.textPrimary), ), ), // 创建时间 Expanded( child: Text( program.createdAt, style: TextStyle(color: AppTheme.textSecondary), ), ), // 状态 SizedBox( width: 80, child: Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: program.status == 1 ? AppTheme.successColor.withValues(alpha: 0.1) : AppTheme.idleColor.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(4), ), child: Text( program.status == 1 ? (l10n?.enabled ?? '启用') : (l10n?.disabled ?? '停用'), style: TextStyle( color: program.status == 1 ? AppTheme.successColor : AppTheme.idleColor, fontSize: 12, ), textAlign: TextAlign.center, ), ), ), // 操作按钮 SizedBox( width: 150, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( icon: const Icon(Icons.edit, size: 20), color: AppTheme.primaryColor, onPressed: () => _showEditDialog(context, ref, program), ), IconButton( icon: const Icon(Icons.delete, size: 20), color: AppTheme.errorColor, onPressed: () => _showDeleteConfirmDialog( context, ref, l10n, [program.id!], ), ), IconButton( icon: const Icon(Icons.visibility, size: 20), color: AppTheme.textSecondary, onPressed: () => context.go('/programs/${program.id}'), ), ], ), ), ], ), ); } /// 显示新增对话框 void _showAddDialog(BuildContext context, WidgetRef ref) { showDialog( context: context, builder: (context) => const ProgramFormDialog(), ); } /// 导入程序 Future _importPrograms(BuildContext context, WidgetRef ref) async { final l10n = AppLocalizations.of(context); try { // 选择文件 final result = await FilePicker.platform.pickFiles( type: FileType.custom, allowedExtensions: ['xlsx'], allowMultiple: false, ); if (result == null || result.files.isEmpty) { return; } final file = result.files.first; if (file.path == null) { if (!context.mounted) return; ToastService.showError(context, l10n?.cannotReadFile ?? '无法读取文件'); return; } // 解析并写入数据库 final importedCount = await ExcelImportService.instance.importFromExcel(File(file.path!)); // 刷新程序列表 ref.read(programsProvider.notifier).loadPrograms(); if (!context.mounted) return; if (importedCount > 0) { ToastService.showSuccess(context, l10n?.processedPrograms(importedCount) ?? '成功处理 $importedCount 个程序'); } else { ToastService.showWarning(context, l10n?.noValidProgramData ?? 'Excel 中无有效程序数据'); } } catch (e) { if (!context.mounted) return; ToastService.showError(context, '${l10n?.importFailed ?? '导入失败'}: ${e.toString()}'); } } /// 下载 Excel 模板 Future _downloadTemplate(BuildContext context) async { final l10n = AppLocalizations.of(context); try { final path = await ExcelTemplateService.instance.generateTemplate(); if (!context.mounted) return; ToastService.showSuccess(context, '${l10n?.templateSaved ?? '模板已保存'}: $path'); } catch (e) { if (!context.mounted) return; ToastService.showError(context, '${l10n?.generateTemplateFailed ?? '生成模板失败'}: ${e.toString()}'); } } /// 显示编辑对话框 void _showEditDialog(BuildContext context, WidgetRef ref, Program program) { showDialog( context: context, builder: (context) => ProgramFormDialog(program: program), ); } /// 显示删除确认对话框 void _showDeleteConfirmDialog( BuildContext context, WidgetRef ref, AppLocalizations? l10n, List ids, ) { showDialog( context: context, builder: (context) => AlertDialog( title: Text(l10n?.confirm ?? '确认'), content: Text( ids.length == 1 ? (l10n?.deleteConfirmSingle ?? '确定要删除此程序吗?') : (l10n?.deleteConfirmMultiple(ids.length) ?? '确定要删除选中的 ${ids.length} 个程序吗?'), ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: Text(l10n?.cancel ?? '取消'), ), ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: AppTheme.errorColor, foregroundColor: Colors.white, ), onPressed: () async { final notifier = ref.read(programsProvider.notifier); await notifier.deletePrograms(ids); setState(() { _selectedIds.removeAll(ids); }); Navigator.of(context).pop(); }, child: Text(l10n?.confirm ?? '确认'), ), ], ), ); } }