import { useEffect, useMemo, useRef, memo } from "react"; import * as echarts from "echarts"; // import 'echarts-gl'; // import { useQueryClient } from "@tanstack/react-query"; import type { EChartsType } from "echarts"; import worldGeoJson from "@/assets/echarts-map/json/world.json"; import { geoCoordMap, countryNameMap, countryCodeMap } from "@/data"; import { getUrl } from "@/lib/utils"; import { CONST_TOOLTIP_TYPE } from ".."; const planePathImg = "image://data:image/svg+xml;charset=utf-8;base64,PHN2ZyB3aWR0aD0iNjciIGhlaWdodD0iMTAyIiB2aWV3Qm94PSIwIDAgNjcgMTAyIiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8ZyBmaWx0ZXI9InVybCgjZmlsdGVyMF9mXzYxMTdfMjEyNDA3KSI+CjxwYXRoIGQ9Ik0zNC4yMTA5IDkxLjE4ODZMNTMuNjU3OCA0MC45NThDNTQuOTM4IDM3LjY1MTMgNTUuNzk4MyAzNC4xNTkyIDU1LjM1NjMgMzAuNjQxQzU0LjQzNTcgMjMuMzEyOCA1MC40Njg0IDExLjAyMDggMzQuMjExMiAxMS4wMjA4QzE5LjE5MDMgMTEuMDIwOCAxMy45MTEgMjEuNTE0NiAxMi4wNTU0IDI4Ljg5MTJDMTAuOTAxIDMzLjQ4MDYgMTEuOTkyNiAzOC4yMTg2IDEzLjgyMzEgNDIuNTgyN0wzNC4yMTA5IDkxLjE4ODZaIiBmaWxsPSJ1cmwoI3BhaW50MF9saW5lYXJfNjExN18yMTI0MDcpIi8+CjwvZz4KPGRlZnM+CjxmaWx0ZXIgaWQ9ImZpbHRlcjBfZl82MTE3XzIxMjQwNyIgeD0iMC44OTE3NDQiIHk9IjAuMzMxOTkiIHdpZHRoPSI2NS4yNzA3IiBoZWlnaHQ9IjEwMS41NDUiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIj4KPGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz4KPGZlQmxlbmQgbW9kZT0ibm9ybWFsIiBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHJlc3VsdD0ic2hhcGUiLz4KPGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iNS4zNDQ0MSIgcmVzdWx0PSJlZmZlY3QxX2ZvcmVncm91bmRCbHVyXzYxMTdfMjEyNDA3Ii8+CjwvZmlsdGVyPgo8bGluZWFyR3JhZGllbnQgaWQ9InBhaW50MF9saW5lYXJfNjExN18yMTI0MDciIHgxPSIzNS4yODI2IiB5MT0iMTAuODU2NCIgeDI9IjM1LjI4MjYiIHkyPSI4Ni44NTY0IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiMwMEYyRkYiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMTUwMEZGIi8+CjwvbGluZWFyR3JhZGllbnQ+CjwvZGVmcz4KPC9zdmc+Cg=="; interface LinesItemType { name: string; country_code: string; value: number[]; } type LinesDataType = [LinesItemType, LinesItemType]; type LinesType = [string, LinesDataType[]]; // 创建单个国家的涟漪效果 const createCountryRipple = (countryCode: string) => { const coords = geoCoordMap[countryCode]; if (!coords) return null; return { name: countryCodeMap[countryCode] ?? "", value: coords, country_code: countryCode, }; }; export const WorldGeo = memo( ({ screenData, selectedApp, tooltipType, tooltipClosed, setTooltipClosed, }: { screenData: any; selectedApp: any; tooltipType: string; tooltipClosed: boolean; setTooltipClosed: (value: boolean) => void; }) => { // const queryClient = useQueryClient() const customTooltipRef = useRef(null); const proxyGeoRef = useRef(null); const preMainToData = useRef<{ country_code: string }[]>([]); const mainToData = useMemo(() => { // 使用新的数据结构 const proxiesList = selectedApp && selectedApp?.jumpList ? [selectedApp.jumpList] : screenData?.proxy_info?.proxies ?? []; // 初始化数据数组 - 不再包含 startCountry const data: any = []; // 遍历代理列表 proxiesList.forEach((proxyItem: any) => { // 检查是否有数据数组 if (proxyItem.data && Array.isArray(proxyItem.data)) { // 遍历数据数组中的每个项目 proxyItem.data.forEach((item: any) => { // 如果有 ingress_country_code,则添加一对起点和终点 if (item.ingress_country_code) { // 添加起点(country_code) data.push({ country_code: item.country_code, type: "start", isLine: proxyItem.isLine, // 保存连线标志 }); // 添加终点(ingress_country_code) data.push({ country_code: item.ingress_country_code, type: "end", isLine: proxyItem.isLine, // 保存连线标志 }); } else { // 如果没有 ingress_country_code,只添加 country_code data.push({ country_code: item.country_code, isLine: proxyItem.isLine, // 保存连线标志 }); } }); } }); return data; }, [screenData, selectedApp]); // 创建自定义提示框DOM元素 const createCustomTooltip = () => { // 如果已经存在自定义提示框,则移除它 if (document.getElementById("custom-fixed-tooltip")) { document.getElementById("custom-fixed-tooltip")?.remove(); } // 创建自定义提示框 const tooltip = document.createElement("div"); tooltip.id = "custom-fixed-tooltip"; tooltip.style.position = "fixed"; tooltip.style.zIndex = "1000"; tooltip.style.pointerEvents = "auto"; tooltip.style.backgroundColor = "transparent"; // 设置提示框内容 const currentTooltipType = CONST_TOOLTIP_TYPE[ tooltipType as keyof typeof CONST_TOOLTIP_TYPE ] || CONST_TOOLTIP_TYPE.NESTED_ENCRYPTION; tooltip.innerHTML = `
${ currentTooltipType.title }
`; // 添加到DOM document.body.appendChild(tooltip); customTooltipRef.current = tooltip; // 添加关闭按钮事件 const closeButton = tooltip.querySelector(".close-icon"); if (closeButton) { closeButton.addEventListener("click", () => { setTooltipClosed(false); tooltip.remove(); customTooltipRef.current = null; }); } // 定位提示框 positionCustomTooltip(); }; // 定位自定义提示框 - 优化版本 const positionCustomTooltip = () => { if (!customTooltipRef.current || !proxyGeoRef.current) return; // 找到US点 const coords = geoCoordMap["CA"]; if (!coords) return; try { // 将地理坐标转换为屏幕坐标 const screenCoord = proxyGeoRef.current.convertToPixel( "geo", coords ); if ( screenCoord && Array.isArray(screenCoord) && screenCoord.length === 2 ) { // 设置提示框位置 // customTooltipRef.current.style.left = `${ // screenCoord[0] + screenCoord[0] / 2 + 18 // }px`; // customTooltipRef.current.style.top = `${ // screenCoord[1] - screenCoord[1] / 2 + 7 // }px`; // 设置提示框位置 customTooltipRef.current.style.left = `${ screenCoord[0] + 232 + 7 }px`; customTooltipRef.current.style.top = `${ screenCoord[1] + 40 - 190 }px`; } } catch (error) { console.error("Error positioning tooltip:", error); } }; // 主线每个节点tip竖线的经纬度 const mianLineData = (data: typeof mainToData) => { return ( data .map((item: any) => { const countryCode = item.country_code.toUpperCase(); const coords = geoCoordMap[countryCode] as | [number, number] | undefined; if (!coords) return null; return { name: countryCodeMap[countryCode], coords: [coords, [coords[0], coords[1] + 4]], value: countryCode, }; }) .filter((v: any) => !!v) ?? [] ); }; const getLineItem = ( preCode: string, nextCode: string ): [LinesItemType, LinesItemType] => { return [ { name: countryCodeMap[preCode] ?? "", value: geoCoordMap[preCode] ?? [], country_code: preCode, }, { name: countryCodeMap[nextCode] ?? "", value: geoCoordMap[nextCode] ?? [], country_code: nextCode, }, ]; }; const getLine = () => { // 实现数据处理 const solidData: LinesType[] = [["main", []]]; // 使用"main"替代startCountry.country_code // 收集需要显示涟漪效果的所有点(包括连线和不连线的) const ripplePoints: any[] = []; // 处理主路径数据 for (let i = 0; i < mainToData.length; i++) { // 如果是最后一个元素,则跳过(因为没有下一个元素作为终点) if (i === mainToData.length - 1) continue; const currentItem = mainToData[i]; const nextItem = mainToData[i + 1]; // 获取当前国家代码 const countryCode = currentItem.country_code.toUpperCase(); // 如果当前项是起点,下一项是终点 if (currentItem.type === "start" && nextItem.type === "end") { const startCode = countryCode; const endCode = nextItem.country_code.toUpperCase(); // 无论是否连线,都添加点的涟漪效果 const startPoint = createCountryRipple(startCode); const endPoint = createCountryRipple(endCode); if (startPoint) ripplePoints.push(startPoint); if (endPoint) ripplePoints.push(endPoint); // 检查是否应该绘制连线 if (currentItem.isLine !== false) { solidData[0]?.[1].push(getLineItem(startCode, endCode)); } // 跳过下一项,因为已经处理了 i++; } // 常规情况:当前项到下一项 else { const nextCountryCode = nextItem.country_code.toUpperCase(); // 无论是否连线,都添加点的涟漪效果 const currentPoint = createCountryRipple(countryCode); const nextPoint = createCountryRipple(nextCountryCode); if (currentPoint) ripplePoints.push(currentPoint); if (nextPoint) ripplePoints.push(nextPoint); // 检查是否应该绘制连线 if (currentItem.isLine !== false) { solidData[0]?.[1].push( getLineItem(countryCode, nextCountryCode) ); } } } // 虚线数据处理(保持原有逻辑) const pathList = screenData?.path_list?.filter( (v: any) => v.name !== screenData?.proxy_info?.name ) ?? []; const otherLineList = pathList.map(() => {}); return { solidData, otherLineList, ripplePoints, }; }; // 获取连线经纬度数据 const convertData = (data: LinesDataType[]) => { const res = []; for (let index = 0; index < data.length; index++) { const dataIndex = data[index]; const fromCoord = geoCoordMap[dataIndex?.[0]?.country_code ?? ""]; const toCoord = geoCoordMap[dataIndex?.[1]?.country_code ?? ""]; if (fromCoord && toCoord) { res.push([fromCoord, toCoord]); } } return res; }; // 创建双层点效果 - 大点 const createDualLayerPoint = ( lastExit: LinesItemType, isMainPath: boolean = true ) => { // 创建数据数组,用于两个散点图层 const pointData = lastExit ? [lastExit].map((v) => { return { name: v.name, value: v.value, datas: { country_code: v.country_code, }, }; }) : []; // 根据是否是主路径设置不同的大小和颜色 const outerSize = isMainPath ? 8 : 4; const innerSize = isMainPath ? 4 : 2; // Use selectedApp.color if available, otherwise default to blue const outerColor = selectedApp?.color || "#0ea5e9"; // Use selectedApp.color with fallback const innerColor = "#FFFFFF"; // 白色内层 return [ { // 外层蓝色点,带涟漪效果 type: "effectScatter", coordinateSystem: "geo", zlevel: 3, color: outerColor, symbol: "circle", symbolSize: outerSize, rippleEffect: { period: 8, // 动画时间,值越小速度越快 brushType: "stroke", // 波纹绘制方式 stroke scale: 6, // 波纹圆环最大限制,值越大波纹越大 brushWidth: 2, }, label: { show: false, }, tooltip: { show: false, trigger: "item", showContent: true, alwaysShowContent: true, formatter: (params: any) => { // const countryCode = params.data.datas.country_code; // const countryName = params.data.name; // 创建自定义HTML提示框 return `
嵌套加密
`; }, backgroundColor: "transparent", borderWidth: 0, }, data: pointData, } as echarts.SeriesOption, { // 内层白色点,不带涟漪效果 type: "scatter", // 使用普通scatter,不带特效 coordinateSystem: "geo", zlevel: 4, // 确保在蓝色点上方 color: innerColor, symbol: "circle", symbolSize: innerSize, label: { show: false, }, data: pointData, } as echarts.SeriesOption, ]; }; // 创建路径点的双层效果 const createPathPoints = ( dataItems: LinesDataType[], isMainPath: boolean = true ) => { // 创建数据数组 const pointData = dataItems.map((dataItem: LinesDataType) => { return { name: dataItem[0].name, value: geoCoordMap[dataItem[0].country_code], datas: { country_code: dataItem[0].country_code, }, }; }); // 根据是否是主路径设置不同的大小和颜色 const outerSize = isMainPath ? 8 : 4; const innerSize = isMainPath ? 4 : 2; // Use selectedApp.color if available, otherwise default to blue const outerColor = selectedApp?.color || "#0ea5e9"; // Use selectedApp.color with fallback const innerColor = "#FFFFFF"; // 白色内层 return [ { // 外层蓝色点,带涟漪效果 type: "effectScatter", coordinateSystem: "geo", zlevel: 3, color: outerColor, symbol: "circle", symbolSize: outerSize, rippleEffect: { period: 8, // 动画时间,值越小速度越快 brushType: "stroke", // 波纹绘制方式 stroke scale: 6, // 波纹圆环最大限制,值越大波纹越大 brushWidth: 2, }, label: { show: false, }, tooltip: { show: false, trigger: "item", formatter: (params: any) => { // const countryCode = params.data.datas.country_code; // const countryName = params.data.name; // 创建自定义HTML提示框 return `
嵌套加密
`; }, backgroundColor: "transparent", borderWidth: 0, }, data: pointData, } as echarts.SeriesOption, { // 内层白色点,不带涟漪效果 type: "scatter", // 使用普通scatter,不带特效 coordinateSystem: "geo", zlevel: 4, // 确保在蓝色点上方 color: innerColor, symbol: "circle", symbolSize: innerSize, label: { show: false, }, data: pointData, } as echarts.SeriesOption, ]; }; // 创建带自定义提示框的涟漪点 const createRipplePointsWithTooltip = (ripplePoints: any) => { // Use selectedApp.color if available, otherwise default to blue const outerColor = selectedApp?.color || "#0ea5e9"; // Use selectedApp.color with fallback return { type: "effectScatter", coordinateSystem: "geo", zlevel: 3, color: outerColor, symbol: "circle", symbolSize: 8, rippleEffect: { period: 8, brushType: "stroke", scale: 6, brushWidth: 2, }, label: { show: false, formatter: (params: any) => { return `{${params.data.datas.country_code}|}`; }, }, // 添加提示框配置 tooltip: { show: false, trigger: "item", formatter: (params: any) => { // const countryCode = params.data.datas.country_code; // const countryName = params.data.name; // 创建自定义HTML提示框 return `
嵌套加密
`; }, backgroundColor: "transparent", borderWidth: 0, }, data: ripplePoints.map((point: any) => ({ name: point.name, value: point.value, datas: { country_code: point.country_code, }, })), } as echarts.SeriesOption; }; // 连线 series const getLianData = (series: echarts.SeriesOption[]) => { const { solidData, otherLineList, ripplePoints } = getLine(); console.log(solidData, "solidData"); console.log(otherLineList, "otherLineList"); console.log(ripplePoints, "ripplePoints"); // 如果有需要显示涟漪效果的点,添加它们 if (ripplePoints.length > 0) { // 添加带自定义提示框的外层蓝色点 series.push(createRipplePointsWithTooltip(ripplePoints)); // 添加内层白色点,不带涟漪效果 series.push({ type: "scatter", // 使用普通scatter,不带特效 coordinateSystem: "geo", zlevel: 4, // 确保在蓝色点上方 color: "#FFFFFF", // 白色内层 symbol: "circle", symbolSize: 4, label: { show: false, }, data: ripplePoints.map((point) => ({ name: point.name, value: point.value, datas: { country_code: point.country_code, }, })), } as echarts.SeriesOption); } solidData.forEach((item) => { // 如果没有连线数据,则跳过 if (item[1].length === 0) { return; } const lastExit = item[1]?.[item[1].length - 1]?.[1] ?? null; // 添加飞行线 series.push({ name: item[0], // todo ! 需要在飞线中间添加label type: "lines", zlevel: 1, label: { show: false, position: "middle", formatter: (params: any) => { // 使用自定义样式的文本 return ( "{text|SS签名}" ); }, rich: { text: { color: "#FFB27A", // 字体颜色为 #FFB27A fontSize: 18, // 字体大小为 18px padding: [10,10,10,10], // padding 上下为 4,左右为 8 backgroundColor: "#8B3700", // 背景颜色为 #8B3700 borderRadius: 4, // 可选:添加圆角效果 }, }, // 不需要额外的背景色,因为已经在 rich 中设置了 backgroundColor: "transparent", padding: [0, 0, 0, 0], }, // 飞行线特效 effect: { show: true, // 是否显示 period: 4, // 特效动画时间 trailLength: 0.7, // 特效尾迹长度。取从 0 到 1 的值,数值越大尾迹越长 symbol: planePathImg, // 特效图形标记 symbolSize: [10, 20], }, // 线条样式 lineStyle: { curveness: -0.4, // 飞线弧度 type: "solid", // 飞线类型 color: selectedApp?.color || "#0ea5e9", // Use selectedApp.color with fallback width: 1.5, // 飞线宽度 opacity: 0.1, }, data: convertData( item[1] ) as echarts.LinesSeriesOption["data"], }); // 添加路径点的双层效果 const pathPoints = createPathPoints(item[1], true); series.push(...pathPoints); // 添加出口节点的双层效果 if (lastExit) { const exitNodes = createDualLayerPoint(lastExit, true); series.push(...exitNodes); } }); otherLineList.forEach((line: any) => { line.forEach((item: any) => { const lastExit = item[1]?.[item[1].length - 1]?.[1] ?? null; // 添加虚线 series.push({ name: item[0], type: "lines", zlevel: 1, label: { show: false, }, // 线条样式 lineStyle: { curveness: -0.4, // 飞线弧度 type: [5, 5], // 飞线类型 color: "#F0FFA2", // 飞线颜色 width: 0.5, // 飞线宽度 opacity: 0.6, }, data: convertData( item[1] ) as echarts.LinesSeriesOption["data"], }); // 添加路径点的双层效果(次要路径) const pathPoints = createPathPoints(item[1], false); series.push(...pathPoints); // 添加出口节点的双层效果(次要路径) if (lastExit) { const exitNodes = createDualLayerPoint(lastExit, false); series.push(...exitNodes); } }); }); return true; }; // 主线tip series const getMianLineTipData = (series: echarts.SeriesOption[] = []) => { const rich = Object.keys(countryCodeMap).reduce((object, code) => { object[code] = { color: "transparent", height: 20, width: 20, align: "left", backgroundColor: { image: getUrl( `image/res/flag/${code.toUpperCase()}.png` ), // 动态生成国旗图标 }, }; return object; }, {} as { [key: string]: { [key: string]: number | string | unknown } }); series.push( // 柱状体的主干 { name: "solidTip", type: "lines", zlevel: 5, effect: { show: false, symbolSize: 5, // 图标大小 }, lineStyle: { width: 2, // 尾迹线条宽度 color: "#F0FFA2", opacity: 1, // 尾迹线条透明度 curveness: 0, // 尾迹线条曲直度 }, label: { show: true, position: "end", color: "#fff", formatter: (parameters) => { return `{left|} {gap1|}{${parameters.value}|}{gap2|}{name|${parameters.name}}{gap3|}{right|}`; }, rich: { left: { color: "transparent", height: 35, width: 8, align: "center", backgroundColor: { image: `data:image/svg+xml;charset=utf-8;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMSIgaGVpZ2h0PSI1OCIgdmlld0JveD0iMCAwIDExIDU4IiBmaWxsPSJub25lIj4KPHBhdGggZD0iTTExIDU2LjA4ODRIMVY0Ni4wODg0IiBzdHJva2U9IiNGRkZERDMiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIi8+CjxwYXRoIGQ9Ik0xIDExVjFIMTEiIHN0cm9rZT0iI0ZGRkREMyIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiLz4KPHBhdGggZD0iTTguNzI5NDkgMTlWMzkiIHN0cm9rZT0iI0ZGRkREMyIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiLz4KPC9zdmc+`, // 动态生成国旗图标 }, }, gap1: { height: 35, width: 8, }, ...rich, gap2: { height: 35, width: 6, }, name: { color: "#fff", align: "center", lineHeight: 35, fontSize: 14, padding: [2, 0, 0, 0], }, gap3: { height: 35, width: 8, }, right: { color: "transparent", height: 35, width: 8, align: "center", backgroundColor: { image: `data:image/svg+xml;charset=utf-8;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNCIgaGVpZ2h0PSI1OCIgdmlld0JveD0iMCAwIDE0IDU4IiBmaWxsPSJub25lIj4KPHBhdGggZD0iTTEyLjczMDIgNDYuMDQzOVY1Ni4wNDM5SDIuNzMwMjIiIHN0cm9rZT0iI0ZGRkREMyIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiLz4KPHBhdGggZD0iTTIuNzMwMjIgMS4wNDM5NUgxMi43MzAyVjExLjA0MzkiIHN0cm9rZT0iI0ZGRkREMyIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiLz4KPHBhdGggZD0iTTEgMTkuMDQzOVYzOS4wNDM5IiBzdHJva2U9IiNGRkZERDMiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIi8+Cjwvc3ZnPg==`, // 动态生成国旗图标 }, }, }, backgroundColor: "#080A00", }, silent: true, data: mianLineData(mainToData), } ); }; const isCN = (code: string) => { return ["HK", "MO", "TW", "CN"].includes(code.toUpperCase()); }; const getRegions = () => { const codeList: string[] = []; const regionsData = mainToData; regionsData.forEach((item: any) => codeList.push( isCN(item.country_code) ? "CN" : item.country_code.toUpperCase() ) ); const regions = codeList.map((item) => { return { name: countryCodeMap[item], // 中国 itemStyle: { color: "#172554", areaColor: "#172554", borderColor: "#0ea5e9", // 边框颜色 borderWidth: 1.2, // 边框宽度 borderType: "solid", // 修改为实线边框 }, }; }); return regions; }; const getOption = () => { const series: echarts.SeriesOption[] = []; getLianData(series); // getMianLineTipData(series);// 添加主线tip 暂时隐藏 const regions = getRegions(); const option = { backgroundColor: "transparent", // 全局提示框配置 tooltip: { show: true, trigger: "item", enterable: true, confine: true, // 保持提示框在图表范围内 appendToBody: true, // 将提示框附加到body以获得更好的定位 // position: function(pos:any, params, dom, rect, size) { position: function (pos: any) { // 自定义定位逻辑(如果需要) return [pos[0] + 10, pos[1] - 50]; // 从光标偏移 }, }, // 底图样式 geo: { map: "world", // 地图类型 roam: true, // 是否开启缩放 zoom: 1, // 初始缩放大小 // center: [11.3316626, 19.5845024], // 地图中心点 layoutCenter: ["50%", "50%"], //地图位置 scaleLimit: { // 缩放等级 min: 1, max: 3, }, label: { show: false, }, nameMap: countryNameMap, // 自定义地区的名称映射 // 三维地理坐标系样式 itemStyle: { areaColor: "#020617", // 修改为要求的填充颜色 borderColor: "#cbd5e1", // 修改为要求的边框颜色 borderWidth: 1, // 边框宽度 borderType: "dashed", // 修改为点线边框 }, emphasis: { itemStyle: { areaColor: "#172554", // 修改为鼠标悬停时的填充颜色 borderColor: "#0ea5e9", // 修改为鼠标悬停时的边框颜色 borderWidth: 1.2, // 修改为鼠标悬停时的边框宽度 borderType: "solid", // 修改为实线边框 }, label: false, }, tooltip: { show: true, trigger: "item", triggerOn: "click", // 提示框触发的条件 enterable: true, // 鼠标是否可进入提示框浮层中,默认为false,如需详情内交互,如添加链接,按钮,可设置为 true backgroundColor: "rgba(0,0,0,0.8)", borderColor: "rgba(0,0,0,0.2)", textStyle: { color: "#fff", }, formatter: (parameters: { name: string; data: | { name: string; datas: { tradingCountry: string }; } | undefined; }) => { if (parameters.data?.name) return parameters.data.name; return parameters.name; }, }, regions, }, series: series, }; return option; }; const handleResize = () => { proxyGeoRef.current?.resize(); }; useEffect(() => { preMainToData.current?.some( (item, index) => item.country_code !== mainToData[index]?.country_code ) && proxyGeoRef.current?.clear(); preMainToData.current = mainToData; const option = getOption(); proxyGeoRef.current?.setOption(option); }, [screenData, mainToData]); useEffect(() => { const chartDom = document.getElementById("screenGeo"); proxyGeoRef.current = echarts.init(chartDom); echarts.registerMap( "world", worldGeoJson as unknown as Parameters< typeof echarts.registerMap >[1] ); const option = getOption(); option && proxyGeoRef.current?.setOption(option); // 页面resize时触发 window.addEventListener("resize", handleResize); return () => { window.removeEventListener("resize", handleResize); proxyGeoRef.current?.dispose(); proxyGeoRef.current = null; }; }, []); useEffect(() => { if (tooltipClosed) { createCustomTooltip(); } }, [tooltipClosed, tooltipType]); return (
); } );