119 lines
2.9 KiB
JavaScript
119 lines
2.9 KiB
JavaScript
// app/composables/useArticleNavContext.js
|
|
// 维护文章列表上下文(顺序、当前位置)供详情页/抽屉使用
|
|
import { computed } from 'vue'
|
|
|
|
const STORAGE_KEY = 'article:list:context'
|
|
|
|
function useStorage () {
|
|
if (process.server) return null
|
|
return window.sessionStorage
|
|
}
|
|
|
|
function readPersisted () {
|
|
try {
|
|
const store = useStorage()
|
|
if (!store) return null
|
|
const raw = store.getItem(STORAGE_KEY)
|
|
if (!raw) return null
|
|
return JSON.parse(raw)
|
|
} catch (err) {
|
|
console.warn('[ArticleNav] read persist failed', err)
|
|
return null
|
|
}
|
|
}
|
|
|
|
function writePersisted (payload) {
|
|
try {
|
|
const store = useStorage()
|
|
if (!store) return
|
|
store.setItem(STORAGE_KEY, JSON.stringify(payload))
|
|
} catch (err) {
|
|
console.warn('[ArticleNav] write persist failed', err)
|
|
}
|
|
}
|
|
|
|
function uniqSlugs (slugs = []) {
|
|
const seen = new Set()
|
|
const res = []
|
|
for (const s of slugs) {
|
|
if (!s || seen.has(s)) continue
|
|
seen.add(s)
|
|
res.push(s)
|
|
}
|
|
return res
|
|
}
|
|
|
|
export function useArticleNavContext () {
|
|
const state = useState('article:nav', () => {
|
|
// SSR 时返回空状态,客户端会在下面恢复
|
|
if (process.server) {
|
|
return {
|
|
listId: '',
|
|
slugs: [],
|
|
current: ''
|
|
}
|
|
}
|
|
// 客户端首次初始化时从 sessionStorage 恢复
|
|
return readPersisted() || {
|
|
listId: '',
|
|
slugs: [],
|
|
current: ''
|
|
}
|
|
})
|
|
|
|
// 客户端挂载时,确保从 sessionStorage 恢复状态
|
|
if (process.client) {
|
|
const persisted = readPersisted()
|
|
if (persisted && persisted.slugs && persisted.slugs.length > 0) {
|
|
// 如果 sessionStorage 中有数据,但 state 是空的,则恢复
|
|
if (!state.value.slugs || state.value.slugs.length === 0) {
|
|
console.log('[ArticleNav] 从 sessionStorage 恢复导航上下文:', persisted)
|
|
state.value = persisted
|
|
}
|
|
}
|
|
}
|
|
|
|
const index = computed(() => {
|
|
if (!state.value.current) return -1
|
|
return state.value.slugs.findIndex((s) => s === state.value.current)
|
|
})
|
|
|
|
const prevSlug = computed(() => {
|
|
const i = index.value
|
|
return i > 0 ? state.value.slugs[i - 1] : ''
|
|
})
|
|
|
|
const nextSlug = computed(() => {
|
|
const i = index.value
|
|
return i >= 0 && i < state.value.slugs.length - 1 ? state.value.slugs[i + 1] : ''
|
|
})
|
|
|
|
const hasNav = computed(() => Boolean(state.value.slugs.length))
|
|
|
|
function setList (listId, slugs) {
|
|
if (!Array.isArray(slugs)) return
|
|
state.value.listId = listId || ''
|
|
state.value.slugs = uniqSlugs(slugs.filter(Boolean))
|
|
writePersisted(state.value)
|
|
}
|
|
|
|
function setCurrent (slug) {
|
|
const s = slug || ''
|
|
state.value.current = s
|
|
if (s && !state.value.slugs.includes(s)) {
|
|
state.value.slugs = uniqSlugs([...(state.value.slugs || []), s])
|
|
}
|
|
writePersisted(state.value)
|
|
}
|
|
|
|
return {
|
|
state,
|
|
index,
|
|
prevSlug,
|
|
nextSlug,
|
|
hasNav,
|
|
setList,
|
|
setCurrent
|
|
}
|
|
}
|