类似SKU等问题,JS逻辑处理及VUE框架展示(H5)

H5的SKU实例:
前言:上一篇文章说到“管理系统SKU”的相关功能的代码书写;这一篇就讲解一下H5端该怎么用管理系统设置好的sku信息来渲染商品购买时的动态界面。
一、需求
1.首先这个功能其实就是普遍意义上的用户选择好商品后,弹出规格选择框的页面;网上有很多类似SKU相关的插件,用起来也是屡试不爽,但是我这里写出来主要是与上一个管理系统相照应,同时在有些朋友需要个性化的时候可以拿来用,因为一般那种sku插件都是有限制几个规格的,而我这个就比较开放,可以任意添加,同时样式也可以直接进行更改,加以修改就可以是自己的东西;
2.简单说一下这个功能,用户选择购买或者加入购物车后,就会从底部弹起一个弹窗;顶部是商品图片,图片会随规格的不同而改变图片,图片右边是价格和已选择规格,同样也是随规格不同而不同;底部是确认按钮;中间就是我们的规格,每一种规格单独占一块区域,由规格名为标题,规格值循环出的“按钮”为内容;
3.当选择任意一个规格中的规格值时,其他的规格要根据选择来判断自身规格值哪些能够用,哪些必须禁用不可选择;
4.限制用户不可点击规格值的条件有:库存不足、没有该组合(也就是价格为0或者空)
二、成品展示
在这里插入图片描述
全部置灰状态(在没有确定规格之前,图片有一个默认值)
在这里插入图片描述
三、实现过程梳理
1.html部分,也是vue组件中的一部分,可以先复制运行一下看看效果在用,随便写的案例,命名也不是特别规范用的时候自己规范一下就好了,功能是有的。

<template>
    <div class="choose">
      <div class="mengo"></div>
      <div class="chooseBox">
        <div class="top">
          <img :src="submitsData.cover||defaultImg" alt="商品图片" />
          <div class="content">
            <div class="specification">
              <div class="money">¥{{submitsData.money}}</div>
              <div v-if="submitsData.money==0" class="choosed">选择规格</div>
              <div v-else class="choosed">{{JSON.stringify(submitsData.specification)}}</div>
            </div>
            <div class="num">
              <button class="sub btn" @click="sub()"></button>
              <input type="number" v-model="submitsData.num" readonly="readonly" />
              <button class="add btn" @click="add()"></button>
            </div>
          </div>
        </div>
        <div class="specification">
          <div v-for="(i,index) in specificationList" :key="index">
            <p class="title">{{i.name}}</p>
            <div class="content">
              <label class="item" :class="{active : j.value==submitsData.specification[index]}" v-for="(j,index2) in i.value" :key="index2">
                <input
                  :id="'r'+index+index2"
                  type="radio"
                  @click="typeChekced(index,j.value,0)"
                  @change="typeChekced(index,j.value,1)"
                  name="index"
                  v-model="submitsData.specification[index]"
                  :value="j.value"
                  class="lable_input"
                  :disabled="j.disabled"
                />
                <span :class="j.disabled?'greyFont':''">{{j.value}}</span>
              </label>
            </div>
          </div>
        </div>
        <div class="btn">
          <button @click="submits">确定</button>
        </div>
      </div>
    </div>
</template>

2.js部分
(1)这个地方我们就慢慢讲一下,因为比较复杂;首先submitsData这个对象是你要提交给后台的,也就是用户所选规格及其他参数,这里其他参数就不要了就只要specification,也就是我们的规格;讲解代码我都放到代码注释去,细心看就行

export default {
	data() {
		return{
			submitsData:{
				specification: [],//当前已选择的规格,也就是提交的数据
				num: 1,//数量
				speId: "",//规格对应的Id
				maxNum: 0,//库存值,也就是最大可选数量
				money: 0,//该规格下的价格
        		cover: "",//该规格的图片
			},
			specificationList: [],//这是我们这个商品所有的规格组合,也就是上面图中必须全部显示出来的那些按钮
		    speList: [{speList:["700g","坚果"]},{speList:["500g","坚果"]},{speList:["700g","零食"]}],//后台告诉我们可用的规格
		    defaultImg: "http://wjjknet.oss-cn-shenzhen.aliyuncs.com/upload/productImages/2020/08/17/32de767b-bf32-4d4c-8897-a34bc7854fab.jpg",//默认商品图片
	    }
    },
   mounted() {
    this.init();
   },
   methods:{
    	init(){
    		//规格重构,为每一个规格增加一个disable属性,保证后面禁用功能的实现
	      for (let i = 0; i < this.specificationList.length; i++) {
	        for (let j = 0; j < this.specificationList[i].value.length; j++) {
	          this.specificationList[i].value[j] = { 'value': this.specificationList[i].value[j], 'disabled': true }
	        }
	      }
	      //取当前规格对应可选择规格数大小的数组,
	      //然后再清空数组每一项即可得到一个未选中任何规格的数组
	      //这句话不怎么好理解,简单的说就是让提交规格中的数组长度等于规格种类数,
	      //这样用户在选择对应规格的时候数据才会双向绑定,
	      //如果有3中规格,你数组长度只有2,那么当用户选择第三个规格值的时候就没有绑定值,
	      //有时候就会出现点不动,或者点了有效果但是没数据;注意使用深拷贝不然会一起动
	      this.submitsData.specification = JSON.parse(JSON.stringify(this.speList[0].speList));
	      for (let i = 0; i < this.specificationList.length; i++) {
	        this.submitsData.specification[i] = "";
	      }
	      this.renderBtn();
    	},
    }
}

