mirror of https://github.com/certd/certd
perf: 模版导入流水线
parent
529482a83e
commit
dcc8c56969
|
@ -8,7 +8,7 @@ export const EVENT_CERT_APPLY_SUCCESS = "CertApply.success";
|
||||||
|
|
||||||
export abstract class CertApplyBaseConvertPlugin extends AbstractTaskPlugin {
|
export abstract class CertApplyBaseConvertPlugin extends AbstractTaskPlugin {
|
||||||
@TaskInput({
|
@TaskInput({
|
||||||
title: "域名",
|
title: "证书域名",
|
||||||
component: {
|
component: {
|
||||||
name: "a-select",
|
name: "a-select",
|
||||||
vModel: "value",
|
vModel: "value",
|
||||||
|
|
|
@ -19,7 +19,9 @@ defineOptions({
|
||||||
});
|
});
|
||||||
|
|
||||||
const getScope: any = inject("get:scope");
|
const getScope: any = inject("get:scope");
|
||||||
const getPluginType: any = inject("get:plugin:type");
|
const getPluginType: any = inject("get:plugin:type", () => {
|
||||||
|
return "access";
|
||||||
|
});
|
||||||
const formItemContext = Form.useInjectFormItemContext();
|
const formItemContext = Form.useInjectFormItemContext();
|
||||||
const props = defineProps<{} & ComponentPropsType>();
|
const props = defineProps<{} & ComponentPropsType>();
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,9 @@ const emit = defineEmits<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const getScope: any = inject("get:scope");
|
const getScope: any = inject("get:scope");
|
||||||
const getPluginType: any = inject("get:plugin:type");
|
const getPluginType: any = inject("get:plugin:type", () => {
|
||||||
|
return "plugin";
|
||||||
|
});
|
||||||
|
|
||||||
const attrs = useAttrs();
|
const attrs = useAttrs();
|
||||||
|
|
||||||
|
|
|
@ -69,9 +69,15 @@ const emit = defineEmits<{
|
||||||
|
|
||||||
const attrs = useAttrs();
|
const attrs = useAttrs();
|
||||||
|
|
||||||
const getCurrentPluginDefine: any = inject("getCurrentPluginDefine");
|
const getCurrentPluginDefine: any = inject("getCurrentPluginDefine", () => {
|
||||||
const getScope: any = inject("get:scope");
|
return {};
|
||||||
const getPluginType: any = inject("get:plugin:type");
|
});
|
||||||
|
const getScope: any = inject("get:scope", () => {
|
||||||
|
return {};
|
||||||
|
});
|
||||||
|
const getPluginType: any = inject("get:plugin:type", () => {
|
||||||
|
return "plugin";
|
||||||
|
});
|
||||||
|
|
||||||
const searchKeyRef = ref("");
|
const searchKeyRef = ref("");
|
||||||
const optionsRef = ref([]);
|
const optionsRef = ref([]);
|
||||||
|
@ -96,7 +102,7 @@ const getOptions = async () => {
|
||||||
}
|
}
|
||||||
const pluginType = getPluginType();
|
const pluginType = getPluginType();
|
||||||
const { form } = getScope();
|
const { form } = getScope();
|
||||||
const input = pluginType === "plugin" ? form.input : form;
|
const input = (pluginType === "plugin" ? form?.input : form) || {};
|
||||||
|
|
||||||
for (let key in define.input) {
|
for (let key in define.input) {
|
||||||
const inWatches = props.watches.includes(key);
|
const inWatches = props.watches.includes(key);
|
||||||
|
@ -186,7 +192,7 @@ watch(
|
||||||
() => {
|
() => {
|
||||||
const pluginType = getPluginType();
|
const pluginType = getPluginType();
|
||||||
const { form, key } = getScope();
|
const { form, key } = getScope();
|
||||||
const input = pluginType === "plugin" ? form.input : form;
|
const input = (pluginType === "plugin" ? form?.input : form) || {};
|
||||||
const watches = {};
|
const watches = {};
|
||||||
for (const key of props.watches) {
|
for (const key of props.watches) {
|
||||||
watches[key] = input[key];
|
watches[key] = input[key];
|
||||||
|
@ -198,10 +204,11 @@ watch(
|
||||||
},
|
},
|
||||||
async (value, oldValue) => {
|
async (value, oldValue) => {
|
||||||
const { form } = value;
|
const { form } = value;
|
||||||
const oldForm = oldValue.form;
|
const oldForm: any = oldValue?.form;
|
||||||
let changed = oldForm == null || optionsRef.value.length == 0;
|
let changed = oldForm == null || optionsRef.value.length == 0;
|
||||||
for (const key of props.watches) {
|
for (const key of props.watches) {
|
||||||
if (form[key] != oldForm[key]) {
|
//@ts-ignore
|
||||||
|
if (oldForm && form[key] != oldForm[key]) {
|
||||||
changed = true;
|
changed = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,9 @@ const attrs = useAttrs();
|
||||||
|
|
||||||
const otpCodeRef = ref("");
|
const otpCodeRef = ref("");
|
||||||
const getScope: any = inject("get:scope");
|
const getScope: any = inject("get:scope");
|
||||||
const getPluginType: any = inject("get:plugin:type");
|
const getPluginType: any = inject("get:plugin:type", () => {
|
||||||
|
return "access";
|
||||||
|
});
|
||||||
|
|
||||||
async function loginWithOTPCode(otpCode: string) {
|
async function loginWithOTPCode(otpCode: string) {
|
||||||
const { form } = getScope();
|
const { form } = getScope();
|
||||||
|
|
|
@ -61,6 +61,15 @@ export const certdResources = [
|
||||||
isMenu: false,
|
isMenu: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "流水线模版批量创建",
|
||||||
|
name: "PipelineTemplateImport",
|
||||||
|
path: "/certd/pipeline/template/import",
|
||||||
|
component: "/certd/pipeline/template/import/index.vue",
|
||||||
|
meta: {
|
||||||
|
isMenu: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: "证书仓库",
|
title: "证书仓库",
|
||||||
name: "CertStore",
|
name: "CertStore",
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { useRouter } from "vue-router";
|
||||||
import { useModal } from "/@/use/use-modal";
|
import { useModal } from "/@/use/use-modal";
|
||||||
import createCrudOptionsPipeline from "../crud";
|
import createCrudOptionsPipeline from "../crud";
|
||||||
import * as pipelineApi from "../api";
|
import * as pipelineApi from "../api";
|
||||||
|
import { useTemplate } from "/@/views/certd/pipeline/template/use";
|
||||||
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||||
const api = templateApi;
|
const api = templateApi;
|
||||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||||
|
@ -29,6 +30,9 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const model = useModal();
|
const model = useModal();
|
||||||
|
|
||||||
|
const { openCreateFromTemplateDialog } = useTemplate();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
crudOptions: {
|
crudOptions: {
|
||||||
request: {
|
request: {
|
||||||
|
@ -73,6 +77,27 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||||
buttons: {
|
buttons: {
|
||||||
edit: { show: false },
|
edit: { show: false },
|
||||||
copy: { show: false },
|
copy: { show: false },
|
||||||
|
use: {
|
||||||
|
text: null,
|
||||||
|
title: "使用此模版创建流水线",
|
||||||
|
icon: "ion:duplicate-outline",
|
||||||
|
click({ row }) {
|
||||||
|
openCreateFromTemplateDialog({
|
||||||
|
templateId: row.id,
|
||||||
|
onCreated: ({ id }) => {
|
||||||
|
router.push({ path: "/certd/pipeline/detail", query: { id, editMode: true } });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
import: {
|
||||||
|
text: null,
|
||||||
|
title: "批量导入创建",
|
||||||
|
icon: "ion:duplicate",
|
||||||
|
click({ row }) {
|
||||||
|
router.push({ path: "/certd/pipeline/template/import", query: { templateId: row.id } });
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
columns: {
|
columns: {
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
import { CreateCrudOptionsProps, CreateCrudOptionsRet, importTable } from "@fast-crud/fast-crud";
|
||||||
|
import { Modal, notification } from "ant-design-vue";
|
||||||
|
|
||||||
|
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||||
|
return {
|
||||||
|
crudOptions: {
|
||||||
|
mode: {
|
||||||
|
name: "local",
|
||||||
|
isMergeWhenUpdate: true,
|
||||||
|
isAppendWhenAdd: true,
|
||||||
|
},
|
||||||
|
//启用addRow按钮
|
||||||
|
actionbar: {
|
||||||
|
buttons: {
|
||||||
|
//禁用弹框添加
|
||||||
|
add: { show: false },
|
||||||
|
//启用添加行
|
||||||
|
addRow: { show: true },
|
||||||
|
//导入按钮
|
||||||
|
import: {
|
||||||
|
show: false,
|
||||||
|
text: "批量导入",
|
||||||
|
type: "primary",
|
||||||
|
click() {
|
||||||
|
const modal = Modal.info({
|
||||||
|
title: "批量导入",
|
||||||
|
okText: "关闭",
|
||||||
|
content() {
|
||||||
|
async function onChange(e: any) {
|
||||||
|
const file = e.target.files[0];
|
||||||
|
await importTable(crudExpose, { file, append: true });
|
||||||
|
modal.destroy();
|
||||||
|
notification.success({
|
||||||
|
message: "导入成功",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
1、<a href={"template-import.csv"}>下载导入模板</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
2、<span>模板填充数据</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span>3、导入:</span>
|
||||||
|
<input type={"file"} onInput={onChange}></input>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
table: {
|
||||||
|
remove: {
|
||||||
|
//删除数据后不请求后台
|
||||||
|
refreshTable: false,
|
||||||
|
},
|
||||||
|
editable: {
|
||||||
|
enabled: true,
|
||||||
|
mode: "row",
|
||||||
|
activeTrigger: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
search: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
toolbar: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
pagination: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
columns: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { useColumns } from "@fast-crud/fast-crud";
|
||||||
|
import { createExtraColumns } from "/@/views/certd/pipeline/template/use";
|
||||||
|
import TemplateImportTable from "/@/views/certd/pipeline/template/import/table.vue";
|
||||||
|
import { Ref } from "vue";
|
||||||
|
|
||||||
|
export function createFormOptions(detail: Ref): any {
|
||||||
|
const { buildFormOptions } = useColumns();
|
||||||
|
|
||||||
|
const crudOptions = {
|
||||||
|
columns: {
|
||||||
|
...createExtraColumns(),
|
||||||
|
templateProps: {
|
||||||
|
title: "流水线导入",
|
||||||
|
type: "text",
|
||||||
|
form: {
|
||||||
|
order: 1,
|
||||||
|
component: {
|
||||||
|
name: TemplateImportTable,
|
||||||
|
detail: detail,
|
||||||
|
},
|
||||||
|
col: {
|
||||||
|
span: 24,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return buildFormOptions(crudOptions);
|
||||||
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
<template>
|
||||||
|
<fs-page class="page-template-import">
|
||||||
|
<template #header>
|
||||||
|
<div class="title flex flex-1 items-center">
|
||||||
|
<fs-button class="back" icon="ion:chevron-back-outline" @click="goBack"></fs-button>
|
||||||
|
<div class="ml-10">从模版{{ detail?.template?.title }}批量创建流水线</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<fs-form v-if="importFromOptions" ref="formRef" class="mt-10" v-bind="importFromOptions"> </fs-form>
|
||||||
|
<div class="p-10">
|
||||||
|
<a-button class="ml-20" type="primary" @click="doImport">确定导入 </a-button>
|
||||||
|
</div>
|
||||||
|
</fs-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="tsx">
|
||||||
|
import { onMounted, ref, Ref } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
|
import { templateApi } from "../api";
|
||||||
|
import { createFormOptions } from "/@/views/certd/pipeline/template/import/form";
|
||||||
|
import { cloneDeep } from "lodash-es";
|
||||||
|
import { fillPipelineByDefaultForm } from "/@/views/certd/pipeline/certd-form/use";
|
||||||
|
import { eachSteps } from "/@/views/certd/pipeline/utils";
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const templateId = route.query.templateId as string;
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
function goBack() {
|
||||||
|
router.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
type TemplateDetail = {
|
||||||
|
template: any;
|
||||||
|
pipeline: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
const detail: Ref<TemplateDetail> = ref();
|
||||||
|
|
||||||
|
async function getTemplateDetail() {
|
||||||
|
if (!templateId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
detail.value = await templateApi.GetDetail(parseInt(templateId));
|
||||||
|
}
|
||||||
|
|
||||||
|
const importFromOptions = ref();
|
||||||
|
onMounted(async () => {
|
||||||
|
await getTemplateDetail();
|
||||||
|
importFromOptions.value = createFormOptions(detail);
|
||||||
|
});
|
||||||
|
|
||||||
|
const formRef = ref();
|
||||||
|
async function doImport() {
|
||||||
|
await formRef.value.validate();
|
||||||
|
|
||||||
|
const form = formRef.value.getFormData();
|
||||||
|
|
||||||
|
const importTableRef = formRef.value.getComponentRef("templateProps");
|
||||||
|
|
||||||
|
const templateList = importTableRef.value.getData();
|
||||||
|
|
||||||
|
for (let i = 0; i < templateList.length; i++) {
|
||||||
|
const tempInputs = templateList[i];
|
||||||
|
const title = tempInputs.title;
|
||||||
|
delete tempInputs.title;
|
||||||
|
|
||||||
|
let newPipeline = cloneDeep(detail.value.pipeline);
|
||||||
|
newPipeline = fillPipelineByDefaultForm(newPipeline, form);
|
||||||
|
//填充模版参数
|
||||||
|
const steps: any = {};
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newPipeline.title = title;
|
||||||
|
const groupId = form.groupId;
|
||||||
|
|
||||||
|
await templateApi.CreatePipelineByTemplate({
|
||||||
|
title,
|
||||||
|
content: JSON.stringify(newPipeline),
|
||||||
|
keepHistoryCount: 30,
|
||||||
|
groupId,
|
||||||
|
templateId: parseInt(templateId),
|
||||||
|
pipeline: {
|
||||||
|
title: form.title,
|
||||||
|
templateProps: templateList,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.page-template-import {
|
||||||
|
.ant-table-container {
|
||||||
|
.ant-select {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,115 @@
|
||||||
|
<template>
|
||||||
|
<fs-crud ref="crudRef" class="template-import-table" v-bind="crudBinding">
|
||||||
|
<template #actionbar-right>
|
||||||
|
<div class="helper ml-10">1. 点击添加按钮,添加一行记录; 2.输入流水线参数; 3. 点击右边“确认创建”,批量创建流水线。</div>
|
||||||
|
</template>
|
||||||
|
</fs-crud>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="tsx">
|
||||||
|
import { computed, onMounted, ref, Ref, nextTick, watch } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
|
import { templateApi } from "../api";
|
||||||
|
import { usePluginStore } from "/@/store/plugin";
|
||||||
|
import { useStepHelper } from "../utils";
|
||||||
|
import { useFs } from "@fast-crud/fast-crud";
|
||||||
|
import createCrudOptions from "./crud";
|
||||||
|
import { Form } from "ant-design-vue";
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: "TemplateImportTable",
|
||||||
|
});
|
||||||
|
|
||||||
|
const formItemContext = Form.useInjectFormItemContext();
|
||||||
|
|
||||||
|
type TemplateDetail = {
|
||||||
|
template: any;
|
||||||
|
pipeline: any;
|
||||||
|
};
|
||||||
|
const templateProps: Ref = ref({
|
||||||
|
input: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
detail: TemplateDetail;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const pluginStore = usePluginStore();
|
||||||
|
|
||||||
|
const { getStepsMap } = useStepHelper(pluginStore);
|
||||||
|
|
||||||
|
function buildColumns(steps: any) {
|
||||||
|
const columns: any = {
|
||||||
|
title: {
|
||||||
|
title: "流水线标题",
|
||||||
|
type: "text",
|
||||||
|
form: {
|
||||||
|
component: {
|
||||||
|
placeholder: "请输入流水线标题",
|
||||||
|
},
|
||||||
|
rules: [{ required: true, message: "请输入流水线标题" }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
for (const inputKey in templateProps.value.input) {
|
||||||
|
const [stepId, key] = inputKey.split(".");
|
||||||
|
const item = steps[stepId].input[key];
|
||||||
|
columns[inputKey] = {
|
||||||
|
title: item.define.title,
|
||||||
|
type: "text",
|
||||||
|
form: {
|
||||||
|
...item.define,
|
||||||
|
},
|
||||||
|
column: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
table: {
|
||||||
|
slots: {
|
||||||
|
headerCell({ column }: any) {
|
||||||
|
const col = columns[column.key];
|
||||||
|
if (col && col?.form?.helper) {
|
||||||
|
return (
|
||||||
|
<span class={"flex "}>
|
||||||
|
{col.title}
|
||||||
|
<a-tooltip title={col.form.helper}>
|
||||||
|
<fs-icon class={"ml-5"} icon={"ion:alert-circle-outline"}></fs-icon>
|
||||||
|
</a-tooltip>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
columns,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//启用行编辑模式
|
||||||
|
const { crudBinding, crudRef, crudExpose, appendCrudOptions } = useFs({ createCrudOptions, context: {} });
|
||||||
|
onMounted(async () => {
|
||||||
|
await pluginStore.init();
|
||||||
|
await nextTick();
|
||||||
|
const steps = getStepsMap(props.detail.pipeline);
|
||||||
|
templateProps.value = JSON.parse(props.detail.template?.content ?? "{input:{}}");
|
||||||
|
appendCrudOptions({ ...buildColumns(steps) });
|
||||||
|
crudBinding.value.data = [];
|
||||||
|
await crudExpose.editable.enable({ mode: "row" });
|
||||||
|
});
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
getData() {
|
||||||
|
return crudBinding.value.data;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.template-import-table {
|
||||||
|
.ant-table-container {
|
||||||
|
.ant-select {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -8,10 +8,69 @@ import { ref } from "vue";
|
||||||
import { fillPipelineByDefaultForm } from "/@/views/certd/pipeline/certd-form/use";
|
import { fillPipelineByDefaultForm } from "/@/views/certd/pipeline/certd-form/use";
|
||||||
import { cloneDeep } from "lodash-es";
|
import { cloneDeep } from "lodash-es";
|
||||||
|
|
||||||
|
export function createExtraColumns() {
|
||||||
|
const groupDictRef = dict({
|
||||||
|
url: "/pi/pipeline/group/all",
|
||||||
|
value: "id",
|
||||||
|
label: "name",
|
||||||
|
});
|
||||||
|
const randomHour = Math.floor(Math.random() * 6);
|
||||||
|
const randomMin = Math.floor(Math.random() * 60);
|
||||||
|
return {
|
||||||
|
triggerCron: {
|
||||||
|
title: "定时触发",
|
||||||
|
type: "text",
|
||||||
|
form: {
|
||||||
|
value: `0 ${randomMin} ${randomHour} * * *`,
|
||||||
|
component: {
|
||||||
|
name: "cron-editor",
|
||||||
|
vModel: "modelValue",
|
||||||
|
placeholder: "0 0 4 * * *",
|
||||||
|
},
|
||||||
|
col: {
|
||||||
|
span: 24,
|
||||||
|
},
|
||||||
|
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: 999,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function useTemplate() {
|
export function useTemplate() {
|
||||||
const { openCrudFormDialog } = useFormWrapper();
|
const { openCrudFormDialog } = useFormWrapper();
|
||||||
|
|
||||||
async function openCreateFromTemplateDialog(req: { templateId?: number }) {
|
async function openCreateFromTemplateDialog(req: { templateId?: number; onCreated?: (ctx: any) => void }) {
|
||||||
//检查是否流水线数量超出限制
|
//检查是否流水线数量超出限制
|
||||||
await checkPipelineLimit();
|
await checkPipelineLimit();
|
||||||
const detail = await templateApi.GetDetail(req.templateId);
|
const detail = await templateApi.GetDetail(req.templateId);
|
||||||
|
@ -24,12 +83,6 @@ export function useTemplate() {
|
||||||
const templateProps = JSON.parse(detail.template.content || "{}");
|
const templateProps = JSON.parse(detail.template.content || "{}");
|
||||||
const pipeline = detail.pipeline;
|
const pipeline = detail.pipeline;
|
||||||
|
|
||||||
const groupDictRef = dict({
|
|
||||||
url: "/pi/pipeline/group/all",
|
|
||||||
value: "id",
|
|
||||||
label: "name",
|
|
||||||
});
|
|
||||||
|
|
||||||
const wrapperRef = ref();
|
const wrapperRef = ref();
|
||||||
function getFormData() {
|
function getFormData() {
|
||||||
if (!wrapperRef.value) {
|
if (!wrapperRef.value) {
|
||||||
|
@ -38,8 +91,6 @@ export function useTemplate() {
|
||||||
return wrapperRef.value.getFormData();
|
return wrapperRef.value.getFormData();
|
||||||
}
|
}
|
||||||
|
|
||||||
const randomHour = Math.floor(Math.random() * 6);
|
|
||||||
const randomMin = Math.floor(Math.random() * 60);
|
|
||||||
const templateFormRef = ref();
|
const templateFormRef = ref();
|
||||||
|
|
||||||
async function doSubmit(opts: { form: any }) {
|
async function doSubmit(opts: { form: any }) {
|
||||||
|
@ -67,13 +118,16 @@ export function useTemplate() {
|
||||||
const title = form.title;
|
const title = form.title;
|
||||||
newPipeline.title = title;
|
newPipeline.title = title;
|
||||||
const groupId = form.groupId;
|
const groupId = form.groupId;
|
||||||
await templateApi.CreatePipelineByTemplate({
|
const { id } = await templateApi.CreatePipelineByTemplate({
|
||||||
title,
|
title,
|
||||||
content: JSON.stringify(newPipeline),
|
content: JSON.stringify(newPipeline),
|
||||||
keepHistoryCount: 30,
|
keepHistoryCount: 30,
|
||||||
groupId,
|
groupId,
|
||||||
templateId: detail.template.id,
|
templateId: detail.template.id,
|
||||||
});
|
});
|
||||||
|
if (req.onCreated) {
|
||||||
|
req.onCreated({ id });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const crudOptions = {
|
const crudOptions = {
|
||||||
|
@ -104,50 +158,7 @@ export function useTemplate() {
|
||||||
rules: [{ required: true, message: "请输入流水线标题" }],
|
rules: [{ required: true, message: "请输入流水线标题" }],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
triggerCron: {
|
...createExtraColumns(),
|
||||||
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: 99,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -48,10 +48,10 @@ export class TemplateService extends BaseService<TemplateEntity> {
|
||||||
newPipeline.title = template.title + "模版流水线"
|
newPipeline.title = template.title + "模版流水线"
|
||||||
newPipeline.templateId = template.id
|
newPipeline.templateId = template.id
|
||||||
newPipeline.isTemplate = true
|
newPipeline.isTemplate = true
|
||||||
|
newPipeline.userId = template.userId
|
||||||
|
|
||||||
const pipelineJson: Pipeline = JSON.parse(newPipeline.content)
|
const pipelineJson: Pipeline = JSON.parse(newPipeline.content)
|
||||||
delete pipelineJson.triggers
|
delete pipelineJson.triggers
|
||||||
pipelineJson.id = template.id
|
|
||||||
pipelineJson.userId = template.userId
|
pipelineJson.userId = template.userId
|
||||||
pipelineJson.title = newPipeline.title
|
pipelineJson.title = newPipeline.title
|
||||||
newPipeline.content = JSON.stringify(pipelineJson)
|
newPipeline.content = JSON.stringify(pipelineJson)
|
||||||
|
@ -121,6 +121,8 @@ export class TemplateService extends BaseService<TemplateEntity> {
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.pipelineService.save(newPipeline)
|
await this.pipelineService.save(newPipeline)
|
||||||
|
|
||||||
|
return newPipeline
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue