forked from SmartBench/CMVR-IOT-UI
- 使用 ResizeObserver 替代 window resize 事件监听,提高性能 - 优化组件布局,确保在不同屏幕尺寸下正常显示 - 修复了一些样式问题,如重复 padding、min-height 等 - 优化了图表组件的初始化和销毁逻辑,提高资源利用率
566 lines
18 KiB
Vue
566 lines
18 KiB
Vue
<script setup>
|
|
import {useRoute} from "vue-router"
|
|
import dayjs from "dayjs";
|
|
import {deviceTeach, deviceUpdateProfile} from "@/api/device/register.js";
|
|
import {formatNumber} from "@/views/device/register/components/index.js";
|
|
import {convertNumbersToStringAndRemoveNulls} from "@/utils/fn.js";
|
|
|
|
const data = reactive({
|
|
form: {
|
|
workModule: 1,//工作模式
|
|
posStep: 1,//位置步进
|
|
postureStep: 1,//姿态步进
|
|
jointStep: 1,//关节步进
|
|
joint1: 0.000012,//关节1
|
|
joint2: 0.000012,//
|
|
joint3: 0.000012,//
|
|
joint4: 0.000012,//
|
|
joint5: 0.000012,//
|
|
joint6: 0.000012,//关节6
|
|
x: 11,//
|
|
y: 11,//
|
|
z: 11,//
|
|
rx: 11,//
|
|
ry: 11,//
|
|
rz: 11,//
|
|
arm: 1,//目标
|
|
coordinate: 1,//坐标系
|
|
},
|
|
speed: 20,//速度
|
|
zeroPos: {//零位姿态
|
|
x: 0,
|
|
y: 0,
|
|
z: 0,
|
|
rx: 0,
|
|
ry: 0,
|
|
rz: 0,
|
|
},
|
|
initPos: {
|
|
x: 1,
|
|
y: 1,
|
|
z: 1,
|
|
rx: 1,
|
|
ry: 1,
|
|
rz: 1,
|
|
},//初始姿态
|
|
armList: [
|
|
{
|
|
label: '机械臂1',
|
|
value: 1
|
|
},
|
|
{
|
|
label: '机械臂2',
|
|
value: 2
|
|
}
|
|
],
|
|
coordinateList: [
|
|
{
|
|
label: 'x轴',
|
|
value: 1
|
|
},
|
|
{
|
|
label: 'y轴',
|
|
value: 2
|
|
},
|
|
{
|
|
label: 'z轴',
|
|
value: 3
|
|
}
|
|
],
|
|
time: dayjs().format("YYYY-MM-DD HH:mm:ss"),
|
|
})
|
|
|
|
const {
|
|
form, time,
|
|
zeroPos,
|
|
initPos,
|
|
armList,
|
|
coordinateList
|
|
} = toRefs(data)
|
|
|
|
const route = useRoute()
|
|
let timer = null
|
|
|
|
onUnmounted(() => {
|
|
clearInterval(timer)
|
|
})
|
|
|
|
//获取机械臂参数
|
|
const getDeviceInfo = () => {
|
|
deviceTeach(devId.value).then((res) => {
|
|
let data = formatNumber(res.data)//字符类型转数字类型
|
|
Object.assign(form.value, data)
|
|
//设置初始位置
|
|
initPos.value.x = data.x
|
|
initPos.value.y = data.y
|
|
initPos.value.z = data.z
|
|
initPos.value.rx = data.rx
|
|
initPos.value.ry = data.ry
|
|
initPos.value.rz = data.rz
|
|
})
|
|
}
|
|
|
|
onMounted(() => {
|
|
getDeviceInfo()
|
|
const posControls = document.querySelectorAll(".btn")
|
|
//监听按钮点击事件
|
|
posControls.forEach((item) => {
|
|
item.addEventListener('click', (e) => {
|
|
const target = e.currentTarget.classList[1]
|
|
const vals = target.split("_")//x_0 rx_0
|
|
const pos = vals[0]
|
|
const calc = !!Number(vals[1])
|
|
if (calc) {
|
|
form.value[pos] += 0.01 * data.speed
|
|
} else {
|
|
form.value[pos] -= 0.01 * data.speed
|
|
}
|
|
})
|
|
})
|
|
//监听失焦事件
|
|
// const warp = document.querySelector(".mechanical-arm")
|
|
// warp.addEventListener('focusout', handleFocus, false)
|
|
|
|
timer = setInterval(() => {
|
|
time.value = dayjs().format("YYYY-MM-DD HH:mm:ss")
|
|
getDeviceInfo()
|
|
}, 1000)
|
|
})
|
|
watch(() => JSON.parse(JSON.stringify(form.value)), (newData, oldData) => {
|
|
// const change = findDifferences(oldData, newData)
|
|
const data = convertNumbersToStringAndRemoveNulls(newData)
|
|
deviceUpdateProfile({idDeDeviceRegistration: devId.value, ...data}).then((res)=>{
|
|
console.log(res)
|
|
})
|
|
})
|
|
|
|
const handleFocus = (e) => {
|
|
const type = e.target.type
|
|
if (type === "number") {
|
|
// console.log("数字框失焦")
|
|
}
|
|
}
|
|
|
|
const devId = computed(() => {
|
|
return route.path.split("/")[2]
|
|
})
|
|
const deviceModel = computed(() => {
|
|
return route.path.split("/")[1]
|
|
})
|
|
|
|
const handleResets = (type) => {
|
|
if (type === "zero") {
|
|
Object.assign(form.value, zeroPos.value)
|
|
return
|
|
}
|
|
if (type === "init") {
|
|
Object.assign(form.value, initPos.value)
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="mechanical-arm">
|
|
<div class="top flex between">
|
|
<div class="left " style="width: 38%">
|
|
<div class="card" style="height: 100%">
|
|
<div class="title">机械臂</div>
|
|
<div class="body">机械臂图片</div>
|
|
</div>
|
|
</div>
|
|
<div class="center" style="width: 22%">
|
|
<div class="card">
|
|
<div class="title">工作模式</div>
|
|
<div class="body">
|
|
<el-radio-group v-model="form.workModule">
|
|
<el-radio :value="1">真实机械臂</el-radio>
|
|
<el-radio :value="2">仿真机械臂</el-radio>
|
|
</el-radio-group>
|
|
</div>
|
|
</div>
|
|
<div class="card" style="margin-top: 10px">
|
|
<div class="title">步进模式</div>
|
|
<div class="mb10">
|
|
<div class="mb5 action-label">位置步进( mm )</div>
|
|
<el-input-number style="width: 100%;" v-model="form.posStep" :step="2"/>
|
|
</div>
|
|
|
|
<div class="mb10">
|
|
<div class="mb5 action-label">姿态步进( deg )</div>
|
|
<el-input-number style="width: 100%;" v-model="form.postureStep" :step="2"/>
|
|
</div>
|
|
|
|
<div>
|
|
<div class="mb5 action-label">关节步进( deg )</div>
|
|
<el-input-number style="width: 100%;" v-model="form.jointStep" :step="2"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="right " style="width: 38%">
|
|
<div class="card" style="height: 100%">
|
|
<div class="title">位置控制</div>
|
|
<div class="body">
|
|
<div class="btn-wrap pos-control" style="transform: rotateX(40deg);height: calc(100% - 70px)">
|
|
<div class="btn z_0">
|
|
<span>Z-</span>
|
|
<svg t="1747126605366" class="turn-bottom" viewBox="0 0 1024 1024" version="1.1"
|
|
xmlns="http://www.w3.org/2000/svg" p-id="1461" width="32" height="32">
|
|
<path
|
|
d="M311.432533 448.170667H163.84L510.293333 3.413333 856.746667 448.170667h-147.592534L760.832 1006.933333H259.754667z"
|
|
fill="#4076ff" p-id="1462"></path>
|
|
</svg>
|
|
</div>
|
|
<div class="btn x_0">
|
|
<span> X-</span>
|
|
<svg t="1747126605366" class="turn-up" viewBox="0 0 1024 1024" version="1.1"
|
|
xmlns="http://www.w3.org/2000/svg" p-id="1461" width="32" height="32">
|
|
<path
|
|
d="M311.432533 448.170667H163.84L510.293333 3.413333 856.746667 448.170667h-147.592534L760.832 1006.933333H259.754667z"
|
|
fill="#4076ff" p-id="1462"></path>
|
|
</svg>
|
|
</div>
|
|
<div class="btn z_1">
|
|
<span>Z+</span>
|
|
<svg t="1747126605366" class="turn-up" viewBox="0 0 1024 1024" version="1.1"
|
|
xmlns="http://www.w3.org/2000/svg" p-id="1461" width="32" height="32">
|
|
<path
|
|
d="M311.432533 448.170667H163.84L510.293333 3.413333 856.746667 448.170667h-147.592534L760.832 1006.933333H259.754667z"
|
|
fill="#4076ff" p-id="1462"></path>
|
|
</svg>
|
|
</div>
|
|
<div class="btn y_0">
|
|
<span>Y-</span>
|
|
<svg t="1747126605366" class="turn-left" viewBox="0 0 1024 1024" version="1.1"
|
|
xmlns="http://www.w3.org/2000/svg" p-id="1461" width="32" height="32">
|
|
<path
|
|
d="M311.432533 448.170667H163.84L510.293333 3.413333 856.746667 448.170667h-147.592534L760.832 1006.933333H259.754667z"
|
|
fill="#4076ff" p-id="1462"></path>
|
|
</svg>
|
|
</div>
|
|
<div class="btn"></div>
|
|
<div class="btn y_1">
|
|
<span>Y+</span>
|
|
<svg t="1747126605366" class="turn-right" viewBox="0 0 1024 1024" version="1.1"
|
|
xmlns="http://www.w3.org/2000/svg" p-id="1461" width="32" height="32">
|
|
<path
|
|
d="M311.432533 448.170667H163.84L510.293333 3.413333 856.746667 448.170667h-147.592534L760.832 1006.933333H259.754667z"
|
|
fill="#4076ff" p-id="1462"></path>
|
|
</svg>
|
|
</div>
|
|
<div class="btn"></div>
|
|
<div class="btn x_1">
|
|
<span>X+</span>
|
|
<svg t="1747126605366" class="turn-bottom" viewBox="0 0 1024 1024" version="1.1"
|
|
xmlns="http://www.w3.org/2000/svg" p-id="1461" width="32" height="32">
|
|
<path
|
|
d="M311.432533 448.170667H163.84L510.293333 3.413333 856.746667 448.170667h-147.592534L760.832 1006.933333H259.754667z"
|
|
fill="#4076ff" p-id="1462"></path>
|
|
</svg>
|
|
</div>
|
|
<div class="btn"></div>
|
|
|
|
</div>
|
|
<div>
|
|
<div class="mb10 action-label">参考坐标系</div>
|
|
<el-select
|
|
|
|
v-model="form.coordinate"
|
|
>
|
|
<el-option
|
|
v-for="item in coordinateList"
|
|
:key="item.value"
|
|
:label="item.label"
|
|
:value="item.value"
|
|
/>
|
|
</el-select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="bottom flex between">
|
|
<div class="left " style="width: 30%">
|
|
<div class="card" style="height: 100%">
|
|
<div class="title">机械臂位置姿态</div>
|
|
<div class="body">
|
|
<div class="flex mb10">
|
|
<div class="left flex1">
|
|
<div class="pos-title mb10">位置 ( m )</div>
|
|
<div class="mb10"> X: {{ form.x.toFixed(6) }}</div>
|
|
<div class="mb10"> Y: {{ form.y.toFixed(6) }}</div>
|
|
<div class="mb10"> Z: {{ form.z.toFixed(6) }}</div>
|
|
</div>
|
|
<div class="right flex1">
|
|
<div class="pos-title mb10">姿态 ( deg )</div>
|
|
<div class="mb10"> RX: {{ form.rx.toFixed(6) }}</div>
|
|
<div class="mb10"> RY: {{ form.ry.toFixed(6) }}</div>
|
|
<div class="mb10"> RZ: {{ form.rz.toFixed(6) }}</div>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<div class="mb10 action-label">目标</div>
|
|
<el-select
|
|
v-model="form.arm"
|
|
>
|
|
<el-option
|
|
v-for="item in armList"
|
|
:key="item.value"
|
|
:label="item.label"
|
|
:value="item.value"
|
|
/>
|
|
</el-select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="center " style="width: 30%">
|
|
<div class="card" style="height: 100%">
|
|
<div class="title">关节控制单位( deg )</div>
|
|
<div class="body">
|
|
<div class="mb10 action-label-pos">
|
|
<div class="action-label">关节1</div>
|
|
<el-input-number v-model="form.joint1" :step="0.000001" :precision="6"/>
|
|
</div>
|
|
<div class="mb10 action-label-pos">
|
|
<div class="action-label">关节2</div>
|
|
<el-input-number v-model="form.joint2" :step="0.000001" :precision="6"/>
|
|
</div>
|
|
<div class="mb10 action-label-pos">
|
|
<div class="action-label">关节3</div>
|
|
<el-input-number v-model="form.joint3" :step="0.000001" :precision="6"/>
|
|
</div>
|
|
<div class="mb10 action-label-pos">
|
|
<div class="action-label">关节4</div>
|
|
<el-input-number v-model="form.joint4" :step="0.000001" :precision="6"/>
|
|
</div>
|
|
<div class="mb10 action-label-pos">
|
|
<div class="action-label">关节5</div>
|
|
<el-input-number v-model="form.joint5" :step="0.000001" :precision="6"/>
|
|
</div>
|
|
<div class="mb10 action-label-pos">
|
|
<div class="action-label">关节6</div>
|
|
<el-input-number v-model="form.joint6" :step="0.000001" :precision="6"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="right " style="width: 38%">
|
|
<div class="card" style="height: 100%">
|
|
<div class="title">姿态控制</div>
|
|
<div class="body">
|
|
<div class="btn-wrap">
|
|
<div class="btn rz_0 c">
|
|
<span>Z-</span>
|
|
<svg t="1747126605366" class="turn-lb" viewBox="0 0 1024 1024" version="1.1"
|
|
xmlns="http://www.w3.org/2000/svg" p-id="1461" width="32" height="32">
|
|
<path
|
|
d="M311.432533 448.170667H163.84L510.293333 3.413333 856.746667 448.170667h-147.592534L760.832 1006.933333H259.754667z"
|
|
fill="#4076ff" p-id="1462"></path>
|
|
</svg>
|
|
</div>
|
|
<div class="btn rx_0 c">
|
|
<span>X-</span>
|
|
<svg t="1747126605366" class="turn-up" viewBox="0 0 1024 1024" version="1.1"
|
|
xmlns="http://www.w3.org/2000/svg" p-id="1461" width="32" height="32">
|
|
<path
|
|
d="M311.432533 448.170667H163.84L510.293333 3.413333 856.746667 448.170667h-147.592534L760.832 1006.933333H259.754667z"
|
|
fill="#4076ff" p-id="1462"></path>
|
|
</svg>
|
|
</div>
|
|
<div class="btn rz_1 c">
|
|
<span>Z+</span>
|
|
<svg t="1747126605366" class="turn-rb" viewBox="0 0 1024 1024" version="1.1"
|
|
xmlns="http://www.w3.org/2000/svg" p-id="1461" width="32" height="32">
|
|
<path
|
|
d="M311.432533 448.170667H163.84L510.293333 3.413333 856.746667 448.170667h-147.592534L760.832 1006.933333H259.754667z"
|
|
fill="#4076ff" p-id="1462"></path>
|
|
</svg>
|
|
</div>
|
|
<div class="btn ry_0 c">
|
|
<span> Y-</span>
|
|
<svg t="1747126605366" class="turn-left" viewBox="0 0 1024 1024" version="1.1"
|
|
xmlns="http://www.w3.org/2000/svg" p-id="1461" width="32" height="32">
|
|
<path
|
|
d="M311.432533 448.170667H163.84L510.293333 3.413333 856.746667 448.170667h-147.592534L760.832 1006.933333H259.754667z"
|
|
fill="#4076ff" p-id="1462"></path>
|
|
</svg>
|
|
</div>
|
|
<div class="btn"></div>
|
|
<div class="btn ry_1 c">
|
|
<span>Y+</span>
|
|
<svg t="1747126605366" class="turn-right" viewBox="0 0 1024 1024" version="1.1"
|
|
xmlns="http://www.w3.org/2000/svg" p-id="1461" width="32" height="32">
|
|
<path
|
|
d="M311.432533 448.170667H163.84L510.293333 3.413333 856.746667 448.170667h-147.592534L760.832 1006.933333H259.754667z"
|
|
fill="#4076ff" p-id="1462"></path>
|
|
</svg>
|
|
</div>
|
|
<div class="btn"></div>
|
|
<div class="btn rx_1 c">
|
|
<span> X+</span>
|
|
<svg t="1747126605366" class="turn-bottom" viewBox="0 0 1024 1024" version="1.1"
|
|
xmlns="http://www.w3.org/2000/svg" p-id="1461" width="32" height="32">
|
|
<path
|
|
d="M311.432533 448.170667H163.84L510.293333 3.413333 856.746667 448.170667h-147.592534L760.832 1006.933333H259.754667z"
|
|
fill="#4076ff" p-id="1462"></path>
|
|
</svg>
|
|
</div>
|
|
<div class="btn"></div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="actions flex align-center between">
|
|
<div class="left">
|
|
<el-button @click="handleResets('zero')">零位姿态</el-button>
|
|
<el-button @click="handleResets('init')">初始位姿</el-button>
|
|
</div>
|
|
<div class="center">
|
|
{{ time }}
|
|
</div>
|
|
<div class="right flex align-center">
|
|
<div style="width: 100px;user-select: none">速度</div>
|
|
<el-slider size="small" :show-tooltip="false" v-model="data.speed"/>
|
|
<div style="margin-left: 20px"> {{ data.speed }}%</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped lang="scss">
|
|
.mechanical-arm {
|
|
width: 100vw;
|
|
height: 100vh;
|
|
overflow: hidden;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: space-between;
|
|
|
|
.top {
|
|
padding: 10px 10px 0;
|
|
height: 48%;
|
|
|
|
.center {
|
|
display: flex;
|
|
flex-direction: column;
|
|
|
|
.card:last-child {
|
|
flex: 1;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
.bottom {
|
|
padding: 0 10px;
|
|
height: 44%;
|
|
|
|
|
|
}
|
|
|
|
.actions {
|
|
height: 6%;
|
|
box-shadow: 0 0 8px rgba(0, 0, 0, 0.2);
|
|
padding: 0 10px;
|
|
|
|
.right {
|
|
width: 300px;
|
|
|
|
}
|
|
|
|
.center {
|
|
user-select: none;
|
|
}
|
|
}
|
|
|
|
.action-label {
|
|
color: #606266;
|
|
font-size: 14px;
|
|
user-select: none;
|
|
}
|
|
|
|
.action-label-pos {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
|
|
.el-input-number {
|
|
width: calc(100% - 50px);
|
|
}
|
|
}
|
|
|
|
.card {
|
|
border-radius: 8px;
|
|
border: 1px solid #e4e7ed;
|
|
padding: 10px;
|
|
|
|
.title {
|
|
color: #333;
|
|
font-size: 14px;
|
|
margin-bottom: 10px;
|
|
font-weight: 900;
|
|
user-select: none;
|
|
}
|
|
|
|
.body {
|
|
height: calc(100% - 29px);
|
|
overflow-y: auto;
|
|
|
|
.pos-title {
|
|
user-select: none;
|
|
}
|
|
|
|
.btn-wrap {
|
|
height: 100%;
|
|
display: grid;
|
|
grid-template-columns: repeat(3, 1fr);
|
|
grid-template-rows: repeat(3, 1fr);
|
|
justify-items: center;
|
|
align-items: center;
|
|
|
|
.btn {
|
|
|
|
cursor: pointer;
|
|
|
|
span {
|
|
user-select: none;
|
|
}
|
|
|
|
svg {
|
|
width: 60px;
|
|
height: 60px;
|
|
transform-origin: center center;
|
|
|
|
}
|
|
}
|
|
|
|
.turn-lb {
|
|
transform: rotate(-135deg);
|
|
}
|
|
|
|
.turn-rb {
|
|
transform: rotate(135deg);
|
|
}
|
|
|
|
.turn-left {
|
|
transform: rotate(-90deg);
|
|
}
|
|
|
|
.turn-right {
|
|
transform: rotate(90deg);
|
|
}
|
|
|
|
.turn-bottom {
|
|
transform: rotate(-180deg);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
</style>
|