初学微信小程序实战-简易小米商城

前言

做为一名前端初学者,前段时间学习了关于微信小程序的内容,以为颇有意思,忍不住本身动手撸了一个小米商城的微信小程序。固然,是不完整的,由于如出一辙实太大了(┬_┬),只是简单完成了基本功能和几个分类商品页面。因为找不到小米的数据接口,因此这个小程序里全部的数据都是我本身写的,在EasyMock里。所以有些图片和数据看起来会有些不协调,这也是我第一次作一个比较完整的项目,不足之处但愿你们多多包涵。写下这篇文章既是对本身这段时间学习微信小程序的总结和回顾,也但愿对看到这篇文章的微信小程序初学者有所帮助。css

工具

  • 微信开发者工具
  • Easy Mock(我本身写的数据,有接口的能够直接使用接口)
  • 微信小程序官方文档
  • 阿里巴巴矢量库(图标)
  • 官方小米商城微信小程序(模仿)

小程序部分功能展现

基本结构

  • components 自定义组件
  • images 小程序图标和图片
  • pages 页面
  • 配置文件

tabbar

tabbar的配置须要在微信小程序的根文件app.json中配置(图片名字请忽略(┬_┬))html

app.json前端

"tabBar": {
    "selectedColor": "#ff6700",
    "color": "#666666",
    "borderStyle": "black",
    "list": [
      {
        "pagePath": "pages/index/index",
        "text": "首页",
        "iconPath": "./images/system-icon/首页.png",
        "selectedIconPath": "./images/system-icon/首页1.png"
      },
      {
        "pagePath": "pages/classify/classify",
        "text": "分类",
        "iconPath": "./images/system-icon/分类.png",
        "selectedIconPath": "./images/system-icon/分类1.png"
      },
      {
        "pagePath": "pages/discover/discover",
        "text": "发现",
        "iconPath": "./images/system-icon/发现.png",
        "selectedIconPath": "./images/system-icon/发现1.png"
      },
      {
        "pagePath": "pages/cart/cart",
        "text": "购物车",
        "iconPath": "./images/system-icon/购物车.png",
        "selectedIconPath": "./images/system-icon/购物车1.png"
      },
      {
        "pagePath": "pages/user/user",
        "text": "个人",
        "iconPath": "./images/system-icon/个人.png",
        "selectedIconPath": "./images/system-icon/个人1.png"
      }
    ]
  },
复制代码

头部导航栏

由于官方小米商城小程序并无自定义导航栏,并且如今支持单页设置,因此我也只是在每一个页面单独设置了名字和基本样式,在每一个页面的app.json里直接添加下面几个定义导航栏的属性git

效果程序员

app.json

{
  "usingComponents": {},
  "navigationBarBackgroundColor": "#f8f8f8",
  "navigationBarTextStyle": "black",
  "navigationBarTitleText": "手机频道",
  "backgroundColor": "#eeeeee",
  "backgroundTextStyle": "light"
}
复制代码

首页

搜索商品

由于数据都是本身写的,因此搜索也没有api能够用了(┬_┬),只能本身写了,在网上找了很久应该怎么写搜索,最后仍是问了大佬同窗才有了个思路,将全部商品数据都放在一个数组里,经过匹配关键词筛选出匹配的商品。github

由于数据都写在EasyMock里,经过搜索页面input输入框bindkeyInput函数实时获取到输入的关键词(e.detail.value),经过模板字符串动态传入关键词到Easy Mock里传回匹配的数据。json

html小程序

<input class="input" maxlength="10" bindinput="bindKeyInput" placeholder="输入关键词" />
复制代码

js微信小程序

//获取输入框输入的关键词匹配搜索数据
  bindKeyInput: function (e) {
    this.setData({
      inputValue: e.detail.value
    })
    wx.request({
      url: `https://www.easy-mock.com/mock/5d030bd22c61271f2b41de46/search?goodsname=${e.detail.value}`,
      success: (res) => {
        this.setData({
          searchgoods:res.data.data
        })
      }
    })
  },
