mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-05 15:36:49 +08:00
fix(resource): 修复被 keep-alive 缓存的资源页在后台触发搜索并清空详情页参数的问题
- 路由查询参数监听增加前台页面判断,避免详情页携带的媒体身份参数触发后台资源搜索 - 搜索完成后仅在仍停留本页时清理地址栏参数,防止误删其他页面的 query - 补充回归测试
This commit is contained in:
@@ -931,6 +931,74 @@ describe('resource page search flow', () => {
|
|||||||
await latestEventSource(4)
|
await latestEventSource(4)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('does not run a background search when another route updates the global query while the page is cached', async () => {
|
||||||
|
const rendered = await renderResource({
|
||||||
|
path: '/resource',
|
||||||
|
query: { keyword: '前台搜索', result_type: 'torrent' },
|
||||||
|
})
|
||||||
|
const source = await latestEventSource()
|
||||||
|
finishStream(source, [createTorrent({ title: '前台结果' })])
|
||||||
|
await screen.findByText('前台结果')
|
||||||
|
expect(EventSourceFake.instances).toHaveLength(1)
|
||||||
|
|
||||||
|
// 模拟 keep-alive 缓存页在后台时,媒体详情路由携带与资源搜索一致的媒体身份参数
|
||||||
|
await rendered.router.push({
|
||||||
|
path: '/media',
|
||||||
|
query: {
|
||||||
|
media_source: 'themoviedb',
|
||||||
|
media_id: '42',
|
||||||
|
title: '媒体标题',
|
||||||
|
year: '2025',
|
||||||
|
type: '电视剧',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await flushAsyncWork()
|
||||||
|
|
||||||
|
expect(EventSourceFake.instances).toHaveLength(1)
|
||||||
|
expect(mocks.apiGet).not.toHaveBeenCalledWith(
|
||||||
|
'search/media/42',
|
||||||
|
expect.objectContaining({ params: expect.objectContaining({ media_source: 'themoviedb' }) }),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('keeps the query of other routes when a search finishes after navigating away', async () => {
|
||||||
|
const rendered = await renderResource({
|
||||||
|
path: '/resource',
|
||||||
|
query: { keyword: '待完成搜索', result_type: 'torrent' },
|
||||||
|
})
|
||||||
|
await latestEventSource()
|
||||||
|
const replaceSpy = vi.spyOn(rendered.router, 'replace')
|
||||||
|
|
||||||
|
// 搜索未完成时跳转到媒体详情页,模拟详情页路由携带媒体身份参数
|
||||||
|
await rendered.router.push({
|
||||||
|
path: '/media',
|
||||||
|
query: {
|
||||||
|
media_source: 'themoviedb',
|
||||||
|
media_id: '42',
|
||||||
|
title: '媒体标题',
|
||||||
|
year: '2025',
|
||||||
|
type: '电视剧',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await flushAsyncWork()
|
||||||
|
|
||||||
|
// 完成此刻仍处于打开状态的全部搜索流(含修复前的隐藏搜索流),
|
||||||
|
// 确保任何残留搜索都不会清理其他页面的查询参数
|
||||||
|
for (const source of EventSourceFake.instances) {
|
||||||
|
finishStream(source, [createTorrent({ title: '后台结果' })])
|
||||||
|
}
|
||||||
|
await flushAsyncWork()
|
||||||
|
|
||||||
|
expect(replaceSpy).not.toHaveBeenCalled()
|
||||||
|
expect(rendered.router.currentRoute.value.query).toEqual({
|
||||||
|
media_source: 'themoviedb',
|
||||||
|
media_id: '42',
|
||||||
|
title: '媒体标题',
|
||||||
|
year: '2025',
|
||||||
|
type: '电视剧',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it('does not let a late business failure from an old fallback replace the current empty-state message', async () => {
|
it('does not let a late business failure from an old fallback replace the current empty-state message', async () => {
|
||||||
let resolveOldRequest!: (value: unknown) => void
|
let resolveOldRequest!: (value: unknown) => void
|
||||||
mocks.apiGet.mockReturnValueOnce(
|
mocks.apiGet.mockReturnValueOnce(
|
||||||
|
|||||||
@@ -1060,6 +1060,9 @@ async function fetchData(options: { force?: boolean; params?: SearchParams; sile
|
|||||||
} else {
|
} else {
|
||||||
resetSearchResults()
|
resetSearchResults()
|
||||||
startLoadingProgress()
|
startLoadingProgress()
|
||||||
|
// 记录发起搜索时的页面路径,搜索完成后只清理本页地址栏参数,
|
||||||
|
// 避免用户搜索期间跳转其他页面(如媒体详情)时误删其 query 参数
|
||||||
|
const searchPath = route.path
|
||||||
try {
|
try {
|
||||||
await searchByStream(currentSearchParams, requestToken)
|
await searchByStream(currentSearchParams, requestToken)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -1077,7 +1080,7 @@ async function fetchData(options: { force?: boolean; params?: SearchParams; sile
|
|||||||
if (requestId !== activeSearchRequestId) return
|
if (requestId !== activeSearchRequestId) return
|
||||||
stopLoadingProgress()
|
stopLoadingProgress()
|
||||||
// 搜索完成后移除地址栏参数,避免分享/刷新残留搜索条件
|
// 搜索完成后移除地址栏参数,避免分享/刷新残留搜索条件
|
||||||
if (Object.keys(route.query).length > 0) {
|
if (route.path === searchPath && Object.keys(route.query).length > 0) {
|
||||||
await router.replace({ path: route.path, query: {} })
|
await router.replace({ path: route.path, query: {} })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1395,6 +1398,9 @@ watchEffect(() => {
|
|||||||
watch(
|
watch(
|
||||||
() => route.query,
|
() => route.query,
|
||||||
query => {
|
query => {
|
||||||
|
// 页面被 keep-alive 缓存后仍会响应全局路由变化;只有本页处于前台时才消费查询参数,
|
||||||
|
// 否则详情页等路由携带的媒体身份参数会触发后台资源搜索
|
||||||
|
if (route.path !== '/resource') return
|
||||||
if (Object.keys(query).length === 0) return
|
if (Object.keys(query).length === 0) return
|
||||||
|
|
||||||
const nextSearchParams = createSearchParams(query)
|
const nextSearchParams = createSearchParams(query)
|
||||||
|
|||||||
Reference in New Issue
Block a user