任务模块
This commit is contained in:
parent
5e38aa11b6
commit
cbaa331831
@ -1,11 +1,27 @@
|
||||
//响应拦截器
|
||||
import axios from 'axios'; // 引入axios用于创建新请求
|
||||
import { ElMessage } from 'element-plus'; // 引入Element Plus的消息组件
|
||||
import store from '../../store'; // 引入store用于统一处理登出
|
||||
|
||||
export function setupResponseInterceptors(instance) {
|
||||
// 用于存储正在刷新token的Promise
|
||||
let isRefreshing = false;
|
||||
// 存储等待token刷新的请求队列
|
||||
let requests = [];
|
||||
|
||||
// 统一处理登录过期的函数
|
||||
const handleTokenExpired = (message = '登录已过期,请重新登录') => {
|
||||
// 显示提示消息
|
||||
ElMessage.error(message);
|
||||
|
||||
// 使用store的logout action清除认证状态
|
||||
store.dispatch('logout');
|
||||
|
||||
// 延迟跳转,让用户有时间看到提示
|
||||
setTimeout(() => {
|
||||
window.location.href = '/login';
|
||||
}, 1500);
|
||||
};
|
||||
|
||||
instance.interceptors.response.use(
|
||||
response => {
|
||||
@ -20,70 +36,110 @@ export function setupResponseInterceptors(instance) {
|
||||
return data;
|
||||
},
|
||||
async error => {
|
||||
console.log('响应错误:', error);
|
||||
|
||||
// 获取原始请求配置
|
||||
const originalRequest = error.config;
|
||||
|
||||
// 判断是否401错误(token过期)且没有在刷新中
|
||||
if (error.response?.status === 401 && !originalRequest._retry) {
|
||||
// 标记该请求已尝试过重试
|
||||
originalRequest._retry = true;
|
||||
|
||||
// 如果当前没有在刷新token
|
||||
if (!isRefreshing) {
|
||||
isRefreshing = true;
|
||||
|
||||
// 检查是否有响应
|
||||
if (!error.response) {
|
||||
// 网络错误或请求被中断
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
// 检查错误响应中的各种可能的错误信息位置
|
||||
const errorResponse = error.response;
|
||||
console.log('错误响应:', errorResponse);
|
||||
const errorData = errorResponse.data || {};
|
||||
const errorMessage = errorData.message || errorData.error || errorResponse.statusText || '';
|
||||
const errorStack = errorData.stack || '';
|
||||
const statusCode = errorResponse.status;
|
||||
|
||||
// 处理403错误(权限不足)
|
||||
if (errorResponse.status === "403") {
|
||||
// 显示权限不足提示,但不重定向
|
||||
ElMessage.error(errorMessage || '您没有权限执行此操作');
|
||||
return Promise.reject("您没有权限执行此操作");
|
||||
}
|
||||
|
||||
// 检查是否为令牌错误(检查多种可能的错误信息格式)
|
||||
const isTokenError =
|
||||
statusCode === 401 ||
|
||||
(statusCode === 500 && (
|
||||
errorMessage.includes('令牌') ||
|
||||
errorMessage.includes('token') ||
|
||||
errorMessage.includes('Token') ||
|
||||
errorStack.includes('TokenIllegal') ||
|
||||
errorStack.includes('TheTokenIllegalException') ||
|
||||
(typeof errorData === 'string' && errorData.includes('令牌'))
|
||||
));
|
||||
|
||||
// 如果是令牌错误,尝试刷新令牌或直接跳转登录页
|
||||
if (isTokenError) {
|
||||
// 如果是401错误且没有在刷新中,尝试刷新令牌
|
||||
if (statusCode === 401 && !originalRequest._retry) {
|
||||
// 标记该请求已尝试过重试
|
||||
originalRequest._retry = true;
|
||||
|
||||
try {
|
||||
// 从localStorage获取refreshToken
|
||||
const refreshToken = localStorage.getItem('refreshToken');
|
||||
// 如果当前没有在刷新token
|
||||
if (!isRefreshing) {
|
||||
isRefreshing = true;
|
||||
|
||||
if (!refreshToken) {
|
||||
// 无refreshToken,跳转到登录页
|
||||
window.location.href = '/login';
|
||||
return Promise.reject(error);
|
||||
try {
|
||||
// 从localStorage获取refreshToken
|
||||
const refreshToken = localStorage.getItem('refreshToken');
|
||||
|
||||
if (!refreshToken) {
|
||||
// 无refreshToken,跳转到登录页
|
||||
handleTokenExpired();
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
// 创建表单数据
|
||||
const formData = new FormData();
|
||||
formData.append('refreshToken', refreshToken);
|
||||
|
||||
// 调用刷新token接口
|
||||
const response = await axios.post('/admin/getAccessToken', formData);
|
||||
|
||||
// 获取新的token
|
||||
const { accessToken, refreshToken: newRefreshToken } = response.data.data;
|
||||
|
||||
// 更新本地存储
|
||||
localStorage.setItem('accessToken', accessToken);
|
||||
if (newRefreshToken) {
|
||||
localStorage.setItem('refreshToken', newRefreshToken);
|
||||
}
|
||||
|
||||
// 更新当前请求的Authorization头
|
||||
originalRequest.headers.Authorization = accessToken;
|
||||
|
||||
// 执行队列中的所有请求
|
||||
requests.forEach(cb => cb(accessToken));
|
||||
requests = [];
|
||||
|
||||
// 重新发送之前失败的请求
|
||||
return instance(originalRequest);
|
||||
} catch (refreshError) {
|
||||
// 刷新token失败,清除token并跳转到登录页
|
||||
handleTokenExpired();
|
||||
return Promise.reject(refreshError);
|
||||
} finally {
|
||||
isRefreshing = false;
|
||||
}
|
||||
|
||||
// 创建表单数据
|
||||
const formData = new FormData();
|
||||
formData.append('refreshToken', refreshToken);
|
||||
|
||||
// 调用刷新token接口
|
||||
const response = await axios.post('/admin/getAccessToken', formData);
|
||||
|
||||
// 获取新的token
|
||||
const { accessToken, refreshToken: newRefreshToken } = response.data.data;
|
||||
|
||||
// 更新本地存储
|
||||
localStorage.setItem('accessToken', accessToken);
|
||||
if (newRefreshToken) {
|
||||
localStorage.setItem('refreshToken', newRefreshToken);
|
||||
}
|
||||
|
||||
// 更新当前请求的Authorization头
|
||||
originalRequest.headers.Authorization = accessToken;
|
||||
|
||||
// 执行队列中的所有请求
|
||||
requests.forEach(cb => cb(accessToken));
|
||||
requests = [];
|
||||
|
||||
// 重新发送之前失败的请求
|
||||
return instance(originalRequest);
|
||||
} catch (refreshError) {
|
||||
// 刷新token失败,清除token并跳转到登录页
|
||||
localStorage.removeItem('accessToken');
|
||||
localStorage.removeItem('refreshToken');
|
||||
window.location.href = '/login';
|
||||
return Promise.reject(refreshError);
|
||||
} finally {
|
||||
isRefreshing = false;
|
||||
} else {
|
||||
// 如果已经在刷新中,将请求加入队列
|
||||
return new Promise(resolve => {
|
||||
requests.push(token => {
|
||||
originalRequest.headers.Authorization = token;
|
||||
resolve(instance(originalRequest));
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// 如果已经在刷新中,将请求加入队列
|
||||
return new Promise(resolve => {
|
||||
requests.push(token => {
|
||||
originalRequest.headers.Authorization = token;
|
||||
resolve(instance(originalRequest));
|
||||
});
|
||||
});
|
||||
// 其他令牌错误,直接处理登录过期
|
||||
handleTokenExpired();
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
13
src/api/modules/task.js
Normal file
13
src/api/modules/task.js
Normal file
@ -0,0 +1,13 @@
|
||||
import instance from '../../utils/axios.js'
|
||||
|
||||
// 任务相关API
|
||||
const taskApi = {
|
||||
// 获取运行中的任务列表
|
||||
getRunningTasks: () => instance.get('/task/test'),
|
||||
|
||||
// 停止指定任务
|
||||
stopTask: (taskId) => instance.get(`/task/stopTask?taskId=${taskId}`),
|
||||
};
|
||||
|
||||
// 导出模块
|
||||
export { taskApi };
|
||||
@ -57,10 +57,6 @@
|
||||
{
|
||||
title: '权限管理',
|
||||
path: '/user/permission'
|
||||
},
|
||||
{
|
||||
title: '用户角色管理',
|
||||
path: '/user/userRole'
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -164,6 +160,19 @@
|
||||
}]
|
||||
}]
|
||||
},
|
||||
{
|
||||
title: '任务管理',
|
||||
path: '/task',
|
||||
icon: TrendCharts,
|
||||
children:[{
|
||||
title: '任务列表',
|
||||
path: '/task/list',
|
||||
children:[{
|
||||
title: '任务列表',
|
||||
path: '/task/list'
|
||||
}]
|
||||
}]
|
||||
},
|
||||
{
|
||||
title: '功能模块',
|
||||
path: '/useModule',
|
||||
|
||||
@ -28,11 +28,6 @@ const routes = [{
|
||||
component: () => import('@/views/User/List.vue'),
|
||||
meta: { title: '用户列表' }
|
||||
},
|
||||
{
|
||||
path: '/user/edit',
|
||||
component: () => import('@/views/User/Edit.vue'),
|
||||
meta: { title: '新增用户' }
|
||||
},
|
||||
{
|
||||
path: '/user/role',
|
||||
component: () => import('@/views/User/Role.vue'),
|
||||
@ -43,11 +38,6 @@ const routes = [{
|
||||
component: () => import('@/views/User/Permission.vue'),
|
||||
meta: { title: '权限管理' }
|
||||
},
|
||||
{
|
||||
path: '/user/userRole',
|
||||
component: () => import('@/views/User/UserRole.vue'),
|
||||
meta: { title: '用户角色管理' }
|
||||
},
|
||||
{
|
||||
path: '/shop/list',
|
||||
component: () => import('@/views/Shop/index.vue'),
|
||||
@ -83,11 +73,6 @@ const routes = [{
|
||||
component: () => import('@/views/order/wechat/list.vue'),
|
||||
meta: { title: '订单列表' }
|
||||
},
|
||||
{
|
||||
path: '/websocket/demo',
|
||||
component: () => import('@/views/websocket/index.vue'),
|
||||
meta: { title: 'WebSocket演示' }
|
||||
},
|
||||
{
|
||||
path: '/book/selection/center',
|
||||
component: () => import('@/views/baseInfo/index.vue'),
|
||||
@ -97,6 +82,11 @@ const routes = [{
|
||||
path: '/warehouse/depot/list',
|
||||
component: () => import('@/views/Warehouse/Depot/List.vue'),
|
||||
meta: { title: '货区管理' }
|
||||
},
|
||||
{
|
||||
path: '/task/list',
|
||||
component: () => import('@/views/Task/List.vue'),
|
||||
meta: { title: '任务列表' }
|
||||
}
|
||||
]
|
||||
}]
|
||||
|
||||
219
src/views/Task/List.vue
Normal file
219
src/views/Task/List.vue
Normal file
@ -0,0 +1,219 @@
|
||||
<template>
|
||||
<div class="task-list-container">
|
||||
<el-card>
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>任务列表</span>
|
||||
<el-button type="primary" size="small" @click="refreshTasks">刷新</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<div v-if="loading" class="loading-content">
|
||||
<el-skeleton :rows="5" animated />
|
||||
</div>
|
||||
<div v-else-if="taskList.length === 0" class="empty-content">
|
||||
暂无运行中的任务
|
||||
</div>
|
||||
<el-table v-else :data="taskList" border style="width: 100%">
|
||||
<el-table-column prop="id" label="任务ID" width="180" />
|
||||
<el-table-column prop="fileName" label="文件名称" />
|
||||
<el-table-column prop="shopNames" label="店铺名称" />
|
||||
<el-table-column prop="dataNum" label="数据总数" width="100" />
|
||||
<el-table-column prop="taskStatus" label="状态" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getTaskStatusType(scope.row.taskStatus)">
|
||||
{{ getTaskStatusText(scope.row.taskStatus) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" width="160" />
|
||||
<el-table-column label="操作" width="100" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
type="danger"
|
||||
size="small"
|
||||
@click="handleStopTask(scope.row.id)"
|
||||
:loading="stoppingTasks.includes(scope.row.id)"
|
||||
:disabled="scope.row.taskStatus !== '1'"
|
||||
>
|
||||
停止任务
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-dialog v-model="detailVisible" title="任务详情" width="500px">
|
||||
<el-descriptions :column="1" border>
|
||||
<el-descriptions-item label="任务ID">{{ currentTask.id }}</el-descriptions-item>
|
||||
<el-descriptions-item label="文件名称">{{ currentTask.fileName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="店铺名称">{{ currentTask.shopNames }}</el-descriptions-item>
|
||||
<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-descriptions-item>
|
||||
<el-descriptions-item label="执行消息">
|
||||
<div class="task-msg">{{ currentTask.msg }}</div>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-dialog>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { taskApi } from '@/api/modules/task'
|
||||
|
||||
// 任务列表数据
|
||||
const taskList = ref([])
|
||||
const loading = ref(false)
|
||||
const stoppingTasks = ref([])
|
||||
const detailVisible = ref(false)
|
||||
const currentTask = ref({})
|
||||
|
||||
// 获取任务列表
|
||||
const fetchTaskList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await taskApi.getRunningTasks()
|
||||
console.log(res)
|
||||
if (res.code === 200) {
|
||||
taskList.value = res.data || []
|
||||
} else {
|
||||
ElMessage.error(res.data.message || '获取任务列表失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取任务列表出错:', error)
|
||||
ElMessage.error('获取任务列表失败: ' + (error.message || '未知错误'))
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 刷新任务列表
|
||||
const refreshTasks = () => {
|
||||
fetchTaskList()
|
||||
}
|
||||
|
||||
// 停止任务
|
||||
const handleStopTask = async (taskId) => {
|
||||
console.log("taskId",taskId)
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`确定要停止任务吗?`,
|
||||
'警告',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
|
||||
stoppingTasks.value.push(taskId)
|
||||
const res = await taskApi.stopTask(taskId)
|
||||
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('任务已成功停止')
|
||||
// 停止成功后刷新列表
|
||||
fetchTaskList()
|
||||
} else {
|
||||
ElMessage.error(res.data.message || '停止任务失败')
|
||||
}
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('停止任务出错:', error)
|
||||
ElMessage.error('停止任务失败: ' + (error.message || '未知错误'))
|
||||
}
|
||||
} finally {
|
||||
stoppingTasks.value = stoppingTasks.value.filter(id => id !== taskId)
|
||||
}
|
||||
}
|
||||
|
||||
// 任务状态显示处理
|
||||
const getTaskStatusText = (status) => {
|
||||
const statusMap = {
|
||||
'0': '未开始',
|
||||
'1': '执行中',
|
||||
'2': '已完成',
|
||||
'3': '已中止',
|
||||
'4': '执行失败'
|
||||
}
|
||||
return statusMap[status] || '未知状态'
|
||||
}
|
||||
|
||||
const getTaskStatusType = (status) => {
|
||||
const typeMap = {
|
||||
'0': 'info',
|
||||
'1': 'success',
|
||||
'2': 'success',
|
||||
'3': 'warning',
|
||||
'4': 'danger'
|
||||
}
|
||||
return typeMap[status] || 'info'
|
||||
}
|
||||
|
||||
// 查看任务详情
|
||||
const showTaskDetail = (task) => {
|
||||
currentTask.value = task
|
||||
detailVisible.value = true
|
||||
}
|
||||
|
||||
// 计算进度
|
||||
const calculateProgress = (task) => {
|
||||
if (!task || !task.msg) return 0
|
||||
|
||||
try {
|
||||
// 尝试从消息中提取进度信息
|
||||
const totalMatch = task.msg.match(/总执行数据:(\d+)条/)
|
||||
const executedMatch = task.msg.match(/已执行条数:(\d+)条/)
|
||||
|
||||
if (totalMatch && executedMatch) {
|
||||
const total = parseInt(totalMatch[1])
|
||||
const executed = parseInt(executedMatch[1])
|
||||
|
||||
if (total > 0) {
|
||||
return Math.floor((executed / total) * 100)
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
} catch (e) {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// 组件挂载时获取任务列表
|
||||
onMounted(() => {
|
||||
fetchTaskList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.task-list-container {
|
||||
padding: 20px;
|
||||
}
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.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;
|
||||
overflow-y: auto;
|
||||
padding: 8px;
|
||||
background-color: #f8f8f8;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
@ -119,8 +119,13 @@ export default {
|
||||
const res = await getRoleList()
|
||||
roleList.value = res.data || []
|
||||
} catch (error) {
|
||||
console.error('获取角色列表失败', error)
|
||||
ElMessage.error('获取角色列表失败')
|
||||
console.log(error)
|
||||
if(error.status === 403){
|
||||
ElMessage.error("您没有权限执行此操作")
|
||||
}else{
|
||||
console.error('获取角色列表失败', error)
|
||||
ElMessage.error('获取角色列表失败')
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user