您的当前位置:首页正文

小程序商品规格属性界面布局,实现每种属性选择一个

2024-11-25 来源:个人技术集锦

废话不多说,上代码,

wxml:

<scroll-view class="container" style='height:{{noScrollHeight}};overflow:hidden;margin-bottom:100rpx;'>
  <view>
    <swiper class="goodsimgs" indicator-dots="true" autoplay="true" interval="3000" duration="1000">
      <swiper-item wx:for="{{goods.imageList}}" wx:key="unique">
        <image src="{{item.imageUrl}}" background-size="cover" mode='scaleToFill'></image>
      </swiper-item>
    </swiper>
    <!-- 收藏 -->
    <view class="l-collect" bindtap="closeAttrOrCollect" hidden='true'>
      <image class="icon" src="{{ collectBackImage }}"></image>
    </view>
    <view class="goods-info">
      <view class="c">
        <text class="num">库存{{goods.numCurrent}}{{goods.unit}}</text>
        <view class="price_share">
          <view class="price">
            <text class="pricel">¥{{goods.price}}</text>
            <text class="priceMart">¥{{goods.priceMart}}</text>
          </view>
          <button class='btnShare' open-type='share'>
            <view class="share">
              <image class="img" src="/static/images/icon_share .png" />
              <text class="txt">分享</text>
            </view>
          </button>
        </view>
        <text class="name">商品名称:{{goods.name}}</text>
      </view>
    </view>
    <view class="coupon-manage" bindtap='openCoupon' wx:if="{{couponList.length>0}}">
      <view class="txt">领券</view>
      <image class="img" src="/static/images/address_right.png"></image> 
    </view>
    <view class="goods-introduction">
      <text class="txt">商品简介</text>
      <!-- 宽度不变,高度自动变化,保持原图宽高比不变 -->
      <image class="img" wx:for="{{nodes}}" src="{{item.source}}" mode="widthFix" wx:key="unique" />
    </view>
  </view>
  <view class="bottom-btn" wx:if="{{!openAttr}}">
    <!-- 首页 -->
    <view class="l l-index" bindtap="goIndex">
      <image class="icon" src="/static/images/icon_index.png"></image>
    </view>
    <!-- 客服 -->
    <view class="l l-service">
      <!-- <button class="kf_button" open-type="contact" session-from="weapp">
        <image class="kf_image" src="/static/images/detail_kefu.png"></image>
      </button> -->
      <view class="df_1 l_h15">
        <contact-button size="27" class='pos' session-from="weapp"></contact-button>
        <image class="icon_kf" src="/static/images/detail_kefu.png"></image>
      </view>
    </view>
    <!-- 购物车 -->
    <view class="l l-cart">
      <view class="box">
        <text wx:if="{{cartGoodsCount>0}}" class="cart-count">{{cartGoodsCount}}</text>
        <image bindtap="openCartPage" class="shopcar" src="/static/images/icon_shopcar.png"></image>
      </view>
    </view>
    <view class="c" bindtap="addToCart">添加购物车</view>
    <view hidden='true' class="r" bindtap="addFast">购买</view>
  </view>
  <!-- 选择规格属性 -->
  <view wx:if="{{openAttr}}" class="animation-element-wrapper">
    <view class="blank" bindtap='closePop' />
    <view class="attr-pop">
      <view class="img-info">
        <image class="img" src="{{selectedImg}}"></image>
        <view class="info">
          <view class="c">
            <view class="p">¥{{selectedPrice}}</view>
            <view class="s">库存{{selectedNumCurrent}}{{goods.unit}}</view>
            <view class="a">{{checkedSpecText}}</view>
          </view>
        </view>
        <image class="imgClose" src="/static/images/icon_close.png" bindtap='closePop'></image>
      </view>
      <!-- 规格 -->
      <scroll-view class="spec-con" scroll-y='true'>
        <view class="spec-item" wx:for="{{attributeList}}" wx:key="name">
          <view class="name">{{item.name}}</view>
          <view class="values">
            <view class="value {{vitem.checked?'selected':''}}" bindtap="clickSkuValue" wx:for-item="vitem" wx:for="{{item.valueList}}" wx:key="id" data-value-id="{{vitem.id}}" data-value="{{item.name}}">
              {{vitem.value}}
            </view>
          </view>
        </view>
        <!-- 数量 -->
        <view class="number-item">
          <view class="name">数量</view>
          <view class="selnum">
            <view class="cut" bindtap="cutNumber">-</view>
            <!-- 数字键盘有点问题  so先disabled-->
            <input value="{{number}}" class="number" type="number" confirm-type='done' disabled='true' bindconfirm='confirmNum' />
            <view class="add" bindtap="addNumber">+</view>
          </view>
        </view>
      </scroll-view>
      <!-- 确定  #E64340-->
      <button class="btnConfirm" type='warn' bindtap="btnConfirm"> 确定 </button>
    </view>
  </view>
  <view wx:if="{{openCoupon}}" class="animation-element-wrapper">
    <view class="blank" bindtap='closeCoupon'/>
    <view class="coupon-pop">
      <view class="coupon-info">
        <image class="imgClose" src="/static/images/icon_close.png" bindtap='closeCoupon'></image>
        <view class="txtHeader">优惠券</view>
      </view>
      <view class='txtBody'>可领取优惠券</view>
      <scroll-view class="coupon-con" scroll-y='true'>
        <couponListView is-hidden="{{isHidden}}" coupon-list="{{couponList}}" />
      </scroll-view>
    </view>
  </view>
