Appearance
实战篇 7-结算页面开发
结算页面开发
上一章我们讲解了购物车页面,而在购物车页面点击「去结算」便会进入结算页。结算页也是在购物流程里十分重要的页面,一般用户会在这里选择收货地址、付款方式、发票等,并进行最终的付款流程。本章我们将进行结算页的讲解。
页面布局
先来看一眼页面结构:
可以见到,整个页面的布局分为三大块内容,分别是:
- 收货地址,结算商品模块;主要是选择收货地址和结算商品的展示
- 支付信息模块,包括支付方式,优惠券等;主要是支付方式的选择以及优惠券的使用
- 结算总金额模块,展示支付总额。
在 JSX 上,页面结构如下所示,十分清晰:
<View className='balance'>
<View className='balance_good'>...</View>
<View className='balance_pay'>...</View>
<View className='balance_amount'>...</View>
<View className='balance_bottom'>...</View>
</View>
从布局可以看出,页面的整体逻辑并不是特别复杂,逻辑比较集中的地方是结算商品数据的拉取与显示、地址,优惠券,支付方式的选择、支付逻辑、吸底地址栏与弹出层的动画实现。不过在第 18 章 《通过小程序云搭建电商后台服务》一文中已提到,我们实现的只是最简单的电商流程,所以 Demo 中并没有实现地址模块与优惠券模块,在这个页面里显示的相关内容只是虚拟数据。因此,下面重点讲述的是数据的拉取与显示与动画实现。
结算商品的数据拉取与显示
上一节中说过,有一些模块采用的是假数据,而真正从数据中拉取数据的是红色框框住的部分,主要是需要结算的商品数据与结算底栏。仅仅从页面结构中也可以看出,相较于购物车页,这里的逻辑是十分简单的,仅仅就是拉取数据,渲染数据就行了。所以我们要做的主要工作是数据的处理。
const data = result.data // result 为接口返回的数据
const balanceData= handleBalanceData(data) // handleCartData 为处理数据的函数
dispatch({type: 'RECEIVE_BALANCE_INFO', payload: balanceData}) // dispatch 处理过后的函数
...
// handleBalanceData 处理后的数据
{
payCommodities: [{
shop: {...}, // 商品店铺的信息
goods: {...}, // 对应商品信息
}, ...],
isNeedBanlance, // 是否需要结算
payNum, // 总的商品数量
totalPrice // 总结算金额
}
看过我们上一章购物车的读者应该都知道,这里的处理是类似的,都是数据渲染前的预处理。将接口拉到的结算页数据处理为方便直接渲染的结构,分别是 结算商品数据、总的商品数量、总结算金额,对应着上图两个红框。isNeedBanlance
这个值比较特殊,主要是为了预防一些意外的情况,例如在没有结算商品的情况下进入了结算页,这时候这个值就是 false
,然后会进行一个处理,自动跳转到购物车页。
页面动画实现
动画这个,在前面的章节几乎完全没有提及,但其实每个页面或多或少都会有些动画效果,只是受限于篇幅没有讲解。现在结算页逻辑不算太复杂,所以我们在这里就主要说下该页面的两个动画,分别是吸底地址栏及浮层弹出动画。
吸底地址栏
在 GIF 中可以看到,滚动到顶部时,地址栏渐隐;滚动到非顶部时,出现吸底地址栏,并且有渐现动画。显然,我们需要小程序的 onPageScroll
API,用于监听页面的滚动事件,函数部分代码如下:
onPageScroll (e) {
// e.scrollTop 为 0,且 this.state.reachTop 为 false
if (!e.scrollTop && !this.state.reachTop) {
this.setState({
reachTop: true
})
}
if (this.state.reachTop) {
this.setState({
reachTop: false
})
}
}
// ...JSX
<View className={reachTop ? 'balance_addr top' : 'balance_addr'}>
<View className='balance_addr_icon' />
<Text className='balance_addr_text'> 广东省深圳市宝安区龙光世纪大厦 </Text>
</View>
// ...scss
balance_addr {
// ....
opacity: 1;
transition: opacity 300ms ease;
&.top {
opacity: 0;
}
}
在 e.scrollTop
为 0,且 this.state.reachTop
为 false
时,将 this.state.reachTop
设置为 true
,说明已经到达顶部。同时可以在 JSX 里见到,在到达顶部时地址栏容器的类名会多一个 top
,将透明度变为 0,再配合 transition
展示出渐隐渐现的动画效果。
选项弹出层 - ActionSheet
通过 GIF 可以看到,这个动画比吸底地址栏的渐隐渐现稍显复杂,主要包含 ActionSheet 的进场动画和离场动画。如果只通过一个状态控制浮层的展示与隐藏,并不能实现在进场和离场时都有动画,例如入下面的代码:
showMask && <View className='paybox'>...</View>
这样会发现,在控制隐藏时,浮层是直接从 DOM 结构中移除的,会导致没有隐藏部分的动画。
所以,我们用了两个状态控制,分别用于控制 DOM 的隐藏及离场的动画显示。代码如下:
showPayWay &&
<View className={aniShowPayWay ? 'balance_pay_choose show' : 'balance_pay_choose'}>
<View className='mask' onAnimationEnd={this.onAnimationEnd}></View>
<View className='main'>...</View>
</View>
// onAnimationEnd
// 监听隐藏动画已结束,再将浮层从 DOM 中移除
onAnimationEnd () {
if (!this.state.aniShowPayWay) {
this.setState({
showPayWay: false
})
}
}
// ..scss
&.show {
opacity: 1;
z-index: 10;
.mask {
animation-name: showPanelMask;
}
.main {
animation-name: showDetailWrapper
}
}
.mask {
animation-name: hidePanelMask;
}
.main {
animation-name: hideDetailWrapper;
}
aniShowPayWay
用于控制进场动画和离场动画,可以见到,是否有 show
样式类名会决定执行的是进场还是离场动画。同时对动画进行事件监听,假如是离场动画结束(this.state.aniShowPayWay
为 false
),则隐藏 DOM。至此,进场离场的动画便能完成实现。
支付模块
支付模块可以说是结算页面的最主要的部分了,一个普通的电商程序的支付流程一般如下:
- 提交订单请求,后台生成订单,同时返回生成的订单号
- 调取微信接口取得用户参数,同时请求生成支付相关信息的接口,而后台返回数据包,签名数据等
- 根据后台返回的支付数据,调取微信小程序
Taro.requestPayment
API,唤起支付
也就是说,这里请求了两次,先是请求生成订单并得到订单号;第二次根据订单号和用户信息,再次请求,获得调取支付的各种信息,最后再调取支付。
这里值得注意的点就是第二次请求需要支付相关的 userinfo
:iv
和 encryptedData
,而这些信息需要通过 Taro.getUserInfo
接口拿到,但由于接口调整的原因,在用户未授权的情况下,调取该接口不再弹出浮层提示用户授权,而是需要用户手动去点击小程序中 open-type
为 getUserInfo
的 button
组件以进行授权。(具体细节可以查看本小册第 23 章《 微信小程序端用户授权处理》)。所以我们在全局维护一个是否授权字段时,若未授权,则点击支付时再弹出自己模拟的模态框引导用户授权。
PS:需要说明的是,由于跑通真实的支付流程是需要商家证书等一系列认证,所以我们的 Demo 并没有跑通真正的支付,而只是会生成一个订单,上面仅作支付流程唤起的讲解。
小结
本文从页面布局、数据拉取与展示、动画实现、支付模块 这四个方面来阐述如何使用 Taro 开发结算页,整体的开发难度相较购物车可能有一定的下降,但同样也是电商黄金流程中十分重要的一环。
下一章我们将介绍小程序用户端的授权处理。