perf: 输入证书域名时,支持点击导入域名

This commit is contained in:
xiaojunnuo
2026-01-25 22:57:59 +08:00
parent bca0eefc83
commit 40be42406c
8 changed files with 118 additions and 37 deletions

View File

@@ -1,24 +1,35 @@
<template>
<div class="domain-select">
<div class="flex flex-row">
<a-select class="domain-select-input" show-search :filter-option="filterOption" :options="optionsRef" :value="value" v-bind="attrs" @click="onClick" @update:value="emit('update:value', $event)">
<a-select
class="domain-select-input"
:dropdown-style="dropdownStyle"
show-search
:filter-option="filterOption"
:options="optionsRef"
:value="value"
v-bind="attrs"
@click="onClick"
@update:value="emit('update:value', $event)"
>
<template #dropdownRender="{ menuNode }">
<template v-if="search">
<div class="flex w-full" style="padding: 4px 8px">
<a-input
ref="inputRef"
v-model:value="searchKeyRef"
class="flex-1"
allow-clear
placeholder="这里可以搜索域名(数据来自设置->域名管理),列表中没有的域名可以直接在上面输入框输入"
@keydown.enter="doSearch"
/>
<a-button class="ml-2" :loading="loading" type="text" @click="doSearch">
<template #icon>
<search-outlined />
</template>
查询
</a-button>
<div class="flex w-full items-center justify-between flex-wrap" style="padding: 4px 8px">
<div class="flex-1 flex flex-row items-center">
<a-input
ref="inputRef"
v-model:value="searchKeyRef"
class="flex-1"
allow-clear
placeholder="这里可以搜索域名(数据来自“设置->域名管理”),列表中没有的域名可以直接在上面输入框输入"
@keydown.enter="doSearch"
/>
<fs-button type="primary" class="m-1" :loading="loading" icon="mingcute:search-2-line" @click="doSearch"> 查询 </fs-button>
</div>
<div class="manager flex flex-row items-center">
<fs-button type="primary" class="m-1" icon="mingcute:vip-1-line" @click="openDomainImportDialog">导入域名</fs-button>
<fs-button class="m-1" type="primary" icon="carbon:gui-management" @click="openDomainManager">管理域名</fs-button>
</div>
</div>
<div v-if="hasError" class="helper p-2" :class="{ error: hasError }">
{{ message }}
@@ -31,6 +42,15 @@
<a-pagination v-model:current="pagerRef.pageNo" simple :total="pagerRef.total" :page-size="pagerRef.pageSize" @change="onPageChange" />
</div>
</template>
<template #option="scope">
<div class="flex flex-row items-center">
<span class="min-w-60">{{ scope.label }}</span>
<div>
<fs-values-format :model-value="scope.challengeType" :dict="challengeTypeDict"></fs-values-format>
<fs-values-format :model-value="scope.dnsProviderType" :dict="dnsProviderTypeDict"></fs-values-format>
</div>
</div>
</template>
</a-select>
<div class="ml-5">
<fs-button :loading="loading" title="刷新我的域名列表" icon="ion:refresh-outline" @click="refreshOptions"></fs-button>
@@ -44,6 +64,9 @@
<script setup lang="ts">
import { computed, defineComponent, ref, Ref, useAttrs } from "vue";
import { request } from "/@/api/service";
import { Dicts } from "../lib/dicts";
import { useRouter } from "vue-router";
import { useDomainImport, useDomainImportManage } from "/@/views/certd/cert/domain/use";
defineOptions({
name: "DomainSelector",
@@ -114,10 +137,14 @@ const getOptions = async () => {
options.push({
value: item.domain,
label: item.domain,
dnsProviderType: item.dnsProviderType,
challengeType: item.challengeType,
});
options.push({
value: `*.${item.domain}`,
label: `*.${item.domain}`,
dnsProviderType: item.dnsProviderType,
challengeType: item.challengeType,
});
}
@@ -162,6 +189,29 @@ async function paginationClick(e: any) {
e.stopPropagation();
e.preventDefault();
}
const dnsProviderTypeDict = Dicts.dnsProviderTypeDict;
const challengeTypeDict = Dicts.challengeTypeDict;
const router = useRouter();
function openDomainManager(e: any) {
e.preventDefault();
// router.push("/certd/cert/domain");
window.open(`${window.location.origin}/#/certd/cert/domain`);
}
const openDomainImportManageDialog = useDomainImportManage();
function openDomainImportDialog() {
openDomainImportManageDialog({
zIndex: 2060,
afterSubmit: res => {
refreshOptions();
},
});
}
const dropdownStyle = ref({
zIndex: 2000,
});
</script>
<style lang="less"></style>

View File

@@ -6,6 +6,7 @@ export type FormOptionReq = {
onSubmit?: any;
body?: any;
initialForm?: any;
zIndex?: number;
};
export function useFormDialog() {
@@ -19,6 +20,7 @@ export function useFormDialog() {
form: {
initialForm: req.initialForm,
wrapper: {
zIndex: req.zIndex,
title: req.title,
saveRemind: false,
slots: {

View File

@@ -3,11 +3,10 @@ import { Modal, notification } from "ant-design-vue";
import { Ref, ref } from "vue";
import { useRouter } from "vue-router";
import * as api from "./api";
import DomainImportTaskStatus from "./import.vue";
import { useDomainImportManage } from "./use";
import { Dicts } from "/@/components/plugins/lib/dicts";
import { useSettingStore } from "/@/store/settings";
import { useUserStore } from "/@/store/user";
import { useFormDialog } from "/@/use/use-dialog";
import { createAccessApi } from "/@/views/certd/access/api";
import { useI18n } from "/src/locales";
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
@@ -51,7 +50,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
url: "pi/dnsProvider/dnsProviderTypeDict",
});
const { openFormDialog } = useFormDialog();
const openDomainImportManageDialog = useDomainImportManage();
return {
crudOptions: {
settings: {
@@ -104,13 +103,8 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
color: "gold",
icon: "mingcute:vip-1-line",
click: async () => {
settingStore.checkPlus();
await openFormDialog({
title: "从域名提供商导入域名",
body: () => {
return <DomainImportTaskStatus />;
},
onSubmit: async (form: any) => {
await openDomainImportManageDialog({
afterSubmit: res => {
crudExpose.doRefresh();
},
});

View File

@@ -42,7 +42,7 @@
<div v-else>未执行</div>
</td>
<td>
<fs-button type="primary" icon="ion:play-outline" @click="startTask(item)">执行</fs-button>
<fs-button type="primary" icon="ion:play-outline" :disabled="item.task?.status === 'running'" @click="startTask(item)">执行</fs-button>
<fs-button type="primary" class="ml-2" danger icon="ion:trash-outline" @click="deleteTask(item)">删除</fs-button>
</td>
</tr>
@@ -53,7 +53,7 @@
</template>
<script setup lang="ts">
import { onMounted, ref } from "vue";
import { onMounted, onUnmounted, ref } from "vue";
import * as api from "./api";
import { Modal } from "ant-design-vue";
import { useDomainImport } from "./use";
@@ -90,7 +90,10 @@ const openDomainImportDialog = useDomainImport();
async function addTask() {
await openDomainImportDialog({
afterSubmit: async () => {
afterSubmit: async (res?: any) => {
if (res) {
await api.ImportTaskStart(res.key);
}
await loadImportTaskStatus();
},
});
@@ -105,8 +108,16 @@ async function editTask(item: any) {
});
}
const checkIntervalRef = ref();
onMounted(async () => {
await loadImportTaskStatus();
checkIntervalRef.value = setInterval(async () => {
await loadImportTaskStatus();
}, 3000);
});
onUnmounted(() => {
clearInterval(checkIntervalRef.value);
});
</script>

View File

@@ -3,7 +3,8 @@ 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 DomainImportTaskStatus from "./import.vue";
export function useDomainImport() {
const { openFormDialog } = useFormDialog();
@@ -49,7 +50,7 @@ export function useDomainImport() {
},
};
return function openDomainImportDialog(req: { afterSubmit?: () => void; form?: any }) {
return function openDomainImportDialog(req: { afterSubmit?: (res?: any) => void; form?: any }) {
openFormDialog({
title: "从域名提供商导入域名",
columns: columns,
@@ -57,13 +58,33 @@ export function useDomainImport() {
...req.form,
},
onSubmit: async (form: any) => {
await api.ImportTaskSave({
const res = await api.ImportTaskSave({
key: form.key,
dnsProviderType: form.dnsProviderType,
dnsProviderAccessId: form.dnsProviderAccessId,
});
if (req.afterSubmit) {
req.afterSubmit();
req.afterSubmit(res);
}
},
});
};
}
export function useDomainImportManage() {
const { openFormDialog } = useFormDialog();
const settingStore = useSettingStore();
return async function openDomainImportManageDialog(req: { afterSubmit?: (res?: any) => void; form?: any; zIndex?: number }) {
settingStore.checkPlus();
await openFormDialog({
title: "从域名提供商导入域名",
body: () => {
return <DomainImportTaskStatus />;
},
zIndex: req.zIndex,
onSubmit: async (form: any) => {
if (req.afterSubmit) {
req.afterSubmit(form);
}
},
});

View File

@@ -261,7 +261,10 @@ export class DomainService extends BaseService<DomainEntity> {
const itemHandle = async (domainRecord: any) => {
task.incrementCurrent()
const domain = domainRecord.domain
let domain = domainRecord.domain
if (domain.endsWith(".")) {
domain = domain.slice(0, -1)
}
const old = await this.findOne({
where: {
@@ -410,7 +413,7 @@ export class DomainService extends BaseService<DomainEntity> {
await this.deleteDomainImportTask({userId,key})
}
await this.addDomainImportTask({userId,dnsProviderType,dnsProviderAccessId,index})
return await this.addDomainImportTask({userId,dnsProviderType,dnsProviderAccessId,index})
}

View File

@@ -13,7 +13,7 @@ export abstract class CertApplyBaseConvertPlugin extends AbstractTaskPlugin {
vModel: "value",
mode: "tags",
// open: false,
placeholder: "foo.com / *.foo.com / *.sub.foo.com / *.bar.com",
placeholder: "请输入证书域名,比如:foo.com / *.foo.com / *.sub.foo.com / *.bar.com",
tokenSeparators: [",", " ", "", "、", "|"],
search: true,
pager:true,

View File

@@ -192,7 +192,7 @@ export class HuaweiDnsProvider extends AbstractDnsProvider {
let list = ret.zones || []
list = list.map((item: any) => ({
id: item.id,
domain: item.name,
domain: item.name.endsWith(".") ? item.name.slice(0, -1) : item.name,
}));
return {
total:ret.metadata.total_count || list.length,