美文网首页
支付宝小程序-电商平台

支付宝小程序-电商平台

作者: sweetBoy_9126 | 来源:发表于2019-05-25 15:49 被阅读0次

问题:
小程序里如果通过条件渲染不起作用,可以尝试通过双重的a:if来判断(下面只有type为1和2的情况,本来我们可以用a:else的但是不起作用)
对于input表单的双向绑定

<form onSubmit="dropOut">
    <view class="drop-warpper white">
      <view class="line">
        <label>收款人姓名</label>
        <input placeholder="请输入你的姓名" value="{{person.name}}" onBlur="modifyName"/>
      </view>
      <view class="line">
        <label>支付宝账号</label>
        <input placeholder="请输入你的支付宝账号" value="{{person.number}}" onBlur="modifyNumber" />
      </view>
    </view>
    <text class="intro">请正确填写您本人支付宝,提现发起后,24小时内即可到账</text>
    <button size="default" class="down-button {{person.name && person.number ? 'active' : ''}}" onTap="toDownload" hover-class="none">立即提现</button>
  </form>
data: {
    person: {
      name: '',
      number: ''
    }
  },
modifyName(e) {
    const {value} =e.detail
    this.setData({
      'person.name': value
    })
  },
  modifyNumber(e) {
    const {value} = e.detail
    this.setData({
      'person.number': value
    })
  },
  toDownload() {
    const {name,number} = this.data.person
    if (!name.trim()) {
      app.$toast('fail','名字不能为空')
    } else if(!number.trim()) {
      app.$toast('fail','支付宝账号不能为空')
    } else {
      my.navigateTo({
        url: `/pages/goldCenter/goldConvert/downGuide/downGuide?accountBalance=${this.data.accountBalance}`
      })
    }
  }

要点:通过给每个input监听一个onBlur失去焦点事件,来修改绑定的值,然后点击按钮的时候判定一下对应的值为不为空

<view slot="header" class="header-title" a:if="{{type === 1}}">金币福利兑换说明</view>
<view slot="header" class="header-title" a:if="{{type === 2}}">优惠券详情</view>

基本目录结构

  • acss
    可以通过page来修改当前页面的样式
page {
  background: green;
}

新建页面的时候选择新建小程序页面(相关的四个文件都会被创建)
app.json

"pages": [
    // 第一个就是首页
    "pages/index/index",
    "pages/hello/hello"
  ],

app的生命周期

onLaunch -> 项目初始化完成时触发,只触发一次
onSHow -> 页面展示的时候触发
onHide -> 页面隐藏的时候触发
onError -> 程序出现错误时触发

App({
  onLaunch(options) {
    // 第一次打开
    // options.query == {number:1}
   console.log('第一次打开')
  },
  onShow(options) {
    // 从后台被 scheme 重新打开
    console.log('页面展示')
  },
  onHide() {
    console.log('页面隐藏')
    throw new Error('此处发生错误')
  },
  onError() {
    console.log('页面报错')
  }
});

page的生命周期

onLoad -> 页面加载时执行,每个页面只执行一次
onReady -> 页面初次渲染完成,每个页面只执行一次
onShow -> 页面显示后执行一次,可执行多次
onHide -> 页面每次隐藏时执行一次
onUnload -> 页面卸载执行一次

  1. 这里要注意是先执行的onShow页面显示,然后等到页面视图渲染完成后才执行onReady
  2. 页面卸载钩子触发可以通过页面重定向(会先关闭当前页面,然后进行跳转)
<view onTap="close">关闭</view>
close() {
    my.redirectTo({
      // 这里需要注意这里的url需要写成绝对路径
      url: '/pages/index/index'
    });
  }

onReachBottom -> 在有滚动条的情况下页面滚动到底部时触发
onPullDownRefresh -> 下拉刷新的时候触发的钩子(必须在对应的页面的json里开启"pullRefresh": true)
onShareAppMessage -> 分享时触发的钩子

注意分享钩子必须返回一个对象,对象里面有三个属性,title,desc和path

onShareAppMessage() {
    console.log('分享')
    return {
      title: '你好女朋友',
      desc: '我是你未来的男朋友',
      path: 'pages/hello/hellp'
    }
  }

全局常量、对象、自定义函数的使用

在app.js里定义全局的属性、对象和方法

