import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:tdesign_flutter/tdesign_flutter.dart'; import 'package:go_router/go_router.dart'; import '../../core/theme/app_colors.dart'; import '../../core/utils/responsive.dart'; import '../../shared/widgets/form_section.dart'; import '../../shared/widgets/form_field_row.dart'; import 'expense_apply_controller.dart'; import 'expense_model.dart'; class ExpenseApplyPage extends ConsumerStatefulWidget { final String? editId; const ExpenseApplyPage({super.key, this.editId}); @override ConsumerState createState() => _ExpenseApplyPageState(); } class _ExpenseApplyPageState extends ConsumerState { final _remarkController = TextEditingController(); static const _types = ['差旅费', '办公用品', '招待费', '交通费', '通讯费', '其他']; @override void dispose() { _remarkController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final controller = ref.watch(expenseApplyProvider(widget.editId).notifier); final state = ref.watch(expenseApplyProvider(widget.editId)); final r = ResponsiveHelper.of(context); return Scaffold( appBar: TDNavBar( title: widget.editId != null ? '编辑报销' : '费用报销申请', titleColor: Colors.white, backgroundColor: const Color(0xFF00ABF3), centerTitle: false, ), body: Column( children: [ Expanded( child: Align(alignment: Alignment.topCenter, child: ConstrainedBox( constraints: BoxConstraints(maxWidth: r.formMaxWidth), child: SingleChildScrollView( padding: const EdgeInsets.symmetric(vertical: 8), child: _buildForm(controller, state), ), ), ), ), _buildBottomButtons(controller, state), ], ), ); } Widget _buildForm( ExpenseApplyController controller, ExpenseApplyState state) { return Column( children: [ FormSection( title: '基本信息', children: [ FormFieldRow( label: '报销类型', value: state.expense.expenseType, onTap: () => _showTypePicker(controller), ), FormFieldRow( label: '报销金额', value: '¥${state.expense.totalAmount.toStringAsFixed(2)}', showArrow: false, ), FormFieldRow( label: '备注说明', value: state.expense.remark.isEmpty ? null : state.expense.remark, hint: '选填', onTap: () => _showRemarkEditor(), ), ], ), FormSection( title: '报销明细', trailing: TextButton( onPressed: () => _showAddDetailDialog(controller), child: const Text('+ 添加明细', style: TextStyle(fontSize: 12)), ), children: state.expense.details.isEmpty ? [ const Padding( padding: EdgeInsets.all(14), child: Text('暂无明细,点击上方添加', style: TextStyle( color: AppColors.textHint, fontSize: 13)), ), ] : state.expense.details.asMap().entries.map((entry) { final d = entry.value; return ListTile( title: Text(d.expenseDesc, style: const TextStyle( fontSize: 13, color: AppColors.textPrimary)), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ Text( '¥${d.totalAmount.toStringAsFixed(2)}', style: const TextStyle( color: AppColors.primary, fontSize: 13)), IconButton( icon: const Icon(Icons.close, size: 16, color: AppColors.textHint), onPressed: () { controller.removeDetail(entry.key); controller.recalculateAmount(); }, ), ], ), ); }).toList(), ), const SizedBox(height: 16), ], ); } Widget _buildBottomButtons( ExpenseApplyController controller, ExpenseApplyState state) { return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.white, boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.05), blurRadius: 4, offset: const Offset(0, -1), ), ], ), child: Row(children: [ Expanded( child: TDButton( text: '存草稿', size: TDButtonSize.large, type: TDButtonType.outline, theme: TDButtonTheme.defaultTheme, isBlock: true, disabled: state.isSubmitting, onTap: state.isSubmitting ? null : () async { await controller.saveDraft(); if (context.mounted) context.pop(); }, ), ), const SizedBox(width: 12), Expanded( flex: 2, child: TDButton( text: '提交审批', size: TDButtonSize.large, type: TDButtonType.fill, theme: TDButtonTheme.primary, isBlock: true, disabled: state.isSubmitting, onTap: state.isSubmitting ? null : () async { final ok = await controller.submit(); if (context.mounted && ok) context.pop(); }, ), ), ]), ); } void _showTypePicker(ExpenseApplyController controller) { TDPicker.showMultiPicker( context, title: '选择报销类型', data: [_types], onConfirm: (selected) { controller.updateType(selected.first); }, ); } void _showRemarkEditor() { final state = ref.read(expenseApplyProvider(widget.editId)); _remarkController.text = state.expense.remark; showDialog( context: context, builder: (_) => TDAlertDialog( title: '备注说明', contentWidget: TDInput( controller: _remarkController, maxLines: 3, hintText: '请输入备注信息…', ), leftBtn: TDDialogButtonOptions(title: '取消', action: () => Navigator.pop(context)), rightBtn: TDDialogButtonOptions(title: '确定', action: () { ref.read(expenseApplyProvider(widget.editId).notifier).updateRemark(_remarkController.text); Navigator.pop(context); }), ), ); } void _showAddDetailDialog(ExpenseApplyController controller) { final nameCtrl = TextEditingController(); final amountCtrl = TextEditingController(); final descCtrl = TextEditingController(); showDialog( context: context, builder: (_) => TDAlertDialog( title: '添加明细', contentWidget: Column( mainAxisSize: MainAxisSize.min, children: [ TDInput( controller: nameCtrl, hintText: '费用名称', ), const SizedBox(height: 8), TDInput( controller: amountCtrl, hintText: '金额', inputType: TextInputType.number, ), const SizedBox(height: 8), TDInput( controller: descCtrl, hintText: '描述', ), ], ), leftBtn: TDDialogButtonOptions(title: '取消', action: () => Navigator.pop(context)), rightBtn: TDDialogButtonOptions(title: '添加', action: () { final amount = double.tryParse(amountCtrl.text) ?? 0.0; controller.addDetail(ExpenseDetailModel( id: DateTime.now().millisecondsSinceEpoch.toString(), expenseId: '', expenseDate: DateTime.now(), expenseType: '', expenseDesc: nameCtrl.text, amount: amount, totalAmount: amount, remark: descCtrl.text, )); controller.recalculateAmount(); Navigator.pop(context); }), ), ); } }