全景视频 根据 王勇的 修改
This commit is contained in:
@@ -1007,12 +1007,68 @@ export default function DeviceStatusPage({
|
||||
style={{ borderRadius: 12, marginBottom: 8 }}
|
||||
bodyStyle={{ padding: 12, height: 'calc(100vh - 220px)', minHeight: 500 }}
|
||||
>
|
||||
<Space style={{ marginBottom: 8 }}>
|
||||
<Space style={{ marginBottom: 8, }} align="start" >
|
||||
<Button type={mowerView === 'front' ? 'primary' : 'default'} onClick={() => setMowerView('front')}>前视</Button>
|
||||
<Button type={mowerView === 'back' ? 'primary' : 'default'} onClick={() => setMowerView('back')}>后视</Button>
|
||||
<Button type={mowerView === 'left' ? 'primary' : 'default'} onClick={() => setMowerView('left')}>左视</Button>
|
||||
<Button type={mowerView === 'right' ? 'primary' : 'default'} onClick={() => setMowerView('right')}>右视</Button>
|
||||
<Button type={mowerView === 'all' ? 'primary' : 'default'} onClick={() => setMowerView('all')}>全景</Button>
|
||||
{mowerView == "all" ? <> <div style={{
|
||||
|
||||
background: 'rgba(0,0,0,0.7)',
|
||||
color: '#fff',
|
||||
padding: '10px 12px',
|
||||
borderRadius: 8,
|
||||
fontSize: 11,
|
||||
zIndex: 10,
|
||||
backdropFilter: 'blur(4px)'
|
||||
}}>
|
||||
<div style={{ marginBottom: 6, fontWeight: 600, color: '#1890ff' }}>机器人状态</div>
|
||||
<div style={{ display: 'flex', gap: '3px 10px', flexWrap: 'wrap' }}>
|
||||
<span>电量:</span>
|
||||
<span style={{ color: (runningStatus.battery || 0) < 20 ? '#ff4d4f' : '#52c41a' }}>
|
||||
{runningStatus.battery !== undefined ? `${runningStatus.battery}%` : '-'}
|
||||
</span>
|
||||
<span>电压:</span>
|
||||
<span>{runningStatus.voltage !== undefined ? `${Number(runningStatus.voltage).toFixed(2)}V` : '-'}</span>
|
||||
<span>左电机:</span>
|
||||
<span>{runningStatus.leftCurrent !== undefined ? `${Number(runningStatus.leftCurrent).toFixed(2)}A` : '-'}</span>
|
||||
<span>右电机:</span>
|
||||
<span>{runningStatus.rightCurrent !== undefined ? `${Number(runningStatus.rightCurrent).toFixed(2)}A` : '-'}</span>
|
||||
<span>左速度:</span>
|
||||
<span>{runningStatus.leftMeasureSpeed !== undefined ? `${Number(runningStatus.leftMeasureSpeed).toFixed(1)}rpm` : '-'}</span>
|
||||
<span>右速度:</span>
|
||||
<span>{runningStatus.rightMeasureSpeed !== undefined ? `${Number(runningStatus.rightMeasureSpeed).toFixed(1)}rpm` : '-'}</span>
|
||||
<span>左温度:</span>
|
||||
<span>{runningStatus.leftMotorTemp !== undefined ? `${Number(runningStatus.leftMotorTemp).toFixed(1)}℃` : '-'}</span>
|
||||
<span>右温度:</span>
|
||||
<span>{runningStatus.rightMotorTemp !== undefined ? `${Number(runningStatus.rightMotorTemp).toFixed(1)}℃` : '-'}</span>
|
||||
<span>主控温度:</span>
|
||||
<span>{runningStatus.chipTemp !== undefined ? `${Number(runningStatus.chipTemp).toFixed(1)}℃` : '-'}</span>
|
||||
<span>航向角:</span>
|
||||
<span>{runningStatus.yaw !== undefined ? `${Number(runningStatus.yaw).toFixed(1)}°` : '-'}</span>
|
||||
<span>俯仰角:</span>
|
||||
<span>{runningStatus.pitch !== undefined ? `${Number(runningStatus.pitch).toFixed(1)}°` : '-'}</span>
|
||||
<span>航向角状态:</span>
|
||||
<span>{runningStatus.headingStatus !== undefined ? `${runningStatus.headingStatus == 1 ? '初始化' : '未初始化'}` : '-'}</span>
|
||||
<span>定位:</span>
|
||||
<span>
|
||||
{runningStatus.qual == 0 ? '无效' :
|
||||
runningStatus.qual == 1 ? 'GPS单点' :
|
||||
runningStatus.qual == 2 ? 'DGPS差分' :
|
||||
runningStatus.qual == 4 ? 'RTK固定' :
|
||||
runningStatus.qual == 5 ? 'RTK浮点' : '未知'}
|
||||
</span>
|
||||
<span>卫星:</span>
|
||||
<span>{runningStatus.satelliteCnt || 0}颗</span>
|
||||
<span>模式:</span>
|
||||
<span>
|
||||
{runningStatus.controlMode == 1 ? '本地' :
|
||||
runningStatus.controlMode == 3 ? '远程' : '未知'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</> : null}
|
||||
</Space>
|
||||
|
||||
<div style={{
|
||||
@@ -1029,7 +1085,6 @@ export default function DeviceStatusPage({
|
||||
robotVideo={robotVideo}
|
||||
error={error}
|
||||
ready={ready}
|
||||
statusData={runningStatus}
|
||||
/>
|
||||
) : (
|
||||
<div style={{ position: 'relative', width: '100%', height: '100%' }}>
|
||||
|
||||
@@ -1,229 +1,123 @@
|
||||
import React, { useState, useRef, useEffect, memo } from 'react';
|
||||
|
||||
import VideoPlayer from '../VideoPlayer.tsx';
|
||||
import DraggablePanel from './DraggablePanel.jsx';
|
||||
|
||||
// 视频子面板,使用 memo 避免不必要的重渲染
|
||||
const VideoSubPanel = memo(({
|
||||
idx,
|
||||
label,
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
bounds,
|
||||
serialNumber,
|
||||
robotVideo,
|
||||
error,
|
||||
ready
|
||||
}) => {
|
||||
const hasVideo = ready && !error;
|
||||
if (!hasVideo) return null;
|
||||
export function MowerAllView({ serialNumber, robotVideo, error, ready }) {
|
||||
return (
|
||||
<DraggablePanel
|
||||
initialX={x}
|
||||
initialY={y}
|
||||
width={width}
|
||||
height={height}
|
||||
bounds={bounds}
|
||||
handleContent={
|
||||
<div style={{
|
||||
width: '60px',
|
||||
height: '100%',
|
||||
background: 'rgba(0,0,0,0.5)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
borderRadius: '8px',
|
||||
}}>
|
||||
<span style={{ color: '#e9f0eb', fontSize: 16, fontWeight: 'bold' }}>
|
||||
⋮⋮ {label}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
}
|
||||
>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
gap: 8, // 三列之间间距
|
||||
padding: 8,
|
||||
boxSizing: 'border-box',
|
||||
overflow: 'auto'
|
||||
}}>
|
||||
{/* ========== 左列:左视 ========== */}
|
||||
<div style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
border: '1px solid #fff3',
|
||||
borderRadius: 8,
|
||||
overflow: 'hidden',
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 8,
|
||||
}}>
|
||||
<VideoPlayer
|
||||
deviceId={serialNumber}
|
||||
idx={idx}
|
||||
className="w-full h-full object-contain"
|
||||
video={robotVideo}
|
||||
error={error}
|
||||
ready={ready}
|
||||
/>
|
||||
</div>
|
||||
</DraggablePanel>
|
||||
);
|
||||
});
|
||||
|
||||
export function MowerAllView({ serialNumber, robotVideo, error, ready, statusData }) {
|
||||
const containerRef = useRef(null);
|
||||
const [panelSize, setPanelSize] = useState(null);
|
||||
const [initialPositions, setInitialPositions] = useState(null);
|
||||
const [containerBounds, setContainerBounds] = useState(null);
|
||||
|
||||
// 初始化尺寸、窗口缩放重新计算面板尺寸
|
||||
useEffect(() => {
|
||||
const calcSize = () => {
|
||||
if (!containerRef.current) return;
|
||||
const rect = containerRef.current.getBoundingClientRect();
|
||||
if (rect.width === 0 || rect.height === 0) return;
|
||||
|
||||
const w = rect.width * 0.30;
|
||||
const h = Math.floor(rect.height * 0.35);
|
||||
|
||||
setPanelSize({ width: w, height: h });
|
||||
setContainerBounds({ width: rect.width, height: rect.height });
|
||||
setInitialPositions({
|
||||
left: { x: 8, y: 8 },
|
||||
right: { x: rect.width - w - 8, y: 8 },
|
||||
back: { x: (rect.width - w) / 2, y: rect.height - h - 8 }
|
||||
});
|
||||
};
|
||||
|
||||
calcSize();
|
||||
const timer = setTimeout(calcSize, 100);
|
||||
window.addEventListener('resize', calcSize);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timer);
|
||||
window.removeEventListener('resize', calcSize);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
style={{
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
overflow: 'hidden'
|
||||
}}
|
||||
>
|
||||
{/* 前视主画面 - 铺满 */}
|
||||
<div style={{ position: 'absolute', inset: 0, zIndex: 1 }}>
|
||||
<VideoPlayer
|
||||
deviceId={serialNumber}
|
||||
idx={1}
|
||||
className="w-full h-full object-contain"
|
||||
video={robotVideo}
|
||||
error={error}
|
||||
ready={ready}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 三个可拖拽子面板 */}
|
||||
{panelSize && initialPositions && containerBounds && (
|
||||
<>
|
||||
<VideoSubPanel
|
||||
idx={3}
|
||||
label="左视"
|
||||
x={initialPositions.left.x}
|
||||
y={initialPositions.left.y}
|
||||
width={panelSize.width}
|
||||
height={panelSize.height}
|
||||
bounds={containerBounds}
|
||||
serialNumber={serialNumber}
|
||||
robotVideo={robotVideo}
|
||||
error={error}
|
||||
ready={ready}
|
||||
/>
|
||||
<VideoSubPanel
|
||||
idx={4}
|
||||
label="右视"
|
||||
x={initialPositions.right.x}
|
||||
y={initialPositions.right.y}
|
||||
width={panelSize.width}
|
||||
height={panelSize.height}
|
||||
bounds={containerBounds}
|
||||
serialNumber={serialNumber}
|
||||
robotVideo={robotVideo}
|
||||
error={error}
|
||||
ready={ready}
|
||||
/>
|
||||
<VideoSubPanel
|
||||
idx={2}
|
||||
label="后视"
|
||||
x={initialPositions.back.x}
|
||||
y={initialPositions.back.y}
|
||||
width={panelSize.width}
|
||||
height={panelSize.height}
|
||||
bounds={containerBounds}
|
||||
serialNumber={serialNumber}
|
||||
robotVideo={robotVideo}
|
||||
error={error}
|
||||
ready={ready}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 状态详情面板 */}
|
||||
{statusData && (
|
||||
<div style={{
|
||||
position: 'absolute',
|
||||
bottom: 8,
|
||||
left: 8,
|
||||
background: 'rgba(0,0,0,0.7)',
|
||||
color: '#fff',
|
||||
padding: '10px 12px',
|
||||
flex: 1,
|
||||
position: 'relative',
|
||||
border: '1px solid #eae4e4',
|
||||
flexShrink: 0,
|
||||
borderRadius: 8,
|
||||
fontSize: 11,
|
||||
zIndex: 20,
|
||||
backdropFilter: 'blur(4px)'
|
||||
overflow: "hidden",
|
||||
maxHeight: 240,
|
||||
transform: 'translateY(50%)'
|
||||
|
||||
}}>
|
||||
<div style={{ marginBottom: 6, fontWeight: 600, color: '#1890ff' }}>机器人状态</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'auto auto', gap: '3px 10px' }}>
|
||||
<span>电量:</span>
|
||||
<span style={{ color: (statusData.battery || 0) < 20 ? '#ff4d4f' : '#52c41a' }}>
|
||||
{statusData.battery !== undefined ? `${statusData.battery}%` : '-'}
|
||||
</span>
|
||||
<span>电压:</span>
|
||||
<span>{statusData.voltage !== undefined ? `${Number(statusData.voltage).toFixed(2)}V` : '-'}</span>
|
||||
<span>左电机:</span>
|
||||
<span>{statusData.leftCurrent !== undefined ? `${Number(statusData.leftCurrent).toFixed(2)}A` : '-'}</span>
|
||||
<span>右电机:</span>
|
||||
<span>{statusData.rightCurrent !== undefined ? `${Number(statusData.rightCurrent).toFixed(2)}A` : '-'}</span>
|
||||
<span>左速度:</span>
|
||||
<span>{statusData.leftMeasureSpeed !== undefined ? `${Number(statusData.leftMeasureSpeed).toFixed(1)}rpm` : '-'}</span>
|
||||
<span>右速度:</span>
|
||||
<span>{statusData.rightMeasureSpeed !== undefined ? `${Number(statusData.rightMeasureSpeed).toFixed(1)}rpm` : '-'}</span>
|
||||
<span>左温度:</span>
|
||||
<span>{statusData.leftMotorTemp !== undefined ? `${Number(statusData.leftMotorTemp).toFixed(1)}℃` : '-'}</span>
|
||||
<span>右温度:</span>
|
||||
<span>{statusData.rightMotorTemp !== undefined ? `${Number(statusData.rightMotorTemp).toFixed(1)}℃` : '-'}</span>
|
||||
<span>主控温度:</span>
|
||||
<span>{statusData.chipTemp !== undefined ? `${Number(statusData.chipTemp).toFixed(1)}℃` : '-'}</span>
|
||||
<span>航向角:</span>
|
||||
<span>{statusData.yaw !== undefined ? `${Number(statusData.yaw).toFixed(1)}°` : '-'}</span>
|
||||
<span>俯仰角:</span>
|
||||
<span>{statusData.pitch !== undefined ? `${Number(statusData.pitch).toFixed(1)}°` : '-'}</span>
|
||||
<span>航向角状态:</span>
|
||||
<span>{statusData.headingStatus !== undefined ? `${statusData.headingStatus == 1 ? '初始化' : '未初始化'}` : '-'}</span>
|
||||
<span>定位:</span>
|
||||
<span>
|
||||
{statusData.qual == 0 ? '无效' :
|
||||
statusData.qual == 1 ? 'GPS单点' :
|
||||
statusData.qual == 2 ? 'DGPS差分' :
|
||||
statusData.qual == 4 ? 'RTK固定' :
|
||||
statusData.qual == 5 ? 'RTK浮点' : '未知'}
|
||||
</span>
|
||||
<span>卫星:</span>
|
||||
<span>{statusData.satelliteCnt || 0}颗</span>
|
||||
<span>模式:</span>
|
||||
<span>
|
||||
{statusData.controlMode == 1 ? '本地' :
|
||||
statusData.controlMode == 3 ? '远程' : '未知'}
|
||||
</span>
|
||||
</div>
|
||||
<VideoPlayer
|
||||
deviceId={serialNumber}
|
||||
idx={3}
|
||||
className="w-full h-full object-contain"
|
||||
video={robotVideo}
|
||||
error={error}
|
||||
ready={ready}
|
||||
/>
|
||||
<div style={{ position: 'absolute', top: 4, left: 8, color: '#fff', fontSize: 12, background: '#0006', padding: '2px 6px', borderRadius: 4 }}>左视</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* ========== 中列:上-前视 下-后视 ========== */}
|
||||
<div style={{
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 8,
|
||||
}}>
|
||||
{/* 前视 */}
|
||||
<div style={{
|
||||
position: 'relative',
|
||||
border: '1px solid #eae4e4',
|
||||
flexShrink: 0,
|
||||
borderRadius: 8,
|
||||
overflow: "hidden",
|
||||
height: 240
|
||||
}}>
|
||||
<VideoPlayer
|
||||
deviceId={serialNumber}
|
||||
idx={1}
|
||||
className="w-full h-full object-contain"
|
||||
video={robotVideo}
|
||||
error={error}
|
||||
ready={ready}
|
||||
/>
|
||||
<div style={{ position: 'absolute', top: 4, left: 8, color: '#fff', fontSize: 12, background: '#0006', padding: '2px 6px', borderRadius: 4 }}>前视</div>
|
||||
</div>
|
||||
{/* 后视 */}
|
||||
<div style={{
|
||||
height: 240,
|
||||
position: 'relative',
|
||||
border: '1px solid #eae4e4',
|
||||
flexShrink: 0,
|
||||
borderRadius: 8,
|
||||
overflow: "hidden"
|
||||
}}>
|
||||
<VideoPlayer
|
||||
deviceId={serialNumber}
|
||||
idx={2}
|
||||
className="w-full h-full object-contain"
|
||||
video={robotVideo}
|
||||
error={error}
|
||||
ready={ready}
|
||||
/>
|
||||
<div style={{ position: 'absolute', top: 4, left: 8, color: '#fff', fontSize: 12, background: '#0006', padding: '2px 6px', borderRadius: 4 }}>后视</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div style={{
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 8,
|
||||
}}>
|
||||
<div style={{
|
||||
flex: 1,
|
||||
position: 'relative',
|
||||
border: '1px solid #eae4e4',
|
||||
flexShrink: 0,
|
||||
borderRadius: 8,
|
||||
overflow: "hidden",
|
||||
maxHeight: 240,
|
||||
transform: 'translateY(50%)'
|
||||
|
||||
}}>
|
||||
<VideoPlayer
|
||||
deviceId={serialNumber}
|
||||
idx={4}
|
||||
className="w-full h-full object-contain"
|
||||
video={robotVideo}
|
||||
error={error}
|
||||
ready={ready}
|
||||
/>
|
||||
<div style={{ position: 'absolute', top: 4, left: 8, color: '#fff', fontSize: 12, background: '#0006', padding: '2px 6px', borderRadius: 4 }}>右视</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user