前段時間,隨著馬化騰現身全國多地用微信小程式乘坐公交的新聞出現,微信小程式的熱度可謂是更上了一層。微信小程式現身至今,因其不用下載就可使用的方便等優點,發展趨勢一直良好。
盒馬鮮生的問世也是充滿了熱度,實現了快速配送,可謂是阿里巴巴對線下超市完全重構的新零售業態。
兩個都這麼方便的東西碰撞到一起,會發生什麼呢?
最近正好在學習微信小程式,於是照著盒馬鮮生APP改編了一個微信小程式。
文末有GitHub原始碼地址,之後我也會不斷更新完善這個小程式。如果你也對這個微信小程式感興趣,歡迎交流,共同學習。
功能簡介
本著便利的理念,實現了商城類APP的主要功能。
- 使用者能夠根據點選不同的商品分類,跳轉頁面看到各種商品的列表。
- 點選商品可以加入購物車,在購物車中還能實現對商品的增加或者減少數量、全選反選商品、刪除購物車商品等操作。
- 首頁中,點選左上角的按鈕能夠新增預設收貨地址。
- 點選右上角的掃一掃標誌,還能掃描二維碼(下面的gif介紹是模擬器的效果,只能開啟電腦中的圖片進行掃描,真機可以開啟相機進行掃描)。
- 首頁中最頂上實現了圖片輪播及自動切換,底部實現了滾動檢視,能夠橫著滑動展示商品資訊。
我們先來看看效果圖,稍後再仔細介紹每一點的實現方式。
頁面簡介
這裡主要介紹了該小程式的主要介面資訊、展示了幾個頁面跳轉及商品列表資訊。請關注首頁上方的圖片輪播及底部的橫向滾動檢視。
關於購物車的操作
將商品加入購物車。
對購物車中商品的數量進行增加或者減少。
對購物車的商品進行全選或反選。
刪除購物車內商品。
增加預設收貨地址、二維碼掃一掃
新增收貨地址。
二維碼掃一掃(這裡是模擬機測試的,只能新增本地圖片進行掃描,真機可以開啟相機掃一掃)
接下來就是實現方式的介紹。
功能實現詳解
首頁圖片輪播和底部的滾動檢視
微信小程式自帶元件 滑塊檢視容器swiper,能夠實現滑塊檢視,每一個檢視都放在一個swiper-item中。設定引數auto:play就可以自動播放導致swiper變化。
<swiper class="page__bd__scroll" current=`{{activeIndex}}` bindchange=`swiperTab` autoplay="true" interval="2000" duration="1000">
// interval是自動切換時間間隔,duration是滑動動畫時長
// 每一個swiper-item就是一個檢視
<swiper-item>
<image class="page__scroll__item" src=""/>
</swiper-item>
<swiper-item>
<image class="page__scroll__item" src=""/>
</swiper-item>
<swiper-item>
<image class="page__scroll__item" src=""/>
</swiper-item>
<swiper-item>
<image class="page__scroll__item" src=""/>
</swiper-item>
<swiper-item>
<image class="page__scroll__item" src=""/>
</swiper-item>
</swiper>
複製程式碼
橫向滾動檢視的實現:
微信自帶元件 可滾動檢視區域scroll-view,通過設定屬性名scroll-x或者scroll-y可以實現檢視的橫向滾動或者縱向滾動。
// 屬性名scroll-x定義了該檢視允許橫向滾動
<scroll-view scroll-x class="scrollx-section__content">
// 利用迴圈從後臺獲取資料,在檢視中有多個view,也就是在頁面中能看到的多個商品展示。
<block wx:for="{{scrollXList}}" wx:key="index" wx:for-index="index">
<view class="scrollx-section__content__item">
<view class="scrollx-section__item__wrapper">
<view class="view__wrapper__image">
<image src="https://user-gold-cdn.xitu.io/2017/12/15/16057cf8f3865280" />
</view>
<view class="view__wrapper__intro">
<view class="wrapper__intro__title">
<text>{{item.name}}</text>
<text>{{item.secName}}</text>
</view>
<view class="wrapper__intro__content left">
<text>{{item.leftTitle}}</text>
<text>{{item.leftSecTitle}}</text>
</view>
<view class="wrapper__intro__content right">
<text>{{item.rightTitle}}</text>
<text>{{item.rightSecTitle}}</text>
</view>
<view class="wrapper__intro__price">
<a>¥{{item.price}}</a><a>/{{item.unit}}</a><a id="{{index}}" bindtap="addInCart">+</a>
</view>
</view>
</view>
</view>
</block>
</scroll-view>
複製程式碼
關於購物車的操作
將商品加入購物車
在不同的頁面根據分類資訊能夠跳轉到不同的頁面進入商品列表,比如能在首頁和分類頁,可以根據分類資訊進入不同的商品列表,將商品加入購物車。要想在首頁、商品分類介面、購物車多個介面都獲取到購物車列表資訊,單個頁面的資料作用域只在本資料夾,要想多個頁面共同操作同一個資料應該怎麼做呢?
對於在多個頁面進行傳值的資料,我們可以在app.js中設定一個全域性變數,再在每個頁面都引入這個全域性資料,就可以實現多個頁面共用一個資料了。
// app.js中的全域性變數
globalData: {
cardList: [],
goodsSortsChoice: null // 用來標記首頁商品分類 使用者點選了哪個分類,進而顯示不一樣的商品列表
}
複製程式碼
這裡我設定了兩個全域性變數。cardList是購物車資料,使用者點選商品加入購物車,就能將商品加入該陣列。goodsSortsChoice是一個標記。在首頁和分類介面都有不同的分類介紹,該標記能記住使用者在分類介面點選了哪一個分類,然後根據這個使用者的點選,跳轉至商品展示介面,展示不同的資訊。
在商品列表介面,為每一件商品的加入購物車選項都新增了一個點選事件addCount,同時,要判斷使用者點選了哪一件商品,就要為每一件商品加上一個index,這裡我的做法是在迴圈輸出後臺的商品列表資料時,為每個迴圈動態繫結data-index=”index”,再為每一個“+”設定一個id=”{{index}}”,進行點選判斷。
<block wx:for="{{goods}}" wx:key="index" wx:for-index="index">
<view class="weui-cells">
<view class="weui-cell">
<view class="weui-cell__hd">
<image src="https://user-gold-cdn.xitu.io/2017/12/15/16057cf8f3865280" />
</view>
<view class="weui-cell__bd">
<view class="goodsList__bd__intro">{{item.name}}</view>
<view class="view__bd__price">
<text class="price left">¥{{item.price}}/{{item.unit}}</text>
<text class="add right" bindtap="addInCart" id="{{index}}">+</text>
</view>
</view>
</view>
</view>
</block>
複製程式碼
js部分就是實現將商品加入購物車的方法addInCart。這裡將商品加入購物車前要先遍歷已有的購物車陣列進行判斷,如果商品已經在購物車中,就直接將購物車中的該商品數量加一,否則才直接將商品新增至全域性的購物車陣列。
addInCart: function(e) {
const good = this.data.goods[e.currentTarget.id]; // 根據index,判斷使用者點選了哪個商品加入購物車
const cart = app.globalData.cardList; // 獲取購物車列表
// 設定一個標記,判斷使用者想加入購物車的商品是否已經存在購物車了
var flag = false;
// some 是es6新增的方法,用於遍歷整個陣列,如果陣列中存在一個及以上元素,就返回true
flag = cart.some((item) => {
return item === good;
})
console.log(flag);
// 如果購物車中沒有該元素,就將該商品加入購物車,否則就將該商品的購買數量加一
if(!flag) {
cart.push(good); // 使用者選擇商品加入購物車後,將該商品加入購物車列表
wx.showToast({
title: `商品已加入購物車`,
icon: `success`,
duration: 2000
})
} else {
// 商品已經存在購物車,直接將購買數量加一
this.data.goods[e.currentTarget.id].count ++;
}
},
複製程式碼
wx.showToast是微信自帶的API,能夠在頁面中出現一個彈窗。
增加或減少購物車中商品的購買數量
增減商品購買數量的思想是,給加減號分別繫結兩個點選事件 addCount 和 reduceCount,並且在迴圈輸出購物車列表的商品時,為加減號新增index索引,用於判斷使用者點選了哪一件商品。
<block wx:for="{{goodsList}}" wx:key="index" data-index="index">
<view class="weui-cell">
<view class="weui-cell__hd">
<icon id="{{index}}" bindtap="selectGoods" type="{{item.type}}" color="#23a3ff"></icon>
</view>
<view class="weui-cell__bd">
<image src="https://user-gold-cdn.xitu.io/2017/12/15/16057cf8f3865280" />
</view>
<view class="weui-cell__ft right">
<text class="proIntr left">{{item.name}}</text>
<text class="price left">¥{{item.price}}/{{item.unit}}</text>
<view class="count">
<text class="reduce left" bindtap="reduceCount" id="{{index}}">-</text>
<text class="number left">{{item.count}}</text>
<text class="add left" bindtap="addCount" id="{{index}}">+</text>
</view>
</view>
</view>
</block>
複製程式碼
js部分:
// 增加商品數量
addCount:function (e) {
var that = this;
// 根據點選事件獲取使用者點選了哪一件商品
const goodId = e.currentTarget.id;
that.data.goodsList[goodId].count++;
this.setData({
goodsList: that.data.goodsList
})
// 每一次增減商品數量都要重新計算購物車總錢數
this.sumMoney();
},
// 減少商品數量
reduceCount: function(e) {
var that = this;
const goodId = e.currentTarget.id;
if(that.data.goodsList[goodId].count <= 1) {
that.data.goodsList[goodId].count = 1;
wx.showModal({
title: `數量小於1`,
content: `不允許操作`,
duration: 2000
})
} else {
that.data.goodsList[goodId].count--;
}
this.setData({
goodsList: that.data.goodsList
})
// 每一次增減商品數量都要重新計算購物車總錢數
this.sumMoney();
},
複製程式碼
購物車商品總價的計算
對於選中的商品,呼叫sumMoney()計算總價。該方法是遍歷購物車中的商品,獲得每件商品的單價和件數,進行相乘後相加。
// 計算所有商品的錢數
sumMoney: function() {
// count用於記錄每件商品的購買數量
var count = 0;
// goods是購物車中的商品,對其進行遍歷,計算價格
const goods = this.data.goodsList;
for(let i = 0; i < goods.length; i++) {
count += goods[i].count*goods[i].price;
}
this.setData({
sum: count
})
},
複製程式碼
商品的全選和反選
選中的商品和未選中的商品,在列表中展示時,最重要的一個差別是商品列前是藍色的小勾還是空心的圓點。
因此要先為購物車的商品設定一個狀態,對介面的樣式進行改變。對於這個狀態值,是在載入購物車介面前就要有該狀態,因此最先我想在後臺資料中為每個商品新增一個狀態值。但是這樣做有很大的不足之處,對於這個狀態值,只有購物車介面需要,對於其他介面來說是多餘的,給後臺多新增一個資料就意味著要更改所有後臺商品資料,增大了實現的複雜度。後面我又想到了一個方法。在購物車介面載入前,先遍歷一遍購物車,為每一件購物車新增一個屬性type=”success”(type引數設定的妙處:success 和 circle類名是微信元件icon的一個狀態值,能顯示小勾或空心圓點)。
購物車onload方法,遍歷購物車中的商品,新增狀態type:
onLoad: function (options) {
const cardList = app.globalData.cardList;
cardList.map(item => {
item.type = "success";
});
},
複製程式碼
前臺介面展示部分:
<icon id="{{index}}" bindtap="selectGoods" type="{{item.type}}" color="#23a3ff"></icon>
複製程式碼
通過這種方式,就實現了動態改變商品狀態的方法。
我們可以建立一個方法,遍歷購物車中的商品,如果全選了,就吧全選的選項勾上。
// 用來判斷是否全選
allSelected: function() {
const goods = this.data.goodsList;
// some是es6新增的方法,如果陣列中至少有一個符合條件的,就會返回true
var symbol = goods.some(good => {
return good.type === "circle"
})
// 經過symbol標記,如果購物車中有未選中的商品,全選狀態就是空心圓
if(symbol) {
this.data.allStatus = "circle"
} else {
// 如果購物車中所有商品都被選中了,全選狀態就是一個勾
this.data.allStatus = "success"
}
this.setData({
allStatus: this.data.allStatus
})
},
複製程式碼
說回全選和反選操作。全選就是頁面底部總計一欄,打上了勾為全選,首先給全選框設定一個點選事件。若當前為全選狀態,點選後變成空心原點,反之亦然。
<view class="shopping__ft">
<view class="shopping__ft__hd">
<!-- 給全選按鈕一個點選事件selOrUnsel -->
<icon bindtap="selOrUnsel" type="{{allStatus}}" color="#23a3ff"></icon>
全選
</view>
<view class="shopping__ft__bd">
<text>合計:</text>
<text>¥{{sum}}</text>
</view>
<view class="shopping__ft__ft" bindtap="sumMoney">
去結算
</view>
</view>
複製程式碼
js實現:
selOrUnsel: function() {
// 獲得全選按鈕和商品列表
const status = this.data.allStatus;
const goods = this.data.goodsList;
// 點選按鈕後檢視當前全選框的狀態,對其進行取反的改變,並且對商品進行全選或反選
if(status === "success") {
// 如果全選按鈕之前是選中的,就變成空心圓
this.data.allStatus = "circle";
// 遍歷商品列表的每一項進行設定狀態屬性未未選中
goods.map(good => {
good.type = "circle";
})
} else {
this.data.allStatus = "success";
// 如果點選之前未選中全選按鈕,就進行全選。遍歷購物車列表改變所有商品的狀態值
goods.map(good => {
good.type = "success";
})
}
// 將結果設定回頁面上進行顯示
this.setData({
goodsList: this.data.goodsList
})
this.setData({
allStatus: this.data.allStatus
})
},
複製程式碼
刪除購物車中的商品
購物車介面右上角有一個刪除,能夠刪除選定的商品。先給“刪除”新增一個點選事件delGoods。:
<view class="shopping__hd">
<view class="shopping__hd__content">
<view class="shopping__title">
購物車
<a class="shopping__title__delete right" bindtap="delGoods">刪除</a>
</view>
</view>
</view>
複製程式碼
js實現 delGoods 方法,用一個陣列存放已經選中的要刪除的商品,再遍歷要刪除的商品陣列,用 splice 方法逐個刪除。
// 刪除商品
delGoods: function() {
const goods = this.data.goodsList;
// 對購物車中所有的元素進行遍歷,找出選中的元素,組成selGoods陣列
const selGoods = goods.map(good => {
if(good.type === "success") {
return good;
}
})
wx.showModal({
title: "確定要刪除所選商品?",
success: (res) => {
// 使用者點選確定
if(res.confirm) {
// 對要刪除的元素陣列進行遍歷,逐個用splice方法進行刪除
selGoods.map(sel => {
goods.splice(sel);
})
// 刪除成功以後從新設定頁面的值
this.setData({
goodsList: this.data.goodsList
})
} else if (res.cancel) {
}
}
})
},
複製程式碼
新增預設收貨地址
在首頁的左上角有個小點,點選能夠新增預設的收貨地址。
新增預設收貨地址也需要在多個頁面進行傳值,因為顯示預設地址和設定預設地址不在同一個介面。考慮到預設收貨地址需要長期儲存在使用者的個人資訊中,這次我們用到了 storage 進行資料的儲存。
話不多說,要實現功能,繫結一個事件 chooseAddr 先。
<!-- 我現在是首頁 -->
<view class="page__hd__input-left left">
<image src="https://user-gold-cdn.xitu.io/2017/12/15/16057cf8f81b6d70" bindtap="chooseAddr" />
</view>
複製程式碼
將頁面跳轉到預設收貨地址展示介面,這裡能顯示輸入的收貨地址
chooseAddr: function() {
wx.navigateTo({
url: "../chooseAddress/chooseAddress"
})
},
複製程式碼
在顯示預設收貨地址的介面,右上角還有一個按鈕,可以新增收貨地址,那我們再跳轉一遍頁面到設定預設收貨地址介面吧。
<!-- 我是顯示預設收貨地址的介面 -->
<view class="choose-addr__hd">
<text class="choose-addr__title">選擇收貨地址</text>
<text class="choose-addr__add right" bindtap="addNewAddr">新增地址</text>
</view>
複製程式碼
// 我要跳轉到設定預設收貨地址介面啦
addNewAddr: function() {
wx.navigateTo({
url: "../newAddr/newAddr"
})
},
複製程式碼
在設定預設收貨地址介面,給每個輸入框分別設定輸入改變事件bindinput,用於獲得輸入框的內容。
<view class="newAddr__bd">
<view class="weui-cells weui-cells_form gray-input">
<view class="weui-cell">
<view class="weui-cell__hd">
<text class="weui-label mr60">收貨地址</text>
</view>
<view class="weui-cell__bd">
<input bindinput="getAddress" class="weui-input" placeholder="請輸入收貨地址" />
</view>
</view>
<view class="weui-cell">
<view class="weui-cell__hd">
<text class="weui-label mr94">門牌號</text>
</view>
<view class="weui-cell__bd">
<input bindinput="getNum" class="weui-input" placeholder="門牌號" />
</view>
</view>
</view>
<view class="weui-cells weui-cells_form second-weui-cells">
<view class="weui-cell">
<view class="weui-cell__hd">
<text class="weui-label mr48">聯絡人</text>
</view>
<view class="weui-cell__bd">
<input bindinput="getName" class="weui-input" placeholder="聯絡人" />
</view>
</view>
<view class="weui-cell">
<view class="weui-cell__hd">
<label class="weui-label mr44">手機號</label>
</view>
<view class="weui-cell__bd">
<input bindinput="getPhone" class="weui-input" placeholder="請輸入手機號" />
</view>
</view>
</view>
</view>
複製程式碼
輸入改變事件獲得輸入框的內容:
data: {
address: ``,
num: ``,
name: ``,
phone: ``
},
getAddress: function(e) {
this.setData({
address: e.detail.value
})
},
getNum: function(e) {
this.setData({
num: e.detail.value
})
},
getName: function(e) {
this.setData({
name: e.detail.value
})
},
getPhone: function(e) {
this.setData({
phone: e.detail.value
})
},
複製程式碼
噹噹噹!說了這麼多,storage 儲存資料的重點來啦!
wxml 介面,設定一個點選事件 saveInfo ,點選後進行資料的儲存:
<view class="newAddr__hd">
<text class="newAddr__add left" bindtap="backToChooseAddr">返回</text>
<text class="newAddr__title">選擇收貨地址</text>
<text bindtap="saveInfo" class="newAddr__add right">儲存</text>
</view>
複製程式碼
微信提供了一個API:wx.setStorage 能按照鍵(key)值(data)對的方式在快取中存資料。
key 是 本地快取中的指定的 key,data 是需要儲存的內容,success 是介面呼叫成功的回撥函式。
// 使用者點選儲存後,對輸入的資料進行儲存,並反饋儲存狀態
saveInfo: function() {
wx.setStorage({
key: "name",
data: [{address:this.data.address}, {num: this.data.num}, {name: this.data.name}, {phone: this.data.phone}],
success: function() {
// 資料設定成功後,彈框提示使用者資訊儲存完整,並跳回展示預設地址的介面
wx.showToast({
title: "地址儲存成功",
icon: `success`,
duration: 2000
})
setTimeout(function(){
wx.navigateTo({
url: "../chooseAddress/chooseAddress"
})
},1000);
}
})
複製程式碼
資料設定成功後,跳回展示預設地址的介面。在這裡,我們要獲取快取中的資料。
微信提供了一個API:wx.getStorage 能從本地快取中非同步獲取指定 key 對應的內容。key 是本地快取中的指定的 key,success 是介面呼叫的回撥函式。
onShow: function () {
var that = this;
wx.getStorage({
key: "name",
success: function(res) {
if(res.data.length > 0) {
that.setData({
address: res.data[0].address,
num: res.data[1].num,
name: res.data[2].name,
phone: res.data[3].phone
})
}
}
})
},
複製程式碼
掃描二維碼
在首頁的右上角,有一個掃一掃的圖片,點選後能掃描二維碼。
給這張圖片繫結一個點選事件scan:
<view class="page__hd__input-right left">
<image bindtap="scan" src="https://user-gold-cdn.xitu.io/2017/12/15/16057cf8f3b9287a" />
</view>
複製程式碼
呼叫微信自帶的 API:wx.scanCode 就能掃描二維碼啦。
scan: function() {
wx.scanCode({
success: (res) => {
console.log(res)
}
})
},
複製程式碼
後臺資料
關於後臺資料的來源,我使用的是用 easymock 構建模擬資料。easymock 對於暫時只關注前臺介面的程式設計師來說是真的超級好用。之前寫過一篇文章使用Easy Mock構建模擬資料歡迎移步檢視。
寫在最後
寫微信小程式最重要的可能就是檢視文件了。微信給開發人員提供了很詳細的各種元件和API的介紹和使用方法。
最後奉上GitHub原始碼地址:https://github.com/TeanLee/hema
如果覺得還不錯的話,請給個start鼓勵一下~
本小程式我也會不斷更新,歡迎批評、指導、交流: