approval_actions.dart 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. import 'package:flutter/material.dart';
  2. import '../../core/theme/app_colors.dart';
  3. import '../models/user_model.dart';
  4. class ApprovalActions extends StatefulWidget {
  5. final String status;
  6. final UserRole userRole;
  7. final VoidCallback onApprove;
  8. final VoidCallback onReject;
  9. final VoidCallback? onEdit;
  10. final VoidCallback? onWithdraw;
  11. final bool isSubmitting;
  12. const ApprovalActions({
  13. super.key,
  14. required this.status,
  15. required this.userRole,
  16. required this.onApprove,
  17. required this.onReject,
  18. this.onEdit,
  19. this.onWithdraw,
  20. this.isSubmitting = false,
  21. });
  22. @override
  23. State<ApprovalActions> createState() => _ApprovalActionsState();
  24. }
  25. class _ApprovalActionsState extends State<ApprovalActions> {
  26. final _opinionCtrl = TextEditingController();
  27. Future<void> _showOpinionDialog(String action) async {
  28. final result = await showDialog<bool>(
  29. context: context,
  30. builder: (ctx) => AlertDialog(
  31. title: Text(action == 'approve' ? '确认通过' : '确认拒绝'),
  32. content: Column(
  33. mainAxisSize: MainAxisSize.min,
  34. children: [
  35. Text('确定要${action == 'approve' ? '通过' : '拒绝'}该申请吗?'),
  36. const SizedBox(height: 12),
  37. TextField(
  38. controller: _opinionCtrl,
  39. maxLines: 3,
  40. decoration: InputDecoration(
  41. hintText: '审批意见(选填)',
  42. border: OutlineInputBorder(
  43. borderRadius: BorderRadius.circular(8)),
  44. ),
  45. ),
  46. ],
  47. ),
  48. actions: [
  49. TextButton(
  50. onPressed: () => Navigator.pop(ctx, false),
  51. child: const Text('取消'),
  52. ),
  53. TextButton(
  54. onPressed: () => Navigator.pop(ctx, true),
  55. child: Text('确定',
  56. style: TextStyle(
  57. color: action == 'approve'
  58. ? AppColors.primary
  59. : AppColors.error)),
  60. ),
  61. ],
  62. ),
  63. );
  64. if (result == true) {
  65. if (action == 'approve') {
  66. widget.onApprove();
  67. } else {
  68. widget.onReject();
  69. }
  70. }
  71. }
  72. @override
  73. Widget build(BuildContext context) {
  74. final isPending = widget.status == 'pending';
  75. final isDraft = widget.status == 'draft';
  76. final canApprove = isPending &&
  77. (widget.userRole == UserRole.approver ||
  78. widget.userRole == UserRole.finance ||
  79. widget.userRole == UserRole.admin);
  80. return Container(
  81. padding: const EdgeInsets.all(12),
  82. decoration: BoxDecoration(
  83. color: Colors.white,
  84. boxShadow: [
  85. BoxShadow(
  86. color: Colors.black.withValues(alpha: 0.05),
  87. blurRadius: 4, offset: const Offset(0, -1)),
  88. ],
  89. ),
  90. child: canApprove
  91. ? Row(children: [
  92. Expanded(
  93. child: OutlinedButton(
  94. onPressed: widget.isSubmitting
  95. ? null
  96. : () => _showOpinionDialog('reject'),
  97. style: OutlinedButton.styleFrom(
  98. foregroundColor: AppColors.error,
  99. side: const BorderSide(color: AppColors.error),
  100. shape: RoundedRectangleBorder(
  101. borderRadius: BorderRadius.circular(8)),
  102. minimumSize: const Size(double.infinity, 48),
  103. ),
  104. child: const Text('拒绝'),
  105. ),
  106. ),
  107. const SizedBox(width: 12),
  108. Expanded(
  109. flex: 2,
  110. child: ElevatedButton(
  111. onPressed: widget.isSubmitting
  112. ? null
  113. : () => _showOpinionDialog('approve'),
  114. style: ElevatedButton.styleFrom(
  115. backgroundColor: AppColors.primary,
  116. foregroundColor: Colors.white,
  117. shape: RoundedRectangleBorder(
  118. borderRadius: BorderRadius.circular(8)),
  119. minimumSize: const Size(double.infinity, 48),
  120. ),
  121. child: widget.isSubmitting
  122. ? const SizedBox(
  123. width: 20, height: 20,
  124. child: CircularProgressIndicator(
  125. strokeWidth: 2, color: Colors.white))
  126. : const Text('通过'),
  127. ),
  128. ),
  129. ])
  130. : Row(children: [
  131. if (isDraft && widget.onEdit != null) ...[
  132. Expanded(
  133. child: OutlinedButton(
  134. onPressed: widget.onEdit,
  135. child: const Text('编辑'),
  136. ),
  137. ),
  138. const SizedBox(width: 12),
  139. ],
  140. if (isPending && widget.onWithdraw != null)
  141. Expanded(
  142. child: OutlinedButton(
  143. onPressed: widget.onWithdraw,
  144. style: OutlinedButton.styleFrom(
  145. foregroundColor: AppColors.warning,
  146. side: const BorderSide(color: AppColors.warning),
  147. ),
  148. child: const Text('撤回'),
  149. ),
  150. ),
  151. ]),
  152. );
  153. }
  154. @override
  155. void dispose() {
  156. _opinionCtrl.dispose();
  157. super.dispose();
  158. }
  159. }