微信小程序购物车总结:
注册并创建项目
登陆 https://mp.weixin.qq.com 进行注册,获取 AppID并在设置中进行一些配置(如服务器域名),下载小程序的开发工具,扫描二维码登陆,创建新项目。在创建项目时可以选择创建一个 quick start 项目,这样会自动生成一个简单的 demo,有助于我们了解项目的结构和组成。
框架
小程序开发框架的目标是通过尽可能简单、高效的方式让开发者可以在微信中开发具有原生 APP 体验的服务。
框架提供了自己的视图层描述语言 WXML
和 WXSS
,以及基于 JavaScript
的逻辑层框架,并在视图层与逻辑层间提供了数据传输和事件系统,让开发者能够专注于数据与逻辑
响应的数据绑定
框架的核心是一个响应的数据绑定系统。
整个小程序框架系统分为两部分:视图层(View)和逻辑层(App Service)
框架可以让数据与视图非常简单地保持同步。当做数据修改的时候,只需要在逻辑层修改数据,视图层就会做相应的更新。
最近做了一个购物车的微信小程序
如图所示:
滑动列表,使用微信组件 scroll-view
<scroll-view class='scroll-view scoll-view-fixed' scroll-x scroll-with-animation>
<view bindtap="setActiveIndex" class="scroll-view-item {{activeIndex==index?'active':''}}"
wx:for="{{protype}}" data-index="{{index}}">
{{item}}
</view>
</scroll-view>
protype:[
'热销', '集成灶', '蒸箱烤箱', '水槽', '夏季焕新', '生鲜食品', '美妆服饰', '家居家电',
'新鲜水果','品质家纺','酒水饮料', '美容护肤', '家用电器', '内衣鞋袜','休闲零食'
],
activeIndex:0,
setActiveIndex(e){
console.log(e)
this.setData({
activeIndex: e.currentTarget.dataset.index
})
},
如图所示:
轮播图 使用微信组件 swiper
<view class='swiper-image'>
<swiper indicator-dots autoplay>
<block wx:for="{{imgUrls}}">
<swiper-item>
<image src='{{item}}' class='slide-image'></image>
</swiper-item>
</block>
</swiper>
</view>
imgUrls:[
"/imgs/banner/1.jpg",
"/imgs/banner/2.jpg",
"/imgs/banner/3.jpg",
"/imgs/banner/4.jpg",
"/imgs/banner/5.jpg",
],
如图所示:
轮播公告 使用微信组件 swiper
<view class='view-adv'>
<view style='float: left;font-size:13px;color:#999999;'>公告:</view>
<!-- autoplay 自动播放 vertical 垂直 circular循环 -->
<swiper class='swiper-adv' autoplay vertical circular>
<swiper-item>
<text>官方汇款</text>
</swiper-item>
<swiper-item>
<text>官方公告</text>
</swiper-item>
<swiper-item>
<text>官方活动</text>
</swiper-item>
<swiper-item>
<text>官方获奖</text>
</swiper-item>
</swiper>
</view>
如图所示:
监听热销商品滚动
<!-- 热销商品 -->
<scroll-view class='hot-prod' scroll-x bindscroll="hotProdScroll">
<view class="one-row">
<view class="row-item" wx:for='{{6}}'>
<image src='{{hotprods[index]}}'></image>
<view class="placeholder">热销{{index+1}}</view>
</view>
</view>
<view class="two-row">
<view class="row-item" wx:for="{{5}}">
<image src='{{hotprods[6+index]}}'></image>
<view class="placeholder">热销{{index+1+6}}</view>
</view>
</view>
</scroll-view>
<!-- 显示进度 -->
<view class='prod-progress'>
<view class='progress-outer'>
<view class='progress-inner' style='margin-left:{{hotProdMarginLeft}}px'></view>
</view>
</view>
hotprods:[
"/imgs/hotprod/1.png",
"/imgs/hotprod/2.png",
"/imgs/hotprod/3.png",
"/imgs/hotprod/4.png",
"/imgs/hotprod/5.png",
"/imgs/hotprod/6.png",
"/imgs/hotprod/7.png",
"/imgs/hotprod/8.png",
"/imgs/hotprod/9.png",
"/imgs/hotprod/10.png",
"/imgs/hotprod/11.png",
],
hotProdMarginLeft: 0, //左边距
//监听热销商品滚动
hotProdScroll(e) {
this.setData({
hotProdMarginLeft: e.detail.scrollLeft
});
},
如图所示:
<view class='goods-container'>
<view class="goods-item" wx:for="{{goods}}">
<view class='goods-image'>
<image src='{{item.pic}}'></image>
</view>
<view class='goods-title'>{{item.name}}</view>
<view class='goods-price'>¥{{item.price}}</view>
</view>
</view>
</view>
goods: [
{ name: '御泥坊男士黑茶清爽控油矿物泥浆面膜去黑头祛痘收缩毛孔补水新品', price: 2323, pic: '/imgs/1.jpg' },
{ name: '台湾欣兰黑里透白冻膜225g竹炭清洁收缩毛孔温和去黑白头面膜', price: 169, pic: '/imgs/2.jpg' },
{ name: 'SHERO CHING', price: 178, pic: '/imgs/3.jpg' },
{ name: 'JS-sdsosodsd', price: 545, pic: '/imgs/4.jpg' },
{ name: 'asdsaasddsads', price: 20, pic: '/imgs/5.jpg' },
{ name: '3223434', price: 199, pic: '/imgs/6.jpg' },
{ name: '【两瓶420】【欣欣】佑珍提拉面膜紧致改善细纹抬头法令纹去黄', price: 240, pic: '/imgs/7.jpg' },
{ name: '韩国A. by Bom超能婴儿叶子面膜冰凝叶清凉舒缓补水保湿10片包邮', price: 99, pic: '/imgs/8.jpg' },
{ name: '手串手串手串', price: 200, pic: '/imgs/9.jpg' },
{ name: 'Bershka 女士 2017秋冬新款粉色飞行员夹克短外套 01232551622', price: 2323, pic: '/imgs/10.jpg' },
{ name: '御泥坊清爽平衡泥浆面膜清洁 男女泥膜收缩毛孔 去黑头竹炭火山泥', price: 454, pic: '/imgs/11.jpg' },
{ name: '御泥坊美白嫩肤蚕丝面膜20片男女蚕丝玻尿酸面膜补水美白淡斑保湿', price: 444, pic: '/imgs/12.jpg' },
{ name: '御泥坊蜂蜜矿物睡眠面膜免洗滋润补水保湿护肤化妆正品男女包邮', price: 455, pic: '/imgs/13.jpg' },
{ name: '御泥坊清润莹亮黑面膜控油收缩毛孔深清洁玻尿酸男女补水保湿面膜', price: 454, pic: '/imgs/14.jpg' },
{ name: '御泥坊美白嫩肤黑面膜贴女士蚕丝玻尿酸祛斑面膜补水美白淡斑保湿', price: 3434, pic: '/imgs/15.jpg' },
{ name: '御泥坊人参矿物蚕丝面膜7片*3盒 紧致抗皱去皱淡化细纹补水正品女', price: 5454, pic: '/imgs/16.jpg' },
{ name: '御泥坊玫瑰滋养睡眠面膜免洗补水保湿护肤品夜间男女学生收缩毛孔', price: 344, pic: '/imgs/17.jpg' },
{ name: '御泥坊红豆薏米面膜100g提亮肤色改善暗黄红润调理泥膜正品女包邮', price: 545, pic: '/imgs/18.jpg' },
{ name: '御泥坊旗舰店葡萄籽美白淡斑祛黄黑面膜套21片 清洁补水保湿嫩肤', price: 434, pic: '/imgs/19.jpg' },
{ name: '御泥坊水润黑白面膜贴补水保湿收缩毛孔亮肤玻尿酸护肤套装正品女', price: 69, pic: '/imgs/20.jpg' },
{ name: '御泥坊小迷糊阿狸加菲猫玻尿酸秋季补水保湿亮肤黑面膜花瑶花男女', price: 233, pic: '/imgs/21.jpg' },
]
如图所示:
app.json 是对整个小程序的全局配置。我们可以在这个文件中配置小程序是由哪些页面组成,配置小程序的窗口背景色,配置导航条样式,配置默认标题
{
"pages": [
"index/index",
"shoping/shoping",
"usercenter/usercenter",
"search/search",
"goodstype/goodstype"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#E02E24",
"navigationBarTextStyle": "white"
},
"tabBar": {
"color": "#333",
"selectedColor": "red",
"backgroundColor": "#fff",
"borderStyle": "black",
"position": "bottom",
"list": [
{
"pagePath": "index/index",
"selectedIconPath": "/imgs/nav/home-on.png",
"iconPath": "/imgs/nav/home-off.png",
"text": "首页"
},
{
"pagePath": "shoping/shoping",
"selectedIconPath": "/imgs/nav/cart-on.png",
"iconPath": "/imgs/nav/cart-off.png",
"text": "购物车"
},
{
"pagePath": "usercenter/usercenter",
"text": "个人中心",
"selectedIconPath": "/imgs/nav/my-on.png",
"iconPath": "/imgs/nav/my-off.png"
},
{
"pagePath": "goodstype/goodstype",
"text": "分类页面",
"selectedIconPath": "/imgs/nav/search-on.png",
"iconPath": "/imgs/nav/search-off.png"
}
]
}
}
pages
指定了小程序的组成页面,第一个代表小程序的初始页面。
window
用于设置小程序的状态栏、导航条、标题、窗口背景色。
tabBar
用于配置客户端窗口的底部或顶部 tab 栏的样式以及 tab 切换时显示的对应页面
如图所示:
购物车
如图所示:
先来弄清楚购物车的需求
- 单选、全选和取消,而且会随着选中的商品计算出总价
- 单个商品购买数量的增加和减少
- 删除商品。当购物车为空时,页面会变为空购物车的布局
- 首先是一个商品列表(carts),列表里的单品需要:商品图(image),商品名(title),单价(price),数量(num),是否选中(selected),商品id(id)
- 然后左下角的全选,需要一个字段(selectAllStatus)表示是否全选了
- 右下角的总价(totalPrice)
- 最后需要知道购物车是否为空(hasList)
布局 wxml
<view class='car-container'>
<!-- 顶部信息 -->
<view class='car-top'>
<view class='title'>购物车 {{userInfo}}</view>
<view class='btn-edit'>{{cargoods.length}}件</view>
</view>
<!-- 购物车列表 -->
<view class='car-list'>
<!-- 购物车产品 -->
<view class='car-item' wx:for='{{cargoods}}'>
<!--列表项,总长度为按钮的宽度50px+goods-info的长度 -->
<view class='car-prod' data-index='{{index}}' style='left:{{moveLength[index]}}px;' bindtouchend='moveEnd' bindtouchstart='moveStart' bindtouchmove='moving' bindtouchcancel='moveEnd'>
<view class='goods-info {{item.pitch?"active":""}}' data-index='{{index}}' catchtap='toggleBuy' >
<view class='goods-info-inner'>
<view class='checkbox'></view>
<view class='goods-image'>
<image src='{{item.pic}}'></image>
</view>
<view class='good-info'>
<view class='name'>{{item.name}}</view>
<view class='footer'>
<view class='price'>¥{{item.price}}</view>
<view class='numbers'>
<view class='number-box'>
<view class='controler left' data-index='{{index}}' catchtap='reduceNumber'>-</view>
<view class='controler middle'>{{item.numbers}}</view>
<view class='controler right' data-index='{{index}}' catchtap='addNumber'>+</view>
</view>
</view>
</view>
</view>
</view>
<view class='btn-delete' data-index='{{index}}' catchtap='removeGoods'>删除</view>
</view>
</view>
</view>
<!-- 没有商品信息 -->
<view wx:if='{{cargoods.length==0}}'>
<navigator url='/index/index' open-type='switchTab' style='padding:100rpx 20rpx;'>
购物车空空如也,赶紧去挑选你心意的产品吧~~~~~
</navigator>
</view>
</view>
<!-- 购物车底部 -->
<view class='car-footer-container'>
<view class='car-footer'>
<view class='select-all {{selectAll?"active":""}}' catchtap='checkAll'>全选</view>
<view class='btn-buy'>
<text style='color:red;padding:0 10px;'>合计:¥{{totalPrice}}</text>
<button type='warn' disabled='{{totalPrice==0}}'>去结算</button>
</view>
</view>
</view>
</view>
知道了需要这些数据,在页面初始化的时候我们先定义好这些
初始化
data: {
moveLength: [0, 0, 0, 0], //移动的长度
moveStartX: [0, 0, 0, 0], //x轴移动的起点
windowWidth: 0, //窗体的宽度
cargoods: [], //购物车信息
totalPrice: 0, //总价
selectAll: false, //是否全选
userInfo: '', //用户信息
cargoods:[
{ pic: "/imgs/1.jpg", name: "御泥坊男士黑茶清爽控油矿物泥浆面膜去黑头祛痘收缩毛孔补水新品", price: 2323, numbers:1,pitch:false},
{ pic: "/imgs/2.jpg", name: "台湾欣兰黑里透白冻膜225g竹炭清洁收缩毛孔温和去黑白头面膜", price: 169, numbers: 1, pitch: false },
{ pic: "/imgs/3.jpg", name: "SHERO CHING", price: 545, numbers: 1, pitch: false },
{ pic: "/imgs/5.jpg", name: "asdsaasddsads", price: 20, numbers: 1, pitch: false },
{ pic: "/imgs/4.jpg", name: "3223434", price: 199, numbers: 1, pitch: false },
{ pic: "/imgs/6.jpg", name: "【两瓶420】【欣欣】佑珍提拉面膜紧致改善细纹抬头法令纹去黄", price: 240, numbers: 1, pitch: false },
]
},
计算总价
总价 = 选中的商品1的 价格 * 数量 + 选中的商品2的 价格 * 数量 + ...
根据公式,可以得到
totalPrice() {
var tempGoods = this.data.cargoods;
var totalPrice = 0;
totalPrice = tempGoods.reduce(function (total, current) {
if (current.pitch) {
return total + current.price * current.numbers;
} else {
return total;
}
}, 0)
return parseFloat(totalPrice.toFixed(2))
},
页面中的其他操作会导致总价格变化的都需要调用该方法。
如图所示:
删除商品
点击删除则从购物车列表中删除当前元素,删除之后如果购物车为空,改变购物车为空标识hasList为false
removeGoods(e) {
var index = e.currentTarget.dataset.index
var goods = this.data.cargoods;
goods.splice(index, 1)
this.setData({
cargoods: goods
})
this.setData({
totalPrice: this.totalPrice()
})
console.log(index, goods);
},
如图所示:
增减数量
点击+号,num加1,点击-号,如果num > 1,则减1
//减少数量
reduceNumber(e) {
var index = e.currentTarget.dataset.index
var goods = this.data.cargoods[index];
if (goods.numbers == 1) {
return;
}
var key = 'cargoods[' + index + '].numbers';
this.setData({
[key]: goods.numbers - 1, // [key] 是 es5中的计算属性命名,将变量的内容作为key
})
this.setData({
totalPrice: this.totalPrice()
})
},
//增加数量
addNumber(e) {
var index = e.currentTarget.dataset.index
var goods = this.data.cargoods[index];
var key = 'cargoods[' + index + '].numbers';
this.setData({
[key]: goods.numbers + 1, // [key] 是 es5中的计算属性命名,将变量的内容作为key
})
this.setData({
totalPrice: this.totalPrice()
})
},
如图所示:
全选事件和选择事件
//选择或取消购买
toggleBuy(e) {
var index = e.currentTarget.dataset.index
var goods = this.data.cargoods[index];
var key = 'cargoods[' + index + '].pitch';
//设置购买状态
this.setData({
[key]: !goods.pitch, // [key] 是 es5中的计算属性命名,将变量的内容作为key
})
//只要有一个没有选中,全选取消
var st=this.data.cargoods.find(arr=>arr.pitch==false)
if(st){
this.setData({
selectAll:false
})
}else{
this.setData({
selectAll:true
})
}
//重新计算总价
this.setData({
totalPrice: this.totalPrice()
})
},
//全选,全不选
checkAll(e) {
var checked = !this.data.selectAll;
this.setData({
selectAll: checked
})
var goods = this.data.cargoods;
var len = goods.length;
for (var i = 0; i < len; i++) {
var key = 'cargoods[' + i + '].pitch';
this.setData({
[key]: checked, // [key] 是 es5中的计算属性命名,将变量的内容作为key
})
}
this.setData({
totalPrice: this.totalPrice()
})
},
如图所示:
移动开始
moveStart(e) {
var index = e.currentTarget.dataset.index;
var key = "moveStartX[" + index + "]"
this.setData({
//这个地方的算法有点复杂,当前坐标pageX+当前的元素移动的位置moveLength,才能保证同步移动,要考虑元素的偏移量
[key]: e.touches[0].pageX - this.data.moveLength[index]
});
},
如图所示:
移动中
moving(e) {
var index = e.currentTarget.dataset.index;
var key = "moveLength[" + index + "]"
//获取移动的距离
var length = e.changedTouches[0].pageX - this.data.moveStartX[index];
this.setData({
[key]: length
});
},
如图所示:
移动结束
moveEnd(e) {
var index = e.currentTarget.dataset.index;
var key = "moveLength[" + index + "]"
//最终移动的长度
var len = e.changedTouches[0].pageX - this.data.moveStartX[index]
//向左移动超过了50px
if (len < -50) {
len = -50; //显示删除按钮
} else if (len == 0 || this.data.moveLength[index] == -50) {
//由于点击时也会触发 bindtouchstart和bindtouchend事件,此时很有可能没有移动或是moveLength初始值-50的情况
//这种情况不做任何的操作
return;
} else {
len = 0; //隐藏删除按钮
}
this.setData({
[key]: len
});
},
如图所示:
生命周期函数--监听页面加载
onLoad: function (options) {
var app = getApp();
//获取全局的数据
var globalData = app.globalData;
console.log(globalData.cargoods)
var sysInfo = wx.getSystemInfoSync();
this.setData({
windowWidth: sysInfo.windowWidth,
cargoods: globalData.cargoods //将全局数据,赋值本地变量,方便后面的操作
})
this.setData({
totalPrice: this.totalPrice()
})
},
总结:
虽然一个购物车功能比较简单,但是里面涉及到微信小程序的知识点还是比较多的,适合新手练习掌握。