From c54f1da1266dec5ee8bb57f7a8a08a2efc23c437 Mon Sep 17 00:00:00 2001 From: lixiaolong <702156524@qq.com> Date: Wed, 12 Aug 2026 09:31:45 +0800 Subject: [PATCH] =?UTF-8?q?feat(flow):=20=E6=96=B0=E5=A2=9E=E5=B7=A5?= =?UTF-8?q?=E4=BD=9C=E6=B5=81=E6=89=A7=E8=A1=8C=E8=B5=84=E6=BA=90=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加了 flowExecution 工具模块用于处理工作流运行时资源配置 - 实现了工作流角色定义的加载、合并和验证功能 - 集成了机器人和设备分配逻辑到任务执行界面 - 更新了 RuntimeAssignmentsEditor 组件以支持动态设备分配 - 移除了静态机器人选择,改为基于流程角色的动态分配 - 优化了工作流资源定义合并逻辑,支持插槽复用配置 - 增强了设备冲突检测和错误处理机制 - 统一了多个视图中的工作流执行资源配置流程 --- src/utils/flowExecution.js | 82 ++++++++++++++ src/utils/flowResources.js | 7 +- .../components/RuntimeAssignmentsEditor.vue | 101 ++++++++++++++---- src/views/inspection/runTask/index.vue | 88 +++++---------- .../intelligenceTest/runningTask/index.vue | 30 +++--- src/views/is/project/evaluate/Run.vue | 23 ++-- src/views/test/configs/Run.vue | 22 ++-- src/views/test/configs/index.vue | 62 ----------- src/views/vi/testProject/evaluate/Run.vue | 23 ++-- 9 files changed, 251 insertions(+), 187 deletions(-) create mode 100644 src/utils/flowExecution.js diff --git a/src/utils/flowExecution.js b/src/utils/flowExecution.js new file mode 100644 index 0000000..44be2b6 --- /dev/null +++ b/src/utils/flowExecution.js @@ -0,0 +1,82 @@ +import { getFlowParamsApi } from '@/api/device/terminal' +import { getDetect } from '@/api/test/detect' +import { + getResourceRoles, + mergeResourceRoleDefinitions, + normalizeFlowResourceSchema, + validateFlowResources, +} from '@/utils/flowResources' + +const clone = (value) => JSON.parse(JSON.stringify(value || [])) + +const parseFlowData = (itemId, flowData) => { + if (!flowData) throw new Error(`流程项[${itemId}]还没有配置工作流`) + try { + const graph = typeof flowData === 'string' ? JSON.parse(flowData) : flowData + const normalizedGraph = normalizeFlowResourceSchema(graph) + const validation = validateFlowResources(normalizedGraph) + if (validation.errors.length) { + throw new Error(validation.errors[0].message) + } + return validation.graph + } catch (error) { + throw new Error(`流程项[${itemId}]不可执行:${error.message || '工作流数据无法解析'}`) + } +} + +const assertCompatibleDefinitions = (roleGroups) => { + const rolesByKey = new Map() + roleGroups.flat().forEach((role) => { + const existing = rolesByKey.get(role.roleKey) + if (!existing) { + rolesByKey.set(role.roleKey, clone([role])[0]) + return + } + const slotsByKey = new Map((existing.deviceSlots || []).map((slot) => [slot.slotKey, slot])) + ;(role.deviceSlots || []).forEach((slot) => { + const existingSlot = slotsByKey.get(slot.slotKey) + if (existingSlot && String(existingSlot.deviceKind ?? '') !== String(slot.deviceKind ?? '')) { + throw new Error(`机器人角色“${existing.displayName}”的设备用途“${slot.displayName}”在不同流程中类型不一致`) + } + if (!existingSlot) existing.deviceSlots.push(clone([slot])[0]) + }) + }) +} + +export const loadFlowItemRuntimeRoles = async (items = []) => { + if (items.some((item) => !item?.itemId)) throw new Error('任务编排中存在缺少流程项ID的数据') + const uniqueItems = Array.from(new Map( + items.filter((item) => item?.itemId).map((item) => [item.itemId, item]), + ).values()) + if (!uniqueItems.length) throw new Error('当前任务没有编排任何流程项') + + const roleGroups = await Promise.all(uniqueItems.map(async (item) => { + const response = await getDetect(item.itemId) + const graph = parseFlowData(item.itemName || item.itemId, response.data?.flowData) + return getResourceRoles(graph) + })) + assertCompatibleDefinitions(roleGroups) + return mergeResourceRoleDefinitions(roleGroups) +} + +export const loadTaskRuntimeRoles = async (taskId) => { + if (!taskId) throw new Error('任务没有关联流程编排') + const response = await getFlowParamsApi(taskId) + return loadFlowItemRuntimeRoles(response.rows || []) +} + +export const createRuntimeAssignments = (roles = [], primaryRobotId = '') => roles.map((role, index) => ({ + roleKey: role.roleKey, + members: [{ + memberKey: `${role.roleKey}_1`, + robotId: index === 0 ? primaryRobotId || role.legacyRobotId || '' : role.legacyRobotId || '', + devices: Object.fromEntries((role.deviceSlots || []).map((slot) => [ + slot.slotKey, + slot.legacyDeviceId || '', + ])), + }], +})) + +export const firstAssignedRobotId = (assignments = []) => ( + clone(assignments).flatMap((item) => item.members || []).find((member) => member.robotId)?.robotId || '' +) diff --git a/src/utils/flowResources.js b/src/utils/flowResources.js index dedd8c3..dceddc1 100644 --- a/src/utils/flowResources.js +++ b/src/utils/flowResources.js @@ -313,10 +313,15 @@ export const mergeResourceRoleDefinitions = (roleGroups = []) => { merged.push(clone(sourceRole)) return } + role.required = role.required !== false || sourceRole.required !== false ;(sourceRole.deviceSlots || []).forEach((sourceSlot) => { - if (!role.deviceSlots.some((slot) => slot.slotKey === sourceSlot.slotKey)) { + const slot = role.deviceSlots.find((item) => item.slotKey === sourceSlot.slotKey) + if (!slot) { role.deviceSlots.push(clone(sourceSlot)) + return } + slot.required = slot.required !== false || sourceSlot.required !== false + slot.allowReuse = slot.allowReuse === true && sourceSlot.allowReuse === true }) }) return merged diff --git a/src/views/flow/components/RuntimeAssignmentsEditor.vue b/src/views/flow/components/RuntimeAssignmentsEditor.vue index 42c418e..3abd07e 100644 --- a/src/views/flow/components/RuntimeAssignmentsEditor.vue +++ b/src/views/flow/components/RuntimeAssignmentsEditor.vue @@ -9,13 +9,14 @@ - + @@ -36,18 +37,21 @@ + diff --git a/src/views/inspection/runTask/index.vue b/src/views/inspection/runTask/index.vue index 55f7ece..61651ea 100644 --- a/src/views/inspection/runTask/index.vue +++ b/src/views/inspection/runTask/index.vue @@ -20,7 +20,7 @@ @click="removeTask(task)" /> -
机器人:{{ task.robotName }}
+
主要机器人:{{ task.robotName || '执行时安排' }}
@@ -44,7 +44,7 @@
ID: {{ runTask.taskId }}
-
机器人:{{ runTask.robotName }}
+
主要机器人:{{ runTask.robotName || '按流程角色安排' }}
开始时间: {{ formatDate(runTask.startTime) }}
@@ -84,7 +84,9 @@ - + + +