task
This commit is contained in:
parent
a821bef69f
commit
082abbb60a
@ -8,6 +8,18 @@ const taskApi = {
|
||||
|
||||
// 停止指定任务
|
||||
stopTask: (taskId) => instance.get(`/task/stopTask?taskId=${taskId}`),
|
||||
|
||||
// 获取任务日志列表
|
||||
getLogsList: (id) => instance.get(`/task/logsList?id=${id}`),
|
||||
|
||||
// 获取任务日志消息
|
||||
getLogsMsg: (id) => instance.get(`/task/logsMsg?id=${id}`),
|
||||
|
||||
// 获取任务详细日志列表
|
||||
getLogsDetailList: (taskId, shopId) => instance.get(`/task/logsDetailList/${taskId}/${shopId}`),
|
||||
|
||||
// 下载日志文件
|
||||
downloadLogs: (fileName) => instance.get(`/task/downloadLogs/${fileName}`, { responseType: 'blob' }),
|
||||
};
|
||||
|
||||
// 导出模块
|
||||
|
||||
@ -13,15 +13,8 @@
|
||||
<div v-else-if="taskList.length === 0" class="empty-content">
|
||||
暂无运行中的任务
|
||||
</div>
|
||||
<el-table
|
||||
v-else
|
||||
:data="taskList"
|
||||
border
|
||||
style="width: 100%"
|
||||
height="500"
|
||||
max-height="500"
|
||||
:header-cell-style="{ backgroundColor: '#f5f7fa', color: '#606266', textAlign: 'center' }"
|
||||
>
|
||||
<el-table v-else :data="taskList" border style="width: 100%" height="500" max-height="500"
|
||||
:header-cell-style="{ backgroundColor: '#f5f7fa', color: '#606266', textAlign: 'center' }">
|
||||
<el-table-column prop="id" label="任务ID" width="180" align="center" />
|
||||
<el-table-column prop="fileName" label="文件名称" align="center" />
|
||||
<el-table-column prop="shopNames" label="店铺名称" align="center" />
|
||||
@ -34,17 +27,27 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" width="160" align="center" />
|
||||
<el-table-column label="操作" width="100" fixed="right" align="center">
|
||||
<el-table-column label="操作" width="180" fixed="right" align="center">
|
||||
<template #default="scope">
|
||||
<div class="operation-buttons">
|
||||
<el-tooltip content="查看日志" placement="top" :hide-after="1500">
|
||||
<el-button type="primary" size="small" @click="handleViewLogs(scope.row.id)" circle>
|
||||
<el-icon><Document /></el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="停止任务" placement="top" :hide-after="1500">
|
||||
<el-button
|
||||
type="danger"
|
||||
size="small"
|
||||
@click="handleStopTask(scope.row.id)"
|
||||
:loading="stoppingTasks.includes(scope.row.id)"
|
||||
:disabled="scope.row.taskStatus !== '1'"
|
||||
circle
|
||||
>
|
||||
停止任务
|
||||
<el-icon><VideoPause /></el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@ -52,15 +55,9 @@
|
||||
|
||||
<!-- 分页控件独立于卡片外部 -->
|
||||
<div class="pagination-container">
|
||||
<el-pagination
|
||||
v-model:current-page="currentPage"
|
||||
v-model:page-size="pageSize"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="total"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
<el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize" :page-sizes="[10, 20, 50, 100]"
|
||||
layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange" />
|
||||
</div>
|
||||
|
||||
<el-dialog v-model="detailVisible" title="任务详情" width="500px">
|
||||
@ -71,16 +68,76 @@
|
||||
<el-descriptions-item label="数据总数">{{ currentTask.dataNum }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">{{ currentTask.createTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="执行进度">
|
||||
<el-progress
|
||||
:percentage="calculateProgress(currentTask)"
|
||||
:status="currentTask.taskStatus === '1' ? 'success' : 'exception'"
|
||||
/>
|
||||
<el-progress :percentage="calculateProgress(currentTask)"
|
||||
:status="currentTask.taskStatus === '1' ? 'success' : 'exception'" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="执行消息">
|
||||
<div class="task-msg">{{ currentTask.msg }}</div>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 任务日志弹窗 -->
|
||||
<el-dialog v-model="logsVisible" title="任务日志" width="900px" :close-on-click-modal="false">
|
||||
<div class="logs-container">
|
||||
<!-- 日志消息区域 -->
|
||||
<div class="logs-message-section">
|
||||
<div class="logs-message-content">
|
||||
<div v-if="logsLoading" class="loading-text">加载中...</div>
|
||||
<div v-else class="logs-text">
|
||||
<div v-for="(line, index) in formatLogMessage(logsMessage)" :key="index" class="log-line">
|
||||
{{ line }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="refresh-btn-container">
|
||||
<el-button type="primary" size="small" @click="refreshLogs" :loading="logsLoading" icon="Refresh">
|
||||
刷新
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 店铺日志列表 -->
|
||||
<div class="logs-table-section">
|
||||
<el-table :data="logsList" border style="width: 100%"
|
||||
:header-cell-style="{ backgroundColor: '#f5f7fa', color: '#606266', textAlign: 'center' }">
|
||||
<el-table-column prop="shopName" label="店铺名称" align="center" />
|
||||
<el-table-column prop="progress" label="进度" width="100" align="center">
|
||||
<template #default="scope">
|
||||
{{ scope.row.progress }}%
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间/修改时间" width="300" align="center">
|
||||
<template #default="scope">
|
||||
{{ scope.row.createTime }} / {{ scope.row.updateTime }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="150" align="center">
|
||||
<template #default="scope">
|
||||
<div class="operation-buttons">
|
||||
<el-tooltip content="查看详情" placement="top" :hide-after="1500">
|
||||
<el-button type="primary" size="small" @click="handleViewDetail(scope.row)" circle>
|
||||
<el-icon><View /></el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="下载日志" placement="top" :hide-after="1500">
|
||||
<el-button type="success" size="small" @click="handleDownload(scope.row)" circle>
|
||||
<el-icon><Download /></el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="logsVisible = false">关闭</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -88,6 +145,7 @@
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { taskApi } from '@/api/modules/task'
|
||||
import { Document, VideoPause, View, Download, Refresh } from '@element-plus/icons-vue'
|
||||
|
||||
// 任务列表数据
|
||||
const taskList = ref([])
|
||||
@ -101,6 +159,13 @@ const total = ref(0)
|
||||
const currentPage = ref(1) // 界面显示的当前页,从1开始
|
||||
const pageSize = ref(10) // 每页条数
|
||||
|
||||
// 日志相关数据
|
||||
const logsVisible = ref(false)
|
||||
const logsLoading = ref(false)
|
||||
const logsList = ref([])
|
||||
const logsMessage = ref('')
|
||||
const currentTaskId = ref('')
|
||||
|
||||
// 获取任务列表
|
||||
const fetchTaskList = async () => {
|
||||
loading.value = true
|
||||
@ -175,7 +240,7 @@ const refreshTasks = () => {
|
||||
|
||||
// 停止任务
|
||||
const handleStopTask = async (taskId) => {
|
||||
console.log("taskId",taskId)
|
||||
console.log("taskId", taskId)
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`确定要停止任务吗?`,
|
||||
@ -260,6 +325,130 @@ const calculateProgress = (task) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 查看日志
|
||||
const handleViewLogs = async (taskId) => {
|
||||
currentTaskId.value = taskId
|
||||
logsVisible.value = true
|
||||
await fetchLogs()
|
||||
}
|
||||
|
||||
// 获取日志数据
|
||||
const fetchLogs = async () => {
|
||||
logsLoading.value = true
|
||||
try {
|
||||
// 并行调用两个接口
|
||||
const [logsListRes, logsMsgRes] = await Promise.all([
|
||||
taskApi.getLogsList(currentTaskId.value),
|
||||
taskApi.getLogsMsg(currentTaskId.value)
|
||||
])
|
||||
|
||||
console.log('日志列表响应:', logsListRes)
|
||||
console.log('日志消息响应:', logsMsgRes)
|
||||
|
||||
// 处理日志列表数据
|
||||
if (logsListRes.code === 200) {
|
||||
if (Array.isArray(logsListRes.data)) {
|
||||
logsList.value = logsListRes.data
|
||||
} else if (logsListRes.data && Array.isArray(logsListRes.data.data)) {
|
||||
logsList.value = logsListRes.data.data
|
||||
} else {
|
||||
logsList.value = []
|
||||
}
|
||||
} else {
|
||||
logsList.value = []
|
||||
ElMessage.error('获取日志列表失败: ' + (logsListRes.message || '未知错误'))
|
||||
}
|
||||
|
||||
// 处理日志消息数据
|
||||
if (logsMsgRes.code === 200) {
|
||||
// 如果返回的是字符串,直接使用
|
||||
if (typeof logsMsgRes.data === 'string') {
|
||||
logsMessage.value = logsMsgRes.data
|
||||
}
|
||||
// 如果返回的是对象,尝试获取消息字段
|
||||
else if (logsMsgRes.data && typeof logsMsgRes.data === 'object') {
|
||||
logsMessage.value = logsMsgRes.data.message || logsMsgRes.data.msg || JSON.stringify(logsMsgRes.data)
|
||||
} else {
|
||||
logsMessage.value = '暂无日志消息'
|
||||
}
|
||||
} else {
|
||||
logsMessage.value = '获取日志消息失败'
|
||||
ElMessage.error('获取日志消息失败: ' + (logsMsgRes.message || '未知错误'))
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取日志数据出错:', error)
|
||||
ElMessage.error('获取日志数据失败: ' + (error.message || '未知错误'))
|
||||
logsList.value = []
|
||||
logsMessage.value = '获取日志数据失败'
|
||||
} finally {
|
||||
logsLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 刷新日志
|
||||
const refreshLogs = () => {
|
||||
fetchLogs()
|
||||
}
|
||||
|
||||
// 查看详细日志
|
||||
const handleViewDetail = async (row) => {
|
||||
try {
|
||||
const res = await taskApi.getLogsDetailList(row.taskId, row.shopId)
|
||||
console.log('详细日志响应:', res)
|
||||
|
||||
if (res.code === 200) {
|
||||
// 这里可以打开一个新的弹窗显示详细日志,或者跳转到详细页面
|
||||
ElMessage.success('查看详细日志功能待实现')
|
||||
} else {
|
||||
ElMessage.error('获取详细日志失败: ' + (res.message || '未知错误'))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取详细日志出错:', error)
|
||||
ElMessage.error('获取详细日志失败: ' + (error.message || '未知错误'))
|
||||
}
|
||||
}
|
||||
|
||||
// 下载日志
|
||||
const handleDownload = async (row) => {
|
||||
try {
|
||||
// 构建文件名,根据后端逻辑应该是 taskId + shopId + ".txt"
|
||||
const fileName = `${row.taskId}${row.shopId}.txt`
|
||||
|
||||
const response = await taskApi.downloadLogs(fileName)
|
||||
|
||||
// 创建下载链接
|
||||
const blob = new Blob([response.data])
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.download = fileName
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
window.URL.revokeObjectURL(url)
|
||||
|
||||
ElMessage.success('文件下载成功')
|
||||
} catch (error) {
|
||||
console.error('下载日志文件出错:', error)
|
||||
ElMessage.error('下载日志文件失败: ' + (error.message || '未知错误'))
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化日志消息
|
||||
const formatLogMessage = (message) => {
|
||||
if (!message) return ['暂无日志消息']
|
||||
|
||||
// 将日志消息按行分割,处理各种可能的换行符
|
||||
const lines = message.split(/\r?\n|\r/).filter(line => line.trim() !== '')
|
||||
|
||||
if (lines.length === 0) {
|
||||
return ['暂无日志消息']
|
||||
}
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
// 组件挂载时获取任务列表
|
||||
onMounted(() => {
|
||||
fetchTaskList()
|
||||
@ -270,18 +459,22 @@ onMounted(() => {
|
||||
.task-list-container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.empty-content, .loading-content {
|
||||
|
||||
.empty-content,
|
||||
.loading-content {
|
||||
min-height: 300px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.task-msg {
|
||||
white-space: pre-line;
|
||||
max-height: 150px;
|
||||
@ -290,6 +483,7 @@ onMounted(() => {
|
||||
background-color: #f8f8f8;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
margin-top: 15px;
|
||||
background-color: #fff;
|
||||
@ -300,6 +494,21 @@ onMounted(() => {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.operation-buttons {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.operation-buttons .el-button {
|
||||
margin: 0;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.operation-buttons .el-button:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
/* 表格样式 */
|
||||
:deep(.el-table) {
|
||||
border-radius: 4px;
|
||||
@ -341,4 +550,92 @@ onMounted(() => {
|
||||
padding: 8px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
/* 日志弹窗样式 */
|
||||
.logs-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.logs-message-section {
|
||||
background-color: #f5f7fa;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.logs-message-content {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.logs-text {
|
||||
color: #606266;
|
||||
line-height: 1.6;
|
||||
white-space: pre-line;
|
||||
font-size: 14px;
|
||||
min-height: 120px;
|
||||
padding: 12px;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #e4e7ed;
|
||||
}
|
||||
|
||||
.log-line {
|
||||
margin-bottom: 4px;
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
color: #909399;
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.refresh-btn-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.logs-table-section {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* 日志弹窗表格样式 */
|
||||
.logs-table-section :deep(.el-table) {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.logs-table-section :deep(.el-table th) {
|
||||
background-color: #f5f7fa !important;
|
||||
color: #606266;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.logs-table-section :deep(.el-button) {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.logs-table-section :deep(.el-button:last-child) {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
/* 弹窗底部样式 */
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* 日志弹窗标题样式 */
|
||||
:deep(.el-dialog__header) {
|
||||
padding: 20px 20px 10px;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
}
|
||||
|
||||
:deep(.el-dialog__title) {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
}
|
||||
</style>
|
||||
@ -380,7 +380,10 @@ const submitCardSecretRequest = async () => {
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取卡密失败:', error)
|
||||
ElMessage.error('获取卡密失败: ' + (error.message || '未知错误'))
|
||||
// if(error.status === 400){
|
||||
// ElMessage.error('获取卡密失败: ' + (error.message || '未知错误'))
|
||||
// }
|
||||
ElMessage.error('获取卡密失败: ' + (error.response.data.message || '未知错误'))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
<template>
|
||||
编辑/新增
|
||||
</template>
|
||||
|
||||
<script>
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
Loading…
Reference in New Issue
Block a user