shop_uniapp/components/spec-popup/spec-popup.vue
2024-09-28 22:53:48 +08:00

709 lines
21 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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">
<text class="xs lighter m-r-10" v-if="pId > 0">零售价: </text>
<price-format :first-size="46" :second-size="32" :subscript-size="32"
:price="group ? (checkedGoods.team_price * (goodsNum || 1)).toFixed(1) : (checkedGoods.price * (goodsNum || 1)).toFixed(1)"
:weight="500" />
<!-- <view class="vip-price flex"
v-if="!group && !isSeckill && checkedGoods.member_price && pId == 0">
<view class="price-name xxs">会员价</view>
<view style="padding: 0 11rpx">
<price-format :price="checkedGoods.member_price * (goodsNum || 1)" :first-size="22"
:second-size="22" :subscript-size="22" :weight="500" color="#7B3200" />
</view>
</view> -->
</view>
<view class="primary" v-if="pId > 0">
<view v-if="priceType == 0">
<!-- 三种价格可能会同时显示 -->
<view v-if="checkedGoods.purchase_price > 0">
<text class="xs lighter">集采价:</text>
<price-format :color="colorConfig.primary" class="m-r-10" :first-size="34"
:second-size="26" :subscript-size="26"
:price="parseInt((purchase_child_price * (goodsNum || 1)).toFixed(1))|| (checkedGoods.purchase_price * (goodsNum || 1)).toFixed(1)"
:weight="500"></price-format>
</view>
<view v-if="checkedGoods.issuing_price > 0">
<text class="xs lighter">代发价:</text>
<price-format :color="colorConfig.primary" class="m-r-10" :first-size="34"
:second-size="26" :subscript-size="26"
:price="(checkedGoods.issuing_price * (goodsNum || 1)).toFixed(1)"
:weight="500"></price-format>
</view>
<view v-if="(checkedGoods.wholesale_pce > 0 && wholesaleState == 2) || seeWholesale">
<text class="xs lighter">批发价:</text>
<price-format :color="colorConfig.primary" class="m-r-10" :first-size="34"
:second-size="26" :subscript-size="26"
:price="wholesaleIndex != -1 ? (checkedGoods.wholesale_pce * goods_number * checkedGoods.number[wholesaleIndex].w_discount / 100 + checkedGoods.wholesale_pce * goodsNum).toFixed(1) : (checkedGoods.wholesale_pce * (goodsNum || 1)).toFixed(1)"
:weight="500"></price-format>
</view>
</view>
<view v-else>
<view v-if="priceType == 1">
<text class="xs lighter">集采价:</text>
<price-format :color="colorConfig.primary" class="m-r-10" :first-size="34"
:second-size="26" :subscript-size="26"
:price="parseInt((purchase_child_price * (goodsNum || 1)).toFixed(1)) || (checkedGoods.purchase_price * (goodsNum || 1)).toFixed(1)"
:weight="500"></price-format>
</view>
<view v-if="priceType == 2">
<text class="xs lighter">代发价:</text>
<price-format :color="colorConfig.primary" class="m-r-10" :first-size="34"
:second-size="26" :subscript-size="26"
:price="(checkedGoods.issuing_price * (goodsNum || 1)).toFixed(1)"
:weight="500"></price-format>
</view>
<view v-if="priceType == 3 || seeWholesale">
<text class="xs lighter">批发价:</text>
<price-format :color="colorConfig.primary" class="m-r-10" :first-size="34"
:second-size="26" :subscript-size="26"
:price="wholesaleIndex != -1 ? (checkedGoods.wholesale_pce * goods_number * checkedGoods.number[wholesaleIndex].w_discount / 100 + checkedGoods.wholesale_pce * goodsNum).toFixed(1) : (checkedGoods.wholesale_pce * (goodsNum || 1)).toFixed(1)"
:weight="500"></price-format>
</view>
</view>
</view>
<!-- <view class="primary flex" v-if="pId == 0">
<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 && pId == 0">
<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;">
<view>
<!-- 链接 -->
<view class="flex flex-wrap price-method" v-if="pId > 0 && priceType == 0">
<view
v-if="checkedGoods.purchase_price > 0 || checkedGoods.issuing_price > 0 || checkedGoods.wholesale_pce > 0"
:class="[{primary: priceCurrent == 0}, 'name m-b-20 m-r-20 ']" @click="choosePrice(0)">
默认
</view>
<view v-if="checkedGoods.purchase_price > 0"
:class="[{primary: priceCurrent == 1}, 'name m-b-20 m-r-10']" @click="choosePrice(1)">
集采价
</view>
<view v-if="checkedGoods.issuing_price > 0"
:class="[{primary: priceCurrent == 2}, 'name m-b-20 m-r-10']" @click="choosePrice(2)">
代发价
</view>
<view v-if="(checkedGoods.wholesale_pce > 0 && wholesaleState == 2) || seeWholesale"
:class="[{primary: priceCurrent == 3}, 'name m-b-20']" @click="choosePrice(3)">
批发价
</view>
</view>
<!-- 扫码 -->
<view class="flex flex-wrap price-method" v-if="pId > 0 && priceType > 0">
<view v-if="priceType == 1 && checkedGoods.purchase_price > 0"
:class="[{primary: priceCurrent == 1}, 'name m-b-20 m-r-10']" @click="choosePrice(1)">
集采价
</view>
<view v-if="priceType == 2 && checkedGoods.issuing_price > 0"
:class="[{primary: priceCurrent == 2}, 'name m-b-20 m-r-10']" @click="choosePrice(2)">
代发价
</view>
<view v-if="(priceType == 3 && checkedGoods.wholesale_pce > 0) || seeWholesale"
:class="[{primary: priceCurrent == 3}, 'name m-b-20']" @click="choosePrice(3)">
批发价
</view>
</view>
<view class="flex flex-wrap spec-list"
v-if="(pId > 0 && priceType >= 0 && priceCurrent == 3 && wholesaleState == 2) || priceCurrent == 3">
<view v-for="(item, index) in checkedGoods.number" v-if="item.goods_number" :key="index"
:class="[{checked: wholesaleIndex == index}, 'spec-item sm']"
@click="chooseWholesaleList(index, item.goods_number)">
{{item.goods_number}}
</view>
</view>
</view>
<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 ' : '' ) +
( checkedGoods.spec_value_ids_arr[index] != specitem.id && isDisable(specitem.id) ? 'unspec-disabled ':'') +
( isDisable(specitem.id) ? 'spec-disabled':'')" @click="choseSpecItem(index, index2)">
{{ specitem.value }}
</view>
</view>
</view>
</view>
<view class="spec" v-if="purchase_sku.length != 0 && priceCurrent == 1">
<view class="name m-b-20">规格</view>
<view class="flex flex-wrap">
<view v-for="(item, index) in purchase_sku" :key="index" class="spec-item sm"
:class="item.sel == 1?'checked':''" @click="choseSku(index)">
{{ item.purchase_content }}
</view>
</view>
</view>
</scroll-view>
<view v-if="showNum" class="good-num flex row-between m-l-20 m-r-20">
<view class="label">数量</view>
<!-- 为0不限制 -->
<u-number-box v-if="show" v-model="goodsNum" :min="1" :max="isPanic && checkedGoods.limited_num == 1 ? 1 : checkedGoods.stock"></u-number-box>
</view>
</view>
<view v-if="shop.is_pay" class="btns flex row-between bg-white"
:class="specValueText.indexOf('请选择') != -1? 'disabled':''">
<template v-if="checkedGoods.stock != 0">
<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>
</template>
<button v-else class="bg-gray br60 white btn" size="lg">缺货</button>
</view>
</view>
</u-popup>
</template>
<script>
export default {
data() {
return {
shop: {},
checkedGoods: {
stock: 0
},
outOfStock: [],
specList: [],
goods_item: [],
purchase_sku: [],
spec_value_str: '',
purchase_child_price: 0,
disable: [],
goodsNum: 1,
showPop: false,
priceCurrent: 0,
showNum: true,
wholesaleIndex: -1,
purchase_price: 0,
issuing_price: 0,
goods_number: 1
};
},
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
},
isPanic: {
type: Boolean,
default: false
},
type: {
type: Boolean,
default: false
},
uId: {
type: Number,
default: 0
},
wholesaleState: {
type: Number,
default: 0
},
wholesalePrice: {
type: String,
default: 0
},
pId: {
type: Number,
default: 0
},
priceType: {
type: Number,
default: 0
},
seeWholesale: {
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.priceCurrent == 3 && this.wholesaleIndex == -1) {
return `请选择 批发数量`
} else {
if (this.checkedGoods?.stock != 0 && spec_str == '') {
console.log('this.checkedGoods=', this.checkedGoods)
}
if (this.checkedGoods?.stock != 0 && spec_str == '')
return `已选择 ${this.checkedGoods.spec_value_str} ${this.goodsNum} 件`
else if (this.checkedGoods?.stock != 0 && spec_str.length)
return `请选择 ${spec_str.slice(0, spec_str.length - 1)}`
else return `库存不足`
}
}
},
watch: {
goods(value) {
// 扫码进来单独显示批发价需要隐藏商品加减数量
if ((this.pId > 0 && this.priceType == 3) || this.seeWholesale) {
this.hideNum()
}
this.purchase_price = value.purchase_price;
this.issuing_price = value.issuing_price;
this.specList = value.goods_spec || [];
this.goods_item = value.goods_item;
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]
}
// 点击链接进入,如果只是包含了一个批发价
if (this.pId > 0 && this.priceType == 0 && this.checkedGoods.wholesale_pce > 0 && this.wholesaleState ==
2) {
this.hideNum()
}
/*
集采会员批发价的高亮显示
if (this.pId == 0 && this.uId && this.wholesaleState == 2 && this.checkedGoods.wholesale_pce > 0) {
this.hideNum()
}
*/
let checkedGoods = JSON.parse(JSON.stringify(this.checkedGoods))
this.spec_value_str = checkedGoods.spec_value_str
if (this.checkedGoods.purchase_sku) {
this.purchase_sku = JSON.parse(JSON.stringify(this.checkedGoods.purchase_sku));
}
},
// 规格列表变更时
specList(value) {
const res = this.goods.goods_item.filter(item => this.checkedGoods.spec_value_ids === item.spec_value_ids)
if (res.length != 0) {
const result = JSON.parse(JSON.stringify(res[0]))
result.spec_value_ids_arr = result.spec_value_ids.split(',')
this.checkedGoods = result;
this.spec_value_str = this.checkedGoods.spec_value_str
// 同步到父组件
this.$emit('change', {
detail: this.checkedGoods
});
}
// 当前选择的规格ID数组
const idsArr = this.checkedGoods.spec_value_ids_arr;
// 缺货规格列表
const outOfStock = this.outOfStock;
// 计算当前规格与缺货规格匹配
const getArrGather = this.getArrResult(idsArr, outOfStock);
// 计算出缺货的规格项
this.disable = this.getOutOfStockArr(getArrGather, idsArr)
},
show(val) {
this.showPop = val
}
},
methods: {
choseSku: function(index) {
console.log(index)
let purchase_sku = this.purchase_sku;
let purchase_child_price = 0
for (let i = 0, len = purchase_sku.length; i < len; i++) {
purchase_sku[i].sel = 0
}
purchase_sku[index].sel = 1
purchase_child_price = purchase_sku[index].purchase_child_price
this.purchase_child_price = purchase_child_price
this.purchase_sku = purchase_sku
this.checkedGoods.spec_value_str = this.spec_value_str + ',' + purchase_sku[index].purchase_content
this.purchase_sku = [...this.purchase_sku]
},
hideNum() {
this.priceCurrent = 3
this.goodsNum = 0;
// this.showNum = false;
},
chooseWholesaleList(index, num) {
this.wholesaleIndex = index;
// this.goodsNum = num;
this.goods_number = num;
this.goodsNum = num;
},
choosePrice(index) {
this.priceCurrent = index;
this.checkedGoods.spec_value_str = this.spec_value_str
if (index < 3) {
// this.showNum = true;
this.goodsNum = 1;
this.goods_number = 1;
this.wholesaleIndex = -1;
} else if (index == 3) {
this.goodsNum = 0;
}
},
isDisable(spec_id) {
// 如果有禁用的就直接返回
if (this.disable.includes(spec_id)) return true
// 是否全选的缺货
const checked = this.checkedGoods;
const isLess = this.goods.goods_item.filter(item => {
return checked.stock == 0 && checked.spec_value_ids === item.spec_value_ids
})
if (isLess.length) {
return checked.spec_value_ids_arr.includes(spec_id + "")
}
},
onClose() {
this.$emit('close')
},
onClick(type) {
this.changeIndex()
let {
checkedGoods,
goodsNum,
goods_number
} = this;
if (this.specValueText.indexOf('请选择') != -1) return this.$toast({
title: this.specValueText
})
if (checkedGoods.stock == 0) return this.$toast({
title: '当前选择库存不足'
})
checkedGoods.goodsNum = goodsNum;
checkedGoods.price_type = "default"
console.log(this.priceCurrent)
if (this.priceCurrent == 0) {
checkedGoods.w_price = checkedGoods.price
} else if (this.priceCurrent == 1) {
if (this.purchase_child_price == 0 && this.purchase_sku.length != 0) {
this.$toast({
title: '请选择规格',
})
return;
}
checkedGoods.w_price = this.purchase_child_price || checkedGoods.purchase_price
} else if (this.priceCurrent == 2) {
checkedGoods.w_price = checkedGoods.issuing_price
} else if (this.priceCurrent == 3) {
checkedGoods.w_price = checkedGoods.wholesale_pce
checkedGoods.basic_num = goods_number
checkedGoods.exc_num = goodsNum
checkedGoods.goodsNum = +goods_number + goodsNum
let wholesaleIndex = this.wholesaleIndex
let w_discount = checkedGoods.number[wholesaleIndex].w_discount
checkedGoods.w_discount = w_discount
}
let number = this.checkedGoods.number || []
let cunt = 0;
for (let i = 0, len = number.length; i < len; i++) {
if (number[i].goods_number) {
cunt++
}
}
let texts = '请选择批发数量'
if (cunt == 0) {
texts = '库存不足'
}
if (checkedGoods.number && this.priceCurrent == 3 && this.wholesaleIndex == -1) return this.$toast({
title: texts
})
/*
集采会员下的批发价
if (this.pId == 0 && this.uId && this.wholesaleState == 2 && this.checkedGoods.wholesale_pce > 0 && this.priceCurrent == 3) {
checkedGoods.price_type = "jicai_wholesale"
}
*/
this.$emit(type, {
detail: checkedGoods
});
},
// 选择规格
choseSpecItem(index, index2) {
console.log('index=', index)
console.log('index2=', index2)
const id = this.specList[index].spec_value[index2].id;
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(',');
if (this.checkedGoods.purchase_sku) {
this.purchase_sku = JSON.parse(JSON.stringify(this.checkedGoods.purchase_sku));
}
// 重新渲染页面
this.specList = [...this.specList]
this.wholesaleIndex = -1
this.purchase_child_price = 0
},
// 过滤出需要进行禁用的规格
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]
});
},
changeIndex() {
if (this.pId > 0 && this.priceType > 0) {
this.priceCurrent = this.priceType
}
}
}
};
</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;
}
.price-method {
padding: 32rpx 24rpx 0;
}
.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;
}
&.spec-disabled {
// opacity: .5;
position: relative;
}
&.spec-disabled::after {
content: '缺货';
position: absolute;
right: -20rpx;
top: -24rpx;
color: $-color-white;
width: 70rpx;
height: 36rpx;
text-align: center;
line-height: 36rpx;
font-size: 22rpx;
border-radius: 20rpx 20rpx 20rpx 0;
background-color: $-color-primary;
}
&.unspec-disabled::after {
background-color: gray;
}
}
}
}
.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>