复制代码

Easy Mock _data=[] 存放的是全部能够搜索到的商品数据,太多我就不放了,经过正则匹配关键词返回符合条件的数组data,没有匹配项返回空数组并在下面使用for循环换渲染出来api

Easy Mock搜索接口

{
  "success": true,
  "data": function({
    _req,
    Mock
  }) {
    let i = 0,
    _data=[],
     goodsname = _req.query.goodsname;
    if (goodsname != '') {
      let data = []
      for (let j = 0; j < _data.length; j++) {
        if (eval('/' + goodsname + '/').test(_data[j].goodsname)) {
          data.push(_data[j])
        }
        if (data.length > 8) break;
      }
      return data
    } else {
      return []
    }
  },
  "msg": "数据请求成功"
}
复制代码

由于_data数组里放了全部商品的所有数据,所以在点击下面搜索项时我写了个点击函数toDetail将经过index拿到的具体商品数据存入了全局app.js里的goodsdetail数组里,而后在商品详情页拿到这个数组里的数据来渲染详情页

//将商品详情存入全局变量goodsdetail
toDetail:function(e){
    var index = e.currentTarget.dataset.index;
    app.goodsdetail = this.data.searchgoods[index];

    wx.navigateTo({
      url: '/pages/goods/goods',
    })
  },
复制代码

搜索基本就写了这么多,如今写的时候回头看其实还有历史记录啥的都没写o(︶︿︶)o ,仍是有不少能够修改,但在这里俺就不说这些没用的了。

swiper 展现热门商品

通常首页的轮播图都是用来展现最近的热卖商品,也是一个电商平台必不可少的东西,能让用户第一时间了解新产品和热销商品。我在轮播图中加了点击事件跳转详情页,方法跟上面同样,直接上代码:

html

<swiper indicator-dots="true" autoplay="true" interval="{{interval}}" duration="{{duration}}" circular="true" bindchange="swiperChange">
      <block wx:for="{{imgUrls}}" wx:key="{{index}}">
        <swiper-item>
          <image src="{{item.url}}" class="slide-image" width="100%" bindtap="toDetail" />
        </swiper-item>
      </block>
    </swiper>
复制代码

js

//轮播图的切换事件
  swiperChange: function (e) {
    // console.log(e.detail.current)
    this.setData({
      swiperCurrent: e.detail.current
    })
  },
  //轮播图点击事件
  toDetail:function(e){
    var index = this.data.swiperCurrent;
    let detail = this.data.imgUrls[index];
    app.goodsdetail = detail;
    this.setData({
      detail: detail
    })
    wx.navigateTo({
      url: '/pages/goods/goods',
    })
  },
复制代码

分类

这里展现的是平台上全部商品的大分类,经过点击能够进入相应的分类页面,都是些结构和样式,关键是经过浮动布局给挤上去 float:left。

html

<view class='goods-classify'>
    <view class='classify-list' wx:for="{{classlist}}" wx:key="{{index}}">
      <navigator url='{{item.url}}' hover-class='other-navigator-hover'>
        <image src='{{item.imgsrc}}'></image>
        <view class='classify-text'>
          <text>{{item.text}}</text>
        </view>
      </navigator>
    </view>
  </view>
复制代码

商品列表

由于这里是我写的第一段商品列表,以前也没写过相似须要渲染多组数据的,在这里犯了很大错误,商品列表的每个不一样样式的商品的结构和样式都是直接撸。一开始为了写商品列表左右两边的样式,强行在数据里把两组商品数据放进了商品数组每一项中,虽然最后仍是实现了功能,可是极其麻烦(由于两边要分别写html,css和点击事件的js,数据结构根本不成样子),原本想放代码,看了看实在太多,并且很丑陋及繁杂,这里就不放了。如今看还不如直接分两边盒子写出商品列表错开的效果,看起来更酷炫,但愿刚开始学习微信小程序的朋友引觉得戒(不过应该也没有人跟我同样犯这种低级错误吧(┬_┬));

