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.
shop-app/pages/goods/detail/components/SkuPopup.vue

370 lines
8.8 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!--
* @Author: ch
* @Date: 2022-03-24 11:30:55
* @LastEditors: ch
* @LastEditTime: 2022-04-30 15:41:16
* @Description: file content
-->
<template>
<u-popup :show="visible" round="24rpx" closeable @close="close">
<view class="sku-popup">
<view class="product-info">
<!-- <image class="product-info--img" src="https://msb-edu-dev.oss-cn-beijing.aliyuncs.com/test/1.png"/> -->
<image class="product-info--img" :src="goodsInfo.pictureList[0]"/>
<view>
<view v-if="curSku.sellPrice" class="product-info--price">¥{{curSku.sellPrice}}</view>
<view v-else class="product-info--price">
¥{{activityStatus === 'startActivity' ? goodsInfo.productActivityVO.activityPrice : goodsInfo.startingPrice}}
</view>
<view class="product-info--sku">{{curSku.name}}</view>
<view>库存:{{curSku.stock}}</view>
</view>
</view>
<view class="attr-group" v-for="(item, index) in attributeGroupList" :key="item.id">
<text class="attr-name">{{item.name}}</text>
<view class="attr-items">
<text class="attr-item" :class="{'attr-item__active' : i.active, 'attr-item__disabled' : i.disabled}"
v-for="i in item.attributes" :key="i.symbol"
@click="handleAttrItem(i, index)"
>{{i.name}}</text>
</view>
</view>
<view class="sku-num">
<view class="sku-num--single-box">
<template>
<text>数量</text>
<!-- <text class="sku-num--single">{{maxBuyNum}}</text> -->
</template>
</view>
<u-number-box :min="1" :max="maxBuyNum" button-size="40rpx" bgColor="#F5F6FA"
v-model="curBuyNum" >
<text slot="minus" class="cart-item--stepper-icon">-</text>
<text slot="plus" class="cart-item--stepper-icon">+</text>
</u-number-box>
</view>
<view class="footer">
<view class="btn-bg" v-if="mode == 1">
<UiButton class="btn" @click="addCart" size="max" :disable="curSku.stock == 0">加入购物车</UiButton>
<UiButton class="btn btn__buy" @click="buyNow" size="max" :disable="curSku.stock == 0">立即购买</UiButton>
</view>
<UiButton v-else class="btn__confirm" type="gradual" size="max" @click="confirm"></UiButton>
</view>
</view>
</u-popup>
</template>
<script>
import UiButton from '@/components/UiButton.vue';
import {ApiPutAddCart} from '@/common/api/cart';
export default {
components: { UiButton },
props: {
// true 组件显示 false 组件隐藏
visible : {
type : Boolean,
default : false
},
// 模式 1:都显示 2:只显示购物车 3:只显示立即购买
mode: {
type: Number,
default: 1,
},
// 商品详情信息
goodsInfo: {
type: Object,
default: {},
},
// 商品sku信息
skuInfo : {
type : Array,
default : []
},
// 活动状态
activityStatus : {
type : String,
default : 'noActivity'
}
},
data() {
return {
// 属性组数据因为会修改数据不能直接操作props中传入的数据
attributeGroupList : [],
// 数量
curBuyNum : 1,
};
},
watch : {
goodsInfo(newData){
if(newData.attributeGroupList){
this.attributeGroupList = newData.attributeGroupList;
// 请求数据返回后设置默认选中规格
if(this.skuInfo.length){
this.setDefaultAttr();
}
}
},
skuInfo(newData){
// 请求数据返回后设置默认选中规格
if(newData.length){
this.setDefaultAttr();
}
}
},
computed : {
/**
* 当前选中SKU根据选中规格计算
*/
curSku(){
return this.skuInfo.find(i => i.attributeSymbolList === this.selectedSymbol.join(',')) || {};
},
// [1,.,3]
selectedSymbol(){
return this.attributeGroupList.map(item => {
const activeAttr = item.attributes.find(i => i.active);
return activeAttr ? activeAttr.symbol : '.'
}).filter(i => i)//.sort();
},
/**
* 最大可购买数量
* 1、有限购则对比限购跟库存取最小值
* 2、没限购取库存
*/
maxBuyNum(){
const singleBuyLimit = this.goodsInfo.singleBuyLimit;
const stock = this.curSku.stock;
return singleBuyLimit ? Math.min(singleBuyLimit, stock || 1) : stock;
}
},
methods: {
/**
* 设置默认选中规格
*/
setDefaultAttr(){
const curSku = this.skuInfo.find(i => i.stock > 0);
if(!curSku){
return false
}
this.attributeGroupList.forEach((item, index) => {
for(let i of item.attributes){
if(curSku.attributeSymbolList.includes(i.symbol)){
this.$set(i,'active', true);
this.setDisabledItem(i, index, true);
break;
}
}
});
this.$emit('input',this.curSku);
},
/**
* 点击属性项,设置选中和禁用项
*/
handleAttrItem(item, groupIndex){
// 禁用选项
if(item.disabled){
return false;
}
// 每次重选规格购买数量都置为1
this.curBuyNum = 1;
const active = item.active;
// 把当前选项组的装先置为未选状态
this.attributeGroupList[groupIndex].attributes.forEach(item =>{
this.$set(item,'active', false);
});
// 设置当前点击选项选中
this.$set(item,'active', !active);
this.setDisabledItem(item, groupIndex);
this.$emit('input',this.curSku);
},
/**
* 每次点击选项属性时,计算不可选属性
*/
setDisabledItem(item, groupIndex){
this.attributeGroupList.forEach((group, idx) => {
// 拿到已选项数组,这个是按照项组顺序排序好的缓存数组
let symbolCache = Object.assign([],this.selectedSymbol);
// 跳过当前属性组
if(groupIndex === idx) return false;
// 遍历其他选项组中的选项
group.attributes.forEach( item => {
// 根据选项组下标,补位选项属性
symbolCache[idx] = item.symbol;
const reg = new RegExp(symbolCache.join(','));
// 根据补位选项寻找是否有有效SKU有则可选没有则禁用
const res = this.skuInfo.filter(i => reg.test(i.attributeSymbolList)).find(i => i.stock > 0);
if(res){
item.disabled = false;
}else{
item.disabled = true;
}
})
})
},
/**
* 加入购物车
*/
async addCart(){
if(!this.curSku.skuId){
uni.$u.toast('请选择规格~');
return false;
}
const {error, result} = await ApiPutAddCart({
productSkuId : this.curSku.skuId,
productId : this.goodsInfo.id,
number : this.curBuyNum
});
if(error){
uni.$u.toast(error.message);
return false;
}
uni.$u.toast('加入购物车成功~');
this.close();
// this.$Router.push('/cart');
},
/**
* 立即购买
*/
buyNow(){
if(!this.curSku.skuId){
uni.$u.toast('请选择规格~');
return false;
}
let query = {
mode : 'buyNow',
skuId : this.curSku.skuId,
num : this.curBuyNum,
activityType : 1
}
const {productActivityVO} = this.goodsInfo;
if(this.activityStatus === 'startActivity'){
query.activityType = 2;
query.activityId = productActivityVO.activityId;
query.activityTimeId = productActivityVO.activityTimeId;
}
this.$Router.push({
path : '/orderSubmit',
query
})
},
confirm(){
if(this.mode == 2){
this.addCart();
}
if(this.mode == 3){
this.buyNow();
}
},
close(){
this.$emit('update:visible', false);
}
}
};
</script>
<style lang="scss" scoped>
.sku-popup{
padding: 30rpx;
}
.product-info{
display: flex;
font-size: $font-size-base;
color: $color-grey4;
line-height: 42rpx;
&--img{
width: 200rpx;
height: 200rpx;
margin-right: 40rpx;
}
&--price{
font-size: 40rpx;
color: $color-yellow4;
line-height: 48rpx;
margin-bottom: 20rpx;
}
}
.attr-group{
margin-top: 40rpx;
font-size: 30rpx;
}
.attr-items{
display: flex;
flex-wrap: wrap;
padding: 10rpx 0;
}
.attr-item{
background: $color-grey1;
line-height: 70rpx;
border-radius: 100rpx;
border: 1px solid $color-grey1;
color: $color-grey5;
display: block;
min-width: 158rpx;
max-width: 630rpx;
text-align: center;
margin: 20rpx 28rpx 0 0;
padding: 0 20rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
&__active{
background: #FFF3EE;
border-color: $color-yellow4;
color: $color-yellow3;
}
&__disabled{
color: $color-grey3;
}
}
.sku-num{
display: flex;
justify-content: space-between;
margin: 40rpx 0;
line-height: 40rpx;
&--single-box{
display: flex;
align-items: center;
}
&--single{
color: $color-grey4;
margin-left: 10rpx;
font-size: $font-size-base;
display: block;
height: 30rpx;
}
}
.footer{
display: flex;
justify-content: space-between;
.btn-bg{
background: #FFE6D9;
border-radius: 50rpx;
height: 80rpx;
}
.btn{
border: 0;
padding: 0;
border-radius: 0;
width: 340rpx;
font-size: $font-size-base;
color: $color-yellow3;
&::after{
border: 0;
}
&__buy{
width: 350rpx;
background: url('@/static/goods/buy_max.png');
background-size: 350rpx;
color: $color-grey0;
}
}
.btn__confirm{
margin: 0;
width: 670rpx;
}
}
</style>