新增权限域名限制和链接背景上传
This commit is contained in:
7
mail-vue/package-lock.json
generated
7
mail-vue/package-lock.json
generated
@@ -22,7 +22,6 @@
|
||||
"pinia": "^3.0.2",
|
||||
"pinia-plugin-persistedstate": "^4.2.0",
|
||||
"vue": "^3.5.13",
|
||||
"vue-cropper": "^1.1.4",
|
||||
"vue-i18n": "^11.1.10",
|
||||
"vue-router": "^4.5.0"
|
||||
},
|
||||
@@ -3842,12 +3841,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vue-cropper": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmmirror.com/vue-cropper/-/vue-cropper-1.1.4.tgz",
|
||||
"integrity": "sha512-5m98vBsCEI9rbS4JxELxXidtAui3qNyTHLHg67Qbn7g8cg+E6LcnC+hh3SM/p94x6mFh6KRxT1ttnta+wCYqWA==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/vue-i18n": {
|
||||
"version": "11.1.10",
|
||||
"resolved": "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-11.1.10.tgz",
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
"pinia": "^3.0.2",
|
||||
"pinia-plugin-persistedstate": "^4.2.0",
|
||||
"vue": "^3.5.13",
|
||||
"vue-cropper": "^1.1.4",
|
||||
"vue-i18n": "^11.1.10",
|
||||
"vue-router": "^4.5.0"
|
||||
},
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import { useI18n } from "vue-i18n";
|
||||
import {useSettingStore} from "@/store/setting.js";
|
||||
const settingStore = useSettingStore()
|
||||
import zhCn from 'element-plus/es/locale/lang/zh-cn';
|
||||
import zhCn from 'element-plus/es/locale/lang/zh-cn';;
|
||||
const { locale } = useI18n()
|
||||
locale.value = settingStore.lang
|
||||
</script>
|
||||
@@ -96,7 +96,6 @@ const en = {
|
||||
expand: 'Expand',
|
||||
collapse: 'Collapse',
|
||||
daily: 'Daily',
|
||||
emailBlock: '*Intercept all incoming emails to @example.com',
|
||||
searchRegKeyDesc: 'Enter invite code to search',
|
||||
remainingUses: 'Remaining uses',
|
||||
exhausted: 'Exhausted',
|
||||
@@ -168,11 +167,11 @@ const en = {
|
||||
addOsDomain: 'Add domain',
|
||||
domainDesc: 'Domain',
|
||||
addTurnstileSecret: 'Add turnstile secret',
|
||||
backgroundCropping: 'Background cropping',
|
||||
backgroundTitle: 'Change background',
|
||||
tgBotDesc: 'Forward received emails to a Telegram bot',
|
||||
tgBotToken: 'Bot token',
|
||||
toBotTokenDesc: 'Multiple user chat_ids, separated by commas',
|
||||
otherEmailDec: 'emails can be forwarded to external email, but must be verified via cloudflare.',
|
||||
otherEmailDesc: 'emails can be forwarded to external email, but must be verified via cloudflare.',
|
||||
otherEmailInputDesc: 'Separate multiple email addresses with commas.',
|
||||
forwardingRulesDesc: 'Rule-based forwarding only forwards emails received by the specified address.',
|
||||
ruleEmailsInputDesc: 'Separate multiple email addresses with commas.',
|
||||
@@ -246,7 +245,17 @@ const en = {
|
||||
totalUserAccount: '{msg}',
|
||||
sendBanned: 'Banned',
|
||||
wrote: 'wrote',
|
||||
support: 'Support'
|
||||
support: 'Support',
|
||||
supportDesc: 'Buy me tea',
|
||||
featDesc: 'Feature Description',
|
||||
emailInterception: 'Email Interception',
|
||||
emailInterceptionDesc: '*Intercept emails by blocking entire domain using @example.com to prevent users from receiving emails from certain websites.',
|
||||
availableDomains: 'Available domains',
|
||||
availableDomainsDesc: 'Restrict users to email domains specified. Domains not on the approved list will be blocked from registration, adding email addresses, and sending/receiving emails. If left blank, all domains will be allowed by default.',
|
||||
backgroundUrlDesc: 'Image URL',
|
||||
localUpload: ' Local upload',
|
||||
imageLink: 'Image URL',
|
||||
backgroundWarning: 'Image file size affects website load speed.'
|
||||
}
|
||||
|
||||
export default en
|
||||
|
||||
@@ -96,7 +96,6 @@ const zh = {
|
||||
expand: '展开',
|
||||
collapse: '收起',
|
||||
daily: '每天',
|
||||
emailBlock: '输入邮箱拦截收件, 拦截所有前缀 *@example.com',
|
||||
searchRegKeyDesc: '输入注册码搜索',
|
||||
remainingUses: '剩余次数',
|
||||
exhausted: '已用尽',
|
||||
@@ -165,14 +164,14 @@ const zh = {
|
||||
community: '交流',
|
||||
changeTitle: '修改标题',
|
||||
addResendTokenDesc: '输入内容添加,不填则删除',
|
||||
addOsDomain: '添加访对象存储问域名',
|
||||
addOsDomain: '添加访问域名',
|
||||
domainDesc: '域名',
|
||||
addTurnstileSecret: '添加 Turnstile 密钥',
|
||||
backgroundCropping: '背景截图',
|
||||
backgroundTitle: '设置背景',
|
||||
tgBotDesc: '可以将接收的邮件转发到Tg机器人',
|
||||
tgBotToken: '机器人 token',
|
||||
toBotTokenDesc: '用户 chat_id 多个用,分开',
|
||||
otherEmailDec: '可以将邮件转到其他服务商邮箱,需要在cloudflare验证邮箱',
|
||||
otherEmailDesc: '可以将邮件转到其他服务商邮箱,需要在cloudflare验证邮箱',
|
||||
otherEmailInputDesc: '多个邮箱用, 分开',
|
||||
forwardingRulesDesc: '规则转发只会转发设置邮箱所接收的邮件',
|
||||
ruleEmailsInputDesc: '多个邮箱用, 分开',
|
||||
@@ -246,6 +245,16 @@ const zh = {
|
||||
totalUserAccount: '{msg} 个',
|
||||
sendBanned: '已禁用',
|
||||
wrote: '来信',
|
||||
support: '赞助'
|
||||
support: '赞助',
|
||||
supportDesc: '请我喝杯奶茶',
|
||||
featDesc: '功能说明',
|
||||
emailInterception: '邮件拦截',
|
||||
emailInterceptionDesc: '拦截邮件, 要拦截整个域名输入 *@example.com, 可用于禁止用户接收某些网站的邮件',
|
||||
availableDomains: '可用域名',
|
||||
availableDomainsDesc: '限制用户只能使用指定的域名邮箱,不在配置名单内的域名会被禁止使用注册添加邮箱,接收发送邮件等功能,留空默认允许可用所有域名',
|
||||
backgroundUrlDesc: '在线图片链接',
|
||||
localUpload: '本地上传',
|
||||
imageLink: '图片链接',
|
||||
backgroundWarning: '图片文件大小会影响网站加载速度',
|
||||
}
|
||||
export default zh
|
||||
@@ -61,8 +61,8 @@
|
||||
</el-menu-item>
|
||||
<el-menu-item @click="router.push({name: 'sys-setting'})" index="sys-setting" v-perm="'setting:query'"
|
||||
:class="route.meta.name === 'sys-setting' ? 'choose-item' : ''">
|
||||
<Icon icon="eos-icons:system-ok-outlined" width="18" height="18" />
|
||||
<span class="menu-name" style="margin-left: 24px">{{$t('SystemSettings')}}</span>
|
||||
<Icon icon="eos-icons:system-ok-outlined" width="18" height="18" style="margin-left: 2px" />
|
||||
<span class="menu-name" style="margin-left: 22px">{{$t('SystemSettings')}}</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</div>
|
||||
|
||||
@@ -38,7 +38,6 @@ const handleResize = () => {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
uiStore.writerRef = writerRef
|
||||
|
||||
window.addEventListener('resize', handleResize)
|
||||
|
||||
@@ -2,8 +2,6 @@ import {createApp} from 'vue';
|
||||
import App from './App.vue';
|
||||
import router from './router';
|
||||
import './style.css';
|
||||
import VueCropper from 'vue-cropper';
|
||||
import 'vue-cropper/dist/index.css'
|
||||
import { init } from '@/init/init.js';
|
||||
import { createPinia } from 'pinia';
|
||||
import piniaPersistedState from 'pinia-plugin-persistedstate';
|
||||
@@ -12,7 +10,7 @@ const pinia = createPinia().use(piniaPersistedState)
|
||||
import i18n from "@/i18n/index.js";
|
||||
const app = createApp(App).use(pinia)
|
||||
await init()
|
||||
app.use(router).use(VueCropper).use(i18n).directive('perm',perm)
|
||||
app.use(router).use(i18n).directive('perm',perm)
|
||||
app.config.devtools = true;
|
||||
|
||||
app.mount('#app');
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
import {useSettingStore} from "@/store/setting.js";
|
||||
export function cvtR2Url(key) {
|
||||
const settingStore = useSettingStore();
|
||||
return settingStore.settings.r2Domain + '/' + key
|
||||
|
||||
if (!key) {
|
||||
return + 'https://' + ''
|
||||
}
|
||||
|
||||
if (key.startsWith('https://')) {
|
||||
return key
|
||||
}
|
||||
|
||||
const { settings } = useSettingStore();
|
||||
|
||||
let domain = settings.r2Domain
|
||||
|
||||
if (!domain.startsWith('http')) {
|
||||
return 'https://' + domain + '/' + key
|
||||
}
|
||||
|
||||
if (domain.endsWith("/")) {
|
||||
domain = domain.slice(0, -1);
|
||||
}
|
||||
return domain + '/' + key
|
||||
}
|
||||
@@ -40,11 +40,11 @@ export function fromNow(date) {
|
||||
if (diffSeconds < 60) return `Just now`;
|
||||
if (diffMinutes < 60) return `${diffMinutes} min ago`;
|
||||
if (diffHours < 2) return `${diffHours} hour${diffHours > 1 ? 's' : ''} ago`;
|
||||
return d.format('HH:mm');
|
||||
return d.format('hh:mm A');
|
||||
}
|
||||
|
||||
if (now.subtract(1, 'day').isSame(d, 'day')) {
|
||||
return d.format('hh:mm A');
|
||||
return d.format('MMM D');
|
||||
}
|
||||
|
||||
return d.year() === now.year()
|
||||
|
||||
@@ -14,13 +14,18 @@ export function formatBytes(bytes) {
|
||||
return `${size} ${units[i]}`;
|
||||
}
|
||||
|
||||
export function fileToBase64(file) {
|
||||
export function fileToBase64(file, type = false) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => {
|
||||
const base64 = reader.result.split(',')[1];
|
||||
resolve(base64);
|
||||
if (type) {
|
||||
const base64 = reader.result;
|
||||
resolve(base64);
|
||||
} else {
|
||||
const base64 = reader.result.split(',')[1];
|
||||
resolve(base64);
|
||||
}
|
||||
};
|
||||
reader.onerror = reject;
|
||||
});
|
||||
@@ -50,4 +55,4 @@ export function compressImage(file, config = {}) {
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -47,15 +47,52 @@
|
||||
</el-table>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
<el-dialog top="5vh" class="dialog" v-model="roleFormShow" :title="dialogType.title" @closed="resetForm">
|
||||
<el-dialog top="5vh" class="dialog" v-model="roleFormShow" @closed="resetForm">
|
||||
<template #header>
|
||||
<span style="font-size: 18px">{{dialogType.title}}</span>
|
||||
<el-popover
|
||||
width ="340"
|
||||
:title="t('featDesc')"
|
||||
placement="bottom"
|
||||
>
|
||||
<template #reference>
|
||||
<Icon class="warning" icon="fe:warning" width="18" height="18"/>
|
||||
</template>
|
||||
<div style="font-weight: bold;margin-bottom: 2px;">{{t('emailInterception')}}</div>
|
||||
<div>{{t('emailInterceptionDesc')}}</div>
|
||||
<div style="font-weight: bold;margin-top: 10px;margin-bottom: 2px;">{{t('availableDomains')}}</div>
|
||||
<div>
|
||||
{{t('availableDomainsDesc')}}
|
||||
</div>
|
||||
</el-popover>
|
||||
</template>
|
||||
<div class="dialog-box">
|
||||
<el-input class="dialog-input" v-model="form.name" type="text" :maxlength="12" :placeholder="$t('roleName')" autocomplete="off" />
|
||||
<el-input class="dialog-input" v-model="form.description" :maxlength="30" type="text" :placeholder="$t('description')" autocomplete="off" />
|
||||
<el-input-tag class="dialog-input-tag" tag-type="warning" :class="form.banEmail.length === 0 ? 'dialog-input' : '' " v-model="form.banEmail" @add-tag="banEmailAddTag" type="text" :placeholder="$t('emailBlock')" autocomplete="off" />
|
||||
<el-input-tag class="dialog-input-tag" tag-type="warning" :class="form.banEmail.length === 0 ? 'dialog-input' : '' " v-model="form.banEmail" @add-tag="banEmailAddTag" type="text" :placeholder="$t('emailInterception')" autocomplete="off" />
|
||||
<el-radio-group class="dialog-radio" v-model="form.banEmailType" v-if="form.banEmail.length > 0">
|
||||
<el-radio :label="$t('removeAll')" :value="0" />
|
||||
<el-radio :label="$t('removeBody')" :value="1" />
|
||||
</el-radio-group>
|
||||
<el-select
|
||||
class="dialog-input"
|
||||
v-model="form.availDomain"
|
||||
multiple
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
:reserve-keyword="false"
|
||||
tag-type="success"
|
||||
:placeholder="$t('availableDomains')"
|
||||
@change="availDomainChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in domainOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<div class="dialog-input">
|
||||
<el-input-number :placeholder="$t('order')" :min="0" :max="9999" v-model.number="form.sort" controls-position="right" autocomplete="off" />
|
||||
</div>
|
||||
@@ -108,6 +145,7 @@ import {roleAdd, roleDelete, rolePermTree, roleRoleList, roleSet, roleSetDef} fr
|
||||
import loading from '@/components/loading/index.vue';
|
||||
import {useRoleStore} from "@/store/role.js";
|
||||
import {useUserStore} from "@/store/user.js";
|
||||
import {useSettingStore} from "@/store/setting.js";
|
||||
import {isEmail} from "@/utils/verify-utils.js";
|
||||
import {useI18n} from "vue-i18n";
|
||||
|
||||
@@ -115,6 +153,7 @@ defineOptions({
|
||||
name: 'role'
|
||||
})
|
||||
|
||||
const { domainList } = useSettingStore();
|
||||
const { t, locale } = useI18n();
|
||||
const userStore = useUserStore();
|
||||
const roleStore = useRoleStore();
|
||||
@@ -144,8 +183,11 @@ const form = reactive({
|
||||
accountCount: 0,
|
||||
sort: 0,
|
||||
isDefault: 0,
|
||||
availDomain: []
|
||||
})
|
||||
|
||||
let domainOptions = []
|
||||
|
||||
const expand = ref(false)
|
||||
|
||||
let chooseRole = {}
|
||||
@@ -156,6 +198,18 @@ rolePermTree().then(tree => {
|
||||
treeList.push(...tree)
|
||||
})
|
||||
|
||||
domainOptions = domainList.map(domain => ({label: domain,value: domain}))
|
||||
|
||||
|
||||
function availDomainChange() {
|
||||
const index = form.availDomain.findIndex(domain => {
|
||||
return !domainOptions.map(option => option.value).includes(domain)
|
||||
})
|
||||
if (index > -1) {
|
||||
form.availDomain.splice(index,1)
|
||||
}
|
||||
}
|
||||
|
||||
function banEmailAddTag(val) {
|
||||
const emails = Array.from(new Set(
|
||||
val.split(/[,,]/).map(item => item.trim()).filter(item => item)
|
||||
@@ -270,6 +324,7 @@ function resetForm() {
|
||||
form.accountCount = 0
|
||||
form.banEmail = []
|
||||
form.banEmailType = 0
|
||||
form.availDomain = []
|
||||
tree.value.setCheckedKeys([])
|
||||
}
|
||||
|
||||
@@ -285,6 +340,7 @@ function openRoleSet(role) {
|
||||
form.sendCount = role.sendCount
|
||||
form.accountCount = role.accountCount
|
||||
form.banEmail = role.banEmail
|
||||
form.availDomain = role.availDomain
|
||||
nextTick(() => {
|
||||
tree.value.setCheckedKeys(role.permIds)
|
||||
})
|
||||
@@ -394,7 +450,7 @@ window.onresize = () => {
|
||||
.warning {
|
||||
position: relative;
|
||||
left: 5px;
|
||||
top: 5px;
|
||||
top: 2px;
|
||||
color: gray;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -73,9 +73,8 @@ import {accountSetName} from "@/request/account.js";
|
||||
import {useAccountStore} from "@/store/account.js";
|
||||
import {useI18n} from "vue-i18n";
|
||||
import {useSettingStore} from "@/store/setting.js";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
const { t, locale } = useI18n()
|
||||
const { t } = useI18n()
|
||||
const settingStore = useSettingStore()
|
||||
const accountStore = useAccountStore()
|
||||
const userStore = useUserStore();
|
||||
|
||||
@@ -101,13 +101,13 @@
|
||||
fit="cover"
|
||||
>
|
||||
<template #error>
|
||||
<div class="error-image" @click="openCut">
|
||||
<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="openCut">
|
||||
<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">
|
||||
@@ -286,10 +286,10 @@
|
||||
</div>
|
||||
<div class="concerning-item">
|
||||
<span>{{$t('support')}} : </span>
|
||||
<el-button @click="jump('https://afdian.com/a/eoao_')" >
|
||||
Afdian
|
||||
<el-button @click="jump('https://support.skymail.ink')" >
|
||||
{{t('supportDesc')}}
|
||||
<template #icon>
|
||||
<Icon color="#8261DB" icon="simple-icons:afdian" width="24" height="24" />
|
||||
<Icon color="#79D6B5" icon="simple-icons:buymeacoffee" width="20" height="20" />
|
||||
</template>
|
||||
</el-button>
|
||||
</div>
|
||||
@@ -299,7 +299,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Dialogs remain the same -->
|
||||
<el-dialog v-model="editTitleShow" :title="$t('changeTitle')" width="340" @closed="editTitle = ''">
|
||||
<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>
|
||||
@@ -319,7 +319,7 @@
|
||||
<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 = ''">
|
||||
<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>
|
||||
@@ -334,24 +334,37 @@
|
||||
</form>
|
||||
</el-dialog>
|
||||
<el-dialog
|
||||
v-model="cutShow"
|
||||
:title="$t('backgroundCropping')"
|
||||
v-model="showSetBackground"
|
||||
class="cut-dialog"
|
||||
@closed="closedSetBackground"
|
||||
>
|
||||
<div class="cropper">
|
||||
<vueCropper
|
||||
ref="cropper"
|
||||
:img="cutImage"
|
||||
:fixedNumber="[16,9]"
|
||||
outputType="jpeg"
|
||||
:fixed="true"
|
||||
:centerBox="true"
|
||||
:full="true"
|
||||
:autoCrop="true"
|
||||
:outputSize="0.92"
|
||||
></vueCropper>
|
||||
</div>
|
||||
<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>
|
||||
@@ -387,7 +400,7 @@
|
||||
<template #header>
|
||||
<div class="forward-head">
|
||||
<span class="forward-set-title">{{$t('otherEmail')}}</span>
|
||||
<el-tooltip effect="dark" :content="$t('otherEmailDec')">
|
||||
<el-tooltip effect="dark" :content="$t('otherEmailDesc')">
|
||||
<Icon class="warning" icon="fe:warning" width="18" height="18"/>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
@@ -454,6 +467,7 @@ 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({
|
||||
@@ -462,9 +476,8 @@ defineOptions({
|
||||
|
||||
const { t, locale } = useI18n();
|
||||
const firstLoading = ref(true)
|
||||
const cropper = ref()
|
||||
const cutImage = ref('')
|
||||
const cutShow = ref(false)
|
||||
const backgroundImage = ref('')
|
||||
const localUpShow = ref(false)
|
||||
const accountStore = useAccountStore();
|
||||
const userStore = useUserStore();
|
||||
const editTitleShow = ref(false)
|
||||
@@ -481,6 +494,9 @@ const editTitle = ref('')
|
||||
const settingLoading = ref(false)
|
||||
const r2DomainInput = ref('')
|
||||
const loginOpacity = ref(0)
|
||||
const backgroundUrl = ref('')
|
||||
let backgroundFile = {}
|
||||
const showSetBackground = ref(false)
|
||||
let backup = '{}'
|
||||
const resendTokenForm = reactive({
|
||||
domain: '',
|
||||
@@ -517,6 +533,17 @@ 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
|
||||
})
|
||||
|
||||
const resendList = computed(() => {
|
||||
|
||||
let list = Object.keys(setting.value.resendTokens).map(key => {
|
||||
@@ -548,13 +575,11 @@ const compareByLengthAndUpperCase = (a, b, key) => {
|
||||
};
|
||||
|
||||
|
||||
|
||||
settingQuery().then(settingData => {
|
||||
setting.value = settingData
|
||||
resendTokenForm.domain = setting.value.domainList[0]
|
||||
loginOpacity.value = setting.value.loginOpacity
|
||||
firstLoading.value = false
|
||||
})
|
||||
function closedSetBackground() {
|
||||
backgroundImage.value = ''
|
||||
localUpShow.value = false
|
||||
backgroundUrl.value = setting.value.background?.startsWith('http') ? setting.value.background : ''
|
||||
}
|
||||
|
||||
function openTgSetting() {
|
||||
tgBotStatus.value = setting.value.tgBotStatus
|
||||
@@ -697,6 +722,7 @@ function delBackground() {
|
||||
cancelButtonText: t('cancel'),
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
backgroundUrl.value = ''
|
||||
setting.value.background = null
|
||||
editSetting({background: null})
|
||||
})
|
||||
@@ -709,21 +735,43 @@ function saveTurnstileKey() {
|
||||
editSetting(settingForm)
|
||||
}
|
||||
|
||||
function saveBackground() {
|
||||
settingLoading.value = true
|
||||
cropper.value.getCropData(data => {
|
||||
setBackground(data).then(key => {
|
||||
setting.value.background = key
|
||||
cutShow.value = false
|
||||
async function saveBackground() {
|
||||
|
||||
let image = ''
|
||||
|
||||
if (localUpShow.value) {
|
||||
image = await fileToBase64(backgroundFile,true);
|
||||
} else {
|
||||
if (!backgroundUrl.value.startsWith('http')) {
|
||||
ElMessage({
|
||||
message: t('changSuccessMsg'),
|
||||
type: "success",
|
||||
message: '图片地址不正确',
|
||||
type: "error",
|
||||
plain: true
|
||||
})
|
||||
}).finally(() => {
|
||||
settingLoading.value = false
|
||||
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() {
|
||||
@@ -732,14 +780,15 @@ function openCut() {
|
||||
doc.setAttribute('accept', 'image/*')
|
||||
doc.click()
|
||||
doc.onchange = async (e) => {
|
||||
cutImage.value = URL.createObjectURL(e.target.files[0])
|
||||
cutShow.value = true
|
||||
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)
|
||||
editSetting(settingForm, true)
|
||||
}
|
||||
|
||||
function openResendForm() {
|
||||
@@ -824,7 +873,7 @@ function editSetting(settingForm, refreshStatus = true) {
|
||||
thirdEmailShow.value = false
|
||||
forwardRulesShow.value = false
|
||||
}).catch((e) => {
|
||||
console.log(e)
|
||||
console.error(e)
|
||||
loginOpacity.value = setting.value.loginOpacity
|
||||
setting.value = {...setting.value, ...JSON.parse(backup)}
|
||||
}).finally(() => {
|
||||
@@ -892,6 +941,8 @@ function editSetting(settingForm, refreshStatus = true) {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.settings-card {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
@@ -939,6 +990,14 @@ function editSetting(settingForm, refreshStatus = true) {
|
||||
}
|
||||
}
|
||||
|
||||
.title-icon.warning {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
color: gray;
|
||||
cursor: pointer;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.warning {
|
||||
margin-left: 5px;
|
||||
color: gray;
|
||||
@@ -959,6 +1018,11 @@ function editSetting(settingForm, refreshStatus = true) {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.background-url {
|
||||
width: min(calc(100vw - 70px), 500px);
|
||||
}
|
||||
|
||||
|
||||
:deep(.el-dialog) {
|
||||
width: 400px !important;
|
||||
@media (max-width: 440px) {
|
||||
@@ -1027,8 +1091,7 @@ function editSetting(settingForm, refreshStatus = true) {
|
||||
padding-top: 15px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
|
||||
justify-content: space-between;
|
||||
.el-button {
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
@@ -644,7 +644,6 @@ function setType() {
|
||||
function resetUserForm() {
|
||||
userForm.password = null
|
||||
userForm.userId = 0
|
||||
userForm.type = 0
|
||||
}
|
||||
|
||||
function search() {
|
||||
|
||||
Reference in New Issue
Block a user