以后的组件化商品列表

以前的错误让我懊恼了很久,也意识到这样写的效率过低,由于是电商平台,不论是分类仍是推荐,大多数都是商品列表,只不过可能不一样的分类风格样式不同。因而我就想把商品列表写成一个组件,只要以后须要用到商品列表的时候,就能够把不一样样式的商品列表组件放上去从新渲染数据就行。

商品列表组件 html

<view class='goodsList' wx:for='{{goodsList}}' wx:key='{{index}}'>
  <view class='bgcline'></view>
  <view class=" {{item.big ? 'big' : 'goods-Box'}}" data-index='{{index}}' bindtap='toDetail' style=''>
    <image src="{{item.url}}" />
    <view class="goodsText">
    <view class='box-left'>
      <text class="title">{{item.goodsname}}</text>
      <text class="disc">{{item.goodsspecial}}</text>
    </view>
    <view class='box-right'>
      <text class="price">¥{{item.goodsprice}}</text>
      <view class='lijibuy'>
        <view class='buy-box'>
          <view class='text-box'>
            <text>{{item.buy}}</text>
          </view>
        </view>
      </view>
      </view>
    </view>
  </view>
</view>
复制代码

以前写重点推荐商品时,每一个大图都要从新写样式和结构太麻烦了。在这个组件里我添加了一个判断条件,判断在每条数据中是否有big属性,若是为true,就添加一个类名,使用大图的样式,若是没有,则使用另外一套小图的样式。

js

const app = getApp()
Component({
  properties: {
    goodsList:{
      type:Object,
      value:[]
    },
    detail:{
      type: Object,
      value: []
    }
  },
  data: {
  },
  methods: {
    toDetail: function (e) {
      const goodslist = this.data.goodslist
      var index = e.currentTarget.dataset.index;
      var detail = this.properties.goodsList[index];
      app.goodsdetail = detail;
      this.setData({
        detail
      })
      wx.navigateTo({
        url: '/pages/goods/goods',
      })
    },
  }
})
复制代码

分类

分类分为左右两个scroll-view。经过点击获取当前id和index,改变toView和curIndex的值来实现两边的互动,右边经过计算滚动的高度来计算大概的区域计算curIndex进行定位。

html

<scroll-view class='classify-left'>
    <view wx:for="{{classify}}" wx:key="{{classify.id}}" data-id='{{item.id}}' data-index='{{index}}' bindtap='scrollleft' class="classify-list {{curIndex == index ?'on':'' }}">
      {{item.name}}
    </view>
  </scroll-view>
  <scroll-view class='classify-right' scroll-y scroll-into-view="{{toView}}" scroll-with-animation="{{scrollanimation}}" bindscroll="scrollright">
    <block wx:for="{{clsgoods}}" wx:key="{{item.title}}">
      <view class='classify-box' id='{{item.id}}'  data-index1="{{index}}" bindtap="getindex" id="{{item.id}}">
        <view class="classify-title">
          <text>{{item.title}}</text>
        </view>
          <view class='product-list' wx:for="{{item.detail}}" wx:for-item="product" wx:key="{{index}}" bindtap='toDetail' data-index2="{{index}}" data-id='{{item.id}}'>
              <image src='{{product.url}}'></image>
              <view class='product-name'>
                <text>{{product.goodsname}}</text>
              </view>
          </view>
      </view>
    </block>
  </scroll-view>
复制代码

js

//左边scroll-view的bindscroll方法
scrollleft(e) {
    this.setData({
      toView: e.target.dataset.id,
      curIndex: e.target.dataset.index
    })
  },
//右边scroll-view的bindscroll方法
  scrollright(e) {
    let index = Math.floor(e.detail.scrollTop / 500);
    this.setData({
      curIndex: index
    })
  },
复制代码

