Files
ittoview/src/pages/ProcessMatrixPage.tsx
史悦 6505f977d9 feat(矩阵): 添加知识领域和过程组的显示/隐藏功能
feat(详情页): 为ITTO内容添加显示/隐藏控制功能

refactor: 优化状态管理使用localStorage持久化
2026-02-14 00:42:45 +08:00

211 lines
7.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 49过程矩阵页面
*/
import { useEffect, useState } from 'react'
import { ProcessMatrix } from '@/components/visualize'
import { Maximize2, Minimize2, Eye } from 'lucide-react'
import { useAppStore } from '@/stores/useAppStore'
import { clsx } from 'clsx'
import { knowledgeAreas, processGroups } from '@/data'
const STORAGE_KEY = 'ittoview:process-matrix:hidden-items'
interface HiddenIds {
knowledgeAreas: Set<string>
processGroups: Set<string>
}
// 从 localStorage 加载并清洗无效 ID
function loadHiddenIds(): HiddenIds {
try {
const raw = localStorage.getItem(STORAGE_KEY)
if (!raw) return { knowledgeAreas: new Set<string>(), processGroups: new Set<string>() }
const parsed = JSON.parse(raw)
if (typeof parsed !== 'object' || !parsed) {
return { knowledgeAreas: new Set<string>(), processGroups: new Set<string>() }
}
// 清洗无效 ID
const validKaIds = new Set(knowledgeAreas.map(ka => ka.id))
const validPgIds = new Set(processGroups.map(pg => pg.id))
const kaIds = Array.isArray(parsed.knowledgeAreas)
? parsed.knowledgeAreas.filter((id: string) => validKaIds.has(id))
: []
const pgIds = Array.isArray(parsed.processGroups)
? parsed.processGroups.filter((id: string) => validPgIds.has(id))
: []
return {
knowledgeAreas: new Set(kaIds),
processGroups: new Set(pgIds),
}
} catch {
return { knowledgeAreas: new Set<string>(), processGroups: new Set<string>() }
}
}
export function ProcessMatrixPage() {
const isFullScreen = useAppStore((s) => s.matrixFullScreen)
const setMatrixFullScreen = useAppStore((s) => s.setMatrixFullScreen)
const setSidebarOpen = useAppStore((s) => s.setSidebarOpen)
// 显示/隐藏状态管理
const [hiddenIds, setHiddenIds] = useState(() => loadHiddenIds())
const hiddenKnowledgeAreaIds = hiddenIds.knowledgeAreas
const hiddenProcessGroupIds = hiddenIds.processGroups
// 持久化状态
useEffect(() => {
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify({
knowledgeAreas: Array.from(hiddenKnowledgeAreaIds),
processGroups: Array.from(hiddenProcessGroupIds),
}))
} catch (error) {
console.warn('无法保存矩阵显示状态:', error)
}
}, [hiddenKnowledgeAreaIds, hiddenProcessGroupIds])
const toggleKnowledgeArea = (id: string) => {
setHiddenIds(prev => {
const next = new Set(prev.knowledgeAreas)
if (next.has(id)) {
next.delete(id)
} else {
next.add(id)
}
return { ...prev, knowledgeAreas: next }
})
}
const toggleProcessGroup = (id: string) => {
setHiddenIds(prev => {
const next = new Set(prev.processGroups)
if (next.has(id)) {
next.delete(id)
} else {
next.add(id)
}
return { ...prev, processGroups: next }
})
}
const showAll = () => {
setHiddenIds({ knowledgeAreas: new Set(), processGroups: new Set() })
}
const hasHidden = hiddenKnowledgeAreaIds.size > 0 || hiddenProcessGroupIds.size > 0
const toggleFullScreen = () => {
if (!isFullScreen) {
setSidebarOpen(false)
}
setMatrixFullScreen(!isFullScreen)
}
// Handle Escape key to exit full screen
useEffect(() => {
const handleEsc = (e: KeyboardEvent) => {
if (e.key === 'Escape' && isFullScreen) {
setMatrixFullScreen(false)
}
}
window.addEventListener('keydown', handleEsc)
return () => window.removeEventListener('keydown', handleEsc)
}, [isFullScreen, setMatrixFullScreen])
return (
<div className={clsx(isFullScreen ? "fixed inset-0 z-50 bg-white dark:bg-gray-900 p-0 m-0" : "space-y-6")}>
{/* 隐藏滚动条的样式 */}
{isFullScreen && (
<style>{`
.no-scrollbar::-webkit-scrollbar {
display: none;
}
.no-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}
`}</style>
)}
{!isFullScreen && (
<div className="flex justify-between items-end gap-4">
<div>
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">49</h1>
<p className="text-gray-500 dark:text-gray-400 mt-1">
×
</p>
</div>
<div className="flex items-center gap-2">
{hasHidden && (
<button
onClick={showAll}
className="flex items-center gap-2 px-3 py-2 text-sm font-medium text-indigo-700 dark:text-indigo-300 bg-indigo-50 dark:bg-indigo-900/30 border border-indigo-200 dark:border-indigo-800 rounded-lg hover:bg-indigo-100 dark:hover:bg-indigo-900/50 transition-colors"
>
<Eye className="w-4 h-4" />
<span className="text-xs opacity-75">
( {hiddenKnowledgeAreaIds.size} / {hiddenProcessGroupIds.size} )
</span>
</button>
)}
<button
onClick={toggleFullScreen}
className="flex items-center gap-2 px-3 py-2 text-sm font-medium text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-700 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors shadow-sm"
>
<Maximize2 className="w-4 h-4" />
</button>
</div>
</div>
)}
<div className={clsx(
"bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 overflow-hidden transition-all duration-300",
!isFullScreen && "p-4",
isFullScreen && "h-full w-full border-0 rounded-none shadow-none flex flex-col"
)}>
{isFullScreen && (
<div className="flex justify-between items-center px-4 py-2 bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 shrink-0">
<span className="font-medium text-gray-900 dark:text-white">49</span>
<div className="flex items-center gap-2">
{hasHidden && (
<button
onClick={showAll}
className="flex items-center gap-2 px-3 py-1.5 text-sm font-medium text-indigo-700 dark:text-indigo-300 bg-indigo-50 dark:bg-indigo-900/30 border border-indigo-200 dark:border-indigo-800 rounded-md hover:bg-indigo-100 dark:hover:bg-indigo-900/50 transition-colors"
>
<Eye className="w-4 h-4" />
<span className="text-xs opacity-75">
({hiddenKnowledgeAreaIds.size} / {hiddenProcessGroupIds.size} )
</span>
</button>
)}
<button
onClick={toggleFullScreen}
className="flex items-center gap-2 px-3 py-1.5 text-sm font-medium text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md hover:bg-gray-50 dark:hover:bg-gray-600 transition-colors"
>
<Minimize2 className="w-4 h-4" />
退
</button>
</div>
</div>
)}
<div className={clsx("relative", isFullScreen ? "flex-1 overflow-hidden" : "")}>
<ProcessMatrix
className={clsx(isFullScreen && "h-full w-full overflow-auto no-scrollbar")}
isFullScreen={isFullScreen}
hiddenKnowledgeAreaIds={hiddenKnowledgeAreaIds}
hiddenProcessGroupIds={hiddenProcessGroupIds}
onToggleKnowledgeArea={toggleKnowledgeArea}
onToggleProcessGroup={toggleProcessGroup}
/>
</div>
</div>
</div>
)
}