小程序外卖订单界面

1.效果界面
微信截图_20191230134113.png
微信截图_20191230134433.png
微信截图_20191230134502.png
微信截图_20191230134515.png

2.涉及功能

*左侧商品类型、右侧商品可以相互控制;
*商品列表加减及购物车商品加减icon消失、显示;
*商品每一次加减,页面视图变化(数量、价格变化、购物车置灰);

3.贴上所有代码

1.wxml
    
<view class="container">
    <view class="index-cont">
      <!-- 左边类型 -->
      <view class="index-left">
        <view wx:for="{{foodsList}}" wx:key="index" class="item {{curId === 'item'+index?'on':''}}" data-id="item{{index}}" bindtap="scrollToViewFn">{{item.name}}</view>
      </view>
      <!-- 右边产品 -->
      <scroll-view class="index-right" scroll-y="{{true}}" scroll-into-view="{{initView}}" scroll-with-animation="true" bindscroll="onPageScroll">
        <view class="boxs">
          <block wx:for="{{foodsList}}" wx:key="index">
            <view class="index-title" id="item{{index}}">{{item.name}}</view>
            <view class="item" wx:for="{{item.list}}" wx:key="ind" wx:for-item="itm" wx:for-index="ind" bindtap="showGoodDetail(itm)">
              <view class="pic"><image src="{{itm.pic}}" mode="aspectFill"></image></view>
              <view class="main">
                <view class="tit">{{itm.title}}</view>
                <view class="desc">{{itm.info}}</view>
                <view class="money">¥{{itm.price}}</view>
              </view>
              <view class="box">
                <view wx:if="{{itm.num !== 0}}" class="icon" catchtap="reduceNum(index, ind, itm)"><image src="../../../static/images/reduce-icon.png" alt=""></image></view>
                <input wx:if="{{itm.num !== 0}}" type="text" disabled wx:model="{{itm.num}}"/>
                <view class="icon" catchtap="addNum(index, ind, itm)"><image src="../../../static/images/add-icon.png" alt=""></image></view>
              </view>
            </view>
          </block>
        </view>
      </scroll-view>
    </view>
    <view class="index-cart">
      <view class="left">
        <view class="cart-num" wx:if="{{cartList.length === 0}}">
          <image src="../../../static/images/cart.png"></image>
        </view>
        <view class="cart-num on" wx:else bindtap="showCartMask">
          <image src="../../../static/images/cart.png"></image>
          <text>{{totalNum}}</text>
        </view>
        <view class="cart-money">¥{{totalMoney}}</view>
      </view>
      <view class="order-btn" bindtap="submitOrder">去结算</view>
    </view>

    <!--购物车弹窗-->
    <view class="dialog" wx:if="{{isShowCartMask && cartList.length !== 0}}" bindtap="hiddenCartMak()">
      <view class="boxs" catchtap="stopMaopao()">
        <view class="title-block">
          <text>已选商品</text>
          <view class="clear" bindtap="clearCart"><image src="../../../static/images/del.png"></image>清空</view>
        </view>
        <scroll-view class="content" scroll-y="{{true}}" scroll-with-animation="true">
          <block wx:for="{{cartList}}" wx:key="index">
            <view class="item" id="{{item.view}}">
              <view class="tit">{{item.name}}</view>
              <view class="right">
                <text>¥{{item.price}}</text>
                <view class="box">
                  <view class="icon" bindtap="reduceCart(index, item)"><image src="../../../static/images/reduce-icon.png" alt=""></image></view>
                  <input type="text" disabled wx:model="{{item.num}}"/>
                  <view class="icon" bindtap="addCart(index, item)"><image src="../../../static/images/add-icon.png" alt=""></image></view>
                </view>
              </view>
            </view>
          </block>
        </scroll-view>
      </view>
    </view>
    <!--商品详情弹窗-->
    <view class="dialog1" wx:if="{{isShowDetail}}">
      <scroll-view class="detbox" scroll-y="{{true}}" scroll-with-animation="true">
        <image class="img" src="{{goodDetail.pic}}" mode="aspectFit"></image>
        <view class="box">
          <view class="tit">{{goodDetail.title}}</view>
          <view class="money">¥{{goodDetail.price}}</view>
          <view class="desc">{{goodDetail.info}}</view>
        </view>
        <view class="close" bindtap="hideDetail"><image src="../../../static/images/close_ico.png"></image></view>
      </scroll-view>
    </view>
  </view>