点击进入详情页时仍是以前的方法,经过id和index定位当前商品,将数据传递给全局变量goodsdetail,以后在商品详情页获取goodsdetail数据渲染

商品详情页

做为一个电商小程序最重要的固然是商品,可是每一个商品的详情页不可能每一个都写,这时候就要利用数据来渲染,上面已经说过,每次点击商品时,会将当前点击的商品数据传入全局变量goodsdetail中,而后在打开详情页时拿到它渲染页面

商品数据

展现商品是最显眼的就是图片和视频,因此这里放的是一个swiper,可是我每条数据里都只放了一张图片地址,因此只有一张图片轮播,有多的图片只要放在数据源的swiper里就行。

地理位置

这里选择地理位置采用的是微信的一个api 点击能够进入一个页面选择地址,而且能够搜索地址

js

// 选择位置
  selectLocation: function(e) { //自行定义tap事件
    var that = this
    wx.chooseLocation({ //微信API--打开地图选择位置。
      success: function(res) { //成功以后,返回四组参数
        console.log(res.address)  
        that.setData({
          address: res.address     // 我这里只返回了详细地址
        })
        wx.setStorageSync('address', res.address)
      },
      fail: function(error) {
      },
      complete: function(e) {
      }
    })
  },
复制代码

商品参数和说明弹出框

我在数据里并无写每一个商品的具体参数和评价,由于实在太多了(┬_┬),因此这里我放的只是写死的假数据,固然,若是有数据只要拿到渲染出来就行。

说明弹出框

html和css代码太多了我就不放进来了,我会在文章最后放上个人github连接源码,有兴趣的朋友能够去看看,我在这里就说下原理

仍是经过三目运算符判断是否显示遮罩层,经过点击改变变量状态来修改类名,同时会有不一样的样式出现,实现弹出框的弹出,点击再次改变,实现弹出框消失。

//打开说明
  showexplain: function () {
    this.setData({
      isshowTrue: true
    })
  },
  //关闭说明
  hideshow: function () {
    this.setData({
      isshowTrue: false
    })
  },
复制代码

加入购物车

加购物车是每一个电商类都须要的,结构的关键就是要使用相对于浏览器窗口进行绝对定位和弹性布局将购物车那一栏定位在页面底部

html

<view class="buy">
    <view class="image">
      <image src="../../images/system-icon/goodscarton.png" />
    </view>
    <text class="order" bindtap="addcart">
            加入购物车
        </text>
    <text class="buy1">
            当即购买
        </text>
  </view>
复制代码

css

.buy{
    position: fixed;
    bottom: 0;
    width: 100%;   
    background: #FFF;
    border-top: 0.5rpx solid rgb(201, 199, 199);
    box-sizing: border-box
    
}
.buy .image image{
    width: 100rpx;
    height: 100rpx;
    float: left;
    flex: 1
}
.buy .order,.buy1{
    display: inline-block;
    flex: 1;
    text-align: center;
    line-height: 100%;
    color: #fff
      
}
.buy .order{
    background: rgb(245, 63, 18);
}
.buy .buy1{
    background: rgb(221, 73, 54);
}
复制代码

加入购物车我是经过缓存来实现的,点击加入购物车,给出showtoast提示。将当前商品详情页面的数据push进一个缓存数组中,在购物车页面拿到这个缓存的购物车数据渲染。固然,加入购物车后再次点进来须要加一个判断是否已经加入购物车。直接上代码:

js

