"use client" import type React from "react" import { SiteHeader } from "@/components/site-header" import { TreeLayout } from "@/components/tree/tree-layout" import { useFamily } from "@/context/family-context" import { Button } from "@/components/ui/button" import { ZoomIn, ZoomOut, Move, Filter, Download } from "lucide-react" import { useState, useRef } from "react" export default function TreePage() { const { treeData } = useFamily() const [scale, setScale] = useState(1) const [position, setPosition] = useState({ x: 0, y: 0 }) const [isDragging, setIsDragging] = useState(false) const [startPos, setStartPos] = useState({ x: 0, y: 0 }) const [dragStartPos, setDragStartPos] = useState({ x: 0, y: 0 }) const containerRef = useRef(null) const handleMouseDown = (e: React.MouseEvent) => { // 如果点击的是节点卡片,不启动拖拽 const target = e.target as HTMLElement if (target.closest('.family-node-card')) { return } setIsDragging(true) setStartPos({ x: e.clientX - position.x, y: e.clientY - position.y }) setDragStartPos({ x: e.clientX, y: e.clientY }) } const handleMouseMove = (e: React.MouseEvent) => { if (!isDragging) return // 只有移动超过5px才算拖拽,避免误触 const deltaX = Math.abs(e.clientX - dragStartPos.x) const deltaY = Math.abs(e.clientY - dragStartPos.y) if (deltaX < 5 && deltaY < 5) return setPosition({ x: e.clientX - startPos.x, y: e.clientY - startPos.y, }) } const handleMouseUp = () => { setIsDragging(false) } const handleExportImage = async () => { if (!containerRef.current) return try { // 临时重置位置和缩放以获取完整视图 const originalPosition = { ...position } const originalScale = scale setPosition({ x: 0, y: 0 }) setScale(1) // 等待 DOM 更新 await new Promise(resolve => setTimeout(resolve, 800)) // 找到实际的族谱容器(包含 TreeLayout 的 div) const treeContainer = containerRef.current.querySelector('div[style*="transform"]') as HTMLElement if (!treeContainer) { throw new Error('找不到族谱容器') } // 查找所有连接线(使用更宽泛的选择器) const allDivs = treeContainer.querySelectorAll('div') const lineElements: HTMLElement[] = [] allDivs.forEach(div => { const el = div as HTMLElement const classes = String(el.className || '') // 查找包含 bg-foreground 的元素(连接线) if (classes.includes('bg-foreground')) { lineElements.push(el) } }) // 克隆容器用于导出 const clonedContainer = treeContainer.cloneNode(true) as HTMLElement // 在克隆的容器中找到对应的连接线并设置样式 const clonedDivs = clonedContainer.querySelectorAll('div') let lineIndex = 0 clonedDivs.forEach(div => { const el = div as HTMLElement const classes = String(el.className || '') if (classes.includes('bg-foreground') && lineElements[lineIndex]) { const original = lineElements[lineIndex] const computed = window.getComputedStyle(original) // 直接设置内联样式 el.style.backgroundColor = computed.backgroundColor el.style.width = computed.width el.style.height = computed.height el.style.position = 'absolute' el.style.left = computed.left el.style.top = computed.top lineIndex++ } }) // 移除卡片边框 clonedContainer.querySelectorAll('*').forEach((child) => { const el = child as HTMLElement const classes = String(el.className || '') // 跳过连接线和连接线容器 if (classes.includes('bg-foreground') || classes.includes('pointer-events-none')) { return } // 移除其他元素的边框 el.style.border = 'none' el.style.outline = 'none' }) // 临时添加到 DOM clonedContainer.style.position = 'fixed' clonedContainer.style.left = '-9999px' clonedContainer.style.top = '0' document.body.appendChild(clonedContainer) // 等待渲染 await new Promise(resolve => setTimeout(resolve, 200)) // 动态导入 dom-to-image-more(仅在客户端) const domtoimage = await import('dom-to-image-more') // 使用 toBlob 方法 const blob = await domtoimage.default.toBlob(clonedContainer, { quality: 0.95, bgcolor: '#ffffff' }) // 清理克隆的容器 document.body.removeChild(clonedContainer) // 恢复原始位置和缩放 setPosition(originalPosition) setScale(originalScale) // 创建下载链接 const url = URL.createObjectURL(blob) const link = document.createElement('a') link.download = `family-tree-${new Date().toISOString().split('T')[0]}.png` link.href = url link.click() // 清理 setTimeout(() => URL.revokeObjectURL(url), 100) } catch (error) { console.error('导出图片失败:', error) } } // Handle root not found if (!treeData.rootId) { return (
No family tree data found.
) } return (
{/* Toolbar */}
{/* Tree Canvas */}
) }