feat:

优化行为验证
This commit is contained in:
john 2024-06-14 18:41:22 +08:00
parent 84d566b787
commit 637cdf9175
5 changed files with 208 additions and 235 deletions

View File

@ -1,3 +1,4 @@
import { Toast } from "antd-mobile";
import axios, { AxiosRequestConfig, AxiosRequestHeaders } from "axios";
//基础URLaxios将会自动拼接在url前
@ -85,29 +86,17 @@ const requestHandler = <T>(
return new Promise<axiosTypes<responseTypes<T>>>((resolve, reject) => {
response
.then((res) => {
if (res.data.code != 0 && res.data.code != 200) {
Toast.show({ content: res.data.msg });
}
//业务代码 可根据需求自行处理
// const data = res.data;
// if (data.code !== 200 && data.code !== 0) {
// //特定状态码 处理特定的需求
// if (data.code == 401) {
// console.log("登录异常,执行登出...");
// }
// const e = JSON.stringify(data);
// console.log(`请求错误:${e}`);
// //数据请求错误 使用reject将错误返回
// reject(data);
// } else {
// //数据请求正确 使用resolve将结果返回
// resolve(res as axiosTypes<responseTypes<T>>);
// }
resolve(res as axiosTypes<responseTypes<T>>);
})
.catch((error) => {
const e = JSON.stringify(error);
console.log(`网络错误:${e}`);
Toast.show({ content: "NetWork Error" });
reject(error);
});
});

View File

@ -1,115 +1,32 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { useState, useEffect, useRef } from "react";
import { useState, useRef } from "react";
import { useTranslation } from "react-i18next";
import { Form, Input, Button, Modal } from "antd-mobile";
import { Form, Input, Button } from "antd-mobile";
import "../pages/SignIn.scss";
import { useCountdown } from "../hooks/useCountdown";
import { sendCode, signUp } from "../api";
import { signUp } from "../api";
import { signUpTypes, ErrorType } from "../type/SignIn";
import { Toast } from "antd-mobile";
import { useNavigate } from "react-router-dom";
import { Md5 } from "ts-md5";
import { FormInstance } from "antd-mobile/es/components/form";
import useUserStore from "../store/user";
import SilderVertify, { SilderVertify_handleType } from "./SilderVertify";
import SendVerificationCode from "./SendVerificationCode";
function EmailForm() {
const formRef = useRef<FormInstance>(null);
const navigate = useNavigate();
const { t } = useTranslation();
const { start, time } = useCountdown();
const [email, setEmail] = useState("");
const [authCode, setAuthCode] = useState("");
const [shareCode, setShareCode] = useState("");
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const [remainingTime, setRemainingTime] = useState(0);
const { Lang } = useUserStore();
const SilderVertifyRef = useRef<SilderVertify_handleType>(null);
const config = {
headers: {
"Accept-Language": Lang,
},
};
const sendVerificationCode = async () => {
try {
if (email === "") {
return;
}
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
Toast.show({ content: t("Invalid email") });
return;
}
Modal.show({
content: (
<>
<SilderVertify
account={email}
ref={SilderVertifyRef}
onFinish={async (pox) => {
const timestamp = `${new Date().getTime()}`;
const res = await sendCode(
{
account: email,
areaCode: "",
status: 2,
signature: Md5.hashStr(
`Neer${email},${2},${timestamp}GetCode`
),
timestamp,
posX: pox,
},
config
);
console.log("res", res);
if (res.status === 200 && res.data.data?.sms) {
Toast.show({
content: t("send successfully"),
afterClose: () => {
start(60 * 1000);
},
});
Modal.clear();
} else {
Toast.show({
content: res.data.msg,
afterClose: () => {
// start(60 * 1000);
},
});
if (res.data.code == 1021 || res.data.code == 1015) {
SilderVertifyRef.current?._init();
return;
}
Modal.clear();
}
}}
/>
</>
),
closeOnMaskClick: true,
});
} catch (error: unknown) {
// 检查 error 是否是 ErrorType 类型
if (typeof error === "object" && error !== null && "msg" in error) {
const typedError = error as ErrorType; // 使用类型断言
Toast.show({
content: typedError.msg,
afterClose: () => {
// start(60 * 1000);
},
});
} else {
// 处理不是 ErrorType 类型的错误
console.log("An unexpected error occurred");
}
console.warn(error);
}
};
useEffect(() => {
setRemainingTime(time / 1000);
}, [time]);
const handleSignUp = async () => {
if (email === "" || authCode === "") {
@ -260,15 +177,16 @@ function EmailForm() {
onChange={(code) => setAuthCode(code)}
/>
</Form.Item>
{remainingTime === 0 ? (
<p className="send-code" onClick={sendVerificationCode}>
{t("Send verification code")}
</p>
) : (
<p className="send-code">
{remainingTime} {t("seconds")}
</p>
)}
<SendVerificationCode
account={email}
beforeSendVerificationCode={() => {
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
Toast.show({ content: t("Invalid email") });
return false;
}
return true;
}}
/>
<Form.Item name="shareCode">
<Input

View File

@ -1,7 +1,7 @@
/*
* @LastEditors: John
* @Date: 2024-06-06 21:23:46
* @LastEditTime: 2024-06-13 16:31:54
* @LastEditTime: 2024-06-14 16:26:14
* @Author: John
*/
/*
@ -10,24 +10,22 @@
* @LastEditTime: 2024-06-13 15:56:56
* @Author: John
*/
import { useState, useEffect, useRef } from "react";
import { useState, useRef } from "react";
import { useTranslation } from "react-i18next";
import { Form, Input, Button, Modal } from "antd-mobile";
import { Form, Input, Button } from "antd-mobile";
import { useNavigate } from "react-router-dom";
import "../pages/SignIn.scss";
import useUserStore from "../store/user.ts";
import { useCountdown } from "../hooks/useCountdown";
import { sendCode, signUp } from "../api";
import { signUp } from "../api";
import { signUpTypes, ErrorType } from "../type/SignIn";
import { Toast } from "antd-mobile";
import { Md5 } from "ts-md5";
import { FormInstance } from "antd-mobile/es/components/form/form";
import SilderVertify, { SilderVertify_handleType } from "./SilderVertify.tsx";
import SendVerificationCode from "./SendVerificationCode";
const defaultAreaCode = "+1";
function PhoneForm() {
const formRef = useRef<FormInstance>(null);
const { t } = useTranslation();
const { start, time } = useCountdown();
const navigate = useNavigate();
const {
SelectCountry,
@ -54,10 +52,9 @@ function PhoneForm() {
const [authCode, setAuthCode] = useState("");
const [shareCode, setShareCode] = useState("");
const [remainingTime, setRemainingTime] = useState(0);
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const SilderVertifyRef = useRef<SilderVertify_handleType>(null);
const gotoSelectCountry = () => {
UpdatePreviousPathName("/");
@ -71,80 +68,6 @@ function PhoneForm() {
"Accept-Language": Lang,
},
};
const sendVerificationCode = async () => {
try {
if (CurrentPhoneNumber === "") {
return;
}
Modal.show({
content: (
<>
<SilderVertify
account={CurrentPhoneNumber}
ref={SilderVertifyRef}
onFinish={async (pox) => {
const timestamp = `${new Date().getTime()}`;
const res = await sendCode(
{
account: CurrentPhoneNumber,
areaCode: SelectCountry?.code || defaultAreaCode,
status: 2,
signature: Md5.hashStr(
`Neer${CurrentPhoneNumber},${2},${timestamp}GetCode`
),
timestamp,
posX: pox,
},
config
);
if (res.status === 200 && res.data.data?.sms) {
Toast.show({
content: t("send successfully"),
afterClose: () => {
start(60 * 1000);
},
});
Modal.clear();
} else {
Toast.show({
content: res.data.msg,
afterClose: () => {
// start(60 * 1000);
},
});
if (res.data.code == 1021 || res.data.code == 1015) {
SilderVertifyRef.current?._init();
return;
}
Modal.clear();
}
}}
/>
</>
),
closeOnMaskClick: true,
});
} catch (error: unknown) {
// 检查 error 是否是 ErrorType 类型
if (typeof error === "object" && error !== null && "msg" in error) {
const typedError = error as ErrorType; // 使用类型断言
Toast.show({
content: typedError.msg,
afterClose: () => {
// start(60 * 1000);
},
});
} else {
// 处理不是 ErrorType 类型的错误
console.log("An unexpected error occurred");
}
console.warn(error);
}
};
useEffect(() => {
setRemainingTime(time / 1000);
}, [time]);
const handleSignUp = async () => {
if (CurrentPhoneNumber === "" || authCode === "") {
@ -305,15 +228,17 @@ function PhoneForm() {
onChange={(code) => setAuthCode(code)}
/>
</Form.Item>
{remainingTime === 0 ? (
<p className="send-code" onClick={sendVerificationCode}>
{t("Send verification code")}
</p>
) : (
<p className="send-code">
{remainingTime} {t("seconds")}
</p>
)}
<SendVerificationCode
account={CurrentPhoneNumber}
areaCode={SelectCountry?.code || defaultAreaCode}
beforeSendVerificationCode={() => {
if (CurrentPhoneNumber == "") {
Toast.show({ content: t("Invalid phone number") });
return false;
}
return true;
}}
/>
<Form.Item name="shareCode">
<Input
placeholder={t("Please enter the invitation code (optional)")}

View File

@ -0,0 +1,167 @@
import { DotLoading, Modal, Toast } from "antd-mobile";
import { useEffect, useRef, useState } from "react";
import SilderVertify, { SilderVertify_handleType } from "./SilderVertify";
import { useCountdown } from "../hooks/useCountdown";
import useUserStore from "../store/user";
import { sendCode } from "../api";
import { Md5 } from "ts-md5";
import { useTranslation } from "react-i18next";
import { ErrorType } from "../type/SignIn";
export default function ({
account,
areaCode,
beforeSendVerificationCode,
}: {
account: string;
areaCode?: string;
beforeSendVerificationCode?: () => boolean;
}) {
const [remainingTime, setRemainingTime] = useState(0);
const { start, time } = useCountdown();
const { t } = useTranslation();
const { Lang } = useUserStore();
const [bigImage, setBigImage] = useState<string>("");
const [smallImage, setSmallImage] = useState<string>("");
const [smallImageY, setSmallImageY] = useState(0);
const SilderVertifyRef = useRef<SilderVertify_handleType>(null);
const [visible, setVisible] = useState(false);
const [sendingverfityCodeLoading, setSendingverfityCodeLoading] =
useState(false);
const config = {
headers: {
"Accept-Language": Lang,
},
};
async function getVerfityCode() {
return new Promise<void>(async (reslove, _reject) => {
const timestamp = `${new Date().getTime()}`;
if (sendingverfityCodeLoading) return;
setSendingverfityCodeLoading(true);
try {
const res = await sendCode(
{
account: account,
areaCode,
status: 2,
signature: Md5.hashStr(`Neer${account},${2},${timestamp}GetCode`),
timestamp,
},
config
);
if (res.status == 200 && res.data.code == 0) {
setBigImage(res.data.data?.captcha?.bigImageBase64 || "");
setSmallImage(res.data.data?.captcha?.smallImageBase64 || "");
setSmallImageY(res.data.data?.captcha?.posY || 0);
reslove();
}
} catch (error) {}
setSendingverfityCodeLoading(false);
});
}
const sendVerificationCode = async () => {
try {
if (beforeSendVerificationCode && !beforeSendVerificationCode?.()) {
return;
}
await getVerfityCode();
setVisible(true);
} catch (error: unknown) {
// 检查 error 是否是 ErrorType 类型
if (typeof error === "object" && error !== null && "msg" in error) {
const typedError = error as ErrorType; // 使用类型断言
Toast.show({
content: typedError.msg,
afterClose: () => {
// start(60 * 1000);
},
});
} else {
// 处理不是 ErrorType 类型的错误
console.log("An unexpected error occurred");
}
console.warn(error);
}
};
useEffect(() => {
setRemainingTime(time / 1000);
}, [time]);
return (
<>
{remainingTime === 0 ? (
<p className="send-code" onClick={sendVerificationCode}>
{t("Send verification code")}
</p>
) : (
<p className="send-code">
{remainingTime} {t("seconds")}
</p>
)}
<Modal
visible={visible}
content={
<>
<SilderVertify
bigImage={bigImage}
smallImage={smallImage}
smallImageY={smallImageY}
ref={SilderVertifyRef}
onFinish={async (pox) => {
const timestamp = `${new Date().getTime()}`;
const res = await sendCode(
{
account: account,
areaCode,
status: 2,
signature: Md5.hashStr(
`Neer${account},${2},${timestamp}GetCode`
),
timestamp,
posX: pox,
},
config
);
if (res.status === 200 && res.data.data?.sms) {
Toast.show({
content: t("send successfully"),
afterClose: () => {
start(60 * 1000);
},
});
setVisible(false);
} else {
Toast.show({
content: res.data.msg,
});
if (res.data.code == 1021 || res.data.code == 1015) {
await getVerfityCode();
SilderVertifyRef.current?._init();
return;
}
setVisible(false);
}
}}
/>
</>
}
closeOnMaskClick={true}
onClose={() => {
setVisible(false);
}}
/>
<Modal
visible={sendingverfityCodeLoading}
content={
<>
<DotLoading />
</>
}
closeOnMaskClick={false}
onClose={() => {
setSendingverfityCodeLoading(false);
}}
/>
</>
);
}

View File

@ -1,14 +1,11 @@
import { useDrag } from "@use-gesture/react";
import { forwardRef, useState, useImperativeHandle, useEffect } from "react";
import { Md5 } from "ts-md5";
import { sendCode } from "../api";
import useUserStore from "../store/user";
import { useTranslation } from "react-i18next";
/*
* @LastEditors: John
* @Date: 2024-06-06 16:41:24
* @LastEditTime: 2024-06-06 21:07:00
* @LastEditTime: 2024-06-14 16:35:28
* @Author: John
*/
export type SilderVertify_handleType = {
@ -17,10 +14,12 @@ export type SilderVertify_handleType = {
const SilderVertify = forwardRef<
SilderVertify_handleType,
{
account: string;
bigImage: string;
smallImage: string;
smallImageY: number;
onFinish: (pox: number) => Promise<void>;
}
>(function ({ onFinish, account }, ref) {
>(function ({ bigImage, smallImage, smallImageY, onFinish }, ref) {
const [offsetX, setOffsetX] = useState(0);
// Set the drag hook and define component movement based on gesture data.
const bind = useDrag(async ({ event, down, movement: [mx, _] }) => {
@ -36,10 +35,6 @@ const SilderVertify = forwardRef<
}
});
const [bigImage, setBigImage] = useState<string>("");
const [smallImage, setSmallImage] = useState<string>("");
const [smallImageY, setSmallImageY] = useState(0);
const { SelectCountry, Lang } = useUserStore();
const { t } = useTranslation();
useImperativeHandle(ref, () => {
return {
@ -49,29 +44,8 @@ const SilderVertify = forwardRef<
};
});
const config = {
headers: {
"Accept-Language": Lang,
},
};
async function init() {
setOffsetX(0);
const timestamp = `${new Date().getTime()}`;
const res = await sendCode(
{
account: account,
areaCode: SelectCountry?.code || "+1",
status: 2,
signature: Md5.hashStr(`Neer${account},${2},${timestamp}GetCode`),
timestamp,
},
config
);
if (res.status == 200 && res.data.code == 0) {
setBigImage(res.data.data?.captcha?.bigImageBase64 || "");
setSmallImage(res.data.data?.captcha?.smallImageBase64 || "");
setSmallImageY(res.data.data?.captcha?.posY || 0);
}
}
useEffect(() => {
init();