//加入购物车,将数据放入缓存
addcart: function() {
    this.setData({
      cartmsg: this.data.goodsdetail
    })
    const nowgoodsname = this.data.goodsdetail.goodsname
    const addcartsuccess = this.data.addcartsuccess
    const cartMsg = app.globalData.cartMsg
    if (addcartsuccess == true) {
      wx.showToast({
        title: '已经加入购物车!',
        icon: 'none',
        duration: 2000
      })
    } else {
      app.globalData.cartMsg.push(this.data.cartmsg);
      wx.showToast({
        title: '成功加入购物车',
        icon: 'success',
        duration: 2000
      })
      this.setData({
        addcartsuccess: true
      })
    }
  },
  //每次进入商品详情页在生命周期函数onload中经过商品名称判断是否已经在购物车中
  onLoad: function(options) {
    this.setData({
      goodsdetail: app.goodsdetail
    })
    const nowgoodsname = this.data.goodsdetail.goodsname
    const addcartsuccess = this.data.addcartsuccess
    const cartMsg = app.globalData.cartMsg
    for (let i = 0; i < cartMsg.length; i++) {
      if (cartMsg[i].goodsname == nowgoodsname) {
        this.setData({
          addcartsuccess: true
        })
      }
    }
    const address = wx.getStorageSync('address')
    this.setData({
      address
    })
  },
复制代码

购物车

对于用户来讲,最重要的固然是购物车。须要实现的功能就是展现用户已经加入购物车的商品,并对商品的参数进行修改以及价格的计算。

购物车是否为空

进入购物车首先须要判断你的购物车里是否有商品,若是购物车为空时,显示一套购物车为空时的结构和样式,若是不为空则将缓存中购物车的数据拿到并渲染。

html

<scroll-view class='main' scroll-y wx:if="{{flag}}">
  <view class='orderlist' wx:for="{{cartlist}}" wx:key="{{item}}">
    <icon wx:if="{{item.selected}}" type="success" color="red" data-index="{{index}}" class="cart-pro-select" bindtap="selectList" />
    <icon wx:else type="circle" class="cart-pro-select" data-index="{{index}}" bindtap="selectList" />
    <navigator class="cart-pro-goods" >
      <image class="cart-thumb" src="{{item.url}}"></image>
    </navigator>
    <text class="cart-pro-name">{{item.goodsname}}</text>
    <text class="cart-pro-price">¥{{item.goodsprice}}</text>
    <view class="cart-count-box">
      <text class="cart-count-down" bindtap="minusCount" data-index="{{index}}">-</text>
      <text class="cart-count-num">{{item.number}}</text>
      <text class="cart-count-add" bindtap="addCount" data-index="{{index}}">+</text>
    </view>
    <text class="cart-del" bindtap="deleteList" data-index="{{index}}">删除</text>
    <view class="cart-footer">
      <icon wx:if="{{selectAllStatus}}" type="success_circle" class="total-select" color="red" bindtap="selectAll" />
      <icon wx:else type="circle" class="total-select" color="#010" bindtap="selectAll" />
      <text class="selAl">全选</text>
      <text class="cart-total-price">合计:</text>
      <text class="pricCount">{{totalPrice}}元</text>
      <text class="pay">结算{{totalPrice}}元</text>
    </view>
  </view>
</scroll-view>
<view class='kongcart' wx:else>
  <view class='kongcart'>
    <view class='cart-circle'>
      <view class='carticon'>
        <image src='{{carticon}}'></image>
      </view>
    </view>
  </view>
  <view class='kongtip'>
    <view class='tiptext'>
      <text>{{kongtip}}</text>
    </view>
  </view>
  <view class='tomilite'>
    <navigator url='/pages/index/index' open-type='switchTab' hover-class='other-navigator-hover'>
      <view class='tolitebutton'>
        <view class='textbutton'>
          <text>{{tolitebutton}}</text>
        </view>
      </view>
    </navigator>
  </view>
</view>
复制代码

js

