Initial commit

This commit is contained in:
史悦
2026-02-02 18:30:58 +08:00
commit ae1ca8bfaa
40 changed files with 10900 additions and 0 deletions

View File

@@ -0,0 +1,239 @@
import { useParams, Link, useLocation, useNavigate } from 'react-router-dom'
import { motion } from 'framer-motion'
import { ArrowLeft, ArrowRight, FileText, Wrench, FileOutput, LayoutGrid } from 'lucide-react'
import { getProcessDetail, processes, artifactMap, toolMap } from '@/data'
export function ProcessDetailPage() {
const { id } = useParams()
const location = useLocation()
const navigate = useNavigate()
const processDetail = id ? getProcessDetail(id) : null
// 检查是否从矩阵页面来
const fromMatrix = location.state?.from === 'matrix'
if (!processDetail) {
return (
<div className="flex flex-col items-center justify-center py-20">
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
</h2>
<Link
to="/knowledge-areas"
className="text-indigo-600 dark:text-indigo-400 hover:underline"
>
</Link>
</div>
)
}
const ka = processDetail.knowledgeArea
const pg = processDetail.processGroup
// 获取前后过程
const currentIndex = processes.findIndex(p => p.id === id)
const prevProcess = currentIndex > 0 ? processes[currentIndex - 1] : null
const nextProcess = currentIndex < processes.length - 1 ? processes[currentIndex + 1] : null
return (
<div className="space-y-6">
{/* 返回按钮 + 面包屑 */}
<div className="flex items-center gap-4">
{fromMatrix && (
<button
onClick={() => navigate('/process-matrix')}
className="flex items-center gap-2 px-3 py-1.5 rounded-lg bg-indigo-50 dark:bg-indigo-900/30 text-indigo-600 dark:text-indigo-400 hover:bg-indigo-100 dark:hover:bg-indigo-900/50 transition-colors text-sm font-medium"
>
<LayoutGrid size={16} />
</button>
)}
<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>
{ka && (
<>
<Link
to={`/knowledge-areas/${ka.id}`}
className="hover:text-indigo-600 dark:hover:text-indigo-400"
>
{ka.name}
</Link>
<span>/</span>
</>
)}
<span className="text-gray-900 dark:text-white">{processDetail.name}</span>
</nav>
</div>
{/* 过程标题 */}
<motion.div
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
className="rounded-xl p-6 bg-white dark:bg-gray-800 shadow-sm border border-gray-100 dark:border-gray-700"
>
<div className="flex items-start gap-4">
<div
className="flex h-16 w-16 items-center justify-center rounded-xl text-white font-bold text-xl shrink-0"
style={{ backgroundColor: ka?.color || '#6366F1' }}
>
{processDetail.code}
</div>
<div className="flex-1">
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">
{processDetail.name}
</h1>
<p className="text-gray-500 dark:text-gray-400">{processDetail.nameEn}</p>
<div className="flex items-center gap-3 mt-3">
{ka && (
<span
className="px-3 py-1 rounded-full text-xs font-medium text-white"
style={{ backgroundColor: ka.color }}
>
{ka.name}
</span>
)}
{pg && (
<span
className="px-3 py-1 rounded-full text-xs font-medium text-white"
style={{ backgroundColor: pg.color }}
>
{pg.name}
</span>
)}
</div>
</div>
</div>
</motion.div>
{/* ITTO表格 */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.1 }}
className="grid lg:grid-cols-3 gap-6"
>
{/* 输入 */}
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 overflow-hidden">
<div className="flex items-center gap-2 px-4 py-3 bg-blue-50 dark:bg-blue-900/30 border-b border-blue-100 dark:border-blue-800">
<FileText size={18} className="text-blue-600 dark:text-blue-400" />
<h3 className="font-semibold text-blue-900 dark:text-blue-100">
({processDetail.inputs.length})
</h3>
</div>
<ul className="divide-y divide-gray-100 dark:divide-gray-700">
{processDetail.inputs.map((inputId) => {
const artifact = artifactMap.get(inputId)
return (
<li key={inputId} className="px-4 py-3 hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors">
<div className="font-medium text-gray-900 dark:text-white">
{artifact?.name || inputId}
</div>
{artifact && (
<div className="text-sm text-gray-500 dark:text-gray-400">
{artifact.nameEn}
</div>
)}
</li>
)
})}
</ul>
</div>
{/* 工具与技术 */}
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 overflow-hidden">
<div className="flex items-center gap-2 px-4 py-3 bg-amber-50 dark:bg-amber-900/30 border-b border-amber-100 dark:border-amber-800">
<Wrench size={18} className="text-amber-600 dark:text-amber-400" />
<h3 className="font-semibold text-amber-900 dark:text-amber-100">
({processDetail.tools.length})
</h3>
</div>
<ul className="divide-y divide-gray-100 dark:divide-gray-700">
{processDetail.tools.map((toolId) => {
const tool = toolMap.get(toolId)
return (
<li key={toolId} className="px-4 py-3 hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors">
<div className="font-medium text-gray-900 dark:text-white">
{tool?.name || toolId}
</div>
{tool && (
<div className="text-sm text-gray-500 dark:text-gray-400">
{tool.nameEn}
</div>
)}
</li>
)
})}
</ul>
</div>
{/* 输出 */}
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 overflow-hidden">
<div className="flex items-center gap-2 px-4 py-3 bg-emerald-50 dark:bg-emerald-900/30 border-b border-emerald-100 dark:border-emerald-800">
<FileOutput size={18} className="text-emerald-600 dark:text-emerald-400" />
<h3 className="font-semibold text-emerald-900 dark:text-emerald-100">
({processDetail.outputs.length})
</h3>
</div>
<ul className="divide-y divide-gray-100 dark:divide-gray-700">
{processDetail.outputs.map((outputId) => {
const artifact = artifactMap.get(outputId)
return (
<li key={outputId} className="px-4 py-3 hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors">
<div className="font-medium text-gray-900 dark:text-white">
{artifact?.name || outputId}
</div>
{artifact && (
<div className="text-sm text-gray-500 dark:text-gray-400">
{artifact.nameEn}
</div>
)}
</li>
)
})}
</ul>
</div>
</motion.div>
{/* 前后导航 */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.2 }}
className="flex items-center justify-between pt-6 border-t border-gray-200 dark:border-gray-700"
>
{prevProcess ? (
<Link
to={`/process/${prevProcess.id}`}
className="flex items-center gap-2 text-gray-600 dark:text-gray-400 hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors"
>
<ArrowLeft size={18} />
<div>
<div className="text-xs text-gray-400"></div>
<div className="font-medium">{prevProcess.code} {prevProcess.name}</div>
</div>
</Link>
) : (
<div />
)}
{nextProcess ? (
<Link
to={`/process/${nextProcess.id}`}
className="flex items-center gap-2 text-gray-600 dark:text-gray-400 hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors text-right"
>
<div>
<div className="text-xs text-gray-400"></div>
<div className="font-medium">{nextProcess.code} {nextProcess.name}</div>
</div>
<ArrowRight size={18} />
</Link>
) : (
<div />
)}
</motion.div>
</div>
)
}