知识点:
- 地址列表
- 地址编辑添加
- 下单结果页
地址列表页
下单需要选择地址,这里我们再做一个地址列表页,点击选择地址的时候跳转到这个页面,选择完成之后回调选择好的地址结果。
新建文件 /src/center/addressList.js,在文件中创建一个简单的列表,这里把列表和内容分开,使用几个空的方法替代。
{/*地址列表*/}
<FlatList
data={this.state.list}
refreshing={this.state.refreshing}
onRefresh={() => this.refresh()}
keyExtractor={(item) => item.id}
renderItem={({ item }) =>
<AddressItem
address={item}
select={this.select.bind(this)}
defaultSelect={this.defaultSelect.bind(this)}
goEdit={this.goEdit.bind(this)}
delAddress={this.delAddress.bind(this)} />
}
onEndReached={() => this.next()}
ListEmptyComponent={
<Text style={styles.emptyList} allowFontScaling={false}>暂无收货地址</Text>
}
/>
每一项地址都包含选择、编辑、删除、设为默认 4 个事件,这里使用列表传入的事件,本身并不处理事件。
const { address, selected, isSelect, isSelected } = this.props
return <View style={styles.addressItem} key={address.id}>
<TouchableWithoutFeedback onPress={() => this.props.select(address)}>
<View style={styles.addressInfo}>
{isSelect &&
<View style={styles.addressCheck}>
{isSelected &&
<Image source={{ uri: require('../images/icon-address-check') }}
style={{ width: px(30), height: px(20), marginTop: px(50) }} />
}
</View>
}
<View style={styles.addressDetail}>
<View style={styles.head}>
<Text allowFontScaling={false} style={[styles.addressDetail1, {
lineHeight: px(35)
}]}>{address.name} {address.phone}</Text>
{address.defaultYn == 'Y' && <View style={styles.normalBtn}>
<Text allowFontScaling={false} style={styles.normalText}>默认地址</Text>
</View>}
</View>
<View>
<Text allowFontScaling={false} style={styles.addressDetail2}>
{address.province}-{address.city}-{address.district}
{address.detail}
</Text>
</View>
<View>
<Text allowFontScaling={false} style={styles.addressDetail2}>{address.cardNo}</Text>
</View>
</View>
</View>
</TouchableWithoutFeedback>
<View style={styles.addressAction}>
<TouchableWithoutFeedback onPress={() => this.props.defaultSelect(address.id)}>
<View style={styles.radio}>
{
address.defaultYn == 'Y' && <Image source={{ uri: require('../images/icon-default-address') }}
resizeMode='cover'
style={{ width: px(34), height: px(34) }} />
}
{
address.defaultYn == 'N' && <Image source={{ uri: require('../images/icon-default-address-un') }}
resizeMode='cover'
style={{ width: px(34), height: px(34) }} />
}
<Text allowFontScaling={false} style={styles.radioLabel}>
{address.defaultYn == 'Y' ? '默认地址' : '设为默认'}
</Text>
</View>
</TouchableWithoutFeedback>
<TouchableWithoutFeedback onPress={() => this.props.goEdit(address.id)}>
<View style={styles.addressActionBtn}>
<Image style={{ width: px(24), height: px(24), marginTop: px(1) }} source={{ uri: require('../images/icon-address-edit') }}></Image>
<Text allowFontScaling={false} style={styles.addressActionBtnTxt}>编辑</Text>
</View>
</TouchableWithoutFeedback>
<TouchableWithoutFeedback onPress={() => this.props.delAddress(address.id)}>
<View style={styles.addressActionBtn}>
<Image style={{ width: px(25), height: px(25) }} source={{ uri: require('../images/icon-address-del') }}></Image>
<Text allowFontScaling={false} style={styles.addressActionBtnTxt}>删除</Text>
</View>
</TouchableWithoutFeedback>
</View>
</View>
给列表添加几个事件,这里仅作参考,具体的逻辑看接口返回的数据格式而定。
componentDidMount() {
this.refresh();
}
//获取第一页
refresh() {
let list = [{
id: "2",
name: "收货人",
phone: "15600222222",
province: "北京",
city: "北京",
district: "朝阳区",
detail: "朝阳大悦城xxx号xxx店"
}]
this.setState({ list })
//真是情况的代码
// try {
// this.start = 1;
// let list = request.get('/get/address,do', {
// start: this.start
// })
// if (!list || list.constructor !== Array) return;
// this.setState({ list })
// } catch (e) {
// toast(e.message)
// }
}
选中地址的时候使用回调方法,将地址放入上一页的地址栏中。同时调用路由的返回方法。
//选中并返回
select(address) {
if (this.state.callback) this.state.callback(address);
this.props.navigation.goBack();
}
远程请求做一个例子,这里先注释掉假装已经能用了。
//设置默认地址
defaultSelect(id) {
// try {
// request.post("/set/address.do",{id:id})
// this.refresh();
// } catch (e) {
// toast(e.message)
// }
}
//删除
delAddress(id) {
// try {
// request.post("/del/address.do",{id:id})
// this.refresh();
// } catch (e) {
// toast(e.message)
// }
}
这里把新建和修改放在一个页面,通过 ID 的是否存在来判断使用哪个模式。
//跳转编辑
goEdit(id) {
this.props.navigation.navigate('AddressEdit', {
id,
callback: () => this.refresh()
});
}
//添加新地址
goCreate() {
this.props.navigation.navigate('AddressEdit', {
callback: () => this.refresh()
});
}
地址编辑页
地址编辑页其实就是一个表单提交页,各家的方法各不相同,这里我介绍一种我在使用的方式。
进入页面的时候会检查本地是否有地址数据包,然后将本地数据包的时间戳上报给服务器,服务器判断数据是否需要更新,然后返回新数据包或者空。下面再呈现正常的界面,这里的数据包其实就是省市三级联动的具体城市内容,我在这里做一个简单的模拟。
这里先看一下样式:
再看一下代码的结构,这里就不显示全部的代码了。
在组件初始化的时候去拿数据,通过时间戳来判断是否有更新,将数据放在变量中。这里可以适当放一个 loading 动画。
address_data = {}
async componentDidMount() {
try {
//获取默认数据
let data = await getItem("address")
if (!data) data = { time: 0 }
//使用时间戳获取数据
let res = await request.get("get/address/data", { time: data.time })
//有返回则更新本地数据
if (res) {
data = res;
setItem("address", data);
}
//复制给组件
this.address_data = data;
} catch (e) {
toast(e.message)
}
}
最后在保存的时候将地址回传给上一页,到这里地址的编辑和选择就结束了。
//保存地址
async save() {
let data={
id:this.state.id,
}
if (!this.state.name) {
toast('请输入收货人姓名'); return;
}
if (!this.state.phone) {
toast('请输入收货人电话'); return;
}
if (!this.state.detail) {
toast('请输入收货地址'); return;
}
// try {
// let data=request.post("/address",this.state);
// } catch (e) {
// toast(e.message)
// }
this.callback && this.callback(this.state);
this.props.navigation.goBack();
}
下单结果页
通常在下单之后都要进入一个支付结果页,这个页面一方面是通知用户支付的结果,同时也是再一次查询服务器,对比支付的真实情况。
在微信支付之后增加一次跳转,不管支付成功与否都跳转。
finally {
this.props.navigation.navigate('Success', { orderNo: '' })
}
按照下面的样子做一个类似的界面,这里要判断支付结果,显示隐藏支付成功和失败的样子。
照例使用假数据调试这个页面:
async componentDidMount() {
this.setState({
image: "http://imgbeta.daling.com/data/files/mobile/2016/06/08/14653741419171.jpg_710x440.jpg",
orderNo: "NS2029284774849",
payAmount: "200.00",
pay: "1"
})
// try {
// let data = request.get("/get/order", { orderid: this.state.orderNo })
// } catch (e) {
// toast(e.message)
// }
}
这里还要记得处理一下安卓的物理返回键,不然用户点击返回就回到了上一页的下单页了。
//添加物理返回键的监听
componentWillMount() {
BackHandler.addEventListener('hardwareBackPress', this.noGoBack);
}
//注销监听
componentWillUnmount() {
BackHandler.removeEventListener("hardwareBackPress", this.noGoBack);
}
//禁止返回
noGoBack() {
return true;
}
其他跳转还是按照正常的来,但是回到首页需要重置路由。当前的路由栈里已经存在了下单以及详情等页面,这个时候直接跳转会让之前的路由继续存在,但是我们又不能让用户返回到上一页,此时就会出现 bug,所有这里直接重置路由的内容,然后路由变成正常的样子。
//跳转到订单详情页
goDetail() {
this.props.navigation.navigate('OrderDetail', {
orderNo: this.state.orderNo
});
}
//重置路由到首页
goHome() {
this.props.navigation.dispatch(NavigationActions.reset({
index: 0,
actions: [
NavigationActions.navigate({ routeName: 'Tabs' })
]
}));
}
//调用支付
async pay() {
let isInstalled = await isWXAppInstalled();
if (!isInstalled) {
toast('请安装微信客户端');
return;
}
try {
let res = await pay(wx_data);
//微信返回结果
} catch (e) {
//微信调起失败/取消
}
}
这里有一个小窍门,在页面转换的时候可以将页面的信息记录在一个单独的地方,这样就可以随时查询路由栈中的顺序了。
如果遇到请求不到线上产品的,请检查网络、代理设置。如果还有问题,请修改 /src/utils/request.js,使用模拟的 header 信息。
let headers = {
uid: 0,
jsversion: "0.1.1",
utoken: "",
platform: Platform.OS,
clientid: "db7b4e395b7a0ec75c6078ddf7db276c588a694f",
version: '1.0.5',
model: "apple",
OSVersion: '11.2',
brand: "apple",
channel: "appstore",
net: 'WIFI',
bundle: "13b2bcaba812600af5465c8c649f34d3",
xcrole: 0,
}