feat(矩阵): 添加知识领域和过程组的显示/隐藏功能
feat(详情页): 为ITTO内容添加显示/隐藏控制功能 refactor: 优化状态管理使用localStorage持久化
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
import { Link } from 'react-router-dom'
|
||||
import { motion } from 'framer-motion'
|
||||
import { clsx } from 'clsx'
|
||||
import { Eye, EyeOff } from 'lucide-react'
|
||||
import {
|
||||
knowledgeAreas,
|
||||
processGroups,
|
||||
@@ -14,9 +15,20 @@ import {
|
||||
interface ProcessMatrixProps {
|
||||
className?: string
|
||||
isFullScreen?: boolean
|
||||
hiddenKnowledgeAreaIds?: Set<string>
|
||||
hiddenProcessGroupIds?: Set<string>
|
||||
onToggleKnowledgeArea?: (id: string) => void
|
||||
onToggleProcessGroup?: (id: string) => void
|
||||
}
|
||||
|
||||
export function ProcessMatrix({ className, isFullScreen = false }: ProcessMatrixProps) {
|
||||
export function ProcessMatrix({
|
||||
className,
|
||||
isFullScreen = false,
|
||||
hiddenKnowledgeAreaIds = new Set(),
|
||||
hiddenProcessGroupIds = new Set(),
|
||||
onToggleKnowledgeArea,
|
||||
onToggleProcessGroup,
|
||||
}: ProcessMatrixProps) {
|
||||
// 构建矩阵数据:knowledgeAreaId -> processGroupId -> Process[]
|
||||
const matrix = new Map<string, Map<string, typeof processes>>()
|
||||
|
||||
@@ -47,62 +59,105 @@ export function ProcessMatrix({ className, isFullScreen = false }: ProcessMatrix
|
||||
<th className="sticky left-0 z-10 bg-gray-100 dark:bg-gray-800 p-3 text-left text-sm font-semibold text-gray-700 dark:text-gray-300 border border-gray-200 dark:border-gray-700 min-w-[160px]">
|
||||
知识领域 / 过程组
|
||||
</th>
|
||||
{processGroups.map(pg => (
|
||||
<th
|
||||
key={pg.id}
|
||||
className="p-3 text-center text-sm font-semibold text-white border border-gray-200 dark:border-gray-700 min-w-[140px]"
|
||||
style={{ backgroundColor: pg.color }}
|
||||
>
|
||||
<div>{pg.name}</div>
|
||||
<div className="text-xs opacity-80 font-normal mt-1">
|
||||
{pg.processCount} 个过程
|
||||
</div>
|
||||
</th>
|
||||
))}
|
||||
{processGroups.map(pg => {
|
||||
const isHidden = hiddenProcessGroupIds.has(pg.id)
|
||||
return (
|
||||
<th
|
||||
key={pg.id}
|
||||
className="p-3 text-center text-sm font-semibold text-white border border-gray-200 dark:border-gray-700 min-w-[140px]"
|
||||
style={{ backgroundColor: pg.color }}
|
||||
>
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<div className={clsx("transition-opacity duration-150", isHidden && "opacity-30")}>
|
||||
<div>{pg.name}</div>
|
||||
<div className="text-xs opacity-80 font-normal mt-1">
|
||||
{pg.processCount} 个过程
|
||||
</div>
|
||||
</div>
|
||||
{onToggleProcessGroup && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onToggleProcessGroup(pg.id)}
|
||||
aria-label={isHidden ? `显示${pg.name}` : `隐藏${pg.name}`}
|
||||
aria-pressed={!isHidden}
|
||||
className="flex items-center gap-1 px-2 py-1 rounded text-xs font-medium bg-white/20 hover:bg-white/30 transition-colors"
|
||||
>
|
||||
{isHidden ? <Eye size={12} /> : <EyeOff size={12} />}
|
||||
<span>{isHidden ? '显示' : '隐藏'}</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</th>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
{/* 表体:知识领域 × 过程 */}
|
||||
<tbody>
|
||||
{knowledgeAreas.map((ka, kaIndex) => (
|
||||
<motion.tr
|
||||
key={ka.id}
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: kaIndex * 0.05 }}
|
||||
>
|
||||
{/* 知识领域名称 */}
|
||||
<td
|
||||
className="sticky left-0 z-10 p-3 border border-gray-200 dark:border-gray-700 font-medium"
|
||||
style={{
|
||||
backgroundColor: `${ka.color}15`,
|
||||
borderLeftWidth: 4,
|
||||
borderLeftColor: ka.color,
|
||||
}}
|
||||
{knowledgeAreas.map((ka, kaIndex) => {
|
||||
const isKaHidden = hiddenKnowledgeAreaIds.has(ka.id)
|
||||
return (
|
||||
<motion.tr
|
||||
key={ka.id}
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: kaIndex * 0.05 }}
|
||||
>
|
||||
<Link
|
||||
to={`/knowledge-areas/${ka.id}`}
|
||||
className="hover:underline text-gray-900 dark:text-white"
|
||||
{/* 知识领域名称 */}
|
||||
<td
|
||||
className="sticky left-0 z-10 p-3 border border-gray-200 dark:border-gray-700 font-medium"
|
||||
style={{
|
||||
backgroundColor: `${ka.color}15`,
|
||||
borderLeftWidth: 4,
|
||||
borderLeftColor: ka.color,
|
||||
}}
|
||||
>
|
||||
<span className="font-bold mr-2" style={{ color: ka.color }}>
|
||||
{ka.order}
|
||||
</span>
|
||||
{ka.name}
|
||||
</Link>
|
||||
</td>
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<Link
|
||||
to={`/knowledge-areas/${ka.id}`}
|
||||
className={clsx(
|
||||
"hover:underline text-gray-900 dark:text-white transition-opacity duration-150",
|
||||
isKaHidden && "opacity-30"
|
||||
)}
|
||||
>
|
||||
<span className="font-bold mr-2" style={{ color: ka.color }}>
|
||||
{ka.order}
|
||||
</span>
|
||||
{ka.name}
|
||||
</Link>
|
||||
{onToggleKnowledgeArea && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onToggleKnowledgeArea(ka.id)}
|
||||
aria-label={isKaHidden ? `显示${ka.name}` : `隐藏${ka.name}`}
|
||||
aria-pressed={!isKaHidden}
|
||||
className="flex items-center gap-1 px-1.5 py-0.5 rounded text-xs font-medium hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors shrink-0"
|
||||
style={{ color: ka.color }}
|
||||
>
|
||||
{isKaHidden ? <Eye size={12} /> : <EyeOff size={12} />}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
{/* 每个过程组的单元格 */}
|
||||
{processGroups.map(pg => {
|
||||
const cellProcesses = matrix.get(ka.id)?.get(pg.id) || []
|
||||
const isCellHidden = isKaHidden || hiddenProcessGroupIds.has(pg.id)
|
||||
return (
|
||||
<td
|
||||
key={pg.id}
|
||||
className="p-2 border border-gray-200 dark:border-gray-700 align-top bg-white dark:bg-gray-800"
|
||||
>
|
||||
<div className={clsx(
|
||||
"gap-1",
|
||||
isFullScreen ? "grid grid-cols-2" : "space-y-1"
|
||||
)}>
|
||||
<div
|
||||
className={clsx(
|
||||
"gap-1 transition-opacity duration-150",
|
||||
isFullScreen ? "grid grid-cols-2" : "space-y-1",
|
||||
isCellHidden && "opacity-0 pointer-events-none"
|
||||
)}
|
||||
style={isCellHidden ? { visibility: 'hidden' } : undefined}
|
||||
>
|
||||
{cellProcesses.map(p => (
|
||||
<Link
|
||||
key={p.id}
|
||||
@@ -142,7 +197,7 @@ export function ProcessMatrix({ className, isFullScreen = false }: ProcessMatrix
|
||||
)
|
||||
})}
|
||||
</motion.tr>
|
||||
))}
|
||||
)})}
|
||||
</tbody>
|
||||
|
||||
{/* 表尾:统计 */}
|
||||
|
||||
Reference in New Issue
Block a user