mirror of
https://github.com/opsre/LiteOps.git
synced 2026-06-09 20:57:25 +08:00
✨ feat: 添加复制构建任务功能
This commit is contained in:
@@ -382,6 +382,13 @@ class BuildTaskView(View):
|
|||||||
'message': '任务名称、项目和环境不能为空'
|
'message': '任务名称、项目和环境不能为空'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
# 检查任务名称是否已存在
|
||||||
|
if BuildTask.objects.filter(name=name).exists():
|
||||||
|
return JsonResponse({
|
||||||
|
'code': 400,
|
||||||
|
'message': f'任务名称 "{name}" 已存在,请使用其他名称'
|
||||||
|
})
|
||||||
|
|
||||||
# 验证参数配置格式
|
# 验证参数配置格式
|
||||||
if parameters:
|
if parameters:
|
||||||
import re
|
import re
|
||||||
@@ -649,6 +656,12 @@ class BuildTaskView(View):
|
|||||||
|
|
||||||
# 更新其他字段
|
# 更新其他字段
|
||||||
if 'name' in data:
|
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
|
task.name = name
|
||||||
if 'description' in data:
|
if 'description' in data:
|
||||||
task.description = description
|
task.description = description
|
||||||
|
|||||||
@@ -89,6 +89,12 @@ const routes = [
|
|||||||
component: () => import('../views/build/BuildTaskEdit.vue'),
|
component: () => import('../views/build/BuildTaskEdit.vue'),
|
||||||
meta: { title: '编辑构建任务', permission: '/build/tasks' }
|
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',
|
path: 'history',
|
||||||
name: 'build-history',
|
name: 'build-history',
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="build-task-edit">
|
<div class="build-task-edit">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<a-page-header
|
<a-page-header
|
||||||
:title="isEdit ? '编辑构建任务' : '新建构建任务'"
|
:title="isEdit ? '编辑构建任务' : (isCopy ? '复制构建任务' : '新建构建任务')"
|
||||||
@back="handleBack"
|
@back="handleBack"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -427,6 +427,7 @@ const router = useRouter();
|
|||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const formRef = ref();
|
const formRef = ref();
|
||||||
const isEdit = ref(false);
|
const isEdit = ref(false);
|
||||||
|
const isCopy = ref(false);
|
||||||
const submitLoading = ref(false);
|
const submitLoading = ref(false);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const projectOptions = ref([]);
|
const projectOptions = ref([]);
|
||||||
@@ -665,7 +666,7 @@ const handleSubmit = async () => {
|
|||||||
submitData.external_script_token_id = undefined;
|
submitData.external_script_token_id = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isEdit.value) {
|
if (!isEdit.value || isCopy.value) {
|
||||||
delete submitData.task_id;
|
delete submitData.task_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -674,14 +675,26 @@ const handleSubmit = async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (response.data.code === 200) {
|
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');
|
router.push('/build/tasks');
|
||||||
} else {
|
} else {
|
||||||
throw new Error(response.data.message);
|
throw new Error(response.data.message);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Submit task error:', 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 {
|
} finally {
|
||||||
submitLoading.value = false;
|
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) => {
|
const filterOption = (input, option) => {
|
||||||
return (
|
return (
|
||||||
@@ -861,9 +958,14 @@ const truncateUrl = (url) => {
|
|||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const taskId = route.query.task_id;
|
const taskId = route.query.task_id;
|
||||||
|
const sourceTaskId = route.query.source_task_id;
|
||||||
|
|
||||||
if (taskId) {
|
if (taskId) {
|
||||||
isEdit.value = true;
|
isEdit.value = true;
|
||||||
await loadTaskDetail(taskId);
|
await loadTaskDetail(taskId);
|
||||||
|
} else if (sourceTaskId) {
|
||||||
|
isCopy.value = true;
|
||||||
|
await loadTaskDetailForCopy(sourceTaskId);
|
||||||
} else {
|
} else {
|
||||||
loadProjects();
|
loadProjects();
|
||||||
loadEnvironments();
|
loadEnvironments();
|
||||||
|
|||||||
@@ -194,6 +194,9 @@
|
|||||||
<a-menu-item key="edit" @click="handleEdit(record)">
|
<a-menu-item key="edit" @click="handleEdit(record)">
|
||||||
编辑
|
编辑
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
|
<a-menu-item key="copy" @click="handleCopy(record)">
|
||||||
|
复制
|
||||||
|
</a-menu-item>
|
||||||
<a-menu-divider />
|
<a-menu-divider />
|
||||||
<a-menu-item
|
<a-menu-item
|
||||||
key="enable-disable"
|
key="enable-disable"
|
||||||
@@ -409,7 +412,8 @@ import {
|
|||||||
CloseCircleOutlined,
|
CloseCircleOutlined,
|
||||||
StopOutlined,
|
StopOutlined,
|
||||||
FieldTimeOutlined,
|
FieldTimeOutlined,
|
||||||
LoadingOutlined
|
LoadingOutlined,
|
||||||
|
CopyOutlined
|
||||||
} from '@ant-design/icons-vue';
|
} from '@ant-design/icons-vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import axios from 'axios';
|
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 () => {
|
const handleDownloadLog = async () => {
|
||||||
if (!selectedHistoryId.value) {
|
if (!selectedHistoryId.value) {
|
||||||
@@ -1410,6 +1431,8 @@ const handleViewBuildDetail = (record) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
Reference in New Issue
Block a user