Files
Chengdong Zhang 21e99bb52b feat(mall): classic B2B2C PC storefront with fixed mock API layer
- Mock adapter implementing @vmall/shared ApiClient (localStorage-persisted
  cart/orders/invoices), runtime switch via mockApi flag (default on)
- Fixed bilingual mock catalog: 3-level categories, 24 products w/ SKUs,
  stores, brands, banners, floors, seckill/collective/integral, comments,
  coupons, addresses, seeded orders/shipments/invoices
- B2B2C mall shell: top bar, logo/search/cart header, dark nav + category
  mega-menu, value-prop footer, back-to-top; 1200px grid, #ca151e theme
- UI primitives replacing element-plus: carousel, pagination, breadcrumb,
  qty stepper, rating stars, modal, tabs, step bar, product card
- Pages: home floors, search (filters/sort/paging), goods detail (SKU
  picker, store rail, review tabs), cart -> checkout -> pay -> success,
  auth pages, user center (dashboard/orders/addresses/favorites/coupons/
  invoices), stores, seckill, collective, integral
- i18n split into per-domain locale modules (en+zh)
- OpenSpec change mall-pc-storefront-replica archived; all specs green
2026-09-17 19:13:29 +08:00

40 lines
4.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Agent contracts — mall-pc-storefront-replica
Read this whole file plus `proposal.md` and `design-reference.md` before editing.
## Hard rules
- `apps/mall` only. NEVER edit `packages/shared`, other apps, or backend.
- TS strict: no `any` / `as any` / `@ts-ignore`. Vue 3 `<script setup lang="ts">`.
- All UI copy via `$t()` / `t()`; keys live ONLY in your assigned `apps/mall/locales/<domain>.ts` file (shape: `export default { en: { <domain>: {...} }, zh: { <domain>: {...} } }`). Extend the existing structure; both en and zh always.
- Mock content fields are `LocalizedText` (`{en, zh}`); render with `t as pick` from `@vmall/shared`: `pick(text, locale)` where `const { locale } = useI18n()`.
- Money: integer minor units. Render prices ONLY via `<PriceText :amount-minor="n" currency="USD" />` (auto-imported component). No float math, no hardcoded exponent.
- Styling: scoped CSS per component using the global tokens/utilities in `apps/mall/assets/mall.css` (`--mall-red` etc., `.w1200`, `.mbtn`, `.mpanel`, `.mtable`, `.section-bg`, `.hover-lift`). No Tailwind, no element-plus, no new deps.
- Do NOT run builds/tests/dev servers; the integrator validates.
- Do NOT create/modify files outside your assigned list. If you believe a shared file must change, note it in your final report instead.
## Mock data layer (import from `~/mock/data`; read the file for full signatures)
- Shared-contract data: `MOCK_PRODUCTS: Product[]`, `MOCK_CATEGORIES: Category[]`, `MOCK_CURRENCIES`, `MOCK_USER`.
- Helpers: `searchMockProducts({q, categoryId, brandId, shopId, sort, order, page, perPage})``{items,total,page,per_page}`; `productById(idOrSlug)`; `productDetail(idOrSlug)``{product, store, comments, commentStats, coupons, salesRank}`; `lowestSku(product)`; `salesOf(p)`; `commentCountOf(p)`; `topCategories()`; `childCategories(id)`; `categorySubtreeIds(id)`; `brandOf(productId)`; `MOCK_BRANDS`.
- Stores: `MOCK_STORES: MockStore[]`, `storeById`, `storeDetail(idOrSlug)``{store, products, salesRank}`.
- Home: `MOCK_BANNERS`, `MOCK_PROMOS`, `MOCK_QUICK_LINKS`, `homeFloors()`.
- Marketing: `SECKILL_SESSIONS`, `seckillProducts()``{product, seckillPriceMinor, soldPct}[]`, `collectiveProducts()``{product, need, joined}[]`, `INTEGRAL_PRODUCTS`, banner images `/mock/integral-banner.svg`, `/mock/collective-banner.svg`.
- User center: `MOCK_ADDRESSES: MockAddress[]`, `MOCK_FAVORITES: MockFavorite[]`, `USER_STATS`, `MOCK_COUPONS`.
- Images: `/mock/*.svg` (product-1..24, banner-1..3, promo-1..3, floor-adv-1..6, store-1..4, avatar).
## API adapter (via `const { $api } = useNuxtApp()`)
Full `@vmall/shared` `ApiClient`, mock-backed with in-memory state: auth (login/register accept anything → demo user), `listProducts({page, per_page, category_id, q, shop_id})`, `getProduct`, `listCategories`, `listCurrencies`, `convert`, cart CRUD, `checkout(address, currency)``Order[]` (one per shop, clears cart), `listMyOrders`, `getOrder`, `cancelOrder`, `payOrder`, `listMyShipments`, `confirmDelivered`, `requestInvoice`, `listMyInvoices`. shop/* and admin/* throw — never call them.
Cart badge: `useCartStore()``.count`, `.refresh()` after cart mutations.
## Session
`useSessionStore()`: `isLoggedIn`, `user`, `setAuth(tokens)`, `logout()`, `hydrate()` (app.vue hydrates). Route guard: `definePageMeta({ middleware: "auth" })` on pages requiring login (user center, checkout).
## Components (auto-imported)
- Shell (already placed in app.vue — pages do NOT re-add): `ShellTopBar`, `ShellSiteHeader`, `ShellSiteFooter`.
- `UiCarousel` `{images: {image,url?}[], height?, interval?}`; `UiPagination` `{page,total,perPage}` emits `change`; `UiBreadcrumb` `{items: {label,to?}[]}` (already 1200px-wide); `UiQtyStepper` `v-model, max`; `UiRatingStars` `{value,size?}`; `UiEmptyState` `{text?}`; `UiModal` `{open,title?}` emits `close`, slots default+footer; `UiTabs` `{tabs:[{key,label}], modelValue}` emits update, default slot receives `{active}`; `UiStepBar` `{steps: string[], active: number}`; `UiProductCard` `{product: Product}`; `PriceText` `{amountMinor, currency}`; `StatusBadge` `{status, kind}` (uses shared `order.status.*` etc. keys).
## Routes (fixed)
`/` home (done); `/search` (query: q, category, brand, sort, order, page); `/goods/[id]`; `/cart`; `/checkout`, `/checkout/pay`, `/checkout/success`; `/login`, `/register`, `/forgot-password`; `/stores`, `/stores/[id]`; `/seckill`, `/collective`, `/integral`; `/user` shell + `/user` (dashboard), `/user/orders`, `/user/orders/[id]`, `/user/addresses`, `/user/favorites`, `/user/coupons`, `/user/invoices`.
## Visual reference
Follow `design-reference.md` (reference digest): 1200px grid, `#ca151e` red, `#333` dark nav, hairline `#efefef` borders, 1214px text, card hover lift, red price + struck market price, 4-step bar on transaction pages, right-floating 400px auth panel over `/mock/login-bg.svg`, user center 234px left menu + white `.mpanel` content.