402 lines
18 KiB
HTML
402 lines
18 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block title %}{{ product.name }} - 太白购物商城{% endblock %}
|
||
|
||
{% block head %}
|
||
<link rel="stylesheet" href="{{ url_for('static', filename='css/product_detail.css') }}">
|
||
<link rel="stylesheet" href="{{ url_for('static', filename='css/review.css') }}">
|
||
{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="container">
|
||
<!-- 面包屑导航 -->
|
||
<nav aria-label="breadcrumb" class="mb-3">
|
||
<ol class="breadcrumb">
|
||
<li class="breadcrumb-item"><a href="{{ url_for('main.index') }}">首页</a></li>
|
||
<li class="breadcrumb-item">
|
||
<a href="{{ url_for('main.product_list', category_id=product.category_id) }}">
|
||
{{ product.category.name }}
|
||
</a>
|
||
</li>
|
||
<li class="breadcrumb-item active">{{ product.name }}</li>
|
||
</ol>
|
||
</nav>
|
||
|
||
<div class="row">
|
||
<!-- 左侧:商品图片 -->
|
||
<div class="col-md-6">
|
||
{% if images %}
|
||
<!-- 主图显示区域 -->
|
||
<div id="productImageCarousel" class="carousel slide mb-3" data-bs-ride="carousel">
|
||
<div class="carousel-inner">
|
||
{% for image in images %}
|
||
<div class="carousel-item {% if loop.first or image.is_main %}active{% endif %}">
|
||
<img src="{{ image.image_url }}" class="d-block w-100" alt="{{ product.name }}"
|
||
style="height: 400px; object-fit: cover; border-radius: 8px;">
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
{% if images|length > 1 %}
|
||
<button class="carousel-control-prev" type="button" data-bs-target="#productImageCarousel" data-bs-slide="prev">
|
||
<span class="carousel-control-prev-icon"></span>
|
||
</button>
|
||
<button class="carousel-control-next" type="button" data-bs-target="#productImageCarousel" data-bs-slide="next">
|
||
<span class="carousel-control-next-icon"></span>
|
||
</button>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<!-- 缩略图 -->
|
||
{% if images|length > 1 %}
|
||
<div class="row">
|
||
{% for image in images %}
|
||
<div class="col-3 mb-2">
|
||
<img src="{{ image.image_url }}" class="img-thumbnail thumbnail-image"
|
||
alt="{{ product.name }}" style="height: 80px; object-fit: cover; cursor: pointer;"
|
||
onclick="goToSlide({{ loop.index0 }})">
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
{% endif %}
|
||
{% else %}
|
||
<!-- 无图片占位 -->
|
||
<div class="bg-light d-flex align-items-center justify-content-center"
|
||
style="height: 400px; border-radius: 8px;">
|
||
<i class="bi bi-image text-muted" style="font-size: 5rem;"></i>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<!-- 右侧:商品信息 -->
|
||
<div class="col-md-6">
|
||
<h2 class="mb-3">{{ product.name }}</h2>
|
||
|
||
<!-- 品牌 -->
|
||
{% if product.brand %}
|
||
<p class="text-muted mb-2">
|
||
<strong>品牌:</strong>{{ product.brand }}
|
||
</p>
|
||
{% endif %}
|
||
|
||
<!-- 价格 -->
|
||
<div class="price-section mb-4">
|
||
<div class="d-flex align-items-baseline">
|
||
<span class="text-danger fw-bold" style="font-size: 2rem;">
|
||
¥<span id="currentPrice">{{ "%.2f"|format(product.price) }}</span>
|
||
</span>
|
||
{% if product.original_price and product.original_price > product.price %}
|
||
<span class="text-muted text-decoration-line-through ms-3" style="font-size: 1.2rem;">
|
||
¥{{ "%.2f"|format(product.original_price) }}
|
||
</span>
|
||
<span class="badge bg-danger ms-2">
|
||
省{{ "%.0f"|format(((product.original_price - product.price) / product.original_price * 100)) }}%
|
||
</span>
|
||
{% endif %}
|
||
</div>
|
||
<div class="mt-2">
|
||
<small class="text-muted">销量:{{ product.sales_count }} | 浏览:{{ product.view_count }}</small>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 商品规格选择 -->
|
||
{% if inventory_list and inventory_list|length > 1 %}
|
||
<div class="specs-section mb-4">
|
||
<h6>选择规格:</h6>
|
||
<div id="specsContainer">
|
||
{% set spec_groups = {} %}
|
||
{% for sku in inventory_list %}
|
||
{% if sku.spec_combination %}
|
||
{% for spec_name, spec_value in sku.spec_combination.items() %}
|
||
{% if spec_name not in spec_groups %}
|
||
{% set _ = spec_groups.update({spec_name: []}) %}
|
||
{% endif %}
|
||
{% if spec_value not in spec_groups[spec_name] %}
|
||
{% set _ = spec_groups[spec_name].append(spec_value) %}
|
||
{% endif %}
|
||
{% endfor %}
|
||
{% endif %}
|
||
{% endfor %}
|
||
|
||
{% for spec_name, spec_values in spec_groups.items() %}
|
||
<div class="spec-group mb-3">
|
||
<label class="form-label">{{ spec_name }}:</label>
|
||
<div class="spec-options">
|
||
{% for spec_value in spec_values %}
|
||
<button type="button" class="btn btn-outline-secondary spec-option me-2 mb-2"
|
||
data-spec-name="{{ spec_name }}" data-spec-value="{{ spec_value }}">
|
||
{{ spec_value }}
|
||
</button>
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- 库存信息 -->
|
||
<div class="stock-section mb-4">
|
||
<div class="row">
|
||
<div class="col-6">
|
||
<strong>库存:</strong>
|
||
<span id="stockCount" class="text-success">
|
||
{% if inventory_list %}
|
||
{% if inventory_list|length == 1 %}
|
||
{{ inventory_list[0].stock }}
|
||
{% else %}
|
||
请选择规格
|
||
{% endif %}
|
||
{% else %}
|
||
暂无库存
|
||
{% endif %}
|
||
</span>
|
||
<span id="stockUnit">件</span>
|
||
</div>
|
||
{% if product.weight %}
|
||
<div class="col-6">
|
||
<strong>重量:</strong>{{ product.weight }}kg
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 购买数量 -->
|
||
<div class="quantity-section mb-4">
|
||
<label class="form-label"><strong>数量:</strong></label>
|
||
<div class="input-group" style="width: 150px;">
|
||
<button class="btn btn-outline-secondary" type="button" onclick="changeQuantity(-1)">-</button>
|
||
<input type="number" class="form-control text-center" id="quantity" value="1" min="1" max="999">
|
||
<button class="btn btn-outline-secondary" type="button" onclick="changeQuantity(1)">+</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 操作按钮 -->
|
||
<div class="action-buttons mb-4">
|
||
<div class="d-grid gap-2 d-md-flex">
|
||
<button type="button" class="btn btn-warning btn-lg flex-fill" id="addToCartBtn"
|
||
onclick="addToCart()" {% if not inventory_list %}disabled{% endif %}>
|
||
<i class="bi bi-cart-plus"></i> 加入购物车
|
||
</button>
|
||
<button type="button" class="btn btn-danger btn-lg flex-fill" id="buyNowBtn"
|
||
onclick="buyNow()" {% if not inventory_list %}disabled{% endif %}>
|
||
<i class="bi bi-lightning-fill"></i> 立即购买
|
||
</button>
|
||
</div>
|
||
<div class="mt-2">
|
||
<button type="button" class="btn btn-outline-secondary" onclick="addToFavorites()">
|
||
<i class="bi bi-heart"></i> 收藏商品
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 服务承诺 -->
|
||
<div class="service-promises">
|
||
<h6>服务承诺:</h6>
|
||
<ul class="list-unstyled">
|
||
<li><i class="bi bi-check-circle text-success"></i> 正品保证</li>
|
||
<li><i class="bi bi-check-circle text-success"></i> 7天无理由退换</li>
|
||
<li><i class="bi bi-check-circle text-success"></i> 全国包邮</li>
|
||
<li><i class="bi bi-check-circle text-success"></i> 售后服务</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 商品详情标签页 -->
|
||
<div class="row mt-5">
|
||
<div class="col-12">
|
||
<ul class="nav nav-tabs" id="productDetailTabs" role="tablist">
|
||
<li class="nav-item" role="presentation">
|
||
<button class="nav-link active" id="description-tab" data-bs-toggle="tab"
|
||
data-bs-target="#description" type="button" role="tab">商品详情</button>
|
||
</li>
|
||
<li class="nav-item" role="presentation">
|
||
<button class="nav-link" id="specs-tab" data-bs-toggle="tab"
|
||
data-bs-target="#specs" type="button" role="tab">规格参数</button>
|
||
</li>
|
||
<li class="nav-item" role="presentation">
|
||
<button class="nav-link" id="reviews-tab" data-bs-toggle="tab"
|
||
data-bs-target="#reviews" type="button" role="tab"
|
||
onclick="loadProductReviews({{ product.id }})">商品评价</button>
|
||
</li>
|
||
</ul>
|
||
|
||
<div class="tab-content" id="productDetailTabContent">
|
||
<!-- 商品详情 -->
|
||
<div class="tab-pane fade show active" id="description" role="tabpanel">
|
||
<div class="card">
|
||
<div class="card-body">
|
||
{% if product.description %}
|
||
<div class="product-description">
|
||
{{ product.description|replace('\n', '<br>')|safe }}
|
||
</div>
|
||
{% else %}
|
||
<p class="text-muted">暂无详细描述</p>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 规格参数 -->
|
||
<div class="tab-pane fade" id="specs" role="tabpanel">
|
||
<div class="card">
|
||
<div class="card-body">
|
||
<table class="table table-striped">
|
||
<tbody>
|
||
<tr>
|
||
<td width="150"><strong>商品名称</strong></td>
|
||
<td>{{ product.name }}</td>
|
||
</tr>
|
||
{% if product.brand %}
|
||
<tr>
|
||
<td><strong>商品品牌</strong></td>
|
||
<td>{{ product.brand }}</td>
|
||
</tr>
|
||
{% endif %}
|
||
<tr>
|
||
<td><strong>商品分类</strong></td>
|
||
<td>{{ product.category.name }}</td>
|
||
</tr>
|
||
{% if product.weight %}
|
||
<tr>
|
||
<td><strong>商品重量</strong></td>
|
||
<td>{{ product.weight }}kg</td>
|
||
</tr>
|
||
{% endif %}
|
||
<tr>
|
||
<td><strong>上架时间</strong></td>
|
||
<td>{{ product.created_at.strftime('%Y-%m-%d') }}</td>
|
||
</tr>
|
||
{% if inventory_list %}
|
||
<tr>
|
||
<td><strong>库存信息</strong></td>
|
||
<td>
|
||
{% if inventory_list|length == 1 %}
|
||
{{ inventory_list[0].stock }}件
|
||
{% else %}
|
||
多规格商品,请选择具体规格查看库存
|
||
{% endif %}
|
||
</td>
|
||
</tr>
|
||
{% endif %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 商品评价 -->
|
||
<div class="tab-pane fade" id="reviews" role="tabpanel">
|
||
<div class="card">
|
||
<div class="card-body">
|
||
<div id="reviewsContainer">
|
||
<div class="text-center p-4 text-muted">
|
||
<i class="bi bi-star"></i> 点击标签页加载评价
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 推荐商品 -->
|
||
{% if recommended_products %}
|
||
<div class="row mt-5">
|
||
<div class="col-12">
|
||
<h4><i class="bi bi-heart-fill text-danger"></i> 相关推荐</h4>
|
||
<hr>
|
||
</div>
|
||
{% for rec_product in recommended_products %}
|
||
<div class="col-lg-3 col-md-6 mb-4">
|
||
<div class="card h-100 product-card">
|
||
<a href="{{ url_for('main.product_detail', product_id=rec_product.id) }}" class="text-decoration-none">
|
||
{% if rec_product.main_image %}
|
||
<img src="{{ rec_product.main_image }}" class="card-img-top" alt="{{ rec_product.name }}"
|
||
style="height: 200px; object-fit: cover;">
|
||
{% else %}
|
||
<div class="card-img-top bg-light d-flex align-items-center justify-content-center"
|
||
style="height: 200px;">
|
||
<i class="bi bi-image text-muted" style="font-size: 3rem;"></i>
|
||
</div>
|
||
{% endif %}
|
||
</a>
|
||
<div class="card-body">
|
||
<h6 class="card-title">
|
||
<a href="{{ url_for('main.product_detail', product_id=rec_product.id) }}"
|
||
class="text-decoration-none text-dark">
|
||
{{ rec_product.name[:40] }}{% if rec_product.name|length > 40 %}...{% endif %}
|
||
</a>
|
||
</h6>
|
||
<div class="d-flex justify-content-between align-items-center">
|
||
<span class="text-danger fw-bold">¥{{ "%.2f"|format(rec_product.price) }}</span>
|
||
<small class="text-muted">销量{{ rec_product.sales_count }}</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<!-- 库存数据(用于JavaScript) -->
|
||
<script type="application/json" id="inventoryData">
|
||
{% if inventory_data %}
|
||
{{ inventory_data|tojson }}
|
||
{% else %}
|
||
[]
|
||
{% endif %}
|
||
</script>
|
||
|
||
<script>
|
||
// 设置全局变量供JS使用
|
||
window.productId = {{ product.id }};
|
||
window.currentProductId = {{ product.id }};
|
||
window.isLoggedIn = {% if session.user_id %}true{% else %}false{% endif %};
|
||
</script>
|
||
|
||
<!-- 图片查看模态框 -->
|
||
<div class="modal fade" id="imageModal" tabindex="-1">
|
||
<div class="modal-dialog modal-lg">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title">查看图片</h5>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||
</div>
|
||
<div class="modal-body text-center">
|
||
<img id="modalImage" src="" class="img-fluid" alt="评价图片">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endblock %}
|
||
|
||
{% block scripts %}
|
||
<script src="{{ url_for('static', filename='js/product_detail.js') }}"></script>
|
||
<script src="{{ url_for('static', filename='js/review.js') }}"></script>
|
||
<script>
|
||
// 处理登录状态检查
|
||
{% if not session.user_id %}
|
||
function addToCart() {
|
||
if (confirm('请先登录后再加入购物车,是否前往登录?')) {
|
||
window.location.href = '/auth/login?next=' + encodeURIComponent(window.location.pathname);
|
||
}
|
||
}
|
||
|
||
function buyNow() {
|
||
if (confirm('请先登录后再购买,是否前往登录?')) {
|
||
window.location.href = '/auth/login?next=' + encodeURIComponent(window.location.pathname);
|
||
}
|
||
}
|
||
|
||
function addToFavorites() {
|
||
if (confirm('请先登录后再收藏,是否前往登录?')) {
|
||
window.location.href = '/auth/login?next=' + encodeURIComponent(window.location.pathname);
|
||
}
|
||
}
|
||
{% endif %}
|
||
</script>
|
||
{% endblock %}
|