feat(admin): 商品管理列表持久化筛选/页码/滚动位置,详情页返回智能回退
This commit is contained in:
@@ -3,12 +3,12 @@ import { readError } from '@/shared/utils/error'
|
||||
import { CopyDocument, EditPen, Search } from '@element-plus/icons-vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
import { fetchAdminUsers, type AdminUserItem } from '@/features/admin/api/adminUsers'
|
||||
import { fetchAdminFileBlob } from '@/shared/api/files'
|
||||
import AuthImage from '@/shared/components/business/AuthImage.vue'
|
||||
import { adminPath } from '@/shared/utils/adminPath'
|
||||
import { adminPath, isAdminPath } from '@/shared/utils/adminPath'
|
||||
import {
|
||||
centToYuan,
|
||||
formatCentWithSymbol,
|
||||
@@ -59,6 +59,17 @@ import { calculatePlatformPricing, roundRatio as roundPricingRatio } from '@/sha
|
||||
const adminSession = useAdminSessionStore()
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
// 返回列表:有站内后台历史则回退(保留列表页筛选与滚动恢复),否则直接回列表页。
|
||||
function goBackToList() {
|
||||
const back = (window.history.state as { back?: string | null } | null)?.back
|
||||
if (back && isAdminPath(back)) {
|
||||
router.back()
|
||||
return
|
||||
}
|
||||
void router.push(adminPath('listings'))
|
||||
}
|
||||
const loading = ref(false)
|
||||
const submitting = ref(false)
|
||||
const userSearching = ref(false)
|
||||
@@ -596,9 +607,7 @@ async function openScreenshot(url: string) {
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<el-button :icon="CopyDocument" @click="copyListingCode">复制编号</el-button>
|
||||
<RouterLink :to="adminPath('listings')">
|
||||
<el-button>返回列表</el-button>
|
||||
</RouterLink>
|
||||
<el-button @click="goBackToList">返回列表</el-button>
|
||||
<el-button
|
||||
v-if="canAdjustExternal"
|
||||
type="primary"
|
||||
|
||||
@@ -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