CMVR-IOT-UI/src/views/device/register/components/DexHand/LeftTopHand.vue

270 lines
7.2 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="left-top-hand" v-loading="loading">
<el-button
class="copy-button"
type="button"
:icon="isCopied ? 'Check' : 'CopyDocument'"
:disabled="isCopied"
@click="copyParams"
aria-label="复制当前参数"
>
{{ isCopied ? '完成' : '复制' }}
</el-button>
<img src="@/assets/images/hand.png" ref="handImage" class="hand-image" />
<div v-for="(slider, index) in sliders" :key="index" class="slider-container" :style="slider.style">
<span :class="`slider-title slider-title-${index}`">{{ slider.value }}</span>
<el-slider
v-model="slider.value"
:vertical="slider.vertical"
:height="slider.height + 'px'"
@change="updateSeriesData(slider.value, index)"
:class="`slider-${index}`"
/>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import { status, setDexHandAngle } from '@/api/device/dexHand';
const emit = defineEmits(['update:topSeriesData']);
const loading = ref(false);
const handImage = ref(null);
const containerRef = ref(null); // 新增:引用父容器
const defaultHandImageInfo = { width: 891, height: 981 };
const imageAspectRatio = defaultHandImageInfo.width / defaultHandImageInfo.height;
const terminalId = ref('');
const deviceId = ref('');
// 1. 定义 props
const props = defineProps({
initialDeviceId: { // 示例 prop用于从弹窗接收cameraId
type: String,
default: null,
},
initialTerminalId: { // 示例 prop用于从弹窗接收 terminalId
type: String,
default: null,
},
});
// 2. 使用 watchEffect 来响应 props 和路由的变化
watchEffect(() => {
terminalId.value = props.initialTerminalId;
deviceId.value = props.initialDeviceId;
console.log(deviceId.value, terminalId.value,props)
});
const sliders = ref([
{ value: 0, defalutHeight: 466, height: 0, x: 120, y: 140, vertical: true, style: {} }, // 小拇指
{ value: 0, defalutHeight: 466, height: 0, x: 230, y: 140, vertical: true, style: {} }, // 无名指
{ value: 0, defalutHeight: 466, height: 0, x: 373, y: 140, vertical: true, style: {} }, // 中指
{ value: 0, defalutHeight: 466, height: 0, x: 468, y: 140, vertical: true, style: {} }, // 食指
{ value: 0, defalutHeight: 346, height: 0, x: 608, y: 360, vertical: true, style: {} }, // 大拇指弯曲
{ value: 0, defalutHeight: 633, height: 0, x: 85, y: 723, vertical: false, style: {} }, // 大拇指旋转
]);
function updateSeriesData(value, i) {
console.log(deviceId.value, terminalId.value)
loading.value = true;
setDexHandAngle({ deviceId: deviceId.value, terminalId: terminalId.value, value: value / 100, id: i }).then(() => {
loading.value = false;
updateToSeriesData();
}).catch(() => {
loading.value = false;
});
}
const isCopied = ref(false);
const copyParams = () => {
navigator.clipboard.writeText(JSON.stringify(sliders.value))
.then(() => {
isCopied.value = true;
setTimeout(() => {
isCopied.value = false;
}, 3000);
})
.catch(err => {
console.error('复制失败:', err);
});
};
const debounce = (func, delay) => {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func.apply(null, args), delay);
};
};
const updateSliderPositions = () => {
const img = handImage.value;
if (img) {
const { width, height } = img.getBoundingClientRect();
console.log('Window resized - New dimensions:', width, height);
const widthRatio = width / height;
let realWidth = 0;
let realHeight = 0;
if (widthRatio > imageAspectRatio) {
realHeight = height;
realWidth = realHeight * imageAspectRatio;
} else {
realWidth = width;
realHeight = realWidth / imageAspectRatio;
}
const realImageAspectRatio = realWidth / defaultHandImageInfo.width;
console.log('Real dimensions:', realWidth, realHeight, realImageAspectRatio);
sliders.value.forEach((slider, index) => {
slider.height = slider.defalutHeight * realImageAspectRatio;
slider.style = {
top: (height / 2 - realHeight / 2) + slider.y * realImageAspectRatio - 16 + 'px',
left: (width / 2 - realWidth / 2) + slider.x * realImageAspectRatio - 16 + 'px',
position: 'absolute',
zIndex: 10,
width: slider.height + 'px',
};
});
}
};
const updateToSeriesData = () => {
status({ deviceId: deviceId.value, terminalId: terminalId.value }).then((res) => {
const newTopSeriesData = res.data.handsList.map(item => item.force);
emit('update:topSeriesData', newTopSeriesData);
});
};
const data = ref({
debouncedResize: null,
resizeObserver: null,
});
onMounted(() => {
updateSliderPositions();
// 使用 ResizeObserver 监听父容器尺寸变化
const container = containerRef.value || handImage.value?.parentElement;
if (container) {
const resizeObserver = new ResizeObserver(debounce(updateSliderPositions, 10));
resizeObserver.observe(container);
data.value.resizeObserver = resizeObserver;
} else {
console.error('Failed to find container for ResizeObserver');
}
const debouncedResize = debounce(updateSliderPositions, 0);
window.addEventListener('resize', debouncedResize);
data.value.debouncedResize = debouncedResize;
updateToSeriesData();
});
onUnmounted(() => {
// 清理 ResizeObserver
if (data.value.resizeObserver) {
data.value.resizeObserver.disconnect();
data.value.resizeObserver = null;
}
// 清理 window 的 resize 事件
if (data.value.debouncedResize) {
window.removeEventListener('resize', data.value.debouncedResize);
data.value.debouncedResize = null;
}
});
</script>
<style scoped>
.left-top-hand {
position: relative;
height: 100%;
}
.hand-image {
width: 100%;
height: 100%;
object-fit: contain;
}
.slider-container {
position: absolute;
zIndex: 10;
}
.slider-container ::v-deep(.el-slider__button) {
width: 25px !important;
height: 12px !important;
background-color: #409eff !important;
border-radius: 0 !important;
}
.slider-5 ::v-deep(.el-slider__button) {
width: 12px !important;
height: 25px !important;
background-color: #409eff !important;
border-radius: 0 !important;
}
.slider-container .el-slider__runway {
background-color: #007bff;
border-radius: 2px;
}
.slider-title {
color: #409eff;
position: absolute;
bottom: -20px;
left: 10px;
}
.slider-title-5 {
bottom: 8px;
left: -30px;
}
.image {
width: 100%;
height: 50%;
object-fit: contain;
}
@media (max-width: 768px) {
.slider-container {
width: 5vw;
}
}
.copy-button {
position: absolute;
background-color: #FFFfff;
padding: 5px 10px;
border-radius: 5px;
border: 0;
color: #646362;
font-size: 16px;
cursor: pointer;
transition: all 0.3s ease;
outline: none;
}
.copy-button:hover {
background-color: #FFF5E1;
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
.copy-button:active {
background-color: #FFDAB9;
transform: translateY(0);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.copy-button:focus {
box-shadow: 0 0 0 3px rgba(255, 165, 0, 0.3);
}
.copy-button:disabled {
background-color: #F0E8E0;
cursor: not-allowed;
transform: none;
box-shadow: none;
}
</style>