diff --git a/src/data/index.ts b/src/data/index.ts index 9a7e0f1..33f9a8b 100644 --- a/src/data/index.ts +++ b/src/data/index.ts @@ -16,6 +16,8 @@ import type { Artifact, ToolTechnique, DataFlow, + ProcessRef, + ProcessEntityUse, } from '../types/itto'; // 导出原始数据 @@ -64,14 +66,37 @@ processGroups.forEach(pg => { ); }); +// 工具函数:规范化 ProcessRef(将字符串或对象统一处理) +export function normalizeProcessRef(ref: ProcessRef): { + id: string; + detail?: ProcessEntityUse['detail']; + note?: string; +} { + if (typeof ref === 'string') { + return { id: ref }; + } + return ref; +} + +// 工具函数:从 ProcessRef 中提取 ID +export function extractId(ref: ProcessRef): string { + return typeof ref === 'string' ? ref : ref.id; +} + +// 工具函数:检查 ProcessRef 数组中是否包含某个 ID +export function includesId(refs: ProcessRef[], targetId: string): boolean { + return refs.some(ref => extractId(ref) === targetId); +} + // 计算数据流向关系 export function computeDataFlows(): DataFlow[] { const flows: DataFlow[] = []; processes.forEach(sourceProcess => { - sourceProcess.outputs.forEach(outputId => { + sourceProcess.outputs.forEach(outputRef => { + const outputId = extractId(outputRef); processes.forEach(targetProcess => { - if (targetProcess.id !== sourceProcess.id && targetProcess.inputs.includes(outputId)) { + if (targetProcess.id !== sourceProcess.id && includesId(targetProcess.inputs, outputId)) { flows.push({ sourceProcessId: sourceProcess.id, targetProcessId: targetProcess.id, @@ -91,14 +116,14 @@ export function getArtifactUsage(artifactId: string): { asOutput: Process[]; } { return { - asInput: processes.filter(p => p.inputs.includes(artifactId)), - asOutput: processes.filter(p => p.outputs.includes(artifactId)), + asInput: processes.filter(p => includesId(p.inputs, artifactId)), + asOutput: processes.filter(p => includesId(p.outputs, artifactId)), }; } // 获取工具的使用情况 export function getToolUsage(toolId: string): Process[] { - return processes.filter(p => p.tools.includes(toolId)); + return processes.filter(p => includesId(p.tools, toolId)); } // 获取过程的完整信息(包含关联数据) @@ -110,9 +135,21 @@ export function getProcessDetail(processId: string) { ...process, knowledgeArea: knowledgeAreaMap.get(process.knowledgeAreaId), processGroup: processGroupMap.get(process.processGroupId), - inputDetails: process.inputs.map(id => artifactMap.get(id)).filter(Boolean), - toolDetails: process.tools.map(id => toolMap.get(id)).filter(Boolean), - outputDetails: process.outputs.map(id => artifactMap.get(id)).filter(Boolean), + inputDetails: process.inputs.map(ref => { + const normalized = normalizeProcessRef(ref); + const artifact = artifactMap.get(normalized.id); + return artifact ? { ...artifact, detail: normalized.detail, note: normalized.note } : null; + }).filter(Boolean), + toolDetails: process.tools.map(ref => { + const normalized = normalizeProcessRef(ref); + const tool = toolMap.get(normalized.id); + return tool ? { ...tool, detail: normalized.detail, note: normalized.note } : null; + }).filter(Boolean), + outputDetails: process.outputs.map(ref => { + const normalized = normalizeProcessRef(ref); + const artifact = artifactMap.get(normalized.id); + return artifact ? { ...artifact, detail: normalized.detail, note: normalized.note } : null; + }).filter(Boolean), }; } diff --git a/src/data/processes.json b/src/data/processes.json index 6a499a0..0a80ee5 100644 --- a/src/data/processes.json +++ b/src/data/processes.json @@ -29,7 +29,27 @@ "processGroupId": "PG02", "order": 2, "inputs": ["A001", "A092", "A005", "A006"], - "tools": ["TT001", "TT002", "TT022", "TT032"], + "tools": [ + "TT001", + { + "id": "TT002", + "detail": [ + { "label": "头脑风暴" }, + { "label": "核对单" }, + { "label": "焦点小组" }, + { "label": "访谈" } + ] + }, + { + "id": "TT022", + "detail": [ + { "label": "冲突管理" }, + { "label": "引导" }, + { "label": "会议管理" } + ] + }, + "TT032" + ], "outputs": ["A008"], "w5h1": { "who": "项目经理主导,团队参与", diff --git a/src/pages/ProcessDetailPage.tsx b/src/pages/ProcessDetailPage.tsx index 41c4926..a72000d 100644 --- a/src/pages/ProcessDetailPage.tsx +++ b/src/pages/ProcessDetailPage.tsx @@ -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 ( -
  • -
    {artifact?.name || inputId}
    - {artifact &&
    {artifact.nameEn}
    } +
  • +
    {inputDetail.name || inputDetail.id}
    + {inputDetail.nameEn &&
    {inputDetail.nameEn}
    } + {hasDetail && ( +
    + +
    + )} + {inputDetail.note && ( +
    + 💡 {inputDetail.note} +
    + )}
  • ) })} @@ -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 ( -
  • -
    {tool?.name || toolId}
    - {tool &&
    {tool.nameEn}
    } +
  • +
    {toolDetail.name || toolDetail.id}
    + {toolDetail.nameEn &&
    {toolDetail.nameEn}
    } + {hasDetail && ( +
    + +
    + )} + {toolDetail.note && ( +
    + 💡 {toolDetail.note} +
    + )}
  • ) })} @@ -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 ( -
  • -
    {artifact?.name || outputId}
    - {artifact &&
    {artifact.nameEn}
    } +
  • +
    {outputDetail.name || outputDetail.id}
    + {outputDetail.nameEn &&
    {outputDetail.nameEn}
    } + {hasDetail && ( +
    + +
    + )} + {outputDetail.note && ( +
    + 💡 {outputDetail.note} +
    + )}
  • ) })} diff --git a/src/pages/ProcessGraphPage.tsx b/src/pages/ProcessGraphPage.tsx index 03484c3..aa209ea 100644 --- a/src/pages/ProcessGraphPage.tsx +++ b/src/pages/ProcessGraphPage.tsx @@ -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() 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() 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, diff --git a/src/types/itto.ts b/src/types/itto.ts index a315ffa..737c5d7 100644 --- a/src/types/itto.ts +++ b/src/types/itto.ts @@ -36,6 +36,23 @@ export interface Process5W1H { how: string; // 如何执行(关键方法) } +// 明细项 +export interface DetailItem { + id?: string; // 可选:若明细在工具库里注册,可复用该 ID + label: string; // 明细名称 + description?: string; // 明细描述 +} + +// 过程实体使用(支持明细) +export interface ProcessEntityUse { + id: string; // 实体ID(工件/工具ID) + detail?: DetailItem[]; // 明细列表 + note?: string; // 针对此过程的补充说明 +} + +// 过程引用类型(字符串ID 或 带明细的对象) +export type ProcessRef = string | ProcessEntityUse; + // 过程 export interface Process { id: string; // 如 "P4.1" @@ -45,9 +62,9 @@ export interface Process { knowledgeAreaId: string; // 所属知识领域ID processGroupId: string; // 所属过程组ID order: number; // 在知识领域内的序号 - inputs: string[]; // 输入工件ID列表 - tools: string[]; // 工具与技术ID列表 - outputs: string[]; // 输出工件ID列表 + inputs: ProcessRef[]; // 输入工件ID列表(支持明细) + tools: ProcessRef[]; // 工具与技术ID列表(支持明细) + outputs: ProcessRef[]; // 输出工件ID列表(支持明细) w5h1?: Process5W1H; // 5W1H记忆辅助信息 }