mirror of
https://github.com/certd/certd.git
synced 2026-04-23 19:57:27 +08:00
perf: 支持邮件模版设置
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<div class="params-show">
|
||||
<div v-for="item of params" :key="item.value" class="item">
|
||||
<span class="label">{{ item.label }}:</span>
|
||||
<fs-copyable>{{ item.value }}</fs-copyable>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
defineOptions({
|
||||
name: "ParamsShow",
|
||||
});
|
||||
|
||||
const props = defineProps<{
|
||||
params: { value: string; label: string }[];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.params-show {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
|
||||
.item {
|
||||
width: 200px;
|
||||
display: flex;
|
||||
margin-bottom: 5px;
|
||||
align-items: center;
|
||||
|
||||
.label {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.fs-copyable {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -11,6 +11,7 @@ import AccessSelector from "/@/views/certd/access/access-selector/index.vue";
|
||||
import InputPassword from "./common/input-password.vue";
|
||||
import CertInfoUpdater from "/@/views/certd/pipeline/cert-upload/index.vue";
|
||||
import ApiTest from "./common/api-test.vue";
|
||||
import ParamsShow from "./common/params-show.vue";
|
||||
export * from "./cert/index.js";
|
||||
export default {
|
||||
install(app: any) {
|
||||
@@ -29,5 +30,6 @@ export default {
|
||||
app.component("RemoteInput", RemoteInput);
|
||||
app.component("CertDomainsGetter", CertDomainsGetter);
|
||||
app.component("InputPassword", InputPassword);
|
||||
app.component("ParamsShow", ParamsShow);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -780,6 +780,18 @@ export default {
|
||||
oauthAutoRedirectHelper: "Whether to auto redirect to OAuth2 login when login (using the first enabled OAuth2 login type)",
|
||||
oauthOnly: "OAuth2 Login Only",
|
||||
oauthOnlyHelper: "Whether to only allow OAuth2 login, disable password login",
|
||||
|
||||
email: {
|
||||
templates: "Email Templates",
|
||||
templateType: "Template Type",
|
||||
templateProvider: "Template Config",
|
||||
|
||||
templateSetting: "Email Template Setting",
|
||||
serverSetting: "Email Server Setting",
|
||||
sendTest: "Send Test",
|
||||
|
||||
templateProviderSelectorPlaceholder: "Not Configured",
|
||||
},
|
||||
},
|
||||
},
|
||||
modal: {
|
||||
|
||||
@@ -780,6 +780,18 @@ export default {
|
||||
oauthAutoRedirectHelper: "是否自动跳转第三方登录(使用第一个已启用的第三方登录类型)",
|
||||
oauthOnly: "仅使用第三方登录",
|
||||
oauthOnlyHelper: "是否仅使用第三方登录,关闭密码登录(注意:请务必在测试第三方登录功能正常后再开启)",
|
||||
|
||||
email: {
|
||||
templates: "邮件模板",
|
||||
templateType: "模板类型",
|
||||
templateProvider: "模板配置",
|
||||
|
||||
templateSetting: "邮件模板设置",
|
||||
serverSetting: "邮件服务器设置",
|
||||
sendTest: "发送测试",
|
||||
|
||||
templateProviderSelectorPlaceholder: "未配置",
|
||||
},
|
||||
},
|
||||
},
|
||||
modal: {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { request } from "/src/api/service";
|
||||
const apiPrefix = "/mine/email";
|
||||
const apiSettingPrefix = "/sys/settings";
|
||||
|
||||
export async function TestSend(receiver: string) {
|
||||
await request({
|
||||
@@ -10,3 +11,10 @@ export async function TestSend(receiver: string) {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function GetEmailTemplates() {
|
||||
return await request({
|
||||
url: apiSettingPrefix + "/getEmailTemplates",
|
||||
method: "post",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7,53 +7,93 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="flex-o">
|
||||
<a-form :model="formState" name="basic" :label-col="{ span: 8 }" :wrapper-col="{ span: 16 }" autocomplete="off" class="email-form-box" @finish="onFinish" @finish-failed="onFinishFailed">
|
||||
<div v-if="!formState.usePlus" class="email-form">
|
||||
<a-form-item :label="t('certd.useCustomEmailServer')"> </a-form-item>
|
||||
<a-form-item :label="t('certd.smtpDomain')" name="host" :rules="[{ required: true, message: t('certd.pleaseEnterSmtpDomain') }]">
|
||||
<a-input v-model:value="formState.host" />
|
||||
</a-form-item>
|
||||
<div class="email-form">
|
||||
<a-form :model="formState" name="basic" :label-col="{ span: 8 }" :wrapper-col="{ span: 16 }" autocomplete="off" class="" @finish="onFinish">
|
||||
<h2>{{ t("certd.sys.setting.email.serverSetting") }}</h2>
|
||||
<a-tabs v-model:active-key="activeKey" @change="onChangeActiveKey">
|
||||
<a-tab-pane key="custom" :tab="t('certd.useCustomEmailServer')">
|
||||
<div>
|
||||
<a-form-item :label="t('certd.smtpDomain')" name="host" :rules="[{ required: true, message: t('certd.pleaseEnterSmtpDomain') }]">
|
||||
<a-input v-model:value="formState.host" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item :label="t('certd.smtpPort')" name="port" :rules="[{ required: true, message: t('certd.pleaseEnterSmtpPort') }]">
|
||||
<a-input v-model:value="formState.port" />
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('certd.smtpPort')" name="port" :rules="[{ required: true, message: t('certd.pleaseEnterSmtpPort') }]">
|
||||
<a-input v-model:value="formState.port" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item :label="t('certd.username')" :name="['auth', 'user']" :rules="[{ required: true, message: t('certd.pleaseEnterUsername') }]">
|
||||
<a-input v-model:value="formState.auth.user" />
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('certd.password')" :name="['auth', 'pass']" :rules="[{ required: true, message: t('certd.pleaseEnterPassword') }]">
|
||||
<a-input-password v-model:value="formState.auth.pass" />
|
||||
<div class="helper">{{ t("certd.qqEmailAuthCodeHelper") }}</div>
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('certd.senderEmail')" name="sender" :rules="[{ required: true, message: t('certd.pleaseEnterSenderEmail') }]">
|
||||
<a-input v-model:value="formState.sender" />
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('certd.useSsl')" name="secure">
|
||||
<a-switch v-model:checked="formState.secure" />
|
||||
<div class="helper">{{ t("certd.sslPortNote") }}</div>
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('certd.ignoreCertValidation')" :name="['tls', 'rejectUnauthorized']">
|
||||
<a-switch v-model:checked="formState.tls.rejectUnauthorized" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item :wrapper-col="{ offset: 8, span: 16 }">
|
||||
<a-button type="primary" html-type="submit">{{ t("certd.save") }}</a-button>
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div class="email-form">
|
||||
<a-form-item :label="t('certd.useOfficialEmailServer')" name="usePlus">
|
||||
<div class="flex-o">
|
||||
<a-switch v-model:checked="formState.usePlus" :disabled="!settingStore.isPlus" @change="onUsePlusChanged" />
|
||||
<vip-button class="ml-5" mode="button"></vip-button>
|
||||
<a-form-item :label="t('certd.username')" :name="['auth', 'user']" :rules="[{ required: true, message: t('certd.pleaseEnterUsername') }]">
|
||||
<a-input v-model:value="formState.auth.user" />
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('certd.password')" :name="['auth', 'pass']" :rules="[{ required: true, message: t('certd.pleaseEnterPassword') }]">
|
||||
<a-input-password v-model:value="formState.auth.pass" />
|
||||
<div class="helper">{{ t("certd.qqEmailAuthCodeHelper") }}</div>
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('certd.senderEmail')" name="sender" :rules="[{ required: true, message: t('certd.pleaseEnterSenderEmail') }]">
|
||||
<a-input v-model:value="formState.sender" />
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('certd.useSsl')" name="secure">
|
||||
<a-switch v-model:checked="formState.secure" />
|
||||
<div class="helper">{{ t("certd.sslPortNote") }}</div>
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('certd.ignoreCertValidation')" :name="['tls', 'rejectUnauthorized']">
|
||||
<a-switch v-model:checked="formState.tls.rejectUnauthorized" />
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div class="helper">{{ t("certd.useOfficialEmailServerHelper") }}</div>
|
||||
</a-form-item>
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="plus" class="plus" :disabled="!settingStore.isPlus">
|
||||
<template #tab>
|
||||
<span class="flex items-center">
|
||||
{{ t("certd.useOfficialEmailServer") }}
|
||||
<vip-button class="ml-5" mode="button"></vip-button>
|
||||
</span>
|
||||
</template>
|
||||
<a-form-item :label="t('certd.useOfficialEmailServer')" name="usePlus">
|
||||
<div class="flex-o">
|
||||
<a-switch v-model:checked="formState.usePlus" :disabled="!settingStore.isPlus" @change="onUsePlusChanged" />
|
||||
</div>
|
||||
<div class="helper">{{ t("certd.useOfficialEmailServerHelper") }}</div>
|
||||
</a-form-item>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
<a-form-item :wrapper-col="{ offset: 8, span: 16 }">
|
||||
<a-button type="primary" html-type="submit">{{ t("certd.save") }}</a-button>
|
||||
</a-form-item>
|
||||
|
||||
<h2>{{ t("certd.sys.setting.email.templateSetting") }}</h2>
|
||||
<a-form-item :label="t('certd.sys.setting.email.templates')" :name="['templates']">
|
||||
<div class="flex flex-wrap">
|
||||
<table class="w-full table-auto border-collapse border border-gray-400">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border border-gray-300 px-4 py-2 w-1/3">{{ t("certd.sys.setting.email.templateType") }}</th>
|
||||
<th class="border border-gray-300 px-4 py-2 w-1/3">{{ t("certd.sys.setting.email.templateProvider") }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(item, key) of emailTemplates" :key="key">
|
||||
<td class="border border-gray-300 px-4 py-2">
|
||||
<div class="flex items-center" :title="item.desc">
|
||||
<fs-icon :icon="item.icon" class="mr-2 text-blue-600" />
|
||||
{{ item.title }}
|
||||
</div>
|
||||
</td>
|
||||
<td class="border border-gray-300 px-4 py-2">
|
||||
<AddonSelector v-model:model-value="item.addonId" addon-type="emailTemplate" from="sys" :type="item.name" :placeholder="t('certd.sys.setting.email.templateProviderSelectorPlaceholder')" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item :wrapper-col="{ offset: 8, span: 16 }">
|
||||
<a-button type="primary" html-type="submit">{{ t("certd.save") }}</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
<div class="email-form">
|
||||
<a-form :model="testFormState" name="basic" :label-col="{ span: 8 }" :wrapper-col="{ span: 16 }" autocomplete="off" @finish="onTestSend">
|
||||
<h2>{{ t("certd.sys.setting.email.sendTest") }}</h2>
|
||||
<a-form-item :label="t('certd.testReceiverEmail')" name="receiver" :rules="[{ required: true, message: t('certd.pleaseEnterTestReceiverEmail') }]">
|
||||
<a-input v-model:value="testFormState.receiver" />
|
||||
<div class="helper">{{ t("certd.saveBeforeTest") }}</div>
|
||||
@@ -71,14 +111,14 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive } from "vue";
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import * as api from "../api";
|
||||
import * as emailApi from "./api.email";
|
||||
import { notification } from "ant-design-vue";
|
||||
import { useSettingStore } from "/src/store/settings";
|
||||
import * as _ from "lodash-es";
|
||||
import { useI18n } from "/src/locales";
|
||||
|
||||
import AddonSelector from "../../../certd/addon/addon-selector/index.vue";
|
||||
const { t } = useI18n();
|
||||
defineOptions({
|
||||
name: "EmailSetting",
|
||||
@@ -98,8 +138,14 @@ interface FormState {
|
||||
};
|
||||
sender: string;
|
||||
usePlus: boolean;
|
||||
templates: {
|
||||
pipelineResult: any;
|
||||
registerCode: any;
|
||||
forgotPassword: any;
|
||||
};
|
||||
}
|
||||
|
||||
const activeKey = ref("custom");
|
||||
const formState = reactive<Partial<FormState>>({
|
||||
auth: {
|
||||
user: "",
|
||||
@@ -109,27 +155,65 @@ const formState = reactive<Partial<FormState>>({
|
||||
usePlus: false,
|
||||
});
|
||||
|
||||
const emailTemplates = ref([]);
|
||||
async function loadEmailTemplates() {
|
||||
emailTemplates.value = await emailApi.GetEmailTemplates();
|
||||
}
|
||||
|
||||
function fillEmailTemplates(form: any) {
|
||||
const providers: any = {};
|
||||
for (const item of emailTemplates.value) {
|
||||
const type = item.name;
|
||||
providers[type] = {
|
||||
type: type,
|
||||
title: item.title,
|
||||
icon: item.icon,
|
||||
addonId: item.addonId || null,
|
||||
};
|
||||
}
|
||||
form.templates = providers;
|
||||
return providers;
|
||||
}
|
||||
|
||||
async function load() {
|
||||
const data: any = await api.EmailSettingsGet();
|
||||
_.merge(formState, data);
|
||||
}
|
||||
|
||||
load();
|
||||
onMounted(async () => {
|
||||
await load();
|
||||
refreshActiveKeyByUsePlus();
|
||||
await loadEmailTemplates();
|
||||
});
|
||||
|
||||
const onFinish = async (form: any) => {
|
||||
console.log("Success:", form);
|
||||
fillEmailTemplates(form);
|
||||
await api.EmailSettingsSave(form);
|
||||
notification.success({
|
||||
message: t("certd.saveSuccess"),
|
||||
});
|
||||
};
|
||||
|
||||
const onFinishFailed = (errorInfo: any) => {
|
||||
// console.log("Failed:", errorInfo);
|
||||
};
|
||||
|
||||
async function onUsePlusChanged() {
|
||||
await api.EmailSettingsSave(formState);
|
||||
refreshActiveKeyByUsePlus();
|
||||
await onFinish(formState);
|
||||
}
|
||||
|
||||
async function refreshActiveKeyByUsePlus() {
|
||||
if (formState.usePlus) {
|
||||
activeKey.value = "plus";
|
||||
} else {
|
||||
activeKey.value = "custom";
|
||||
}
|
||||
}
|
||||
async function onChangeActiveKey(key: string) {
|
||||
activeKey.value = key;
|
||||
if (key === "plus") {
|
||||
formState.usePlus = true;
|
||||
} else {
|
||||
formState.usePlus = false;
|
||||
}
|
||||
await onFinish(formState);
|
||||
}
|
||||
|
||||
interface TestFormState {
|
||||
@@ -157,12 +241,21 @@ const settingStore = useSettingStore();
|
||||
|
||||
<style lang="less">
|
||||
.page-setting-email {
|
||||
.ant-tabs-nav {
|
||||
margin-left: 80px;
|
||||
}
|
||||
.email-form-box {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 20px 0px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.email-form {
|
||||
width: 500px;
|
||||
width: 700px;
|
||||
max-width: 100%;
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
@@ -172,5 +265,10 @@ const settingStore = useSettingStore();
|
||||
color: #999;
|
||||
font-size: 10px;
|
||||
}
|
||||
.addon-selector {
|
||||
.inner {
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user