984 lines
24 KiB
Vue
984 lines
24 KiB
Vue
<template>
|
||
<div class="product-detail" v-if="product">
|
||
<!-- 面包屑导航 -->
|
||
<section class="breadcrumb-section">
|
||
<div class="container">
|
||
<div class="breadcrumb">
|
||
<router-link to="/">首页</router-link>
|
||
<span class="separator">/</span>
|
||
<router-link to="/products">产品中心</router-link>
|
||
<span class="separator">/</span>
|
||
<span class="current">{{ product.productName || product.name || '产品详情' }}</span>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 产品详情主体 -->
|
||
<section class="product-main section">
|
||
<div class="container">
|
||
<div class="product-layout">
|
||
<!-- 产品图片 -->
|
||
<div class="product-images">
|
||
<div class="main-image">
|
||
<div class="image-placeholder">
|
||
<svg viewBox="0 0 400 400" width="100%" height="400">
|
||
<rect width="400" height="400" :fill="product.color" opacity="0.2"/>
|
||
<circle cx="200" cy="200" r="80" :fill="product.color" opacity="0.5"/>
|
||
<text x="200" y="210" text-anchor="middle" :fill="product.color"
|
||
font-size="24" font-weight="bold">{{ (product.productName || product.name || '产品').substring(0, 2) }}</text>
|
||
</svg>
|
||
</div>
|
||
</div>
|
||
<div class="thumbnail-list">
|
||
<div v-for="i in 4" :key="i" class="thumbnail" :class="{ active: i === 1 }">
|
||
<svg viewBox="0 0 100 100" width="100%" height="80">
|
||
<rect width="100" height="100" :fill="product.color" opacity="0.3"/>
|
||
<text x="50" y="55" text-anchor="middle" :fill="product.color"
|
||
font-size="12" font-weight="bold">{{ i }}</text>
|
||
</svg>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 产品信息 -->
|
||
<div class="product-info">
|
||
<div class="product-badges">
|
||
<span v-if="product.isNew" class="badge new">新品</span>
|
||
<span v-if="product.isHot" class="badge hot">热销</span>
|
||
</div>
|
||
|
||
<h1 class="product-title">{{ product.productName || product.name || '产品名称' }}</h1>
|
||
<p class="product-subtitle">{{ getCategoryName(product.category) }}</p>
|
||
|
||
<div class="price-section">
|
||
<div class="current-price">¥{{ product.price }}</div>
|
||
<div v-if="product.originalPrice" class="original-price">原价:¥{{ product.originalPrice }}</div>
|
||
</div>
|
||
|
||
<div class="product-specs">
|
||
<div class="spec-item">
|
||
<label>材质:</label>
|
||
<span>{{ product.material || '天然桦皮' }}</span>
|
||
</div>
|
||
<div class="spec-item">
|
||
<label>尺寸:</label>
|
||
<span>{{ product.size || '长20cm × 宽15cm × 高8cm' }}</span>
|
||
</div>
|
||
<div class="spec-item">
|
||
<label>重量:</label>
|
||
<span>{{ product.weight || '约200g' }}</span>
|
||
</div>
|
||
<div class="spec-item">
|
||
<label>产地:</label>
|
||
<span>{{ product.origin || '黑龙江大兴安岭' }}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="craftsman-section" v-if="product.craftsman">
|
||
<h3>手工艺人</h3>
|
||
<div class="craftsman-info">
|
||
<div class="craftsman-avatar">
|
||
<el-icon size="40"><User /></el-icon>
|
||
</div>
|
||
<div class="craftsman-details">
|
||
<div class="craftsman-name">{{ product.craftsman }}</div>
|
||
<div class="craftsman-title">{{ product.craftsmanTitle || '桦皮工艺传承人' }}</div>
|
||
<div class="craftsman-desc">{{ product.craftsmanDesc || '从事桦皮工艺制作30余年,作品精美,技艺精湛' }}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="stock-section">
|
||
<div class="stock-info">
|
||
<span :class="['stock-status', product.stock > 0 ? 'in-stock' : 'out-stock']">
|
||
{{ product.stock > 0 ? `现货${product.stock}件` : '暂时缺货' }}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="purchase-section">
|
||
<div class="quantity-selector">
|
||
<label>数量:</label>
|
||
<div class="quantity-controls">
|
||
<el-button size="small" @click="decreaseQuantity" :disabled="quantity <= 1">-</el-button>
|
||
<span class="quantity">{{ quantity }}</span>
|
||
<el-button size="small" @click="increaseQuantity" :disabled="quantity >= product.stock">+</el-button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="action-buttons">
|
||
<el-button
|
||
type="primary"
|
||
size="large"
|
||
:disabled="product.stock === 0"
|
||
@click="addToCart"
|
||
>
|
||
加入购物车
|
||
</el-button>
|
||
<el-button
|
||
type="danger"
|
||
size="large"
|
||
:disabled="product.stock === 0"
|
||
@click="buyNow"
|
||
>
|
||
立即购买
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 产品详细信息 -->
|
||
<section class="product-details section">
|
||
<div class="container">
|
||
<el-tabs v-model="activeTab" class="detail-tabs">
|
||
<el-tab-pane label="产品详情" name="details">
|
||
<div class="detail-content">
|
||
<h3>产品介绍</h3>
|
||
<p>{{ product.fullDescription || product.description }}</p>
|
||
|
||
<h3>制作工艺</h3>
|
||
<div class="craft-process">
|
||
<div v-for="(step, index) in craftSteps" :key="index" class="craft-step">
|
||
<div class="step-number">{{ index + 1 }}</div>
|
||
<div class="step-content">
|
||
<h4>{{ step.title }}</h4>
|
||
<p>{{ step.description }}</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<h3>文化内涵</h3>
|
||
<p>{{ product.culturalMeaning || '桦皮工艺是鄂伦春族传统手工艺,承载着深厚的民族文化内涵,每一件作品都体现了匠人的精湛技艺和对传统文化的传承。' }}</p>
|
||
</div>
|
||
</el-tab-pane>
|
||
|
||
<el-tab-pane label="规格参数" name="specs">
|
||
<div class="specs-table">
|
||
<table>
|
||
<tbody>
|
||
<tr>
|
||
<td>产品名称</td>
|
||
<td>{{ product.productName || product.name || '产品名称' }}</td>
|
||
</tr>
|
||
<tr>
|
||
<td>产品分类</td>
|
||
<td>{{ getCategoryName(product.category) }}</td>
|
||
</tr>
|
||
<tr>
|
||
<td>材质</td>
|
||
<td>{{ product.material || '天然桦皮' }}</td>
|
||
</tr>
|
||
<tr>
|
||
<td>尺寸</td>
|
||
<td>{{ product.size || '长20cm × 宽15cm × 高8cm' }}</td>
|
||
</tr>
|
||
<tr>
|
||
<td>重量</td>
|
||
<td>{{ product.weight || '约200g' }}</td>
|
||
</tr>
|
||
<tr>
|
||
<td>产地</td>
|
||
<td>{{ product.origin || '黑龙江大兴安岭' }}</td>
|
||
</tr>
|
||
<tr>
|
||
<td>保养方式</td>
|
||
<td>避免阳光直射,保持干燥,定期清洁</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</el-tab-pane>
|
||
|
||
<el-tab-pane label="用户评价" name="reviews">
|
||
<div class="reviews-section">
|
||
<div class="reviews-summary">
|
||
<div class="rating-overview">
|
||
<div class="rating-score">4.8</div>
|
||
<div class="rating-stars">
|
||
<el-icon v-for="i in 5" :key="i" color="#ffd700"><StarFilled /></el-icon>
|
||
</div>
|
||
<div class="rating-count">基于 {{ reviews.length }} 条评价</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="reviews-list">
|
||
<div v-for="review in reviews" :key="review.id" class="review-item">
|
||
<div class="review-header">
|
||
<div class="reviewer-info">
|
||
<div class="reviewer-avatar">
|
||
<el-icon><User /></el-icon>
|
||
</div>
|
||
<div class="reviewer-details">
|
||
<div class="reviewer-name">{{ review.userName }}</div>
|
||
<div class="review-date">{{ review.date }}</div>
|
||
</div>
|
||
</div>
|
||
<div class="review-rating">
|
||
<el-icon v-for="i in review.rating" :key="i" color="#ffd700"><StarFilled /></el-icon>
|
||
</div>
|
||
</div>
|
||
<div class="review-content">
|
||
{{ review.content }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</el-tab-pane>
|
||
</el-tabs>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 相关推荐 -->
|
||
<section class="related-products section">
|
||
<div class="container">
|
||
<h2 class="section-title">相关推荐</h2>
|
||
<div class="products-grid">
|
||
<div
|
||
v-for="relatedProduct in relatedProducts"
|
||
:key="relatedProduct.id"
|
||
class="product-card card"
|
||
@click="$router.push(`/product/${relatedProduct.id}`)"
|
||
>
|
||
<div class="product-image">
|
||
<div class="image-placeholder">
|
||
<svg viewBox="0 0 200 150" width="100%" height="150">
|
||
<rect width="200" height="150" :fill="relatedProduct.color" opacity="0.2"/>
|
||
<circle cx="100" cy="75" r="30" :fill="relatedProduct.color" opacity="0.5"/>
|
||
<text x="100" y="80" text-anchor="middle" :fill="relatedProduct.color"
|
||
font-size="12" font-weight="bold">{{ (relatedProduct.productName || relatedProduct.name || '产品').substring(0, 2) }}</text>
|
||
</svg>
|
||
</div>
|
||
</div>
|
||
<div class="product-info">
|
||
<h3 class="product-name">{{ relatedProduct.productName || relatedProduct.name || '产品名称' }}</h3>
|
||
<div class="product-price">¥{{ relatedProduct.price }}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
|
||
<div v-else class="loading">
|
||
<el-icon class="is-loading" size="50"><Loading /></el-icon>
|
||
<p>加载中...</p>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, computed, onMounted } from 'vue'
|
||
import { useRoute, useRouter } from 'vue-router'
|
||
import { useMainStore } from '../stores'
|
||
import { User, StarFilled, Loading } from '@element-plus/icons-vue'
|
||
import { ElMessage } from 'element-plus'
|
||
import { productApi } from '../api/product'
|
||
|
||
const route = useRoute()
|
||
const router = useRouter()
|
||
const store = useMainStore()
|
||
|
||
// 响应式数据
|
||
const product = ref<any>(null)
|
||
const quantity = ref(1)
|
||
const activeTab = ref('details')
|
||
|
||
// 产品分类
|
||
const categories = ref([
|
||
{ id: 'handicrafts', name: '手工艺品' },
|
||
{ id: 'food', name: '特色食品' },
|
||
{ id: 'cultural', name: '文创产品' },
|
||
{ id: 'clothing', name: '服饰配饰' }
|
||
])
|
||
|
||
// 制作工艺步骤
|
||
const craftSteps = ref([
|
||
{
|
||
title: '选材',
|
||
description: '选择优质的桦树皮,确保材质坚韧有韧性'
|
||
},
|
||
{
|
||
title: '处理',
|
||
description: '将桦皮进行清洁、软化处理,去除杂质'
|
||
},
|
||
{
|
||
title: '裁剪',
|
||
description: '根据设计图案精确裁剪桦皮材料'
|
||
},
|
||
{
|
||
title: '缝制',
|
||
description: '使用传统针法将各部分缝制组装'
|
||
},
|
||
{
|
||
title: '装饰',
|
||
description: '添加民族特色图案和装饰元素'
|
||
},
|
||
{
|
||
title: '整理',
|
||
description: '最后整理修饰,确保产品完美呈现'
|
||
}
|
||
])
|
||
|
||
// 相关产品
|
||
const relatedProducts = ref<any[]>([])
|
||
|
||
// 用户评价
|
||
const reviews = ref([
|
||
{
|
||
id: 1,
|
||
userName: '文化爱好者',
|
||
rating: 5,
|
||
date: '2024-01-15',
|
||
content: '非常精美的工艺品,做工精细,很有民族特色,值得收藏!'
|
||
},
|
||
{
|
||
id: 2,
|
||
userName: '传统工艺迷',
|
||
rating: 5,
|
||
date: '2024-01-10',
|
||
content: '质量很好,包装精美,能感受到浓厚的鄂伦春族文化气息。'
|
||
},
|
||
{
|
||
id: 3,
|
||
userName: '手工艺收藏家',
|
||
rating: 4,
|
||
date: '2024-01-05',
|
||
content: '工艺精湛,材质天然,是很好的文化艺术品。'
|
||
}
|
||
])
|
||
|
||
// 计算属性
|
||
const getCategoryName = (categoryId: string) => {
|
||
const category = categories.value.find(c => c.id === categoryId)
|
||
return category ? category.name : ''
|
||
}
|
||
|
||
// 方法
|
||
const increaseQuantity = () => {
|
||
if (quantity.value < product.value.stock) {
|
||
quantity.value++
|
||
}
|
||
}
|
||
|
||
const decreaseQuantity = () => {
|
||
if (quantity.value > 1) {
|
||
quantity.value--
|
||
}
|
||
}
|
||
|
||
const addToCart = () => {
|
||
if (product.value.stock === 0) {
|
||
ElMessage.warning('商品暂时缺货')
|
||
return
|
||
}
|
||
|
||
for (let i = 0; i < quantity.value; i++) {
|
||
store.addToCart(product.value)
|
||
}
|
||
ElMessage.success(`已添加 ${quantity.value} 件商品到购物车`)
|
||
}
|
||
|
||
const buyNow = () => {
|
||
if (product.value.stock === 0) {
|
||
ElMessage.warning('商品暂时缺货')
|
||
return
|
||
}
|
||
|
||
addToCart()
|
||
router.push('/cart')
|
||
}
|
||
|
||
// 生命周期
|
||
onMounted(async () => {
|
||
const productId = route.params.id as string
|
||
|
||
if (!productId) {
|
||
ElMessage.error('产品ID无效')
|
||
router.push('/products')
|
||
return
|
||
}
|
||
|
||
try {
|
||
// 从API获取产品数据
|
||
const response = await productApi.getProductById(productId)
|
||
if (response && response.id) {
|
||
product.value = {
|
||
...response,
|
||
color: getRandomColor(),
|
||
// 设置默认值
|
||
material: response.material || '天然材料',
|
||
size: response.size || '标准尺寸',
|
||
weight: response.weight || '约200g',
|
||
origin: response.origin || '黑龙江大兴安岭',
|
||
culturalMeaning: response.culturalMeaning || '承载着深厚的鄂伦春族文化内涵'
|
||
}
|
||
|
||
// 获取相关产品
|
||
await getRelatedProducts(response.categoryId)
|
||
} else {
|
||
ElMessage.error('产品不存在')
|
||
router.push('/products')
|
||
}
|
||
} catch (error) {
|
||
console.error('获取产品详情失败:', error)
|
||
// 检查是否是"未找到对应数据"的错误
|
||
if (error.message && error.message.includes('未找到对应数据')) {
|
||
ElMessage.error('产品不存在')
|
||
} else {
|
||
ElMessage.error('获取产品详情失败,请稍后重试')
|
||
}
|
||
router.push('/products')
|
||
}
|
||
})
|
||
|
||
// 获取相关产品
|
||
const getRelatedProducts = async (categoryId: string) => {
|
||
try {
|
||
const response = await productApi.getProductList({
|
||
pageNo: 1,
|
||
pageSize: 4,
|
||
categoryId: categoryId,
|
||
status: 1
|
||
})
|
||
if (response?.records && response.records.length > 0) {
|
||
relatedProducts.value = response.records
|
||
.filter((p: any) => p.id !== product.value.id)
|
||
.slice(0, 4)
|
||
.map((p: any) => ({
|
||
...p,
|
||
color: getRandomColor()
|
||
}))
|
||
} else {
|
||
relatedProducts.value = []
|
||
}
|
||
} catch (error) {
|
||
console.error('获取相关产品失败:', error)
|
||
relatedProducts.value = []
|
||
}
|
||
}
|
||
|
||
// 获取随机颜色
|
||
const getRandomColor = () => {
|
||
const colors = ['#8b4513', '#2d5016']
|
||
return colors[Math.floor(Math.random() * colors.length)]
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
/* 面包屑导航 */
|
||
.breadcrumb-section {
|
||
background: var(--background-light);
|
||
padding: 1rem 0;
|
||
border-bottom: 1px solid var(--border-color);
|
||
}
|
||
|
||
.breadcrumb {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
.breadcrumb a {
|
||
color: var(--primary-color);
|
||
text-decoration: none;
|
||
}
|
||
|
||
.breadcrumb a:hover {
|
||
text-decoration: underline;
|
||
}
|
||
|
||
.separator {
|
||
color: var(--text-light);
|
||
}
|
||
|
||
.current {
|
||
color: var(--text-color);
|
||
font-weight: 500;
|
||
}
|
||
|
||
/* 产品主体布局 */
|
||
.product-layout {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 4rem;
|
||
align-items: start;
|
||
}
|
||
|
||
/* 产品图片 */
|
||
.product-images {
|
||
position: sticky;
|
||
top: 2rem;
|
||
}
|
||
|
||
.main-image {
|
||
width: 100%;
|
||
margin-bottom: 1rem;
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.image-placeholder {
|
||
width: 100%;
|
||
height: 400px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: var(--background-light);
|
||
}
|
||
|
||
.thumbnail-list {
|
||
display: flex;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.thumbnail {
|
||
width: 80px;
|
||
height: 80px;
|
||
border-radius: 4px;
|
||
overflow: hidden;
|
||
cursor: pointer;
|
||
border: 2px solid transparent;
|
||
transition: border-color 0.3s ease;
|
||
}
|
||
|
||
.thumbnail.active,
|
||
.thumbnail:hover {
|
||
border-color: var(--primary-color);
|
||
}
|
||
|
||
/* 产品信息 */
|
||
.product-info {
|
||
padding: 1rem 0;
|
||
}
|
||
|
||
.product-badges {
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
.badge {
|
||
display: inline-block;
|
||
padding: 4px 12px;
|
||
border-radius: 12px;
|
||
font-size: 0.8rem;
|
||
font-weight: bold;
|
||
color: white;
|
||
margin-right: 0.5rem;
|
||
}
|
||
|
||
.badge.new {
|
||
background: #ff4757;
|
||
}
|
||
|
||
.badge.hot {
|
||
background: #ff6b35;
|
||
}
|
||
|
||
.product-title {
|
||
font-size: 2rem;
|
||
font-weight: bold;
|
||
color: var(--primary-color);
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
|
||
.product-subtitle {
|
||
color: var(--secondary-color);
|
||
font-size: 1.1rem;
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
.price-section {
|
||
margin-bottom: 2rem;
|
||
}
|
||
|
||
.current-price {
|
||
font-size: 2.5rem;
|
||
font-weight: bold;
|
||
color: var(--secondary-color);
|
||
}
|
||
|
||
.original-price {
|
||
font-size: 1.2rem;
|
||
color: var(--text-light);
|
||
text-decoration: line-through;
|
||
margin-left: 1rem;
|
||
}
|
||
|
||
.product-specs {
|
||
background: var(--background-light);
|
||
padding: 1.5rem;
|
||
border-radius: 8px;
|
||
margin-bottom: 2rem;
|
||
}
|
||
|
||
.spec-item {
|
||
display: flex;
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
|
||
.spec-item:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.spec-item label {
|
||
font-weight: 500;
|
||
color: var(--text-color);
|
||
width: 80px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.craftsman-section {
|
||
background: var(--background-light);
|
||
padding: 1.5rem;
|
||
border-radius: 8px;
|
||
margin-bottom: 2rem;
|
||
}
|
||
|
||
.craftsman-section h3 {
|
||
margin-bottom: 1rem;
|
||
color: var(--primary-color);
|
||
}
|
||
|
||
.craftsman-info {
|
||
display: flex;
|
||
gap: 1rem;
|
||
}
|
||
|
||
.craftsman-avatar {
|
||
width: 60px;
|
||
height: 60px;
|
||
border-radius: 50%;
|
||
background: var(--primary-color);
|
||
color: white;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.craftsman-name {
|
||
font-weight: bold;
|
||
font-size: 1.1rem;
|
||
margin-bottom: 0.25rem;
|
||
}
|
||
|
||
.craftsman-title {
|
||
color: var(--secondary-color);
|
||
font-size: 0.9rem;
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
|
||
.craftsman-desc {
|
||
color: var(--text-light);
|
||
font-size: 0.9rem;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.stock-section {
|
||
margin-bottom: 2rem;
|
||
}
|
||
|
||
.stock-status {
|
||
font-size: 1rem;
|
||
padding: 8px 16px;
|
||
border-radius: 20px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.stock-status.in-stock {
|
||
background: rgba(46, 160, 67, 0.1);
|
||
color: #2ea043;
|
||
}
|
||
|
||
.stock-status.out-stock {
|
||
background: rgba(218, 54, 51, 0.1);
|
||
color: #da3633;
|
||
}
|
||
|
||
.purchase-section {
|
||
border-top: 1px solid var(--border-color);
|
||
padding-top: 2rem;
|
||
}
|
||
|
||
.quantity-selector {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 1rem;
|
||
margin-bottom: 2rem;
|
||
}
|
||
|
||
.quantity-selector label {
|
||
font-weight: 500;
|
||
}
|
||
|
||
.quantity-controls {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 1rem;
|
||
}
|
||
|
||
.quantity {
|
||
font-size: 1.2rem;
|
||
font-weight: bold;
|
||
min-width: 2rem;
|
||
text-align: center;
|
||
}
|
||
|
||
.action-buttons {
|
||
display: flex;
|
||
gap: 1rem;
|
||
}
|
||
|
||
.action-buttons .el-button {
|
||
flex: 1;
|
||
height: 50px;
|
||
font-size: 1.1rem;
|
||
}
|
||
|
||
/* 详细信息标签页 */
|
||
.detail-tabs {
|
||
margin-top: 3rem;
|
||
}
|
||
|
||
.detail-content h3 {
|
||
color: var(--primary-color);
|
||
margin: 2rem 0 1rem;
|
||
font-size: 1.3rem;
|
||
}
|
||
|
||
.detail-content h3:first-child {
|
||
margin-top: 0;
|
||
}
|
||
|
||
.detail-content p {
|
||
line-height: 1.8;
|
||
color: var(--text-color);
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
.craft-process {
|
||
margin: 2rem 0;
|
||
}
|
||
|
||
.craft-step {
|
||
display: flex;
|
||
gap: 1rem;
|
||
margin-bottom: 2rem;
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.step-number {
|
||
width: 40px;
|
||
height: 40px;
|
||
border-radius: 50%;
|
||
background: var(--primary-color);
|
||
color: white;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-weight: bold;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.step-content h4 {
|
||
color: var(--primary-color);
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
|
||
.step-content p {
|
||
color: var(--text-light);
|
||
margin: 0;
|
||
}
|
||
|
||
.specs-table table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
}
|
||
|
||
.specs-table td {
|
||
padding: 1rem;
|
||
border-bottom: 1px solid var(--border-color);
|
||
}
|
||
|
||
.specs-table td:first-child {
|
||
font-weight: 500;
|
||
color: var(--text-color);
|
||
width: 150px;
|
||
}
|
||
|
||
.reviews-summary {
|
||
background: var(--background-light);
|
||
padding: 2rem;
|
||
border-radius: 8px;
|
||
margin-bottom: 2rem;
|
||
text-align: center;
|
||
}
|
||
|
||
.rating-score {
|
||
font-size: 3rem;
|
||
font-weight: bold;
|
||
color: var(--primary-color);
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
|
||
.rating-stars {
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
|
||
.rating-count {
|
||
color: var(--text-light);
|
||
}
|
||
|
||
.review-item {
|
||
border-bottom: 1px solid var(--border-color);
|
||
padding: 1.5rem 0;
|
||
}
|
||
|
||
.review-item:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.review-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
.reviewer-info {
|
||
display: flex;
|
||
gap: 1rem;
|
||
align-items: center;
|
||
}
|
||
|
||
.reviewer-avatar {
|
||
width: 40px;
|
||
height: 40px;
|
||
border-radius: 50%;
|
||
background: var(--primary-color);
|
||
color: white;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.reviewer-name {
|
||
font-weight: 500;
|
||
margin-bottom: 0.25rem;
|
||
}
|
||
|
||
.reviewer-date {
|
||
color: var(--text-light);
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
.review-content {
|
||
line-height: 1.6;
|
||
color: var(--text-color);
|
||
}
|
||
|
||
/* 相关推荐 */
|
||
.related-products {
|
||
background: var(--background-light);
|
||
}
|
||
|
||
.products-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||
gap: 1.5rem;
|
||
}
|
||
|
||
.product-card {
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.product-card:hover {
|
||
transform: translateY(-5px);
|
||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
|
||
}
|
||
|
||
.product-card .product-image {
|
||
width: 100%;
|
||
height: 150px;
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
.product-card .product-info {
|
||
padding: 1rem;
|
||
}
|
||
|
||
.product-card .product-name {
|
||
font-size: 1rem;
|
||
font-weight: 500;
|
||
color: var(--primary-color);
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
|
||
.product-card .product-price {
|
||
font-size: 1.2rem;
|
||
font-weight: bold;
|
||
color: var(--secondary-color);
|
||
}
|
||
|
||
/* 加载状态 */
|
||
.loading {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
height: 50vh;
|
||
color: var(--text-light);
|
||
}
|
||
|
||
.loading p {
|
||
margin-top: 1rem;
|
||
font-size: 1.1rem;
|
||
}
|
||
|
||
/* 响应式设计 */
|
||
@media (max-width: 768px) {
|
||
.product-layout {
|
||
grid-template-columns: 1fr;
|
||
gap: 2rem;
|
||
}
|
||
|
||
.product-images {
|
||
position: static;
|
||
}
|
||
|
||
.product-title {
|
||
font-size: 1.5rem;
|
||
}
|
||
|
||
.current-price {
|
||
font-size: 2rem;
|
||
}
|
||
|
||
.action-buttons {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.craftsman-info {
|
||
flex-direction: column;
|
||
text-align: center;
|
||
}
|
||
|
||
.quantity-selector {
|
||
flex-direction: column;
|
||
align-items: stretch;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.quantity-controls {
|
||
justify-content: center;
|
||
}
|
||
|
||
.products-grid {
|
||
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
||
}
|
||
}
|
||
</style> |