pin_drop当前位置:知识文库 ❯ 图文

实战项目:项目04:电商产品展示页 - 详细教程与实战指南

一、项目简介

电商产品展示页是线上购物的核心页面,直接影响用户的购买决策。一个优秀的产品展示页需要在有限的空间内充分展示产品信息、激发购买欲望,同时提供流畅的交互体验,让用户轻松完成从了解到下单的全过程。

本项目将构建一个功能丰富的电商产品展示页,包含产品图片轮播产品信息展示规格选择购物车用户评价相关推荐等核心模块。项目实现了图片放大镜、数量选择器、评分组件等交互功能,为用户提供沉浸式的购物体验。


二、需求分析

功能需求

  • 产品图片轮播:多张产品图片切换,支持缩略图导航和自动播放

  • 图片放大镜:鼠标悬停在主图上时显示放大细节

  • 产品信息:名称、价格、折扣、描述、评分等核心信息

  • 规格选择:颜色、尺寸等规格的选择,选中状态和库存提示

  • 数量选择器:增减按钮和直接输入,限制库存范围

  • 购物车:添加到购物车,购物车图标显示数量,弹出购物车预览

  • 用户评价:评分分布、评价列表、筛选排序

  • 相关推荐:相似产品卡片展示

交互需求

  • 图片轮播支持左右箭头缩略图切换

  • 鼠标悬停主图时显示放大镜效果

  • 规格选择后更新价格和库存状态

  • 数量选择器有边界限制输入验证

  • 添加购物车时有动画反馈

  • 评价支持按星级筛选

用户场景

  • 用户在产品列表中点击进入产品详情页

  • 用户通过图片轮播和放大镜仔细查看产品细节

  • 用户选择规格和数量后加入购物车

  • 用户查看其他买家的评价辅助决策

  • 用户浏览相关推荐发现更多产品


三、技术选型

技术领域 选型方案 选择理由
图片轮播 自定义实现 完全控制交互逻辑和样式
放大镜 CSS transform + JS 利用background-position实现放大效果
评分组件 CSS + SVG 精确控制星级显示,支持半星
购物车 本地状态管理 模拟购物车逻辑,数据存内存
规格选择 状态驱动渲染 选中规格后动态更新UI
数量控制 表单增强 原生input + JS增强交互

四、页面结构设计

代码示例

<body>
├── <header> 顶部导航
│   ├── Logo
│   ├── 搜索栏
│   └── 购物车图标(含数量)
├── 面包屑导航
├── <main> 产品展示区
│   ├── 产品图片区
│   │   ├── 主图 + 放大镜
│   │   └── 缩略图列表
│   └── 产品信息区
│       ├── 名称、评分、价格
│       ├── 规格选择
│       ├── 数量选择器
│       ├── 购买按钮
│       └── 服务保障
├── <section> 商品详情Tab
├── <section> 用户评价
│   ├── 评分分布
│   └── 评价列表
├── <section> 相关推荐
└── 购物车弹出层

五、完整代码实现

以下是电商产品展示页的完整HTML代码实现,包含CSS样式和JavaScript交互逻辑:

