CMVR-IOT-UI/src/views/flow/components/ActionQueueSingleRun.vue

112 lines
4.8 KiB
Vue
Raw Normal View History

<template>
<el-form label-position="top">
<el-form-item label="机器人" required>
<el-select v-model="robotId" filterable style="width: 100%" @change="loadDevices">
<el-option v-for="item in robotOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="队列总超时(毫秒)" required>
<el-input-number v-model="totalTimeoutMs" :min="1" :max="2147483647" style="width: 100%" />
</el-form-item>
<el-form-item label="动作标识(可选)">
<el-input v-model="actionId" placeholder="留空时自动生成" />
</el-form-item>
<div v-for="(step, index) in steps" :key="step.stepId || index" class="single-queue-step">
<div class="single-queue-step__title">步骤 {{ index + 1 }} · {{ actionName(step.type) }}</div>
<el-form-item v-if="deviceKind(step.type)" label="执行设备" required>
<el-select v-model="step.deviceId" filterable :loading="deviceLoading" style="width: 100%">
<el-option
v-for="device in devicesForStep(step)"
:key="device.deviceId"
:label="device.deviceName && device.deviceName !== device.deviceId ? `${device.deviceName} (${device.deviceId})` : device.deviceId"
:value="device.deviceId"
/>
</el-select>
</el-form-item>
<div class="single-queue-step__params">{{ stepSummary(step) }}</div>
</div>
</el-form>
</template>
<script setup>
import { ref, watch } from 'vue'
import { ElMessage } from 'element-plus'
import { getRobotDevicesByRobotId } from '@/api/inspection/robot'
import { DEVICE_KIND } from '@/utils/robotDevice'
const props = defineProps({
nodeParams: { type: Array, default: () => [] },
robotOptions: { type: Array, default: () => [] },
initialRobotId: { type: String, default: '' },
})
const labels = {
DELAY: '等待', ARM_MOVE_J: '机械臂关节运动', ARM_MOVE_L: '机械臂直线运动',
AGV_NAVIGATE_TO_POSE: '底盘导航到坐标', AGV_NAVIGATE_TO_STATION: '底盘导航到站点', AGV_FOLLOW_PATH: '底盘按路径导航',
}
const robotId = ref('')
const actionId = ref('')
const totalTimeoutMs = ref(60000)
const steps = ref([])
const devices = ref([])
const deviceLoading = ref(false)
const actionName = (type) => labels[type] || type
const deviceKind = (type) => type?.startsWith('ARM_') ? DEVICE_KIND.ARM : (type?.startsWith('AGV_') ? DEVICE_KIND.AGV : null)
const devicesForStep = (step) => devices.value.filter((device) => Number(device.deviceKind) === deviceKind(step.type))
const stepSummary = (step) => {
if (step.type === 'DELAY') return `等待 ${step.params?.durationMs || 0} 毫秒`
if (step.type === 'AGV_NAVIGATE_TO_STATION') return `站点 ${step.params?.stationId || '-'}`
return JSON.stringify(step.params || {})
}
const initData = async () => {
const values = Object.fromEntries(props.nodeParams.map((item) => [item.name, item.input]))
actionId.value = values.actionId || ''
totalTimeoutMs.value = Number(values.totalTimeoutMs || 60000)
steps.value = JSON.parse(JSON.stringify(Array.isArray(values.steps) ? values.steps : []))
robotId.value = props.initialRobotId || ''
if (robotId.value) await loadDevices(robotId.value)
}
const loadDevices = async (value) => {
devices.value = []
steps.value.forEach((step) => { step.deviceId = '' })
if (!value) return
deviceLoading.value = true
try {
const response = await getRobotDevicesByRobotId(value)
devices.value = Array.isArray(response.data) ? response.data : []
steps.value.forEach((step) => {
const options = devicesForStep(step)
if (options.length) step.deviceId = options[0].deviceId
})
} finally {
deviceLoading.value = false
}
}
const validate = () => {
if (!robotId.value) return ElMessage.warning('请选择机器人'), false
if (!steps.value.length) return ElMessage.warning('动作队列不能为空'), false
for (let index = 0; index < steps.value.length; index += 1) {
const step = steps.value[index]
if (deviceKind(step.type) && !step.deviceId) {
ElMessage.warning(`步骤 ${index + 1} 没有可用的执行设备`)
return false
}
}
return true
}
const getPayload = () => ({ robotId: robotId.value, actionId: actionId.value, totalTimeoutMs: totalTimeoutMs.value, steps: steps.value })
const getRobotId = () => robotId.value
watch(() => [props.nodeParams, props.initialRobotId], initData, { immediate: true, deep: true })
defineExpose({ validate, getPayload, getRobotId })
</script>
<style scoped>
.single-queue-step { margin-bottom: 10px; padding: 12px; border: 1px solid #dfe4ec; border-radius: 8px; }
.single-queue-step__title { margin-bottom: 10px; color: #1f2937; font-size: 13px; font-weight: 650; }
.single-queue-step__params { overflow: hidden; color: #7a8494; font-size: 12px; line-height: 18px; text-overflow: ellipsis; white-space: nowrap; }
</style>