ReactNative【实战系列教程】我的小红书 2 -- 快捷登录、手机号密码登录
最终效果
技术要点
用户协议 – 打开本地浏览器
点击后,直接打开本地浏览器浏览网页
// 最终需修改为 《用户协议》 的网址Linking.openURL("https://www.baidu.com");
手机号输入框的 344 展示
onChangeText={(text: string) => {setPhone(formatPhone(text));
}}
export function formatPhone(phone: string): string {let trim: string = phone.replace(/\s+/g, "");const result = [trim.slice(0, 3), trim.slice(3, 7), trim.slice(7, 11)].filter((item) => !!item).join(" ");return result;
}
但在访问接口登录时,传参需清除空格
UserStore.requestLogin(replaceBlank(phone), pwd, (success: boolean) => {
export function replaceBlank(phone: string): string {return phone ? phone.replace(/\s+/g, "") : "";
}
代码实现
app/_layout.tsx
<Stack>{/* 登录页 */}<Stack.Screen name="login" options={{ headerShown: false }} /><Stack.Screen name="(tabs)" options={{ headerShown: false }} /><Stack.Screen name="+not-found" /></Stack>
app/login.tsx
import UserStore from "@/stores/UserStore";
import { formatPhone, replaceBlank } from "@/utils/StringUtil";
import { Toast } from "@ant-design/react-native";
import AntDesign from "@expo/vector-icons/AntDesign";
import Entypo from "@expo/vector-icons/Entypo";
import FontAwesome from "@expo/vector-icons/FontAwesome";
import { useRouter } from "expo-router";
import { useState } from "react";
import {Image,KeyboardAvoidingView,Linking,Platform,StyleSheet,Text,TextInput,TouchableOpacity,View,
} from "react-native";
import Animated from "react-native-reanimated";
export default function LoginScreen() {const router = useRouter();const [ifRead, setIfRead] = useState(false);const [ifQuickLogin, setIfQuickLogin] = useState(false);const [phone, setPhone] = useState<string>("");const [pwd, setPwd] = useState<string>("");const [showPassword, setShowPassword] = useState(false);const canLogin = phone?.length === 13 && pwd?.length >= 6;const onLoginPress = async () => {if (!canLogin) {return;}if (!ifRead) {Toast.show({content: "请先同意并阅读用户协议",});return;}UserStore.requestLogin(replaceBlank(phone), pwd, (success: boolean) => {if (success) {Toast.success("登录成功");router.replace("/(tabs)");} else {Toast.fail("登录失败,账号/密码错误!");}});};const render_passwordLogin = () => {const passwordLogin_styles = StyleSheet.create({inputBox: {borderColor: "#ccc",borderBottomColor: "#ccc",borderBottomWidth: 1,flexDirection: "row",alignItems: "center",marginBottom: 14,height: 55,},phonePrefix: {fontSize: 20,color: "#bbb",marginRight: 8,},phoneInput: {fontSize: 24,paddingLeft: 14,textAlignVertical: "bottom",height: 75,flex: 1,},passwordInput: {fontSize: 20,textAlignVertical: "bottom",flex: 1,height: 75,},loginButton: {backgroundColor: "#ff2442",},loginButtonDisable: {backgroundColor: "#DDDDDD",},moreBox: {flexDirection: "row",justifyContent: "space-between",marginBottom: 26,},tip: {fontSize: 14,color: "#bbb",marginBottom: 20,textAlign: "center",},toCodeLoginBtn: {flexDirection: "row",alignItems: "center",},toCodeLoginBtnTxt: {marginLeft: 6,},moreBoxTxt: {color: "#303080",},});return (<View><Text style={passwordLogin_styles.tip}>未注册的手机号登录成功后将自动注册</Text><KeyboardAvoidingViewbehavior={Platform.OS === "ios" ? "padding" : "height"}keyboardVerticalOffset={Platform.OS === "ios" ? 64 : 30}><View style={passwordLogin_styles.inputBox}><Text style={passwordLogin_styles.phonePrefix}>+86</Text><AntDesign name="caretdown" size={14} color="#bbb" /><TextInputstyle={passwordLogin_styles.phoneInput}placeholder="请输入手机号"placeholderTextColor="#bbb"autoFocus={false}keyboardType="number-pad"maxLength={13}value={phone}onChangeText={(text: string) => {setPhone(formatPhone(text));}}/></View><View style={passwordLogin_styles.inputBox}><TextInputstyle={passwordLogin_styles.passwordInput}placeholder="请输入密码"placeholderTextColor="#bbb"autoFocus={false}maxLength={20}secureTextEntry={!showPassword}value={pwd}onChangeText={(text: string) => {setPwd(text);}}/><TouchableOpacityactiveOpacity={0.7}onPress={() => {setShowPassword(!showPassword);}}><Entyponame={showPassword ? "eye" : "eye-with-line"}size={28}color="#bbb"/></TouchableOpacity></View></KeyboardAvoidingView><View style={passwordLogin_styles.moreBox}><TouchableOpacitystyle={passwordLogin_styles.toCodeLoginBtn}onPress={() => {Toast.show({content: "待需要时完善",});}}><FontAwesome name="exchange" size={12} color="#303080" /><Textstyle={[passwordLogin_styles.toCodeLoginBtnTxt,passwordLogin_styles.moreBoxTxt,]}>验证码登录</Text></TouchableOpacity><TouchableOpacityonPress={() => {Toast.show({content: "待需要时完善",});}}><Text style={passwordLogin_styles.moreBoxTxt}>忘记密码?</Text></TouchableOpacity></View><TouchableOpacitystyle={[styles.btn,canLogin? passwordLogin_styles.loginButton: passwordLogin_styles.loginButtonDisable,]}activeOpacity={canLogin ? 0.7 : 1}onPress={onLoginPress}><Text style={[styles.btnTxt]}>登录</Text></TouchableOpacity></View>);};return (<View style={styles.page}><Imagesource={require("@/assets/images/icon.png")}style={{ width: 200, height: 200, marginTop: 100 }}resizeMode="cover"/><Animated.View style={styles.LoginBox}>{ifQuickLogin ? (<><TouchableOpacitystyle={[styles.btn, styles.oneKeyLoginButton]}activeOpacity={0.7}onPress={() => {Toast.show({content: "待需要时完善",});}}><Text style={[styles.btnTxt, styles.oneKeyLoginTxt]}>一键登录</Text></TouchableOpacity><TouchableOpacitystyle={[styles.btn, styles.wxLoginButton]}activeOpacity={0.7}onPress={() => {Toast.show({content: "待需要时完善",});}}><AntDesign name="wechat" size={24} color="white" /><Text style={[styles.btnTxt]}>微信登录</Text></TouchableOpacity></>) : (render_passwordLogin())}<TouchableOpacityactiveOpacity={0.7}style={styles.moreLoginWayBox}onPress={() => {setIfQuickLogin(!ifQuickLogin);}}><Text style={[styles.moreLoginWayTxt]}>{ifQuickLogin ? "其他登录方式" : "快捷登录"}</Text><Entypo name="chevron-small-right" size={20} /></TouchableOpacity><View style={styles.moreBox}><TouchableOpacityonPress={() => {setIfRead(!ifRead);}}>{ifRead ? (<AntDesignstyle={styles.moreIcon}name="checkcircle"size={14}color="#05c160"/>) : (<Entypostyle={styles.moreIcon}name="circle"size={14}color="gray"/>)}</TouchableOpacity><Text style={styles.infoTxt}>我已阅读并同意</Text><TouchableOpacityonPress={() => {// 最终需修改为 《用户协议》 的网址Linking.openURL("https://www.baidu.com");}}><Text style={styles.linkTxt}>《用户协议》</Text></TouchableOpacity><Text style={styles.infoTxt}>和</Text><TouchableOpacityonPress={() => {// 最终需修改为 《隐私政策》 的网址Linking.openURL("https://www.baidu.com");}}><Text style={styles.linkTxt}>《隐私政策》</Text></TouchableOpacity></View></Animated.View></View>);
}
const styles = StyleSheet.create({page: {flex: 1,alignItems: "center",justifyContent: "space-between",backgroundColor: "#e8e8e7",padding: 40,},LoginBox: {width: "100%",padding: 20,marginBottom: 100,},btn: {width: "100%",height: 56,borderRadius: 28,justifyContent: "center",alignItems: "center",flexDirection: "row",marginBottom: 20,},btnTxt: {fontSize: 18,color: "white",marginBottom: 6,marginLeft: 14,},oneKeyLoginButton: {backgroundColor: "#ff2442",},oneKeyLoginTxt: {marginLeft: 4,},wxLoginButton: {backgroundColor: "#05c160",},moreBox: {flexDirection: "row",alignItems: "center",justifyContent: "center",},moreIcon: {marginRight: 4,marginTop: 2,},moreLoginWayBox: {flexDirection: "row",alignItems: "center",justifyContent: "center",marginBottom: 60,},moreLoginWayTxt: {fontSize: 16,marginBottom: 4,},infoTxt: {fontSize: 14,color: "gray",},linkTxt: {fontSize: 14,color: "blue",},
});
utils/StringUtil.ts
export function formatPhone(phone: string): string {let trim: string = phone.replace(/\s+/g, "");const result = [trim.slice(0, 3), trim.slice(3, 7), trim.slice(7, 11)].filter((item) => !!item).join(" ");return result;
}
export function replaceBlank(phone: string): string {return phone ? phone.replace(/\s+/g, "") : "";
}