323 lines
8.3 KiB
Vue
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> |