小程序-仿小米有品商城

1,542 阅读6分钟

前言

作为一个刚入门学前端的小白我来说,写这个小程序花了很多时间,遇到了很多问题,也学到了很多,非常感谢那些帮我解决问题的老师和同学。这个小程序主要实现的是购物车功能,有些许功能未得以实现,比如搜索功能,由于小米有品没有开源的api,搜索的很多数据需要自己来写,觉得这是一项巨大的工程,然后你懂得(就没写搜索功能了)。项目地址。希望能给初学者一些帮助。

开发前的准备

项目部分截图(gif)

项目主要页面及功能实现

首页

  • 商品详情页功能实现

1.分享功能 利用button组件,把catchtap="shares"来点击分享实现分享给微信好友的弹框(同时出现遮罩层),在此之前要先给包容弹框的盒子设置一个遮罩层,点击页面的任何一个地方遮罩层消失。

<view class="share-mask {{active ? 'active' : ''}}" bindtap="hideMask">
  <view class="shares">  
      <view class="share">分享</view>
      <view class="share_content">
        <view class="share_left">
          <button catchtap="shares" class="shares-btn" open-type="share" hover-class="none">
            <image class="shareImg" src="../../../images/wechat.png"/>
            <text>发送给朋友</text>
          </button>
        </view>    
        <view class="share_right">
          <image class="shareImg" src="../../../images/poster.png"/>
          <text>生成图库</text>
        </view>
      </view>
      <view class="cancel">取消</view>
  </view>
</view>

 // 分享
  toShare() {
    this.setData({
      active: true
    })
  },
  // 隐藏遮罩层
  hideMask() {
    this.setData({
      active: false
    })
  },
  // 分享给微信好友
  onShareAppMessage (options) {
    return {
      title:this.data.detail[0].detailOne
    }
  }

分类页面

request请求并获取数据,点击商品分类跳转相关商品集合

  <view class="category-left">
        <view wx:for="{{category}}" wx:key="{{item.id}}"
         data-id="{{item.id}}" data-index="{{index}}"
         class="cate-list {{ curIndex === index ? 'on' : '' }}" bindtap="switchTab" >{{item.name}}</view>
    </view>
    <scroll-view class="categroy-right" scroll-y scroll-into-view="{{toView}}" scroll-with-animation="{{true}}">
            <view class="cate-box" id="{{detailList.id}}">
                <view class="cate-banner">
                    <image src="{{detailList.banner}}"></image>
                </view>
                <view class="cate-title">
                    <text>{{detailList.cate}}</text>
                </view>
                <view class="product">
                    <view class="product-list" wx:for="{{detailList.detail}}" wx:key="index" wx:for-item="product">
                        <navigator url="#">
                            <image src="{{product.thumb}}" />
                            <view class="classname">
                                <text>{{product.name}}</text>
                            </view>
                        </navigator>
                    </view>
                </view>
            </view>
    </scroll-view>
