chore: 模版创建流水线

This commit is contained in:
xiaojunnuo
2025-06-25 14:41:27 +08:00
parent 821c6d807d
commit 9296ba7492
15 changed files with 290 additions and 23 deletions
@@ -1,6 +1,6 @@
<script lang="ts" setup>
defineOptions({
name: "LayoutFooter"
name: "LayoutFooter",
});
</script>
@@ -37,6 +37,11 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
editRequest,
delRequest,
},
addForm: {
onSuccess: ({ res }) => {
router.push({ path: "/certd/pipeline/template/edit", query: { templateId: res.id, editMode: "true" } });
},
},
form: {
labelCol: {
//固定label宽度
@@ -95,7 +100,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
show: true,
},
column: {
width: 200,
width: 400,
sorter: true,
cellRender({ row, value }) {
return <router-link to={{ path: "/certd/pipeline/template/edit", query: { templateId: row.id } }}>{value}</router-link>;
@@ -117,8 +122,12 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
editForm: {
show: false,
},
form: {
column: {
show: false,
},
form: {
show: true,
helper: "复制该流水线配置作为模版来源",
component: {
valuesFormat: {
labelFormatter: (item: any) => {
@@ -7,18 +7,33 @@
</div>
<div class="more flex items-center flex-1 justify-end">
<loading-button type="primary" @click="doSave">保存</loading-button>
<loading-button type="primary" @click="doSave">保存模版</loading-button>
<loading-button class="ml-10" type="primary" @click="useTemplateCreate">使用模版</loading-button>
<loading-button class="ml-10" type="primary" danger @click="doDelete">删除模版</loading-button>
</div>
</template>
<div class="page-template-edit">
<div class="base"></div>
<div class="props flex p-10">
<div class="task-list w-50%">
<div class="block-title">
原始任务参数
<div class="helper">点击加号将字段作为模版变量</div>
<div class="block-title flex flex-between">
<div>
模版流水线参数
<div class="helper">点击加号将字段作为模版变量</div>
</div>
<div class="more">
<router-link
v-if="detail?.template?.pipelineId > 0"
:to="{
path: '/certd/pipeline/detail',
query: { id: detail?.template?.pipelineId, editMode: true },
}"
>
修改模版流水线
</router-link>
</div>
</div>
<a-collapse v-model:active-key="activeKey">
<a-collapse v-if="detail?.template?.pipelineId > 0" v-model:active-key="activeKey">
<a-collapse-panel v-for="(step, stepId) in steps" :key="stepId" class="step-item" :header="step.title">
<div class="step-inputs flex flex-wrap">
<div v-for="(input, key) of step.input" :key="key" class="hover:bg-gray-100 p-5 w-full xl:w-[50%]">
@@ -36,6 +51,17 @@
</div>
</a-collapse-panel>
</a-collapse>
<div v-else-if="detail?.template?.pipelineId === 0">
<div class="p-20 flex flex-col flex-center text-sm">
<div class="mb-10">还未绑定模版流水线</div>
<div>
<a-button type="primary" @click="bindPipelineByCreate">创建新流水线作为模版</a-button>
<a-button type="primary" @click="bindPipelineByCopy">从已有流水线复制</a-button>
</div>
</div>
</div>
</div>
<div class="template-props w-50%">
@@ -60,7 +86,9 @@ import { templateApi } from "./api";
import { usePluginStore } from "/@/store/plugin";
import { useStepHelper } from "./utils";
import TemplateForm from "./form.vue";
import { Modal, notification } from "ant-design-vue";
import { useTabbarStore } from "/@/vben/stores";
import { useTemplate } from "./use";
const route = useRoute();
const templateId = route.query.templateId as string;
@@ -79,6 +107,9 @@ const templateProps: Ref = ref({
});
const detail: Ref<TemplateDetail> = ref();
async function getTemplateDetail() {
if (!templateId) {
return;
}
const res = await templateApi.GetDetail(parseInt(templateId));
detail.value = res;
templateProps.value = JSON.parse(res.template.content ?? "{input:{}}");
@@ -100,7 +131,7 @@ onMounted(async () => {
const { getStepsMap } = useStepHelper(pluginStore);
const steps = computed(() => {
if (!detail.value) {
if (!detail.value || !detail.value.pipeline) {
return {};
}
@@ -126,5 +157,36 @@ async function doSave() {
title: detail.value.template.title,
content: JSON.stringify(templateProps.value),
});
notification.success({
message: "保存成功",
});
}
const tabbar = useTabbarStore();
async function doDelete() {
Modal.confirm({
title: "确定删除模版?",
content: "删除后,该模版流水线将不能再使用",
onOk() {
templateApi.DelObj(detail.value.template.id);
notification.success({
message: "删除成功",
});
tabbar.closeTab({ fullPath: route.fullPath } as any, router);
},
});
}
async function bindPipelineByCreate() {
//
// openAddCertdPipelineDialog({ templateId: detail.value.template.id });
}
async function bindPipelineByCopy() {}
const { openCreateFromTemplateDialog } = useTemplate();
async function useTemplateCreate() {
openCreateFromTemplateDialog({ templateId: detail.value.template.id });
}
</script>
@@ -1,5 +1,5 @@
<template>
<a-form ref="templateFormRef" class="template-form" :model="templateForm" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-form ref="templateFormRef" class="template-form w-full" :model="templateForm" :label-col="labelCol" :wrapper-col="wrapperCol">
<template v-for="(item, key) in templateFormColumns" :key="key">
<fs-form-item v-if="item.show !== false" :model-value="get(templateForm, key)" :item="item" :get-context-fn="getScopeFunc(key)" @update:model-value="set(templateForm, key, $event)" />
</template>
@@ -28,7 +28,12 @@ const steps = computed(() => {
return getStepsMap(props.pipeline);
});
const labelCol = ref({ span: 6 });
const labelCol = ref({
span: null,
style: {
width: "145px",
},
});
const wrapperCol = ref({ span: 16 });
const templateForm: any = reactive({});
const templateFormColumns = computed(() => {
@@ -0,0 +1,112 @@
import { dict, useFormWrapper } from "@fast-crud/fast-crud";
import { checkPipelineLimit } from "/@/views/certd/pipeline/utils";
import { templateApi } from "/@/views/certd/pipeline/template/api";
import TemplateForm from "./form.vue";
import NotificationSelector from "/@/views/certd/notification/notification-selector/index.vue";
import GroupSelector from "/@/views/certd/pipeline/group/group-selector.vue";
import { ref } from "vue";
export function useTemplate() {
const { openCrudFormDialog } = useFormWrapper();
async function openCreateFromTemplateDialog(req: { templateId?: number }) {
//检查是否流水线数量超出限制
await checkPipelineLimit();
const detail = await templateApi.GetDetail(req.templateId);
if (!detail) {
throw new Error("模板不存在");
}
if (!detail.template?.pipelineId) {
throw new Error("还未绑定模版流水线");
}
const templateProps = JSON.parse(detail.template.content || "{}");
const pipeline = detail.pipeline;
const groupDictRef = dict({
url: "/pi/pipeline/group/all",
value: "id",
label: "name",
});
const wrapperRef = ref();
function getFormData() {
if (!wrapperRef.value) {
return null;
}
return wrapperRef.value.getFormData();
}
const randomHour = Math.floor(Math.random() * 6);
const randomMin = Math.floor(Math.random() * 60);
const crudOptions = {
form: {
wrapper: {
title: `从模版<${detail.template.title}>创建流水线`,
width: 1100,
slots: {
"form-body-top": () => {
return (
<div class={"w-full flex"}>
<TemplateForm input={templateProps.input} pipeline={pipeline} />
</div>
);
},
},
},
},
columns: {
triggerCron: {
title: "定时触发",
type: "text",
form: {
value: `0 ${randomMin} ${randomHour} * * *`,
component: {
name: "cron-editor",
vModel: "modelValue",
placeholder: "0 0 4 * * *",
},
helper: "点击上面的按钮,选择每天几点定时执行。\n建议设置为每天触发一次,证书未到期之前任务会跳过,不会重复执行",
order: 100,
},
},
notification: {
title: "失败通知",
type: "text",
form: {
value: 0,
component: {
name: NotificationSelector,
vModel: "modelValue",
on: {
selectedChange(opts: any) {
opts.form.notificationTarget = opts.$event;
},
},
},
order: 101,
helper: "任务执行失败实时提醒",
},
},
groupId: {
title: "流水线分组",
type: "dict-select",
dict: groupDictRef,
form: {
component: {
name: GroupSelector,
vModel: "modelValue",
},
order: 9999,
},
},
},
};
const wrapper = await openCrudFormDialog({ crudOptions });
wrapperRef.value = wrapper;
}
return {
openCreateFromTemplateDialog,
};
}