This commit is contained in:
freedakgmail
2025-12-10 15:21:27 +08:00
parent e7167f4340
commit 1ae59960e8
148 changed files with 681 additions and 477 deletions
+111 -3
View File
@@ -312,12 +312,50 @@ const D3OrgChartFlowComponent = forwardRef<D3OrgChartRef, D3OrgChartFlowProps>(
chartRef.current.innerHTML = ''
}
// iOS/iPadOS 检测(包括 Safari、Chrome 等所有浏览器)
// iPad 上所有浏览器都使用 WebKit 引擎
const container = chartRef.current
const isTouchDevice = navigator.maxTouchPoints > 1
const isWebKit = /AppleWebKit/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent)
const isIOSChrome = /CriOS/.test(navigator.userAgent)
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) ||
(navigator.platform === 'MacIntel' && isTouchDevice) ||
isIOSChrome
console.log('Device detection:', { isIOS, isTouchDevice, isWebKit, isIOSChrome, userAgent: navigator.userAgent })
// 对于 iOS,设置明确的像素高度
if (isIOS) {
const headerHeight = 120
const calculatedHeight = window.innerHeight - headerHeight
container.style.height = `${calculatedHeight}px`
}
const data = convertToChartData()
// 获取容器实际尺寸 - iOS Safari 可能返回 0,所以使用 window 尺寸作为后备
const containerRect = chartRef.current.getBoundingClientRect()
let containerWidth = containerRect.width
let containerHeight = containerRect.height
// 如果尺寸无效,使用 window 尺寸
if (!containerWidth || containerWidth < 100) {
containerWidth = window.innerWidth
}
if (!containerHeight || containerHeight < 100) {
containerHeight = window.innerHeight - 120
}
console.log('D3 Chart container size:', { containerWidth, containerHeight, isIOS })
// 初始化图表
const chart = new OrgChart()
.container(chartRef.current)
.data(data)
.svgWidth(containerWidth)
.svgHeight(containerHeight)
.scaleExtent([0.1, 2]) // 缩放范围
.initialZoom(0.5) // 初始缩放级别
.nodeWidth((d: any) => {
const spouseCount = d.data.spouseCount || 0
return spouseCount === 0 ? 170 : spouseCount === 1 ? 300 : 170 + spouseCount * 110
@@ -425,9 +463,11 @@ const D3OrgChartFlowComponent = forwardRef<D3OrgChartRef, D3OrgChartFlowProps>(
box-shadow: 0 4px 20px rgba(0,0,0,0.08);
cursor: pointer;
transition: all 0.3s ease;
-webkit-transform: translateZ(0);
transform: translateZ(0);
"
onmouseover="this.style.boxShadow='0 12px 40px rgba(0,0,0,0.15)'; this.style.transform='translateY(-4px) scale(1.02)'"
onmouseout="this.style.boxShadow='0 4px 20px rgba(0,0,0,0.08)'; this.style.transform='translateY(0) scale(1)'"
onmouseout="this.style.boxShadow='0 4px 20px rgba(0,0,0,0.08)'; this.style.transform='translateZ(0)'"
>
<!-- 顶部装饰条 -->
<div style="
@@ -661,6 +701,54 @@ const D3OrgChartFlowComponent = forwardRef<D3OrgChartRef, D3OrgChartFlowProps>(
.expandAll() // 初始化时展开所有节点
.fit() // 自适应全图显示
// 延迟再次 fit
setTimeout(() => chart.fit(), 100)
setTimeout(() => chart.fit(), 300)
// 所有触摸设备都需要特殊处理 - iOS foreignObject bug 修复
if (isTouchDevice) {
setTimeout(() => {
const w = window.innerWidth
const h = window.innerHeight - 120
if (chartRef.current) {
chartRef.current.style.height = `${h}px`
chartRef.current.style.width = `${w}px`
// iOS foreignObject bug 修复:强制重绘 SVG
const svg = chartRef.current.querySelector('svg')
if (svg) {
// 触发重绘
svg.style.display = 'none'
void (svg as any).getBBox() // 强制回流
svg.style.display = ''
}
}
// 重新设置 SVG 尺寸并渲染
chart.svgWidth(w).svgHeight(h).render().expandAll().fit()
console.log('Touch device delayed render:', { w, h })
}, 500)
// 再次强制重绘
setTimeout(() => {
if (chartRef.current) {
const svg = chartRef.current.querySelector('svg')
if (svg) {
svg.style.transform = 'translateZ(0)'
// 强制所有 foreignObject 重新定位
const foreignObjects = svg.querySelectorAll('foreignObject')
foreignObjects.forEach((fo: Element) => {
(fo as HTMLElement).style.transform = 'translateZ(0)'
})
}
}
chart.fit()
}, 1000)
setTimeout(() => chart.fit(), 1500)
}
// 注册全局配偶点击处理函数
window.d3ChartSpouseClick = (event: Event, spouseId: string) => {
event.stopPropagation()
@@ -708,6 +796,27 @@ const D3OrgChartFlowComponent = forwardRef<D3OrgChartRef, D3OrgChartFlowProps>(
}
}, [members, rootId, onMemberClick, avatarCache])
// 监听窗口大小变化,重新适应视图
useEffect(() => {
const handleResize = () => {
if (chartInstanceRef.current) {
// 延迟执行 fit,等待布局稳定
setTimeout(() => {
chartInstanceRef.current?.fit()
}, 100)
}
}
window.addEventListener('resize', handleResize)
// 监听屏幕方向变化(iPad 等设备)
window.addEventListener('orientationchange', handleResize)
return () => {
window.removeEventListener('resize', handleResize)
window.removeEventListener('orientationchange', handleResize)
}
}, [])
// 监听 selectedMembers 变化,通过 DOM 操作更新选中标记
useEffect(() => {
console.log('Selection useEffect triggered, selectedMembers:', selectedMembers, 'isReady:', isReady)
@@ -825,12 +934,11 @@ const D3OrgChartFlowComponent = forwardRef<D3OrgChartRef, D3OrgChartFlowProps>(
}))
return (
<div className="w-full h-full bg-[url('https://www.transparenttextures.com/patterns/rice-paper.png')] bg-repeat relative" key={`d3-chart-${rootId}`}>
<div className="w-full h-full bg-[url('https://www.transparenttextures.com/patterns/rice-paper.png')] bg-repeat relative overflow-hidden" key={`d3-chart-${rootId}`}>
{/* 图表容器 */}
<div
ref={chartRef}
className="w-full h-full"
style={{ overflow: 'hidden' }}
/>
{/* 自定义右键菜单 */}