feat: 新增流程总览图页面及导航功能

添加流程总览图页面,包含五组十域可交互SVG流程图,支持模块点击跳转至对应流程详情页。同时在侧边栏和首页添加导航入口,优化流程详情页的返回逻辑和布局样式。
This commit is contained in:
史悦
2026-02-06 10:59:26 +08:00
parent 59974c4969
commit 409e388403
8 changed files with 408 additions and 16 deletions

View File

@@ -6,6 +6,7 @@ import { ProcessGroupsPage } from './pages/ProcessGroupsPage'
import { ProcessDetailPage } from './pages/ProcessDetailPage'
import { ProcessMatrixPage } from './pages/ProcessMatrixPage'
import { ProcessGraphPage } from './pages/ProcessGraphPage'
import { ProcessRoadmapPage } from './pages/ProcessRoadmapPage'
import { ArtifactDetailPage } from './pages/ArtifactDetailPage'
import { ToolDetailPage } from './pages/ToolDetailPage'
import { SettingsPage } from './pages/SettingsPage'
@@ -21,6 +22,7 @@ function App() {
<Route path="/process-groups/:id" element={<ProcessGroupsPage />} />
<Route path="/process/:id" element={<ProcessDetailPage />} />
<Route path="/process-matrix" element={<ProcessMatrixPage />} />
<Route path="/process-roadmap" element={<ProcessRoadmapPage />} />
<Route path="/process-graph" element={<ProcessGraphPage />} />
<Route path="/artifact/:id" element={<ArtifactDetailPage />} />
<Route path="/tool/:id" element={<ToolDetailPage />} />

View File

@@ -30,9 +30,7 @@ export function Layout({ children }: LayoutProps) {
sidebarOpen ? 'lg:ml-64' : 'lg:ml-20'
)}
>
<div className="mx-auto max-w-7xl">
{children}
</div>
{children}
</main>
</div>
</div>

View File

