前言
微信小程序作為一款耳熟能詳?shù)膽?yīng)用荣挨,相信大家或多或少都已經(jīng)有過這方面的接觸了。作為一名剛接觸前端不久的小白朴摊,手寫一款小程序是很好的對知識的一次鞏固默垄。下面讓我們來開始小程序的開發(fā)吧。
開發(fā)準(zhǔn)備
項(xiàng)目目錄結(jié)構(gòu)
頁面解構(gòu)
商城首頁
頁面解析
- Navigation是小程序的頂部導(dǎo)航組件甚纲。原生的Navigation組件并不滿足的我們的需求口锭,我們需要在其中加一個(gè)搜索框,這就需要我們對其進(jìn)行自定義了介杆。好了話不多說鹃操,擼起袖子就是干。
Navigation
我們先要對你需要修改的頁面json文件進(jìn)行配置
{
"navigationStyle": "custom"
}
為了寫出來的Navigation能適配所有手機(jī)春哨,我們也需要對app.js進(jìn)行修改荆隘。
App({
onLaunch: function () {
// 獲取頂部欄信息
wx.getSystemInfo({
success: res => {
//導(dǎo)航高度
this.globalData.navHeight = res.statusBarHeight + 46;
}, fail(err) {
console.log(err);
}
})
},
globalData: {
userInfo: null,
navHeight: 0
}
})
微信官方提供了查詢狀態(tài)欄的高度的API(wx.getSystemInfo),大家也可以自己看看悲靴。
然后就是我們自定義導(dǎo)航欄的時(shí)候啦臭胜。直接上代碼
<view>
<view class='nav bg-white' style='height:{{navH}}px'>
<view class='nav-title'>
<view class="INinputheader">
<image src="../../images/icon/mi.png" mode="widthFix" class="image"/>
<text>小米</text>
<icon class="INsearchicon" type="search" size="12"/>
<navigator url="../search/search">
<input class="weui-input" placeholder="搜索商品"/>
</navigator>
</view>
</view>
</view>
</view>
由于自定義導(dǎo)航欄的代碼過于累贅,我們在此就不貼出來了癞尚。大家可以點(diǎn)擊這里
查看哦耸三。其實(shí)還是覺得自定義的導(dǎo)航欄有點(diǎn)丑,但想要模仿的一模一樣真的挺困難的浇揩。
- 首頁的輪播圖區(qū)域仪壮,微信官方文檔提供了swiper組件,直接用就好了胳徽。所以多看文檔真的是一種有效的學(xué)習(xí)手段积锅。
- 首頁的頻道分類頁面分別由不同的頁面構(gòu)成,點(diǎn)擊相應(yīng)的icon進(jìn)行跳轉(zhuǎn)
我的思路是在js文件下每個(gè)icon下寫對應(yīng)的跳轉(zhuǎn)后的url养盗,這樣使用wx:for就能跳轉(zhuǎn)到各自相應(yīng)的頁面了缚陷,下面是部分代碼。
<view class="container1">
<view class="flex-box" >
<view class="list-item" wx:for="{{img_icon_Urls1}}" wx:for-item="icon1" wx:key="index">
<navigator url="{{icon1.url}}">
<image src="{{icon1.icon_img}}" class="icon-size"/>
</navigator>
</view>
</view>
<view class="flex-box" >
<view class="list-item" wx:for="{{img_icon_Urls1}}" wx:for-item="icon1" wx:key="index">
<text class="text-center">{{icon1.name}}</text>
</view>
</view>
<view class="flex-box" >
<view class="list-item" wx:for="{{img_icon_Urls2}}" wx:for-item="icon2" wx:key="index">
<image src="{{icon2.icon_img}}" class="icon-size"/>
</view>
</view>
<view class="flex-box" >
<view class="list-item" wx:for="{{img_icon_Urls2}}" wx:for-item="icon2" wx:key="index">
<text class="text-center">{{icon2.name}}</text>
</view>
</view>
</view>
- 這里數(shù)據(jù)我們都是放在一個(gè)js文件中的往核,在全局app.js進(jìn)行了引用
import data from "./utils/data";
App({
onLaunch: function () {
Object.assign(this.globalData,data);
},
globalData: {
userInfo: null,
}
})
這樣我們在每個(gè)頁面就可以引用數(shù)據(jù)的時(shí)候都需要聲明以下代碼箫爷,這樣我們就可以在app.js文件中拿到自己需要的數(shù)據(jù)。
const app = getApp();
下面是商城首頁的js文件
const showDetail=(e)=>{
const id = e.currentTarget.dataset.pid;
wx.navigateTo({
url: `/pages/commodity/commodity?id=${id}`
})
};
const app = getApp();
Page({
data:{
img_title_Urls:[],
img_icon_Urls1:[],
img_icon_Urls2:[],
recommand:[],
love:[],
},
onLoad(){
const img_title_Urls = app.globalData.img_title_Urls,
img_icon_Urls1 = app.globalData.img_icon_Urls1,
img_icon_Urls2 = app.globalData.img_icon_Urls2,
recommand = app.globalData.recommand,
love = app.globalData.love;
this.setData({
img_title_Urls,
img_icon_Urls1,
img_icon_Urls2,
recommand,
love,
navH: app.globalData.navHeight
});
},
showDetail,
})
功能
-
首頁搜索功能
頁面解析:這里我們引用了有贊的組件的搜索框聂儒,如果搜索的內(nèi)容匹配到了數(shù)據(jù)虎锚,我們就在其下方顯示出來。
<van-search placeholder="搜索商品" value="{{ value }}" background="#FFFFFF" bind:search="Search"/>
<view class="box" wx:for="{{good_list}}" wx:key="index">
<view data-pid="{{item.id}}" bindtap="showDetail">
<text >{{item.name}}</text>
<view class="line"></view>
</view>
</view>
思路分析:
- 輸入需要查詢的關(guān)鍵字或者準(zhǔn)確的內(nèi)容衩婚,進(jìn)行模糊查詢窜护。
- 查詢后就在下方顯示出查詢到的內(nèi)容。
- 點(diǎn)擊內(nèi)容會跳到相應(yīng)商品的詳細(xì)信息非春。
var search = (list,keyword) =>{ //模糊查詢函數(shù)
var arr = [];
for(let i = 0;i < list.length;i++)
{
if(list[i].split(keyword).length > 1){
arr.push(list[i])
}
}
return arr;
};
const showDetail=(e)=>{
const id = e.currentTarget.dataset.pid;
wx.navigateTo({
url: `/pages/commodity/commodity?id=${id}`
})
};
const app = getApp();
Page({
data:{
commodity:[], //總商品列表
name:[],
good_list:[], //搜索后的商品列表
classify:[
{
name:"手機(jī)",
url:"../navi/phone/phone"
}
]
},
Search(event){
const name = this.data.name;
const commodity = this.data.commodity;
const keyword = event.detail;
// wx.setStorageSync('history',keyword); //設(shè)置搜索歷史
var arr = [];
arr = search(name,keyword);
var product = [];
for(let i = 0;i < commodity.length;i++){
for(let j = 0;j < arr.length ;j++){
if(commodity[i].name == arr[j]){
product.push(commodity[i]);
}
}
}
this.setData({
good_list:product
})
},
onLoad:function(){
const commodity = app.globalData.commodity_detial.filter(item=>{
return true;
});
var name = [];
for(let i = 0;i<commodity.length;i++){
name.push(commodity[i].name)
}
this.setData({
name,
commodity
})
},
showDetail,
})
商品分類頁面
頁面解析
思路分析:
- 頁面由兩個(gè)scroll-view構(gòu)成柱徙,左邊為商品的菜單欄,右邊為對應(yīng)菜單的商品區(qū)奇昙。點(diǎn)擊左邊的菜單欄項(xiàng)目會跳到相應(yīng)的商品區(qū)坐搔。這里由scroll-view里的參數(shù)scroll-into-view可以實(shí)現(xiàn),值得注意的是此時(shí)設(shè)的id不能為數(shù)字敬矩,否則會報(bào)錯(cuò)概行。有不懂的童鞋,可以點(diǎn)擊這里參考官方文檔弧岳。
<view class="main">
<scroll-view scroll-y="{{true}}" class="nav" scroll-left="{{navScrollLeft}}" scroll-with-animation="{{true}}" >
<block wx:for="{{navData}}" wx:for-index="id" wx:for-item="navItem" wx:key="index">
<view class="nav-item {{currentTab == id?'active':''}}" data-current="{{id}}" data-id="{{navItem.id}}" bindtap="scrollToView">{{navItem.name}}</view>
</block>
</scroll-view>
<scroll-view scroll-y="{{true}}" scroll-with-animation="{{true}}" scroll-into-view="{{toView}}" enable-back-to-top scroll-top="{{scrollTop}}" class="nav1">
<view wx:for="{{item}}" wx:for-item="item" wx:key="index" >
<view id="{{item.id}}" >
<view class="flex-box">
<view class="linebox">
<view class="line1"></view>
</view>
<text class="title">{{item.name}}</text>
<view class="linebox">
<view class="line2"></view>
</view>
</view>
<view class="flex-box" >
<view wx:for="{{item.cate_list}}" wx:for-item="cate" wx:key="{{item.id}}">
<image src="{{cate.img}}" class="img-size list-item" mode="widthFix" data-cid="{{cate.id}}" bindtap="showcDetail"/>
<text class="font">{{cate.name}}</text>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
這里是跳轉(zhuǎn)到相應(yīng)商品區(qū)的代碼凳忙。
scrollToView(e){
const cur = e.currentTarget.dataset.current;
const id = e.currentTarget.dataset.id;
this.setData({
toView: id,
currentTab: cur,
})
},
- 點(diǎn)擊右邊的商品區(qū)域會跳轉(zhuǎn)到對應(yīng)商品的詳細(xì)信息頁面。因?yàn)槲覀冞@里的商品詳情頁都是統(tǒng)一做的禽炬,跳轉(zhuǎn)的時(shí)候我們會傳遞商品的id過去,在詳情頁我們會對其匹配相應(yīng)的id涧卵,這樣商品的詳細(xì)信息就可以對應(yīng)的輸出來了。下面為跳轉(zhuǎn)到商品詳情頁代碼腹尖。
const showcDetail=(e)=>{
const id = e.currentTarget.dataset.cid;
wx.navigateTo({
url: `/pages/commodity/commodity?id=${id}`
})
};
商品詳情頁面
頁面解析
- 這里為所有商品的統(tǒng)一界面柳恐,跳轉(zhuǎn)時(shí)我們會把相應(yīng)商品的數(shù)據(jù)傳輸?shù)竭@個(gè)頁面,這樣顯示的就是為不同的商品界面。
- 由于原生的weui有些太丑了乐设,就引用了有贊的tabs和GoodsAction組件讼庇。有興趣的童鞋可以點(diǎn)擊查看。
這里貼代碼的時(shí)候mackdown編輯出了問題近尚,所以只貼了tabs標(biāo)簽和下方的加入購物車部分蠕啄。完整的代碼可以點(diǎn)這里
<view>
<van-tabs active="{{active}}" title-active-color="#ff4a00" title-inactive-color="#c0c0c0c" duration="0.1" >
<van-tab title="商品詳情">
<view wx:for="{{commodity.overview}}" wx:key="index">
<image src="{{item}}" mode="widthFix" class="i1"/>
</view>
</van-tab>
<van-tab title="規(guī)格參數(shù)">
<view wx:for="{{commodity.parameter}}" wx:key="index">
<image src="{{item}}" mode="widthFix" class="i1"/>
</view>
</van-tab>
</van-tabs>
</view>
<van-goods-action>
<van-goods-action-icon icon="cart-o" text="購物車" bind:click="go_sh_cart"/>
<van-goods-action-button type="warning"color="#ff4a00" text="加入購物車" bind:click="go_select"/>
<van-goods-action-button type="danger" text="立即購買" />
</van-goods-action>
商品類別選擇
頁面解析
- 商品跳轉(zhuǎn)后,會取得對應(yīng)商品的數(shù)據(jù)。點(diǎn)擊不同的商品類別戈锻,對應(yīng)的值會進(jìn)行改變歼跟。代碼有點(diǎn)過長,這里只貼了部分格遭,有興趣的童鞋可以點(diǎn)擊這里查看完整的哈街。
onLoad: function (options) {
const id = options.id;
const commodity = app.globalData.commodity_detial.filter(item=>{
return item.id == id;
});
this.setData({
id:commodity[0].id,
commodity:commodity[0],
name:commodity[0].name,
version:commodity[0].select_list.version[0].name,
price:commodity[0].select_list.version[0].price,
color:commodity[0].select_list.color[0].name,
img_url:commodity[0].select_list.color[0].img
});
},
- 這里同樣用了有贊的步進(jìn)器stepper組件
wxml
<van-stepper value="{{ 1 }}" max="5" integer="true" bind:change="onChange" button-size="40" input-width="40"/>
js(這里可以取得步進(jìn)器中的值)
onChange(event) {
const number = event.detail;
// console.log(number)
this.setData({
num:number
})
}
- 點(diǎn)擊確定按鈕后,加入到購物車當(dāng)中拒迅,且不會影響到前面所添加的商品叹卷。這里確實(shí)卡了好久,腦殼疼坪它。這里有個(gè)小坑骤竹,在使用wx.showToast要使用一個(gè)延時(shí)函數(shù),否則調(diào)用成功提示會一閃而過往毡。
submit(){
const that = this;
wx.setStorageSync('id',that.data.id);
wx.setStorageSync('name',that.data.name);
wx.setStorageSync('img_url',that.data.img_url);
wx.setStorageSync('version',that.data.version);
wx.setStorageSync('price',that.data.price);
wx.setStorageSync('color',that.data.color);
wx.setStorageSync('num',that.data.num);
wx.setStorageSync('selected',that.data.selected);
const value = wx.getStorageSync('cart_list');
const temp = {
'id':wx.getStorageSync('id'),
'name': wx.getStorageSync('name'),
'img_url': wx.getStorageSync('img_url'),
'version': wx.getStorageSync('version'),
'price': wx.getStorageSync('price'),
'color': wx.getStorageSync('color'),
'num': wx.getStorageSync('num'),
'selected': wx.getStorageSync('selected'),
}
if(value == ""){
wx.setStorageSync('cart_list', [temp]);
}else{
wx.setStorageSync('cart_list', [temp, ...value]);
}
//使用wx.showToast時(shí)要使用一個(gè)延時(shí)(setTimeout)蒙揣,否則成功調(diào)用后會一閃而過
wx.showToast({
title: "成功加入購物車",
icon: 'success',
duration: 2000,
success(){
setTimeout(function(){
wx.navigateBack({
delta: 1
})
}, 1000);
}
})
}
地址選擇和新增地址頁
頁面解析
-
這里新增的地址都是通過wx.getStorageSync的API保存在本地,以便后續(xù)的添加开瞭,當(dāng)然有數(shù)據(jù)庫操作更加方便懒震。
發(fā)現(xiàn)頁
這里沒寫太多,只是簡單的切了下頁面嗤详,發(fā)現(xiàn)抽獎功能沒后臺不好做个扰。
購物車頁面
頁面解析
首先判斷購物車cart_list的值是否存在,這里用
wx:if="{{cart_list == ''}}"
作為判斷條件葱色。
-
購物車為空時(shí)递宅,顯示購物車還是空的,到小米商城逛逛苍狰。點(diǎn)擊按鈕會跳轉(zhuǎn)到商城首頁办龄。
- 購物車不為空時(shí),可以對商品進(jìn)行選擇淋昭,計(jì)算總價(jià)俐填,計(jì)算總數(shù),滑動刪除等操作翔忽。
[圖片上傳失敗...(image-ddc19a-1581859478570)]
<view class="container">
<view wx:if="{{cart_list == ''}}">
<view class="empty">
<view class="cart_icon">
<image src="../../images/icon/cart_empty.png" mode="aspectFill" />
</view>
<view class="prompt">購物車還是空的</view>
<button type="warn" size="default" class="button" style="background: #ff6600;" bindtap="go">
<text >到小米商城逛逛</text>
</button>
</view>
</view>
<view wx:else class="mt">
<view wx:for="{{cart_list}}" wx:key="index">
<view class="weui-slidecells">
<mp-slideview buttons="{{slideButtons}}" bindbuttontap="slideButtonTap" data-index="{{index}}">
<view class="weui-slidecell">
<view class="box">
<view class="icon">
<icon wx:if="{{item.selected}}" type="success" color="rgb(255,103,0)" bindtap="selectList" data-index="{{index}}" size="20" class="i"/>
<icon wx:else type="circle" bindtap="selectList" data-index="{{index}}" size="20" color="rgb(255,103,0)" class="i"/>
</view>
<view class="navi">
<navigator url="../commodity/commodity?id={{item.id}}">
<image src="{{item.img_url}}"></image>
</navigator>
</view>
<text class="title">{{item.name}} {{item.version}} {{item.color}}</text>
<text class="num">{{item.num}} X</text>
<text class="price"> {{item.price}}元</text>
</view>
</view>
</mp-slideview>
</view>
</view>
<view class="submit_title">
<view class="icon">
<icon wx:if="{{selectedAll}}" type="success" size="20" color="rgb(255,103,0)" bindtap="selectAll" class="i1"/>
<icon wx:else type="circle" size="20" color="rgb(255,103,0)" bindtap="selectAll" class="i1"/>
</view>
<text class="all">全選</text>
<view class="allprice">
<text >合計(jì):</text>
<text class="totalprice"> {{totalPrice}}元</text>
</view>
<view class="submit">結(jié)算({{totalNum}})</view>
</view>
</view>
</view>
功能
- 判斷商品是否選中
selectList(e){
const index = e.currentTarget.dataset.index;
let cart_list = this.data.cart_list;
const selected = cart_list[index].selected;
cart_list[index].selected = !selected;
const symbol = cart_list.some(cart => {
return cart.selected === false;
});
if (symbol) {
this.data.selectedAll = false;
} else {
this.data.selectedAll = true;
}
this.setData({
cart_list,
selectedAll: this.data.selectedAll
});
this.getTotalPrice(); //狀態(tài)改變英融,重新計(jì)算總價(jià)
}
- 計(jì)算商品總價(jià)和總數(shù)
getTotalPrice(){
let cart_list = this.data.cart_list;
let total = 0,
num = 0;
for(let i = 0; i<cart_list.length; i++) {
if(cart_list[i].selected) { // 判斷選中才會計(jì)算價(jià)格
total = total + cart_list[i].num * cart_list[i].price;
num = num + parseInt(cart_list[i].num);
}
}
this.setData({
cart_list: cart_list,
totalPrice: total,
totalNum: num
});
}
- 判斷商品是否全選
selectAll(e){
let selectedAll = this.data.selectedAll;
selectedAll = !selectedAll;
let cart_list = this.data.cart_list;
for (let i = 0; i < cart_list.length; i++) {
cart_list[i].selected = selectedAll; // 改變所有商品狀態(tài)
}
this.setData({
selectedAll: selectedAll,
cart_list: cart_list
});
this.getTotalPrice(); // 重新獲取總價(jià)
}
-
刪除商品
slideButtonTap(e){
const index=e.currentTarget.dataset.index;
console.log(index);
this.data.cart_list.splice(index, 1);
wx.clearStorageSync("num");
this.setData({
cart_list: this.data.cart_list
});
}
這里我們使用的是左滑刪除盏檐,微信官方文檔提供了Slideview組件,可以點(diǎn)擊這里查看驶悟。
我的頁面
頁面解析
這里我們只做簡單的頁面胡野,頭像和名字都是通過open-data來獲取的微信頭像和名字。這里大部分都是需要后臺來做的撩银,就沒有過多的花費(fèi)時(shí)間了。
結(jié)語
在寫這個(gè)項(xiàng)目的時(shí)候碰到了許多的難題豺憔,每天都是在寫bug和debug中度過额获。程序員的一天真的好真實(shí),但是這也讓我學(xué)習(xí)了很多恭应,寫代碼一定要親手嘗試抄邀,這樣才能更快的提升自己的能力。由于小米商城這個(gè)項(xiàng)目是在太大了昼榛,只做了部分主要功能境肾。希望能給予他人一點(diǎn)幫助,如果文章中有錯(cuò)誤或不妥之處胆屿,歡迎大家指正奥喻。這里是項(xiàng)目的地址,如果覺得還不錯(cuò)的話非迹,就star一下吧环鲤。