perf(domain): 添加域名过期时间同步进度显示功能

添加同步进度对话框,展示同步任务的实时进度和状态
更新中英文翻译文件,添加相关文案
修改AGENTS.md文档中的格式化命令说明
This commit is contained in:
xiaojunnuo
2026-05-07 00:22:41 +08:00
parent a7e281e278
commit 9d2937dd4b
5 changed files with 112 additions and 15 deletions
@@ -20,6 +20,7 @@ export default {
importFromProvider: "Import from Domain Provider",
syncExpirationDate: "Sync Domain Expiration Time",
syncTaskSubmitted: "Sync task submitted",
syncExpirationProgress: "Sync Domain Expiration Progress",
expirationMonitorSetting: "Domain Expiration Monitor Settings",
subdomainDnsHelper: "Note: In DNS validation mode, subdomains do not need to be maintained here, otherwise certificate application may be affected (except delegated subdomains or free second-level domains).",
path: "Path",
@@ -28,6 +29,9 @@ export default {
progress: "Progress",
operation: "Operation",
total: "Total",
current: "Current",
running: "Running",
done: "Done",
skipped: "Skipped",
failed: "Failed",
notExecuted: "Not executed",
@@ -20,6 +20,7 @@ export default {
importFromProvider: "从域名提供商导入",
syncExpirationDate: "同步域名过期时间",
syncTaskSubmitted: "同步任务已提交",
syncExpirationProgress: "同步域名过期时间进度",
expirationMonitorSetting: "域名过期监控设置",
subdomainDnsHelper: "注意:DNS校验方式下,子域名不需要在此处维护,否则会影响证书申请(子域名托管或免费二级域名除外)",
path: "路径",
@@ -28,6 +29,9 @@ export default {
progress: "进度",
operation: "操作",
total: "总数",
current: "当前",
running: "运行中",
done: "已完成",
skipped: "跳过",
failed: "失败",
notExecuted: "未执行",
@@ -3,7 +3,7 @@ import { Modal, notification } from "ant-design-vue";
import { Ref, ref } from "vue";
import { useRouter } from "vue-router";
import * as api from "./api";
import { useDomainImportManage } from "./use";
import { useDomainImportManage, useSyncExpirationProcess } from "./use";
import { Dicts } from "/@/components/plugins/lib/dicts";
import { useSettingStore } from "/@/store/settings";
import { useUserStore } from "/@/store/user";
@@ -52,6 +52,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
});
const openDomainImportManageDialog = useDomainImportManage();
const openSyncExpirationProcessDialog = useSyncExpirationProcess({ crudExpose });
const subdomainConfirmed = ref(false);
return {
@@ -140,13 +141,11 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
icon: "ion:refresh-outline",
text: t("certd.domain.syncExpirationDate"),
click: async () => {
await api.SyncExpirationStart();
notification.success({
message: t("certd.domain.syncTaskSubmitted"),
});
setTimeout(() => {
crudExpose.doRefresh();
}, 2000);
try {
await api.SyncExpirationStart();
} finally {
await openSyncExpirationProcessDialog();
}
},
},
monitorSettingSave: {
@@ -361,7 +360,9 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
<fs-values-format modelValue={row.challengeType} dict={Dicts.challengeTypeDict} color={"auto"}></fs-values-format>
<fs-values-format modelValue={row.httpUploaderType} dict={httpUploaderTypeDict} color={"auto"}></fs-values-format>
<fs-values-format class={"ml-5"} modelValue={row.httpUploaderAccess} dict={accessDict} color={"auto"}></fs-values-format>
<a-tag class={"ml-5 flex items-center"}>{t("certd.domain.path")}: {row.httpUploadRootDir}</a-tag>
<a-tag class={"ml-5 flex items-center"}>
{t("certd.domain.path")}: {row.httpUploadRootDir}
</a-tag>
</div>
);
}
@@ -1,11 +1,10 @@
import { message } from "ant-design-vue";
import * as api from "./api";
import { useFormDialog } from "/@/use/use-dialog";
import { compute } from "@fast-crud/fast-crud";
import { Dicts } from "/@/components/plugins/lib/dicts";
import { useSettingStore } from "/@/store/settings";
import { Ref, ref } from "vue";
import * as api from "./api";
import DomainImportTaskStatus from "./import.vue";
import { useI18n } from "/@/locales";
import { useSettingStore } from "/@/store/settings";
import { useFormDialog } from "/@/use/use-dialog";
export function useDomainImport() {
const { openFormDialog } = useFormDialog();
const { t } = useI18n();
@@ -92,3 +91,92 @@ export function useDomainImportManage() {
});
};
}
export function useSyncExpirationProcess(opts: { crudExpose: any }) {
const { openFormDialog } = useFormDialog();
const { t } = useI18n();
return async function openSyncExpirationProcessDialog() {
const taskStatus: Ref<any> = ref({});
const errors: Ref<string[]> = ref([]);
const timerRef: Ref<any> = ref(null);
function stop() {
if (timerRef.value) {
clearTimeout(timerRef.value);
timerRef.value = null;
}
}
async function loadStatus() {
const status = await api.SyncExpirationStatus();
taskStatus.value = status || {};
errors.value = taskStatus.value.errors || [];
if (taskStatus.value.status === "running") {
stop();
timerRef.value = setTimeout(async () => {
await loadStatus();
}, 3000);
} else {
stop();
await opts.crudExpose.doRefresh();
}
}
await loadStatus();
await openFormDialog({
title: t("certd.domain.syncExpirationProgress"),
body: () => {
const progress = Math.min(Math.round(taskStatus.value.progress || 0), 100);
const isRunning = taskStatus.value.status === "running";
const errorList = errors.value.map(item => {
return <div>{item}</div>;
});
return (
<div class={"w-full"}>
<div class={"mt-4 flex flex-wrap gap-2"}>
<a-tag color={isRunning ? "processing" : "success"}>{isRunning ? t("certd.domain.running") : t("certd.domain.done")}</a-tag>
<a-tag class={"m-0"} color={"blue"}>
{t("certd.domain.total")}{taskStatus.value.total || 0}
</a-tag>
<a-tag class={"m-0"} color={"green"}>
{t("certd.success")}{taskStatus.value.successCount || 0}
</a-tag>
<a-tag class={"m-0"} color={"red"}>
{t("certd.domain.failed")}{taskStatus.value.errorCount || 0}
</a-tag>
<a-tag class={"m-0"} color={"cyan"}>
{t("certd.domain.current")}{taskStatus.value.current || 0}
</a-tag>
</div>
<div class={"mt-4 pr-4"}>
<a-progress percent={progress} status={errors.value.length > 0 ? "exception" : isRunning ? "active" : "success"} />
</div>
{errors.value.length > 0 && <div class={"mt-2 break-words text-red-500 mb-4"}>{errorList}</div>}
</div>
);
},
wrapper: {
width: 600,
footer: false,
buttons: {
cancel: {
show: false,
},
reset: {
show: false,
},
ok: {
show: true,
},
},
onClosed() {
stop();
},
},
});
};
}