1. 程式人生 > >小程式購物車功能(支援手動輸入數量)以及側邊欄和列表欄聯動的實現

小程式購物車功能(支援手動輸入數量)以及側邊欄和列表欄聯動的實現

小組剛完成一個小程式專案,第一版正式釋出了,過程中也遇到了很多問題,這裡記錄一下我負責的模組中的購物車功能的實現過程。後期再把其他小夥伴的模組也一併貼上來分析一下,自己也學習一下他們的獨門技能!效果圖如下:

在這裡,計數器、購物籃做成元件用於複用,由於左右聯動的功能在另一個模組也有用到,所以就把這個功能也做成了一個元件,傳入不通的資料即可,左右聯動的實現方法在之前一片部落格有寫到

這篇部落格主要講一下選擇商品的功能,以及支援手動輸入的功能實現

數量選擇器

結構上採用左、中、右結構,加減號分別繫結兩個事件,中間輸入框繫結獲取焦點、失去焦點和change事件,用於支援手動輸入數量

<view class="wrapper">
    <view class="num-choose {{totalNum || change ? 'active': ''}}">
        <view class="minus {{totalNum || change ? 'active': ''}}" catchtap="minusHandle"             
          data-goodsId="{{goodsId}}">
            <tty-icon type="control-reduce"></tty-icon>
        </view>
        <view class="task task-left"></view>
        <input class="num" wx:if="{{totalNum || change}}" type="number" maxlength="4" 
          bindinput="changeNum"
         value="{{totalNum}}" bindfocus="focusNum" bindblur="blurNum" data-goodsId="
           {{goodsId}}"></input>
            <view class="task task-right"></view>
        <view class="plus" catchtap="plusHandle" data-goodsId="{{goodsId}}">
            <tty-icon type="control-community"></tty-icon>
        </view>
    </view>
</view>

作為元件,事件必須傳導到最上層,在那裡對資料進行處理

methods: {
    plusHandle(e) { // 增加事件
      let {totalNum, typeOneIndex, typeTwoIndex, goodsIndex, totalStock} = this.data
      let goodsId = e.currentTarget.dataset.goodsid;
      // let pageX = e.touches[0].pageX;
      // let pageY = e.touches[0].pageY;
      totalNum++;
      if (totalStock === 0) {
        wx.showToast({
          title: '請增加庫存!',
          icon: 'none',
          duration: 1500
        })
        return
      }
      if (totalNum > totalStock) { // 超過庫存
        wx.showToast({
          title: '庫存不足,請增加庫存!',
          icon: 'none',
          duration: 1500
        })
        return
      }
      this.setData({
        totalNum,
        change: false
      });
      let myEventDetail = {
        goodsId: goodsId,
        // pageX: pageX,
        // pageY: pageY,
        totalNum: totalNum,
        typeOneIndex: typeOneIndex,
        typeTwoIndex: typeTwoIndex,
        goodsIndex: goodsIndex,
        action: 'plus' // 增加事件標記
      }; // detail物件,提供給事件監聽函式
      this.triggerEvent('StepperEvent', myEventDetail) // 觸發父級事件
    },
    focusNum(e) {
      // 手動輸入時獲取基礎資料
      this.triggerEvent('StepperEvent', {
        action: 'getNum',
        num: Number(e.detail.value)
      });
    },
    blurNum(e) {
      // 失去焦點,改變狀態
      this.setData({
        change: false
      });
      // 購物籃失去焦點,商品數為0則清空
      let myEventDetail = {}
      let goodsId = e.currentTarget.dataset.goodsid;
      myEventDetail = {
        goodsId: goodsId,
        action: 'blur'
      };
      this.triggerEvent('StepperEvent', myEventDetail)
    },
    changeNum(e) {
      let num = Number(e.detail.value)
      let {typeOneIndex, typeTwoIndex, goodsIndex, totalStock} = this.data
      let goodsId = e.currentTarget.dataset.goodsid;
      let myEventDetail = {}
      let totalNum = 0;
      if (num > totalStock) { // 超過庫存
        wx.showToast({
          title: '庫存不足,請增加庫存!',
          icon: 'none',
          duration: 1500
        })
        this.setData({
          totalNum: num,
          change: false
        });
        totalNum = totalStock;
      } else {
        totalNum = num;
      }
      myEventDetail = {
        goodsId: goodsId,
        totalNum: totalNum,
        typeOneIndex: typeOneIndex,
        typeTwoIndex: typeTwoIndex,
        goodsIndex: goodsIndex,
        action: 'change'
      };
      this.setData({
        totalNum: totalNum,
        change: true
      });
      this.triggerEvent('StepperEvent', myEventDetail)
    },
    minusHandle(e) {
      let num = this.data.totalNum;
      let {typeOneIndex, typeTwoIndex, goodsIndex} = this.data
      let goodsId = e.currentTarget.dataset.goodsid;
      if (num <= 0) {
        return
      }
      num--;
      let myEventDetail = {
        goodsId: goodsId,
        totalNum: num,
        typeOneIndex: typeOneIndex,
        typeTwoIndex: typeTwoIndex,
        goodsIndex: goodsIndex,
        action: 'minus'
      };
      this.setData({
        totalNum: num,
        change: false
      });
      this.triggerEvent('StepperEvent', myEventDetail);
    }
  }

