chore: 模版导入式创建流水线

pull/453/head
xiaojunnuo 2025-06-29 01:33:43 +08:00
parent 37e6548246
commit 04422a4637
10 changed files with 916 additions and 843 deletions

View File

@ -133,5 +133,12 @@ async function loadLocaleMessages(lang: SupportedLanguagesType) {
return setI18nLanguage(lang); return setI18nLanguage(lang);
} }
export function useI18n() {
return {
t: i18n.global.t,
locale: i18n.global.locale,
};
}
export { i18n, loadLocaleMessages, loadLocalesMap, loadLocalesMapFromDir, setupI18n, setI18nLanguage }; export { i18n, loadLocaleMessages, loadLocalesMap, loadLocalesMapFromDir, setupI18n, setI18nLanguage };
export default i18n; export default i18n;

View File

@ -1,12 +1,12 @@
import { i18n, loadLocaleMessages, loadLocalesMap, loadLocalesMapFromDir, setupI18n, setI18nLanguage } from "./i18n"; import { i18n, loadLocaleMessages, loadLocalesMap, loadLocalesMapFromDir, setupI18n, setI18nLanguage, useI18n } from "./i18n";
const $t = i18n.global.t; const $t = i18n.global.t;
const $te = i18n.global.te; const $te = i18n.global.te;
export { $t, $te, i18n, loadLocaleMessages, loadLocalesMap, loadLocalesMapFromDir, setupI18n, setI18nLanguage }; export { $t, $te, i18n, loadLocaleMessages, loadLocalesMap, loadLocalesMapFromDir, setupI18n, setI18nLanguage, useI18n };
export { type ImportLocaleFn, type LocaleSetupOptions, type SupportedLanguagesType } from "./typing"; export { type ImportLocaleFn, type LocaleSetupOptions, type SupportedLanguagesType } from "./typing";
// export type { CompileError } from "@intlify/core-base"; // export type { CompileError } from "@intlify/core-base";
export { useI18n } from "vue-i18n"; // export { useI18n } from "vue-i18n";
export type { Locale } from "vue-i18n"; export type { Locale } from "vue-i18n";

View File

