| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137 |
- 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 '../../shared/models/user_model.dart';
- import '../../shared/widgets/app_card.dart';
- import '../../shared/widgets/approval_timeline.dart';
- import '../../shared/widgets/approval_actions.dart';
- import 'expense_model.dart';
- import 'expense_list_controller.dart';
- class ExpenseDetailPage extends ConsumerWidget {
- final String id;
- const ExpenseDetailPage({super.key, required this.id});
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- final expense = mockExpenses.firstWhere((e) => e.id == id,
- orElse: () => mockExpenses.first);
- return Scaffold(
- appBar: TDNavBar(
- title: '报销单详情',
- titleColor: Colors.white,
- backgroundColor: const Color(0xFF00ABF3),
- centerTitle: false,
- ),
- body: Column(children: [
- Expanded(
- child: SingleChildScrollView(
- padding: const EdgeInsets.all(12),
- child: Column(children: [
- _buildStatusHeader(expense),
- const SizedBox(height: 12),
- _buildInfoSection(expense),
- const SizedBox(height: 12),
- _buildDetailSection(expense),
- const SizedBox(height: 12),
- if (expense.approvalRecords.isNotEmpty || expense.approvalChain.isNotEmpty)
- AppCard(
- child: ApprovalTimeline(
- records: expense.approvalRecords,
- chain: expense.approvalChain,
- currentApproverId: expense.currentApproverId,
- ),
- ),
- ]),
- ),
- ),
- ApprovalActions(
- status: expense.status,
- userRole: UserRole.employee,
- onApprove: () {},
- onReject: () {},
- onEdit: () => context.push('/expense/apply?id=${expense.id}'),
- onWithdraw: () {},
- ),
- ]),
- );
- }
- Widget _buildStatusHeader(ExpenseModel expense) {
- final (icon, color, text) = switch (expense.status) {
- 'approved' => (Icons.check_circle, AppColors.success, '已通过'),
- 'rejected' => (Icons.cancel, AppColors.error, '已拒绝'),
- 'draft' => (Icons.edit, AppColors.textHint, '草稿'),
- _ => (Icons.schedule, AppColors.warning, '待审批'),
- };
- return Container(
- padding: const EdgeInsets.all(20),
- decoration: BoxDecoration(
- color: color.withValues(alpha: 0.08),
- borderRadius: BorderRadius.circular(12)),
- child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
- Icon(icon, color: color, size: 36),
- const SizedBox(width: 10),
- Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
- Text(text, style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: color)),
- if (expense.status == 'pending')
- Text('当前审批人:${expense.currentApproverId}',
- style: const TextStyle(fontSize: 12, color: AppColors.textSecondary)),
- ]),
- ]),
- );
- }
- Widget _buildInfoSection(ExpenseModel expense) {
- return _buildDetailCard('基本信息', [
- TDCell(title: '报销单号', note: expense.reportNo, showBottomBorder: true),
- TDCell(title: '申请人', note: '${expense.applicantName} · ${expense.deptName}', showBottomBorder: true),
- TDCell(title: '报销类型', note: expense.expenseType, showBottomBorder: true),
- TDCell(title: '发票数量', note: '${expense.invoiceCount}张', showBottomBorder: true),
- TDCell(title: '总金额', note: '¥${expense.totalAmount.toStringAsFixed(2)}', showBottomBorder: expense.remark.isNotEmpty),
- if (expense.remark.isNotEmpty)
- TDCell(title: '备注', note: expense.remark, showBottomBorder: false),
- ]);
- }
- Widget _buildDetailSection(ExpenseModel expense) {
- if (expense.details.isEmpty) return const SizedBox.shrink();
- return _buildDetailCard('报销明细', [
- ...expense.details.map((d) => TDCell(
- titleWidget: Text(d.expenseDesc, style: const TextStyle(fontSize: 13, color: AppColors.textPrimary)),
- description: d.expenseDate.toString().substring(0, 10),
- note: '¥${d.totalAmount.toStringAsFixed(2)}',
- noteWidget: Text('¥${d.totalAmount.toStringAsFixed(2)}', style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500, color: AppColors.primary)),
- showBottomBorder: true,
- )),
- Padding(
- padding: const EdgeInsets.fromLTRB(14, 12, 14, 12),
- child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
- const Text('合计', style: TextStyle(fontSize: 13, fontWeight: FontWeight.w600, color: AppColors.textPrimary)),
- Text('¥${expense.totalAmount.toStringAsFixed(2)}', style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600, color: AppColors.primary)),
- ]),
- ),
- ]);
- }
- Widget _buildDetailCard(String title, List<Widget> cells) {
- return Container(
- margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
- decoration: BoxDecoration(
- color: AppColors.cardWhite,
- borderRadius: BorderRadius.circular(10),
- boxShadow: [BoxShadow(color: Colors.black.withValues(alpha: 0.04), blurRadius: 4, offset: const Offset(0, 1))],
- ),
- child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
- Padding(
- padding: const EdgeInsets.fromLTRB(14, 12, 14, 8),
- child: Text(title, style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w600, color: AppColors.textPrimary)),
- ),
- ...cells,
- ]),
- );
- }
- }
|