feat(admin): 商品管理列表持久化筛选/页码/滚动位置,详情页返回智能回退

This commit is contained in:
yml2213
2026-08-15 13:20:35 +08:00
parent ac47006711
commit 429ce74eff
2 changed files with 120 additions and 7 deletions
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { ArrowDown, Search } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import { computed, onMounted, reactive, ref } from 'vue'
import { computed, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'
import {
emptyListingPublishOptions,
@@ -70,6 +70,8 @@ const {
page: 1,
page_size: 10,
},
// 首次加载由 onMounted 控制:先恢复持久化的筛选/页码再查询
immediate: false,
})
const listings = computed(() => listingPage.value.items)
@@ -162,7 +164,109 @@ function resetFilters() {
void queryListings()
}
onMounted(loadPublishOptions)
// ========== 列表状态持久化 ==========
// 详情页返回/刷新后恢复筛选条件、页码与滚动位置,避免筛选丢失。
const adminListingsStateKey = 'hfb.admin.listings.state'
let stateSaveTimer: number | undefined
let scrollSaveTimer: number | undefined
function adminMainEl() {
return document.querySelector('.admin-main')
}
function adminMainScrollTop() {
return adminMainEl()?.scrollTop || 0
}
function saveListState() {
try {
window.sessionStorage.setItem(
adminListingsStateKey,
JSON.stringify({
filters: { ...filters },
page: currentPage.value,
pageSize: pageSize.value,
scrollTop: adminMainScrollTop(),
})
)
} catch {
// sessionStorage 不可用时仅损失状态恢复
}
}
function restoreListState() {
try {
const raw = window.sessionStorage.getItem(adminListingsStateKey)
if (!raw) return
const state = JSON.parse(raw) as {
filters?: AdminListingQuery
page?: number
pageSize?: number
}
if (state.filters && typeof state.filters === 'object') {
Object.assign(filters, state.filters)
}
if (typeof state.page === 'number' && state.page > 0) {
currentPage.value = state.page
}
if (
typeof state.pageSize === 'number' &&
[10, 20, 50, 100].includes(state.pageSize)
) {
pageSize.value = state.pageSize
}
} catch {
// 损坏的状态忽略,按默认条件加载
}
}
function restoreListScroll() {
try {
const raw = window.sessionStorage.getItem(adminListingsStateKey)
if (!raw) return
const state = JSON.parse(raw) as { scrollTop?: number }
const top = Number(state.scrollTop)
if (!Number.isFinite(top) || top <= 0) return
requestAnimationFrame(() => {
requestAnimationFrame(() => {
const main = adminMainEl()
if (main) main.scrollTop = top
})
})
} catch {
// 忽略
}
}
function handleAdminMainScroll() {
if (scrollSaveTimer !== undefined) window.clearTimeout(scrollSaveTimer)
scrollSaveTimer = window.setTimeout(saveListState, 300)
}
// 筛选/页码变化时防抖保存;滚动位置随每次保存一起写入
watch(
[filters, currentPage, pageSize],
() => {
if (stateSaveTimer !== undefined) window.clearTimeout(stateSaveTimer)
stateSaveTimer = window.setTimeout(saveListState, 200)
},
{ deep: true }
)
onMounted(async () => {
restoreListState()
void loadPublishOptions()
await loadListings()
restoreListScroll()
adminMainEl()?.addEventListener('scroll', handleAdminMainScroll, { passive: true })
})
onBeforeUnmount(() => {
saveListState()
adminMainEl()?.removeEventListener('scroll', handleAdminMainScroll)
if (stateSaveTimer !== undefined) window.clearTimeout(stateSaveTimer)
if (scrollSaveTimer !== undefined) window.clearTimeout(scrollSaveTimer)
})
async function loadPublishOptions() {
try {