Merge remote-tracking branch 'origin/lxl-dev' into lxl-dev
This commit is contained in:
commit
d3f344338a
@ -1,3 +1,7 @@
|
||||
/**
|
||||
* 平台设备类型枚举,供流程资源匹配、机器人设备筛选和界面展示共同使用。
|
||||
* Object.freeze 防止运行期间被改写,数值与后端 deviceKind 协议保持一致。
|
||||
*/
|
||||
export const DEVICE_KIND = Object.freeze({
|
||||
AGV: 1,
|
||||
ARM: 2,
|
||||
@ -8,6 +12,10 @@ export const DEVICE_KIND = Object.freeze({
|
||||
SPEAKER: 13,
|
||||
})
|
||||
|
||||
/**
|
||||
* 流程动作到所需设备类型的全局映射。
|
||||
* 作用范围为所有根据 action 判断执行设备的流程模块;未列出的动作视为没有固定设备类型。
|
||||
*/
|
||||
export const DEVICE_KIND_BY_ACTION = Object.freeze({
|
||||
AGV_MOVE_TO_POINT: DEVICE_KIND.AGV,
|
||||
AGV_MOVE_TO_STATION: DEVICE_KIND.AGV,
|
||||
@ -28,8 +36,16 @@ export const DEVICE_KIND_BY_ACTION = Object.freeze({
|
||||
VI_PLAY_CORPUS: DEVICE_KIND.SPEAKER,
|
||||
})
|
||||
|
||||
/**
|
||||
* 查询某个流程动作所需的设备类型。
|
||||
* 直接读取统一映射;未知或无需固定设备的动作返回 undefined,由调用方决定降级策略。
|
||||
*/
|
||||
export const getDeviceKindForAction = (action) => DEVICE_KIND_BY_ACTION[action]
|
||||
|
||||
/**
|
||||
* 将机器人设备接口数据转换为 Element Plus 下拉框选项,供全局设备选择器复用。
|
||||
* 每项以 deviceId 作为值;设备名有效且不同于 ID 时展示“名称 (ID)”,否则仅展示 ID。
|
||||
*/
|
||||
export const toDeviceOptions = (devices = []) => devices.map((device) => ({
|
||||
value: device.deviceId,
|
||||
label: device.deviceName && device.deviceName !== device.deviceId
|
||||
|
||||
@ -15,12 +15,9 @@
|
||||
<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"
|
||||
<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"
|
||||
/>
|
||||
:value="device.deviceId" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<div class="single-queue-step__params">{{ stepSummary(step) }}</div>
|
||||
@ -34,31 +31,67 @@ import { ElMessage } from 'element-plus'
|
||||
import { getRobotDevicesByRobotId } from '@/api/inspection/robot'
|
||||
import { DEVICE_KIND } from '@/utils/robotDevice'
|
||||
|
||||
/**
|
||||
* 组件输入配置,仅在当前单次动作队列运行表单内使用。
|
||||
* nodeParams 提供流程节点参数,robotOptions 提供可选机器人,initialRobotId 用于初始化默认机器人。
|
||||
*/
|
||||
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: '底盘按路径导航',
|
||||
}
|
||||
/** 当前选中的机器人 ID,作用于设备查询、校验和最终运行请求。 */
|
||||
const robotId = ref('')
|
||||
/** 本次动作队列标识;为空时由服务端自动生成,仅随当前运行请求提交。 */
|
||||
const actionId = ref('')
|
||||
/** 整个动作队列的超时时间(毫秒),作用于当前运行请求。 */
|
||||
const totalTimeoutMs = ref(60000)
|
||||
/** 当前节点解析出的动作步骤副本,组件内可补充 deviceId,不回写原始 props。 */
|
||||
const steps = ref([])
|
||||
/** 当前机器人挂载的设备列表,用于为各动作步骤筛选可执行设备。 */
|
||||
const devices = ref([])
|
||||
/** 设备接口请求状态,仅控制当前组件设备选择框的加载效果。 */
|
||||
const deviceLoading = ref(false)
|
||||
|
||||
/**
|
||||
* 获取动作类型的展示名称。
|
||||
* 先查询本地中文映射,未配置的类型直接显示原值,避免未知动作显示为空。
|
||||
*/
|
||||
const actionName = (type) => labels[type] || type
|
||||
|
||||
/**
|
||||
* 根据动作名前缀判断所需设备类型。
|
||||
* 当前组件只处理机械臂和底盘队列动作;等待等无设备动作返回 null。
|
||||
*/
|
||||
const deviceKind = (type) => type?.startsWith('ARM_') ? DEVICE_KIND.ARM : (type?.startsWith('AGV_') ? DEVICE_KIND.AGV : null)
|
||||
|
||||
/**
|
||||
* 筛选单个步骤可使用的设备,范围限定为当前所选机器人的在线设备结果。
|
||||
* 将接口 deviceKind 与动作要求统一转为数值后比较,兼容字符串和数字类型。
|
||||
*/
|
||||
const devicesForStep = (step) => devices.value.filter((device) => Number(device.deviceKind) === deviceKind(step.type))
|
||||
|
||||
/**
|
||||
* 生成步骤参数摘要,仅用于当前组件的只读预览。
|
||||
* 等待和站点导航使用易读文案,其余动作保留完整 JSON 信息。
|
||||
*/
|
||||
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 || {})
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用节点参数重新初始化表单,作用于首次渲染及输入属性变化。
|
||||
* 步骤:1. 将参数数组转成按名称索引的对象;2. 设置基本字段并深拷贝步骤;
|
||||
* 3. 应用默认机器人;4. 若存在机器人,则加载设备并为步骤建立默认设备选择。
|
||||
*/
|
||||
const initData = async () => {
|
||||
const values = Object.fromEntries(props.nodeParams.map((item) => [item.name, item.input]))
|
||||
actionId.value = values.actionId || ''
|
||||
@ -68,7 +101,13 @@ const initData = async () => {
|
||||
if (robotId.value) await loadDevices(robotId.value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载指定机器人的设备并重建各步骤设备选择,影响当前组件的 devices 和 steps。
|
||||
* 切换机器人后旧 deviceId 已不再可靠,因此先清空设备及所有步骤选择;无机器人时直接结束。
|
||||
* 接口返回后逐步按动作类型筛选设备,并默认选择第一个匹配项;finally 保证加载状态复位。
|
||||
*/
|
||||
const loadDevices = async (value) => {
|
||||
// 机器人发生变化时,旧设备可能属于另一台机器人,必须整体清除以避免误提交。
|
||||
devices.value = []
|
||||
steps.value.forEach((step) => { step.deviceId = '' })
|
||||
if (!value) return
|
||||
@ -76,6 +115,7 @@ const loadDevices = async (value) => {
|
||||
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
|
||||
@ -85,9 +125,14 @@ const loadDevices = async (value) => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验当前动作队列是否具备运行条件,供父组件在提交前调用。
|
||||
* 依次检查机器人、队列非空及每个需要设备的步骤;首次失败即提示并终止,全部通过返回 true。
|
||||
*/
|
||||
const validate = () => {
|
||||
if (!robotId.value) return ElMessage.warning('请选择机器人'), false
|
||||
if (!steps.value.length) return ElMessage.warning('动作队列不能为空'), false
|
||||
// 等待类步骤无需设备;机械臂或底盘步骤必须存在与当前机器人匹配的 deviceId。
|
||||
for (let index = 0; index < steps.value.length; index += 1) {
|
||||
const step = steps.value[index]
|
||||
if (deviceKind(step.type) && !step.deviceId) {
|
||||
@ -97,15 +142,41 @@ const validate = () => {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/** 汇总当前表单数据为运行接口载荷;返回范围包含机器人、队列配置和全部步骤。 */
|
||||
const getPayload = () => ({ robotId: robotId.value, actionId: actionId.value, totalTimeoutMs: totalTimeoutMs.value, steps: steps.value })
|
||||
|
||||
/** 返回当前选中的机器人 ID,供父组件处理兼容字段或运行上下文。 */
|
||||
const getRobotId = () => robotId.value
|
||||
|
||||
/** 监听节点配置和默认机器人变化,深度变化时重建当前组件的表单及设备选项。 */
|
||||
watch(() => [props.nodeParams, props.initialRobotId], initData, { immediate: true, deep: true })
|
||||
|
||||
/** 向父组件开放提交前校验、载荷读取和机器人 ID 读取能力。 */
|
||||
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; }
|
||||
.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>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user