(2)上面讲了数据准备,接下来将操作相关的JS,下面的都是方法,直接放到上面的methods里面即可
先看第一个方法,也就是我们在init()中首先调用的方法,直接看代码,有详细讲解和数据举例

    //通过计算标识出哪些规格的属性是禁用的
    renderBtn() {
      var list = this.specificationList;
      var ownList = this.speList;
      var nowList = this.submitsData.specification;
      try {
        console.log('所有规格:', list)//[[{disabled: false,value: "300g"},{disabled: false,value: "700g"}],[{disabled: false,value: "坚果"},{disabled: false,value: "坚1果"}],]
        console.log('可用规格:', ownList)//[{speList:["700g","坚果"]},{speList:["700g","坚果"]},{speList:["700g","坚果"]}]
        console.log('选择的规格:', nowList)//["700g","坚果"],这里数组下标的值就是list的第一层数组所在的下标
        for (let i = 0; i < list.length; i++) {//必须重置所有状态未禁用,否则会不能正常出现效果
          for (let j = 0; j < list[i].value.length; j++) {
            list[i].value[j].disabled = true;
          }
        }
        if (list.length == 1) {
          for (let i = 0; i < ownList.length; i++) {
            for (let j = 0; j < list[0].value.length; j++) {
              if (ownList[i].speList[0] == list[0].value[j].value) {
                list[0].value[j].disabled = false;
              }
            }
          }
          return
        }
        for (let i = 0; i < nowList.length; i++) {
          // console.log('模拟当前在循环被点击按钮为:', (nowList[i] || '未选择'), i)
          for (let m = 0; m < list.length; m++) {//m代表规格种类的下标,如果为1就是代表正在处理第一种规格,如重量【300kg,500kg】这个数组
            if (m != i) {//去掉非当前循环选择作为参照规格的下标,也就是去掉的规格就直接填充进入对比序列,如去掉300g 那么如果是两个规格的就是["300g","待定"]
              for (let j = 0; j < list[m].value.length; j++) {
                // console.log('当前处理的按钮为:', list[m].value[j].value)
                for (let k = 0; k < ownList.length; k++) {
                  if (nowList[i] == "") {
                    if (list[m].value[j].value == ownList[k].speList[m]) {
                      list[m].value[j].disabled = false;
                    }
                  } else {
                    if (nowList[i] == ownList[k].speList[i] && list[m].value[j].value == ownList[k].speList[m]) {
                      list[m].value[j].disabled = false;
                    }
                  }
                  // console.log((nowList[i] || '未选择') + '**' + list[m].value[j].value, '处理结果:', list[m].value[j].disabled, i + '_' + j + '_' + k, ownList[k].speList[i], ownList[k].speList[m])
                }
              }
            }
          }
        }
        
      } catch (e) {
        console.log(e)
      }
    },

(3)用户点击按钮后执行的方法

    typeChekced(index, value, int) {
      if (int == 0 && this.submitsData.specification[index] != "" && this.submitsData.specification[index] == value) {//这里一定要注意只有当不触发change事件的时候(int值来判断)且需要清除选中的时候去变动我们v-model绑定的值,否则会导致渲染极其卡顿(猜测是多次重绘的结果)
        this.submitsData.specification[index] = "";
        this.submitsData.speId = null;
        this.submitsData.maxNum = 0;
        this.submitsData.num = 0;
        this.submitsData.money = 0;
        this.submitsData.cover = this.defaultImg;
      }
      if (int == 1) {
        let subSpecification = JSON.stringify(this.submitsData.specification);
        for (let i of this.speList) {
          if (i.specification == subSpecification) {
          //下面的赋值因人而异,根据后端反的字段来,这里就是找到对应规格下的相关参数并赋值
            this.submitsData.speId = i.id;
            this.submitsData.maxNum = i.inventory;
            this.submitsData.num = 1;
            this.submitsData.money = i.money;
            this.submitsData.cover = i.exhibitionImg;
          }
        }
      }
      //最关键的就是每次赋完值又要重新计算哪些可选哪些不可选
      this.renderBtn();
    },
    sub() { //数量减
      if (this.submitsData.num <= 1) {
        return
      }
      this.submitsData.num--;
    },
    add() {     //数量加
      if (this.submitsData.num == 0) {
        $.alert("请先选择规格", "提醒");
        return;
      }
      this.submitsData.num++;
      return;
    },

