// TimelineGridItem.tsx - Grid card layout for timeline items import TimelineImage from '@/components/TimelineImage'; import { StoryItem } from '@/pages/story/data'; import { DeleteOutlined, EditOutlined } from '@ant-design/icons'; import { useIntl, useRequest } from '@umijs/max'; import { Button, message, Popconfirm, Tag, theme } from 'antd'; import React, { useEffect } from 'react'; import { queryStoryItemImages, removeStoryItem } from '../service'; import TimelineVideo from './TimelineVideo'; // 格式化时间数组为易读格式 const formatTimeArray = (time: string | number[] | undefined): string => { if (!time) return ''; // 如果是数组格式 [2025, 12, 23, 8, 55, 39] if (Array.isArray(time)) { const [, , , hour, minute] = time; return `${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}`; } // 如果已经是字符串格式,提取时间部分 const timeStr = String(time); const timePart = timeStr.split(' ')[1]; if (timePart) { const [hour, minute] = timePart.split(':'); return `${hour}:${minute}`; } return timeStr; }; const TimelineGridItem: React.FC<{ item: StoryItem; handleOption: (item: StoryItem, option: 'add' | 'edit' | 'addSubItem' | 'editSubItem') => void; onOpenDetail: (item: StoryItem) => void; refresh: () => void; disableEdit?: boolean; }> = ({ item, handleOption, onOpenDetail, refresh, disableEdit }) => { const intl = useIntl(); const { token } = theme.useToken(); // 动态设置CSS变量以适配主题 useEffect(() => { const root = document.documentElement; // 根据Ant Design的token设置主题变量 root.style.setProperty('--timeline-bg', token.colorBgContainer); root.style.setProperty('--timeline-card-bg', token.colorBgElevated); root.style.setProperty('--timeline-card-border', token.colorBorder); root.style.setProperty('--timeline-card-shadow', `0 2px 8px ${token.colorBgMask}`); root.style.setProperty('--timeline-text-primary', token.colorText); root.style.setProperty('--timeline-text-secondary', token.colorTextSecondary); root.style.setProperty('--timeline-text-tertiary', token.colorTextTertiary); root.style.setProperty('--timeline-header-color', token.colorPrimary); root.style.setProperty('--timeline-location-bg', `${token.colorSuccess}1A`); // 10% opacity root.style.setProperty('--timeline-location-border', `${token.colorSuccess}4D`); // 30% opacity root.style.setProperty('--timeline-location-color', token.colorSuccess); root.style.setProperty('--timeline-image-border', token.colorBorder); root.style.setProperty('--timeline-more-bg', token.colorBgMask); root.style.setProperty('--timeline-more-color', token.colorWhite); }, [token]); const { data: imagesList } = useRequest( async () => { return await queryStoryItemImages(item.instanceId); }, { refreshDeps: [item.instanceId], }, ); const handleDelete = async () => { try { if (!item.instanceId) return; const response = await removeStoryItem(item.instanceId); if (response.code === 200) { message.success(intl.formatMessage({ id: 'story.deleteSuccess' })); refresh(); } else { message.error(intl.formatMessage({ id: 'story.deleteFailed' })); } } catch (error) { message.error(intl.formatMessage({ id: 'story.deleteFailed' })); } }; // 动态计算卡片大小(1-4格) const calculateCardSize = () => { const imageCount = imagesList?.length || 0; const descriptionLength = item.description?.length || 0; // 根据图片数量和描述长度决定卡片大小 if (imageCount >= 4 || descriptionLength > 200) { return 4; // 占据整行 } else if (imageCount >= 2 || descriptionLength > 100) { return 2; // 占据2格 } else if (imageCount >= 1 && descriptionLength > 50) { return 2; // 有图片且描述较长,占据2格 } else { return 1; // 默认占据1格 } }; const cardSize = calculateCardSize(); // 统一的文本长度 - 根据卡片大小调整 const getDescriptionMaxLength = (size: number) => { switch (size) { case 1: return 80; case 2: return 150; case 3: return 200; case 4: return 300; default: return 100; } }; const descriptionMaxLength = getDescriptionMaxLength(cardSize); // 截断描述文本 const truncateText = (text: string | undefined, maxLength: number) => { if (!text) return ''; return text.length > maxLength ? text.substring(0, maxLength) + '...' : text; }; // 只返回article元素,不包含任何其他元素 return (
onOpenDetail(item)}> {/* Action buttons */} {!disableEdit && (
e.stopPropagation()}>
)} {/* Time header */}

{formatTimeArray(item.storyItemTime)}

{/* Title */}
{item.title}
{/* Description */}

{truncateText(item.description, descriptionMaxLength)}

{/* Images preview - 固定间隔,单行展示,多余折叠 */} {item.videoUrl ? (
) : ( imagesList && imagesList.length > 0 && (
{imagesList .filter((imageInstanceId) => imageInstanceId && imageInstanceId.trim() !== '') .slice(0, 6) // 最多显示6张图片 .map((imageInstanceId, index) => (
))} {imagesList.length > 6 && (
+{imagesList.length - 6}
)}
) )} {/* Location badge */} {item.location && 📍 {item.location}} {/* Creator/Updater tags */}
{item.createName && ( {item.createName} )} {item.updateName && item.updateName !== item.createName && ( {item.updateName} )}
); }; export default TimelineGridItem;