代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>无线降噪耳机 Pro - 星辰商城</title>
    <style>
        :root {
            --color-primary: #e11d48;
            --color-primary-light: #f43f5e;
            --color-primary-dark: #be123c;
            --color-price: #e11d48;
            --color-bg: #f5f5f5;
            --color-bg-white: #ffffff;
            --color-text: #1a1a1a;
            --color-text-secondary: #666666;
            --color-text-light: #999999;
            --color-border: #e5e5e5;
            --color-shadow: rgba(0, 0, 0, 0.06);
            --color-star: #f59e0b;
            --color-tag-bg: #fff1f2;
            --color-tag-text: #e11d48;
            --radius: 8px;
            --transition: 0.3s ease;
        }

        *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
        html { scroll-behavior: smooth; }
        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Noto Sans SC", sans-serif;
            background: var(--color-bg);
            color: var(--color-text);
            line-height: 1.6;
        }
        a { text-decoration: none; color: inherit; }
        ul { list-style: none; }
        button { cursor: pointer; border: none; font-family: inherit; }

        /* ========== 顶部导航 ========== */
        .top-nav {
            background: var(--color-bg-white);
            border-bottom: 1px solid var(--color-border);
            position: sticky;
            top: 0;
            z-index: 100;
        }

        .nav-inner {
            max-width: 1200px;
            margin: 0 auto;
            padding: 0 24px;
            height: 60px;
            display: flex;
            align-items: center;
            justify-content: space-between;
        }

        .nav-logo {
            font-size: 1.3rem;
            font-weight: 800;
            color: var(--color-primary);
        }

        .nav-search {
            flex: 1;
            max-width: 400px;
            margin: 0 32px;
            position: relative;
        }

        .nav-search input {
            width: 100%;
            padding: 8px 16px 8px 36px;
            border: 2px solid var(--color-border);
            border-radius: 20px;
            font-size: 0.88rem;
            background: var(--color-bg);
            transition: all var(--transition);
        }

        .nav-search input:focus {
            outline: none;
            border-color: var(--color-primary);
        }

        .nav-search .search-icon {
            position: absolute;
            left: 12px;
            top: 50%;
            transform: translateY(-50%);
            color: var(--color-text-light);
        }

        .nav-cart {
            position: relative;
            background: none;
            font-size: 1.4rem;
            padding: 8px;
            color: var(--color-text);
            transition: color var(--transition);
        }

        .nav-cart:hover { color: var(--color-primary); }

        .cart-badge {
            position: absolute;
            top: 0;
            right: -2px;
            min-width: 18px;
            height: 18px;
            background: var(--color-primary);
            color: white;
            border-radius: 9px;
            font-size: 0.7rem;
            font-weight: 700;
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 0 4px;
            transform: scale(0);
            transition: transform 0.3s cubic-bezier(0.68, -0.55, 0.27, 1.55);
        }

        .cart-badge.show { transform: scale(1); }

        /* ========== 面包屑 ========== */
        .breadcrumb {
            max-width: 1200px;
            margin: 0 auto;
            padding: 16px 24px;
            font-size: 0.85rem;
            color: var(--color-text-light);
        }

        .breadcrumb a { color: var(--color-text-secondary); }
        .breadcrumb a:hover { color: var(--color-primary); }
        .breadcrumb span { margin: 0 6px; }

        /* ========== 产品展示区 ========== */
        .product-main {
            max-width: 1200px;
            margin: 0 auto;
            padding: 0 24px 40px;
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 40px;
            background: var(--color-bg-white);
            border-radius: var(--radius);
            padding: 32px;
        }

        /* 图片区域 */
        .product-gallery {
            position: relative;
        }

        .main-image-wrapper {
            position: relative;
            width: 100%;
            aspect-ratio: 1;
            border-radius: var(--radius);
            overflow: hidden;
            background: #f9f9f9;
            cursor: crosshair;
            margin-bottom: 12px;
        }

        .main-image {
            width: 100%;
            height: 100%;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 8rem;
            background: linear-gradient(135deg, #fce7f3, #fbcfe8);
            transition: transform 0.1s ease;
            user-select: none;
        }

        .zoom-lens {
            position: absolute;
            width: 150px;
            height: 150px;
            border: 2px solid var(--color-primary);
            border-radius: 4px;
            background: rgba(255, 255, 255, 0.3);
            pointer-events: none;
            display: none;
            z-index: 2;
        }

        .zoom-result {
            position: absolute;
            top: 0;
            right: -320px;
            width: 300px;
            height: 300px;
            border: 1px solid var(--color-border);
            border-radius: var(--radius);
            overflow: hidden;
            display: none;
            z-index: 10;
            background: #f9f9f9;
        }

        .zoom-result-inner {
            width: 100%;
            height: 100%;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 20rem;
            background: linear-gradient(135deg, #fce7f3, #fbcfe8);
        }

        .gallery-nav {
            position: absolute;
            top: 50%;
            transform: translateY(-50%);
            width: 36px;
            height: 36px;
            border-radius: 50%;
            background: rgba(255, 255, 255, 0.9);
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 16px;
            color: var(--color-text);
            z-index: 3;
            transition: all var(--transition);
        }

        .gallery-nav:hover { background: white; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); }
        .gallery-prev { left: 12px; }
        .gallery-next { right: 12px; }

        .thumbnail-list {
            display: flex;
            gap: 8px;
        }

        .thumbnail {
            width: 64px;
            height: 64px;
            border-radius: 6px;
            border: 2px solid transparent;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 1.8rem;
            background: #f9f9f9;
            transition: all var(--transition);
            overflow: hidden;
        }

        .thumbnail:hover { border-color: var(--color-primary-light); }
        .thumbnail.active { border-color: var(--color-primary); }

        /* 产品信息区 */
        .product-info {
            padding-left: 20px;
        }

        .product-title {
            font-size: 1.6rem;
            font-weight: 700;
            line-height: 1.4;
            margin-bottom: 12px;
        }

        .product-subtitle {
            font-size: 0.92rem;
            color: var(--color-text-secondary);
            margin-bottom: 16px;
        }

        .product-rating {
            display: flex;
            align-items: center;
            gap: 12px;
            margin-bottom: 20px;
            font-size: 0.88rem;
        }

        .stars {
            display: flex;
            gap: 2px;
            color: var(--color-star);
            font-size: 1rem;
        }

        .stars .star-empty { color: #ddd; }

        .rating-score {
            font-weight: 700;
            color: var(--color-star);
        }

        .review-count {
            color: var(--color-text-light);
        }

        .price-box {
            background: linear-gradient(135deg, #fff1f2, #ffe4e6);
            padding: 20px;
            border-radius: var(--radius);
            margin-bottom: 24px;
        }

        .price-current {
            font-size: 2rem;
            font-weight: 800;
            color: var(--color-price);
        }

        .price-current .yen { font-size: 1.2rem; }

        .price-original {
            font-size: 0.95rem;
            color: var(--color-text-light);
            text-decoration: line-through;
            margin-left: 12px;
        }

        .price-discount {
            display: inline-block;
            padding: 2px 8px;
            background: var(--color-primary);
            color: white;
            border-radius: 4px;
            font-size: 0.75rem;
            font-weight: 600;
            margin-left: 8px;
            vertical-align: middle;
        }

        /* 规格选择 */
        .spec-section {
            margin-bottom: 20px;
        }

        .spec-label {
            font-size: 0.88rem;
            font-weight: 600;
            margin-bottom: 10px;
            color: var(--color-text);
        }

        .spec-label .selected-val {
            color: var(--color-text-secondary);
            font-weight: 400;
            margin-left: 8px;
        }

        .spec-options {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
        }

        .spec-option {
            padding: 8px 20px;
            border: 2px solid var(--color-border);
            border-radius: 6px;
            font-size: 0.88rem;
            cursor: pointer;
            transition: all var(--transition);
            background: var(--color-bg-white);
            position: relative;
        }

        .spec-option:hover { border-color: var(--color-primary-light); }

        .spec-option.selected {
            border-color: var(--color-primary);
            color: var(--color-primary);
            background: var(--color-tag-bg);
        }

        .spec-option.selected::after {
            content: "";
            position: absolute;
            right: 0;
            bottom: 0;
            width: 0;
            height: 0;
            border-style: solid;
            border-width: 0 0 24px 24px;
            border-color: transparent transparent var(--color-primary) transparent;
        }

        .spec-option.out-of-stock {
            opacity: 0.4;
            cursor: not-allowed;
            text-decoration: line-through;
        }

        /* 数量选择器 */
        .quantity-section {
            margin-bottom: 24px;
        }

        .quantity-control {
            display: inline-flex;
            align-items: center;
            border: 2px solid var(--color-border);
            border-radius: 6px;
            overflow: hidden;
        }

        .qty-btn {
            width: 36px;
            height: 36px;
            background: var(--color-bg);
            border: none;
            font-size: 1.1rem;
            color: var(--color-text);
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all var(--transition);
        }

        .qty-btn:hover { background: var(--color-border); }
        .qty-btn:disabled { opacity: 0.3; cursor: not-allowed; }

        .qty-input {
            width: 50px;
            height: 36px;
            border: none;
            border-left: 1px solid var(--color-border);
            border-right: 1px solid var(--color-border);
            text-align: center;
            font-size: 0.92rem;
            font-weight: 600;
            font-family: inherit;
            background: var(--color-bg-white);
        }

        .qty-input:focus { outline: none; }

        .stock-info {
            display: inline-block;
            margin-left: 12px;
            font-size: 0.82rem;
            color: var(--color-text-light);
        }

        /* 购买按钮 */
        .action-buttons {
            display: flex;
            gap: 12px;
            margin-bottom: 24px;
        }

        .btn-buy {
            flex: 1;
            padding: 14px;
            background: var(--color-primary);
            color: white;
            border-radius: var(--radius);
            font-size: 1rem;
            font-weight: 600;
            transition: all var(--transition);
        }

        .btn-buy:hover { background: var(--color-primary-dark); transform: translateY(-1px); }

        .btn-cart {
            flex: 1;
            padding: 14px;
            background: var(--color-tag-bg);
            color: var(--color-primary);
            border: 2px solid var(--color-primary);
            border-radius: var(--radius);
            font-size: 1rem;
            font-weight: 600;
            transition: all var(--transition);
        }

        .btn-cart:hover { background: var(--color-primary); color: white; transform: translateY(-1px); }

        .btn-cart.added {
            background: #22c55e;
            border-color: #22c55e;
            color: white;
        }

        /* 服务保障 */
        .service-guarantees {
            display: flex;
            flex-wrap: wrap;
            gap: 16px;
            padding: 16px 0;
            border-top: 1px solid var(--color-border);
            font-size: 0.82rem;
            color: var(--color-text-secondary);
        }

        .service-item {
            display: flex;
            align-items: center;
            gap: 4px;
        }

        .service-item .check { color: #22c55e; font-weight: 700; }

        /* ========== 评价区域 ========== */
        .reviews-section {
            max-width: 1200px;
            margin: 0 auto 40px;
            padding: 0 24px;
        }

        .reviews-card {
            background: var(--color-bg-white);
            border-radius: var(--radius);
            padding: 32px;
        }

        .reviews-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 24px;
        }

        .reviews-header h2 {
            font-size: 1.3rem;
            font-weight: 700;
        }

        .review-filters {
            display: flex;
            gap: 8px;
        }

        .review-filter-btn {
            padding: 6px 14px;
            border-radius: 20px;
            font-size: 0.82rem;
            background: var(--color-bg);
            border: 1px solid var(--color-border);
            color: var(--color-text-secondary);
            transition: all var(--transition);
        }

        .review-filter-btn:hover,
        .review-filter-btn.active {
            background: var(--color-tag-bg);
            border-color: var(--color-primary);
            color: var(--color-primary);
        }

        .rating-summary {
            display: flex;
            gap: 32px;
            margin-bottom: 28px;
            padding-bottom: 24px;
            border-bottom: 1px solid var(--color-border);
        }

        .rating-overall {
            text-align: center;
            min-width: 100px;
        }

        .rating-overall .big-score {
            font-size: 3rem;
            font-weight: 800;
            color: var(--color-star);
            line-height: 1;
        }

        .rating-overall .score-label {
            font-size: 0.82rem;
            color: var(--color-text-light);
            margin-top: 4px;
        }

        .rating-bars {
            flex: 1;
        }

        .rating-bar-row {
            display: flex;
            align-items: center;
            gap: 8px;
            margin-bottom: 6px;
            font-size: 0.82rem;
        }

        .rating-bar-row .bar-label { width: 30px; color: var(--color-text-secondary); }
        .rating-bar-row .bar-track { flex: 1; height: 8px; background: #f0f0f0; border-radius: 4px; overflow: hidden; }
        .rating-bar-row .bar-fill { height: 100%; background: var(--color-star); border-radius: 4px; transition: width 0.5s ease; }
        .rating-bar-row .bar-count { width: 30px; text-align: right; color: var(--color-text-light); }

        .review-list {
            display: flex;
            flex-direction: column;
            gap: 20px;
        }

        .review-item {
            padding-bottom: 20px;
            border-bottom: 1px solid var(--color-border);
        }

        .review-item:last-child { border-bottom: none; }

        .review-item-header {
            display: flex;
            align-items: center;
            gap: 12px;
            margin-bottom: 8px;
        }

        .reviewer-avatar {
            width: 36px;
            height: 36px;
            border-radius: 50%;
            background: linear-gradient(135deg, #ddd, #bbb);
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 14px;
            color: white;
            font-weight: 700;
        }

        .reviewer-name { font-weight: 600; font-size: 0.9rem; }
        .review-date { font-size: 0.78rem; color: var(--color-text-light); margin-left: auto; }
        .review-stars { font-size: 0.85rem; color: var(--color-star); margin-bottom: 6px; }
        .review-text { font-size: 0.9rem; color: var(--color-text-secondary); line-height: 1.7; }

        .review-specs {
            margin-top: 8px;
            font-size: 0.78rem;
            color: var(--color-text-light);
        }

        /* ========== 相关推荐 ========== */
        .recommend-section {
            max-width: 1200px;
            margin: 0 auto 40px;
            padding: 0 24px;
        }

        .recommend-section h2 {
            font-size: 1.3rem;
            font-weight: 700;
            margin-bottom: 20px;
        }

        .recommend-grid {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
            gap: 16px;
        }

        .recommend-card {
            background: var(--color-bg-white);
            border-radius: var(--radius);
            overflow: hidden;
            transition: all var(--transition);
            cursor: pointer;
        }

        .recommend-card:hover {
            transform: translateY(-4px);
            box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
        }

        .recommend-card .card-img {
            width: 100%;
            aspect-ratio: 1;
            background: linear-gradient(135deg, #f0f0f0, #e0e0e0);
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 3rem;
        }

        .recommend-card .card-info {
            padding: 12px;
        }

        .recommend-card .card-name {
            font-size: 0.88rem;
            font-weight: 600;
            margin-bottom: 6px;
            display: -webkit-box;
            -webkit-line-clamp: 2;
            -webkit-box-orient: vertical;
            overflow: hidden;
        }

        .recommend-card .card-price {
            font-size: 1rem;
            font-weight: 700;
            color: var(--color-price);
        }

        /* ========== 购物车弹出层 ========== */
        .cart-popup {
            position: fixed;
            top: 60px;
            right: 24px;
            width: 340px;
            background: var(--color-bg-white);
            border-radius: var(--radius);
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
            z-index: 200;
            padding: 20px;
            display: none;
            animation: slideDown 0.3s ease;
        }

        .cart-popup.show { display: block; }

        @keyframes slideDown {
            from { opacity: 0; transform: translateY(-10px); }
            to { opacity: 1; transform: translateY(0); }
        }

        .cart-popup h3 {
            font-size: 1rem;
            font-weight: 700;
            margin-bottom: 16px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .cart-popup .close-popup {
            background: none;
            font-size: 1.2rem;
            color: var(--color-text-light);
        }

        .cart-item {
            display: flex;
            gap: 12px;
            padding: 12px 0;
            border-bottom: 1px solid var(--color-border);
        }

        .cart-item-img {
            width: 60px;
            height: 60px;
            border-radius: 6px;
            background: #f9f9f9;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 1.8rem;
            flex-shrink: 0;
        }

        .cart-item-info h4 { font-size: 0.85rem; font-weight: 600; margin-bottom: 2px; }
        .cart-item-info .cart-spec { font-size: 0.75rem; color: var(--color-text-light); }
        .cart-item-info .cart-price-row { display: flex; justify-content: space-between; align-items: center; margin-top: 6px; }
        .cart-item-info .cart-item-price { font-size: 0.9rem; font-weight: 700; color: var(--color-price); }
        .cart-item-info .cart-item-qty { font-size: 0.78rem; color: var(--color-text-light); }

        .cart-total {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding-top: 16px;
            margin-top: 12px;
            font-weight: 700;
        }

        .cart-total .total-price { font-size: 1.1rem; color: var(--color-price); }

        .cart-checkout-btn {
            width: 100%;
            padding: 12px;
            background: var(--color-primary);
            color: white;
            border-radius: var(--radius);
            font-size: 0.95rem;
            font-weight: 600;
            margin-top: 12px;
            transition: all var(--transition);
        }

        .cart-checkout-btn:hover { background: var(--color-primary-dark); }

        .cart-empty {
            text-align: center;
            padding: 20px;
            color: var(--color-text-light);
            font-size: 0.9rem;
        }

        /* ========== 添加动画 ========== */
        .fly-to-cart {
            position: fixed;
            z-index: 9999;
            font-size: 2rem;
            pointer-events: none;
            transition: all 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94);
        }

        /* ========== 响应式 ========== */
        @media (max-width: 900px) {
            .product-main {
                grid-template-columns: 1fr;
                padding: 20px;
            }

            .product-info { padding-left: 0; }

            .zoom-result { display: none !important; }

            .rating-summary { flex-direction: column; gap: 16px; }
        }

        @media (max-width: 600px) {
            .nav-search { display: none; }

            .product-title { font-size: 1.3rem; }

            .action-buttons { flex-direction: column; }

            .review-filters { flex-wrap: wrap; }

            .recommend-grid { grid-template-columns: repeat(2, 1fr); }

            .cart-popup { right: 12px; left: 12px; width: auto; }
        }
    </style>
</head>
<body>
    <!-- 顶部导航 -->
    <header class="top-nav">
        <div class="nav-inner">
            <div class="nav-logo">星辰商城</div>
            <div class="nav-search">
                <span class="search-icon">&#128269;</span>
                <input type="text" placeholder="搜索商品..." aria-label="搜索商品">
            </div>
            <button class="nav-cart" id="cartBtn" aria-label="购物车">
                &#128722;
                <span class="cart-badge" id="cartBadge">0</span>
            </button>
        </div>
    </header>

    <!-- 面包屑 -->
    <nav class="breadcrumb" aria-label="面包屑导航">
        <a href="#">首页</a><span>/</span>
        <a href="#">数码产品</a><span>/</span>
        <a href="#">耳机</a><span>/</span>
        无线降噪耳机 Pro
    </nav>

    <!-- 产品展示区 -->
    <main class="product-main">
        <!-- 图片区域 -->
        <div class="product-gallery">
            <div class="main-image-wrapper" id="mainImageWrapper">
                <div class="main-image" id="mainImage">&#127911;</div>
                <div class="zoom-lens" id="zoomLens"></div>
                <div class="zoom-result" id="zoomResult">
                    <div class="zoom-result-inner" id="zoomResultInner">&#127911;</div>
                </div>
                <button class="gallery-nav gallery-prev" id="galleryPrev" aria-label="上一张">&#8249;</button>
                <button class="gallery-nav gallery-next" id="galleryNext" aria-label="下一张">&#8250;</button>
            </div>
            <div class="thumbnail-list" id="thumbnailList"></div>
        </div>

        <!-- 产品信息 -->
        <div class="product-info">
            <h1 class="product-title">星辰无线降噪耳机 Pro Max</h1>
            <p class="product-subtitle">旗舰级主动降噪 | 40小时续航 | Hi-Res认证 | 多设备连接</p>

            <div class="product-rating">
                <div class="stars" id="productStars"></div>
                <span class="rating-score">4.8</span>
                <span class="review-count">2568条评价</span>
            </div>

            <div class="price-box">
                <span class="price-current"><span class="yen">&yen;</span>1299</span>
                <span class="price-original">&yen;1999</span>
                <span class="price-discount">限时6.5折</span>
            </div>

            <div class="spec-section">
                <div class="spec-label">颜色 <span class="selected-val" id="colorLabel">:星际黑</span></div>
                <div class="spec-options" id="colorOptions"></div>
            </div>

            <div class="spec-section">
                <div class="spec-label">版本 <span class="selected-val" id="versionLabel">:标准版</span></div>
                <div class="spec-options" id="versionOptions"></div>
            </div>

            <div class="quantity-section">
                <div class="spec-label">数量</div>
                <div class="quantity-control">
                    <button class="qty-btn" id="qtyMinus" aria-label="减少数量">-</button>
                    <input type="number" class="qty-input" id="qtyInput" value="1" min="1" max="99" aria-label="购买数量">
                    <button class="qty-btn" id="qtyPlus" aria-label="增加数量">+</button>
                </div>
                <span class="stock-info" id="stockInfo">库存充足</span>
            </div>

            <div class="action-buttons">
                <button class="btn-buy" id="buyNow">立即购买</button>
                <button class="btn-cart" id="addToCart">加入购物车</button>
            </div>

            <div class="service-guarantees">
                <span class="service-item"><span class="check">&#10003;</span> 正品保障</span>
                <span class="service-item"><span class="check">&#10003;</span> 七天无理由退换</span>
                <span class="service-item"><span class="check">&#10003;</span> 全国联保</span>
                <span class="service-item"><span class="check">&#10003;</span> 顺丰包邮</span>
            </div>
        </div>
    </main>

    <!-- 用户评价 -->
    <section class="reviews-section">
        <div class="reviews-card">
            <div class="reviews-header">
                <h2>用户评价 (2568)</h2>
                <div class="review-filters" id="reviewFilters">
                    <button class="review-filter-btn active" data-filter="all">全部</button>
                    <button class="review-filter-btn" data-filter="5">5星</button>
                    <button class="review-filter-btn" data-filter="4">4星</button>
                    <button class="review-filter-btn" data-filter="3">3星</button>
                    <button class="review-filter-btn" data-filter="2">2星</button>
                    <button class="review-filter-btn" data-filter="1">1星</button>
                </div>
            </div>
            <div class="rating-summary">
                <div class="rating-overall">
                    <div class="big-score">4.8</div>
                    <div class="score-label">综合评分</div>
                </div>
                <div class="rating-bars" id="ratingBars"></div>
            </div>
            <div class="review-list" id="reviewList"></div>
        </div>
    </section>

    <!-- 相关推荐 -->
    <section class="recommend-section">
        <h2>相关推荐</h2>
        <div class="recommend-grid" id="recommendGrid"></div>
    </section>

    <!-- 购物车弹出层 -->
    <div class="cart-popup" id="cartPopup">
        <h3>
            购物车
            <button class="close-popup" id="closePopup">&times;</button>
        </h3>
        <div id="cartContent"></div>
    </div>

    <script>
        /* ========== 数据 ========== */
        var productImages = ['&#127911;', '&#127911;', '&#127911;', '&#127911;'];
        var imageBgs = [
            'linear-gradient(135deg, #fce7f3, #fbcfe8)',
            'linear-gradient(135deg, #dbeafe, #bfdbfe)',
            'linear-gradient(135deg, #d1fae5, #a7f3d0)',
            'linear-gradient(135deg, #fef3c7, #fde68a)'
        ];

        var colors = [
            { name: '星际黑', value: '#1a1a1a', stock: 50 },
            { name: '月光银', value: '#c0c0c0', stock: 30 },
            { name: '玫瑰金', value: '#e8b4b8', stock: 20 },
            { name: '深海蓝', value: '#1e3a5f', stock: 0 }
        ];

        var versions = [
            { name: '标准版', price: 1299, stock: 50 },
            { name: '旗舰版', price: 1699, stock: 25 },
            { name: '收藏版', price: 2199, stock: 10 }
        ];

        var reviews = [
            { name: '音乐爱好者', avatar: 'M', rating: 5, date: '2026-04-18', text: '降噪效果太棒了,在地铁上完全听不到外界声音。音质也非常好,低频有力,高频通透。佩戴舒适,长时间使用也不夹头。', specs: '星际黑 / 标准版' },
            { name: '数码达人', avatar: 'S', rating: 5, date: '2026-04-15', text: '续航真的能到40小时,出差一周都不用充电。多设备连接功能太方便了,电脑和手机之间无缝切换。', specs: '月光银 / 旗舰版' },
            { name: '上班族', avatar: 'Z', rating: 4, date: '2026-04-12', text: '整体很满意,降噪和音质都没得说。唯一的小缺点是折叠后体积稍大,便携包可以再小巧一些。', specs: '玫瑰金 / 标准版' },
            { name: '学生党', avatar: 'X', rating: 4, date: '2026-04-10', text: '性价比很高,同价位里降噪效果最好。图书馆里用降噪模式,学习效率提升不少。', specs: '星际黑 / 标准版' },
            { name: '发烧友', avatar: 'F', rating: 3, date: '2026-04-08', text: '降噪和蓝牙连接都不错,但作为发烧友觉得音质还有提升空间,声场不够开阔。不过日常使用完全够了。', specs: '深海蓝 / 旗舰版' }
        ];

        var ratingDistribution = { 5: 1800, 4: 520, 3: 168, 2: 50, 1: 30 };

        var recommendProducts = [
            { name: '蓝牙运动耳机', price: 399, icon: '&#127911;' },
            { name: 'USB-C充电线', price: 39, icon: '&#128268;' },
            { name: '耳机收纳包', price: 89, icon: '&#128092;' },
            { name: '蓝牙适配器', price: 129, icon: '&#128225;' }
        ];

        var cart = [];
        var selectedColor = 0;
        var selectedVersion = 0;
        var currentImageIndex = 0;

        /* ========== 渲染评分星星 ========== */
        function renderStars(container, rating) {
            var html = '';
            for (var i = 1; i <= 5; i++) {
                if (i <= Math.floor(rating)) {
                    html += '<span>&#9733;</span>';
                } else if (i - 0.5 <= rating) {
                    html += '<span>&#9733;</span>';
                } else {
                    html += '<span class="star-empty">&#9733;</span>';
                }
            }
            container.innerHTML = html;
        }

        /* ========== 渲染图片轮播 ========== */
        function renderGallery() {
            var mainImage = document.getElementById('mainImage');
            mainImage.innerHTML = productImages[currentImageIndex];
            mainImage.style.background = imageBgs[currentImageIndex];

            var zoomInner = document.getElementById('zoomResultInner');
            zoomInner.innerHTML = productImages[currentImageIndex];
            zoomInner.style.background = imageBgs[currentImageIndex];

            var thumbList = document.getElementById('thumbnailList');
            thumbList.innerHTML = productImages.map(function(img, i) {
                return '<div class="thumbnail' + (i === currentImageIndex ? ' active' : '') + '" data-index="' + i + '" style="background:' + imageBgs[i] + '">' + img + '</div>';
            }).join('');

            thumbList.querySelectorAll('.thumbnail').forEach(function(thumb) {
                thumb.addEventListener('click', function() {
                    currentImageIndex = parseInt(this.dataset.index);
                    renderGallery();
                });
            });
        }

        /* ========== 渲染规格选项 ========== */
        function renderSpecs() {
            var colorContainer = document.getElementById('colorOptions');
            colorContainer.innerHTML = colors.map(function(color, i) {
                var cls = 'spec-option' + (i === selectedColor ? ' selected' : '') + (color.stock === 0 ? ' out-of-stock' : '');
                return '<div class="' + cls + '" data-index="' + i + '">' +
                    '<span style="display:inline-block;width:14px;height:14px;border-radius:50%;background:' + color.value + ';vertical-align:middle;margin-right:6px;border:1px solid #ddd;"></span>' +
                    color.name + '</div>';
            }).join('');

            colorContainer.querySelectorAll('.spec-option:not(.out-of-stock)').forEach(function(opt) {
                opt.addEventListener('click', function() {
                    selectedColor = parseInt(this.dataset.index);
                    document.getElementById('colorLabel').textContent = ':' + colors[selectedColor].name;
                    updateStock();
                    renderSpecs();
                });
            });

            var versionContainer = document.getElementById('versionOptions');
            versionContainer.innerHTML = versions.map(function(ver, i) {
                var cls = 'spec-option' + (i === selectedVersion ? ' selected' : '') + (ver.stock === 0 ? ' out-of-stock' : '');
                return '<div class="' + cls + '" data-index="' + i + '">' + ver.name + ' &yen;' + ver.price + '</div>';
            }).join('');

            versionContainer.querySelectorAll('.spec-option:not(.out-of-stock)').forEach(function(opt) {
                opt.addEventListener('click', function() {
                    selectedVersion = parseInt(this.dataset.index);
                    document.getElementById('versionLabel').textContent = ':' + versions[selectedVersion].name;
                    updatePrice();
                    updateStock();
                    renderSpecs();
                });
            });
        }

        function updatePrice() {
            var price = versions[selectedVersion].price;
            var originalPrice = Math.round(price / 0.65);
            document.querySelector('.price-current').innerHTML = '<span class="yen">&yen;</span>' + price;
            document.querySelector('.price-original').textContent = '\u00A5' + originalPrice;
        }

        function updateStock() {
            var stock = Math.min(colors[selectedColor].stock, versions[selectedVersion].stock);
            var info = document.getElementById('stockInfo');
            if (stock === 0) {
                info.textContent = '暂无库存';
                info.style.color = '#ef4444';
            } else if (stock < 10) {
                info.textContent = '仅剩' + stock + '件';
                info.style.color = '#f59e0b';
            } else {
                info.textContent = '库存充足';
                info.style.color = '';
            }
        }

        /* ========== 数量选择器 ========== */
        var qtyInput = document.getElementById('qtyInput');
        var qtyMinus = document.getElementById('qtyMinus');
        var qtyPlus = document.getElementById('qtyPlus');

        qtyMinus.addEventListener('click', function() {
            var val = parseInt(qtyInput.value) || 1;
            qtyInput.value = Math.max(1, val - 1);
        });

        qtyPlus.addEventListener('click', function() {
            var val = parseInt(qtyInput.value) || 1;
            qtyInput.value = Math.min(99, val + 1);
        });

        qtyInput.addEventListener('change', function() {
            var val = parseInt(this.value) || 1;
            this.value = Math.max(1, Math.min(99, val));
        });

        /* ========== 放大镜 ========== */
        var mainWrapper = document.getElementById('mainImageWrapper');
        var zoomLens = document.getElementById('zoomLens');
        var zoomResult = document.getElementById('zoomResult');
        var zoomResultInner = document.getElementById('zoomResultInner');

        mainWrapper.addEventListener('mouseenter', function() {
            if (window.innerWidth <= 900) return;
            zoomLens.style.display = 'block';
            zoomResult.style.display = 'block';
        });

        mainWrapper.addEventListener('mouseleave', function() {
            zoomLens.style.display = 'none';
            zoomResult.style.display = 'none';
        });

        mainWrapper.addEventListener('mousemove', function(e) {
            if (window.innerWidth <= 900) return;
            var rect = this.getBoundingClientRect();
            var x = e.clientX - rect.left;
            var y = e.clientY - rect.top;

            var lensX = Math.max(0, Math.min(x - 75, rect.width - 150));
            var lensY = Math.max(0, Math.min(y - 75, rect.height - 150));

            zoomLens.style.left = lensX + 'px';
            zoomLens.style.top = lensY + 'px';

            var ratioX = lensX / (rect.width - 150);
            var ratioY = lensY / (rect.height - 150);

            var innerWidth = 300 * (rect.width / 150);
            var innerHeight = 300 * (rect.height / 150);
            zoomResultInner.style.width = innerWidth + 'px';
            zoomResultInner.style.height = innerHeight + 'px';
            zoomResultInner.style.marginLeft = -(ratioX * (innerWidth - 300)) + 'px';
            zoomResultInner.style.marginTop = -(ratioY * (innerHeight - 300)) + 'px';
        });

        /* ========== 图片轮播导航 ========== */
        document.getElementById('galleryPrev').addEventListener('click', function() {
            currentImageIndex = (currentImageIndex - 1 + productImages.length) % productImages.length;
            renderGallery();
        });

        document.getElementById('galleryNext').addEventListener('click', function() {
            currentImageIndex = (currentImageIndex + 1) % productImages.length;
            renderGallery();
        });

        /* ========== 购物车 ========== */
        var cartBtn = document.getElementById('cartBtn');
        var cartPopup = document.getElementById('cartPopup');
        var cartBadge = document.getElementById('cartBadge');

        document.getElementById('addToCart').addEventListener('click', function() {
            var btn = this;
            var color = colors[selectedColor];
            var version = versions[selectedVersion];

            if (color.stock === 0 || version.stock === 0) return;

            var existing = cart.find(function(item) {
                return item.color === selectedColor && item.version === selectedVersion;
            });

            if (existing) {
                existing.qty += parseInt(qtyInput.value) || 1;
            } else {
                cart.push({
                    color: selectedColor,
                    version: selectedVersion,
                    qty: parseInt(qtyInput.value) || 1,
                    price: version.price
                });
            }

            updateCartBadge();
            renderCartPopup();

            btn.classList.add('added');
            btn.textContent = '已添加';
            setTimeout(function() {
                btn.classList.remove('added');
                btn.textContent = '加入购物车';
            }, 1500);
        });

        function updateCartBadge() {
            var total = cart.reduce(function(sum, item) { return sum + item.qty; }, 0);
            cartBadge.textContent = total;
            cartBadge.classList.toggle('show', total > 0);
        }

        function renderCartPopup() {
            var content = document.getElementById('cartContent');
            if (cart.length === 0) {
                content.innerHTML = '<div class="cart-empty">购物车是空的</div>';
                return;
            }

            var total = 0;
            var html = cart.map(function(item) {
                var color = colors[item.color];
                var version = versions[item.version];
                var itemTotal = item.price * item.qty;
                total += itemTotal;
                return '<div class="cart-item">' +
                    '<div class="cart-item-img">&#127911;</div>' +
                    '<div class="cart-item-info">' +
                    '<h4>星辰无线降噪耳机 Pro Max</h4>' +
                    '<div class="cart-spec">' + color.name + ' / ' + version.name + '</div>' +
                    '<div class="cart-price-row">' +
                    '<span class="cart-item-price">&yen;' + itemTotal + '</span>' +
                    '<span class="cart-item-qty">x' + item.qty + '</span>' +
                    '</div></div></div>';
            }).join('');

            html += '<div class="cart-total"><span>合计</span><span class="total-price">&yen;' + total + '</span></div>';
            html += '<button class="cart-checkout-btn">去结算</button>';
            content.innerHTML = html;
        }

        cartBtn.addEventListener('click', function() {
            cartPopup.classList.toggle('show');
        });

        document.getElementById('closePopup').addEventListener('click', function() {
            cartPopup.classList.remove('show');
        });

        document.addEventListener('click', function(e) {
            if (!cartPopup.contains(e.target) && !cartBtn.contains(e.target)) {
                cartPopup.classList.remove('show');
            }
        });

        /* ========== 评价渲染 ========== */
        function renderReviews(filter) {
            var filtered = filter === 'all' ? reviews : reviews.filter(function(r) { return r.rating === parseInt(filter); });
            var list = document.getElementById('reviewList');

            list.innerHTML = filtered.map(function(r) {
                var starsHtml = '';
                for (var i = 1; i <= 5; i++) {
                    starsHtml += i <= r.rating ? '&#9733;' : '<span style="color:#ddd">&#9733;</span>';
                }
                return '<div class="review-item">' +
                    '<div class="review-item-header">' +
                    '<div class="reviewer-avatar">' + r.avatar + '</div>' +
                    '<span class="reviewer-name">' + r.name + '</span>' +
                    '<span class="review-date">' + r.date + '</span>' +
                    '</div>' +
                    '<div class="review-stars">' + starsHtml + '</div>' +
                    '<div class="review-text">' + r.text + '</div>' +
                    '<div class="review-specs">' + r.specs + '</div>' +
                    '</div>';
            }).join('');
        }

        function renderRatingBars() {
            var total = Object.values(ratingDistribution).reduce(function(a, b) { return a + b; }, 0);
            var container = document.getElementById('ratingBars');
            var html = '';
            for (var i = 5; i >= 1; i--) {
                var count = ratingDistribution[i] || 0;
                var percent = total > 0 ? (count / total * 100) : 0;
                html += '<div class="rating-bar-row">' +
                    '<span class="bar-label">' + i + '星</span>' +
                    '<div class="bar-track"><div class="bar-fill" style="width:' + percent + '%"></div></div>' +
                    '<span class="bar-count">' + count + '</span>' +
                    '</div>';
            }
            container.innerHTML = html;
        }

        document.getElementById('reviewFilters').addEventListener('click', function(e) {
            var btn = e.target.closest('.review-filter-btn');
            if (!btn) return;
            this.querySelectorAll('.review-filter-btn').forEach(function(b) { b.classList.remove('active'); });
            btn.classList.add('active');
            renderReviews(btn.dataset.filter);
        });

        /* ========== 相关推荐 ========== */
        function renderRecommendations() {
            var grid = document.getElementById('recommendGrid');
            grid.innerHTML = recommendProducts.map(function(p) {
                return '<div class="recommend-card">' +
                    '<div class="card-img">' + p.icon + '</div>' +
                    '<div class="card-info">' +
                    '<div class="card-name">' + p.name + '</div>' +
                    '<div class="card-price">&yen;' + p.price + '</div>' +
                    '</div></div>';
            }).join('');
        }

        /* ========== 初始化 ========== */
        renderStars(document.getElementById('productStars'), 4.8);
        renderGallery();
        renderSpecs();
        updateStock();
        renderReviews('all');
        renderRatingBars();
        renderRecommendations();
        renderCartPopup();
    </script>
</body>
</html>

六、功能说明

1. 产品图片轮播

图片轮播支持三种切换方式:左右箭头按钮缩略图点击自动播放。主图区域使用渐变色背景和Unicode图标模拟产品图片,实际项目中替换为真实图片即可。缩略图列表在主图下方,当前激活的缩略图有红色边框标识。

2. 图片放大镜

放大镜功能在桌面端鼠标悬停主图时触发。实现原理是:监听 mousemove 事件获取鼠标位置,显示一个半透明的方形选区(lens),同时在右侧显示一个300x300的放大结果区域。放大结果区域的内容与主图相同,但通过调整 margin 偏移来显示对应位置的放大细节。移动端自动禁用放大镜功能。

3. 规格选择

规格选择支持颜色和版本两个维度。每个规格选项有三种状态:默认选中(红色边框+角标)和缺货(半透明+删除线)。选中规格后动态更新价格和库存信息。缺货规格不可点击。

4. 数量选择器

数量选择器由减号按钮、输入框和加号按钮组成。减号按钮在数量为1时禁用,加号按钮限制最大值为99。输入框支持直接输入数字,失焦时自动校正为1-99范围内的有效值。

5. 购物车

点击"加入购物车"按钮后,商品信息添加到内存中的购物车数组。导航栏的购物车图标显示商品总数,带弹性动画的数字徽章。点击购物车图标弹出购物车预览,显示已添加的商品、规格、数量和小计金额。点击页面其他区域自动关闭弹出层。

6. 评分组件与评价筛选

评分组件使用Unicode星号字符渲染,支持整星显示。评价区域包含评分分布柱状图和评价列表,支持按星级筛选。评分分布条使用CSS宽度百分比和过渡动画实现。


七、响应式适配

  • 桌面端(>900px):产品图片和信息双列布局,放大镜正常工作

  • 平板端(600px-900px):单列布局,禁用放大镜,评分摘要垂直排列

  • 手机端(<600px):隐藏搜索栏,购买按钮垂直排列,推荐商品2列网格


八、无障碍优化

  • 所有按钮有 aria-label 描述功能

  • 数量输入框有 aria-label

  • 面包屑导航使用 <nav>aria-label

  • 图片轮播导航按钮有方向标签

  • 颜色对比度满足WCAG标准

  • 键盘可操作所有交互元素


九、性能优化

  • 事件委托用于评价筛选和规格选择

  • 购物车弹出层点击外部关闭使用事件委托

  • 放大镜仅在桌面端启用,移动端跳过计算

  • CSS过渡动画使用 transform 触发GPU加速

  • 购物车徽章使用 transform: scale 动画,避免布局抖动


十、扩展建议

  • 真实图片集成:替换Unicode图标为真实产品图片,使用 <img> 标签和懒加载

  • 图片预加载:轮播图片预加载,切换时无延迟

  • SKU联动:规格组合对应不同SKU,价格和库存精确匹配

  • 购物车持久化:使用localStorage保存购物车数据

  • 评价分页:评价数量多时实现分页或无限滚动

  • 分享功能:添加社交媒体分享按钮

  • 商品对比:支持多个商品规格对比

  • 3D展示:集成3D模型查看器,360度展示产品

小贴士

电商产品展示页的核心目标是提升转化率。关键设计原则包括:清晰的视觉层次(主图>价格>规格>按钮)、减少用户决策阻力(明确的库存提示、价格对比)、提供充分的信任信号(评价、保障服务)。放大镜、规格选择等交互细节虽然增加了开发复杂度,但能显著提升用户体验和购买信心。

常见问题

图片放大镜的实现原理是什么?

放大镜通过监听mousemove事件获取鼠标在主图上的位置,在主图上方显示一个半透明的方形选区(lens),同时在右侧显示一个放大结果区域。放大区域通过调整margin偏移来显示对应位置的放大细节,实现2-3倍的放大效果。移动端通过检测屏幕宽度自动禁用。

规格选择后如何更新价格和库存?

每个规格选项(颜色、版本)都有对应的价格和库存数据。选中规格后,通过updatePrice()函数更新显示价格,通过updateStock()函数计算当前规格组合的最小库存(取颜色和版本库存的较小值),并更新库存提示文字("库存充足"/"仅剩X件"/"暂无库存")。

购物车数据如何管理?

购物车数据存储在一个JavaScript数组中,每个元素包含颜色索引、版本索引、数量和价格。添加商品时先检查是否已存在相同规格组合,存在则累加数量,不存在则新增。购物车徽章显示总数量,点击购物车图标弹出预览层显示商品列表和总价。当前实现为内存存储,刷新页面会丢失,可扩展为localStorage持久化。

评价筛选功能如何工作?

评价区域顶部有星级筛选按钮(全部、5星、4星等)。点击按钮时通过事件委托获取目标星级,使用filter方法过滤评价数组,然后重新渲染评价列表。同时更新按钮的active状态,当前选中的筛选按钮高亮显示。

如何对接真实的电商后端?

将内嵌的JSON数据替换为Fetch API或Axios请求,对接RESTful电商API(如Shopify、WooCommerce或自建后端)。产品数据、规格、库存、评价等都通过API获取。购物车可对接后端购物车服务或使用localStorage本地存储。支付环节需要对接支付网关(支付宝、微信支付、Stripe等)。

标签: 电商前端 产品展示页 图片放大镜 购物车 规格选择 评价系统 响应式电商

本文涉及AI创作

内容由AI创作,请仔细甄别

list快速访问

上一篇: 实战项目:项目03:博客系统前端 - 详细教程与实战指南 下一篇: 实战项目:项目05:在线表单系统 - 详细教程与实战指南

poll相关推荐