Files
vmall/openspec/changes/archive/2026-09-17-mall-pc-storefront-replica/design-reference.md
T
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

139 lines
24 KiB
Markdown
Raw 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.
# PC buyer mall reference digest
## 1. Home route table
`resources/js/plugins/routes/index.js` defines one parent `path: '/'` component `Home/index` (the global buyer shell). Its children are:
| Path | Page file | Purpose / mock data boundary |
|---|---|---|
| `/` | `Home/default.vue` | Home landing page: hero/banner, quick links and category product floors. `/home` response supplies `goods` groups; each group has `name`, `goods[]`, and each product has `id`, `goods_master_image`, `goods_name`, `goods_subname`, `goods_price`, `goods_market_price`. |
| `/login` | `Home/auth/login.vue` | Username/password login plus WeChat login. |
| `/register` | `Home/auth/register.vue` | Phone registration, password confirmation and SMS code. |
| `/forget_password` | `Home/auth/forget_password.vue` | Phone, new password/confirmation and SMS code reset. |
| `/stores` | `Home/stores/list.vue` | Store directory, default/distance sort, pagination. |
| `/store/:id` | `Home/stores/info.vue` | Store profile/rating, store sales rail, product listing/sort. |
| `/store/join` | `Home/stores/index.vue` | Merchant onboarding landing; links into join steps. |
| `/store/step_1`, `/store/step_2`, `/store/step_3` | corresponding `Home/stores/step_*.vue` | Join agreement/data/review state steps. |
| `/s/:params?` | `Home/goods/index.vue` | Product search/list; optional base64 JSON query (`keywords`, `pid/sid/tid`, `class_id`, `brand_id`, `sort_type`, `sort_order`). |
| `/goods/:id` | `Home/goods/info.vue` | Product detail, gallery/zoom, SKU, buy/cart, store, detail/comments/service. |
| `/carts` | `Home/carts/index.vue` | Cart grouped by store, selection/quantity/removal and checkout. |
| `/integral` | `Home/integral/index.vue` | Points-mall home (banner, account panel, recommendations/category rails). |
| `/integral/search/:params?` | `Home/integral/goods.vue` | Points product search/category/sort list. |
| `/integral/goods/:id` | `Home/integral/info.vue` | Points product detail and quantity selection. |
| `/integral/order/:id/:buy_num` | `Home/integral/orders/index.vue` | Address + points redemption order. |
| `/seckills` | `Home/seckills/index.vue` | Timed seckill sessions/products. |
| `/collectives/:params?` | `Home/collectives/index.vue` | Group-buy product list, using ordinary goods filters with `is_collective: 1`. |
| `/user` | `Home/users/index.vue` with child `Home/users/default.vue` | Buyer center shell and dashboard. |
| `/user/address` (+ `/form/:id?`) | `Home/users/address/index.vue` (+ `form.vue`) | Address list and add/edit. |
| `/user/comments`, `/user/comment/add/:id` | comments pages | Buyer comments and add-comment flow. |
| `/user/info`, `/user/safe/**`, `/user/oauth`, `/user/cashes` | user subpages | Profile, safety/password/phone, OAuth binding and withdrawal. |
| `/user/favorites` | `Home/users/favorites/index.vue` | Toggle product favorites/store follows. |
| `/user/money`, `/user/integral`, `/user/frozen_money` | money-log pages | Balance, points and frozen-funds records. |
| `/user/orders`, `/user/order/:id`, `/user/order/comment/:id`, `/user/order/refund/**` | order subpages | Order list/detail/comment/refund flows. |
| `/user/integral_order` | integral-order page | Users points orders. |
| `/user/coupons` | `Home/users/coupons/index.vue` | Coupon table. |
| `/user/distribution/**` | distribution pages | Distribution profile/members/commissions. |
| `/user/article/:name` | article page | Named announcement/help/about content. |
| `/order/before/:params` | `Home/orders/before.vue` | Address/coupon/remark and order preview. |
| `/order/pay/:params` | `Home/orders/pay.vue` | Payment selection and generated order summary. |
| `/order/success` | `Home/orders/success.vue` | Completed payment result. |
`views/App.vue` is only `<router-view/>`; `/Admin/login` and `/Seller/login` are separate non-buyer routes (Admin/Seller areas exist but are out of this survey). `router.js` creates a history router, installs `baseRoute`, and has global progress/auth navigation logic.
## 2. Global layout anatomy
The naming is slightly split: `views/Home/index.vue` is the shell, `views/Home/default.vue` is the actual home landing page, and `components/home/base.vue` is the shared outer layout.
1. **`components/home/top.vue` fixed top bar (`.head`, 30px):** 1200px `.head_in`; left links in order: 平台官网, separator, 商城官网, separator, 联系方式, separator, 关于我们, separator, city/location (default 北京市, optionally IP/AMap-resolved). Right side conditionally shows 登录/注册 or `欢迎您,nickname`, 个人中心, 注销账号; then 商家入驻, 手机端, APP下载. Uses `#ca151e` for login/location/merchant links.
2. **`components/home/head.vue` fixed header (`top:30px`, white):** `.center_top center1200` logo (`.shop_logo`, 242x74), then `.shop_top_seach` with 434px text input (`data.keywords`), search icon button and `.index_my_car` “我的商城/我的购物车” with cart count badge `.shop_car_dot`. Below is 40px dark `.shop_top_nav`: 240px red `.shop_top_nav_left` “全部商品” (hover mounts `leftbar`), then links 首页, 店铺街, 秒杀, 拼团, 积分商城, 帮助中心. Header collapses logo/search area after scroll >200; home category menu can collapse after scroll. Search pushes `/s/` + base64 JSON `{keywords}`.
3. **`components/home/leftbar.vue` category mega-menu:** 240px x 450px `.leftbar` (white) or `.leftbar2` (black translucent when over hero). Each top-level `goodsClass` renders `.left_bar_block`: `.class_1` top category name, `.class_2` up to three child names, and hidden hover `.subbar` 960px wide. Mega menu contains optional top tags, right brand tiles from `goodsBrand[]` (`thumb`), optional advert, and `.subbar_subnav` rows: child heading `name`, grandchildren `{id,pid,name}`. Navigation encodes `pid/sid/tid/class_id` into `/s/:params`.
4. **`components/home/base.vue`:** `<div class="home_default"><top/><div class="padding_content"/><slot name="main"/><foot/></div>`. `.padding_content` is 228px to offset fixed top + header. On mount dispatches `init/loadCommon` (common config, classes, brands, cart, etc.). In `Home/index.vue`, the main slot is `<head-view/><router-view/>`; therefore every buyer page receives the top/header/footer shell. Home `default.vue` itself is just the route content under that shell.
5. **`components/home/foot.vue`:** centered 1200px `.shop_foot`; four value propositions in order (“500强企业 品质保证”, “7天退货 15天换货”, “99元起免运费”, “448家维修网点 全国联保”), then copyright/ICP/security text interpolating `common.web_name` and `common.icp`, plus Element `el-backtop`. Replace `el-backtop` with a small fixed scroll-to-top control in Nuxt.
Element Plus pieces to replace in this shell: `el-icon`/icons (search, cart, etc.), `el-image` (native img with lazy loading), `el-carousel`/transition (CSS/Headless carousel), `el-backtop`, and any `el-breadcrumb` used by child pages.
## 3. Homepage sections and mock data
`views/Home/default.vue` is intentionally simpler than the nav labels suggest—there are no dedicated seckill/collective/brand sections in this file. Those are quick links in `banner_adv`; product floors are generic category groups.
1. **Hero carousel (`components/home/banner.vue`, first child):** `<el-carousel height=450>` → each `<el-carousel-item>``.shop_banner` background image (`background-position:center; background-size:cover`) → full 1200x450 router link. `banner` shape: `[{image:string, url?:string, name?:string}]`; default landing mock is one `default_banner.jpg` item. Nuxt replacement: regular CSS/JS carousel.
2. **Below-hero promo/quick-link strip (`components/home/banner_adv.vue`):** `.banner_bottom_adv center1200`; left 240px six 78x80 cells in order: 实名认证 (`/user/safe`), 积分商城, 优惠团购, 秒杀活动, 公告 (`/user`), 入驻商家. Each icon is an image; right side has up to three 312x160 linked promo images. `index_adv` shape: `[{image:string, url?:string, name?:string}]`. Uses `el-image` (replace with img).
3. **Floor background:** `.index_bg` gray `#f5f5f5`, top margin 40px and 20px/60px padding. For every `data.goods` category group, render `.goods_class_list w1200`; if `v.goods.length > 0`, first `adv-view` full-width placeholder (`adv:{image,url?}`), then `.title` group name + “查看更多”.
4. **Floor body:** left `.index_adv_goods_left` 234x614 image from `data.classLeftAdv[k].image_url` or fallback `pc_class_btadv.jpg`; right `.index_class_goods_right` 966px with product cards in `<ul>`. Product cards are 220x300 white `.product_act_in` with 140x140 image, title, subtitle, red sale price and struck market price. Floor product shape: `{id, goods_master_image, goods_name, goods_subname, goods_price, goods_market_price}`; floor group shape: `{name, goods:[product...]}`. Note `classLeftAdv` is initialized but not assigned from `/home` in the shown code, so a mock can supply it separately.
5. **`components/home/adv.vue`:** a linked lazy image, default `width/height:'100%'`, margin top/bottom 40px; `adv:{image,url?}`.
Visual card behavior: hover lifts cards by 3px and adds `0 2px 12px rgba(0,0,0,.1)`. A Nuxt mock module should include `home {goods: Floor[], classLeftAdv: Adv[]}`, `banner`, and `index_adv` even though the current banner/floor images are hardcoded assets.
## 4. Goods list (`views/Home/goods/index.vue`)
- Breadcrumb `.w1200 breadcrumb`: 首页, then either `搜索 "..." 结果列表` (decoded `keywords`) or 商品列表.
- **Filter block `.goods_where w1200`:** three `.item` rows: 商品分类, 品牌筛选, 筛选排序. Category has “全部”, top-level `common.classes`, selected second-level and third-level children; each class is `{id,name,children:[...]}`. Brand options are `{id,name}`. The route state/query shape is `{keywords?,pid?,sid?,tid?,class_id?:number[],brand_id?,sort_type?,sort_order?}` encoded in base64 path.
- **Important reference limitation:** despite a request to survey “attrs/brands/price”, this template has no attribute facet row and no price-range controls. It exposes category + brands + sort only. Do not infer an attrs/price UI from this file; if the new app adds them, it is an intentional enhancement rather than parity.
- Sort choices are default, 价格 (`goods_price`), 销量 (`goods_sale`), 评论 (`order_comment_count`), each toggling `sort_order` asc/desc. In the shown template the sort controls are also in `.goods_where`, not a separate bar.
- **Results:** `.s_goods_content`; `.s_goods_list` five cards/row (`.item` 224x364). Each card `<dl>` has 176x176 image, title, red price, footer “立即购买” and `{order_comment_count} 人评论`; links `/goods/{id}`. Product list shape: `{id,goods_master_image,goods_name,goods_price,order_comment_count}` (market/subname are not rendered here).
- **Pagination:** `.fy` Element `el-pagination` total/prev/pager/next; response fields are `data`, `total`, `per_page`, `last_page`, `current_page`; empty state is `el-empty`. Replace with plain button/page-number pagination, empty state, and native img.
## 5. Goods detail (`views/Home/goods/info.vue`)
- **Breadcrumb:** `.mbx w1200`: 首页 → every `goods_info.classList[{name}]` → product name.
- **Gallery:** `.goods_info_top_left` has `.goods_image_item` containing custom `components/home/vue-piczoom.vue`, then thumbnail rail `.pic_zoom_list` with previous/next controls and `goods_images_thumb_150[]`; selected index `chose_img_pos`, display image `goods_images_thumb_400[]`, zoom source `goods_images[]`. Mock fields: `goods_images`, `goods_images_thumb_400`, `goods_images_thumb_150` arrays of URLs.
- **Product summary:** `.goods_info_top_right` title/subtitle, favorite toggle, sale/market prices, sale count, QR hint, coupon cards, optional seckill and group-buy activity, full-reduction messages. Core `goods_info`: `id,goods_name,goods_subname,goods_price,goods_market_price,goods_sale,goods_stock,goods_content,classList,attrList,skuList,sale_list`; `coupons[]` entries `{id,money,use_money}`, `full_reductions[]` `{money,use_money}`, `seckills` `{discount,end_time,format_time}`, `collectives` `{discount,need}`, `collective_list[]` `{id,avatar,nickname,orders_count,need}`.
- **SKU/quantity:** `attrList[]` each `{name,specs:[{id,name,is_chose?}]}`; `skuList[]` each `{id,spec_id:number[],goods_price,goods_market_price,goods_stock}`. Chosen `sku_id`, `buy_num`, current stock; “立即购买”, “加入购物车” (regular goods) or “立即抢购” (seckill). Group buy adds `collective_id`.
- **Store card / left rail:** `.goods_info_content` left `.left_item` store card with `store_info.store_name`, `store_company_name`, `area_info`, `store_address`, `id`, `user_id`; four scores in `rate_info` (`scoreAll`, `agreeAll`, `serviceAll`, `speedAll`); buttons enter store/customer service. Below, 销售排行 from `goods_info.sale_list[]` product `{id,goods_master_image,goods_name,goods_price}`.
- **Tabs:** right `.right_item` Element tabs: 商品详情 renders sanitized `goods_content`; 用户评价 renders statistics `{all,good,commonly,bad,rate}`, comment filters, paginated `comments[]` where `{avatar,nickname,score,content,image:string[],reply}`; 售后服务 renders `store_info.after_sale_service`. Replace `el-tabs`, `el-rate`, `el-pagination`, `el-image` with native tabs, stars and controls. `Chat` is a separate common component and may be replaced with a simple contact CTA if chat is not in scope.
## 6. Cart and order flow
**Cart (`views/Home/carts/index.vue`):** `.create_order_1 w1200` begins a four-step `.step_bar` (cart active, address, payment, success). If nonempty, `.cart_th` header columns are 商品, 规格, 单价(元), 数量, 小计, 操作. `.cart_table` groups `data.list` by store: store group `{store_id,store_name,checked,css,cart_list[]}`, item `{cart_id,goods_id,goods_image,goods_name,sku_name,goods_price,buy_num,checked}`. Each item has checkbox, 40px thumb/name link, SKU, +/- quantity controls, subtotal and 移除. Bottom select-all summarizes `allCount`, `allPrice` and 结算; empty `el-empty`. Pagination response state exists (`data.params={page,per_page,last_page,total}`) but no pager is rendered. Replace Element checkboxes/empty/icons.
**Order before (`orders/before.vue`):** same step bar with cart/address active. Address selection list entries `{id,receive_name,receive_tel,area_info,address,is_default}`; then order preview grouped by store. Order group includes `store_info:{id,store_logo,store_name}`, `coupon_id`, `coupons[]:{id,money}`, `is_coupon`, `goods_list[]:{goods_master_image,goods_name,sku_name,goods_price,buy_num,total}`. Shows remark textarea (`data.remark`), total, create-order button. Submit payload uses encoded route `params`, `address_id`, comma-joined `coupon_id`, `remark`.
**Order pay (`orders/pay.vue`):** step bar through payment; balance panel `userInfo.money`; payment options are balance/password, WeChat and Alipay. Displays generated `order[]` groups `{order_no,coupon_money,order_goods[]:{goods_image,goods_name,sku_name,goods_price,buy_num,total_price}}`, `total`, `freight_money`; balance password and QR modal state `{pay_password,qr_code}`. Replace `el-dialog`, QR Vue and Element grid with Tailwind modal/grid and a QR library.
**Order success (`orders/success.vue`):** all four steps active, success result (“订单支付成功!”), link to `/user`; replace Element `el-result` with a simple success panel.
## 7. User center
`Home/users/index.vue` is a two-column `.user_default` shell: 234px `.user_left` + 20px gap, 946px `.user_right` with nested `<router-view>`. Profile card has avatar, `nickname`, edit link, account-profile progress (`check`) and safety indicators (`phone`, `check`). Nav menu groups/items, in order:
- **订单中心:** 我的订单 `/user/orders`, 收货地址 `/user/address`, 评论列表 `/user/comments`.
- **会员中心:** 个人中心 `/user`, 用户资料 `/user/info`, 账户安全 `/user/safe`, 账号绑定 `/user/oauth`, 资金提现 `/user/cashes`, 收藏/关注 `/user/favorites`, 我的优惠券 `/user/coupons`.
- **积分商城:** 积分订单 `/user/integral_order`.
- **资产记录:** 平台余额 `/user/money`, 冻结资金 `/user/frozen_money`, 平台积分 `/user/integral`.
- **分销分佣:** 分销信息 `/user/distribution`, 分销会员 `/user/distribution_users`, 分销佣金 `/user/distribution_logs`.
- **帮助中心:** 网站公告、其他合作、帮助中心、关于我们 at `/user/article/:name`.
`users/default.vue` dashboard data shape: `user_info:{money,integral,frozen_money}`, `count[5]` for pending payment/shipping/receipt/comment/after-sale, `order[]` with `{id,created_at,order_no,order_status,order_status_cn,order_goods[]:{goods_image,goods_name,sku_name,buy_num,total_price}}`, `fav[]` with `{out_id,goods_master_image,goods_name,goods_price}`, and local `history[]` with `{id,image,goods_name,goods_price}`. It renders balance cards, five status links/badges, recent orders, recent favorites and browsing history, all in `.user_main` cards.
Focused page shapes:
- **Address:** `addresses[]:{id,receive_name,receive_tel,area_info,address,is_default}`; add/edit fields are `receive_name`, `receive_tel`, `area` (cascader), `address`, `is_default`; paginated response. Uses `el-dialog`, `el-form`, `el-pagination`.
- **Orders:** filter `{order_status,order_no,created_at:[start,end],is_type}`; statuses -1/0/1/2/3/4/5/6 = all/cancelled/wait-pay/wait-ship/wait-receive/wait-comment/after-sale/completed. Cards use the same order/item shape above and action buttons; logistics modal expects `order_info.delivery_list[]:{context,time}`.
- **Favorites:** toggle `isGoods`; product table fields `goods_master_image,goods_name,goods_price,created_at`, store table fields `store_logo,store_name,created_at`, records carry `is_type,out_id`.
- **Coupons:** generic table columns `name,money,use_money,end_time,created_at`; no custom card UI. `table-view` is a large Element-based shared component and should be replaced with a plain responsive table in Nuxt.
## 8. Stores, seckill, collective and integral pages
- **Stores list (`stores/list.vue`):** breadcrumb + one sort row (default/distance), then store rows with 80px logo and `{id,store_logo,store_name,store_company_name,area_info,store_address,distance}`; center hardcodes 好评率 100%, right shows distance and 进入店铺; 30/page pagination. Location comes from local `lonlat`. Replace `el-breadcrumb`, icons, image and pagination.
- **Store info (`stores/info.vue`):** 350px banner from `store_info.store_slide[]`, left store profile/rates/company/address/favorite/contact and sales ranking; right store-scoped sort (default/price/sales/comments) plus 220x300 product cards with `{id,goods_master_image,goods_name,goods_subname,goods_price,goods_market_price}` and pagination. Store payload adds `id,user_id,store_verify,store_status,store_logo,sale_list[]`, `rate:{scoreAll,agreeAll,serviceAll,speedAll}`, and `after_sale_service` where relevant.
- **Seckills (`seckills/index.vue`):** breadcrumb, four equal timed session selectors (`timeList` hours, `timeIndex`, `timeFormat` countdown), then a 4-column product grid using `{id,goods_master_image,goods_name,goods_subname,goods_price,goods_market_price}`, paginated by `{page,per_page,last_page,total,start_time}`. Countdown refreshes the page when the hour expires.
- **Collectives (`collectives/index.vue`):** 350px collective banner, then the same 3-row category/brand/sort filter and 5-column standard goods cards as `/s`; API query adds `is_collective:1`. Mock the same class/brand/filter/product/page shapes as goods list.
- **Integral home (`integral/index.vue`):** 350px banner, four info blocks (login/register or avatar + `userInfo.integral`, plus private customization/24H/priority cards), then “热门推荐” and category rails. Data is `{store_slide?,userInfo:{avatar,integral},recommend:[{id,goods_master_image,goods_name,goods_price,goods_market_price}],list:[{name,integral_goods:[same product shape]}]}`.
- **Integral goods/detail/order:** `integral/goods.vue` is a category/sort-only list (`class[]:{id,name}`, product card uses “立即兑换”); `integral/info.vue` is zoom gallery + points price/market price, sale/stock and quantity, “立即兑换”, with `goods_content`; `integral/orders/index.vue` reuses address/remark four-step checkout but has one points product `{goods_master_image,goods_name,sku_name,goods_price,buy_num}`, and submits `{id,buy_num,address_id,remark}` to points pay.
## 9. Auth pages
All three use `.home_login` with `user_login__bgs.png`, a 1200px `.login_block`, and right-floating white `.login_item` 400x450, 40px padding, 50px top margin. Inputs are 320px wide, 35px high; submit is full-width red `#ca151e`; no shared top/header is rendered inside the auth template (they still are routed beneath the Home shell in the current route tree).
- **Login:** title tabs “帐号登录 | 扫码登录”; `username`, `password`; 登录 button; links 注册 and 忘记密码; Element divider and WeChat icon/click. Mock `{username,password,provider:'users'}`.
- **Register:** title “注册账号 | 账号登录”; `username` (phone), `password`, `re_password`, SMS `code`, send-code countdown (`code_text`, `math`); 注册 + forgot-password link. Optional inviter id from query/session. Mock `{username,password,re_password,code,code_text,math}`.
- **Forgot password:** title “忘记密码 | 账号登录”; `username`, new `password`, `re_password`, SMS `code`, send-code state; 确定 + 立即登录. Same `{username,password,re_password,code,code_text,math}`.
Element replacements here: `el-divider` → border/label, `el-message` → toast, and ordinary inputs/buttons can be native/Tailwind.
## 10. Theme and visual language
- `plugins/css/style.css` resets body/common block margins and list styles, sets `html,body,#app` height 100%, base `font-size:14px`, color `#333`, no explicit `font-family` (browser/system font applies). Links default `#333` with no decoration; hover `#000`. `body` margin is zero.
- Fixed desktop container is `.center1200` and `.w1200`, both `width:1200px;margin:0 auto`; floats are `.float_left/.float_right`; `.clear` clears floats. Other recurring utility classes: `.breadcrumb` (30px vertical margin), `.shadow` (`0 2px 12px rgba(0,0,0,.1)`), `.x20` 1px gray divider with 20px margin, `.user_main` white padded card, `.table_lists` min-height 600px, `.block_title` 16px with 3px red left border.
- `base.scss` has `$admin_background:#f4f4f4`; `.home` Element primary CSS vars are `--el-color-primary:#e50e19` and light variants, but storefront source consistently uses `#ca151e` for active links, red prices, buttons, selected filters, nav, and borders. Preserve both: `#ca151e` is the visual brand red; `#e50e19` is Element theme fallback. Status colors are green `#67c23a`, gray `#909399`, yellow `#e6a23c`, red `#f56c6c` (round utility classes).
- Button language: white bordered `.default_btn`, red `.error_btn`, yellow `.warn_btn`, green `.success_btn`, gray `.gray_btn`; all compact 12px, 3px radius, 6px/15px padding. Cards/rows are mostly white with `#efefef` 1px rules, subtle hover lift/shadow, restrained 1216px text, and red price emphasis. Home/floor backgrounds use `#f5f5f5`; user center uses `#f1f1f1`; header nav is `#333` with a red category block.
- Replication recommendation: keep 1200px desktop grid, 2030px section rhythm, 20px card gaps, 40px header/nav spacing, 220224px product cards, gray separators, low-radius 3px controls, red active states and strikethrough market prices. Since the reference relies heavily on Element Plus (`el-carousel`, `el-image`, `el-breadcrumb`, `el-pagination`, `el-empty`, `el-checkbox`, `el-select`, `el-dialog`, `el-form`, `el-tabs`, `el-rate`, `el-result`, `el-badge`, `el-progress`, `el-timeline`, `el-icon`), a fresh Nuxt 3/Tailwind build should provide plain semantic replacements rather than porting Elements runtime.