mirror of
https://github.com/opsre/LiteOps.git
synced 2026-02-06 23:21:31 +08:00
✨ feat: 添加复制构建任务功能
This commit is contained in:
@@ -382,6 +382,13 @@ class BuildTaskView(View):
|
||||
'message': '任务名称、项目和环境不能为空'
|
||||
})
|
||||
|
||||
# 检查任务名称是否已存在
|
||||
if BuildTask.objects.filter(name=name).exists():
|
||||
return JsonResponse({
|
||||
'code': 400,
|
||||
'message': f'任务名称 "{name}" 已存在,请使用其他名称'
|
||||
})
|
||||
|
||||
# 验证参数配置格式
|
||||
if parameters:
|
||||
import re
|
||||
@@ -649,6 +656,12 @@ class BuildTaskView(View):
|
||||
|
||||
# 更新其他字段
|
||||
if 'name' in data:
|
||||
# 检查任务名称是否已存在
|
||||
if BuildTask.objects.filter(name=name).exclude(task_id=task_id).exists():
|
||||
return JsonResponse({
|
||||
'code': 400,
|
||||
'message': f'任务名称 "{name}" 已存在,请使用其他名称'
|
||||
})
|
||||
task.name = name
|
||||
if 'description' in data:
|
||||
task.description = description
|
||||
|
||||
@@ -89,6 +89,12 @@ const routes = [
|
||||
component: () => import('../views/build/BuildTaskEdit.vue'),
|
||||
meta: { title: '编辑构建任务', permission: '/build/tasks' }
|
||||
},
|
||||
{
|
||||
path: 'tasks/copy',
|
||||
name: 'build-task-copy',
|
||||
component: () => import('../views/build/BuildTaskEdit.vue'),
|
||||
meta: { title: '复制构建任务', permission: '/build/tasks' }
|
||||
},
|
||||
{
|
||||
path: 'history',
|
||||
name: 'build-history',
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="build-task-edit">
|
||||
<div class="page-header">
|
||||
<a-page-header
|
||||
:title="isEdit ? '编辑构建任务' : '新建构建任务'"
|
||||
:title="isEdit ? '编辑构建任务' : (isCopy ? '复制构建任务' : '新建构建任务')"
|
||||
@back="handleBack"
|
||||
/>
|
||||
</div>
|
||||
@@ -427,6 +427,7 @@ const router = useRouter();
|
||||
const route = useRoute();
|
||||
const formRef = ref();
|
||||
const isEdit = ref(false);
|
||||
const isCopy = ref(false);
|
||||
const submitLoading = ref(false);
|
||||
const loading = ref(false);
|
||||
const projectOptions = ref([]);
|
||||
@@ -665,7 +666,7 @@ const handleSubmit = async () => {
|
||||
submitData.external_script_token_id = undefined;
|
||||
}
|
||||
|
||||
if (!isEdit.value) {
|
||||
if (!isEdit.value || isCopy.value) {
|
||||
delete submitData.task_id;
|
||||
}
|
||||
|
||||
@@ -674,14 +675,26 @@ const handleSubmit = async () => {
|
||||
});
|
||||
|
||||
if (response.data.code === 200) {
|
||||
message.success(`${isEdit.value ? '更新' : '创建'}构建任务成功`);
|
||||
let successMsg = '创建构建任务成功';
|
||||
if (isEdit.value) {
|
||||
successMsg = '更新构建任务成功';
|
||||
} else if (isCopy.value) {
|
||||
successMsg = '复制构建任务成功';
|
||||
}
|
||||
message.success(successMsg);
|
||||
router.push('/build/tasks');
|
||||
} else {
|
||||
throw new Error(response.data.message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Submit task error:', error);
|
||||
message.error(error.message || `${isEdit.value ? '更新' : '创建'}构建任务失败`);
|
||||
let errorMsg = '创建构建任务失败';
|
||||
if (isEdit.value) {
|
||||
errorMsg = '更新构建任务失败';
|
||||
} else if (isCopy.value) {
|
||||
errorMsg = '复制构建任务失败';
|
||||
}
|
||||
message.error(error.message || errorMsg);
|
||||
} finally {
|
||||
submitLoading.value = false;
|
||||
}
|
||||
@@ -770,6 +783,90 @@ const loadTaskDetail = async (taskId) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 复制任务加载任务详情
|
||||
const loadTaskDetailForCopy = async (sourceTaskId) => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const token = localStorage.getItem('token');
|
||||
const response = await axios.get(`/api/build/tasks/${sourceTaskId}`, {
|
||||
headers: { 'Authorization': token }
|
||||
});
|
||||
|
||||
if (response.data.code === 200) {
|
||||
// 新任务不设置task_id
|
||||
formState.task_id = '';
|
||||
formState.name = response.data.data.name + ' - Copy';
|
||||
formState.description = response.data.data.description;
|
||||
formState.branch = response.data.data.branch;
|
||||
|
||||
// 外部脚本库配置
|
||||
formState.use_external_script = response.data.data.use_external_script || false;
|
||||
formState.external_script_repo_url = response.data.data.external_script_repo_url || '';
|
||||
formState.external_script_directory = response.data.data.external_script_directory || '';
|
||||
formState.external_script_branch = response.data.data.external_script_branch || '';
|
||||
formState.external_script_token_id = response.data.data.external_script_token_id || undefined;
|
||||
|
||||
const stages = response.data.data.stages || [];
|
||||
formState.stages = stages.map(stage => ({
|
||||
name: stage.name || '',
|
||||
script: stage.script || '',
|
||||
}));
|
||||
|
||||
if (formState.stages.length === 0) {
|
||||
formState.stages.push({
|
||||
name: '构建',
|
||||
script: '',
|
||||
});
|
||||
}
|
||||
|
||||
// 加载参数配置
|
||||
const parameters = response.data.data.parameters || [];
|
||||
formState.parameters = parameters.map(param => {
|
||||
const choicesText = (param.choices || []).join('\n');
|
||||
const defaultValuesText = (param.default_values || []).join(',');
|
||||
return {
|
||||
name: param.name || '',
|
||||
description: param.description || '',
|
||||
choices: param.choices || [],
|
||||
choicesText: choicesText,
|
||||
choiceOptions: (param.choices || []).map(choice => ({
|
||||
label: choice,
|
||||
value: choice
|
||||
})),
|
||||
default_values: param.default_values || [],
|
||||
defaultValuesText: defaultValuesText,
|
||||
};
|
||||
});
|
||||
|
||||
formState.notification_channels = response.data.data.notification_channels || [];
|
||||
|
||||
if (response.data.data.project) {
|
||||
formState.project_id = response.data.data.project.project_id;
|
||||
}
|
||||
if (response.data.data.environment) {
|
||||
formState.environment_id = response.data.data.environment.environment_id;
|
||||
}
|
||||
if (response.data.data.git_token) {
|
||||
formState.git_token_id = response.data.data.git_token.credential_id;
|
||||
}
|
||||
|
||||
// 加载相关选项数据
|
||||
await Promise.all([
|
||||
loadProjects(),
|
||||
loadEnvironments(),
|
||||
loadGitCredentials()
|
||||
]);
|
||||
} else {
|
||||
message.error(response.data.message || '加载任务详情失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载任务详情失败:', error);
|
||||
message.error('加载任务详情失败');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 过滤选项方法
|
||||
const filterOption = (input, option) => {
|
||||
return (
|
||||
@@ -861,9 +958,14 @@ const truncateUrl = (url) => {
|
||||
|
||||
onMounted(async () => {
|
||||
const taskId = route.query.task_id;
|
||||
const sourceTaskId = route.query.source_task_id;
|
||||
|
||||
if (taskId) {
|
||||
isEdit.value = true;
|
||||
await loadTaskDetail(taskId);
|
||||
} else if (sourceTaskId) {
|
||||
isCopy.value = true;
|
||||
await loadTaskDetailForCopy(sourceTaskId);
|
||||
} else {
|
||||
loadProjects();
|
||||
loadEnvironments();
|
||||
|
||||
@@ -194,6 +194,9 @@
|
||||
<a-menu-item key="edit" @click="handleEdit(record)">
|
||||
编辑
|
||||
</a-menu-item>
|
||||
<a-menu-item key="copy" @click="handleCopy(record)">
|
||||
复制
|
||||
</a-menu-item>
|
||||
<a-menu-divider />
|
||||
<a-menu-item
|
||||
key="enable-disable"
|
||||
@@ -409,7 +412,8 @@ import {
|
||||
CloseCircleOutlined,
|
||||
StopOutlined,
|
||||
FieldTimeOutlined,
|
||||
LoadingOutlined
|
||||
LoadingOutlined,
|
||||
CopyOutlined
|
||||
} from '@ant-design/icons-vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import axios from 'axios';
|
||||
@@ -1306,6 +1310,23 @@ const handleEdit = (record) => {
|
||||
});
|
||||
};
|
||||
|
||||
// 处理复制任务
|
||||
const handleCopy = (record) => {
|
||||
// 检查权限
|
||||
const module = 'build_task';
|
||||
const action = 'create';
|
||||
if (!hasFunctionPermission(module, action)) {
|
||||
message.error('你没有创建构建任务的权限');
|
||||
return;
|
||||
}
|
||||
|
||||
stopLogUpdate(); // 确保在跳转前清理定时器
|
||||
router.push({
|
||||
name: 'build-task-copy',
|
||||
query: { source_task_id: record.task_id }
|
||||
});
|
||||
};
|
||||
|
||||
// 下载日志
|
||||
const handleDownloadLog = async () => {
|
||||
if (!selectedHistoryId.value) {
|
||||
@@ -1410,6 +1431,8 @@ const handleViewBuildDetail = (record) => {
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
Reference in New Issue
Block a user