轻松聘
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

139 lines
5.2 KiB

import { useEffect, useRef, useState } from 'react';
import { Alert, Pressable, StyleSheet, Text, TextInput, View } from 'react-native';
import { colors } from '../../../src/theme';
import { SMS_HELP_BODY, SMS_HELP_TITLE } from '../../../src/constants';
import { useOnboarding } from '../../../src/hooks';
import { ScreenShell } from '../ui/ScreenShell';
import { PrimaryButton } from '../ui/PrimaryButton';
const RESEND_SECONDS = 60;
/** P2W 微信登录短信验证码页:×关闭 + 4格验证码 + 重新发送倒计时 + 语音/收不到短信入口 */
export function P2WechatCode() {
const { data, navigate, back } = useOnboarding();
const [code, setCode] = useState('');
const [left, setLeft] = useState(RESEND_SECONDS);
const timer = useRef<ReturnType<typeof setInterval> | null>(null);
/** 隐藏输入框的引用:点格子区域时把焦点交回它,键盘才会弹出来 */
const inputRef = useRef<TextInput>(null);
useEffect(() => {
timer.current = setInterval(() => {
setLeft((l) => {
if (l <= 1) {
if (timer.current) clearInterval(timer.current);
return 0;
}
return l - 1;
});
}, 1000);
return () => {
if (timer.current) clearInterval(timer.current);
};
}, []);
const phone = data.phone;
/**
* 手机尾号(含两侧空格,无尾号时为空串,句子里不留空)。
* 原先是 `phone.length === 11 ? phone.slice(7) : '9367'` —— 没有号码时拿编造的
* 「9367」当用户的号码展示。
*/
const tail = phone.length === 11 ? ` ${phone.slice(7)} ` : '';
const canNext = code.length >= 4;
/** 「接收不到短信」提示,与 P2 用同一份文案常量 */
const showSmsHelp = () => Alert.alert(SMS_HELP_TITLE, SMS_HELP_BODY);
const resend = () => {
if (left > 0) return;
setLeft(RESEND_SECONDS);
setCode('');
timer.current = setInterval(() => {
setLeft((l) => {
if (l <= 1) {
if (timer.current) clearInterval(timer.current);
return 0;
}
return l - 1;
});
}, 1000);
};
return (
<ScreenShell showBack onBack={back} backIcon="close">
<Text style={styles.h1}>输入短信验证码</Text>
<Text style={styles.subtitle}>已向您的手机{tail}发送验证码</Text>
{/* 4 格分离输入:透明输入层覆盖,格子仅做展示;点格子把焦点交回输入框 */}
<Pressable style={styles.cellsWrap} onPress={() => inputRef.current?.focus()}>
<View style={styles.cellsRow}>
{[0, 1, 2, 3].map((i) => (
<View key={i} style={styles.cell}>
<Text style={styles.cellText}>{code[i] ?? ''}</Text>
<View style={[styles.cellLine, code.length === i && styles.cellLineActive]} />
</View>
))}
</View>
<TextInput
ref={inputRef}
style={styles.hiddenInput}
value={code}
onChangeText={(t) => setCode(t.replace(/[^\d]/g, '').slice(0, 4))}
keyboardType="number-pad"
maxLength={4}
autoFocus
/>
</Pressable>
<Pressable onPress={resend} hitSlop={6} disabled={left > 0}>
<Text style={[styles.resend, left > 0 && styles.resendDisabled]}>
{left > 0 ? `重新发送(${left}s)` : '重新发送'}
</Text>
</Pressable>
<View style={styles.btnArea}>
<PrimaryButton label="下一步" active={canNext} onPress={() => navigate('B1')} />
</View>
{/* 「语音接听验证码」入口已移除:项目没有语音验证码通道,摆一个点了没反应的入口
等于宣称有这个能力。等接入语音通道再恢复(同 P5 移除「自动定位」的理由) */}
<Pressable onPress={showSmsHelp} hitSlop={6} style={styles.helpRow}>
<Text style={styles.helpText}>接收不到短信?</Text>
</Pressable>
<View style={styles.toastBar}>
<Text style={styles.toastText}>短信验证码正在发送,请注意查收</Text>
</View>
</ScreenShell>
);
}
const styles = StyleSheet.create({
h1: { fontSize: 24, fontWeight: '800', color: colors.textPrimary, marginTop: 14 },
subtitle: { fontSize: 14, color: colors.textSecondary, marginTop: 12 },
cellsWrap: { marginTop: 34 },
cellsRow: { flexDirection: 'row', gap: 18 },
cell: { flex: 1, alignItems: 'center' },
cellText: { fontSize: 26, fontWeight: '700', color: colors.textPrimary, minHeight: 36 },
cellLine: { width: '100%', height: 2, backgroundColor: colors.border, marginTop: 8 },
cellLineActive: { backgroundColor: colors.primary },
hiddenInput: {
...StyleSheet.absoluteFill,
opacity: 0,
},
resend: { textAlign: 'center', fontSize: 14, color: colors.textSecondary, marginTop: 22 },
resendDisabled: { color: colors.textHint },
btnArea: { marginTop: 30 },
helpRow: { alignSelf: 'flex-start', marginTop: 22 },
// 已是可点入口,用可读对比度的 primaryText(textHint 在 13pt 下不达 AA)
helpText: { fontSize: 13, color: colors.primaryText },
toastBar: {
alignSelf: 'center',
marginTop: 30,
paddingHorizontal: 18,
paddingVertical: 10,
borderRadius: 8,
backgroundColor: colors.borderLight,
},
toastText: { fontSize: 13, color: colors.textDisabled },
});