dman-web-admin/src/views/system/TaskManager.vue
2025-03-12 08:22:08 +08:00

323 lines
8.3 KiB
Vue

<template>
<page-container>
<div class="task-manager">
<div class="system-status-card">
<a-card title="系统状态" :bordered="false">
<a-descriptions :column="3">
<a-descriptions-item label="运行状态">
<a-tag :color="systemStatus.running ? 'success' : 'error'">
{{ systemStatus.running ? '运行中' : '已停止' }}
</a-tag>
</a-descriptions-item>
<a-descriptions-item label="任务数量">
{{ systemStatus.job_count || 0 }}
</a-descriptions-item>
<a-descriptions-item label="时区">
{{ systemStatus.timezone || 'Asia/Shanghai' }}
</a-descriptions-item>
</a-descriptions>
</a-card>
</div>
<div class="table-header">
<h1>定时任务管理</h1>
<a-space>
<a-button type="primary" @click="refreshData">
<template #icon><reload-outlined /></template>
刷新
</a-button>
</a-space>
</div>
<a-table
:columns="columns"
:data-source="tableData"
:pagination="pagination"
:loading="loading"
@change="handleTableChange"
row-key="id"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'status'">
<a-tag :color="record.active ? 'success' : 'default'">
{{ record.active ? '运行中' : '已暂停' }}
</a-tag>
</template>
<template v-if="column.key === 'next_run_time'">
{{ formatDateTime(record.next_run_time) }}
</template>
<template v-if="column.key === 'action'">
<a-space>
<a-button
v-if="record.active"
type="primary"
danger
size="small"
:loading="record.loading"
@click="pauseJob(record)"
>
暂停
</a-button>
<a-button
v-else
type="primary"
size="small"
:loading="record.loading"
@click="resumeJob(record)"
>
恢复
</a-button>
</a-space>
</template>
</template>
</a-table>
</div>
</page-container>
</template>
<script>
import { defineComponent, ref, onMounted } from 'vue'
import { message } from 'ant-design-vue'
import request from '@/utils/request'
import PageContainer from '@/components/PageContainer.vue'
import { ReloadOutlined } from '@ant-design/icons-vue'
import dayjs from 'dayjs'
export default defineComponent({
name: 'TaskManager',
components: {
PageContainer,
ReloadOutlined
},
setup() {
const loading = ref(false)
const tableData = ref([])
const systemStatus = ref({
running: false,
job_count: 0,
timezone: 'Asia/Shanghai'
})
const pagination = ref({
current: 1,
pageSize: 10,
total: 0,
showSizeChanger: true,
showTotal: (total) => `${total} 条记录`
})
const columns = [
{
title: 'ID',
dataIndex: 'id',
key: 'id',
width: 80,
align: 'center'
},
{
title: '任务名称',
dataIndex: 'name',
key: 'name',
width: 200
},
{
title: '触发器',
dataIndex: 'trigger',
key: 'trigger',
width: 250,
ellipsis: true
},
{
title: '下次执行时间',
key: 'next_run_time',
dataIndex: 'next_run_time',
width: 180
},
{
title: '状态',
key: 'status',
width: 100,
align: 'center'
},
{
title: '操作',
key: 'action',
width: 120,
align: 'center',
fixed: 'right'
}
]
// 格式化日期时间
const formatDateTime = (value) => {
if (!value) return '-'
return dayjs(value).format('YYYY-MM-DD HH:mm:ss')
}
// 获取系统状态
const fetchSystemStatus = async () => {
try {
const res = await request.get('/api/scheduler/status')
if (res.code === 200) {
systemStatus.value = res.data
} else {
message.error(res.message || '获取系统状态失败')
}
} catch (error) {
console.error('获取系统状态失败:', error)
message.error('获取系统状态失败')
}
}
// 获取任务列表
const fetchData = async () => {
try {
loading.value = true
const res = await request.get('/api/scheduler/jobs')
if (res.code === 200) {
// 为每个任务添加loading状态
tableData.value = (res.data || []).map(item => ({
...item,
loading: false
}))
pagination.value.total = tableData.value.length
} else {
message.error(res.message || '获取数据失败')
}
} catch (error) {
console.error('获取任务列表失败:', error)
message.error('获取数据失败')
} finally {
loading.value = false
}
}
// 刷新数据
const refreshData = async () => {
await Promise.all([fetchSystemStatus(), fetchData()])
message.success('数据已刷新')
}
// 处理表格分页变化
const handleTableChange = (pag) => {
pagination.value.current = pag.current
pagination.value.pageSize = pag.pageSize
fetchData()
}
// 暂停任务
const pauseJob = async (record) => {
try {
// 设置当前任务的loading状态
const index = tableData.value.findIndex(item => item.id === record.id)
if (index !== -1) {
tableData.value[index].loading = true
}
const res = await request.post(`/api/scheduler/jobs/${record.id}/pause`)
if (res.code === 200) {
message.success('任务已暂停')
// 更新任务状态
if (index !== -1) {
tableData.value[index].active = false
}
// 刷新系统状态
fetchSystemStatus()
} else {
message.error(res.message || '操作失败')
}
} catch (error) {
console.error('暂停任务失败:', error)
message.error('操作失败')
} finally {
// 清除loading状态
const index = tableData.value.findIndex(item => item.id === record.id)
if (index !== -1) {
tableData.value[index].loading = false
}
}
}
// 恢复任务
const resumeJob = async (record) => {
try {
// 设置当前任务的loading状态
const index = tableData.value.findIndex(item => item.id === record.id)
if (index !== -1) {
tableData.value[index].loading = true
}
const res = await request.post(`/api/scheduler/jobs/${record.id}/resume`)
if (res.code === 200) {
message.success('任务已恢复')
// 更新任务状态
if (index !== -1) {
tableData.value[index].active = true
}
// 刷新系统状态
fetchSystemStatus()
} else {
message.error(res.message || '操作失败')
}
} catch (error) {
console.error('恢复任务失败:', error)
message.error('操作失败')
} finally {
// 清除loading状态
const index = tableData.value.findIndex(item => item.id === record.id)
if (index !== -1) {
tableData.value[index].loading = false
}
}
}
onMounted(() => {
refreshData()
})
return {
loading,
tableData,
columns,
pagination,
systemStatus,
formatDateTime,
handleTableChange,
pauseJob,
resumeJob,
refreshData
}
}
})
</script>
<style scoped>
.task-manager {
background: #fff;
padding: 16px;
}
.system-status-card {
margin-bottom: 24px;
}
.table-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.table-header h1 {
margin: 0;
font-size: 18px;
}
:deep(.ant-table-content) {
overflow-x: auto;
}
:deep(.ant-descriptions-item-label) {
font-weight: 500;
}
</style>