Files
pig-farm-controller-fe/src/components/PlanDetail.vue

581 lines
21 KiB
Vue
Raw Normal View History

2025-09-22 17:10:21 +08:00
<template>
2025-09-22 17:21:08 +08:00
<div class="plan-detail">
2025-09-22 17:10:21 +08:00
<div v-if="loading" class="loading">
<el-skeleton animated />
</div>
<div v-else-if="error" class="error">
<el-alert
:title="'加载计划内容失败 (ID: ' + planId + ')'"
:description="error"
type="error"
show-icon
@close="error = null"
/>
<el-button type="primary" @click="fetchPlan" class="retry-btn">重新加载</el-button>
</div>
2025-09-22 18:10:12 +08:00
<div v-else-if="plan && plan.id">
2025-09-22 17:10:21 +08:00
<el-card class="box-card">
<template #header>
<div class="card-header">
<span>{{ plan.name }} - 内容</span>
2025-09-22 17:46:28 +08:00
<div>
2025-09-22 18:22:29 +08:00
<template v-if="!isSubPlan">
<el-button class="button" type="primary" @click="savePlanContent" v-if="isEditingContent">保存</el-button>
2025-09-23 19:34:19 +08:00
<el-button class="button" type="danger" @click="cancelEdit" v-if="isEditingContent">取消</el-button>
2025-09-22 18:22:29 +08:00
<el-button class="button" @click="enterEditMode" v-else>编辑内容</el-button>
</template>
2025-09-22 18:10:12 +08:00
<!-- Dynamic Add Buttons -->
<template v-if="isEditingContent">
<el-button
v-if="plan.content_type === 'sub_plans' || !plan.content_type"
type="primary"
size="small"
@click="showAddSubPlanDialog"
>增加子计划</el-button>
<el-button
v-if="plan.content_type === 'tasks' || !plan.content_type"
type="primary"
size="small"
2025-09-23 19:34:19 +08:00
@click="showTaskEditorDialog()"
2025-09-22 18:10:12 +08:00
>增加子任务</el-button>
</template>
2025-09-22 17:46:28 +08:00
</div>
2025-09-22 17:10:21 +08:00
</div>
</template>
2025-09-22 18:10:12 +08:00
<!-- Display Tasks -->
2025-09-22 17:10:21 +08:00
<div v-if="plan.content_type === 'tasks'">
<h4>任务列表</h4>
2025-09-22 18:10:12 +08:00
<el-timeline v-if="plan.tasks.length > 0">
2025-09-22 17:10:21 +08:00
<el-timeline-item
2025-09-22 18:10:12 +08:00
v-for="(task, index) in plan.tasks"
2025-09-22 17:46:28 +08:00
:key="task.id || 'new-task-' + index"
2025-09-22 18:10:12 +08:00
:timestamp="'执行顺序: ' + (task.execution_order !== undefined ? task.execution_order : index + 1)"
2025-09-22 17:10:21 +08:00
placement="top"
>
<el-card>
2025-09-22 18:22:29 +08:00
<h5>{{ task.name }} ({{ task.type === 'waiting' ? '延时任务' : '未知任务' }})</h5>
2025-09-22 17:10:21 +08:00
<p>{{ task.description }}</p>
2025-09-22 18:22:29 +08:00
<p v-if="task.type === 'waiting' && task.parameters?.delay_duration">
延时: {{ task.parameters.delay_duration }}
2025-09-22 18:10:12 +08:00
</p>
2025-09-22 17:46:28 +08:00
<el-button-group v-if="isEditingContent">
2025-09-22 17:10:21 +08:00
<el-button type="primary" size="small" @click="editTask(task)">编辑</el-button>
<el-button type="danger" size="small" @click="deleteTask(task)">删除</el-button>
</el-button-group>
</el-card>
</el-timeline-item>
</el-timeline>
2025-09-22 18:10:12 +08:00
<el-empty v-else description="暂无任务"></el-empty>
2025-09-22 17:10:21 +08:00
</div>
2025-09-22 18:10:12 +08:00
<!-- Display Sub-plans -->
2025-09-22 17:10:21 +08:00
<div v-else-if="plan.content_type === 'sub_plans'">
<h4>子计划列表</h4>
2025-09-22 18:10:12 +08:00
<div v-if="plan.sub_plans.length > 0">
2025-09-22 18:22:29 +08:00
<div v-for="(subPlan, index) in plan.sub_plans" :key="subPlan.id || 'new-subplan-' + index" class="sub-plan-wrapper">
2025-09-22 18:10:12 +08:00
<el-card>
2025-09-22 18:22:29 +08:00
<div class="sub-plan-card-content">
2025-09-22 18:10:12 +08:00
<!-- Pass child_plan_id to recursive PlanDetail -->
<plan-detail :plan-id="subPlan.child_plan_id" :is-sub-plan="true" />
2025-09-22 18:22:29 +08:00
<el-button-group v-if="isEditingContent" class="sub-plan-actions">
2025-09-22 18:10:12 +08:00
<el-button type="danger" size="small" @click="deleteSubPlan(subPlan)">删除</el-button>
</el-button-group>
</div>
</el-card>
</div>
2025-09-22 17:10:21 +08:00
</div>
2025-09-22 18:10:12 +08:00
<el-empty v-else description="暂无子计划"></el-empty>
2025-09-22 17:10:21 +08:00
</div>
2025-09-22 18:10:12 +08:00
<div v-else-if="!plan.content_type">
<el-empty description="请添加子计划或子任务"></el-empty>
2025-09-22 17:10:21 +08:00
</div>
</el-card>
</div>
<div v-else>
<el-empty description="没有计划数据"></el-empty>
</div>
2025-09-22 17:46:28 +08:00
2025-09-22 18:10:12 +08:00
<!-- Add Sub-plan Dialog -->
2025-09-22 17:46:28 +08:00
<el-dialog
v-model="addSubPlanDialogVisible"
title="选择子计划"
width="600px"
@close="resetAddSubPlanDialog"
>
<el-select
v-model="selectedSubPlanId"
placeholder="请选择一个计划作为子计划"
filterable
style="width: 100%;"
>
<el-option
v-for="item in availablePlans"
:key="item.id"
:label="item.name"
:value="item.id"
></el-option>
</el-select>
<template #footer>
<span class="dialog-footer">
<el-button @click="addSubPlanDialogVisible = false">取消</el-button>
<el-button type="primary" @click="confirmAddSubPlan">确定</el-button>
</span>
</template>
</el-dialog>
2025-09-23 19:34:19 +08:00
<!-- Task Editor Dialog (for Add and Edit) -->
2025-09-22 17:46:28 +08:00
<el-dialog
2025-09-23 19:34:19 +08:00
v-model="taskEditorDialogVisible"
:title="isEditingTask ? '编辑子任务' : '增加子任务'"
2025-09-22 17:46:28 +08:00
width="600px"
2025-09-23 19:34:19 +08:00
@close="resetTaskEditorDialog"
2025-09-22 17:46:28 +08:00
>
2025-09-23 19:34:19 +08:00
<el-form :model="currentTaskForm" ref="taskFormRef" :rules="taskFormRules" label-width="100px">
2025-09-22 17:46:28 +08:00
<el-form-item label="任务类型" prop="type">
2025-09-23 19:34:19 +08:00
<el-select v-model="currentTaskForm.type" placeholder="请选择任务类型" style="width: 100%;" :disabled="isEditingTask">
2025-09-22 18:10:12 +08:00
<!-- Only Delay Task for now -->
2025-09-22 17:46:28 +08:00
<el-option label="延时任务" value="delay_task"></el-option>
</el-select>
</el-form-item>
<el-form-item label="任务名称" prop="name">
2025-09-23 19:34:19 +08:00
<el-input v-model="currentTaskForm.name" placeholder="请输入任务名称"></el-input>
2025-09-22 17:46:28 +08:00
</el-form-item>
<el-form-item label="任务描述" prop="description">
2025-09-23 19:34:19 +08:00
<el-input type="textarea" v-model="currentTaskForm.description" placeholder="请输入任务描述"></el-input>
2025-09-22 17:46:28 +08:00
</el-form-item>
2025-09-22 18:10:12 +08:00
<!-- Dynamic task component for specific parameters -->
2025-09-23 19:34:19 +08:00
<template v-if="currentTaskForm.type === 'delay_task'">
2025-09-22 18:10:12 +08:00
<DelayTaskEditor
2025-09-23 19:34:19 +08:00
:parameters="currentTaskForm.parameters"
@update:parameters="val => currentTaskForm.parameters = val"
2025-09-22 18:22:29 +08:00
prop-path="parameters.delay_duration"
:is-editing="true"
/>
2025-09-22 18:10:12 +08:00
</template>
<!-- More task types can be rendered here -->
2025-09-22 17:46:28 +08:00
</el-form>
<template #footer>
<span class="dialog-footer">
2025-09-23 19:34:19 +08:00
<el-button @click="taskEditorDialogVisible = false">取消</el-button>
<el-button type="primary" @click="confirmTaskEdit">确定</el-button>
2025-09-22 17:46:28 +08:00
</span>
</template>
</el-dialog>
2025-09-22 17:10:21 +08:00
</div>
</template>
<script>
import apiClient from '../api/index.js';
2025-09-22 18:10:12 +08:00
import { ElMessage, ElMessageBox } from 'element-plus';
2025-09-22 18:22:29 +08:00
import { ArrowDown } from '@element-plus/icons-vue';
import DelayTaskEditor from './tasks/DelayTask.vue';
2025-09-22 17:10:21 +08:00
export default {
2025-09-22 18:10:12 +08:00
name: 'PlanDetail',
components: {
2025-09-22 18:22:29 +08:00
DelayTaskEditor,
2025-09-22 18:10:12 +08:00
// Self-reference for recursion
'plan-detail': this,
2025-09-22 18:22:29 +08:00
ArrowDown,
2025-09-22 18:10:12 +08:00
},
2025-09-22 17:10:21 +08:00
props: {
planId: {
type: [Number, String],
required: true,
},
2025-09-22 18:10:12 +08:00
isSubPlan: {
type: Boolean,
default: false,
},
2025-09-22 17:10:21 +08:00
},
data() {
return {
2025-09-22 18:10:12 +08:00
plan: {
id: null,
name: '',
description: '',
execution_type: 'automatic',
execute_num: 0,
cron_expression: '',
2025-09-22 18:22:29 +08:00
content_type: null,
sub_plans: [],
tasks: [],
2025-09-22 18:10:12 +08:00
},
2025-09-22 17:10:21 +08:00
loading: false,
error: null,
2025-09-22 18:10:12 +08:00
isEditingContent: false,
// Add Sub-plan dialog
addSubPlanDialogVisible: false,
selectedSubPlanId: null,
availablePlans: [],
2025-09-22 17:46:28 +08:00
2025-09-23 19:34:19 +08:00
// Task Editor dialog (for Add and Edit)
taskEditorDialogVisible: false,
isEditingTask: false,
editingTaskOriginalId: null,
currentTaskForm: {
2025-09-22 18:22:29 +08:00
type: 'delay_task',
2025-09-22 17:46:28 +08:00
name: '',
description: '',
2025-09-23 19:34:19 +08:00
parameters: {},
2025-09-22 17:46:28 +08:00
},
2025-09-23 19:34:19 +08:00
taskFormRules: {
2025-09-22 17:46:28 +08:00
type: [{ required: true, message: '请选择任务类型', trigger: 'change' }],
name: [{ required: true, message: '请输入任务名称', trigger: 'blur' }],
2025-09-23 19:34:19 +08:00
// Rule for delay_duration will be added/removed dynamically
2025-09-22 17:46:28 +08:00
},
2025-09-22 17:10:21 +08:00
};
},
computed: {
2025-09-22 18:22:29 +08:00
delayDurationRules() {
2025-09-22 18:10:12 +08:00
return [{ required: true, message: '请输入延时时间', trigger: 'blur' }];
},
2025-09-22 17:10:21 +08:00
},
watch: {
planId: {
immediate: true,
handler(newId) {
if (newId) {
this.fetchPlan();
}
},
},
2025-09-23 19:34:19 +08:00
'currentTaskForm.type'(newType) {
console.log("PlanDetail: currentTaskForm.type changed to", newType);
2025-09-22 18:10:12 +08:00
if (newType === 'delay_task') {
2025-09-23 19:34:19 +08:00
this.taskFormRules['parameters.delay_duration'] = this.delayDurationRules;
if (this.currentTaskForm.parameters.delay_duration === undefined) {
this.currentTaskForm.parameters.delay_duration = 1;
console.log("PlanDetail: Initialized currentTaskForm.parameters.delay_duration to", this.currentTaskForm.parameters.delay_duration);
2025-09-22 18:10:12 +08:00
}
} else {
2025-09-23 19:34:19 +08:00
if (this.taskFormRules['parameters.delay_duration']) {
delete this.taskFormRules['parameters.delay_duration'];
console.log("PlanDetail: Removed delay_duration rule.");
2025-09-22 18:10:12 +08:00
}
2025-09-23 19:34:19 +08:00
if (this.currentTaskForm.parameters.delay_duration !== undefined) {
delete this.currentTaskForm.parameters.delay_duration;
console.log("PlanDetail: Removed delay_duration parameter.");
2025-09-22 18:10:12 +08:00
}
}
},
2025-09-22 17:10:21 +08:00
},
methods: {
async fetchPlan() {
this.loading = true;
this.error = null;
try {
const response = await apiClient.plans.get(this.planId);
2025-09-22 18:10:12 +08:00
this.plan = {
...response.data,
sub_plans: response.data.sub_plans || [],
tasks: response.data.tasks || [],
};
2025-09-22 18:22:29 +08:00
this.updateContentType();
2025-09-22 17:10:21 +08:00
} catch (err) {
this.error = err.message || '未知错误';
console.error(`加载计划 (ID: ${this.planId}) 失败:`, err);
} finally {
this.loading = false;
}
},
2025-09-22 18:10:12 +08:00
updateContentType() {
if (this.plan.sub_plans.length > 0) {
this.plan.content_type = 'sub_plans';
} else if (this.plan.tasks.length > 0) {
this.plan.content_type = 'tasks';
} else {
2025-09-22 18:22:29 +08:00
this.plan.content_type = null;
2025-09-22 18:10:12 +08:00
}
},
2025-09-22 18:22:29 +08:00
enterEditMode() {
this.isEditingContent = true;
2025-09-23 19:34:19 +08:00
console.log("PlanDetail: Entered edit mode.");
2025-09-22 17:46:28 +08:00
},
async savePlanContent() {
2025-09-22 18:22:29 +08:00
this.updateContentType();
2025-09-22 17:46:28 +08:00
try {
2025-09-22 18:10:12 +08:00
const submitData = {
2025-09-22 18:22:29 +08:00
id: this.plan.id,
2025-09-22 18:10:12 +08:00
name: this.plan.name,
description: this.plan.description,
execution_type: this.plan.execution_type,
execute_num: this.plan.execute_num,
cron_expression: this.plan.cron_expression,
sub_plan_ids: this.plan.content_type === 'sub_plans'
2025-09-22 18:22:29 +08:00
? this.plan.sub_plans.map(sp => sp.child_plan_id)
2025-09-22 18:10:12 +08:00
: [],
tasks: this.plan.content_type === 'tasks'
? this.plan.tasks.map((task, index) => ({
name: task.name,
description: task.description,
2025-09-23 19:34:19 +08:00
type: task.type,
2025-09-22 18:22:29 +08:00
execution_order: index + 1,
2025-09-22 18:10:12 +08:00
parameters: task.parameters || {},
}))
: [],
};
delete submitData.execute_count;
delete submitData.status;
2025-09-23 19:34:19 +08:00
console.log("PlanDetail: Submitting data", submitData);
2025-09-22 18:10:12 +08:00
await apiClient.plans.update(this.planId, submitData);
2025-09-22 17:46:28 +08:00
ElMessage.success('计划内容已保存');
2025-09-22 18:22:29 +08:00
this.isEditingContent = false;
this.fetchPlan();
2025-09-22 17:46:28 +08:00
} catch (error) {
ElMessage.error('保存计划内容失败: ' + (error.message || '未知错误'));
console.error('保存计划内容失败:', error);
}
},
2025-09-23 19:34:19 +08:00
async cancelEdit() {
console.log("PlanDetail: Cancelled edit, re-fetching plan.");
await this.fetchPlan();
2025-09-22 18:22:29 +08:00
this.isEditingContent = false;
ElMessage.info('已取消编辑');
},
2025-09-22 18:10:12 +08:00
// --- Sub-plan related methods ---
2025-09-22 17:46:28 +08:00
async showAddSubPlanDialog() {
2025-09-22 18:10:12 +08:00
if (this.plan.tasks.length > 0) {
try {
await ElMessageBox.confirm('当前计划包含任务,添加子计划将清空现有任务。是否继续?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
});
2025-09-22 18:22:29 +08:00
this.plan.tasks = [];
2025-09-22 18:10:12 +08:00
} catch (e) {
return;
}
}
2025-09-22 17:46:28 +08:00
this.addSubPlanDialogVisible = true;
await this.fetchAvailablePlans();
},
async fetchAvailablePlans() {
try {
const response = await apiClient.plans.list();
this.availablePlans = response.data.plans.filter(p =>
2025-09-22 18:22:29 +08:00
p.id !== this.planId
2025-09-22 17:46:28 +08:00
);
} catch (error) {
ElMessage.error('加载可用计划失败: ' + (error.message || '未知错误'));
console.error('加载可用计划失败:', error);
}
},
confirmAddSubPlan() {
if (!this.selectedSubPlanId) {
ElMessage.warning('请选择一个子计划');
return;
}
const selectedPlan = this.availablePlans.find(p => p.id === this.selectedSubPlanId);
if (selectedPlan) {
2025-09-22 18:10:12 +08:00
this.plan.sub_plans.push({
2025-09-22 18:22:29 +08:00
id: Date.now(),
2025-09-22 18:10:12 +08:00
child_plan_id: selectedPlan.id,
2025-09-22 18:22:29 +08:00
child_plan: selectedPlan,
2025-09-22 18:10:12 +08:00
execution_order: this.plan.sub_plans.length + 1,
2025-09-22 17:46:28 +08:00
});
2025-09-22 18:22:29 +08:00
this.updateContentType();
2025-09-22 17:46:28 +08:00
ElMessage.success(`子计划 "${selectedPlan.name}" 已添加`);
this.addSubPlanDialogVisible = false;
this.resetAddSubPlanDialog();
} else {
ElMessage.error('未找到选中的计划');
}
},
resetAddSubPlanDialog() {
this.selectedSubPlanId = null;
this.availablePlans = [];
},
deleteSubPlan(subPlanToDelete) {
2025-09-22 18:10:12 +08:00
ElMessageBox.confirm(`确认删除子计划 "${subPlanToDelete.child_plan?.name || '未知子计划'}" 吗?`, '提示', {
2025-09-22 17:46:28 +08:00
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
2025-09-22 18:10:12 +08:00
this.plan.sub_plans = this.plan.sub_plans.filter(sub => sub.id !== subPlanToDelete.id);
2025-09-22 18:22:29 +08:00
this.plan.sub_plans.forEach((item, index) => item.execution_order = index + 1);
this.updateContentType();
2025-09-22 17:46:28 +08:00
ElMessage.success('子计划已删除');
}).catch(() => {
});
},
2025-09-22 18:10:12 +08:00
// --- Task related methods ---
2025-09-23 19:34:19 +08:00
showTaskEditorDialog(task = null) {
console.log("PlanDetail: Showing task editor dialog.");
if (this.plan.sub_plans.length > 0 && !task) {
ElMessageBox.confirm('当前计划包含子计划,添加任务将清空现有子计划。是否继续?', '警告', {
2025-09-22 18:10:12 +08:00
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
2025-09-23 19:34:19 +08:00
}).then(() => {
this.plan.sub_plans = [];
this.taskEditorDialogVisible = true;
this.prepareTaskForm(task);
}).catch(() => {
// User cancelled
2025-09-22 18:10:12 +08:00
});
return;
}
2025-09-23 19:34:19 +08:00
this.taskEditorDialogVisible = true;
this.prepareTaskForm(task);
2025-09-22 17:46:28 +08:00
},
2025-09-23 19:34:19 +08:00
prepareTaskForm(task = null) {
// Reset properties of the existing reactive object
this.currentTaskForm.type = 'delay_task';
this.currentTaskForm.name = '';
this.currentTaskForm.description = '';
this.currentTaskForm.parameters = {}; // Ensure parameters is reset
2025-09-22 17:46:28 +08:00
2025-09-23 19:34:19 +08:00
if (task) {
this.isEditingTask = true;
this.editingTaskOriginalId = task.id;
// Update properties of the existing reactive object
this.currentTaskForm.type = task.type === 'waiting' ? 'delay_task' : task.type; // Convert backend type to UI type
this.currentTaskForm.name = task.name;
this.currentTaskForm.description = task.description;
// Deep copy parameters to ensure reactivity for nested changes
this.currentTaskForm.parameters = JSON.parse(JSON.stringify(task.parameters || {}));
console.log("PlanDetail: Prepared currentTaskForm for editing:", JSON.parse(JSON.stringify(this.currentTaskForm)));
} else {
this.isEditingTask = false;
this.editingTaskOriginalId = null;
// Properties are already reset above for new task mode
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
this.updateTaskFormRules();
// Also, ensure delay_duration is initialized if it's a delay_task
if (this.currentTaskForm.type === 'delay_task' && this.currentTaskForm.parameters.delay_duration === undefined) {
this.currentTaskForm.parameters.delay_duration = 1;
}
},
updateTaskFormRules() {
// Clear existing dynamic rules
if (this.taskFormRules['parameters.delay_duration']) {
delete this.taskFormRules['parameters.delay_duration'];
}
// Apply rules based on current type
if (this.currentTaskForm.type === 'delay_task') {
this.taskFormRules['parameters.delay_duration'] = this.delayDurationRules;
}
console.log("PlanDetail: Updated taskFormRules:", JSON.parse(JSON.stringify(this.taskFormRules)));
},
confirmTaskEdit() {
console.log("PlanDetail: confirmTaskEdit called. currentTaskForm before validation:", JSON.parse(JSON.stringify(this.currentTaskForm)));
this.$refs.taskFormRef.validate(async (valid) => {
console.log("PlanDetail: Form validation result:", valid);
if (valid) {
if (this.isEditingTask) {
// Find and update the existing task
const index = this.plan.tasks.findIndex(t => t.id === this.editingTaskOriginalId);
if (index !== -1) {
// Update properties of the existing task
this.plan.tasks[index].name = this.currentTaskForm.name;
this.plan.tasks[index].description = this.currentTaskForm.description;
this.plan.tasks[index].type = this.currentTaskForm.type === 'delay_task' ? 'waiting' : this.currentTaskForm.type;
this.plan.tasks[index].parameters = this.currentTaskForm.parameters;
ElMessage.success(`子任务 "${this.currentTaskForm.name}" 已更新`);
} else {
ElMessage.error('未找到要编辑的任务');
}
} else {
// Add a new task
const newTask = {
id: Date.now(),
execution_order: this.plan.tasks.length + 1,
type: this.currentTaskForm.type === 'delay_task' ? 'waiting' : this.currentTaskForm.type,
name: this.currentTaskForm.name,
description: this.currentTaskForm.description,
parameters: this.currentTaskForm.parameters,
};
this.plan.tasks.push(newTask);
ElMessage.success(`子任务 "${newTask.name}" 已添加`);
}
2025-09-22 18:22:29 +08:00
this.updateContentType();
2025-09-23 19:34:19 +08:00
this.taskEditorDialogVisible = false;
this.resetTaskEditorDialog();
2025-09-22 17:46:28 +08:00
}
});
},
2025-09-23 19:34:19 +08:00
resetTaskEditorDialog() {
console.log("PlanDetail: Resetting task editor dialog.");
this.$refs.taskFormRef.resetFields();
this.isEditingTask = false;
this.editingTaskOriginalId = null;
// Manually reset properties to ensure clean state for next use
this.currentTaskForm.type = 'delay_task';
this.currentTaskForm.name = '';
this.currentTaskForm.description = '';
this.currentTaskForm.parameters = {};
console.log("PlanDetail: currentTaskForm after full reset:", JSON.parse(JSON.stringify(this.currentTaskForm)));
this.updateTaskFormRules();
2025-09-22 17:16:05 +08:00
},
2025-09-23 19:34:19 +08:00
editTask(task) {
console.log('PlanDetail: Calling showTaskEditorDialog for editing task:', task);
this.showTaskEditorDialog(task);
2025-09-22 17:10:21 +08:00
},
2025-09-22 17:46:28 +08:00
deleteTask(taskToDelete) {
ElMessageBox.confirm(`确认删除任务 "${taskToDelete.name}" 吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
2025-09-22 18:10:12 +08:00
this.plan.tasks = this.plan.tasks.filter(task => task.id !== taskToDelete.id);
2025-09-22 18:22:29 +08:00
this.plan.tasks.forEach((item, index) => item.execution_order = index + 1);
this.updateContentType();
2025-09-22 17:46:28 +08:00
ElMessage.success('任务已删除');
}).catch(() => {
});
},
2025-09-22 17:10:21 +08:00
},
};
</script>
<style scoped>
2025-09-22 17:21:08 +08:00
.plan-detail {
2025-09-22 17:10:21 +08:00
margin-top: 10px;
}
.loading, .error {
padding: 20px;
text-align: center;
}
.retry-btn {
margin-top: 15px;
}
2025-09-22 18:22:29 +08:00
.sub-plan-wrapper {
margin-bottom: 10px;
}
2025-09-22 17:10:21 +08:00
.sub-plan-container {
margin-left: 20px;
margin-top: 10px;
2025-09-22 18:22:29 +08:00
border-left: 2px solid #ebeef5;
2025-09-22 17:46:28 +08:00
padding-left: 10px;
2025-09-22 17:10:21 +08:00
}
2025-09-22 18:22:29 +08:00
.sub-plan-card-content {
display: flex;
flex-direction: column;
gap: 10px;
}
.sub-plan-actions {
align-self: flex-end;
}
2025-09-22 17:10:21 +08:00
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
2025-09-22 17:46:28 +08:00
/* 调整子计划卡片内部的header避免重复样式 */
.sub-plan-container .card-header {
2025-09-22 18:22:29 +08:00
padding: 0;
2025-09-22 17:46:28 +08:00
}
2025-09-22 17:10:21 +08:00
</style>