import 'package:flutter/material.dart'; import 'package:tdesign_flutter/tdesign_flutter.dart'; import '../../../core/i18n/app_localizations.dart'; import '../../../core/theme/app_colors_extension.dart'; import '../../../core/utils/amount_utils.dart'; import '../expense_model.dart'; /// 查看费用报销明细弹窗(只读)。 /// /// 使用 [TDSlidePopupRoute] 从底部滑出,展示报销明细的所有字段。 class ExpenseDetailViewDialog extends StatelessWidget { final ExpenseDetailModel detail; final AppLocalizations l10n; const ExpenseDetailViewDialog({ super.key, required this.detail, required this.l10n, }); /// 显示弹窗。 static void show(BuildContext context, ExpenseDetailModel detail) { final l10n = AppLocalizations.of(context); FocusScope.of(context).unfocus(); Navigator.push( context, TDSlidePopupRoute( slideTransitionFrom: SlideTransitionFrom.bottom, isDismissible: true, builder: (_) => ExpenseDetailViewDialog(detail: detail, l10n: l10n), ), ); } @override Widget build(BuildContext context) { final colors = Theme.of(context).extension()!; final d = detail; final catLabel = d.categoryName.isNotEmpty ? '${d.expenseCategory}/${d.categoryName}' : d.expenseCategory; return ConstrainedBox( constraints: BoxConstraints(maxHeight: MediaQuery.of(context).size.height * 0.8), child: Container( decoration: BoxDecoration( color: colors.bgCard, borderRadius: const BorderRadius.vertical(top: Radius.circular(16)), ), child: SafeArea( top: false, child: Column( mainAxisSize: MainAxisSize.min, children: [ // 标题栏 Padding( padding: const EdgeInsets.fromLTRB(20, 20, 12, 8), child: Row( children: [ const SizedBox(width: 24), Expanded(child: Center(child: Text(l10n.get('expenseDetails'), style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600)))), GestureDetector( onTap: () => Navigator.pop(context), child: Icon(Icons.close, color: colors.textSecondary), ), ], ), ), // 内容 Flexible( child: SingleChildScrollView( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _row(l10n.get('expenseCategory'), catLabel, colors), _row(l10n.get('amountInclTax'), formatAmount(d.totalAmount), colors), if (d.approvedAmount > 0) _row(l10n.get('approvedAmount'), formatAmount(d.approvedAmount), colors), _row(l10n.get('amountExcludingTax'), formatAmount(d.amount), colors), if (d.taxAmount > 0) _row(l10n.get('taxAmount'), formatAmount(d.taxAmount), colors), if (d.taxRate > 0) _row(l10n.get('taxRate'), '${d.taxRate.toStringAsFixed(0)}%', colors), _row(l10n.get('acctSubject'), d.acctSubjectId.isNotEmpty ? '${d.acctSubjectId}${d.acctSubjectName.isNotEmpty ? '/${d.acctSubjectName}' : ''}' : '-', colors), _row(l10n.get('project'), d.projectId.isNotEmpty ? '${d.projectId}${d.projectName.isNotEmpty ? '/${d.projectName}' : ''}' : '-', colors), _row(l10n.get('costDept'), d.costDeptId.isNotEmpty ? '${d.costDeptId}${d.costDeptName.isNotEmpty ? '/${d.costDeptName}' : ''}' : '-', colors), _row(l10n.get('customerVendor'), d.customerVendorId.isNotEmpty ? '${d.customerVendorId}${d.customerVendorName.isNotEmpty ? '/${d.customerVendorName}' : ''}' : '-', colors), _row(l10n.get('expenseApplyNo'), d.aeNo.isNotEmpty ? d.aeNo : '-', colors), _row(l10n.get('applyDate'), d.aeDd.isNotEmpty ? _formatDate(d.aeDd) : '-', colors), _row(l10n.get('applicant'), d.sqMan.isNotEmpty ? '${d.sqMan}${d.sqManName.isNotEmpty ? '/${d.sqManName}' : ''}' : '-', colors), _row(l10n.get('bankName'), d.bankName.isNotEmpty ? d.bankName : '-', colors), _row(l10n.get('bankAccountName'), d.bankAccountName.isNotEmpty ? d.bankAccountName : '-', colors), _row(l10n.get('bankAccount'), d.bankAccount.isNotEmpty ? d.bankAccount : '-', colors), _row(l10n.get('remark'), d.remark.isNotEmpty ? d.remark : '-', colors), ], ), ), ), ], ), ), ), ); } String _formatDate(String raw) { final dt = DateTime.tryParse(raw); if (dt == null) return raw; return '${dt.year}-${dt.month.toString().padLeft(2, '0')}-${dt.day.toString().padLeft(2, '0')}'; } Widget _row(String label, String value, AppColorsExtension colors) { return Padding( padding: const EdgeInsets.only(bottom: 16), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( width: 100, child: Text(label, style: TextStyle(fontSize: 14, color: colors.textSecondary)), ), const SizedBox(width: 12), Expanded(child: Text(value, style: const TextStyle(fontSize: 14))), ], ), ); } }