</scroll-view>

js:

var app = getApp();
var WxParse = require('../../lib/wxParse/wxParse.js');
var util = require('../../utils/util.js');
var api = require('../../config/api.js');
var user = require('../../services/user.js');

Page({
  data: {
    goods: {},
    attributeList: [],
    cartGoodsCount: 0,
    userHasCollect: 0,
    number: 1,
    checkedSpecText: '请选择规格数量',
    openAttr: false,
    noCollectImage: "/static/images/icon_collect.png",
    hasCollectImage: "/static/images/icon_collect_checked.png",
    collectBackImage: "/static/images/icon_collect.png",

    id: 0,
    name: '',
    price: 0.00,
    priceMart: 0.00,
    numCurrent: 0,
    unit: '',
    imageList: [],
    nodes: [],
    isCollect: true,
    selectedImg: '',
    selectedPrice: 0,
    selectedNumCurrent: 0,
    ids: '',
    commitWay: 0,
    noScrollHeight: '100%',
    goodsAttributeId: '0',
    openCoupon: false,
    couponList: [],
  },
  getGoodsInfo: function() {
    let that = this;
    util.request(api.GoodsDetail, {
      id: that.data.id
    }).then(function(res) {
      if (res.errno === 0) {

        that.setData({
          goods: res.data,
          selectedImg: res.data.imageList[0].imageUrl,
          selectedPrice: res.data.price,
          selectedNumCurrent: res.data.numCurrent,
          cartGoodsCount: res.data.shopcarNum,
        });
      }
    });

  },

  getGoodsIntroduction: function() {
    let that = this;
    util.request(api.GoodsIntroduction, {
      id: that.data.id
    }).then(function(res) {
      if (res.errno === 0) {
        that.setData({
          nodes: res.data,
        });
      }
    });
  },
  getAttributeList: function() {
    let that = this;
    util.request(api.AttributeList, {
      id: that.data.id
    }).then(function(res) {
      if (res.errno === 0) {
        let _attributeList = res.data.attributeList;
        // 如果仅仅只存在一个规格,那么商品页面初始化时默认checked
        if (_attributeList.length == 1) {
          if (_attributeList[0].valueList.length == 1) {
            _attributeList[0].valueList[0].checked = true
            that.setData({
              'checkedSpecText': "已选择:" + _attributeList[0].valueList[0].value,
              'ids': '' + _attributeList[0].valueList[0].id
            });
            that.getNumAndPrice();
          }
        }
        that.setData({
          attributeList: _attributeList,
        });

      }
    });
  },
  getNumAndPrice: function() {
    let that = this;
    util.request(api.NumAndPrice, {
      id: that.data.id,
      ids: that.data.ids
    }).then(function(res) {
      if (res.errno === 0) {
        if (!util.isBlank(res.data)) {
          that.setData({
            selectedPrice: res.data.price,
            selectedNumCurrent: res.data.numCurrent,
            selectedImg: res.data.imageUrl,
            goodsAttributeId: res.data.id,
          });
        } else {
          that.setData({
            selectedPrice: that.data.goods.price,
            selectedNumCurrent: 0,
            selectedImg: that.data.goods.imageList[0].imageUrl,
          });
        }
      }
    });
  },

  clickSkuValue: function(event) {
    let that = this;
    let specName = event.currentTarget.dataset.value;
    let specValueId = event.currentTarget.dataset.valueId;

    //TODO 性能可优化,加文末链接入群探讨
    let _attributeList = this.data.attributeList;
    for (let i = 0; i < _attributeList.length; i++) {
      if (_attributeList[i].name === specName) {
        for (let j = 0; j < _attributeList[i].valueList.length; j++) {
          if (_attributeList[i].valueList[j].id == specValueId) {
            //如果已经选中,则反选
            if (_attributeList[i].valueList[j].checked) {
              _attributeList[i].valueList[j].checked = false;
            } else {
              _attributeList[i].valueList[j].checked = true;
            }
          } else {
            _attributeList[i].valueList[j].checked = false;
          }
        }
      }
    }
    this.setData({
      'attributeList': _attributeList,
    });
    //重新计算spec改变后的信息
    this.changeSpecInfo();
    this.getNumAndPrice();
    //重新计算哪些值不可以点击,先不做
  },
  /*
   *返回已选属性的list
   */
  getCheckedSpecValue: function() {
    let checkedValues = [];
    let _attributeList = this.data.attributeList;
    for (let i = 0; i < _attributeList.length; i++) {
      let _checkedObj = {
        //name: _attributeList[i].specification,
        name: _attributeList[i].name,
        valueId: 0,
        valueText: ''
      };
      for (let j = 0; j < _attributeList[i].valueList.length; j++) {
        if (_attributeList[i].valueList[j].checked) {
          _checkedObj.valueId = _attributeList[i].valueList[j].id;
          _checkedObj.valueText = _attributeList[i].valueList[j].value;
          //checkedValues.push(_checkedObj);//放在这里,就不用过滤valueId为0的
        }
      }
      checkedValues.push(_checkedObj);
    }

    return checkedValues;

  },
  //根据已选的值,计算其它值的状态
  setSpecValueStatus: function() {

  },
  //判断规格是否选择完整
  isCheckedAllSpec: function() {
    return !this.getCheckedSpecValue().some(function(v) {
      if (v.valueId == 0) {
        return true;
      }
    });
  },
  getCheckedSpecKey: function() {
    let checkedValue = this.getCheckedSpecValue().map(function(v) {
      return v.valueId;
    });

    return checkedValueId;
  },
  getCheckedSpecText: function() {
    let checkedValue = this.getCheckedSpecValue().map(function(v) {
      return v.valueText;
    });

    return checkedValueText;
  },
  changeSpecInfo: function() {

    let checkedNameValue = this.getCheckedSpecValue();

    //设置选择的信息 (获取已选属性的文本数组?)
    let checkedValue = checkedNameValue.filter(function(v) {
      if (v.valueId != 0) {
        return true;
      } else {
        return false;
      }
    }).map(function(v) {
      return v.valueText;
    });
    let checkedValueId = checkedNameValue.filter(function(v) {
      if (v.valueId != 0) {
        return true;
      } else {
        return false;
      }
    }).map(function(v) {
      return v.valueId;
    });
    if (checkedValue.length > 0) {
      this.setData({
        'checkedSpecText': checkedValue.join(' '),
        'ids': checkedValueId.join(',')
      });
    } else {
      this.setData({
        'checkedSpecText': '请选择规格数量'
      });
    }
  },
  onLoad: function(options) {
    // 页面初始化 options为页面跳转所带来的参数
    if (options.id) {
      this.setData({
        id: parseInt(options.id)
      });
      this.getGoodsInfo();
      this.getGoodsIntroduction();
      this.getCouponList();
    }
  },
  onReady: function() {
    // 页面渲染完成

  },
  onShow: function() {
    
  },
  onHide: function() {
    // 页面隐藏

  },
  onUnload: function() {
    // 页面关闭

  },
  //收藏
  closeAttrOrCollect: function() {
    let that = this;
    if (this.data.isCollect) {
      this.setData({
        isCollect: false,
        collectBackImage: this.data.hasCollectImage
      });
    } else {
      this.setData({
        isCollect: true,
        collectBackImage: this.data.noCollectImage
      });
    }
    return;
    //添加或是取消收藏
    util.request(api.CollectAddOrDelete, {
        typeId: 0,
        valueId: this.data.id
      }, "POST")
      .then(function(res) {
        let _res = res;
        if (_res.errno == 0) {
          if (_res.data.type == 'add') {
            that.setData({
              'collectBackImage': that.data.hasCollectImage
            });
          } else {
            that.setData({
              'collectBackImage': that.data.noCollectImage
            });
          }

        } else {
          wx.showToast({
            image: '/static/images/icon_error.png',
            title: _res.errmsg,
            mask: true
          });
        }

      });
    //}

  },
  openCartPage: function() {
    wx.switchTab({
      url: '/pages/cart/cart'
    });
  },
  addToCart: function(commitWay) {
    var that = this;
    if (this.data.openAttr == false) {
      this.setData({
        commitWay: 1,
      });
      this.openPop();
    } else {
      //提示选择完整规格
      if (!this.isCheckedAllSpec()) {
        wx.showToast({
          image: '/static/images/icon_error.png',
          title: '请选择完整规格'
        });
        return false;
      }

      //验证库存
      if (this.data.selectedNumCurrent < this.data.number) {
        wx.showToast({
          image: '/static/images/icon_error.png',
          title: '没有库存'
        });
        return false;
      }

      //添加到购物车
      util.request(api.CartAdd, {
          goodsId: this.data.goods.id,
          goodsAttributeId: this.data.goodsAttributeId,
          number: this.data.number,
          setmealId: 0,
          activeId: 0,
          isSetmeal: 0,
          isJoinActivte: 0
        }, "POST")
        .then(function(res) {
          let _res = res;
          if (_res.errno == 0) {
            that.closePop();
            that.setData({
              cartGoodsCount: _res.data.shopcarNum,
            });
            wx.showToast({
              title: '添加成功'
            });
          } else {
            wx.showToast({
              image: '/static/images/icon_error.png',
              title: _res.errmsg,
              mask: true
            });
          }
        });
    }

  },
  cutNumber: function() {
    this.setData({
      number: (this.data.number - 1 > 1) ? this.data.number - 1 : 1
    });
  },
  addNumber: function() {
    if (this.data.number < this.data.selectedNumCurrent) {
      this.setData({
        number: this.data.number + 1
      });
    }
  },
  confirmNum: function() {
    if (this.data.number > this.data.selectedNumCurrent || this.data.number == 0) {
      util.showErrToast("数量超出范围");
    }
  },
  goIndex: function() {
    wx.switchTab({
      url: '/pages/index/index',
    })
  },
  openPop: function() {
    let _this = this;
    wx.getSystemInfo({
      success: function(res) {
        let data = res.screenHeight + 'rpx';
        _this.setData({
          noScrollHeight: data
        });
      },
    })
    if (this.data.openAttr == false) {
      //打开规格选择窗口
      this.setData({
        openAttr: true,
        num: 1,
      });
      this.getAttributeList();
    }
  },
  closePop: function() {
    this.setData({
      openAttr: false,
      noScrollHeight: '100%',
    });
  },
  btnConfirm: function() {
    if (this.data.commitWay == 1) {
      this.addToCart();
    } else if (this.data.commitWay == 2) {
      this.addFast();
    }
  },
  onShareAppMessage: function() {
    return {
      title: app.globalData.mallName,
      desc: this.data.goods.name,
      path: '/pages/goods/goods?id=' + this.data.id,
    }
  },
  openCoupon() {
    let _this = this;
    wx.getSystemInfo({
      success: function(res) {
        let data = res.screenHeight + 'rpx';
        _this.setData({
          noScrollHeight: data
        });
      },
    })
    if (this.data.openCoupon == false) {
      this.setData({
        openCoupon: true,
      });
    }
  },
  closeCoupon() {
    this.setData({
      openCoupon: false,
      noScrollHeight: '100%',
    });
  },
  getCouponList: function () {
    let that = this;
    util.request(api.CouponList, { sceneType: 2, goodsId: that.data.id, categoryId:0},"POST").then(function (res) {
      if (res.errno === 0) {
        that.setData({
          couponList: res.data,
        });
      }
    });
  },
})

时间原因,有些代码可优化,加群一起探讨,如果需要wxss,群里也有,效果图如下

显示全文