|
@@ -1,162 +1,491 @@
|
|
|
-import React, { useCallback, useEffect, useRef, useState } from 'react'
|
|
|
+import React, { useCallback, useEffect, useRef, useState, useMemo } from 'react'
|
|
|
import styles from './index.module.scss'
|
|
|
-import { Swiper } from 'antd-mobile'
|
|
|
-import { A1listType, myInfo } from '@/utils/history'
|
|
|
-import classNames from 'classnames'
|
|
|
-import { EyeOutlined } from '@ant-design/icons'
|
|
|
-import CountUp from 'react-countup'
|
|
|
-import { A1_APIgetNumList } from '@/store/action/A1list'
|
|
|
-import logoImg from '../../assets/img/logo.png'
|
|
|
-import flooImg from '../../assets/img/flooImg.jpg'
|
|
|
-
|
|
|
-type NumListRowType = {
|
|
|
- num: string
|
|
|
- showVisit: number
|
|
|
-}
|
|
|
+import Left from '@/pages/components/Left'
|
|
|
+import Right from '@/pages/components/Right'
|
|
|
+import BottomSearch from '@/pages/components/BottomSearch'
|
|
|
+import MapTab from '@/pages/components/MapTab'
|
|
|
+import Detail from '@/pages/components/Detail'
|
|
|
+import { ReactComponent as MapSvg } from '@/assets/img/map.svg'
|
|
|
+import svgPanZoom from 'svg-pan-zoom'
|
|
|
+import { useDispatch, useSelector } from 'react-redux'
|
|
|
+import {
|
|
|
+ Martyr_APIgetList,
|
|
|
+ Martyr_APIgetRelativeList,
|
|
|
+ Martyr_APIgetClueList,
|
|
|
+ Martyr_APIgetDetail,
|
|
|
+ Martyr_APIgetRelationList
|
|
|
+} from '@/store/action/martyr'
|
|
|
+import { RootState } from '@/store'
|
|
|
+import { MartyrItem, RelativeItem, RelationShipItem } from '@/types/api/martyr'
|
|
|
+import { ClueItem } from '@/types/api/clue'
|
|
|
+import { cityIdToName, addDefs, nameToCityId } from '@/pages/A1home/util'
|
|
|
|
|
|
function A1home() {
|
|
|
- const [list, setList] = useState<A1listType[]>([])
|
|
|
+ const svgRef = useRef<SVGSVGElement>(null)
|
|
|
+ const panZoomInstance = useRef<ReturnType<typeof svgPanZoom> | null>(null)
|
|
|
+ const [isAddClassName, setIsAddClassName] = useState(false)
|
|
|
+ const [curCityId, setCurCityId] = useState('')
|
|
|
+ const [isDragging, setIsDragging] = useState(false)
|
|
|
+ const [enableTooltipEvents, setEnableTooltipEvents] = useState(true)
|
|
|
+ const [relativeList, setRelativeList] = useState<Record<string, RelativeItem[]>>({})
|
|
|
+ const [clueList, setClueList] = useState<Record<string, ClueItem[]>>({})
|
|
|
+ const [martyrDetail, setMartyrDetail] = useState<MartyrItem>()
|
|
|
+ const [relationList, setRelationList] = useState<RelationShipItem[]>()
|
|
|
+
|
|
|
+ const dispatch = useDispatch()
|
|
|
+ // 获取烈士列表,亲属列表,线索列表,烈士详情,人物关系
|
|
|
+ const getListFu = useCallback(
|
|
|
+ () =>
|
|
|
+ dispatch(
|
|
|
+ Martyr_APIgetList({
|
|
|
+ nativeProvince: '山东省'
|
|
|
+ })
|
|
|
+ ),
|
|
|
+ [dispatch]
|
|
|
+ )
|
|
|
+ const getRelativeList = useCallback(
|
|
|
+ async () =>
|
|
|
+ await Martyr_APIgetRelativeList({
|
|
|
+ nativeProvince: '山东省'
|
|
|
+ }),
|
|
|
+ []
|
|
|
+ )
|
|
|
+ const getClueList = useCallback(
|
|
|
+ async () =>
|
|
|
+ await Martyr_APIgetClueList({
|
|
|
+ nativeProvince: '山东省'
|
|
|
+ }),
|
|
|
+ []
|
|
|
+ )
|
|
|
+ const getMartyrDetail = useCallback(
|
|
|
+ async (id: number) =>
|
|
|
+ await Martyr_APIgetDetail({
|
|
|
+ id
|
|
|
+ }),
|
|
|
+ []
|
|
|
+ )
|
|
|
+ const getRelationList = useCallback(
|
|
|
+ async (id: number) =>
|
|
|
+ await Martyr_APIgetRelationList({
|
|
|
+ martyrId: id
|
|
|
+ }),
|
|
|
+ []
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+ const { list: listAll } = useSelector((state: RootState) => state.Martyr.tableInfo)
|
|
|
+ const martyrListByCity = useMemo(
|
|
|
+ () =>
|
|
|
+ listAll.reduce((acc, current) => {
|
|
|
+ const key = current.nativeCity
|
|
|
+ if (!acc[key]) acc[key] = []
|
|
|
+ acc[key].push(current)
|
|
|
+ return acc
|
|
|
+ }, {} as Record<string, typeof listAll>),
|
|
|
+ [listAll]
|
|
|
+ )
|
|
|
+
|
|
|
+ console.log(martyrListByCity, 'relativeList', relativeList)
|
|
|
|
|
|
- const listRef = useRef<A1listType[]>([])
|
|
|
useEffect(() => {
|
|
|
- listRef.current = list
|
|
|
- }, [list])
|
|
|
-
|
|
|
- // 获取全部访问量
|
|
|
- const getNumListFu = useCallback(async (base?: boolean) => {
|
|
|
- // 参数 base =》第一次进页面
|
|
|
-
|
|
|
- const res = await A1_APIgetNumList()
|
|
|
- if (res.code === 0) {
|
|
|
- const arr: NumListRowType[] = res.data
|
|
|
-
|
|
|
- const listTemp = [...myInfo.swArr]
|
|
|
-
|
|
|
- arr.forEach(v1 => {
|
|
|
- listTemp.forEach(v2 => {
|
|
|
- if (v2.code === v1.num) {
|
|
|
- if (!base) {
|
|
|
- const listRefNum = listRef.current.find(c => c.code === v1.num)!.newNum
|
|
|
- v2.oldNum = listRefNum
|
|
|
- if (listRefNum !== v1.showVisit) v2.changeSta = true
|
|
|
+ getListFu()
|
|
|
+ getRelativeList().then(res => {
|
|
|
+ setRelativeList(
|
|
|
+ res.data.reduce((acc: Record<string, RelativeItem[]>, current: RelativeItem) => {
|
|
|
+ const key = current.city
|
|
|
+ if (!acc[key]) acc[key] = []
|
|
|
+ acc[key].push(current)
|
|
|
+ return acc
|
|
|
+ }, {} as Record<string, typeof listAll>)
|
|
|
+ )
|
|
|
+ })
|
|
|
+ getClueList().then(res => {
|
|
|
+ setClueList(
|
|
|
+ res.data.reduce((acc: Record<string, ClueItem[]>, current: ClueItem) => {
|
|
|
+ const key = current.city
|
|
|
+ if (!acc[key]) acc[key] = []
|
|
|
+ acc[key].push(current)
|
|
|
+ return acc
|
|
|
+ }, {} as Record<string, typeof listAll>)
|
|
|
+ )
|
|
|
+ })
|
|
|
+ }, [getClueList, getListFu, getRelativeList])
|
|
|
+
|
|
|
+ // 聚焦到指定城市
|
|
|
+ const focusOnCity = useCallback((cityId: string) => {
|
|
|
+ if (panZoomInstance.current && svgRef.current) {
|
|
|
+ const zoomLevel = 1.8
|
|
|
+ const svgPoint = svgRef.current.createSVGPoint()
|
|
|
+ const city = svgRef.current.querySelector(`#${cityId}`)?.querySelector('ellipse')!
|
|
|
+ svgPoint.x = city?.cx.baseVal.value
|
|
|
+ svgPoint.y = city?.cy.baseVal.value
|
|
|
+ const transformedPoint = svgPoint.matrixTransform(svgRef.current.getCTM()!)
|
|
|
+ const { width: viewportWidth, height: viewportHeight } =
|
|
|
+ panZoomInstance.current.getSizes().viewBox
|
|
|
+ const panX = viewportWidth / 2 - transformedPoint.x * zoomLevel
|
|
|
+ const panY = viewportHeight / 2 - transformedPoint.y * zoomLevel
|
|
|
+ const viewport = svgRef.current.querySelector(
|
|
|
+ '.svg-pan-zoom_viewport'
|
|
|
+ ) as SVGGElement
|
|
|
+ viewport.style.transition = 'all 1s ease-in-out'
|
|
|
+ panZoomInstance.current.zoom(zoomLevel)
|
|
|
+ panZoomInstance.current.pan({ x: panX, y: panY })
|
|
|
+
|
|
|
+ setTimeout(() => {
|
|
|
+ viewport.style.transition = 'all 0.05s linear'
|
|
|
+ setIsDragging(false)
|
|
|
+ }, 1000)
|
|
|
+ }
|
|
|
+ }, [])
|
|
|
+
|
|
|
+ // 添加tooltip到指定城市下方
|
|
|
+ const addTooltip = useCallback((cityId: string, tooltipText: string) => {
|
|
|
+ if (svgRef.current) {
|
|
|
+ const city = svgRef.current.querySelector(`#${cityId}`)?.querySelector('ellipse')!
|
|
|
+
|
|
|
+ // 创建提示框元素
|
|
|
+ const tooltip = document.createElementNS('http://www.w3.org/2000/svg', 'g')
|
|
|
+ tooltip.id = `${cityId}Tooltip`
|
|
|
+ const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect')
|
|
|
+ const text = document.createElementNS('http://www.w3.org/2000/svg', 'text')
|
|
|
+
|
|
|
+ // 设置初始属性(修改以下部分)
|
|
|
+ rect.setAttribute('fill', 'url(#jinanGradient)') // 使用渐变引用
|
|
|
+ // rect.setAttribute('stroke', '#6d330fff')
|
|
|
+ rect.setAttribute('rx', '10')
|
|
|
+ rect.setAttribute('width', '100')
|
|
|
+ rect.setAttribute('height', '20')
|
|
|
+ text.setAttribute('text-anchor', 'middle')
|
|
|
+ text.setAttribute('dominant-baseline', 'middle')
|
|
|
+ text.setAttribute('fill', '#F5EEED')
|
|
|
+ text.setAttribute('font-size', '6px')
|
|
|
+ text.setAttribute('font-weight', '600')
|
|
|
+ text.textContent = tooltipText
|
|
|
+
|
|
|
+ // 坐标
|
|
|
+ const cityCX = city?.cx.baseVal.value
|
|
|
+ const cityCY = city?.cy.baseVal.value
|
|
|
+
|
|
|
+ // 计算提示框位置(城市下方)
|
|
|
+ rect.setAttribute('x', (cityCX - 50).toString()) // 100宽度居中
|
|
|
+ rect.setAttribute('y', (cityCY + 20).toString()) // 下移20px
|
|
|
+ text.setAttribute('x', cityCX.toString())
|
|
|
+ text.setAttribute('y', (cityCY + 30).toString()) // 文字在矩形内居中
|
|
|
+
|
|
|
+ tooltip.appendChild(rect)
|
|
|
+ tooltip.appendChild(text)
|
|
|
+
|
|
|
+ // 添加到 SVG 的 viewport 中
|
|
|
+ const viewport = svgRef.current.querySelector('.svg-pan-zoom_viewport')
|
|
|
+ viewport?.appendChild(tooltip)
|
|
|
+
|
|
|
+ // 隐藏tooltip,待mouseEnter再显示
|
|
|
+ tooltip.style.opacity = '0'
|
|
|
+ tooltip.style.pointerEvents = 'none'
|
|
|
+ tooltip.style.transition = 'all 0.3s ease-in-out'
|
|
|
+ }
|
|
|
+ }, [])
|
|
|
+
|
|
|
+ // 更改tooltip文本内容
|
|
|
+ const changeTooltipText = useCallback((cityId: string, text: string) => {
|
|
|
+ if (svgRef.current) {
|
|
|
+ const tooltip = svgRef.current.querySelector(`#${cityId}Tooltip`) as SVGGElement
|
|
|
+ if (tooltip) {
|
|
|
+ const textElement = tooltip.querySelector('text')
|
|
|
+ tooltip.style.opacity = '1'
|
|
|
+ const cityDom = svgRef.current
|
|
|
+ .querySelector('g')
|
|
|
+ ?.querySelector('g')
|
|
|
+ ?.querySelectorAll('path')
|
|
|
+ cityDom?.forEach(item => {
|
|
|
+ item.removeEventListener('mouseleave', (e: MouseEvent) => {
|
|
|
+ const target = e.target as SVGElement
|
|
|
+ const tooltip = svgRef.current?.querySelector(`#tap-${target.id}Tooltip`)
|
|
|
+ if (tooltip) {
|
|
|
+ ; (tooltip as HTMLElement).style.opacity = '0'
|
|
|
}
|
|
|
- v2.newNum = v1.showVisit
|
|
|
- }
|
|
|
+ })
|
|
|
})
|
|
|
- })
|
|
|
+ if (textElement) {
|
|
|
+ textElement.textContent = text
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }, [])
|
|
|
|
|
|
- setList(listTemp)
|
|
|
+ // 初始化添加tooltip
|
|
|
+ const initialTooltip = useCallback(
|
|
|
+ (martyrListByCity: Record<string, MartyrItem[]>) => {
|
|
|
+ if (svgRef.current) {
|
|
|
+ // 去除原来的tooltip
|
|
|
+ svgRef.current.querySelectorAll('g[id$="Tooltip"]').forEach(item => {
|
|
|
+ item.remove()
|
|
|
+ })
|
|
|
+
|
|
|
+ Object.keys(martyrListByCity).forEach(item => {
|
|
|
+ console.log(martyrListByCity[item][0].cityId)
|
|
|
+ addTooltip(
|
|
|
+ `${martyrListByCity[item][0].cityId}`,
|
|
|
+ `烈士 ${martyrListByCity[item].length} | 亲属 ${relativeList[item]?.length || 0
|
|
|
+ } | 线索 ${clueList[item]?.length || 0}`
|
|
|
+ )
|
|
|
+ })
|
|
|
+ }
|
|
|
+ },
|
|
|
+ [addTooltip, clueList, relativeList]
|
|
|
+ )
|
|
|
+
|
|
|
+ // 鼠标移入移出点击城市版块
|
|
|
+ const mouseEnter = useCallback((e: MouseEvent) => {
|
|
|
+ const target = e.target as SVGElement
|
|
|
+ const tooltip = svgRef.current?.querySelector(`#tap-${target.id}Tooltip`)
|
|
|
+ if (tooltip) {
|
|
|
+ ; (tooltip as HTMLElement).style.opacity = '1'
|
|
|
+ }
|
|
|
+ }, [])
|
|
|
+ const mouseLeave = useCallback((e: MouseEvent) => {
|
|
|
+ const target = e.target as SVGElement
|
|
|
+ const tooltip = svgRef.current?.querySelector(`#tap-${target.id}Tooltip`)
|
|
|
+ if (tooltip) {
|
|
|
+ ; (tooltip as HTMLElement).style.opacity = '0'
|
|
|
}
|
|
|
}, [])
|
|
|
+ const clickCity = useCallback(
|
|
|
+ (e: MouseEvent) => {
|
|
|
+ if (isDragging) {
|
|
|
+ // 添加拖拽状态判断
|
|
|
+ setIsDragging(false)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const target = e.target as SVGElement
|
|
|
+ console.log(target.id)
|
|
|
+ focusOnCity(`tap-${target.id}`)
|
|
|
+ setTimeout(() => {
|
|
|
+ if (martyrListByCity[cityIdToName[`tap-${target.id}`]]) {
|
|
|
+ setCurCityId(`tap-${target.id}`)
|
|
|
+ } else {
|
|
|
+ setCurCityId('')
|
|
|
+ }
|
|
|
|
|
|
- useEffect(() => {
|
|
|
- getNumListFu(true)
|
|
|
- }, [getNumListFu])
|
|
|
+ }, 800)
|
|
|
+ },
|
|
|
+ [focusOnCity, isDragging, martyrListByCity]
|
|
|
+ )
|
|
|
|
|
|
- // 定时发送
|
|
|
- const timeRef = useRef(-1)
|
|
|
+ // 绘制箭头
|
|
|
+ const drawArrows = useCallback((startCityId: string, targetCityIds: string[]) => {
|
|
|
+ if (!svgRef.current) return
|
|
|
|
|
|
- useEffect(() => {
|
|
|
- clearInterval(timeRef.current)
|
|
|
- timeRef.current = window.setInterval(() => {
|
|
|
- getNumListFu()
|
|
|
- }, 120000)
|
|
|
- return () => {
|
|
|
- clearInterval(timeRef.current)
|
|
|
+ // 清除旧箭头
|
|
|
+ svgRef.current.querySelectorAll('.connection-arrow').forEach(arrow => arrow.remove())
|
|
|
+
|
|
|
+ const startCity = svgRef.current
|
|
|
+ .querySelector(`#${startCityId}`)
|
|
|
+ ?.querySelector('ellipse')
|
|
|
+ if (!startCity) return
|
|
|
+
|
|
|
+ // 获取起点坐标
|
|
|
+ const startX = startCity.cx.baseVal.value
|
|
|
+ const startY = startCity.cy.baseVal.value
|
|
|
+
|
|
|
+ targetCityIds.forEach(targetCityId => {
|
|
|
+ const targetCity = svgRef.current
|
|
|
+ ?.querySelector(`#${targetCityId}`)
|
|
|
+ ?.querySelector('ellipse')
|
|
|
+ if (!targetCity) return
|
|
|
+
|
|
|
+ // 获取终点坐标
|
|
|
+ const endX = targetCity.cx.baseVal.value
|
|
|
+ const endY = targetCity.cy.baseVal.value
|
|
|
+
|
|
|
+ // 修改弯曲幅度计算
|
|
|
+ const dx = endX - startX
|
|
|
+ const dy = endY - startY
|
|
|
+ const distance = Math.sqrt(dx * dx + dy * dy) // 计算两点距离
|
|
|
+ const curvature = distance * 0.2 // 动态弯曲幅度(原50px改为距离的20%)
|
|
|
+ const angle = Math.atan2(dy, dx)
|
|
|
+
|
|
|
+ // 调整控制点计算(增加最小弯曲限制)
|
|
|
+ const minCurvature = 30 // 最小弯曲幅度
|
|
|
+ const finalCurvature = Math.max(curvature, minCurvature)
|
|
|
+ const controlX =
|
|
|
+ (startX + endX) / 2 + finalCurvature * Math.cos(angle - Math.PI / 2)
|
|
|
+ const controlY =
|
|
|
+ (startY + endY) / 2 + finalCurvature * Math.sin(angle - Math.PI / 2)
|
|
|
+
|
|
|
+ // 创建路径
|
|
|
+ const path = document.createElementNS('http://www.w3.org/2000/svg', 'path')
|
|
|
+ path.classList.add('connection-arrow')
|
|
|
+ path.setAttribute(
|
|
|
+ 'd',
|
|
|
+ `M ${startX} ${startY} Q ${controlX} ${controlY} ${endX} ${endY}`
|
|
|
+ )
|
|
|
+ path.setAttribute('fill', 'none')
|
|
|
+ path.setAttribute('stroke', '#ff6600')
|
|
|
+ path.setAttribute('stroke-width', '1.5')
|
|
|
+ path.setAttribute('stroke-dasharray', '5,2')
|
|
|
+ path.setAttribute('marker-end', 'url(#arrowhead)')
|
|
|
+
|
|
|
+ // 添加到viewport
|
|
|
+ const viewport = svgRef?.current?.querySelector('.svg-pan-zoom_viewport')
|
|
|
+ // viewport?.appendChild(path);
|
|
|
+ // 插入在第一个tooltip前
|
|
|
+ viewport?.insertBefore(path, viewport.querySelector('g[id$="Tooltip"]'))
|
|
|
+ })
|
|
|
+ }, [])
|
|
|
+
|
|
|
+ // 删除箭头
|
|
|
+ const removeArrows = useCallback(() => {
|
|
|
+ if (svgRef.current) {
|
|
|
+ svgRef.current
|
|
|
+ .querySelectorAll('.connection-arrow')
|
|
|
+ .forEach(arrow => arrow.remove())
|
|
|
}
|
|
|
- }, [getNumListFu])
|
|
|
-
|
|
|
- // 动画是否在进行中
|
|
|
- const moveStaFu = useCallback(
|
|
|
- (code: string) => {
|
|
|
- const arr = [...list]
|
|
|
- list.forEach(v => {
|
|
|
- if (v.changeSta && v.code === code) v.changeSta = false
|
|
|
+ }, [])
|
|
|
+
|
|
|
+ // 点击进入烈士详情
|
|
|
+ const handleItemClick = useCallback(
|
|
|
+ (cityId: string, name: string, martyrId: number) => {
|
|
|
+ getMartyrDetail(martyrId).then(res => {
|
|
|
+ setMartyrDetail(res.data)
|
|
|
+ const arrowToCity = res.data.kinship.map((item: any) => nameToCityId[item.city]).filter((item: any) => item?.city !== res.data.nativeCity)
|
|
|
+ console.log(arrowToCity, 'arrowToCity')
|
|
|
+ drawArrows(cityId, arrowToCity)
|
|
|
+ })
|
|
|
+ getRelationList(martyrId).then(res => {
|
|
|
+ setRelationList(res.data)
|
|
|
})
|
|
|
- setList(arr)
|
|
|
+ setIsAddClassName(true)
|
|
|
+ focusOnCity(cityId)
|
|
|
+ changeTooltipText(cityId, name)
|
|
|
+ setEnableTooltipEvents(false)
|
|
|
+
|
|
|
},
|
|
|
- [list]
|
|
|
+ [changeTooltipText, drawArrows, focusOnCity, getMartyrDetail, getRelationList]
|
|
|
)
|
|
|
|
|
|
- // 新窗口打开页面
|
|
|
- const openPage = useCallback((url: string) => {
|
|
|
- window.open(url, '_blank')
|
|
|
- }, [])
|
|
|
+ // 设置拖拽
|
|
|
+ useEffect(() => {
|
|
|
+ if (svgRef.current) {
|
|
|
+ // 动态设置 viewBox 属性
|
|
|
+ svgRef.current.setAttribute('viewBox', '0 0 1420 945')
|
|
|
+ // 初始化 svg-pan-zoom
|
|
|
+ svgRef.current.style.width = '100%'
|
|
|
+ svgRef.current.style.height = '100%'
|
|
|
+ panZoomInstance.current = svgPanZoom(svgRef.current, {
|
|
|
+ zoomEnabled: true,
|
|
|
+ panEnabled: true,
|
|
|
+ controlIconsEnabled: false,
|
|
|
+ fit: true,
|
|
|
+ center: true,
|
|
|
+ onPan: () => {
|
|
|
+ setIsDragging(true)
|
|
|
+ setCurCityId('')
|
|
|
+ },
|
|
|
+ onZoom: () => setIsDragging(true)
|
|
|
+ })
|
|
|
+
|
|
|
+ // 组件加载完成后,将济南坐标放到屏幕中心
|
|
|
+ focusOnCity('tap-jinan')
|
|
|
+ const viewport = svgRef.current.querySelector(
|
|
|
+ '.svg-pan-zoom_viewport'
|
|
|
+ ) as SVGGElement
|
|
|
+ if (viewport) {
|
|
|
+ viewport.addEventListener('click', e => {
|
|
|
+ setIsAddClassName(false)
|
|
|
+ setEnableTooltipEvents(true)
|
|
|
+ removeArrows()
|
|
|
+ })
|
|
|
+ viewport.dispatchEvent(new MouseEvent('click'))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return () => {
|
|
|
+ // 组件卸载时销毁实例
|
|
|
+ if (panZoomInstance.current) {
|
|
|
+ panZoomInstance.current.destroy()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }, [focusOnCity, initialTooltip, removeArrows])
|
|
|
|
|
|
- // 跳甲方链接
|
|
|
- const toNewUrl = useCallback(() => {
|
|
|
- window.open(
|
|
|
- 'https://m.cctvnews.cctv.com/collect/index.html?actNumber=15091951045133564423',
|
|
|
- '_blank'
|
|
|
- )
|
|
|
+ // 初始添加tooltip
|
|
|
+ useEffect(() => {
|
|
|
+ const viewport = svgRef?.current?.querySelector(
|
|
|
+ '.svg-pan-zoom_viewport'
|
|
|
+ ) as SVGGElement
|
|
|
+ if (martyrListByCity && viewport) {
|
|
|
+ viewport.addEventListener('click', e => {
|
|
|
+ initialTooltip(martyrListByCity)
|
|
|
+ })
|
|
|
+ }
|
|
|
+ viewport.dispatchEvent(new MouseEvent('click'))
|
|
|
+ viewport.removeEventListener('click', e => {
|
|
|
+ initialTooltip(martyrListByCity)
|
|
|
+ })
|
|
|
+ }, [martyrListByCity, initialTooltip])
|
|
|
+
|
|
|
+ // 鼠标移入移出点击事件
|
|
|
+ useEffect(() => {
|
|
|
+ if (svgRef.current) {
|
|
|
+ const cityDom = svgRef.current
|
|
|
+ .querySelector('g')
|
|
|
+ ?.querySelector('g')
|
|
|
+ ?.querySelectorAll('path')
|
|
|
+ cityDom?.forEach(item => {
|
|
|
+ item.removeEventListener('mouseenter', mouseEnter)
|
|
|
+ item.removeEventListener('mouseleave', mouseLeave)
|
|
|
+ item.removeEventListener('click', clickCity)
|
|
|
+
|
|
|
+ if (enableTooltipEvents) {
|
|
|
+ item.addEventListener('mouseenter', mouseEnter)
|
|
|
+ item.addEventListener('mouseleave', mouseLeave)
|
|
|
+ item.addEventListener('click', clickCity)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ return () => {
|
|
|
+ cityDom?.forEach(item => {
|
|
|
+ item.removeEventListener('mouseenter', mouseEnter)
|
|
|
+ item.removeEventListener('mouseleave', mouseLeave)
|
|
|
+ item.removeEventListener('click', clickCity)
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }, [addTooltip, enableTooltipEvents, mouseEnter, mouseLeave, clickCity])
|
|
|
+
|
|
|
+ // 在useEffect中添加箭头标记定义
|
|
|
+ useEffect(() => {
|
|
|
+ if (svgRef.current) {
|
|
|
+ addDefs(svgRef)
|
|
|
+ }
|
|
|
}, [])
|
|
|
|
|
|
return (
|
|
|
<div className={styles.A1home}>
|
|
|
- <div className='A1main'>
|
|
|
- <div className='A1top'>
|
|
|
- <Swiper loop autoplay>
|
|
|
- {myInfo.swArr
|
|
|
- .filter(v => v.isSW)
|
|
|
- .map(item => (
|
|
|
- <Swiper.Item key={item.id}>
|
|
|
- <div
|
|
|
- // onClick={() => history.push(`/scene/${item.id}`)}
|
|
|
- onClick={() => openPage(item.link)}
|
|
|
- >
|
|
|
- <img
|
|
|
- src={`${myInfo.serverUrl}/bwCN/myData/img/${item.id}h.jpg`}
|
|
|
- alt=''
|
|
|
- />
|
|
|
- </div>
|
|
|
- </Swiper.Item>
|
|
|
- ))}
|
|
|
- </Swiper>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div className='A1box1'>
|
|
|
- <div className='A1tit'>展览精选</div>
|
|
|
- <div className='A1_1list'>
|
|
|
- {list
|
|
|
- .filter(v => v.loc === 1)
|
|
|
- .map(item => (
|
|
|
- <div className='A1_1row' key={item.id}>
|
|
|
- <img
|
|
|
- src={`${myInfo.serverUrl}/bwCN/myData/img/${item.id}s.jpg`}
|
|
|
- alt=''
|
|
|
- // onClick={() => history.push(`/scene/${item.id}`)}
|
|
|
- onClick={() => openPage(item.link)}
|
|
|
- />
|
|
|
- <div className='A1_1row1'>{item.name}</div>
|
|
|
- <div className='A1_1row2'>{item.partOf}</div>
|
|
|
- <div
|
|
|
- className={classNames('A1_1row3', item.changeSta ? 'A1_1row3Ac' : '')}
|
|
|
- >
|
|
|
- <EyeOutlined rev={undefined} />
|
|
|
-
|
|
|
- <CountUp
|
|
|
- onEnd={() => moveStaFu(item.code)}
|
|
|
- start={item.oldNum}
|
|
|
- end={item.newNum}
|
|
|
- />
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- ))}
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div className='FlooImg' onClick={toNewUrl}>
|
|
|
- <img src={flooImg} alt='' />
|
|
|
- </div>
|
|
|
-
|
|
|
- {/* <div onClick={() => getNumListFu()}>15456465</div> */}
|
|
|
- <div className='A1BottomTxt'>持续上新,敬请期待</div>
|
|
|
-
|
|
|
- {/* 公司logo */}
|
|
|
- <div className='A1logo'>
|
|
|
- <img src={logoImg} alt='' />
|
|
|
- </div>
|
|
|
+ <div className='map'>
|
|
|
+ <MapSvg ref={svgRef} id='mapSvg' />
|
|
|
+ {curCityId && (
|
|
|
+ <MapTab
|
|
|
+ cityId={curCityId}
|
|
|
+ setCityId={setCurCityId}
|
|
|
+ clueList={clueList}
|
|
|
+ relativeList={relativeList}
|
|
|
+ martyrListByCity={martyrListByCity}
|
|
|
+ handleItemClick={handleItemClick}
|
|
|
+ />
|
|
|
+ )}
|
|
|
</div>
|
|
|
+ <Left classN={isAddClassName ? 'animatedL' : ''} />
|
|
|
+ <Right
|
|
|
+ classN={isAddClassName ? 'animatedR' : ''}
|
|
|
+ handleItemClick={handleItemClick}
|
|
|
+ />
|
|
|
+ <Detail relationList={relationList} martyrDetail={martyrDetail} classN={isAddClassName ? 'animatedD' : ''} />
|
|
|
+ <img
|
|
|
+ className={isAddClassName ? 'headLine animatedH' : 'headLine'}
|
|
|
+ src={require('@/assets/img/headLineBg.png')}
|
|
|
+ alt=''
|
|
|
+ />
|
|
|
+ <BottomSearch handleItemClick={handleItemClick} classN={isAddClassName ? 'animatedB' : ''} />
|
|
|
</div>
|
|
|
)
|
|
|
}
|