From 3fd82e2fd907ca773253fe3270a318350bcd2e45 Mon Sep 17 00:00:00 2001 From: mmc <1556375442@qq.com> Date: Mon, 15 Jun 2026 11:30:32 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B7=AF=E5=BE=84=E8=A7=84=E5=88=92=E7=95=8C?= =?UTF-8?q?=E9=9D=A2=E5=A2=9E=E5=8A=A0=E8=A7=86=E9=A2=91=20=20=20=E5=B0=81?= =?UTF-8?q?=E8=A3=85=E6=8B=96=E6=8B=BD=E7=BB=84=E4=BB=B6=20=E5=92=8C=20?= =?UTF-8?q?=E5=85=A8=E6=99=AF=E8=A7=86=E9=A2=91=E7=BB=84=E4=BB=B6=20=20?= =?UTF-8?q?=E8=AE=BE=E5=A4=87=E7=8A=B6=E6=80=81=E7=95=8C=E9=9D=A2=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E5=AE=8C=E6=88=90=E7=82=B9=20=20=E6=9C=BA=E5=99=A8?= =?UTF-8?q?=E4=BA=BA=E4=BB=BB=E5=8A=A1=E7=95=8C=E9=9D=A2=E7=9A=84topcard?= =?UTF-8?q?=E5=80=BC=E7=9A=84=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 8 +- package.json | 2 +- src/components/Draggable.tsx | 60 +++++++++ src/components/devices/DeviceOverviewPage.tsx | 6 +- src/components/devices/DeviceStatusPage.tsx | 116 +++--------------- src/components/devices/RobotTaskPage.tsx | 53 +++++++- src/components/useWebRTCStream.tsx | 3 +- 7 files changed, 136 insertions(+), 112 deletions(-) create mode 100644 src/components/Draggable.tsx diff --git a/package-lock.json b/package-lock.json index aeae6ff..13e9603 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,7 +33,7 @@ "qrcode": "^1.5.4", "react": "^19.0.0", "react-dom": "^19.0.0", - "react-draggable": "^4.5.0", + "react-draggable": "^4.6.0", "react-redux": "^9.2.0", "react-router-dom": "^7.14.1", "recharts": "^3.8.1", @@ -6545,9 +6545,9 @@ } }, "node_modules/react-draggable": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.5.0.tgz", - "integrity": "sha512-VC+HBLEZ0XJxnOxVAZsdRi8rD04Iz3SiiKOoYzamjylUcju/hP9np/aZdLHf/7WOD268WMoNJMvYfB5yAK45cw==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.6.0.tgz", + "integrity": "sha512-g4vqY53xhmPrBnZvGP+1YQV0eYnB3o0VLzoi6q2IpwnQrxIZ34tYRKpVtsWIXPg4D/pvLn+oYCW5gOK2cWIrgA==", "license": "MIT", "dependencies": { "clsx": "^2.1.1", diff --git a/package.json b/package.json index 9ae5e90..50e52ba 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "qrcode": "^1.5.4", "react": "^19.0.0", "react-dom": "^19.0.0", - "react-draggable": "^4.5.0", + "react-draggable": "^4.6.0", "react-redux": "^9.2.0", "react-router-dom": "^7.14.1", "recharts": "^3.8.1", diff --git a/src/components/Draggable.tsx b/src/components/Draggable.tsx new file mode 100644 index 0000000..9e0154d --- /dev/null +++ b/src/components/Draggable.tsx @@ -0,0 +1,60 @@ +import { PlayCircleOutlined, CloseOutlined } from '@ant-design/icons'; +import React, { useRef } from 'react'; +import Draggable from 'react-draggable'; +import clsx from 'clsx'; + +interface Props { + children: React.ReactNode; + defaultPosition?: { x: number; y: number }; + // 新增 + title: string; + onClose: () => void; +} + +const DraggableBox: React.FC = ({ + children, + defaultPosition = { x: 0, y: 0 }, + title, + onClose, +}) => { + const nodeRef = useRef(null); + + return ( + +
+ {/* 拖拽标题栏 + 关闭按钮 */} +
+
+ + {title} +
+ {/* 关闭按钮,阻止拖拽冒泡 */} +
{ + e.stopPropagation(); + onClose(); + }} + className="w-6 h-6 flex items-center justify-center rounded hover:bg-red-100 cursor-pointer transition-colors" + > + +
+
+ + {/* 传入的视频内容 */} +
{children}
+
+
+ ); +}; + +export default DraggableBox; diff --git a/src/components/devices/DeviceOverviewPage.tsx b/src/components/devices/DeviceOverviewPage.tsx index 8f0a0ec..8d2799b 100644 --- a/src/components/devices/DeviceOverviewPage.tsx +++ b/src/components/devices/DeviceOverviewPage.tsx @@ -641,6 +641,7 @@ export default function DeviceOverviewPage({ onViewStatus, selectedSn, onDeviceS title: '操作', key: 'action', width: 80, + fixed: 'right', render: (_, record) => ( {/* 编辑按钮绑定弹窗 */} @@ -1007,10 +1008,7 @@ export default function DeviceOverviewPage({ onViewStatus, selectedSn, onDeviceS )} - {/*原有视频放大弹窗*/} - {/* setZoomedVideo(null)} footer={null} width={1000} centered bodyStyle={{ padding: 0, height: 600, background: '#000' }} destroyOnClose> - {zoomedVideo && } - */} + diff --git a/src/components/devices/DeviceStatusPage.tsx b/src/components/devices/DeviceStatusPage.tsx index 268590b..3394154 100644 --- a/src/components/devices/DeviceStatusPage.tsx +++ b/src/components/devices/DeviceStatusPage.tsx @@ -53,6 +53,7 @@ import VideoPlayer from '../VideoPlayer.tsx'; import DeviceController from './DeviceController'; import { DatePicker } from 'antd'; import dayjs from 'dayjs'; +import { MowerAllView } from './MowerAllView'; import { useMqtt } from '@/hooks/useMqtt'; import envConfig from '../../../env'; const { RangePicker } = DatePicker; @@ -70,6 +71,8 @@ export default function DeviceStatusPage({ const [devices, setDevices] = useState([]); const [planedevices, setPlaneDevices] = useState([]); const [devicesAll, setDevicesAll] = useState([]); + const [arrivedPoint, setArrivedPoint] = useState(null);//完成点 + // 获取数据 const fetchAllData = async () => { @@ -112,6 +115,11 @@ export default function DeviceStatusPage({ } }; + const handleFinishEdPoint = (point) => { + setArrivedPoint(point); + } + + useEffect(() => { fetchAllData(); }, [userInfo, stationId]); @@ -367,6 +375,8 @@ export default function DeviceStatusPage({ controller.onHasControlRightChange = setHasControlRight; controller.onRequestControlStatusChange = setConnecting; controller.onWsStatusChange = setWsStatus; + controller.handleArrivedPoint = handleFinishEdPoint; + controller.onPermissionRequest = (content) => { setModalContent(content); @@ -628,7 +638,7 @@ export default function DeviceStatusPage({ > {!isUAV && ( - + @@ -658,101 +668,12 @@ export default function DeviceStatusPage({ ) : robotVideo ? ( // 全景模式 = T 型排布 mowerView === 'all' ? ( -
- {/* 第一行:左视 + 前视 + 右视 三等分 */} -
- {/* 左视 */} -
- -
左视
-
- - {/* 前视 */} -
- -
前视
-
- - {/* 右视 */} -
- -
右视
-
-
- - {/* 第二行:后视 单独一行居中 */} -
-
- -
后视
-
-
-
+ ) : ( // 单画面模式 @@ -987,6 +909,6 @@ export default function DeviceStatusPage({ >

{modalContent.content}

- + ); } diff --git a/src/components/devices/RobotTaskPage.tsx b/src/components/devices/RobotTaskPage.tsx index 15e0877..801bd69 100644 --- a/src/components/devices/RobotTaskPage.tsx +++ b/src/components/devices/RobotTaskPage.tsx @@ -33,6 +33,7 @@ import { getSiteDeviceTaskHistory, } from '../../api/device'; import dayjs from 'dayjs'; +import DraggableBox from '../Draggable'; import utils, { getLocationQuality } from '../../lib/utils'; import CesiumMap from '../Map'; import { fix2, getVal } from '../../lib/utils'; @@ -40,6 +41,9 @@ import { useSelector } from 'react-redux'; import { RootState } from '@/src/store'; import envConfig from '../../../env'; import DeviceController from './DeviceController'; +import Draggable, { DraggableData, DraggableEvent } from 'react-draggable'; +import { MowerAllView } from './mowerAllView.jsx'; +import { useWebRTCPlayer } from '../useWebRTCStream.js'; const { Text, Title } = Typography; const { RangePicker } = DatePicker; @@ -54,6 +58,7 @@ export default function RobotTaskPage() { const [taskList, setTaskList] = useState([]); const [arrivedPoint, setArrivedPoint] = useState(null);//完成点 + // 设备数据 const [devices, setDevices] = useState([]); @@ -74,6 +79,7 @@ export default function RobotTaskPage() { // 当前执行任务的机器人 const [executingRobot, setExecutingRobot] = useState(null); + const [showVideo, setShowVideo] = useState(false); // 控制视频组件显示 // 任务池和历史任务数据 const [taskPoolList, setTaskPoolList] = useState([]); @@ -88,7 +94,10 @@ export default function RobotTaskPage() { robot: null, planId: null, }); - + const { video: robotVideo, error, ready } = useWebRTCPlayer( + executingRobot?.serialNumber ? executingRobot?.serialNumber : '', + `webrtc://1.95.137.212/live/livestream/${executingRobot?.serialNumber}`, + ); const [historyFilterForm, setHistoryFilterForm] = useState({ taskStatus: null, robot: null, @@ -96,6 +105,9 @@ export default function RobotTaskPage() { }); + + + const fontSmall = 'clamp(10px, 0.7vw, 11px)'; const fontNormal = 'clamp(12px, 0.75vw, 13px)'; const fontTitle = 'clamp(14px, 0.85vw, 16px)'; @@ -114,6 +126,7 @@ export default function RobotTaskPage() { const controllerRef = useRef(null); const handleFinish = (data) => { setExecute(false); + setShowVideo(false); // 任务完成关闭视频 utils.message.success('任务已完成'); //if (controllerRef.current) { // controllerRef.current.destroy?.(); @@ -128,7 +141,6 @@ export default function RobotTaskPage() { } const handleFinishEdPoint = (point) => { - console.log('到达完成点11111111111', point); setArrivedPoint(point); } @@ -139,6 +151,7 @@ export default function RobotTaskPage() { controller.onDeviceInfoUpdate = setRobotRealtimePosition; controller.onIsFinished = handleFinish; controller.handleArrivedPoint = handleFinishEdPoint; + controller.init(); @@ -377,6 +390,8 @@ export default function RobotTaskPage() { fetchTaskPoolList(); fetchTaskHistoryList(); controllerRef.current?.connectDevice(selectedRobotForExecute); + setShowVideo(true); // 连接成功后自动打开视频 + } else { setExecute(false); @@ -804,13 +819,13 @@ export default function RobotTaskPage() { } }; - const groupBoardData = () => { + const groupBoardData = useMemo(() => { const waitList = taskList.filter(item => item.taskStaus === 'NEW'); const runList = taskList.filter(item => ['EXECUTING', 'PAUSE'].includes(item.taskStaus)); const finishList = taskList.filter(item => ['FINISH', 'FAILED'].includes(item.taskStaus)); return { waitList, runList, finishList }; - }; - const { waitList, runList, finishList } = groupBoardData(); + }, [taskList]); + const { waitList, runList, finishList } = groupBoardData; const topCards = [ { title: '今日机器人计划', value: taskList.length, unit: '条', desc: '进行中', icon: , color: '#165DFF' }, @@ -1005,6 +1020,17 @@ export default function RobotTaskPage() { return {info.text}; })()} + + {/* 视频开关 */} + + 视频: + + )} @@ -1532,6 +1558,23 @@ export default function RobotTaskPage() { + + {showVideo && executingRobot && ( + setShowVideo(false)} + > +
+ +
+
+ )} ); } diff --git a/src/components/useWebRTCStream.tsx b/src/components/useWebRTCStream.tsx index 6af0488..ada533a 100644 --- a/src/components/useWebRTCStream.tsx +++ b/src/components/useWebRTCStream.tsx @@ -47,7 +47,8 @@ export function useWebRTCPlayer(deviceId: string, url: string) { v.style.height = '100%'; v.style.objectFit = 'contain'; videoRef.current = v; - const token = "eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6Ijg4N2JiMWU4LTdmNjMtNDlhNy05NjVmLTRjMWFlOGMyZTIzYiJ9.PVeQayWLEPtHHWBQFtHb1Qkc5T-KVINPdXxChJ5vRasBpXOvyiGRfPPjWvB63aKdTZWLE2xV6r_8BCV0aphYmg" + const token = "eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjFkZTA1ZDU2LTM2NTYtNDdmNS05NjI4LWM1NGZhNjRhOWU4OCJ9.Wpa9LO3fQ9DKV8Ct-VildxD4amrtCzTJWjPCGLgHu60P44PxRsArBSlAKhO9vrErmVRaHLe6_5Z_v5kegg07Aw" + //const token = localStorage.getItem('videotoken') || JSON.parse(localStorage.getItem('userInfo') || '{}')?.token; const videoUrl = `${url}?token=${token}`;