import 'package:flutter/material.dart'; import 'package:social_app/core/theme/design_tokens.dart'; import '../../data/models/tool_result.dart'; /// 卡片类型常量 const _calendarCardType = 'calendar_card.v1'; const _calendarListType = 'calendar_event_list.v1'; const _calendarOperationType = 'calendar_operation.v1'; const _errorCardType = 'error_card.v1'; const _aiGeneratedSource = 'ai_generated'; const _agentGeneratedSource = 'agent_generated'; const _primaryActionType = 'primary'; class UiSchemaRenderer { static Widget render(UiCard card) { return switch (card.cardType) { _calendarCardType => _renderCalendarCard(card), _calendarListType => _renderCalendarList(card), _calendarOperationType => _renderCalendarOperation(card), _errorCardType => _renderErrorCard(card), _ => _renderUnknownCard(card), }; } static Widget _renderCalendarCard(UiCard card) { final data = CalendarCardData.fromJson(card.data); final color = data.color != null ? Color(int.parse(data.color!.replaceFirst('#', '0xFF'))) : AppColors.blue500; final isAiGenerated = data.sourceType == _aiGeneratedSource || data.sourceType == _agentGeneratedSource; return Container( decoration: BoxDecoration( color: AppColors.messageCardBg, borderRadius: BorderRadius.circular(AppRadius.lg), border: Border.all(color: AppColors.messageCardBorder), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( height: AppSpacing.sm, decoration: BoxDecoration( color: color, borderRadius: BorderRadius.only( topLeft: Radius.circular(AppRadius.lg), topRight: Radius.circular(AppRadius.lg), ), ), ), Padding( padding: EdgeInsets.all(AppSpacing.lg), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (isAiGenerated) ...[ _buildAiTag(), SizedBox(height: AppSpacing.sm), ], Text( _formatTime(data.startAt, data.endAt), style: TextStyle(fontSize: 12, color: AppColors.slate500), ), SizedBox(height: AppSpacing.sm), Text( data.title, style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: AppColors.slate900, ), ), if (data.description != null) ...[ SizedBox(height: AppSpacing.xs), Text( data.description!, style: TextStyle(fontSize: 14, color: AppColors.slate600), ), ], if (data.location != null) ...[ SizedBox(height: AppSpacing.sm), _buildLocation(data.location!), ], if (card.actions != null && card.actions!.isNotEmpty) ...[ SizedBox(height: AppSpacing.md), _buildActions(card.actions!), ], ], ), ), ], ), ); } static Widget _buildAiTag() { return Container( padding: EdgeInsets.symmetric( horizontal: AppSpacing.sm, vertical: AppSpacing.xs, ), decoration: BoxDecoration( color: AppColors.messageTagBg, borderRadius: BorderRadius.circular(AppRadius.sm), ), child: Text( 'AI生成', style: TextStyle(fontSize: 10, color: AppColors.blue600), ), ); } static Widget _buildLocation(String location) { return Row( children: [ Icon(Icons.location_on_outlined, size: 16, color: AppColors.slate500), SizedBox(width: AppSpacing.xs), Text( location, style: TextStyle(fontSize: 12, color: AppColors.slate500), ), ], ); } static Widget _buildActions(List actions) { return Wrap( spacing: AppSpacing.sm, children: actions.map((action) => _buildActionButton(action)).toList(), ); } static Widget _buildActionButton(CardAction action) { final isPrimary = action.type == _primaryActionType; return GestureDetector( onTap: () => _handleAction(action), child: Container( padding: EdgeInsets.symmetric( horizontal: AppSpacing.md, vertical: AppSpacing.sm, ), decoration: BoxDecoration( color: isPrimary ? AppColors.blue500 : AppColors.messageBtnWrap, borderRadius: BorderRadius.circular(AppRadius.sm), border: Border.all( color: isPrimary ? AppColors.blue500 : AppColors.messageBtnBorder, ), ), child: Text( action.label, style: TextStyle( fontSize: 14, color: isPrimary ? AppColors.white : AppColors.slate600, ), ), ), ); } static Widget _renderCalendarList(UiCard card) { final rawItems = card.data['items']; final items = rawItems is List ? rawItems : const []; final paginationRaw = card.data['pagination']; final pagination = paginationRaw is Map ? paginationRaw : const {}; final page = pagination['page']; final total = pagination['total']; return Container( padding: EdgeInsets.all(AppSpacing.lg), decoration: BoxDecoration( color: AppColors.messageCardBg, borderRadius: BorderRadius.circular(AppRadius.lg), border: Border.all(color: AppColors.messageCardBorder), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '日程列表', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: AppColors.slate900, ), ), if (page != null || total != null) ...[ SizedBox(height: AppSpacing.xs), Text( '第${page ?? '-'}页 · 共${total ?? '-'}条', style: TextStyle(fontSize: 12, color: AppColors.slate500), ), ], SizedBox(height: AppSpacing.sm), if (items.isEmpty) Text( '暂无日程', style: TextStyle(fontSize: 14, color: AppColors.slate500), ), for (final item in items) if (item is Map) Padding( padding: EdgeInsets.only(bottom: AppSpacing.xs), child: Text( item['title']?.toString() ?? '未命名日程', style: TextStyle(fontSize: 14, color: AppColors.slate700), ), ), ], ), ); } static Widget _renderCalendarOperation(UiCard card) { final ok = card.data['ok'] == true; final operation = card.data['operation']?.toString() ?? 'operation'; final message = card.data['message']?.toString() ?? (ok ? '操作成功' : '操作失败'); return Container( padding: EdgeInsets.all(AppSpacing.lg), decoration: BoxDecoration( color: ok ? AppColors.messageCardBg : AppColors.warningBackground, borderRadius: BorderRadius.circular(AppRadius.lg), border: Border.all( color: ok ? AppColors.messageCardBorder : AppColors.red400, ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '日程$operation结果', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, color: ok ? AppColors.slate900 : AppColors.red600, ), ), SizedBox(height: AppSpacing.xs), Text( message, style: TextStyle( fontSize: 13, color: ok ? AppColors.slate600 : AppColors.red600, ), ), if (card.actions != null && card.actions!.isNotEmpty) ...[ SizedBox(height: AppSpacing.md), _buildActions(card.actions!), ], ], ), ); } static Widget _renderErrorCard(UiCard card) { final message = card.data['message'] as String? ?? '发生错误'; return Container( padding: EdgeInsets.all(AppSpacing.lg), decoration: BoxDecoration( color: AppColors.warningBackground, borderRadius: BorderRadius.circular(AppRadius.lg), border: Border.all(color: AppColors.red400), ), child: Row( children: [ Icon(Icons.error_outline, size: 20, color: AppColors.red600), SizedBox(width: AppSpacing.sm), Expanded( child: Text( message, style: TextStyle(fontSize: 14, color: AppColors.red600), ), ), ], ), ); } static Widget _renderUnknownCard(UiCard card) { return Container( padding: EdgeInsets.all(AppSpacing.lg), decoration: BoxDecoration( color: AppColors.messageCardBg, borderRadius: BorderRadius.circular(AppRadius.lg), border: Border.all(color: AppColors.border), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '未知卡片类型: ${card.cardType}', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: AppColors.slate600, ), ), SizedBox(height: AppSpacing.sm), Text( card.data.toString(), style: TextStyle(fontSize: 12, color: AppColors.slate500), ), ], ), ); } static String _formatTime(String startAt, String? endAt) { try { final start = DateTime.parse(startAt); final buffer = StringBuffer(); buffer.write('${start.month}月${start.day}日 '); buffer.write( '${start.hour.toString().padLeft(2, '0')}:${start.minute.toString().padLeft(2, '0')}', ); if (endAt != null) { final end = DateTime.parse(endAt); buffer.write( ' - ${end.hour.toString().padLeft(2, '0')}:${end.minute.toString().padLeft(2, '0')}', ); } return buffer.toString(); } catch (e) { return startAt; } } static void _handleAction(CardAction action) { // TODO: 实现 action 处理 } }