App({
   // 里面是你要定义的全局属性或方法
  name: 'lifa',
  age: 18,
  body: {
    leg: 120,
    eyes: 'big'
  },
  make() {
    console.log('aaa')
  }
}

在app.js中定义的只需要在每一个页面的.js中通过一个getApp()就可以获取到app的实例

const app = getApp()
Page({
  onReady() {
    console.log(app)
    console.log('当前页面渲染完成')
    console.log(app.name)
    console.log(app.age)
    console.log(app.body)
    app.make()
  },
})

设置背景色和标题

全局设置

  • app.json
"window": {
    "defaultTitle": "My App",
    "titleBarColor": "#ccc"
  }

单页面配置

{
   "defaultTitle": "one",
    "titleBarColor": "#ccc"
}

可以在目录pages里配置一个目录里面装别的文件

tabBar

textColor: 文字颜色
selectedColor: 文字选中后颜色
backgroundColor: 背景颜色
items: 每个单独tab项(数组)

items tab数组配置
pagePath:页面路径
name: 文字
icon: 未选中的图标
activeIcon: 选中后的图标

导航栏数据结构

"tabBar": {
  "textColor": "字体颜色",
  "selectedColor": "选中颜色",
  "backgroundColor": "底色",
  "items": [
      {
        "pagePath": "pages/index/index",
        "name": "首页",
        "icon": "未选中图标",
        "activeIcon": "选中后图标"
      }
    ]
}

获取和修改data要通过this.data.属性名

导航组件<navigate></navigate>

hover-class: 点击时改变的样式
hover-start-time: 按住ms后出现点击状态
hover-stay-time: 手指释放ms后保留的状态
url: 页面跳转的地址
open-type:跳转类型

  1. 默认:navigate -> 保留当前页面,跳转到新页面(左上方会有返回按钮)
  1. redirect -> 销毁当前页面,跳转新页面,不能再返回当前页(会触发onUnload)
  2. navigateBack -> 返回上一级页面(与navigate配合使用,就和直接在左上方点返回是一样的)
  3. switchTab -> 跳转到tab选项卡页面
<navigator url="/pages/index/index">
页面跳转(默认navigate)
</navigator>

<navigator url="/pages/index/index"
  open-type="redirect"
>
页面跳转(redirect)
</navigator>

<navigator url="/pages/other/hello/hello" 
open-type="switchTab"
 hover-class="other-navigator-hover">
 页面跳转到首页(switchTab)
 </navigator>

导航api

功能和navigator组件一致

 <view onTap="jump">
   导航api跳转
 </view>
Page({
  data: {},
  onLoad() {},
  jump(){
    my.navigateTo(
      {
        url: '/pages/index/index'
      }
    )
  }
});

数据绑定和条件渲染

<view a:if="{{student.age < 18}}">未成年</view>
<view a:elif="{{student.age > 18}}">成年了</view>
<view a:else>18</view>

Page({
  data: {
    student: {
      age: 18
      }
    }
})

列表渲染

默认每一个是item,索引是index,可以通过 a:for-item自定义item,和a:for-index自定义index

<view a:for="{student.courses}" a:for-item="lesson" a:for-index="i">
  下标:{{i}},
  课程名: {{lesson.name}},
  节数:{{lesson.counts}}
</view>
data: {
  student: {
    courses: [
      {
        name: 'lili',
         counts: 100
      },
      {
         name: 'lili',
         counts: 100
      }
    ]
  }
}

view和block的区别
block就相当于template不会有标签显示在页面中,而view会作为标签显示在页面中

6种不同的事件类型

tap:点击事件
longTap:

<view onTap="tap">
  点击事件
</view>
<view onLongTap="longTap">
  长按点击事件
</view>
<view onTouchStart="touchStart">
  开始触摸(点击)
</view>
<view onTouchMove="touchMove">
  触摸后移动
</view>
<view onTouchEnd="touchEnd">
  触摸结束
</view>
<view onTouchCancel="touchCancel">
  触摸中断(非正常结束,比如触摸过程中手机来电)
</view>
Page({
  data: {},
  onLoad() {},
  tap() {
    console.log('点击事件')
  },
  longTap() {
    console.log('长按点击事件')
  },
  touchStart() {
    console.log('开始触摸')
  },
  touchMove() {
    console.log('触摸后移动')
  },
  touchEnd() {
    console.log('触摸结束')
  },
  touchCancel() {
    console.log('触摸中断')
  }

});

dataset自定义数据的使用

<view data-name="lifa" onTap="getDataName">
  获取dataName
</view>
getDataName(e) {
    console.log(e)
    console.log(e.target.dataset.name) //lifa
  }

用来在列表循环的方法中拿到你对应item中的数据

<view a:for="{{lists}}" onTap="getIndex" data-index="{{index}}">
  ...
</view>
data: {
  lists: [
    {name: 'lifa', age: 18},
    {name: 'linlin', age: 18}
  ]
},
getIndex(e){
  const {index} = e.target.dataset
  console.log(index) //对应的每次点击拿到当前的索引值 
}

图片组件

lazyLoad是否懒加载,默认为false
onError图片加载错误时执行的方法
onLoad图片加载成功后触发

<image src="" class="" style="" lazyLoad="{{true}}" onError="imgError" onLoad="imgLoad"/>
imgError() {
    console.log('加载失败')
  },
  imgLoad() {
    console.log('加载成功')
  }

图片的4种缩放模式与9种裁剪模式

  1. 4中缩放模式

其中scaleToFill是默认的模式

  1. 9中裁剪模式

轮播组件:swiper
可滚动视图组件:scroll-view

<scroll-view scroll-x="{{true}}" class="scroll-items" >
    <image mode="scaleToFill" src="/resources/items/1001.png" class="shop-img"/>
    <image mode="scaleToFill" src="/resources/items/1002.png" class="shop-img"/>
    <image mode="scaleToFill" src="/resources/items/1001.png" class="shop-img"/>
    <image mode="scaleToFill" src="/resources/items/1002.png" class="shop-img"/>
    <image mode="scaleToFill" src="/resources/items/1001.png" class="shop-img"/>
    <image mode="scaleToFill" src="/resources/items/1002.png" class="shop-img"/>
    <image mode="scaleToFill" src="/resources/items/1001.png" class="shop-img"/>
    <image mode="scaleToFill" src="/resources/items/1002.png" class="shop-img"/>
  </scroll-view>
</view>
 .scroll-items {
  width: 100%;
  white-space: nowrap;
}

接口调用-轮播图

小程序的api都是my.的
一般在onReady里请求接口
全局的baseUrl可以写在app.js里

// app.js
App({
  baseUrl: 'https://www.imoocdsp.com',
})
// index.axml
<swiper indicator-dots="{{true}}">
    <block a:for="{{carousels}}">
      <swiper-item >
        <image mode="scaleToFill" src="{{item.imageUrl}}"  class="swiper-img"/>
      </swiper-item>
    </block>
</swiper>
// index.js
const app = getApp()
Page({
  data: {
    carousels: []
  },
  onReady() {
    my.request({
      url: app.baseUrl + `/index/carousels`, // 目标服务器url
      method: 'post',
      headers: {'content-type': 'application/json'},
      success: (res) => {
        this.setData({
          carousels: res.data.data
        })
      },
    });
  }
});

接口封装

// request.js
export default function request(url, method='post', data={}) {
  return new Promise((resolve, reject) => {
    let option = {
      url,
      method,
      data
    }
    my.request({
      ...option,
      success: (res) => {
          if(res.data.status === 200) {
            resolve(res.data)
          } else {
            reject(res.data)
          }
      },
      fail: () => {
        reject('失败')
      }
    })
  })
}
//api/index.js
import request from '/untils/request.js'
const url = {
  Carousels: '/index/carousels',
  RecommendProduct: '/index/items/rec',
  newList: '/index/items/new'
}
const host = 'https://www.imoocdsp.com'
for (let key in url) {
  if(url.hasOwnProperty(key)){
        url[key] = host + url[key]
    }
}
export default {
  carousels() {
    return request(url.Carousels)
  },
  recommendProduct() {
    return request(url.RecommendProduct)
  },
  newList() {
    return request(url.newList)
  }
}

对上面封装的接口进行使用

import index from '/api/index.js'
Page({
  data: {
    carousels: [],
    newItemList: [],
    recommendProduct: []
  },
  onReady() {
    index.carousels().then(
      (res => {
        this.setData({carousels: res.data})
      })
    )
    index.recommendProduct().then(
      res => {
        this.setData({recommendProduct: res.data})
      }
    )
    index.newList().then(
      res => {
        this.setData({newItemList: res.data})
      }
    )
  }
});

因为我们的onReady生命周期就和created一样只会在页面第一次加载的时候执行一遍,所以我们的数据更新可以在下拉刷新中操作
将一开始的数据获取的操作放到一个初始化方法里,然后在onReady和onPullDownRefresh中都调用,前面说过下拉刷新要在json中配置"pullRefresh": true

onPullDownRefresh () {
    this.initData()
  },
  onReady() {
    this.initData()
  },
  initData() {
    index.carousels().then(
      (res => {
        this.setData({carousels: res.data})
      })
    )
    index.recommendProduct().then(
      res => {
        this.setData({recommendProduct: res.data})
      }
    )
    index.newList().then(
      res => {
        console.log(res.data)
        this.setData({newItemList: res.data})
      }
    )
  }

navigate带参跳转到下一页面

<input type="text" placeholder="请输入搜索商品名..." auto-focus class="search-input"
                    confirm-type="search" onConfirm="searchItems" />
searchItems(e) {
    const { value } = e.detail
    if (value.trim() !== '') {
      my.navigateTo({
        // 1.将参数放到url中
        url: "/pages/query/list/list?itemName=" + value
      })
    }
  }

// 2.在跳转到的页面(list)中的onLoad钩子里获取当前传进来的参数
// - list
onLoad(params) {
  const {itemName} = params
  console.log(itemName) // 这里的itemName就是你传进来的参数
}

navigator组件带参

<navigator url="/pages/query/list/list?searchType=cat&catId=0&catName=奢侈品" open-type="navigate">
            <image src="/resources/category/0-luxury.png" class="cat-ico" />
            <view class="cat-name">奢侈品</view>
        </navigator>

显示loading

  1. 顶部topbar的loading
// 开启
my.showNavigationBarLoading()

// 关闭
my.hideNavigationBarLoading()
  1. body里的loading
// 开启
my.showLoading({
  content: '疯狂加载中...' // 自定义的loding文字
})

// 关闭
my.hideLoading()

这里开启的时候在请求最初的位置,关闭在complete方法里,因为不管成功还是失败都会关闭

export default function request(url, method='post', data={}, headers='application/json') {
  return new Promise((resolve, reject) => {
    let option = {
      url: app.baseUrl + url,
      method,
      headers: {'content-type': headers},
      data
    }
    my.showLoading({
      content: '疯狂加载中...'
    })
    my.request({
      ...option,
      dataType: 'json',
      success: (res) => {
          if(res.data.status === 200) {
            resolve(res.data)
          } else {
            reject(res.data)
          }
      },
      fail: () => {
        reject('失败')
      },
      complete: () => {
        my.hideLoading()
      }
    })
  })
}

公用模板组件的使用

新建一个template文件,在这个文件里建你需要公用的组件,然后在里面的axml中使用一个template标签,并且指定name,然后在当前模板中的acss中把公用的样式拷贝进去

<template name="itemList">
    // 这里面是公用的代码
    <view class="new-item-list">

    <view class="item-outter" a:for="{{newItemList}}">

      <image src="{{item.cover}}" class="new-item-cover" />

      <view class="item-border">
        <view class="tags" a:for="{{item.tagList}}" a:for-item="tag">{{tag}}</view>
      </view> 

      <view class="price-border">
        <view class="price">¥{{item.priceDiscountYuan}}</view>
        <view class="like-counts">
          {{item.likeCounts}}
          <image src="/resources/icon/smallIco/likes.png" class="like-ico" />
        </view>
      </view> 

    </view>


  </view>

</template>

在需要使用模板的地方通过import标签引入

<import src="/pages/template/itemList/itemList">
// 这里的is后面的就是你上面的name
//newItemList键名会作为传入模板中的变量,也就是上面对应的newItemList,也就是键名是什么你模板里就对应写什么
// list当前页面的data里的数据,将list赋给newItemList
<template is="itemList" data="{{newItemList: list}}"></tempalte>

修改内部属性

this.setData({
prize: this.data.prize+1 //这里就是通过this.data
})

动态添加一个class

// 如果sIndex+1等于index就添加active否则就为空,必须通过三元运算符判断
<view class="check-item {{index === sIndex+1 ? 'active' : ''}}"></view>

使用动画api实现加入购物车动画

  1. 首先需要在组件中使用animation属性绑定数据
<view animation="{{animationInfo}}" class="animation-img" style="opacity:{{animationOpacity}};background-image:url('{{headerImages[0]}}')"></view>
  1. 在js里导出这个动画(这里需要注意每次要修改动画实例绑定的数据都需要重新export())
onShow() {
    // 创建动画
    var animation = my.createAnimation({
      duration: 500
    })
    this.animation = animation
    this.setData({
      // 导出动画效果到页面
      animationInfo: animation.export()
    })
  },
  addToCart() {
    this.setData({
      animationOpacity: 1
    })
    this.showAddToCartAnimation()
  },
  showAddToCartAnimation() {
    // 旋转的同时在水平方向移动,动画结束必须加.step()
    this.animation.rotate(-180).translateX('296rpx').step()
    this.setData({
      animationInfo: this.animation.export()
    })
  }
  1. 实现购物车动画复原(把设置的值都变为0),每次点击玩过一段时间动画复原,再点击重新开始
    思路:点击完动画结束后给一个setTimeout,然后在几秒后把动画复原,这样下次再点击又从最开始的状态开始动画
// 复原动画
    setTimeout(() => {
      // 这里因为需要先将动画的小圆点透明度变为0,然后再还原动画,所以不能同时设置
      this.setData({
        animationOpacity: 0,
        cartIco: 'cart-full'
      })
      this.animation.rotate(0).translateX(0).step({
        duration: 0
      })
      // 再透明度变为0后再还原动画
      setTimeout(() => {
        this.setData({
          animationInfo: this.animation.export()
        })
      },550)
    },600)

完整代码

<view class="add-to-cart" onTap="addToCart">
        <!-- 定义动画组件,创建实例 -->
        <view animation="{{animationInfo}}" class="animation-img" style="opacity:{{animationOpacity}};background-image:url('{{headerImages[0]}}')"></view>
        放入购物车
    </view>
onShow() {
    // 创建动画
    var animation = my.createAnimation({
      duration: 500
    })
    this.animation = animation
    this.setData({
      // 导出动画效果到页面
      animationInfo: animation.export()
    })
  },
  addToCart() {
    this.setData({
      animationOpacity: 1
    })
    this.showAddToCartAnimation()
  },
  showAddToCartAnimation() {
    // 旋转的同时在水平方向移动
    this.animation.rotate(-180).translateX('370rpx').step()
    this.setData({
      animationInfo: this.animation.export()
    })

    // 复原动画
    setTimeout(() => {
      // 这里因为需要先将动画的小圆点透明度变为0,然后再还原动画,所以不能同时设置
      this.setData({
        animationOpacity: 0,
        cartIco: 'cart-full'
      })
      this.animation.rotate(0).translateX(0).step({
        duration: 0
      })
      // 再透明度变为0后再还原动画
      setTimeout(() => {
        this.setData({
          animationInfo: this.animation.export()
        })
      },550)
    },600)
  }

使用缓存api实现购物车商品添加

设置缓存

my.setStorageSync({
  key: 'student',
  data: {
    name: '',
    age: 18
  }
})

获取缓存

var stu = my.getStorageSync({
  key: 'student'
})
console.log(stu)

删除缓存(同步 )

my.removeStorageSync({
  key: 'student'
})

清除所有缓存

my.clearStorage()

实现购物车重复商品不再添加到数组里而是对应id下的count+1

addToCart() {
    this.setData({
      animationOpacity: 1
    })
    this.showAddToCartAnimation()
    this.cartItemIncrease()

  },
cartItemIncrease() {
    const itemId = this.data.item.id
    let cartArray = my.getStorageSync({
      key: 'cartArray', // 缓存数据的key
    }).data
    // 先判断是否有缓存的数据,如果没有就等于一个空数组
    if(!cartArray) {
      cartArray = []
    }
    // 把当前的对象赋值给这个数组
    let cartItem = {itemId,counts:1}
    cartArray.forEach((item,index) => {
      // 如果数组里面有id等于你当前的对象里的id就把当前index记录下来
      if (item.itemId === cartItem.itemId) {
        this.setData({
          index
        })
      }
    })
    // 通过index是否大于-1来判断是直接push还是当前数组里对应的id里的count+1
    if (this.data.index > -1) {
      cartArray[this.data.index].counts+=1
    } else {
      cartArray.push(cartItem)
    }
    

    my.setStorageSync({
      key: 'cartArray', // 缓存数据的key
      data: cartArray, // 要缓存的数据
    });

  },

相关文章

网友评论

      本文标题:支付宝小程序-电商平台

      本文链接:https://www.haomeiwen.com/subject/wmaazqtx.html