1917 lines
58 KiB
Vue
1917 lines
58 KiB
Vue
<template>
|
||
<div class="settings-container">
|
||
<div class="loading" :class="firstLoading ? 'loading-show' : 'loading-hide'">
|
||
<loading/>
|
||
</div>
|
||
<el-scrollbar class="scroll" v-if="!firstLoading">
|
||
<div class="scroll-body">
|
||
<div class="card-grid">
|
||
<!-- Website Settings Card -->
|
||
<div class="settings-card">
|
||
<div class="card-title">{{ $t('websiteSetting') }}</div>
|
||
<div class="card-content">
|
||
<div class="setting-item">
|
||
<div><span>{{ $t('websiteReg') }}</span></div>
|
||
<div>
|
||
<el-switch @change="change" :before-change="beforeChange" :active-value="0" :inactive-value="1"
|
||
v-model="setting.register"/>
|
||
</div>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div><span>{{ $t('loginDomain') }}</span></div>
|
||
<div>
|
||
<el-switch @change="change" :before-change="beforeChange" :active-value="0" :inactive-value="1"
|
||
v-model="setting.loginDomain"/>
|
||
</div>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div><span>{{ $t('regKey') }}</span></div>
|
||
<div>
|
||
<el-select
|
||
@change="change"
|
||
:style="`width: ${ locale === 'en' ? 100 : 80 }px;`"
|
||
v-model="setting.regKey"
|
||
placeholder="Select"
|
||
>
|
||
<el-option
|
||
v-for="item in regKeyOptions"
|
||
:key="item.value"
|
||
:label="item.label"
|
||
:value="item.value"
|
||
/>
|
||
</el-select>
|
||
</div>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div><span>{{ $t('addAccount') }}</span></div>
|
||
<div>
|
||
<el-switch @change="change" :before-change="beforeChange" :active-value="0" :inactive-value="1"
|
||
v-model="setting.addEmail"/>
|
||
</div>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div>
|
||
<span>{{ $t('multipleEmail') }}</span>
|
||
<el-tooltip effect="dark" :content="$t('multipleEmailDesc')">
|
||
<Icon class="warning" icon="fe:warning" width="18" height="18"/>
|
||
</el-tooltip>
|
||
</div>
|
||
<div>
|
||
<el-switch @change="change" :before-change="beforeChange" :active-value="0" :inactive-value="1"
|
||
v-model="setting.manyEmail"/>
|
||
</div>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div>
|
||
<span>{{ $t('emailPrefix') }}</span>
|
||
</div>
|
||
<div class="forward">
|
||
<el-button class="opt-button" size="small" type="primary" @click="openEmailPrefix">
|
||
<Icon icon="fluent:settings-48-regular" width="18" height="18"/>
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Personalization Settings Card -->
|
||
<div class="settings-card">
|
||
<div class="card-title">{{ $t('customization') }}</div>
|
||
<div class="card-content">
|
||
<div class="setting-item">
|
||
<div class="title-item"><span>{{ $t('websiteTitle') }}</span></div>
|
||
<div class="email-title">
|
||
<span>{{ setting.title }}</span>
|
||
<el-button class="opt-button" size="small" type="primary" @click="editTitleShow = true">
|
||
<Icon icon="lsicon:edit-outline" width="16" height="16"/>
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div class="title-item"><span>{{ $t('loginBoxOpacity') }}</span></div>
|
||
<div>
|
||
<el-input-number size="small" v-model="loginOpacity" @change="opacityChange" :precision="2"
|
||
:step="0.01" :max="1" :min="0"/>
|
||
</div>
|
||
</div>
|
||
<div class="setting-item personalized">
|
||
<div><span>{{ $t('loginBackground') }}</span></div>
|
||
<div>
|
||
<el-image
|
||
class="background"
|
||
:src="cvtR2Url(setting.background)"
|
||
:preview-src-list="[cvtR2Url(setting.background)]"
|
||
show-progress
|
||
fit="cover"
|
||
>
|
||
<template #error>
|
||
<div class="error-image">
|
||
<Icon icon="ph:image" width="24" height="24"/>
|
||
</div>
|
||
</template>
|
||
</el-image>
|
||
<div class="background-btn">
|
||
<el-button class="opt-button" size="small" type="primary" @click="openSetBackground">
|
||
<Icon icon="lsicon:edit-outline" width="16" height="16"/>
|
||
</el-button>
|
||
<el-button class="opt-button" size="small" type="primary" @click="delBackground">
|
||
<Icon icon="material-symbols:delete-outline-rounded" width="16" height="16"/>
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Email Sending Settings Card -->
|
||
<div class="settings-card">
|
||
<div class="card-title">{{ $t('emailSetting') }}</div>
|
||
<div class="card-content">
|
||
<div class="setting-item">
|
||
<div><span>{{ $t('receiveEmail') }}</span></div>
|
||
<div>
|
||
<el-switch @change="change" :before-change="beforeChange" :active-value="0" :inactive-value="1"
|
||
v-model="setting.receive"/>
|
||
</div>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div>
|
||
<span>{{ $t('autoRefresh') }}</span>
|
||
<el-tooltip effect="dark" :content="$t('autoRefreshDesc')">
|
||
<Icon class="warning" icon="fe:warning" width="18" height="18"/>
|
||
</el-tooltip>
|
||
</div>
|
||
<div>
|
||
<el-select
|
||
@change="change"
|
||
:style="`width: ${ locale === 'en' ? 100 : 80 }px;`"
|
||
v-model="setting.autoRefresh"
|
||
placeholder="Select"
|
||
>
|
||
<el-option
|
||
v-for="item in authRefreshOptions"
|
||
:key="item.value"
|
||
:label="item.label"
|
||
:value="item.value"
|
||
/>
|
||
</el-select>
|
||
</div>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div><span>{{ $t('sendEmail') }}</span></div>
|
||
<div>
|
||
<el-switch @change="change" :before-change="beforeChange" :active-value="0" :inactive-value="1"
|
||
v-model="setting.send"/>
|
||
</div>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div>
|
||
<span>{{ $t('noRecipientTitle') }}</span>
|
||
<el-tooltip effect="dark" :content="$t('noRecipientDesc')">
|
||
<Icon class="warning" icon="fe:warning" width="18" height="18"/>
|
||
</el-tooltip>
|
||
</div>
|
||
<div>
|
||
<el-switch @change="change" :before-change="beforeChange" :active-value="0" :inactive-value="1"
|
||
v-model="setting.noRecipient"/>
|
||
</div>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div><span>{{ $t('resendToken') }}</span></div>
|
||
<div>
|
||
<el-button class="opt-button" style="margin-top: 0" @click="openResendList" size="small"
|
||
type="primary">
|
||
<Icon icon="ic:round-list" width="18" height="18"/>
|
||
</el-button>
|
||
<el-button class="opt-button" style="margin-top: 0" @click="openResendForm" size="small"
|
||
type="primary">
|
||
<Icon icon="material-symbols:add-rounded" width="16" height="16"/>
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div><span>{{ $t('blackList') }}</span></div>
|
||
<div>
|
||
<el-button class="opt-button" style="margin-top: 0" @click="openBlackListForm" size="small"
|
||
type="primary">
|
||
<Icon icon="fluent:settings-48-regular" width="16" height="16"/>
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Object Storage Card -->
|
||
<div class="settings-card">
|
||
<div class="card-title">{{ $t('oss') }}</div>
|
||
<div class="card-content">
|
||
<div class="r2domain-item">
|
||
<div>
|
||
<span>{{ $t('osDomain') }}</span>
|
||
<el-tooltip effect="dark" :content="$t('ossDomainDesc')">
|
||
<Icon class="warning" icon="fe:warning" width="18" height="18"/>
|
||
</el-tooltip>
|
||
</div>
|
||
<div class="r2domain">
|
||
<span>{{ setting.r2Domain || '' }}</span>
|
||
<el-button class="opt-button" size="small" type="primary" @click="r2DomainShow = true">
|
||
<Icon icon="lsicon:edit-outline" width="16" height="16"/>
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div>
|
||
<span>{{ $t('s3Configuration') }}</span>
|
||
</div>
|
||
<div class="r2domain">
|
||
<el-button class="opt-button" size="small" type="primary" @click="addS3Show = true">
|
||
<Icon icon="fluent:settings-48-regular" width="16" height="16"/>
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div>
|
||
<span>{{ $t('storageType') }}</span>
|
||
</div>
|
||
<div class="r2domain">
|
||
<div class="storage-type">
|
||
<el-tag>{{ setting.storageType }}</el-tag>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="settings-card">
|
||
<div class="card-title">{{ $t('emailPush') }}</div>
|
||
<div class="card-content">
|
||
<div class="setting-item">
|
||
<div><span>{{ $t('tgBot') }}</span></div>
|
||
<div class="forward">
|
||
<span>{{ setting.tgBotStatus === 0 ? $t('enabled') : $t('disabled') }}</span>
|
||
<el-button class="opt-button" size="small" type="primary" @click="openTgSetting">
|
||
<Icon icon="fluent:settings-48-regular" width="18" height="18"/>
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div><span>{{ $t('otherEmail') }}</span></div>
|
||
<div class="forward">
|
||
<span>{{ setting.forwardStatus === 0 ? $t('enabled') : $t('disabled') }}</span>
|
||
<el-button class="opt-button" size="small" type="primary" @click="openThirdEmailSetting">
|
||
<Icon icon="fluent:settings-48-regular" width="18" height="18"/>
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div><span>{{ $t('forwardingRules') }}</span></div>
|
||
<div class="forward">
|
||
<span>{{ setting.ruleType === 0 ? $t('forwardAll') : $t('rules') }}</span>
|
||
<el-button class="opt-button" size="small" type="primary" @click="openForwardRules">
|
||
<Icon icon="fluent:settings-48-regular" width="18" height="18"/>
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Turnstile Verification Card -->
|
||
<div class="settings-card">
|
||
<div class="card-title">{{ $t('turnstileSetting') }}</div>
|
||
<div class="card-content">
|
||
<div class="setting-item">
|
||
<div><span>{{ $t('signUpVerification') }}</span></div>
|
||
<div>
|
||
<el-button class="opt-button" size="small" type="primary" @click="openRegVerifyCount">
|
||
<Icon icon="fluent:settings-48-regular" width="18" height="18"/>
|
||
</el-button>
|
||
<el-select
|
||
@change="change"
|
||
:style="`width: ${ locale === 'en' ? 100 : 80 }px;`"
|
||
v-model="setting.registerVerify"
|
||
placeholder="Select"
|
||
class="bot-verify-select"
|
||
>
|
||
<el-option key="1" :value="0" :label="$t('enable')"/>
|
||
<el-option key="1" :value="1" :label="$t('disable')"/>
|
||
<el-option key="1" :value="2" :label="$t('rulesVerify')"/>
|
||
</el-select>
|
||
</div>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div><span>{{ $t('addEmailVerification') }}</span></div>
|
||
<div>
|
||
<el-button class="opt-button" size="small" type="primary" @click="openAddVerifyCount">
|
||
<Icon icon="fluent:settings-48-regular" width="18" height="18"/>
|
||
</el-button>
|
||
<el-select
|
||
@change="change"
|
||
:style="`width: ${ locale === 'en' ? 100 : 80 }px;`"
|
||
v-model="setting.addEmailVerify"
|
||
placeholder="Select"
|
||
class="bot-verify-select"
|
||
>
|
||
<el-option key="1" :value="0" :label="$t('enable')"/>
|
||
<el-option key="1" :value="1" :label="$t('disable')"/>
|
||
<el-option key="1" :value="2" :label="$t('rulesVerify')"/>
|
||
</el-select>
|
||
</div>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div><span>Site Key</span></div>
|
||
<div class="bot-verify">
|
||
<span>{{ setting.siteKey }}</span>
|
||
<el-button class="opt-button" size="small" type="primary" @click="turnstileShow = true">
|
||
<Icon icon="lsicon:edit-outline" width="16" height="16"/>
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div><span>Secret Key</span></div>
|
||
<div class="bot-verify">
|
||
<span> {{ setting.secretKey }} </span>
|
||
<el-button class="opt-button" size="small" type="primary" @click="turnstileShow = true">
|
||
<Icon icon="lsicon:edit-outline" width="16" height="16"/>
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="settings-card">
|
||
<div class="card-title">{{ $t('noticeTitle') }}</div>
|
||
<div class="card-content">
|
||
<div class="setting-item">
|
||
<div><span>{{ $t('noticePopup') }}</span></div>
|
||
<div class="forward">
|
||
<span>{{ setting.notice === 0 ? $t('enabled') : $t('disabled') }}</span>
|
||
<el-button class="opt-button" size="small" type="primary" @click="openNoticePopupSetting">
|
||
<Icon icon="fluent:settings-48-regular" width="18" height="18"/>
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
<div class="setting-item">
|
||
<div><span>{{ $t('popUp') }}</span></div>
|
||
<div class="forward">
|
||
<el-button class="opt-button" size="small" type="primary" @click="openNoticePopup">
|
||
<Icon icon="mynaui:click-solid" width="18" height="18"/>
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="settings-card about">
|
||
<div class="card-title">{{ $t('about') }}</div>
|
||
<div class="card-content">
|
||
<div class="concerning-item">
|
||
<span>{{ $t('version') }} :</span>
|
||
<el-badge is-dot :hidden="!hasUpdate">
|
||
<el-button @click="jump('https://github.com/maillab/cloud-mail/releases')">
|
||
{{ currentVersion }}
|
||
<template #icon>
|
||
<Icon icon="qlementine-icons:version-control-16" style="font-size: 20px" color="#1890FF"/>
|
||
</template>
|
||
</el-button>
|
||
</el-badge>
|
||
</div>
|
||
<div class="concerning-item">
|
||
<span>{{ $t('community') }} : </span>
|
||
<div class="community">
|
||
<el-button @click="jump('https://github.com/maillab/cloud-mail')">
|
||
Github
|
||
<template #icon>
|
||
<Icon icon="codicon:github-inverted" width="22" height="22"/>
|
||
</template>
|
||
</el-button>
|
||
<el-button @click="jump('https://t.me/cloud_mail_tg')">
|
||
Telegram
|
||
<template #icon>
|
||
<Icon icon="logos:telegram" width="30" height="30"/>
|
||
</template>
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
<div class="concerning-item">
|
||
<span>{{ $t('support') }} : </span>
|
||
<el-button @click="jump('https://doc.skymail.ink/support.html')">
|
||
{{ t('supportDesc') }}
|
||
<template #icon>
|
||
<Icon color="#79D6B5" icon="simple-icons:buymeacoffee" width="20" height="20"/>
|
||
</template>
|
||
</el-button>
|
||
</div>
|
||
<div class="concerning-item">
|
||
<span>{{ $t('help') }} : </span>
|
||
<el-button @click="jump('https://doc.skymail.ink')">
|
||
{{ t('document') }}
|
||
<template #icon>
|
||
<Icon color="#79D6B5" icon="fluent-color:document-32" width="18" height="18"/>
|
||
</template>
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Dialogs remain the same -->
|
||
<el-dialog v-model="editTitleShow" :title="$t('changeTitle')" width="340" @closed="editTitle = setting.title">
|
||
<form>
|
||
<el-input type="text" :placeholder="$t('websiteTitle')" v-model="editTitle"/>
|
||
<el-button type="primary" :loading="settingLoading" @click="saveTitle">{{ $t('save') }}</el-button>
|
||
</form>
|
||
</el-dialog>
|
||
<el-dialog v-model="resendTokenFormShow" :title="$t('resendToken')" width="340" @closed="cleanResendTokenForm">
|
||
<form>
|
||
<el-select style="margin-bottom: 15px" v-model="resendTokenForm.domain" placeholder="Select">
|
||
<el-option
|
||
v-for="item in settingStore.domainList"
|
||
:key="item"
|
||
:label="item"
|
||
:value="item"
|
||
/>
|
||
</el-select>
|
||
<el-input type="text" :placeholder="$t('addResendTokenDesc')" v-model="resendTokenForm.token"/>
|
||
<el-button type="primary" :loading="settingLoading" @click="saveResendToken">{{ $t('save') }}</el-button>
|
||
</form>
|
||
</el-dialog>
|
||
<el-dialog v-model="r2DomainShow" :title="$t('addOsDomain')" width="340"
|
||
@closed="r2DomainInput = setting.r2Domain">
|
||
<form>
|
||
<el-input type="text" :placeholder="$t('domainDesc')" v-model="r2DomainInput"/>
|
||
<el-button type="primary" :loading="settingLoading" @click="saveR2domain">{{ $t('save') }}</el-button>
|
||
</form>
|
||
</el-dialog>
|
||
<el-dialog v-model="turnstileShow" :title="$t('addTurnstileSecret')" width="340"
|
||
@closed="turnstileForm.secretKey = '';turnstileForm.siteKey = ''">
|
||
<form>
|
||
<el-input type="text" placeholder="Site Key" v-model="turnstileForm.siteKey"/>
|
||
<el-input type="text" style="margin-top: 15px" placeholder="Secret Key" v-model="turnstileForm.secretKey"/>
|
||
<el-button type="primary" :loading="settingLoading" @click="saveTurnstileKey">{{ $t('save') }}</el-button>
|
||
</form>
|
||
</el-dialog>
|
||
<el-dialog
|
||
v-model="showSetBackground"
|
||
class="cut-dialog"
|
||
@closed="closedSetBackground"
|
||
>
|
||
<template #header>
|
||
<span style="font-size: 18px">
|
||
{{ $t('backgroundTitle') }}
|
||
<el-tooltip>
|
||
<template #content>
|
||
<span>{{ $t('backgroundWarning') }}</span>
|
||
</template>
|
||
<Icon class="title-icon warning" icon="fe:warning" width="18" height="18"/>
|
||
</el-tooltip>
|
||
</span>
|
||
</template>
|
||
<el-input :placeholder="$t('backgroundUrlDesc')" v-model="backgroundUrl" v-if="!localUpShow"
|
||
class="background-url"/>
|
||
<el-image
|
||
v-if="localUpShow"
|
||
:preview-src-list="[backgroundImage]"
|
||
show-progress
|
||
class="cropper"
|
||
fit="cover"
|
||
:src="backgroundImage"
|
||
></el-image>
|
||
<div class="cut-button">
|
||
<el-button type="primary" link @click="openCut" v-if="!localUpShow">
|
||
{{ $t('localUpload') }}
|
||
</el-button>
|
||
<el-button type="primary" link @click="localUpShow = false" v-if="localUpShow">
|
||
{{ $t('imageLink') }}
|
||
</el-button>
|
||
<el-button type="primary" :loading="settingLoading" @click="saveBackground">{{ $t('save') }}</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
<el-dialog
|
||
v-model="tgSettingShow"
|
||
class="forward-dialog"
|
||
>
|
||
<template #header>
|
||
<div class="forward-head">
|
||
<span class="forward-set-title">{{ $t('tgBot') }}</span>
|
||
<el-tooltip effect="dark" :content="$t('tgBotDesc')">
|
||
<Icon class="warning" icon="fe:warning" width="18" height="18"/>
|
||
</el-tooltip>
|
||
</div>
|
||
</template>
|
||
<div class="forward-set-body">
|
||
<el-input :placeholder="$t('tgBotToken')" v-model="tgBotToken"></el-input>
|
||
<el-input-tag tag-type="warning" :placeholder="$t('toBotTokenDesc')" v-model="tgChatId"
|
||
@add-tag="addChatTag"></el-input-tag>
|
||
<el-input tag-type="warning" :placeholder="$t('customDomainDesc')" v-model="customDomain" ></el-input>
|
||
<div class="tg-msg-label">
|
||
<span>{{t('from')}}</span>
|
||
<el-select v-model="tgMsgFrom" >
|
||
<el-option
|
||
v-for="item in tgMsgFromOption"
|
||
:key="item.value"
|
||
:label="item.label"
|
||
:value="item.value"
|
||
/>
|
||
</el-select>
|
||
</div>
|
||
<div class="tg-msg-label">
|
||
<span>{{t('recipient')}}</span>
|
||
<el-select v-model="tgMsgTo" >
|
||
<el-option
|
||
v-for="item in tgMsgToOption"
|
||
:key="item.value"
|
||
:label="item.label"
|
||
:value="item.value"
|
||
/>
|
||
</el-select>
|
||
</div>
|
||
<div class="tg-msg-label">
|
||
<span>{{t('emailText')}}</span>
|
||
<el-select v-model="tgMsgText" >
|
||
<el-option
|
||
v-for="item in tgMsgTextOption"
|
||
:key="item.value"
|
||
:label="item.label"
|
||
:value="item.value"
|
||
/>
|
||
</el-select>
|
||
</div>
|
||
</div>
|
||
<template #footer>
|
||
<div class="dialog-footer">
|
||
<el-switch v-model="tgBotStatus" :active-value="0" :inactive-value="1" :active-text="$t('enable')"
|
||
:inactive-text="$t('disable')"/>
|
||
<el-button :loading="settingLoading" type="primary" @click="tgBotSave">
|
||
{{ $t('save') }}
|
||
</el-button>
|
||
</div>
|
||
</template>
|
||
</el-dialog>
|
||
<el-dialog
|
||
v-model="thirdEmailShow"
|
||
class="forward-dialog"
|
||
>
|
||
<template #header>
|
||
<div class="forward-head">
|
||
<span class="forward-set-title">{{ $t('otherEmail') }}</span>
|
||
<el-tooltip effect="dark" :content="$t('otherEmailDesc')">
|
||
<Icon class="warning" icon="fe:warning" width="18" height="18"/>
|
||
</el-tooltip>
|
||
</div>
|
||
</template>
|
||
<div class="forward-set-body">
|
||
<el-input-tag tag-type="warning" :placeholder="$t('otherEmailInputDesc')" v-model="forwardEmail"
|
||
@add-tag="emailAddTag"></el-input-tag>
|
||
</div>
|
||
<template #footer>
|
||
<div class="dialog-footer">
|
||
<el-switch v-model="forwardStatus" :active-value="0" :inactive-value="1" :active-text="$t('enable')"
|
||
:inactive-text="$t('disable')"/>
|
||
<el-button :loading="settingLoading" type="primary" @click="forwardEmailSave">
|
||
{{ $t('save') }}
|
||
</el-button>
|
||
</div>
|
||
</template>
|
||
</el-dialog>
|
||
<el-dialog
|
||
v-model="forwardRulesShow"
|
||
class="forward-dialog"
|
||
>
|
||
<template #header>
|
||
<div class="forward-head">
|
||
<span class="forward-set-title">{{ $t('forwardingRules') }}</span>
|
||
<el-tooltip effect="dark" :content="$t('forwardingRulesDesc')">
|
||
<Icon class="warning" icon="fe:warning" width="18" height="18"/>
|
||
</el-tooltip>
|
||
</div>
|
||
</template>
|
||
<div class="forward-set-body">
|
||
<el-input-tag :placeholder="$t('ruleEmailsInputDesc')" tag-type="success" v-model="ruleEmail"
|
||
@add-tag="ruleEmailAddTag"/>
|
||
</div>
|
||
<template #footer>
|
||
<div class="dialog-footer">
|
||
<el-radio-group v-model="ruleType">
|
||
<el-radio :value="0">{{ $t('forwardAll') }}</el-radio>
|
||
<el-radio :value="1">{{ $t('rules') }}</el-radio>
|
||
</el-radio-group>
|
||
<el-button :loading="settingLoading" type="primary" @click="ruleEmailSave">
|
||
{{ $t('save') }}
|
||
</el-button>
|
||
</div>
|
||
</template>
|
||
</el-dialog>
|
||
<el-dialog class="resend-table" v-model="showResendList" :title="$t('resendTokenList')">
|
||
<el-table :data="resendList">
|
||
<el-table-column :min-width="emailColumnWidth" property="key" :label="$t('domain')"
|
||
:show-overflow-tooltip="true"/>
|
||
<el-table-column :width="tokenColumnWidth" property="value" label="Token" fixed="right"
|
||
:show-overflow-tooltip="true"/>
|
||
</el-table>
|
||
</el-dialog>
|
||
<el-dialog v-model="regVerifyCountShow" :title="$t('rulesVerifyTitle',{count: regVerifyCount})"
|
||
@closed="regVerifyCount = setting.regVerifyCount">
|
||
<form>
|
||
<el-input-number type="text" v-model="regVerifyCount" :min="1">
|
||
</el-input-number>
|
||
<el-button type="primary" :loading="settingLoading" @click="saveRegVerifyCount">{{ $t('save') }}</el-button>
|
||
</form>
|
||
</el-dialog>
|
||
<el-dialog v-model="addVerifyCountShow" :title="$t('rulesVerifyTitle',{count: addVerifyCount})"
|
||
@closed="addVerifyCount = setting.addVerifyCount">
|
||
<form>
|
||
<el-input-number type="text" v-model="addVerifyCount" :min="1"/>
|
||
<el-button type="primary" :loading="settingLoading" @click="saveAddVerifyCount">{{ $t('save') }}</el-button>
|
||
</form>
|
||
</el-dialog>
|
||
<el-dialog top="5vh" v-model="noticePopupShow" :title="$t('noticePopup')" class="notice-popup"
|
||
@closed="resetNoticeForm">
|
||
<form>
|
||
<el-input v-model="noticeForm.noticeTitle" :placeholder="t('titleDesc')"/>
|
||
<div class="notice-line-item">
|
||
<el-select v-model="noticeForm.noticeType">
|
||
<template #prefix>
|
||
<span style="margin-right: 10px">{{ $t('icon') }}</span>
|
||
</template>
|
||
<el-option key="none" label="None" value="none"/>
|
||
<el-option key="primary" label="Primary" value="primary"/>
|
||
<el-option key="success" label="Success" value="success"/>
|
||
<el-option key="warning" label="Warning" value="warning"/>
|
||
<el-option key="info" label="Info" value="info"/>
|
||
</el-select>
|
||
<el-select v-model="noticeForm.noticePosition">
|
||
<template #prefix>
|
||
<span style="margin-right: 10px">{{ $t('position') }}</span>
|
||
</template>
|
||
<el-option key="top-left" :label="t('topLeft')" value="top-left"/>
|
||
<el-option key="top-right" :label="t('topRight')" value="top-right"/>
|
||
<el-option key="bottom-left" :label="t('bottomLeft')" value="bottom-left"/>
|
||
<el-option key="bottom-right" :label="t('bottomRight')" value="bottom-right"/>
|
||
</el-select>
|
||
<el-input-number v-model="noticeForm.noticeWidth">
|
||
<template #prefix>
|
||
{{ $t('width') }}
|
||
</template>
|
||
<template #suffix>
|
||
px
|
||
</template>
|
||
</el-input-number>
|
||
<el-input-number v-model="noticeForm.noticeOffset">
|
||
<template #prefix>
|
||
{{ $t('offset') }}
|
||
</template>
|
||
<template #suffix>
|
||
px
|
||
</template>
|
||
</el-input-number>
|
||
<el-input-number v-model="noticeForm.noticeDuration">
|
||
<template #prefix>
|
||
{{ $t('duration') }}
|
||
</template>
|
||
<template #suffix>
|
||
ms
|
||
</template>
|
||
</el-input-number>
|
||
</div>
|
||
<div class="notice-popup-item">
|
||
<el-input
|
||
v-model="noticeForm.noticeContent"
|
||
:autosize="{ minRows: 15, maxRows: 25 }"
|
||
type="textarea"
|
||
:placeholder="t('noticeContentDesc')"
|
||
/>
|
||
</div>
|
||
</form>
|
||
<template #footer>
|
||
<div class="dialog-footer">
|
||
<el-switch v-model="noticeForm.notice" :active-value="0" :inactive-value="1" :active-text="$t('enable')"
|
||
:inactive-text="$t('disable')"/>
|
||
<div>
|
||
<el-button @click="previewNoticePopup">
|
||
{{ $t('preview') }}
|
||
</el-button>
|
||
<el-button :loading="settingLoading" type="primary" @click="saveNoticePopup">
|
||
{{ $t('save') }}
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
</el-dialog>
|
||
<el-dialog v-model="addS3Show" :title="t('s3Configuration')" width="340" @closed="resetAddS3Form">
|
||
<form>
|
||
<el-input class="dialog-input" type="text" placeholder="Bucket" v-model="s3.bucket"/>
|
||
<el-input class="dialog-input" type="text" placeholder="Endpoint" v-model="s3.endpoint"/>
|
||
<el-input class="dialog-input" type="text" placeholder="Region" v-model="s3.region"/>
|
||
<el-input class="dialog-input" type="text" :placeholder="setting.s3AccessKey || 'Access Key'"
|
||
v-model="s3.s3AccessKey"/>
|
||
<el-input style="margin-bottom: 10px" type="text" :placeholder="setting.s3SecretKey || 'Secret Key'" v-model="s3.s3SecretKey"/>
|
||
<div class="force-path-style">
|
||
<div class="force-path-style-left">
|
||
<span>ForcePathStyle</span>
|
||
<el-tooltip effect="dark" :content="$t('forcePathStyleDesc')">
|
||
<Icon class="warning" icon="fe:warning" width="18" height="18"/>
|
||
</el-tooltip>
|
||
</div>
|
||
<el-switch :before-change="beforeChange" :active-value="0" :inactive-value="1"
|
||
v-model="s3.forcePathStyle"/>
|
||
</div>
|
||
<div class="s3-button">
|
||
<el-button :loading="clearS3Loading" @click="clearS3">{{ t('clear') }}</el-button>
|
||
<el-button type="primary" :loading="settingLoading && !clearS3Loading" @click="saveS3">{{ t('save') }}</el-button>
|
||
</div>
|
||
</form>
|
||
</el-dialog>
|
||
<el-dialog v-model="emailPrefixShow" :title="t('emailPrefix')" @closed="resetEmailPrefix" >
|
||
<div class="email-prefix">
|
||
<div>{{ t('atLeast') }}</div>
|
||
<el-input-number v-model="minEmailPrefix" :min="1" :max="20" style="width: 150px" >
|
||
<template #suffix>
|
||
<span>{{ t('character') }}</span>
|
||
</template>
|
||
</el-input-number>
|
||
</div>
|
||
<div class="prefix-filter">
|
||
<div style="margin-bottom: 10px;">{{ t('mustNotContain') }}</div>
|
||
<el-input-tag style="margin-bottom: 10px;" v-model="emailPrefixFilter" />
|
||
</div>
|
||
<el-button type="primary" style="width: 100%;" :loading="settingLoading" @click="saveEmailPrefix">{{ $t('save') }}</el-button>
|
||
</el-dialog>
|
||
<el-dialog v-model="blackFormShow" class="forward-dialog" @closed="resetBlackList">
|
||
<template #header>
|
||
<div class="forward-head">
|
||
<span class="forward-set-title">{{ $t('blackList') }}</span>
|
||
<el-tooltip effect="dark" :content="$t('blackListDesc')">
|
||
<Icon class="warning" icon="fe:warning" width="18" height="18"/>
|
||
</el-tooltip>
|
||
</div>
|
||
</template>
|
||
<el-form>
|
||
<el-form-item :label="t('blackFromDesc')" label-position="top">
|
||
<el-input-tag v-model="blackListForm.blackFrom" @add-tag="banEmailAddTag" />
|
||
</el-form-item>
|
||
<el-form-item :label="t('blackSubjectDesc')" label-position="top">
|
||
<el-input-tag v-model="blackListForm.blackSubject"/>
|
||
</el-form-item>
|
||
<el-form-item :label="t('blackContentDesc')" label-position="top">
|
||
<el-input-tag v-model="blackListForm.blackContent"/>
|
||
</el-form-item>
|
||
</el-form>
|
||
<el-button type="primary" style="width: 100%;" :loading="settingLoading" @click="saveBlackList">{{ $t('save') }}</el-button>
|
||
</el-dialog>
|
||
</el-scrollbar>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import {computed, defineOptions, reactive, ref} from "vue";
|
||
import {deleteBackground, setBackground, setBlackList, settingQuery, settingSet} from "@/request/setting.js";
|
||
import {useSettingStore} from "@/store/setting.js";
|
||
import {useUiStore} from "@/store/ui.js";
|
||
import {useUserStore} from "@/store/user.js";
|
||
import {useAccountStore} from "@/store/account.js";
|
||
import {Icon} from "@iconify/vue";
|
||
import {cvtR2Url} from "@/utils/convert.js";
|
||
import {storeToRefs} from "pinia";
|
||
import {debounce} from 'lodash-es'
|
||
import {isDomain, isEmail} from "@/utils/verify-utils.js";
|
||
import loading from "@/components/loading/index.vue";
|
||
import {getTextWidth} from "@/utils/text.js";
|
||
import {fileToBase64} from "@/utils/file-utils.js"
|
||
import {useI18n} from 'vue-i18n';
|
||
import axios from "axios";
|
||
|
||
defineOptions({
|
||
name: 'sys-setting'
|
||
})
|
||
|
||
const currentVersion = 'v2.9.0'
|
||
const hasUpdate = ref(false)
|
||
let getUpdateErrorCount = 1;
|
||
const {t, locale} = useI18n();
|
||
const firstLoading = ref(true)
|
||
const backgroundImage = ref('')
|
||
const localUpShow = ref(false)
|
||
const accountStore = useAccountStore();
|
||
const userStore = useUserStore();
|
||
const editTitleShow = ref(false)
|
||
const resendTokenFormShow = ref(false)
|
||
const blackFormShow = ref(false)
|
||
const r2DomainShow = ref(false)
|
||
const turnstileShow = ref(false)
|
||
const tgSettingShow = ref(false)
|
||
const noticePopupShow = ref(false)
|
||
const thirdEmailShow = ref(false)
|
||
const forwardRulesShow = ref(false)
|
||
const emailPrefixShow = ref(false)
|
||
const showResendList = ref(false)
|
||
const settingStore = useSettingStore();
|
||
const uiStore = useUiStore();
|
||
const {settings: setting} = storeToRefs(settingStore);
|
||
const editTitle = ref('')
|
||
const settingLoading = ref(false)
|
||
const clearS3Loading = ref(false)
|
||
const r2DomainInput = ref('')
|
||
const loginOpacity = ref(0)
|
||
const minEmailPrefix = ref(0)
|
||
const emailPrefixFilter = ref([])
|
||
const backgroundUrl = ref('')
|
||
let backgroundFile = {}
|
||
const showSetBackground = ref(false)
|
||
let regVerifyCount = ref(1)
|
||
let addVerifyCount = ref(1)
|
||
let backup = '{}'
|
||
const addS3Show = ref(false)
|
||
const addVerifyCountShow = ref(false)
|
||
const regVerifyCountShow = ref(false)
|
||
const resendTokenForm = reactive({
|
||
domain: '',
|
||
token: '',
|
||
})
|
||
const turnstileForm = reactive({
|
||
siteKey: '',
|
||
secretKey: ''
|
||
})
|
||
|
||
const s3 = reactive({
|
||
bucket: '',
|
||
endpoint: '',
|
||
region: '',
|
||
s3AccessKey: '',
|
||
s3SecretKey: '',
|
||
forcePathStyle: 1
|
||
})
|
||
|
||
const noticeForm = reactive({
|
||
noticeTitle: '',
|
||
noticeContent: '',
|
||
noticeType: '',
|
||
noticeDuration: '',
|
||
noticePosition: '',
|
||
noticeOffset: 0,
|
||
notice: 0,
|
||
noticeWidth: 0
|
||
})
|
||
|
||
const regKeyOptions = computed(() => [
|
||
{label: t('enable'), value: 0},
|
||
{label: t('disable'), value: 1},
|
||
{label: t('optional'), value: 2},
|
||
])
|
||
|
||
const blackListForm = ref({
|
||
blackSubject: [],
|
||
blackContent: [],
|
||
blackFrom: []
|
||
})
|
||
|
||
const authRefreshOptions = computed(() => [
|
||
{label: t('disable'), value: 0},
|
||
{label: '3s', value: 3},
|
||
{label: '5s', value: 5},
|
||
{label: '10s', value: 10},
|
||
{label: '15s', value: 15},
|
||
{label: '20s', value: 20},
|
||
])
|
||
|
||
const tgChatId = ref([])
|
||
const customDomain = ref('')
|
||
const tgBotStatus = ref(0)
|
||
const tgBotToken = ref('')
|
||
const forwardEmail = ref([])
|
||
const forwardStatus = ref(0)
|
||
const emailColumnWidth = ref(0)
|
||
const tokenColumnWidth = ref(0)
|
||
const ruleType = ref(0)
|
||
const ruleEmail = ref([])
|
||
const tgMsgFrom = ref('')
|
||
const tgMsgTo = ref('')
|
||
const tgMsgText = ref('')
|
||
|
||
const tgMsgFromOption = [{label: t('show'), value: 'show'}, {label: t('hide'), value: 'hide'}, {label: t('onlyName'), value:'only-name'}]
|
||
const tgMsgToOption = [{label: t('show'), value: 'show'}, {label: t('hide'), value: 'hide'}]
|
||
const tgMsgTextOption = [{label: t('show'), value: 'show'}, {label: t('hide'), value: 'hide'}]
|
||
const tgMsgLabelWidth = computed(() => locale.value === 'en' ? '120px' : '100px');
|
||
|
||
getSettings()
|
||
getUpdate()
|
||
|
||
function getSettings() {
|
||
settingQuery().then(settingData => {
|
||
setting.value = settingData
|
||
settingStore.domainList = settingData.domainList;
|
||
resendTokenForm.domain = setting.value.domainList[0]
|
||
loginOpacity.value = setting.value.loginOpacity
|
||
minEmailPrefix.value = setting.value.minEmailPrefix
|
||
firstLoading.value = false
|
||
backgroundUrl.value = setting.value.background?.startsWith('http') ? setting.value.background : ''
|
||
editTitle.value = setting.value.title
|
||
r2DomainInput.value = setting.value.r2Domain
|
||
addVerifyCount.value = setting.value.addVerifyCount
|
||
regVerifyCount.value = setting.value.regVerifyCount
|
||
resetNoticeForm()
|
||
resetAddS3Form()
|
||
resetEmailPrefix()
|
||
resetBlackList()
|
||
})
|
||
}
|
||
|
||
|
||
function openNoticePopup() {
|
||
uiStore.showNotice()
|
||
}
|
||
|
||
function openAddVerifyCount() {
|
||
if (settingLoading.value) return
|
||
addVerifyCountShow.value = true
|
||
}
|
||
|
||
function openRegVerifyCount() {
|
||
if (settingLoading.value) return
|
||
regVerifyCountShow.value = true
|
||
}
|
||
|
||
function resetAddS3Form() {
|
||
s3.bucket = setting.value.bucket
|
||
s3.endpoint = setting.value.endpoint
|
||
s3.region = setting.value.region
|
||
s3.s3AccessKey = ''
|
||
s3.s3SecretKey = ''
|
||
s3.forcePathStyle = setting.value.forcePathStyle
|
||
}
|
||
|
||
const resendList = computed(() => {
|
||
|
||
let list = Object.keys(setting.value.resendTokens).map(key => {
|
||
return {
|
||
key: key,
|
||
value: setting.value.resendTokens[key]
|
||
};
|
||
})
|
||
|
||
if (list.length > 0) {
|
||
|
||
const key = list.reduce((a, b) => compareByLengthAndUpperCase(a, b, 'key')).key;
|
||
emailColumnWidth.value = getTextWidth(key) + 30;
|
||
|
||
const value = list.reduce((a, b) => compareByLengthAndUpperCase(a, b, 'value')).value;
|
||
tokenColumnWidth.value = getTextWidth(value) + 30;
|
||
|
||
}
|
||
|
||
return list;
|
||
});
|
||
|
||
function getUpdate() {
|
||
if (getUpdateErrorCount > 5 || !getUpdateErrorCount) return
|
||
axios.get('https://api.github.com/repos/maillab/cloud-mail/releases/latest').then(({data}) => {
|
||
hasUpdate.value = data.name !== currentVersion
|
||
getUpdateErrorCount = 0
|
||
}).catch(e => {
|
||
getUpdateErrorCount++
|
||
setTimeout(() => {
|
||
getUpdate()
|
||
}, 2000)
|
||
console.error('检查更新失败:', e)
|
||
})
|
||
}
|
||
|
||
function saveAddVerifyCount() {
|
||
if (!addVerifyCount.value) {
|
||
addVerifyCount.value = 1
|
||
}
|
||
editSetting({addVerifyCount: addVerifyCount.value})
|
||
}
|
||
|
||
function saveRegVerifyCount() {
|
||
if (!regVerifyCount.value) {
|
||
regVerifyCount.value = 1
|
||
}
|
||
editSetting({regVerifyCount: regVerifyCount.value})
|
||
}
|
||
|
||
const compareByLengthAndUpperCase = (a, b, key) => {
|
||
const getUpperCaseCount = (str) => (str.match(/[A-Z]/g) || []).length;
|
||
if (a[key].length === b[key].length) {
|
||
return getUpperCaseCount(a[key]) > getUpperCaseCount(b[key]) ? a : b;
|
||
}
|
||
return a[key].length > b[key].length ? a : b;
|
||
};
|
||
|
||
|
||
function closedSetBackground() {
|
||
backgroundImage.value = ''
|
||
localUpShow.value = false
|
||
backgroundUrl.value = setting.value.background?.startsWith('http') ? setting.value.background : ''
|
||
}
|
||
|
||
function openTgSetting() {
|
||
tgBotStatus.value = setting.value.tgBotStatus
|
||
tgBotToken.value = setting.value.tgBotToken
|
||
customDomain.value = setting.value.customDomain
|
||
tgMsgFrom.value = setting.value.tgMsgFrom
|
||
tgMsgText.value = setting.value.tgMsgText
|
||
tgMsgTo.value = setting.value.tgMsgTo
|
||
tgChatId.value = []
|
||
if (setting.value.tgChatId) {
|
||
const list = setting.value.tgChatId.split(',')
|
||
tgChatId.value.push(...list)
|
||
}
|
||
tgSettingShow.value = true
|
||
}
|
||
|
||
function openNoticePopupSetting() {
|
||
noticePopupShow.value = true
|
||
}
|
||
|
||
function openResendList() {
|
||
showResendList.value = true
|
||
}
|
||
|
||
function resetNoticeForm() {
|
||
noticeForm.notice = setting.value.notice
|
||
noticeForm.noticeContent = setting.value.noticeContent
|
||
noticeForm.noticeDuration = setting.value.noticeDuration
|
||
noticeForm.noticeTitle = setting.value.noticeTitle
|
||
noticeForm.noticePosition = setting.value.noticePosition
|
||
noticeForm.noticeType = setting.value.noticeType
|
||
noticeForm.noticeOffset = setting.value.noticeOffset
|
||
noticeForm.noticeWidth = setting.value.noticeWidth
|
||
}
|
||
|
||
function saveNoticePopup() {
|
||
noticeForm.noticeOffset = noticeForm.noticeOffset || 0
|
||
noticeForm.noticeWidth = noticeForm.noticeWidth || 0
|
||
noticeForm.noticeDuration = noticeForm.noticeDuration || 0
|
||
editSetting({...noticeForm})
|
||
}
|
||
|
||
function previewNoticePopup() {
|
||
uiStore.previewNotice({...noticeForm})
|
||
}
|
||
|
||
function openThirdEmailSetting() {
|
||
forwardEmail.value = []
|
||
forwardStatus.value = setting.value.forwardStatus
|
||
if (setting.value.forwardEmail) {
|
||
const list = setting.value.forwardEmail.split(',')
|
||
forwardEmail.value.push(...list)
|
||
}
|
||
thirdEmailShow.value = true
|
||
}
|
||
|
||
function openEmailPrefix() {
|
||
emailPrefixShow.value = true
|
||
}
|
||
|
||
function openForwardRules() {
|
||
ruleType.value = setting.value.ruleType
|
||
ruleEmail.value = []
|
||
if (setting.value.ruleEmail) {
|
||
const list = setting.value.ruleEmail.split(',')
|
||
ruleEmail.value.push(...list)
|
||
}
|
||
forwardRulesShow.value = true
|
||
}
|
||
|
||
function emailAddTag(val) {
|
||
const emails = Array.from(new Set(
|
||
val.split(/[,,]/).map(item => item.trim()).filter(item => item)
|
||
));
|
||
|
||
forwardEmail.value.splice(forwardEmail.value.length - 1, 1)
|
||
|
||
emails.forEach(email => {
|
||
if (isEmail(email) && !forwardEmail.value.includes(email)) {
|
||
forwardEmail.value.push(email)
|
||
}
|
||
})
|
||
}
|
||
|
||
function ruleEmailAddTag(val) {
|
||
const emails = Array.from(new Set(
|
||
val.split(/[,,]/).map(item => item.trim()).filter(item => item)
|
||
));
|
||
|
||
ruleEmail.value.splice(ruleEmail.value.length - 1, 1)
|
||
|
||
emails.forEach(email => {
|
||
if (isEmail(email) && !ruleEmail.value.includes(email)) {
|
||
ruleEmail.value.push(email)
|
||
}
|
||
})
|
||
}
|
||
|
||
function addChatTag(val) {
|
||
|
||
const chatIds = Array.from(new Set(
|
||
val.split(/[,,]/).map(item => item.trim()).filter(item => item)
|
||
));
|
||
|
||
tgChatId.value.splice(tgChatId.value.length - 1, 1)
|
||
|
||
chatIds.forEach(id => {
|
||
if (!isNaN(Number(id))) {
|
||
tgChatId.value.push(id)
|
||
}
|
||
})
|
||
}
|
||
|
||
function clearS3() {
|
||
|
||
const form = {
|
||
bucket: '',
|
||
endpoint: '',
|
||
region: '',
|
||
s3AccessKey: '',
|
||
s3SecretKey: '',
|
||
forcePathStyle: 1
|
||
}
|
||
clearS3Loading.value = true
|
||
editSetting(form)
|
||
}
|
||
|
||
function saveS3() {
|
||
|
||
const form = {
|
||
bucket: s3.bucket,
|
||
endpoint: s3.endpoint,
|
||
region: s3.region,
|
||
forcePathStyle: s3.forcePathStyle
|
||
}
|
||
|
||
if (s3.s3AccessKey) form.s3AccessKey = s3.s3AccessKey
|
||
if (s3.s3SecretKey) form.s3SecretKey = s3.s3SecretKey
|
||
|
||
editSetting(form)
|
||
}
|
||
|
||
function tgBotSave() {
|
||
const form = {
|
||
tgBotToken: tgBotToken.value,
|
||
customDomain: customDomain.value,
|
||
tgBotStatus: tgBotStatus.value,
|
||
tgChatId: tgChatId.value + '',
|
||
tgMsgFrom: tgMsgFrom.value,
|
||
tgMsgText: tgMsgText.value,
|
||
tgMsgTo: tgMsgTo.value
|
||
}
|
||
editSetting(form)
|
||
}
|
||
|
||
function forwardEmailSave() {
|
||
const form = {
|
||
forwardStatus: forwardStatus.value,
|
||
forwardEmail: forwardEmail.value + ''
|
||
}
|
||
editSetting(form)
|
||
}
|
||
|
||
|
||
function ruleEmailSave() {
|
||
const form = {
|
||
ruleEmail: ruleEmail.value + '',
|
||
ruleType: ruleType.value
|
||
}
|
||
editSetting(form)
|
||
}
|
||
|
||
function doOpacityChange() {
|
||
const form = {}
|
||
form.loginOpacity = loginOpacity.value
|
||
editSetting(form, true)
|
||
}
|
||
|
||
function resetEmailPrefix() {
|
||
minEmailPrefix.value = setting.value.minEmailPrefix
|
||
emailPrefixFilter.value = setting.value.emailPrefixFilter
|
||
}
|
||
|
||
function resetBlackList() {
|
||
blackListForm.value.blackFrom = setting.value.blackFrom ? setting.value.blackFrom.split(',') : []
|
||
blackListForm.value.blackContent = setting.value.blackContent ? setting.value.blackContent.split(',') : []
|
||
blackListForm.value.blackSubject = setting.value.blackSubject ? setting.value.blackSubject.split(',') : []
|
||
}
|
||
function saveEmailPrefix() {
|
||
const form = {}
|
||
form.minEmailPrefix = minEmailPrefix.value
|
||
form.emailPrefixFilter = emailPrefixFilter.value
|
||
editSetting(form, true)
|
||
}
|
||
|
||
const opacityChange = debounce(doOpacityChange, 1000, {
|
||
leading: false,
|
||
trailing: true
|
||
})
|
||
|
||
function saveBlackList() {
|
||
|
||
let form = {
|
||
blackContent: blackListForm.value.blackContent + '',
|
||
blackSubject: blackListForm.value.blackSubject + '',
|
||
blackFrom: blackListForm.value.blackFrom + ''
|
||
}
|
||
|
||
settingLoading.value = true
|
||
|
||
setBlackList(form).then(() => {
|
||
getSettings()
|
||
ElMessage({
|
||
message: t('setSuccess'),
|
||
type: "success",
|
||
plain: true
|
||
})
|
||
blackFormShow.value = false;
|
||
}).finally(() => {
|
||
settingLoading.value = false;
|
||
})
|
||
}
|
||
|
||
function banEmailAddTag(val) {
|
||
const emails = Array.from(new Set(
|
||
val.split(/[,,]/).map(item => item.trim()).filter(item => item)
|
||
));
|
||
|
||
blackListForm.value.blackFrom.splice(blackListForm.value.blackFrom.length - 1, 1)
|
||
|
||
emails.forEach(email => {
|
||
if ((isEmail(email) || isDomain(email)) && !blackListForm.value.blackFrom.includes(email)) {
|
||
blackListForm.value.blackFrom.push(email)
|
||
}
|
||
})
|
||
}
|
||
|
||
|
||
function delBackground() {
|
||
ElMessageBox.confirm(t('delBackgroundConfirm'), {
|
||
confirmButtonText: t('confirm'),
|
||
cancelButtonText: t('cancel'),
|
||
type: 'warning'
|
||
}).then(() => {
|
||
deleteBackground().then(() => {
|
||
backgroundUrl.value = ''
|
||
setting.value.background = null
|
||
ElMessage({
|
||
message: t('delSuccessMsg'),
|
||
type: "success",
|
||
plain: true
|
||
})
|
||
})
|
||
})
|
||
}
|
||
|
||
function saveTurnstileKey() {
|
||
const settingForm = {}
|
||
settingForm.siteKey = turnstileForm.siteKey
|
||
settingForm.secretKey = turnstileForm.secretKey
|
||
editSetting(settingForm)
|
||
}
|
||
|
||
async function saveBackground() {
|
||
|
||
let image = ''
|
||
|
||
if (localUpShow.value) {
|
||
image = await fileToBase64(backgroundFile, true);
|
||
} else {
|
||
if (backgroundUrl.value && !backgroundUrl.value.startsWith('http')) {
|
||
ElMessage({
|
||
message: t('imageLinkErrorMsg'),
|
||
type: "error",
|
||
plain: true
|
||
})
|
||
return
|
||
}
|
||
image = backgroundUrl.value
|
||
}
|
||
settingLoading.value = true
|
||
|
||
setBackground(image).then(key => {
|
||
setting.value.background = key
|
||
showSetBackground.value = false
|
||
ElMessage({
|
||
message: t('saveSuccessMsg'),
|
||
type: "success",
|
||
plain: true
|
||
})
|
||
localUpShow.value = false
|
||
backgroundImage.value = ''
|
||
}).finally(() => {
|
||
settingLoading.value = false
|
||
})
|
||
|
||
}
|
||
|
||
function openSetBackground() {
|
||
showSetBackground.value = true
|
||
}
|
||
|
||
function openCut() {
|
||
const doc = document.createElement('input')
|
||
doc.setAttribute('type', 'file')
|
||
doc.setAttribute('accept', 'image/*')
|
||
doc.click()
|
||
doc.onchange = async (e) => {
|
||
backgroundFile = e.target.files[0]
|
||
backgroundImage.value = URL.createObjectURL(e.target.files[0])
|
||
localUpShow.value = true
|
||
}
|
||
}
|
||
|
||
function saveR2domain() {
|
||
const settingForm = {r2Domain: r2DomainInput.value}
|
||
editSetting(settingForm)
|
||
}
|
||
|
||
function openResendForm() {
|
||
resendTokenFormShow.value = true
|
||
}
|
||
|
||
function openBlackListForm() {
|
||
blackFormShow.value = true
|
||
}
|
||
|
||
function saveResendToken() {
|
||
const settingForm = {
|
||
resendTokens: {}
|
||
}
|
||
const domain = resendTokenForm.domain.slice(1)
|
||
settingForm.resendTokens[domain] = resendTokenForm.token
|
||
editSetting(settingForm)
|
||
}
|
||
|
||
function backupSetting() {
|
||
const settingForm = {...setting.value}
|
||
delete settingForm.resendTokens
|
||
delete settingForm.siteKey
|
||
delete settingForm.secretKey
|
||
backup = JSON.stringify(setting.value)
|
||
}
|
||
|
||
function cleanResendTokenForm() {
|
||
resendTokenForm.token = ''
|
||
}
|
||
|
||
function beforeChange() {
|
||
if (settingLoading.value) return false
|
||
backupSetting()
|
||
return true
|
||
}
|
||
|
||
function change(e) {
|
||
const settingForm = {...setting.value}
|
||
delete settingForm.siteKey
|
||
delete settingForm.secretKey
|
||
delete settingForm.s3AccessKey
|
||
delete settingForm.s3SecretKey
|
||
delete settingForm.resendTokens
|
||
editSetting(settingForm, false)
|
||
}
|
||
|
||
function saveTitle() {
|
||
editSetting({title: editTitle.value})
|
||
}
|
||
|
||
function jump(href) {
|
||
const doc = document.createElement('a')
|
||
doc.href = href
|
||
doc.target = '_blank'
|
||
doc.click()
|
||
}
|
||
|
||
function editSetting(settingForm, refreshStatus = true) {
|
||
if (settingLoading.value) return
|
||
settingLoading.value = true
|
||
|
||
settingSet(settingForm).then(() => {
|
||
settingLoading.value = false
|
||
ElMessage({
|
||
message: t('saveSuccessMsg'),
|
||
type: "success",
|
||
plain: true
|
||
})
|
||
if (setting.value.manyEmail === 1) {
|
||
accountStore.currentAccountId = userStore.user.account.accountId;
|
||
}
|
||
if (refreshStatus) {
|
||
getSettings()
|
||
}
|
||
editTitleShow.value = false
|
||
r2DomainShow.value = false
|
||
resendTokenFormShow.value = false
|
||
turnstileShow.value = false
|
||
tgSettingShow.value = false
|
||
thirdEmailShow.value = false
|
||
forwardRulesShow.value = false
|
||
addVerifyCountShow.value = false
|
||
regVerifyCountShow.value = false
|
||
noticePopupShow.value = false
|
||
addS3Show.value = false
|
||
emailPrefixShow.value = false
|
||
}).catch((e) => {
|
||
loginOpacity.value = setting.value.loginOpacity
|
||
setting.value = {...setting.value, ...JSON.parse(backup)}
|
||
}).finally(() => {
|
||
settingLoading.value = false
|
||
clearS3Loading.value = false
|
||
})
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.settings-container {
|
||
height: 100%;
|
||
overflow: hidden;
|
||
background: var(--extra-light-fill) !important;
|
||
position: relative;
|
||
|
||
.loading {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
height: 100%;
|
||
z-index: 2;
|
||
width: 100%;
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
}
|
||
|
||
.loading-show {
|
||
transition: all 200ms ease 200ms;
|
||
opacity: 1;
|
||
}
|
||
|
||
.loading-hide {
|
||
transition: var(--loading-hide-transition);
|
||
pointer-events: none;
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
.scroll {
|
||
width: 100%;
|
||
min-height: 100%;
|
||
|
||
:deep(.el-scrollbar__view) {
|
||
height: 100%;
|
||
}
|
||
|
||
.scroll-body {
|
||
min-height: 100%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: space-between;
|
||
}
|
||
}
|
||
|
||
.card-grid {
|
||
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(440px, 1fr));
|
||
padding: 20px;
|
||
gap: 20px;
|
||
@media (max-width: 500px) {
|
||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||
}
|
||
@media (max-width: 1023px) {
|
||
gap: 15px;
|
||
padding: 15px;
|
||
}
|
||
}
|
||
|
||
.background {
|
||
width: 249px;
|
||
height: 140px;
|
||
border-radius: 4px;
|
||
border: 1px solid var(--light-border);
|
||
@media (max-width: 500px) {
|
||
width: 160px;
|
||
height: 90px;
|
||
}
|
||
}
|
||
|
||
.background-btn {
|
||
display: flex;
|
||
gap: 10px;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.bot-verify-select {
|
||
margin-left: 10px;
|
||
}
|
||
|
||
.settings-card {
|
||
background-color: var(--el-bg-color);
|
||
border-radius: 8px;
|
||
border: 1px solid var(--el-border-color);
|
||
transition: all 300ms;
|
||
overflow: hidden;
|
||
}
|
||
|
||
|
||
.card-title {
|
||
font-size: 15px;
|
||
font-weight: bold;
|
||
padding: 10px 20px;
|
||
border-bottom: 1px solid var(--el-border-color);
|
||
}
|
||
|
||
.card-content {
|
||
padding: 20px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 10px;
|
||
}
|
||
|
||
.setting-item {
|
||
display: grid;
|
||
grid-template-columns: auto 1fr;
|
||
gap: 10px;
|
||
font-weight: normal;
|
||
|
||
> div:first-child {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 5px;
|
||
}
|
||
|
||
> div:last-child {
|
||
display: grid;
|
||
grid-template-columns: 1fr auto;
|
||
justify-items: flex-end;
|
||
font-weight: normal;
|
||
}
|
||
}
|
||
|
||
.r2domain-item {
|
||
display: flex;
|
||
gap: 10px;
|
||
> div:first-child {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 5px;
|
||
}
|
||
|
||
> div:last-child {
|
||
flex: 1;
|
||
text-align: right;
|
||
}
|
||
}
|
||
|
||
.title-icon.warning {
|
||
position: relative;
|
||
top: 2px;
|
||
cursor: pointer;
|
||
margin-left: 2px;
|
||
}
|
||
|
||
.warning {
|
||
margin-left: 2px;
|
||
color: grey;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.cropper {
|
||
border-radius: 4px;
|
||
border: 1px solid #D4D7DE;
|
||
height: 397px;
|
||
width: 705px;
|
||
@media (max-width: 767px) {
|
||
width: calc(100vw - 60px);
|
||
height: calc((100vw - 60px) * 9 / 16);
|
||
}
|
||
}
|
||
|
||
.dialog-footer {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.notice-popup-item {
|
||
margin-top: 15px;
|
||
}
|
||
|
||
.notice-line-item {
|
||
margin-top: 15px;
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr 1fr;
|
||
gap: 15px;
|
||
|
||
> * {
|
||
width: 100%;
|
||
}
|
||
|
||
@media (max-width: 840px) {
|
||
grid-template-columns: 1fr 1fr;
|
||
}
|
||
@media (max-width: 580px) {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
|
||
.background-url {
|
||
width: min(calc(100vw - 70px), 500px);
|
||
}
|
||
|
||
|
||
:deep(.el-dialog) {
|
||
width: 400px !important;
|
||
@media (max-width: 440px) {
|
||
width: calc(100% - 40px) !important;
|
||
margin-right: 20px !important;
|
||
margin-left: 20px !important;
|
||
}
|
||
}
|
||
|
||
:deep(.resend-table.el-dialog) {
|
||
min-height: 300px;
|
||
width: 500px !important;
|
||
@media (max-width: 540px) {
|
||
width: calc(100% - 40px) !important;
|
||
margin-right: 20px !important;
|
||
margin-left: 20px !important;
|
||
}
|
||
}
|
||
|
||
:deep(.notice-popup.el-dialog) {
|
||
min-height: 300px;
|
||
width: 820px !important;
|
||
@media (max-width: 860px) {
|
||
width: calc(100% - 40px) !important;
|
||
margin-right: 20px !important;
|
||
margin-left: 20px !important;
|
||
}
|
||
}
|
||
|
||
:deep(.resend-table .el-dialog__header) {
|
||
padding-bottom: 5px;
|
||
}
|
||
|
||
:deep(.el-table__inner-wrapper:before) {
|
||
background: var(--el-bg-color);
|
||
}
|
||
|
||
:deep(.cut-dialog.el-dialog) {
|
||
width: fit-content !important;
|
||
height: fit-content !important;
|
||
}
|
||
|
||
|
||
:deep(.forward-dialog.el-dialog) {
|
||
width: 500px !important;
|
||
@media (max-width: 540px) {
|
||
width: calc(100% - 40px) !important;
|
||
margin-right: 20px !important;
|
||
margin-left: 20px !important;
|
||
}
|
||
}
|
||
|
||
.forward-dialog {
|
||
.forward-head {
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.forward-set-title {
|
||
top: 1px;
|
||
padding-right: 5px;
|
||
position: relative;
|
||
font-size: 16px;
|
||
font-weight: bold;;
|
||
}
|
||
}
|
||
}
|
||
|
||
.error-image {
|
||
background: var(--light-ill);
|
||
height: 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.cut-button {
|
||
padding-top: 15px;
|
||
width: 100%;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
|
||
.el-button {
|
||
width: fit-content;
|
||
}
|
||
}
|
||
|
||
.bot-verify {
|
||
display: grid;
|
||
grid-template-columns: 1fr auto;
|
||
overflow: hidden;
|
||
white-space: nowrap;
|
||
text-overflow: ellipsis;
|
||
|
||
span {
|
||
display: flex;
|
||
align-items: center;
|
||
overflow: hidden;
|
||
white-space: nowrap;
|
||
text-overflow: ellipsis;
|
||
min-width: 0;
|
||
}
|
||
|
||
.el-button {
|
||
width: 48px;
|
||
margin: 0 0 0 10px;
|
||
}
|
||
}
|
||
|
||
.forward-set-body {
|
||
display: flex;
|
||
flex-direction: column;
|
||
|
||
.el-switch {
|
||
align-self: end;
|
||
}
|
||
|
||
> *:nth-child(-n+2) {
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.tg-msg-label {
|
||
margin-top: 10px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
.el-select {
|
||
width: v-bind(tgMsgLabelWidth);
|
||
}
|
||
}
|
||
}
|
||
|
||
.forward {
|
||
span {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.el-button {
|
||
width: 48px;
|
||
margin: 0 0 0 10px;
|
||
}
|
||
}
|
||
|
||
.opt-button {
|
||
width: fit-content !important;
|
||
}
|
||
|
||
.email-prefix {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.prefix-filter {
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.s3-button {
|
||
display: grid;
|
||
grid-template-columns: 80px 1fr;
|
||
gap: 15px;
|
||
|
||
.el-button {
|
||
margin-left: 0;
|
||
}
|
||
}
|
||
|
||
.r2domain {
|
||
display: grid;
|
||
grid-template-columns: 1fr auto;
|
||
align-items: center;
|
||
|
||
.storage-type {
|
||
margin-right: 3px;
|
||
}
|
||
|
||
span {
|
||
overflow: hidden;
|
||
white-space: nowrap;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
.el-button {
|
||
width: 48px;
|
||
margin: 0 0 0 10px;
|
||
}
|
||
}
|
||
|
||
.personalized {
|
||
align-items: start;
|
||
|
||
> div:last-child {
|
||
display: flex;
|
||
justify-content: end;
|
||
|
||
.el-button {
|
||
margin-left: 10px;
|
||
margin-top: 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
.dialog-input {
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.force-path-style {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 10px;
|
||
.force-path-style-left {
|
||
padding-left: 2px;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
gap: 5px;
|
||
}
|
||
}
|
||
|
||
.concerning-item {
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.community {
|
||
display: flex;
|
||
row-gap: 10px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
:deep(.el-button) {
|
||
padding: 0 10px;
|
||
font-weight: normal;
|
||
|
||
i {
|
||
font-size: 22px;
|
||
}
|
||
}
|
||
|
||
> span:first-child {
|
||
font-weight: normal;
|
||
padding-right: 20px;
|
||
white-space: nowrap;
|
||
}
|
||
}
|
||
|
||
.email-title {
|
||
font-weight: normal !important;
|
||
display: grid;
|
||
gap: 10px;
|
||
grid-template-columns: 1fr auto;
|
||
align-items: center;
|
||
|
||
span {
|
||
overflow: hidden;
|
||
white-space: nowrap;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
.el-button {
|
||
margin-top: 0;
|
||
}
|
||
}
|
||
|
||
.token-item {
|
||
padding-top: 0;
|
||
|
||
div:last-child {
|
||
font-weight: normal;
|
||
}
|
||
}
|
||
|
||
form .el-button {
|
||
margin-top: 10px;
|
||
width: 100%;
|
||
}
|
||
|
||
.el-switch {
|
||
height: 28px;
|
||
}
|
||
|
||
|
||
:deep(.el-button--small) {
|
||
margin-top: 2px !important;
|
||
margin-bottom: 2px !important;
|
||
height: 24px;
|
||
}
|
||
|
||
:deep(.el-select__wrapper) {
|
||
min-height: 28px;
|
||
}
|
||
|
||
</style>
|
||
|
||
<style>
|
||
.el-popper.is-dark {
|
||
}
|
||
</style>
|