docs: add frontend routing design

This commit is contained in:
2026-06-16 07:57:22 -06:00
parent dcec78d4b7
commit 19cc1ffdfa

View File

@@ -0,0 +1,80 @@
# Frontend Routing Design
## Goal
Add URL-backed frontend routing so users can refresh or directly open the main app views without losing their place.
## Scope
In scope:
- Add routes for login, book list, book workspace, and admin user management.
- Keep the existing component structure: `LoginPage`, `BookListPage`, `WorkspaceView`, and `AdminPage`.
- Preserve the existing auth behavior and API calls.
- Preserve the existing backend API routes and static SPA fallback.
- Add focused tests for routing behavior.
Out of scope:
- Adding nested lesson-level URLs.
- Adding a new navigation layout.
- Replacing the current auth model.
- Adding a third-party router package unless the existing code makes a local router impractical.
## Approaches Considered
The recommended approach is a small local router built around `window.history` and `popstate`. The project does not currently use `vue-router`, and the app only needs four top-level route states. A local router keeps the change small and avoids a new dependency.
A second option is adding `vue-router`. It would be more conventional for a growing Vue app, but it adds dependency and setup overhead for a narrow routing surface.
A third option is hash routing, such as `/#/books/b1`. It avoids server fallback concerns, but the server already serves `dist/index.html` for unknown paths, so clean history URLs are a better fit.
## Routes
The frontend will support these clean URLs:
- `/login` shows `LoginPage`.
- `/books` shows `BookListPage`.
- `/books/:bookId` shows `WorkspaceView` for the selected book.
- `/admin` shows `AdminPage`.
Unknown paths redirect to the best available default: `/books` when logged in and `/login` when logged out.
## Auth Behavior
`App.vue` still calls `fetchMe()` on mount. While logged out, any route except `/login` resolves to the login page and updates the URL to `/login`.
After login succeeds, the app routes to `/books`. Logout continues to clear tokens through `useAuth`; when the app observes the logged-out state, it routes to `/login`.
The admin page remains visible only through the existing admin entry point in `BookListPage`. If a non-admin user reaches `/admin` directly, the backend admin API will still return authorization errors. The frontend may render the page shell, but protected data will not load.
## Component Behavior
`BookListPage` keeps emitting `open` and `admin`. `App.vue` will translate those events into route changes:
- `open(id)` navigates to `/books/{id}`.
- `admin` navigates to `/admin`.
`WorkspaceView` keeps emitting `back`; `App.vue` maps it to `/books`.
`AdminPage` keeps emitting `back`; `App.vue` maps it to `/books`.
This preserves component contracts and confines route ownership to the app shell.
## Error Handling
Book load errors remain handled by `WorkspaceView`. Its existing "返回列表" action navigates back to `/books`.
Route parsing should be strict enough to avoid invalid view state. Empty or malformed book IDs fall back to `/books`.
## Testing
Add or update `App.test.ts` coverage for:
- Starting at `/books` renders the book list.
- Opening a book updates the URL to `/books/:bookId` and renders the workspace.
- Pressing workspace back updates the URL to `/books`.
- Opening admin updates the URL to `/admin`.
- Logged-out users are routed to `/login`.
Run the focused app tests and the project build after implementation.