doc: 调价注释

This commit is contained in:
zhanghao 2026-08-13 10:23:55 +08:00
parent 3ccf3e2c00
commit 0780d298c6
2 changed files with 95 additions and 8 deletions

View File

@ -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

View File

@ -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>