import React, { useState } from 'react'; import { Layers, ChevronDown, Tag, Eye, Plus, X } from 'lucide-react'; import { useStore } from '../store/useStore'; import type { TemplateClass } from '../store/useStore'; import { cn } from '../lib/utils'; import { getActiveTemplate } from '../lib/templateSelection'; export function OntologyInspector() { const templates = useStore((state) => state.templates); const activeTemplateId = useStore((state) => state.activeTemplateId); const activeClassId = useStore((state) => state.activeClassId); const activeClass = useStore((state) => state.activeClass); const masks = useStore((state) => state.masks); const selectedMaskIds = useStore((state) => state.selectedMaskIds); const setMasks = useStore((state) => state.setMasks); const setActiveTemplateId = useStore((state) => state.setActiveTemplateId); const setActiveClass = useStore((state) => state.setActiveClass); // Project-level custom classes (in addition to template classes) const [customClasses, setCustomClasses] = useState([]); const [showAddForm, setShowAddForm] = useState(false); const [newClassName, setNewClassName] = useState(''); const [newClassColor, setNewClassColor] = useState('#06b6d4'); const activeTemplate = getActiveTemplate(templates, activeTemplateId); const templateClasses = activeTemplate?.classes || []; const allClasses = [...templateClasses, ...customClasses].sort((a, b) => b.zIndex - a.zIndex); const handleSelectClass = (templateClass: TemplateClass) => { if (activeTemplate && !activeTemplateId) { setActiveTemplateId(activeTemplate.id); } setActiveClass(templateClass); const selectedIdSet = new Set(selectedMaskIds); const hasSelectedMasks = masks.some((mask) => selectedIdSet.has(mask.id)); if (!hasSelectedMasks) return; const templateId = activeTemplate?.id || activeTemplateId || undefined; setMasks(masks.map((mask) => { if (!selectedIdSet.has(mask.id)) return mask; return { ...mask, templateId: templateId || mask.templateId, classId: templateClass.id, className: templateClass.name, classZIndex: templateClass.zIndex, label: templateClass.name, color: templateClass.color, saveStatus: mask.annotationId ? 'dirty' : 'draft', saved: mask.annotationId ? false : mask.saved, }; })); }; const handleAddCustom = () => { if (!newClassName.trim()) return; const maxZ = allClasses.length > 0 ? Math.max(...allClasses.map((c) => c.zIndex)) : 0; const newClass: TemplateClass = { id: `custom-${Date.now()}`, name: newClassName.trim(), color: newClassColor, zIndex: maxZ + 10, category: '自定义', }; setCustomClasses([...customClasses, newClass]); handleSelectClass(newClass); setNewClassName(''); setShowAddForm(false); }; return (
本体论与属性分类管理树
{/* Template Selector */}

当前激活模板

{activeTemplate && (
{activeTemplate.classes?.length ?? 0} 个分类来自模板 {customClasses.length > 0 && ` + ${customClasses.length} 个自定义`}
)}
{/* Semantic Classification Tree */}

语义分类树 (高度/Z-Index)

{allClasses.map(cls => (
))} {allClasses.length === 0 && (
请先选择一个模板
)}
{/* Add Custom Class */}

自定义分类

{showAddForm && (
setNewClassColor(e.target.value)} className="w-8 h-8 rounded bg-transparent border-0 cursor-pointer" /> setNewClassName(e.target.value)} placeholder="分类名称" className="flex-1 bg-[#111] border border-white/10 rounded px-2 py-1 text-xs text-white" onKeyDown={(e) => e.key === 'Enter' && handleAddCustom()} />
)}
{/* Current Active Object Properties */}

特定目标实例属性追踪

{activeClass?.name || activeTemplate?.name || '未选择'}
当前选中区域: {selectedMaskIds.length}
0.9412
降维点拓扑锚点: 12 节点
); }