VUE类似淘宝选择商品多规格(库存判断)

论坛 期权论坛 编程之家     
选择匿名的用户   2021-5-31 20:53   11   0

1.组件效果展示

也可访问链接查看网页效果...


瞎封装组件系列:

VUE简单提示框

VUE树形图(递归实现)

VUE多店铺购物车

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>

写的不好,多多指教。

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP