- 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
24 KiB
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 | User’s 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.
components/home/top.vuefixed 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#ca151efor login/location/merchant links.components/home/head.vuefixed header (top:30px, white):.center_top center1200logo (.shop_logo, 242x74), then.shop_top_seachwith 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 mountsleftbar), then links 首页, 店铺街, 秒杀, 拼团, 积分商城, 帮助中心. Header collapses logo/search area after scroll >200; home category menu can collapse after scroll. Search pushes/s/+ base64 JSON{keywords}.components/home/leftbar.vuecategory mega-menu: 240px x 450px.leftbar(white) or.leftbar2(black translucent when over hero). Each top-levelgoodsClassrenders.left_bar_block:.class_1top category name,.class_2up to three child names, and hidden hover.subbar960px wide. Mega menu contains optional top tags, right brand tiles fromgoodsBrand[](thumb), optional advert, and.subbar_subnavrows: child headingname, grandchildren{id,pid,name}. Navigation encodespid/sid/tid/class_idinto/s/:params.components/home/base.vue:<div class="home_default"><top/><div class="padding_content"/><slot name="main"/><foot/></div>..padding_contentis 228px to offset fixed top + header. On mount dispatchesinit/loadCommon(common config, classes, brands, cart, etc.). InHome/index.vue, the main slot is<head-view/><router-view/>; therefore every buyer page receives the top/header/footer shell. Homedefault.vueitself is just the route content under that shell.components/home/foot.vue: centered 1200px.shop_foot; four value propositions in order (“500强企业 品质保证”, “7天退货 15天换货”, “99元起免运费”, “448家维修网点 全国联保”), then copyright/ICP/security text interpolatingcommon.web_nameandcommon.icp, plus Elementel-backtop. Replaceel-backtopwith 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.
- Hero carousel (
components/home/banner.vue, first child):<el-carousel height=450>→ each<el-carousel-item>→.shop_bannerbackground image (background-position:center; background-size:cover) → full 1200x450 router link.bannershape:[{image:string, url?:string, name?:string}]; default landing mock is onedefault_banner.jpgitem. Nuxt replacement: regular CSS/JS carousel. - 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_advshape:[{image:string, url?:string, name?:string}]. Usesel-image(replace with img). - Floor background:
.index_bggray#f5f5f5, top margin 40px and 20px/60px padding. For everydata.goodscategory group, render.goods_class_list w1200; ifv.goods.length > 0, firstadv-viewfull-width placeholder (adv:{image,url?}), then.titlegroup name + “查看更多”. - Floor body: left
.index_adv_goods_left234x614 image fromdata.classLeftAdv[k].image_urlor fallbackpc_class_btadv.jpg; right.index_class_goods_right966px with product cards in<ul>. Product cards are 220x300 white.product_act_inwith 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...]}. NoteclassLeftAdvis initialized but not assigned from/homein the shown code, so a mock can supply it separately. components/home/adv.vue: a linked lazy image, defaultwidth/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搜索 "..." 结果列表(decodedkeywords) or 商品列表. - Filter block
.goods_where w1200: three.itemrows: 商品分类, 品牌筛选, 筛选排序. Category has “全部”, top-levelcommon.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 togglingsort_orderasc/desc. In the shown template the sort controls are also in.goods_where, not a separate bar. - Results:
.s_goods_content;.s_goods_listfive cards/row (.item224x364). 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:
.fyElementel-paginationtotal/prev/pager/next; response fields aredata,total,per_page,last_page,current_page; empty state isel-empty. Replace with plain button/page-number pagination, empty state, and native img.
5. Goods detail (views/Home/goods/info.vue)
- Breadcrumb:
.mbx w1200: 首页 → everygoods_info.classList[{name}]→ product name. - Gallery:
.goods_info_top_lefthas.goods_image_itemcontaining customcomponents/home/vue-piczoom.vue, then thumbnail rail.pic_zoom_listwith previous/next controls andgoods_images_thumb_150[]; selected indexchose_img_pos, display imagegoods_images_thumb_400[], zoom sourcegoods_images[]. Mock fields:goods_images,goods_images_thumb_400,goods_images_thumb_150arrays of URLs. - Product summary:
.goods_info_top_righttitle/subtitle, favorite toggle, sale/market prices, sale count, QR hint, coupon cards, optional seckill and group-buy activity, full-reduction messages. Coregoods_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}. Chosensku_id,buy_num, current stock; “立即购买”, “加入购物车” (regular goods) or “立即抢购” (seckill). Group buy addscollective_id. - Store card / left rail:
.goods_info_contentleft.left_itemstore card withstore_info.store_name,store_company_name,area_info,store_address,id,user_id; four scores inrate_info(scoreAll,agreeAll,serviceAll,speedAll); buttons enter store/customer service. Below, 销售排行 fromgoods_info.sale_list[]product{id,goods_master_image,goods_name,goods_price}. - Tabs: right
.right_itemElement tabs: 商品详情 renders sanitizedgoods_content; 用户评价 renders statistics{all,good,commonly,bad,rate}, comment filters, paginatedcomments[]where{avatar,nickname,score,content,image:string[],reply}; 售后服务 rendersstore_info.after_sale_service. Replaceel-tabs,el-rate,el-pagination,el-imagewith native tabs, stars and controls.Chatis 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 arereceive_name,receive_tel,area(cascader),address,is_default; paginated response. Usesel-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 expectsorder_info.delivery_list[]:{context,time}. - Favorites: toggle
isGoods; product table fieldsgoods_master_image,goods_name,goods_price,created_at, store table fieldsstore_logo,store_name,created_at, records carryis_type,out_id. - Coupons: generic table columns
name,money,use_money,end_time,created_at; no custom card UI.table-viewis 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 locallonlat. Replaceel-breadcrumb, icons, image and pagination. - Store info (
stores/info.vue): 350px banner fromstore_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 addsid,user_id,store_verify,store_status,store_logo,sale_list[],rate:{scoreAll,agreeAll,serviceAll,speedAll}, andafter_sale_servicewhere relevant. - Seckills (
seckills/index.vue): breadcrumb, four equal timed session selectors (timeListhours,timeIndex,timeFormatcountdown), 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 addsis_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.vueis a category/sort-only list (class[]:{id,name}, product card uses “立即兑换”);integral/info.vueis zoom gallery + points price/market price, sale/stock and quantity, “立即兑换”, withgoods_content;integral/orders/index.vuereuses 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, SMScode, 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, newpassword,re_password, SMScode, 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.cssresets body/common block margins and list styles, setshtml,body,#appheight 100%, basefont-size:14px, color#333, no explicitfont-family(browser/system font applies). Links default#333with no decoration; hover#000.bodymargin is zero.- Fixed desktop container is
.center1200and.w1200, bothwidth:1200px;margin:0 auto; floats are.float_left/.float_right;.clearclears floats. Other recurring utility classes:.breadcrumb(30px vertical margin),.shadow(0 2px 12px rgba(0,0,0,.1)),.x201px gray divider with 20px margin,.user_mainwhite padded card,.table_listsmin-height 600px,.block_title16px with 3px red left border. base.scsshas$admin_background:#f4f4f4;.homeElement primary CSS vars are--el-color-primary:#e50e19and light variants, but storefront source consistently uses#ca151efor active links, red prices, buttons, selected filters, nav, and borders. Preserve both:#ca151eis the visual brand red;#e50e19is 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#efefef1px rules, subtle hover lift/shadow, restrained 12–16px text, and red price emphasis. Home/floor backgrounds use#f5f5f5; user center uses#f1f1f1; header nav is#333with a red category block. - Replication recommendation: keep 1200px desktop grid, 20–30px section rhythm, 20px card gaps, 40px header/nav spacing, 220–224px 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 Element’s runtime.