stemper元件是linkPage(商品左右聯動)元件的子元件,需要在linkPage裡過渡一下

linkPage.wxml

<view class="stepper" wx:if="{{!management}}">
    <tty-stepper size="small" goodsId="{{pes.goods_id}}" typeOneIndex="{{typeOneIndex}}"     
       typeTwoIndex="{{typeTwoIndex}}" goodsIndex="{{goodsIndex}}" totalStock="
       {{pes.stock}}" totalNum="{{pes.count}}" bindStepperEvent="stepperEvent">
    </tty-stepper>
</view>

linkPage.js

stepperEvent(e) {
      let myEventDetail = e.detail;
      if(myEventDetail.action === 'blur') return
      this.triggerEvent('ContentEvent', myEventDetail)
    }

購物籃和列表欄的觸發事件要有所區分

choose-goods.wxml

最後所有資料彙集到主頁面進行處理

choose-goods.js  根據事件標記區分是哪一類事件,進行不同的處理

/*商品列表新增事件*/
  contentEvent(e) {
    let {totalNum, action, typeOneIndex, typeTwoIndex, goodsIndex} = e.detail;
    if (action === 'getNum' || action === 'blur') {
      this.setData({ // 獲取總基數
        basePageTotalNum: this.data.pageTotalNum - e.detail.num
      });
      return
    }
    if (action === 'overStock') {
      Dialog({
        message: '該商品庫存不夠啦~',
        confirmButtonText: '我知道了',
        selector: '#tty-dialog-msg'
      }).then(() => {});
      return
    }
    this.addGoods(totalNum, action, typeOneIndex, typeTwoIndex, goodsIndex);
  },
  /*新增商品*/
  addGoods(totalNum, action, typeOneIndex, typeTwoIndex, goodsIndex) {
    let {typeData, goodsData, pageTotalNum} = this.data;
    let goods = typeData[typeOneIndex].goods_category_two[typeTwoIndex].goods[goodsIndex];
    typeData[typeOneIndex].goods_category_two[typeTwoIndex].goods[goodsIndex].count = totalNum;
    if (action === 'plus') {
      pageTotalNum++; // 商品總數
    } else if (action === 'minus') {
      pageTotalNum--;
    } else if (action === 'change') {
      pageTotalNum = this.data.basePageTotalNum + totalNum;
    }
    if (pageTotalNum < 0) {
      pageTotalNum = 0;
      return
    }
    goods.count = totalNum;
    goods.typeOneIndex = typeOneIndex;
    goods.typeTwoIndex = typeTwoIndex;
    goods.goodsIndex = goodsIndex;
    if (totalNum === 1 && action === 'plus') { // 購物籃裡沒有,則新增
      goodsData.unshift(goods);
    } else {
      if(goodsData.includes(goods)) { // 如果購物籃中已有商品,在原有基礎上加1
        let index = goodsData.indexOf(goods)
        totalNum === 0 ? goodsData.splice(index, 1) : goodsData[index].count = totalNum;
      }else { // 如果購物籃中沒有,則新增(針對手動修改)
        goodsData.unshift(goods);
      }
    }

    this.setData({
      goodsData,
      typeData,
      pageTotalNum,
      basePageTotalNum: pageTotalNum - totalNum
    });
    this.calculateMoney(goodsData);
  }
