expense_detail_page.dart 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter_riverpod/flutter_riverpod.dart';
  3. import '../../core/theme/app_colors.dart';
  4. import '../../core/utils/date_utils.dart' as du;
  5. import '../../core/utils/responsive.dart';
  6. import '../../shared/widgets/form_section.dart';
  7. import '../../shared/widgets/form_field_row.dart';
  8. import '../../shared/widgets/status_tag.dart';
  9. import 'expense_api.dart';
  10. import 'expense_model.dart';
  11. final expenseDetailProvider =
  12. FutureProvider.autoDispose.family<ExpenseModel, String>(
  13. (ref, id) async {
  14. return ref.read(expenseApiProvider).fetchDetail(id);
  15. });
  16. class ExpenseDetailPage extends ConsumerWidget {
  17. final String id;
  18. const ExpenseDetailPage({super.key, required this.id});
  19. @override
  20. Widget build(BuildContext context, WidgetRef ref) {
  21. final detailAsync = ref.watch(expenseDetailProvider(id));
  22. final r = ResponsiveHelper.of(context);
  23. return Scaffold(
  24. appBar: AppBar(title: const Text('报销单详情')),
  25. body: detailAsync.when(
  26. loading: () =>
  27. const Center(child: CircularProgressIndicator()),
  28. error: (_, __) => const Center(child: Text('加载失败')),
  29. data: (expense) => Center(
  30. child: ConstrainedBox(
  31. constraints:
  32. BoxConstraints(maxWidth: r.detailTwoColumns ? 700 : double.infinity),
  33. child: SingleChildScrollView(
  34. padding: const EdgeInsets.symmetric(vertical: 8),
  35. child: Column(
  36. children: [
  37. FormSection(
  38. title: '基本信息',
  39. children: [
  40. FormFieldRow(
  41. label: '单号',
  42. value: expense.reportNo,
  43. showArrow: false),
  44. FormFieldRow(
  45. label: '报销类型',
  46. value: expense.expenseType,
  47. showArrow: false),
  48. FormFieldRow(
  49. label: '报销金额',
  50. value: '¥${expense.totalAmount.toStringAsFixed(2)}',
  51. showArrow: false),
  52. FormFieldRow(
  53. label: '部门',
  54. value: expense.deptName,
  55. showArrow: false),
  56. FormFieldRow(
  57. label: '申请人',
  58. value: expense.applicantName,
  59. showArrow: false),
  60. FormFieldRow(
  61. label: '创建时间',
  62. value: du.DateUtils.formatDateTime(
  63. expense.createTime),
  64. showArrow: false),
  65. FormFieldRow(
  66. label: '状态',
  67. value: '',
  68. showArrow: false,
  69. onTap: null,
  70. ), // 状态用 StatusTag
  71. Padding(
  72. padding: const EdgeInsets.fromLTRB(14, 0, 14, 12),
  73. child: Row(
  74. children: [
  75. const SizedBox(
  76. width: 72,
  77. child: Text('状态',
  78. style: TextStyle(
  79. color: AppColors.textSecondary,
  80. fontSize: 13))),
  81. StatusTag(status: expense.status),
  82. ],
  83. ),
  84. ),
  85. FormFieldRow(
  86. label: '备注',
  87. value: expense.remark.isEmpty
  88. ? '-'
  89. : expense.remark,
  90. showArrow: false),
  91. ],
  92. ),
  93. FormSection(
  94. title: '报销明细',
  95. children: expense.details.isEmpty
  96. ? [
  97. const Padding(
  98. padding: EdgeInsets.all(14),
  99. child: Text('无明细',
  100. style: TextStyle(
  101. color: AppColors.textHint,
  102. fontSize: 13)),
  103. )
  104. ]
  105. : expense.details
  106. .map((d) => Padding(
  107. padding: const EdgeInsets.symmetric(
  108. horizontal: 14, vertical: 8),
  109. child: Row(
  110. mainAxisAlignment:
  111. MainAxisAlignment.spaceBetween,
  112. children: [
  113. Column(
  114. crossAxisAlignment:
  115. CrossAxisAlignment.start,
  116. children: [
  117. Text(d.expenseDesc,
  118. style: const TextStyle(
  119. fontSize: 13,
  120. color: AppColors
  121. .textPrimary)),
  122. Text(d.invoiceNo,
  123. style: const TextStyle(
  124. fontSize: 10,
  125. color: AppColors
  126. .textHint)),
  127. ],
  128. ),
  129. Text(
  130. '¥${d.totalAmount.toStringAsFixed(2)}',
  131. style: const TextStyle(
  132. color: AppColors.primary,
  133. fontSize: 14)),
  134. ],
  135. ),
  136. ))
  137. .toList(),
  138. ),
  139. ],
  140. ),
  141. ),
  142. ),
  143. ),
  144. ),
  145. );
  146. }
  147. }