| | |
| | | <h3>多设备测试编排</h3> |
| | | <p v-if="group">当前设备组:{{ group.groupName }}({{ group.deviceCount || '-' }} 台设备)</p> |
| | | <p v-else class="warning">请先在左侧选择一个设备组</p> |
| | | <p v-if="group && loadDeviceName" class="sub-info">上大车设备:{{ loadDeviceName }}</p> |
| | | <p v-if="group && loadDeviceName" class="sub-info">当前设备:{{ loadDeviceName }}</p> |
| | | </div> |
| | | <div class="action-buttons"> |
| | | <el-button |
| | |
| | | </div> |
| | | </div> |
| | | |
| | | <el-form :model="form" label-width="120px"> |
| | | <el-form-item label="玻璃ID列表"> |
| | | <el-form :model="form" label-width="120px" :rules="rules" ref="formRef"> |
| | | <el-form-item label="玻璃ID列表" prop="glassIds" required> |
| | | <el-input |
| | | v-model="glassIdsInput" |
| | | type="textarea" |
| | | :rows="4" |
| | | placeholder="请输入玻璃条码,支持多行或逗号分隔" |
| | | placeholder="请输入玻璃条码,支持多行或逗号分隔,每行一个或逗号分隔" |
| | | show-word-limit |
| | | :maxlength="5000" |
| | | /> |
| | | <div class="form-tip"> |
| | | 已输入 {{ glassIds.length }} 个玻璃ID |
| | | </div> |
| | | </el-form-item> |
| | | <el-form-item label="位置编码"> |
| | | <el-input v-model="form.positionCode" placeholder="例如:POS1" /> |
| | | </el-form-item> |
| | | <el-form-item label="存储位置"> |
| | | <el-input-number v-model="form.storagePosition" :min="1" :max="200" /> |
| | | </el-form-item> |
| | | |
| | | <el-divider content-position="left">执行配置</el-divider> |
| | | |
| | | <el-form-item label="执行间隔 (ms)"> |
| | | <el-input-number v-model="form.executionInterval" :min="100" :max="10000" /> |
| | | <el-input-number |
| | | v-model="form.executionInterval" |
| | | :min="100" |
| | | :max="10000" |
| | | :step="100" |
| | | placeholder="设备操作间隔时间" |
| | | /> |
| | | <div class="form-tip">每个设备操作之间的间隔时间(毫秒)</div> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="超时时间 (分钟)"> |
| | | <el-input-number |
| | | v-model="form.timeoutMinutes" |
| | | :min="1" |
| | | :max="60" |
| | | :step="1" |
| | | placeholder="任务超时时间" |
| | | /> |
| | | <div class="form-tip">任务执行的最大超时时间</div> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="重试次数"> |
| | | <el-input-number |
| | | v-model="form.retryCount" |
| | | :min="0" |
| | | :max="10" |
| | | :step="1" |
| | | placeholder="失败重试次数" |
| | | /> |
| | | <div class="form-tip">设备操作失败时的最大重试次数</div> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | |
| | | const emit = defineEmits(['task-started']) |
| | | |
| | | const form = reactive({ |
| | | positionCode: '', |
| | | storagePosition: null, |
| | | executionInterval: 1000 |
| | | executionInterval: 1000, |
| | | timeoutMinutes: 30, |
| | | retryCount: 3 |
| | | }) |
| | | |
| | | const formRef = ref(null) |
| | | |
| | | const rules = { |
| | | glassIds: [ |
| | | { |
| | | validator: (rule, value, callback) => { |
| | | if (glassIds.value.length === 0) { |
| | | callback(new Error('请至少输入一个玻璃ID')) |
| | | } else if (glassIds.value.length > 100) { |
| | | callback(new Error('玻璃ID数量不能超过100个')) |
| | | } else { |
| | | // 验证玻璃ID格式 |
| | | const invalidIds = glassIds.value.filter(id => { |
| | | // 简单的格式验证:不能为空,长度在1-50之间 |
| | | return !id || id.length === 0 || id.length > 50 |
| | | }) |
| | | if (invalidIds.length > 0) { |
| | | callback(new Error(`存在无效的玻璃ID格式,请检查`)) |
| | | } else { |
| | | callback() |
| | | } |
| | | } |
| | | }, |
| | | trigger: 'blur' |
| | | } |
| | | ] |
| | | } |
| | | |
| | | const glassIdsInput = ref('') |
| | | const loading = ref(false) |
| | |
| | | ElMessage.warning('请先选择设备组') |
| | | return |
| | | } |
| | | |
| | | // 表单验证 |
| | | if (!formRef.value) return |
| | | try { |
| | | await formRef.value.validate() |
| | | } catch (error) { |
| | | ElMessage.warning('请检查表单输入') |
| | | return |
| | | } |
| | | |
| | | if (glassIds.value.length === 0) { |
| | | ElMessage.warning('请至少输入一个玻璃ID') |
| | | return |
| | | } |
| | | |
| | | try { |
| | | loading.value = true |
| | | await multiDeviceTaskApi.startTask({ |
| | | |
| | | // 构建任务参数 |
| | | const parameters = { |
| | | glassIds: glassIds.value, |
| | | executionInterval: form.executionInterval || 1000 |
| | | } |
| | | |
| | | // 设备特定配置已移除,如有需要可在此扩展 |
| | | if (form.timeoutMinutes) { |
| | | parameters.timeoutMinutes = form.timeoutMinutes |
| | | } |
| | | if (form.retryCount !== null) { |
| | | parameters.retryCount = form.retryCount |
| | | } |
| | | |
| | | // 异步启动任务,立即返回,不阻塞 |
| | | const response = await multiDeviceTaskApi.startTask({ |
| | | groupId: props.group.id || props.group.groupId, |
| | | parameters: { |
| | | glassIds: glassIds.value, |
| | | positionCode: form.positionCode || null, |
| | | storagePosition: form.storagePosition, |
| | | executionInterval: form.executionInterval |
| | | } |
| | | parameters |
| | | }) |
| | | ElMessage.success('任务已启动') |
| | | emit('task-started') |
| | | |
| | | const task = response?.data |
| | | if (task && task.taskId) { |
| | | ElMessage.success(`任务已启动(异步执行): ${task.taskId}`) |
| | | emit('task-started', task) |
| | | |
| | | // 立即刷新监控列表,显示新启动的任务 |
| | | setTimeout(() => { |
| | | emit('task-started') |
| | | }, 500) |
| | | |
| | | // 重置表单(保留执行配置),方便继续启动其他设备组 |
| | | glassIdsInput.value = '' |
| | | |
| | | // 提示用户可以继续启动其他设备组 |
| | | ElMessage.info('可以继续选择其他设备组启动测试,多个设备组将并行执行') |
| | | } else { |
| | | ElMessage.warning('任务启动响应异常') |
| | | } |
| | | } catch (error) { |
| | | ElMessage.error(error?.message || '任务启动失败') |
| | | } finally { |
| | |
| | | const response = await deviceInteractionApi.executeOperation({ |
| | | deviceId: loadDeviceId.value, |
| | | operation: 'clearGlass', |
| | | params: { |
| | | positionCode: form.positionCode || null |
| | | } |
| | | params: {} |
| | | }) |
| | | if (response?.code !== 200) { |
| | | throw new Error(response?.message || 'PLC清空失败') |
| | |
| | | gap: 12px; |
| | | align-items: center; |
| | | } |
| | | |
| | | .form-tip { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | margin-top: 4px; |
| | | line-height: 1.4; |
| | | } |
| | | </style> |
| | | |