mirror of
https://github.com/schroinerxy/cloud-mail.git
synced 2026-06-21 19:35:50 +08:00
1315 lines
39 KiB
Vue
1315 lines
39 KiB
Vue
<template>
|
||
<div class="settings-container">
|
||
<div v-if="firstLoading" class="loading">
|
||
<loading />
|
||
</div>
|
||
<el-scrollbar class="scroll" v-else >
|
||
<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('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('physicallyWipeData')}}</span>
|
||
<el-tooltip effect="dark" :content="$t('physicallyWipeDataDesc')">
|
||
<Icon class="warning" icon="fe:warning" width="18" height="18"/>
|
||
</el-tooltip>
|
||
</div>
|
||
<div>
|
||
<el-button class="opt-button" style="margin-top: 0" @click="physicsDeleteAllData" size="small"
|
||
type="primary">
|
||
<Icon icon="material-symbols:delete-outline-rounded" width="16" height="16"/>
|
||
</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" @click="openSetBackground">
|
||
<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('receiveEmails')}}</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.autoRefreshTime"
|
||
placeholder="Select"
|
||
>
|
||
<el-option
|
||
v-for="item in options"
|
||
: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('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>
|
||
</div>
|
||
|
||
<!-- R2 Object Storage Card -->
|
||
<div class="settings-card">
|
||
<div class="card-title">{{$t('R2OS')}}</div>
|
||
<div class="card-content">
|
||
<div class="setting-item">
|
||
<div><span>{{$t('osDomain')}}</span></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>
|
||
</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 about">
|
||
<div class="card-title">{{$t('about')}}</div>
|
||
<div class="card-content">
|
||
<div class="concerning-item">
|
||
<span>{{$t('version')}} :</span>
|
||
<span>v1.5.0</span>
|
||
</div>
|
||
<div class="concerning-item">
|
||
<span>{{$t('community')}} : </span>
|
||
<el-button @click="jump('https://github.com/eoao/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 class="concerning-item">
|
||
<span>{{$t('support')}} : </span>
|
||
<el-button @click="jump('https://support.skymail.ink')" >
|
||
{{t('supportDesc')}}
|
||
<template #icon>
|
||
<Icon color="#79D6B5" icon="simple-icons:buymeacoffee" width="20" height="20" />
|
||
</template>
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Dialogs remain the same -->
|
||
<el-dialog v-model="editTitleShow" :title="$t('changeTitle')" width="340" >
|
||
<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>
|
||
</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="showRegVerifyCount" :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="showAddVerifyCount" :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-scrollbar>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import {computed, defineOptions, reactive, ref} from "vue";
|
||
import {physicsDeleteAll, setBackground, settingQuery, settingSet} from "@/request/setting.js";
|
||
import {useSettingStore} from "@/store/setting.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 {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';
|
||
|
||
defineOptions({
|
||
name: 'sys-setting'
|
||
})
|
||
|
||
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 r2DomainShow = ref(false)
|
||
const turnstileShow = ref(false)
|
||
const tgSettingShow = ref(false)
|
||
const thirdEmailShow = ref(false)
|
||
const forwardRulesShow = ref(false)
|
||
const showResendList = ref(false)
|
||
const settingStore = useSettingStore();
|
||
const {settings: setting} = storeToRefs(settingStore);
|
||
const editTitle = ref('')
|
||
const settingLoading = ref(false)
|
||
const r2DomainInput = ref('')
|
||
const loginOpacity = ref(0)
|
||
const backgroundUrl = ref('')
|
||
let backgroundFile = {}
|
||
const showSetBackground = ref(false)
|
||
let regVerifyCount = ref(1)
|
||
let addVerifyCount = ref(1)
|
||
let backup = '{}'
|
||
const showAddVerifyCount = ref(false)
|
||
const showRegVerifyCount = ref(false)
|
||
const resendTokenForm = reactive({
|
||
domain: '',
|
||
token: '',
|
||
})
|
||
const turnstileForm = reactive({
|
||
siteKey: '',
|
||
secretKey: ''
|
||
})
|
||
|
||
const regKeyOptions = [
|
||
{label: t('enable'), value: 0},
|
||
{label: t('disable'), value: 1},
|
||
{label: t('optional'), value: 2},
|
||
]
|
||
|
||
const options = [
|
||
{label: t('disable'), value: 0},
|
||
{label: '3s', value: 3},
|
||
{label: '5s', value: 5},
|
||
{label: '7s', value: 7},
|
||
{label: '10s', value: 10},
|
||
{label: '15s', value: 15},
|
||
{label: '20s', value: 20}
|
||
]
|
||
|
||
const tgChatId = 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([])
|
||
|
||
|
||
settingQuery().then(settingData => {
|
||
setting.value = settingData
|
||
resendTokenForm.domain = setting.value.domainList[0]
|
||
loginOpacity.value = setting.value.loginOpacity
|
||
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
|
||
})
|
||
|
||
function openAddVerifyCount() {
|
||
if (settingLoading.value) return
|
||
showAddVerifyCount.value = true
|
||
}
|
||
|
||
function openRegVerifyCount() {
|
||
if (settingLoading.value) return
|
||
showRegVerifyCount.value = true
|
||
}
|
||
|
||
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 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
|
||
tgChatId.value = []
|
||
if (setting.value.tgChatId) {
|
||
const list = setting.value.tgChatId.split(',')
|
||
tgChatId.value.push(...list)
|
||
}
|
||
tgSettingShow.value = true
|
||
}
|
||
|
||
function openResendList() {
|
||
showResendList.value = true
|
||
}
|
||
|
||
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 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 tgBotSave() {
|
||
const form = {
|
||
tgBotToken: tgBotToken.value,
|
||
tgBotStatus: tgBotStatus.value,
|
||
tgChatId: tgChatId.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)
|
||
}
|
||
|
||
const opacityChange = debounce(doOpacityChange, 1000, {
|
||
leading: false,
|
||
trailing: true
|
||
})
|
||
|
||
function physicsDeleteAllData() {
|
||
ElMessageBox.prompt(t('clearAllDelConfirm'), {
|
||
confirmButtonText: t('confirm'),
|
||
cancelButtonText: t('cancel'),
|
||
dangerouslyUseHTMLString: true,
|
||
title: t('warning'),
|
||
type: 'warning',
|
||
inputPattern: new RegExp(`^${t('delInputPattern')}$`),
|
||
inputErrorMessage: t('inputErrorMessage'),
|
||
}).then(() => {
|
||
physicsDeleteAll().then(() => {
|
||
ElMessage({
|
||
message: t('delSuccessMsg'),
|
||
type: "success",
|
||
plain: true
|
||
})
|
||
})
|
||
})
|
||
}
|
||
|
||
function delBackground() {
|
||
ElMessageBox.confirm(t('delBackgroundConfirm'), {
|
||
confirmButtonText: t('confirm'),
|
||
cancelButtonText: t('cancel'),
|
||
type: 'warning'
|
||
}).then(() => {
|
||
backgroundUrl.value = ''
|
||
setting.value.background = null
|
||
editSetting({background: null})
|
||
})
|
||
}
|
||
|
||
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('changSuccessMsg'),
|
||
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, true)
|
||
}
|
||
|
||
function openResendForm() {
|
||
resendTokenFormShow.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.resendTokens
|
||
editSetting(settingForm, false)
|
||
}
|
||
|
||
function refresh() {
|
||
settingQuery().then(setting => {
|
||
settingStore.settings = setting;
|
||
settingStore.domainList = setting.domainList;
|
||
})
|
||
}
|
||
|
||
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('changSuccessMsg'),
|
||
type: "success",
|
||
plain: true
|
||
})
|
||
if (setting.value.manyEmail === 1) {
|
||
accountStore.currentAccountId = userStore.user.accountId;
|
||
}
|
||
if (refreshStatus) {
|
||
refresh()
|
||
}
|
||
editTitleShow.value = false
|
||
r2DomainShow.value = false
|
||
resendTokenFormShow.value = false
|
||
turnstileShow.value = false
|
||
tgSettingShow.value = false
|
||
thirdEmailShow.value = false
|
||
forwardRulesShow.value = false
|
||
showAddVerifyCount.value = false
|
||
showRegVerifyCount.value = false
|
||
}).catch((e) => {
|
||
loginOpacity.value = setting.value.loginOpacity
|
||
setting.value = {...setting.value, ...JSON.parse(backup)}
|
||
}).finally(() => {
|
||
settingLoading.value = false
|
||
})
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.settings-container {
|
||
height: 100%;
|
||
overflow: hidden;
|
||
background: #FAFCFF !important;
|
||
.loading {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
height: 100%;
|
||
}
|
||
}
|
||
|
||
.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(420px, 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: 230px;
|
||
height: 120px;
|
||
border-radius: 4px;
|
||
border: 1px solid #e4e7ed;
|
||
@media (max-width: 500px) {
|
||
width: 150px;
|
||
height: 83px;
|
||
}
|
||
}
|
||
|
||
.background-btn {
|
||
display: flex;
|
||
gap: 10px;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.bot-verify-select {
|
||
margin-left: 10px;
|
||
}
|
||
|
||
.settings-card {
|
||
background-color: #fff;
|
||
border-radius: 8px;
|
||
border: 1px solid var(--el-border-color);
|
||
transition: all 300ms;
|
||
overflow: hidden;
|
||
}
|
||
|
||
@media (min-width: 885px) {
|
||
.about {
|
||
height: 210px;
|
||
}
|
||
}
|
||
|
||
.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: bold;
|
||
> 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;
|
||
}
|
||
}
|
||
|
||
.title-icon.warning {
|
||
position: relative;
|
||
top: 2px;
|
||
color: gray;
|
||
cursor: pointer;
|
||
margin-left: 2px;
|
||
}
|
||
|
||
.warning {
|
||
margin-left: 5px;
|
||
color: gray;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.cropper {
|
||
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;
|
||
}
|
||
|
||
.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(.resend-table .el-dialog__header) {
|
||
padding-bottom: 5px;
|
||
}
|
||
|
||
:deep(.el-table__inner-wrapper:before) {
|
||
background: #fff;
|
||
}
|
||
|
||
: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;
|
||
position: relative;
|
||
font-size: 16px;
|
||
font-weight: bold;
|
||
}
|
||
}
|
||
}
|
||
|
||
.error-image {
|
||
background: #f5f7fa;
|
||
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;
|
||
gap: 15px;
|
||
.el-switch {
|
||
align-self: end;
|
||
}
|
||
}
|
||
|
||
.forward {
|
||
span {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.el-button {
|
||
width: 48px;
|
||
margin: 0 0 0 10px;
|
||
}
|
||
}
|
||
|
||
.opt-button {
|
||
width: fit-content !important;
|
||
}
|
||
|
||
.r2domain {
|
||
display: grid;
|
||
grid-template-columns: 1fr auto;
|
||
align-items: center;
|
||
|
||
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;
|
||
}
|
||
}
|
||
}
|
||
|
||
.concerning-item {
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
:deep(.el-button) {
|
||
padding: 0 10px;
|
||
|
||
i {
|
||
font-size: 22px;
|
||
}
|
||
}
|
||
|
||
> span:first-child {
|
||
font-weight: bold;
|
||
padding-right: 20px;
|
||
}
|
||
}
|
||
|
||
.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: 15px;
|
||
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> |