// 商品数量作减法时
minusCount: function (e) {
    let cartlist = this.data.cartlist;
    const index = e.target.dataset.index;
    let num = this.data.cartlist[index].number;
    num -= 1;
    cartlist[index].number = num;
    if (num == 0) {
      cartlist.splice(index, 1)
    }
    if (this.data.cartlist.length == 0) {
      this.setData({
        flag: false
      })
    }
    this.setData({
      cartlist
    })
    this.getTotalPrice()
  },
  // 商品数量作加法时
  addCount: function (e) {
    let cartlist = this.data.cartlist;
    const index = e.target.dataset.index;
    let num = this.data.cartlist[index].number;
    num += 1;
    cartlist[index].number = num;
    this.setData({
      cartlist
    })
    this.getTotalPrice()
  },
  //点击是否选中全部商品或取消全部选中并计算价格
  selectAll: function (e) {
    let selectAllStatus = this.data.selectAllStatus;
    selectAllStatus = !selectAllStatus;
    let cartlist = this.data.cartlist;
    for (let i = 0; i < cartlist.length; i++) {
      cartlist[i].selected = selectAllStatus;
    }
    this.setData({
      cartlist,
      selectAllStatus,
    })
    this.getTotalPrice()
  },
  //计算当前选中商品的总价格
  getTotalPrice: function (e) {
    let cartlist = this.data.cartlist;
    let total = 0;
    for (let i = 0; i < cartlist.length; i++) {
      if (cartlist[i].selected) {
        total += (+cartlist[i].goodsprice * +cartlist[i].number);
      }
    }
    this.setData({
      totalPrice: total
    });
  },
  //计算选中商品的价格
  selectList: function (e) {
    const index = e.currentTarget.dataset.index;
    let cartlist = this.data.cartlist;
    const selected = cartlist[index].selected;
    cartlist[index].selected = !selected;
    const a = [];
    for (let i = 0; i < cartlist.length; i++) {
      if (cartlist[i].selected) {
        a.push(cartlist[index])
      }
    }
    if (cartlist.length <= a.length) {
      this.setData({
        selectAllStatus: true, cartlist
      });
    } else {
      this.setData({
        selectAllStatus: false, cartlist
      });
    }
    this.getTotalPrice()
  },
  //删除商品,当数量为0时也删除
  deleteList: function (e) {
    const index = e.target.dataset.index;
    let cartlist = this.data.cartlist;
    cartlist.splice(index, 1)
    if (this.data.cartlist.length == 0) {
      this.setData({
        flag: false
      })
    }
    this.setData({
      cartlist
    })

  },
  //由于数据中没有数量这个属性,每次显示时给新加入没有数量属性的商品添加数量=1属性
  fff() {
    let cartlist = app.globalData.cartMsg
    for(let i =0;i<cartlist.length;i++){
      if (!cartlist[i].number) {
        cartlist.map(e => {
          return e['number'] = 1
        })
      }
    }
    if (cartlist.length > 0) {
      this.setData({
        flag: true,
        cartlist
      })
    }
  },
  //生命周期函数  第一次进入购物车页面时获取缓存中购物车数据
  onLoad: function (options) {
    if (wx.getStorageSync('cartMsg')){
      app.globalData.cartMsg = wx.getStorageSync('cartMsg')
      this.setData({
        cartlist: app.globalData.cartMsg
      })
    }
  },
  //生命周期函数,每次显示购物车页面时获取缓存中购物车数据刷新
  onShow: function () {
    if (app.globalData.cartMsg.length > 0) {
      const cartMsg = app.globalData.cartMsg
      wx.setStorageSync('cartMsg', cartMsg)
    }else{
    }
    this.fff()
  },
  //当购物车页面隐藏时,存一次缓存数据
  onHide: function () {
    const cartMsg = app.globalData.cartMsg
    wx.setStorageSync('cartMsg', cartMsg)
  },

复制代码

结语

这是个人第一次项目实战,收获不少,虽然回头看看仍是有不少不足跟bug,写的过程头皮发麻,可是完成了仍是满满的成就感(若是不掉头发的话)。在此也但愿个人文章能给屏幕前的你带来帮助,做为一名萌新程序员,还有许多技术要学习,也迫切须要一个实习的机会来锻炼本身,有大佬看到但愿能给个机会(づ。◕‿‿◕。)づ,将来也但愿能在这与社区的朋友们互相帮助,共同进步!共勉!

最后奉上个人项目源码地址github项目源码

相关文章
相关标签/搜索