@ -11,8 +11,7 @@ import * as api from "../api";
import { PluginGroup, usePluginStore } from "/@/store/plugin"; import { PluginGroup, usePluginStore } from "/@/store/plugin";
import { createNotificationApi } from "/@/views/certd/notification/api"; import { createNotificationApi } from "/@/views/certd/notification/api";
import GroupSelector from "../group/group-selector.vue"; import GroupSelector from "../group/group-selector.vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "/src/locales";
export function fillPipelineByDefaultForm(pipeline: any, form: any) { export function fillPipelineByDefaultForm(pipeline: any, form: any) {
const triggers = []; const triggers = [];
@ -34,241 +33,240 @@ export function fillPipelineByDefaultForm(pipeline: any, form: any) {
} }
export function setRunnableIds(pipeline: any) { export function setRunnableIds(pipeline: any) {
const { t } = useI18n(); const { t } = useI18n();
const idMap: any = {}; const idMap: any = {};
function createId(oldId: any) { function createId(oldId: any) {
if (oldId == null) { if (oldId == null) {
return nanoid(); return nanoid();
} }
const newId = nanoid(); const newId = nanoid();
idMap[oldId] = newId; idMap[oldId] = newId;
return newId; return newId;
} }
if (pipeline.stages) { if (pipeline.stages) {
for (const stage of pipeline.stages) { for (const stage of pipeline.stages) {
stage.id = createId(stage.id); stage.id = createId(stage.id);
if (stage.tasks) { if (stage.tasks) {
for (const task of stage.tasks) { for (const task of stage.tasks) {
task.id = createId(task.id); task.id = createId(task.id);
if (task.steps) { if (task.steps) {
for (const step of task.steps) { for (const step of task.steps) {
step.id = createId(step.id); step.id = createId(step.id);
} }
} }
} }
} }
} }
} }
for (const trigger of pipeline.triggers) { for (const trigger of pipeline.triggers) {
trigger.id = nanoid(); trigger.id = nanoid();
} }
for (const notification of pipeline.notifications) { for (const notification of pipeline.notifications) {
notification.id = nanoid(); notification.id = nanoid();
} }
let content = JSON.stringify(pipeline); let content = JSON.stringify(pipeline);
for (const key in idMap) { for (const key in idMap) {
content = content.replaceAll(key, idMap[key]); content = content.replaceAll(key, idMap[key]);
} }
return JSON.parse(content); return JSON.parse(content);
} }
export function useCertPipelineCreator() { export function useCertPipelineCreator() {
const { t } = useI18n(); const { t } = useI18n();
const { openCrudFormDialog } = useFormWrapper(); const { openCrudFormDialog } = useFormWrapper();
const pluginStore = usePluginStore(); const pluginStore = usePluginStore();
const router = useRouter(); const router = useRouter();
function createCrudOptions(certPlugins: any[], getFormData: any, doSubmit: any): CreateCrudOptionsRet { function createCrudOptions(certPlugins: any[], getFormData: any, doSubmit: any): CreateCrudOptionsRet {
const inputs: any = {}; const inputs: any = {};
const moreParams = []; const moreParams = [];
for (const plugin of certPlugins) { for (const plugin of certPlugins) {
for (const inputKey in plugin.input) { for (const inputKey in plugin.input) {
if (inputs[inputKey]) { if (inputs[inputKey]) {
//如果两个插件有的字段,直接显示 //如果两个插件有的字段,直接显示
inputs[inputKey].form.show = true; inputs[inputKey].form.show = true;
continue; continue;
} }
const inputDefine = cloneDeep(plugin.input[inputKey]); const inputDefine = cloneDeep(plugin.input[inputKey]);
if (!inputDefine.required && !inputDefine.maybeNeed) { if (!inputDefine.required && !inputDefine.maybeNeed) {
moreParams.push(inputKey); moreParams.push(inputKey);
// continue; // continue;
} }
useReference(inputDefine); useReference(inputDefine);
inputs[inputKey] = { inputs[inputKey] = {
title: inputDefine.title, title: inputDefine.title,
form: { form: {
...inputDefine, ...inputDefine,
show: compute(ctx => { show: compute(ctx => {
const form = getFormData(); const form = getFormData();
if (!form) { if (!form) {
return false; return false;
} }
let inputDefineShow = true; let inputDefineShow = true;
if (inputDefine.show != null) { if (inputDefine.show != null) {
const computeShow = inputDefine.show as any; const computeShow = inputDefine.show as any;
if (computeShow === false) { if (computeShow === false) {
inputDefineShow = false; inputDefineShow = false;
} else if (computeShow && computeShow.computeFn) { } else if (computeShow && computeShow.computeFn) {
inputDefineShow = computeShow.computeFn({ form }); inputDefineShow = computeShow.computeFn({ form });
} }
} }
return form?.certApplyPlugin === plugin.name && inputDefineShow; return form?.certApplyPlugin === plugin.name && inputDefineShow;
}), }),
}, },
}; };
} }
} }
const pluginStore = usePluginStore(); const pluginStore = usePluginStore();
const randomHour = Math.floor(Math.random() * 6); const randomHour = Math.floor(Math.random() * 6);
const randomMin = Math.floor(Math.random() * 60); const randomMin = Math.floor(Math.random() * 60);
const groupDictRef = dict({ const groupDictRef = dict({
url: "/pi/pipeline/group/all", url: "/pi/pipeline/group/all",
value: "id", value: "id",
label: "name", label: "name",
}); });
return { return {
crudOptions: { crudOptions: {
form: { form: {
doSubmit, doSubmit,
wrapper: { wrapper: {
width: 1350, width: 1350,
saveRemind: false, saveRemind: false,
title: t("certd.pipelineForm.createTitle"), title: t("certd.pipelineForm.createTitle"),
}, },
group: { group: {
groups: { groups: {
more: { more: {
header: t("certd.pipelineForm.moreParams"), header: t("certd.pipelineForm.moreParams"),
columns: moreParams, columns: moreParams,
collapsed: true, collapsed: true,
}, },
}, },
}, },
}, },
columns: { columns: {
certApplyPlugin: { certApplyPlugin: {
title: t("certd.plugin.selectTitle"), title: t("certd.plugin.selectTitle"),
type: "dict-select", type: "dict-select",
dict: dict({ dict: dict({
data: [ data: [
{ value: "CertApply", label: "JS-ACME" }, { value: "CertApply", label: "JS-ACME" },
{ value: "CertApplyLego", label: "Lego-ACME" }, { value: "CertApplyLego", label: "Lego-ACME" },
], ],
}), }),
form: { form: {
order: 0, order: 0,
value: "CertApply", value: "CertApply",
helper: { helper: {
render: () => { render: () => {
return ( return (
<ul> <ul>
<li>{t("certd.plugin.jsAcme")}</li> <li>{t("certd.plugin.jsAcme")}</li>
<li>{t("certd.plugin.legoAcme")}</li> <li>{t("certd.plugin.legoAcme")}</li>
</ul> </ul>
); );
}, },
}, },
valueChange: { valueChange: {
handle: async ({ form, value }) => { handle: async ({ form, value }) => {
const config = await pluginStore.getPluginConfig({ const config = await pluginStore.getPluginConfig({
name: value, name: value,
type: "builtIn", type: "builtIn",
}); });
if (config.sysSetting?.input) { if (config.sysSetting?.input) {
merge(form, config.sysSetting.input); merge(form, config.sysSetting.input);
} }
}, },
immediate: true, immediate: true,
}, },
}, },
}, },
...inputs, ...inputs,
triggerCron: { triggerCron: {
title: t("certd.pipelineForm.triggerCronTitle"), title: t("certd.pipelineForm.triggerCronTitle"),
type: "text", type: "text",
form: { form: {
value: `0 ${randomMin} ${randomHour} * * *`, value: `0 ${randomMin} ${randomHour} * * *`,
component: { component: {
name: "cron-editor", name: "cron-editor",
vModel: "modelValue", vModel: "modelValue",
placeholder: "0 0 4 * * *", placeholder: "0 0 4 * * *",
}, },
helper: t("certd.pipelineForm.triggerCronHelper"), helper: t("certd.pipelineForm.triggerCronHelper"),
order: 100, order: 100,
}, },
}, },
notification: { notification: {
title: t("certd.pipelineForm.notificationTitle"), title: t("certd.pipelineForm.notificationTitle"),
type: "text", type: "text",
form: { form: {
value: 0, value: 0,
component: { component: {
name: NotificationSelector, name: NotificationSelector,
vModel: "modelValue", vModel: "modelValue",
on: { on: {
selectedChange({ $event, form }) { selectedChange({ $event, form }) {
form.notificationTarget = $event; form.notificationTarget = $event;
}, },
}, },
}, },
order: 101, order: 101,
helper: t("certd.pipelineForm.notificationHelper"), helper: t("certd.pipelineForm.notificationHelper"),
}, },
}, },
groupId: { groupId: {
title: t("certd.pipelineForm.groupIdTitle"), title: t("certd.pipelineForm.groupIdTitle"),
type: "dict-select", type: "dict-select",
dict: groupDictRef, dict: groupDictRef,
form: { form: {
component: { component: {
name: GroupSelector, name: GroupSelector,
vModel: "modelValue", vModel: "modelValue",
}, },
order: 9999, order: 9999,
}, },
} },
},
},
};
}
}, async function getCertPlugins() {
}, const pluginGroup = await pluginStore.getGroups();
}; const pluginGroups: { [key: string]: PluginGroup } = pluginGroup.groups;
} const certPluginGroup = pluginGroups.cert;
async function getCertPlugins() { const certPlugins = [];
const pluginGroup = await pluginStore.getGroups(); for (const plugin of certPluginGroup.plugins) {
const pluginGroups: { [key: string]: PluginGroup } = pluginGroup.groups; const detail: any = await pluginStore.getPluginDefine(plugin.name);
const certPluginGroup = pluginGroups.cert; certPlugins.push(detail);
}
return certPlugins;
}
const certPlugins = []; async function openAddCertdPipelineDialog(req: { defaultGroupId?: number }) {
for (const plugin of certPluginGroup.plugins) { //检查是否流水线数量超出限制
const detail: any = await pluginStore.getPluginDefine(plugin.name); await checkPipelineLimit();
certPlugins.push(detail);
}
return certPlugins;
}
async function openAddCertdPipelineDialog(req: { defaultGroupId?: number }) { const wrapperRef = ref();
//检查是否流水线数量超出限制 function getFormData() {
await checkPipelineLimit(); if (!wrapperRef.value) {
return null;
}
return wrapperRef.value.getFormData();
}
const wrapperRef = ref(); async function doSubmit({ form }: any) {
function getFormData() { // const certDetail = readCertDetail(form.cert.crt);
if (!wrapperRef.value) { // 添加certd pipeline
return null; const pluginInput = omit(form, ["triggerCron", "notification", "notificationTarget", "certApplyPlugin", "groupId"]);
}
return wrapperRef.value.getFormData();
}
async function doSubmit({ form }: any) {
// const certDetail = readCertDetail(form.cert.crt);
// 添加certd pipeline
const pluginInput = omit(form, ["triggerCron", "notification", "notificationTarget", "certApplyPlugin", "groupId"]);
let pipeline: any = { let pipeline: any = {
title: form.domains[0] + "证书自动化", title: form.domains[0] + "证书自动化",
runnableType: "pipeline", runnableType: "pipeline",
@ -301,38 +299,38 @@ export function useCertPipelineCreator() {
], ],
}; };
pipeline = fillPipelineByDefaultForm(pipeline, form); pipeline = fillPipelineByDefaultForm(pipeline, form);
pipeline = setRunnableIds(pipeline); pipeline = setRunnableIds(pipeline);
const groupId = form.groupId; const groupId = form.groupId;
const id = await api.Save({ const id = await api.Save({
title: pipeline.title, title: pipeline.title,
content: JSON.stringify(pipeline), content: JSON.stringify(pipeline),
keepHistoryCount: 30, keepHistoryCount: 30,
type: "cert", type: "cert",
groupId, groupId,
}); });
if (form.email) { if (form.email) {
try { try {
//创建一个默认的邮件通知 //创建一个默认的邮件通知
const notificationApi = createNotificationApi(); const notificationApi = createNotificationApi();
await notificationApi.GetOrCreateDefault({ email: form.email }); await notificationApi.GetOrCreateDefault({ email: form.email });
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }
} }
message.success("创建成功,请添加证书部署任务"); message.success("创建成功,请添加证书部署任务");
router.push({ path: "/certd/pipeline/detail", query: { id, editMode: "true" } }); router.push({ path: "/certd/pipeline/detail", query: { id, editMode: "true" } });
} }
const certPlugins = await getCertPlugins(); const certPlugins = await getCertPlugins();
const { crudOptions } = createCrudOptions(certPlugins, getFormData, doSubmit); const { crudOptions } = createCrudOptions(certPlugins, getFormData, doSubmit);
//@ts-ignore //@ts-ignore
crudOptions.columns.groupId.form.value = req.defaultGroupId || undefined; crudOptions.columns.groupId.form.value = req.defaultGroupId || undefined;
const wrapper = await openCrudFormDialog({ crudOptions }); const wrapper = await openCrudFormDialog({ crudOptions });
wrapperRef.value = wrapper; wrapperRef.value = wrapper;
} }
return { return {
openAddCertdPipelineDialog, openAddCertdPipelineDialog,
}; };
} }

