Files
cloud-mail/mail-vue/src/views/sys-setting/index.vue
T

1315 lines
39 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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>