mirror of
https://github.com/schroinerxy/cloud-mail.git
synced 2026-06-21 19:35:50 +08:00
新增批量删除用户
This commit is contained in:
@@ -494,7 +494,7 @@ function addItem(email) {
|
||||
const existIndex = emailList.findIndex(item => item.emailId === email.emailId)
|
||||
|
||||
if (existIndex > -1) {
|
||||
return
|
||||
return false;
|
||||
}
|
||||
|
||||
email.formatText = htmlToText(email);
|
||||
@@ -511,7 +511,7 @@ function addItem(email) {
|
||||
}
|
||||
|
||||
total.value++
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -532,6 +532,7 @@ function addItem(email) {
|
||||
}
|
||||
|
||||
total.value++
|
||||
return true;
|
||||
}
|
||||
|
||||
function handleCheckAllChange(val) {
|
||||
@@ -639,7 +640,10 @@ function handleList(list) {
|
||||
5: { icon: 'bi:send-arrow-up-fill', color: '#FBBD08', content: t('delayed') },
|
||||
7: { icon: 'ic:round-mark-email-read', color: '#FBBD08', content: t('noRecipient') },
|
||||
};
|
||||
if (email.isDel) email.isdelContent = t('selectDeleted');
|
||||
|
||||
if (email.isDel) {
|
||||
email.isDelContent = t('selectDeleted');
|
||||
}
|
||||
email.statusIcon = statusIconMap[email.status];
|
||||
})
|
||||
}
|
||||
|
||||
@@ -292,7 +292,8 @@ const en = {
|
||||
to: 'To',
|
||||
clear: 'Clear',
|
||||
include: 'Include',
|
||||
delAllEmailConfirm: 'Do you really want to delete it?',
|
||||
delAllConfirm: 'Do you really want to delete it?',
|
||||
pin: 'Pin',
|
||||
s3Configuration: 'S3 Configuration',
|
||||
confirmDeletionOfContacts: 'Confirm clearing contacts?',
|
||||
recentContacts: 'Recent contacts',
|
||||
@@ -309,7 +310,9 @@ const en = {
|
||||
character: '',
|
||||
mustNotContain: 'Must Not Contain',
|
||||
mustNotContainDesc: 'Separate with commas',
|
||||
setSuccess: 'Settings saved successfully'
|
||||
setSuccess: 'Settings saved successfully',
|
||||
details: 'Details',
|
||||
userDetails: 'User Details'
|
||||
}
|
||||
|
||||
export default en
|
||||
|
||||
@@ -292,7 +292,8 @@ const zh = {
|
||||
to: '至',
|
||||
clear: '清除',
|
||||
include: '包含',
|
||||
delAllEmailConfirm: '确定要删除吗?',
|
||||
delAllConfirm: '确定要删除吗?',
|
||||
pin: '置顶',
|
||||
s3Configuration: 'S3 配置',
|
||||
confirmDeletionOfContacts: '确认清除这些联系人吗?',
|
||||
recentContacts: '最近联系人',
|
||||
@@ -309,6 +310,8 @@ const zh = {
|
||||
character: '位',
|
||||
mustNotContain: '禁止包含',
|
||||
mustNotContainDesc: '输入多个值用,分开',
|
||||
setSuccess: '设置成功'
|
||||
setSuccess: '设置成功',
|
||||
details: '详情',
|
||||
userDetails: '用户详情'
|
||||
}
|
||||
export default zh
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item v-if="hasPerm('email:send')" @click="openSetName(item)">{{ $t('rename') }}</el-dropdown-item>
|
||||
<el-dropdown-item v-if="item.accountId !== userStore.user.account.accountId" @click="setAsTop(item, index)">{{ $t('置顶') }}</el-dropdown-item>
|
||||
<el-dropdown-item v-if="item.accountId !== userStore.user.account.accountId" @click="setAsTop(item, index)">{{ $t('pin') }}</el-dropdown-item>
|
||||
<el-dropdown-item v-if="item.accountId !== userStore.user.account.accountId && hasPerm('account:delete')"
|
||||
@click="remove(item)">{{ $t('delete') }}
|
||||
</el-dropdown-item>
|
||||
|
||||
@@ -18,8 +18,8 @@ export function userSetType(params) {
|
||||
}
|
||||
|
||||
|
||||
export function userDelete(userId) {
|
||||
return http.delete('/user/delete', {params:{userId}})
|
||||
export function userDelete(userIds) {
|
||||
return http.delete('/user/delete', {params:{userIds: userIds + ''}})
|
||||
}
|
||||
|
||||
export function userAdd(form) {
|
||||
|
||||
@@ -204,7 +204,7 @@ function batchDelete() {
|
||||
}
|
||||
|
||||
ElMessageBox.confirm(
|
||||
t('delAllEmailConfirm'),
|
||||
t('delAllConfirm'),
|
||||
{
|
||||
confirmButtonText: t('confirm'),
|
||||
cancelButtonText: t('cancel'),
|
||||
|
||||
@@ -110,9 +110,9 @@ async function latest() {
|
||||
if (!existIds.has(email.emailId)) {
|
||||
|
||||
existIds.add(email.emailId)
|
||||
scroll.value.addItem(email)
|
||||
const flag = scroll.value.addItem(email)
|
||||
|
||||
if (innerWidth > 1367) {
|
||||
if (innerWidth > 1367 && flag) {
|
||||
ElNotification({
|
||||
type: 'primary',
|
||||
message: `<div style="cursor: pointer;"><div style="overflow: hidden;white-space: nowrap;text-overflow: ellipsis; font-weight: bold;font-size: 16px;margin-bottom: 5px;">${email.name}</div><div style="color: teal;">${email.subject}</div></div>`,
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<Icon class="icon" @click="changeTimeSort" icon="material-symbols-light:timer-arrow-up-outline" v-else width="28"
|
||||
height="28"/>
|
||||
<Icon class="icon" icon="ion:reload" width="18" height="18" @click="refresh"/>
|
||||
<Icon class="icon" icon="pepicons-pencil:expand" width="26" height="26" @click="changeExpand"/>
|
||||
<Icon class="icon" icon="uiw:delete" width="16" height="16" @click="delUser"/>
|
||||
</div>
|
||||
<el-scrollbar ref="scrollbarRef" class="scrollbar">
|
||||
<div>
|
||||
@@ -34,78 +34,12 @@
|
||||
<el-table
|
||||
@filter-change="tableFilter"
|
||||
:empty-text="first ? '' : null"
|
||||
:default-expand-all="expandStatus"
|
||||
:data="users"
|
||||
:preserve-expanded-content="preserveExpanded"
|
||||
style="width: 100%;"
|
||||
:key="key"
|
||||
ref="tableRef"
|
||||
>
|
||||
<el-table-column :width="expandWidth" type="expand">
|
||||
<template #default="props">
|
||||
<div class="details">
|
||||
<div v-if="props.row.username"><span class="details-item-title">LinuxDo:</span>
|
||||
<el-avatar :src="props.row.avatar" :size="30" class="linuxdo-avatar" />
|
||||
<span style="margin: 0 10px">用户名:{{props.row.username}}</span>
|
||||
<span>
|
||||
等级:<el-tag type="success">{{props.row.trustLevel}}</el-tag>
|
||||
</span>
|
||||
</div>
|
||||
<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">{{ $t('tabMailboxes') }}:</span>{{
|
||||
props.row.accountCount
|
||||
}}
|
||||
</div>
|
||||
<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">{{ $t('perm') }}:</span>
|
||||
{{ toRoleName(props.row.type) }}
|
||||
</div>
|
||||
<div v-if="!statusShow">
|
||||
<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">{{ $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">{{ $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">{{ $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">{{ $t('reset') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :width="expandWidth" type="selection" :selectable="row => row.type !== 0" />
|
||||
<el-table-column show-overflow-tooltip :tooltip-formatter="tableRowFormatter" :label="$t('tabEmailAddress')"
|
||||
:min-width="emailWidth">
|
||||
<template #default="props">
|
||||
@@ -147,11 +81,11 @@
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('tabSetting')" :width="settingWidth">
|
||||
<template #default="props">
|
||||
<el-dropdown trigger="click">
|
||||
<el-dropdown>
|
||||
<el-button size="small" type="primary">{{ $t('action') }}</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click="openSetPwd(props.row)">{{ $t('chgPwd') }}</el-dropdown-item>
|
||||
<el-dropdown-item @click="openSetPwd(props.row)" v-if="(props.row.type !== 0 || userStore.user.type === 0)">{{ $t('chgPwd') }}</el-dropdown-item>
|
||||
<el-dropdown-item @click="openSetType(props.row)">{{ $t('perm') }}</el-dropdown-item>
|
||||
<template v-if="props.row.type !== 0">
|
||||
<el-dropdown-item v-if="props.row.isDel !== 1" @click="setStatus(props.row)">
|
||||
@@ -159,8 +93,8 @@
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item v-else @click="restore(props.row)">{{ $t('restore') }}</el-dropdown-item>
|
||||
</template>
|
||||
<el-dropdown-item @click="openAccountList(props.row.userId)">{{ $t('account') }}</el-dropdown-item>
|
||||
<el-dropdown-item @click="delUser(props.row)" v-if="props.row.type !== 0">{{ $t('delete') }}</el-dropdown-item>
|
||||
<el-dropdown-item @click="openAccountList(props.row.userId)" v-if="(props.row.type !== 0 || userStore.user.type === 0)" >{{ $t('account') }}</el-dropdown-item>
|
||||
<el-dropdown-item @click="openDetails(props.row)" v-if="(props.row.type !== 0 || userStore.user.type === 0)" >{{ $t('details') }}</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
@@ -288,6 +222,70 @@
|
||||
/>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<el-dialog class="account-dialog" v-model="detailsShow" :title="t('userDetails')" >
|
||||
<div class="details">
|
||||
<div v-if="userDetails.username"><span class="details-item-title">LinuxDo:</span>
|
||||
<el-avatar :src="userDetails.avatar" :size="30" class="linuxdo-avatar" />
|
||||
<span style="margin: 0 10px">用户名:{{userDetails.username}}</span>
|
||||
<span>
|
||||
等级:<el-tag type="success">{{userDetails.trustLevel}}</el-tag>
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="!sendNumShow"><span
|
||||
class="details-item-title">{{ $t('tabSent') }}:</span>{{ userDetails.sendEmailCount }}
|
||||
</div>
|
||||
<div v-if="!accountNumShow"><span class="details-item-title">{{ $t('tabMailboxes') }}:</span>{{
|
||||
userDetails.accountCount
|
||||
}}
|
||||
</div>
|
||||
<div v-if="!createTimeShow"><span class="details-item-title">{{ $t('tabRegisteredAt') }}:</span>{{
|
||||
tzDayjs(userDetails.createTime).format('YYYY-MM-DD HH:mm')
|
||||
}}
|
||||
</div>
|
||||
<div v-if="!typeShow"><span class="details-item-title">{{ $t('perm') }}:</span>
|
||||
{{ toRoleName(userDetails.type) }}
|
||||
</div>
|
||||
<div v-if="!statusShow">
|
||||
<span class="details-item-title">{{ $t('tabStatus') }}:</span>
|
||||
<el-tag disable-transitions v-if="userDetails.isDel === 1" type="info">{{ $t('deleted') }}</el-tag>
|
||||
<el-tag disable-transitions v-else-if="userDetails.status === 0" type="primary">{{ $t('active') }}
|
||||
</el-tag>
|
||||
<el-tag disable-transitions v-else-if="userDetails.status === 1" type="danger">{{ $t('banned') }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<div><span class="details-item-title">{{ $t('registrationIp') }}:</span>{{
|
||||
userDetails.createIp || $t('unknown')
|
||||
}}
|
||||
</div>
|
||||
<div><span class="details-item-title">{{ $t('recentIP') }}:</span>{{
|
||||
userDetails.activeIp || $t('unknown')
|
||||
}}
|
||||
</div>
|
||||
<div><span class="details-item-title">{{ $t('recentActivity') }}:</span>{{
|
||||
userDetails.activeTime ? tzDayjs(userDetails.activeTime).format('YYYY-MM-DD') : $t('unknown')
|
||||
}}
|
||||
</div>
|
||||
<div><span
|
||||
class="details-item-title">{{ $t('loginDevice') }}:</span>{{ userDetails.device || $t('unknown') }}
|
||||
</div>
|
||||
<div><span class="details-item-title">{{ $t('loginSystem') }}:</span>{{ userDetails.os || $t('unknown') }}
|
||||
</div>
|
||||
<div><span
|
||||
class="details-item-title">{{ $t('browserLogin') }}:</span>{{ userDetails.browser || $t('unknown') }}
|
||||
</div>
|
||||
<div>
|
||||
<span class="details-item-title">{{ $t('sendEmail') }}:</span>
|
||||
<span>{{ formatSendCount(userDetails) }}</span>
|
||||
<el-tag style="margin-left: 10px" v-if="userDetails.sendAction.hasPerm">
|
||||
{{ formatSendType(userDetails) }}
|
||||
</el-tag>
|
||||
<el-button size="small" style="margin-left: 10px"
|
||||
v-if="userDetails.sendAction.hasPerm && userDetails.sendAction.sendCount"
|
||||
@click="resetSendCount(userDetails)" type="primary">{{ $t('reset') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -336,10 +334,12 @@ const statusShow = ref(true)
|
||||
const typeShow = ref(true)
|
||||
const receiveWidth = ref(null)
|
||||
const phonePageShow = ref(false)
|
||||
const detailsShow = ref(false);
|
||||
const layout = ref('prev, pager, next, sizes, total')
|
||||
const pageSize = ref('')
|
||||
const expandStatus = ref(false)
|
||||
const users = ref([])
|
||||
const tableRef = ref({})
|
||||
const userDetails = ref({})
|
||||
const total = ref(0)
|
||||
const first = ref(true)
|
||||
const scrollbarRef = ref(null)
|
||||
@@ -378,7 +378,6 @@ const settingLoading = ref(false)
|
||||
const tableLoading = ref(true)
|
||||
const roleList = reactive([])
|
||||
const mySelect = ref({})
|
||||
const key = ref(0)
|
||||
const accountList = reactive([])
|
||||
const accountParams = reactive({
|
||||
size: 10,
|
||||
@@ -460,6 +459,11 @@ function openAccountList(userId) {
|
||||
accountShow.value = true
|
||||
}
|
||||
|
||||
function openDetails(user) {
|
||||
userDetails.value = user;
|
||||
detailsShow.value = true;
|
||||
}
|
||||
|
||||
function getAccountList(loading = false) {
|
||||
accountLoading.value = loading
|
||||
userAllAccount(accountParams.userId,accountParams.num, accountParams.size).then(({list,total}) => {
|
||||
@@ -540,11 +544,6 @@ const tableRowFormatter = (data) => {
|
||||
return data.row.email
|
||||
}
|
||||
|
||||
function changeExpand() {
|
||||
expandStatus.value = !expandStatus.value
|
||||
key.value++
|
||||
}
|
||||
|
||||
const openSelect = () => {
|
||||
mySelect.value.toggleMenu()
|
||||
}
|
||||
@@ -679,18 +678,23 @@ function resetSendCount(user) {
|
||||
}
|
||||
|
||||
function delUser(user) {
|
||||
const rows = tableRef.value.getSelectionRows();
|
||||
const userIds = rows.map(row => row.userId);
|
||||
if (userIds.length === 0) {
|
||||
return;
|
||||
}
|
||||
ElMessageBox.confirm(t('delConfirm', {msg: user.email}), {
|
||||
confirmButtonText: t('confirm'),
|
||||
cancelButtonText: t('cancel'),
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
userDelete(user.userId).then(() => {
|
||||
userDelete(userIds).then(() => {
|
||||
ElMessage({
|
||||
message: t('delSuccessMsg'),
|
||||
type: "success",
|
||||
plain: true
|
||||
})
|
||||
getUserList(false)
|
||||
getUserList(true)
|
||||
})
|
||||
});
|
||||
}
|
||||
@@ -888,7 +892,7 @@ function adjustWidth() {
|
||||
typeShow.value = width > 767
|
||||
emailWidth.value = width > 480 ? 230 : null
|
||||
settingWidth.value = width < 480 ? (locale.value === 'en' ? 85 : 75) : null
|
||||
expandWidth.value = width < 480 ? 25 : 40
|
||||
expandWidth.value = width < 480 ? 30 : 35
|
||||
pagerCount.value = width < 768 ? 7 : 11
|
||||
receiveWidth.value = width < 480 ? 90 : null
|
||||
layout.value = width < 768 ? 'pager' : 'prev, pager, next,sizes, total'
|
||||
@@ -990,13 +994,9 @@ function adjustWidth() {
|
||||
}
|
||||
|
||||
.details {
|
||||
padding: 15px 15px 15px 52px;
|
||||
padding: 10px 10px 10px 10px;
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
@media (max-width: 767px) {
|
||||
padding-left: 35px;
|
||||
}
|
||||
|
||||
.details-item-title {
|
||||
white-space: pre;
|
||||
color: #909399;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import BizError from "../error/biz-error";
|
||||
import orm from "../entity/orm";
|
||||
import {oauth} from "../entity/oauth";
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { eq, inArray } from 'drizzle-orm';
|
||||
import userService from "./user-service";
|
||||
import loginService from "./login-service";
|
||||
import cryptoUtils from "../utils/crypto-utils";
|
||||
@@ -102,7 +102,11 @@ const oauthService = {
|
||||
},
|
||||
|
||||
async deleteByUserId(c, userId) {
|
||||
await orm(c).delete(oauth).where(eq(oauth.userId, userId)).run();
|
||||
await this.deleteByUserIds(c, [userId]);
|
||||
},
|
||||
|
||||
async deleteByUserIds(c, userIds) {
|
||||
await orm(c).delete(oauth).where(inArray(oauth.userId, userIds)).run();
|
||||
},
|
||||
|
||||
//定时任务凌晨清除未绑定邮箱的oauth用户
|
||||
|
||||
@@ -42,10 +42,12 @@ const userService = {
|
||||
user.account = account;
|
||||
user.name = account.name;
|
||||
user.permKeys = permKeys;
|
||||
user.role = roleRow
|
||||
user.role = roleRow;
|
||||
user.type = userRow.type;
|
||||
|
||||
if (c.env.admin === userRow.email) {
|
||||
user.role = constant.ADMIN_ROLE
|
||||
user.type = 0;
|
||||
}
|
||||
|
||||
return user;
|
||||
@@ -98,11 +100,11 @@ const userService = {
|
||||
},
|
||||
|
||||
async physicsDelete(c, params) {
|
||||
const { userId } = params
|
||||
await accountService.physicsDeleteByUserIds(c, [userId])
|
||||
await oauthService.deleteByUserId(c, userId);
|
||||
await orm(c).delete(user).where(eq(user.userId, userId)).run();
|
||||
await c.env.kv.delete(kvConst.AUTH_INFO + userId);
|
||||
let { userIds } = params;
|
||||
userIds = userIds.split(',').map(Number);
|
||||
await accountService.physicsDeleteByUserIds(c, userIds);
|
||||
await oauthService.deleteByUserIds(c, userIds);
|
||||
await orm(c).delete(user).where(inArray(user.userId, userIds)).run();
|
||||
},
|
||||
|
||||
async list(c, params) {
|
||||
|
||||
Reference in New Issue
Block a user