6 changed files with 779 additions and 25 deletions
@ -0,0 +1,114 @@ |
|||||
|
import Vue from 'vue' |
||||
|
import axios from "axios"; |
||||
|
import { Message, MessageBox, Loading } from "element-ui"; // 引入 Element UI 组件
|
||||
|
import store from '@/store'; |
||||
|
|
||||
|
const http = axios.create({ |
||||
|
timeout: 6000 // 请求超时时间
|
||||
|
}) |
||||
|
|
||||
|
// 添加请求拦截器
|
||||
|
http.interceptors.request.use((config) => { |
||||
|
const { customBaseURL } = config.params || {}; |
||||
|
if (customBaseURL) { |
||||
|
config.baseURL = customBaseURL; |
||||
|
delete config.params.customBaseURL; |
||||
|
} else { |
||||
|
config.baseURL = process.env.VUE_APP_URL; |
||||
|
} |
||||
|
|
||||
|
const token = store.state.user.userInfo.token; |
||||
|
config.headers['token'] = token |
||||
|
config.headers['Content-Type'] = 'application/json;charset=UTF-8'; |
||||
|
|
||||
|
// 显示加载中状态(Element UI 的 Loading)
|
||||
|
if (config.loading !== false) { // 默认显示,可通过参数关闭
|
||||
|
config.loadingInstance = Loading.service({ |
||||
|
lock: true, |
||||
|
text: '加载中...', |
||||
|
background: 'rgba(0, 0, 0, 0.7)' |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
return config; |
||||
|
}, (error) => { |
||||
|
return Promise.reject(error); |
||||
|
}); |
||||
|
|
||||
|
// 添加响应拦截器
|
||||
|
http.interceptors.response.use(response => { |
||||
|
// 关闭加载状态
|
||||
|
if (response.config.loadingInstance) { |
||||
|
response.config.loadingInstance.close(); |
||||
|
} |
||||
|
|
||||
|
if (response.status === 200 || response.status === 1) { |
||||
|
return response.data; |
||||
|
} |
||||
|
}, error => { |
||||
|
// 关闭加载状态
|
||||
|
if (error.config && error.config.loadingInstance) { |
||||
|
error.config.loadingInstance.close(); |
||||
|
} |
||||
|
|
||||
|
if (error.response && error.response.status) { |
||||
|
switch (error.response.status) { |
||||
|
case 401: |
||||
|
MessageBox.confirm('请登录后操作', '提示', { |
||||
|
confirmButtonText: '去登录', |
||||
|
cancelButtonText: '取消', |
||||
|
type: 'warning' |
||||
|
}).then(() => { |
||||
|
// 登录操作
|
||||
|
}).catch(() => { |
||||
|
// 取消登录回调
|
||||
|
}); |
||||
|
break; |
||||
|
case 404: |
||||
|
Message({ |
||||
|
message: '网络繁忙,请刷新再试', |
||||
|
type: 'error', |
||||
|
duration: 2000 |
||||
|
}); |
||||
|
break; |
||||
|
default: |
||||
|
Message({ |
||||
|
message: '网络繁忙,请刷新再试', |
||||
|
type: 'error', |
||||
|
duration: 2000 |
||||
|
}); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
return Promise.reject(error); |
||||
|
}); |
||||
|
|
||||
|
// 请求方法挂载
|
||||
|
Vue.prototype.get = (params, url, loading = true) => { |
||||
|
return new Promise((resolve, reject) => { |
||||
|
http.get(url, { |
||||
|
params, |
||||
|
loading // 传递加载状态参数
|
||||
|
}) |
||||
|
.then(res => { |
||||
|
resolve(res); |
||||
|
}) |
||||
|
.catch(err => { |
||||
|
reject(err); |
||||
|
}); |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
Vue.prototype.post = (data, url, loading = true) => { |
||||
|
return new Promise((resolve, reject) => { |
||||
|
http.post(url, data, { |
||||
|
loading // 传递加载状态参数
|
||||
|
}) |
||||
|
.then(res => { |
||||
|
resolve(res); |
||||
|
}) |
||||
|
.catch(err => { |
||||
|
reject(err); |
||||
|
}); |
||||
|
}) |
||||
|
} |
@ -0,0 +1,128 @@ |
|||||
|
export default { |
||||
|
install(Vue) { |
||||
|
Vue.prototype.util = { |
||||
|
// 格式化富文本
|
||||
|
formateRichText(str) { |
||||
|
if (!str) return ""; |
||||
|
var reg = new RegExp("<img", "g"); |
||||
|
str = str.replace(reg, "<img class='sz-xcx-fwb-img' width='100%'") |
||||
|
reg = new RegExp("<IMG", "g"); |
||||
|
str = str.replace(reg, "<img class='sz-xcx-fwb-img' width='100%'") |
||||
|
reg = new RegExp(" ", "g"); |
||||
|
str = str.replace(reg, '<span style="width: 8rpx;display: inline-block;"></span>') |
||||
|
reg = new RegExp("section", "g"); |
||||
|
str = str.replace(reg, 'div'); |
||||
|
reg = new RegExp("↵", "g"); |
||||
|
str = str.replace(reg, '<br />'); |
||||
|
str = str.replace(/<table/g, '<table border="1" cellspacing="0" style="border-collapse:collapse"') |
||||
|
return str; |
||||
|
}, |
||||
|
// 手机号验证规则
|
||||
|
mobileValid(val) { |
||||
|
return /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/.test(val); |
||||
|
}, |
||||
|
// 身份证验证规则
|
||||
|
idNumberValid(val) { |
||||
|
return /^\d{17}(\d{1}|[X|x])$/.test(val); |
||||
|
}, |
||||
|
// 护照验证正则
|
||||
|
passportValid(val) { |
||||
|
return /^([a-zA-z]|[0-9]){5,17}$/.test(val); |
||||
|
}, |
||||
|
// 台胞证正则
|
||||
|
taiwanValid(val) { |
||||
|
return /^\d{8}|^[a-zA-Z0-9]{10}|^\d{18}$/.test(val); |
||||
|
}, |
||||
|
// 港澳通行证正则
|
||||
|
gangaoValid(val) { |
||||
|
return /^([A-Z]\d{6,10}(\(\w{1}\))?)$/.test(val); |
||||
|
}, |
||||
|
// 外国人永久居留证正则
|
||||
|
foreignerValid(val) { |
||||
|
return /(^[A-Za-z]{3})([0-9]{12}$)/.test(val); |
||||
|
}, |
||||
|
// 军官证正则
|
||||
|
officerValid(val) { |
||||
|
return /^[\u4E00-\u9FA5](字第)([0-9a-zA-Z]{4,8})(号?)$/.test(val); |
||||
|
}, |
||||
|
// 邮箱验证正则
|
||||
|
emailValid(val) { |
||||
|
return /^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/.test(val) |
||||
|
}, |
||||
|
// 获取路径参数
|
||||
|
getUrlPara(url) { |
||||
|
let arrUrl = url.split("?"); |
||||
|
let para = arrUrl[1]; |
||||
|
return para ? para.split('&') : false; |
||||
|
}, |
||||
|
openMap(item) { |
||||
|
let data = { |
||||
|
type: 'map', |
||||
|
lon: item.scene_lon, |
||||
|
lat: item.scene_lat, |
||||
|
name: item.title, |
||||
|
address: item.address |
||||
|
} |
||||
|
uni.navigateTo({ |
||||
|
url: '/subPackages/h5Web/h5Web?data=' + JSON.stringify(data) |
||||
|
}) |
||||
|
}, |
||||
|
showImg(img) { |
||||
|
if(!img) return; |
||||
|
if (img.indexOf('https://') != -1 || img.indexOf('http://') != -1) { |
||||
|
return img; |
||||
|
} else { |
||||
|
return 'https://changshu.js-dyyj.com' + img; |
||||
|
} |
||||
|
}, |
||||
|
// 跳回小程序
|
||||
|
gotoDetailMini(item) { |
||||
|
console.log(item) |
||||
|
if(item.link_type == 1) { |
||||
|
// 外部小程序
|
||||
|
let data = { |
||||
|
type: 'xcx', |
||||
|
url: item.ext_link |
||||
|
} |
||||
|
uni.navigateTo({ |
||||
|
url: '/subPackages/h5Web/h5Web?data=' + JSON.stringify(data) |
||||
|
}) |
||||
|
return |
||||
|
}else if(item.link_type == 2){ |
||||
|
// 外部H5
|
||||
|
// window.location.href = item.ext_link
|
||||
|
window.location.href = 'https://m.cloud.sz-trip.com/MailMerchandiseDetail?type=ticket&platform=changshu&id=' + item.id |
||||
|
return |
||||
|
} |
||||
|
// switch (item.genre){
|
||||
|
// // 景点
|
||||
|
// case 'ticket':
|
||||
|
// uni.navigateTo({
|
||||
|
// url: '/subPackages/ticketBooking/detail?id=' + item.id
|
||||
|
// })
|
||||
|
// break;
|
||||
|
// // 酒店
|
||||
|
// case 'hotel':
|
||||
|
// uni.navigateTo({
|
||||
|
// url: '/subPackages/hotelHomestay/detail?id=' + item.id
|
||||
|
// })
|
||||
|
// break;
|
||||
|
// // 美食
|
||||
|
// case 'food':
|
||||
|
// uni.navigateTo({
|
||||
|
// url: '/subPackages/food/foodDetail?id=' + item.id
|
||||
|
// })
|
||||
|
// break;
|
||||
|
// // 攻略
|
||||
|
// case 'article':
|
||||
|
// uni.navigateTo({
|
||||
|
// url: '/subPackages/travelGuide/detail?id=' + item.id
|
||||
|
// })
|
||||
|
// break;
|
||||
|
// default:
|
||||
|
// break;
|
||||
|
// }
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,521 @@ |
|||||
|
<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">商品详情</span> |
||||
|
<span class="tab-item">商品评价(125)</span> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 商品详情 --> |
||||
|
<div class="product-detail-main"></div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
name: "ProductDetail", |
||||
|
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, |
||||
|
}, |
||||
|
], |
||||
|
}; |
||||
|
}, |
||||
|
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> |
||||
|
|
Loading…
Reference in new issue