feat: 验证码组件
This commit is contained in:
parent
1f7a101fc5
commit
f7146adf13
@ -1,10 +1,11 @@
|
||||
import Error from "@/assets/icons/svg/error.svg";
|
||||
import { fetchApi } from "@/lib/server-api-util";
|
||||
import { User } from "@/types/user";
|
||||
import OTPInputView from '@twotalltotems/react-native-otp-input';
|
||||
import { router } from "expo-router";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ActivityIndicator, Animated, TextInput as RNTextInput, TextInput, TouchableOpacity, View } from "react-native";
|
||||
import { Animated, TextInput as RNTextInput, StyleSheet, TouchableOpacity, View } from "react-native";
|
||||
import { useAuth } from "../../contexts/auth-context";
|
||||
import { ThemedText } from "../ThemedText";
|
||||
|
||||
@ -18,7 +19,7 @@ const Code = ({ phone }: CodeProps) => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const refs = useRef<Array<RNTextInput | null>>(Array(6).fill(null));
|
||||
const shakeAnim = useRef(new Animated.Value(0)).current;
|
||||
const [code, setCode] = useState<string[]>(['', '', '', '', '', '']);
|
||||
const [code, setCode] = useState<string[]>([]);
|
||||
const [error, setError] = useState<string>('');
|
||||
|
||||
const focusNext = (index: number, value: string) => {
|
||||
@ -111,7 +112,7 @@ const Code = ({ phone }: CodeProps) => {
|
||||
}, [countdown]);
|
||||
|
||||
return (
|
||||
<View className="flex-1 bg-white px-6">
|
||||
<View className="flex-1 bg-white px-2">
|
||||
<View className="flex-1 justify-center">
|
||||
<View className="items-center mb-8">
|
||||
<ThemedText className="text-2xl font-semibold mb-2 text-gray-900">
|
||||
@ -125,60 +126,28 @@ const Code = ({ phone }: CodeProps) => {
|
||||
</ThemedText>
|
||||
</View>
|
||||
|
||||
<Animated.View
|
||||
style={{
|
||||
transform: [{ translateX: shakeAnim }],
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
gap: 24,
|
||||
marginBottom: 16,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
<OTPInputView
|
||||
pinCount={6} // 验证码长度
|
||||
onCodeChanged={(code) => {
|
||||
setCode([code]);
|
||||
}}
|
||||
>
|
||||
{code.map((digit, index) => (
|
||||
<TextInput
|
||||
key={index}
|
||||
ref={(ref) => {
|
||||
if (ref) {
|
||||
refs.current[index] = ref;
|
||||
}
|
||||
}}
|
||||
style={{ width: 40, height: 40 }}
|
||||
className="bg-[#FFF8DE] rounded-xl text-textTertiary text-3xl text-center"
|
||||
keyboardType="number-pad"
|
||||
maxLength={6}
|
||||
value={digit}
|
||||
onChangeText={text => handleCodeChange(text, index)}
|
||||
onKeyPress={({ nativeEvent }) => focusPrevious(index, nativeEvent.key)}
|
||||
selectTextOnFocus
|
||||
caretHidden={true}
|
||||
autoFocus={index === 0}
|
||||
textContentType="oneTimeCode"
|
||||
autoComplete="one-time-code"
|
||||
importantForAutofill="yes"
|
||||
/>
|
||||
))}
|
||||
</Animated.View>
|
||||
onCodeFilled={(code) => {
|
||||
handleTelLogin()
|
||||
}}
|
||||
code={code.join('')}
|
||||
autoFocusOnLoad={false}
|
||||
codeInputFieldStyle={styles.underlineStyleBase}
|
||||
codeInputHighlightStyle={styles.underlineStyleHighLighted}
|
||||
style={styles.otpContainer}
|
||||
placeholderCharacter="-"
|
||||
placeholderTextColor="#AC7E35"
|
||||
/>
|
||||
<View className={`w-full flex-row justify-end mb-[1rem] items-center ${error ? 'opacity-100' : 'opacity-0'}`}>
|
||||
<Error />
|
||||
<ThemedText className="text-base font-medium !text-buttonFill ml-2">
|
||||
{error}
|
||||
</ThemedText>
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
className="bg-buttonFill py-3 rounded-full items-center justify-center"
|
||||
onPress={handleTelLogin}
|
||||
disabled={isLoading}
|
||||
>
|
||||
{isLoading ? (
|
||||
<ActivityIndicator color="#ffffff" />
|
||||
) : (
|
||||
<ThemedText className="!text-white font-medium text-base">
|
||||
{t("auth.telLogin.continue", { ns: 'login' })}
|
||||
</ThemedText>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
|
||||
<View className="flex-row justify-center mt-4">
|
||||
<ThemedText className="!text-textPrimary">
|
||||
@ -201,5 +170,32 @@ const Code = ({ phone }: CodeProps) => {
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
padding: 20,
|
||||
},
|
||||
otpContainer: {
|
||||
width: '100%',
|
||||
height: 100
|
||||
},
|
||||
underlineStyleBase: {
|
||||
width: 50,
|
||||
height: 50,
|
||||
borderWidth: 0,
|
||||
borderRadius: 16,
|
||||
fontSize: 18,
|
||||
color: '#000',
|
||||
textAlign: 'center',
|
||||
backgroundColor: '#FFF8DE'
|
||||
},
|
||||
underlineStyleHighLighted: {
|
||||
borderColor: '#E2793F',
|
||||
backgroundColor: '#FFF8DE',
|
||||
borderWidth: 2,
|
||||
},
|
||||
});
|
||||
|
||||
export default Code
|
||||
Loading…
x
Reference in New Issue
Block a user