feat: 支持 ITTO 明细功能

- 更新类型定义,支持 ProcessRef(字符串或对象)
- 添加 DetailItem 和 ProcessEntityUse 接口
- 为 P1.2(制定项目管理计划)添加工具明细示例
  - 数据收集:头脑风暴、核对单、焦点小组、访谈
  - 人际关系与团队技能:冲突管理、引导、会议管理
- 更新数据查询函数,支持新数据结构
- 更新前端展示,支持明细显示(带缩进和项目符号)
- 修复 ProcessGraphPage 类型错误

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

Co-Authored-By: HAPI <noreply@hapi.run>
This commit is contained in:
ittoview
2026-02-14 13:49:42 +00:00
parent 3c1451ca3f
commit 145e6e7549
5 changed files with 163 additions and 34 deletions

View File

@@ -1,7 +1,7 @@
import { useParams, Link, useLocation, useNavigate } from 'react-router-dom'
import { motion, AnimatePresence } from 'framer-motion'
import { ArrowLeft, ArrowRight, FileText, Wrench, FileOutput, LayoutGrid, Workflow, User, Target, Clock, MapPin, HelpCircle, Cog, Eye, EyeOff } from 'lucide-react'
import { getProcessDetail, processes, artifactMap, toolMap } from '@/data'
import { getProcessDetail, processes } from '@/data'
import { useState, useEffect } from 'react'
// 5W1H图标和标签配置
@@ -237,12 +237,29 @@ export function ProcessDetailPage() {
transition={{ duration: 0.18 }}
className="divide-y divide-gray-100 dark:divide-gray-700"
>
{processDetail.inputs.map((inputId) => {
const artifact = artifactMap.get(inputId)
{processDetail.inputDetails?.map((inputDetail: any) => {
const hasDetail = inputDetail.detail && inputDetail.detail.length > 0
return (
<li key={inputId} className="px-3 py-2 hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors">
<div className="font-medium text-gray-900 dark:text-white text-sm">{artifact?.name || inputId}</div>
{artifact && <div className="text-xs text-gray-500 dark:text-gray-400">{artifact.nameEn}</div>}
<li key={inputDetail.id} className="px-3 py-2 hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors">
<div className="font-medium text-gray-900 dark:text-white text-sm">{inputDetail.name || inputDetail.id}</div>
{inputDetail.nameEn && <div className="text-xs text-gray-500 dark:text-gray-400">{inputDetail.nameEn}</div>}
{hasDetail && (
<div className="mt-2 pl-3 border-l-2 border-blue-200 dark:border-blue-700">
<ul className="space-y-1">
{inputDetail.detail.map((item: any, idx: number) => (
<li key={item.id || idx} className="text-xs text-gray-600 dark:text-gray-400 flex items-start gap-1.5">
<span className="text-blue-500 dark:text-blue-400 mt-0.5"></span>
<span>{item.label}</span>
</li>
))}
</ul>
</div>
)}
{inputDetail.note && (
<div className="mt-1.5 text-xs text-gray-500 dark:text-gray-400 italic">
💡 {inputDetail.note}
</div>
)}
</li>
)
})}
@@ -290,12 +307,29 @@ export function ProcessDetailPage() {
transition={{ duration: 0.18 }}
className="divide-y divide-gray-100 dark:divide-gray-700"
>
{processDetail.tools.map((toolId) => {
const tool = toolMap.get(toolId)
{processDetail.toolDetails?.map((toolDetail: any) => {
const hasDetail = toolDetail.detail && toolDetail.detail.length > 0
return (
<li key={toolId} className="px-3 py-2 hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors">
<div className="font-medium text-gray-900 dark:text-white text-sm">{tool?.name || toolId}</div>
{tool && <div className="text-xs text-gray-500 dark:text-gray-400">{tool.nameEn}</div>}
<li key={toolDetail.id} className="px-3 py-2 hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors">
<div className="font-medium text-gray-900 dark:text-white text-sm">{toolDetail.name || toolDetail.id}</div>
{toolDetail.nameEn && <div className="text-xs text-gray-500 dark:text-gray-400">{toolDetail.nameEn}</div>}
{hasDetail && (
<div className="mt-2 pl-3 border-l-2 border-amber-200 dark:border-amber-700">
<ul className="space-y-1">
{toolDetail.detail.map((item: any, idx: number) => (
<li key={item.id || idx} className="text-xs text-gray-600 dark:text-gray-400 flex items-start gap-1.5">
<span className="text-amber-500 dark:text-amber-400 mt-0.5"></span>
<span>{item.label}</span>
</li>
))}
</ul>
</div>
)}
{toolDetail.note && (
<div className="mt-1.5 text-xs text-gray-500 dark:text-gray-400 italic">
💡 {toolDetail.note}
</div>
)}
</li>
)
})}
@@ -343,12 +377,29 @@ export function ProcessDetailPage() {
transition={{ duration: 0.18 }}
className="divide-y divide-gray-100 dark:divide-gray-700"
>
{processDetail.outputs.map((outputId) => {
const artifact = artifactMap.get(outputId)
{processDetail.outputDetails?.map((outputDetail: any) => {
const hasDetail = outputDetail.detail && outputDetail.detail.length > 0
return (
<li key={outputId} className="px-3 py-2 hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors">
<div className="font-medium text-gray-900 dark:text-white text-sm">{artifact?.name || outputId}</div>
{artifact && <div className="text-xs text-gray-500 dark:text-gray-400">{artifact.nameEn}</div>}
<li key={outputDetail.id} className="px-3 py-2 hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors">
<div className="font-medium text-gray-900 dark:text-white text-sm">{outputDetail.name || outputDetail.id}</div>
{outputDetail.nameEn && <div className="text-xs text-gray-500 dark:text-gray-400">{outputDetail.nameEn}</div>}
{hasDetail && (
<div className="mt-2 pl-3 border-l-2 border-emerald-200 dark:border-emerald-700">
<ul className="space-y-1">
{outputDetail.detail.map((item: any, idx: number) => (
<li key={item.id || idx} className="text-xs text-gray-600 dark:text-gray-400 flex items-start gap-1.5">
<span className="text-emerald-500 dark:text-emerald-400 mt-0.5"></span>
<span>{item.label}</span>
</li>
))}
</ul>
</div>
)}
{outputDetail.note && (
<div className="mt-1.5 text-xs text-gray-500 dark:text-gray-400 italic">
💡 {outputDetail.note}
</div>
)}
</li>
)
})}

View File

@@ -18,6 +18,7 @@ import {
getProcessDetail,
getArtifactUsage,
getToolUsage,
extractId,
} from '@/data'
export function ProcessGraphPage() {
@@ -92,8 +93,8 @@ export function ProcessGraphPage() {
// 2. 添加工件节点和关系
const usedArtifacts = new Set<string>()
processes.forEach(p => {
p.inputs.forEach(id => usedArtifacts.add(id))
p.outputs.forEach(id => usedArtifacts.add(id))
p.inputs.forEach(ref => usedArtifacts.add(extractId(ref)))
p.outputs.forEach(ref => usedArtifacts.add(extractId(ref)))
})
artifacts.forEach(a => {
@@ -127,7 +128,7 @@ export function ProcessGraphPage() {
// 3. 添加工具节点和关系
const usedTools = new Set<string>()
processes.forEach(p => {
p.tools.forEach(id => usedTools.add(id))
p.tools.forEach(ref => usedTools.add(extractId(ref)))
})
tools.forEach(t => {
@@ -160,7 +161,8 @@ export function ProcessGraphPage() {
// 4. 构建边
processes.forEach(p => {
// 输入关系: Artifact -> Process
p.inputs.forEach(inputId => {
p.inputs.forEach(inputRef => {
const inputId = extractId(inputRef)
if (addedNodeIds.has(inputId)) {
edges.push({
source: inputId,
@@ -176,7 +178,8 @@ export function ProcessGraphPage() {
})
// 输出关系: Process -> Artifact
p.outputs.forEach(outputId => {
p.outputs.forEach(outputRef => {
const outputId = extractId(outputRef)
if (addedNodeIds.has(outputId)) {
edges.push({
source: p.id,
@@ -192,7 +195,8 @@ export function ProcessGraphPage() {
})
// 工具关系: Tool -> Process
p.tools.forEach(toolId => {
p.tools.forEach(toolRef => {
const toolId = extractId(toolRef)
if (addedNodeIds.has(toolId)) {
edges.push({
source: toolId,