import { Modal, Timeline, Tag, Avatar, Empty, Spin, Button, Space, Tooltip } from 'antd'; import { useState, useEffect } from 'react'; import { BugOutlined, StarOutlined, FileTextOutlined, BgColorsOutlined, ThunderboltOutlined, ExperimentOutlined, ToolOutlined, QuestionCircleOutlined, GithubOutlined, ReloadOutlined, ClockCircleOutlined, } from '@ant-design/icons'; import { fetchChangelog, groupChangelogByDate, getCachedChangelog, cacheChangelog, markChangelogFetched, shouldFetchChangelog, clearChangelogCache, type ChangelogEntry, } from '../services/changelogService'; interface ChangelogModalProps { visible: boolean; onClose: () => void; } // 提交类型图标和颜色配置 const typeConfig: Record = { feature: { icon: , color: 'green', label: '新功能' }, fix: { icon: , color: 'red', label: '修复' }, docs: { icon: , color: 'blue', label: '文档' }, style: { icon: , color: 'purple', label: '样式' }, refactor: { icon: , color: 'orange', label: '重构' }, perf: { icon: , color: 'gold', label: '性能' }, test: { icon: , color: 'cyan', label: '测试' }, chore: { icon: , color: 'default', label: '杂项' }, other: { icon: , color: 'default', label: '其他' }, }; export default function ChangelogModal({ visible, onClose }: ChangelogModalProps) { const [changelog, setChangelog] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [page, setPage] = useState(1); const [hasMore, setHasMore] = useState(true); // 加载更新日志 const loadChangelog = async (pageNum: number = 1, append: boolean = false) => { setLoading(true); setError(null); try { // 如果是第一页,先尝试使用缓存 if (pageNum === 1 && !append) { const cached = getCachedChangelog(); if (cached && cached.length > 0) { setChangelog(cached); // 后台刷新 if (shouldFetchChangelog()) { fetchChangelog(pageNum, 30) .then(entries => { setChangelog(entries); cacheChangelog(entries); markChangelogFetched(); }) .catch(console.error); } setLoading(false); return; } } const entries = await fetchChangelog(pageNum, 30); if (entries.length === 0) { setHasMore(false); } else { if (append) { setChangelog(prev => [...prev, ...entries]); } else { setChangelog(entries); // 缓存第一页数据 if (pageNum === 1) { cacheChangelog(entries); markChangelogFetched(); } } } } catch (err) { setError(err instanceof Error ? err.message : '获取更新日志失败'); } finally { setLoading(false); } }; // 初始加载 useEffect(() => { if (visible) { loadChangelog(1, false); setPage(1); setHasMore(true); } }, [visible]); // 加载更多 const handleLoadMore = () => { const nextPage = page + 1; setPage(nextPage); loadChangelog(nextPage, true); }; // 刷新(清除缓存并重新加载) const handleRefresh = () => { clearChangelogCache(); setPage(1); setHasMore(true); loadChangelog(1, false); }; // 按日期分组 const groupedChangelog = groupChangelogByDate(changelog); const sortedDates = Array.from(groupedChangelog.keys()).sort((a, b) => b.localeCompare(a)); // 格式化日期 const formatDate = (dateStr: string) => { const date = new Date(dateStr); const now = new Date(); const diffDays = Math.floor((now.getTime() - date.getTime()) / (1000 * 60 * 60 * 24)); if (diffDays === 0) return '今天'; if (diffDays === 1) return '昨天'; if (diffDays < 7) return `${diffDays} 天前`; return date.toLocaleDateString('zh-CN', { year: 'numeric', month: 'long', day: 'numeric' }); }; // 格式化时间 const formatTime = (dateStr: string) => { return new Date(dateStr).toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' }); }; return ( 更新日志 )} {!hasMore && changelog.length > 0 && (
已显示所有更新日志
)} )}
💡 提示:更新日志每小时自动刷新一次,数据来源于 GitHub 提交历史
); }