This commit is contained in:
freedakgmail
2025-12-21 22:42:39 +08:00
parent 5983f13282
commit 5a604e89f9
10 changed files with 117 additions and 18 deletions
+61 -11
View File
@@ -52,6 +52,9 @@ export function TreeLayout({
const VERTICAL_GAP = 48 // 3rem = 48px
const CONNECTOR_HEIGHT = 48 // 连接线垂直高度
// 节点注册计数,用于触发连线重新计算
const [nodeRegisteredCount, setNodeRegisteredCount] = useState(0)
// 折叠状态管理:存储被折叠的节点ID
const [collapsedNodes, setCollapsedNodes] = useState<Set<string>>(() => new Set())
@@ -81,6 +84,8 @@ export function TreeLayout({
const handleNodeRef = useCallback((memberId: string, el: HTMLDivElement | null) => {
if (el) {
nodeRefs.current.set(memberId, el)
// 触发连线重新计算
setNodeRegisteredCount(prev => prev + 1)
} else {
nodeRefs.current.delete(memberId)
}
@@ -102,10 +107,14 @@ export function TreeLayout({
}, [relationMode, onMemberClick, router])
// 检查节点是否被任何祖先折叠(使用闭包避免递归依赖问题)
// 注意:这个函数用于判断节点是否应该被隐藏,不用于连线计算
const isNodeHiddenByCollapse = useCallback((nodeId: string): boolean => {
// 如果没有折叠的节点,直接返回 false
if (collapsedNodes.size === 0) return false
const checkHidden = (id: string): boolean => {
const member = getMember(id)
if (!member) return true
if (!member) return false // 找不到成员时返回 false,不隐藏
// 检查父节点是否被折叠
const parentId = member.fatherId || member.motherId
@@ -123,26 +132,54 @@ export function TreeLayout({
const calculateConnections = useCallback(() => {
const newConnections: Connection[] = []
const containerRect = containerRef.current?.getBoundingClientRect()
if (!containerRect) return
if (!containerRect) {
return
}
// 检查是否有足够的节点已注册
if (nodeRefs.current.size === 0) return
if (nodeRefs.current.size === 0) {
return
}
let processedCount = 0
let skippedNoChildren = 0
let skippedCollapsed = 0
let skippedHidden = 0
let skippedNotInDom = 0
let skippedZeroSize = 0
nodeRefs.current.forEach((nodeElement, nodeId) => {
const member = getMember(nodeId)
if (!member || !member.childrenIds || member.childrenIds.length === 0) return
if (!member || !member.childrenIds || member.childrenIds.length === 0) {
skippedNoChildren++
return
}
// 如果该节点被折叠,不绘制到子节点的连线
if (collapsedNodes.has(nodeId)) return
if (collapsedNodes.has(nodeId)) {
skippedCollapsed++
return
}
// 如果该节点被祖先折叠隐藏,跳过
if (isNodeHiddenByCollapse(nodeId)) return
if (isNodeHiddenByCollapse(nodeId)) {
skippedHidden++
return
}
// 检查元素是否仍在 DOM 中且可见
if (!document.body.contains(nodeElement)) return
if (!document.body.contains(nodeElement)) {
skippedNotInDom++
return
}
const parentRect = nodeElement.getBoundingClientRect()
// 如果元素不可见(宽高为0),跳过
if (parentRect.width === 0 || parentRect.height === 0) return
if (parentRect.width === 0 || parentRect.height === 0) {
skippedZeroSize++
return
}
processedCount++
// 考虑缩放因素:getBoundingClientRect 返回的是缩放后的尺寸
// 需要除以 scale 来获取实际的逻辑位置
@@ -209,6 +246,8 @@ export function TreeLayout({
timers.push(setTimeout(calculateConnections, 50))
timers.push(setTimeout(calculateConnections, 150))
timers.push(setTimeout(calculateConnections, 300))
timers.push(setTimeout(calculateConnections, 500))
timers.push(setTimeout(calculateConnections, 1000))
return () => timers.forEach(timer => clearTimeout(timer))
}, [collapsedNodes, calculateConnections])
@@ -217,13 +256,24 @@ export function TreeLayout({
calculateConnections()
}, [scale, calculateConnections])
// 节点注册变化时重新计算连线(防抖)
useEffect(() => {
if (nodeRegisteredCount === 0) return
const timer = setTimeout(calculateConnections, 100)
return () => clearTimeout(timer)
}, [nodeRegisteredCount, calculateConnections])
useEffect(() => {
// 多次尝试计算,确保 DOM 完全渲染
// 对于大型族谱(100+人),需要更长的延迟
const timers: NodeJS.Timeout[] = []
timers.push(setTimeout(calculateConnections, 50))
timers.push(setTimeout(calculateConnections, 150))
timers.push(setTimeout(calculateConnections, 100))
timers.push(setTimeout(calculateConnections, 300))
timers.push(setTimeout(calculateConnections, 500))
timers.push(setTimeout(calculateConnections, 1000))
timers.push(setTimeout(calculateConnections, 2000))
timers.push(setTimeout(calculateConnections, 3000))
timers.push(setTimeout(calculateConnections, 5000))
// 监听窗口大小变化
window.addEventListener('resize', calculateConnections)
@@ -395,7 +445,7 @@ export function TreeLayout({
top: `${top}px`,
width: `${width}px`,
height: `${height}px`,
backgroundColor: 'rgba(180, 83, 9, 0.7)', // amber-700 with 70% opacity
backgroundColor: 'rgba(180, 83, 9, 0.7)',
transform: isHorizontal ? 'scaleY(0.5)' : 'scaleX(0.5)',
transformOrigin: 'top left',
}}