CMVR-BID/src/views/inspection/cockpit/components/ManualTakeover.vue
2026-05-26 14:45:49 +08:00

628 lines
21 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="monitor-container">
<div class="left-container">
<div class="robot-container">
<div class="title">选择机器人</div>
<div class="robot-box">
<div v-for="item, index in 4" class="robot" :class="{ active: activeBot === index }"
@click="changeBot(index)">
<div>
<SvgIcon name="bot" :color="colorList[index]" />
</div>
<div>
<div class="bot-name">机器人#01</div>
<div class="bot-local">厂区A-在线</div>
</div>
</div>
</div>
</div>
<div class="split-line"></div>
<div class="mode-container">
<div class="title">控制模式</div>
<div class="mode-box">
<div class="mode-item" :class="{ activeMode: activeMode === 'auto' }" @click="changeMode('auto')">
<SvgIcon name="cpu" :color="activeMode === 'auto' ? '#00FF88' : '#fff'" :size="16" />
<span class="label">自动巡检</span>
</div>
<div class="mode-item" :class="{ activeMode: activeMode === 'artificial' }"
@click="changeMode('artificial')">
<SvgIcon name="gamepad" :color="activeMode === 'artificial' ? '#00FF88' : '#fff'" :size="16" />
<span class="label">人工接管</span>
</div>
</div>
</div>
<div v-if="activeMode === 'artificial'" class="artificial-panel">
<el-radio-group v-model="activeController" size="small" @change="handlerController">
<el-radio :value="1">底盘</el-radio>
<el-radio :value="2">机械臂</el-radio>
<el-radio :value="3">相机</el-radio>
</el-radio-group>
<div class="controller-panel">人工控制面板</div>
<div v-if="activeController === 1" class="direction-controller-container">
<div class="title">
<SvgIcon name="navigation" color="00D4FF" :size="16" />
<span class="label">方向控制器</span>
</div>
<div class="direction-box">
<div class="direction-container">
<div class="item"></div>
<div class="item">
<SvgIcon name="chevron-up" :size="24" />
</div>
<div class="item"></div>
<div class="item">
<SvgIcon name="chevron-left" :size="24" />
</div>
<div class="item">
<SvgIcon name="chevron-stop" :size="52" />
</div>
<div class="item">
<SvgIcon name="chevron-right" :size="24" />
</div>
<div class="item"></div>
<div class="item">
<SvgIcon name="chevron-down" :size="24" />
</div>
<div class="item"></div>
</div>
</div>
<div class="speed-box">
<div class="label">移动速度</div>
<div class="speed">{{ speed }}m/s</div>
</div>
<el-slider v-model="speed" :max="2" :step="0.1" size="small" />
</div>
<div v-if="activeController === 2" class="robotic-arm-controller-container">
<el-radio-group v-model="activeRoboticArm" @change="handlerArm" size="small">
<el-radio :value="1">末端控制</el-radio>
<el-radio :value="2">关节控制</el-radio>
<el-radio :value="3">姿态控制</el-radio>
</el-radio-group>
<div class="mode-tabs">
<div class="mode-tab active">左手</div>
<div class="mode-tab">右手</div>
</div>
<div class="robotic-arm-box">
<div class="robotic-arm-status">
<div class="coord-display">
<div class="coord-item">
<div class="coord-key key-x">X</div>
<div class="coord-val" id="coordX">+124.50</div>
</div>
<div class="coord-item">
<div class="coord-key key-y">Y</div>
<div class="coord-val" id="coordY">−38.20</div>
</div>
<div class="coord-item">
<div class="coord-key key-z">Z</div>
<div class="coord-val" id="coordZ">+502.00</div>
</div>
</div>
</div>
<div class="axis-group">
<!-- X Axis -->
<div class="axis-row x">
<div class="axis-label x">X</div>
<div class="axis-slider-wrap">
<div class="axis-slider-top">
<span class="axis-name">前后平移</span>
<span class="axis-value" id="valX">0.00</span>
</div>
<el-slider v-model="speed" :max="2" :step="0.1" size="small" />
</div>
</div>
<!-- Y Axis -->
<div class="axis-row y">
<div class="axis-label y">Y</div>
<div class="axis-slider-wrap">
<div class="axis-slider-top">
<span class="axis-name">左右平移</span>
<span class="axis-value" id="valY">0.00</span>
</div>
<el-slider v-model="speed" :max="2" :step="0.1" size="small" />
</div>
</div>
<!-- Z Axis -->
<div class="axis-row z">
<div class="axis-label z">Z</div>
<div class="axis-slider-wrap">
<div class="axis-slider-top">
<span class="axis-name">上下平移</span>
<span class="axis-value" id="valZ">0.00</span>
</div>
<el-slider v-model="speed" :max="2" :step="0.1" size="small" />
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="video-container">
<div v-if="activeMode === 'auto' || activeController === 1" class="map-container">地图</div>
<div v-if="activeMode === 'artificial' && activeController === 2" class="model-container">机械臂模型</div>
<div v-if="activeMode === 'artificial' && activeController === 3" class="monitor-container">监控</div>
</div>
<div class="right-container">
<div class="container-title">车载监控</div>
<div class="monitor-container"></div>
<div class="split-line"></div>
<div class="container-title">语音对话</div>
</div>
</div>
</template>
<script setup>
import SvgIcon from "@/components/SvgIcon";
const activeBot = ref(0)
const changeBot = (index) => {
activeBot.value = index
}
const colorList = ['#00D4FF', '#FFB300', '#00FF88', '#fff']
const activeMode = ref('auto')
const changeMode = (mode) => {
activeMode.value = mode
}
const activeController = ref(1)
const speed = ref(0.8)
const activeRoboticArm = ref(1)
const handlerController = (value) => {
if (value === 2 && [1, 3].includes(activeRoboticArm.value)) {
}
}
const handlerArm = (value) => {
if ([1, 3].includes(value)) {
}
}
</script>
<style lang="scss" scoped>
.monitor-container {
width: 100%;
height: 100%;
display: flex;
.left-container {
width: 360px;
height: 100%;
padding: 16px;
.robot-container {
.title {
color: #8899BB;
font-size: 14px;
margin-bottom: 16px;
}
.robot-box {
display: flex;
flex-wrap: wrap;
gap: 10px;
align-items: center;
justify-content: space-between;
.robot {
flex: 0 0 calc(50% - 10px);
background: #0F1E38;
height: 56px;
border-radius: 8px;
padding: 16px;
display: flex;
align-items: center;
gap: 20px;
margin-bottom: 16px;
cursor: pointer;
border: 1px solid transparent;
.bot-name {
color: #fff;
font-size: 13px;
}
.bot-local {
color: #00d4ff;
font-size: 11px;
margin-top: 4px;
}
}
.active {
border-color: #00c2ff;
color: #fff;
box-shadow: 0 0 10px rgba(0, 194, 255, 0.5);
}
}
}
.split-line {
width: 100%;
height: 1px;
margin: 16px 0;
background: #1E3A5F;
}
.mode-container {
.title {
color: #8899BB;
font-size: 14px;
margin-bottom: 16px;
}
.mode-box {
display: flex;
flex-wrap: wrap;
gap: 10px;
align-items: center;
justify-content: space-between;
.mode-item {
height: 36px;
flex: 0 0 calc(50% - 10px);
background: #0F1E38;
border: 1px solid transparent;
border-radius: 8px;
padding: 16px;
margin-bottom: 12px;
display: flex;
align-items: center;
gap: 16px;
cursor: pointer;
.label {
color: #8899BB;
font-size: 13px;
}
}
.activeMode {
border-color: #00FF88;
.label {
color: #00FF88;
}
}
}
}
.controller-panel {
padding: 16px;
color: #fff;
font-size: 16px;
}
.direction-controller-container {
padding: 16px;
height: 272px;
background: #0F1E38;
border-radius: 10px;
.title {
display: flex;
align-items: center;
gap: 16px;
margin-bottom: 16px;
.label {
color: #fff;
font-size: 14px;
}
}
.direction-box {
display: flex;
align-items: center;
justify-content: center;
.direction-container {
display: flex;
flex-wrap: wrap;
gap: 4px;
width: 164px;
height: 164px;
.item {
// flex: 0 0 calc(33.333% - 4px);
width: 52px;
height: 52px;
background: #1E3A5F;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
}
}
}
.speed-box {
display: flex;
align-items: center;
justify-content: space-between;
margin: 8px 0;
.label {
color: #8899BB;
font-size: 12px;
}
.speed {
color: #00D4FF;
font-size: 12px;
}
}
:deep(.el-slider__button) {
scale: 0.5;
}
}
.robotic-arm-controller-container {
:deep(.el-radio-group) {
display: flex;
align-items: center;
justify-content: space-between;
}
.mode-tabs {
display: flex;
padding: 12px 20px 0;
gap: 6px;
.mode-tab {
flex: 1;
padding: 8px 0;
text-align: center;
font-size: 12px;
font-weight: 500;
color: #4a5580;
background: transparent;
border: 1px solid transparent;
border-bottom: none;
border-radius: 8px 8px 0 0;
cursor: pointer;
transition: all 0.25s ease;
position: relative;
user-select: none;
&:hover {
color: #8b97bf;
background: #3b82f60d;
}
}
.active {
color: #3b82f6;
background: #111a36;
border-color: #1c2a52;
font-weight: 700;
&:after {
content: '';
position: absolute;
bottom: 0;
left: 20%;
right: 20%;
height: 2px;
background: var(--accent);
border-radius: 1px;
box-shadow: 0 0 8px var(--accent-glow);
}
}
}
.robotic-arm-box {
.robotic-arm-status {
.coord-display {
display: flex;
gap: 8px;
padding: 12px;
background: #070b1a;
border: 1px solid #1c2a52;
border-radius: 8px;
margin-bottom: 16px;
.coord-item {
flex: 1;
text-align: center;
padding: 6px 0;
border-radius: 6px;
background: rgba(255, 255, 255, 0.02);
.coord-key {
font-size: 12px;
font-weight: bold;
margin-bottom: 3px;
}
.key-x {
color: #f87171;
}
.key-y {
color: #34d399;
;
}
.key-z {
color: #38bdf8;
;
}
.coord-val {
font-size: 14px;
font-weight: bold;
color: #e8edf8;
}
}
}
}
.axis-group {
display: flex;
flex-direction: column;
gap: 12px;
margin-bottom: 16px;
.axis-row {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 14px;
background: #0c1228;
border: 1px solid #1c2a52;
border-radius: 10px;
transition: all 0.2s ease;
&:hover {
border-color: #2a3f7a;
background: #3b82f608;
}
.axis-label {
width: 28px;
height: 28px;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
font-size: 13px;
font-weight: 700;
flex-shrink: 0;
}
.x {
background: #f8717126;
color: #f87171;
border: 1px solid #f871714d;
}
.y {
background: #34d39926;
color: #34d399;
border: 1px solid #34d3994d;
}
.z {
background: #38bdf826;
color: #38bdf8;
border: 1px solid #38bdf84d;
}
.axis-slider-wrap {
flex: 1;
display: flex;
flex-direction: column;
gap: 4px;
.axis-slider-top {
display: flex;
justify-content: space-between;
align-items: center;
.axis-name {
font-size: 11px;
color: #8b97bf;
font-weight: 500;
}
.axis-value {
font-size: 12px;
font-weight: 500;
color: #e8edf8;
min-width: 52px;
text-align: right;
}
}
:deep(.el-slider__button) {
scale: 0.5;
}
}
}
}
}
}
}
.video-container {
flex: 1;
border: 1px solid #1E3A5F;
border-top: 0;
.map-container {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
color: #00c2ff;
font-size: 20px;
}
.model-container {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
color: #00c2ff;
font-size: 20px;
}
.monitor-container {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
color: #00c2ff;
font-size: 20px;
}
}
.right-container {
width: 360px;
height: 100%;
padding: 16px;
.container-title {
color: #fff;
font-size: 16px;
font-weight: bold;
margin-bottom: 16px;
}
.split-line {
width: 100%;
height: 1px;
margin: 16px 0;
background: #1E3A5F;
}
.monitor-container {
width: 100%;
height: 360px;
}
}
}
</style>