File diff suppressed because it is too large Load Diff

View File

@ -22,7 +22,6 @@
</fs-page> </fs-page>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onActivated, onMounted, ref } from "vue"; import { onActivated, onMounted, ref } from "vue";
import { dict, useFs } from "@fast-crud/fast-crud"; import { dict, useFs } from "@fast-crud/fast-crud";

View File

@ -77,27 +77,27 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
buttons: { buttons: {
edit: { show: false }, edit: { show: false },
copy: { show: false }, copy: { show: false },
use: { // use: {
text: null, // text: null,
title: "使用此模版创建流水线", // title: "使用此模版创建流水线",
icon: "ion:duplicate-outline", // icon: "ion:duplicate-outline",
click({ row }) { // click({ row }) {
openCreateFromTemplateDialog({ // openCreateFromTemplateDialog({
templateId: row.id, // templateId: row.id,
onCreated: ({ id }) => { // onCreated: ({ id }) => {
router.push({ path: "/certd/pipeline/detail", query: { id, editMode: true } }); // router.push({ path: "/certd/pipeline/detail", query: { id, editMode: "true" } });
}, // },
}); // });
}, // },
}, // },
import: { // import: {
text: null, // text: null,
title: "批量导入创建", // title: "批量导入创建",
icon: "ion:duplicate", // icon: "ion:duplicate",
click({ row }) { // click({ row }) {
router.push({ path: "/certd/pipeline/template/import", query: { templateId: row.id } }); // router.push({ path: "/certd/pipeline/template/import", query: { templateId: row.id } });
}, // },
}, // },
}, },
}, },
columns: { columns: {
@ -124,11 +124,19 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
search: { search: {
show: true, show: true,
}, },
form: {
rules: [{ required: true, message: "请输入模版名称" }],
},
column: { column: {
width: 400, width: 400,
sorter: true, sorter: true,
cellRender({ row, value }) { cellRender({ row, value }) {
return <router-link to={{ path: "/certd/pipeline/template/edit", query: { templateId: row.id } }}>{value}</router-link>; return (
<router-link class={"flex items-center"} to={{ path: "/certd/pipeline/template/edit", query: { templateId: row.id } }}>
<fs-icon icon={"ion:create-outline"}></fs-icon>
<span class={"ml-5"}> {value}</span>
</router-link>
);
}, },
}, },
}, },
@ -182,6 +190,46 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
}, },
}, },
}, },
useCreate: {
title: "使用此模版",
form: { show: false },
column: {
conditionalRender: false,
width: 400,
cellRender({ row }) {
function create() {
openCreateFromTemplateDialog({
templateId: row.id,
onCreated: ({ id }) => {
router.push({ path: "/certd/pipeline/detail", query: { id, editMode: "true" } });
},
});
}
return (
<a class={"flex items-center"} onClick={create}>
<fs-icon icon={"ion:duplicate-outline"}></fs-icon>
<span class={"ml-5"}>线</span>
</a>
);
},
},
},
useImport: {
title: "使用此模版",
form: { show: false },
column: {
conditionalRender: false,
width: 400,
cellRender({ row }) {
return (
<router-link class={"flex items-center"} to={{ path: "/certd/pipeline/template/import", query: { templateId: row.id } }}>
<fs-icon icon={"ion:duplicate"}></fs-icon>
<span class={"ml-5"}>线</span>
</router-link>
);
},
},
},
}, },
}, },
}; };

