import { useState, useMemo } from 'react' import { ChevronRight, ChevronDown, Folder, FolderOpen, Plus, Pencil, Trash2, FolderInput } from 'lucide-react' import type { BrowserGroupWithCount, BrowserGroupInput } from '../types' import { createGroup, updateGroup, deleteGroup } from '../api' interface GroupTreeNavProps { groups: BrowserGroupWithCount[] selectedGroupId: string | null onSelectGroup: (groupId: string | null) => void onRefresh: () => void } interface TreeNode extends BrowserGroupWithCount { children: TreeNode[] level: number } // 构建树形结构 function buildTree(groups: BrowserGroupWithCount[]): TreeNode[] { const map = new Map() const roots: TreeNode[] = [] // 初始化所有节点 groups.forEach(g => { map.set(g.groupId, { ...g, children: [], level: 0 }) }) // 构建父子关系 groups.forEach(g => { const node = map.get(g.groupId)! if (g.parentId && map.has(g.parentId)) { const parent = map.get(g.parentId)! node.level = parent.level + 1 parent.children.push(node) } else { roots.push(node) } }) // 按 sortOrder 排序 const sortNodes = (nodes: TreeNode[]) => { nodes.sort((a, b) => a.sortOrder - b.sortOrder) nodes.forEach(n => sortNodes(n.children)) } sortNodes(roots) return roots } export function GroupTreeNav({ groups, selectedGroupId, onSelectGroup, onRefresh }: GroupTreeNavProps) { const [expanded, setExpanded] = useState>(new Set()) const [showCreateModal, setShowCreateModal] = useState(false) const [createParentId, setCreateParentId] = useState('') const [newGroupName, setNewGroupName] = useState('') const [editingGroup, setEditingGroup] = useState(null) const [contextMenu, setContextMenu] = useState<{ x: number; y: number; group: BrowserGroupWithCount } | null>(null) const tree = useMemo(() => buildTree(groups), [groups]) const toggleExpand = (groupId: string) => { setExpanded(prev => { const next = new Set(prev) if (next.has(groupId)) { next.delete(groupId) } else { next.add(groupId) } return next }) } const handleCreate = async () => { if (!newGroupName.trim()) return const input: BrowserGroupInput = { groupName: newGroupName.trim(), parentId: createParentId, sortOrder: 0, } await createGroup(input) setShowCreateModal(false) setNewGroupName('') setCreateParentId('') onRefresh() } const handleRename = async () => { if (!editingGroup || !newGroupName.trim()) return const input: BrowserGroupInput = { groupName: newGroupName.trim(), parentId: editingGroup.parentId, sortOrder: editingGroup.sortOrder, } await updateGroup(editingGroup.groupId, input) setEditingGroup(null) setNewGroupName('') onRefresh() } const handleDelete = async (groupId: string) => { if (!confirm('确定删除此分组?子分组和实例将移动到父分组。')) return await deleteGroup(groupId) if (selectedGroupId === groupId) { onSelectGroup(null) } onRefresh() } const handleContextMenu = (e: React.MouseEvent, group: BrowserGroupWithCount) => { e.preventDefault() setContextMenu({ x: e.clientX, y: e.clientY, group }) } const renderNode = (node: TreeNode) => { const isExpanded = expanded.has(node.groupId) const isSelected = selectedGroupId === node.groupId const hasChildren = node.children.length > 0 return (
onSelectGroup(node.groupId)} onContextMenu={(e) => handleContextMenu(e, node)} > {hasChildren ? ( ) : null} {isExpanded && hasChildren ? ( ) : ( )} {node.groupName} {node.instanceCount}
{isExpanded && node.children.map(child => renderNode(child))}
) } return (
分组
{/* 全部 */}
onSelectGroup(null)} > 全部
{/* 未分组 */}
onSelectGroup('__ungrouped__')} > 未分组
{/* 分组树 */} {tree.length > 0 && (
我的分组
{tree.map(node => renderNode(node))}
)}
{/* 创建分组弹窗 */} {showCreateModal && (
setShowCreateModal(false)}>
e.stopPropagation()}>

新建分组

setNewGroupName(e.target.value)} autoFocus /> {groups.length > 0 && ( )}
)} {/* 重命名弹窗 */} {editingGroup && (
setEditingGroup(null)}>
e.stopPropagation()}>

重命名分组

setNewGroupName(e.target.value)} autoFocus />
)} {/* 右键菜单 */} {contextMenu && (
setContextMenu(null)} >
)} {/* 点击其他地方关闭右键菜单 */} {contextMenu && (
setContextMenu(null)} /> )}
) }