Files
ittoview/src/pages/KnowledgeAreasPage.tsx
ittoview 492406b540 style(首页): 移除工具技术统计卡片及PMBOK第6版字样
- 首页删除"工具技术"统计卡片,数据不准确不宜展示
- 知识领域页、过程组页副标题去除"PMBOK第6版定义的"前缀
- 侧边栏底部删除"PMBOK 第6版"标签
- 设置页删除"基于 PMBOK 第6版"说明

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

Co-Authored-By: HAPI <noreply@hapi.run>
2026-02-25 08:42:32 +00:00

186 lines
8.3 KiB
TypeScript

import { useEffect, useRef } from 'react'
import { Link, useParams } from 'react-router-dom'
import { motion } from 'framer-motion'
import { ArrowRight, FileText, Wrench, FileOutput, Lightbulb } from 'lucide-react'
import { knowledgeAreas, processesByKnowledgeArea, knowledgeAreaMap, processGroupMap } from '@/data'
const containerVariants = {
hidden: { opacity: 0 },
visible: { opacity: 1, transition: { staggerChildren: 0.03 } },
}
const itemVariants = {
hidden: { opacity: 0, y: 10 },
visible: { opacity: 1, y: 0 },
}
// 跳过动画时的变体(立即显示)
const skipAnimationVariants = {
hidden: { opacity: 1, y: 0 },
visible: { opacity: 1, y: 0 },
}
export function KnowledgeAreasPage() {
const { id } = useParams()
const selectedKA = id ? knowledgeAreaMap.get(id) : null
const processes = id ? processesByKnowledgeArea.get(id) || [] : []
// 检测是否应该跳过动画(返回页面时)
const hasVisitedRef = useRef(false)
const shouldSkipAnimation = hasVisitedRef.current
useEffect(() => {
// 标记已访问,下次渲染时跳过动画
hasVisitedRef.current = true
}, [])
// 根据是否跳过动画选择变体
const activeItemVariants = shouldSkipAnimation ? skipAnimationVariants : itemVariants
if (selectedKA) {
return (
<div className="space-y-4">
{/* 面包屑 */}
<nav className="flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400">
<Link to="/knowledge-areas" className="hover:text-indigo-600 dark:hover:text-indigo-400"></Link>
<span>/</span>
<span className="text-gray-900 dark:text-white">{selectedKA.name}</span>
</nav>
{/* 知识领域标题 - 紧凑版 */}
<motion.div
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
className="rounded-xl p-4"
style={{ backgroundColor: `${selectedKA.color}15` }}
>
<div className="flex items-center gap-3">
<div
className="flex h-12 w-12 items-center justify-center rounded-lg text-white font-bold text-lg"
style={{ backgroundColor: selectedKA.color }}
>
{selectedKA.order}
</div>
<div className="flex-1">
<h1 className="text-xl font-bold text-gray-900 dark:text-white">{selectedKA.name}</h1>
<p className="text-sm text-gray-500 dark:text-gray-400">{selectedKA.nameEn}</p>
</div>
<div className="text-right">
<div className="text-2xl font-bold text-gray-900 dark:text-white">{processes.length}</div>
<div className="text-xs text-gray-500"></div>
</div>
</div>
<p className="mt-2 text-sm text-gray-600 dark:text-gray-300">{selectedKA.description}</p>
</motion.div>
{/* 敏捷裁剪因素 */}
{selectedKA.tailoringFactors && selectedKA.tailoringFactors.length > 0 && (
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.1 }}
className="bg-white dark:bg-gray-800 rounded-lg p-4 shadow-sm border border-gray-100 dark:border-gray-700"
>
<div className="flex items-center gap-2 mb-3">
<Lightbulb size={18} className="text-amber-500" />
<h2 className="text-base font-semibold text-gray-900 dark:text-white"></h2>
</div>
<div className="space-y-3">
{selectedKA.tailoringFactors.map((factor, index) => (
<div key={index} className="flex gap-3">
<div className="flex-shrink-0 w-6 h-6 rounded-full bg-amber-100 dark:bg-amber-900/30 flex items-center justify-center text-amber-600 dark:text-amber-400 text-xs font-medium">
{index + 1}
</div>
<div className="flex-1">
<h3 className="text-sm font-medium text-gray-900 dark:text-white mb-1">{factor.title}</h3>
<p className="text-xs text-gray-600 dark:text-gray-400">{factor.description}</p>
</div>
</div>
))}
</div>
</motion.div>
)}
{/* 过程列表 - 紧凑版 */}
<motion.div variants={containerVariants} initial="hidden" animate="visible" className="space-y-2">
{processes.map((process) => {
const pg = processGroupMap.get(process.processGroupId)
return (
<motion.div key={process.id} variants={activeItemVariants}>
<Link
to={`/process/${process.id}`}
className="group flex items-center gap-3 bg-white dark:bg-gray-800 rounded-lg p-3 shadow-sm border border-gray-100 dark:border-gray-700 hover:shadow-md hover:border-gray-200 dark:hover:border-gray-600 transition-all"
>
<div
className="flex h-9 w-9 items-center justify-center rounded-lg text-white font-medium text-sm shrink-0"
style={{ backgroundColor: selectedKA.color }}
>
{process.code}
</div>
<div className="flex-1 min-w-0">
<h3 className="font-medium text-gray-900 dark:text-white text-sm truncate">{process.name}</h3>
<p className="text-xs text-gray-500 dark:text-gray-400 truncate">{process.nameEn}</p>
</div>
<div className="flex items-center gap-3 text-xs text-gray-500 dark:text-gray-400 shrink-0">
<span className="flex items-center gap-1"><FileText size={12} />{process.inputs.length}</span>
<span className="flex items-center gap-1"><Wrench size={12} />{process.tools.length}</span>
<span className="flex items-center gap-1"><FileOutput size={12} />{process.outputs.length}</span>
{pg && (
<span className="px-2 py-0.5 rounded-full text-xs font-medium text-white hidden sm:inline" style={{ backgroundColor: pg.color }}>
{pg.name}
</span>
)}
<ArrowRight size={16} className="text-gray-400 group-hover:text-gray-600 dark:group-hover:text-gray-300 group-hover:translate-x-0.5 transition-all" />
</div>
</Link>
</motion.div>
)
})}
</motion.div>
</div>
)
}
// 显示知识领域列表 - 紧凑版双列
return (
<div className="space-y-4">
<div>
<h1 className="text-xl font-bold text-gray-900 dark:text-white"></h1>
<p className="text-sm text-gray-500 dark:text-gray-400">10</p>
</div>
<motion.div
variants={containerVariants}
initial="hidden"
animate="visible"
className="grid md:grid-cols-2 gap-3"
>
{knowledgeAreas.map((ka) => (
<motion.div key={ka.id} variants={activeItemVariants}>
<Link
to={`/knowledge-areas/${ka.id}`}
className="group flex items-center gap-3 bg-white dark:bg-gray-800 rounded-lg p-3 shadow-sm border border-gray-100 dark:border-gray-700 hover:shadow-md transition-all"
style={{ borderLeftWidth: 3, borderLeftColor: ka.color }}
>
<div
className="flex h-10 w-10 items-center justify-center rounded-lg text-white font-bold shrink-0"
style={{ backgroundColor: ka.color }}
>
{ka.order}
</div>
<div className="flex-1 min-w-0">
<h3 className="font-semibold text-gray-900 dark:text-white text-sm">{ka.name}</h3>
<p className="text-xs text-gray-500 dark:text-gray-400 truncate">{ka.nameEn}</p>
</div>
<div className="flex items-center gap-2 shrink-0">
<span className="text-sm font-medium text-gray-600 dark:text-gray-300">{ka.processCount}</span>
<ArrowRight size={16} className="text-gray-400 group-hover:text-gray-600 dark:group-hover:text-gray-300 group-hover:translate-x-0.5 transition-all" />
</div>
</Link>
</motion.div>
))}
</motion.div>
</div>
)
}