View File

@ -112,7 +112,9 @@ async function getTemplateDetail() {
} }
const res = await templateApi.GetDetail(parseInt(templateId)); const res = await templateApi.GetDetail(parseInt(templateId));
detail.value = res; detail.value = res;
templateProps.value = JSON.parse(res.template.content ?? "{input:{}}"); if (res.template.content) {
templateProps.value = JSON.parse(res.template.content);
}
} }
const pluginStore = usePluginStore(); const pluginStore = usePluginStore();

View File

@ -20,8 +20,8 @@ import { templateApi } from "../api";
import { createFormOptions } from "/@/views/certd/pipeline/template/import/form"; import { createFormOptions } from "/@/views/certd/pipeline/template/import/form";
import { cloneDeep } from "lodash-es"; import { cloneDeep } from "lodash-es";
import { fillPipelineByDefaultForm } from "/@/views/certd/pipeline/certd-form/use"; import { fillPipelineByDefaultForm } from "/@/views/certd/pipeline/certd-form/use";
import { eachSteps } from "/@/views/certd/pipeline/utils"; import { createPipelineByTemplate } from "/@/views/certd/pipeline/template/use";
import { notification, Modal } from "ant-design-vue";
const route = useRoute(); const route = useRoute();
const templateId = route.query.templateId as string; const templateId = route.query.templateId as string;
@ -59,44 +59,44 @@ async function doImport() {
const importTableRef = formRef.value.getComponentRef("templateProps"); const importTableRef = formRef.value.getComponentRef("templateProps");
const templateList = importTableRef.value.getData(); const templateList = importTableRef.getData();
for (let i = 0; i < templateList.length; i++) { const progress = ref({ total: templateList.length, current: 0 });
const tempInputs = templateList[i]; async function requestImport() {
const title = tempInputs.title; for (let i = 0; i < templateList.length; i++) {
delete tempInputs.title; const tempInputs = templateList[i];
const title = tempInputs.title;
delete tempInputs.title;
let newPipeline = cloneDeep(detail.value.pipeline); let newPipeline = cloneDeep(detail.value.pipeline);
newPipeline = fillPipelineByDefaultForm(newPipeline, form); newPipeline = fillPipelineByDefaultForm(newPipeline, form);
//
const steps: any = {};
eachSteps(newPipeline, (step: any) => {
steps[step.id] = step;
});
for (const inputKey in tempInputs) { await createPipelineByTemplate({
const [stepId, key] = inputKey.split("."); templateId: parseInt(templateId),
const step = steps[stepId]; templateForm: tempInputs,
if (step) { pipeline: newPipeline,
step.input[key] = tempInputs[inputKey]; title: title,
} groupId: form.groupId,
});
progress.value.current = progress.value.current + 1;
} }
notification.success({
newPipeline.title = title; message: "导入完成",
const groupId = form.groupId;
await templateApi.CreatePipelineByTemplate({
title,
content: JSON.stringify(newPipeline),
keepHistoryCount: 30,
groupId,
templateId: parseInt(templateId),
pipeline: {
title: form.title,
templateProps: templateList,
},
}); });
importTableRef.clear();
} }
requestImport();
Modal.info({
title: "导入中",
content() {
return (
<div>
当前导入进度 {progress.value.current} / {progress.value.total}
</div>
);
},
});
} }
</script> </script>

