expense_detail_view_dialog.dart 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. import 'package:flutter/material.dart';
  2. import 'package:tdesign_flutter/tdesign_flutter.dart';
  3. import '../../../core/i18n/app_localizations.dart';
  4. import '../../../core/theme/app_colors_extension.dart';
  5. import '../expense_model.dart';
  6. /// 查看费用报销明细弹窗(只读)。
  7. ///
  8. /// 使用 [TDSlidePopupRoute] 从底部滑出,展示报销明细的所有字段。
  9. class ExpenseDetailViewDialog extends StatelessWidget {
  10. final ExpenseDetailModel detail;
  11. final AppLocalizations l10n;
  12. const ExpenseDetailViewDialog({
  13. super.key,
  14. required this.detail,
  15. required this.l10n,
  16. });
  17. /// 显示弹窗。
  18. static void show(BuildContext context, ExpenseDetailModel detail) {
  19. final l10n = AppLocalizations.of(context);
  20. FocusScope.of(context).unfocus();
  21. Navigator.push(
  22. context,
  23. TDSlidePopupRoute<void>(
  24. slideTransitionFrom: SlideTransitionFrom.bottom,
  25. isDismissible: true,
  26. builder: (_) => ExpenseDetailViewDialog(detail: detail, l10n: l10n),
  27. ),
  28. );
  29. }
  30. @override
  31. Widget build(BuildContext context) {
  32. final colors = Theme.of(context).extension<AppColorsExtension>()!;
  33. final d = detail;
  34. final catLabel = d.categoryName.isNotEmpty
  35. ? '${d.expenseCategory}/${d.categoryName}'
  36. : d.expenseCategory;
  37. return Container(
  38. decoration: BoxDecoration(
  39. color: colors.bgCard,
  40. borderRadius: const BorderRadius.vertical(top: Radius.circular(16)),
  41. ),
  42. child: SafeArea(
  43. top: false,
  44. child: Column(
  45. mainAxisSize: MainAxisSize.min,
  46. children: [
  47. // 标题栏
  48. Padding(
  49. padding: const EdgeInsets.fromLTRB(20, 20, 12, 8),
  50. child: Row(
  51. children: [
  52. Expanded(child: Center(child: Text(l10n.get('expenseDetails'), style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600)))),
  53. GestureDetector(
  54. onTap: () => Navigator.pop(context),
  55. child: Icon(Icons.close, color: colors.textSecondary),
  56. ),
  57. ],
  58. ),
  59. ),
  60. // 内容
  61. Flexible(
  62. child: SingleChildScrollView(
  63. padding: const EdgeInsets.all(20),
  64. child: Column(
  65. crossAxisAlignment: CrossAxisAlignment.start,
  66. children: [
  67. _row(l10n.get('expenseCategory'), catLabel, colors),
  68. _row(l10n.get('amountInclTax'), '¥${d.totalAmount.toStringAsFixed(2)}', colors),
  69. if (d.approvedAmount > 0)
  70. _row(l10n.get('approvedAmount'), '¥${d.approvedAmount.toStringAsFixed(2)}', colors),
  71. _row(l10n.get('amountExcludingTax'), '¥${d.amount.toStringAsFixed(2)}', colors),
  72. if (d.taxAmount > 0)
  73. _row(l10n.get('taxAmount'), '¥${d.taxAmount.toStringAsFixed(2)}', colors),
  74. if (d.taxRate > 0)
  75. _row(l10n.get('taxRate'), '${d.taxRate.toStringAsFixed(0)}%', colors),
  76. _row(l10n.get('acctSubject'), d.acctSubjectId.isNotEmpty ? '${d.acctSubjectId}${d.acctSubjectName.isNotEmpty ? '/${d.acctSubjectName}' : ''}' : '-', colors),
  77. _row(l10n.get('project'), d.projectId.isNotEmpty && d.projectName.isNotEmpty ? '${d.projectId}/${d.projectName}' : '-', colors),
  78. _row(l10n.get('costDept'), d.costDeptId.isNotEmpty && d.costDeptName.isNotEmpty ? '${d.costDeptId}/${d.costDeptName}' : '-', colors),
  79. _row(l10n.get('customerVendor'), d.customerVendorName.isNotEmpty ? '${d.customerVendorId}/${d.customerVendorName}' : '-', colors),
  80. _row(l10n.get('expenseApplyNo'), d.aeNo.isNotEmpty ? d.aeNo : '-', colors),
  81. _row(l10n.get('applyDate'), d.aeDd.isNotEmpty ? d.aeDd : '-', colors),
  82. _row(l10n.get('applicant'), d.sqManName.isNotEmpty ? '${d.sqMan}/${d.sqManName}' : '-', colors),
  83. _row(l10n.get('bankName'), d.bankName.isNotEmpty ? d.bankName : '-', colors),
  84. _row(l10n.get('bankAccountName'), d.bankAccountName.isNotEmpty ? d.bankAccountName : '-', colors),
  85. _row(l10n.get('bankAccount'), d.bankAccount.isNotEmpty ? d.bankAccount : '-', colors),
  86. _row(l10n.get('remark'), d.remark.isNotEmpty ? d.remark : '-', colors),
  87. ],
  88. ),
  89. ),
  90. ),
  91. ],
  92. ),
  93. ),
  94. );
  95. }
  96. Widget _row(String label, String value, AppColorsExtension colors) {
  97. return Padding(
  98. padding: const EdgeInsets.only(bottom: 16),
  99. child: Row(
  100. crossAxisAlignment: CrossAxisAlignment.start,
  101. children: [
  102. SizedBox(
  103. width: 100,
  104. child: Text(label, style: TextStyle(fontSize: 14, color: colors.textSecondary)),
  105. ),
  106. const SizedBox(width: 12),
  107. Expanded(child: Text(value, style: const TextStyle(fontSize: 14))),
  108. ],
  109. ),
  110. );
  111. }
  112. }