2025-09-19 14:38:47 +08:00
|
|
|
<template>
|
2025-09-19 15:15:20 +08:00
|
|
|
<div class="plan-list">
|
2025-09-19 14:38:47 +08:00
|
|
|
<el-card>
|
|
|
|
|
<template #header>
|
|
|
|
|
<div class="card-header">
|
2025-09-19 15:15:20 +08:00
|
|
|
<h2 class="page-title">计划管理</h2>
|
2025-09-19 14:38:47 +08:00
|
|
|
<el-button type="primary" @click="addPlan">添加计划</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<el-table :data="plans" style="width: 100%">
|
|
|
|
|
<el-table-column prop="id" label="计划ID" width="80" />
|
|
|
|
|
<el-table-column prop="name" label="计划名称" width="180" />
|
|
|
|
|
<el-table-column prop="description" label="描述" />
|
|
|
|
|
<el-table-column prop="execution_type" label="执行类型" width="120">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<el-tag v-if="scope.row.execution_type === 'automatic'">自动</el-tag>
|
|
|
|
|
<el-tag v-else>手动</el-tag>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column prop="status" label="状态" width="100">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<el-tag v-if="scope.row.status === 0" type="success">启用</el-tag>
|
|
|
|
|
<el-tag v-else-if="scope.row.status === 1" type="warning">禁用</el-tag>
|
|
|
|
|
<el-tag v-else type="info">已完成</el-tag>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="操作" width="200">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<el-button size="small" @click="editPlan(scope.row)">编辑</el-button>
|
|
|
|
|
<el-button size="small" @click="startPlan(scope.row)" :loading="startingPlanId === scope.row.id">
|
|
|
|
|
启动
|
|
|
|
|
</el-button>
|
|
|
|
|
<el-button size="small" type="danger" @click="deletePlan(scope.row)">删除</el-button>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
</el-table>
|
|
|
|
|
</el-card>
|
|
|
|
|
|
2025-09-21 13:32:51 +08:00
|
|
|
<!-- 使用新的计划表单组件 -->
|
|
|
|
|
<PlanForm
|
|
|
|
|
v-model:visible="dialogVisible"
|
|
|
|
|
:plan-data="currentPlan"
|
|
|
|
|
:is-edit="isEdit"
|
|
|
|
|
@success="handlePlanSuccess"
|
|
|
|
|
@cancel="handlePlanCancel"
|
|
|
|
|
/>
|
2025-09-19 15:15:20 +08:00
|
|
|
</div>
|
2025-09-19 14:38:47 +08:00
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
import apiClient from '../api/index.js';
|
2025-09-21 13:32:51 +08:00
|
|
|
import PlanForm from './PlanForm.vue';
|
2025-09-19 14:38:47 +08:00
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
name: 'PlanList',
|
2025-09-21 13:32:51 +08:00
|
|
|
components: {
|
|
|
|
|
PlanForm
|
|
|
|
|
},
|
2025-09-19 14:38:47 +08:00
|
|
|
data() {
|
|
|
|
|
return {
|
|
|
|
|
plans: [],
|
|
|
|
|
dialogVisible: false,
|
|
|
|
|
isEdit: false,
|
|
|
|
|
currentPlan: {
|
|
|
|
|
id: null,
|
|
|
|
|
name: '',
|
|
|
|
|
description: '',
|
|
|
|
|
execution_type: 'automatic',
|
2025-09-21 13:32:51 +08:00
|
|
|
execute_num: 0,
|
|
|
|
|
cron_expression: ''
|
2025-09-19 14:38:47 +08:00
|
|
|
},
|
|
|
|
|
startingPlanId: null
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
async mounted() {
|
|
|
|
|
await this.loadPlans();
|
|
|
|
|
},
|
|
|
|
|
methods: {
|
|
|
|
|
// 加载计划列表
|
|
|
|
|
async loadPlans() {
|
|
|
|
|
try {
|
|
|
|
|
const response = await apiClient.plans.list();
|
|
|
|
|
this.plans = response.data?.plans || [];
|
|
|
|
|
} catch (err) {
|
|
|
|
|
this.$message.error('加载计划列表失败: ' + (err.message || '未知错误'));
|
|
|
|
|
console.error('加载计划列表失败:', err);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
addPlan() {
|
|
|
|
|
this.currentPlan = {
|
|
|
|
|
id: null,
|
|
|
|
|
name: '',
|
|
|
|
|
description: '',
|
2025-09-21 13:32:51 +08:00
|
|
|
execution_type: 'automatic',
|
|
|
|
|
execute_num: 0,
|
|
|
|
|
cron_expression: ''
|
2025-09-19 14:38:47 +08:00
|
|
|
};
|
|
|
|
|
this.isEdit = false;
|
|
|
|
|
this.dialogVisible = true;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
editPlan(plan) {
|
|
|
|
|
this.currentPlan = { ...plan };
|
|
|
|
|
this.isEdit = true;
|
|
|
|
|
this.dialogVisible = true;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
async deletePlan(plan) {
|
|
|
|
|
try {
|
|
|
|
|
await this.$confirm('确认删除该计划吗?', '提示', {
|
|
|
|
|
confirmButtonText: '确定',
|
|
|
|
|
cancelButtonText: '取消',
|
|
|
|
|
type: 'warning'
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await apiClient.plans.delete(plan.id);
|
|
|
|
|
this.$message.success('删除成功');
|
|
|
|
|
await this.loadPlans();
|
|
|
|
|
} catch (err) {
|
|
|
|
|
if (err !== 'cancel') {
|
|
|
|
|
this.$message.error('删除失败: ' + (err.message || '未知错误'));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
async startPlan(plan) {
|
|
|
|
|
try {
|
|
|
|
|
this.startingPlanId = plan.id;
|
|
|
|
|
await apiClient.plans.start(plan.id);
|
|
|
|
|
this.$message.success('计划启动成功');
|
|
|
|
|
await this.loadPlans();
|
|
|
|
|
} catch (err) {
|
|
|
|
|
this.$message.error('启动失败: ' + (err.message || '未知错误'));
|
|
|
|
|
} finally {
|
|
|
|
|
this.startingPlanId = null;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
2025-09-21 13:32:51 +08:00
|
|
|
// 处理计划表单提交成功
|
|
|
|
|
async handlePlanSuccess(planData) {
|
2025-09-19 14:38:47 +08:00
|
|
|
try {
|
|
|
|
|
if (this.isEdit) {
|
|
|
|
|
// 编辑计划
|
2025-09-21 13:32:51 +08:00
|
|
|
await apiClient.plans.update(planData.id, planData);
|
2025-09-19 14:38:47 +08:00
|
|
|
this.$message.success('计划更新成功');
|
|
|
|
|
} else {
|
|
|
|
|
// 添加新计划
|
2025-09-21 13:32:51 +08:00
|
|
|
const planRequest = {
|
|
|
|
|
...planData,
|
|
|
|
|
content_type: 'tasks' // 默认使用任务类型
|
2025-09-19 14:38:47 +08:00
|
|
|
};
|
|
|
|
|
|
2025-09-21 13:32:51 +08:00
|
|
|
await apiClient.plans.create(planRequest);
|
2025-09-19 14:38:47 +08:00
|
|
|
this.$message.success('计划添加成功');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await this.loadPlans();
|
|
|
|
|
} catch (err) {
|
|
|
|
|
this.$message.error('保存失败: ' + (err.message || '未知错误'));
|
|
|
|
|
}
|
2025-09-21 13:32:51 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 处理计划表单取消
|
|
|
|
|
handlePlanCancel() {
|
|
|
|
|
this.dialogVisible = false;
|
2025-09-19 14:38:47 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
2025-09-19 15:15:20 +08:00
|
|
|
.plan-list {
|
|
|
|
|
padding: 20px;
|
|
|
|
|
max-width: 1200px;
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-19 14:38:47 +08:00
|
|
|
.card-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
align-items: center;
|
2025-09-19 15:15:20 +08:00
|
|
|
padding: 15px 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.page-title {
|
|
|
|
|
margin: 0;
|
|
|
|
|
font-size: 1.5rem;
|
|
|
|
|
font-weight: bold;
|
2025-09-19 14:38:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dialog-footer {
|
|
|
|
|
text-align: right;
|
|
|
|
|
}
|
2025-09-19 15:15:20 +08:00
|
|
|
|
|
|
|
|
@media (max-width: 768px) {
|
|
|
|
|
.plan-list {
|
|
|
|
|
padding: 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card-header {
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 15px;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-09-19 14:38:47 +08:00
|
|
|
</style>
|