<template>
|
|
<u-popup mode="bottom" v-model="specShow" closeable border-radius="40" height="70%" @close="hide">
|
|
<view class="spec-popup h-100">
|
|
<view class="spec-popup-header position-fixed top-0 left-0 vw-100 bg-white p-20 flex align-end">
|
|
<view class="m-r-20">
|
|
<image class="spec-popup-header-image" :src="image == ''?goodsDetail.pic[0]:image" mode="aspectFit"></image>
|
|
</view>
|
|
<text class="text-red font-40" v-if="price">{{ price }} {{ goodsType == 'integralStore' ? '兑购' : '元' }}</text>
|
|
</view>
|
|
<view class="spec-popup-container">
|
|
|
|
<view class="m-b-20">
|
|
<view class="spec-popup-item" v-for="(item, index) in specList" :key="index">
|
|
<view class="spec-popup-item-title text-grey font-32 m-b-20">{{ item.title }}</view>
|
|
<view class="flex flex-wrap">
|
|
<view v-for="(attribute, attributeIndex) in item.list" :key="attributeIndex" :class="[
|
|
'spec-popup-item-suk',
|
|
selectSpec[item.title] === attribute.name ? 'spec-popup-item-suk--active' : '' ,
|
|
attribute.able ? '' : 'spec-popup-item-suk--disabled'
|
|
]" @click="changeSpec(item.title, attribute.name, attribute.able)">
|
|
{{ attribute.name }}
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="spec-popup-number flex align-center justify-between">
|
|
<text class="font-32 text-black">数量</text>
|
|
<u-number-box :min="goodsDetail.sumShop" :max="goodsDetail.stock" v-model="sukNum"></u-number-box>
|
|
</view>
|
|
|
|
</view>
|
|
<view class="spec-popup-confirm bg-white position-fixed bottom-0 left-0 vw-100 flex align-center justify-center">
|
|
<view class="spec-popup-confirm-btn">
|
|
<u-button shape="circle" type="primary" class='btn' @click="$u.debounce(addShopping, 500)">确定</u-button>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</u-popup>
|
|
</template>
|
|
|
|
<script>
|
|
import { IMG_URL } from '@/env.js'
|
|
export default {
|
|
props: {
|
|
goodsDetail: {
|
|
typeof: Object,
|
|
default: () => {}
|
|
},
|
|
},
|
|
data () {
|
|
return {
|
|
IMG_URL,
|
|
specShow: false,
|
|
specList: [], // property 列表
|
|
skuList: [], // sku 列表
|
|
selectSpec: {}, // 选择数据的对象
|
|
sukNum: 1,
|
|
price: 0,
|
|
type: '',
|
|
goodsType: '',
|
|
image:''
|
|
}
|
|
},
|
|
methods: {
|
|
show (bool, type, goodsType) {
|
|
this.type = type
|
|
this.goodsType = goodsType
|
|
this.specShow = bool;
|
|
if (bool) {
|
|
this.sukNum = this.goodsDetail.sumShop
|
|
let specList = JSON.parse(JSON.stringify(this.goodsDetail.attributeListVo))
|
|
this.skuList = this.goodsDetail.skuListSku;
|
|
// 初始化选择数据的对象
|
|
specList.forEach(item => {
|
|
this.$set(this.selectSpec, item.title, "");
|
|
})
|
|
// 将规格数据处理成我们视图所需要的数据类型
|
|
this.specList = specList.map(item => {
|
|
return {
|
|
title: item.title,
|
|
list: item.list.map(its => {
|
|
return {
|
|
name: its,
|
|
// 判断是否可以选择
|
|
able: this.isAble(item.title, its)
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
},
|
|
hide () {
|
|
this.specShow = false
|
|
this.type = ''
|
|
this.$emit('close')
|
|
},
|
|
// 核心判断逻辑
|
|
// 判断规格是否可以被选择 核心函数 key当前的规格的title value规格值
|
|
isAble(key, value) {
|
|
// 深拷贝 避免被影响
|
|
let copySelectSpec = JSON.parse(JSON.stringify(this.selectSpec));
|
|
// 用对象的好处就在这了 直接赋值当前验证项
|
|
copySelectSpec[key] = value;
|
|
// console.log(copySelectSpec, key, value)
|
|
// 用数组的 some 方法 效率高 符合条件直接退出循环
|
|
let flag = this.skuList.some(item => {
|
|
// 条件判断 核心逻辑判断
|
|
let i = 0;
|
|
// 这个for in 循环的逻辑就对底子不深的人来说就看不懂了 原理就是循环已经选中的 和 正在当前对比的数据 和 所有的sku对比 只有当前验证的所有项满足sku中的规格或者其他规格为空时 即满足条件 稍微有点复杂 把注释的调试代码打开就调试下就可以看懂了
|
|
for (let k in copySelectSpec) {
|
|
// console.log(copySelectSpec[k]) // 注释的调试看逻辑代码
|
|
if (copySelectSpec?.[k] && item.list.includes(copySelectSpec[k])) {
|
|
// console.log(item)
|
|
i++
|
|
} else if (copySelectSpec[k] == "") {
|
|
i++;
|
|
}
|
|
}
|
|
// 符合下面条件就退出了 不符合会一直循环知道循环结束没有符合的条件就 return false 了
|
|
return i == this.goodsDetail.attributeListVo.length
|
|
})
|
|
return flag
|
|
},
|
|
// 点击事件
|
|
changeSpec(key, value, able) {
|
|
if (!able) return
|
|
let flag = true
|
|
if (this.selectSpec[key] === value) {
|
|
this.selectSpec[key] = ''
|
|
} else {
|
|
this.selectSpec[key] = value
|
|
}
|
|
for (let key in this.selectSpec) {
|
|
if (!this.selectSpec[key]) flag = false
|
|
}
|
|
if (flag) {
|
|
// // debugger
|
|
this.skuList.forEach(item => {
|
|
let arr = Object.values(this.selectSpec)
|
|
if (arr.toString() === item.list.toString()) this.price = item.price
|
|
if (arr.toString() === item.list.toString()) this.image = item.image
|
|
})
|
|
// for (let i = 0; i < this.skuList.length; i++) {
|
|
// let item = this.skuList[i]
|
|
// let bool = true
|
|
// for (var j = 0; j < item.list.length; j++) {
|
|
// let subItem = item.list[j]
|
|
// bool = true
|
|
// console.log('0000000000')
|
|
// if (!this.selectSpec.hasOwnProperty(subItem)) {
|
|
// bool = false
|
|
// break
|
|
// }
|
|
// }
|
|
// console.log(subItemm,this.selectSpec, bool)
|
|
// if (bool) {
|
|
// this.price = item.price
|
|
// break
|
|
// }
|
|
// }
|
|
}
|
|
console.log(flag)
|
|
// forEach循环改变原数组
|
|
this.specList.forEach(item => {
|
|
item.list.forEach(its => {
|
|
its.able = this.isAble(item.title, its.name);
|
|
});
|
|
});
|
|
},
|
|
addShopping () {
|
|
this.$emit('addShopping', {num: this.sukNum, price: this.price, spec: this.selectSpec}, this.type)
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.spec-popup {
|
|
&-header {
|
|
height: 200rpx;
|
|
box-sizing: border-box;
|
|
&-image {
|
|
width: 160rpx;
|
|
height: 160rpx;
|
|
}
|
|
}
|
|
|
|
&-container {
|
|
padding: 220rpx 40rpx 120rpx;
|
|
}
|
|
|
|
&-item {
|
|
&-suk {
|
|
height: 60rpx;
|
|
border: 2rpx solid transparent;
|
|
border-radius: 34rpx;
|
|
margin-right: 40rpx;
|
|
padding: 0 30px;
|
|
margin-bottom: 20rpx;
|
|
font-size: 32rpx;
|
|
color: #000;
|
|
background: #f5f5f5;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
&--active {
|
|
border-color: $u-type-primary;
|
|
color: $u-type-primary;
|
|
background: #EEFCFF;
|
|
}
|
|
|
|
&--disabled {
|
|
opacity: 0.5;
|
|
border-color: #e0e0e0;
|
|
color: #999999;
|
|
}
|
|
}
|
|
}
|
|
|
|
&-confirm {
|
|
height: 100rpx;
|
|
&-btn {
|
|
width: 90%;
|
|
height: 80rpx;
|
|
/deep/ .btn{
|
|
button{
|
|
font-size: 36rpx;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//数量组件样式调整
|
|
|
|
</style>
|