You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

537 lines
13 KiB

<template>
<div>
<div class="product-detail-container">
<!-- 左侧图片轮播区域 -->
<div class="left-section">
<el-carousel
ref="carousel"
height="500px"
class="product-carousel"
@change="handleCarouselChange"
indicator-position="none"
>
<el-carousel-item v-for="(img, index) in productImages" :key="index">
<img
:src="img"
:alt="`商品图片${index + 1}`"
class="carousel-img"
/>
</el-carousel-item>
</el-carousel>
<div class="hint-text">
温馨提示以上图片仅供参考若图片与实物有所不同则以实物为准
</div>
<!-- 自定义图片指示器 -->
<div class="image-indicators">
<div
v-for="(img, index) in productImages"
:key="index"
class="indicator-item"
:class="{ active: activeIndex === index }"
@click="handleIndicatorClick(index)"
>
<img :src="img" :alt="`缩略图${index + 1}`" class="indicator-img" />
</div>
</div>
</div>
<!-- 右侧商品信息区域保持不变 -->
<div class="right-section">
<h2 class="product-title">
{{ productTitle }}
<span class="product-count">[{{ productCount }}]</span>
</h2>
<div class="product-tags">
<el-tag type="info" size="mini">[产品标签]</el-tag>
<span class="subtitle">{{ productSubtitle }}</span>
</div>
<div class="price-info">
<span class="price-label">售价</span>
<span class="price-amount">¥{{ productPrice }}</span>
<span class="sales-volume">已售 {{ salesVolume }}万</span>
</div>
<div class="product-attr">
<div class="attr-item">
<span class="attr-label">起订量</span>
<span class="attr-value">{{ moq }}</span>
</div>
<div class="attr-item">
<span class="attr-label">收货方式</span>
<span class="attr-value">{{ deliveryMethod }}</span>
</div>
<div class="attr-item spec-group">
<span class="attr-label">商品规格</span>
<div class="custom-radio-group">
<label
v-for="(spec, idx) in productSpecs"
:key="idx"
class="custom-radio"
:class="{ 'is-checked': selectedSpec === spec }"
@click="selectedSpec = spec"
>
<span class="radio-text">{{ spec }}</span>
</label>
</div>
</div>
<div class="attr-item">
<span class="attr-label">发货地</span>
<span class="attr-value">{{ origin }}</span>
</div>
<div class="attr-item">
<span class="attr-label">其他</span>
<span class="attr-value">{{ otherInfo }}</span>
</div>
<div class="attr-item">
<span class="attr-label">配送范围</span>
<span class="attr-value">{{ deliveryRange }}</span>
</div>
</div>
<div class="quantity-control">
<el-button
icon="el-icon-minus"
circle
@click="decreaseQuantity"
></el-button>
<span class="quantity-value">{{ quantity }}</span>
<el-button
icon="el-icon-plus"
circle
@click="increaseQuantity"
></el-button>
<el-button type="primary" class="buy-btn">一口价购买</el-button>
<el-button type="success" class="cart-btn">加入购物车</el-button>
<el-button type="info" class="bargain-btn">议价</el-button>
<el-button
icon="el-icon-share"
circle
class="share-btn"
@click="handleShare"
></el-button>
</div>
</div>
</div>
<div class="product-bottom">
<!-- 左侧热销模块 -->
<div class="hot-recommend-sidebar">
<div class="hot-title">热销推荐</div>
<div
v-for="(item, index) in hotRecommendData"
:key="index"
class="product-item"
>
<img v-lazy="item.imgUrl" alt="" />
<div class="product-name">{{ item.title }}</div>
<div class="product-price">¥{{ item.price }}</div>
</div>
</div>
<!-- 右侧 -->
<div class="product-right">
<div class="product-tabs">
<span
:class="['tab-item', { active: !tabIndex }]"
@click="tabIndex = 0"
>商品详情</span
>
<span
:class="['tab-item', { active: tabIndex }]"
@click="tabIndex = 1"
>商品评价(125)</span
>
</div>
<!-- 商品详情 -->
<div class="product-detail-main" v-show="!tabIndex"></div>
<!-- 商品评价 -->
<Evaluate v-show="tabIndex" />
</div>
</div>
</div>
</template>
<script>
import Evaluate from "@/components/product/Evaluate.vue";
export default {
name: "ProductDetail",
components: {
Evaluate,
},
data() {
return {
productImages: [
"https://picsum.photos/id/102/500/500",
"https://picsum.photos/id/103/500/500",
"https://picsum.photos/id/104/500/500",
"https://picsum.photos/id/105/500/500",
],
activeIndex: 0, // 当前激活的图片索引
productTitle: "面包",
productCount: "52个",
productSubtitle: "副标题",
productPrice: 509,
salesVolume: 1.22,
moq: 1,
deliveryMethod: "邮寄",
productSpecs: ["规格一", "规格二", "规格三", "规格四", "规格五"],
selectedSpec: "规格一",
origin: "江苏省苏州市吴中区",
otherInfo: "下单填写留言,即免费赠送精美贺卡!",
deliveryRange: "全国(可配送至全国1000多个城市,苏州市区内免配送费)",
quantity: 1,
hotRecommendData: [
{
id: 1,
imgUrl: "https://picsum.photos/id/103/500/500", // 替换成实际图片地址,也可用本地相对路径
title: "北欧花艺素雅仿真花",
price: 359,
},
{
id: 2,
imgUrl: "https://picsum.photos/id/103/500/500",
title: "生日玫瑰鲜花",
price: 359,
},
{
id: 3,
imgUrl: "https://picsum.photos/id/103/500/500",
title: "香雪兰小苍兰鲜花",
price: 359,
},
{
id: 4,
imgUrl: "https://picsum.photos/id/103/500/500",
title: "现代创意简约仿真花艺",
price: 359,
},
],
tabIndex: 0,
};
},
methods: {
// 处理轮播图切换事件 - 同步更新activeIndex
handleCarouselChange(index) {
this.activeIndex = index;
},
// 处理指示器点击事件 - 修复切换功能
handleIndicatorClick(index) {
// 1. 更新当前激活索引
this.activeIndex = index;
// 2. 关键修复:确保轮播组件已加载,再调用切换方法
this.$nextTick(() => {
if (this.$refs.carousel) {
// 调用Element UI轮播组件的官方方法切换图片
this.$refs.carousel.setActiveItem(index);
}
});
},
decreaseQuantity() {
if (this.quantity > 1) {
this.quantity--;
}
},
increaseQuantity() {
this.quantity++;
},
handleShare() {
this.$message.info("分享功能待实现");
},
},
mounted() {
// 初始化检查轮播组件是否存在
if (!this.$refs.carousel) {
console.warn("轮播组件未正确加载,请检查ref属性是否设置");
}
},
};
</script>
<style lang="scss" scoped>
.product-detail-container {
display: flex;
padding: 20px;
background-color: #fff;
border: 1px solid #eaeaea;
border-radius: 4px;
.left-section {
width: 40%;
margin-right: 20px;
display: flex;
flex-direction: column;
.product-carousel {
border: 1px solid #eaeaea;
border-radius: 4px;
margin-bottom: 15px;
.carousel-img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.hint-text {
color: #ccc;
font-size: 12px;
margin-bottom: 10px;
}
.image-indicators {
display: flex;
gap: 10px;
justify-content: center;
padding: 5px 0;
.indicator-item {
width: 80px;
height: 80px;
cursor: pointer;
border: 2px solid transparent;
border-radius: 4px;
transition: all 0.3s ease;
overflow: hidden;
&.active {
border-color: #409eff;
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
}
.indicator-img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
}
}
}
.right-section {
width: 60%;
.product-title {
font-size: 20px;
font-weight: bold;
margin-bottom: 10px;
.product-count {
font-size: 14px;
color: #666;
}
}
.product-tags {
margin-bottom: 15px;
.subtitle {
margin-left: 5px;
color: #666;
}
}
.price-info {
display: flex;
align-items: center;
margin-bottom: 15px;
.price-label {
font-weight: bold;
margin-right: 10px;
}
.price-amount {
font-size: 24px;
color: #ff4d4f;
margin-right: 20px;
}
.sales-volume {
color: #999;
}
}
.product-attr {
margin-bottom: 20px;
.attr-item {
display: flex;
flex-direction: column;
margin-bottom: 15px;
.attr-label {
font-weight: bold;
margin-bottom: 8px;
color: #333;
}
.attr-value {
color: #666;
}
&.spec-group {
margin-top: 20px;
margin-bottom: 20px;
.custom-radio-group {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 5px;
.custom-radio {
display: inline-block;
padding: 8px 15px;
border: 1px solid #ddd;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s ease;
background-color: #fff;
position: relative;
&:hover {
border-color: #409eff;
}
&.is-checked {
border-color: #409eff;
background-color: #f0f7ff;
color: #409eff;
font-weight: 500;
&::after {
/* content: "✓"; */
position: absolute;
right: 5px;
bottom: 2px;
font-size: 12px;
color: #409eff;
}
}
.radio-text {
user-select: none;
}
}
}
}
}
}
.quantity-control {
display: flex;
align-items: center;
gap: 10px;
margin-top: 30px;
.quantity-value {
width: 40px;
text-align: center;
}
.buy-btn,
.cart-btn,
.bargain-btn {
margin-right: 10px;
}
}
}
}
.product-bottom {
margin-top: 50px;
display: flex;
.hot-recommend-sidebar {
min-width: 200px;
padding: 10px;
// border: 1px solid #eee;
background-color: #f7f9fa;
margin-right: 20px;
.hot-title {
border-left: 4px solid #ff4d4f;
padding-left: 10px;
margin-bottom: 20px;
}
.product-item {
margin-bottom: 20px;
text-align: center;
img {
width: 100%;
height: auto;
margin-bottom: 8px;
}
.product-name {
font-size: 14px;
margin-bottom: 4px;
}
.product-price {
font-size: 12px;
color: #f40;
}
}
}
.product-right {
width: 1000%;
}
.product-tabs {
display: flex;
// border-bottom: 1px solid #eee;
margin-bottom: 10px;
background-color: #f7f9fa;
padding: 10px;
.tab-item {
padding: 10px 20px;
cursor: pointer;
margin-right: 10px;
color: #333;
&.active {
color: #ff4d4f;
border-bottom: 2px solid #ff4d4f;
}
}
}
.product-detail-main {
flex: 1;
padding: 10px;
background-color: #fff;
.product-banner {
width: 100%;
height: auto;
margin-bottom: 20px;
}
.product-desc {
font-size: 14px;
line-height: 1.6;
color: #666;
}
}
}
</style>