diff --git a/frontend/src/features/admin/views/AdminListingDetailView.vue b/frontend/src/features/admin/views/AdminListingDetailView.vue
index 59644a3..e98b51b 100644
--- a/frontend/src/features/admin/views/AdminListingDetailView.vue
+++ b/frontend/src/features/admin/views/AdminListingDetailView.vue
@@ -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) {
复制编号
-
- 返回列表
-
+ 返回列表
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 {