Page({
  data: {
    category:[],
      detail: [],
    detailList: {},
    curIndex:0,
    toView:"youpin",
  },
  switchTab(e){
    let currentId = e.currentTarget.dataset.id
    let currentIndex = e.currentTarget.dataset.index
    this.setData({
      detailList: this.data.detail[currentIndex],
      curIndex: currentIndex
    })
  }

购物车页面

此页面简单的实现了点击首页的商品,购物车会显示相关加入的商品,以及加减、全选、取消全选(点击任意一个商品,全选消失)的功能。

 selectAll() {
    let selectAllStatus = this.data.selectAllStatus
    selectAllStatus = !selectAllStatus
    let carts = this.data.carts
    for (let i = 0; i < carts.length; i++) {
      carts[i].selected = selectAllStatus
    }
    this.setData({
      selectAllStatus,
      carts
    })
    this.getTotalPrice()
  },
  selectList (e) {
    console.log(e.currentTarget.dataset.index)
    let oIndex = e.currentTarget.dataset.index
    let carts = this.data.carts
    let test = carts[oIndex].selected
    test = !test
    let cartSelect = `carts[${oIndex}].selected`
    this.setData({
      [cartSelect]: test
    })
    if (this.data.carts.find(function(e) {return e.selected === false})) {
      this.setData({
        selectAllStatus: false
      })
    } else {
      this.setData({
        selectAllStatus: true
      })
    }
    this.getTotalPrice()
  },
  getTotalPrice() {
    let carts = this.data.carts
    let total = 0
    let totalNum = 0
    for (let i = 0; i < carts.length; i++) {
      if (carts[i].selected) {
        total += carts[i].num * carts[i].price
        totalNum += carts[i].num
      }
    }
    this.setData({
      totalPrice: total.toFixed(2),
      totalNum
    })
  },
  minusCount(e) {
    // console.log(e)
    const index = e.target.id
    let carts = this.data.carts
    // console.log(index,carts)
    let num = carts[index].num
    if (num <= 1) {
      return
    }
    num = num - 1
    carts[index].num = num
    this.setData({
      carts: carts
    })
    this.getTotalPrice()
  },
  addCount(e) {
    const index = e.target.id
    let carts = this.data.carts
    let num = carts[index].num
    num = num + 1
    carts[index].num = num
    this.setData({
      carts: carts
    })
    this.getTotalPrice()
  },

个人页面

该页面没怎么写功能,大部分都是静态的,就不过多介绍了。

页面传值

使用全局变量解决页面的传值问题

主要是点击商品详情页(goods.js)的加入购物车,购物车页面(cart.js)会显示被加入的商品,通过把数据存储在全局变量里来传值。

1.首先在app.js全局变量里定义一个carts[]数组 globalData: { cart:[] }

2.其次在goods.js页面和cart.js页面头部定义const app = getApp(); 在goods.js的addCart()方法里app.globalData.cart = [...app.globalData.cart, this.data.detail] 用es6的解构方法,把从easy-mock里获取到的detail[]传到全局变量的cart[]里。这种写法与push()方法不同在于push()方法会修改数组里的值,而不是创建一个新的数组。

3.最后在cart.js的onShow生命周期里设置购物车的数据源,即获取goods.js在app.js里的cart[]

this.setData({
        hasGoods: true,
        carts: app.globalData.cart
      })

点击首页的商品列表跳到相应的商品详情页

首页跳转到商品详情页时,通过每个商品的id来跳转

// pages/index/index.js
// 去到商品详情页
  toLists:function(e) {
    // console.log(e)
    let id = e.currentTarget.dataset.id
    wx.navigateTo({
      url: `/pages/goods/goods/goods?id=${id}`
    })
  }

在商品详情页把商品的id赋值给商品的index来设置数据源

// pages/goods/goods.js
 onLoad: function (options) {
    const id = options.id
    this.setData({
      index: id
    })
  }

使用路径传参

如:/pages/goods/goods/goods?id=${id} ,这里应注意下,如果参数要传的值是一条对象的话,我们都知道小程序里是不支持传对象的,此时把值传过去的时候应该用JSON.stringify()把对象转为字符串,拿到值的时候应该用JSON.parse()把字符串转为对象。

页面其它功能

商品结算时的合计

onShow () {
    let self = this
    wx.request({
      url:'https://www.easy-mock.com/mock/5d077b333780f05f8385d2c1/xiaoMi_youPin/goods_detail',
      success(res){
        console.log(res)
        self.setData({
          goods:res.data.data.goods
        })
        self.setData({
          sum: self.data.goods[0].goodsPrice + self.data.goods[0].carriage - self.data.goods[0].discounts
        })
      }
    })
  }

这里的合计是写在onShow声明周期里的,我想说的是应该注意计算合计与请求接口数据会产生异步问题, 应该把计算合计另外放在self.setData({})里,并放在请求接口数据的self.setData({})下面,因为请求接口数据的时间大于计算合计的时间,必须要拿到数据后才能计算合计。这两个不能放在同一个self.setData({})里,否则得不到合计的值,如下代码是不正确的

self.setData({
          goods:res.data.data.goods,
          sum: self.data.goods[0].goodsPrice + self.data.goods[0].carriage - self.data.goods[0].discounts
        })

收获地址管理

用户收获地址管理实现了新增地址和管理地址功能,在utils文件夹下引入了weui.wxss和自定义了身份城市区县的数据area.js。

//pages/goods/addressList/addressList.js
Page({
  data: {
    addressList:[]
  },
  onLoad: function (options) {
    var arr = wx.getStorageSync('addressList') || [];
    console.info("缓存数据:" + arr);
    // 更新数据  
    this.setData({
      addressList: arr
    });
  },
  onShow: function () {
    this.onLoad();
  },
  addAddress:function(){
    wx.navigateTo({ 
      url: '../../goods/address/address' 
    });
  },
  /* 删除item */
  delAddress: function (e) {
    this.data.addressList.splice(e.target.id.substring(3), 1);
    // 更新data数据对象  
    if (this.data.addressList.length > 0) {
      this.setData({
        addressList: this.data.addressList
      })
      wx.setStorageSync('addressList', this.data.addressList);
    } else {
      this.setData({
        addressList: this.data.addressList
      })
      wx.setStorageSync('addressList', []);
    }
  }
})
//pages/goods/address/address.js
var area = require('../../../utils/area.js');
var areaInfo = []; //所有省市区县数据
var provinces = []; //省
var provinceNames = []; //省名称
var citys = []; //城市
var cityNames = []; //城市名称
var countys = []; //区县
var countyNames = []; //区县名称
var value = [0, 0, 0]; //数据位置下标
var addressList = null;
Page({
  data: {
    transportValues: ["收货时间不限", "周六日/节假日收货", "周一至周五收货"],
    transportIndex: 0,
    provinceIndex: 0, //省份
    cityIndex: 0, //城市
    countyIndex: 0, //区县
    provinceNames: ["江西省","北京省"]
  },
  onShow: function() {
    var that = this;
    area.getAreaInfo(function(arr) {
      areaInfo = arr;
      //获取省份数据
      that.getProvinceData();
    });
  },
  // 获取省份数据
  getProvinceData: function() {
    var that = this;
    var s;
    provinces = [];
    provinceNames = [];
    var num = 0;
    for (var i = 0; i < areaInfo.length; i++) {
      s = areaInfo[i];
      if (s.di == "00" && s.xian == "00") {
        provinces[num] = s;
        provinceNames[num] = s.name;
        num++;
      }
    }
    that.setData({
      provinceNames: provinceNames
    })

    that.getCityArr();
    that.getCountyInfo();
  },

  // 获取城市数据
  getCityArr: function(count = 0) {
    var c;
    citys = [];
    cityNames = [];
    var num = 0;
    for (var i = 0; i < areaInfo.length; i++) {
      c = areaInfo[i];
      if (c.xian == "00" && c.sheng == provinces[count].sheng && c.di != "00") {
        citys[num] = c;
        cityNames[num] = c.name;
        num++;
      }
    }
    if (citys.length == 0) {
      citys[0] = {
        name: ''
      };
      cityNames[0] = {
        name: ''
      };
    }
    var that = this;
    that.setData({
      citys: citys,
      cityNames: cityNames
    })
    console.log('cityNames:' + cityNames);
    that.getCountyInfo(count, 0);
  },

  // 获取区县数据
  getCountyInfo: function(column0 = 0, column1 = 0) {
    var c;
    countys = [];
    countyNames = [];
    var num = 0;
    for (var i = 0; i < areaInfo.length; i++) {
      c = areaInfo[i];
      if (c.xian != "00" && c.sheng == provinces[column0].sheng && c.di == citys[column1].di) {
        countys[num] = c;
        countyNames[num] = c.name;
        num++;
      }
    }
    if (countys.length == 0) {
      countys[0] = {
        name: ''
      };
      countyNames[0] = {
        name: ''
      };
    }
    console.log('countyNames:' + countyNames);
    var that = this;
    that.setData({
      countys: countys,
      countyNames: countyNames
    })
  },

  bindTransportDayChange: function(e) {
    console.log('picker country 发生选择改变,携带值为', e.detail.value);
    this.setData({
      transportIndex: e.detail.value
    })
  },

  bindProvinceNameChange: function(e) {
    var that = this;
    console.log('picker province 发生选择改变,携带值为', e.detail.value);
    var val = e.detail.value
    that.getCityArr(val); //获取地级市数据
    that.getCountyInfo(val, 0); //获取区县数据

    value = [val, 0, 0];
    this.setData({
      provinceIndex: e.detail.value,
      cityIndex: 0,
      countyIndex: 0,
      value: value
    })
  },

  bindCityNameChange: function(e) {
    var that = this;
    console.log('picker city 发生选择改变,携带值为', e.detail.value);
    var val = e.detail.value
    that.getCountyInfo(value[0], val); //获取区县数据
    value = [value[0], val, 0];
    this.setData({
      cityIndex: e.detail.value,
      countyIndex: 0,
      value: value
    })
  },

  bindCountyNameChange: function(e) {
    var that = this;
    console.log('picker county 发生选择改变,携带值为', e.detail.value);
    this.setData({
      countyIndex: e.detail.value
    })
  },

  saveAddress: function(e) {
    var consignee = e.detail.value.consignee;
    var mobile = e.detail.value.mobile;
    var transportDay = e.detail.value.transportDay;
    var provinceName = e.detail.value.provinceName;
    var cityName = e.detail.value.cityName;
    var countyName = e.detail.value.countyName;
    var address = e.detail.value.address;
    console.log(transportDay + "," + provinceName + "," + cityName + "," + countyName + "," + address); //输出该文本 
    var arr = wx.getStorageSync('addressList') || [];
    console.log("arr,{}", arr);
    addressList = {
      consignee: consignee,
      mobile: mobile,
      address: provinceName + cityName + countyName+address,
      transportDay: transportDay
    }
      arr.push(addressList);
    wx.setStorageSync('addressList', arr);
    wx.navigateBack({ })
  }
})

结语

这个小程序作为我学前端以来写的第一个项目,写的过程是漫长的,同时也是痛并快乐着的。希望能给那些初写小程序商城的同学们一些帮助,毕竟我刚开始写这个小程序的时候是迷茫的,不知如何下手。觉得写得还ok的话,可不可以动动你们的小拇指给我点个赞呢!若有哪写得不足得地方可以在下面的评论区附上你们的建议。最后再加一句,我是2020届的毕业生,即将面临实习的压力,有哪位大佬可以稍微关照下嘛。