import 'package:flutter/material.dart'; import 'package:tdesign_flutter/tdesign_flutter.dart'; import '../../core/theme/app_colors.dart'; import '../../core/theme/app_colors_extension.dart'; import '../../core/i18n/app_localizations.dart'; /// 审核过程明细弹窗 /// /// 调用 [AuditTrailDialog.show] 弹出,自动拉取数据并展示。 class AuditTrailDialog extends StatelessWidget { final Future>> Function() fetcher; const AuditTrailDialog({super.key, required this.fetcher}); /// 显示弹窗,[fetcher] 负责调 API 获取数据。 static Future show( BuildContext context, { required Future>> Function() fetcher, }) { return Navigator.push( context, TDSlidePopupRoute( slideTransitionFrom: SlideTransitionFrom.bottom, builder: (_) => AuditTrailDialog(fetcher: fetcher), ), ); } @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); final colors = Theme.of(context).extension()!; return FutureBuilder>>( future: fetcher(), builder: (ctx, snapshot) { return SafeArea( child: SizedBox( height: MediaQuery.of(context).size.height * 0.8, child: Container( decoration: BoxDecoration( color: colors.bgPage, borderRadius: const BorderRadius.vertical(top: Radius.circular(16)), ), child: Column( children: [ Padding( padding: const EdgeInsets.fromLTRB(20, 16, 12, 12), child: Row( children: [ Text( l10n.get('auditTrail'), style: TextStyle(fontSize: AppFontSizes.title, fontWeight: FontWeight.w600, color: colors.textPrimary), ), const Spacer(), GestureDetector( onTap: () => Navigator.pop(context), child: Icon(Icons.close, size: 20, color: colors.textSecondary), ), ], ), ), Divider(height: 1, color: colors.border), Flexible( child: _buildContent(snapshot, colors), ), ], ), ), ), ); }, ); } Widget _buildContent(AsyncSnapshot>> snapshot, AppColorsExtension colors) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: TDLoading(size: TDLoadingSize.medium, icon: TDLoadingIcon.activity)); } if (snapshot.hasError || !snapshot.hasData || snapshot.data!.isEmpty) { return const Center(child: Text('—', style: TextStyle(fontSize: 14, color: Color(0xFF999999)))); } final items = snapshot.data!; return ListView.builder( shrinkWrap: true, padding: const EdgeInsets.all(16), itemCount: items.length, itemBuilder: (_, i) { final item = items[i]; final action = item['action']?.toString() ?? ''; Color iconColor; IconData icon; if (action == 'Y') { icon = Icons.check_circle; iconColor = colors.success; } else if (action == 'N') { icon = Icons.cancel; iconColor = colors.danger; } else { icon = Icons.hourglass_empty; iconColor = colors.infoText; } return Padding( padding: const EdgeInsets.only(bottom: 12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(icon, size: 18, color: iconColor), const SizedBox(width: 8), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded(child: Text(item['user']?.toString() ?? '', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: colors.textPrimary))), Text(item['date']?.toString() ?? '', style: TextStyle(fontSize: 12, color: colors.textSecondary)), ], ), if ((item['node']?.toString() ?? '').isNotEmpty) ...[ const SizedBox(height: 2), Text(item['node']?.toString() ?? '', style: TextStyle(fontSize: 13, color: colors.textSecondary)), ], ], ), ), ], ), if ((item['remark']?.toString() ?? '').isNotEmpty) ...[ const SizedBox(height: 4), Padding( padding: const EdgeInsets.only(left: 26), child: Text(item['remark']?.toString() ?? '', style: TextStyle(fontSize: 13, color: colors.textSecondary)), ), ], ], ), ); }, ); } }