diff --git a/src/api/block.ts b/src/api/block.ts index 513b720..d533d90 100644 --- a/src/api/block.ts +++ b/src/api/block.ts @@ -13,7 +13,7 @@ export class BlockChainApi { public coreConfig: CoreConfig; public blockConfig: BlockConfig public baseUrl = import.meta.env.VITE_BLOCK_URL; - // public baseUrl = "http://10.66.66.234:26657"; + // public baseUrl = "http://47.82.97.10:26657"; constructor() { this.coreConfig = {} as CoreConfig; diff --git a/src/pages/anti-dark-analysis-network/data/index.ts b/src/pages/anti-dark-analysis-network/data/index.ts index db909b9..9c406cc 100644 --- a/src/pages/anti-dark-analysis-network/data/index.ts +++ b/src/pages/anti-dark-analysis-network/data/index.ts @@ -4,7 +4,7 @@ export const screenData = { account: "admin", account_is_admin: true, exclusive: "none", - name: "default(10.66.66.234)-c250", + name: "default(47.82.97.10)-c250", proxies: ["意大利-米兰-312", "南苏丹-朱巴-374"], proxies_code: ["it", "ss"], use: true, @@ -14,7 +14,7 @@ export const screenData = { proxy_info: { exclusive: "", - name: "default(10.66.66.234)-c250", + name: "default(47.82.97.10)-c250", wg: false, change_time: 0, change_at: 0, diff --git a/src/pages/anti-forensics-forwarding/components/world-geo.tsx b/src/pages/anti-forensics-forwarding/components/world-geo.tsx index 2589917..73a7ebc 100644 --- a/src/pages/anti-forensics-forwarding/components/world-geo.tsx +++ b/src/pages/anti-forensics-forwarding/components/world-geo.tsx @@ -1,19 +1,20 @@ import { useEffect, useRef, memo, useState } 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"; + // 连线动画的间隔时间(毫秒) -const LINE_ANIMATION_INTERVAL = 500; // 3秒 +const LINE_ANIMATION_INTERVAL = 500; + interface LinesItemType { name: string; country_code: string; value: number[]; - color?: string; // 添加颜色属性 + color?: string; } + type LinesDataType = [LinesItemType, LinesItemType]; type LinesType = [string, LinesDataType[]]; @@ -29,18 +30,17 @@ const CustomTooltip = ({ }) => { const [visibleLogs, setVisibleLogs] = useState([]); const [isComplete, setIsComplete] = useState(false); - + // 使用useEffect实现逐条显示日志的效果 useEffect(() => { console.log("logs-------", logs.length); if (!logs || logs.length === 0) return; - + // 重置状态 setVisibleLogs([]); setIsComplete(false); - let currentIndex = 0; - + // 创建一个定时器,每500毫秒显示一条新日志 const timer = setInterval(() => { if (currentIndex < logs.length) { @@ -51,23 +51,22 @@ const CustomTooltip = ({ setIsComplete(true); } }, 500); - + // 清理函数 return () => { clearInterval(timer); }; }, [logs]); // 当logs变化时重新开始动画 - + // 自动滚动到最新的日志 const logsContainerRef = useRef(null); - useEffect(() => { if (logsContainerRef.current && visibleLogs.length > 0) { logsContainerRef.current.scrollTop = logsContainerRef.current.scrollHeight; } }, [visibleLogs]); - + return (
-
0 ? "日志加载中..." : "暂无日志记录"}
)} - - {/* {!isComplete && logs.length > 0 && ( -
- 处理中... -
- )} */} @@ -136,16 +128,6 @@ const CustomTooltip = ({ ); }; -// 添加一个淡入动画的CSS(可以放在你的全局CSS文件中) -// @keyframes fadeIn { -// from { opacity: 0; transform: translateY(5px); } -// to { opacity: 1; transform: translateY(0); } -// } -// -// .animate-fadeIn { -// animation: fadeIn 0.3s ease-out forwards; -// } - // 创建单个国家的涟漪效果 const createCountryRipple = (countryCode: string, color?: string) => { const coords = geoCoordMap[countryCode]; @@ -154,7 +136,7 @@ const createCountryRipple = (countryCode: string, color?: string) => { name: countryCodeMap[countryCode] ?? "", value: coords, country_code: countryCode, - color: color || "#0ea5e9", // 添加颜色属性,如果没有则使用默认颜色 + color: color || "#0ea5e9", }; }; @@ -167,7 +149,7 @@ export const WorldGeo = memo( setTooltipClosed, logs, }: { - logs:any[]; + logs: any[]; nestedEncryption: any; passAuthentication: any; dynamicRouteGeneration: any; @@ -190,12 +172,11 @@ export const WorldGeo = memo( >([]); const labelContainerRef = useRef(null); const labelsRef = useRef([]); - + // 添加状态来跟踪当前显示的连线索引 - const [nestedEncryptionLineIndex, setNestedEncryptionLineIndex] = - useState(-1); + const [nestedEncryptionLineIndex, setNestedEncryptionLineIndex] = useState(-1); const [dynamicRouteLineIndex, setDynamicRouteLineIndex] = useState(-1); - + // 添加状态来存储所有连线数据 const [nestedEncryptionLines, setNestedEncryptionLines] = useState< { from: string; to: string; color?: string }[] @@ -203,25 +184,25 @@ export const WorldGeo = memo( const [dynamicRouteLines, setDynamicRouteLines] = useState< { from: string; to: string; color?: string }[] >([]); - + // 添加状态来存储所有点 const [allPoints, setAllPoints] = useState([]); - + // 使用ref来跟踪动画状态,避免重新渲染 const animationTimerRef = useRef(null); const dynamicAnimationTimerRef = useRef(null); - + // 添加状态来跟踪数据是否已经变化 const nestedEncryptionKeyRef = useRef(""); const dynamicRouteKeyRef = useRef(""); - + + // 添加一个ref来跟踪图表是否已初始化 + const chartInitializedRef = useRef(false); + // 初始化时提取所有点的函数 const extractAllPoints = () => { const points: any[] = []; - - // console.log("Extracting points from nestedEncryption:", nestedEncryption); - // console.log("Extracting points from dynamicRouteGeneration:", dynamicRouteGeneration); - + // 从嵌套加密数据中提取点 if (nestedEncryption && Array.isArray(nestedEncryption)) { nestedEncryption.forEach((item: any) => { @@ -236,7 +217,6 @@ export const WorldGeo = memo( ) { points.push(fromPoint); } - // 如果有终点,也添加到点集合 if (dataItem.ingress_country_code) { const toCode = dataItem.ingress_country_code.toUpperCase(); @@ -249,7 +229,7 @@ export const WorldGeo = memo( } }); } - + // 从动态路由数据中提取点 if (dynamicRouteGeneration && Array.isArray(dynamicRouteGeneration)) { dynamicRouteGeneration.forEach((item: any) => { @@ -264,7 +244,6 @@ export const WorldGeo = memo( ) { points.push(fromPoint); } - // 如果有终点,也添加到点集合 if (dataItem.ingress_country_code) { const toCode = dataItem.ingress_country_code.toUpperCase(); @@ -277,11 +256,10 @@ export const WorldGeo = memo( } }); } - - console.log("Extracted points:", points); + return points; }; - + // 修改初始化逻辑,确保在数据变化时立即提取点 useEffect(() => { // 提取所有点 @@ -290,21 +268,18 @@ export const WorldGeo = memo( setAllPoints(points); } }, [nestedEncryption, dynamicRouteGeneration]); // 监听数据变化 - + // 启动嵌套加密连线动画的函数 const startNestedEncryptionAnimation = ( connections: { from: string; to: string; color?: string }[] ) => { if (connections.length === 0) return; - let index = 0; - + // 递归函数,用于按顺序显示连线 const animateNextLine = () => { setNestedEncryptionLineIndex(index); - index++; - if (index < connections.length) { animationTimerRef.current = setTimeout( animateNextLine, @@ -312,11 +287,11 @@ export const WorldGeo = memo( ); } }; - + // 开始动画 animateNextLine(); }; - + // 处理嵌套加密数据变化 useEffect(() => { // 清除任何现有的动画定时器 @@ -324,15 +299,15 @@ export const WorldGeo = memo( clearTimeout(animationTimerRef.current); animationTimerRef.current = null; } - + const allExtractedPoints: any[] = []; - + // 处理嵌套加密数据 if (nestedEncryption && Array.isArray(nestedEncryption)) { const points: any[] = []; const connections: { from: string; to: string; color?: string }[] = []; let shouldStartAnimation = false; - + nestedEncryption.forEach((item: any) => { if (item.data && Array.isArray(item.data)) { item.data.forEach((dataItem: any) => { @@ -350,7 +325,7 @@ export const WorldGeo = memo( allExtractedPoints.push(fromPoint); } } - + // 如果有终点,也添加到点集合 if (dataItem.ingress_country_code) { const toCode = dataItem.ingress_country_code.toUpperCase(); @@ -363,7 +338,7 @@ export const WorldGeo = memo( allExtractedPoints.push(toPoint); } } - + // 检查是否需要开始连线动画 if (item.isLine === true) { connections.push({ @@ -377,10 +352,10 @@ export const WorldGeo = memo( }); } }); - + // 生成当前数据的唯一键 const currentKey = JSON.stringify(nestedEncryption); - + // 检查数据是否变化 if ( currentKey !== nestedEncryptionKeyRef.current || @@ -388,11 +363,10 @@ export const WorldGeo = memo( ) { nestedEncryptionKeyRef.current = currentKey; setNestedEncryptionLines(connections); - + // 如果有连线数据且需要开始动画,重置索引并启动动画 if (connections.length > 0 && shouldStartAnimation) { setNestedEncryptionLineIndex(-1); // 重置索引 - // 启动连线动画 setTimeout(() => { startNestedEncryptionAnimation(connections); @@ -403,7 +377,7 @@ export const WorldGeo = memo( } } } - + // 更新所有点 if (allExtractedPoints.length > 0) { setAllPoints((prevPoints) => { @@ -425,7 +399,30 @@ export const WorldGeo = memo( }); } }, [nestedEncryption]); - + + // 启动动态路由连线动画的函数 + const startDynamicRouteAnimation = ( + connections: { from: string; to: string; color?: string }[] + ) => { + if (connections.length === 0) return; + let index = 0; + + // 递归函数,用于按顺序显示连线 + const animateNextLine = () => { + setDynamicRouteLineIndex(index); + index++; + if (index < connections.length) { + dynamicAnimationTimerRef.current = setTimeout( + animateNextLine, + LINE_ANIMATION_INTERVAL + ); + } + }; + + // 开始动画 + animateNextLine(); + }; + // 处理动态路由数据变化 useEffect(() => { // 清除任何现有的动画定时器 @@ -433,15 +430,15 @@ export const WorldGeo = memo( clearTimeout(dynamicAnimationTimerRef.current); dynamicAnimationTimerRef.current = null; } - + const allExtractedPoints: any[] = []; - + // 处理动态路由数据 if (dynamicRouteGeneration && Array.isArray(dynamicRouteGeneration)) { const points: any[] = []; const connections: { from: string; to: string; color?: string }[] = []; let shouldStartAnimation = false; - + dynamicRouteGeneration.forEach((item: any) => { if (item.data && Array.isArray(item.data)) { item.data.forEach((dataItem: any) => { @@ -459,7 +456,7 @@ export const WorldGeo = memo( allExtractedPoints.push(fromPoint); } } - + // 如果有终点,也添加到点集合 if (dataItem.ingress_country_code) { const toCode = dataItem.ingress_country_code.toUpperCase(); @@ -472,7 +469,7 @@ export const WorldGeo = memo( allExtractedPoints.push(toPoint); } } - + // 检查是否需要开始连线动画 if (item.isLine === true) { connections.push({ @@ -486,19 +483,18 @@ export const WorldGeo = memo( }); } }); - + // 生成当前数据的唯一键 const currentKey = JSON.stringify(dynamicRouteGeneration); - + // 检查数据是否变化 if (currentKey !== dynamicRouteKeyRef.current || shouldStartAnimation) { dynamicRouteKeyRef.current = currentKey; setDynamicRouteLines(connections); - + // 如果有连线数据且需要开始动画,重置索引并启动动画 if (connections.length > 0 && shouldStartAnimation) { setDynamicRouteLineIndex(-1); // 重置索引 - // 启动连线动画 setTimeout(() => { startDynamicRouteAnimation(connections); @@ -509,7 +505,7 @@ export const WorldGeo = memo( } } } - + // 更新所有点 if (allExtractedPoints.length > 0) { setAllPoints((prevPoints) => { @@ -531,33 +527,7 @@ export const WorldGeo = memo( }); } }, [dynamicRouteGeneration]); - - // 启动动态路由连线动画的函数 - const startDynamicRouteAnimation = ( - connections: { from: string; to: string; color?: string }[] - ) => { - if (connections.length === 0) return; - - let index = 0; - - // 递归函数,用于按顺序显示连线 - const animateNextLine = () => { - setDynamicRouteLineIndex(index); - - index++; - - if (index < connections.length) { - dynamicAnimationTimerRef.current = setTimeout( - animateNextLine, - LINE_ANIMATION_INTERVAL - ); - } - }; - - // 开始动画 - animateNextLine(); - }; - + // 组件卸载时清除定时器 useEffect(() => { return () => { @@ -571,7 +541,7 @@ export const WorldGeo = memo( } }; }, []); - + const getLineItem = ( preCode: string, nextCode: string, @@ -592,11 +562,11 @@ export const WorldGeo = memo( }, ]; }; - + const getLine = () => { // 实现数据处理 const solidData: LinesType[] = []; // 不再使用单一数组,而是分开存储 - + // 处理嵌套加密连线 - 放入单独的数组 if (nestedEncryptionLineIndex >= 0 && nestedEncryptionLines.length > 0) { const nestedLines: LinesDataType[] = []; @@ -614,7 +584,7 @@ export const WorldGeo = memo( solidData.push(["nested", nestedLines]); } } - + // 处理动态路由连线 - 放入单独的数组 if (dynamicRouteLineIndex >= 0 && dynamicRouteLines.length > 0) { const dynamicLines: LinesDataType[] = []; @@ -632,23 +602,25 @@ export const WorldGeo = memo( solidData.push(["dynamic", dynamicLines]); } } - + // 虚线数据处理(保持原有逻辑) const otherLineList: any = []; - + return { solidData, otherLineList, ripplePoints: allPoints, // 使用 allPoints 确保点始终显示 }; }; - + // 定位自定义提示框 - 优化版本 const positionCustomTooltip = () => { if (!customTooltipRef.current || !proxyGeoRef.current) return; + // 找到US点 const coords = geoCoordMap[nestedEncryption?.[0]?.code ?? "GL"]; if (!coords) return; + try { // 将地理坐标转换为屏幕坐标 const screenCoord = proxyGeoRef.current.convertToPixel("geo", coords); @@ -665,23 +637,24 @@ export const WorldGeo = memo( console.error("Error positioning tooltip:", error); } }; - + // 处理关闭tooltip const handleCloseTooltip = () => { setTooltipClosed(false); - setTooltipClosed(false); }; - + // 获取连线经纬度数据 const convertData = (data: LinesDataType[]) => { const res = []; const midpoints = []; + 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 ?? ""]; const fromCountry = dataIndex?.[0]?.country_code ?? ""; const toCountry = dataIndex?.[1]?.country_code ?? ""; + if (fromCoord && toCoord) { res.push({ coords: [fromCoord, toCoord], @@ -690,18 +663,22 @@ export const WorldGeo = memo( color: dataIndex?.[0]?.color || "#0ea5e9", }, }); + // 计算中点,考虑曲线的弧度 const curveness = -0.4; // 与飞线弧度相同 const x1 = fromCoord[0]; const y1 = fromCoord[1]; const x2 = toCoord[0]; const y2 = toCoord[1]; + // 计算控制点 const cpx = (x1 + x2) / 2 - (y2 - y1) * curveness; const cpy = (y1 + y2) / 2 - (x1 - x2) * curveness; + // 计算曲线上的中点 (t=0.5 时的贝塞尔曲线点) const midX = x1 * 0.25 + cpx * 0.5 + x2 * 0.25; const midY = y1 * 0.25 + cpy * 0.5 + y2 * 0.25; + midpoints.push({ id: `line-label-${index}`, midpoint: [midX, midY], @@ -710,11 +687,10 @@ export const WorldGeo = memo( }); } } - // 更新中点引用 - // lineMidpointsRef.current = midpoints; + return res; }; - + // 创建双层点效果 - 大点 const createDualLayerPoint = ( lastExit: LinesItemType, @@ -734,12 +710,15 @@ export const WorldGeo = memo( }; }) : []; + // 根据是否是主路径设置不同的大小和颜色 const outerSize = isMainPath ? 8 : 4; const innerSize = isMainPath ? 4 : 2; + // 使用传入的颜色或从数据中获取颜色,如果都没有则使用默认颜色 const outerColor = color || lastExit?.color || "#0ea5e9"; const innerColor = "#FFFFFF"; // 白色内层 + return [ { // 外层蓝色点,带涟漪效果 @@ -766,20 +745,13 @@ export const WorldGeo = memo( 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, @@ -801,15 +773,17 @@ export const WorldGeo = memo( } as echarts.SeriesOption, ]; }; - + // 添加新方法:根据经纬度数组创建蓝色涟漪小点(不包含白色内层点) const createRipplePointsFromCoordinates = ( coordinates: [number, number][], series: echarts.SeriesOption[] ) => { if (!coordinates || coordinates.length === 0) return; + // 使用selectedApp.color或默认蓝色 const outerColor = "#01FF5E"; + // 只创建外层带涟漪效果的点 series.push({ type: "effectScatter", @@ -833,7 +807,7 @@ export const WorldGeo = memo( })), } as echarts.SeriesOption); }; - + // 创建路径点的双层效果 const createPathPoints = ( dataItems: LinesDataType[], @@ -851,12 +825,15 @@ export const WorldGeo = memo( }, }; }); + // 根据是否是主路径设置不同的大小和颜色 const outerSize = isMainPath ? 8 : 4; const innerSize = isMainPath ? 4 : 2; + // 使用传入的颜色或从数据中获取颜色,如果都没有则使用默认颜色 const outerColor = color || dataItems[0]?.[0]?.color || "#0ea5e9"; const innerColor = "#FFFFFF"; // 白色内层 + return [ { // 外层蓝色点,带涟漪效果 @@ -881,20 +858,13 @@ export const WorldGeo = memo( 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, @@ -916,7 +886,7 @@ export const WorldGeo = memo( } as echarts.SeriesOption, ]; }; - + // 创建带自定义提示框的涟漪点 const createRipplePointsWithTooltip = (ripplePoints: any) => { return { @@ -948,20 +918,13 @@ export const WorldGeo = memo( 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, @@ -976,15 +939,16 @@ export const WorldGeo = memo( })), } as echarts.SeriesOption; }; - + // 连线 series const getLianData = (series: echarts.SeriesOption[]) => { const { solidData, otherLineList, ripplePoints } = getLine(); - + // 如果有需要显示涟漪效果的点,添加它们 if (ripplePoints.length > 0) { // 添加带自定义提示框的外层蓝色点 series.push(createRipplePointsWithTooltip(ripplePoints)); + // 添加内层白色点,不带涟漪效果 series.push({ type: "scatter", // 使用普通scatter,不带特效 @@ -1006,17 +970,17 @@ export const WorldGeo = memo( })), } as echarts.SeriesOption); } - + // 处理每个连线组 solidData.forEach((item) => { // 如果没有连线数据,则跳过 if (item[1].length === 0) { return; } - + // 为每条连线创建飞行线 const pathColor = item[0] === "nested" ? "#0ea5e9" : "#F0FFA2"; // 根据类型设置默认颜色 - + // 添加飞行线 series.push({ name: item[0], @@ -1043,11 +1007,11 @@ export const WorldGeo = memo( }, data: convertData(item[1]) as echarts.LinesSeriesOption["data"], }); - + // 添加路径点的双层效果 const pathPoints = createPathPoints(item[1], true, pathColor); series.push(...pathPoints); - + // 添加出口节点的双层效果 item[1].forEach((lineData) => { const lastExit = lineData[1]; @@ -1061,13 +1025,14 @@ export const WorldGeo = memo( } }); }); - + // 处理其他线(保持原有逻辑) otherLineList.forEach((line: any) => { line.forEach((item: any) => { const lastExit = item[1]?.[item[1].length - 1]?.[1] ?? null; // 获取当前路径的颜色 const pathColor = item[1]?.[0]?.[0]?.color || "#F0FFA2"; // 从第一个点获取颜色,如果没有则使用默认颜色 + // 添加虚线 series.push({ name: item[0], @@ -1086,9 +1051,11 @@ export const WorldGeo = memo( }, data: convertData(item[1]) as echarts.LinesSeriesOption["data"], }); + // 添加路径点的双层效果(次要路径) const pathPoints = createPathPoints(item[1], false, pathColor); series.push(...pathPoints); + // 添加出口节点的双层效果(次要路径) if (lastExit) { const exitNodes = createDualLayerPoint(lastExit, false, pathColor); @@ -1096,16 +1063,17 @@ export const WorldGeo = memo( } }); }); - + return true; }; - + // 创建A点和B点,并添加飞线和标签 const createSpecialPoints = (series: echarts.SeriesOption[]) => { // 定义点A和点B的坐标 const pointA = geoCoordMap[passAuthentication[0]?.startPoint ?? "GL"]; const pointB = geoCoordMap[passAuthentication[0]?.endPoint ?? "CA"]; const newPointB = [pointB[0] + 14, pointB[1] + 10]; + // 添加A点 - 带涟漪效果的双层点 series.push( // 外层带涟漪效果的点 @@ -1167,6 +1135,7 @@ export const WorldGeo = memo( ], } as echarts.SeriesOption ); + // 添加B点 - 大型圆形区域 series.push({ type: "scatter", @@ -1201,6 +1170,7 @@ export const WorldGeo = memo( }, ], } as echarts.SeriesOption); + // 添加A到B的飞线(无特效) series.push({ type: "lines", @@ -1221,18 +1191,22 @@ export const WorldGeo = memo( }, ], } as echarts.SeriesOption); + // 计算飞线中点坐标(考虑曲率) const x1 = pointA[0]; const y1 = pointA[1]; const x2 = newPointB[0]; const y2 = newPointB[1]; const curveness = -0.4; + // 计算控制点 const cpx = (x1 + x2) / 2 - (y2 - y1) * curveness; const cpy = (y1 + y2) / 2 - (x1 - x2) * curveness; + // 计算曲线上的中点 (t=0.5 时的贝塞尔曲线点) const midX = x1 * 0.25 + cpx * 0.5 + x2 * 0.25; const midY = y1 * 0.25 + cpy * 0.5 + y2 * 0.25; + // 将中点添加到 lineMidpointsRef 中,以便使用 DOM 方式创建标签 lineMidpointsRef.current.push({ id: "special-line-label", @@ -1240,13 +1214,14 @@ export const WorldGeo = memo( fromCountry: "A", toCountry: "B", }); + return series; }; - + const getOption = () => { const series: echarts.SeriesOption[] = []; getLianData(series); - // getMianLineTipData(series); // 添加主线tip 暂时隐藏 + if ( passAuthentication.length && passAuthentication[0]?.authenticationPoint @@ -1257,6 +1232,7 @@ export const WorldGeo = memo( series ); } + const option = { backgroundColor: "transparent", // 全局提示框配置 @@ -1266,7 +1242,6 @@ export const WorldGeo = memo( 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]; // 从光标偏移 @@ -1277,7 +1252,6 @@ export const WorldGeo = memo( map: "world", // 地图类型 roam: true, // 是否开启缩放 zoom: 1, // 初始缩放大小 - // center: [11.3316626, 19.5845024], // 地图中心点 layoutCenter: ["50%", "50%"], //地图位置 scaleLimit: { // 缩放等级 @@ -1330,9 +1304,10 @@ export const WorldGeo = memo( }, series: series, }; + return option; }; - + // 创建DOM标签 const createDOMLabels = () => { // 清除现有标签 @@ -1352,6 +1327,7 @@ export const WorldGeo = memo( container.style.width = "100%"; container.style.height = "100%"; container.style.overflow = "hidden"; + // 添加到地图容器 const chartDom = document.getElementById("screenGeo"); if (chartDom) { @@ -1360,6 +1336,7 @@ export const WorldGeo = memo( labelContainerRef.current = container; } } + // 创建新标签 lineMidpointsRef.current.forEach((point, index) => { const label = document.createElement("div"); @@ -1371,6 +1348,7 @@ export const WorldGeo = memo( label.style.whiteSpace = "nowrap"; label.style.pointerEvents = "none"; label.style.zIndex = "1001"; + // 特殊线标签(A到B的线) if (point.id === "special-line-label") { label.style.backgroundColor = "#8B3700"; @@ -1391,31 +1369,36 @@ export const WorldGeo = memo( label.style.fontWeight = "normal"; label.textContent = "SS签名"; } + // 添加到容器 labelContainerRef.current?.appendChild(label); labelsRef.current.push(label); }); + // 更新标签位置 updateLabelPositions(); }; - + // 更新标签位置 const updateLabelPositions = () => { if (!proxyGeoRef.current || !labelContainerRef.current) return; + lineMidpointsRef.current.forEach((point, index) => { const label = labelsRef.current[index]; if (!label) return; + const pixelPoint = proxyGeoRef.current?.convertToPixel( "geo", point.midpoint ); + if (pixelPoint && Array.isArray(pixelPoint)) { label.style.left = `${pixelPoint[0]}px`; label.style.top = `${pixelPoint[1]}px`; } }); }; - + const handleResize = () => { proxyGeoRef.current?.resize(); updateLabelPositions(); @@ -1423,70 +1406,118 @@ export const WorldGeo = memo( positionCustomTooltip(); } }; - - // 更新图表 + + // 更新图表 - 关键修改:使用 silent 参数避免重新渲染动画 useEffect(() => { - const option = getOption(); - proxyGeoRef.current?.setOption(option); - }, [nestedEncryptionLineIndex, dynamicRouteLineIndex, allPoints]); // 当连线索引或点变化时更新图表 - + if (!proxyGeoRef.current || !chartInitializedRef.current) return; + + // 获取当前系列 + const series: echarts.SeriesOption[] = []; + getLianData(series); + + // 使用 setOption 更新图表,但保持动画状态 + proxyGeoRef.current.setOption( + { series: series }, + { + notMerge: false, // 不合并会导致闪烁 + silent: true, // 静默更新,不触发动画重新开始 + lazyUpdate: true // 延迟更新 + } + ); + }, [nestedEncryptionLineIndex, dynamicRouteLineIndex]); + + // 处理数据变化 - 关键修改:使用 silent 参数避免重新渲染动画 useEffect(() => { + if (!proxyGeoRef.current || !chartInitializedRef.current) return; + lineMidpointsRef.current = []; // 重置中点数据 - const option = getOption(); - proxyGeoRef.current?.setOption(option); + + // 获取当前系列 + const series: echarts.SeriesOption[] = []; + getLianData(series); + + if ( + passAuthentication.length && + passAuthentication[0]?.authenticationPoint + ) { + createSpecialPoints(series); + createRipplePointsFromCoordinates( + passAuthentication[0]?.authenticationPoint || [], + series + ); + } + + // 使用 setOption 更新图表,但保持动画状态 + proxyGeoRef.current.setOption( + { series: series }, + { + notMerge: false, // 不合并会导致闪烁 + silent: true, // 静默更新,不触发动画重新开始 + lazyUpdate: true // 延迟更新 + } + ); + // 创建DOM标签 setTimeout(createDOMLabels, 100); }, [nestedEncryption, dynamicRouteGeneration, passAuthentication]); - + + // 初始化图表 useEffect(() => { const chartDom = document.getElementById("screenGeo"); proxyGeoRef.current = echarts.init(chartDom); + echarts.registerMap( "world", worldGeoJson as unknown as Parameters[1] ); - + // 初始化时提取所有点 const initialPoints = extractAllPoints(); if (initialPoints.length > 0) { setAllPoints(initialPoints); } - + const option = getOption(); option && proxyGeoRef.current?.setOption(option); + + // 标记图表已初始化 + chartInitializedRef.current = true; + // 添加地图交互事件监听器 proxyGeoRef.current?.on("georoam", updateLabelPositions); + // 页面resize时触发 window.addEventListener("resize", handleResize); + return () => { window.removeEventListener("resize", handleResize); proxyGeoRef.current?.off("georoam", updateLabelPositions); + // 清理DOM标签 if (labelContainerRef.current) { labelContainerRef.current.remove(); labelContainerRef.current = null; labelsRef.current = []; } + proxyGeoRef.current?.dispose(); proxyGeoRef.current = null; }; }, []); - + // 在地图初始化后定位tooltip useEffect(() => { if (tooltipClosed) { positionCustomTooltip(); } }, [tooltipClosed, nestedEncryption]); - + return (
{tooltipClosed && ( @@ -1495,3 +1526,5 @@ export const WorldGeo = memo( ); } ); + +export default WorldGeo; \ No newline at end of file diff --git a/src/pages/anti-forensics-forwarding/data/index.ts b/src/pages/anti-forensics-forwarding/data/index.ts index db909b9..9c406cc 100644 --- a/src/pages/anti-forensics-forwarding/data/index.ts +++ b/src/pages/anti-forensics-forwarding/data/index.ts @@ -4,7 +4,7 @@ export const screenData = { account: "admin", account_is_admin: true, exclusive: "none", - name: "default(10.66.66.234)-c250", + name: "default(47.82.97.10)-c250", proxies: ["意大利-米兰-312", "南苏丹-朱巴-374"], proxies_code: ["it", "ss"], use: true, @@ -14,7 +14,7 @@ export const screenData = { proxy_info: { exclusive: "", - name: "default(10.66.66.234)-c250", + name: "default(47.82.97.10)-c250", wg: false, change_time: 0, change_at: 0, diff --git a/src/pages/decentralized-lastic-network/data/index.ts b/src/pages/decentralized-lastic-network/data/index.ts index db909b9..9c406cc 100644 --- a/src/pages/decentralized-lastic-network/data/index.ts +++ b/src/pages/decentralized-lastic-network/data/index.ts @@ -4,7 +4,7 @@ export const screenData = { account: "admin", account_is_admin: true, exclusive: "none", - name: "default(10.66.66.234)-c250", + name: "default(47.82.97.10)-c250", proxies: ["意大利-米兰-312", "南苏丹-朱巴-374"], proxies_code: ["it", "ss"], use: true, @@ -14,7 +14,7 @@ export const screenData = { proxy_info: { exclusive: "", - name: "default(10.66.66.234)-c250", + name: "default(47.82.97.10)-c250", wg: false, change_time: 0, change_at: 0, diff --git a/src/pages/new-home/components/world-geo.tsx b/src/pages/new-home/components/world-geo.tsx index 0085ab1..b748288 100644 --- a/src/pages/new-home/components/world-geo.tsx +++ b/src/pages/new-home/components/world-geo.tsx @@ -130,7 +130,7 @@ const CustomTooltip = ({ />
- {filteredLogs.length > 0 && ( + {filteredLogs.length > 0 ? (
)} */}
+ ) : ( +
+ {logs.length > 0 ? "日志加载中..." : "暂无日志记录"} +
)} @@ -281,7 +285,7 @@ const CustomTooltipLeft = ({ /> - {filteredLogs.length > 0 && ( + {filteredLogs.length > 0 ? (
)} */}
+ ) : ( +
+ {logs.length > 0 ? "日志加载中..." : "暂无日志记录"} +
)} { - if (tooltipClosed) { setShowTooltip1(true); setShowTooltip2(true); // 确保另一个是关闭的 diff --git a/src/pages/new-home/data/index.ts b/src/pages/new-home/data/index.ts index db909b9..9c406cc 100644 --- a/src/pages/new-home/data/index.ts +++ b/src/pages/new-home/data/index.ts @@ -4,7 +4,7 @@ export const screenData = { account: "admin", account_is_admin: true, exclusive: "none", - name: "default(10.66.66.234)-c250", + name: "default(47.82.97.10)-c250", proxies: ["意大利-米兰-312", "南苏丹-朱巴-374"], proxies_code: ["it", "ss"], use: true, @@ -14,7 +14,7 @@ export const screenData = { proxy_info: { exclusive: "", - name: "default(10.66.66.234)-c250", + name: "default(47.82.97.10)-c250", wg: false, change_time: 0, change_at: 0, diff --git a/src/store/web3Slice.ts b/src/store/web3Slice.ts index 723d86e..6ddd4d9 100644 --- a/src/store/web3Slice.ts +++ b/src/store/web3Slice.ts @@ -84,7 +84,7 @@ const initialState: Iweb3Slice = { account: "admin", account_is_admin: true, exclusive: "none", - name: "default(10.66.66.234)-c250", + name: "default(47.82.97.10)-c250", proxies: [], proxies_code: [], use: true, @@ -94,7 +94,7 @@ const initialState: Iweb3Slice = { proxy_info: { exclusive: "", - name: "default(10.66.66.234)-c250", + name: "default(47.82.97.10)-c250", wg: false, change_time: 0, change_at: 0,