封装组件countDownButton.js
import React , {Component}from ‘react‘;
import { PropTypes} from ‘prop-types‘;
import {View,Text,TouchableOpacity,StyleSheet} from ‘react-native‘;
let isCalled = false, timeOut;//isCalled:falses表示可以调用目标方法 timeOut:存放定时器
export default class TimerButton extends Component {
constructor(props) {
super(props)
this.state = {
timerCount: this.props.timerCount || 60,//设置默认倒计时长 只要“||”前面为false,不管“||”后面是true还是false,都返回“||”后面的值。只要“||”前面为true,不管“||”后面是true还是false,都返回“||”前面的值。
timerTitle: this.props.timerTitle || ‘获取验证码‘,//设置正常状态下按钮默认标题
counting: false,//true为倒数时状态标志
};
}
componentWillUnmount(){
this.interval&&clearInterval(this.interval)
}
//倒计时方法
_countDownAction(){
const {timerCount}=this.state//获取timerCount最初值,触发倒计时后不更新此值,因为在timerCount时长内一直setInterval()方法
this.interval = setInterval(() =>{
const timer = this.state.timerCount - 1
if(timer===0){
this.interval&&clearInterval(this.interval);//清除倒计,恢复状态
// console.log(‘timerCount‘,timerCount)
this.setState({
timerCount: timerCount,//将最初获取的timerCount不变值更新回state
timerTitle: this.props.timerTitle || ‘获取验证码‘,
counting: false,
})
}else{
this.setState({
timerCount:timer,
timerTitle: `重新获取(${timer}s)`,
counting: true,
},
()=> console.log(‘timerCount2‘,this.state.timerCount)//更新state后立即实时获取的最新值的回调
)
}
},1000)
}
//防止频繁点击方法
callOnceInInterval = (functionTobeCalled, interval = 2000) => {
// 当isCalled==false时
if (isCalled==false) {//可以写成 if (!isCalled)
// 将isCalled取反
isCalled = true;
// 清除定时器
clearTimeout(timeOut);
timeOut = setTimeout(() => {
isCalled = false;//2秒后将全局变量isCalled变为false,为下次return functionTobeCalled()调用做准备,在此时间之内isCalled = true,故点击无效
}, interval);
return functionTobeCalled();//将目标方法返回出去实现有效点击
}
};
render(){
const {onClick,style,textStyle,} = this.props
const {counting,timerTitle} = this.state
return (
<TouchableOpacity
activeOpacity={counting ? 1 : 0.7} //正常状态按下时不透明度为 0.7,倒数状态不透明度为1
onPress={()=>{this.callOnceInInterval(onClick)}}
disabled={counting?true:false}//判断倒数状态时呈禁用状态
style={[counting?styles.btnStyle:style]}//倒数状态呈灰色 正常状态蓝色
>
<Text style={[styles.btnTextStyle,textStyle]}>{timerTitle}</Text>
</TouchableOpacity>
)
}
}
const styles = StyleSheet.create({
btnStyle:{
// justifyContent:‘center‘,
// alignItems:‘center‘,
marginLeft:20,
backgroundColor:‘#f0f0f0‘,
borderRadius:6
},
})
在页面中使用countDownButton组件:
WriteInfoScreen.js
/**
*Created by *** on 2020/4/14
*/
import React, { Component, PureComponent } from ‘react‘;
import {
StyleSheet,
Text,
Image,
View,
TextInput,
ScrollView,
FlatList,
TouchableOpacity,
Dimensions
} from ‘react-native‘;
import LoadingView from "../../../../component/Loading";
import * as api from "../../../../networking/Api";
import CountDownButton from ‘../child/countDownButton‘;
// 填写信息
const windowHeight = Dimensions.get(‘window‘).height;
var screenWidth = Dimensions.get(‘window‘).width;
export default class WriteInfoScreen extends Component {
constructor() {
super(...arguments)
this.state = {
accountName:‘‘,//用户名
idCard:‘‘,//身份证号
phoneNumber:‘‘,//预留手机号
bankAccount:‘‘,//银行账户
// accountName:‘梦玲珑‘,//用户名
// idCard:‘440106199003078716‘,//身份证号
// phoneNumber:‘13600000013‘,//预留手机号
// bankAccount:‘6217900000000000006‘,//银行账户
verificationCode:‘‘,//验证码
banckItem:{},//银行卡选择列表信息
ChannelItem:{},//支付渠道选择列表信息
customerId:‘‘,
nameAndIdCard:‘‘,
data:‘‘,//授权结果
message:‘‘,//授权结果说明
getCodeData:‘‘,//获取验证码返回数据(getCodeData.id或者getCodeData.reqSn)
code:null,//用于授权结果页判断跳转
enable:false//用于限制倒数按钮状态
}
}
componentDidMount(){
const { banckItem, //银行卡选择列表信息
ChannelItem, //支付渠道选择列表信息
customerId,//用户ID
nameAndIdCard,
phoneNumber
} = this.props.navigation.state.params.data
const {customer_name,person_identity_card}=nameAndIdCard
this.setState({
banckItem: banckItem,
ChannelItem:ChannelItem,
customerId:customerId,
accountName:customer_name,
phoneNumber:phoneNumber,
// idCard:person_identity_card,
})
console.log(‘banckItem:‘,banckItem,‘ChannelItem:‘,ChannelItem,‘customerId:‘,customerId,‘nameAndIdCard:‘,nameAndIdCard)
}
//授权结果页返回重制输入框值
componentWillReceiveProps(nextProps){
if(nextProps.navigation.state.params.inputVlue){
const{accountName,idCard,phoneNumber,bankAccount} = nextProps.navigation.state.params.inputVlue
console.log(‘授权结果页返回重制输入框值‘)
this.setState({
accountName:accountName,
phoneNumber: phoneNumber,
idCard: idCard,
bankAccount: bankAccount,
verificationCode:‘‘,
})
}
}
//立即开通
_openNow = ()=>{
const{bankAccount,phoneNumber,idCard,accountName,verificationCode,customerId,getCodeData}=this.state;
const{
bank,//银行卡代码
bankName,//银行名称
}=this.state.banckItem;
const{
loanType,//借款类型
loanTypeAuth,//还款渠道
payType,//支付方式
}=this.state.ChannelItem;
// 输入框值打印
console.log(‘accountName:‘,accountName,‘idCard:‘,idCard,‘phoneNumber:‘,phoneNumber,‘bankAccount:‘,bankAccount,‘verificationCode:‘,verificationCode,);
// 判断各项是否为空
if(accountName==‘‘){
EDMoney.Toast.show(‘请输入户名‘)
}else if(idCard==‘‘){
EDMoney.Toast.show(‘请输入身份证号‘)
}else if(phoneNumber==‘‘){
EDMoney.Toast.show(‘请输入预留手机号‘)
}else if(bankAccount==‘‘){
EDMoney.Toast.show(‘请输入银行账户‘)
}else if(verificationCode==‘‘){
EDMoney.Toast.show(‘请输入验证码‘)
}else{
//当reqSn且id都是Undefined的时候(未获取验证码状态)
if(!getCodeData.reqSn && !getCodeData.id){
EDMoney.Toast.show(‘请先获取验证码‘)
}else{
//当存在reqSn时,通联渠道已获取验证码
if(getCodeData.reqSn){
console.log(‘reqSn+++:‘,getCodeData.reqSn)
// console.log(‘id+++:‘,getCodeData.id)
//通联系列渠道
api.confirmDingCardreqSn(customerId,bankAccount,bank,phoneNumber,idCard,loanType,loanTypeAuth,accountName,payType,bankName,verificationCode,getCodeData.reqSn)
.then((res) => {
console.log(‘confirmDingCardreqSn res+++++:‘,res)
//请求成功后刷新数据
this.setState({
data: res.data,//交易成功或失败
message: res.message,//作成功或操作失败
code: res.code,
getCodeData:‘‘//作废上一轮已使用过的验证码(重置后需重新获取才能进行下一轮立即开通请求)
})
this.props.navigation.navigate(‘authorizeResult‘,{ data:this.state.data,message:this.state.message,code:this.state.code,inputVlue:this.state,})
})
.catch((error) => {
console.log(‘confirmDingCardreqSn error+++++:‘,error)
//success:false时不符合后台接口条件,弹窗提示
if (error.errors) {
console.log(‘confirmDingCardreqSn error.errors+++++:‘,error.errors)
EDMoney.Toast.show(error.errors._error)
// this.props.navigation.navigate(‘authorizeResult‘,{ data:this.state.data,message:this.state.message})
} else {
// 网络错误,请求失败(未连接后台)
EDMoney.Toast.show(‘网络错误‘)
}
})
}else{
//当其他情况时,其他渠道已获取验证码
// console.log(‘reqSn---:‘,getCodeData.reqSn)
console.log(‘id---:‘,getCodeData.id)
//其他渠道
api.confirmDingCardID(customerId,bankAccount,bank,phoneNumber,idCard,loanType,loanTypeAuth,accountName,payType,bankName,verificationCode,getCodeData.id+‘‘)
.then((res) => {
console.log(‘confirmDingCardID res+++++:‘,res)
//请求成功后刷新数据
this.setState({
data: res.data,//交易成功或失败
message: res.message,//作成功或操作失败
code: res.code,
getCodeData:‘‘//作废上一轮已使用过的验证码(重置后需重新获取才能进行下一轮立即开通请求)
})
this.props.navigation.navigate(‘authorizeResult‘,{ data:this.state.data,message:this.state.message,code:this.state.code,inputVlue:this.state,})
})
.catch((error) => {
console.log(‘请求失败+++++:‘,error)
if (error.errors) {
// EDMoney.Toast.show(error.errors._error)
// this.props.navigation.navigate(‘authorizeResult‘,{ data:this.state.data,message:this.state.message})
} else {
EDMoney.Toast.show(‘网络错误‘)
}
})
}
}
}
}
// 输入框失去焦点后执行
updateText = (text,sign)=>{
console.log(‘text‘,text)
// 更新不同输入框的值到State
switch (sign) {
case ‘accountName‘:
this.setState({accountName : text})
break;
case ‘idCard‘:
var idCardReg = /^[0-9]{18}/;//18位数字规则
//不符合规则执行
if(!idCardReg.test(text)){
if(text){
EDMoney.Toast.show(‘身份证号格式不正确!‘)
this.setState({idCard : text})
//当输入值不符合格式时,先更新value值为‘ ’(最初是为‘’)
// this.setState({idCard : ‘ ‘})
// 然后瞬间再次恢复value值回最初始的‘’,起到重置输入框效果
// setTimeout(() =>{
// this.setState({idCard : ‘‘})
// }, 10)
break
}else{
this.setState({idCard : ‘‘})
break
}
}else{
this.setState({idCard : text})
break;
}
case ‘phoneNumber‘:
var phoneNumberReg = /^1[3-9]\d{9}$/;//11位手机号码规则
if(!phoneNumberReg.test(text)){
if(text){
EDMoney.Toast.show(‘手机号码格式不正确!‘)
this.setState({phoneNumber : text})
// this.setState({phoneNumber : ‘ ‘})
// setTimeout(() =>{
// this.setState({phoneNumber : ‘‘})
// }, 10)
break
}else{
this.setState({phoneNumber : ‘‘})
break
}
}else{
this.setState({phoneNumber : text})
break;
}
case ‘bankAccount‘:
var bankAccountReg = /^[0-9]*$/;//纯数字银行卡规则
if(!bankAccountReg.test(text)){
if(text){
EDMoney.Toast.show(‘银行卡号格式不对!‘)
this.setState({phoneNumber : text})
// this.setState({bankAccount : ‘ ‘})
// setTimeout(() =>{
// this.setState({bankAccount : ‘‘})
// }, 10)
break
}else{
this.setState({phoneNumber : ‘‘})
break
}
}else{
this.setState({bankAccount : text})
break;
}
case ‘verificationCode‘:
var verificationCodeReg = /^\d{6}$/;//6位验证码规则
if(!verificationCodeReg.test(text)){
// 有值且不符合条件
if(text){
EDMoney.Toast.show(‘请输入6位数的验证码!‘)
this.setState({verificationCode : text})
// this.setState({verificationCode : ‘ ‘})
// setTimeout(() =>{
// this.setState({verificationCode : ‘‘})
// }, 10)
break
}else{
// 空且不符合条件
this.setState({verificationCode : ‘‘})
break
}
}else{
// 有值且符合条件
this.setState({verificationCode : text})
break;
}
default:
break;
}
}
// 获取验证码方法
_getCode = ()=>{
const{bankAccount,phoneNumber,idCard,accountName,customerId,}=this.state;
console.log(‘accountName:‘,accountName,‘idCard:‘,idCard,‘phoneNumber:‘,phoneNumber,‘bankAccount:‘,bankAccount,);
const{
bank,//银行卡代码
bankName,//银行名称
}=this.state.banckItem;
const{
loanType,//借款类型
loanTypeAuth,//还款渠道
payType,//支付方式
}=this.state.ChannelItem;
//判断信息是否填写完整
if(accountName==‘‘){
EDMoney.Toast.show(‘请输入户名‘)
return false;//如果为空跳出_getCode方法以阻止请求
}else if(idCard==‘‘){
EDMoney.Toast.show(‘请输入身份证号‘)
return false;
}else if(phoneNumber==‘‘){
EDMoney.Toast.show(‘请输入预留手机号‘)
return false;
}else if(bankAccount==‘‘){
EDMoney.Toast.show(‘请输入银行账户‘)
return false;
}else{
console.log(‘customerId:‘,customerId,‘bankAccount:‘,bankAccount,‘bank:‘,bank,‘phoneNumber:‘,phoneNumber,‘idCard:‘,idCard,‘loanType:‘,loanType,‘loanTypeAuth:‘,loanTypeAuth,‘accountName:‘,
accountName,‘payType:‘,payType,‘bankName:‘,bankName,);
this.refs.countDown._countDownAction();//请求前调用倒计时组件的方法开始倒计时
api.binDingCard(customerId,bankAccount,bank,phoneNumber,idCard,loanType,loanTypeAuth,accountName,payType,bankName,)
.then((res) => {
console.log(‘res.data‘,res.data)
// 请求成功后刷新数据
this.setState({
getCodeData: res.data,
})
console.log(‘获取验证码成功开始倒计‘)
// this.refs.countDown._countDownAction();//请求成功并后台验证通过后调用倒计时组件的方法开始倒计时
})
.catch((error) => {
if (error.errors) {
EDMoney.Toast.show(error.errors._error)
} else {
EDMoney.Toast.show(‘网络错误‘)
}
})
}
}
render() {
const { banckItem, ChannelItem } = this.props.navigation.state.params.data
const{bankAccount,phoneNumber,idCard,accountName,verificationCode,enable}=this.state;
return (
<View style={styles.root}>
<ScrollView showsVerticalScrollIndicator={false} keyboardShouldPersistTaps={‘never‘}>
<View>
<View style={styles.channelName}>
<Text style={styles.channelNameText}>{ChannelItem.channelPayName}</Text>
</View>
<View style={styles.write} >
<View style={styles.infoItem}>
<Text style={styles.infoItemText}>户名</Text>
<TextInput
underlineColorAndroid=‘transparent‘//去除安卓端下划线
placeholder=‘请输入户名‘
// placeholderTextColor="#e1e1e1"
onBlur={(event) => this.updateText(
event.nativeEvent.text,‘accountName‘
)}
value={accountName}
style={styles.inputStyle}
></TextInput>
</View>
<View style={styles.infoItem}>
<Text style={styles.infoItemText}>身份证号</Text>
<TextInput
underlineColorAndroid=‘transparent‘
placeholder=‘请输入身份证号‘
// placeholderTextColor="#e1e1e1"
onBlur={(event) => this.updateText(
event.nativeEvent.text,‘idCard‘
)}
keyboardType=‘numeric‘
value={idCard}
style={styles.inputStyle}
maxLength={18}
></TextInput>
</View>
<View style={[styles.infoItem,{marginBottom:0}]}>
<Text style={styles.infoItemText}>预留手机号</Text>
<TextInput
underlineColorAndroid=‘transparent‘
placeholder=‘请输入预留手机号‘
// placeholderTextColor="#e1e1e1"
onBlur={(event) => this.updateText(
event.nativeEvent.text,‘phoneNumber‘
)}
keyboardType=‘numeric‘
value={phoneNumber}
style={styles.inputStyle}
maxLength={11}
></TextInput>
</View>
<View style={[styles.infoItem,{marginBottom:0}]}>
<Text style={styles.infoItemText}></Text>
<Text style={[styles.inputStyle,{textAlign:‘center‘,borderWidth:0,lineHeight:50,backgroundColor:0}]}>{banckItem.bankName}</Text>
</View>
<View style={styles.infoItem}>
<Text style={styles.infoItemText}>银行账户</Text>
<TextInput
underlineColorAndroid=‘transparent‘
placeholder=‘请输入银行账号‘
// placeholderTextColor="#e1e1e1"
onBlur={(event) => this.updateText(
event.nativeEvent.text,‘bankAccount‘
)}
keyboardType=‘numeric‘
value={bankAccount}
style={styles.inputStyle}
></TextInput>
</View>
<View style={styles.infoItem}>
<TextInput
underlineColorAndroid=‘transparent‘
placeholder=‘请输入验证码‘
// placeholderTextColor="#e1e1e1"
onBlur={(event) => this.updateText(
event.nativeEvent.text,‘verificationCode‘
)}
keyboardType=‘numeric‘
maxLength={6}
value={verificationCode}
style={styles.code}
></TextInput>
< CountDownButton
style={{marginLeft:20,backgroundColor:‘#00a8ff‘,borderRadius:6}}
textStyle={{width: 120,color: ‘#fff‘,fontSize: 17,lineHeight:50,textAlign:‘center‘}}
timerCount={10}//设置倒计时长
timerTitle={‘获取验证码‘}//设置按钮默认标题
onClick={this._getCode}
ref="countDown"
// timerActiveTitle={[‘请在(‘,‘s)后重试‘]}//倒计时的数字会插在数组第一项之后,如:[‘请在‘, ‘秒后重新获取‘],显示为【请在60秒后重新获取】
/>
</View>
</View>
</View>
</ScrollView>
<View>
<TouchableOpacity
onPress={()=>this._openNow()}
style={{backgroundColor:‘#00a8ff‘}}>
<Text style={styles.getCode}>立即开通</Text>
</TouchableOpacity>
</View>
</View>
)
}
}
const styles = StyleSheet.create({
root:{
backgroundColor:‘#f0f0f0‘,
flex:1
},
channelName:{
backgroundColor:‘#fff‘,
borderTopColor:‘#e6e6e6‘,
borderTopWidth:1,
padding:10,
marginBottom:10,
minWidth:300
},
channelNameText:{
fontSize:20,
lineHeight:45,
textAlign:‘center‘
},
infoItem:{
flexDirection:‘row‘,
alignItems: ‘center‘,
marginBottom:20
},
infoItemText:{
fontSize:18,
flex:2,
lineHeight:50,
textAlign:‘left‘,
},
write:{
minWidth:300,
backgroundColor:‘#fff‘,
paddingLeft:15,
paddingRight:15,
paddingTop:20
},
inputStyle:{
height:50,
fontSize:18,
textAlign:‘left‘,
paddingLeft:10,
flex:4,
borderRadius:4,
backgroundColor:‘#f0f0f0‘
},
code:{
height:50,
fontSize:18,
textAlign:‘left‘,
paddingLeft:10,
flex:4,
borderRadius:3,
backgroundColor:‘#f0f0f0‘
},
getCode:{
fontSize:18,
color:‘#fff‘,
lineHeight:60,
textAlign:‘center‘,
marginLeft:10
}
})
原文:https://www.cnblogs.com/itgezhu/p/12809681.html