fix: 复习详情返回按钮改为返回总览
This commit is contained in:
@@ -82,4 +82,18 @@ test.describe('Review page', () => {
|
||||
await expect(page.getByRole('button', { name: '重新加载' })).toBeVisible();
|
||||
await expect(page.getByText('暂无复习任务')).toHaveCount(0);
|
||||
});
|
||||
|
||||
test('详情页返回按钮为「返回总览」并回到复习总览', async ({ page }) => {
|
||||
await page.route('**/api/review/task/**', (route) =>
|
||||
route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ code: 200, data: [] }) }),
|
||||
);
|
||||
|
||||
await page.locator('.task-card').first().click();
|
||||
await expect(page).toHaveURL(/\/review\/detail\/task\/T001/, { timeout: 5000 });
|
||||
|
||||
const backBtn = page.locator('.head .back-btn');
|
||||
await expect(backBtn).toHaveText('← 返回总览');
|
||||
await backBtn.click();
|
||||
await expect(page).toHaveURL(/\/review$/, { timeout: 5000 });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,6 +22,13 @@ async function mountAt(path: string) {
|
||||
})
|
||||
}
|
||||
|
||||
// 真实挂载:shallow 会存根化 el-button 且不渲染插槽,无法断言按钮文案与点击行为
|
||||
async function mountRealAt(path: string) {
|
||||
await router.push(path)
|
||||
await router.isReady()
|
||||
return mount(MyHead, { global: { plugins: [ElementPlus, router] } })
|
||||
}
|
||||
|
||||
describe('MyHead.vue', () => {
|
||||
beforeEach(async () => {
|
||||
vi.clearAllMocks()
|
||||
@@ -30,6 +37,49 @@ describe('MyHead.vue', () => {
|
||||
await router.isReady()
|
||||
})
|
||||
|
||||
it('默认返回首页,复习详情按路由 meta 返回总览', async () => {
|
||||
localStorage.setItem('isLoggedIn', 'true')
|
||||
|
||||
const studyWrapper = await mountRealAt('/study')
|
||||
expect(studyWrapper.find('.back-btn').text()).toBe('← 返回首页')
|
||||
await studyWrapper.find('.back-btn').trigger('click')
|
||||
await vi.waitFor(() => expect(router.currentRoute.value.path).toBe('/welcome'))
|
||||
studyWrapper.unmount()
|
||||
|
||||
const taskDetailWrapper = await mountRealAt('/review/detail/task/T1')
|
||||
expect(taskDetailWrapper.find('.back-btn').text()).toBe('← 返回总览')
|
||||
await taskDetailWrapper.find('.back-btn').trigger('click')
|
||||
await vi.waitFor(() => expect(router.currentRoute.value.path).toBe('/review'))
|
||||
taskDetailWrapper.unmount()
|
||||
})
|
||||
|
||||
it('多入口详情页优先返回来路,无历史时兜底到上级', async () => {
|
||||
localStorage.setItem('isLoggedIn', 'true')
|
||||
const backSpy = vi.spyOn(router, 'back').mockImplementation(() => {})
|
||||
|
||||
// 从回忆复习页进入:存在历史,回退到实际来路
|
||||
await mountRealAt('/review/recall/T1')
|
||||
const fromRecall = await mountRealAt('/review/detail/report/9')
|
||||
expect(fromRecall.find('.back-btn').text()).toBe('← 返回上一页')
|
||||
await fromRecall.find('.back-btn').trigger('click')
|
||||
expect(backSpy).toHaveBeenCalledTimes(1)
|
||||
fromRecall.unmount()
|
||||
|
||||
// 直接深链或刷新进入:没有历史,回退到该页声明的上级
|
||||
window.history.replaceState(
|
||||
{ back: null, current: '/review/detail/report/9', forward: null, position: 0 },
|
||||
'',
|
||||
)
|
||||
const deepLink = mount(MyHead, { global: { plugins: [ElementPlus, router] } })
|
||||
expect(deepLink.find('.back-btn').text()).toBe('← 返回上一页')
|
||||
await deepLink.find('.back-btn').trigger('click')
|
||||
expect(backSpy).toHaveBeenCalledTimes(1)
|
||||
await vi.waitFor(() => expect(router.currentRoute.value.path).toBe('/review'))
|
||||
deepLink.unmount()
|
||||
|
||||
backSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('derives route-based computed properties from real route', async () => {
|
||||
const loginWrapper = await mountAt('/login')
|
||||
expect(loginWrapper.vm.isLoginRoute).toBe(true)
|
||||
@@ -46,8 +96,7 @@ describe('MyHead.vue', () => {
|
||||
expect(welcomeWrapper.vm.pageTitle).toBeUndefined()
|
||||
})
|
||||
|
||||
it('clears login state and navigates to login on logout', async () => {
|
||||
vi.mocked(logout).mockResolvedValue({ code: 200 } as any)
|
||||
it('clears login state and navigates to login on logout', async () => { vi.mocked(logout).mockResolvedValue({ code: 200 } as any)
|
||||
localStorage.setItem('isLoggedIn', 'true')
|
||||
const wrapper = await mountAt('/study')
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
v-if="showBackButton"
|
||||
text
|
||||
class="back-btn"
|
||||
@click="router.push('/welcome')"
|
||||
@click="goBack"
|
||||
>
|
||||
← 返回首页
|
||||
← {{ backLabel }}
|
||||
</el-button>
|
||||
<template v-if="pageTitle">
|
||||
<h1 class="head-title">{{ pageTitle }}</h1>
|
||||
@@ -39,8 +39,21 @@ const route = useRoute();
|
||||
const isLoginRoute = computed(() => route.path === "/login");
|
||||
const showBackButton = computed(() => route.path !== "/login" && route.path !== "/welcome");
|
||||
const pageTitle = computed(() => route.meta?.title as string | undefined);
|
||||
// 返回目标由路由 meta 声明,未声明时回首页
|
||||
const backTo = computed(() => (route.meta?.backTo as string | undefined) || "/welcome");
|
||||
const backLabel = computed(() => (route.meta?.backLabel as string | undefined) || "返回首页");
|
||||
// 有多个入口的页面(如复习详情)优先回到用户来路,无历史时回退到声明的上级
|
||||
const backUseHistory = computed(() => route.meta?.backUseHistory === true);
|
||||
const loggingOut = ref(false);
|
||||
|
||||
const goBack = () => {
|
||||
if (backUseHistory.value && window.history.state?.back) {
|
||||
router.back();
|
||||
return;
|
||||
}
|
||||
router.push(backTo.value);
|
||||
};
|
||||
|
||||
const handleLogout = async () => {
|
||||
if (loggingOut.value) return;
|
||||
loggingOut.value = true;
|
||||
@@ -58,7 +71,7 @@ const handleLogout = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
defineExpose({ isLoginRoute, showBackButton, pageTitle, loggingOut, handleLogout });
|
||||
defineExpose({ isLoginRoute, showBackButton, pageTitle, backTo, backLabel, loggingOut, goBack, handleLogout });
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
+8
-2
@@ -30,12 +30,18 @@ const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: "/review/detail/task/:taskNum",
|
||||
component: () => import("@/components/ReviewDetail.vue"),
|
||||
meta: { requiresAuth: true, title: "复习详情" },
|
||||
meta: { requiresAuth: true, title: "复习详情", backTo: "/review", backLabel: "返回总览" },
|
||||
},
|
||||
{
|
||||
path: "/review/detail/:type/:id",
|
||||
component: () => import("@/components/ReviewDetail.vue"),
|
||||
meta: { requiresAuth: true, title: "复习详情" },
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
title: "复习详情",
|
||||
backTo: "/review",
|
||||
backLabel: "返回上一页",
|
||||
backUseHistory: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/review/recall/:taskNum",
|
||||
|
||||
Reference in New Issue
Block a user