Compare commits

..

2 Commits

Author SHA1 Message Date
780665fa9a feat: 任务执行日志 2026-06-05 17:18:47 +08:00
64d6cad887 feat: 执行任务 2026-06-05 16:53:40 +08:00
3 changed files with 159 additions and 43 deletions

View File

@ -64,4 +64,12 @@ export function resumeRunTask(id) {
url: `/inspection/taskInstance/resume/${id}`,
method: 'put'
})
}
export function getTaskLogListApi(params) {
return request({
url: '/inspection/taskLog/list',
method: 'get',
params: params
})
}

View File

@ -21,7 +21,8 @@
</div>
</div>
<div class="robot">机器人{{ task.robotName }}</div>
<el-progress :color="customColorMethod" :percentage="parseFloat(taskRunningInfo[task.id]?.progress.toFixed(2)) || 0" />
<el-progress :color="customColorMethod"
:percentage="parseFloat(taskRunningInfo[task.id]?.progress?.toFixed(2)) || 0" />
</div>
</div>
</div>
@ -56,17 +57,12 @@
<div class="progress-label">执行进度</div>
<el-progress :color="customColorMethod"
:percentage="parseFloat(taskRunningInfo[runTask.id]?.progress.toFixed(2)) || 0" />
<taskSteps :list="pointList_runintTask[runTask.taskId]"
:percentage="parseFloat(taskRunningInfo[runTask.id]?.progress?.toFixed(2)) || 0" />
<taskSteps :list="pointList_runningTask[runTask.taskId]"
:activeStep="taskRunningInfo[runTask.id]?.activeStep || 0" />
<div class="run-log-container">
<div>执行日志</div>
<div class="log-container">
<div class="log" v-for="value, index in 4">
<div class="time">11:02:06</div>
<div class="context">移动机器人至点位{{ index + 1 }}</div>
</div>
</div>
<taskLog :list="taskLogList[runTask.id]" />
</div>
</div>
</div>
@ -102,7 +98,7 @@
<el-table-column align="center" class-name="small-padding fixed-width" label="操作"
width="260">
<template #default="scope">
<el-button icon="Edit" link type="primary" @click="handleUpdate(scope.row)">
<el-button icon="Edit" link type="primary" @click="handleOpenLog(scope.row)">
日志
</el-button>
</template>
@ -140,16 +136,21 @@
</div>
</template>
</el-dialog>
<el-dialog title="任务执行日志" v-model="logDialogVisible" width="500px" append-to-body>
<taskLog :list="logDialogData" :height="300" />
</el-dialog>
</div>
</template>
<script setup>
import SvgIcon from "@/components/SvgIcon";
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 } from "@/api/inspection/runTask";
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
@ -216,6 +217,9 @@ const taskChange = (value) => {
}
const runTaskRef = ref();
/**
* 确定添加执行任务
*/
const confirmTaskDialog = () => {
runTaskRef.value.validate((valid) => {
if (valid) {
@ -249,19 +253,41 @@ const getList = () => {
runingTaskList.value = res.rows.filter(item => item.status !== '5')
for (let i = 0; i < runTaskList.value.length; i++) {
getPointByTaskId(runTaskList.value[i].taskId)
getTaskLogList(runTaskList.value[i].id)
}
}
})
}
const pointList_runintTask = ref({})
const pointList_runningTask = ref({})
/**
* 获取正在执行任务的点位列表
* @param taskId
*/
const getPointByTaskId = async (taskId) => {
const res = await getWaypointByTaskId(taskId)
if (res.code === 200) {
pointList_runintTask.value[taskId] = res.data
pointList_runningTask.value[taskId] = res.data
}
}
const taskLogList = ref({})
/**
* 获取正在执行任务的执行日志
* @param taskInstanceId
*/
const getTaskLogList = async (taskInstanceId) => {
const res = await getTaskLogListApi({ taskInstanceId })
if (res.code === 200) {
taskLogList.value[taskInstanceId] = res.rows
}
}
/**
* 自定义进度条值不同区间所显示的颜色
* @param percentage
*/
const customColorMethod = (percentage) => {
if (percentage < 30) {
return '#6f7ad3'
@ -283,6 +309,10 @@ const queryParams = reactive({
pageSize: 10,
});
/**
* 开始执行任务
* @param task
*/
const startTask = async (task) => {
if (task.status === '3') {
let res = await resumeRunTask(task.id);
@ -299,6 +329,10 @@ const startTask = async (task) => {
}
}
/**
* 暂停执行任务
* @param task
*/
const pauseTask = async (task) => {
let res = await pauseRunTask(task.id);
if (res.code === 200) {
@ -307,6 +341,10 @@ const pauseTask = async (task) => {
}
}
/**
* 停止执行任务
* @param task
*/
const stopTask = async (task) => {
let res = await stopRunTask(task.id);
if (res.code === 200) {
@ -315,6 +353,10 @@ const stopTask = async (task) => {
}
}
/**
* 删除执行任务
* @param task
*/
const removeTask = async (task) => {
ElMessageBox.confirm('确定要删除该任务吗?', '提示', {
confirmButtonText: '确定',
@ -356,6 +398,9 @@ const taskRunningInfo = ref({})
const socketChannelCallbacks = ref({})
const channel = 'InspectionTaskInstance';
/**
* 订阅websocket
*/
const subscribeDebounced = debounce(async (sub) => {
if (!socket) {
console.warn(`执行任务订阅失败: socket=${!!socket}`);
@ -379,19 +424,25 @@ const subscribeDebounced = debounce(async (sub) => {
const callback = (data) => {
if (data !== '500') {
if (data.status !== 0) {
let activeStep = pointList_runintTask.value[data.taskId].findIndex(item => item.detectItemId === data.itemId) || 0
let activeStep = pointList_runningTask.value[data.taskId].findIndex(item => item.detectItemId === data.itemId) || 0
if (data.progress == 100) {
activeStep += 1
}
taskRunningInfo.value[data.insId] = {
taskRunningInfo.value[data.taskInstanceId] = {
...data,
activeStep
}
if (!taskLogList.value[data.taskInstanceId]) {
taskLogList.value[data.taskInstanceId] = []
}
taskLogList.value[data.taskInstanceId].push({
...data
})
} else {
delete taskRunningInfo.value[data.insId]
delete taskRunningInfo.value[data.taskInstanceId]
delete taskLogList.value[data.taskInstanceId]
getList()
}
console.log('taskRunningInfo', taskRunningInfo.value)
} else {
//
subscribeDebounced(false);
@ -408,6 +459,15 @@ const subscribeDebounced = debounce(async (sub) => {
}
}, 300);
const logDialogVisible = ref(false)
const logDialogData = ref([])
const handleOpenLog = async (row) => {
logDialogVisible.value = true
const res = await getTaskLogListApi({ taskInstanceId: row.id })
if (res.code === 200) {
logDialogData.value = res.rows
}
}
onMounted(() => {
getTaskData();
getRobotData();
@ -559,32 +619,7 @@ onUnmounted(() => {
.run-log-container {
.log-container {
margin-top: 12px;
max-height: 150px;
overflow-y: auto;
background: #fff;
border: 1px solid #eee;
border-radius: 8px;
padding: 12px;
.log {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 8px;
.time {
color: #999;
font-size: 14px;
}
.context {
color: #333;
font-size: 14px;
}
}
}
}
}
}

View File

@ -0,0 +1,73 @@
<template>
<div class="log-container" :style="{ 'height': height + 'px' }" ref="messageContainer">
<div class="log" v-for="log, index in list">
<div class="time">{{ formatDate(log.logTime) }}</div>
<div class="context">{{ log.logContent }}</div>
</div>
</div>
</template>
<script setup>
import { formatDate } from "@/utils/index";
import { watch } from "vue";
const props = defineProps({
list: {
type: Array,
default: () => []
},
height: {
type: Number,
default: 150
}
})
const messageContainer = ref(null)
const SCROLL_DELAY = 100
const scrollToBottom = () => {
console.log(123, messageContainer.value)
nextTick(() => {
setTimeout(() => {
if (messageContainer.value) {
console.log('messageContainer.value.scrollHeight', messageContainer.value.scrollHeight)
messageContainer.value.scrollTop = messageContainer.value.scrollHeight
}
}, SCROLL_DELAY)
})
}
watch(() => props.list, () => {
scrollToBottom()
}, { deep: true })
onMounted(() => {
scrollToBottom()
})
</script>
<style lang="scss" scoped>
.log-container {
margin-top: 12px;
overflow-y: auto;
background: #fff;
border: 1px solid #eee;
border-radius: 8px;
padding: 12px;
.log {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 8px;
.time {
color: #999;
font-size: 14px;
}
.context {
color: #333;
font-size: 14px;
}
}
}
</style>