refactor(整合): 将更新日志改为模态框实现并补充最近20条更新记录
- 将 ChangelogPage 改为 ChangelogModal 模态框组件 - 移除 /changelog 和 /updates 路由,改为模态框弹出 - 修改 Header 按钮点击行为,触发模态框而非路由跳转 - 根据最近20条 git 提交记录补充更新数据 - 优化模态框样式,支持响应式布局和深色模式 - 修复 JSON 中的引号转义问题 via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Routes, Route, Navigate } from 'react-router-dom'
|
||||
import { Routes, Route } from 'react-router-dom'
|
||||
import { Layout } from './components/layout/Layout'
|
||||
import { HomePage } from './pages/HomePage'
|
||||
import { KnowledgeAreasPage } from './pages/KnowledgeAreasPage'
|
||||
@@ -8,7 +8,6 @@ import { ProcessMatrixPage } from './pages/ProcessMatrixPage'
|
||||
import { ProcessGraphPage } from './pages/ProcessGraphPage'
|
||||
import { ArtifactDetailPage } from './pages/ArtifactDetailPage'
|
||||
import { ToolDetailPage } from './pages/ToolDetailPage'
|
||||
import { ChangelogPage } from './pages/ChangelogPage'
|
||||
import { SettingsPage } from './pages/SettingsPage'
|
||||
import ProcessPracticePage from './pages/ProcessPracticePage'
|
||||
|
||||
@@ -27,8 +26,6 @@ function App() {
|
||||
<Route path="/process-practice" element={<ProcessPracticePage />} />
|
||||
<Route path="/artifact/:id" element={<ArtifactDetailPage />} />
|
||||
<Route path="/tool/:id" element={<ToolDetailPage />} />
|
||||
<Route path="/changelog" element={<ChangelogPage />} />
|
||||
<Route path="/updates" element={<Navigate to="/changelog" replace />} />
|
||||
<Route path="/settings" element={<SettingsPage />} />
|
||||
</Routes>
|
||||
</Layout>
|
||||
|
||||
171
src/components/ChangelogModal.tsx
Normal file
171
src/components/ChangelogModal.tsx
Normal file
@@ -0,0 +1,171 @@
|
||||
import { motion, AnimatePresence } from 'framer-motion'
|
||||
import { X, History, CalendarDays, Tag } from 'lucide-react'
|
||||
import { changelogEntries } from '@/data'
|
||||
import type { ChangelogType } from '@/types/itto'
|
||||
|
||||
interface ChangelogModalProps {
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
const containerVariants = {
|
||||
hidden: { opacity: 0 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
transition: {
|
||||
staggerChildren: 0.06,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const itemVariants = {
|
||||
hidden: { opacity: 0, y: 12 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
duration: 0.25,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const typeMeta: Record<ChangelogType, { label: string; className: string }> = {
|
||||
feat: { label: '新功能', className: 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-300' },
|
||||
fix: { label: '修复', className: 'bg-rose-100 text-rose-700 dark:bg-rose-900/30 dark:text-rose-300' },
|
||||
style: { label: '样式', className: 'bg-pink-100 text-pink-700 dark:bg-pink-900/30 dark:text-pink-300' },
|
||||
refactor: { label: '重构', className: 'bg-violet-100 text-violet-700 dark:bg-violet-900/30 dark:text-violet-300' },
|
||||
docs: { label: '文档', className: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300' },
|
||||
perf: { label: '性能', className: 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300' },
|
||||
test: { label: '测试', className: 'bg-cyan-100 text-cyan-700 dark:bg-cyan-900/30 dark:text-cyan-300' },
|
||||
chore: { label: '工程', className: 'bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300' },
|
||||
}
|
||||
|
||||
function formatDate(date: string) {
|
||||
const [year, month, day] = date.split('-').map(Number)
|
||||
if (!year || !month || !day) return date
|
||||
return `${year}年${month}月${day}日`
|
||||
}
|
||||
|
||||
export function ChangelogModal({ isOpen, onClose }: ChangelogModalProps) {
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{isOpen && (
|
||||
<>
|
||||
{/* 背景遮罩 */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
onClick={onClose}
|
||||
className="fixed inset-0 bg-black/50 z-50"
|
||||
/>
|
||||
|
||||
{/* 模态框 */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.95, y: 20 }}
|
||||
animate={{ opacity: 1, scale: 1, y: 0 }}
|
||||
exit={{ opacity: 0, scale: 0.95, y: 20 }}
|
||||
className="fixed inset-4 md:inset-8 lg:inset-16 z-50 flex items-center justify-center"
|
||||
>
|
||||
<div className="bg-white dark:bg-gray-800 rounded-2xl shadow-2xl w-full h-full flex flex-col overflow-hidden">
|
||||
{/* 头部 */}
|
||||
<div className="flex items-center justify-between px-6 py-4 border-b border-gray-200 dark:border-gray-700 flex-shrink-0">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-indigo-50 dark:bg-indigo-900/50">
|
||||
<History size={20} className="text-indigo-600 dark:text-indigo-400" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-gray-900 dark:text-white">更新日志</h2>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
共 {changelogEntries.length} 条记录
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="flex h-10 w-10 items-center justify-center rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-500 dark:text-gray-400 transition-colors"
|
||||
aria-label="关闭"
|
||||
>
|
||||
<X size={20} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 内容区域 */}
|
||||
<div className="flex-1 overflow-y-auto p-6">
|
||||
{changelogEntries.length > 0 ? (
|
||||
<motion.div
|
||||
variants={containerVariants}
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
className="relative max-w-4xl mx-auto"
|
||||
>
|
||||
{/* 时间轴竖线 */}
|
||||
<div className="absolute left-[15px] top-8 bottom-8 w-[2px] bg-gray-200 dark:bg-gray-700" />
|
||||
|
||||
{/* 更新记录列表 */}
|
||||
<div className="space-y-6">
|
||||
{changelogEntries.map((entry, index) => {
|
||||
const meta = typeMeta[entry.type]
|
||||
return (
|
||||
<motion.div
|
||||
key={entry.id || `${entry.date}-${index}`}
|
||||
variants={itemVariants}
|
||||
className="relative flex gap-6"
|
||||
>
|
||||
{/* 时间轴节点 */}
|
||||
<div className="relative flex-shrink-0">
|
||||
<div className="w-8 h-8 rounded-full bg-white dark:bg-gray-800 border-2 border-indigo-500 dark:border-indigo-400 flex items-center justify-center z-10">
|
||||
<div className="w-3 h-3 rounded-full bg-indigo-500 dark:bg-indigo-400" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 更新卡片 */}
|
||||
<div className="flex-1 pb-2">
|
||||
<div className="bg-gray-50 dark:bg-gray-900 rounded-xl border border-gray-200 dark:border-gray-700 p-4 hover:border-indigo-300 dark:hover:border-indigo-600 transition-colors">
|
||||
{/* 标签行 */}
|
||||
<div className="flex flex-wrap items-center gap-2 mb-2">
|
||||
{/* 类型标签 */}
|
||||
<span className={`inline-flex items-center gap-1 px-2.5 py-1 rounded-md text-xs font-medium ${meta.className}`}>
|
||||
<Tag size={12} />
|
||||
{meta.label}
|
||||
</span>
|
||||
|
||||
{/* 范围标签 */}
|
||||
{entry.scope && (
|
||||
<span className="inline-flex items-center px-2.5 py-1 rounded-md text-xs font-medium bg-gray-200 text-gray-700 dark:bg-gray-700 dark:text-gray-300">
|
||||
{entry.scope}
|
||||
</span>
|
||||
)}
|
||||
|
||||
{/* 日期(桌面端靠右,移动端换行) */}
|
||||
<span className="inline-flex items-center gap-1.5 text-xs text-gray-500 dark:text-gray-400 sm:ml-auto">
|
||||
<CalendarDays size={12} />
|
||||
{formatDate(entry.date)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* 标题 */}
|
||||
<h3 className="text-sm font-medium text-gray-900 dark:text-white">
|
||||
{entry.title}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</motion.div>
|
||||
) : (
|
||||
<div className="flex flex-col items-center justify-center py-16 text-gray-400 dark:text-gray-500">
|
||||
<History size={48} className="mb-4" />
|
||||
<p className="text-sm">暂无更新记录</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
)
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
import { useAppStore } from '@/stores/useAppStore'
|
||||
import { Menu, Search, Sun, Moon, X, History } from 'lucide-react'
|
||||
import { useState, useMemo, useRef, useEffect } from 'react'
|
||||
import { useNavigate, useLocation } from 'react-router-dom'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { processes, artifacts, tools, knowledgeAreaMap } from '@/data'
|
||||
import { ChangelogModal } from '@/components/ChangelogModal'
|
||||
|
||||
interface SearchResult {
|
||||
type: 'process' | 'artifact' | 'tool'
|
||||
@@ -21,11 +22,10 @@ export function Header() {
|
||||
const searchQuery = useAppStore((s) => s.searchQuery)
|
||||
const setSearchQuery = useAppStore((s) => s.setSearchQuery)
|
||||
const [isSearchOpen, setIsSearchOpen] = useState(false)
|
||||
const [isChangelogOpen, setIsChangelogOpen] = useState(false)
|
||||
const searchRef = useRef<HTMLDivElement>(null)
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const isChangelogPage = location.pathname === '/changelog' || location.pathname === '/updates'
|
||||
|
||||
// 搜索结果
|
||||
const searchResults = useMemo<SearchResult[]>(() => {
|
||||
@@ -244,12 +244,8 @@ export function Header() {
|
||||
<div className="flex items-center gap-2">
|
||||
{/* 更新日志 */}
|
||||
<button
|
||||
onClick={() => navigate('/changelog')}
|
||||
className={`flex h-10 w-10 items-center justify-center rounded-lg transition-colors ${
|
||||
isChangelogPage
|
||||
? 'bg-indigo-50 text-indigo-600 dark:bg-indigo-900/50 dark:text-indigo-400'
|
||||
: 'hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-500 dark:text-gray-400'
|
||||
}`}
|
||||
onClick={() => setIsChangelogOpen(true)}
|
||||
className="flex h-10 w-10 items-center justify-center rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-500 dark:text-gray-400 transition-colors"
|
||||
aria-label="查看更新日志"
|
||||
title="更新日志"
|
||||
>
|
||||
@@ -266,6 +262,9 @@ export function Header() {
|
||||
{darkMode ? <Sun size={20} /> : <Moon size={20} />}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 更新日志模态框 */}
|
||||
<ChangelogModal isOpen={isChangelogOpen} onClose={() => setIsChangelogOpen(false)} />
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,144 @@
|
||||
{
|
||||
"changelogEntries": [
|
||||
{
|
||||
"id": "2026-03-08-changelog-page",
|
||||
"id": "2026-03-08-changelog-modal",
|
||||
"date": "2026-03-08",
|
||||
"type": "feat",
|
||||
"title": "新增更新时间轴浏览页面与顶部快捷入口",
|
||||
"scope": "整合"
|
||||
},
|
||||
{
|
||||
"id": "2026-03-08-practice-fix-offset",
|
||||
"date": "2026-03-08",
|
||||
"type": "fix",
|
||||
"title": "修正底部固定区域偏移方式",
|
||||
"scope": "练习"
|
||||
},
|
||||
{
|
||||
"id": "2026-03-08-practice-optimize-layout",
|
||||
"date": "2026-03-08",
|
||||
"type": "feat",
|
||||
"title": "优化辅助信息显示和布局",
|
||||
"scope": "练习"
|
||||
},
|
||||
{
|
||||
"id": "2026-03-07-practice-show-itto",
|
||||
"date": "2026-03-07",
|
||||
"type": "feat",
|
||||
"title": "答对后显示ITTO明细信息",
|
||||
"scope": "练习"
|
||||
},
|
||||
{
|
||||
"id": "2026-03-04-stakeholder-fix-p10-4",
|
||||
"date": "2026-03-04",
|
||||
"type": "fix",
|
||||
"title": "修复P10.4监督干系人参与输出重复项目管理计划",
|
||||
"scope": "相关方"
|
||||
},
|
||||
{
|
||||
"id": "2026-03-04-stakeholder-fix-p10-3",
|
||||
"date": "2026-03-04",
|
||||
"type": "fix",
|
||||
"title": "修复P10.3管理干系人参与输出重复项目管理计划",
|
||||
"scope": "相关方"
|
||||
},
|
||||
{
|
||||
"id": "2026-03-04-stakeholder-fix-identify",
|
||||
"date": "2026-03-04",
|
||||
"type": "fix",
|
||||
"title": "修正识别干系人输出项目管理计划更新",
|
||||
"scope": "相关方"
|
||||
},
|
||||
{
|
||||
"id": "2026-03-04-procurement-fix-output",
|
||||
"date": "2026-03-04",
|
||||
"type": "fix",
|
||||
"title": "修正实施采购输出项目文件更新明细",
|
||||
"scope": "采购"
|
||||
},
|
||||
{
|
||||
"id": "2026-03-04-risk-fix-quantitative-tool",
|
||||
"date": "2026-03-04",
|
||||
"type": "fix",
|
||||
"title": "修正实施定量风险分析工具从决策树分析改为数据分析",
|
||||
"scope": "风险"
|
||||
},
|
||||
{
|
||||
"id": "2026-03-04-risk-fix-identify-output",
|
||||
"date": "2026-03-04",
|
||||
"type": "fix",
|
||||
"title": "修正识别风险输出项目文件更新明细",
|
||||
"scope": "风险"
|
||||
},
|
||||
{
|
||||
"id": "2026-03-04-communication-add-detail",
|
||||
"date": "2026-03-04",
|
||||
"type": "feat",
|
||||
"title": "为管理沟通输出项目沟通记录添加明细",
|
||||
"scope": "沟通"
|
||||
},
|
||||
{
|
||||
"id": "2026-03-04-communication-add-report-tool",
|
||||
"date": "2026-03-04",
|
||||
"type": "feat",
|
||||
"title": "新增项目报告工具并修正管理沟通工具列表",
|
||||
"scope": "沟通"
|
||||
},
|
||||
{
|
||||
"id": "2026-03-04-communication-add-analysis-tool",
|
||||
"date": "2026-03-04",
|
||||
"type": "feat",
|
||||
"title": "新增沟通需求分析工具并修正规划沟通管理工具列表",
|
||||
"scope": "沟通"
|
||||
},
|
||||
{
|
||||
"id": "2026-03-04-resource-add-eef-update",
|
||||
"date": "2026-03-04",
|
||||
"type": "feat",
|
||||
"title": "新增事业环境因素更新并修正资源管理过程输出",
|
||||
"scope": "资源"
|
||||
},
|
||||
{
|
||||
"id": "2026-03-04-quality-fix-tool-name",
|
||||
"date": "2026-03-04",
|
||||
"type": "fix",
|
||||
"title": "修正TT066名称从「测试与检查的规则」改为「测试/产品评估」",
|
||||
"scope": "质量"
|
||||
},
|
||||
{
|
||||
"id": "2026-03-04-quality-add-decision-tool",
|
||||
"date": "2026-03-04",
|
||||
"type": "feat",
|
||||
"title": "新增决策技术工具并应用于规划质量管理和管理质量",
|
||||
"scope": "质量"
|
||||
},
|
||||
{
|
||||
"id": "2026-03-04-close-fix-output",
|
||||
"date": "2026-03-04",
|
||||
"type": "fix",
|
||||
"title": "修复P1.7输出应为项目文件更新而非项目管理计划更新",
|
||||
"scope": "整合"
|
||||
},
|
||||
{
|
||||
"id": "2026-03-04-process-name-fix",
|
||||
"date": "2026-03-04",
|
||||
"type": "fix",
|
||||
"title": "修正P1.2和P3.5过程名称「制定」为「制订」",
|
||||
"scope": "整合"
|
||||
},
|
||||
{
|
||||
"id": "2026-03-04-change-control-fix",
|
||||
"date": "2026-03-04",
|
||||
"type": "fix",
|
||||
"title": "修复P1.6输出移除变更日志保留项目管理计划更新",
|
||||
"scope": "整合"
|
||||
},
|
||||
{
|
||||
"id": "2026-03-03-dify-integration",
|
||||
"date": "2026-03-03",
|
||||
"type": "feat",
|
||||
"title": "集成Dify智能对话助手",
|
||||
"scope": "整合"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
import { motion } from 'framer-motion'
|
||||
import { History, CalendarDays, Tag } from 'lucide-react'
|
||||
import { changelogEntries } from '@/data'
|
||||
import type { ChangelogType } from '@/types/itto'
|
||||
|
||||
const containerVariants = {
|
||||
hidden: { opacity: 0 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
transition: {
|
||||
staggerChildren: 0.06,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const itemVariants = {
|
||||
hidden: { opacity: 0, y: 12 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
duration: 0.25,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const typeMeta: Record<ChangelogType, { label: string; className: string }> = {
|
||||
feat: { label: '新功能', className: 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-300' },
|
||||
fix: { label: '修复', className: 'bg-rose-100 text-rose-700 dark:bg-rose-900/30 dark:text-rose-300' },
|
||||
style: { label: '样式', className: 'bg-pink-100 text-pink-700 dark:bg-pink-900/30 dark:text-pink-300' },
|
||||
refactor: { label: '重构', className: 'bg-violet-100 text-violet-700 dark:bg-violet-900/30 dark:text-violet-300' },
|
||||
docs: { label: '文档', className: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300' },
|
||||
perf: { label: '性能', className: 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300' },
|
||||
test: { label: '测试', className: 'bg-cyan-100 text-cyan-700 dark:bg-cyan-900/30 dark:text-cyan-300' },
|
||||
chore: { label: '工程', className: 'bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300' },
|
||||
}
|
||||
|
||||
function formatDate(date: string) {
|
||||
const [year, month, day] = date.split('-').map(Number)
|
||||
if (!year || !month || !day) return date
|
||||
return `${year}年${month}月${day}日`
|
||||
}
|
||||
|
||||
export function ChangelogPage() {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* 页面标题 */}
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">更新日志</h1>
|
||||
<p className="text-gray-500 dark:text-gray-400 mt-1">
|
||||
记录项目的演进历史和重要变更 · 共 {changelogEntries.length} 条记录
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 时间轴列表 */}
|
||||
{changelogEntries.length > 0 ? (
|
||||
<motion.div
|
||||
variants={containerVariants}
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
className="relative"
|
||||
>
|
||||
{/* 时间轴竖线 */}
|
||||
<div className="absolute left-[15px] top-8 bottom-8 w-[2px] bg-gray-200 dark:bg-gray-700" />
|
||||
|
||||
{/* 更新记录列表 */}
|
||||
<div className="space-y-6">
|
||||
{changelogEntries.map((entry, index) => {
|
||||
const meta = typeMeta[entry.type]
|
||||
return (
|
||||
<motion.div
|
||||
key={entry.id || `${entry.date}-${index}`}
|
||||
variants={itemVariants}
|
||||
className="relative flex gap-6"
|
||||
>
|
||||
{/* 时间轴节点 */}
|
||||
<div className="relative flex-shrink-0">
|
||||
<div className="w-8 h-8 rounded-full bg-white dark:bg-gray-800 border-2 border-indigo-500 dark:border-indigo-400 flex items-center justify-center z-10">
|
||||
<div className="w-3 h-3 rounded-full bg-indigo-500 dark:bg-indigo-400" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 更新卡片 */}
|
||||
<div className="flex-1 pb-2">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 p-5 hover:shadow-md transition-shadow">
|
||||
{/* 标签行 */}
|
||||
<div className="flex flex-wrap items-center gap-2 mb-3">
|
||||
{/* 类型标签 */}
|
||||
<span className={`inline-flex items-center gap-1 px-2.5 py-1 rounded-md text-xs font-medium ${meta.className}`}>
|
||||
<Tag size={12} />
|
||||
{meta.label}
|
||||
</span>
|
||||
|
||||
{/* 范围标签 */}
|
||||
{entry.scope && (
|
||||
<span className="inline-flex items-center px-2.5 py-1 rounded-md text-xs font-medium bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300">
|
||||
{entry.scope}
|
||||
</span>
|
||||
)}
|
||||
|
||||
{/* 日期(桌面端靠右,移动端换行) */}
|
||||
<span className="inline-flex items-center gap-1.5 text-xs text-gray-500 dark:text-gray-400 sm:ml-auto">
|
||||
<CalendarDays size={12} />
|
||||
{formatDate(entry.date)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* 标题 */}
|
||||
<h3 className="text-base font-medium text-gray-900 dark:text-white">
|
||||
{entry.title}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</motion.div>
|
||||
) : (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
className="flex flex-col items-center justify-center py-16 text-gray-400 dark:text-gray-500"
|
||||
>
|
||||
<History size={48} className="mb-4" />
|
||||
<p className="text-sm">暂无更新记录</p>
|
||||
</motion.div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -7,5 +7,4 @@ export { ProcessRoadmapPage } from './ProcessRoadmapPage'
|
||||
export { ProcessGraphPage } from './ProcessGraphPage'
|
||||
export { ArtifactDetailPage } from './ArtifactDetailPage'
|
||||
export { ToolDetailPage } from './ToolDetailPage'
|
||||
export { ChangelogPage } from './ChangelogPage'
|
||||
export { SettingsPage } from './SettingsPage'
|
||||
|
||||
Reference in New Issue
Block a user