mirror of
https://github.com/certd/certd.git
synced 2026-04-30 01:07:28 +08:00
perf: 首次使用提示新手教程按钮
This commit is contained in:
@@ -39,7 +39,7 @@ Certd® 是一个免费的全自动证书管理系统,让你的网站证书永
|
|||||||
* **站点证书监控**: 定时监控网站证书的过期时间
|
* **站点证书监控**: 定时监控网站证书的过期时间
|
||||||
* **多用户管理**: 用户可以管理自己的证书流水线
|
* **多用户管理**: 用户可以管理自己的证书流水线
|
||||||
* **多语言支持**: 中英双语切换
|
* **多语言支持**: 中英双语切换
|
||||||
* **一键无忧升级**: 版本向下兼容
|
* **无忧升级**: 版本向下兼容
|
||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
|||||||
@@ -15,6 +15,8 @@ Certd 是一款开源、免费、全自动申请和部署更新SSL证书的工
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## 1、关于证书续期
|
## 1、关于证书续期
|
||||||
>* 实际上没有办法不改变证书文件本身情况下直接续期或者续签。
|
>* 实际上没有办法不改变证书文件本身情况下直接续期或者续签。
|
||||||
>* 我们所说的续期,其实就是按照全套流程重新申请一份新证书,然后重新部署上去。
|
>* 我们所说的续期,其实就是按照全套流程重新申请一份新证书,然后重新部署上去。
|
||||||
|
|||||||
@@ -15,3 +15,4 @@
|
|||||||
## 2. 图文教程链接
|
## 2. 图文教程链接
|
||||||
如果不方便登录系统,您还可以直接查看 [图文教程](https://gitee.com/certd/certd/blob/v2/step.md)
|
如果不方便登录系统,您还可以直接查看 [图文教程](https://gitee.com/certd/certd/blob/v2/step.md)
|
||||||
|
|
||||||
|

|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 109 KiB |
@@ -1,5 +1,5 @@
|
|||||||
import { HttpClient, ILogger, utils } from "@certd/basic";
|
import { HttpClient, ILogger, utils } from "@certd/basic";
|
||||||
import { IAccess, IServiceGetter, Pager, PageRes, PageSearch, Registrable } from "@certd/pipeline";
|
import { IAccess, IServiceGetter, PageRes, PageSearch, Registrable } from "@certd/pipeline";
|
||||||
|
|
||||||
export type DnsProviderDefine = Registrable & {
|
export type DnsProviderDefine = Registrable & {
|
||||||
accessType: string;
|
accessType: string;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Pager, PageRes, PageSearch } from "@certd/pipeline";
|
import { HttpClient, ILogger } from "@certd/basic";
|
||||||
|
import { PageRes, PageSearch } from "@certd/pipeline";
|
||||||
|
import punycode from "punycode.js";
|
||||||
import { CreateRecordOptions, DnsProviderContext, DnsProviderDefine, DomainRecord, IDnsProvider, RemoveRecordOptions } from "./api.js";
|
import { CreateRecordOptions, DnsProviderContext, DnsProviderDefine, DomainRecord, IDnsProvider, RemoveRecordOptions } from "./api.js";
|
||||||
import { dnsProviderRegistry } from "./registry.js";
|
import { dnsProviderRegistry } from "./registry.js";
|
||||||
import { HttpClient, ILogger } from "@certd/basic";
|
|
||||||
import punycode from "punycode.js";
|
|
||||||
export abstract class AbstractDnsProvider<T = any> implements IDnsProvider<T> {
|
export abstract class AbstractDnsProvider<T = any> implements IDnsProvider<T> {
|
||||||
ctx!: DnsProviderContext;
|
ctx!: DnsProviderContext;
|
||||||
http!: HttpClient;
|
http!: HttpClient;
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from "vue";
|
import { onMounted, ref } from "vue";
|
||||||
import TutorialSteps from "/@/components/tutorial/tutorial-steps.vue";
|
import TutorialSteps from "/@/components/tutorial/tutorial-steps.vue";
|
||||||
|
import { mitter } from "/@/utils/util.mitt";
|
||||||
|
import { useI18n } from "/@/locales";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "TutorialModal",
|
name: "TutorialModal",
|
||||||
@@ -8,6 +10,7 @@ defineOptions({
|
|||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
showIcon?: boolean;
|
showIcon?: boolean;
|
||||||
|
mode?: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const openedRef = ref(false);
|
const openedRef = ref(false);
|
||||||
@@ -15,17 +18,26 @@ function open() {
|
|||||||
openedRef.value = true;
|
openedRef.value = true;
|
||||||
}
|
}
|
||||||
const slots = defineSlots();
|
const slots = defineSlots();
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
mitter.on("openTutorialModal", () => {
|
||||||
|
if (props.mode === "nav") {
|
||||||
|
open();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
const { t } = useI18n();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="tutorial-button pointer" @click="open">
|
<div class="tutorial-button pointer" @click="open">
|
||||||
<template v-if="!slots.default">
|
<template v-if="!slots.default">
|
||||||
<fs-icon v-if="showIcon === false" icon="ant-design:question-circle-outlined" class="mr-0.5"></fs-icon>
|
<fs-icon v-if="showIcon === false" icon="ant-design:question-circle-outlined" class="mr-0.5"></fs-icon>
|
||||||
<div class="hidden md:block">{{ $t("tutorial.title") }}</div>
|
<div class="hidden md:block">{{ t("tutorial.title") }}</div>
|
||||||
</template>
|
</template>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
<a-modal v-model:open="openedRef" class="tutorial-modal" width="90%">
|
<a-modal v-model:open="openedRef" class="tutorial-modal" width="90%">
|
||||||
<template #title>{{ $t("tutorial.title") }}</template>
|
<template #title>{{ t("tutorial.title") }}</template>
|
||||||
<tutorial-steps v-if="openedRef" />
|
<tutorial-steps v-if="openedRef" />
|
||||||
<template #footer></template>
|
<template #footer></template>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<div class="text">
|
<div class="text">
|
||||||
<h3 class="title">{{ number }} {{ currentStepItem.title }}</h3>
|
<h3 class="title">{{ number }} {{ currentStepItem.title }}</h3>
|
||||||
<div class="description mt-5">
|
<div class="description mt-5">
|
||||||
<div v-for="desc of currentStepItem.descriptions">{{ desc }}</div>
|
<div v-for="(desc, index) of currentStepItem.descriptions" :key="index">{{ desc }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="currentStepItem.body">
|
<div v-if="currentStepItem.body">
|
||||||
<fs-render :render-func="currentStepItem.body" />
|
<fs-render :render-func="currentStepItem.body" />
|
||||||
@@ -29,9 +29,12 @@
|
|||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
import { FsRender } from "@fast-crud/fast-crud";
|
import { FsRender } from "@fast-crud/fast-crud";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
|
|
||||||
const { t } = useI18n();
|
|
||||||
import SimpleSteps from "./simple-steps.vue";
|
import SimpleSteps from "./simple-steps.vue";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
mode?: string;
|
||||||
|
}>();
|
||||||
|
const { t } = useI18n();
|
||||||
type Step = {
|
type Step = {
|
||||||
title: string;
|
title: string;
|
||||||
subTitle?: string;
|
subTitle?: string;
|
||||||
@@ -69,10 +72,10 @@ const steps = ref<Step[]>([
|
|||||||
title: t("guide.createCertPipeline.items.successTitle"),
|
title: t("guide.createCertPipeline.items.successTitle"),
|
||||||
descriptions: [t("guide.createCertPipeline.items.successDesc")],
|
descriptions: [t("guide.createCertPipeline.items.successDesc")],
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
title: t("guide.createCertPipeline.items.nextTitle"),
|
// title: t("guide.createCertPipeline.items.nextTitle"),
|
||||||
descriptions: [t("guide.createCertPipeline.items.nextDesc")],
|
// descriptions: [t("guide.createCertPipeline.items.nextDesc")],
|
||||||
},
|
// },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ provide("fn:ai.open", openChat);
|
|||||||
</template>
|
</template>
|
||||||
<template #header-right-0>
|
<template #header-right-0>
|
||||||
<div class="hover:bg-accent ml-1 mr-2 cursor-pointer rounded-full hidden md:block">
|
<div class="hover:bg-accent ml-1 mr-2 cursor-pointer rounded-full hidden md:block">
|
||||||
<tutorial-button class="flex-center header-btn" />
|
<tutorial-button class="flex-center header-btn" mode="nav" />
|
||||||
</div>
|
</div>
|
||||||
<div class="hover:bg-accent ml-1 mr-2 cursor-pointer rounded-full">
|
<div class="hover:bg-accent ml-1 mr-2 cursor-pointer rounded-full">
|
||||||
<vip-button class="flex-center header-btn" mode="nav" />
|
<vip-button class="flex-center header-btn" mode="nav" />
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export default {
|
|||||||
legoCertPipeline: "Lego Certificate Pipeline",
|
legoCertPipeline: "Lego Certificate Pipeline",
|
||||||
customPipeline: "Custom Pipeline",
|
customPipeline: "Custom Pipeline",
|
||||||
batchAddPipeline: "Add Pipeline Use Template",
|
batchAddPipeline: "Add Pipeline Use Template",
|
||||||
|
myPipelinesDesc: "Pipeline Mode: Apply -> Deploy -> Schedule",
|
||||||
},
|
},
|
||||||
order: {
|
order: {
|
||||||
confirmTitle: "Order Confirmation",
|
confirmTitle: "Order Confirmation",
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export default {
|
|||||||
legoCertPipeline: "Lego证书流水线",
|
legoCertPipeline: "Lego证书流水线",
|
||||||
customPipeline: "自定义流水线",
|
customPipeline: "自定义流水线",
|
||||||
batchAddPipeline: "模版批量创建流水线",
|
batchAddPipeline: "模版批量创建流水线",
|
||||||
|
myPipelinesDesc: "流水线模式:申请证书->部署证书->定时运行",
|
||||||
},
|
},
|
||||||
order: {
|
order: {
|
||||||
confirmTitle: "订单确认",
|
confirmTitle: "订单确认",
|
||||||
|
|||||||
@@ -167,8 +167,8 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
show: true,
|
show: true,
|
||||||
},
|
},
|
||||||
type: "text",
|
type: "text",
|
||||||
helper: t("certd.helperIpCname"),
|
|
||||||
form: {
|
form: {
|
||||||
|
helper: t("certd.helperIpCname"),
|
||||||
rules: [{ required: true, message: t("certd.ruleIpRequired") }],
|
rules: [{ required: true, message: t("certd.ruleIpRequired") }],
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<fs-page class="page-cert">
|
<fs-page class="page-cert">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="title">{{ t("certd.myPipelines") }}</div>
|
<div class="title">
|
||||||
|
{{ t("certd.myPipelines") }}
|
||||||
|
<div class="sub">{{ t("certd.pipelinePage.myPipelinesDesc") }}</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<!-- <a-alert v-if="settingStore.sysPublic.notice" type="warning" show-icon>
|
<!-- <a-alert v-if="settingStore.sysPublic.notice" type="warning" show-icon>
|
||||||
<template #message>
|
<template #message>
|
||||||
|
|||||||
@@ -138,32 +138,33 @@
|
|||||||
</a-row>
|
</a-row>
|
||||||
</a-card>
|
</a-card>
|
||||||
</div>
|
</div>
|
||||||
|
<a-tour v-bind="tour" v-model:current="tour.current" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { FsIcon } from "@fast-crud/fast-crud";
|
import { FsIcon } from "@fast-crud/fast-crud";
|
||||||
import SimpleSteps from "/@/components/tutorial/simple-steps.vue";
|
import { notification } from "ant-design-vue";
|
||||||
import { useUserStore } from "/@/store/user";
|
|
||||||
import { computed, ComputedRef, onMounted, Ref, ref } from "vue";
|
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import StatisticCard from "/@/views/framework/home/dashboard/statistic-card.vue";
|
import { computed, ComputedRef, onMounted, Ref, ref } from "vue";
|
||||||
import TutorialButton from "/@/components/tutorial/index.vue";
|
|
||||||
import DayCount from "./charts/day-count.vue";
|
|
||||||
import PieCount from "./charts/pie-count.vue";
|
|
||||||
import ExpiringList from "./charts/expiring-list.vue";
|
|
||||||
import SuiteCard from "./suite-card.vue";
|
|
||||||
import { useSettingStore } from "/@/store/settings";
|
|
||||||
import { SiteInfo } from "/@/store/settings/api.basic";
|
|
||||||
import { UserInfoRes } from "/@/store/user/api.user";
|
|
||||||
import { GetStatisticCount } from "/@/views/framework/home/dashboard/api";
|
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import * as api from "./api";
|
import * as api from "./api";
|
||||||
|
import DayCount from "./charts/day-count.vue";
|
||||||
|
import ExpiringList from "./charts/expiring-list.vue";
|
||||||
|
import NoticeBar from "./notice-bar.vue";
|
||||||
|
import SuiteCard from "./suite-card.vue";
|
||||||
|
import TutorialButton from "/@/components/tutorial/index.vue";
|
||||||
|
import SimpleSteps from "/@/components/tutorial/simple-steps.vue";
|
||||||
|
import { usePluginStore } from "/@/store/plugin";
|
||||||
|
import { useSettingStore } from "/@/store/settings";
|
||||||
|
import { SiteInfo } from "/@/store/settings/api.basic";
|
||||||
|
import { useUserStore } from "/@/store/user";
|
||||||
|
import { UserInfoRes } from "/@/store/user/api.user";
|
||||||
|
import { LocalStorage } from "/@/utils/util.storage";
|
||||||
|
import { GetStatisticCount } from "/@/views/framework/home/dashboard/api";
|
||||||
|
import StatisticCard from "/@/views/framework/home/dashboard/statistic-card.vue";
|
||||||
import { useI18n } from "/src/locales";
|
import { useI18n } from "/src/locales";
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
import { usePluginStore } from "/@/store/plugin";
|
|
||||||
import { notification } from "ant-design-vue";
|
|
||||||
import NoticeBar from "./notice-bar.vue";
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "DashboardUser",
|
name: "DashboardUser",
|
||||||
});
|
});
|
||||||
@@ -291,6 +292,9 @@ onMounted(async () => {
|
|||||||
loadLatestVersion();
|
loadLatestVersion();
|
||||||
loadCount();
|
loadCount();
|
||||||
loadPluginGroups();
|
loadPluginGroups();
|
||||||
|
// if (count.value.pipelineCount === 0) {
|
||||||
|
tourHandleOpen(true);
|
||||||
|
// }
|
||||||
});
|
});
|
||||||
|
|
||||||
function openUpgradeUrl() {
|
function openUpgradeUrl() {
|
||||||
@@ -325,6 +329,50 @@ const noticeList = computed(() => {
|
|||||||
|
|
||||||
return list;
|
return list;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function useTour() {
|
||||||
|
const tour = ref({
|
||||||
|
open: false,
|
||||||
|
current: 0,
|
||||||
|
steps: [],
|
||||||
|
onClose: () => {
|
||||||
|
tour.value.open = false;
|
||||||
|
LocalStorage.set("home-tour-off", true, 999999999);
|
||||||
|
},
|
||||||
|
onFinish: () => {
|
||||||
|
tour.value.open = false;
|
||||||
|
LocalStorage.set("home-tour-off", true, 999999999);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const tourHandleOpen = (val: boolean): void => {
|
||||||
|
// if (LocalStorage.get("home-tour-off")) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
initSteps();
|
||||||
|
tour.value.open = val;
|
||||||
|
};
|
||||||
|
|
||||||
|
function initSteps() {
|
||||||
|
//@ts-ignore
|
||||||
|
tour.value.steps = [
|
||||||
|
{
|
||||||
|
title: "您是第一次使用吗?",
|
||||||
|
description: "此处可以查看新手教程哦 ↑↑↑↑",
|
||||||
|
target: () => {
|
||||||
|
return document.querySelector("header .tutorial-button");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
tour,
|
||||||
|
tourHandleOpen,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const { tour, tourHandleOpen } = useTour();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
|
|||||||
Reference in New Issue
Block a user