知识点:
- 使用路由跳转内页
- 开发完整的商品详情页
- 弹出选择数量
前面的代码已经加入 Git 仓库,链接地址详见这里,将项目放在本地的同学也可以试着将代码放到仓库里,这个地方可以使用单独的分支合并到 master,也可以直接覆盖带 master 分支。
为了能够更直观的看到每次提交的代码,我尽量把每一节的内容都做一次提交,备注也会写的非常清楚,另外我也会不定时的增加各种注释到代码里。一般情况只需要切换到不同的提交时间点即可看到当时使用的代码了。
添加页面和跳转
在 src 目录下添加一个新的目录 detail,用来放新加入的详情页,新建 goods.js,并写几行简单的代码。
在 src 下的 index.js 中,将新建的页面加入到路由里。
这样新建的详情页就加入了路由管理,只要在需要跳转的时候执行传入路由的名字就可以了,这里改造 home 页,将几个地方的跳转改成刚才的路由名称就可以了。
点击商品就会发现已经可以跳转到详情页了,但是现在详情页什么都没有,同时也不能返回到上一页。
自定义顶部
在 src 下新建一个 component 目录,用来存放头部组件以及以后会用到的一些其他组件,将组件抽成一个公用的组件是一个非常明智的方法,它会让你省下很多的时间。最好的做法就是做一个兼容很多情况的组件,然后使用继承的方法做一些参数的重载。
改造这个组件,让 iOS 的顶部和安卓的顶部高度一致,把 iOS 顶部的系统组件露出来,默认情况下 iOS 顶部在小图标下面,安卓则有一个系统的顶部横条,小图标不影响正常的界面。
顶部组件默认有一个中间的标题,下面再加一个默认的左侧返回键,返回键使用的是路由组件的 gobanck 方法,右侧给一个自定义右侧按钮的方法。
把这个自定义组件放在详情页,然后给一个 title 值,一个简单的顶部组件就做完了。
刷新页面看效果,点击一下返回键,看看是不是可以用了。
详情页顶部
上半部分包括头图和几个属性介绍。
详情页的所有内容使用 ScrollView 包裹,这是一个比较简单的列表组件,使用非常方便,在列表不长的情况下请尽量使用这个组件。
将路由传进来的参数保存在组件的变量里。由于传入的参数有2个,所有这个地方使用了2个变量,同时后面做判断的时候也要判断2次。
在组件初始化之后调用接口,将接口返回的数据存入组件的 state 中,这里注意把 state 中的变量提前写好,减少不必要的 bug。
在最上面添加一个商品图片,然后接着添加价格等信息。
到这里,详情页的简单信息就实现了,后面再添加一个详细的商品信息就可以了。
这里需要注意的是,接口返回的图片有大有小,这里做了一个处理,图片太大的话就缩小一些。
最终效果和正常的电商 App 就很相似了,如果有特殊的样式还可以再自己调整。
上面的开发完成之后图片是同时加载的,导致有时候上部分的图片显示延迟,后面优化的时候会优化这种情况。
购买和加入购物车
展示的界面有了,下面再加上加入购物车、购买等按钮就可以了。
按钮要求悬浮在界面的最下方,所以要将这几个按钮放在 ScrollView 的后面,放在里面虽然也可以做到,但是非常不推荐,这样会增加渲染难度。
这里有3个按钮,分别是购物车、立即购买和分享商品。
购买弹出层
点击立即购买按钮不会直接跳转到下单页,这里会弹出一个简单的选择数量的页面。在 RN 中如果需要使用这样的一个层就需要用 Modal 组件来做,Modal 在 RN 中是一个单独的 view 层,默认覆盖在所有的页面上面。
这里设置 Modal 不使用默认的动画、全完透明,给 onRequestClose 添加一个默认的方法,不然会在手机上报错。
首先给 Modal 加一个内容层,这一层的主要作用是自己做遮罩的样式,这里是一个半透明的黑色遮罩,内部加一层点击区域,效果等于再点击黑色透明层的时候能够关掉弹出层。
``` javascript <View style={SpecificationsStyle.warp}> <TouchableOpacity activeOpacity={0.9} style={ { width: px(750), flex: 1 }} onPress={() => this.setState({ priceBoxStatus: false })}></TouchableOpacity> </View>
接下来开始写商品简略信息,包括商品小图、价格。
![enter image description here](http://images.gitbook.cn/f016c6d0-1a3a-11e8-9971-a10b6269e5d3)
下面再加一层用来放选择商品数量和购买按钮。
![enter image description here](http://images.gitbook.cn/4549d7a0-1a3b-11e8-9971-a10b6269e5d3)
最后把几个事件实现一下。
javascript //购买方法 openbuy() { this.setState({ priceBoxStatus: true }) } //数量减少 reduce() { if (this.state.buyNumber == 1) return; let buyNumber = Number(this.state.buyNumber) || 2; if (isNaN(buyNumber)) buyNumber = 2; this.setState({ buyNumber: --buyNumber }) } //改数量 changeNumber(num) { num = Number(num); if (isNaN(num)) num = 1; if (num > this.state.limitStock) { num = 1 toast(库存不足
); } this.setState({ buyNumber: num }); } //增加数量 plus() { if (this.state.isBuyLimit == 1 && this.state.buyNumber >= this.state.buyLimitNum) { toast(该商品为限购商品,${this.state.buyLimitMsg}
); return; } if (this.state.buyNumber >= this.state.limitStock) { toast(库存仅剩${this.state.limitStock}件
); return; } let qty = this.state.buyNumber || 1; if (isNaN(qty)) qty = 1; this.setState({ buyNumber: ++qty }) }
最终效果如图所示,点击立即购买按钮即可弹出这一个弹出层,弹出层可以选择数量,默认保持数量为1,受库存以及最大可选择的数量影响,也可以直接点击按钮跳转到购买页。
<img src="http://images.gitbook.cn/bf604470-1a3b-11e8-9971-a10b6269e5d3" width = "70%" />
下面是最终的 goods.js 文件的代码:
javascript 'use strict';
import React from 'react'; import { StyleSheet, View, Text, TouchableWithoutFeedback, TouchableOpacity, ImageBackground, Image, ScrollView, Platform, Modal, TextInput } from 'react-native'; import { log, logWarm, logErr } from '../utils/log' import request from '../utils/request' import px from '../utils/px' import toast from '../utils/toast' import Header from '../component/header'
export default class extends React.Component {
constructor(props) {
super(props);
this.state = {
image: "",
labelList: [],
goodsShowName: "",
goodsShowDesc: "",
salePrice: 0,
discount: '10',
marketPrice: 0,
isInBond: 0,
isForeignSupply: 0,
taxation: 0,
detail: {
mobile_detail: { list: [] }
},
priceBoxStatus: false,
smallImage: '',
buyNumber: 1,
};
this.id = this.props.navigation.state.params.id;
this.sku = this.props.navigation.state.params.sku;
}
render() {
return <View style={
{ fl