@@ -6,6 +6,7 @@ import {
BookOpen,
Layers,
LayoutGrid,
Workflow,
Share2,
Settings,
ChevronLeft,
@@ -15,6 +16,7 @@ import {
const navItems = [
{ path: '/', label: '首页', icon: Home },
{ path: '/process-matrix', label: '49过程矩阵', icon: LayoutGrid },
{ path: '/process-roadmap', label: '流程总览图', icon: Workflow },
{ path: '/knowledge-areas', label: '知识领域', icon: BookOpen },
{ path: '/process-groups', label: '过程组', icon: Layers },
{ path: '/process-graph', label: '过程关系图', icon: Share2 },

View File

@@ -110,18 +110,26 @@ export function ProcessMatrix({ className, isFullScreen = false }: ProcessMatrix
state={{ from: 'matrix' }}
className={clsx(
"block px-2 py-1.5 rounded text-xs hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors",
isFullScreen && "flex items-center h-full"
isFullScreen && "h-full"
)}
title={p.name}
>
<span
className="inline-block px-1.5 py-0.5 rounded text-white font-medium mr-1 shrink-0"
style={{ backgroundColor: ka.color, fontSize: '10px' }}
>
{p.code}
</span>
<span className="text-gray-700 dark:text-gray-300 line-clamp-2">
{p.name}
</span>
<div className={clsx("flex items-center gap-1 min-w-0", isFullScreen && "h-full")}>
<span
className="inline-block px-1.5 py-0.5 rounded text-white font-medium shrink-0"
style={{ backgroundColor: ka.color, fontSize: '10px' }}
>
{p.code}
</span>
<span
className={clsx(
"block min-w-0 text-gray-700 dark:text-gray-300",
isFullScreen ? "line-clamp-2" : "truncate"
)}
>
{p.name}
</span>
</div>
</Link>
))}
{cellProcesses.length === 0 && (

View File

@@ -1,6 +1,6 @@
import { Link } from 'react-router-dom'
import { motion } from 'framer-motion'
import { BookOpen, Layers, LayoutGrid, ArrowRight } from 'lucide-react'
import { BookOpen, Layers, LayoutGrid, Workflow, ArrowRight } from 'lucide-react'
import { stats } from '@/data'
const features = [
@@ -12,6 +12,14 @@ const features = [
color: 'from-emerald-500 to-teal-500',
count: stats.processCount,
},
{
icon: Workflow,
title: '流程总览图',
description: '五组十域可交互SVG流程图支持模块点击跳转',
link: '/process-roadmap',
color: 'from-orange-500 to-amber-500',
count: null,
},
{
icon: BookOpen,
title: '知识领域',
@@ -116,7 +124,7 @@ export function HomePage() {
variants={containerVariants}
initial="hidden"
animate="visible"
className="grid md:grid-cols-3 gap-6"
className="grid md:grid-cols-2 xl:grid-cols-4 gap-6"
>
{features.map((feature) => {
const Icon = feature.icon

View File

@@ -1,6 +1,6 @@
import { useParams, Link, useLocation, useNavigate } from 'react-router-dom'
import { motion } from 'framer-motion'
import { ArrowLeft, ArrowRight, FileText, Wrench, FileOutput, LayoutGrid, User, Target, Clock, MapPin, HelpCircle, Cog } from 'lucide-react'
import { ArrowLeft, ArrowRight, FileText, Wrench, FileOutput, LayoutGrid, Workflow, User, Target, Clock, MapPin, HelpCircle, Cog } from 'lucide-react'
import { getProcessDetail, processes, artifactMap, toolMap } from '@/data'
// 5W1H图标和标签配置
@@ -20,6 +20,7 @@ export function ProcessDetailPage() {
const processDetail = id ? getProcessDetail(id) : null
const fromMatrix = location.state?.from === 'matrix'
const fromRoadmap = location.state?.from === 'roadmap'
if (!processDetail) {
return (
@@ -53,6 +54,15 @@ export function ProcessDetailPage() {
</button>
)}
{fromRoadmap && (
<button
onClick={() => navigate('/process-roadmap')}
className="flex items-center gap-1.5 px-2.5 py-1 rounded-lg bg-amber-50 dark:bg-amber-900/30 text-amber-700 dark:text-amber-300 hover:bg-amber-100 dark:hover:bg-amber-900/50 transition-colors font-medium"
>
<Workflow size={14} />
</button>
)}
<nav className="flex items-center gap-1.5 text-gray-500 dark:text-gray-400">
<Link to="/knowledge-areas" className="hover:text-indigo-600 dark:hover:text-indigo-400"></Link>
<span>/</span>
@@ -205,6 +215,7 @@ export function ProcessDetailPage() {
{prevProcess ? (
<Link
to={`/process/${prevProcess.id}`}
state={location.state}
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-sm"
>
<ArrowLeft size={16} />
@@ -217,6 +228,7 @@ export function ProcessDetailPage() {
{nextProcess ? (
<Link
to={`/process/${nextProcess.id}`}
state={location.state}
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 text-sm"
>
<div>

View File

@@ -0,0 +1,361 @@
import { useMemo, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { processes } from '@/data'
interface Hotspot {
id: string
label: string
x: number
y: number
w: number
h: number
to: string
}
const svgStyles = `
.text { font-family: "Microsoft YaHei", "PingFang SC", sans-serif; font-size: 11px; fill: #333; text-anchor: middle; }
.group-title { font-size: 16px; font-weight: bold; }
.box { stroke-width: 1; rx: 4; ry: 4; }
.box-init { fill: #FFF9E6; stroke: #FAD266; }
.box-plan { fill: #EBF5FF; stroke: #91C7F2; }
.box-exec { fill: #F0F9EB; stroke: #C2E7B0; }
.box-moni { fill: #FFF9E6; stroke: #FAD266; }
.box-close { fill: #EBF5FF; stroke: #91C7F2; }
.box-red { fill: #FF0000; stroke: #CC0000; }
.text-white { fill: #FFFFFF; }
.arrow { stroke: #FF0000; stroke-width: 2; fill: none; }
.arrow-grey { stroke: #CCC; stroke-width: 1.5; fill: none; }
.marker { fill: #FF0000; }
.marker-grey { fill: #CCC; }
`
const staticSvgBody = `
<!-- 1. 启动过程组 -->
<rect x="20" y="20" width="100" height="350" class="box box-init" />
<rect x="30" y="30" width="80" height="40" class="box box-init" />
<text x="70" y="55" class="text group-title">启动</text>
<rect x="30" y="85" width="80" height="40" class="box" style="fill:white; stroke:#EEE;" />
<text x="70" y="110" class="text">10.1识别干系人</text>
<rect x="30" y="140" width="80" height="30" class="box box-red" />
<text x="70" y="159" class="text text-white">1.1制定项目章程</text>
<!-- 2. 规划过程组 -->
<rect x="130" y="20" width="450" height="350" class="box box-plan" />
<rect x="140" y="30" width="80" height="40" class="box" style="fill:#A0B7CC; stroke:none;" />
<text x="180" y="55" class="text group-title" style="fill:white;">规划</text>
<!-- 规划左侧列表 -->
<rect x="140" y="80" width="100" height="230" class="box" style="fill:#B8D5EB; stroke:none;" />
<text x="190" y="100" class="text">2.1规划范围管理</text>
<text x="190" y="123" class="text">3.1规划进度管理</text>
<text x="190" y="146" class="text">4.1规划成本管理</text>
<text x="190" y="169" class="text">5.1规划质量管理</text>
<text x="190" y="192" class="text">6.1规划资源管理</text>
<text x="190" y="215" class="text">7.1规划沟通管理</text>
<text x="190" y="238" class="text">8.1规划风险管理</text>
<text x="190" y="261" class="text">9.1规划采购管理</text>
<text x="190" y="284" class="text">10.2规划干系人</text>
<!-- 规划中部流程 -->
<rect x="250" y="30" width="70" height="30" class="box" style="fill:#C7E1F5; stroke:none;" />
<text x="285" y="49" class="text">2.2收集需求</text>
<path d="M320,45 L345,45" class="arrow-grey" marker-end="url(#arrowhead-grey)" />
<rect x="350" y="30" width="70" height="30" class="box" style="fill:#C7E1F5; stroke:none;" />
<text x="385" y="49" class="text">2.3定义范围</text>
<path d="M420,45 L445,45" class="arrow-grey" marker-end="url(#arrowhead-grey)" />
<rect x="450" y="30" width="70" height="30" class="box" style="fill:#C7E1F5; stroke:none;" />
<text x="485" y="49" class="text">2.4创建WBS</text>
<path d="M485,60 L485,85" class="arrow" marker-end="url(#arrowhead)" />
<rect x="250" y="90" width="70" height="35" class="box" style="fill:#C7E1F5; stroke:none;" />
<text x="285" y="102" class="text">6.2估算活动</text>
<text x="285" y="117" class="text">资源</text>
<path d="M285,125 L285,145" class="arrow" marker-end="url(#arrowhead)" />
<rect x="350" y="90" width="70" height="35" class="box" style="fill:#C7E1F5; stroke:none;" />
<text x="385" y="102" class="text">3.3排列活动</text>
<text x="385" y="117" class="text">顺序</text>
<path d="M350,107 L325,107" class="arrow-grey" marker-end="url(#arrowhead-grey)" />
<rect x="450" y="90" width="70" height="35" class="box" style="fill:#C7E1F5; stroke:none;" />
<text x="485" y="110" class="text">3.2定义活动</text>
<path d="M450,107 L425,107" class="arrow-grey" marker-end="url(#arrowhead-grey)" />
<rect x="250" y="150" width="70" height="35" class="box" style="fill:#C7E1F5; stroke:none;" />
<text x="285" y="162" class="text">3.4估算活动</text>
<text x="285" y="177" class="text">持续时间</text>
<path d="M320,167 L345,167" class="arrow-grey" marker-end="url(#arrowhead-grey)" />
<rect x="350" y="150" width="70" height="35" class="box" style="fill:#C7E1F5; stroke:none;" />
<text x="385" y="162" class="text">3.5制定进度</text>
<text x="385" y="177" class="text">计划</text>
<path d="M420,167 L445,167" class="arrow-grey" marker-end="url(#arrowhead-grey)" />
<rect x="450" y="150" width="70" height="35" class="box" style="fill:#C7E1F5; stroke:none;" />
<text x="485" y="167" class="text">4.2估算成本</text>
<path d="M485,185 L485,205" class="arrow" marker-end="url(#arrowhead)" />
<rect x="450" y="210" width="70" height="30" class="box" style="fill:#C7E1F5; stroke:none;" />
<text x="485" y="229" class="text">4.3制定预算</text>
<rect x="250" y="235" width="70" height="30" class="box" style="fill:#C7E1F5; stroke:none;" />
<text x="285" y="254" class="text">8.2识别风险</text>
<path d="M320,250 L345,250" class="arrow" marker-end="url(#arrowhead)" />
<rect x="350" y="235" width="70" height="35" class="box" style="fill:#C7E1F5; stroke:none;" />
<text x="385" y="247" class="text">8.3实施定性</text>
<text x="385" y="262" class="text">风险分析</text>
<path d="M420,250 L445,250" class="arrow" marker-end="url(#arrowhead)" />
<rect x="450" y="235" width="70" height="35" class="box" style="fill:#C7E1F5; stroke:none;" />
<text x="485" y="247" class="text">8.4实施定量</text>
<text x="485" y="262" class="text">风险分析</text>
<path d="M485,270 L485,285" class="arrow" marker-end="url(#arrowhead)" />
<rect x="450" y="290" width="70" height="35" class="box" style="fill:#C7E1F5; stroke:none;" />
<text x="485" y="302" class="text">8.5规划风险</text>
<text x="485" y="317" class="text">应对</text>
<rect x="140" y="325" width="430" height="30" class="box box-red" />
<text x="355" y="344" class="text text-white">1.2 制定项目管理计划</text>
<!-- 3. 执行过程组 -->
<rect x="590" y="20" width="220" height="350" class="box box-exec" />
<rect x="660" y="30" width="80" height="40" class="box" style="fill:#A5C294; stroke:none;" />
<text x="700" y="55" class="text group-title" style="fill:white;">执行</text>
<rect x="600" y="35" width="50" height="30" class="box" style="fill:#E2F0D9; stroke:none;" />
<text x="625" y="54" class="text" style="font-size:9px">6.3获取资源</text>
<path d="M625,65 L625,85" class="arrow" marker-end="url(#arrowhead)" />
<rect x="600" y="90" width="70" height="30" class="box" style="fill:#E2F0D9; stroke:none;" />
<text x="635" y="109" class="text">6.4建设团队</text>
<path d="M670,105 L695,105" class="arrow" marker-end="url(#arrowhead)" />
<rect x="700" y="90" width="70" height="30" class="box" style="fill:#E2F0D9; stroke:none;" />
<text x="735" y="109" class="text">6.5管理团队</text>
<rect x="600" y="140" width="70" height="35" class="box" style="fill:#E2F0D9; stroke:none;" />
<text x="635" y="152" class="text">8.6实施风险</text>
<text x="635" y="167" class="text">应对</text>
<rect x="700" y="140" width="70" height="30" class="box" style="fill:#E2F0D9; stroke:none;" />
<text x="735" y="159" class="text">9.2实施采购</text>
<rect x="600" y="190" width="70" height="30" class="box" style="fill:#E2F0D9; stroke:none;" />
<text x="635" y="209" class="text">7.2管理沟通</text>
<rect x="700" y="190" width="70" height="35" class="box" style="fill:#E2F0D9; stroke:none;" />
<text x="735" y="202" class="text">10.3干系人</text>
<text x="735" y="217" class="text">参与管理</text>
<rect x="600" y="245" width="200" height="30" class="box" style="fill:#E2F0D9; stroke:none;" />
<text x="700" y="264" class="text">5.2管理质量</text>
<rect x="600" y="285" width="80" height="35" class="box box-red" />
<text x="640" y="297" class="text text-white">1.4 管理项目</text>
<text x="640" y="312" class="text text-white">知识</text>
<rect x="600" y="325" width="200" height="30" class="box box-red" />
<text x="700" y="344" class="text text-white">1.3 指导与管理项目工作</text>
<!-- 4. 监控过程组 -->
<rect x="130" y="380" width="530" height="150" class="box box-moni" />
<rect x="140" y="470" width="80" height="40" class="box" style="fill:#FCDD8B; stroke:none;" />
<text x="180" y="495" class="text group-title">监控</text>
<g transform="translate(230, 390)">
<rect x="0" y="0" width="75" height="30" class="box" style="fill:#FFF2CC; stroke:none;" />
<text x="37" y="19" class="text">2.6控制范围</text>
<rect x="85" y="0" width="75" height="30" class="box" style="fill:#FFF2CC; stroke:none;" />
<text x="122" y="19" class="text">6.6控制资源</text>
<rect x="170" y="0" width="75" height="30" class="box" style="fill:#FFF2CC; stroke:none;" />
<text x="207" y="19" class="text">9.3控制采购</text>
<rect x="255" y="0" width="75" height="30" class="box" style="fill:#FFF2CC; stroke:none;" />
<text x="292" y="19" class="text">5.3控制质量</text>
<rect x="0" y="45" width="75" height="30" class="box" style="fill:#FFF2CC; stroke:none;" />
<text x="37" y="64" class="text">3.6控制进度</text>
<rect x="85" y="45" width="75" height="30" class="box" style="fill:#FFF2CC; stroke:none;" />
<text x="122" y="64" class="text">7.3监督沟通</text>
<rect x="170" y="45" width="85" height="35" class="box" style="fill:#FFF2CC; stroke:none;" />
<text x="212" y="57" class="text">10.4监督</text>
<text x="212" y="72" class="text">干系人参与</text>
<rect x="85" y="95" width="75" height="30" class="box" style="fill:#FFF2CC; stroke:none;" />
<text x="122" y="114" class="text">8.7监督风险</text>
<rect x="170" y="95" width="75" height="30" class="box" style="fill:#FFF2CC; stroke:none;" />
<text x="207" y="114" class="text">4.4控制成本</text>
<rect x="255" y="95" width="75" height="30" class="box" style="fill:#FFF2CC; stroke:none;" />
<text x="292" y="114" class="text">2.5确认范围</text>
</g>
<rect x="575" y="400" width="75" height="35" class="box box-red" />
<text x="612" y="412" class="text text-white">1.5监控项目</text>
<text x="612" y="427" class="text text-white">工作</text>
<rect x="575" y="445" width="75" height="35" class="box box-red" />
<text x="612" y="457" class="text text-white">1.6实施整体</text>
<text x="612" y="472" class="text text-white">变更控制</text>
<!-- 5. 收尾过程组 -->
<rect x="670" y="380" width="140" height="150" class="box box-close" />
<rect x="700" y="470" width="80" height="40" class="box" style="fill:#A0B7CC; stroke:none;" />
<text x="740" y="495" class="text group-title" style="fill:white;">收尾</text>
<rect x="690" y="400" width="100" height="35" class="box box-red" />
<text x="740" y="412" class="text text-white">1.7 结束项目</text>
<text x="740" y="427" class="text text-white">或阶段</text>
<!-- 核心流程箭头 -->
<path d="M110,155 L140,325" class="arrow" marker-end="url(#arrowhead)" />
<path d="M570,340 L590,340" class="arrow" marker-end="url(#arrowhead)" />
<path d="M700,355 L700,380 L612,380 L612,395" class="arrow" marker-end="url(#arrowhead)" />
<path d="M650,417 L685,417" class="arrow" marker-end="url(#arrowhead)" />
`
export function ProcessRoadmapPage() {
const navigate = useNavigate()
const [hoveredId, setHoveredId] = useState<string | null>(null)
const processIdByCode = useMemo(
() => new Map(processes.map((process) => [process.code, process.id])),
[]
)
const hotspots = useMemo<Hotspot[]>(() => {
const routeByCode = (code: string) => {
const processId = processIdByCode.get(code)
return processId ? `/process/${processId}` : '/process-matrix'
}
return [
{ id: '10.1', label: '10.1 识别干系人', x: 30, y: 85, w: 80, h: 40, to: routeByCode('10.1') },
{ id: '1.1', label: '1.1 制定项目章程', x: 30, y: 140, w: 80, h: 30, to: routeByCode('1.1') },
{ id: '2.1', label: '2.1 规划范围管理', x: 142, y: 87, w: 96, h: 20, to: routeByCode('2.1') },
{ id: '3.1', label: '3.1 规划进度管理', x: 142, y: 110, w: 96, h: 20, to: routeByCode('3.1') },
{ id: '4.1', label: '4.1 规划成本管理', x: 142, y: 133, w: 96, h: 20, to: routeByCode('4.1') },
{ id: '5.1', label: '5.1 规划质量管理', x: 142, y: 156, w: 96, h: 20, to: routeByCode('5.1') },
{ id: '6.1', label: '6.1 规划资源管理', x: 142, y: 179, w: 96, h: 20, to: routeByCode('6.1') },
{ id: '7.1', label: '7.1 规划沟通管理', x: 142, y: 202, w: 96, h: 20, to: routeByCode('7.1') },
{ id: '8.1', label: '8.1 规划风险管理', x: 142, y: 225, w: 96, h: 20, to: routeByCode('8.1') },
{ id: '9.1', label: '9.1 规划采购管理', x: 142, y: 248, w: 96, h: 20, to: routeByCode('9.1') },
{ id: '10.2', label: '10.2 规划相关方参与', x: 142, y: 271, w: 96, h: 20, to: routeByCode('10.2') },
{ id: '2.2', label: '2.2 收集需求', x: 250, y: 30, w: 70, h: 30, to: routeByCode('2.2') },
{ id: '2.3', label: '2.3 定义范围', x: 350, y: 30, w: 70, h: 30, to: routeByCode('2.3') },
{ id: '2.4', label: '2.4 创建WBS', x: 450, y: 30, w: 70, h: 30, to: routeByCode('2.4') },
{ id: '6.2', label: '6.2 估算活动资源', x: 250, y: 90, w: 70, h: 35, to: routeByCode('6.2') },
{ id: '3.3', label: '3.3 排列活动顺序', x: 350, y: 90, w: 70, h: 35, to: routeByCode('3.3') },
{ id: '3.2', label: '3.2 定义活动', x: 450, y: 90, w: 70, h: 35, to: routeByCode('3.2') },
{ id: '3.4', label: '3.4 估算活动持续时间', x: 250, y: 150, w: 70, h: 35, to: routeByCode('3.4') },
{ id: '3.5', label: '3.5 制定进度计划', x: 350, y: 150, w: 70, h: 35, to: routeByCode('3.5') },
{ id: '4.2', label: '4.2 估算成本', x: 450, y: 150, w: 70, h: 35, to: routeByCode('4.2') },
{ id: '4.3', label: '4.3 制定预算', x: 450, y: 210, w: 70, h: 30, to: routeByCode('4.3') },
{ id: '8.2', label: '8.2 识别风险', x: 250, y: 235, w: 70, h: 30, to: routeByCode('8.2') },
{ id: '8.3', label: '8.3 实施定性风险分析', x: 350, y: 235, w: 70, h: 35, to: routeByCode('8.3') },
{ id: '8.4', label: '8.4 实施定量风险分析', x: 450, y: 235, w: 70, h: 35, to: routeByCode('8.4') },
{ id: '8.5', label: '8.5 规划风险应对', x: 450, y: 290, w: 70, h: 35, to: routeByCode('8.5') },
{ id: '1.2', label: '1.2 制定项目管理计划', x: 140, y: 325, w: 430, h: 30, to: routeByCode('1.2') },
{ id: '6.3', label: '6.3 获取资源', x: 600, y: 35, w: 50, h: 30, to: routeByCode('6.3') },
{ id: '6.4', label: '6.4 建设团队', x: 600, y: 90, w: 70, h: 30, to: routeByCode('6.4') },
{ id: '6.5', label: '6.5 管理团队', x: 700, y: 90, w: 70, h: 30, to: routeByCode('6.5') },
{ id: '8.6', label: '8.6 实施风险应对', x: 600, y: 140, w: 70, h: 35, to: routeByCode('8.6') },
{ id: '9.2', label: '9.2 实施采购', x: 700, y: 140, w: 70, h: 30, to: routeByCode('9.2') },
{ id: '7.2', label: '7.2 管理沟通', x: 600, y: 190, w: 70, h: 30, to: routeByCode('7.2') },
{ id: '10.3', label: '10.3 管理相关方参与', x: 700, y: 190, w: 70, h: 35, to: routeByCode('10.3') },
{ id: '5.2', label: '5.2 管理质量', x: 600, y: 245, w: 200, h: 30, to: routeByCode('5.2') },
{ id: '1.4', label: '1.4 管理项目知识', x: 600, y: 285, w: 80, h: 35, to: routeByCode('1.4') },
{ id: '1.3', label: '1.3 指导与管理项目工作', x: 600, y: 325, w: 200, h: 30, to: routeByCode('1.3') },
{ id: '2.6', label: '2.6 控制范围', x: 230, y: 390, w: 75, h: 30, to: routeByCode('2.6') },
{ id: '6.6', label: '6.6 控制资源', x: 315, y: 390, w: 75, h: 30, to: routeByCode('6.6') },
{ id: '9.3', label: '9.3 控制采购', x: 400, y: 390, w: 75, h: 30, to: routeByCode('9.3') },
{ id: '5.3', label: '5.3 控制质量', x: 485, y: 390, w: 75, h: 30, to: routeByCode('5.3') },
{ id: '3.6', label: '3.6 控制进度', x: 230, y: 435, w: 75, h: 30, to: routeByCode('3.6') },
{ id: '7.3', label: '7.3 监督沟通', x: 315, y: 435, w: 75, h: 30, to: routeByCode('7.3') },
{ id: '10.4', label: '10.4 监督相关方参与', x: 400, y: 435, w: 85, h: 35, to: routeByCode('10.4') },
{ id: '8.7', label: '8.7 监督风险', x: 315, y: 485, w: 75, h: 30, to: routeByCode('8.7') },
{ id: '4.4', label: '4.4 控制成本', x: 400, y: 485, w: 75, h: 30, to: routeByCode('4.4') },
{ id: '2.5', label: '2.5 确认范围', x: 485, y: 485, w: 75, h: 30, to: routeByCode('2.5') },
{ id: '1.5', label: '1.5 监控项目工作', x: 575, y: 400, w: 75, h: 35, to: routeByCode('1.5') },
{ id: '1.6', label: '1.6 实施整体变更控制', x: 575, y: 445, w: 75, h: 35, to: routeByCode('1.6') },
{ id: '1.7', label: '1.7 结束项目或阶段', x: 690, y: 400, w: 100, h: 35, to: routeByCode('1.7') },
]
}, [processIdByCode])
const handleOpen = (to: string) => {
if (to.startsWith('/process/')) {
navigate(to, { state: { from: 'roadmap' } })
return
}
navigate(to)
}
return (
<div className="space-y-4">
<div>
<h1 className="text-2xl font-bold text-gray-900 dark:text-white"></h1>
</div>
<div className="rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 p-2">
<svg
width="1260"
height="840"
viewBox="0 0 840 560"
className="block w-full h-auto max-w-[1260px] mx-auto"
>
<style>{svgStyles}</style>
<defs>
<marker id="arrowhead" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto">
<path d="M0,0 L6,3 L0,6 Z" className="marker" />
</marker>
<marker id="arrowhead-grey" markerWidth="5" markerHeight="5" refX="4" refY="2.5" orient="auto">
<path d="M0,0 L5,2.5 L0,5 Z" className="marker-grey" />
</marker>
</defs>
<g dangerouslySetInnerHTML={{ __html: staticSvgBody }} />
{hotspots.map((spot) => {
const isHovered = hoveredId === spot.id
return (
<g
key={spot.id}
role="button"
tabIndex={0}
onClick={() => handleOpen(spot.to)}
onMouseEnter={() => setHoveredId(spot.id)}
onMouseLeave={() => setHoveredId(null)}
onFocus={() => setHoveredId(spot.id)}
onBlur={() => setHoveredId(null)}
onKeyDown={(event) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault()
handleOpen(spot.to)
}
}}
style={{ cursor: 'pointer' }}
>
<title>{`${spot.label}(点击进入)`}</title>
<rect
x={spot.x}
y={spot.y}
width={spot.w}
height={spot.h}
fill={isHovered ? 'rgba(245, 158, 11, 0.12)' : 'transparent'}
stroke={isHovered ? '#F59E0B' : 'transparent'}
strokeWidth={1.5}
rx={4}
/>
</g>
)
})}
</svg>
</div>
</div>
)
}

View File

@@ -3,6 +3,7 @@ export { KnowledgeAreasPage } from './KnowledgeAreasPage'
export { ProcessGroupsPage } from './ProcessGroupsPage'
export { ProcessDetailPage } from './ProcessDetailPage'
export { ProcessMatrixPage } from './ProcessMatrixPage'
export { ProcessRoadmapPage } from './ProcessRoadmapPage'
export { ProcessGraphPage } from './ProcessGraphPage'
export { ArtifactDetailPage } from './ArtifactDetailPage'
export { ToolDetailPage } from './ToolDetailPage'