/*購物框新增事件*/
  basketEvent(e) {
    if (e.detail.action === 'getNum') {
      this.setData({ // 獲取計算基數
        basePageTotalNum: this.data.pageTotalNum - e.detail.num
      });
      return
    }
    let {
      totalNum, // 商品選擇的數量
      goodsId, // 修改商品時對應的商品id
      action // 操作行為
    } = e.detail;
    let {
      typeData, // 列表資料
      goodsData, // 購物籃資料
      pageTotalNum // 頁面商品總數
    } = this.data;

    if (action === 'plus') {
      pageTotalNum++;
    } else if (action === 'minus') {
      pageTotalNum--;
    } else if (action === 'change') {
      pageTotalNum = this.data.basePageTotalNum + totalNum;
    }
    if (pageTotalNum < 0) {
      pageTotalNum = 0;
    }
    for (let i = 0; i < goodsData.length; i++) {
      if (goodsData[i].goods_id === goodsId) {
        // 獲取索引,處理列表商品數量
        let typeOneIndex = goodsData[i].typeOneIndex;
        let typeTwoIndex = goodsData[i].typeTwoIndex;
        let goodsIndex = goodsData[i].goodsIndex;
        let count = 0;
        
        if (action === 'plus') {
          goodsData[i].count++;
          count = goodsData[i].count
        } else if (action === 'minus') {
          goodsData[i].count--;
          count = goodsData[i].count
          if (goodsData[i].count <= 0) {
            goodsData[i].count = 0;
            goodsData.splice(i, 1);
          }
        } else if (action === 'change') {
          if(totalNum <= 0) {
            goodsData[i].count = 0;
          }else {
            goodsData[i].count = totalNum;
          }
          count = goodsData[i].count
          // 失去焦點,數量為0,刪除
        } else if (action === 'blur' && goodsData[i].count === 0) {
          count = goodsData[i].count
          goodsData.splice(i, 1);
        }
        typeData[typeOneIndex].goods_category_two[typeTwoIndex].goods[goodsIndex].count = count;
      }
    }

    this.setData({
      goodsData,
      typeData,
      pageTotalNum,
      basePageTotalNum: pageTotalNum - totalNum
    });
    this.calculateMoney(goodsData);// 計算總金額
  }

列表欄中新增商品時,購物籃中沒有商品,跟隨列表欄變化而變化,屬於正向計算,比較簡單,有則加1,無則新增

反過來,購物籃中修改資料時就麻煩些了,需要拿到對應的商品id,反向查詢列表欄中的商品,並做修改

由上面的效果圖可以知道,資料的格式是三層巢狀的陣列,這就導致資料的查詢比較麻煩,必須迴圈遍歷才能找到最底層的資料

"data": [{
      goods_category_one_id: "221310651891449856",
      goods_category_one_name: "農藥",
      goods_category_two: [
        {
          goods_category_two_id: "221310651891449858",
          goods_category_two_name: "殺蟲劑",
          goods: [{
            amount: 0,
            base_unit_id: "221310649857212419",
            base_unit_name: "桶",
            count: 0,
            goods_id: "258169085668364288",
            goods_name: "聯苯·蟲蟎腈",
            goods_spec: "",
            price: 3.5,
            stock: 24,
            url: ""
          }]
        },
        {
          goods_category_two_id: "221310651891449859",
          goods_category_two_name: "殺菌劑",
          goods: [{
              amount: 0,
              base_unit_id: "221310649857212422",
              base_unit_name: "克",
              count: 0,
              goods_id: "258168407638147072",
              goods_name: "聯苯·蟲蟎腈",
              goods_spec: "",
              price: 3.5,
              stock: 24,
              url: ""
            },
            {
              amount: 0,
              base_unit_id: "221310649857212421",
              base_unit_name: "克",
              count: 0,
              goods_id: "259282725129682944",
              goods_name: "速克靈100g",
              goods_spec: "",
              price: 5.6,
              stock: 0,
              url: ""
            },
            {
              amount: 0,
              base_unit_id: "221310649857212420",
              base_unit_name: "盒",
              count: 0,
              goods_id: "259285646445645824",
              goods_name: "解決",
              goods_spec: "",
              price: 56,
              stock: 12,
              url: ""
            }
          ]
        }
      ]
    }