(4)样式我还是给出来,可以自己进行更改哈(注意这里是相对于窗口定位的哦)

    .choose {
        background-color: rgba(0, 0, 0, .6);
        opacity: 1;
        position: fixed;
        bottom: 0;
        left: 0;
        right: 0;
        width: 100%;
        height: 100%;
        z-index: 100;

        .mengo {
            background-color: rgba(0, 0, 0, .6);
            opacity: 1;
            position: absolute;
            bottom: 0;
            left: 0;
            right: 0;
            width: 100%;
            height: 100%;
            z-index: 101;
        }

        .chooseBox {
            position: absolute;
            height: 80%;
            padding: 0.35rem;
            width: calc(100% - 0.7rem);
            background-color: white;
            top: 20%;
            left: 0;
            right: 0;
            z-index: 102;
            overflow-y: scroll;
            overflow-x: hidden;
            display: flex;
            flex-direction: column;

            .top {
                height: 2.19rem;
                width: 100%;
                display: flex;
                flex-direction: row;

                img {
                    width: 2.19rem;
                    height: 2.19rem;
                    border-radius: 0.1rem;
                    object-fit: scale-down;
                }

                .content {
                    display: flex;
                    flex-direction: column;
                    height: 2.19rem;
                    margin-left: 0.35rem;
                    width: calc(100% - 0.35rem - 2.19rem);

                    .specification {
                        display: flex;
                        flex-direction: column;
                        width: 100%;
                        height: 1rem;

                        .money {
                            font-size: 0.32rem;
                            color: #ed6969;
                            height: 0.32rem;
                            line-height: 0.32rem;
                            width: 100%;
                            margin-top: 0.2rem;
                        }

                        .choosed {
                            font-size: 0.28rem;
                            color: #989898;
                            margin-top: 0.2rem;
                            height: 0.28rem;
                            line-height: 0.28rem;
                            width: 100%;
                        }
                    }

                    .num {
                        display: flex;
                        flex-direction: row;
                        margin-top: 0.5rem;
                        justify-content: flex-end;
                        width: 100%;
                        height: 0.35rem;

                        input {
                            width: 3em;
                            text-align: center;
                            font-size: 0.25rem;
                            color: #989898;
                        }

                        .btn {
                            background: none;
                            width: 0.33rem;
                            height: 0.33rem;
                            background-size: 100%;
                            background-repeat: no-repeat;
                        }

                        .sub {
                            background-image: url("/static/wx/images/sub.png");
                        }

                        .add {
                            background-image: url("/static/wx/images/add.png");
                        }
                    }
                }
            }

            .specification {
                margin-top: 0.6rem;
                width: 100%;

                .title {
                    font-size: 0.28rem;
                    color: #333333;
                    font-weight: bold;
                }

                .content {
                    display: flex;
                    flex-wrap: wrap;
                    box-sizing: border-box;
                    padding: 0.3rem 0;
                    margin-bottom: 0.3rem;

                    .active {
                        color: #fa694c !important;
                        background-color: #ffdfda !important;
                        border: 1px solid #fa694c !important;
                        display: flex;
                        align-items: center;
                        justify-content: center;
                        text-align: center;
                    }

                    .item {
                        border-radius: 0.1rem;
                        background-color: #f3f3f3;
                        font-size: 0.23rem;
                        color: #333333;
                        padding: 0.1rem 0.3rem;
                        display: flex;
                        align-items: center;
                        margin-bottom: 0.2rem;
                        margin-right: 0.2rem;

                        .lable_input {
                            width: 0;
                            height: 0;
                        }

                        .img-label {
                            display: inline-block;
                            width: 0.5rem;
                            height: 0.5rem;
                            background-color: #d5d5d5;
                            border-radius: 0.1rem;
                            overflow: hidden;
                            margin-right: 0.17rem;
                        }

                        .greyFont {
                            color: #cccccc;
                        }
                    }
                }
            }

            .btn {
                text-align: center;
                box-sizing: border-box;

                button {
                    width: 100%;
                    height: 0.85rem;
                    border-radius: 50px;
                    background-color: #fa694c;
                    color: #fff;
                    font-size: 0.35rem;
                    margin-top: 2rem;
                }
            }
        }
    }

结语:以上就是手写sku功能的代码,有些地方确实很不容易理解;但是你先测试一下就知道是不是对的,然后再去一步一步的走debug就能懂了。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值