2.script
createPage({
  data: {
    foodsList: [], // 商品数据
    cartList: [], // 购物车数据
    isShowCartMask: false,
    totalNum: 0,
    totalMoney: 0,
    initView: 'item0', // 根据此变量的变化,控制左侧选中状态、右侧滑动
    curId: 'item0',
    isShowDetail: false,
    goodDetail: {},
    screenWidth: 0, // 手机屏幕宽度
    heightArray: [0] // 右侧每一个类型的高度区间数组
  },
  onLoad() {
    this.getGoodsData()
  },
  methods: {
    async getGoodsData() {
      const that = this
      const res = await getGoodsInfo({})
      this.foodsList = res
      wx.getSystemInfo({
        success: (ress) => {
          that.screenWidth = ress.windowWidth
        }
      })
      this.getHeightSection()
    },
    // 设置高度区间 所有单位转化为rpx
    getHeightSection() {
      const that = this
      let hg = 0
      for (let index = 0; index < that.foodsList.length - 1; index++) {
        hg += 70 + that.foodsList[index].list.length * 212
        that.heightArray.push(hg)
      }
    },
    // 获取高度区间的下标
    getHeightIndex(arr, hg) {
      const that = this
      arr.forEach((item, index) => {
        if (hg >= item) {
          that.setData({
            curId: 'item' + index
          })
        }
      })
    },
    // 左边菜单控制右边
    scrollToViewFn(e) {
      this.setData({
        initView: e.target.dataset.id,
        curId: e.target.dataset.id
      })
    },
    // 右边滚动控制左边
    onPageScroll(e) {
      const that = this
      let scrollTop = e.detail.scrollTop * 750 / that.screenWidth
      this.getHeightIndex(that.heightArray, scrollTop)
    },
    // 商品列表的减号点击
    reduceNum(index, ind, item) {
      const that = this
      let val = 'foodsList[' + index + '].list[' + ind + '].num'
      this.setData({
        [val]: item.num - 1
      })
      // 如果商品为0,就把当前商品在购物车清除
      // 如果不为0, 就将当前商品数量减1
      if (that.foodsList[index].list[ind].num === 0) {
        that.removeAarry(that.cartList, item.id)
      } else {
        that.cartList.forEach((itm, i) => {
          if (itm.id === item.id) {
            let value = 'cartList[' + i + '].num'
            that.setData({
              [value]: itm.num - 1
            })
          }
        })
      }
      this.computed()
    },
    // 商品列表的加号点击
    addNum(index, ind, item) {
      const that = this
      let val = 'foodsList[' + index + '].list[' + ind + '].num'
      this.setData({
        [val]: item.num + 1
      })
      // 如果商品为1,就把当前商品加入购物车
      // 否则, 就将当前商品数量加1
      if (that.foodsList[index].list[ind].num === 1) {
        let val = { id: item.id, name: item.title, price: item.price, num: 1, index: index, ind: ind, pic: item.pic }
        that.cartList.push(val)
      } else {
        that.cartList.forEach((itm, i) => {
          if (itm.id === item.id) {
            let value = 'cartList[' + i + '].num'
            that.setData({
              [value]: itm.num + 1
            })
          }
        })
      }
      this.computed()
    },
    // 购物车的减号点击
    reduceCart(index, item) {
      const that = this
      let val = 'foodsList[' + item.index + '].list[' + item.ind + '].num'
      let val1 = 'cartList[' + index + '].num'
      this.setData({
        [val]: item.num - 1,
        [val1]: item.num - 1
      })
      // 如果商品为0,就把当前商品在购物车清除
      // 如果不为0, 就将当前商品数量减1
      if (that.cartList[index].num === 0) {
        that.removeAarry(that.cartList, item.id)
      }
      this.computed()
    },
    // 购物车的加号点击
    addCart(index, item) {
      const that = this
      let val = 'cartList[' + index + '].num'
      that.setData({
        [val]: item.num + 1
      })
      this.computed()
    },
    // 清空购物车
    clearCart() {
      const that = this
      wx.showModal({
        title: '提示',
        content: '清空购物车?',
        success: function (res) {
          if (res.confirm) {
            that.setData({
              cartList: []
            })
            that.foodsList.forEach((item, i) => {
              item.list.forEach((itm, j) => {
                let value = 'foodsList[' + i + '].list[' + j + '].num'
                that.setData({
                  [value]: 0
                })
              })
            })
            that.computed()
          }
        }
      })
    },
    // 计算选择商品总价格和总数量
    computed() {
      const that = this
      let num = 0
      let money = 0
      that.cartList.forEach(item => {
        num += item.num
        money += parseFloat(item.price) * item.num
      })
      that.setData({
        totalNum: num,
        totalMoney: money
      })
    },
    // 将数量为0的时候,对应商品在购物车中删除
    removeAarry(arr, id) {
      arr.forEach((item, index) => {
        if (item.id === id) {
          arr.splice(index, 1)
        }
      })
      return arr
    },
    showCartMask() {
      this.isShowCartMask = !this.isShowCartMask
    },
    hiddenCartMak() {
      this.isShowCartMask = false
    },
    stopMaopao() {
    },
    showGoodDetail(item) {
      this.goodDetail = item
      this.isShowDetail = true
    },
    hideDetail() {
      this.isShowDetail = false
    },
    // 订单提交
    submitOrder() {
    }
  }
})

