新增中英文切换

This commit is contained in:
eoao
2025-07-22 22:02:23 +08:00
parent 249edb9e19
commit bbcff4908d
64 changed files with 1951 additions and 1298 deletions

View File

@@ -1,7 +1,7 @@
<template>
<div class="box-404">
<el-empty :image-size="isPhone ? 120 : 0" class="empty" description="404错误, 找不到页面" >
<el-button type="primary" @click="router.replace({name: 'layout'})">返回首页</el-button>
<el-empty :image-size="isPhone ? 120 : 0" class="empty" :description="$t('error404')" >
<el-button type="primary" @click="router.replace({name: 'layout'})">{{$t('home')}}</el-button>
</el-empty>
</div>
</template>

View File

@@ -2,7 +2,7 @@
<div class="email-list-box">
<emailScroll ref="sysEmailScroll"
:get-emailList="getEmailList"
:email-delete="sysEmailDelete"
:email-delete="allEmailDelete"
:star-add="starAdd"
:star-cancel="starCancel"
:show-star="false"
@@ -12,13 +12,13 @@
:show-account-icon="false"
@jump="jumpContent"
@refresh-before="refreshBefore"
:type="'sys-email'"
:type="'all-email'"
>
<template #first>
<el-input
v-model="searchValue"
placeholder="输入内容查询"
:placeholder="$t('searchByContent')"
class="search-input"
>
<template #prefix>
@@ -26,13 +26,13 @@
<el-select
ref="mySelect"
v-model="params.searchType"
placeholder="请选择"
:placeholder="$t('select')"
class="select"
>
<el-option key="3" label="发件人" :value="'name'"/>
<el-option key="4" label="主题" :value="'subject'"/>
<el-option key="1" label="用户" :value="'user'"/>
<el-option key="2" label="邮箱" :value="'account'"/>
<el-option key="3" :label="$t('sender')" :value="'name'"/>
<el-option key="4" :label="$t('subject')" :value="'subject'"/>
<el-option key="1" :label="$t('user')" :value="'user'"/>
<el-option key="2" :label="$t('selectEmail')" :value="'account'"/>
</el-select>
<div style="color: #333;display: flex;">
<span>{{ selectTitle }}</span>
@@ -42,11 +42,11 @@
</template>
</el-input>
<el-select v-model="params.type" placeholder="Select" class="status-select">
<el-option key="1" label="全部" value="all"/>
<el-option key="3" label="已接收" value="receive"/>
<el-option key="2" label="已发送" value="send"/>
<el-option key="4" label="已删除" value="delete"/>
<el-option key="4" label="无人收件" value="noone"/>
<el-option key="1" :label="$t('all')" value="all"/>
<el-option key="3" :label="$t('received')" value="receive"/>
<el-option key="2" :label="$t('sent')" value="send"/>
<el-option key="4" :label="$t('deleted')" value="delete"/>
<el-option key="4" :label="$t('noRecipient')" value="noone"/>
</el-select>
<Icon class="icon" icon="iconoir:search" @click="search" width="20" height="20"/>
<Icon class="icon" @click="changeTimeSort" icon="material-symbols-light:timer-arrow-down-outline"
@@ -64,16 +64,18 @@ import emailScroll from "@/components/email-scroll/index.vue"
import {computed, defineOptions, reactive, ref, watch} from "vue";
import {useEmailStore} from "@/store/email.js";
import {
sysEmailList,
sysEmailDelete
} from "@/request/sys-email.js";
allEmailList,
allEmailDelete
} from "@/request/all-email.js";
import {Icon} from "@iconify/vue";
import router from "@/router/index.js";
import { useI18n } from 'vue-i18n';
defineOptions({
name: 'sys-email'
name: 'all-email'
})
const { t } = useI18n();
const emailStore = useEmailStore();
const sysEmailScroll = ref({})
const searchValue = ref('')
@@ -95,13 +97,13 @@ const params = reactive({
const selectTitle = computed(() => {
if (params.searchType === 'user') return '用户'
if (params.searchType === 'account') return '邮箱'
if (params.searchType === 'name') return '发件人'
if (params.searchType === 'subject') return '主题'
if (params.searchType === 'user') return t('user')
if (params.searchType === 'account') return t('selectEmail')
if (params.searchType === 'name') return t('sender')
if (params.searchType === 'subject') return t('subject')
})
const paramsStar = localStorage.getItem('sys-email-params')
const paramsStar = localStorage.getItem('all-email-params')
if (paramsStar) {
const locaParams = JSON.parse(paramsStar)
params.type = locaParams.type
@@ -111,7 +113,7 @@ if (paramsStar) {
}
watch(() => params, () => {
localStorage.setItem('sys-email-params',JSON.stringify(params))
localStorage.setItem('all-email-params',JSON.stringify(params))
}, {
deep: true
})
@@ -163,12 +165,12 @@ function jumpContent(email) {
emailStore.contentData.delType = 'physics'
emailStore.contentData.showStar = false
emailStore.contentData.showReply = false
router.push('/content')
router.push({name: 'content'})
}
function getEmailList(emailId, size) {
return sysEmailList({emailId, size, ...params})
return allEmailList({emailId, size, ...params})
}
</script>

View File

@@ -8,7 +8,7 @@
<div class="number-item">
<div class="top">
<div class="left">
<div>收件数量</div>
<div>{{$t('totalReceived')}}</div>
<div>
<el-statistic :formatter="value => Math.round(value)" :value="receiveData"/>
</div>
@@ -20,14 +20,14 @@
</div>
</div>
<div class="delete-ratio">
<div>正常 <span class="normal">{{numberCount.normalReceiveTotal}}</span></div>
<div>删除 <span class="deleted">{{numberCount.delReceiveTotal}}</span></div>
<div>{{$t('active')}} <span class="normal">{{numberCount.normalReceiveTotal}}</span></div>
<div>{{$t('deleted')}} <span class="deleted">{{numberCount.delReceiveTotal}}</span></div>
</div>
</div>
<div class="number-item">
<div class="top">
<div class="left">
<div>发件数量</div>
<div>{{$t('totalSent')}}</div>
<div>
<el-statistic :formatter="value => Math.round(value)" :value="sendData"/>
</div>
@@ -39,14 +39,14 @@
</div>
</div>
<div class="delete-ratio">
<div>正常 <span class="normal">{{numberCount.normalSendTotal}}</span></div>
<div>删除 <span class="deleted">{{numberCount.delSendTotal}}</span></div>
<div>{{$t('active')}} <span class="normal">{{numberCount.normalSendTotal}}</span></div>
<div>{{$t('deleted')}} <span class="deleted">{{numberCount.delSendTotal}}</span></div>
</div>
</div>
<div class="number-item">
<div class="top">
<div class="left">
<div>邮箱数量</div>
<div>{{$t('totalMailboxes')}}</div>
<div>
<el-statistic :formatter="value => Math.round(value)" :value="accountData"/>
</div>
@@ -58,14 +58,14 @@
</div>
</div>
<div class="delete-ratio">
<div>正常 <span class="normal">{{numberCount.normalAccountTotal}}</span></div>
<div>删除 <span class="deleted">{{numberCount.delAccountTotal}}</span></div>
<div>{{$t('active')}} <span class="normal">{{numberCount.normalAccountTotal}}</span></div>
<div>{{$t('deleted')}} <span class="deleted">{{numberCount.delAccountTotal}}</span></div>
</div>
</div>
<div class="number-item">
<div class="top">
<div class="left">
<div>用户数量</div>
<div>{{$t('totalUsers')}}</div>
<div>
<el-statistic :formatter="value => Math.round(value)" :value="userData"/>
</div>
@@ -77,15 +77,15 @@
</div>
</div>
<div class="delete-ratio">
<div>正常 <span class="normal">{{numberCount.normalUserTotal}}</span></div>
<div>删除 <span class="deleted">{{numberCount.delUserTotal}}</span></div>
<div>{{$t('active')}} <span class="normal">{{numberCount.normalUserTotal}}</span></div>
<div>{{$t('deleted')}} <span class="deleted">{{numberCount.delUserTotal}}</span></div>
</div>
</div>
</div>
<div class="picture">
<div class="picture-item">
<div class="title" style="display: flex;justify-content: space-between;">
<span>邮件来源</span>
<span>{{$t('emailSource')}}</span>
<span class="source-button" v-if="false">
<el-radio-group v-model="checkedSourceType" >
<el-radio-button label="发件人" value="sender" />
@@ -98,7 +98,7 @@
</div>
</div>
<div class="picture-item">
<div class="title">用户增长</div>
<div class="title">{{$t('userGrowth')}}</div>
<div class="increase-line">
</div>
@@ -106,11 +106,11 @@
</div>
<div class="picture-cs">
<div class="picture-cs-item">
<div class="title">邮件增长</div>
<div class="title">{{$t('emailGrowth')}}</div>
<div class="email-column"></div>
</div>
<div class="picture-cs-item">
<div class="title">今日发件</div>
<div class="title">{{$t('sentToday')}}</div>
<div class="send-count"></div>
</div>
</div>
@@ -129,11 +129,14 @@ import {useUiStore} from "@/store/ui.js";
import {debounce} from "lodash-es";
import loading from "@/components/loading/index.vue";
import {useRoute} from "vue-router";
import { useI18n } from 'vue-i18n';
import {toUtc, tzDayjs} from "@/utils/day.js";
defineOptions({
name: 'analysis'
})
const { t } = useI18n();
const route = useRoute();
const uiStore = useUiStore()
const checkedSourceType = ref('sender')
@@ -194,7 +197,9 @@ let boxKey = ref(0)
let senderPieLeft = window.innerWidth < 500 ? `${window.innerWidth - 110}` : '72%'
onMounted(() => {
analysisEcharts().then(data => {
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
analysisEcharts(timeZone).then(data => {
receiveTotal.value = data.numberCount.receiveTotal
sendTotal.value = data.numberCount.sendTotal
accountTotal.value = data.numberCount.accountTotal
@@ -374,7 +379,7 @@ function createIncreaseLine() {
formatter: function (params) {
let result = ''
params.forEach(item => {
result = `${item.marker} 用户数: ${item.value}`;
result = `${item.marker} ${t('growthTotalUsers')} ${item.value}`;
});
return result;
},
@@ -507,7 +512,7 @@ function createEmailColumnChart() {
}
},
legend: {
data: ['接收', '发送'],
data: [t('emailReceived'), t('emailSent')],
top: '0'
},
grid: {
@@ -542,7 +547,7 @@ function createEmailColumnChart() {
},
series: [
{
name: '接收',
name: t('emailReceived'),
type: 'bar',
stack: 'total', // 堆叠组标识(必须相同)
barWidth: '60%',
@@ -559,7 +564,7 @@ function createEmailColumnChart() {
}
},
{
name: '发送',
name: t('emailSent'),
type: 'bar',
stack: 'total', // 堆叠组标识(必须相同)
emphasis: {
@@ -587,7 +592,7 @@ function createSendGauge() {
let option = {
tooltip: {},
series: [{
name: '今日发件',
name: t('sentToday'),
type: 'gauge',
max: 100,
// 进度条颜色(新增)
@@ -625,7 +630,7 @@ function createSendGauge() {
},
data: [{
value: daySendTotal,
name: '次数',
name: t('total'),
// 名称标签颜色(新增)
title: {
color: '#333' // 灰色标签

View File

@@ -18,20 +18,20 @@
<div class="content">
<div class="email-info">
<div>
<div class="send"><span class="send-source">发件人</span>
<div class="send"><span class="send-source">{{$t('from')}}</span>
<div class="send-name">
<span class="send-name-title">{{ email.name }}</span>
<span><{{ email.sendEmail }}></span>
</div>
</div>
<div class="receive"><span class="source">收件人</span><span class="receive-email">{{ formateReceive(email.recipient) }}</span></div>
<div class="receive"><span class="source">{{$t('recipient')}}</span><span class="receive-email">{{ formateReceive(email.recipient) }}</span></div>
<div class="date">
<div>{{ formatDetailDate(email.createTime) }}</div>
</div>
</div>
<el-alert v-if="email.status === 3" :closable="false" :title="'发送失败: ' + toMessage(email.message)" class="email-msg" type="error" show-icon />
<el-alert v-if="email.status === 4" :closable="false" title="被标记为垃圾邮件" class="email-msg" type="warning" show-icon />
<el-alert v-if="email.status === 5" :closable="false" title="邮件发送被延迟" class="email-msg" type="warning" show-icon />
<el-alert v-if="email.status === 3" :closable="false" :title="`${$t('bounced')} ` + toMessage(email.message)" class="email-msg" type="error" show-icon />
<el-alert v-if="email.status === 4" :closable="false" :title="$t('complained')" class="email-msg" type="warning" show-icon />
<el-alert v-if="email.status === 5" :closable="false" :title="$t('delayed')" class="email-msg" type="warning" show-icon />
</div>
<el-scrollbar class="htm-scrollbar" :class="email.attList.length === 0 ? 'bottom-distance' : ''">
<ShadowHtml :html="formatImage(email.content)" v-if="email.content" />
@@ -39,8 +39,8 @@
</el-scrollbar>
<div class="att" v-if="email.attList.length > 0">
<div class="att-title">
<span>附件列表</span>
<span> {{email.attList.length}} </span>
<span>{{$t('attachments')}}</span>
<span>{{$t('attCount',{total: email.attList.length})}}</span>
</div>
<div class="att-box">
@@ -87,8 +87,9 @@ import {getExtName, formatBytes} from "@/utils/file-utils.js";
import {cvtR2Url} from "@/utils/convert.js";
import {getIconByName} from "@/utils/icon-utils.js";
import {useSettingStore} from "@/store/setting.js";
import {sysEmailDelete} from "@/request/sys-email.js";
import {allEmailDelete} from "@/request/all-email.js";
import {useUiStore} from "@/store/ui.js";
import {useI18n} from "vue-i18n";
const uiStore = useUiStore();
const settingStore = useSettingStore();
@@ -99,7 +100,7 @@ const email = emailStore.contentData.email
const showPreview = ref(false)
const srcList = reactive([])
const { t } = useI18n()
watch(() => accountStore.currentAccountId, () => {
handleBack()
})
@@ -166,15 +167,15 @@ const handleBack = () => {
}
const handleDelete = () => {
ElMessageBox.confirm('确认删除该邮件吗?', {
confirmButtonText: '确定',
cancelButtonText: '取消',
ElMessageBox.confirm(t('delEmailConfirm'), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}).then(() => {
if (emailStore.contentData.delType === 'logic') {
emailDelete(email.emailId).then(() => {
ElMessage({
message: '删除成功',
message: t('delSuccessMsg'),
type: 'success',
plain: true,
})
@@ -182,9 +183,9 @@ const handleDelete = () => {
})
} else {
sysEmailDelete(email.emailId).then(() => {
allEmailDelete(email.emailId).then(() => {
ElMessage({
message: '删除成功',
message: t('delSuccessMsg'),
type: 'success',
plain: true,
})

View File

@@ -14,10 +14,10 @@
:type="'draft'"
>
<template #name="props">
<span class="send-email" >{{props.email.receiveEmail.join(',') || '(无收件人)'}}</span>
<span class="send-email" >{{props.email.receiveEmail.join(',') || '('+$t('noRecipient')+')'}}</span>
</template>
<template #subject="props" >
{{props.email.subject || '(无标题)'}}
{{props.email.subject || '('+$t('noSubject')+')'}}
</template>
</emailScroll>
</template>

View File

@@ -11,16 +11,16 @@
<div class="form-wrapper">
<div class="container">
<span class="form-title">{{settingStore.settings.title}}</span>
<span class="form-desc" v-if="show === 'login'">请输入账号信息以开始使用邮箱系统</span>
<span class="form-desc" v-else>请输入账号密码以开始注册邮箱系统</span>
<span class="form-desc" v-if="show === 'login'">{{$t('loginTitle')}}</span>
<span class="form-desc" v-else>{{$t('regTitle')}}</span>
<div v-if="show === 'login'">
<el-input class="email-input" v-model="form.email" type="text" placeholder="邮箱" autocomplete="off">
<el-input class="email-input" v-model="form.email" type="text" :placeholder="$t('emailAccount')" autocomplete="off">
<template #append>
<div @click.stop="openSelect">
<el-select
ref="mySelect"
v-model="suffix"
placeholder="请选择"
:placeholder="$t('select')"
class="select"
>
<el-option
@@ -37,20 +37,20 @@
</div>
</template>
</el-input>
<el-input v-model="form.password" placeholder="密码" type="password" autocomplete="off">
<el-input v-model="form.password" :placeholder="$t('password')" type="password" autocomplete="off">
</el-input>
<el-button class="btn" type="primary" @click="submit" :loading="loginLoading"
>登录
>{{$t('loginBtn')}}
</el-button>
</div>
<div v-else>
<el-input class="email-input" v-model="registerForm.email" type="text" placeholder="邮箱" autocomplete="off">
<el-input class="email-input" v-model="registerForm.email" type="text" :placeholder="$t('emailAccount')" autocomplete="off">
<template #append>
<div @click.stop="openSelect">
<el-select
ref="mySelect"
v-model="suffix"
placeholder="请选择"
:placeholder="$t('select')"
class="select"
>
<el-option
@@ -67,22 +67,22 @@
</div>
</template>
</el-input>
<el-input v-model="registerForm.password" placeholder="密码" type="password" autocomplete="off" />
<el-input v-model="registerForm.confirmPassword" placeholder="确认密码" type="password" autocomplete="off" />
<el-input v-if="settingStore.settings.regKey === 0" v-model="registerForm.code" placeholder="注册码" type="text" autocomplete="off" />
<el-input v-if="settingStore.settings.regKey === 2" v-model="registerForm.code" placeholder="注册码(可选)" type="text" autocomplete="off" />
<el-input v-model="registerForm.password" :placeholder="$t('password')" type="password" autocomplete="off" />
<el-input v-model="registerForm.confirmPassword" :placeholder="$t('confirmPwd')" type="password" autocomplete="off" />
<el-input v-if="settingStore.settings.regKey === 0" v-model="registerForm.code" :placeholder="$t('regKey')" type="text" autocomplete="off" />
<el-input v-if="settingStore.settings.regKey === 2" v-model="registerForm.code" :placeholder="$t('regKeyOptional')" type="text" autocomplete="off" />
<div v-show="verifyShow"
class="register-turnstile"
:data-sitekey="settingStore.settings.siteKey"
data-callback="onTurnstileSuccess"
></div>
<el-button class="btn" type="primary" @click="submitRegister" :loading="registerLoading"
>注册
>{{$t('regBtn')}}
</el-button>
</div>
<template v-if="settingStore.settings.register === 0">
<div class="switch" @click="show = 'register'" v-if="show === 'login'">还没有账号? <span>创建账号</span></div>
<div class="switch" @click="show = 'login'" v-else>已有账号? <span>去登录</span></div>
<div class="switch" @click="show = 'register'" v-if="show === 'login'">{{$t('noAccount')}} <span>{{$t('regSwitch')}}</span></div>
<div class="switch" @click="show = 'login'" v-else>{{$t('hasAccount')}} <span>{{$t('loginSwitch')}}</span></div>
</template>
</div>
</div>
@@ -101,8 +101,10 @@ import {useUserStore} from "@/store/user.js";
import {Icon} from "@iconify/vue";
import {cvtR2Url} from "@/utils/convert.js";
import {loginUserInfo} from "@/request/my.js";
import {permsToRouter} from "@/utils/perm.js";
import {permsToRouter} from "@/perm/perm.js";
import {useI18n} from "vue-i18n";
const { t } = useI18n();
const accountStore = useAccountStore();
const userStore = useUserStore();
const settingStore = useSettingStore();
@@ -161,7 +163,7 @@ const submit = () => {
if (!form.email) {
ElMessage({
message: '邮箱不能为空',
message: t('emptyEmailMsg'),
type: 'error',
plain: true,
})
@@ -170,7 +172,7 @@ const submit = () => {
if (!isEmail(form.email + suffix.value)) {
ElMessage({
message: '输入的邮箱不合法',
message: t('notEmailMsg'),
type: 'error',
plain: true,
})
@@ -179,7 +181,7 @@ const submit = () => {
if (!form.password) {
ElMessage({
message: '密码不能为空',
message: t('emptyPwdMsg'),
type: 'error',
plain: true,
})
@@ -207,7 +209,7 @@ function submitRegister() {
if (!registerForm.email) {
ElMessage({
message: '邮箱不能为空',
message: t('emptyEmailMsg'),
type: 'error',
plain: true,
})
@@ -216,7 +218,7 @@ function submitRegister() {
if (!isEmail(registerForm.email + suffix.value)) {
ElMessage({
message: '输入的邮箱不合法',
message: t('notEmailMsg'),
type: 'error',
plain: true,
})
@@ -225,7 +227,7 @@ function submitRegister() {
if (!registerForm.password) {
ElMessage({
message: '密码不能为空',
message: t('emptyPwdMsg'),
type: 'error',
plain: true,
})
@@ -234,7 +236,7 @@ function submitRegister() {
if (registerForm.password.length < 6) {
ElMessage({
message: '密码最少六位',
message: t('pwdLengthMsg'),
type: 'error',
plain: true,
})
@@ -244,7 +246,7 @@ function submitRegister() {
if (registerForm.password !== registerForm.confirmPassword) {
ElMessage({
message: '两次密码输入不一致',
message: t('confirmPwdFailMsg'),
type: 'error',
plain: true,
})
@@ -256,7 +258,7 @@ function submitRegister() {
if (!registerForm.code) {
ElMessage({
message: '注册码不能为空',
message: t('emptyRegKeyMsg'),
type: 'error',
plain: true,
})
@@ -298,7 +300,7 @@ function submitRegister() {
turnstileId = null
verifyToken = ''
ElMessage({
message: '注册成功',
message: t('regSuccessMsg'),
type: 'success',
plain: true,
})

View File

@@ -6,7 +6,7 @@
<el-input
v-model="params.code"
class="search-input"
placeholder="输入注册码搜索"
:placeholder="$t('searchRegKeyDesc')"
>
</el-input>
</div>
@@ -27,18 +27,18 @@
<span class="code" @click="copyCode(item.code)">{{item.code}}</span>
</div>
<div class="info-left-item">
<div>剩余次数</div>
<div>{{$t('remainingUses')}}</div>
<div v-if="item.count">{{item.count}}</div>
<el-tag v-else type="danger">已用尽</el-tag>
<el-tag v-else type="danger">{{$t('exhausted')}}</el-tag>
</div>
<div class="info-left-item">
<div>权限身份</div>
<div>{{$t('roleDesc')}}</div>
<el-tag>{{item.roleName}}</el-tag>
</div>
<div class="info-left-item">
<div>有效至期</div>
<div>{{$t('validUntil')}}</div>
<div v-if="item.expireTime">{{ formatExpireTime(item.expireTime)}}</div>
<el-tag v-else type="danger">已过期</el-tag>
<el-tag v-else type="danger">{{$t('expired')}}</el-tag>
</div>
</div>
<div class="info-right">
@@ -46,9 +46,9 @@
<Icon icon="fluent:settings-24-filled" width="21" height="21" color="#909399" />
<template #dropdown >
<el-dropdown-menu>
<el-dropdown-item @click="copyCode(item.code)">复制</el-dropdown-item>
<el-dropdown-item @click="openHistory(item)">记录</el-dropdown-item>
<el-dropdown-item @click="deleteRegKey(item)">删除</el-dropdown-item>
<el-dropdown-item @click="copyCode(item.code)">{{$t('copy')}}</el-dropdown-item>
<el-dropdown-item @click="openHistory(item)">{{$t('history')}}</el-dropdown-item>
<el-dropdown-item @click="deleteRegKey(item)">{{$t('delete')}}</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
@@ -57,37 +57,37 @@
</div>
</div>
<div class="empty" v-if="regKeyData.length === 0">
<el-empty v-if="!regKeyFirst" :image-size="isMobile ? 120 : 0" description="没有任何注册码"/>
<el-empty v-if="!regKeyFirst" :image-size="isMobile ? 120 : 0" :description="$t('noCodeFound')"/>
</div>
</el-scrollbar>
<el-dialog v-model="showAdd" title="添加注册码">
<el-dialog v-model="showAdd" :title="$t('addRegKey')">
<div class="container">
<el-input v-model="addForm.code" placeholder="注册码">
<el-input v-model="addForm.code" :placeholder="$t('regKey')">
<template #suffix>
<Icon @click.stop="genCode" class="gen-code" icon="bitcoin-icons:refresh-filled" width="24" height="24" />
</template>
</el-input>
<el-select v-model="addForm.roleId" placeholder="身份类型">
<el-select v-model="addForm.roleId" :placeholder="$t('roleDesc')">
<el-option v-for="item in roleList" :label="item.name" :value="item.roleId" :key="item.roleId"/>
</el-select>
<el-date-picker
v-model="addForm.expireTime"
type="date"
placeholder="有效至期"
:placeholder="$t('validUntil')"
/>
<el-input-number v-model="addForm.count" :min="1" :max="99999"/>
<el-button class="btn" type="primary" @click="submit" :loading="addLoading"
>添加
>{{$t('add')}}
</el-button>
</div>
</el-dialog>
<el-dialog class="history-list" v-model="showRegKeyHistory" title="使用记录">
<el-dialog class="history-list" v-model="showRegKeyHistory" :title="$t('useHistory')">
<div class="loading" :class="historyLoading ? 'loading-show' : 'loading-hide'">
<loading />
</div>
<el-table v-if="!historyLoading" :data="historyList" :fit="true" style="height: 100%" >
<el-table-column :min-width="emailColumnWidth" property="email" label="用户" :show-overflow-tooltip="true" />
<el-table-column :width="createTimeColumnWidth" :formatter="formatUserCreateTime" property="createTime" label="时间" fixed="right" :show-overflow-tooltip="true" />
<el-table-column :min-width="emailColumnWidth" property="email" :label="$t('user')" :show-overflow-tooltip="true" />
<el-table-column :width="createTimeColumnWidth" :formatter="formatUserCreateTime" property="createTime" :label="$t('date')" fixed="right" :show-overflow-tooltip="true" />
</el-table>
</el-dialog>
</div>
@@ -104,6 +104,7 @@ import {regKeyAdd, regKeyList, regKeyClearNotUse, regKeyDelete, regKeyHistory} f
import { getTextWidth } from "@/utils/text.js";
import dayjs from "dayjs";
import {tzDayjs} from "@/utils/day.js";
import {useI18n} from "vue-i18n";
defineOptions({
name: 'reg-key'
@@ -115,6 +116,7 @@ const params = reactive({
code: '',
})
const { t } = useI18n()
const roleList = reactive([])
const addLoading = ref(false)
const showAdd = ref(false)
@@ -185,25 +187,44 @@ function formatUserCreateTime(regKey) {
const currentYear = dayjs().year();
const expireYear = createTime.year();
if (expireYear === currentYear) {
return createTime.format('M月D日 HH:mm');
if(settingStore.lang === 'zh') {
if (expireYear === currentYear) {
return createTime.format('M月D日 HH:mm');
} else {
return createTime.format('YYYY年M月D日 HH:mm');
}
} else {
return createTime.format('YYYY年M月D日 HH:mm');
if (createYear === currentYear) {
return createTime.format('MMM D, HH:mm');
} else {
return createTime.format('MMM D, YYYY HH:mm');
}
}
}
function formatExpireTime(expireTime) {
expireTime = tzDayjs(expireTime);
const expireDate = tzDayjs(expireTime);
const currentYear = dayjs().year();
const expireYear = expireTime.year();
const expireYear = expireDate.year();
if (expireYear === currentYear) {
return expireTime.format('M月D日');
if (settingStore.lang === 'zh') {
return expireYear === currentYear
? expireDate.format('M月D日')
: expireDate.format('YYYY年M月D日');
} else {
return expireTime.format('YYYY年M月D日');
return expireYear === currentYear
? expireDate.format('MMM D')
: expireDate.format('MMM D, YYYY');
}
}
function refresh() {
params.code = null
getList(true)
@@ -229,7 +250,7 @@ async function copyCode(code) {
try {
await navigator.clipboard.writeText(code);
ElMessage({
message: '复制成功',
message: t('copySuccessMsg'),
type: 'success',
plain: true,
})
@@ -257,14 +278,14 @@ function generateRandomCode(length = 8) {
}
function clearNotUse() {
ElMessageBox.confirm(`确认清除所有不可用的注册码?`, {
confirmButtonText: '确定',
cancelButtonText: '取消',
ElMessageBox.confirm(t('clearRegKey'), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}).then(() => {
regKeyClearNotUse().then(() => {
ElMessage({
message: '清除成功',
message: t('clearSuccess'),
type: 'success',
plain: true,
})
@@ -277,7 +298,7 @@ function submit() {
if (!addForm.code) {
ElMessage({
message: "注册码不能为空",
message: $('emptyRegKeyMsg'),
type: "error",
plain: true
})
@@ -286,7 +307,7 @@ function submit() {
if (!addForm.roleId) {
ElMessage({
message: "身份类型不能为空",
message: t('emptyRole'),
type: "error",
plain: true
})
@@ -295,7 +316,7 @@ function submit() {
if (!addForm.expireTime) {
ElMessage({
message: "有效时间不能为空",
message: t('emptyTimeMsg'),
type: "error",
plain: true
})
@@ -304,7 +325,7 @@ function submit() {
if (!addForm.count) {
ElMessage({
message: "使用次数不能为空",
message: t('emptyCountMsg'),
type: "error",
plain: true
})
@@ -316,7 +337,7 @@ function submit() {
showAdd.value = false
resetForm()
ElMessage({
message: "添加成功",
message: t('addSuccessMsg'),
type: "success",
plain: true
})
@@ -327,15 +348,15 @@ function submit() {
}
function deleteRegKey(regKey){
ElMessageBox.confirm(`确认删除${regKey.code}吗?`, {
confirmButtonText: '确定',
cancelButtonText: '取消',
ElMessageBox.confirm(t('delConfirm',{msg: regKey.code}), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}).then(() => {
regKeyDelete([regKey.regKeyId]).then(() => {
getList()
ElMessage({
message: "删除成功",
message: t('delSuccessMsg'),
type: "success",
plain: true
})

View File

@@ -14,31 +14,31 @@
style="width: 100%;"
>
<el-table-column width="10" />
<el-table-column label="身份名称" prop="name" :min-width="roleWidth">
<el-table-column :label="$t('roleName')" prop="name" :min-width="roleWidth">
<template #default="props">
<div class="role-name">
<span >{{props.row.name}}</span>
<span v-if="props.row.isDefault"><el-tag class="def-tag" >默认</el-tag></span>
<span v-if="props.row.isDefault"><el-tag class="def-tag" >{{$t('default')}}</el-tag></span>
</div>
</template>
</el-table-column>
<el-table-column label="排序" :width="sortWidth" prop="sort"/>
<el-table-column v-if="desShow" label="描述" min-width="200" prop="description" >
<el-table-column :label="$t('order')" :width="sortWidth" prop="sort"/>
<el-table-column v-if="desShow" :label="$t('description')" min-width="200" prop="description" >
<template #default="props">
<div class="description">
<span >{{props.row.description}}</span>
</div>
</template>
</el-table-column>
<el-table-column label="操作" :width="settingWidth">
<el-table-column :label="$t('tabSetting')" :width="settingWidth">
<template #default="props">
<el-dropdown trigger="click">
<el-button size="small" type="primary">操作</el-button>
<el-button size="small" type="primary">{{$t('action')}}</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="openRoleSet(props.row)">修改</el-dropdown-item>
<el-dropdown-item @click="setDef(props.row)">默认</el-dropdown-item>
<el-dropdown-item @click="delRole(props.row)">删除</el-dropdown-item>
<el-dropdown-item @click="openRoleSet(props.row)">{{$t('change')}}</el-dropdown-item>
<el-dropdown-item @click="setDef(props.row)">{{$t('default')}}</el-dropdown-item>
<el-dropdown-item @click="delRole(props.row)">{{$t('delete')}}</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
@@ -49,19 +49,19 @@
</el-scrollbar>
<el-dialog top="5vh" class="dialog" v-model="roleFormShow" :title="dialogType.title" @closed="resetForm">
<div class="dialog-box">
<el-input class="dialog-input" v-model="form.name" type="text" :maxlength="12" placeholder="身份名称" autocomplete="off" />
<el-input class="dialog-input" v-model="form.description" :maxlength="30" type="text" placeholder="描述" 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="输入邮箱拦截收件, 拦截所有前缀 *@example.com" autocomplete="off" />
<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-radio-group class="dialog-radio" v-model="form.banEmailType" v-if="form.banEmail.length > 0">
<el-radio label="丢弃邮件" :value="0" />
<el-radio label="移除正文" :value="1" />
<el-radio :label="$t('removeAll')" :value="0" />
<el-radio :label="$t('removeBody')" :value="1" />
</el-radio-group>
<div class="dialog-input">
<el-input-number placeholder="排序" :min="0" :max="9999" v-model.number="form.sort" controls-position="right" autocomplete="off" />
<el-input-number :placeholder="$t('order')" :min="0" :max="9999" v-model.number="form.sort" controls-position="right" autocomplete="off" />
</div>
<el-radio-group v-model="expand" size="small" @change="expandChange" class="perm-expand">
<el-radio-button label="展开" :value="true" />
<el-radio-button label="收起" :value="false" />
<el-radio-button :label="$t('expand')" :value="true" />
<el-radio-button :label="$t('collapse')" :value="false" />
</el-radio-group>
<el-tree
:expand-on-click-node="false"
@@ -79,25 +79,23 @@
<div>
<span>{{node.label}}</span>
<span class="send-num" v-if="data.permKey === 'email:send'" @click.stop>
<el-input-number v-model="form.sendCount" controls-position="right" :max="99999" size="small" placeholder="数量" >
<el-input-number v-model="form.sendCount" controls-position="right" :max="99999" size="small" :placeholder="$t('total')" >
</el-input-number>
<el-select v-model="form.sendType" placeholder="Select" size="small" style="width: 60px;margin-left: 5px;">
<el-option label="总数" value="count" />
<el-option label="每天" value="day" />
<el-select v-model="form.sendType" placeholder="Select" size="small" style="width: 65px;margin-left: 5px;">
<el-option :label="$t('total')" value="count" />
<el-option :label="$t('daily')" value="day" />
<el-option :label="$t('btnBan')" value="ban" />
</el-select>
<el-tooltip effect="dark" content="零无限制 负数无次数">
<Icon class="warning" icon="fe:warning" width="18" height="18"/>
</el-tooltip>
</span>
<span class="send-num" v-if="data.permKey === 'account:add'" @click.stop>
<el-input-number v-model="form.accountCount" controls-position="right" :min="0" :max="99999" size="small" placeholder="数量" >
<el-input-number v-model="form.accountCount" controls-position="right" :min="0" :max="99999" size="small" :placeholder="$t('total')" >
</el-input-number>
</span>
</div>
</template>
</el-tree>
<el-button class="btn" type="primary" :loading="permLoading" @click="roleFormClick"
>保存
>{{$t('save')}}
</el-button>
</div>
</el-dialog>
@@ -111,11 +109,13 @@ import loading from '@/components/loading/index.vue';
import {useRoleStore} from "@/store/role.js";
import {useUserStore} from "@/store/user.js";
import {isEmail} from "@/utils/verify-utils.js";
import {useI18n} from "vue-i18n";
defineOptions({
name: 'role'
})
const { t, locale } = useI18n();
const userStore = useUserStore();
const roleStore = useRoleStore();
const roleFormShow = ref(false)
@@ -182,7 +182,7 @@ function roleFormClick() {
function setDef(role) {
roleSetDef(role.roleId).then(() => {
ElMessage({
message: "设置成功",
message: t('changSuccessMsg'),
type: "success",
plain: true
})
@@ -191,14 +191,14 @@ function setDef(role) {
}
function delRole(role) {
ElMessageBox.confirm(`确认删除 ${role.name} 吗?`, {
confirmButtonText: '确定',
cancelButtonText: '取消',
ElMessageBox.confirm(t('delConfirm', {msg: role.name}), {
confirmButtonText: t('confirm'),
cancelButtonText: t('confirm'),
type: 'warning'
}).then(() => {
roleDelete(role.roleId).then(() => {
ElMessage({
message: "删除成功",
message: t('copySuccessMsg'),
type: "success",
plain: true
})
@@ -228,7 +228,7 @@ function setRole() {
if (!form.name) {
ElMessage({
message: "身份名不能为空",
message: t('emptyRoleNameMsg'),
type: "error",
plain: true
})
@@ -243,7 +243,7 @@ function setRole() {
permLoading.value = true
roleSet(params).then(() => {
ElMessage({
message: "修改成功",
message: t('changSuccessMsg'),
type: "success",
plain: true
})
@@ -275,7 +275,7 @@ function resetForm() {
function openRoleSet(role) {
chooseRole = role
dialogType.title = '修改身份'
dialogType.title = t('changeRoleTitle')
dialogType.type = 'set'
roleFormShow.value = true
form.sort = role.sort
@@ -292,7 +292,7 @@ function openRoleSet(role) {
function openAddRole() {
dialogType.title = '添加身份'
dialogType.title = t('addRoleTitle')
dialogType.type = 'add'
roleFormShow.value = true
}
@@ -306,7 +306,7 @@ function addRole() {
permLoading.value = true
roleAdd(params).then(() => {
ElMessage({
message: "添加成功",
message: t('addSuccessMsg'),
type: "success",
plain: true
})
@@ -335,7 +335,7 @@ function getRoleList() {
function adjustWidth() {
desShow.value = window.innerWidth > 767
settingWidth.value = window.innerWidth < 480 ? 75 : null
settingWidth.value = window.innerWidth < 480 ? (locale.value === 'en' ? 85 : 75) : null
sortWidth.value = window.innerWidth < 480 ? 75 : null
roleWidth.value = window.innerWidth < 480 ? 180 : 200
}

View File

@@ -1,72 +1,97 @@
<template>
<div class="box">
<div class="pass">
<div class="title">账户与密码</div>
<div class="pass-item">
<div>用户名</div>
<div class="container">
<div class="title">{{$t('profile')}}</div>
<div class="item">
<div>{{$t('username')}}</div>
<div>
<span v-if="setNameShow" class="edit-name-input">
<el-input v-model="accountName" ></el-input>
<span class="edit-name" @click="setName">
保存
{{$t('save')}}
</span>
</span>
<span v-else class="user-name">
<span >{{ userStore.user.name }}</span>
<span class="edit-name" @click="showSetName">
修改
{{$t('change')}}
</span>
</span>
</div>
</div>
<div class="pass-item">
<div>邮箱</div>
<div class="item">
<div>{{$t('emailAccount')}}</div>
<div>{{ userStore.user.email }}</div>
</div>
<div class="pass-item">
<div>密码</div>
<div class="item">
<div>{{$t('password')}}</div>
<div>
<el-button type="primary" @click="pwdShow = true">修改密码</el-button>
<el-button type="primary" @click="pwdShow = true">{{$t('changePwdBtn')}}</el-button>
</div>
</div>
</div>
<div class="del-email" v-perm="'my:delete'">
<div class="title">删除账户</div>
<div style="color: #585d69;">
此操作将永久删除您的账户及其所有数据无法恢复
</div>
<div class="container lang">
<div class="title">{{$t('language')}}</div>
<div>
<el-button type="primary" @click="deleteConfirm">删除账户</el-button>
<el-select v-model="lang" placeholder="Select" style="width: 100px">
<el-option
key="zh"
label="简体中文"
value="zh"/>
<el-option
key="en"
label="English"
value="en"/>
</el-select>
</div>
</div>
<el-dialog v-model="pwdShow" title="修改密码" width="340">
<div class="del-email" v-perm="'my:delete'">
<div class="title">{{$t('deleteUser')}}</div>
<div style="color: #585d69;">
{{$t('delAccountMsg')}}
</div>
<div>
<el-button type="primary" @click="deleteConfirm">{{$t('deleteUserBtn')}}</el-button>
</div>
</div>
<el-dialog v-model="pwdShow" :title="$t('changePassword')" width="340">
<div class="update-pwd">
<el-input type="password" placeholder="新的密码" v-model="form.password"/>
<el-input type="password" placeholder="确认密码" v-model="form.newPwd"/>
<el-button type="primary" :loading="setPwdLoading" @click="submitPwd">保存</el-button>
<el-input type="password" :placeholder="$t('newPassword')" v-model="form.password"/>
<el-input type="password" :placeholder="$t('confirmPassword')" v-model="form.newPwd"/>
<el-button type="primary" :loading="setPwdLoading" @click="submitPwd">{{$t('save')}}</el-button>
</div>
</el-dialog>
</div>
</template>
<script setup>
import {defineOptions} from "vue";
import {reactive, ref} from 'vue'
import {reactive, ref, defineOptions, watch} from 'vue'
import {resetPassword, userDelete} from "@/request/my.js";
import {useUserStore} from "@/store/user.js";
import router from "@/router/index.js";
import { storeToRefs } from 'pinia'
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 settingStore = useSettingStore()
const accountStore = useAccountStore()
const userStore = useUserStore();
const setPwdLoading = ref(false)
const setNameShow = ref(false)
const accountName = ref(null)
const { lang } = storeToRefs(settingStore)
defineOptions({
name: 'setting'
})
watch(() => lang.value,() => {
window.location.reload()
})
function showSetName() {
accountName.value = userStore.user.name
setNameShow.value = true
@@ -76,7 +101,7 @@ function setName() {
if (!accountName.value) {
ElMessage({
message: '用户名不能为空',
message: t('emptyUserNameMsg'),
type: 'error',
plain: true,
})
@@ -94,7 +119,7 @@ function setName() {
accountSetName(userStore.user.accountId,name).then(() => {
ElMessage({
message: '修改成功',
message: t('changSuccessMsg'),
type: 'success',
plain: true,
})
@@ -113,16 +138,16 @@ const form = reactive({
})
const deleteConfirm = () => {
ElMessageBox.confirm('确认删除当前账号及所有数据吗?', {
confirmButtonText: '确定',
cancelButtonText: '取消',
ElMessageBox.confirm(t('delAccountConfirm'), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}).then(() => {
userDelete().then(() => {
localStorage.removeItem('token');
router.replace('/login');
ElMessage({
message: '删除成功',
message: t('delSuccessMsg'),
type: 'success',
plain: true,
})
@@ -135,7 +160,7 @@ function submitPwd() {
if (!form.password) {
ElMessage({
message: '密码不能为空',
message: t('emptyPwdMsg'),
type: 'error',
plain: true,
})
@@ -144,7 +169,7 @@ function submitPwd() {
if (form.password.length < 6) {
ElMessage({
message: '密码不能小于6位',
message: t('pwdLengthMsg'),
type: 'error',
plain: true,
})
@@ -153,7 +178,7 @@ function submitPwd() {
if (form.password !== form.newPwd) {
ElMessage({
message: '两次密码输入不一致',
message: t('confirmPwdFailMsg'),
type: 'error',
plain: true,
})
@@ -163,7 +188,7 @@ function submitPwd() {
setPwdLoading.value = true
resetPassword(form.password).then(() => {
ElMessage({
message: '修改成功',
message: t('changSuccessMsg'),
type: 'success',
plain: true,
})
@@ -197,13 +222,13 @@ function submitPwd() {
font-weight: bold;
}
.pass {
.container {
font-size: 14px;
display: grid;
gap: 20px;
margin-bottom: 40px;
.pass-item {
.item {
display: grid;
grid-template-columns: 50px 1fr;
gap: 140px;

View File

@@ -8,21 +8,21 @@
<div class="card-grid">
<!-- Website Settings Card -->
<div class="settings-card">
<div class="card-title">网站设置</div>
<div class="card-title">{{$t('websiteSetting')}}</div>
<div class="card-content">
<div class="setting-item">
<div><span>用户注册</span></div>
<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>注册码</span></div>
<div><span>{{$t('regKey')}}</span></div>
<div>
<el-select
@change="change"
style="width: 80px;"
:style="`width: ${ locale === 'en' ? 100 : 80 }px;`"
v-model="setting.regKey"
placeholder="Select"
>
@@ -36,7 +36,7 @@
</div>
</div>
<div class="setting-item">
<div><span>添加邮箱</span></div>
<div><span>{{$t('addAccount')}}</span></div>
<div>
<el-switch @change="change" :before-change="beforeChange" :active-value="0" :inactive-value="1"
v-model="setting.addEmail"/>
@@ -44,8 +44,8 @@
</div>
<div class="setting-item">
<div>
<span>多号模式</span>
<el-tooltip effect="dark" content="开启后账号栏出现一个用户可以添加多个邮箱">
<span>{{$t('multipleEmail')}}</span>
<el-tooltip effect="dark" :content="$t('multipleEmailDesc')">
<Icon class="warning" icon="fe:warning" width="18" height="18"/>
</el-tooltip>
</div>
@@ -56,8 +56,8 @@
</div>
<div class="setting-item">
<div>
<span>物理清空数据</span>
<el-tooltip effect="dark" content="该操作会物理清空所有已被删除的数据">
<span>{{$t('physicallyWipeData')}}</span>
<el-tooltip effect="dark" :content="$t('physicallyWipeDataDesc')">
<Icon class="warning" icon="fe:warning" width="18" height="18"/>
</el-tooltip>
</div>
@@ -73,10 +73,10 @@
<!-- Personalization Settings Card -->
<div class="settings-card">
<div class="card-title">个性化设置</div>
<div class="card-title">{{$t('customization')}}</div>
<div class="card-content">
<div class="setting-item">
<div class="title-item"><span>网站标题</span></div>
<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">
@@ -85,13 +85,13 @@
</div>
</div>
<div class="setting-item">
<div class="title-item"><span>登录透明</span></div>
<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>登录背景</span></div>
<div><span>{{$t('loginBackground')}}</span></div>
<div>
<el-image
class="background"
@@ -121,10 +121,10 @@
<!-- Email Sending Settings Card -->
<div class="settings-card">
<div class="card-title">邮件设置</div>
<div class="card-title">{{$t('emailSetting')}}</div>
<div class="card-content">
<div class="setting-item">
<div><span>邮件接收</span></div>
<div><span>{{$t('receiveEmails')}}</span></div>
<div>
<el-switch @change="change" :before-change="beforeChange" :active-value="0" :inactive-value="1"
v-model="setting.receive"/>
@@ -132,15 +132,15 @@
</div>
<div class="setting-item">
<div>
<span>轮询刷新</span>
<el-tooltip effect="dark" content="轮询请求服务器获取最新邮件">
<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: 80px;"
:style="`width: ${ locale === 'en' ? 100 : 80 }px;`"
v-model="setting.autoRefreshTime"
placeholder="Select"
>
@@ -154,14 +154,14 @@
</div>
</div>
<div class="setting-item">
<div><span>邮件发送</span></div>
<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>添加 Resend Token</span></div>
<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"/>
@@ -176,12 +176,12 @@
<!-- R2 Object Storage Card -->
<div class="settings-card">
<div class="card-title">R2对象存储</div>
<div class="card-title">{{$t('R2OS')}}</div>
<div class="card-content">
<div class="setting-item">
<div><span>访问域名</span></div>
<div><span>{{$t('osDomain')}}</span></div>
<div class="r2domain">
<span>{{ setting.r2Domain || '' }}</span>
<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>
@@ -191,30 +191,30 @@
</div>
<div class="settings-card">
<div class="card-title">邮件推送</div>
<div class="card-title">{{$t('emailPush')}}</div>
<div class="card-content">
<div class="setting-item">
<div><span>Telegram 机器人</span></div>
<div><span>{{$t('tgBot')}}</span></div>
<div class="forward">
<span>{{ setting.tgBotStatus === 0 ? '已开启' : '已关闭' }}</span>
<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>第三方邮箱</span></div>
<div><span>{{$t('otherEmail')}}</span></div>
<div class="forward">
<span>{{ setting.forwardStatus === 0 ? '已开启' : '已关闭' }}</span>
<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>转发规则</span></div>
<div><span>{{$t('forwardingRules')}}</span></div>
<div class="forward">
<span>{{ setting.ruleType === 0 ? '全部转发' : '规则转发' }}</span>
<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>
@@ -225,17 +225,17 @@
<!-- Turnstile Verification Card -->
<div class="settings-card">
<div class="card-title">Turnstile 人机验证</div>
<div class="card-title">{{$t('turnstileSetting')}}</div>
<div class="card-content">
<div class="setting-item">
<div><span>注册验证</span></div>
<div><span>{{$t('signUpVerification')}}</span></div>
<div>
<el-switch @change="change" :before-change="beforeChange" :active-value="0" :inactive-value="1"
v-model="setting.registerVerify"/>
</div>
</div>
<div class="setting-item">
<div><span>添加验证</span></div>
<div><span>{{$t('addEmailVerification')}}</span></div>
<div>
<el-switch @change="change" :before-change="beforeChange" :active-value="0" :inactive-value="1"
v-model="setting.addEmailVerify"/>
@@ -263,14 +263,14 @@
</div>
<div class="settings-card about">
<div class="card-title">关于</div>
<div class="card-title">{{$t('about')}}</div>
<div class="card-content">
<div class="concerning-item">
<span>版本:</span>
<span>v1.4.0</span>
<span>{{$t('version')}} :</span>
<span>v1.3.1</span>
</div>
<div class="concerning-item">
<span>交流:</span>
<span>{{$t('community')}} : </span>
<el-button @click="jump('https://t.me/cloud_mail_tg')">
telegram
<template #icon>
@@ -290,13 +290,13 @@
</div>
<!-- Dialogs remain the same -->
<el-dialog v-model="editTitleShow" title="修改标题" width="340" @closed="editTitle = ''">
<el-dialog v-model="editTitleShow" :title="$t('changeTitle')" width="340" @closed="editTitle = ''">
<form>
<el-input type="text" placeholder="网站标题" v-model="editTitle"/>
<el-button type="primary" :loading="settingLoading" @click="saveTitle">保存</el-button>
<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="添加 Resend Token" width="340" @closed="cleanResendTokenForm">
<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
@@ -306,27 +306,27 @@
:value="item"
/>
</el-select>
<el-input type="text" placeholder="输入内容添加,不填则删除" v-model="resendTokenForm.token"/>
<el-button type="primary" :loading="settingLoading" @click="saveResendToken">保存</el-button>
<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="添加R2访问域名" width="340" @closed="r2DomainInput = ''">
<el-dialog v-model="r2DomainShow" :title="$t('addOsDomain')" width="340" @closed="r2DomainInput = ''">
<form>
<el-input type="text" placeholder="R2访问域名" v-model="r2DomainInput"/>
<el-button type="primary" :loading="settingLoading" @click="saveR2domain">保存</el-button>
<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="添加 Turnstile 密钥" width="340"
<el-dialog v-model="turnstileShow" :title="$t('addTurnstileSecret')" width="340"
@closed="turnstileForm.secretKey = '';turnstileForm.siteKey = ''">
<form>
<el-input type="text" placeholder="siteKey" v-model="turnstileForm.siteKey"/>
<el-input type="text" style="margin-top: 15px" placeholder="secretKey" v-model="turnstileForm.secretKey"/>
<el-button type="primary" :loading="settingLoading" @click="saveTurnstileKey">保存</el-button>
<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="cutShow"
title="背景截图"
:title="$t('backgroundCropping')"
class="cut-dialog"
>
<div class="cropper">
@@ -343,31 +343,30 @@
></vueCropper>
</div>
<div class="cut-button">
<el-button type="primary" :loading="settingLoading" @click="saveBackground">保存</el-button>
<el-button type="primary" :loading="settingLoading" @click="saveBackground">{{$t('save')}}</el-button>
</div>
</el-dialog>
<el-dialog
v-model="tgSettingShow"
title="Telegram 机器人"
class="forward-dialog"
>
<template #header>
<div class="forward-head">
<span class="forward-set-title">Telegram 机器人</span>
<el-tooltip effect="dark" content="可以将接收的邮件转发到Tg机器人">
<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="机器人 token" v-model="tgBotToken"></el-input>
<el-input-tag tag-type="warning" placeholder="用户 chat_id 多个用,分开 12345,54321" v-model="tgChatId" @add-tag="addChatTag" ></el-input-tag>
<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="开启" inactive-text="关闭" />
<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>
@@ -378,20 +377,20 @@
>
<template #header>
<div class="forward-head">
<span class="forward-set-title">第三方邮箱</span>
<el-tooltip effect="dark" content="可以将邮件转到其他服务商邮箱需要在cloudflare验证邮箱">
<span class="forward-set-title">{{$t('otherEmail')}}</span>
<el-tooltip effect="dark" :content="$t('otherEmailDec')">
<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="多个邮箱用, 分开 example1.com,example2.com" v-model="forwardEmail" @add-tag="emailAddTag"></el-input-tag>
<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="开启" inactive-text="关闭" />
<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>
@@ -402,31 +401,31 @@
>
<template #header>
<div class="forward-head">
<span class="forward-set-title">转发规则</span>
<el-tooltip effect="dark" content="规则转发只会转发设置邮箱所接收的邮件">
<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="多个邮箱用, 分开 example1.com,example2.com" tag-type="success" v-model="ruleEmail" @add-tag="ruleEmailAddTag" />
<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" >全部转发</el-radio>
<el-radio :value="1" >规则转发</el-radio>
<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="Token 列表">
<el-dialog class="resend-table" v-model="showResendList" :title="$t('resendTokenList')">
<el-table :data="resendList" >
<el-table-column :min-width="emailColumnWidth" property="key" label="域名" :show-overflow-tooltip="true" />
<el-table-column :width="tokenColumnWidth" property="value" label="token" fixed="right" :show-overflow-tooltip="true" />
<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-scrollbar>
@@ -446,11 +445,13 @@ 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 { useI18n } from 'vue-i18n';
defineOptions({
name: 'sys-setting'
})
const { t, locale } = useI18n();
const firstLoading = ref(true)
const cropper = ref()
const cutImage = ref('')
@@ -482,13 +483,13 @@ const turnstileForm = reactive({
})
const regKeyOptions = [
{label: '开启', value: 0},
{label: '关闭', value: 1},
{label: '可选', value: 2},
{label: t('enable'), value: 0},
{label: t('disable'), value: 1},
{label: t('optional'), value: 2},
]
const options = [
{label: '关闭', value: 0},
{label: t('disable'), value: 0},
{label: '3s', value: 3},
{label: '5s', value: 5},
{label: '7s', value: 7},
@@ -658,18 +659,18 @@ const opacityChange = debounce(doOpacityChange, 1000, {
})
function physicsDeleteAllData() {
ElMessageBox.prompt('此操作不可逆转, 输入 <b style="font-weight: bold">确认删除</b> 继续操作', {
confirmButtonText: '确定',
cancelButtonText: '取消',
ElMessageBox.prompt(t('clearAllDelConfirm'), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
dangerouslyUseHTMLString: true,
title: '警告',
title: t('warning'),
type: 'warning',
inputPattern: /^确认删除$/,
inputErrorMessage: '请输入确认删除',
inputPattern: new RegExp(`^${t('delInputPattern')}$`),
inputErrorMessage: t('inputErrorMessage'),
}).then(() => {
physicsDeleteAll().then(() => {
ElMessage({
message: "删除成功",
message: t('delSuccessMsg'),
type: "success",
plain: true
})
@@ -678,9 +679,9 @@ function physicsDeleteAllData() {
}
function delBackground() {
ElMessageBox.confirm('确定要删除这个背景吗?', {
confirmButtonText: '确定',
cancelButtonText: '取消',
ElMessageBox.confirm(t('delBackgroundConfirm'), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}).then(() => {
setting.value.background = null
@@ -702,7 +703,7 @@ function saveBackground() {
setting.value.background = key
cutShow.value = false
ElMessage({
message: "设置成功",
message: t('changSuccessMsg'),
type: "success",
plain: true
})
@@ -725,14 +726,6 @@ function openCut() {
function saveR2domain() {
const settingForm = {r2Domain: r2DomainInput.value}
if (settingForm.r2Domain && !settingForm.r2Domain.startsWith('http')) {
ElMessage({
message: "域名必须以http或https开头",
type: "error",
plain: true
})
return;
}
editSetting(settingForm)
}
@@ -800,7 +793,7 @@ function editSetting(settingForm, refreshStatus = true) {
settingSet(settingForm).then(() => {
settingLoading.value = false
ElMessage({
message: "设置成功",
message: t('changSuccessMsg'),
type: "success",
plain: true
})

View File

@@ -6,15 +6,15 @@
<el-input
v-model="params.email"
class="search-input"
placeholder="输入邮箱名搜索"
:placeholder="$t('searchByEmail')"
>
</el-input>
</div>
<el-select v-model="params.status" placeholder="Select" class="status-select">
<el-option :key="-1" label="全部" :value="-1"/>
<el-option :key="0" label="正常" :value="0"/>
<el-option :key="1" label="封禁" :value="1"/>
<el-option :key="-2" label="删除" :value="-2"/>
<el-option :key="-1" :label="$t('all')" :value="-1"/>
<el-option :key="0" :label="$t('active')" :value="0"/>
<el-option :key="1" :label="$t('banned')" :value="1"/>
<el-option :key="-2" :label="$t('deleted')" :value="-2"/>
</el-select>
<Icon class="icon" icon="iconoir:search" @click="search" width="20" height="20"/>
<Icon class="icon" @click="changeTimeSort" icon="material-symbols-light:timer-arrow-down-outline"
@@ -41,93 +41,93 @@
<el-table-column :width="expandWidth" type="expand">
<template #default="props">
<div class="details">
<div v-if="!sendNumShow"><span class="details-item-title">发件数量:</span>{{ props.row.sendEmailCount }}
<div v-if="!sendNumShow"><span class="details-item-title">{{$t('tabSent')}}:</span>{{ props.row.sendEmailCount }}
</div>
<div v-if="!accountNumShow"><span class="details-item-title">邮箱数量:</span>{{
<div v-if="!accountNumShow"><span class="details-item-title">{{$t('tabMailboxes')}}:</span>{{
props.row.accountCount
}}
</div>
<div v-if="!createTimeShow"><span class="details-item-title">注册时间:</span>{{
tzDayjs(props.row.createTime).format('YYYY-MM-DD HH:mm:ss')
<div v-if="!createTimeShow"><span class="details-item-title">{{$t('tabRegisteredAt')}}:</span>{{
tzDayjs(props.row.createTime).format('YYYY-MM-DD HH:mm')
}}
</div>
<div v-if="!typeShow"><span class="details-item-title">身份类型:</span> {{ toRoleName(props.row.type) }}
<div v-if="!typeShow"><span class="details-item-title">{{$t('perm')}}:</span> {{ toRoleName(props.row.type) }}
</div>
<div v-if="!statusShow">
<span class="details-item-title">状态:</span>
<el-tag disable-transitions v-if="props.row.isDel === 1" type="info">删除</el-tag>
<el-tag disable-transitions v-else-if="props.row.status === 0" type="primary">正常</el-tag>
<el-tag disable-transitions v-else-if="props.row.status === 1" type="danger">封禁</el-tag>
<span class="details-item-title">{{$t('tabStatus')}}:</span>
<el-tag disable-transitions v-if="props.row.isDel === 1" type="info">{{$t('deleted')}}</el-tag>
<el-tag disable-transitions v-else-if="props.row.status === 0" type="primary">{{$t('active')}}</el-tag>
<el-tag disable-transitions v-else-if="props.row.status === 1" type="danger">{{$t('banned')}}</el-tag>
</div>
<div><span class="details-item-title">注册IP:</span>{{ props.row.createIp || '未知' }}</div>
<div><span class="details-item-title">近期IP:</span>{{ props.row.activeIp || '未知' }}</div>
<div><span class="details-item-title">近期活动:</span>{{
props.row.activeTime ? tzDayjs(props.row.activeTime).format('YYYY-MM-DD') : '未知'
<div><span class="details-item-title">{{$t('registrationIp')}}:</span>{{ props.row.createIp || $t('unknown') }}</div>
<div><span class="details-item-title">{{$t('recentIP')}}:</span>{{ props.row.activeIp || $t('unknown') }}</div>
<div><span class="details-item-title">{{$t('recentActivity')}}:</span>{{
props.row.activeTime ? tzDayjs(props.row.activeTime).format('YYYY-MM-DD') : $t('unknown')
}}
</div>
<div><span class="details-item-title">登录设备:</span>{{ props.row.device || '未知' }}</div>
<div><span class="details-item-title">登录系统:</span>{{ props.row.os || '未知' }}</div>
<div><span class="details-item-title">登录浏览器:</span>{{ props.row.browser || '未知' }}</div>
<div><span class="details-item-title">{{$t('loginDevice')}}:</span>{{ props.row.device || $t('unknown') }}</div>
<div><span class="details-item-title">{{$t('loginSystem')}}:</span>{{ props.row.os || $t('unknown') }}</div>
<div><span class="details-item-title">{{$t('browserLogin')}}:</span>{{ props.row.browser || $t('unknown') }}</div>
<div>
<span class="details-item-title">发件次数:</span>
<span class="details-item-title">{{$t('sendEmail')}}:</span>
<span>{{ formatSendCount(props.row) }}</span>
<el-tag style="margin-left: 10px" v-if="props.row.sendAction.hasPerm" >
{{ formatSendType(props.row) }}
</el-tag>
<el-button size="small" style="margin-left: 10px"
v-if="props.row.sendAction.hasPerm && props.row.sendAction.sendCount"
@click="resetSendCount(props.row)" type="primary">重置
@click="resetSendCount(props.row)" type="primary">{{$t('reset')}}
</el-button>
</div>
</div>
</template>
</el-table-column>
<el-table-column show-overflow-tooltip :tooltip-formatter="tableRowFormatter" label="用户邮箱" :min-width="emailWidth">
<el-table-column show-overflow-tooltip :tooltip-formatter="tableRowFormatter" :label="$t('tabEmailAddress')" :min-width="emailWidth">
<template #default="props">
<div class="email-row">{{ props.row.email }}</div>
</template>
</el-table-column>
<el-table-column :formatter="formatterReceive" label-class-name="receive" column-key="receive"
:filtered-value="filteredValue" :filters="filters" :width="receiveWidth" label="收件数量"
:filtered-value="filteredValue" :filters="filters" :width="receiveWidth" :label="$t('tabReceived')"
prop="receiveEmailCount"/>
<el-table-column :formatter="formatterSend" label-class-name="send" column-key="send"
:filtered-value="filteredValue" :filters="filters" v-if="sendNumShow" label="发件数量"
:filtered-value="filteredValue" :filters="filters" v-if="sendNumShow" :label="$t('tabSent')"
prop="sendEmailCount"/>
<el-table-column :formatter="formatterAccount" label-class-name="account" column-key="account"
:filtered-value="filteredValue" :filters="filters" v-if="accountNumShow" label="邮箱数量"
:filtered-value="filteredValue" :filters="filters" v-if="accountNumShow" :label="$t('tabMailboxes')"
prop="accountCount"/>
<el-table-column v-if="createTimeShow" label="注册时间" min-width="160" prop="createTime">
<el-table-column v-if="createTimeShow" :label="$t('tabRegisteredAt')" min-width="160" prop="createTime">
<template #default="props">
{{ tzDayjs(props.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
{{ tzDayjs(props.row.createTime).format('YYYY-MM-DD HH:mm') }}
</template>
</el-table-column>
<el-table-column v-if="statusShow" min-width="60px" label="状态" prop="status">
<el-table-column v-if="statusShow" min-width="60px" :label="$t('tabStatus')" prop="status">
<template #default="props">
<el-tag disable-transitions v-if="props.row.isDel === 1" type="info">删除</el-tag>
<el-tag disable-transitions v-else-if="props.row.status === 0" type="primary">正常</el-tag>
<el-tag disable-transitions v-else-if="props.row.status === 1" type="danger">封禁</el-tag>
<el-tag disable-transitions v-if="props.row.isDel === 1" type="info">{{$t('deleted')}}</el-tag>
<el-tag disable-transitions v-else-if="props.row.status === 0" type="primary">{{$t('active')}}</el-tag>
<el-tag disable-transitions v-else-if="props.row.status === 1" type="danger">{{$t('banned')}}</el-tag>
</template>
</el-table-column>
<el-table-column v-if="typeShow" label="身份类型" min-width="140" prop="type">
<el-table-column v-if="typeShow" :label="$t('tabRole')" min-width="140" prop="type">
<template #default="props">
<div class="type">
{{ toRoleName(props.row.type) }}
</div>
</template>
</el-table-column>
<el-table-column label="设置" :width="settingWidth">
<el-table-column :label="$t('tabSetting')" :width="settingWidth">
<template #default="props">
<el-dropdown trigger="click">
<el-button size="small" type="primary">操作</el-button>
<el-button size="small" type="primary">{{$t('action')}}</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="openSetPwd(props.row)">改密</el-dropdown-item>
<el-dropdown-item @click="openSetType(props.row)">权限</el-dropdown-item>
<el-dropdown-item @click="openSetPwd(props.row)">{{$t('chgPwd')}}</el-dropdown-item>
<el-dropdown-item @click="openSetType(props.row)">{{$t('perm')}}</el-dropdown-item>
<el-dropdown-item v-if="props.row.isDel !== 1" @click="setStatus(props.row)">
{{ setStatusName(props.row) }}
</el-dropdown-item>
<el-dropdown-item v-else @click="restore(props.row)">恢复</el-dropdown-item>
<el-dropdown-item @click="delUser(props.row)">删除</el-dropdown-item>
<el-dropdown-item v-else @click="restore(props.row)">{{$t('restore')}}</el-dropdown-item>
<el-dropdown-item @click="delUser(props.row)">{{$t('delete')}}</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
@@ -163,35 +163,35 @@
</div>
</div>
</el-scrollbar>
<el-dialog class="dialog" v-model="setPwdShow" title="修改密码" @closed="resetUserForm">
<el-dialog class="dialog" v-model="setPwdShow" :title="$t('changePassword')" @closed="resetUserForm">
<div class="dialog-box">
<el-input v-model="userForm.password" type="password" placeholder="密码" autocomplete="off">
<el-input v-model="userForm.password" type="password" :placeholder="$t('newPassword')" autocomplete="off">
</el-input>
<el-button class="btn" type="primary" :loading="settingLoading" @click="updatePwd"
>保存
>{{$t('save')}}
</el-button>
</div>
</el-dialog>
<el-dialog class="dialog" v-model="setTypeShow" title="设置权限" @closed="resetUserForm">
<el-dialog class="dialog" v-model="setTypeShow" :title="$t('changePerm')" @closed="resetUserForm">
<div class="dialog-box">
<el-input disabled model-value="超级管理员" v-if="userForm.type === 0" />
<el-input disabled :model-value="$t('admin')" v-if="userForm.type === 0" />
<el-select v-else v-model="userForm.type" placeholder="Select" >
<el-option v-for="item in roleList" :label="item.name" :value="item.roleId" :key="item.roleId"/>
</el-select>
<el-button :disabled="userForm.type === 0" class="btn" :loading="settingLoading" type="primary" @click="setType"
>保存
>{{$t('save')}}
</el-button>
</div>
</el-dialog>
<el-dialog v-model="showAdd" title="添加用户">
<el-dialog v-model="showAdd" :title="$t('addUser')">
<div class="container">
<el-input v-model="addForm.email" type="text" placeholder="邮箱" autocomplete="off">
<el-input v-model="addForm.email" type="text" :placeholder="$t('emailAccount')" autocomplete="off">
<template #append>
<div @click.stop="openSelect">
<el-select
ref="mySelect"
v-model="addForm.suffix"
placeholder="请选择"
:placeholder="$t('select')"
class="select"
>
<el-option
@@ -208,12 +208,12 @@
</div>
</template>
</el-input>
<el-input type="password" v-model="addForm.password" placeholder="密码"/>
<el-select v-model="addForm.type" placeholder="身份类型">
<el-input type="password" v-model="addForm.password" :placeholder="$t('password')"/>
<el-select v-model="addForm.type" :placeholder="$t('perm')">
<el-option v-for="item in roleList" :label="item.name" :value="item.roleId" :key="item.roleId"/>
</el-select>
<el-button class="btn" type="primary" @click="submit" :loading="addLoading"
>添加
>{{$t('add')}}
</el-button>
</div>
</el-dialog>
@@ -239,16 +239,18 @@ import {useSettingStore} from "@/store/setting.js";
import {isEmail} from "@/utils/verify-utils.js";
import {useRoleStore} from "@/store/role.js";
import {useUserStore} from "@/store/user.js";
import { useI18n } from 'vue-i18n';
defineOptions({
name: 'user'
})
const { t, locale } = useI18n();
const roleStore = useRoleStore()
const userStore = useUserStore()
const settingStore = useSettingStore()
const filteredValue = ['normal', 'del']
const filters = [{text: '正常', value: 'normal'}, {text: '删除', value: 'del'}]
const filters = [{text: t('active'), value: 'normal'}, {text: t('deleted'), value: 'del'}]
const preserveExpanded = ref(false)
const emailWidth = ref(230)
const expandWidth = ref(40)
@@ -402,9 +404,9 @@ function formatterReceive(e) {
}
function setStatusName(user) {
if (user.isDel === 1) return '恢复'
if (user.status === 0) return '禁用'
if (user.status === 1) return '启用'
if (user.isDel === 1) return t('restore')
if (user.status === 0) return t('btnBan')
if (user.status === 1) return t('enable')
}
const tableRowFormatter = (data) => {
@@ -435,7 +437,7 @@ function submit() {
if (!addForm.email) {
ElMessage({
message: "邮箱不能为空",
message: t('emptyEmailMsg'),
type: "error",
plain: true
})
@@ -444,7 +446,7 @@ function submit() {
if (!isEmail(addForm.email + addForm.suffix)) {
ElMessage({
message: "非法邮箱",
message: t('notEmailMsg'),
type: "error",
plain: true
})
@@ -453,7 +455,7 @@ function submit() {
if (!addForm.password) {
ElMessage({
message: "密码不能为空",
message: t('emptyPwdMsg'),
type: "error",
plain: true
})
@@ -462,7 +464,7 @@ function submit() {
if (addForm.password.length < 6) {
ElMessage({
message: "密码至少六位",
message: t('pwdLengthMsg'),
type: "error",
plain: true
})
@@ -471,7 +473,7 @@ function submit() {
if (!addForm.type) {
ElMessage({
message: "身份类型不能为空",
message: t('emptyRole'),
type: "error",
plain: true
})
@@ -485,7 +487,7 @@ function submit() {
addLoading.value = false
showAdd.value = false
ElMessage({
message: "添加成功",
message: t('addSuccessMsg'),
type: "success",
plain: true
})
@@ -498,27 +500,29 @@ function submit() {
function formatSendType(user) {
if (user.sendAction.sendType === 'day') return '每天'
if (user.sendAction.sendType === 'count') return '总数'
if (user.sendAction.sendType === 'day') return t('daily')
if (user.sendAction.sendType === 'count') return t('total')
}
function formatSendCount(user) {
if (!user.sendAction.hasPerm) {
return '无权限'
return t('noPerm')
}
if (!user.sendAction.sendCount) {
return '无限制';
return t('noLimit');
}
return user.sendCount + '/' + user.sendAction.sendCount + '次'
let count = user.sendCount + '/' + user.sendAction.sendCount
return count
}
function toRoleName(type) {
if (type === 0) {
return '超级管理员'
return t('admin')
}
const index = roleList.findIndex(role => role.roleId === type)
@@ -530,14 +534,14 @@ function toRoleName(type) {
function resetSendCount(user) {
ElMessageBox.confirm(`确认重置${user.email}发件次数吗?`, {
confirmButtonText: '确定',
cancelButtonText: '取消',
ElMessageBox.confirm(t('reSendConfirm',{msg: user.email}), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}).then(() => {
userRestSendCount(user.userId).then(() => {
ElMessage({
message: "重置成功",
message: t('reSuccessMsg'),
type: "success",
plain: true
})
@@ -547,14 +551,14 @@ function resetSendCount(user) {
}
function delUser(user) {
ElMessageBox.confirm(`确认删除${user.email}吗?`, {
confirmButtonText: '确定',
cancelButtonText: '取消',
ElMessageBox.confirm(t('delConfirm',{msg: user.email}), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}).then(() => {
userDelete(user.userId).then(() => {
ElMessage({
message: "删除成功",
message: t('delSuccessMsg'),
type: "success",
plain: true
})
@@ -568,16 +572,16 @@ function restore(user) {
const type = ref(0)
ElMessageBox.confirm( null, {
confirmButtonText: '确定',
cancelButtonText: '取消',
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
message: () => h('div', [
h('div', { class: 'mb-2' }, `确认要恢复 ${user.email}`),
h('div', { class: 'mb-2' }, t('restoreConfirm', {msg: user.email})),
h(ElRadioGroup, {
modelValue: type.value,
'onUpdate:modelValue': (val) => (type.value = val),
}, [
h(ElRadio, { label: 'option1', value: 0 }, '普通恢复'),
h(ElRadio, { label: 'option2', value: 1 }, '包括已删除的数据'),
h(ElRadio, { label: 'option1', value: 0 }, t('normalRestore')),
h(ElRadio, { label: 'option2', value: 1 }, t('allRestore')),
])
]),
type: 'warning'
@@ -585,7 +589,7 @@ function restore(user) {
userRestore(user.userId,type.value).then(() => {
user.isDel = 0
ElMessage({
message: "恢复成功",
message: t('restoreSuccessMsg'),
type: "success",
plain: true
})
@@ -596,9 +600,9 @@ function restore(user) {
function setStatus(user) {
if (user.status === 0) {
ElMessageBox.confirm(`确认禁用 ${user.email} 吗?`, {
confirmButtonText: '确定',
cancelButtonText: '取消',
ElMessageBox.confirm(t('banRestore',{ msg: user.email }), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}).then(() => {
httpSetStatus(user)
@@ -613,7 +617,7 @@ function httpSetStatus(user) {
userSetStatus({status: status, userId: user.userId}).then(() => {
user.status = status
ElMessage({
message: "设置成功",
message: t('changSuccessMsg'),
type: "success",
plain: true
})
@@ -626,7 +630,7 @@ function setType() {
chooseUser.type = userForm.type
setTypeShow.value = false
ElMessage({
message: "设置成功",
message: t('changSuccessMsg'),
type: "success",
plain: true
})
@@ -652,7 +656,7 @@ function updatePwd() {
if (!userForm.password) {
ElMessage({
message: '密码不能为空',
message: t('emptyPwdMsg'),
type: 'error',
plain: true,
})
@@ -661,7 +665,7 @@ function updatePwd() {
if (userForm.password.length < 6) {
ElMessage({
message: '密码最少六位',
message: t('pwdLengthMsg'),
type: 'error',
plain: true,
})
@@ -672,7 +676,7 @@ function updatePwd() {
userSetPwd({password: userForm.password, userId: userForm.userId}).then(() => {
setPwdShow.value = false
ElMessage({
message: "设置成功",
message: t('changSuccessMsg'),
type: "success",
plain: true
})
@@ -754,7 +758,7 @@ function adjustWidth() {
sendNumShow.value = width > 685
typeShow.value = width > 767
emailWidth.value = width > 480 ? 230 : null
settingWidth.value = width < 480 ? 75 : null
settingWidth.value = width < 480 ? (locale.value === 'en' ? 85 : 75) : null
expandWidth.value = width < 480 ? 25 : 40
pagerCount.value = width < 768 ? 7 : 11
receiveWidth.value = width < 480 ? 90 : null
@@ -894,7 +898,7 @@ function adjustWidth() {
}
.status-select {
width: 80px;
width: 95px;
:deep(.el-select__wrapper) {
min-height: 28px;