Files
vmall/openspec/changes/add-mobile-h5/specs/frontend-mobile/spec.md
T

7.4 KiB

ADDED Requirements

Requirement: Mobile page inventory and navigation structure

The apps/mobile H5 app SHALL provide the migrated tigshop information architecture as Nuxt routes: home /, category browse /category, search /search with results /search/result, product detail /product/:id, cart /cart, checkout /checkout, order list /orders, order detail /orders/:id, coupon center /coupon, wallet /wallet, "My" center /user, and sign-in /login with register /register. Every listed route SHALL be reachable through navigation from the tab bar, the "My" center, or in-flow links, and no route SHALL depend on mini-program-only capabilities.

Scenario: walk the browse-to-buy path

  • WHEN a mobile shopper opens /, drills into /category, opens /search/result, selects a product, adds it to the cart, checks out, and then opens /orders/:id
  • THEN each route renders its documented surface from the shared API client and the created order is visible in the order list and detail

Scenario: user-center entries resolve

  • WHEN a shopper opens /user and taps the coupon, wallet, order, or after-sale entry
  • THEN each entry navigates to its documented route and no entry points at an unbuilt placeholder route

Requirement: Custom tab bar and safe-area shell

The app SHALL render a custom fixed bottom tab bar with exactly four destinations — Home, Category, Cart, My (首页/分类/购物车/我的) mapped to /, /category, /cart, /user — with the active tab indicated by the shared brand token. Tab bar and page chrome SHALL pad with env(safe-area-inset-bottom) and env(safe-area-inset-top) so content is never hidden behind the home indicator or status bar. Routes outside the four tab routes SHALL replace the tab bar with a top back-bar instead.

Scenario: tab bar switches root sections

  • WHEN a shopper taps a tab bar item
  • THEN the matching root route renders with that tab shown active and the tab bar stays fixed above the safe area

Scenario: content clears the home indicator

  • WHEN a shopper opens the cart on a device with a non-zero bottom safe-area inset
  • THEN the last cart line and the tab bar remain fully visible above the inset

Requirement: Shared-contract-only data access

The app SHALL obtain every piece of backend data through the @vmall/shared API client and its existing adapter selection (live backend or fixed-data fallback) and SHALL NOT define its own fetch wrapper, endpoint paths, or response types. Styling SHALL consume @vmall/shared/theme.css tokens rather than a private global stylesheet. Money SHALL be handled as integer minor units with shared formatting helpers and bilingual content SHALL render the active locale from {en, zh} localized text.

Scenario: no private API layer

  • WHEN a developer inspects apps/mobile
  • THEN all backend calls go through @vmall/shared client methods and no page defines its own HTTP request to an API path

Scenario: prices render from minor units

  • WHEN any surface renders a price
  • THEN the amount derives from integer minor units through shared formatting with no floating-point money arithmetic

Requirement: Mobile interaction conventions

Home, search results, and order list SHALL support pull-to-refresh reloading through the shared client. Actions requiring authentication SHALL redirect a signed-out shopper to /login and return them to the interrupted route after sign-in. In-flight mutations (add to cart, place order, claim coupon) SHALL be guarded against duplicate submission.

Scenario: pull to refresh home

  • WHEN a shopper pulls down on the home page
  • THEN home content floors reload from the shared client and the refreshed state renders

Scenario: anonymous add returns to the product

  • WHEN a signed-out shopper adds a product to the cart from /product/:id and then signs in
  • THEN they land back on that product route

Requirement: Mall-identical session and token contract

The app SHALL persist the session exactly as the mall does — AuthTokens.token in localStorage["vmall.token"] and the user in localStorage["vmall.user"] — and SHALL send it as a Bearer header via the shared client's getToken. A restored token SHALL be validated against the auth API on load and a rejected token SHALL clear the stored session and route the shopper to /login, matching mall semantics so one sign-in serves both apps on the same origin storage.

Scenario: token validated on load

  • WHEN a shopper with a stored token reopens the app
  • THEN the session is validated through the shared auth API and the "My" center renders the authenticated user

Scenario: rejected token clears storage

  • WHEN a restored token is rejected by the auth API
  • THEN vmall.token and vmall.user are cleared and the shopper is routed to /login

Requirement: Transaction and account surfaces on existing APIs

Cart, checkout, order list and order detail SHALL read and write backend state exclusively through shared client cart, checkout, order and address methods, so carts and orders survive reloads and match the mall's data. Checkout SHALL offer the customer's saved addresses (defaulting to the default address) with an inline manual address form when none exist, and SHALL present eligible owned coupons per shop order. Order detail SHALL expose the appropriate status actions (pay, cancel, confirm delivery) through the shared client's guarded status transitions. Coupon center SHALL list claimable shop coupons, claim them, and show the customer's owned coupons through the shared client.

Scenario: cart survives reload

  • WHEN a signed-in shopper adds an item and reloads /cart
  • THEN the line is still present from backend state

Scenario: claim from the coupon center

  • WHEN a signed-in shopper claims a coupon with remaining stock from /coupon
  • THEN it appears in the owned-coupon list without any fixture import

Requirement: Dependency-gated after-sale and wallet surfaces

The after-sale entry SHALL expose the customer's after-sale requests through the aftersale shared-client API from add-aftersale-refunds, and the wallet page SHALL render the customer's balances and ledger entries through the wallet shared-client API from add-wallet-settlement. While either change is not yet archived, the corresponding surface SHALL render a localized "Coming soon" placeholder instead of broken or fixture-backed behavior, and SHALL be enabled without further design change once its dependency lands.

Scenario: wallet before its dependency lands

  • WHEN add-wallet-settlement is not yet archived and a shopper opens /wallet
  • THEN a localized "Coming soon" placeholder renders instead of an error

Scenario: after-sale after its dependency lands

  • WHEN add-aftersale-refunds is archived and a shopper opens the after-sale entry from /orders/:id
  • THEN after-sale data renders from the aftersale shared-client API

Requirement: Reachable mobile entry from the mall shell

The mall top bar's "Mobile" (手机端) entry SHALL resolve to the H5 app's home page instead of the current dead / link, so the desktop storefront is the discoverable entry point for the mobile client.

  • WHEN a desktop shopper clicks the mall top bar's Mobile entry
  • THEN the apps/mobile home page opens instead of reloading the mall home