3.css

<style lang='scss'>
@import '../../style/base.scss';
page {
  height: 100%;
}
.container {
  height: 100vh;
  background-color: #fff;
  box-sizing: border-box;
  overflow: hidden;
    .dialog1{
      width: 100%;
      height: 100vh;
      position: fixed;
      top: 0;
      left: 0;
      background-color: rgba(0,0,0, 0.5);
      z-index: 4;
        .detbox{
          position: fixed;
          bottom: 0;
          left: 0;
          right: 0;
          background-color: #fff;
          width: 100%;
          max-height: 700rpx;
          overflow-y: auto;
          color: #333;
          border-radius: 40rpx 40rpx 0 0;
            .img{
              width: 100%;
              height: 375rpx;
              background: rgba(0,0,0,0.6);
            }
            .box{
              padding: 20rpx 30rpx 40rpx;
              box-sizing: border-box;
                .tit{
                  font-size: 28rpx;
                  color: #333;
                  font-weight: bold;
                }
                .money{
                  font-size: 26rpx;
                  color: #f00;
                  margin: 10rpx 0;
                }
                .desc{
                  font-size: 22rpx;
                  color: #666;
                  line-height: 32rpx;
                }
            }
            .close{
              width: 50rpx;
              height: 50rpx;
              position: absolute;
              right: 20rpx;
              top: 20rpx;
              display: flex;
              align-items: center;
              justify-content: center;
                image{
                  width: 40rpx;
                  height: 40rpx;
                }
            }
        }
    }
    .dialog{
      width: 100%;
      height: 100vh;
      position: fixed;
      top: 0;
      left: 0;
      background-color: rgba(0,0,0, 0.5);
      z-index: 2;
        .boxs{
          position: fixed;
          bottom: 80rpx;
          left: 0;
          right: 0;
          z-index: 6;
          background-color: #fff;
          width: 100%;
          max-height: 600rpx;
          color: #333;
            .title-block{
              padding: 0 30rpx;
              box-sizing: border-box;
              display: flex;
              align-items: center;
              justify-content: space-between;
              height: 70rpx;
              background: #EEF0F1;
                text{
                  font-size: 26rpx;
                  color: #666;
                }
                .clear{
                  font-size: 22rpx;
                  color: #888;
                  display: flex;
                  align-items: center;
                    image{
                      width: 24rpx;
                      height: 24rpx;
                      margin-right: 10rpx;
                    }
                }
            }
            .content{
              width: 100%;
              max-height: 530rpx;
              overflow-y: auto;
              padding-bottom: 30rpx;
              box-sizing: border-box;
                .item{
                  width: 690rpx;
                  height: 80rpx;
                  line-height: 80rpx;
                  margin: 0 auto;
                  position: relative;
                  display: flex;
                  align-items: center;
                  justify-content: space-between;
                    &::after{
                      position: absolute;
                      width: 100%;
                      height: 1rpx;
                      background: #f2f2f2;
                      content: '';
                      bottom: 1rpx;
                      left: 0;
                    }
                    .tit{
                      width: 400rpx;
                      overflow: hidden;
                      text-overflow: ellipsis;
                      white-space: nowrap;
                      font-size: 28rpx;
                      color: #333;
                    }
                    .right{
                      display: flex;
                      justify-content: flex-start;
                      align-items: center;
                      height: 80rpx;
                        text{
                          font-size: 26rpx;
                          color: #f00;
                        }
                        .box{
                          display: flex;
                          justify-content: flex-start;
                          align-items: center;
                          flex-wrap: nowrap;
                          margin-left: 20rpx;
                          height: 80rpx;
                            .icon{
                              width: 34rpx;
                              height: 34rpx;
                              display: flex;
                              align-items: center;
                              justify-content: center;
                                image{
                                  width: 34rpx;
                                  height: 34rpx;
                                }
                            }
                            input{
                              width: 60rpx;
                              height: 34rpx;
                              border: none;
                              color: #333;
                              text-align: center;
                              font-size: 26rpx;
                            }
                            
                        }
                    }
                }
            }
        }
    }
    .index-cont{
      height: calc(100vh - 80rpx);
      display: flex;
      justify-content: space-between;
      .index-left{
        width: 160rpx;
        height: 100%;
        background: #efefef;
          .item{
            font-size: 26rpx;
            color: #333;
            border-bottom: 1rpx dashed #666;
            height: 80rpx;
            line-height: 80rpx;
            padding: 0 20rpx;
            box-sizing: border-box;
              &.on{
                background: #fff;
              }
          }
      }
      .index-right{
        width: 590rpx;
        height: 100%;
          .boxs{
            padding: 0 30rpx;
            box-sizing: border-box;
            width: 100%;
          }
          .index-title{
            height: 70rpx;
            line-height: 70rpx;
            background: #f7f7f7;
            padding-left: 30rpx;
            font-size: 26rpx;
            color: #666;
            box-sizing: border-box;
          }
          .item{
            padding: 30rpx 0;
            box-sizing: border-box;
            display: flex;
            justify-content: space-between;
            position: relative;
            height: 212rpx;
              &::after{
                position: absolute;
                top: 0rpx;
                left: 0;
                background: #ccc;
                width: 100%;
                height: 1rpx;
                content: '';
              }
              .pic{
                width: 150rpx;
                height: 150rpx;
                  image{
                    width: 100%;
                    height: 100%;
                  }
              }
              .main{
                width: 380rpx;
                padding-left: 30rpx;
                box-sizing: border-box;
                  .tit{
                    font-size: 26rpx;
                    color: #333;
                    font-weight: bold;
                  }
                  .desc{
                    font-size: 22rpx;
                    color: #999;
                    line-height: 30rpx;
                    margin: 5rpx 0 10rpx;
                    min-height: 65rpx;
                  }
                  .money{
                    font-size: 28rpx;
                    color: #f00;
                  }
              }
              .box{
                display: flex;
                justify-content: flex-start;
                align-items: center;
                flex-wrap: nowrap;
                margin-left: 10rpx;
                height: 34rpx;
                position: absolute;
                right: 0;
                bottom: 30rpx;
                  .icon{
                    width: 34rpx;
                    height: 34rpx;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                      image{
                        width: 34rpx;
                        height: 34rpx;
                      }
                  }
                  input{
                    width: 60rpx;
                    height: 34rpx;
                    border: none;
                    color: #333;
                    text-align: center;
                    font-size: 26rpx;
                  }
                  
              }
          }
      }
    }
    .index-cart{
      width: 100%;
      height: 80rpx;
      display: flex;
      align-items: center;
      justify-content: flex-start;
      position: relative;
      z-index: 3;
        .left{
          width: 470rpx;
          height: 100%;
          background: #3e3a39;
          display: flex;
          align-items: center;
          justify-content: flex-start;
            .cart-num{
              width: 100rpx;
              height: 100rpx;
              background: #6E6D6C;
              position: relative;
              padding:25rpx;
              box-sizing: border-box;
              border-radius: 100%;
              top: -30rpx;
              left: 22rpx;
                &.on{
                  background: $base-color;
                }
                image{
                  width: 50rpx;
                  height: 50rpx;
                }
                text{
                  font-size: 20rpx;
                  color: #fff;
                  display: inline-block;
                  padding: 0 9rpx;
                  box-sizing: border-box;
                  position: absolute;
                  right: 3rpx;
                  top: -3rpx;
                  height: 30rpx;
                  line-height: 30rpx;
                  border-radius: 30rpx;
                  background: #f00;
                }
            }
            .cart-money{
              color: #fff;
              font-size: 30rpx;
              margin-left: 50rpx;
            }
        }
        .order-btn{
          width: 280rpx;
          height: 100%;
          background: $base-color;
          font-size: 28rpx;
          color: #fff;
          display: flex;
          align-items: center;
          justify-content: center;
        }
    }
}

4.ps
小程序使用mpx为框架;
商品列表数据根据接口获取,测试数据可以根据mock数据测试
实际数据类型是

goodLists: [
    {
        id: 'xx',   
        name: 'xx', // 商品类型
        list: [ // 当前商品类型对应的所有商品
            {
                id: 'xx',
                title: 'xx',
                pic: 'xx',
                price: 'xx',
                detail: 'xx',
                num: ''  // num是为了我方便对商品加减操作,让后端加的
            }
        ]
    }
]
阅读 711

推荐阅读