新增邮箱列表置顶排序

This commit is contained in:
eoao
2026-01-20 21:46:00 +08:00
parent ed16cc476e
commit ef002f9863
8 changed files with 94 additions and 24 deletions
@@ -227,8 +227,7 @@ let skeletonRows = 0
const timePaddingRight = ref(''); const timePaddingRight = ref('');
const keyCount = ref(0) const keyCount = ref(0)
const queryParam = reactive({ const queryParam = reactive({
emailId: 0, size: 50
size: 50,
}); });
defineExpose({ defineExpose({
@@ -507,7 +506,7 @@ function addItem(email) {
emailList.push(email); emailList.push(email);
} }
if (email.emailId > latestEmail.value.emailId) { if (email.emailId > latestEmail.value?.emailId) {
latestEmail.value = email latestEmail.value = email
} }
@@ -528,7 +527,7 @@ function addItem(email) {
} }
} }
if (email.emailId > latestEmail.value.emailId) { if (email.emailId > latestEmail.value?.emailId) {
latestEmail.value = email latestEmail.value = email
} }
@@ -566,6 +565,8 @@ function getEmailList(refresh = false) {
if (reqLock) return; if (reqLock) return;
let emailId = emailList.length > 0 ? emailList.at(-1).emailId : 0;
reqLock = true reqLock = true
if (!refresh) { if (!refresh) {
@@ -577,6 +578,7 @@ function getEmailList(refresh = false) {
} else { } else {
getSkeletonRows() getSkeletonRows()
emailId = 0
loading.value = true loading.value = true
scrollTop = 0 scrollTop = 0
} }
@@ -587,10 +589,11 @@ function getEmailList(refresh = false) {
followLoading.value = !refresh; followLoading.value = !refresh;
} }
let start = Date.now(); let start = Date.now();
props.getEmailList(queryParam.emailId, queryParam.size).then(async data => {
props.getEmailList(emailId, queryParam.size).then(async data => {
let end = Date.now(); let end = Date.now();
let duration = end - start; let duration = end - start;
if (duration < 300 && !queryParam.emailId) { if (duration < 300 && !emailId) {
await sleep(300 - duration) await sleep(300 - duration)
} }
firstLoad.value = false firstLoad.value = false
@@ -615,7 +618,6 @@ function getEmailList(refresh = false) {
followLoading.value = data.list.length >= queryParam.size; followLoading.value = data.list.length >= queryParam.size;
total.value = data.total; total.value = data.total;
queryParam.emailId = data.list.length > 0 ? data.list.at(-1).emailId : 0
}).finally(() => { }).finally(() => {
loading.value = false loading.value = false
reqLock = false reqLock = false
@@ -653,7 +655,6 @@ function refresh() {
function refreshList() { function refreshList() {
checkAll.value = false; checkAll.value = false;
isIndeterminate.value = false; isIndeterminate.value = false;
queryParam.emailId = 0;
getEmailList(true); getEmailList(true);
} }
+34 -8
View File
@@ -6,7 +6,7 @@
</div> </div>
<el-scrollbar class="scrollbar" ref="scrollbarRef"> <el-scrollbar class="scrollbar" ref="scrollbarRef">
<div v-infinite-scroll="getAccountList" :infinite-scroll-distance="600" :infinite-scroll-immediate="false"> <div v-infinite-scroll="getAccountList" :infinite-scroll-distance="600" :infinite-scroll-immediate="false">
<el-card class="item" :class="itemBg(item.accountId)" v-for="item in accounts" :key="item.accountId" <el-card class="item" :class="itemBg(item.accountId)" v-for="(item, index) in accounts" :key="item.accountId"
@click="changeAccount(item)"> @click="changeAccount(item)">
<div class="account"> <div class="account">
{{ item.email }} {{ item.email }}
@@ -24,8 +24,8 @@
<Icon icon="fluent:settings-24-filled" width="21" height="21" color="#909399"/> <Icon icon="fluent:settings-24-filled" width="21" height="21" color="#909399"/>
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item v-if="hasPerm('email:send')" @click="openSetName(item)">{{ $t('rename') }} <el-dropdown-item v-if="hasPerm('email:send')" @click="openSetName(item)">{{ $t('rename') }}</el-dropdown-item>
</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 && hasPerm('account:delete')" <el-dropdown-item v-if="item.accountId !== userStore.user.account.accountId && hasPerm('account:delete')"
@click="remove(item)">{{ $t('delete') }} @click="remove(item)">{{ $t('delete') }}
</el-dropdown-item> </el-dropdown-item>
@@ -128,7 +128,14 @@
<script setup> <script setup>
import {Icon} from "@iconify/vue"; import {Icon} from "@iconify/vue";
import {nextTick, reactive, ref, watch} from "vue"; import {nextTick, reactive, ref, watch} from "vue";
import {accountList, accountAdd, accountDelete, accountSetName, accountSetAllReceive} from "@/request/account.js"; import {
accountList,
accountAdd,
accountDelete,
accountSetName,
accountSetAllReceive,
accountSetAsTop
} from "@/request/account.js";
import {sleep} from "@/utils/time-utils.js" import {sleep} from "@/utils/time-utils.js"
import {isEmail} from "@/utils/verify-utils.js"; import {isEmail} from "@/utils/verify-utils.js";
import {useSettingStore} from "@/store/setting.js"; import {useSettingStore} from "@/store/setting.js";
@@ -169,8 +176,7 @@ const addForm = reactive({
}) })
let skeletonRows = 10 let skeletonRows = 10
const queryParams = { const queryParams = {
accountId: 0, size: 10
size: 20
} }
const mySelect = ref() const mySelect = ref()
@@ -288,6 +294,8 @@ function itemBg(accountId) {
return accountStore.currentAccountId === accountId ? 'item-choose' : '' return accountStore.currentAccountId === accountId ? 'item-choose' : ''
} }
function remove(account) { function remove(account) {
ElMessageBox.confirm(t('delConfirm', {msg: account.email}), { ElMessageBox.confirm(t('delConfirm', {msg: account.email}), {
confirmButtonText: t('confirm'), confirmButtonText: t('confirm'),
@@ -317,6 +325,7 @@ function refresh() {
followLoading.value = false followLoading.value = false
noLoading.value = false noLoading.value = false
queryParams.accountId = 0 queryParams.accountId = 0
queryParams.lastSort = null
getSkeletonRows(); getSkeletonRows();
scrollbarRef.value.setScrollTop(0) scrollbarRef.value.setScrollTop(0)
accounts.splice(0, accounts.length) accounts.splice(0, accounts.length)
@@ -335,6 +344,20 @@ function add() {
}, 100) }, 100)
} }
function setAsTop(account, index) {
accountSetAsTop(account.accountId).then(() => {
ElMessage({
message: t('setSuccess'),
type: 'success',
plain: true,
})
const [item] = accounts.splice(index, 1);
accounts.splice(1, 0, item);
});
}
async function copyAccount(account) { async function copyAccount(account) {
try { try {
await navigator.clipboard.writeText(account); await navigator.clipboard.writeText(account);
@@ -365,7 +388,10 @@ function getAccountList() {
let start = Date.now(); let start = Date.now();
accountList(queryParams.accountId, queryParams.size).then(async list => { const accountId = accounts.length > 0 ? accounts.at(-1).accountId : 0;
const lastSort = accounts.length > 0 ? accounts.at(-1).sort : null;
accountList(accountId, queryParams.size, lastSort).then(async list => {
let end = Date.now(); let end = Date.now();
let duration = end - start; let duration = end - start;
@@ -379,7 +405,7 @@ function getAccountList() {
if (accounts.length === 0) { if (accounts.length === 0) {
accountStore.currentAccount = list[0] accountStore.currentAccount = list[0]
} }
queryParams.accountId = list.at(-1).accountId
accounts.push(...list) accounts.push(...list)
loading.value = false loading.value = false
+5 -2
View File
@@ -1,7 +1,7 @@
import http from '@/axios/index.js' import http from '@/axios/index.js'
export function accountList(accountId, size) { export function accountList(accountId, size, lastSort) {
return http.get('/account/list', {params: {accountId, size}}); return http.get('/account/list', {params: {accountId, size, lastSort}});
} }
export function accountAdd(email,token) { export function accountAdd(email,token) {
@@ -20,3 +20,6 @@ export function accountSetAllReceive(accountId) {
return http.put('/account/setAllReceive', {accountId}) return http.put('/account/setAllReceive', {accountId})
} }
export function accountSetAsTop(accountId) {
return http.put('/account/setAsTop', {accountId})
}
+5
View File
@@ -27,3 +27,8 @@ app.put('/account/setAllReceive', async (c) => {
await accountService.setAllReceive(c, await c.req.json(), userContext.getUserId(c)); await accountService.setAllReceive(c, await c.req.json(), userContext.getUserId(c));
return c.json(result.ok()); return c.json(result.ok());
}); });
app.put('/account/setAsTop', async (c) => {
await accountService.setAsTop(c, await c.req.json(), userContext.getUserId(c));
return c.json(result.ok());
});
+1
View File
@@ -9,6 +9,7 @@ export const account = sqliteTable('account', {
createTime: text('create_time').default(sql`CURRENT_TIMESTAMP`), createTime: text('create_time').default(sql`CURRENT_TIMESTAMP`),
userId: integer('user_id').notNull(), userId: integer('user_id').notNull(),
allReceive: integer('all_receive').default(0).notNull(), allReceive: integer('all_receive').default(0).notNull(),
sort: integer('sort').default(0).notNull(),
isDel: integer('is_del').default(0).notNull(), isDel: integer('is_del').default(0).notNull(),
}); });
export default account export default account
+11
View File
@@ -26,10 +26,21 @@ const dbInit = {
await this.v2_5DB(c); await this.v2_5DB(c);
await this.v2_6DB(c); await this.v2_6DB(c);
await this.v2_7DB(c); await this.v2_7DB(c);
await this.v2_8DB(c);
await settingService.refresh(c); await settingService.refresh(c);
return c.text('success'); return c.text('success');
}, },
async v2_8DB(c) {
try {
await c.env.db.batch([
c.env.db.prepare(`ALTER TABLE account ADD COLUMN sort INTEGER NOT NULL DEFAULT 0;`)
]);
} catch (e) {
console.warn(`跳过字段:${e.message}`);
}
},
async v2_7DB(c) { async v2_7DB(c) {
try { try {
await c.env.db.batch([ await c.env.db.batch([
+27 -4
View File
@@ -5,7 +5,7 @@ import userService from './user-service';
import emailService from './email-service'; import emailService from './email-service';
import orm from '../entity/orm'; import orm from '../entity/orm';
import account from '../entity/account'; import account from '../entity/account';
import { and, asc, eq, gt, inArray, count, sql, ne } from 'drizzle-orm'; import { and, asc, eq, gt, inArray, count, sql, ne, or, lt, desc } from 'drizzle-orm';
import {accountConst, isDel, settingConst} from '../const/entity-const'; import {accountConst, isDel, settingConst} from '../const/entity-const';
import settingService from './setting-service'; import settingService from './setting-service';
import turnstileService from './turnstile-service'; import turnstileService from './turnstile-service';
@@ -105,10 +105,11 @@ const accountService = {
list(c, params, userId) { list(c, params, userId) {
let { accountId, size } = params; let { accountId, size, lastSort } = params;
accountId = Number(accountId); accountId = Number(accountId);
size = Number(size); size = Number(size);
lastSort = Number(lastSort);
if (size > 30) { if (size > 30) {
size = 30; size = 30;
@@ -117,12 +118,24 @@ const accountService = {
if (!accountId) { if (!accountId) {
accountId = 0; accountId = 0;
} }
if(Number.isNaN(lastSort)) {
lastSort = 9999999999;
}
return orm(c).select().from(account).where( return orm(c).select().from(account).where(
and( and(
eq(account.userId, userId), eq(account.userId, userId),
eq(account.isDel, isDel.NORMAL), eq(account.isDel, isDel.NORMAL),
gt(account.accountId, accountId))) or(
.orderBy(asc(account.accountId)) lt(account.sort, lastSort),
and(
eq(account.sort, lastSort),
gt(account.accountId, accountId)
)
))
)
.orderBy(desc(account.sort), asc(account.accountId))
.limit(size) .limit(size)
.all(); .all();
}, },
@@ -242,6 +255,16 @@ const accountService = {
} }
await orm(c).update(account).set({ allReceive: accountConst.allReceive.CLOSE }).where(eq(account.userId, userId)).run(); await orm(c).update(account).set({ allReceive: accountConst.allReceive.CLOSE }).where(eq(account.userId, userId)).run();
await orm(c).update(account).set({ allReceive: accountRow.allReceive ? 0 : 1 }).where(eq(account.accountId, accountId)).run(); await orm(c).update(account).set({ allReceive: accountRow.allReceive ? 0 : 1 }).where(eq(account.accountId, accountId)).run();
},
async setAsTop(c, params, userId) {
const { accountId } = params;
console.log(accountId);
const userRow = await userService.selectById(c, userId);
const mainAccountRow = await accountService.selectByEmailIncludeDel(c, userRow.email);
let mainSort = mainAccountRow.sort === 0 ? 2 : mainAccountRow.sort + 1;
await orm(c).update(account).set({ sort: mainSort }).where(eq(account.email, userRow.email )).run();
await orm(c).update(account).set({ sort: mainSort - 1 }).where(and(eq(account.accountId, accountId),eq(account.userId,userId))).run();
} }
}; };
+2 -2
View File
@@ -457,12 +457,12 @@ const emailService = {
) )
.where( .where(
and( and(
gt(email.emailId, emailId),
eq(email.userId, userId), eq(email.userId, userId),
eq(email.isDel, isDel.NORMAL), eq(email.isDel, isDel.NORMAL),
eq(account.isDel, isDel.NORMAL), eq(account.isDel, isDel.NORMAL),
allReceive ? eq(1,1) : eq(email.accountId, accountId), allReceive ? eq(1,1) : eq(email.accountId, accountId),
eq(email.type, emailConst.type.RECEIVE), eq(email.type, emailConst.type.RECEIVE)
gt(email.emailId, emailId)
)) ))
.orderBy(desc(email.emailId)) .orderBy(desc(email.emailId))
.limit(20); .limit(20);