View File

@ -91,7 +91,10 @@ onMounted(async () => {
await pluginStore.init(); await pluginStore.init();
await nextTick(); await nextTick();
const steps = getStepsMap(props.detail.pipeline); const steps = getStepsMap(props.detail.pipeline);
templateProps.value = JSON.parse(props.detail.template?.content ?? "{input:{}}"); if (props.detail.template?.content) {
templateProps.value = JSON.parse(props.detail.template?.content);
}
appendCrudOptions({ ...buildColumns(steps) }); appendCrudOptions({ ...buildColumns(steps) });
crudBinding.value.data = []; crudBinding.value.data = [];
await crudExpose.editable.enable({ mode: "row" }); await crudExpose.editable.enable({ mode: "row" });
@ -101,6 +104,9 @@ defineExpose({
getData() { getData() {
return crudBinding.value.data; return crudBinding.value.data;
}, },
clear() {
crudBinding.value.data = [];
},
}); });
</script> </script>

View File

@ -67,6 +67,34 @@ export function createExtraColumns() {
}; };
} }
export async function createPipelineByTemplate(opts: { templateId: number; title: string; groupId?: string; pipeline: any; templateForm: any; keepHistoryCount?: number }) {
const { title, groupId, pipeline, templateForm, keepHistoryCount, templateId } = opts;
//填充模版参数
const steps: any = {};
eachSteps(pipeline, (step: any) => {
steps[step.id] = step;
});
for (const stepId in templateForm) {
const step = steps[stepId];
const tempStep = templateForm[stepId];
if (step) {
for (const key in tempStep) {
step.input[key] = tempStep[key];
}
}
}
pipeline.title = title;
return await templateApi.CreatePipelineByTemplate({
title,
content: JSON.stringify(pipeline),
keepHistoryCount: keepHistoryCount ?? 30,
groupId,
templateId,
});
}
export function useTemplate() { export function useTemplate() {
const { openCrudFormDialog } = useFormWrapper(); const { openCrudFormDialog } = useFormWrapper();
@ -102,28 +130,12 @@ export function useTemplate() {
let newPipeline = cloneDeep(pipeline); let newPipeline = cloneDeep(pipeline);
newPipeline = fillPipelineByDefaultForm(newPipeline, form); newPipeline = fillPipelineByDefaultForm(newPipeline, form);
//填充模版参数 //填充模版参数
const steps: any = {}; const { id } = await createPipelineByTemplate({
eachSteps(newPipeline, (step: any) => {
steps[step.id] = step;
});
for (const inputKey in tempInputs) {
const [stepId, key] = inputKey.split(".");
const step = steps[stepId];
if (step) {
step.input[key] = tempInputs[inputKey];
}
}
const title = form.title;
newPipeline.title = title;
const groupId = form.groupId;
const { id } = await templateApi.CreatePipelineByTemplate({
title,
content: JSON.stringify(newPipeline),
keepHistoryCount: 30,
groupId,
templateId: detail.template.id, templateId: detail.template.id,
templateForm: tempInputs,
pipeline: newPipeline,
title: form.title,
groupId: form.groupId,
}); });
if (req.onCreated) { if (req.onCreated) {
req.onCreated({ id }); req.onCreated({ id });