import 'package:flutter/material.dart'; import '../../core/i18n/app_localizations.dart'; import '../../core/theme/app_colors.dart'; import '../models/approval_status.dart'; class ApprovalTimeline extends StatelessWidget { final List records; final List chain; final String currentApproverId; const ApprovalTimeline({ super.key, required this.records, required this.chain, this.currentApproverId = '', }); @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( l10n.get('approvalProgress'), style: TextStyle( fontSize: 13, fontWeight: FontWeight.w600, color: AppColors.textPrimary, ), ), const SizedBox(height: 12), ...chain.asMap().entries.map((entry) { final idx = entry.key; final approverId = entry.value; final record = records .where((r) => r.approverId == approverId) .firstOrNull; final isCurrent = approverId == currentApproverId; return _buildNode( l10n, idx, chain.length, approverId, record, isCurrent, ); }), ], ); } Widget _buildNode( AppLocalizations l10n, int index, int total, String approverId, ApprovalRecord? record, bool isCurrent, ) { final isDone = record != null && record.action == 'approve'; final isRejected = record != null && record.action == 'reject'; final isLast = index == total - 1; return IntrinsicHeight( child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Column( children: [ Container( width: 22, height: 22, decoration: BoxDecoration( color: isRejected ? AppColors.danger : isDone ? AppColors.success : isCurrent ? AppColors.primary : const Color(0xFFDDDDDD), shape: BoxShape.circle, ), child: Center( child: isRejected ? const Icon(Icons.close, size: 12, color: Colors.white) : isDone ? const Icon(Icons.check, size: 12, color: Colors.white) : isCurrent ? const Text( '●', style: TextStyle(color: Colors.white, fontSize: 10), ) : null, ), ), if (!isLast) Container( width: 1.5, height: 36, color: isDone ? AppColors.success : const Color(0xFFDDDDDD), ), ], ), const SizedBox(width: 10), Expanded( child: Padding( padding: EdgeInsets.only(bottom: isLast ? 0 : 12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( record?.approverName ?? '审批人$approverId', style: TextStyle( fontSize: 13, fontWeight: FontWeight.w500, color: isRejected ? AppColors.danger : isCurrent ? AppColors.primary : AppColors.textPrimary, ), ), if (record != null && record.action != 'pending') ...[ const SizedBox(height: 2), Text( '${record.action == 'approve' ? l10n.get('approved') : l10n.get('rejected')} · ${record.approvalTime.toString().substring(0, 16)}', style: const TextStyle( fontSize: 11, color: AppColors.textPlaceholder, ), ), if (record.opinion.isNotEmpty) Padding( padding: const EdgeInsets.only(top: 2), child: Text( '${l10n.get('opinion')}${record.opinion}', style: const TextStyle( fontSize: 11, color: AppColors.textSecondary, ), ), ), ] else if (isCurrent) ...[ const SizedBox(height: 2), Text( l10n.get('currentNode'), style: TextStyle(fontSize: 11, color: AppColors.primary), ), ] else ...[ const SizedBox(height: 2), Text( l10n.get('waitHandle'), style: TextStyle( fontSize: 11, color: AppColors.textPlaceholder, ), ), ], ], ), ), ), ], ), ); } }