feat: add TownsPage and router configuration

- Created TownsPage.vue to display a list of towns with filtering options and a modal for details.
- Implemented a router.js file to manage application routes, including the new towns page.
This commit is contained in:
zhangyuheng
2026-03-18 14:10:49 +08:00
parent d254ec86df
commit 5c6d389962
21 changed files with 5401 additions and 449 deletions

View File

@@ -1,127 +1,307 @@
---
description: "Use when rebuilding old-html-ver pages into Vue UI components, page layouts, composables, or styles for the bailuyuan website. Covers component extraction, custom UI design, and canonical legacy pattern selection."
name: "Vue UI Migration"
applyTo: "src/**/*.vue, src/**/*.js, src/**/*.css"
description: "Use when migrating old-html-ver pages into Vue for the bailuyuan website while preserving exact legacy layout, behavior, copy, deep-link behavior, and metadata. Covers removing the temporary review page and demo data, then rebuilding each legacy page 1:1 in Vue."
name: "Vue Legacy Page Parity Migration"
applyTo: "src/**/*.vue, src/**/*.js, src/**/*.css, vite.config.js, package.json, index.html"
---
# Bailuyuan Vue UI Migration Guidelines
# Bailuyuan Vue Legacy Page Parity Migration
## Goal
- Rebuild the legacy pages in `old-html-ver/` with Vue 3 components in `src/`.
- Do not use external UI component libraries.
- Recreate the site's visual language from the legacy HTML/CSS, but normalize duplicated styles into a smaller, cleaner component system.
- When similar legacy UIs use different implementations, keep the strongest pattern and discard weaker duplicates.
- Replace the temporary Vue component review shell with the real site.
- Migrate every page under `old-html-ver/` to Vue.
- Preserve the legacy site's layout, design, copy, interaction details, responsive behavior, and externally visible navigation behavior.
- Keep the data contracts in `public/data/` and `public/stats/` intact unless a task explicitly says otherwise.
## Source Priority
## Non-Negotiable Rules
- Treat `old-html-ver/js/components.js` as the source of truth for shared layout primitives: navbar, mobile menu, page hero, footer.
- Treat `old-html-ver/css/style.css` as the source of truth for global tokens and shared layout behavior.
- Treat `old-html-ver/announcements.html`, `old-html-ver/facilities.html`, and `old-html-ver/towns.html` plus their page CSS as the canonical source for search, filter, card, badge, and detail interaction patterns.
- Treat `old-html-ver/index.html` as the source for the home-page-only bento grid and hero interaction patterns.
- Treat `old-html-ver/stats.html`, `old-html-ver/sponsor.html`, and `old-html-ver/join.html` as sources for page-specific specialized components, not as the default style baseline for shared controls.
- Migrate with exact page parity. Do not redesign, modernize, normalize, simplify, or reinterpret the old site on your own.
- Do not keep the site as a temporary single-page component review shell. A real SPA is allowed, but it must preserve user-visible page behavior and must not remove deep-linking and direct access semantics from the legacy site.
- Do not keep `src/App.vue` as a component gallery or leave `src/demoData.js` in the real page flow.
- Do not replace production-like data with mock cards, placeholder lists, or hand-written demo objects once a page is being migrated.
- Do not silently change legacy copy, labels, icon meaning, filter order, badge meaning, modal sections, CTA wording, or page information architecture.
- Do not drop SEO metadata, structured data, verification tags, canonical URLs, or iframe targets from legacy pages unless explicitly requested.
- Do not introduce external UI kits such as Element Plus, Vuetify, Naive UI, or Ant Design Vue.
## Canonical Pattern Choices
## Current Vue Baseline
- Use the announcements/facilities/towns control bar as the standard filter UI:
- card-like `controls-section`
- `search-box` with leading icon
- labeled `filter-group`
- pill `filter-tag` buttons with a strong active state
- Prefer the facilities/towns card and modal structure as the base pattern for data-detail pages.
- Keep the announcement timeline as a dedicated page pattern instead of forcing it into the facilities/towns card layout.
- Keep leaderboard cards, donation cards, and join wizard cards as specialized components that inherit shared spacing, radius, shadow, and button rules from base UI primitives.
- If a sponsor or stats page control differs from the announcements/facilities/towns version, default to the latter unless the page has a real functional need for a custom variant.
- `src/App.vue` is currently a temporary review page for shared components. It is not a valid final page and must be removed or replaced as part of real migration work.
- `src/demoData.js` is temporary inspection data. It is useful only for base component review and must not remain as the source for migrated pages.
- `src/components/` already contains reusable layout, base, shared, and detail components. Reuse them only when they can reproduce the legacy page without changing the output.
- `vite.config.js` currently has only the default single-entry setup. Future migration work may keep a single-entry SPA or use multiple entries, but the final app must reproduce legacy navigation and direct-entry behavior instead of one showcase page.
## Components To Extract First
## Source Of Truth
- Layout primitives:
- `SiteNavbar`
- `MobileNavDrawer`
- `PageHero`
- `SiteFooter`
- Base UI primitives:
- `BaseButton`
- `BaseCard`
- `BaseModal`
- `BaseBadge`
- `SearchBox`
- `FilterTagGroup`
- `EmptyState`
- `LoadMoreButton`
- Shared content components:
- `FilterPanel`
- `FacilityCard`
- `TownCard`
- `AnnouncementTimeline`
- `AnnouncementCard`
- `LeaderboardCard`
- `PlayerCard`
- `DonationCard`
- `FeatureBentoGrid`
- `FeatureBentoCard`
- `JoinWizard`
- `DeviceCard`
- `PlaystyleCard`
- Detail components:
- `FacilityDetailModal`
- `TownDetailModal`
- `PlayerDetailModal`
- `SponsorModal`
- `ModalSection`
- Shared layout and global visual system:
- `old-html-ver/js/components.js`
- `old-html-ver/css/style.css`
- Page-specific HTML structure and page head metadata:
- `old-html-ver/*.html`
- Page-specific interactions:
- `old-html-ver/js/*.js`
- Page-specific styles:
- `old-html-ver/css/pages/*.css`
- Runtime data contracts:
- `public/data/*.json`
- `public/data/*.txt`
- `public/stats/summary.json`
- `public/stats/*.json`
## Expected Component Responsibilities
## Shared Legacy Primitives To Preserve
- Shared components must be prop-driven and reusable across multiple pages.
- Page components should compose shared components instead of duplicating old HTML blocks.
- Data-driven sections should accept structured props for badges, coordinates, contributor lists, rich text blocks, media blocks, and status labels.
- Repeated filter and search logic should move into Vue composables instead of being reimplemented in each page.
- Navbar, mobile menu, and footer come from `old-html-ver/js/components.js`.
- The reusable page hero pattern comes from `old-html-ver/js/components.js` plus `old-html-ver/css/style.css`.
- The translucent fixed top navigation, 44px header offset, rounded cards, soft shadows, and hero overlays come from `old-html-ver/css/style.css`.
- Shared sponsor parsing logic comes from `old-html-ver/js/data_utils.js` and the `data/sponsors.txt` format.
## URL And Architecture Rules
- A SPA architecture is allowed.
- Multiple page entries are also allowed.
- Choose the architecture that best preserves exact legacy behavior under static hosting.
- Direct access must still work for every migrated legacy page state that users can reasonably share or bookmark.
- For announcements, facilities, and towns, deep links must open the correct page and automatically expand or open the corresponding detail item or modal.
- It is acceptable to replace legacy `.html` paths with SPA routes only if external navigation remains stable enough for users and existing shared links continue to work or are intentionally redirected.
- If `vite.config.js` changes, keep deployment and static asset loading compatible with GitHub Pages style hosting.
- Keep relative fetch paths and static hosting compatibility.
## Reuse Policy For Existing Vue Components
- `SiteNavbar`, `MobileNavDrawer`, `PageHero`, and `SiteFooter` should be aligned to the legacy navbar, mobile menu, hero, and footer behavior from `old-html-ver/js/components.js`.
- `SearchBox`, `FilterTagGroup`, `FilterPanel`, `BaseModal`, `LoadMoreButton`, and related shared components are allowed starting points, but only if the migrated page still matches the legacy page exactly.
- `FeatureBentoGrid`, `JoinWizard`, `FacilityCard`, `TownCard`, `AnnouncementTimeline`, `LeaderboardCard`, `PlayerCard`, `DonationCard`, and detail modals are starting points, not final truth. Adjust them to the old page instead of adjusting the old page to the component.
- If a legacy page needs markup or behavior that existing components cannot express exactly, change the shared component or add a page-specific wrapper instead of forcing a mismatch.
## Page Inventory And Migration Notes
### Home Page
- Source files:
- `old-html-ver/index.html`
- `old-html-ver/js/script.js`
- `old-html-ver/js/data_utils.js`
- `old-html-ver/css/style.css`
- Must preserve:
- skip link to main content
- full head metadata and structured data
- hero background, overlay, and title layout
- rotating subtitle words with the same cadence
- runtime timer from `2021-09-14T09:57:59`
- copy-to-clipboard server IP box and tooltip behavior
- live server status from `https://api.mcstatus.io/v2/status/java/mcpure.lunadeer.cn`
- online player tooltip list and offline fallback states
- bento feature grid with the same card count and hierarchy
- top sponsor section built from `data/sponsors.txt`
- crowdfunding section built from `data/fund_progress.txt`
- Migration notes:
- do not replace live status or counters with static placeholders
- keep the hidden crowdfunding section behavior that only shows when valid data exists
### Announcements Page
- Source files:
- `old-html-ver/announcements.html`
- `old-html-ver/js/announcements_script.js`
- `old-html-ver/css/pages/announcements.css`
- Data source:
- `public/data/announcements.json`
- Must preserve:
- page hero content and layout
- search input behavior
- category filter buttons and active states
- timeline layout and category-specific styling
- first card expanded by default
- click-to-expand with only one expanded card at a time
- direct link behavior that opens the matching item automatically
- share button that copies the deep link
- hidden edit mode triggered by typing `edit`
- rich content block rendering for text, image, and Bilibili video items
- Migration notes:
- keep the secret keyboard shortcut and console hint unless explicitly removed
- keep stable deep-link id generation semantics, whether implemented with hash or router state
### Facilities Page
- Source files:
- `old-html-ver/facilities.html`
- `old-html-ver/js/facilities_script.js`
- `old-html-ver/css/pages/facilities.css`
- Data source:
- `public/data/facilities.json`
- Must preserve:
- search input
- independent type and dimension filters
- facility card layout and status indicator styles
- detail modal content sections
- map link format to `https://mcmap.lunadeer.cn/`
- contributor avatar tags from Minotar
- Bilibili video block rendering in instructions and notes
- direct link behavior that auto-opens the correct facility modal
- Migration notes:
- do not flatten the modal content model into plain strings
- keep status meaning for `online`, `maintenance`, and `offline`
### Towns Page
- Source files:
- `old-html-ver/towns.html`
- `old-html-ver/js/towns_script.js`
- `old-html-ver/css/pages/towns.css`
- Data source:
- `public/data/towns.json`
- Must preserve:
- search input
- scale, type, and recruitment filters
- town card layout with logo image or gradient fallback
- icon badge meanings for scale, type, and recruitment
- detail modal structure
- founders and members sections with avatars
- coordinates secrecy behavior when `coordinatesSecret === true`
- direct link behavior that auto-opens the correct town modal
- Migration notes:
- keep gradient fallback behavior when no logo exists
- do not expose coordinates if the legacy data marks them as secret
### Stats Page
- Source files:
- `old-html-ver/stats.html`
- `old-html-ver/js/stats_script.js`
- `old-html-ver/css/pages/stats.css`
- Data sources:
- `public/stats/summary.json`
- `public/stats/*.json`
- generated by `scripts/statsprocess.py`
- Must preserve:
- updated-at text display
- six leaderboard blocks and their sort rules
- searchable player grid
- incremental load-more pagination with page size 24
- player modal summary fields
- lazy loading of per-player detail JSON when a modal opens
- accordion structure for categorized detailed stats
- search inside large accordion sections
- Migration notes:
- do not swap in `src/demoData.js` player samples
- do not hand-edit generated stats JSON files
- keep raw sort semantics for `walk_raw` and `play_time_raw`
### Sponsor Page
- Source files:
- `old-html-ver/sponsor.html`
- `old-html-ver/js/sponsor_script.js`
- `old-html-ver/css/pages/sponsor.css`
- `old-html-ver/js/data_utils.js`
- Data sources:
- `public/data/sponsors.txt`
- `public/data/fund_progress.txt` when referenced by the page
- Must preserve:
- animated cumulative total amount display
- search input
- project filter generation from real sponsor data
- sponsor grid card layout and order
- sponsor modal with separate desktop QR and mobile button views
- mobile detection behavior
- empty state and load failure fallback text
- Migration notes:
- continue parsing `data/sponsors.txt` as `name, project, amount, [date]`
- keep newest-first display order by reversing the parsed list
### Join Page
- Source files:
- `old-html-ver/join.html`
- `old-html-ver/js/join_script.js`
- `old-html-ver/css/pages/join.css`
- `old-html-ver/js/marked.min.js`
- Data source:
- `public/data/convention.md`
- Must preserve:
- four-step wizard structure
- progress indicator states
- markdown loading for the convention step
- checkbox gating for agreement
- device selection cards
- Java and Bedrock edition toggle behavior
- launcher recommendation blocks per device and edition
- previous and next button states
- final step button set and tutorial rendering flow
- Migration notes:
- keep the lazy generation of step-3 tutorial content tied to the selected device and edition
- using a markdown parser is acceptable because the legacy page already relies on one
### Doc, Map, And Photo Pages
- Source files:
- `old-html-ver/doc.html`
- `old-html-ver/map.html`
- `old-html-ver/photo.html`
- Must preserve:
- navbar only plus fullscreen iframe layout
- inline page sizing behavior with the 44px navbar offset
- external iframe targets exactly as in the legacy site
- page-specific head metadata and structured data
- Migration notes:
- do not over-engineer these pages
- do not wrap them in extra containers that change the viewport sizing behavior
## Demo Removal Rules
- Remove the temporary showcase narrative from `src/App.vue` before treating migration as complete.
- Remove imports from `src/demoData.js` from any real page entry.
- Do not leave placeholder hero copy such as component review or UI audit text in production-facing pages.
- If demo data is still needed for isolated component development, keep it out of real page entry files and public routes.
## Styling Rules
- Keep the site's existing visual direction: soft cards, rounded corners, translucent navigation, strong hero imagery, restrained shadows, and Chinese content-first spacing.
- Reuse the legacy CSS variable vocabulary where it still makes sense, but consolidate it in the Vue codebase instead of copying page CSS wholesale.
- Do not copy-paste duplicated legacy class trees unless they are the chosen canonical pattern.
- Normalize spacing, radii, shadows, and interactive states across components.
- Preserve responsive behavior from the legacy site, especially navbar/mobile menu, hero scaling, filter wrapping, grid collapse, and modal usability.
- When creating styles, prefer local component styles or clearly organized shared styles over page-specific one-off overrides.
- Preserve the legacy CSS variable language and visual rhythm from `old-html-ver/css/style.css`.
- Match legacy spacing, card density, section order, control grouping, icon usage, and breakpoint behavior.
- Preserve hover states, animation cadence, focus states, and modal open-close feel where they are visible to users.
- Avoid large global style rewrites before parity is reached.
## Architecture Rules For Vue Work
## Interaction And Data Rules
- Build shared primitives before page-specific wrappers.
- Use slots only when content structure truly varies; otherwise use typed props with clear names.
- Keep interaction state inside the component or a focused composable such as search, filtering, modal visibility, and pagination.
- Avoid direct DOM manipulation when Vue state and template bindings can express the behavior.
- Preserve current data contracts from `public/data/` and `public/stats/` unless the task explicitly includes changing them.
- Prefer Vue state and template bindings over direct DOM mutation, but keep the visible behavior identical.
- Keep fetch paths relative for local files.
- Maintain current external integrations:
- `https://api.mcstatus.io/v2/status/java/mcpure.lunadeer.cn`
- `https://minotar.net/...`
- `https://crafatar.com/...`
- Bilibili embed iframes
- `https://schema.lunadeer.cn/...`
- `https://mcmap.lunadeer.cn/`
- `https://mcphoto.lunadeer.cn/`
- Render fallback text instead of crashing when remote requests fail, matching the legacy behavior.
## Legacy To Vue Mapping
## Recommended Migration Order
- `components.js` navbar/footer/hero -> shared layout components.
- Announcements page -> `FilterPanel` + `AnnouncementTimeline` + expandable `AnnouncementCard`.
- Facilities page -> `FilterPanel` + `FacilityCard` grid + `FacilityDetailModal`.
- Towns page -> `FilterPanel` + `TownCard` grid + `TownDetailModal`.
- Stats page -> `LeaderboardCard` grid + `PlayerCard` grid + `PlayerDetailModal` + pagination controls.
- Sponsor page -> `DonationCard` grid + `SponsorModal`, while still reusing shared search and filter primitives.
- Join page -> `JoinWizard`, `ProgressStep`, `DeviceCard`, `EditionToggle`, `PlaystyleCard`.
- Home page -> `PageHero` specialization + `FeatureBentoGrid` + sponsor highlight section.
1. Replace the review shell architecture so the project can serve legacy page URLs.
2. Wire shared layout pieces to match `old-html-ver/js/components.js` and `old-html-ver/css/style.css` precisely.
3. Migrate the simple iframe pages: `doc.html`, `map.html`, `photo.html`.
4. Migrate the filter-and-modal data pages: announcements, facilities, towns.
5. Migrate sponsor and stats using the real public data files.
6. Migrate the join wizard with markdown rendering.
7. Migrate the home page with live status, timers, sponsor totals, and crowdfunding.
8. Remove any remaining demo-only entry points and data.
## What To Avoid
## Verification Checklist
- Do not introduce Element Plus, Vuetify, Naive UI, Ant Design Vue, or similar UI kits.
- Do not keep one Vue component per old HTML page section if the section is really a shared pattern.
- Do not preserve inconsistent legacy styling just because it already exists.
- Do not port legacy imperative JavaScript event wiring directly into Vue components.
- Do not silently invent a new visual language that ignores the old site structure and tone.
- Compare new and old pages side by side on desktop and mobile.
- Check that page order, section order, and copy match.
- Check that filters, searches, empty states, and modal behavior match.
- Check deep links for announcements, facilities, and towns, including direct access that auto-opens the correct item or modal.
- Check the home page timer, subtitle rotation, copy-to-clipboard behavior, and live status fallbacks.
- Check stats leaderboard sorting, player search, pagination, and lazy-loaded details.
- Check sponsor total animation, project filters, and desktop vs mobile donation modal content.
- Check join wizard gating, device selection, edition toggle, and markdown rendering.
- Check iframe pages fill the viewport correctly below the fixed navbar.
- Check no production route depends on `src/demoData.js`.
- Check head metadata, structured data, and canonical URLs are preserved for each migrated page.
## Default Implementation Bias
## When Generating New Vue Code
- If multiple legacy versions exist, choose the version that is clearer, more reusable, and visually more stable.
- For search and filtering, bias toward the announcements/facilities/towns implementation.
- For detail dialogs, bias toward the facilities/towns modal structure.
- For cards on data listing pages, bias toward the facilities/towns content density instead of the lighter sponsor/stats cards unless the page truly needs the lighter form.
## When Generating New Vue UI
- State which legacy page and which legacy pattern you are mapping from.
- List which shared component should be reused before creating a new one.
- If creating a new component, explain why an existing shared primitive is insufficient.
- Keep markup semantic and accessible: buttons for actions, labels for fields, dialog semantics for modals, keyboard-friendly navigation states.
- State the exact legacy page being migrated.
- List the specific legacy files used as the source of truth.
- Explain which existing Vue components are being reused and why they still allow exact parity.
- If adding a new component or composable, explain what legacy behavior requires it.
- Treat parity regressions as bugs, even if the Vue implementation looks cleaner internally.