feat(admin): 商品管理列表持久化筛选/页码/滚动位置,详情页返回智能回退
This commit is contained in:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user