1.组件效果展示
也可访问链接查看网页效果...
后面又用uni-app写过一次,比这次稍微清晰一些
瞎封装组件系列:
2.使用方法
引入组件:
import goodsspec from '../../../../components/ghGoodsSpec/ghGoodsSpec.vue'
在<template>合适的区域使用
<goodsspec :goods="goods" :isShow="modalIsShow" @closeModal="closeModal"></goodsspec>
组件规定需要从父组件传商品规格信息的JSON,和一个布尔值,控制规格选择弹出框显示状态。
商品规格信息JSON必须符合组件规定标准(下面是例子中的JSON,库存的json,规格json):
modalIsShow:false,
goods: {
defaultimg:'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3457897174,1250176029&fm=26&gp=0.jpg',
"priceInfo": [{
"SKU": "1_1_5_11",
"stock": 888,
"price": 15535400.00,
"difference": "黑色;34;帆布鞋",
goodsimg:'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3323728883,1529837998&fm=26&gp=0.jpg'
}, {
"SKU": "1_3_5_12",
"stock": 99,
"price": 100.00,
"difference": "黑色;34;凉鞋",
goodsimg:'https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1006940732,1295991734&fm=26&gp=0.jpg'
}, {
"SKU": "1_4_6_13",
"stock": 150,
"price": 10011.00,
"difference": "粉色;36;运动鞋",
goodsimg:'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1916398967,1152344977&fm=26&gp=0.jpg'
},
{
"SKU": "1_3_5_12",
"stock": 9999,
"price": 100.00,
"difference": "红色;35;凉鞋",
goodsimg:'https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2755143450,1175568520&fm=26&gp=0.jpg'
}, {
"SKU": "1_4_5_13",
"stock": 100000,
"price": 10088.00,
"difference": "粉色;35;运动鞋",
goodsimg:'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3305162035,1457776276&fm=26&gp=0.jpg'
}
],
"SKUInfo": [{
"name": "颜色",
"sort": 1,
"items": [{
"name": "黑色",
"value": 1,
"sort": 1
},
{
"name": "红色",
"value": 3,
"sort": 2
},
{
"name": "粉色",
"value": 4,
"sort": 3
}
]
}, {
"name": "尺码",
"sort": 2,
"items": [{
"name": "35",
"value": 5,
"sort": 1
},
{
"name": "36",
"value": 6,
"sort": 2
},
{
"name": "34",
"value": 7,
"sort": 3
}
]
}, {
"name": "分类",
"sort": 4,
"items": [{
"name": "帆布鞋",
"value": 11,
"sort": 1
},
{
"name": "凉鞋",
"value": 12,
"sort": 2
},
{
"name": "运动鞋",
"value": 13,
"sort": 3
}
]
}]
}
3.代码实现
实现原理:
点击规格按钮时,把所选规格装到一个数组中,最后把组合的规格代入商品数量的json中查找。
<template>
<div class="ui-shade" v-show="isShow" @click="changisshow">
<div class="modal_cont" :class="isShow ? 'silderout':''" @click.stop="changisshow()">
<div class="page_modal" @click.stop>
<div id="geuige">
<div class="cose_modal" @click="changisshow()"><span>×</span></div>
<div class="goods_intro flex-star">
<img :src="defaultimg">
<div class="goods_info">
<div class="goods_price">{{price}}</div>
<div class="kucun">库存<span>{{num}}</span>件</div>
<div class="checkre flex-star">
<span>已选:</span>
<div>{{showAttr}}</div>
</div>
</div>
</div>
<div v-for="(item,itemindex) in goods.SKUInfo" class="chose_item">
<p class="chose_item_tit">{{item.name}}</p>
<div class="chose_item_cont flex-star">
<div v-for="(attr,attrindex) in item.items" v-on:click="specificationBtn(attr.name,itemindex,attrindex)"
v-bind:class="[attr.isShow?'':'noneActive',subIndex[itemindex] == attrindex?'productActive':'']">
{{attr.name}}
</div>
</div>
</div>
<div class="num_opt flex-between">
<span>购买数量</span>
<div class="numopt flex-star">
<div class="add">-</div>
<div>2</div>
<div class="reduce">+</div>
</div>
</div>
<div class="btn_cont flex-star">
<input type="button" name="" id="" value="加入购物车" />
<input type="button" name="" id="" value="马上抢" />
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
selectArr: [], //存放被选中的值
showAttr: "",
num: 0,
shopItemInfo: {}, //存放要和选中的值进行匹配的数据
subIndex: [], //是否选中 因为不确定是多规格还是单规格,所以这里定义数组来判断
price: '', //选中规格的价钱
defaultimg:''
}
},
props:{
isShow:{
type:Boolean,
default:false
},
goods:{
type:Object,
required:true
}
},
created: function() {
var self = this;
for (var i in self.goods.priceInfo) {
self.shopItemInfo[self.goods.priceInfo[i].difference] = self.goods.priceInfo[i]; //修改数据结构格式,改成键值对的方式,以方便和选中之后的值进行匹配
}
this.defaultimg = this.goods.defaultimg;
console.log(this.defaultimg,66666);
self.checkItem();
// self.specificationBtn("黑色",0,0)
},
methods: {
changisshow(e) {
this.$emit("closeModal",false)
},
specificationBtn: function(item, n, index) {
var self = this;
console.info(self.selectArr[n] + "66666" + item)
if (self.selectArr[n] != item) {
self.selectArr[n] = item;
self.subIndex[n] = index;
} else {
self.selectArr[n] = undefined;
self.subIndex[n] = -1; //去掉选中的颜色
}
var showarr = self.selectArr;
var tempTop = [];
showarr.forEach((item, i, arr) => {
if (item) {
tempTop.push(item)
}
})
self.showAttr = tempTop.join(";");
self.checkItem();
tempTop = []
},
checkItem: function() {
var self = this;
var option = self.goods.SKUInfo;
var result = []; //定义数组存储被选中的值
for (var i in option) {
result[i] = self.selectArr[i] ? self.selectArr[i] : '';
}
console.log(result);
if (self.shopItemInfo[result.join(";")]) {
self.num = self.shopItemInfo[result.join(";")].stock;
self.price = "¥" + (self.shopItemInfo[result.join(";")].price).toFixed(2);
self.defaultimg = self.shopItemInfo[result.join(";")].goodsimg;
} else {
self.num = 0;
self.price = "¥0.00";
self.defaultimg = self.goods.defaultimg;
}
for (var i in option) {
var last = result[i]; //把选中的值存放到字符串last去
for (var k in option[i].items) {
result[i] = option[i].items[k].name; //赋值,存在直接覆盖,不存在往里面添加name值
option[i].items[k].isShow = self.isMay(result); //在数据里面添加字段isShow来判断是否可以选择
}
console.info(last)
result[i] = last; //还原,目的是记录点下去那个值,避免下一次执行循环时避免被覆盖
}
self.$forceUpdate(); //重绘
},
isMay: function(result) {
for (var i in result) {
if (result[i] == '') {
return true; //如果数组里有为空的值,那直接返回true
}
}
if (this.shopItemInfo[result.join(";")]) { //匹配选中的数据的库存,若不为空返回true反之返回false
return this.shopItemInfo[result.join(";")].stock == 0 ? false : true;
}
}
}
}
</script>
<style scoped="scopedss">
.page_modal {
width: 100%;
background: linear-gradient(to right, #fff, #fff) 0 .2rem no-repeat;
background-size: 100% auto;
color: #222;
position: absolute;
bottom: 0;
}
.goods_intro {
padding: 0 .25rem;
}
.goods_intro img {
width: 2rem;
height: 2rem;
border: 3px solid #fff;
border-radius: 4px;
box-shadow: 0 0 6px rgba(0, 0, 0, .3);
}
.goods_info {
margin-left: .15rem;
}
.goods_price {
color: #feab27;
font-size: .34rem;
}
.kucun {
margin: .05rem 0;
}
.num_opt {
width: calc(100% - .5rem);
margin: 0 .25rem;
padding: .25rem 0;
border-bottom: 1px solid #eeeeee;
}
.numopt div {
width: .7rem;
height: .6rem;
background: #f5f5f5;
text-align: center;
line-height: .6rem;
color: #666;
}
.numopt div:nth-child(2) {
margin: 0 .08rem;
background: white;
color: #333;
}
.chose_item {
width: calc(100% - .5rem);
margin: 0 .25rem;
border-bottom: 1px solid #eeeeee;
padding-top: .3rem;
}
.chose_item_cont {
margin-top: .2rem;
}
.chose_item_cont div {
padding: .12rem .24rem;
background: #f6f4f5;
border-radius: 5px;
margin-bottom: .2rem;
}
.chose_item_cont div:nth-child(n + 2) {
margin-left: .2rem;
}
.chose_item .chose_item_tit {
font-size: .3rem;
font-weight: bold;
}
.ui-shade {
display: flex;
}
.silderout {
display: block;
bottom: 0;
animation: silderout .25s;
}
.btn_cont input {
height: .8rem;
width: 50%;
border: none;
color: white;
}
.btn_cont input:nth-child(1) {
background: #ff9e01;
border-radius: .8rem 0 0 .8rem;
}
.btn_cont input:nth-child(2) {
background: #ff6f00;
border-radius: 0 .8rem .8rem 0;
}
.btn_cont {
width: calc(100% - .5rem);
margin: 0 .25rem;
padding: .15rem 0;
}
.productActive {
background: #feab27 !important;
color: white;
}
.cose_modal {
position: absolute;
width: .4rem;
top: .4rem;
height: .4rem;
border: 1px solid #615763;
border-radius: 50%;
right: .2rem;
line-height: .34rem;
text-align: center;
}
@keyframes silderout {
from {
bottom: -100vh;
}
to {
bottom: 0;
}
}
@keyframes silderin {
from {
bottom: vh;
}
to {
bottom: -100vh;
}
}
.modal_cont {
position: absolute;
height: 100vh;
width: 100vw;
}
.noneActive {
background-color: #ccc;
opacity: 0.4;
color: #000;
pointer-events: none;
}
</style>
写的不好,多多指教。