修复邮件搜索并保留列表选择和同步分组
Some checks failed
Security Checks / security (push) Has been cancelled

This commit is contained in:
smail 2026-09-22 19:27:29 +08:00
parent 81fcc6a0a9
commit c9b95079a0
6 changed files with 62 additions and 16 deletions

View File

@ -5,7 +5,10 @@ import lang from '../i18n/i18n';
const useGroupStore = defineStore('group', () => {
const tag = ref("")
const name = ref(lang.inbox)
return { tag, name }
const keyword = ref("")
const revision = ref(0)
const invalidate = () => { revision.value += 1 }
return { tag, name, keyword, revision, invalidate }
})
export default useGroupStore

View File

@ -26,6 +26,8 @@ export function usePage() {
const groupList = ref([])
const isRefreshing = ref(false)
let refreshTimer = null
let requestVersion = 0
const currentPage = ref(1)
const taskTableDataRef = ref(null)
@ -49,8 +51,10 @@ export function usePage() {
}
})
watch(groupStore, async (newV) => {
tag = newV.tag;
watch([() => groupStore.tag, () => groupStore.keyword], () => {
tag = groupStore.tag;
currentPage.value = 1;
taskTableDataRef.value?.clearSelection();
if (tag === "") {
tag = '{"type":0,"status":-1}'
}
@ -63,19 +67,33 @@ export function usePage() {
const totalPage = ref(0)
const updateList = async function (silent = false) {
if (silent && selectedRows.value.length > 0) return
const version = ++requestVersion
if (!silent) {
isRefreshing.value = true
}
try {
const res = await http.post("/api/v1/email/list", {tag: tag, page_size: 15})
const res = await http.post("/api/v1/email/list", {
tag, keyword: groupStore.keyword, current_page: currentPage.value, page_size: 15
})
if (version !== requestVersion) return
if (res.errorNo !== 0) throw new Error(res.errorMsg)
const available = new Set((res.data.list || []).map(row => row.id))
selectedRows.value.filter(row => !available.has(row.id)).forEach(row => {
taskTableDataRef.value?.toggleRowSelection(row, false)
})
data.value = res.data.list || []
totalPage.value = res.data.total_page || 0
if (currentPage.value > Math.max(1, totalPage.value)) {
currentPage.value = Math.max(1, totalPage.value)
await updateList(silent)
}
} catch (err) {
if (!silent) {
if (!silent && version === requestVersion) {
ElMessage.error(err?.message || '获取邮件列表失败')
}
} finally {
if (!silent) {
if (!silent && version === requestVersion) {
isRefreshing.value = false
}
}
@ -97,6 +115,7 @@ export function usePage() {
updateList()
updateGroupList()
watch(() => groupStore.revision, updateGroupList)
// 定时刷新逻辑:已登录 60s,未登录 300s
const getRefreshInterval = () => {
@ -137,6 +156,7 @@ export function usePage() {
})
onUnmounted(() => {
requestVersion += 1
stopAutoRefreshTimer()
})
@ -237,9 +257,9 @@ export function usePage() {
}
const pageChange = function (p) {
http.post("/api/v1/email/list", {tag: tag, page_size: 15, current_page: p}).then(res => {
data.value = res.data.list || []
})
currentPage.value = p
taskTableDataRef.value?.clearSelection()
updateList()
}
const trustSender = function (senderEmail) {
@ -301,6 +321,7 @@ export function usePage() {
tag,
data,
totalPage,
currentPage,
updateList,
updateGroupList,
rowClick,

View File

@ -45,13 +45,14 @@
<el-table height="100%"
ref="taskTableDataRef"
:data="data"
row-key="id"
:show-header="false"
class="modern-mail-table"
@row-click="rowClick"
@selection-change="selectionChange"
:row-style="rowStyle"
>
<el-table-column type="selection" width="40"/>
<el-table-column type="selection" width="40" :reserve-selection="true"/>
<el-table-column width="30" class-name="status-col">
<template #default="scope">
@ -115,6 +116,7 @@
background
layout="prev, pager, next"
:page-count="totalPage"
v-model:current-page="currentPage"
@current-change="pageChange"
/>
</div>
@ -152,6 +154,7 @@ const {
rowStyle,
formatShortDate,
totalPage,
currentPage,
pageChange,
isOutbox,
trustSender,

View File

@ -1,4 +1,5 @@
import { reactive } from "vue";
import useGroupStore from '../../../stores/group';
import lang from "../../../i18n/i18n";
@ -10,6 +11,8 @@ import { Folder, Delete, Plus } from "@element-plus/icons-vue";
export function usePage() {
const data = reactive([]);
const groupStore = useGroupStore();
const saving = new WeakSet();
http.get("/api/v1/group").then((res) => {
data.push(...res.data);
@ -21,6 +24,7 @@ export function usePage() {
if (res.errorNo !== 0) {
ElMessage({ message: res.errorMsg, type: "error" });
} else {
groupStore.invalidate();
const pc = node.parent.childNodes;
for (let i = 0; i < pc.length; i++) {
if (pc[i].id === node.id) {
@ -63,18 +67,22 @@ export function usePage() {
};
const onInputBlur = function (item) {
if (item.label !== "") {
if (item.id !== -1 || saving.has(item)) return;
if (item.label.trim() !== "") {
saving.add(item);
http.post("/api/v1/group/add", { name: item.label, parent_id: item.parent_id })
.then((res) => {
if (res.errorNo !== 0) {
ElMessage({ message: res.errorMsg, type: "error" });
} else {
http.get("/api/v1/group").then((res) => {
groupStore.invalidate();
return http.get("/api/v1/group").then((res) => {
data.splice(0, data.length);
data.push(...res.data);
});
}
});
}).catch(error => ElMessage.error(error.message))
.finally(() => saving.delete(item));
}
};

View File

@ -47,7 +47,11 @@ export function usePage() {
const data = ref([]);
const searchQuery = ref("");
const searchQuery = ref(groupStore.keyword);
const submitSearch = () => {
groupStore.keyword = searchQuery.value.trim();
router.push({ name: "list" });
};
const activeGroup = ref(groupStore.tag);
@ -55,7 +59,7 @@ export function usePage() {
activeGroup.value = newVal;
});
http.get("/api/v1/group").then((res) => {
const loadGroups = () => http.get("/api/v1/group").then((res) => {
if (res.data) {
// Attempting to flatten the tree for a simpler premium menu or keep it, el-tree is less beautiful.
// For now we assume res.data is an array of items. If it's a tree, we flatten it.
@ -68,8 +72,11 @@ export function usePage() {
}
traverse(res.data);
data.value = list;
}
});
}).catch(error => ElMessage.error(error.message));
watch(() => groupStore.revision, loadGroups);
loadGroups();
const handleMenuSelect = function (index) {
const selected = data.value.find(d => d.tag === index);
@ -111,6 +118,7 @@ export function usePage() {
router,
data,
searchQuery,
submitSearch,
activeGroup,
handleMenuSelect,
openSettings

View File

@ -11,6 +11,8 @@
<div class="search-box">
<el-input
v-model="searchQuery"
@keyup.enter="submitSearch"
@clear="submitSearch"
:placeholder="lang.search"
prefix-icon="Search"
clearable
@ -63,6 +65,7 @@ import { usePage, BrandLogo, Setting, SwitchButton } from './HomeAside.js'
const {
searchQuery,
submitSearch,
lang,
activeGroup,
handleMenuSelect,