glhcp/uniapp/components/spec-popup/spec-popup.vue

397 lines
14 KiB
Vue
Raw Blame History

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.

<template>
<u-popup v-model="showPop" mode="bottom" border-radius="14" :closeable="true" @close="onClose"
:safe-area-inset-bottom="true">
<view class="bg-white spec-contain">
<view class="header flex">
<u-image width="160rpx" height="160rpx" class="m-r-20" border-radius="10rpx"
@click="previewImage(checkedGoods.image)" :src="checkedGoods.image"></u-image>
<view class="goods-info">
<view class="primary flex">
<price-format :first-size="46" :second-size="32" :subscript-size="32"
:price="group ? checkedGoods.team_price : checkedGoods.price" :weight="500" />
<view class="vip-price flex" v-if="!group && !isSeckill && checkedGoods.member_price">
<view class="price-name xxs">会员价</view>
<view style="padding: 0 11rpx">
<price-format :price="checkedGoods.member_price" :first-size="22" :second-size="22"
:subscript-size="22" :weight="500" color="#7B3200" />
</view>
</view>
</view>
<view class="sm" v-if="showStock">
库存:{{checkedGoods.stock}}件
</view>
<view class="sm m-t-10">
<text>{{specValueText}}</text>
</view>
</view>
</view>
<view class="spec-main" style="height: 550rpx;">
<scroll-view style="max-height: 500rpx;" scroll-y="true">
<view class="spec-list">
<view v-for="(item, index) in specList" :key="index" class="spec">
<view class="name m-b-20">
{{ item.name }}
<text
class="primary xs m-l-20">{{checkedGoods.spec_value_ids_arr[index] == '' ? '请选择'+item.name:''}}</text>
</view>
<view class="flex flex-wrap">
<view v-for="(specitem, index2) in item.spec_value" :key="index2" :class="'spec-item sm ' +
( checkedGoods.spec_value_ids_arr[index] == specitem.id ? 'checked' : '' ) +
( isDisable(specitem.id) ? 'disabled':'')" @click="choseSpecItem(index, index2)">
{{ specitem.value }}
</view>
</view>
</view>
</view>
</scroll-view>
<view class="good-num flex row-between m-l-20 m-r-20">
<view class="label">数量</view>
<u-number-box v-if="show" v-model="goodsNum" :min="1" :max="checkedGoods.stock"></u-number-box>
</view>
</view>
<view v-if="shop.is_pay" class="btns flex row-between bg-white" :class="specValueText.indexOf('请选择') != -1 || checkedGoods.stock == 0 ? 'disabled':''">
<button v-if="showAdd && type==0" class="add-cart br60 white btn" size="lg"
@click="onClick('addcart')">{{yellowBtnText}}</button>
<button v-if="showBuy" class="bg-primary br60 white btn" size="lg"
@click="onClick('buynow')">{{redBtnText}}</button>
<button v-if="showConfirm" class="bg-primary br60 white btn" size="lg"
@click="onClick('confirm')"></button>
</view>
</view>
</u-popup>
</template>
<script>
export default {
data() {
return {
shop: {},
checkedGoods: {
stock: 0
}, //选中的
outOfStock: [], //缺货的
specList: [], //规格
disable: [], //不可选择的
goodsNum: 1,
showPop: false
};
},
props: {
show: {
type: Boolean
},
goods: {
type: Object
},
showAdd: {
type: Boolean,
default: true
},
showBuy: {
type: Boolean,
default: true
},
showConfirm: {
type: Boolean,
default: false
},
redBtnText: {
type: String,
default: "立即购买"
},
yellowBtnText: {
type: String,
default: "加入购物车"
},
showStock: {
type: Boolean,
default: true
},
group: {
type: Boolean,
default: false
},
isSeckill: {
type: Boolean,
default: false
},
// type 是用来判断是什么类型的商品0=实物商品1=虚拟商品
type: {
type: Boolean,
default: false
}
},
computed: {
// 选择的规格参数等
specValueText() {
let arr = this.checkedGoods.spec_value_ids?.split(',');
let spec_str = ''
if (arr)
arr.forEach((item, index) => {
if (item == '') spec_str += this.specList[index].name + ','
})
if (this.checkedGoods?.stock != 0 && spec_str == '')
return `已选择 ${this.checkedGoods.spec_value_str} ${this.goodsNum}`
else
return `请选择 ${spec_str.slice(0, spec_str.length - 1)}`
}
},
watch: {
goods(value) {
this.specList = value.goods_spec || [];
let goodsItem = value.goods_item || [];
this.shop = value.shop || {}
this.outOfStock = goodsItem.filter(item => item.stock == 0)
// 找出库存不为0的
const resultArr = goodsItem.filter(item => item.stock != 0)
if (resultArr.length != 0) {
resultArr[0].spec_value_ids_arr = resultArr[0].spec_value_ids.split(',');
this.checkedGoods = resultArr[0]
} else {
// 无法选择
goodsItem[0].spec_value_ids_arr = []
this.disable = goodsItem.map(item => item.spec_value_ids.split(','));
this.checkedGoods = goodsItem[0]
}
},
specList(value) {
if (this.checkedGoods?.stock == 0) return
const res = this.goods.goods_item.filter(item => {
return this.checkedGoods.spec_value_ids === item.spec_value_ids
})
// 库存为0的规格
const idsArr = this.checkedGoods.spec_value_ids_arr;
const outOfStock = this.outOfStock;
// 找出规格相同和规格不相同的余数
const getArrGather = this.getArrResult(idsArr, outOfStock);
// 计算出缺货的规格项
this.disable = this.getOutOfStockArr(getArrGather, idsArr)
if (res.length != 0) {
let result = JSON.parse(JSON.stringify(res[0]))
result.spec_value_ids_arr = result.spec_value_ids.split(',')
this.checkedGoods = result;
// 同步到父组件
this.$emit('change', {
detail: this.checkedGoods
});
}
},
show(val) {
this.showPop = val
}
},
created() {
console.log('spec')
},
methods: {
isDisable(e) {
const res = this.disable.filter(item => item == e)
if (res.length != 0) return true
else return false
},
onClose() {
this.$emit('close')
},
onClick(type) {
let {
checkedGoods,
goodsNum
} = this;
if(this.specValueText.indexOf('请选择') != -1) return this.$toast({
title: this.specValueText
})
if(checkedGoods.stock == 0) return this.$toast({
title: '当前选择库存不足'
})
checkedGoods.goodsNum = goodsNum;
this.$emit(type, {
detail: checkedGoods
});
},
// 选择规格
choseSpecItem(index, index2) {
const id = this.specList[index].spec_value[index2].id;
// 无法选择
const disable = this.disable.filter(item => item == id)
if (disable.length != 0) return
let idsArr = this.checkedGoods.spec_value_ids_arr;
if (id == idsArr[index]) idsArr[index] = ''
else idsArr[index] = id;
//保存已选规格
this.checkedGoods.spec_value_ids_arr = idsArr;
this.checkedGoods.spec_value_ids = idsArr.join(',');
// 重新渲染页面
this.specList = [...this.specList]
},
// 过滤出需要进行禁用的规格
getOutOfStockArr(arr, arr1, result = []) {
arr.forEach(el => {
if (el.num >= (arr1.length - 1)) {
result.push(...el.different)
}
})
return result
},
// 匹配出缺货库存和已选中对比结果
getArrIdentical(arr1, arr2, arr = [], num = 0) {
arr1.forEach(el => {
arr2.forEach(el2 => {
if (el == el2) {
num += 1;
arr.push(el)
}
})
});
return {
num, //n个相同的
different: this.getArrDifference([...new Set(arr)].map(Number), arr2.map(Number)),
identical: [...new Set(arr)]
}
},
// 匹配出已选择和缺库存的
getArrResult(arr, outOfStock, result = []) {
outOfStock.forEach(item => {
const res = this.getArrIdentical(arr, item.spec_value_ids.split(','))
if (res != undefined && res.length != 0) {
result.push(res)
}
})
return result
},
// 找出两个数组中参数不同的
getArrDifference(arr1, arr2) {
return arr1.concat(arr2).filter(function(v, i, arr) {
return arr.indexOf(v) == arr.lastIndexOf(v);
});
},
// 查看商品图片
previewImage(current) {
uni.previewImage({
current,
urls: [current] // 需要预览的图片http链接列表
});
}
}
};
</script>
<style lang="scss" scoped>
.spec-contain {
border-radius: 10rpx 10rpx 0 0;
overflow: hidden;
position: relative;
.close {
position: absolute;
right: 10rpx;
top: 10rpx;
}
.header {
padding: 30rpx;
padding-right: 70rpx;
align-items: flex-start;
border: $-solid-border;
}
.spec-main {
padding: 0 24rpx;
.spec-list {
padding: 30rpx 20rpx;
.spec-item {
line-height: 54rpx;
padding: 0 30rpx;
background-color: #F4F4F4;
border-radius: 60rpx;
margin: 0 20rpx 20rpx 0;
border: 1px solid #F4F4F4;
&.checked {
background-color: #FFE9EB;
color: $-color-primary;
border-color: currentColor;
}
&.disabled {
opacity: .5;
text-decoration: line-through;
}
}
}
}
// 底部按钮无法选择
.disabled {
opacity: 0.4;
}
.btns {
height: 120rpx;
padding: 0 30rpx;
margin-top: 80rpx;
.add-cart {
background-color: #FF9E1E;
}
.btn {
margin: 0 10rpx;
flex: 1;
}
}
.vip-price {
margin: 0 24rpx;
background-color: #FFE9BA;
line-height: 36rpx;
border-radius: 6rpx;
overflow: hidden;
.price-name {
background-color: #101010;
padding: 3rpx 12rpx;
color: #FFD4B7;
position: relative;
overflow: hidden;
&::after {
content: '';
display: block;
width: 20rpx;
height: 20rpx;
position: absolute;
right: -15rpx;
background-color: #FFE9BA;
border-radius: 50%;
top: 50%;
transform: translateY(-50%);
box-sizing: border-box;
}
}
}
}
</style>