Token
接口增加访问权限,当用户登录后要确定用户的身份,这就是token令牌,当然他要有有效期
微信身份体系设计
小程序有一套自己的令牌机制,所以不用我们后端再写一套,小程序登陆开发API获取code后,调取后台获取令牌的token接口,再到微信服务器,返回openid,session_key,openid是用户的唯一标识,切记它不能返回给客户端,支付的时候会用到。受保护的接口一定要携带token
了解uid,根据uid查询用户的数据
如果关联公众号,服务号,小程序,同一个用户要保证ID的不变,这就要用到uid
下面正式开始小程序
开发前的配置、开发经验技巧
- 合法域名的配置,在公众号设置里面
- 开发工具中的关于https的设置
- 在项目中新建文件回车后自动生成四类文件
- app.json里面配置路由
- 数据循环时作用于block标签 block 并不是一个组件,它仅仅是一个包装元素,不会在页面中做任何渲染,只接受控制属性,所以不能写样式。
- model文件,存放数据接口,js文件负责数据双向绑定。
- 布局的时候如果,container时列,再包裹一层view设置横向布局
关于token重发机制很重要,app.js在小程序初始化后校验令牌,小程序没有登录账号密码获取code码对用户来说完全是透明的,
官方小程序 app.js
globalData用于存放用户的个人信息,关注小程序生命周期的理解
//app.js
App({
onLaunch: function () {
// 展示本地存储能力
var logs = wx.getStorageSync('logs') || []
logs.unshift(Date.now())
wx.setStorageSync('logs', logs)
},
globalData: {
userInfo: null
}
})
项目中的 app.js
//app.js
import { Token } from 'utils/token.js';
App({
onLaunch: function () {
var token = new Token();
token.verify();
},
onShow:function(){
},
})
app.json
{
"pages":[
"pages/home/home",
"pages/category/category",
"pages/cart/cart",
"pages/my/my",
"pages/theme/theme",
"pages/product/product",
"pages/order/order",
"pages/pay-result/pay-result"
],
"window":{
"navigationBarTitleText": "",
"navigationBarTextStyle": "#FFFFFF",
"navigationBarBackgroundColor": "#AB956D",
"backgroundColor": "#FFFFFF",
"backgroundTextStyle": "dark",
"enablePullDownRefresh":true
},
"tabBar":{
"list":[{
"pagePath":"pages/home/home",
"iconPath":"imgs/toolbar/home.png",
"selectedIconPath":"imgs/toolbar/home@selected.png",
"text":"主页"
},{
"pagePath":"pages/category/category",
"iconPath":"imgs/toolbar/category.png",
"selectedIconPath":"imgs/toolbar/category@selected.png",
"text":"分类"
},{
"pagePath":"pages/cart/cart",
"iconPath":"imgs/toolbar/cart.png",
"selectedIconPath":"imgs/toolbar/cart@selected.png",
"text":"购物车"
},{
"pagePath":"pages/my/my",
"iconPath":"imgs/toolbar/my.png",
"selectedIconPath":"imgs/toolbar/my@selected.png",
"text":"我的"
}],
"backgroundColor": "#F5F5F5",
"selectedColor": "#AB956D",
"color": "#989898",
"borderStyle": "white",
"position":"bottom"
},
"networkTimeout": {
"request": 20000,
"connectSocket": 20000,
"uploadFile": 20000,
"downloadFile": 20000
},
"debug":true
}
编程技巧 源自官方小程序
关注它末尾补零的技巧、还有返回字符串的拼接
util.js
const formatTime = date => {
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
const hour = date.getHours()
const minute = date.getMinutes()
const second = date.getSeconds()
return [year, month, day].map(formatNumber).join('-') + ' ' + [hour, minute, second].map(formatNumber).join(':')
}
const formatNumber = n => {
n = n.toString()
return n[1] ? n : '0' + n
}
module.exports = {
formatTime: formatTime
}
//logs.js
const util = require('../../utils/util.js')
Page({
data: {
logs: []
},
onLoad: function () {
this.setData({
logs: (wx.getStorageSync('logs') || []).map(log => {
return util.formatTime(new Date(log))
})
})
}
})
// 引用使用es6的module引入和定义
// 全局变量以g_开头
// 私有函数以_开头
utils/token.js
import { Config } from 'config.js';
class Token {
constructor() {
this.verifyUrl = Config.restUrl + 'token/verify';
this.tokenUrl = Config.restUrl + 'token/user';
}
verify() {
var token = wx.getStorageSync('token');
if (!token) {
this.getTokenFromServer();
}
else {
this._veirfyFromServer(token);
}
}
_veirfyFromServer(token) {
var that = this;
wx.request({
url: that.verifyUrl,
method: 'POST',
data: {
token: token
},
success: function (res) {
var valid = res.data.isValid;
if(!valid){
that.getTokenFromServer();
}
}
})
}
getTokenFromServer(callBack) {
var that = this;
wx.login({
success: function (res) {
wx.request({
url: that.tokenUrl,
method:'POST',
data:{
code:res.code
},
success:function(res){
wx.setStorageSync('token', res.data.token);
callBack&&callBack(res.data.token);
}
})
}
})
}
}
export {Token};
utils/config
class Config{
constructor(){
}
}
Config.restUrl = 'REST API 基地址';
Config.onPay=true; //是否启用支付
export {Config};
utils/base
回调函数的应用,令牌重获机制(按项目需求是否需要)
注意回调函数的处理,调用方法的时候传入就返回,没有则不执行
import { Token } from 'token.js';
import { Config } from 'config.js';
class Base {
constructor() {
"use strict";
this.baseRestUrl = Config.restUrl;
this.onPay=Config.onPay;
}
//http 请求类, 当noRefech为true时,不做未授权重试机制
request(params, noRefetch) {
var that = this,
url=this.baseRestUrl + params.url;
if(!params.type){
params.type='get';
}
/*不需要再次组装地址*/
if(params.setUpUrl==false){
url = params.url;
}
wx.request({
url: url,
data: params.data,
method:params.type,
header: {
'content-type': 'application/json',
'token': wx.getStorageSync('token')
},
success: function (res) {
// 判断以2(2xx)开头的状态码为正确
// 异常不要返回到回调中,就在request中处理,记录日志并showToast一个统一的错误即可
var code = res.statusCode.toString();
var startChar = code.charAt(0);
if (startChar == '2') {
params.sCallback && params.sCallback(res.data);
} else {
if (code == '401') {
if (!noRefetch) {
that._refetch(params);
}
}
that._processError(res);
params.eCallback && params.eCallback(res.data);
}
},
fail: function (err) {
//wx.hideNavigationBarLoading();
that._processError(err);
// params.eCallback && params.eCallback(err);
}
});
}
_processError(err){
console.log(err);
}
_refetch(param) {
var token = new Token();
token.getTokenFromServer((token) => {
this.request(param, true);
});
}
/*获得元素上的绑定的值*/
getDataSet(event, key) {
return event.currentTarget.dataset[key];
};
};
export {Base};
home/home.wxml
模板的引入,以及往模版内传数据,详解了数据如何去渲染,以及block的使用,for循环,巧用if判断 、样式的引入 @import "../tpls/base.wxss"; @import "../tpls/products/products-tpl.wxss";
下面是模版
<template name="products">
<view class="products-box">
<block wx:for="{{productsArr}}">
<view class="products-item" bindtap="onProductsItemTap" data-id="{{item.id}}">
<image class="products-image" src="{{item.main_img_url}}" mode="aspectFill"></image>
<view class="products-item-bottom">
<text class="name">{{item.name}}</text>
<view class="price">¥{{item.price}}</view>
</view>
</view>
</block>
</view>
</template>
首页如何使用模版
<import src="../tpls/products/products-tpl.wxml"/>
<view class="container home-container" hidden="{{!loadingHidden}}">
<swiper indicator-dots="true" autoplay="true" class="swiper">
<block wx:for="{{bannerArr}}">
<swiper-item class="banner-item" bindtap="onProductsItemTap" data-id="{{item.key_word}}">
<image class="item-image" src="{{item.img.url}}" mode="aspectFill" />
</swiper-item>
</block>
</swiper>
<view class="home-main">
<!--主题精选-->
<view class="home-main-theme">
<view class="home-main-header">精选主题</view>
<view class="theme-box">
<block wx:for="{{themeArr}}">
<view wx:if="{{index==2}}" class="theme-item big" bindtap="onThemesItemTap" data-id="{{item.id}}" data-name="{{item.name}}">
<image src="{{item.topic_img.url}}"></image>
</view>
<view wx:else class="theme-item" bindtap="onThemesItemTap" data-id="{{item.id}}" data-name="{{item.name}}">
<image src="{{item.topic_img.url}}"></image>
</view>
</block>
</view>
</view>
<!--单品首发-->
<view class="home-main-products">
<view class="home-main-header">最近新品</view>
<template is="products" data="{{productsArr:productsArr}}"/>
</view>
</view>
</view>
<loading hidden="{{loadingHidden}}">
加载中...
</loading>
home/home.model.js
// var Base = require('../../utils/base.js').base;
import {Base} from '../../utils/base.js';
class Home extends Base{
constructor(){
super();
}
/*banner图片信息*/
getBannerData(callback){
var that=this;
var param={
url: 'banner/1',
sCallback:function(data){
data=data.items;
callback && callback(data);
}
};
this.request(param);
}
/*首页主题*/
getThemeData(callback){
var param={
url: 'theme?ids=1,2,3',
sCallback:function(data){
callback && callback(data);
}
};
this.request(param);
}
/*首页部分商品*/
getProductorData(callback){
var param={
url: 'product/recent',
sCallback:function(data){
callback && callback(data);
}
};
this.request(param);
}
};
export {Home};
home/home.js
import { Home } from 'home-model.js';
var home = new Home(); //实例化 首页 对象
Page({
data: {
loadingHidden: false
},
onLoad: function () {
this._loadData();
},
/*加载所有数据*/
_loadData:function(callback){
var that = this;
// 获得bannar信息
home.getBannerData((data) => {
that.setData({
bannerArr: data,
});
});
/*获取主题信息*/
home.getThemeData((data) => {
that.setData({
themeArr: data,
loadingHidden: true
});
});
/*获取单品信息*/
home.getProductorData((data) => {
that.setData({
productsArr: data
});
});
},
/*跳转到商品详情*/
onProductsItemTap: function (event) {
var id = home.getDataSet(event, 'id');
wx.navigateTo({
url: '../product/product?id=' + id
})
},
/*跳转到主题列表*/
onThemesItemTap: function (event) {
var id = home.getDataSet(event, 'id');
var name = home.getDataSet(event, 'name');
wx.navigateTo({
url: '../theme/theme?id=' + id+'&name='+ name
})
},
/*下拉刷新页面*/
onPullDownRefresh: function(){
this._loadData(()=>{
wx.stopPullDownRefresh()
});
},
//分享效果
onShareAppMessage: function () {
return {
title: '零食商贩 Pretty Vendor',
path: 'pages/home/home'
}
}
})
注意setNavigationBarTitle动态设置导航标题,在生命周期里面的应用
theme/theme.js
import { Theme } from 'theme-model.js';
var theme = new Theme(); //实例化 主题列表对象
Page({
data: {
loadingHidden: false
},
onReady:function(){
wx.setNavigationBarTitle({
title: this.data.titleName
});
},
onLoad: function (option) {
this.data.titleName=option.name;
this.data.id=option.id;
wx.setNavigationBarTitle({
title: option.name
});
this._loadData();
},
/*加载所有数据*/
_loadData:function(callback){
var that = this;
/*获取单品列表信息*/
theme.getProductorData(this.data.id,(data) => {
that.setData({
themeInfo: data,
loadingHidden:true
});
});
},
/*跳转到商品详情*/
onProductsItemTap: function (event) {
var id = theme.getDataSet(event, 'id');
wx.navigateTo({
url: '../product/product?id=' + id
})
},
/*下拉刷新页面*/
onPullDownRefresh: function(){
this._loadData(()=>{
wx.stopPullDownRefresh()
});
},
//分享效果
onShareAppMessage: function () {
return {
title: '零食商贩 Pretty Vendor',
path: 'pages/theme/theme?id=' + this.data.id
}
}
})
产品详情页面,picker组件的应用如何从picker里面取值,tab切换的思路,产品详情下面三个tab盒子切换的条件,tab切换关键点在于currentindex,最近在做react native 遇到父组件跳到子组件激活子组件样式,子组件接收到就修改currentindex,否则还是默认为0,默认定义以后就不要动了,直接把传入的值赋值给它
product.wxml
<view class="container detail-container" hidden="{{!loadingHidden}}">
<view class="detail-header-box">
<view class="fixed-btns-box" bindtap="onCartTap">
<view class="fiexd-cart {{isShake?'animate':''}}">
<image src="../../imgs/icon/cart@top.png"></image>
<view wx:if="{{cartTotalCounts>0}}">{{cartTotalCounts}}</view>
</view>
</view>
<view class="detail-topic-img">
<image src="{{product.main_img_url}}" mode="aspectFit"></image>
</view>
<view class="cart-box">
<view class="product-counts">
<picker class="{{product.stock==0?'disabled':''}}" bindchange="bindPickerChange" value="{{index}}" range="{{countsArray}}">
<!--因为picker对flex支持不好,所以加了一层view-->
<view>
<text class="counts-tips">数量</text>
<text class="counts-data">{{productCounts}}</text>
<image class="counts-icon" src="../../imgs/icon/arrow@down.png"></image>
</view>
</picker>
</view>
<view class="middle-border"></view>
<view class="add-cart-btn {{product.stock==0?'disabled':''}}" bindtap="onAddingToCartTap">
<text>加入购物车</text>
<image class="cart-icon" src="../../imgs/icon/cart.png"></image>
<image id="small-top-img" class="small-top-img {{isFly?'animate':''}}"
src="{{product.main_img_url}}" mode="aspectFill" style="{{translateStyle}}"></image>
</view>
</view>
<view class="basic-info-box">
<view class="stock" wx:if="{{product.stock>0}}">有货</view>
<view class="stock no" wx:else>缺货</view>
<view class="name">{{product.name}}</view>
<view class="price">¥{{product.price}}</view>
</view>
</view>
<view class="detail-bottom-box">
<view class="tabs-box">
<block wx:for="{{['商品详情' ,'产品参数','售后保障']}}">
<view class="tabs-item {{currentTabsIndex==index?'selected':''}}" bindtap="onTabsItemTap" data-index="{{index}}">
{{item}}
</view>
</block>
</view>
<view class="product-detail-box">
<view class="product-detail-imgs" hidden="{{currentTabsIndex!=0}}">
<block wx:for="{{product.imgs}}">
<image src="{{item.img_url.url}}" mode="aspectFill"></image>
</block>
</view>
<view class="product-detail-properties" hidden="{{currentTabsIndex!=1}}">
<block wx:for="{{product.properties}}">
<view class="properties-item">
<view class="properties-name">{{item.name}}</view>
<view class="properties-detail">{{item.detail}}</view>
</view>
</block>
</view>
<view class="product-detail-protect" hidden="{{currentTabsIndex!=2}}">
<view>七天无理由免费退货</view>
</view>
</view>
</view>
</view>
<loading hidden="{{loadingHidden}}">
加载中...
</loading>
主要是加入购物车的动画
product.wxss
@import "../tpls/base.wxss";
.detail-container {
background-color:#F9F9F9
}
.detail-header-box,.detail-bottom-box{
background-color: #fff;
}
.detail-topic-img{
display: flex;
justify-content: center;
}
.detail-topic-img image{
width: 100%;
}
.fixed-btns-box{
position: fixed;
top:50rpx;
right:12px;
width: 80rpx;
}
.fiexd-cart image{
height: 64rpx;
width: 64rpx;
}
.fiexd-cart view{
font-size: 24rpx;
background-color: #AB956D;
color: white;
position: absolute;
right: 64rpx;
top: 0rpx;
height: 36rpx;
width: 36rpx;
line-height: 36rpx;
border-radius: 36rpx;
text-align: center;
}
.fiexd-cart.animate{
animation: aCartScale 200ms cubic-bezier(.17,.67,.83,.67);
animation-fill-mode: backwards;
}
@-webkit-keyframes aCartScale{
0%{
-webkit-transform: scale(1.1);
}
100% {
-webkit-transform: scale(1);
}
}
/*选择数量和添加到购物车*/
.cart-box{
width: 660rpx;
height: 100rpx;
margin: 30rpx auto;
border-radius: 100rpx;
background-color: #AB956D;
color: #fff;
display: flex;
align-items: center;
}
.product-counts,.add-cart-btn{
height: 100%;
display: flex;
font-size: 24rpx;
align-items: center;
justify-content: center;
}
.product-counts{
width: 50%;
}
.add-cart-btn{
position: relative;
flex: 1;
}
.add-cart-btn:active{
color: #fff;
}
.add-cart-btn.disabled{
color: #D5D5DB;
}
/*中间分割线*/
.middle-border{
width: 2rpx;
height: 30rpx;
border-right: 1rpx #fff dotted;
}
.small-top-img{
height: 160rpx;
width: 160rpx;
right:6rpx;
position: absolute;
opacity: 0;
}
.small-top-img.animate{
opacity: 1;
/*-webkit-transition:all 1000ms cubic-bezier(.4,.46,.3,1.31);*/
-webkit-transition:all 1000ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
/*数量选择器*/
.product-counts picker{
margin: 0 auto;
height: 100rpx;
width: 100%;
color: #fff;
}
.product-counts picker.disabled{
color: #D5D5DB;
}
.product-counts picker view{
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100rpx;
}
.counts-tips,.counts-data,.counts-icon{
margin: 0 20rpx;
}
.counts-data{
font-size: 28rpx;
}
.counts-icon{
height: 48rpx;
width: 48rpx;
}
.add-cart-btn .cart-icon{
margin-left: 40rpx;
height: 32rpx;
width: 32rpx;
}
/*价格和名称*/
.basic-info-box{
padding: 15rpx 0;
color: #454552;
text-align: center;
}
.basic-info-box>view{
margin: 20rpx auto;
}
.basic-info-box .stock{
font-size: 24rpx;
}
.basic-info-box .stock.no{
color:#B42F2D;
}
.basic-info-box .name{
font-size: 40rpx;
}
.basic-info-box .price{
font-size: 38rpx;
}
/*商品详情*/
.detail-bottom-box{
margin-top: 30rpx;
}
.tabs-box{
height: 90rpx;
display: flex;
justify-content: space-between;
margin-bottom: 15rpx;
}
.tabs-item{
width: 33.3%;
color: #C7C7CB;
font-size: 28rpx;
display: flex;
align-items: center;
justify-content: center;
display: flex;
border-bottom: 1rpx solid #D0D0D7;
}
.tabs-item.selected{
/*color: #AB956D;*/
color: rgba(171,149,109,.8);
/*border-bottom: 2px solid #AB956D;*/
border-bottom: 2px solid rgba(171,149,109,.8);
}
.product-detail-box{
padding-bottom: 20rpx;
}
.product-detail-imgs image{
width: 100%;
height:400rpx;
float: left;
}
.product-detail-properties,.product-detail-protect{
min-height: 80vh;
}
.properties-item{
display: flex;
margin: 25rpx 0;
font-size:24rpx;
}
.properties-name{
width: 160rpx;
color:#808080;
text-align: center;
}
.properties-detail{
flex:1;
color: #333;
padding-right: 40rpx;
}
.product-detail-protect view{
font-size: 24rpx;
color:808080;
text-align: center;
margin-top: 30rpx;
}
product.js
// var productObj = require('product-model.js');
import {Product} from 'product-model.js';
import {Cart} from '../cart/cart-model.js';
var product=new Product(); //实例化 商品详情 对象
var cart=new Cart();
Page({
data: {
loadingHidden:false,
hiddenSmallImg:true,
countsArray:[1,2,3,4,5,6,7,8,9,10],
productCounts:1,
currentTabsIndex:0,
cartTotalCounts:0,
},
onLoad: function (option) {
var id = option.id;
this.data.id=id;
this._loadData();
},
/*加载所有数据*/
_loadData:function(callback){
var that = this;
product.getDetailInfo(this.data.id,(data)=>{
that.setData({
cartTotalCounts:cart.getCartTotalCounts().counts1,
product:data,
loadingHidden:true
});
callback&& callback();
});
},
//选择购买数目
bindPickerChange: function(e) {
this.setData({
productCounts: this.data.countsArray[e.detail.value],
})
},
//切换详情面板
onTabsItemTap:function(event){
var index=product.getDataSet(event,'index');
this.setData({
currentTabsIndex:index
});
},
/*添加到购物车*/
onAddingToCartTap:function(events){
//防止快速点击
if(this.data.isFly){
return;
}
this._flyToCartEffect(events);
this.addToCart();
},
/*将商品数据添加到内存中*/
addToCart:function(){
var tempObj={},keys=['id','name','main_img_url','price'];
for(var key in this.data.product){
if(keys.indexOf(key)>=0){
tempObj[key]=this.data.product[key];
}
}
cart.add(tempObj,this.data.productCounts);
},
/*加入购物车动效*/
_flyToCartEffect:function(events){
//获得当前点击的位置,距离可视区域左上角
var touches=events.touches[0];
var diff={
x:'25px',
y:25-touches.clientY+'px'
},
style='display: block;-webkit-transform:translate('+diff.x+','+diff.y+') rotate(350deg) scale(0)'; //移动距离
this.setData({
isFly:true,
translateStyle:style
});
var that=this;
setTimeout(()=>{
that.setData({
isFly:false,
translateStyle:'-webkit-transform: none;', //恢复到最初状态
isShake:true,
});
setTimeout(()=>{
//页面数量的动态绑定!!!!初始化的数据加上用户点击时的数据相加
var counts=that.data.cartTotalCounts+that.data.productCounts;
that.setData({
isShake:false,
cartTotalCounts:counts
});
},200);
},1000);
},
/*跳转到购物车*/
onCartTap:function(){
wx.switchTab({
url: '/pages/cart/cart'
});
},
/*下拉刷新页面*/
onPullDownRefresh: function(){
this._loadData(()=>{
wx.stopPullDownRefresh()
});
},
//分享效果
onShareAppMessage: function () {
return {
title: '零食商贩 Pretty Vendor',
path: 'pages/product/product?id=' + this.data.id
}
}
})
分类页面wxml
<import src="../tpls/category/category-tpl.wxml"/>
<view class="container category-container">
<view class="category-box">
<view class="left-box">
<block wx:for="{{categoryTypeArr}}">
<view class="menu-item {{currentMenuIndex==index?'selected':''}}" data-id="{{item.id}}" data-index="{{index}}" bindtap="changeCategory" data-title-name="{{item.name}}">
{{item.name}}
</view>
</block>
</view>
<view class="right-box {{transClassArr[currentMenuIndex]}}">
<view class="foods-type-box">
<template is="categorydetail" data="{{categoryInfo:categoryInfo0}}"/>
</view>
<view class="foods-type-box">
<template is="categorydetail" data="{{categoryInfo:categoryInfo1}}"/>
</view>
<view class="foods-type-box">
<template is="categorydetail" data="{{categoryInfo:categoryInfo2}}"/>
</view>
<view class="foods-type-box">
<template is="categorydetail" data="{{categoryInfo:categoryInfo3}}"/>
</view>
<view class="foods-type-box">
<template is="categorydetail" data="{{categoryInfo:categoryInfo4}}"/>
</view>
<view class="foods-type-box">
<template is="categorydetail" data="{{categoryInfo:categoryInfo5}}"/>
</view>
</view>
</view>
<loading hidden="{{loadingHidden}}">
加载中...
</loading>
</view>
分类页面js
import { Category } from 'category-model.js';
var category=new Category(); //实例化 home 的推荐页面
Page({
data: {
transClassArr:['tanslate0','tanslate1','tanslate2','tanslate3','tanslate4','tanslate5'],
currentMenuIndex:0,
loadingHidden:false,
},
onLoad: function () {
this._loadData();
},
/*加载所有数据*/
_loadData:function(callback){
var that = this;
category.getCategoryType((categoryData)=>{
that.setData({
categoryTypeArr: categoryData,
loadingHidden: true
});
that.getProductsByCategory(categoryData[0].id,(data)=>{
var dataObj= {
procucts: data,
topImgUrl: categoryData[0].img.url,
title: categoryData[0].name
};
that.setData({
loadingHidden: true,
categoryInfo0:dataObj
});
callback&& callback();
});
});
},
/*切换分类*/
changeCategory:function(event){
var index=category.getDataSet(event,'index'),
id=category.getDataSet(event,'id')//获取data-set
this.setData({
currentMenuIndex:index
});
//如果数据是第一次请求
if(!this.isLoadedData(index)) {
var that=this;
this.getProductsByCategory(id, (data)=> {
that.setData(that.getDataObjForBind(index,data));
});
}
},
isLoadedData:function(index){
if(this.data['categoryInfo'+index]){
return true;
}
return false;
},
getDataObjForBind:function(index,data){
var obj={},
arr=[0,1,2,3,4,5],
baseData=this.data.categoryTypeArr[index];
for(var item in arr){
if(item==arr[index]) {
obj['categoryInfo' + item]={
procucts:data,
topImgUrl:baseData.img.url,
title:baseData.name
};
return obj;
}
}
},
getProductsByCategory:function(id,callback){
category.getProductsByCategory(id,(data)=> {
callback&&callback(data);
});
},
/*跳转到商品详情*/
onProductsItemTap: function (event) {
var id = category.getDataSet(event, 'id');
wx.navigateTo({
url: '../product/product?id=' + id
})
},
/*下拉刷新页面*/
onPullDownRefresh: function(){
this._loadData(()=>{
wx.stopPullDownRefresh()
});
},
//分享效果
onShareAppMessage: function () {
return {
title: '零食商贩 Pretty Vendor',
path: 'pages/category/category'
}
}
})
分类页面css
@import "../tpls/category/category-tpl.wxss";
.category-container{
/*min-height: 100vh;*/
}
.category-box{
display: flex;
height: 100vh;
overflow: hidden;
}
.left-box{
flex: 0 0 150rpx;
border-right:1rpx solid #D8D8D8;
}
.menu-item{
height: 50rpx;
padding: 20rpx 0;
display: flex;
align-items: center;
justify-content: center;
font-size:28rpx;
border-left: 6rpx solid #fff;
}
.menu-item.selected{
color: #AB956D;
border-left-color:#AB956D;
}
.right-box{
flex: 1;
transition: all 500ms ease-in-out;
}
.tanslate0{
transform:translate(0,0);
}
.tanslate1{
transform:translate(0,-100vh);
}
.tanslate2{
transform:translate(0,-200vh);
}
.tanslate3{
transform:translate(0,-300vh);
}
.tanslate4{
transform:translate(0,-400vh);
}
.tanslate5{
transform:translate(0,-500vh);
}
.foods-type-box{
height: 100vh;
}
购物车模块,数据存到本地,当用户选中某些商品下单购买时,会从缓存中删除该数据,更新缓存,当用用户全部购买时,直接删除整个缓存,首先就是添加到购物车的方法,如果之前没有样的商品,则直接添加一条新的记录, 数量为 counts,如果有,则只将相应数量 + counts,有两个小技巧,第一个检测购物车里是否有该商品,第二个是通过对象的KEY拿到自己想要的数据格式。
检测购物车里是否有该商品
/*购物车中是否已经存在该商品*/
_isHasThatOne(id,arr){
var item,
result={index:-1};
for(let i=0;i<arr.length;i++){
item=arr[i];
if(item.id==id) {
result = {
index:i,
data:item
};
break;
}
}
return result;
}
通过对象的KEY拿到自己想要的数据格式
addToCart:function(){
var tempObj={},keys=['id','name','main_img_url','price'];
for(var key in this.data.product){
if(keys.indexOf(key)>=0){
tempObj[key]=this.data.product[key];
}
}
cart.add(tempObj,this.data.productCounts);
},
防止浮点数运算出现误差
/*
* 计算总金额和选择的商品总数
* */
_calcTotalAccountAndCounts:function(data){
var len=data.length,
account=0,
selectedCounts=0,
selectedTypeCounts=0;
let multiple=100;
for(let i=0;i<len;i++){
//避免 0.05 + 0.01 = 0.060 000 000 000 000 005 的问题,乘以 100 *100
if(data[i].selectStatus) {
account += data[i].counts * multiple * Number(data[i].price)*multiple;
selectedCounts+=data[i].counts;
selectedTypeCounts++;
}
}
return{
selectedCounts:selectedCounts,
selectedTypeCounts:selectedTypeCounts,
account:account/(multiple*multiple)
}
},
购物车页面cart-model.js
顺势掌握小程序缓存数据的方法
/**
* Created by jimmy on 17/03/05.
*/
import {Base} from '../../utils/base.js';
/*
* 购物车数据存放在本地,
* 当用户选中某些商品下单购买时,会从缓存中删除该数据,更新缓存
* 当用用户全部购买时,直接删除整个缓存
*
*/
class Cart extends Base{
constructor(){
super();
this._storageKeyName='cart';
};
/*
* 获取购物车
* param
* flag - {bool} 是否过滤掉不下单的商品
*/
getCartDataFromLocal(flag){
var res = wx.getStorageSync(this._storageKeyName);
if(!res){
res=[];
}
//在下单的时候过滤不下单的商品,
if(flag){
var newRes=[];
for(let i=0;i<res.length;i++){
if(res[i].selectStatus){
newRes.push(res[i]);
}
}
res=newRes;
}
return res;
};
/*
*获得购物车商品总数目,包括分类和不分类
* param:
* flag - {bool} 是否区分选中和不选中
* return
* counts1 - {int} 不分类
* counts2 -{int} 分类
*/
getCartTotalCounts(flag){
var data=this.getCartDataFromLocal(),
counts1=0,
counts2=0;
for(let i=0;i<data.length;i++){
if (flag){
if(data[i].selectStatus) {
counts1 += data[i].counts;
counts2++;
}
}else{
counts1 += data[i].counts;
counts2++;
}
}
return {
counts1:counts1,
counts2:counts2
};
};
/*本地缓存 保存/更新*/
execSetStorageSync(data){
wx.setStorageSync(this._storageKeyName,data);
};
/*
* 加入到购物车
* 如果之前没有样的商品,则直接添加一条新的记录, 数量为 counts
* 如果有,则只将相应数量 + counts
* @params:
* item - {obj} 商品对象,
* counts - {int} 商品数目,
* */
add(item,counts){
var cartData=this.getCartDataFromLocal();
if(!cartData){
cartData=[];
}
var isHadInfo=this._isHasThatOne(item.id,cartData);
//新商品
if(isHadInfo.index==-1) {
item.counts=counts;
item.selectStatus=true; //默认在购物车中为选中状态
cartData.push(item);
}
//已有商品
else{
cartData[isHadInfo.index].counts+=counts;
}
this.execSetStorageSync(cartData); //更新本地缓存
return cartData;
};
/*
* 修改商品数目
* params:
* id - {int} 商品id
* counts -{int} 数目
* */
_changeCounts(id,counts){
var cartData=this.getCartDataFromLocal(),
hasInfo=this._isHasThatOne(id,cartData);
if(hasInfo.index!=-1){
if(hasInfo.data.counts>1){
cartData[hasInfo.index].counts+=counts;
}
}
this.execSetStorageSync(cartData); //更新本地缓存
};
/*
* 增加商品数目
* */
addCounts(id){
this._changeCounts(id,1);
};
/*
* 购物车减
* */
cutCounts(id){
this._changeCounts(id,-1);
};
/*购物车中是否已经存在该商品*/
_isHasThatOne(id,arr){
var item,
result={index:-1};
for(let i=0;i<arr.length;i++){
item=arr[i];
if(item.id==id) {
result = {
index:i,
data:item
};
break;
}
}
return result;
}
/*
* 删除某些商品
*/
delete(ids){
if(!(ids instanceof Array)){
ids=[ids];
}
var cartData=this.getCartDataFromLocal();
for(let i=0;i<ids.length;i++) {
var hasInfo = this._isHasThatOne(ids[i], cartData);
if (hasInfo.index != -1) {
cartData.splice(hasInfo.index, 1); //删除数组某一项
}
}
this.execSetStorageSync(cartData);
}
}
export {Cart};
cart.js
// var CartObj = require('cart-model.js');
import {Cart} from 'cart-model.js';
var cart=new Cart(); //实例化 购物车
var x1=0;
var x2=0;
Page({
data: {
loadingHidden:false,
selectedCounts:0, //总的商品数
selectedTypeCounts:0, //总的商品类型数
},
onLoad: function () {
},
/*
* 页面重新渲染,包括第一次,和onload方法没有直接关系
*/
onShow:function(){
var cartData=cart.getCartDataFromLocal(),
countsInfo=cart.getCartTotalCounts(true);
this.setData({
selectedCounts:countsInfo.counts1,
selectedTypeCounts:countsInfo.counts2,
account:this._calcTotalAccountAndCounts(cartData).account,
loadingHidden:true,
cartData:cartData
});
},
/*离开页面时,更新本地缓存*/
onHide:function(){
cart.execSetStorageSync(this.data.cartData);
},
/*更新购物车商品数据*/
_resetCartData:function(){
var newData = this._calcTotalAccountAndCounts(this.data.cartData); /*重新计算总金额和商品总数*/
this.setData({
account: newData.account,
selectedCounts:newData.selectedCounts,
selectedTypeCounts:newData.selectedTypeCounts,
cartData:this.data.cartData
});
},
/*
* 计算总金额和选择的商品总数
* */
_calcTotalAccountAndCounts:function(data){
var len=data.length,
account=0,
selectedCounts=0,
selectedTypeCounts=0;
let multiple=100;
for(let i=0;i<len;i++){
//避免 0.05 + 0.01 = 0.060 000 000 000 000 005 的问题,乘以 100 *100
if(data[i].selectStatus) {
account += data[i].counts * multiple * Number(data[i].price)*multiple;
selectedCounts+=data[i].counts;
selectedTypeCounts++;
}
}
return{
selectedCounts:selectedCounts,
selectedTypeCounts:selectedTypeCounts,
account:account/(multiple*multiple)
}
},
/*调整商品数目*/
changeCounts:function(event){
var id=cart.getDataSet(event,'id'),
type=cart.getDataSet(event,'type'),
index=this._getProductIndexById(id),
counts=1;
if(type=='add') {
cart.addCounts(id);
}else{
counts=-1;
cart.cutCounts(id);
}
//更新商品页面
this.data.cartData[index].counts+=counts;
this._resetCartData();
},
/*根据商品id得到 商品所在下标*/
_getProductIndexById:function(id){
var data=this.data.cartData,
len=data.length;
for(let i=0;i<len;i++){
if(data[i].id==id){
return i;
}
}
},
/*删除商品*/
delete:function(event){
var id=cart.getDataSet(event,'id'),
index=this._getProductIndexById(id);
this.data.cartData.splice(index,1);//删除某一项商品
this._resetCartData();
//this.toggleSelectAll();
cart.delete(id); //内存中删除该商品
},
/*选择商品*/
toggleSelect:function(event){
var id=cart.getDataSet(event,'id'),
status=cart.getDataSet(event,'status'),
index=this._getProductIndexById(id);
this.data.cartData[index].selectStatus=!status;
this._resetCartData();
},
/*全选*/
toggleSelectAll:function(event){
var status=cart.getDataSet(event,'status')=='true';
var data=this.data.cartData,
len=data.length;
for(let i=0;i<len;i++) {
data[i].selectStatus=!status;
}
this._resetCartData();
},
/*提交订单*/
submitOrder:function(){
wx.navigateTo({
url:'../order/order?account='+this.data.account+'&from=cart'
});
},
/*查看商品详情*/
onProductsItemTap:function(event){
var id = cart.getDataSet(event, 'id');
wx.navigateTo({
url: '../product/product?id=' + id
})
}
})
cart.json
{
"navigationBarTitleText": "购物车",
"enablePullDownRefresh":false
}
cart.wxml
<view class="container cart-container">
<block wx:if="{{cartData.length>0}}">
<view class="cart-box">
<block wx:for="{{cartData}}">
<view class="cart-item {{deleteFlag&&index==currentIndex?'showDeleteBtn':'hideDeleteBtn'}}">
<view class="cart-item-main" data-id="{{item.id}}" data-index="{{index}}">
<view class="cart-item-checkbox" ontap="toggleSelect" data-id="{{item.id}}" data-status="{{item.selectStatus}}">
<image wx:if="{{item.selectStatus}}" src="../../imgs/icon/circle@selected.png"></image>
<image wx:else src="../../imgs/icon/circle@noselected.png"></image>
</view>
<view class="cart-item-img" bindtap="onProductsItemTap" data-id="{{item.id}}">
<image class="good-image" src="{{item.main_img_url}}"></image>
</view>
<view class="cart-item-word">
<view class="title-box">
<text class="title">{{item.name}}</text>
<text>¥{{item.price}}</text>
</view>
<view class="bottom-box">
<view class="cart-item-counts">
<view class="btns {{item.counts==1?'disabled':''}}" bindtap="changeCounts" data-id="{{item.id}}" data-type="cut">-</view>
<view class="counts">{{item.counts}}</view>
<view class="btns" bindtap="changeCounts" data-id="{{item.id}}" data-type="add">+</view>
</view>
<view class="delete" data-id="{{item.id}}" bindtap="delete">×</view>
</view>
</view>
</view>
</view>
</block>
</view>
<view class="footer-account-box all-accounts-box">
<view class="all-select" ontap="toggleSelectAll" data-status="{{selectedTypeCounts==cartData.length?'true':'false'}}">
<image wx:if="{{selectedTypeCounts==cartData.length}}"
class="title-icon" src="../../imgs/icon/all@selected.png"></image>
<image wx:else class="title-icon" src="../../imgs/icon/all.png"></image>
<text>全选({{selectedCounts}})</text>
</view>
<view class="all-price-submit {{account==0?'disabled':''}}" bindtap="submitOrder">
<view class="accounts-btn">下单</view>
<view class="price-text">¥{{account}}</view>
<view class="arrow-icon">
<image wx:if="{{account==0}}" src="../../imgs/icon/arrow@grey.png"></image>
<image wx:else src="../../imgs/icon/arrow.png"></image>
</view>
</view>
</view>
</block>
<view wx:else class="no-data">
您还没有添加任何商品
</view>
<loading hidden="{{loadingHidden}}">
加载中...
</loading>
</view>
真实的项目中,购物车这样的数据是不会放在缓存里的,本课程通过购物车运用小程序的本地缓存能力,以及技巧
删除,支持批量删除。传入一个数组,通过遍历方法依次删除
结合生命周期onhide方法更新本地缓存,以及如何过滤掉不下单的商品,cart.model.js展示了前端和数据库的交互,只暴露方法,方法的详细实现过程不暴露
根据小程序的特性,学习课程调整商品数量的方法,一个方法实现两种操作
再次强调this的指向,在回调函数this的指向发生变化只有使用箭头函数才能保证this的指向。
此课程对于订单模块比较精简,与实际开发过程不一致,仅供参考。
order-model.js
/**
* Created by jimmy on 17/03/09.
*/
import {Base} from '../../utils/base.js'
class Order extends Base{
constructor(){
super();
this._storageKeyName='newOrder';
}
/*下订单*/
doOrder(param,callback){
var that=this;
var allParams = {
url: 'order',
type:'post',
data:{products:param},
sCallback: function (data) {
that.execSetStorageSync(true);
callback && callback(data);
},
eCallback:function(){
}
};
this.request(allParams);
}
/*
* 拉起微信支付
* params:
* norderNumber - {int} 订单id
* return:
* callback - {obj} 回调方法 ,返回参数 可能值 0:商品缺货等原因导致订单不能支付; 1: 支付失败或者支付取消; 2:支付成功;
* */
execPay(orderNumber,callback){
var allParams = {
url: 'pay/pre_order',
type:'post',
data:{id:orderNumber},
sCallback: function (data) {
var timeStamp= data.timeStamp;
if(timeStamp) { //可以支付
wx.requestPayment({
'timeStamp': timeStamp.toString(),
'nonceStr': data.nonceStr,
'package': data.package,
'signType': data.signType,
'paySign': data.paySign,
success: function () {
callback && callback(2);
},
fail: function () {
callback && callback(1);
}
});
}else{
callback && callback(0);
}
}
};
this.request(allParams);
}
/*获得所有订单,pageIndex 从1开始*/
getOrders(pageIndex,callback){
var allParams = {
url: 'order/by_user',
data:{page:pageIndex},
type:'get',
sCallback: function (data) {
callback && callback(data); //1 未支付 2,已支付 3,已发货,4已支付,但库存不足
}
};
this.request(allParams);
}
/*获得订单的具体内容*/
getOrderInfoById(id,callback){
var that=this;
var allParams = {
url: 'order/'+id,
sCallback: function (data) {
callback &&callback(data);
},
eCallback:function(){
}
};
this.request(allParams);
}
/*本地缓存 保存/更新*/
execSetStorageSync(data){
wx.setStorageSync(this._storageKeyName,data);
};
/*是否有新的订单*/
hasNewOrder(){
var flag = wx.getStorageSync(this._storageKeyName);
return flag==true;
}
}
export {Order};
order.js
import {Order} from '../order/order-model.js';
import {Cart} from '../cart/cart-model.js';
import {Address} from '../../utils/address.js';
var order=new Order();
var cart=new Cart();
var address=new Address();
Page({
data: {
fromCartFlag:true,
addressInfo:null
},
/*
* 订单数据来源包括两个:
* 1.购物车下单
* 2.旧的订单
* */
onLoad: function (options) {
var flag=options.from=='cart',
that=this;
this.data.fromCartFlag=flag;
this.data.account=options.account;
//来自于购物车
if(flag) {
this.setData({
productsArr: cart.getCartDataFromLocal(true),
account:options.account,
orderStatus:0
});
/*显示收获地址*/
address.getAddress((res)=> {
that._bindAddressInfo(res);
});
}
//旧订单
else{
this.data.id=options.id;
}
},
onShow:function(){
if(this.data.id) {
var that = this;
//下单后,支付成功或者失败后,点左上角返回时能够更新订单状态 所以放在onshow中
var id = this.data.id;
order.getOrderInfoById(id, (data)=> {
that.setData({
orderStatus: data.status,
productsArr: data.snap_items,
account: data.total_price,
basicInfo: {
orderTime: data.create_time,
orderNo: data.order_no
},
});
// 快照地址
var addressInfo=data.snap_address;
addressInfo.totalDetail = address.setAddressInfo(addressInfo);
that._bindAddressInfo(addressInfo);
});
}
},
/*修改或者添加地址信息*/
editAddress:function(){
var that=this;
wx.chooseAddress({
success: function (res) {
var addressInfo = {
name:res.userName,
mobile:res.telNumber,
totalDetail:address.setAddressInfo(res)
};
that._bindAddressInfo(addressInfo);
//保存地址
address.submitAddress(res,(flag)=>{
if(!flag) {
that.showTips('操作提示','地址信息更新失败!');
}
});
}
})
},
/*绑定地址信息*/
_bindAddressInfo:function(addressInfo){
this.setData({
addressInfo: addressInfo
});
},
/*下单和付款*/
pay:function(){
if(!this.data.addressInfo){
this.showTips('下单提示','请填写您的收货地址');
return;
}
if(this.data.orderStatus==0){
this._firstTimePay();
}else{
this._oneMoresTimePay();
}
},
/*第一次支付*/
_firstTimePay:function(){
var orderInfo=[],
procuctInfo=this.data.productsArr,
order=new Order();
for(let i=0;i<procuctInfo.length;i++){
orderInfo.push({
product_id:procuctInfo[i].id,
count:procuctInfo[i].counts
});
}
var that=this;
//支付分两步,第一步是生成订单号,然后根据订单号支付
order.doOrder(orderInfo,(data)=>{
//订单生成成功
if(data.pass) {
//更新订单状态
var id=data.order_id;
that.data.id=id;
that.data.fromCartFlag=false;
//开始支付
that._execPay(id);
}else{
that._orderFail(data); // 下单失败
}
});
},
/*
* 提示窗口
* params:
* title - {string}标题
* content - {string}内容
* flag - {bool}是否跳转到 "我的页面"
*/
showTips:function(title,content,flag){
wx.showModal({
title: title,
content: content,
showCancel:false,
success: function(res) {
if(flag) {
wx.switchTab({
url: '/pages/my/my'
});
}
}
});
},
/*
*下单失败
* params:
* data - {obj} 订单结果信息
* */
_orderFail:function(data){
var nameArr=[],
name='',
str='',
pArr=data.pStatusArray;
for(let i=0;i<pArr.length;i++){
if(!pArr[i].haveStock){
name=pArr[i].name;
if(name.length>15){
name = name.substr(0,12)+'...';
}
nameArr.push(name);
if(nameArr.length>=2){
break;
}
}
}
str+=nameArr.join('、');
if(nameArr.length>2){
str+=' 等';
}
str+=' 缺货';
wx.showModal({
title: '下单失败',
content: str,
showCancel:false,
success: function(res) {
}
});
},
/* 再次次支付*/
_oneMoresTimePay:function(){
this._execPay(this.data.id);
},
/*
*开始支付
* params:
* id - {int}订单id
*/
_execPay:function(id){
if(!order.onPay) {
this.showTips('支付提示','本产品仅用于演示,支付系统已屏蔽',true);//屏蔽支付,提示
this.deleteProducts(); //将已经下单的商品从购物车删除
return;
}
var that=this;
order.execPay(id,(statusCode)=>{
if(statusCode!=0){
that.deleteProducts(); //将已经下单的商品从购物车删除 当状态为0时,表示
var flag = statusCode == 2;
wx.navigateTo({
url: '../pay-result/pay-result?id=' + id + '&flag=' + flag + '&from=order'
});
}
});
},
//将已经下单的商品从购物车删除
deleteProducts:function() {
var ids=[],arr=this.data.productsArr;
for(let i=0;i<arr.length;i++){
ids.push(arr[i].id);
}
cart.delete(ids);
},
}
)
order.json
{
"navigationBarTitleText": "订单详情",
"enablePullDownRefresh":false
}
order.wxml
<!--订单详情-->
<view class="container order-container">
<!--订单编号和下单时间,如果是旧订单就显示-->
<view class="order-basic-info" wx:if="{{basicInfo}}">
<view class="order-time-no">
<view>
<text class="key">下单时间:</text>
<text class="val">{{basicInfo.orderTime}}</text>
</view>
<view>
<text class="key">订单编号:</text>
<text class="order-no-txt val">{{basicInfo.orderNo}}</text>
</view>
</view>
<view class="order-status">
<text class="order-status-txt unpay" wx:if="{{orderStatus==1}}">待付款</text>
<text class="order-status-txt payed" wx:if="{{orderStatus==2}}">已付款</text>
<text class="order-status-txt done" wx:if="{{orderStatus==3}}">已发货</text>
</view>
</view>
<!--地址-->
<view class="order-address-info {{orderStatus!=0?'disabled':''}}" ontap="editAddress">
<block wx:if="{{addressInfo}}">
<view class="contact-box">
<view>
<view class="contact">
<view>
<image src="../../imgs/icon/user.png"></image>
<text class="val">{{addressInfo.name}}</text>
</view>
<view class="mobile-box">
<image src="../../imgs/icon/mobile.png"></image>
<text class="val">{{addressInfo.mobile}}</text>
</view>
</view>
<view class="detail">{{addressInfo.totalDetail}}</view>
</view>
<view class="contact-icon" wx:if="{{orderStatus==0}}">
<image src="../../imgs/icon/arrow@right.png"></image>
</view>
</view>
</block>
<block wx:else>
<view class="add-new-address">
<text class="icon">+</text>
<text>添加地址</text></view>
</block>
</view>
<!--列表-->
<view class="order-main">
<block wx:for="{{productsArr}}">
<view class="product-item">
<view class="item-left">
<image src="{{item.main_img_url}}"></image>
</view>
<view class="item-middle">
<view>{{item.name}}</view>
<view>¥{{item.price}}</view>
</view>
<view class="item-right">
×{{item.counts}}
</view>
</view>
</block>
</view>
<!--结算-->
<view class="footer-account-box order-accounts">
<view class="total-account">
付款合计:¥{{account}}
</view>
<view wx:if="{{orderStatus<=1}}" class="pay {{!addressInfo?'disabled':''}}" ontap="pay">去付款</view>
</view>
</view>
我的页面
涉及获取用户信息
my-model.js
/**
* Created by jimmy on 17/3/24.
*/
import {Base} from '../../utils/base.js'
class My extends Base{
constructor(){
super();
}
//得到用户信息
getUserInfo(cb){
var that=this;
wx.login({
success: function () {
wx.getUserInfo({
success: function (res) {
typeof cb == "function" && cb(res.userInfo);
//将用户昵称 提交到服务器
if(!that.onPay) {
that._updateUserInfo(res.userInfo);
}
},
fail:function(res){
typeof cb == "function" && cb({
avatarUrl:'../../imgs/icon/user@default.png',
nickName:'零食小贩'
});
}
});
},
})
}
/*更新用户信息到服务器*/
_updateUserInfo(res){
var nickName=res.nickName;
delete res.avatarUrl; //将昵称去除
delete res.nickName; //将昵称去除
var allParams = {
url: 'user/wx_info',
data:{nickname:nickName,extend:JSON.stringify(res)},
type:'post',
sCallback: function (data) {
}
};
this.request(allParams);
}
}
export {My}
my.js
订单列表分页,特别注意分页数据合并方法
import {Address} from '../../utils/address.js';
import {Order} from '../order/order-model.js';
import {My} from '../my/my-model.js';
var address=new Address();
var order=new Order();
var my=new My();
Page({
data: {
pageIndex:1,
isLoadedAll:false,
loadingHidden:false,
orderArr:[],
addressInfo:null
},
onLoad:function(){
this._loadData();
this._getAddressInfo();
},
onShow:function(){
//更新订单,相当自动下拉刷新,只有 非第一次打开 “我的”页面,且有新的订单时 才调用。
var newOrderFlag=order.hasNewOrder();
if(this.data.loadingHidden &&newOrderFlag){
this.onPullDownRefresh();
}
},
_loadData:function(){
var that=this;
my.getUserInfo((data)=>{
that.setData({
userInfo:data
});
});
this._getOrders();
order.execSetStorageSync(false); //更新标志位
},
/**地址信息**/
_getAddressInfo:function(){
var that=this;
address.getAddress((addressInfo)=>{
that._bindAddressInfo(addressInfo);
});
},
/*修改或者添加地址信息*/
editAddress:function(){
var that=this;
wx.chooseAddress({
success: function (res) {
var addressInfo = {
name:res.userName,
mobile:res.telNumber,
totalDetail:address.setAddressInfo(res)
};
if(res.telNumber) {
that._bindAddressInfo(addressInfo);
//保存地址
address.submitAddress(res, (flag)=> {
if (!flag) {
that.showTips('操作提示', '地址信息更新失败!');
}
});
}
//模拟器上使用
else{
that.showTips('操作提示', '地址信息更新失败,手机号码信息为空!');
}
}
})
},
/*绑定地址信息*/
_bindAddressInfo:function(addressInfo){
this.setData({
addressInfo: addressInfo
});
},
/*订单信息*/
_getOrders:function(callback){
var that=this;
order.getOrders(this.data.pageIndex,(res)=>{
var data=res.data;
that.setData({
loadingHidden: true
});
if(data.length>0) {
that.data.orderArr.push.apply(that.data.orderArr,res.data); //数组合并
that.setData({
orderArr: that.data.orderArr
});
}else{
that.data.isLoadedAll=true; //已经全部加载完毕
that.data.pageIndex=1;
}
callback && callback();
});
},
/*显示订单的具体信息*/
showOrderDetailInfo:function(event){
var id=order.getDataSet(event,'id');
wx.navigateTo({
url:'../order/order?from=order&id='+id
});
},
/*未支付订单再次支付*/
rePay:function(event){
var id=order.getDataSet(event,'id'),
index=order.getDataSet(event,'index');
//online 上线实例,屏蔽支付功能
if(order.onPay) {
this._execPay(id,index);
}else {
this.showTips('支付提示','本产品仅用于演示,支付系统已屏蔽');
}
},
/*支付*/
_execPay:function(id,index){
var that=this;
order.execPay(id,(statusCode)=>{
if(statusCode>0){
var flag=statusCode==2;
//更新订单显示状态
if(flag){
that.data.orderArr[index].status=2;
that.setData({
orderArr: that.data.orderArr
});
}
//跳转到 成功页面
wx.navigateTo({
url: '../pay-result/pay-result?id='+id+'&flag='+flag+'&from=my'
});
}else{
that.showTips('支付失败','商品已下架或库存不足');
}
});
},
/*下拉刷新页面*/
onPullDownRefresh: function(){
var that=this;
this.data.orderArr=[]; //订单初始化
this._getOrders(()=>{
that.data.isLoadedAll=false; //是否加载完全
that.data.pageIndex=1;
wx.stopPullDownRefresh();
order.execSetStorageSync(false); //更新标志位
});
},
onReachBottom:function(){
if(!this.data.isLoadedAll) {
this.data.pageIndex++;
this._getOrders();
}
},
/*
* 提示窗口
* params:
* title - {string}标题
* content - {string}内容
* flag - {bool}是否跳转到 "我的页面"
*/
showTips:function(title,content){
wx.showModal({
title: title,
content: content,
showCancel:false,
success: function(res) {
}
});
},
})
my.wxml
<view class="container my-container" hidden="{{!loadingHidden}}">
<view class="my-header">
<image src="{{userInfo.avatarUrl}}"></image>
<text class="name">{{userInfo.nickName}}</text>
</view>
<!--地址管理-->
<view class="my-address">
<block wx:if="{{addressInfo}}">
<view class="item-title" ontap="editAddress">
地址管理
<image src="../../imgs/icon/arrow@right.png"></image>
</view>
<view class="item-main">
<view class="section">
<input disabled name="name" placeholder="姓名" value="{{addressInfo.name}}" />
</view>
<view class="section">
<input disabled type="number" name="mobile" placeholder="手机号码" value="{{addressInfo.mobile}}"/>
</view>
<view class="section">
<input disabled name="detail" placeholder="收货地址" value="{{addressInfo.totalDetail}}"/>
</view>
</view>
</block>
<block wx:else>
<view class="add-new-address" ontap="editAddress"><text class="icon">+</text><text>添加地址</text></view>
</block>
</view>
<view class="my-order">
<view class="item-title">我的订单</view>
<view class="item-main">
<block wx:for="{{orderArr}}">
<view class="order-item">
<view class="order-header" ontap="showOrderDetailInfo" data-id="{{item.id}}">
<text>订单编号:</text>
<text class="order-no-txt">{{item.order_no}}</text>
</view>
<view class="order-main" ontap="showOrderDetailInfo" data-id="{{item.id}}">
<view class="item-left">
<image src="{{item.snap_img}}"></image>
</view>
<view class="item-middle">
<view>{{item.snap_name}}</view>
<view>{{item.total_count}}件商品</view>
</view>
<view class="item-right">
<text class="order-status-txt unpay" wx:if="{{item.status==1}}">待付款</text>
<text class="order-status-txt payed" wx:if="{{item.status==2}}">已付款</text>
<text class="order-status-txt done" wx:if="{{item.status==3}}">已发货</text>
</view>
</view>
<view class="order-bottom" wx:if="{{item.status==1}}">
<text>实付:¥{{item.total_price}}</text>
<view class="pay" ontap="rePay" data-id="{{item.id}}" data-index="{{index}}">付款</view>
</view>
</view>
</block>
</view>
</view>
</view>
<loading hidden="{{loadingHidden}}">
加载中...
</loading>
注意这一块回调函数的应用
下拉刷新的时候,要请求接口初始化数据,这一块用到了回调函数,即初始化了数据也更改了页面的显示状态
/*下拉刷新页面*/
onPullDownRefresh: function(){
var that=this;
this.data.orderArr=[]; //订单初始化
this._getOrders(()=>{
that.data.isLoadedAll=false; //是否加载完全
that.data.pageIndex=1;
wx.stopPullDownRefresh();
order.execSetStorageSync(false); //更新标志位
});
},