This commit is contained in:
2025-10-31 21:56:33 +08:00
parent fcd1b1c140
commit 840fb98988
3 changed files with 264 additions and 235 deletions

View File

@@ -197,10 +197,7 @@ import http from '../utils/http';
* @returns {Promise<Array<PigBatchResponseDTO>>}
*/
export const getPigBatches = (params) => {
const newParams = {
is_active: params.is_active,
};
return http.get('/api/v1/pig-batches', { params: newParams });
return http.get('/api/v1/pig-batches', params);
};
/**

View File

@@ -1,7 +1,7 @@
<template>
<div class="plan-detail">
<div v-if="loading" class="loading">
<el-skeleton animated />
<el-skeleton animated/>
</div>
<div v-else-if="error" class="error">
<el-alert
@@ -20,9 +20,15 @@
<span>{{ plan.name }} - 内容</span>
<div>
<template v-if="!isSubPlan">
<el-button class="button" type="primary" @click="savePlanContent" v-if="isEditingContent" :disabled="plan.plan_type === '系统任务'">保存</el-button>
<el-button class="button" type="danger" @click="cancelEdit" v-if="isEditingContent" :disabled="plan.plan_type === '系统任务'">取消</el-button>
<el-button class="button" @click="enterEditMode" v-else :disabled="plan.plan_type === '系统任务'">编辑内容</el-button>
<el-button class="button" type="primary" @click="savePlanContent" v-if="isEditingContent"
:disabled="plan.plan_type === '系统任务'">保存
</el-button>
<el-button class="button" type="danger" @click="cancelEdit" v-if="isEditingContent"
:disabled="plan.plan_type === '系统任务'">取消
</el-button>
<el-button class="button" @click="enterEditMode" v-else :disabled="plan.plan_type === '系统任务'">
编辑内容
</el-button>
</template>
<!-- Dynamic Add Buttons -->
@@ -33,14 +39,16 @@
size="small"
@click="showAddSubPlanDialog"
:disabled="plan.plan_type === '系统任务'"
>增加子计划</el-button>
>增加子计划
</el-button>
<el-button
v-if="plan.content_type === 'tasks' || !plan.content_type"
type="primary"
size="small"
@click="showTaskEditorDialog()"
:disabled="plan.plan_type === '系统任务'"
>增加子任务</el-button>
>增加子任务
</el-button>
</template>
</div>
</div>
@@ -63,8 +71,12 @@
延时: {{ task.parameters.delay_duration }}
</p>
<el-button-group v-if="isEditingContent">
<el-button type="primary" size="small" @click="editTask(task)" :disabled="plan.plan_type === '系统任务'">编辑</el-button>
<el-button type="danger" size="small" @click="deleteTask(task)" :disabled="plan.plan_type === '系统任务'">删除</el-button>
<el-button type="primary" size="small" @click="editTask(task)"
:disabled="plan.plan_type === '系统任务'">编辑
</el-button>
<el-button type="danger" size="small" @click="deleteTask(task)"
:disabled="plan.plan_type === '系统任务'">删除
</el-button>
</el-button-group>
</el-card>
</el-timeline-item>
@@ -76,13 +88,16 @@
<div v-else-if="plan.content_type === 'sub_plans'">
<h4>子计划列表</h4>
<div v-if="plan.sub_plans.length > 0">
<div v-for="(subPlan, index) in plan.sub_plans" :key="subPlan.id || 'new-subplan-' + index" class="sub-plan-wrapper">
<div v-for="(subPlan, index) in plan.sub_plans" :key="subPlan.id || 'new-subplan-' + index"
class="sub-plan-wrapper">
<el-card>
<div class="sub-plan-card-content">
<!-- Pass child_plan_id to recursive PlanDetail -->
<plan-detail :plan-id="subPlan.child_plan_id" :is-sub-plan="true" />
<plan-detail :plan-id="subPlan.child_plan_id" :is-sub-plan="true"/>
<el-button-group v-if="isEditingContent" class="sub-plan-actions">
<el-button type="danger" size="small" @click="deleteSubPlan(subPlan)" :disabled="plan.plan_type === '系统任务'">删除</el-button>
<el-button type="danger" size="small" @click="deleteSubPlan(subPlan)"
:disabled="plan.plan_type === '系统任务'">删除
</el-button>
</el-button-group>
</div>
</el-card>
@@ -137,16 +152,19 @@
>
<el-form :model="currentTaskForm" ref="taskFormRef" :rules="taskFormRules" label-width="100px">
<el-form-item label="任务类型" prop="type">
<el-select v-model="currentTaskForm.type" placeholder="请选择任务类型" style="width: 100%;" :disabled="isEditingTask || plan.plan_type === '系统任务'">
<el-select v-model="currentTaskForm.type" placeholder="请选择任务类型" style="width: 100%;"
:disabled="isEditingTask || plan.plan_type === '系统任务'">
<!-- Only Delay Task for now -->
<el-option label="延时任务" value="delay_task"></el-option>
</el-select>
</el-form-item>
<el-form-item label="任务名称" prop="name">
<el-input v-model="currentTaskForm.name" placeholder="请输入任务名称" :disabled="plan.plan_type === '系统任务'"></el-input>
<el-input v-model="currentTaskForm.name" placeholder="请输入任务名称"
:disabled="plan.plan_type === '系统任务'"></el-input>
</el-form-item>
<el-form-item label="任务描述" prop="description">
<el-input type="textarea" v-model="currentTaskForm.description" placeholder="请输入任务描述" :disabled="plan.plan_type === '系统任务'"></el-input>
<el-input type="textarea" v-model="currentTaskForm.description" placeholder="请输入任务描述"
:disabled="plan.plan_type === '系统任务'"></el-input>
</el-form-item>
<!-- Dynamic task component for specific parameters -->
<template v-if="currentTaskForm.type === 'delay_task'">
@@ -172,8 +190,8 @@
<script>
import apiClient from '../api/index.js';
import { ElMessage, ElMessageBox } from 'element-plus';
import { ArrowDown } from '@element-plus/icons-vue';
import {ElMessage, ElMessageBox} from 'element-plus';
import {ArrowDown} from '@element-plus/icons-vue';
import DelayTaskEditor from './tasks/DelayTask.vue';
export default {
@@ -227,15 +245,15 @@ export default {
parameters: {},
},
taskFormRules: {
type: [{ required: true, message: '请选择任务类型', trigger: 'change' }],
name: [{ required: true, message: '请输入任务名称', trigger: 'blur' }],
type: [{required: true, message: '请选择任务类型', trigger: 'change'}],
name: [{required: true, message: '请输入任务名称', trigger: 'blur'}],
// Rule for delay_duration will be added/removed dynamically
},
};
},
computed: {
delayDurationRules() {
return [{ required: true, message: '请输入延时时间', trigger: 'blur' }];
return [{required: true, message: '请输入延时时间', trigger: 'blur'}];
},
},
watch: {
@@ -358,7 +376,7 @@ export default {
},
async fetchAvailablePlans() {
try {
const response = await apiClient.plans.getPlans({ plan_type: '自定义任务' });
const response = await apiClient.plans.getPlans({plan_type: '自定义任务', page: 1, page_size: 1000});
this.availablePlans = response.data.plans.filter(p =>
p.id !== this.planId
);
@@ -447,7 +465,7 @@ export default {
this.isEditingTask = false;
this.editingTaskOriginalId = null;
// For new tasks, ensure delay_duration is reactive from start
this.currentTaskForm.parameters = { delay_duration: null };
this.currentTaskForm.parameters = {delay_duration: null};
console.log("PlanDetail: Prepared currentTaskForm for adding:", JSON.parse(JSON.stringify(this.currentTaskForm)));
}
// Manually trigger watch for type to ensure rules and default parameters are set
@@ -479,15 +497,14 @@ export default {
name: this.currentTaskForm.name,
description: this.currentTaskForm.description,
type: this.currentTaskForm.type === 'delay_task' ? 'waiting' : this.currentTaskForm.type,
parameters: { ...this.currentTaskForm.parameters }, // Deep copy parameters to ensure new reference
parameters: {...this.currentTaskForm.parameters}, // Deep copy parameters to ensure new reference
};
this.plan.tasks.splice(index, 1, updatedTask); // Replace the old task with the new one
ElMessage.success(`子任务 "${this.currentTaskForm.name}" 已更新`);
} else {
ElMessage.error('未找到要编辑的任务');
}
}
else {
} else {
// Add a new task
const newTask = {
id: Date.now(),
@@ -495,7 +512,7 @@ export default {
type: this.currentTaskForm.type === 'delay_task' ? 'waiting' : this.currentTaskForm.type,
name: this.currentTaskForm.name,
description: this.currentTaskForm.description,
parameters: { ...this.currentTaskForm.parameters }, // Deep copy parameters to ensure new reference
parameters: {...this.currentTaskForm.parameters}, // Deep copy parameters to ensure new reference
};
this.plan.tasks = [...this.plan.tasks, newTask]; // Create a new array reference
ElMessage.success(`子任务 "${newTask.name}" 已添加`);
@@ -544,35 +561,43 @@ export default {
.plan-detail {
margin-top: 10px;
}
.loading, .error {
padding: 20px;
text-align: center;
}
.retry-btn {
margin-top: 15px;
}
.sub-plan-wrapper {
margin-bottom: 10px;
}
.sub-plan-container {
margin-left: 20px;
margin-top: 10px;
border-left: 2px solid #ebeef5;
padding-left: 10px;
}
.sub-plan-card-content {
display: flex;
flex-direction: column;
gap: 10px;
}
.sub-plan-actions {
align-self: flex-end;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
/* 调整子计划卡片内部的header避免重复样式 */
.sub-plan-container .card-header {
padding: 0;

View File

@@ -6,12 +6,15 @@
<div class="title-container">
<h2 class="page-title">计划管理</h2>
<el-button type="text" @click="loadPlans" class="refresh-btn" title="刷新计划列表">
<el-icon :size="20"><Refresh /></el-icon>
<el-icon :size="20">
<Refresh/>
</el-icon>
</el-button>
</div>
<div class="filter-and-add">
<el-select v-model="planTypeFilter" placeholder="选择计划类型" @change="loadPlans" style="width: 150px; margin-right: 10px;">
<el-select v-model="planTypeFilter" placeholder="选择计划类型" @change="loadPlans"
style="width: 150px; margin-right: 10px;">
<el-option label="所有任务" value="所有任务"></el-option>
<el-option label="自定义任务" value="自定义任务"></el-option>
<el-option label="系统任务" value="系统任务"></el-option>
@@ -23,7 +26,7 @@
<!-- 加载状态 -->
<div v-if="loading" class="loading">
<el-skeleton animated />
<el-skeleton animated/>
</div>
<!-- 错误状态 -->
@@ -47,9 +50,9 @@
:fit="true"
:scrollbar-always-on="true"
@sort-change="handleSortChange">
<el-table-column prop="id" label="计划ID" min-width="100" sortable="custom" />
<el-table-column prop="name" label="计划名称" min-width="120" sortable="custom" />
<el-table-column prop="description" label="计划描述" min-width="150" />
<el-table-column prop="id" label="计划ID" min-width="100" sortable="custom"/>
<el-table-column prop="name" label="计划名称" min-width="120" sortable="custom"/>
<el-table-column prop="description" label="计划描述" min-width="150"/>
<el-table-column prop="execution_type" label="执行类型" min-width="150" sortable="custom">
<template #default="scope">
<el-tag v-if="scope.row.execution_type === 'manual'">手动</el-tag>
@@ -57,7 +60,7 @@
<el-tag v-else type="warning">自动({{ scope.row.execute_num }})</el-tag>
</template>
</el-table-column>
<el-table-column prop="execute_count" label="已执行次数" min-width="120" sortable="custom" />
<el-table-column prop="execute_count" label="已执行次数" min-width="120" sortable="custom"/>
<el-table-column prop="status" label="状态" min-width="100" sortable="custom">
<template #default="scope">
<el-tag v-if="scope.row.status === 0" type="danger">禁用计划</el-tag>
@@ -73,7 +76,8 @@
</el-table-column>
<el-table-column label="操作" width="280">
<template #default="scope">
<el-button size="small" @click="editPlan(scope.row)" :disabled="scope.row.plan_type === '系统任务'">编辑</el-button>
<el-button size="small" @click="editPlan(scope.row)" :disabled="scope.row.plan_type === '系统任务'">编辑
</el-button>
<el-button size="small" @click="showDetails(scope.row)">详情</el-button>
<el-button
size="small"
@@ -84,7 +88,9 @@
>
{{ scope.row.status === 1 ? '停止' : '启动' }}
</el-button>
<el-button size="small" type="danger" @click="deletePlan(scope.row)" :disabled="scope.row.plan_type === '系统任务'">删除</el-button>
<el-button size="small" type="danger" @click="deletePlan(scope.row)"
:disabled="scope.row.plan_type === '系统任务'">删除
</el-button>
</template>
</el-table-column>
</el-table>
@@ -122,7 +128,7 @@
</template>
<script>
import { Refresh } from '@element-plus/icons-vue';
import {Refresh} from '@element-plus/icons-vue';
import apiClient from '../../api';
import PlanForm from '../../components/PlanForm.vue';
import PlanDetail from '../../components/PlanDetail.vue'; // 导入新组件
@@ -168,7 +174,7 @@ export default {
this.error = null;
try {
const response = await apiClient.plans.getPlans({ plan_type: this.planTypeFilter }); // 传递 plan_typeFilter
const response = await apiClient.plans.getPlans({plan_type: this.planTypeFilter, page: 1, page_size: 1000}); // 传递 plan_typeFilter
let fetchedPlans = response.data?.plans || [];
// Default sort by ID ascending
fetchedPlans.sort((a, b) => a.id - b.id);
@@ -183,7 +189,7 @@ export default {
},
// 处理表格排序
handleSortChange({ prop, order }) {
handleSortChange({prop, order}) {
if (!order) {
// 恢复原始顺序
this.plans = [...this.originalPlans];
@@ -259,7 +265,7 @@ export default {
},
editPlan(plan) {
this.currentPlan = { ...plan };
this.currentPlan = {...plan};
this.isEdit = true;
this.dialogVisible = true;
},
@@ -411,6 +417,7 @@ export default {
.plan-list {
padding: 10px;
}
.card-header {
flex-direction: column;
gap: 15px;