diff --git a/index.html b/index.html index 1741dfd..d4ca00a 100644 --- a/index.html +++ b/index.html @@ -12,7 +12,7 @@ - + @@ -23,8 +23,11 @@ - 不阻塞 HTML 解析与首屏渲染(解决白屏) - 保持脚本执行顺序(cesium/hls/veplayer 先于 main.tsx 执行) --> + - + @@ -78,27 +81,27 @@ diff --git a/src/components/Map.jsx b/src/components/Map.jsx index f53c81b..1becfc2 100644 --- a/src/components/Map.jsx +++ b/src/components/Map.jsx @@ -1,35 +1,24 @@ -import React, { useEffect, useRef, useState, useMemo, useImperativeHandle, forwardRef, useCallback, useLayoutEffect } from 'react'; +import React, { useEffect, useRef, useState, useMemo, useImperativeHandle, forwardRef, useCallback } from 'react'; import carImg from '../assets/images/carplane.png'; import inspectImg from "../assets/images/inspectCar.png" import planeImg from '../assets/images/planeicon.png'; import cameraImg from '../assets/images/camera.png'; import droneInspectImg from '../assets/images/xunjiancar.png'; -import gecocar from '../assets/images/gecaocar.png'; -import arrowImg from '../assets/images/arrow.png'; + import iotImg from '../assets/images/iot.png'; import envConfig from '../../env'; import { TracePoint, TPAction, TPMode } from '../lib/TracePoint/tracePoint'; import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; import { getSiteMapInfo, getStationRegions } from '../api/stationManage'; +import { Button } from "antd"; +import { EnvironmentOutlined } from "@ant-design/icons"; +import * as Cesium from 'cesium'; -const CYAN_ALARM_COLOR = new window.Cesium.Color( - 34 / 255, - 211 / 255, - 238 / 255, - 1 -); +import 'cesium/Build/Cesium/Widgets/widgets.css'; -const CYAN_GLOW_COLOR = new window.Cesium.Color( - 34 / 255, - 211 / 255, - 238 / 255, - 1 -); -window.Cesium.Ion.defaultAccessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJmNjk0NjI1MC0wZGYyLTQ1YzItYjJmZi0wY2M5ODEwODllYzIiLCJpZCI6Mzk3MTQxLCJpYXQiOjE3NzI1MjkwMjV9.U6qmakhDgXJVXwLFyWzSxAUOFZRrDkIsRUPP6CCdnKg"; - -const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, finishPoint, drawRealTime, realtimePosition, realtimeType, pathPoints = [], devices = [], onCameraClick, focusLocation, points = [], markedPoints = [], onPathChange, from, onClearDraw, onMarkPoint, routeMode = '', polyPoints, mapLayer, activeType, overlayRefreshKey = 0, focusOverlay = null, onObstacleRadiusChange }, ref) => { +const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, finishPoint, drawRealTime, realtimePosition, realtimeType, pathPoints = [], devices = [], onCameraClick, focusLocation, points = [], markedPoints = [], onPathChange, from, onClearDraw, onMarkPoint, routeMode = '', polyPoints, mapLayer, activeType, overlayRefreshKey = 0, focusOverlay = null, onObstacleRadiusChange, siteMapInfoId }, ref) => { const containerRef = useRef(null); const viewerRef = useRef(null); const tooltipRef = useRef(null); @@ -47,6 +36,8 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, const lastMarkedPointsRef = useRef([]); // 上次渲染的标记点数据,用于增量更新 const onMarkPointRef = useRef(onMarkPoint); // 总是指向最新的 onMarkPoint,避免闭包滞后 const onCameraClickRef = useRef(onCameraClick); // 同样处理,避免闭包滞后 + const lastHoverPositionRef = useRef(null); + const hasInitialDevicesFlyRef = useRef(false); const { t } = useTranslation(); // 场站已配置的返航点 / 分区叠加显示 @@ -127,7 +118,6 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, const focusEntityRef = useRef(null); const hiddenByFocusRef = useRef([]); - const yellowCircleCanvas = useMemo( () => createYellowCircleCanvas(30), [] @@ -152,9 +142,9 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, // 统一相机飞行调度器 // ============================== const FLIGHT_PRIORITY = { - layer: 10, + devices: 10, track: 30, - devices: 40, + layer: 40, focus: 60, realtime: 70, manual: 100, @@ -167,6 +157,57 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, flying: false, }); + useEffect(() => { + if (!containerRef.current) return; + if (viewerRef.current) return; + Cesium.Ion.defaultAccessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJmNjk0NjI1MC0wZGYyLTQ1YzItYjJmZi0wY2M5ODEwODllYzIiLCJpZCI6Mzk3MTQxLCJpYXQiOjE3NzI1MjkwMjV9.U6qmakhDgXJVXwLFyWzSxAUOFZRrDkIsRUPP6CCdnKg"; + + + const viewer = new Cesium.Viewer(containerRef.current, { + useBrowserRecommendedResolution: false, + //terrain: Cesium.Terrain.fromWorldTerrain(), + + + animation: false, + timeline: false, + baseLayerPicker: true, + geocoder: false, + homeButton: false, + sceneModePicker: false, + navigationHelpButton: false, + fullscreenButton: false, + infoBox: false, + imageryProvider: new Cesium.UrlTemplateImageryProvider({ + url: "https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", + fileExtension: "jpg" + }) + }); + viewer.resolutionScale = 2.0; + + viewerRef.current = viewer; + viewer.scene.globe.baseColor = Cesium.Color.BLACK; + viewer.scene.globe.depthTestAgainstTerrain = false; + viewer.scene.requestRenderMode = true; // 👈 加这个(只在需要时渲染) + viewer.scene.globe.enableLighting = false; + + //viewer.camera.flyTo({ + // // 中国中心大致坐标 + 合适高度(200万米) + // destination: Cesium.Cartesian3.fromDegrees(103.84, 31.15, 2000000), + // orientation: { + // heading: Cesium.Math.toRadians(0), + // pitch: Cesium.Math.toRadians(-90), + // roll: 0 + // }, + // duration: 0 // 初始化直接到位,无需动画 + //}); + + try { fetchPublishedLayers() } catch (e) { } + + return () => { + if (lastFlightPromiseRef.current) viewer.camera.cancelFlight(); + }; + }, []); + const flyToView = (viewer, priority, config, key = '') => { if (!viewer || viewer.isDestroyed()) return false; @@ -342,131 +383,11 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, onPathChange && onPathChange([]); }; - - // ========== 替换原有 createDeviceHaloEntity 完整函数 ========== - // 改动说明:弃用 ellipse 静态椭圆,改用 billboard + createGlowBlinkColor 实现闪烁光圈 - // ============================================================ - // 设备状态扩散光圈 - // 工作中:蓝色 - // 空闲:绿色 - // 离线:红色 - // - // 特点: - // 1. 多层圆环同时向外扩散 - // 2. 每一圈都有独立生命周期 - // 3. 自动淡出 - // 4. 光圈大小随距离变化 - // 5. 不修改设备主体 Entity - // ============================================================ - // ============================================================ - // 设备状态扩散光圈 - // ============================================================ - // ============================================================ - // 设备状态扩散光圈 - // 固定屏幕像素大小,不随地图缩放 - // 光圈中心与机器图标中心完全对齐 - // ============================================================ - function createDeviceHaloEntity( - viewer, - id, - position, - haloColor, - size = 100 - ) { - const Cesium = window.Cesium; - if (!viewer || !position || !id) { - return null; - } - const haloId = `${id}_halo`; - - const existingController = viewer.entities.getById(haloId); - const existingRing0 = viewer.entities.getById(`${haloId}_0`); - const existingRing1 = viewer.entities.getById(`${haloId}_1`); - const existingRing2 = viewer.entities.getById(`${haloId}_2`); - - // 判断:四个实体【全部存在】→ 只更新位置,不重建 - const allExist = existingController && existingRing0 && existingRing1 && existingRing2; - if (allExist) { - existingController.position = position; - existingRing0.position = position; - existingRing1.position = position; - existingRing2.position = position; - return existingController; - } - - // 只要任意一个实体缺失:清空旧实体,重新创建整套光圈 - [haloId, `${haloId}_0`, `${haloId}_1`, `${haloId}_2`].forEach(eid => { - const old = viewer.entities.getById(eid); - if (old) viewer.entities.remove(old); - }); - - // ---------------------------------------------------------- - // 参数 - // ---------------------------------------------------------- - const RING_COUNT = 3; - const MIN_SIZE = 100; - const MAX_SIZE = size; - const DURATION = 1600; - const DELAY = 530; - - // ---------------------------------------------------------- - // 创建控制 Entity - // ---------------------------------------------------------- - const controller = viewer.entities.add({ - id: haloId, - position: position, - show: true - }); - - // ---------------------------------------------------------- - // 创建扩散圈 - // ---------------------------------------------------------- - for (let ringIndex = 0; ringIndex < RING_COUNT; ringIndex++) { - const startTime = Date.now() - ringIndex * DELAY; - - const getProgress = () => { - const elapsed = (Date.now() - startTime) % DURATION; - return elapsed / DURATION; - }; - - const ringSize = new Cesium.CallbackProperty(() => { - const progress = getProgress(); - const eased = 1 - Math.pow(1 - progress, 2); - return MIN_SIZE + (MAX_SIZE - MIN_SIZE) * eased; - }, false); - - const ringColor = new Cesium.CallbackProperty(() => { - const progress = getProgress(); - const alpha = 0.42 * Math.pow(1 - progress, 1.5); - return haloColor.withAlpha(alpha); - }, false); - - //viewer.entities.add({ - // id: `${haloId}_${ringIndex}`, - // position: position, - // billboard: { - // image: createGlowCircleCanvas(64), - // width: ringSize, - // height: ringSize, - // horizontalOrigin: Cesium.HorizontalOrigin.CENTER, - // verticalOrigin: Cesium.VerticalOrigin.CENTER, - // pixelOffset: new Cesium.Cartesian2(0, 0), - // color: ringColor, - // disableDepthTestDistance: Number.POSITIVE_INFINITY, - // heightReference: Cesium.HeightReference.NONE - // } - //}); - } - - return controller; - } - // ============================================================ // 实时光圈创建 / 更新(给 realtimeCarEntity 配套使用) - // 特点:与 createDeviceHaloEntity 同样的扩散动画效果,独立一套 id + // 特点:多层扩散动画光圈,独立一套 id // ============================================================ function ensureRealtimeHalo(viewer, position, color, size = 100, haloId = 'realtime_car_halo') { - const Cesium = window.Cesium; const haloBaseId = haloId; const RING_COUNT = 3; const MIN_SIZE = 100; @@ -557,7 +478,6 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, const viewer = viewerRef.current; if (!viewer) return; - const Cesium = window.Cesium; // ====================== // 是否实时模式 @@ -658,18 +578,17 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, } else { + const centerLon = (minLon + maxLon) / 2; + const centerLat = (minLat + maxLat) / 2; - const rectangle = Cesium.Rectangle.fromDegrees( - minLon, - minLat, - maxLon, - maxLat + const destination = Cesium.Cartesian3.fromDegrees( + centerLon, + centerLat, + 800 ); - viewer.scene.requestRender(); - flyToView(viewer, FLIGHT_PRIORITY.track, { - destination: rectangle, + destination, duration: 1.2 }); @@ -692,8 +611,6 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, ); - const Cesium = window.Cesium; - // 每次绘制区域前,先移除上一次绘制的地块 if (polygonRef.current) { try { @@ -781,7 +698,6 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, // 单个点 if (validPoints.length === 1) { - const p = validPoints[0]; viewer.scene.requestRender(); flyToView(viewer, FLIGHT_PRIORITY.track, { @@ -796,7 +712,6 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, } else { - const rectangle = Cesium.Rectangle.fromDegrees( minLon, minLat, @@ -835,7 +750,6 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, }, [polyPoints]) - useEffect(() => { resetFlightPriority(); if (viewerRef.current) { @@ -882,7 +796,7 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, const circleId = `marked-obstacle-circle-${p.id ?? i}`; const entity = viewer.entities.getById(id); - const position = window.Cesium.Cartesian3.fromDegrees(Number(p.lon), Number(p.lat)); + const position = Cesium.Cartesian3.fromDegrees(Number(p.lon), Number(p.lat)); const labelText = `${i + 1}`; if (entity) { @@ -890,8 +804,8 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, if (entity.label) entity.label.text = labelText; } else { const pointColor = isObstacle - ? window.Cesium.Color.fromCssColorString('#ff4d4f') - : window.Cesium.Color.GREEN; + ? Cesium.Color.fromCssColorString('#ff4d4f') + : Cesium.Color.GREEN; viewer.entities.add({ id, position, @@ -903,11 +817,11 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, label: { text: labelText, font: '14px sans-serif', - pixelOffset: new window.Cesium.Cartesian2(0, -20), - fillColor: window.Cesium.Color.WHITE, - outlineColor: window.Cesium.Color.BLACK, + pixelOffset: new Cesium.Cartesian2(0, -20), + fillColor: Cesium.Color.WHITE, + outlineColor: Cesium.Color.BLACK, outlineWidth: 2, - style: window.Cesium.LabelStyle.FILL_AND_OUTLINE, + style: Cesium.LabelStyle.FILL_AND_OUTLINE, } }); markedPointsRef.current.push(id); @@ -928,9 +842,9 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, // ellipse: { // semiMajorAxis: radius, // semiMinorAxis: radius, - // material: new window.Cesium.Color(1.0, 0.3, 0.3, 0.25), + // material: new Cesium.Color(1.0, 0.3, 0.3, 0.25), // outline: true, - // outlineColor: window.Cesium.Color.RED, + // outlineColor: Cesium.Color.RED, // outlineWidth: 2, // height: 0, // }, @@ -958,13 +872,13 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, } if (newPoints.length >= 2) { const linePositions = newPoints.map(p => - window.Cesium.Cartesian3.fromDegrees(Number(p.lon), Number(p.lat)) + Cesium.Cartesian3.fromDegrees(Number(p.lon), Number(p.lat)) ); markedLineRef.current = viewer.entities.add({ polyline: { positions: linePositions, width: 4, - material: window.Cesium.Color.SKYBLUE, + material: Cesium.Color.SKYBLUE, clampToGround: true, } }); @@ -981,28 +895,44 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, } }, [markedPoints]); - // 光圈颜色:工作中蓝(#1677ff)/ 空闲绿(#52c41a)/ 离线红(#ff4d4f) - function getDeviceHaloColor(device) { + function resolveDeviceStatus(device) { + const fsRaw = device?.feStatus; + const stRaw = device?.status; - const fs = String(device?.feStatus ?? '').toUpperCase(); - if (fs == 1) { - return window.Cesium.Color.fromCssColorString('#1677ff'); + // feStatus:0=离线,1=工作中,2=空闲 + if (fsRaw !== undefined && fsRaw !== null && fsRaw !== '') { + const fs = Number(fsRaw); + if (fs === 0) return 'offline'; + if (fs === 1) return 'working'; + if (fs === 2) return 'idle'; } - if (fs == 3) { - return window.Cesium.Color.fromCssColorString('#ff4d4f'); + + // status:1=工作中,2=空闲,3=离线 + if (stRaw !== undefined && stRaw !== null && stRaw !== '') { + const st = Number(stRaw); + if (st === 1) return 'working'; + if (st === 2) return 'idle'; + if (st === 3) return 'offline'; } - return window.Cesium.Color.fromCssColorString('#52c41a'); + + return 'idle'; } - + // 光圈颜色:工作中蓝(#1677ff)/ 空闲绿(#52c41a)/ 离线红(#ff4d4f) + function getDeviceHaloColor(device) { + const state = resolveDeviceStatus(device); + if (state === 'working') return Cesium.Color.fromCssColorString('#1677ff'); + if (state === 'offline') return Cesium.Color.fromCssColorString('#ff4d4f'); + return Cesium.Color.fromCssColorString('#52c41a'); + } const getCursorLngLat = (viewer, screenPos) => { const cartesian = viewer.camera.pickEllipsoid(screenPos, viewer.scene.globe.ellipsoid); - if (!window.Cesium.defined(cartesian)) return { lon: NaN, lat: NaN }; - const cartographic = window.Cesium.Cartographic.fromCartesian(cartesian); + if (!Cesium.defined(cartesian)) return { lon: NaN, lat: NaN }; + const cartographic = Cesium.Cartographic.fromCartesian(cartesian); return { - lon: window.Cesium.Math.toDegrees(cartographic.longitude), - lat: window.Cesium.Math.toDegrees(cartographic.latitude), + lon: Cesium.Math.toDegrees(cartographic.longitude), + lat: Cesium.Math.toDegrees(cartographic.latitude), }; }; @@ -1015,15 +945,14 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, const lonStr = Number.isFinite(lon) ? lon.toFixed(6) : "--"; const latStr = Number.isFinite(lat) ? lat.toFixed(6) : "--"; - if (robot) { + const stateKey = resolveDeviceStatus(robot); const statusMap = { - 1: { text: t('common.working'), color: "#0a40df" }, - 2: { text: t('devices.idle'), color: "#12c60e" }, - 3: { text: t('devices.offline'), color: "#f01212" } + working: { text: t('common.working'), color: '#0a40df' }, + idle: { text: t('devices.idle'), color: '#12c60e' }, + offline: { text: t('devices.offline'), color: '#f01212' } }; - console.log(robot, "9999999999999") - const status = statusMap[robot.feStatus] || statusMap[1]; + const status = statusMap[stateKey] || statusMap.idle; let deviceValuesHTML = ''; if (robot.devicevalue && robot.devicevalue.length > 0) { deviceValuesHTML = robot.devicevalue.map((device) => ` @@ -1031,7 +960,21 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, `).join(''); } tooltip.innerHTML = ` -
🤖 ${robot.deviceAlias || robot.serialNumber || robot.name}
+
🤖 ${robot.deviceAlias || robot.serialNumber || robot.name} ${robot.deviceType != null && robot.deviceType !== '' ? ` + + ${robot.deviceType} + + ` : ''}
${deviceValuesHTML ? `
${t('devices.deviceData')}:${deviceValuesHTML}
` : ''}
${t('systemSetting.longitude')}:${lonStr}
${t('systemSetting.latitude')}:${latStr}
@@ -1046,6 +989,68 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, `; } } + const handleFlyToDevice = () => { + let validPositions = []; + const viewer = viewerRef.current; + + devices.forEach((device, index) => { + + const raw = device?.locationMessage || device?.lastRunningStatus || {}; + const parsed = parseLngLat(raw.longitude, raw.latitude); + if (!parsed) return; + + const { longitude, latitude } = parsed; + + validPositions.push({ lon: longitude, lat: latitude }); + }); + if (validPositions.length > 0) { + + let minLon = 180, + maxLon = -180, + minLat = 90, + maxLat = -90; + + validPositions.forEach(p => { + + minLon = Math.min(minLon, p.lon); + maxLon = Math.max(maxLon, p.lon); + minLat = Math.min(minLat, p.lat); + maxLat = Math.max(maxLat, p.lat); + + }); + + + if (validPositions.length === 1) { + + const p = validPositions[0]; + + flyToView(viewer, FLIGHT_PRIORITY.devices, { + destination: Cesium.Cartesian3.fromDegrees( + p.lon, + p.lat, + 800 + ), + duration: 1.2 + }); + + } else { + + const rectangle = Cesium.Rectangle.fromDegrees( + minLon, + minLat, + maxLon, + maxLat + ); + + flyToView(viewer, FLIGHT_PRIORITY.devices, { + destination: rectangle, + duration: 1.2, + }); + + } + + } + } // 返航点悬浮提示:显示别名与该返航点绑定的机器 function showReturnPointTooltip(tooltip, position, point) { @@ -1209,7 +1214,7 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, } try { - const imageryProvider = new window.Cesium.WebMapServiceImageryProvider({ + const imageryProvider = new Cesium.WebMapServiceImageryProvider({ url: `${baseUrl}/gwc/service/wms`, layers: layerIdentifier, parameters: { @@ -1223,12 +1228,12 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, height: 256, styles: '' }, - tilingScheme: new window.Cesium.GeographicTilingScheme(), + tilingScheme: new Cesium.GeographicTilingScheme(), tileWidth: 256, tileHeight: 256, maximumLevel: 21, minimumLevel: 1, - credit: new window.Cesium.Credit('GeoServer GWC WMS'), + credit: new Cesium.Credit('GeoServer GWC WMS'), usePreCachedTilesIfAvailable: true, enablePickFeatures: false, maximumRequests: 15, @@ -1239,7 +1244,7 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, cesiumLayers.push(layer); } catch (gwcWmsError) { try { - const imageryProvider = new window.Cesium.WebMapServiceImageryProvider({ + const imageryProvider = new Cesium.WebMapServiceImageryProvider({ url: `${baseUrl}/${workspace}/wms`, layers: layerIdentifier, parameters: { @@ -1253,12 +1258,12 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, height: 256, styles: '' }, - tilingScheme: new window.Cesium.WebMercatorTilingScheme(), + tilingScheme: new Cesium.WebMercatorTilingScheme(), tileWidth: 256, tileHeight: 256, maximumLevel: 18, minimumLevel: 5, - credit: new window.Cesium.Credit('GeoServer WMS'), + credit: new Cesium.Credit('GeoServer WMS'), usePreCachedTilesIfAvailable: true, enablePickFeatures: false, maximumRequests: 10, @@ -1268,7 +1273,7 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, const layer = viewerRef.current.imageryLayers.addImageryProvider(imageryProvider); cesiumLayers.push(layer); } catch (wmsError) { - const imageryProvider = new window.Cesium.WebMapServiceImageryProvider({ + const imageryProvider = new Cesium.WebMapServiceImageryProvider({ url: `${baseUrl}/gwc/service/wms`, layers: layerIdentifier, parameters: { @@ -1282,12 +1287,12 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, height: 256, styles: '' }, - tilingScheme: new window.Cesium.GeographicTilingScheme(), + tilingScheme: new Cesium.GeographicTilingScheme(), tileWidth: 256, tileHeight: 256, maximumLevel: 21, minimumLevel: 1, - credit: new window.Cesium.Credit('GeoServer GWC WMS'), + credit: new Cesium.Credit('GeoServer GWC WMS'), usePreCachedTilesIfAvailable: true, enablePickFeatures: false, maximumRequests: 15, @@ -1340,10 +1345,10 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, flyToView(viewerRef.current, FLIGHT_PRIORITY.layer, { // 固定高度,第三个参数就是高度(单位米) - destination: window.Cesium.Cartesian3.fromDegrees(centerLon, centerLat, flyHeight), + destination: Cesium.Cartesian3.fromDegrees(centerLon, centerLat, flyHeight), orientation: { heading: 0, - pitch: window.Cesium.Math.toRadians(-90), // 垂直俯视 + pitch: Cesium.Math.toRadians(-90), // 垂直俯视 roll: 0 }, duration: 2 @@ -1386,14 +1391,13 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, baseColor } = {}) { const start = Date.now(); - return new window.Cesium.CallbackProperty(() => { + return new Cesium.CallbackProperty(() => { const t = (Date.now() - start) / 1000; const alpha = minAlpha + (maxAlpha - minAlpha) * (0.5 + 0.5 * Math.sin(t * speed * Math.PI * 2)); return baseColor.withAlpha(alpha); }, false); } - function createRobotEntity(viewer, robot, index) { const raw = robot?.lastRunningStatus || {}; const parsed = parseLngLat(raw.longitude, raw.latitude); @@ -1402,7 +1406,7 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, const baseId = getRobotEntityId(robot, index); viewer.entities.removeById(baseId); viewer.entities.removeById(baseId + "_alarm"); - const position = window.Cesium.Cartesian3.fromDegrees(longitude, latitude, 0); + const position = Cesium.Cartesian3.fromDegrees(longitude, latitude, 0); viewer.entities.add({ id: baseId + "_alarm", @@ -1411,8 +1415,8 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, image: createGlowCircleCanvas(45), width: 45, height: 45, - verticalOrigin: window.Cesium.VerticalOrigin.BOTTOM, - pixelOffset: new window.Cesium.Cartesian2(0, -6), + verticalOrigin: Cesium.VerticalOrigin.BOTTOM, + pixelOffset: new Cesium.Cartesian2(0, -6), color: createGlowBlinkColor({ minAlpha: 0.25, maxAlpha: 0.75, @@ -1429,70 +1433,23 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, image: carImg, width: 30, height: 30, - verticalOrigin: window.Cesium.VerticalOrigin.BOTTOM, + verticalOrigin: Cesium.VerticalOrigin.BOTTOM, disableDepthTestDistance: Number.POSITIVE_INFINITY }, label: { text: robot.name, font: "14px sans-serif", - pixelOffset: new window.Cesium.Cartesian2(0, -40), - fillColor: window.Cesium?.Color.WHITE, + pixelOffset: new Cesium.Cartesian2(0, -40), + fillColor: Cesium.Color.WHITE, showBackground: true, - backgroundColor: window.Cesium?.Color.BLACK.withAlpha(0.6) + backgroundColor: Cesium.Color.BLACK.withAlpha(0.6) } }); entity.robotData = robot; return entity; } - useEffect(() => { - if (!containerRef.current) return; - if (viewerRef.current) return; - const viewer = new window.Cesium.Viewer(containerRef.current, { - useBrowserRecommendedResolution: false, - - - animation: false, - timeline: false, - baseLayerPicker: true, - geocoder: false, - homeButton: false, - sceneModePicker: false, - navigationHelpButton: false, - fullscreenButton: false, - infoBox: false, - imageryProvider: new window.Cesium.UrlTemplateImageryProvider({ - url: "https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", - fileExtension: "jpg" - }) - }); - viewer.resolutionScale = 2.0; - - viewerRef.current = viewer; - viewer.scene.globe.baseColor = window.Cesium?.Color.BLACK; - viewer.scene.globe.depthTestAgainstTerrain = false; - viewer.scene.requestRenderMode = true; // 👈 加这个(只在需要时渲染) - viewer.scene.globe.enableLighting = false; - - //viewer.camera.flyTo({ - // // 中国中心大致坐标 + 合适高度(200万米) - // destination: window.Cesium.Cartesian3.fromDegrees(103.84, 31.15, 2000000), - // orientation: { - // heading: window.Cesium.Math.toRadians(0), - // pitch: window.Cesium.Math.toRadians(-90), - // roll: 0 - // }, - // duration: 0 // 初始化直接到位,无需动画 - //}); - - - try { fetchPublishedLayers() } catch (e) { } - - return () => { - if (lastFlightPromiseRef.current) viewer.camera.cancelFlight(); - }; - }, []); // 根据场站映射图层定位地图:切换场站时始终飞向该场站的图层 // 不再用可能滞后的 devices 判断(避免用上一场站的机器决策); @@ -1535,7 +1492,6 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, // 在地图上叠加显示:返航点用标志图标,分区用小框 useEffect(() => { const viewer = viewerRef.current; - const Cesium = window.Cesium; if (!viewer || !Cesium) return; // 清理上一次叠加实体 @@ -1755,15 +1711,12 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, stationOverlayIdsRef.current.push(handleId); }); - - viewer.scene.requestRender(); }, [stationOverlay, highlightReturnPointId]); useEffect(() => { const viewer = viewerRef.current; - const Cesium = window.Cesium; if (!viewer || !Cesium || from != "mapEditor") return; @@ -1785,7 +1738,6 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, // 鼠标按下:判断是否点中障碍物圆圈边缘 handler.setInputAction((event) => { const viewer = viewerRef.current; - const Cesium = window.Cesium; if (!viewer || !Cesium) return; @@ -1902,7 +1854,6 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, handler.setInputAction((event) => { const viewer = viewerRef.current; - const Cesium = window.Cesium; const dragging = draggingObstacleRef.current; if (!viewer || !Cesium || !dragging) return; @@ -1918,7 +1869,7 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, // 计算鼠标到圆心的距离,单位米 const nextRadius = Math.max( - 0.5, + 0.01, Cesium.Cartesian3.distance( dragging.center, mousePosition @@ -1977,11 +1928,11 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, if (viewer) { viewer.scene.screenSpaceCameraController.enableRotate = true; } - console.log('radius', radius, index); + console.log('radius', radius, index, siteMapInfoId); // 通知父组件更新列表 // 通知父组件更新列表 - onObstacleRadiusChange?.(index, radius); + onObstacleRadiusChange?.(index, radius, siteMapInfoId); }, Cesium.ScreenSpaceEventType.LEFT_UP); return () => { @@ -1995,7 +1946,6 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, // 点击已配置列表:定位到对应返航点/分区;返航点额外高亮 useEffect(() => { const viewer = viewerRef.current; - const Cesium = window.Cesium; if (!viewer || !Cesium || !focusOverlay) return; if (focusOverlay.type === 'returnPoint') { @@ -2082,9 +2032,6 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, } }, [focusOverlay, stationOverlay]); - - - useEffect(() => { // 如果有 realtimePosition,不显示 focusEntity const hasRealtime = @@ -2107,7 +2054,6 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, return; } const viewer = viewerRef.current; - const Cesium = window.Cesium; // 恢复上一次被隐藏的设备实体 hiddenByFocusRef.current.forEach(e => { @@ -2174,7 +2120,7 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, north = Math.max(north, lat); }); - const rectangle = window.Cesium.Rectangle.fromDegrees(west, south, east, north); + const rectangle = Cesium.Rectangle.fromDegrees(west, south, east, north); flyToView(viewer, FLIGHT_PRIORITY.devices, { destination: rectangle, duration: 1.5 }); } @@ -2187,18 +2133,6 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, return null; } - function createYellowCircleDataUrl(size = 30) { - const canvas = document.createElement('canvas'); - canvas.width = size; - canvas.height = size; - const ctx = canvas.getContext('2d'); - ctx.clearRect(0, 0, size, size); - ctx.beginPath(); - ctx.arc(size / 2, size / 2, size / 2 - 1, 0, Math.PI * 2); - ctx.fillStyle = 'rgba(255, 215, 0, 1)'; - ctx.fill(); - return canvas.toDataURL(); - } // 监听 drawRealTime 变化,每次开始执行任务时重置为定位模式 useEffect(() => { @@ -2262,7 +2196,6 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, const updateFollowCar = (cartesianPos, heading = 0) => { const viewer = viewerRef.current; if (!viewer || !cartesianPos) return; - const Cesium = window.Cesium; const hpr = new Cesium.HeadingPitchRoll(heading, 0, 0); const orientation = Cesium.Transforms.headingPitchRollQuaternion(cartesianPos, hpr); @@ -2286,12 +2219,9 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, } }; - - const updateTraceLine = (clear = false) => { const viewer = viewerRef.current; if (!viewer) return; - const Cesium = window.Cesium; // 主动清空 或 轨迹数据为空:统一清理 if (clear) { @@ -2392,14 +2322,12 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, if (planLineRef.current) { viewer.entities.remove(planLineRef.current); - planLineRef.current = null; } if (polygonRef.current) { viewer.entities.remove(polygonRef.current); - polygonRef.current = null; } viewer.scene.requestRender(); @@ -2448,9 +2376,8 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, useEffect(() => { const viewer = viewerRef.current; - if (!viewer || !window.Cesium) return; + if (!viewer || !Cesium) return; - const Cesium = window.Cesium; const isRealtimeMode = realtimePosition?.lon && @@ -2462,7 +2389,6 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, const validPositions = []; - devices.forEach((device, index) => { const raw = device?.locationMessage || device?.lastRunningStatus || {}; @@ -2493,9 +2419,6 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, let height = 30; const carsize = device?.productId == 138 ? 30 : 30; - - - if (isIOT) { image = device?.type === "camera" ? cameraImg : iotImg; @@ -2528,7 +2451,6 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, } - // ============================== // 航向角:优先用设备自带 heading/yaw/angle,否则按设备索引模拟角度(演示旋转) // ============================== @@ -2555,14 +2477,14 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, image, width, height, - verticalOrigin: window.Cesium.VerticalOrigin.BOTTOM, + verticalOrigin: Cesium.VerticalOrigin.BOTTOM, disableDepthTestDistance: Number.POSITIVE_INFINITY, show: !isIOT, }, //point: { // pixelSize: 12, - // color: createGlowBlinkColor("", "", "", window.Cesium.Color.BLACK), // 绑定到颜色 + // color: createGlowBlinkColor("", "", "", Cesium.Color.BLACK), // 绑定到颜色 // outlineColor: Cesium.Color.WHITE, // outlineWidth: 2 //}, @@ -2601,8 +2523,7 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, haloSize = 0.8; } - createDeviceHaloEntity(viewer, haloId, position, haloColor, 100); - + ensureRealtimeHalo(viewer, position, haloColor, 100, haloId); entityMapRef.current.set(id, entity); @@ -2666,22 +2587,62 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, } }); - // ============================== // 请求渲染 // ============================== viewer.scene.requestRender(); + // 鼠标停在设备上时,设备状态变化立即刷新 tooltip(无需移动鼠标) + if (lastHoverPositionRef.current && tooltipRef.current && tooltipRef.current.style.display === "block") { + const hoverPos = lastHoverPositionRef.current; + const pickedNow = viewer.scene.pick(hoverPos); + const pickedReturnPointNow = (Cesium.defined(pickedNow) && pickedNow.id?.returnPointData) ? pickedNow.id.returnPointData : null; + const pickedRobotNow = (Cesium.defined(pickedNow) && pickedNow.id?.properties?.robot) ? pickedNow.id.properties.robot.getValue() : null; + if (pickedReturnPointNow) { + showReturnPointTooltip(tooltipRef.current, hoverPos, pickedReturnPointNow); + } else if (pickedRobotNow) { + const hoverLngLat = getCursorLngLat(viewer, hoverPos); + showCoordinateTooltip(tooltipRef.current, hoverPos, t, hoverLngLat.lon, hoverLngLat.lat, pickedRobotNow); + } + } + + // ============================== + // 首屏:仅第一次根据 devices 自动定位视野;之后 devices 实时更新不再飞行 + //if (!hasInitialDevicesFlyRef.current && !isRealtimeMode && validPositions.length > 0) { + // let started = false; + // if (validPositions.length === 1) { + // const p = validPositions[0]; + // started = flyToView(viewer, FLIGHT_PRIORITY.devices, { + // destination: Cesium.Cartesian3.fromDegrees(p.lon, p.lat, 800), + // duration: 1.2, + // }, `devices-init:${p.lon.toFixed(5)},${p.lat.toFixed(5)}`); + // } else { + // let minLon = 180, maxLon = -180, minLat = 90, maxLat = -90; + // validPositions.forEach(p => { + // minLon = Math.min(minLon, p.lon); + // maxLon = Math.max(maxLon, p.lon); + // minLat = Math.min(minLat, p.lat); + // maxLat = Math.max(maxLat, p.lat); + // }); + // // 所有点重合时补一点边距,避免 Rectangle 面积为 0 + // const pad = 0.0008; + // if (maxLon - minLon < pad) { minLon -= pad; maxLon += pad; } + // if (maxLat - minLat < pad) { minLat -= pad; maxLat += pad; } + // const rectangle = Cesium.Rectangle.fromDegrees(minLon, minLat, maxLon, maxLat); + // started = flyToView(viewer, FLIGHT_PRIORITY.devices, { destination: rectangle, duration: 1.2 }, 'devices-init:all'); + // } + // if (started) hasInitialDevicesFlyRef.current = true; + //} + // ============================== + // 自动 flyTo(已停用:改为仅首屏按 devices 定位一次) // ============================== // 自动 flyTo - // ============================== //const hasRealtime = isValidCoordinate( // realtimePosition?.lon, // realtimePosition?.lat //); - //if (!hasRealtime && validPositions.length > 0) { // let minLon = 180, @@ -2699,7 +2660,6 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, // }); // if (getCameraMode() !== "devices") return; - // if (validPositions.length === 1) { // const p = validPositions[0]; @@ -2761,7 +2721,7 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, const step = moveSpeedRef.current; const lon = p1.lon + (p2.lon - p1.lon) * step; const lat = p1.lat + (p2.lat - p1.lat) * step; - const newPos = window.Cesium.Cartesian3.fromDegrees(lon, lat, 1); + const newPos = Cesium.Cartesian3.fromDegrees(lon, lat, 1); targetEntity.position = newPos; // 背景光圈跟随机器一起移动 @@ -2776,9 +2736,9 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, } const heading = Math.atan2(p2.lat - p1.lat, p2.lon - p1.lon); - targetEntity.orientation = window.Cesium.Transforms.headingPitchRollQuaternion( + targetEntity.orientation = Cesium.Transforms.headingPitchRollQuaternion( newPos, - new window.Cesium.HeadingPitchRoll(heading, 0, 0) + new Cesium.HeadingPitchRoll(heading, 0, 0) ); const distance = Math.hypot(lon - p2.lon, lat - p2.lat); if (distance < 0.00005) { @@ -2800,13 +2760,13 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, useEffect(() => { const viewer = viewerRef.current; if (!viewer) return; - const handler = new window.Cesium.ScreenSpaceEventHandler(viewer.scene.canvas); + const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas); eventHandlerRef.current = handler; handler.setInputAction((movement) => { const picked = viewer.scene.pick(movement.endPosition); - const pickedReturnPoint = (window.Cesium.defined(picked) && picked.id?.returnPointData) ? picked.id.returnPointData : null; - const pickedRobot = (window.Cesium.defined(picked) && picked.id?.properties?.robot) ? picked.id.properties.robot.getValue() : null; + const pickedReturnPoint = (Cesium.defined(picked) && picked.id?.returnPointData) ? picked.id.returnPointData : null; + const pickedRobot = (Cesium.defined(picked) && picked.id?.properties?.robot) ? picked.id.properties.robot.getValue() : null; // 返航点优先显示「别名 + 绑定机器」,设备显示设备信息,空白区域隐藏 if (pickedReturnPoint) { @@ -2815,10 +2775,10 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, const { lon, lat } = getCursorLngLat(viewer, movement.endPosition); showCoordinateTooltip(tooltipRef.current, movement.endPosition, t, lon, lat, pickedRobot); } else { + lastHoverPositionRef.current = null; tooltipRef.current.style.display = "none"; } - }, window.Cesium.ScreenSpaceEventType.MOUSE_MOVE); - + }, Cesium.ScreenSpaceEventType.MOUSE_MOVE); handler.setInputAction((movement) => { // ========= 增加防护,直接拦截 movement.position undefined ========= @@ -2826,14 +2786,14 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, const picked = viewer.scene.pick(movement.position); let robot = null; - //if (window.Cesium.defined(picked) && picked.id) { + //if (Cesium.defined(picked) && picked.id) { // const robotProp = picked.id.properties?.robot; // robot = robotProp ? (typeof robotProp.getValue === "function" ? robotProp.getValue() : robotProp) : (picked.id.robotData || null); // if (!robot) { // const pickedCarto = viewer.scene.globe.pick(movement.position, viewer.scene); - // if (window.Cesium.defined(pickedCarto)) { - // const clon = window.Cesium.Math.toDegrees(pickedCarto.longitude); - // const clat = window.Cesium.Math.toDegrees(pickedCarto.latitude); + // if (Cesium.defined(pickedCarto)) { + // const clon = Cesium.Math.toDegrees(pickedCarto.longitude); + // const clat = Cesium.Math.toDegrees(pickedCarto.latitude); // let best = null; // let bestDist = 0.002; // devices.forEach(d => { @@ -2856,15 +2816,11 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, return; const cartesian = viewer.camera.pickEllipsoid(movement.position, viewer.scene.globe.ellipsoid); if (!cartesian) return; - const cartographic = window.Cesium.Cartographic.fromCartesian(cartesian); - const lon = window.Cesium.Math.toDegrees(cartographic.longitude); - const lat = window.Cesium.Math.toDegrees(cartographic.latitude); - const newPoint = { lon, lat }; + const cartographic = Cesium.Cartographic.fromCartesian(cartesian); + const lon = Cesium.Math.toDegrees(cartographic.longitude); + const lat = Cesium.Math.toDegrees(cartographic.latitude); onMarkPointRef.current?.({ lon, lat }); - }, window.Cesium.ScreenSpaceEventType.LEFT_CLICK); - - - + }, Cesium.ScreenSpaceEventType.LEFT_CLICK); if (onClearDraw) { onClearDraw(clearDrawingPath); @@ -2905,9 +2861,8 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, useEffect(() => { const viewer = viewerRef.current; - if (!viewer || !window.Cesium) return; + if (!viewer || !Cesium) return; - const Cesium = window.Cesium; viewer.scene.requestRender(); @@ -3161,18 +3116,13 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, // 如果有航向角,设置旋转 let rotation = 0; - if (realtimePosition?.heading !== undefined && !isNaN(Number(realtimePosition.heading))) { let headingDeg = Number(realtimePosition.heading); rotation = Cesium.Math.toRadians(-headingDeg); - - } - - if (!realtimeCarEntityRef.current) { realtimeCarEntityRef.current = viewer.entities.add({ position: position, @@ -3294,7 +3244,6 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, } }); - alarmMapRef.current.forEach(e => { if (e.billboard) e.billboard.show = false; if (e.point) e.point.show = false; @@ -3451,7 +3400,6 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, if (!viewer || !position) return; const { lon, lat } = position; if (lon == null || lat == null || isNaN(Number(lon)) || isNaN(Number(lat))) return; - const Cesium = window.Cesium; const height = options.height || 300; const duration = options.duration ?? 1.5; try { viewer.camera.cancelFlight(); } catch (e) { /* 忽略 */ } @@ -3545,7 +3493,6 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, {t('devices.mapEditor.restArea')} -
{t('devices.mapEditor.obstacle')}
+ )} + ); diff --git a/src/components/devices/DeviceOverviewPage.tsx b/src/components/devices/DeviceOverviewPage.tsx index 027d023..06b8597 100644 --- a/src/components/devices/DeviceOverviewPage.tsx +++ b/src/components/devices/DeviceOverviewPage.tsx @@ -531,7 +531,7 @@ export default function DeviceOverviewPage() { ...d, id: d.deviceId || index, name: d.deviceAlias || d.deviceName || d.callsign || '未知设备', - type: d.productName || t('constants.uav'), + type: d.deviceType || "割草机", status: d.onlineStatus === 1 ? t('devices.online') : t('devices.offline'), mode: d.feStatus == 1 ? '作业中' : d.feStatus == 2 ? t('devices.idle') : d.mode_code == 0 ? t('devices.idle') : d.mode_code == 1 ? '现场调试' : d.mode_code == 2 ? '远程调试' : d.mode_code == 3 ? '固件升级中' : '作业中', capacity_percent: d.capacity_percent || 100, @@ -823,6 +823,12 @@ export default function DeviceOverviewPage() { v{selectedDevice.firmwareVersion} )} + {selectedDevice.deviceType && ( +
+ {t('devices.deviceType')} + {selectedDevice.deviceType} +
+ )} {(selectedDevice.productName?.includes(t('constants.uav')) || selectedDevice.device_sn) && ( <>
diff --git a/src/components/devices/DeviceStatusPage.tsx b/src/components/devices/DeviceStatusPage.tsx index cdb0353..f652520 100644 --- a/src/components/devices/DeviceStatusPage.tsx +++ b/src/components/devices/DeviceStatusPage.tsx @@ -2174,138 +2174,6 @@ export default function DeviceStatusPage() { - {/* 页面顶部按钮区域 */} - - {/*{!isUAV && ( - -
- -
- - {t('devices.pathPlanning')}: - - {t('devices.markedPointsCount', { count: markedPoints?.length })} - -
- - - - - - {t('devices.workMode')} - - - - {isLoopMode ? t('devices.bowMode') : t('devices.abMode')} - - - - - - - - - - {/**/} - - {/* - - - -
- - )}}*/} -
- {isUAV ? t('constants.uav') : t('devices.robot')} + {isUAV ? t('constants.uav') : device?.deviceType ? device?.deviceType : t('devices.robot')} diff --git a/src/components/devices/MapEditorPage.tsx b/src/components/devices/MapEditorPage.tsx index f98d7d9..f956c51 100644 --- a/src/components/devices/MapEditorPage.tsx +++ b/src/components/devices/MapEditorPage.tsx @@ -144,10 +144,14 @@ export default function MapEditorPage() { }; loadDevices(); }, [stationId]); + useEffect(() => { + console.log("siteMapInfoId state:", siteMapInfoId); + }, [siteMapInfoId]); const getSiteMapInfoFn = () => { getSiteMapInfo({ siteId: stationId }).then((res: any) => { if (res?.code === 200 && res.data) { setSiteMapInfoId(res.data.id ?? null); + console.log(res.data.id, "res.data.id") setReturnPointConfigList(res?.data?.restPoints || []); // 同时加载已保存的障碍物点(tempObstaclePoints),方便编辑 const savedObstacles = (res?.data?.tempObstaclePoints || []).map((op: any) => ({ @@ -161,12 +165,16 @@ export default function MapEditorPage() { } else { setSiteMapInfoId(null); + console.log("res.data.id111") + setReturnPointConfigList([]); setObstaclePointConfigList([]); } }).catch(() => { setSiteMapInfoId(null); + console.log("res.data.id112221") + setReturnPointConfigList([]); setObstaclePointConfigList([]); @@ -737,7 +745,8 @@ export default function MapEditorPage() { const handleObstacleRadiusChange = async ( index: number, - radius: number + radius: number, + siteMapInfoId: number | null ) => { console.log("nextList") @@ -749,6 +758,7 @@ export default function MapEditorPage() { ? { ...item, radius: roundedRadius } : item ); + console.log(nextList, "333", siteMapInfoId, "siteMapInfoIdnextList") const payload = { id: siteMapInfoId, tempObstaclePoints: nextList?.map((item) => ({ @@ -1097,6 +1107,8 @@ export default function MapEditorPage() { onObstacleRadiusChange={handleObstacleRadiusChange} + siteMapInfoId={siteMapInfoId} + /> diff --git a/src/components/devices/RobotTaskPage.tsx b/src/components/devices/RobotTaskPage.tsx index 9f284c1..8802c3f 100644 --- a/src/components/devices/RobotTaskPage.tsx +++ b/src/components/devices/RobotTaskPage.tsx @@ -1400,23 +1400,21 @@ export default function RobotTaskPage() { rules = { type: 'MONTH', dayOfMonth: scheduleForm.dayOfMonth, dayTime: scheduleForm.dayTime }; } let success = true; - for (const sn of dispatchRobotSns) { - for (const route of routes) { - const finalData = { - deviceId: sn, - orgId: currentStation?.orgId, - planName: scheduleForm.planName, - routeId: route.id, - siteId: stationId, - deviceTaskPlanRules: rules, - startTime: scheduleForm.excuteDayStartTime, - finishTime: scheduleForm.excuteDayEndTime, - }; - const res = await saveDeviceTask(finalData); - if (res.code !== 200) { - success = false; - utils.message.error(res.msg || t('devices.createFailed')); - } + for (const route of routes) { + const finalData = { + deviceId: dispatchRobotSns, + orgId: currentStation?.orgId, + planName: scheduleForm.planName, + routeId: route.id, + siteId: stationId, + deviceTaskPlanRules: rules, + startTime: scheduleForm.excuteDayStartTime, + finishTime: scheduleForm.excuteDayEndTime, + }; + const res = await saveDeviceTask(finalData); + if (res.code !== 200) { + success = false; + utils.message.error(res.msg || t('devices.createFailed')); } } if (success) { diff --git a/src/components/devices/deviceController.js b/src/components/devices/deviceController.js index de4b754..16d4844 100644 --- a/src/components/devices/deviceController.js +++ b/src/components/devices/deviceController.js @@ -162,6 +162,14 @@ export default class DeviceController { if (msg?.type == 'device_status_changed') { this.deviceStatusChangedMsg?.(msg); } + + if (msg?.type == 'route_post') { + console.log('route_post'); + this.onRoutePost?.(msg); + } + + + }; ws.onerror = () => { @@ -508,7 +516,7 @@ export default class DeviceController { break; case 'route_post': - console.log('route post:', msg); + console.log('route_post:', msg); if (msg.deviceId) { this.onRoutePost?.(msg); } diff --git a/src/locales/zh/index.ts b/src/locales/zh/index.ts index ba5d5c6..e639f8b 100644 --- a/src/locales/zh/index.ts +++ b/src/locales/zh/index.ts @@ -2701,7 +2701,7 @@ const zh = { addRule: '新增规则', editRule: '编辑规则', viewRule: '查看规则', - operator: '比较运算符', + //operator: '比较运算符', invalidJson: '动作参数 JSON 格式不正确', confirmDeleteRule: '确定删除规则「{{name}}」吗?', flvPlaybackError: 'FLV 播放出错', diff --git a/vite.config.ts b/vite.config.ts index 3052d1d..0c64226 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,3 +1,5 @@ + // CESIUM_BASE_URL 由 vite-plugin-cesium 自动注入(值为 base + '/cesium/',小写带尾斜杠) + // 手动覆写会与 viteStaticCopy 的小写 cesium/ 目标路径冲突,因此注释掉 import tailwindcss from '@tailwindcss/vite'; import react from '@vitejs/plugin-react'; import path, { resolve } from 'path'; @@ -27,7 +29,22 @@ export default defineConfig(({ mode }) => { { src: "node_modules/cesium/Build/Cesium", dest: "" - } + }, { + src: 'node_modules/cesium/Build/Cesium/Workers/**', + dest: 'cesium/Workers', + }, + { + src: 'node_modules/cesium/Build/Cesium/ThirdParty/**', + dest: 'cesium/ThirdParty', + }, + { + src: 'node_modules/cesium/Build/Cesium/Assets/**', + dest: 'cesium/Assets', + }, + { + src: 'node_modules/cesium/Build/Cesium/Widgets/**', + dest: 'cesium/Widgets', + }, ] }) @@ -50,71 +67,13 @@ export default defineConfig(({ mode }) => { rollupOptions: { output: { - // ---- 手动分包策略 ---- - // 将第三方库按类型拆分,利于浏览器并行加载与长期缓存 - //manualChunks(id) { - // if (!id.includes('node_modules')) return; - - // // 1. React 生态 - // if ( - // id.includes('node_modules/react') || - // id.includes('node_modules/react-dom') || - // id.includes('node_modules/react-router') || - // id.includes('node_modules/react-redux') || - // id.includes('node_modules/@reduxjs') || - // id.includes('node_modules/scheduler') - // ) { - // return 'react-vendor'; - // } - - // // 2. Ant Design 组件库 - // if ( - // id.includes('node_modules/antd') || - // id.includes('node_modules/@ant-design') || - // id.includes('node_modules/@rc-component') || - // id.includes('node_modules/rc-') - // ) { - // return 'antd-vendor'; - // } - - // // 3. 图表库(echarts / recharts 体积较大) - // if ( - // id.includes('node_modules/echarts') || - // id.includes('node_modules/recharts') || - // id.includes('node_modules/zrender') - // ) { - // return 'charts-vendor'; - // } - - // // 4. 实时音视频 SDK - // if ( - // id.includes('node_modules/agora-rtc-sdk-ng') || - // id.includes('node_modules/@volcengine/rtc') - // ) { - // return 'rtc-vendor'; - // } - - // // 5. 仅在特定页面使用的重库,单独拆分按需加载(不进首屏 vendor) - // if (id.includes('node_modules/mqtt')) return 'mqtt-vendor'; - // if (id.includes('node_modules/socket.io-client')) return 'socket-vendor'; - // if (id.includes('node_modules/docx')) return 'docx-vendor'; - // if (id.includes('node_modules/qrcode')) return 'qrcode-vendor'; - // if (id.includes('node_modules/country-state-city')) return 'csc-vendor'; - // if (id.includes('node_modules/flv.js')) return 'flv-vendor'; - - // // 6. 其他 node_modules(首屏通用依赖:axios、i18next、lodash 等) - // return 'vendor'; - //}, + }, }, }, define: { // Cesium 路径注入 - CESIUM_BASE_URL: JSON.stringify( - '/Cesium' - ), - // 环境变量注入 'import.meta.env.VITE_API_BASE_URL': JSON.stringify(env.VITE_API_BASE_URL ?? env.baseURL ?? ''), 'process.env': {} },