0.8.3.0
This commit is contained in:
@@ -707,6 +707,7 @@ const D3OrgChartFlowComponent = forwardRef<D3OrgChartRef, D3OrgChartFlowProps>(
|
||||
">
|
||||
${sp.birthYear}${sp.deathYear ? `-${sp.deathYear}` : '-'}
|
||||
</div>
|
||||
${sp.ageText ? `<div style="font-size: 9px; color: #000000; margin-top: 2px; font-weight: 500; font-family: serif;">${sp.ageText}</div>` : ''}
|
||||
` : ''}
|
||||
</div>
|
||||
`
|
||||
|
||||
@@ -214,9 +214,6 @@ function PersonCard({
|
||||
|
||||
// 是否显示右键菜单(有可添加的选项时才显示)
|
||||
const hasAddOptions = canEdit && (canAddFather || canAddMother || canAddSpouse || canAddChild)
|
||||
|
||||
// 调试日志
|
||||
console.log('PersonCard render:', member.fullName, { canEdit, hasAddOptions, relationMode })
|
||||
|
||||
// 庄严正式的中国风配色
|
||||
const isDead = !!member.deathDate
|
||||
|
||||
@@ -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',
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user