This commit is contained in:
freedakgmail
2025-11-30 09:16:43 +08:00
parent b436ae0680
commit fd9c064749
227 changed files with 1251 additions and 912 deletions
+82 -33
View File
@@ -4,16 +4,13 @@ import { useFamily } from "@/context/family-context"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { ScrollArea } from "@/components/ui/scroll-area"
import { Clock, Map as MapIcon, ArrowRight, Calendar, User } from 'lucide-react'
import dynamic from 'next/dynamic'
import { useMemo, useEffect, useState } from 'react'
import * as echarts from 'echarts'
import { Clock, Map as MapIcon, ArrowRight, Calendar, User, Loader2 } from 'lucide-react'
import { useMemo, useEffect, useState, useRef, useCallback } from 'react'
import { MemberNameWithStatus } from "@/components/member-name-with-status"
import { useSession } from 'next-auth/react'
import { EmptyStateBadge } from '@/components/empty-state-badge'
import { useRouter } from 'next/navigation'
const ReactECharts = dynamic(() => import('echarts-for-react'), { ssr: false })
import type { ECharts } from 'echarts'
// 中国地图 GeoJSON 数据 - 完整的轮廓
const chinaGeoJSON = {
@@ -194,6 +191,7 @@ export default function TimelinePage() {
const [mapLoaded, setMapLoaded] = useState(false)
const treeId = currentTree?.id
const router = useRouter()
const chartInstanceRef = useRef<ECharts | null>(null)
// 未登录时重定向到登录页
useEffect(() => {
@@ -204,18 +202,38 @@ export default function TimelinePage() {
// 注册中国地图 - 从外部文件加载完整数据(必须在条件渲染之前)
useEffect(() => {
fetch('/china.json')
.then(response => response.json())
.then(geoJson => {
echarts.registerMap('china', geoJson)
setMapLoaded(true)
})
.catch(error => {
console.error('Failed to load China map:', error)
// 如果加载失败,使用简化版本
echarts.registerMap('china', chinaGeoJSON as any)
setMapLoaded(true)
})
let isMounted = true
// 动态导入 echarts 并注册地图
const loadMap = async () => {
try {
const echarts = await import('echarts')
try {
const response = await fetch('/china.json')
const geoJson = await response.json()
if (isMounted) {
echarts.registerMap('china', geoJson)
setMapLoaded(true)
}
} catch (fetchError) {
console.error('Failed to load China map JSON, using fallback:', fetchError)
// 如果加载失败,使用简化版本
if (isMounted) {
echarts.registerMap('china', chinaGeoJSON as any)
setMapLoaded(true)
}
}
} catch (importError) {
console.error('Failed to import echarts:', importError)
}
}
loadMap()
return () => {
isMounted = false
}
}, [])
// -- Timeline Logic -- (必须在条件渲染之前)
@@ -482,6 +500,50 @@ export default function TimelinePage() {
}
}, [migrations])
// 使用 callback ref 来确保 DOM 元素挂载后初始化图表
const initChartRef = useCallback((node: HTMLDivElement | null) => {
if (!node || !mapLoaded) {
console.log('initChartRef: node或mapLoaded不存在', { node: !!node, mapLoaded })
return
}
console.log('initChartRef: 开始初始化图表')
// 动态导入并初始化图表
import('echarts').then((echarts) => {
// 再次检查节点是否存在
if (!node) return
// 检查是否已有实例
let chart = echarts.getInstanceByDom(node)
if (!chart) {
console.log('创建新的图表实例')
// 创建新实例
chart = echarts.init(node)
chartInstanceRef.current = chart
// 监听窗口大小变化
const handleResize = () => {
chart?.resize()
}
window.addEventListener('resize', handleResize)
// 保存清理函数到节点
;(node as any).__resizeHandler = handleResize
} else {
console.log('使用已有的图表实例')
}
// 设置选项
console.log('设置图表选项')
chart.setOption(mapOption, { notMerge: true })
console.log('图表选项设置完成')
}).catch(error => {
console.error('Failed to initialize chart:', error)
})
}, [mapLoaded, mapOption])
// 未登录时不渲染内容(已在 useEffect 中重定向)
if (status === 'loading' || !session) {
return null
@@ -724,23 +786,10 @@ export default function TimelinePage() {
<p>...</p>
</div>
</div>
) : migrations.length === 0 ? (
<div className="flex items-center justify-center h-full">
<div className="text-center space-y-4">
<div className="inline-flex items-center justify-center w-16 h-16 text-primary/80">
<MapIcon className="h-16 w-16" strokeWidth={1} />
</div>
<p className="text-sm text-foreground font-light tracking-wide"></p>
</div>
</div>
) : (
<ReactECharts
key="migration-map"
option={mapOption}
<div
ref={initChartRef}
style={{ height: '100%', width: '100%' }}
opts={{ renderer: 'canvas' }}
notMerge={true}
lazyUpdate={true}
/>
)}
</div>