feat: 按选中任务展示运行详情
This commit is contained in:
parent
239065a7d3
commit
6a24cd17cc
@ -6,18 +6,20 @@
|
||||
<el-button type="primary" @click="addTask">添加任务</el-button>
|
||||
</div>
|
||||
<div class="taskList-container">
|
||||
<div class="task-item" v-for="task in runTaskList">
|
||||
<div v-for="task in runTaskList" :key="task.id" class="task-item"
|
||||
:class="{ 'is-selected': isSelectedTask(task) }"
|
||||
:aria-current="isSelectedTask(task) ? 'true' : undefined" @click="selectTask(task)">
|
||||
<div class="task-content">
|
||||
<div class="task-title">{{ task.taskName }}</div>
|
||||
<div class="task-actions">
|
||||
<SvgIcon class="icon" v-if="task.status === '1'" name="pause" :size="24" color="#FFB300"
|
||||
@click="pauseTask(task)" />
|
||||
@click.stop="pauseTask(task)" />
|
||||
<SvgIcon class="icon" v-if="task.status === '3' || task.status === '5'" name="start"
|
||||
:size="24" color="#00FF88" @click="startTask(task)" />
|
||||
:size="24" color="#00FF88" @click.stop="startTask(task)" />
|
||||
<SvgIcon class="icon" v-if="task.status != '5'" name="stop" :size="24" color="#d81e06"
|
||||
@click="stopTask(task)" />
|
||||
@click.stop="stopTask(task)" />
|
||||
<SvgIcon class="icon" v-if="task.status === '5'" name="remove" :size="24" color="#FFB300"
|
||||
@click="removeTask(task)" />
|
||||
@click.stop="removeTask(task)" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="robot">主要机器人:{{ task.robotName || '执行时安排' }}</div>
|
||||
@ -35,38 +37,38 @@
|
||||
</div>
|
||||
<div class="context-container">
|
||||
<div v-if="activePanel === 'current'">
|
||||
<!-- 当前任务内容 -->
|
||||
<div class="task" v-for="runTask in runingTaskList" :key="runTask.id"
|
||||
:data-task-id="runTask.id" :class="{ 'task--selected': selectedTaskId === runTask.id }"
|
||||
@click="selectedTaskId = runTask.id">
|
||||
<!-- 当前任务区域只展示左侧任务队列中选中的任务,避免同时渲染所有执行中任务。 -->
|
||||
<div v-if="selectedRunTask" :key="selectedRunTask.id" class="task"
|
||||
:data-task-id="selectedRunTask.id">
|
||||
<div class="task-header">
|
||||
<div class="header-container">
|
||||
<div class="task-info">
|
||||
<div class="title">{{ runTask.taskName }}</div>
|
||||
<div class="id">ID: {{ runTask.taskId }}</div>
|
||||
<div class="title">{{ selectedRunTask.taskName }}</div>
|
||||
<div class="id">ID: {{ selectedRunTask.taskId }}</div>
|
||||
</div>
|
||||
<div class="robot">
|
||||
<div>主要机器人:{{ runTask.robotName || '按流程角色安排' }}</div>
|
||||
<div>开始时间: {{ formatDate(runTask.startTime) }}</div>
|
||||
<div>主要机器人:{{ selectedRunTask.robotName || '按流程角色安排' }}</div>
|
||||
<div>开始时间: {{ formatDate(selectedRunTask.startTime) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<el-tag :type="runTask.status === '3' ? 'primary' : 'warning'">
|
||||
{{ taskStatusMap[runTask.status] || '未知状态' }}
|
||||
<el-tag :type="selectedRunTask.status === '3' ? 'primary' : 'warning'">
|
||||
{{ taskStatusMap[selectedRunTask.status] || '未知状态' }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="progress-label">执行进度</div>
|
||||
<el-progress :color="customColorMethod"
|
||||
:percentage="parseFloat(taskRunningInfo[runTask.id]?.progress?.toFixed(2)) || 0" />
|
||||
<taskSteps :list="pointList_runningTask[runTask.taskId]"
|
||||
:activeStep="taskRunningInfo[runTask.id]?.activeStep || 0" />
|
||||
:percentage="parseFloat(taskRunningInfo[selectedRunTask.id]?.progress?.toFixed(2)) || 0" />
|
||||
<taskSteps :list="pointList_runningTask[selectedRunTask.taskId]"
|
||||
:activeStep="taskRunningInfo[selectedRunTask.id]?.activeStep || 0" />
|
||||
<div class="run-log-container">
|
||||
<div>执行日志</div>
|
||||
<taskLog :list="taskLogList[runTask.id]" />
|
||||
<taskLog :list="taskLogList[selectedRunTask.id]" />
|
||||
</div>
|
||||
</div>
|
||||
<el-empty v-else description="暂无任务" />
|
||||
</div>
|
||||
<div v-else-if="activePanel === 'history'">
|
||||
<div :style="containerHeight">
|
||||
@ -170,7 +172,7 @@ 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 { onMounted, onUnmounted } from "vue";
|
||||
import { computed, onMounted, onUnmounted } from "vue";
|
||||
import { addRunTask, getRunTaskList, startRunTask, rerunRunTask, pauseRunTask, stopRunTask, resumeRunTask, deleteRunTask, getTaskLogListApi } from "@/api/inspection/runTask";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { formatDate } from "@/utils/index";
|
||||
@ -220,6 +222,23 @@ const rules = reactive({
|
||||
const taskList = ref([]);
|
||||
const runTaskList = ref([]);
|
||||
|
||||
// 接口和 WebSocket 可能分别以数字、字符串返回任务实例 ID,统一转换后再判断是否为选中任务。
|
||||
const isSelectedTask = (task) => String(task?.id ?? '') === String(selectedTaskId.value)
|
||||
|
||||
// 当前任务详情直接从完整任务队列中取得,因此未执行、执行中和已暂停任务都可以被选中查看。
|
||||
const selectedRunTask = computed(() => runTaskList.value.find(isSelectedTask) || null)
|
||||
|
||||
/**
|
||||
* 点击左侧任务卡片时切换右侧当前任务详情,并从历史记录面板返回当前任务面板。
|
||||
* 卡片内的启动、暂停、停止和删除图标均使用 .stop 阻止冒泡,执行操作时不会意外改变当前选择。
|
||||
*
|
||||
* @param {Object} task 左侧任务队列中被点击的任务实例
|
||||
*/
|
||||
const selectTask = (task) => {
|
||||
selectedTaskId.value = task.id
|
||||
activePanel.value = 'current'
|
||||
}
|
||||
|
||||
const normalizeTaskStatus = (tasks = []) => tasks.map(item => ({
|
||||
...item,
|
||||
status: String(item.status),
|
||||
@ -264,9 +283,12 @@ const cancelTaskDialog = () => {
|
||||
}
|
||||
|
||||
|
||||
const runingTaskList = ref([])
|
||||
/**
|
||||
* 获取任务队列
|
||||
* 获取任务队列并维护当前选中的任务:
|
||||
* 1. 页面首次加载时默认选中队列第一条任务;
|
||||
* 2. 队列刷新后,如果原任务仍存在,则保留用户当前选择;
|
||||
* 3. 如果原任务已经结束或被删除,则选择刷新后队列中的第一条任务;
|
||||
* 4. 如果任务队列为空,则清空选择并由模板展示空状态。
|
||||
*/
|
||||
const getList = () => {
|
||||
return getRunTaskList({
|
||||
@ -275,7 +297,9 @@ const getList = () => {
|
||||
if (res.code === 200) {
|
||||
const tasks = normalizeTaskStatus(res.rows)
|
||||
runTaskList.value = tasks;
|
||||
runingTaskList.value = tasks.filter(item => item.status !== '5')
|
||||
if (!tasks.some(isSelectedTask)) {
|
||||
selectedTaskId.value = tasks[0]?.id ?? ''
|
||||
}
|
||||
for (let i = 0; i < runTaskList.value.length; i++) {
|
||||
getPointByTaskId(runTaskList.value[i].taskId)
|
||||
getTaskLogList(runTaskList.value[i].id)
|
||||
@ -618,6 +642,17 @@ onUnmounted(() => {
|
||||
box-shadow: inset 0 0 0 1px #eee;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 16px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease, box-shadow 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background: var(--el-fill-color-light);
|
||||
}
|
||||
|
||||
&.is-selected {
|
||||
background: var(--el-color-primary-light-9);
|
||||
box-shadow: inset 0 0 0 2px var(--el-color-primary);
|
||||
}
|
||||
|
||||
.task-content {
|
||||
display: flex;
|
||||
@ -684,10 +719,6 @@ onUnmounted(() => {
|
||||
border-radius: 20px;
|
||||
margin-bottom: 12px;
|
||||
|
||||
&--selected {
|
||||
box-shadow: inset 0 0 0 2px var(--el-color-primary);
|
||||
}
|
||||
|
||||
.task-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
@ -6,18 +6,20 @@
|
||||
<el-button type="primary" @click="addTask">添加任务</el-button>
|
||||
</div>
|
||||
<div class="taskList-container">
|
||||
<div class="task-item" v-for="task in runTaskList">
|
||||
<div v-for="task in runTaskList" :key="task.id" class="task-item"
|
||||
:class="{ 'is-selected': isSelectedTask(task) }"
|
||||
:aria-current="isSelectedTask(task) ? 'true' : undefined" @click="selectTask(task)">
|
||||
<div class="task-content">
|
||||
<div class="task-title">{{ task.taskName }}</div>
|
||||
<div class="task-actions">
|
||||
<SvgIcon class="icon" v-if="task.status == '1'" name="pause" :size="24" color="#FFB300"
|
||||
@click="pauseTask(task)" />
|
||||
@click.stop="pauseTask(task)" />
|
||||
<SvgIcon class="icon" v-if="task.status == '3' || task.status == '5'" name="start"
|
||||
:size="24" color="#00FF88" @click="startTask(task)" />
|
||||
:size="24" color="#00FF88" @click.stop="startTask(task)" />
|
||||
<SvgIcon class="icon" v-if="task.status != '5'" name="stop" :size="24" color="#d81e06"
|
||||
@click="stopTask(task)" />
|
||||
@click.stop="stopTask(task)" />
|
||||
<SvgIcon class="icon" v-if="task.status == '5'" name="remove" :size="24" color="#FFB300"
|
||||
@click="removeTask(task)" />
|
||||
@click.stop="removeTask(task)" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="robot">手机:{{ task.phoneName }}</div>
|
||||
@ -36,44 +38,44 @@
|
||||
</div>
|
||||
<div class="context-container">
|
||||
<div v-if="activePanel === 'current'">
|
||||
<!-- 当前任务内容 -->
|
||||
<div class="task" v-for="runTask in runingTaskList" :key="runTask.id"
|
||||
:data-task-id="runTask.id" :class="{ 'task--selected': selectedTaskId === runTask.id }"
|
||||
@click="selectedTaskId = runTask.id">
|
||||
<!-- 当前任务区域只展示左侧队列选中的任务,不再同时渲染全部执行中任务。 -->
|
||||
<div v-if="selectedRunTask" :key="selectedRunTask.id" class="task"
|
||||
:data-task-id="selectedRunTask.id">
|
||||
<div class="task-header">
|
||||
<div class="header-container">
|
||||
<div class="task-info">
|
||||
<div class="title">{{ runTask.taskName }}</div>
|
||||
<div class="id">ID: {{ runTask.taskId }}</div>
|
||||
<div class="title">{{ selectedRunTask.taskName }}</div>
|
||||
<div class="id">ID: {{ selectedRunTask.taskId }}</div>
|
||||
</div>
|
||||
<div class="robot">
|
||||
<div>手机:{{ runTask.phoneName }}</div>
|
||||
<div>车辆:{{ runTask.vehicleName }}</div>
|
||||
<div>开始时间: {{ formatDate(runTask.startTime) }}</div>
|
||||
<div>手机:{{ selectedRunTask.phoneName }}</div>
|
||||
<div>车辆:{{ selectedRunTask.vehicleName }}</div>
|
||||
<div>开始时间: {{ formatDate(selectedRunTask.startTime) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<el-tag :type="runTask.status === '3' ? 'primary' : 'warning'">
|
||||
{{ taskStatusMap[runTask.status] || '未知状态' }}
|
||||
<el-tag :type="selectedRunTask.status === '3' ? 'primary' : 'warning'">
|
||||
{{ taskStatusMap[selectedRunTask.status] || '未知状态' }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="progress-label">执行进度</div>
|
||||
<el-progress :color="customColorMethod"
|
||||
:percentage="parseFloat(taskRunningInfo[runTask.id]?.progress?.toFixed(2)) || 0" />
|
||||
<taskSteps :list="pointList_runningTask[runTask.taskId]"
|
||||
:activeStep="taskRunningInfo[runTask.id]?.activeStep || 0" />
|
||||
:percentage="parseFloat(taskRunningInfo[selectedRunTask.id]?.progress?.toFixed(2)) || 0" />
|
||||
<taskSteps :list="pointList_runningTask[selectedRunTask.taskId]"
|
||||
:activeStep="taskRunningInfo[selectedRunTask.id]?.activeStep || 0" />
|
||||
<div class="run-log-container">
|
||||
<div>执行日志</div>
|
||||
<!--
|
||||
taskId 用于查询检测项,taskStatus 用于在完成/失败/终止后推导最后一个已执行检测项的状态;
|
||||
两者与实例日志分开传入,避免把任务级字段重复写进每一条日志。
|
||||
-->
|
||||
<taskLog :height="460" :task-id="runTask.taskId" :task-status="runTask.status"
|
||||
:list="taskLogList[runTask.id]" />
|
||||
<taskLog :height="460" :task-id="selectedRunTask.taskId"
|
||||
:task-status="selectedRunTask.status" :list="taskLogList[selectedRunTask.id]" />
|
||||
</div>
|
||||
</div>
|
||||
<el-empty v-else description="暂无任务" />
|
||||
</div>
|
||||
<div v-else-if="activePanel === 'history'">
|
||||
<div :style="containerHeight">
|
||||
@ -191,7 +193,7 @@ import { useContainerHeight } from "@/hooks/tableHeight";
|
||||
import { getTaskList, getTestCaseByTaskId } from "@/api/intelligenceTest/taskManage";
|
||||
import { getPhoneList } from "@/api/intelligenceTest/mobilePhone";
|
||||
import { getVehicleList } from "@/api/intelligenceTest/car";
|
||||
import { onMounted, onUnmounted } from "vue";
|
||||
import { computed, onMounted, onUnmounted } from "vue";
|
||||
import {
|
||||
addTaskinstance,
|
||||
getTaskinstanceList,
|
||||
@ -253,6 +255,21 @@ const phoneList = ref([]);
|
||||
|
||||
const runTaskList = ref([]);
|
||||
|
||||
// 任务实例 ID 在接口和 WebSocket 中可能分别是数字或字符串,统一按字符串比较选中状态。
|
||||
const isSelectedTask = (task) => String(task?.id ?? '') === String(selectedTaskId.value)
|
||||
|
||||
// 右侧当前任务始终从完整任务队列中取选中的一条,包括尚未执行、执行中和已暂停任务。
|
||||
const selectedRunTask = computed(() => runTaskList.value.find(isSelectedTask) || null)
|
||||
|
||||
/**
|
||||
* 点击左侧任务卡片时切换右侧详情,并自动回到“当前任务”面板。
|
||||
* 卡片内部操作按钮使用 .stop 阻止冒泡,因此执行暂停/启动等命令时不会意外切换选择。
|
||||
*/
|
||||
const selectTask = (task) => {
|
||||
selectedTaskId.value = task.id
|
||||
activePanel.value = 'current'
|
||||
}
|
||||
|
||||
const normalizeTaskStatus = (tasks = []) => tasks.map(item => ({
|
||||
...item,
|
||||
status: String(item.status),
|
||||
@ -317,9 +334,11 @@ const cancelTaskDialog = () => {
|
||||
}
|
||||
|
||||
|
||||
const runingTaskList = ref([])
|
||||
/**
|
||||
* 获取任务队列
|
||||
* 获取任务队列,并维护当前选中任务:
|
||||
* 1. 首次加载默认选择队列第一条;
|
||||
* 2. 刷新后原任务仍在队列中则保持选择;
|
||||
* 3. 原任务已结束或被删除时回退到新的第一条,队列为空时清空选择。
|
||||
*/
|
||||
const getList = () => {
|
||||
return getTaskinstanceList({
|
||||
@ -328,7 +347,9 @@ const getList = () => {
|
||||
if (res.code === 200) {
|
||||
const tasks = normalizeTaskStatus(res.rows)
|
||||
runTaskList.value = tasks;
|
||||
runingTaskList.value = tasks.filter(item => item.status !== '5')
|
||||
if (!tasks.some(isSelectedTask)) {
|
||||
selectedTaskId.value = tasks[0]?.id ?? ''
|
||||
}
|
||||
for (let i = 0; i < runTaskList.value.length; i++) {
|
||||
getCaseByTaskId(runTaskList.value[i].taskId)
|
||||
getTaskLogList(runTaskList.value[i].id)
|
||||
@ -684,6 +705,17 @@ onUnmounted(() => {
|
||||
box-shadow: inset 0 0 0 1px #eee;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 16px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease, box-shadow 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background: var(--el-fill-color-light);
|
||||
}
|
||||
|
||||
&.is-selected {
|
||||
background: var(--el-color-primary-light-9);
|
||||
box-shadow: inset 0 0 0 2px var(--el-color-primary);
|
||||
}
|
||||
|
||||
.task-content {
|
||||
display: flex;
|
||||
@ -750,10 +782,6 @@ onUnmounted(() => {
|
||||
border-radius: 20px;
|
||||
margin-bottom: 12px;
|
||||
|
||||
&--selected {
|
||||
box-shadow: inset 0 0 0 2px var(--el-color-primary);
|
||||
}
|
||||
|
||||
.task-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user