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'; /// 单据状态标签 + 底部操作按钮。 /// /// 根据 [billStatus] 中的 `approvalStatus`(SHCOUNT0~6)、 /// `isClosed`、`isTransferred`、`isCreator`、`canEdit` 控制展示。 /// /// 使用方式: /// ```dart /// BillStatusBar( /// billStatus: _billStatus, /// onEdit: () => context.push('/xxx/edit/$billNo'), /// onSubmit: () => api.shSubmit(bilNo: billNo, bilDd: ...), /// onCancelSubmit: () => api.shSubmit(bilNo: billNo, bilDd: ..., isCancel: true), /// onTapStatusTag: () => _showAuditTrail(), /// ), /// ``` class BillStatusBar { /// getBillStatus 接口返回的完整 Map,为 null 时显示空状态。 final Map? billStatus; /// "修改"按钮回调 final VoidCallback? onEdit; /// "提交"按钮回调(shSubmit) final VoidCallback? onSubmit; /// "取消提交"按钮回调(shSubmit + isCancel) final VoidCallback? onCancelSubmit; /// "还车"按钮回调(仅用车申请单使用) final VoidCallback? onReturn; /// "修改还车信息"按钮回调(仅用车申请单使用,已还车后显示) final VoidCallback? onEditReturn; /// 点击状态标签回调(查看审批轨迹) final VoidCallback? onTapStatusTag; /// 已关闭状态文本(如"已关闭",仅 expense 使用) final String? closedText; /// 已转单状态文本(如"已转为报销单",expense/expense_apply 使用) final String? transferredText; /// 已还车状态文本(如"已还车",仅用车申请单使用,优先级最高) final String? returnedText; /// 单据类型是否配置了审核流。为 false 时提交/取消提交按钮不显示。 final bool hasAuditFlow; const BillStatusBar({ this.billStatus, this.onEdit, this.onSubmit, this.onCancelSubmit, this.onReturn, this.onEditReturn, this.onTapStatusTag, this.closedText, this.transferredText, this.returnedText, this.hasAuditFlow = false, }); // ── 派生状态 ── bool get _isClosed => billStatus?['isClosed'] == true; bool get _isTransferred => billStatus?['isTransferred'] == true; bool get _isReturned { final extras = billStatus?['extras'] as Map?; return extras?['isReturned'] == true; } String? get _approvalStatus => billStatus?['approvalStatus'] as String?; String? get _approvalText => billStatus?['approvalText'] as String?; bool get _canEdit => billStatus?['canEdit'] == true; // ── 按钮显隐 ── bool get showEdit { if (_isReturned) return false; final s = _approvalStatus; if (s == 'SHCOUNT0' || s == 'SHCOUNT3') return _canEdit; if (s == 'SHCOUNT6') return _canEdit; return false; } bool get showSubmit { if (!hasAuditFlow) return false; if (_isClosed || _isTransferred || _isReturned) return false; final s = _approvalStatus; return s == 'SHCOUNT0' || s == 'SHCOUNT3'; } bool get showCancelSubmit { if (!hasAuditFlow) return false; if (_isClosed || _isTransferred || _isReturned) return false; final s = _approvalStatus; return s == 'SHCOUNT1'; } bool get showReturn { if (onReturn == null) return false; if (_isReturned) return false; final s = _approvalStatus; return (s == 'SHCOUNT4' || s == 'SHCOUNT6') && !_isClosed && !_isTransferred; } bool get showEditReturn { if (onEditReturn == null) return false; return _isReturned; } bool get _showButtons => showEdit || showSubmit || showCancelSubmit || showReturn || showEditReturn; // ── 状态标签数据 ── ({IconData icon, Color color}) _statusStyle() { if (_isReturned) { return (icon: Icons.task_alt, color: Colors.deepPurple); } if (_isClosed) { return (icon: Icons.lock_outline, color: Colors.blueGrey); } if (_isTransferred) { return (icon: Icons.swap_horiz, color: Colors.blue); } final s = _approvalStatus; if (s == null || s.isEmpty) { return (icon: Icons.help_outline, color: Colors.grey); } switch (s) { case 'SHCOUNT0': return (icon: Icons.edit_note, color: Colors.teal); case 'SHCOUNT1': return (icon: Icons.hourglass_empty, color: Colors.orange); case 'SHCOUNT2': return (icon: Icons.cancel_outlined, color: Colors.red); case 'SHCOUNT3': return (icon: Icons.undo, color: Colors.deepOrange); case 'SHCOUNT4': return (icon: Icons.check_circle_outline, color: Colors.green); case 'SHCOUNT5': return (icon: Icons.sync, color: Colors.blue); case 'SHCOUNT6': return (icon: Icons.verified, color: Colors.indigo); default: return (icon: Icons.help_outline, color: Colors.grey); } } String? get _displayText { if (_isReturned) return returnedText; if (_isClosed) return closedText; if (_isTransferred) return transferredText; return _approvalText; } // ── 构建 ── /// 仅状态标签(用于 FormSection trailing) Widget buildStatusTag(BuildContext context) { final style = _statusStyle(); final text = _displayText; if (text == null || text.isEmpty) return const SizedBox.shrink(); final tag = Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), decoration: BoxDecoration( color: style.color.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(6), border: Border.all( color: style.color.withValues(alpha: 0.3), width: 0.5, ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(style.icon, size: 18, color: style.color), const SizedBox(width: 4), Text( text, style: TextStyle( fontSize: 13, fontWeight: FontWeight.w600, color: style.color, ), ), ], ), ); if (onTapStatusTag != null) { return GestureDetector(onTap: onTapStatusTag, child: tag); } return tag; } /// 底部操作按钮区 Widget buildActions(BuildContext context) { if (!_showButtons) return const SizedBox.shrink(); final colors = Theme.of(context).extension()!; final l10n = AppLocalizations.of(context); final bottomPadding = MediaQuery.of(context).padding.bottom; return Container( padding: EdgeInsets.fromLTRB(16, 12, 16, 12 + bottomPadding), decoration: BoxDecoration( color: colors.bgCard, border: Border(top: BorderSide(color: colors.border)), ), child: Row( children: [ if (showEdit) ...[ Expanded( child: TDButton( icon: TDIcons.edit, text: _editLabel(l10n), size: TDButtonSize.medium, type: TDButtonType.outline, shape: TDButtonShape.round, theme: TDButtonTheme.primary, onTap: onEdit, ), ), ], if (showEdit && (showSubmit || showCancelSubmit)) const SizedBox(width: 12), if (showSubmit) Expanded( child: TDButton( icon: TDIcons.send, text: _submitLabel(l10n), size: TDButtonSize.medium, type: TDButtonType.fill, shape: TDButtonShape.round, theme: TDButtonTheme.primary, onTap: onSubmit, ), ), if (showCancelSubmit) Expanded( child: TDButton( icon: TDIcons.rollback, text: _cancelSubmitLabel(l10n), size: TDButtonSize.medium, type: TDButtonType.fill, shape: TDButtonShape.round, theme: TDButtonTheme.danger, onTap: onCancelSubmit, ), ), if (showReturn) ...[ if (showEdit || showSubmit || showCancelSubmit) const SizedBox(width: 12), Expanded( child: TDButton( icon: Icons.directions_car, text: _returnLabel(l10n), size: TDButtonSize.medium, type: TDButtonType.fill, shape: TDButtonShape.round, theme: TDButtonTheme.primary, onTap: onReturn, ), ), ], if (showEditReturn) ...[ if (showEdit || showSubmit || showCancelSubmit || showReturn) const SizedBox(width: 12), Expanded( child: TDButton( icon: Icons.assignment_return_outlined, text: _editReturnLabel(l10n), size: TDButtonSize.medium, type: TDButtonType.outline, shape: TDButtonShape.round, theme: TDButtonTheme.primary, onTap: onEditReturn, ), ), ], ], ), ); } // ── 按钮标签 ── /// 默认返回中文按钮标签(不依赖 i18n,简化跨模块复用) String _editLabel(AppLocalizations l10n) => l10n.get('modify'); String _submitLabel(AppLocalizations l10n) => l10n.get('submit'); String _cancelSubmitLabel(AppLocalizations l10n) => l10n.get('cancelSubmit'); String _returnLabel(AppLocalizations l10n) => l10n.get('returnCar'); String _editReturnLabel(AppLocalizations l10n) => l10n.get('editReturnInfo'); }