问题:
小程序里如果通过条件渲染不起作用,可以尝试通过双重的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 -> 页面卸载执行一次
- 这里要注意是先执行的onShow页面显示,然后等到页面视图渲染完成后才执行onReady
- 页面卸载钩子触发可以通过页面重定向(会先关闭当前页面,然后进行跳转)
<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:跳转类型
- 默认:navigate -> 保留当前页面,跳转到新页面(左上方会有返回按钮)
- redirect -> 销毁当前页面,跳转新页面,不能再返回当前页(会触发onUnload)
- navigateBack -> 返回上一级页面(与navigate配合使用,就和直接在左上方点返回是一样的)
- 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种裁剪模式
- 4中缩放模式
其中scaleToFill是默认的模式
- 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
- 顶部topbar的loading
// 开启
my.showNavigationBarLoading()
// 关闭
my.hideNavigationBarLoading()
- 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实现加入购物车动画
- 首先需要在组件中使用animation属性绑定数据
<view animation="{{animationInfo}}" class="animation-img" style="opacity:{{animationOpacity}};background-image:url('{{headerImages[0]}}')"></view>
- 在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()
})
}
- 实现购物车动画复原(把设置的值都变为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, // 要缓存的数据
});
},
网友评论