- 首页删除"工具技术"统计卡片,数据不准确不宜展示 - 知识领域页、过程组页副标题去除"PMBOK第6版定义的"前缀 - 侧边栏底部删除"PMBOK 第6版"标签 - 设置页删除"基于 PMBOK 第6版"说明 via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run>
138 lines
6.5 KiB
TypeScript
138 lines
6.5 KiB
TypeScript
import { Link, useParams } from 'react-router-dom'
|
|
import { motion } from 'framer-motion'
|
|
import { ArrowRight, FileText, Wrench, FileOutput } from 'lucide-react'
|
|
import { processGroups, processesByProcessGroup, processGroupMap, knowledgeAreaMap } 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 },
|
|
}
|
|
|
|
export function ProcessGroupsPage() {
|
|
const { id } = useParams()
|
|
const selectedPG = id ? processGroupMap.get(id) : null
|
|
const processes = id ? processesByProcessGroup.get(id) || [] : []
|
|
|
|
if (selectedPG) {
|
|
return (
|
|
<div className="space-y-4">
|
|
{/* 面包屑 */}
|
|
<nav className="flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400">
|
|
<Link to="/process-groups" className="hover:text-indigo-600 dark:hover:text-indigo-400">过程组</Link>
|
|
<span>/</span>
|
|
<span className="text-gray-900 dark:text-white">{selectedPG.name}</span>
|
|
</nav>
|
|
|
|
{/* 过程组标题 - 紧凑版 */}
|
|
<motion.div
|
|
initial={{ opacity: 0, y: -10 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
className="rounded-xl p-4"
|
|
style={{ backgroundColor: `${selectedPG.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: selectedPG.color }}
|
|
>
|
|
{selectedPG.order}
|
|
</div>
|
|
<div className="flex-1">
|
|
<h1 className="text-xl font-bold text-gray-900 dark:text-white">{selectedPG.name}</h1>
|
|
<p className="text-sm text-gray-500 dark:text-gray-400">{selectedPG.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">{selectedPG.description}</p>
|
|
</motion.div>
|
|
|
|
{/* 过程列表 - 紧凑版 */}
|
|
<motion.div variants={containerVariants} initial="hidden" animate="visible" className="space-y-2">
|
|
{processes.map((process) => {
|
|
const ka = knowledgeAreaMap.get(process.knowledgeAreaId)
|
|
return (
|
|
<motion.div key={process.id} variants={itemVariants}>
|
|
<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: ka?.color || selectedPG.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>
|
|
{ka && (
|
|
<span className="px-2 py-0.5 rounded-full text-xs font-medium text-white hidden sm:inline" style={{ backgroundColor: ka.color }}>
|
|
{ka.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">5大项目管理过程组</p>
|
|
</div>
|
|
|
|
<motion.div variants={containerVariants} initial="hidden" animate="visible" className="space-y-2">
|
|
{processGroups.map((pg) => (
|
|
<motion.div key={pg.id} variants={itemVariants}>
|
|
<Link
|
|
to={`/process-groups/${pg.id}`}
|
|
className="group flex items-center gap-4 bg-white dark:bg-gray-800 rounded-xl p-4 shadow-sm border border-gray-100 dark:border-gray-700 hover:shadow-md transition-all"
|
|
style={{ borderLeftWidth: 4, borderLeftColor: pg.color }}
|
|
>
|
|
<div
|
|
className="flex h-12 w-12 items-center justify-center rounded-lg text-white font-bold text-lg shrink-0"
|
|
style={{ backgroundColor: pg.color }}
|
|
>
|
|
{pg.order}
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<h3 className="font-semibold text-gray-900 dark:text-white">{pg.name}</h3>
|
|
<p className="text-sm text-gray-500 dark:text-gray-400">{pg.nameEn}</p>
|
|
<p className="text-xs text-gray-400 dark:text-gray-500 mt-1 line-clamp-1">{pg.description}</p>
|
|
</div>
|
|
<div className="flex items-center gap-3 shrink-0">
|
|
<div className="text-right">
|
|
<div className="text-xl font-bold text-gray-900 dark:text-white">{pg.processCount}</div>
|
|
<div className="text-xs text-gray-500">个过程</div>
|
|
</div>
|
|
<ArrowRight size={20} className="text-gray-400 group-hover:text-gray-600 dark:group-hover:text-gray-300 group-hover:translate-x-1 transition-all" />
|
|
</div>
|
|
</Link>
|
|
</motion.div>
|
|
))}
|
|
</motion.div>
|
|
</div>
|
|
)
|
|
}
|