forked from SmartBench/CMVR-IOT-UI
feat(flow): 新增工作流执行资源配置功能
- 添加了 flowExecution 工具模块用于处理工作流运行时资源配置 - 实现了工作流角色定义的加载、合并和验证功能 - 集成了机器人和设备分配逻辑到任务执行界面 - 更新了 RuntimeAssignmentsEditor 组件以支持动态设备分配 - 移除了静态机器人选择,改为基于流程角色的动态分配 - 优化了工作流资源定义合并逻辑,支持插槽复用配置 - 增强了设备冲突检测和错误处理机制 - 统一了多个视图中的工作流执行资源配置流程
This commit is contained in:
parent
e3a2761683
commit
c54f1da126
82
src/utils/flowExecution.js
Normal file
82
src/utils/flowExecution.js
Normal file
@ -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 || ''
|
||||
)
|
||||
@ -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
|
||||
|
||||
@ -9,13 +9,14 @@
|
||||
<el-form-item label="执行机器人" :required="role.required">
|
||||
<el-select
|
||||
:model-value="memberOf(role).robotId"
|
||||
clearable
|
||||
filterable
|
||||
:loading="robotLoading"
|
||||
placeholder="请选择机器人"
|
||||
style="width: 100%"
|
||||
@change="(robotId) => changeRobot(role, robotId)"
|
||||
>
|
||||
<el-option v-for="item in robotOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
<el-option v-for="item in robotOptionsOf(role)" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-for="slot in role.deviceSlots" :key="slot.slotKey" :label="slot.displayName" :required="slot.required">
|
||||
@ -36,18 +37,21 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<el-alert v-if="loadError" :title="loadError" type="error" :closable="false" show-icon />
|
||||
<el-empty v-if="!roles.length" :image-size="52" description="当前任务不需要机器人" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, watch } from 'vue'
|
||||
import { computed, reactive, ref, watch } from 'vue'
|
||||
import { getRobotList, getRobotDevicesByRobotId } from '@/api/inspection/robot'
|
||||
import { toDeviceOptions } from '@/utils/robotDevice'
|
||||
|
||||
const props = defineProps({
|
||||
roles: { type: Array, default: () => [] },
|
||||
modelValue: { type: Array, default: () => [] },
|
||||
robotQuery: { type: Object, default: () => ({}) },
|
||||
requiredMapId: { type: [String, Number], default: '' },
|
||||
})
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
@ -56,6 +60,9 @@ const robotOptions = ref([])
|
||||
const devicesByRole = reactive({})
|
||||
const robotLoading = ref(false)
|
||||
const loadingRoleKey = ref('')
|
||||
const robotLoadError = ref('')
|
||||
const deviceLoadErrors = reactive({})
|
||||
const loadError = computed(() => robotLoadError.value || Object.values(deviceLoadErrors).find(Boolean) || '')
|
||||
const clone = (value) => JSON.parse(JSON.stringify(value || []))
|
||||
|
||||
const assignmentOf = (role) => assignments.value.find((item) => item.roleKey === role.roleKey)
|
||||
@ -64,35 +71,69 @@ const emitValue = () => emit('update:modelValue', clone(assignments.value))
|
||||
|
||||
const loadRobots = async () => {
|
||||
robotLoading.value = true
|
||||
robotLoadError.value = ''
|
||||
try {
|
||||
const response = await getRobotList({ pageNum: 1, pageSize: 1000 })
|
||||
const response = await getRobotList({
|
||||
pageNum: 1,
|
||||
pageSize: 1000,
|
||||
connectStatus: '1',
|
||||
...props.robotQuery,
|
||||
})
|
||||
robotOptions.value = (response.rows || []).map((robot) => ({
|
||||
label: `${robot.robotName || robot.robotId} (${robot.robotId})`,
|
||||
value: robot.robotId,
|
||||
currentMapId: robot.currentMapId,
|
||||
}))
|
||||
} catch (error) {
|
||||
robotOptions.value = []
|
||||
robotLoadError.value = '机器人列表加载失败,请稍后重试'
|
||||
} finally {
|
||||
robotLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const optionsOf = (role, slot) => toDeviceOptions((devicesByRole[role.roleKey] || []).filter((device) => (
|
||||
slot.deviceKind == null || Number(device.deviceKind) === Number(slot.deviceKind)
|
||||
)))
|
||||
const robotOptionsOf = (role) => {
|
||||
const requiresTaskMap = (role.deviceSlots || []).some((slot) => Number(slot.deviceKind) === 1)
|
||||
if (!props.requiredMapId || !requiresTaskMap) return robotOptions.value
|
||||
return robotOptions.value.filter((robot) => String(robot.currentMapId || '') === String(props.requiredMapId))
|
||||
}
|
||||
|
||||
const optionsOf = (role, slot) => {
|
||||
const member = memberOf(role)
|
||||
const unavailableDeviceIds = new Set((role.deviceSlots || [])
|
||||
.filter((otherSlot) => (
|
||||
otherSlot.slotKey !== slot.slotKey
|
||||
&& (!slot.allowReuse || !otherSlot.allowReuse)
|
||||
))
|
||||
.map((otherSlot) => member.devices[otherSlot.slotKey])
|
||||
.filter(Boolean))
|
||||
return toDeviceOptions((devicesByRole[role.roleKey] || []).filter((device) => (
|
||||
(slot.deviceKind == null || Number(device.deviceKind) === Number(slot.deviceKind))
|
||||
&& (!unavailableDeviceIds.has(device.deviceId) || member.devices[slot.slotKey] === device.deviceId)
|
||||
)))
|
||||
}
|
||||
|
||||
const loadDevices = async (role, preserve = true) => {
|
||||
const member = memberOf(role)
|
||||
if (!member.robotId) return
|
||||
if (!member.robotId) {
|
||||
devicesByRole[role.roleKey] = []
|
||||
return
|
||||
}
|
||||
loadingRoleKey.value = role.roleKey
|
||||
delete deviceLoadErrors[role.roleKey]
|
||||
try {
|
||||
const response = await getRobotDevicesByRobotId(member.robotId)
|
||||
devicesByRole[role.roleKey] = Array.isArray(response.data) ? response.data : []
|
||||
role.deviceSlots.forEach((slot) => {
|
||||
;(role.deviceSlots || []).forEach((slot) => {
|
||||
const options = optionsOf(role, slot)
|
||||
if (!preserve || !options.some((option) => option.value === member.devices[slot.slotKey])) {
|
||||
member.devices[slot.slotKey] = options.length === 1 ? options[0].value : ''
|
||||
}
|
||||
})
|
||||
emitValue()
|
||||
} catch (error) {
|
||||
devicesByRole[role.roleKey] = []
|
||||
deviceLoadErrors[role.roleKey] = `“${role.displayName}”的设备列表加载失败,请重新选择机器人`
|
||||
} finally {
|
||||
loadingRoleKey.value = ''
|
||||
}
|
||||
@ -101,37 +142,61 @@ const loadDevices = async (role, preserve = true) => {
|
||||
const changeRobot = async (role, robotId) => {
|
||||
const member = memberOf(role)
|
||||
member.robotId = robotId
|
||||
member.devices = Object.fromEntries(role.deviceSlots.map((slot) => [slot.slotKey, '']))
|
||||
member.devices = Object.fromEntries((role.deviceSlots || []).map((slot) => [slot.slotKey, '']))
|
||||
await loadDevices(role, false)
|
||||
}
|
||||
|
||||
const initialize = async () => {
|
||||
Object.keys(deviceLoadErrors).forEach((roleKey) => delete deviceLoadErrors[roleKey])
|
||||
const existing = clone(props.modelValue)
|
||||
assignments.value = props.roles.map((role) => existing.find((item) => item.roleKey === role.roleKey) || ({
|
||||
roleKey: role.roleKey,
|
||||
members: [{
|
||||
memberKey: `${role.roleKey}_1`,
|
||||
robotId: role.legacyRobotId || '',
|
||||
devices: Object.fromEntries((role.deviceSlots || []).map((slot) => [slot.slotKey, slot.legacyDeviceId || ''])),
|
||||
}],
|
||||
}))
|
||||
assignments.value = props.roles.map((role) => {
|
||||
const existingAssignment = existing.find((item) => item.roleKey === role.roleKey)
|
||||
const existingMember = existingAssignment?.members?.[0]
|
||||
return {
|
||||
roleKey: role.roleKey,
|
||||
members: [{
|
||||
...existingMember,
|
||||
memberKey: `${role.roleKey}_1`,
|
||||
robotId: existingMember?.robotId || role.legacyRobotId || '',
|
||||
devices: Object.fromEntries((role.deviceSlots || []).map((slot) => [
|
||||
slot.slotKey,
|
||||
existingMember?.devices?.[slot.slotKey] || slot.legacyDeviceId || '',
|
||||
])),
|
||||
}],
|
||||
}
|
||||
})
|
||||
emitValue()
|
||||
await loadRobots()
|
||||
await Promise.all(props.roles.filter((role) => memberOf(role).robotId).map((role) => loadDevices(role)))
|
||||
}
|
||||
|
||||
const validate = () => {
|
||||
const physicalDeviceUses = new Map()
|
||||
for (const role of props.roles) {
|
||||
const member = memberOf(role)
|
||||
if (role.required && !member.robotId) return `请为“${role.displayName}”选择机器人`
|
||||
if (!member.robotId) continue
|
||||
for (const slot of role.deviceSlots || []) {
|
||||
if (slot.required && !member.devices[slot.slotKey]) return `请为“${role.displayName}”选择“${slot.displayName}”`
|
||||
const deviceId = member.devices[slot.slotKey]
|
||||
if (!deviceId) continue
|
||||
const physicalKey = `${member.robotId}:${deviceId}`
|
||||
const existingUse = physicalDeviceUses.get(physicalKey)
|
||||
if (existingUse && (!slot.allowReuse || !existingUse.allowReuse)) {
|
||||
return `“${existingUse.roleName} / ${existingUse.slotName}”和“${role.displayName} / ${slot.displayName}”不能使用同一个设备`
|
||||
}
|
||||
physicalDeviceUses.set(physicalKey, {
|
||||
allowReuse: slot.allowReuse,
|
||||
roleName: role.displayName,
|
||||
slotName: slot.displayName,
|
||||
})
|
||||
}
|
||||
}
|
||||
if (loadError.value) return loadError.value
|
||||
return ''
|
||||
}
|
||||
|
||||
watch(() => props.roles, initialize, { immediate: true, deep: true })
|
||||
watch([() => props.roles, () => props.robotQuery, () => props.requiredMapId], initialize, { immediate: true, deep: true })
|
||||
defineExpose({ validate })
|
||||
</script>
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
@click="removeTask(task)" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="robot">机器人:{{ task.robotName }}</div>
|
||||
<div class="robot">主要机器人:{{ task.robotName || '执行时安排' }}</div>
|
||||
<el-progress :color="customColorMethod"
|
||||
:percentage="parseFloat(taskRunningInfo[task.id]?.progress?.toFixed(2)) || 0" />
|
||||
</div>
|
||||
@ -44,7 +44,7 @@
|
||||
<div class="id">ID: {{ runTask.taskId }}</div>
|
||||
</div>
|
||||
<div class="robot">
|
||||
<div>机器人:{{ runTask.robotName }}</div>
|
||||
<div>主要机器人:{{ runTask.robotName || '按流程角色安排' }}</div>
|
||||
<div>开始时间: {{ formatDate(runTask.startTime) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -84,7 +84,9 @@
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="机器人" prop="robotName" show-overflow-tooltip />
|
||||
<el-table-column align="center" label="主要机器人" prop="robotName" show-overflow-tooltip>
|
||||
<template #default="scope">{{ scope.row.robotName || '按流程角色安排' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="开始时间" prop="startTime">
|
||||
<template #default="scope">
|
||||
{{ formatDate(scope.row.startTime) }}
|
||||
@ -117,17 +119,10 @@
|
||||
<el-dialog title="添加任务" v-model="open" width="500px" append-to-body>
|
||||
<el-form ref="runTaskRef" :model="form" :rules="rules" label-width="100px">
|
||||
<el-form-item label="选择任务" prop="taskId">
|
||||
<el-select v-model="form.taskId" placeholder="请选择任务" clearable style="width: 100%"
|
||||
@change="taskChange">
|
||||
<el-select v-model="form.taskId" placeholder="请选择任务" clearable style="width: 100%">
|
||||
<el-option v-for="task in taskList" :key="task.id" :label="task.taskName" :value="task.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="选择机器人" prop="robotId">
|
||||
<el-select v-model="form.robotId" placeholder="请选择机器人" clearable style="width: 100%">
|
||||
<el-option v-for="robot in robotList" :key="robot.id" :label="robot.robotName"
|
||||
:value="robot.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input type="textarea" v-model="form.remark" />
|
||||
</el-form-item>
|
||||
@ -145,7 +140,12 @@
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="本次执行安排" v-model="startDialogVisible" width="560px" append-to-body>
|
||||
<RuntimeAssignmentsEditor ref="runtimeEditorRef" v-model="runtimeAssignments" :roles="runtimeRoles" />
|
||||
<RuntimeAssignmentsEditor
|
||||
ref="runtimeEditorRef"
|
||||
v-model="runtimeAssignments"
|
||||
:roles="runtimeRoles"
|
||||
:required-map-id="runtimeMapId"
|
||||
/>
|
||||
<template #footer>
|
||||
<el-button @click="startDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="startSubmitting" @click="confirmStartTask">开始执行</el-button>
|
||||
@ -159,16 +159,17 @@ import taskSteps from "./task-steps.vue";
|
||||
import taskLog from "./task-log.vue";
|
||||
import { useContainerHeight } from "@/hooks/tableHeight";
|
||||
import { getTaskList, getWaypointByTaskId } from "@/api/inspection/task";
|
||||
import { getRobotList } from "@/api/inspection/robot";
|
||||
import { onMounted, onUnmounted } from "vue";
|
||||
import { addRunTask, getRunTaskList, startRunTask, pauseRunTask, stopRunTask, resumeRunTask, deleteRunTask, getTaskLogListApi } from "@/api/inspection/runTask";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { formatDate } from "@/utils/index";
|
||||
import { debounce } from 'lodash'; // 引入 lodash 的防抖函数
|
||||
import { getFlowParamsApi } from '@/api/device/terminal'
|
||||
import { getDetect } from '@/api/test/detect'
|
||||
import RuntimeAssignmentsEditor from '@/views/flow/components/RuntimeAssignmentsEditor.vue'
|
||||
import { getResourceRoles, mergeResourceRoleDefinitions, normalizeFlowResourceSchema } from '@/utils/flowResources'
|
||||
import {
|
||||
createRuntimeAssignments,
|
||||
firstAssignedRobotId,
|
||||
loadTaskRuntimeRoles,
|
||||
} from '@/utils/flowExecution'
|
||||
|
||||
const topContainerRef = ref();
|
||||
const containerHeight = useContainerHeight(topContainerRef);
|
||||
@ -179,6 +180,7 @@ const pendingStartTask = ref(null)
|
||||
const runtimeRoles = ref([])
|
||||
const runtimeAssignments = ref([])
|
||||
const runtimeEditorRef = ref()
|
||||
const runtimeMapId = ref('')
|
||||
|
||||
const taskStatusMap = {
|
||||
'0': '已完成',
|
||||
@ -191,17 +193,13 @@ const taskStatusMap = {
|
||||
|
||||
const form = reactive({
|
||||
taskId: null,
|
||||
robotId: null,
|
||||
});
|
||||
|
||||
const rules = reactive({
|
||||
taskId: [{ required: true, message: '请选择任务', trigger: 'change' }],
|
||||
robotId: [{ required: true, message: '请选择机器人', trigger: 'change' }],
|
||||
});
|
||||
|
||||
const taskList = ref([]);
|
||||
const robotList = ref([]);
|
||||
|
||||
const runTaskList = ref([]);
|
||||
|
||||
const getTaskData = () => {
|
||||
@ -213,30 +211,12 @@ const getTaskData = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const getRobotData = (mapId = null) => {
|
||||
getRobotList({
|
||||
pageNum: 1,
|
||||
pageSize: 10000,
|
||||
currentMapId: mapId
|
||||
}).then((res) => {
|
||||
robotList.value = res.rows;
|
||||
})
|
||||
}
|
||||
|
||||
const open = ref(false);
|
||||
|
||||
const addTask = () => {
|
||||
open.value = true;
|
||||
}
|
||||
|
||||
const taskChange = (value) => {
|
||||
const task = taskList.value.find(item => item.id === value)
|
||||
const mapId = task.mapId
|
||||
form.robotId = null;
|
||||
robotList.value = []
|
||||
getRobotData(mapId)
|
||||
}
|
||||
|
||||
const runTaskRef = ref();
|
||||
/**
|
||||
* 确定添加执行任务
|
||||
@ -347,25 +327,16 @@ const startTask = async (task) => {
|
||||
ElMessage.warning('当前任务没有关联流程编排')
|
||||
return
|
||||
}
|
||||
const response = await getFlowParamsApi(taskDefinition.taskConfigId)
|
||||
const definitions = await Promise.all((response.rows || []).map(async (item) => {
|
||||
const flowResponse = await getDetect(item.itemId)
|
||||
return flowResponse.data?.flowData
|
||||
? getResourceRoles(normalizeFlowResourceSchema(JSON.parse(flowResponse.data.flowData)))
|
||||
: []
|
||||
}))
|
||||
const roles = mergeResourceRoleDefinitions(definitions)
|
||||
runtimeAssignments.value = roles.map((role, index) => ({
|
||||
roleKey: role.roleKey,
|
||||
members: [{
|
||||
memberKey: `${role.roleKey}_1`,
|
||||
robotId: index === 0 ? task.robotId || '' : '',
|
||||
devices: {}
|
||||
}]
|
||||
}))
|
||||
runtimeRoles.value = roles
|
||||
pendingStartTask.value = task
|
||||
startDialogVisible.value = true
|
||||
try {
|
||||
const roles = await loadTaskRuntimeRoles(taskDefinition.taskConfigId)
|
||||
runtimeAssignments.value = createRuntimeAssignments(roles, task.robotIdentity)
|
||||
runtimeMapId.value = taskDefinition.mapId || ''
|
||||
runtimeRoles.value = roles
|
||||
pendingStartTask.value = task
|
||||
startDialogVisible.value = true
|
||||
} catch (error) {
|
||||
ElMessage.error(error.message || '工作流执行资源加载失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -377,7 +348,7 @@ const confirmStartTask = async () => {
|
||||
}
|
||||
startSubmitting.value = true
|
||||
try {
|
||||
const robotId = runtimeAssignments.value.flatMap(item => item.members).find(member => member.robotId)?.robotId || ''
|
||||
const robotId = firstAssignedRobotId(runtimeAssignments.value)
|
||||
const res = await startRunTask(pendingStartTask.value.id, {
|
||||
robotId,
|
||||
runtimeAssignments: runtimeAssignments.value
|
||||
@ -534,7 +505,6 @@ const handleOpenLog = async (row) => {
|
||||
}
|
||||
onMounted(() => {
|
||||
getTaskData();
|
||||
getRobotData();
|
||||
getList();
|
||||
getHistoryList();
|
||||
subscribeDebounced(true)
|
||||
|
||||
@ -183,10 +183,12 @@ import {
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { formatDate } from "@/utils/index";
|
||||
import { debounce } from 'lodash'; // 引入 lodash 的防抖函数
|
||||
import { getFlowParamsApi } from '@/api/device/terminal'
|
||||
import { getDetect } from '@/api/test/detect'
|
||||
import RuntimeAssignmentsEditor from '@/views/flow/components/RuntimeAssignmentsEditor.vue'
|
||||
import { getResourceRoles, mergeResourceRoleDefinitions, normalizeFlowResourceSchema } from '@/utils/flowResources'
|
||||
import {
|
||||
createRuntimeAssignments,
|
||||
firstAssignedRobotId,
|
||||
loadTaskRuntimeRoles,
|
||||
} from '@/utils/flowExecution'
|
||||
|
||||
const topContainerRef = ref();
|
||||
const containerHeight = useContainerHeight(topContainerRef);
|
||||
@ -367,17 +369,15 @@ const startTask = async (task) => {
|
||||
ElMessage.warning('当前任务没有关联流程编排')
|
||||
return
|
||||
}
|
||||
const response = await getFlowParamsApi(taskDefinition.taskConfigId)
|
||||
const definitions = await Promise.all((response.rows || []).map(async (item) => {
|
||||
const flowResponse = await getDetect(item.itemId)
|
||||
return flowResponse.data?.flowData
|
||||
? getResourceRoles(normalizeFlowResourceSchema(JSON.parse(flowResponse.data.flowData)))
|
||||
: []
|
||||
}))
|
||||
runtimeRoles.value = mergeResourceRoleDefinitions(definitions)
|
||||
runtimeAssignments.value = []
|
||||
pendingStartTask.value = task
|
||||
startDialogVisible.value = true
|
||||
try {
|
||||
const roles = await loadTaskRuntimeRoles(taskDefinition.taskConfigId)
|
||||
runtimeAssignments.value = createRuntimeAssignments(roles)
|
||||
runtimeRoles.value = roles
|
||||
pendingStartTask.value = task
|
||||
startDialogVisible.value = true
|
||||
} catch (error) {
|
||||
ElMessage.error(error.message || '工作流执行资源加载失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -389,7 +389,7 @@ const confirmStartTask = async () => {
|
||||
}
|
||||
startSubmitting.value = true
|
||||
try {
|
||||
const robotId = runtimeAssignments.value.flatMap(item => item.members).find(member => member.robotId)?.robotId || ''
|
||||
const robotId = firstAssignedRobotId(runtimeAssignments.value)
|
||||
const res = await startTaskinstance(pendingStartTask.value.id, {
|
||||
robotId,
|
||||
runtimeAssignments: runtimeAssignments.value
|
||||
|
||||
@ -68,9 +68,11 @@ import { ref, watch } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { addValueProperty, formatTableData } from "@/utils/flow";
|
||||
import { getExecuteRunParams, projectExecute } from "@/api/is/project/index.js";
|
||||
import { getDetect } from '@/api/test/detect'
|
||||
import RuntimeAssignmentsEditor from '@/views/flow/components/RuntimeAssignmentsEditor.vue'
|
||||
import { getResourceRoles, mergeResourceRoleDefinitions, normalizeFlowResourceSchema } from '@/utils/flowResources'
|
||||
import {
|
||||
firstAssignedRobotId,
|
||||
loadFlowItemRuntimeRoles,
|
||||
} from '@/utils/flowExecution'
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
@ -96,13 +98,8 @@ const getProjectParams = async () => {
|
||||
if (res.code === 200) {
|
||||
const data = parseConfig(res.data)
|
||||
formData.value.tableData = data
|
||||
const definitions = await Promise.all(data.map(async (item) => {
|
||||
const response = await getDetect(item.itemId)
|
||||
return response.data?.flowData
|
||||
? getResourceRoles(normalizeFlowResourceSchema(JSON.parse(response.data.flowData)))
|
||||
: []
|
||||
}))
|
||||
roles.value = mergeResourceRoleDefinitions(definitions)
|
||||
runtimeAssignments.value = []
|
||||
roles.value = await loadFlowItemRuntimeRoles(data)
|
||||
}
|
||||
};
|
||||
|
||||
@ -131,7 +128,7 @@ const confirm = () => {
|
||||
ElMessage.warning(assignmentError)
|
||||
return
|
||||
}
|
||||
const robotId = runtimeAssignments.value.flatMap(item => item.members).find(member => member.robotId)?.robotId || ''
|
||||
const robotId = firstAssignedRobotId(runtimeAssignments.value)
|
||||
const res = await projectExecute({
|
||||
runParams,
|
||||
projectId: props.projectId,
|
||||
@ -160,7 +157,11 @@ watch(
|
||||
() => props.drawer,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
getProjectParams()
|
||||
getProjectParams().catch((error) => {
|
||||
roles.value = []
|
||||
runtimeAssignments.value = []
|
||||
ElMessage.error(error.message || '工作流执行资源加载失败')
|
||||
})
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@ -68,9 +68,11 @@ import { emitter } from "@/utils/eventBus";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { getFlowParamsApi } from "@/api/device/terminal.js";
|
||||
import { taskExecute } from "@/api/test/config"
|
||||
import { getDetect } from '@/api/test/detect'
|
||||
import RuntimeAssignmentsEditor from '@/views/flow/components/RuntimeAssignmentsEditor.vue'
|
||||
import { getResourceRoles, mergeResourceRoleDefinitions, normalizeFlowResourceSchema } from '@/utils/flowResources'
|
||||
import {
|
||||
firstAssignedRobotId,
|
||||
loadFlowItemRuntimeRoles,
|
||||
} from '@/utils/flowExecution'
|
||||
|
||||
const props = defineProps({
|
||||
drawer: Boolean,
|
||||
@ -101,12 +103,8 @@ const getFlowParams = async () => {
|
||||
if (res.code === 200) {
|
||||
const data = parseConfig(res.rows)
|
||||
formData.value.tableData = data
|
||||
const definitions = await Promise.all(data.map(async (item) => {
|
||||
const response = await getDetect(item.itemId)
|
||||
if (!response.data?.flowData) return []
|
||||
return getResourceRoles(normalizeFlowResourceSchema(JSON.parse(response.data.flowData)))
|
||||
}))
|
||||
roles.value = mergeResourceRoleDefinitions(definitions)
|
||||
runtimeAssignments.value = []
|
||||
roles.value = await loadFlowItemRuntimeRoles(data)
|
||||
}
|
||||
};
|
||||
|
||||
@ -135,7 +133,7 @@ const confirm = () => {
|
||||
ElMessage.warning(assignmentError)
|
||||
return
|
||||
}
|
||||
const robotId = runtimeAssignments.value.flatMap(item => item.members).find(member => member.robotId)?.robotId || ''
|
||||
const robotId = firstAssignedRobotId(runtimeAssignments.value)
|
||||
const res = await taskExecute({
|
||||
runParams,
|
||||
taskId: props.currentRow.id,
|
||||
@ -159,7 +157,11 @@ watch(
|
||||
() => props.drawer,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
getFlowParams()
|
||||
getFlowParams().catch((error) => {
|
||||
roles.value = []
|
||||
runtimeAssignments.value = []
|
||||
ElMessage.error(error.message || '工作流执行资源加载失败')
|
||||
})
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@ -341,17 +341,14 @@ import {
|
||||
getTaskParamsById,
|
||||
listConfig,
|
||||
taskConfig,
|
||||
taskExecute,
|
||||
updateConfig,
|
||||
} from "@/api/test/config";
|
||||
|
||||
import { flowExecute } from "@/api/flow/flow";
|
||||
import TableSearch from "@/components/TableSearch/index.vue";
|
||||
import draggable from "vuedraggable";
|
||||
import { listDetect } from "@/api/test/detect.js";
|
||||
import { Tools, Delete } from "@element-plus/icons-vue";
|
||||
import { listGroup } from "@/api/system/group.js";
|
||||
import { listTerminal } from "@/api/device/terminal.js";
|
||||
import Run from "./Run.vue";
|
||||
import { ref, reactive, toRefs, onMounted } from "vue";
|
||||
|
||||
@ -415,7 +412,6 @@ function cancel() {
|
||||
taskExecuteOpen.value = false;
|
||||
taskConfigOpen.value = false;
|
||||
executeOpen.value = false;
|
||||
selectedTerminal.value = "";
|
||||
reset();
|
||||
}
|
||||
|
||||
@ -590,46 +586,6 @@ const submitTaskForm = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const formatCode = (arr) => {
|
||||
return arr.map((item) => ({
|
||||
itemId: item.detectId,
|
||||
params: item.params.reduce((acc, param) => {
|
||||
acc[param.paramEnName] = param.sourceValue;
|
||||
return acc;
|
||||
}, {}),
|
||||
}));
|
||||
};
|
||||
|
||||
const handleTaskExecute = (formEl) => {
|
||||
if (!selectedTerminal.value) {
|
||||
proxy.$modal.msgError("请选择执行终端");
|
||||
return;
|
||||
}
|
||||
const data = Object.assign(
|
||||
{},
|
||||
{ runParams: { robotId: selectedTerminal.value } },
|
||||
{ taskId: currentRow.value.id }
|
||||
);
|
||||
// taskExecute(data).then((res) => {
|
||||
// proxy.$modal.msgSuccess("下发成功")
|
||||
// resetTaskList()
|
||||
// executeOpen.value = false
|
||||
// taskExecuteOpen.value = false
|
||||
// currentRow.value = undefined
|
||||
// selectedTerminal.value = ""
|
||||
// })
|
||||
|
||||
flowExecute(data).then((res) => {
|
||||
console.log("res", res);
|
||||
proxy.$modal.msgSuccess("下发成功");
|
||||
resetTaskList();
|
||||
executeOpen.value = false;
|
||||
taskExecuteOpen.value = false;
|
||||
currentRow.value = undefined;
|
||||
selectedTerminal.value = "";
|
||||
});
|
||||
};
|
||||
|
||||
const handleTaskExecuteOpen = (row) => {
|
||||
currentRow.value = row;
|
||||
executeOpen.value = true;
|
||||
@ -661,24 +617,6 @@ function handleGetSystemGroup(row) {
|
||||
});
|
||||
}
|
||||
|
||||
const terminalGroup = ref([]);
|
||||
const selectedTerminal = ref("");
|
||||
|
||||
/** 获取设备终端列表 */
|
||||
function handleGetTerminalGroup(row) {
|
||||
listTerminal({
|
||||
pageNum: 1,
|
||||
pageSize: 1000,
|
||||
}).then((response) => {
|
||||
terminalGroup.value = response.rows?.map((item) => {
|
||||
return {
|
||||
label: `${item.name}(${item.host}:${item.port})`,
|
||||
value: item.id,
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const removeFromB = (id) => {
|
||||
const index = taskRightList.value.findIndex(item => item.id === id)
|
||||
if (index !== -1) taskRightList.value.splice(index, 1)
|
||||
|
||||
@ -68,9 +68,11 @@ import { ref, watch } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { addValueProperty, formatTableData } from "@/utils/flow";
|
||||
import { getExecuteRunParams, projectExecute } from "@/api/vi/project.js"
|
||||
import { getDetect } from '@/api/test/detect'
|
||||
import RuntimeAssignmentsEditor from '@/views/flow/components/RuntimeAssignmentsEditor.vue'
|
||||
import { getResourceRoles, mergeResourceRoleDefinitions, normalizeFlowResourceSchema } from '@/utils/flowResources'
|
||||
import {
|
||||
firstAssignedRobotId,
|
||||
loadFlowItemRuntimeRoles,
|
||||
} from '@/utils/flowExecution'
|
||||
|
||||
const props = defineProps({
|
||||
drawer: Boolean,
|
||||
@ -94,13 +96,8 @@ const getProjectParams = async () => {
|
||||
if (res.code === 200) {
|
||||
const data = parseConfig(res.data)
|
||||
formData.value.tableData = data
|
||||
const definitions = await Promise.all(data.map(async (item) => {
|
||||
const response = await getDetect(item.itemId)
|
||||
return response.data?.flowData
|
||||
? getResourceRoles(normalizeFlowResourceSchema(JSON.parse(response.data.flowData)))
|
||||
: []
|
||||
}))
|
||||
roles.value = mergeResourceRoleDefinitions(definitions)
|
||||
runtimeAssignments.value = []
|
||||
roles.value = await loadFlowItemRuntimeRoles(data)
|
||||
}
|
||||
};
|
||||
|
||||
@ -129,7 +126,7 @@ const confirm = () => {
|
||||
ElMessage.warning(assignmentError)
|
||||
return
|
||||
}
|
||||
const robotId = runtimeAssignments.value.flatMap(item => item.members).find(member => member.robotId)?.robotId || ''
|
||||
const robotId = firstAssignedRobotId(runtimeAssignments.value)
|
||||
const res = await projectExecute({
|
||||
runParams,
|
||||
projectId: props.projectId,
|
||||
@ -158,7 +155,11 @@ watch(
|
||||
() => props.drawer,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
getProjectParams()
|
||||
getProjectParams().catch((error) => {
|
||||
roles.value = []
|
||||
runtimeAssignments.value = []
|
||||
ElMessage.error(error.message || '工作流执行资源加载失败')
|
||||
})
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user