perf: 增加向导

pull/243/head
xiaojunnuo 2024-10-31 18:04:51 +08:00
parent babd5897ae
commit 6d9ef26eca
6 changed files with 140 additions and 72 deletions

View File

@ -132,6 +132,18 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
}
}
}
},
helper: {
render() {
const closeForm = () => {
crudExpose.getFormWrapperRef().close();
};
return (
<router-link to={"/sys/cname/provider"} onClick={closeForm}>
CNAME
</router-link>
);
}
}
},
column: {

View File

@ -1,7 +1,12 @@
<template>
<fs-page class="page-cert">
<template #header>
<div class="title">CNAME记录管理</div>
<div class="title">
CNAME记录管理
<span class="sub">
<a href="https://certd.docmirror.cn/guide/feature/cname/" target="_blank">CNAME功能原理及使用说明</a>
</span>
</div>
</template>
<fs-crud ref="crudRef" v-bind="crudBinding">
<template #pagination-left>

View File

@ -1,79 +1,127 @@
<template>
<fs-page class="fs-pipeline-detail">
<pipeline-edit v-model:edit-mode="editMode" :pipeline-id="pipelineId" :options="pipelineOptionsRef"></pipeline-edit>
<a-tour v-model:current="tourCurrent" :open="tourOpen" :steps="tourSteps" @close="tourHandleOpen(false)" />
</fs-page>
</template>
<script lang="ts">
import { defineComponent, Ref, ref } from "vue";
<script lang="ts" setup>
import { nextTick, onMounted, Ref, ref } from "vue";
import PipelineEdit from "./pipeline/index.vue";
import * as pluginApi from "./api.plugin";
import * as historyApi from "./api.history";
import * as api from "./api";
import { useRoute } from "vue-router";
import { PipelineDetail, PipelineOptions, PluginGroups, RunHistory } from "./pipeline/type";
import { TourProps } from "ant-design-vue";
export default defineComponent({
name: "PipelineDetail",
components: { PipelineEdit },
setup() {
const route = useRoute();
const pipelineId: Ref = ref(route.query.id);
const pipelineOptions: PipelineOptions = {
async getPipelineDetail({ pipelineId }) {
const detail = await api.GetDetail(pipelineId);
return {
pipeline: {
id: detail.pipeline.id,
stages: [],
triggers: [],
...JSON.parse(detail.pipeline.content || "{}")
}
} as PipelineDetail;
},
async getHistoryList({ pipelineId }) {
const list: RunHistory[] = await historyApi.GetList({ pipelineId });
return list;
},
async getHistoryDetail({ historyId }): Promise<RunHistory> {
const detail = await historyApi.GetDetail({ id: historyId });
return detail;
},
async getPluginGroups() {
const groups = await pluginApi.GetGroups({});
return new PluginGroups(groups);
},
async doSave(pipelineConfig: any) {
await api.Save({
id: pipelineConfig.id,
content: JSON.stringify(pipelineConfig)
});
},
async doTrigger(options: { pipelineId: number; stepId?: string }) {
const { pipelineId, stepId } = options;
await api.Trigger(pipelineId, stepId);
}
};
const pipelineOptionsRef: Ref<PipelineOptions> = ref(pipelineOptions);
const editMode = ref(false);
if (route.query.editMode !== "false") {
editMode.value = true;
}
return {
pipelineOptionsRef,
pipelineId,
editMode
};
}
defineOptions({
name: "PipelineDetail"
});
const route = useRoute();
const pipelineId: Ref = ref(route.query.id);
const pipelineOptions: PipelineOptions = {
async getPipelineDetail({ pipelineId }) {
const detail = await api.GetDetail(pipelineId);
onLoaded();
return {
pipeline: {
id: detail.pipeline.id,
stages: [],
triggers: [],
...JSON.parse(detail.pipeline.content || "{}")
}
} as PipelineDetail;
},
async getHistoryList({ pipelineId }) {
const list: RunHistory[] = await historyApi.GetList({ pipelineId });
return list;
},
async getHistoryDetail({ historyId }): Promise<RunHistory> {
const detail = await historyApi.GetDetail({ id: historyId });
return detail;
},
async getPluginGroups() {
const groups = await pluginApi.GetGroups({});
return new PluginGroups(groups);
},
async doSave(pipelineConfig: any) {
await api.Save({
id: pipelineConfig.id,
content: JSON.stringify(pipelineConfig)
});
},
async doTrigger(options: { pipelineId: number; stepId?: string }) {
const { pipelineId, stepId } = options;
await api.Trigger(pipelineId, stepId);
}
};
const pipelineOptionsRef: Ref<PipelineOptions> = ref(pipelineOptions);
const editMode = ref(false);
if (route.query.editMode !== "false") {
editMode.value = true;
}
function useTour() {
const tourOpen = ref<boolean>(false);
const tourCurrent = ref(0);
//@ts-ignore
const tourSteps: TourProps["steps"] = ref([]);
const tourHandleOpen = (val: boolean): void => {
initSteps();
tourOpen.value = val;
};
function initSteps() {
//@ts-ignore
tourSteps.value = [
{
title: "恭喜创建证书流水线成功",
description: "这里就是我们刚创建的证书任务,点击可以修改证书申请参数",
target: () => {
return document.querySelector(".pipeline .stages .stage_0 .task");
}
},
{
title: "添加部署证书任务",
description: "证书申请成功之后还需要部署证书,点击这里可以添加部署任务",
target: () => {
return document.querySelector(".pipeline .stages .last-stage .tasks .task");
}
},
{
title: "手动运行流水线",
description: "点击此处可以手动运行流水线",
target: () => {
return document.querySelector(".pipeline .stages .first-stage .tasks .task");
}
}
];
}
return {
tourOpen,
tourCurrent,
tourSteps,
tourHandleOpen
};
}
const { tourOpen, tourCurrent, tourSteps, tourHandleOpen } = useTour();
async function onLoaded() {
await nextTick();
tourHandleOpen(true);
}
</script>
<style lang="less">
.page-pipeline-detail {

View File

@ -66,7 +66,7 @@
</template>
<template #item="{ element: stage, index }">
<div :key="stage.id" class="stage" :class="{ 'last-stage': isLastStage(index) }">
<div :key="stage.id" class="stage" :class="{ 'last-stage': isLastStage(index), ['stage_' + index]: true }">
<div class="title">
<text-editable v-model="stage.title" :disabled="!editMode"></text-editable>
<div v-plus class="icon-box stage-move-handle">
@ -252,7 +252,7 @@
</template>
<script lang="ts">
import { defineComponent, ref, provide, Ref, watch } from "vue";
import { defineComponent, ref, provide, Ref, watch, onMounted } from "vue";
import { useRouter } from "vue-router";
import PiTaskForm from "./component/task-form/index.vue";
import PiTriggerForm from "./component/trigger-form/index.vue";
@ -261,7 +261,7 @@ import PiTaskView from "./component/task-view/index.vue";
import PiStatusShow from "./component/status-show.vue";
import VDraggable from "vuedraggable";
import _ from "lodash-es";
import { message, Modal, notification } from "ant-design-vue";
import { message, Modal, notification, TourProps } from "ant-design-vue";
import { nanoid } from "nanoid";
import { PipelineDetail, PipelineOptions, PluginGroups, RunHistory } from "./type";
import type { Runnable, Stage } from "@certd/pipeline";

View File

@ -21,9 +21,12 @@
<div class="suggest">
<div>
<tutorial-button class="flex-center">
<a-tag color="blue" class="flex-center">
仅需3步全自动申请部署证书<fs-icon class="font-size-16 ml-5" icon="mingcute:question-line"></fs-icon
></a-tag>
<a-tooltip title="点击查看详细教程">
<a-tag color="blue" class="flex-center">
仅需3步全自动申请部署证书
<fs-icon class="font-size-16 ml-5" icon="mingcute:question-line"></fs-icon>
</a-tag>
</a-tooltip>
</tutorial-button>
<simple-steps></simple-steps>
</div>
@ -79,7 +82,7 @@
支持的部署任务列表 <a-tag color="green">{{ pluginGroups.groups.all.plugins.length }}</a-tag>
</template>
<a-row :gutter="10">
<a-col v-for="item of pluginGroups.groups.all.plugins" class="plugin-item-col" :span="4">
<a-col v-for="item of pluginGroups.groups.all.plugins" :key="item.name" class="plugin-item-col" :span="4">
<a-card>
<a-tooltip :title="item.desc">
<div class="plugin-item pointer">
@ -165,7 +168,7 @@ function transformStatusCount() {
async function loadCount() {
count.value = await GetStatisticCount();
transformStatusCount();
count.value.historyCountPerDay = count.value.historyCountPerDay.map((item) => {
count.value.historyCountPerDay = count.value.historyCountPerDay.map((item: any) => {
return {
name: item.date,
value: item.count

View File

@ -5,7 +5,7 @@
CNAME服务配置
<span class="sub">
此处配置的域名作为其他域名校验的代理当别的域名需要申请证书时通过CNAME映射到此域名上来验证所有权好处是任何域名都可以通过此方式申请证书也无需填写AccessSecret
<a href="https://certd.docmirror.cn/guide/feature/cname/" taget="_blank">CNAME功能原理及使用说明</a>
<a href="https://certd.docmirror.cn/guide/feature/cname/" target="_blank">CNAME功能原理及使用说明</a>
</span>
</div>
</template>