| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346 |
- import { Images } from "@/constants/images";
- import { getUserInfo, realNameAuth, UserInfo } from "@/services/user";
- import { useFocusEffect, useRouter } from "expo-router";
- import React, { useCallback, useState } from "react";
- import {
- Alert,
- ImageBackground,
- KeyboardAvoidingView,
- Platform,
- ScrollView,
- StatusBar,
- StyleSheet,
- Text,
- TextInput,
- TouchableOpacity,
- View,
- } from "react-native";
- import { useSafeAreaInsets } from "react-native-safe-area-context";
- const NAME_REG = /^[\u4e00-\u9fa5]{2,10}$/;
- const ID_REG = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;
- const maskName = (name: string) => {
- if (!name) return "";
- if (name.length <= 1) return name;
- return name[0] + "*".repeat(name.length - 1);
- };
- const maskIdNum = (id: string) => {
- if (!id) return "";
- if (id.length <= 8) return id;
- return id.slice(0, 3) + "*".repeat(id.length - 7) + id.slice(-4);
- };
- export default function CertificationScreen() {
- const router = useRouter();
- const insets = useSafeAreaInsets();
- const [user, setUser] = useState<UserInfo | null>(null);
- const [idName, setIdName] = useState("");
- const [idNum, setIdNum] = useState("");
- const [loading, setLoading] = useState(false);
- const [submitting, setSubmitting] = useState(false);
- const loadUser = useCallback(async () => {
- setLoading(true);
- try {
- const info = await getUserInfo();
- setUser(info);
- } catch (e) {
- console.error("获取用户信息失败:", e);
- } finally {
- setLoading(false);
- }
- }, []);
- useFocusEffect(
- useCallback(() => {
- loadUser();
- }, [loadUser]),
- );
- const isVerified = user?.realNameFlag === 1;
- const handleBack = () => router.back();
- const handleSubmit = async () => {
- const trimName = idName.trim();
- const trimId = idNum.trim();
- if (!trimName) {
- Alert.alert("提示", "请输入真实姓名");
- return;
- }
- if (!NAME_REG.test(trimName)) {
- Alert.alert("提示", "请输入正确的姓名格式(2-10个中文字符)");
- return;
- }
- if (!trimId) {
- Alert.alert("提示", "请输入身份证号");
- return;
- }
- if (!ID_REG.test(trimId)) {
- Alert.alert("提示", "请输入正确的身份证号码");
- return;
- }
- try {
- setSubmitting(true);
- const ok = await realNameAuth(trimName, trimId);
- if (ok) {
- Alert.alert("提示", "提交成功", [
- { text: "确定", onPress: () => router.back() },
- ]);
- }
- } catch (e) {
- console.error("提交实名认证失败:", e);
- } finally {
- setSubmitting(false);
- }
- };
- return (
- <ImageBackground
- source={{ uri: Images.mine.kaixinMineBg }}
- style={styles.container}
- resizeMode="cover"
- >
- <StatusBar barStyle="light-content" />
- <View style={[styles.header, { paddingTop: insets.top }]}>
- <TouchableOpacity style={styles.backBtn} onPress={handleBack}>
- <Text style={styles.backIcon}>‹</Text>
- </TouchableOpacity>
- <Text style={styles.headerTitle}>实名认证</Text>
- <View style={styles.placeholder} />
- </View>
- <KeyboardAvoidingView
- style={{ flex: 1 }}
- behavior={Platform.OS === "ios" ? "padding" : undefined}
- >
- <ScrollView
- style={styles.scrollView}
- showsVerticalScrollIndicator={false}
- keyboardShouldPersistTaps="handled"
- >
- <View style={styles.content}>
- <View style={styles.card}>
- {loading ? (
- <Text style={styles.loadingText}>加载中...</Text>
- ) : isVerified ? (
- <>
- <View style={styles.verifiedBanner}>
- <Text style={styles.verifiedBannerText}>✓ 已完成实名认证</Text>
- </View>
- <View style={styles.formItem}>
- <Text style={styles.label}>真实姓名</Text>
- <View style={styles.readonly}>
- <Text style={styles.readonlyText}>
- {maskName(user?.idName || "")}
- </Text>
- </View>
- </View>
- <View style={styles.formItem}>
- <Text style={styles.label}>身份证号</Text>
- <View style={styles.readonly}>
- <Text style={styles.readonlyText}>
- {maskIdNum(user?.idNum || "")}
- </Text>
- </View>
- </View>
- <View style={styles.tips}>
- <Text style={styles.tipsIcon}>ⓘ</Text>
- <Text style={styles.tipsText}>
- 实名信息已提交,不可修改
- </Text>
- </View>
- </>
- ) : (
- <>
- <View style={styles.formItem}>
- <Text style={styles.label}>真实姓名</Text>
- <TextInput
- style={styles.input}
- value={idName}
- onChangeText={setIdName}
- placeholder="请输入您的真实姓名"
- placeholderTextColor="#bbb"
- maxLength={20}
- />
- </View>
- <View style={styles.formItem}>
- <Text style={styles.label}>身份证号</Text>
- <TextInput
- style={styles.input}
- value={idNum}
- onChangeText={setIdNum}
- placeholder="请输入您的身份证号码"
- placeholderTextColor="#bbb"
- maxLength={18}
- autoCapitalize="characters"
- />
- </View>
- <View style={styles.tips}>
- <Text style={styles.tipsIcon}>⚠️</Text>
- <Text style={styles.tipsText}>
- 请确保您填写的信息真实有效,提交后将无法修改
- </Text>
- </View>
- <TouchableOpacity
- style={[styles.submitBtn, submitting && styles.submitBtnDisabled]}
- onPress={handleSubmit}
- disabled={submitting}
- activeOpacity={0.8}
- >
- <Text style={styles.submitBtnText}>
- {submitting ? "提交中..." : "提交"}
- </Text>
- </TouchableOpacity>
- </>
- )}
- </View>
- </View>
- </ScrollView>
- </KeyboardAvoidingView>
- </ImageBackground>
- );
- }
- const styles = StyleSheet.create({
- container: {
- flex: 1,
- },
- header: {
- flexDirection: "row",
- alignItems: "center",
- justifyContent: "space-between",
- paddingHorizontal: 10,
- height: 80,
- },
- backBtn: {
- width: 40,
- height: 40,
- justifyContent: "center",
- alignItems: "center",
- },
- backIcon: {
- fontSize: 32,
- color: "#fff",
- fontWeight: "bold",
- },
- headerTitle: {
- fontSize: 16,
- fontWeight: "bold",
- color: "#fff",
- },
- placeholder: {
- width: 40,
- },
- scrollView: {
- flex: 1,
- },
- content: {
- paddingHorizontal: 15,
- paddingTop: 10,
- paddingBottom: 30,
- },
- card: {
- backgroundColor: "#fff",
- borderRadius: 15,
- padding: 20,
- },
- loadingText: {
- textAlign: "center",
- color: "#999",
- paddingVertical: 40,
- },
- verifiedBanner: {
- backgroundColor: "#e6f9ef",
- borderRadius: 8,
- paddingVertical: 10,
- paddingHorizontal: 14,
- marginBottom: 20,
- alignItems: "center",
- },
- verifiedBannerText: {
- color: "#1aad5a",
- fontSize: 14,
- fontWeight: "600",
- },
- formItem: {
- marginBottom: 20,
- },
- label: {
- fontSize: 14,
- color: "#333",
- fontWeight: "500",
- marginBottom: 10,
- },
- input: {
- width: "100%",
- height: 40,
- paddingHorizontal: 10,
- borderWidth: 1,
- borderColor: "#e5e5e5",
- borderRadius: 5,
- fontSize: 14,
- color: "#333",
- backgroundColor: "#f8f8f8",
- },
- readonly: {
- width: "100%",
- height: 40,
- paddingHorizontal: 10,
- borderWidth: 1,
- borderColor: "#e5e5e5",
- borderRadius: 5,
- backgroundColor: "#f5f5f5",
- justifyContent: "center",
- },
- readonlyText: {
- fontSize: 14,
- color: "#666",
- letterSpacing: 1,
- },
- tips: {
- flexDirection: "row",
- alignItems: "flex-start",
- padding: 10,
- marginBottom: 20,
- backgroundColor: "#fff9e6",
- borderRadius: 5,
- borderLeftWidth: 2,
- borderLeftColor: "#ffdd02",
- },
- tipsIcon: {
- fontSize: 14,
- marginRight: 6,
- },
- tipsText: {
- flex: 1,
- fontSize: 12,
- color: "#ff9900",
- lineHeight: 18,
- },
- submitBtn: {
- backgroundColor: "#FC7D2E",
- borderRadius: 8,
- paddingVertical: 13,
- alignItems: "center",
- },
- submitBtnDisabled: {
- opacity: 0.6,
- },
- submitBtnText: {
- color: "#fff",
- fontSize: 16,
- fontWeight: "600",
- },
- });
|