CMVR-IOT-UI/src/views/device/register/components/MechanicalArm/components/JointControl.vue
2025-12-09 10:33:05 +08:00

133 lines
3.0 KiB
Vue

<template>
<div class="container">
<div class="joint-control" v-for="joint in jointList" :key="joint.name">
<div class="joint-header">
<span class="joint-name">{{ joint.displayName }}</span>
<span class="joint-type">{{ joint.type.toUpperCase() }}</span>
</div>
<div class="joint-info">
<span class="angle-display">{{ formatAngle(joint.value) }}</span>
<span class="limits">[{{ formatAngle(joint.min) }}, {{ formatAngle(joint.max) }}]</span>
</div>
<div class="slider-container">
<el-slider size="small" v-model="joint.value" :min="joint.min" :max="joint.max" :step="joint.step" @input="(value) => updateJointAngle(joint.name, value)"/>
<div class="slider-labels">
<span>{{ formatAngle(joint.min) }}</span>
<span>{{ formatAngle(joint.max) }}</span>
</div>
</div>
</div>
</div>
</template>
<script setup>
const props = defineProps({
jointList: {
type: Array,
default: () => []
}
})
const emits = defineEmits(['updateJointAngle'])
const updateJointAngle = (name, value) => {
emits('updateJointAngle', { name, value })
}
/**
* 角度格式化
*/
const formatAngle = (radians) => {
const degrees = (radians * 180 / Math.PI).toFixed(1)
return `${degrees}°`
}
</script>
<style lang="scss" scoped>
.container {
width: 100%;
height: 100%;
.joint-control {
border-radius: 6px;
padding: 15px;
margin-bottom: 15px;
border: 1px solid #cfcfcf;
.joint-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
.joint-name {
font-weight: 600;
font-size: 14px;
}
.joint-type {
font-size: 11px;
background: rgba(52, 152, 219, 0.3);
color: #3498db;
padding: 3px 8px;
border-radius: 12px;
font-weight: 500;
}
}
.joint-info {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
font-size: 13px;
.angle-display {
color: #f1c40f;
font-weight: 600;
font-size: 14px;
}
.limits {
color: #95a5a6;
font-size: 11px;
font-family: monospace;
}
}
.slider-container {
padding: 0 5px;
.joint-slider {
width: 100%;
height: 8px;
-webkit-appearance: none;
background: #34495e;
border-radius: 4px;
outline: none;
margin-bottom: 8px;
cursor: pointer;
&::-webkit-slider-thumb {
-webkit-appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: #ecf0f1;
cursor: pointer;
border: 2px solid #3498db;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
transition: all 0.2s;
}
}
.slider-labels {
display: flex;
justify-content: space-between;
font-size: 11px;
color: #7f8c8d;
margin-top: 5px;
}
}
}
}
</style>