import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../core/theme/app_colors.dart'; import '../shell/nav_bar_config.dart'; import '../../core/utils/responsive.dart'; import '../../shared/widgets/form_section.dart'; import '../../shared/widgets/form_field_row.dart'; import '../../core/i18n/app_localizations.dart'; import '../../shared/widgets/action_bar.dart'; class ExpenseApplicationApplyPage extends ConsumerStatefulWidget { final String? id; const ExpenseApplicationApplyPage({super.key, this.id}); @override ConsumerState createState() => _ExpenseApplicationApplyPageState(); } class _ExpenseApplicationApplyPageState extends ConsumerState { final _formKey = GlobalKey(); String _expenseType = '差旅费'; String _urgency = 'normal'; final _amountController = TextEditingController(); final _purposeController = TextEditingController(); final _remarkController = TextEditingController(); static const _types = ['差旅费', '办公用品', '招待费', '交通费', '通讯费', '其他']; @override void dispose() { _amountController.dispose(); _purposeController.dispose(); _remarkController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final r = ResponsiveHelper.of(context); final l10n = AppLocalizations.of(context); ref .read(navBarConfigProvider.notifier) .update( NavBarConfig( title: l10n.get('expenseApplyRequest'), showBack: true, onBack: () => context.pop(), ), ); return Column( children: [ Expanded( child: Align( alignment: Alignment.topCenter, child: ConstrainedBox( constraints: BoxConstraints(maxWidth: r.formMaxWidth), child: SingleChildScrollView( padding: const EdgeInsets.all(16), child: Form( key: _formKey, child: Column( children: [ _buildBasicInfoSection(), const SizedBox(height: 16), _buildControlSection(), const SizedBox(height: 16), _buildExpenseDetailSection(), const SizedBox(height: 16), _buildAttachmentSection(), ], ), ), ), ), ), ), ActionBar( leftLabel: '重置', centerLabel: '存为草稿', rightLabel: '提交审批', onLeftTap: () { setState(() { _expenseType = '差旅费'; _urgency = 'normal'; _amountController.clear(); _purposeController.clear(); _remarkController.clear(); }); }, onCenterTap: _handleSaveDraft, onRightTap: _handleSubmit, showLeft: true, ), ], ); } Widget _buildBasicInfoSection() { return FormSection( title: '基本信息', children: [ FormFieldRow( label: '申请人', value: '张三', readOnly: true, showArrow: false, ), FormFieldRow( label: '所属部门', value: '技术部', readOnly: true, showArrow: false, ), FormFieldRow( label: '申请日期', value: '2026-06-01', readOnly: true, showArrow: false, ), const SizedBox(height: 8), Container(height: 1, color: AppColors.border), const SizedBox(height: 8), const Text( '紧急程度', style: TextStyle( fontSize: AppFontSizes.body, color: AppColors.textSecondary, ), ), const SizedBox(height: 4), Row( children: [ _buildRadio('普通', 'normal'), const SizedBox(width: 24), _buildRadio('紧急', 'urgent'), ], ), FormFieldRow( label: '费用类型', value: _expenseType, onTap: _showTypePicker, ), const SizedBox(height: 8), const Text( '费用事由', style: TextStyle( fontSize: AppFontSizes.body, color: AppColors.textSecondary, ), ), const SizedBox(height: 4), Container( height: 88, padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: AppColors.bgPage, borderRadius: BorderRadius.circular(4), ), child: TextField( controller: _purposeController, maxLines: 3, style: const TextStyle( fontSize: AppFontSizes.body, color: AppColors.textPrimary, ), decoration: const InputDecoration.collapsed(hintText: '请输入费用事由'), ), ), ], ); } Widget _buildControlSection() { return FormSection( title: '关联管控', children: [ FormFieldRow(label: '关联项目', hint: '请选择项目'), FormFieldRow(label: '预算科目', hint: '请选择科目'), _buildBudgetRow(), ], ); } Widget _buildBudgetRow() { return Container( height: 44, padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( '可用预算余额', style: TextStyle( fontSize: AppFontSizes.body, color: AppColors.textSecondary, ), ), const Text( '¥50,000.00', style: TextStyle( fontSize: AppFontSizes.subtitle, fontWeight: FontWeight.w700, color: AppColors.amountPrimary, ), ), ], ), ); } Widget _buildExpenseDetailSection() { final amount = double.tryParse(_amountController.text) ?? 0.0; return FormSection( title: '费用明细', showAction: true, actionText: '添加', onActionTap: () { ScaffoldMessenger.of( context, ).showSnackBar(const SnackBar(content: Text('请在明细中添加费用项'))); }, children: [ if (amount > 0) Container( padding: const EdgeInsets.symmetric(vertical: 8), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Icon( Icons.receipt_long, size: 14, color: AppColors.textSecondary, ), const SizedBox(width: 8), const Text( '费用项', style: TextStyle( fontSize: AppFontSizes.body, color: AppColors.textPrimary, ), ), ], ), Text( '¥${amount.toStringAsFixed(2)}', style: const TextStyle( fontSize: AppFontSizes.body, fontWeight: FontWeight.w500, color: AppColors.amountPrimary, ), ), ], ), ) else const Padding( padding: EdgeInsets.symmetric(vertical: 8), child: Text( '暂无明细,点击上方添加', style: TextStyle( fontSize: AppFontSizes.body, color: AppColors.textPlaceholder, ), ), ), Container(height: 1, color: AppColors.border), Container( height: 36, padding: const EdgeInsets.symmetric(vertical: 8), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( '合计', style: TextStyle( fontSize: AppFontSizes.body, fontWeight: FontWeight.w600, color: AppColors.textPrimary, ), ), Text( '¥${amount.toStringAsFixed(2)}', style: const TextStyle( fontSize: AppFontSizes.subtitle, fontWeight: FontWeight.w700, color: AppColors.amountPrimary, ), ), ], ), ), // Budget warning placeholder Row( children: [ Icon(Icons.info_outline, size: 14, color: AppColors.warning), const SizedBox(width: 6), const Text( '超出预算¥0.00', style: TextStyle( fontSize: AppFontSizes.caption, color: AppColors.warning, ), ), ], ), ], ); } Widget _buildAttachmentSection() { return FormSection( title: '附件上传', children: [ const Text( '最多上传6张图片或PDF文件', style: TextStyle( fontSize: AppFontSizes.caption, color: AppColors.textPlaceholder, ), ), const SizedBox(height: 8), _buildUploadGrid(), ], ); } Widget _buildUploadGrid() { return Wrap( spacing: 8, runSpacing: 8, children: List.generate(6, (i) { return Container( width: 80, height: 80, decoration: BoxDecoration( color: AppColors.bgPage, borderRadius: BorderRadius.circular(4), border: Border.all( color: AppColors.border, width: 1, strokeAlign: BorderSide.strokeAlignInside, ), ), child: i == 0 ? const Center( child: Icon( Icons.add, size: 24, color: AppColors.textPlaceholder, ), ) : const SizedBox.shrink(), ); }), ); } Widget _buildRadio(String label, String value) { final isSelected = _urgency == value; return GestureDetector( onTap: () { setState(() => _urgency = value); }, child: Row( mainAxisSize: MainAxisSize.min, children: [ Container( width: 18, height: 18, decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all( color: isSelected ? AppColors.primary : AppColors.textPlaceholder, width: 2, ), ), child: isSelected ? Center( child: Container( width: 8, height: 8, decoration: const BoxDecoration( shape: BoxShape.circle, color: AppColors.primary, ), ), ) : null, ), const SizedBox(width: 6), Text( label, style: TextStyle( fontSize: AppFontSizes.body, color: isSelected ? AppColors.primary : AppColors.textSecondary, ), ), ], ), ); } void _showTypePicker() { showModalBottomSheet( context: context, builder: (_) => Column( mainAxisSize: MainAxisSize.min, children: _types .map( (t) => ListTile( title: Text(t), onTap: () { setState(() => _expenseType = t); Navigator.pop(context); }, ), ) .toList(), ), ); } void _handleSaveDraft() { if (!_formKey.currentState!.validate()) return; ScaffoldMessenger.of( context, ).showSnackBar(const SnackBar(content: Text('草稿已保存'))); context.pop(); } void _handleSubmit() { if (!_formKey.currentState!.validate()) return; ScaffoldMessenger.of( context, ).showSnackBar(const SnackBar(content: Text('提交成功'))); context.pop(); } }