feat(整合): 新增更新时间轴浏览页面与顶部快捷入口

- 创建 src/data/changelog.json 数据文件
- 添加 ChangelogType 和 ChangelogEntry 类型定义
- 实现更新时间轴页面组件,支持按时间倒序展示
- 添加 /changelog 主路由和 /updates 别名路由
- 在顶部导航右侧添加 History 图标入口,支持激活态高亮
- 使用 Framer Motion 实现渐进式动画效果
- 支持深色模式和响应式布局

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>
This commit is contained in:
ittoview
2026-03-08 03:13:33 +00:00
parent 9f43f1e0e8
commit 8a02139c85
7 changed files with 190 additions and 3 deletions

View File

@@ -1,4 +1,4 @@
import { Routes, Route } from 'react-router-dom'
import { Routes, Route, Navigate } from 'react-router-dom'
import { Layout } from './components/layout/Layout'
import { HomePage } from './pages/HomePage'
import { KnowledgeAreasPage } from './pages/KnowledgeAreasPage'
@@ -8,6 +8,7 @@ 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'
@@ -26,6 +27,8 @@ 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>

View File

@@ -1,7 +1,7 @@
import { useAppStore } from '@/stores/useAppStore'
import { Menu, Search, Sun, Moon, X } from 'lucide-react'
import { Menu, Search, Sun, Moon, X, History } from 'lucide-react'
import { useState, useMemo, useRef, useEffect } from 'react'
import { useNavigate } from 'react-router-dom'
import { useNavigate, useLocation } from 'react-router-dom'
import { processes, artifacts, tools, knowledgeAreaMap } from '@/data'
interface SearchResult {
@@ -24,6 +24,8 @@ export function Header() {
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[]>(() => {
@@ -240,6 +242,20 @@ 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'
}`}
aria-label="查看更新日志"
title="更新日志"
>
<History size={20} />
</button>
{/* 主题切换 */}
<button
onClick={toggleDarkMode}

11
src/data/changelog.json Normal file
View File

@@ -0,0 +1,11 @@
{
"changelogEntries": [
{
"id": "2026-03-08-changelog-page",
"date": "2026-03-08",
"type": "feat",
"title": "新增更新时间轴浏览页面与顶部快捷入口",
"scope": "整合"
}
]
}

View File

@@ -8,6 +8,7 @@ import processGroupsData from './process-groups.json';
import processesData from './processes.json';
import artifactsData from './artifacts.json';
import toolsData from './tools.json';
import changelogData from './changelog.json';
import type {
KnowledgeArea,
@@ -15,6 +16,7 @@ import type {
Process,
Artifact,
ToolTechnique,
ChangelogEntry,
DataFlow,
ProcessRef,
ProcessEntityUse,
@@ -26,6 +28,9 @@ export const processGroups: ProcessGroup[] = processGroupsData.processGroups as
export const processes: Process[] = processesData.processes as Process[];
export const artifacts: Artifact[] = artifactsData.artifacts as Artifact[];
export const tools: ToolTechnique[] = toolsData.tools as ToolTechnique[];
export const changelogEntries: ChangelogEntry[] = [
...(changelogData.changelogEntries as ChangelogEntry[]),
].sort((a, b) => b.date.localeCompare(a.date));
// 创建查找映射表
export const knowledgeAreaMap = new Map<string, KnowledgeArea>(

131
src/pages/ChangelogPage.tsx Normal file
View File

@@ -0,0 +1,131 @@
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>
)
}

View File

@@ -7,4 +7,5 @@ export { ProcessRoadmapPage } from './ProcessRoadmapPage'
export { ProcessGraphPage } from './ProcessGraphPage'
export { ArtifactDetailPage } from './ArtifactDetailPage'
export { ToolDetailPage } from './ToolDetailPage'
export { ChangelogPage } from './ChangelogPage'
export { SettingsPage } from './SettingsPage'

View File

@@ -152,6 +152,26 @@ export interface LearningStats {
lastStudyDate: Date;
}
// 更新类型
export type ChangelogType =
| 'feat'
| 'fix'
| 'style'
| 'refactor'
| 'docs'
| 'perf'
| 'test'
| 'chore';
// 更新记录
export interface ChangelogEntry {
id?: string; // 可选:推荐提供,便于稳定渲染和后续扩展
date: string; // 日期,格式如 "2026-03-08"
type: ChangelogType; // 更新类型
title: string; // 更新标题
scope?: string; // 关联范围,如"整合""风险""矩阵"
}
// 数据流向关系
export interface DataFlow {
sourceProcessId: string; // 源过程ID