From ef175e7beda67ca4094129ff4e75390b7f3f3507 Mon Sep 17 00:00:00 2001 From: mmc <1556375442@qq.com> Date: Thu, 28 May 2026 16:28:10 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=A9=E6=B0=94=E7=AA=97=E5=8F=A3=E4=B8=8E?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=E7=BA=A6=E6=9D=9F=20=E6=B8=A9=E5=BA=A6?= =?UTF-8?q?=E5=92=8C=E9=80=9F=E5=BA=A6=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Map.jsx | 52 +++++++++++++++++-- src/components/devices/WayLinePage.tsx | 70 ++++++++++++++++++++------ src/lib/utils.ts | 9 ++++ 3 files changed, 112 insertions(+), 19 deletions(-) diff --git a/src/components/Map.jsx b/src/components/Map.jsx index f384bb7..2d509a4 100644 --- a/src/components/Map.jsx +++ b/src/components/Map.jsx @@ -706,15 +706,59 @@ export default function CesiumMap({ realtimePosition, pathPoints = [], devices = }; }, []); + const focusEntityRef = useRef(null); + useEffect(() => { - if (!viewerRef.current || !focusLocation) return; + if (!viewerRef.current || !focusLocation) { + if (focusEntityRef.current) { + viewerRef.current?.entities.remove(focusEntityRef.current); + focusEntityRef.current = null; + } + return; + } const viewer = viewerRef.current; + const Cesium = window.Cesium; + + // 选择图片 + let image = carImg; + if (focusLocation.type === "plane" || focusLocation.type === "drone") { + image = planeImg; + } else if (focusLocation.type === "robot") { + image = droneInspectImg; + } else if (focusLocation.type === "mower") { + image = carImg; + } + + // 如果已经有 focusEntity,先移除 + if (focusEntityRef.current) { + viewer.entities.remove(focusEntityRef.current); + } + + // 添加新的 focusEntity + focusEntityRef.current = viewer.entities.add({ + position: Cesium.Cartesian3.fromDegrees(Number(focusLocation.lon), Number(focusLocation.lat), 0), + billboard: { + image: image, + width: 60, + height: 60, + verticalOrigin: Cesium.VerticalOrigin.BOTTOM, + disableDepthTestDistance: Number.POSITIVE_INFINITY, + }, + label: { + text: focusLocation.name, + font: "12px sans-serif", + pixelOffset: new Cesium.Cartesian2(0, -80), + fillColor: Cesium.Color.YELLOW, + showBackground: true, + backgroundColor: Cesium.Color.BLACK.withAlpha(0.6) + } + }); viewer.camera.flyTo({ - destination: window.Cesium.Cartesian3.fromDegrees(Number(focusLocation.lon), Number(focusLocation.lat), focusLocation.height), + destination: Cesium.Cartesian3.fromDegrees(Number(focusLocation.lon), Number(focusLocation.lat), focusLocation.height || 800), orientation: { - heading: window.Cesium.Math.toRadians(0.0), - pitch: window.Cesium.Math.toRadians(-90.0), + heading: Cesium.Math.toRadians(0.0), + pitch: Cesium.Math.toRadians(-90.0), roll: 0.0, }, duration: 1.5 diff --git a/src/components/devices/WayLinePage.tsx b/src/components/devices/WayLinePage.tsx index df8ab90..5c10a26 100644 --- a/src/components/devices/WayLinePage.tsx +++ b/src/components/devices/WayLinePage.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import { Row, Col, Card, Button, Space, Table, Tag, Typography, Progress, Badge, Tabs, Input, Switch, Divider, Tooltip, Timeline, Select, message, DatePicker, @@ -24,6 +24,7 @@ import dayjs from 'dayjs'; import utils from '@/lib/utils'; import CesiumMap from '../Map'; import { getUAVState } from '@/api/device'; +import { fix2, getVal } from '@/lib/utils'; const { Text, Title } = Typography; const { RangePicker } = DatePicker; @@ -38,9 +39,40 @@ export default function WayLinePage({ planedevices, onRefresh }) { // 任务列表 独立筛选状态 const [listDrone, setListDrone] = useState(null); + const [listDroneRealtime, setListDroneRealtime] = useState(null); const [listDateRange, setListDateRange] = useState(null); const [taskList, setTaskList] = useState([]); + const listDroneTimerRef = useRef(null); + + // 定时获取任务列表所选无人机的实时状态 + useEffect(() => { + const fetchRealtime = async () => { + if (listDrone?.gateway_sn && listDrone?.device_sn) { + try { + const res = await getUAVState({ + sn: listDrone.gateway_sn, + droneSn: listDrone.device_sn + }); + if (res.code === 200) { + setListDroneRealtime(res.data); + } + } catch (err) { + console.error('获取列表无人机实时状态失败', err); + } + } else { + setListDroneRealtime(null); + } + }; + + fetchRealtime(); + listDroneTimerRef.current = setInterval(fetchRealtime, 3000); + + return () => { + if (listDroneTimerRef.current) clearInterval(listDroneTimerRef.current); + }; + }, [listDrone]); + // 工作看板 独立筛选状态 const [boardDrone, setBoardDrone] = useState(null); const [boardDateRange, setBoardDateRange] = useState(null); @@ -62,7 +94,9 @@ export default function WayLinePage({ planedevices, onRefresh }) { if (res.code === 200 && res.data) { setFocusLocation({ lon: res.data.longitude, - lat: res.data.latitude + lat: res.data.latitude, + type: drone.type || 'plane', + name: drone.drone_callsign || drone.name || drone.device_sn }); } } catch (err) { @@ -465,11 +499,11 @@ export default function WayLinePage({ planedevices, onRefresh }) { utils.message.success('航线执行任务创建成功'); setRthModalVisible(false); getListTask(); - if (onRefresh) { - setTimeout(() => { - onRefresh(); - }, 1500) - } + //if (onRefresh) { + // setTimeout(() => { + // onRefresh(); + // }, 1500) + //} //await getTrackFn({ // uuid: res.data.task_uuid @@ -502,7 +536,7 @@ export default function WayLinePage({ planedevices, onRefresh }) { const topCards = [ { - title: '今日航线计划', value: todayCount, unit: '条', trend: trend, trendColor: trendColor, + title: '今日航线计划', value: todayCount, unit: '条', desc: '进行中', trend: trend, trendColor: trendColor, icon: , color: '#165DFF' }, { title: '执行中任务', value: runList.length, unit: '个', desc: '进行中', icon: , color: '#165DFF' }, @@ -906,7 +940,7 @@ export default function WayLinePage({ planedevices, onRefresh }) {
风速
-
{listDrone?.wind_speed ?? '-'} m/s
+
{fix2((listDroneRealtime || listDrone)?.wind_speed)} m/s
适宜
@@ -917,7 +951,7 @@ export default function WayLinePage({ planedevices, onRefresh }) {
温度
-
{listDrone?.environment_temperature ?? '-'}℃
+
{fix2((listDroneRealtime || listDrone)?.environment_temperature)}℃
适宜
@@ -927,9 +961,11 @@ export default function WayLinePage({ planedevices, onRefresh }) {
-
降水概率
-
10%
-
低
+
降水状态
+
{getVal((listDroneRealtime || listDrone)?.rainfall)}
+
+ {(listDroneRealtime || listDrone)?.rainfall === 'no_rain' ? '低风险' : '有雨'} +
@@ -939,8 +975,12 @@ export default function WayLinePage({ planedevices, onRefresh }) {
GNSS质量
-
良好
-
适宜
+
+ {(listDroneRealtime || listDrone)?.position_state?.rtk_number ?? 0} RTK +
+
+ {(listDroneRealtime || listDrone)?.position_state?.is_fixed === 'fixing_successful' ? '已固定' : '未固定'} +
diff --git a/src/lib/utils.ts b/src/lib/utils.ts index b034f04..54976d9 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -159,6 +159,15 @@ export function getWeatherType(weatherText = "") { return "cloudy"; } +export const fix2 = (val) => { + if (val === null || val === undefined || isNaN(val)) return '-'; + return Number(val).toFixed(2); +}; + +export const getVal = (val) => val == "no_rain" ? "无" : (val || '-'); + + + // ============================================== // 通用导出表格为 CSV / Excel(通用版,不写死任何字段) // ==============================================