Oroqen-Vue/src/views/ProductDetail.vue

1104 lines
29 KiB
Vue
Raw Normal View History

2025-08-07 21:29:04 +08:00
<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.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.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.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>
<tr>
<td>产品名称</td>
<td>{{ 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>
</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.name.substring(0, 2) }}</text>
</svg>
</div>
</div>
<div class="product-info">
<h3 class="product-name">{{ 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'
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 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 relatedProducts = ref([
{
id: 2,
name: '鄂伦春族刺绣挂画',
price: 288,
color: '#2d5016'
},
{
id: 4,
name: '手工木雕摆件',
price: 128,
color: '#8b4513'
},
{
id: 7,
name: '桦皮茶具套装',
price: 368,
color: '#2d5016'
},
{
id: 8,
name: '民族风头饰',
price: 88,
color: '#8b4513'
}
])
// 模拟产品数据
const mockProducts = [
{
id: 1,
name: '桦皮工艺盒',
category: 'handicrafts',
description: '传统桦皮制作工艺,精美实用的收纳盒',
fullDescription: '这款桦皮工艺盒采用传统的鄂伦春族桦皮制作工艺,选用优质天然桦树皮为原料,经过精心处理和手工制作而成。盒子造型优美,实用性强,既可作为收纳盒使用,也是极具收藏价值的民族工艺品。每一个细节都体现了匠人的精湛技艺和对传统文化的传承。',
price: 168,
originalPrice: 198,
stock: 15,
craftsman: '关小云',
craftsmanTitle: '桦皮工艺传承人',
craftsmanDesc: '从事桦皮工艺制作30余年作品精美技艺精湛多次获得民族工艺品大赛奖项',
isNew: true,
isHot: false,
color: '#8b4513',
material: '天然桦皮',
size: '长20cm × 宽15cm × 高8cm',
weight: '约200g',
origin: '黑龙江大兴安岭',
culturalMeaning: '桦皮工艺是鄂伦春族传统手工艺的重要组成部分,承载着深厚的民族文化内涵。桦皮盒不仅是实用的生活用品,更是民族智慧的结晶,体现了鄂伦春族人民对自然的敬畏和对美好生活的追求。'
},
{
id: 2,
name: '鄂伦春族刺绣挂画',
category: 'handicrafts',
description: '手工刺绣,图案精美,寓意吉祥',
fullDescription: '这幅刺绣挂画采用传统的鄂伦春族刺绣工艺,以丝线为材料,在优质布料上精心绣制而成。图案设计融合了鄂伦春族的传统文化元素,色彩搭配和谐,寓意吉祥如意。每一针每一线都凝聚着绣娘的心血和对民族文化的热爱。',
price: 288,
stock: 8,
craftsman: '孟淑珍',
craftsmanTitle: '刺绣工艺传承人',
craftsmanDesc: '从事刺绣工艺25年擅长传统图案设计作品多次在民族工艺展览中获奖',
isNew: false,
isHot: true,
color: '#2d5016',
material: '优质丝线、棉布',
size: '长40cm × 宽30cm',
weight: '约150g',
origin: '黑龙江大兴安岭',
culturalMeaning: '刺绣是鄂伦春族妇女的传统技艺,图案多取材于自然和生活,体现了鄂伦春族人民对美好生活的向往和对自然的崇敬。'
},
{
id: 3,
name: '传统民族服饰',
category: 'clothing',
description: '复刻传统服饰,展现民族风采',
fullDescription: '这套传统民族服饰严格按照鄂伦春族传统服装样式制作,选用优质面料,手工缝制,细节精美。服装设计体现了鄂伦春族的文化特色,穿着舒适,既可用于文化表演,也可作为收藏品。',
price: 588,
stock: 5,
craftsman: '白热布',
craftsmanTitle: '民族服饰制作师',
craftsmanDesc: '专注民族服饰制作15年深谙传统服装制作工艺作品广受好评',
isNew: false,
isHot: true,
color: '#8b4513',
material: '优质棉麻、丝绸',
size: 'M码可定制',
weight: '约800g',
origin: '黑龙江大兴安岭',
culturalMeaning: '鄂伦春族传统服饰承载着深厚的文化内涵,每一个图案和色彩都有其特定的含义,体现了民族的智慧和审美。'
},
{
id: 4,
name: '手工木雕摆件',
category: 'handicrafts',
description: '精美木雕工艺,展现森林文化',
fullDescription: '这件木雕摆件选用优质木材,经过精心雕刻而成。作品造型生动,线条流畅,充分展现了鄂伦春族的森林文化特色。每一个细节都经过匠人的精心打磨,是极具艺术价值的工艺品。',
price: 128,
stock: 20,
craftsman: '关金山',
craftsmanTitle: '木雕工艺师',
craftsmanDesc: '从事木雕工艺20年擅长动物和自然题材雕刻技艺精湛',
isNew: false,
isHot: false,
color: '#2d5016',
material: '优质木材',
size: '长15cm × 宽10cm × 高12cm',
weight: '约300g',
origin: '黑龙江大兴安岭',
culturalMeaning: '木雕是鄂伦春族传统手工艺之一,多以森林动物和自然景观为题材,体现了民族与自然和谐共生的理念。'
},
{
id: 5,
name: '野生蓝莓干',
category: 'food',
description: '大兴安岭野生蓝莓,天然无添加',
fullDescription: '这款蓝莓干选用大兴安岭原始森林中的野生蓝莓,经过自然晾晒工艺制成。保持了蓝莓的天然营养成分,口感酸甜,是健康的天然零食。无任何人工添加剂,绿色健康。',
price: 58,
originalPrice: 68,
stock: 50,
isNew: true,
isHot: false,
color: '#8b4513',
material: '野生蓝莓',
size: '净含量200g',
weight: '约200g',
origin: '黑龙江大兴安岭',
culturalMeaning: '蓝莓是大兴安岭的特产,鄂伦春族人民世代采集食用,是大自然赐予的珍贵礼物。'
},
{
id: 6,
name: '鄂伦春文化笔记本',
category: 'cultural',
description: '融入民族元素的精美笔记本',
fullDescription: '这款笔记本封面设计融入了鄂伦春族传统文化元素,采用优质纸张制作,书写流畅。既实用又具有文化纪念意义,是学习工作的好伴侣,也是了解鄂伦春族文化的窗口。',
price: 35,
stock: 100,
isNew: false,
isHot: false,
color: '#2d5016',
material: '优质纸张、硬质封面',
size: 'A5规格',
weight: '约250g',
origin: '黑龙江大兴安岭',
culturalMeaning: '笔记本设计元素来源于鄂伦春族传统图案,每一个符号都承载着深厚的文化内涵。'
},
{
id: 7,
name: '桦皮茶具套装',
category: 'handicrafts',
description: '传统桦皮工艺制作的茶具',
fullDescription: '这套桦皮茶具采用传统工艺制作,包含茶壶、茶杯等完整配件。桦皮材质天然环保,具有独特的香味,用于泡茶别有一番风味。是品茶爱好者和文化收藏者的理想选择。',
price: 368,
stock: 3,
craftsman: '关小云',
craftsmanTitle: '桦皮工艺传承人',
craftsmanDesc: '从事桦皮工艺制作30余年作品精美技艺精湛多次获得民族工艺品大赛奖项',
isNew: false,
isHot: true,
color: '#8b4513',
material: '天然桦皮',
size: '茶壶高12cm茶杯直径8cm',
weight: '约500g',
origin: '黑龙江大兴安岭',
culturalMeaning: '桦皮茶具体现了鄂伦春族人民的生活智慧,将实用性与艺术性完美结合。'
},
{
id: 8,
name: '民族风头饰',
category: 'clothing',
description: '传统图案设计的精美头饰',
fullDescription: '这款头饰采用传统鄂伦春族图案设计,手工制作,工艺精美。可用于民族服装搭配,也可作为装饰品收藏。设计典雅,做工精细,充分展现了鄂伦春族的文化魅力。',
price: 88,
stock: 12,
isNew: true,
isHot: false,
color: '#2d5016',
material: '优质布料、装饰珠子',
size: '可调节尺寸',
weight: '约50g',
origin: '黑龙江大兴安岭',
culturalMeaning: '头饰在鄂伦春族传统文化中具有重要地位,不同的图案和颜色代表不同的寓意和身份。'
}
]
// 计算属性
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(() => {
const productId = parseInt(route.params.id as string)
// 模拟从API获取产品数据
setTimeout(() => {
const foundProduct = mockProducts.find(p => p.id === productId)
if (foundProduct) {
product.value = foundProduct
} else {
ElMessage.error('产品不存在')
router.push('/products')
}
}, 500)
})
</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>