tiefen_space_h5/app/login/page.js

409 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import React, { useState, useRef, useEffect } from "react";
import {
Input,
Button,
Swiper,
Tabs,
Divider,
Checkbox,
Toast,
} from "antd-mobile";
import { useRouter } from "next/navigation";
import styles from "./index.module.css";
import { JSEncrypt } from "jsencrypt";
import { handleLogin } from "@/store/actions";
import { saveUserInfo,removeUserInfo } from "@/utils/storeInfo";
import { connect } from "react-redux";
import { cryptoPassword } from "@/utils/crypto";
import require from "@/utils/require";
import {signOut,signIn} from "@/utils/auth";
/*
params格式
{
mid: item.mid,
}
*/
const tabItems = [
{ key: "code", title: "验证码登录" },
{ key: "password", title: "帐号密码登录" },
];
function Login({ handleLogin }) {
const [activeIndex, setActiveIndex] = useState(0);
const [veriCode, setVeriCode] = useState("");
const [isCounting, setIsCounting] = useState(false);
const [seconds, setSeconds] = useState(60);
const [loginInfo, setLoginInfo] = useState({
mobilePhone: "",
regionCode: "86",
password: "",
checked: false,
});
const router = useRouter();
const swiperRef = useRef(null);
useEffect(() => {
handleLogin({ isSignin: false, userToken: null });
signOut()
removeUserInfo();
},[])
useEffect(() => {
let interval;
if (isCounting && seconds > 0) {
interval = setInterval(() => {
setSeconds(seconds - 1);
}, 1000);
} else {
setIsCounting(false);
setSeconds(60);
clearInterval(interval);
}
return () => {
clearInterval(interval);
};
}, [isCounting, seconds]);
const handleSubmit = async (type) => {
const { mobilePhone, password, regionCode, checked } = loginInfo;
//验证数据格式
if (!checked) {
Toast.show({
icon: "fail",
content: "请先阅读并同意《用户协议》和《隐私政策》后登录",
position: "top",
});
return;
}
if (!mobilePhone.match(/^1[3456789]\d{9}$/)) {
Toast.show({
icon: "fail",
content: "手机号码格式错误",
position: "top",
});
return;
}
if (type === "password") {
if (password.length < 8) {
Toast.show({
icon: "fail",
content: "密码不得小于8位",
position: "top",
});
return;
}
if (password.length > 15) {
Toast.show({
icon: "fail",
content: "密码不得大于15位",
position: "top",
});
return;
}
} else {
if (veriCode.length !== 6) {
Toast.show({
icon: "fail",
content: "请输入正确的验证码",
position: "top",
});
return;
}
}
//对手机号进行RSA加密
const encrypt = new JSEncrypt();
encrypt.setPublicKey(process.env.NEXT_PUBLIC_RSA_KEY);
const mobile_phone = encrypt.encrypt(mobilePhone);
//MD5加密password
const encryptedPassword = cryptoPassword(password);
//发送登录请求
let body = {
mobile_phone,
region_code: regionCode,
};
body =
type === "password"
? {
...body,
password: encryptedPassword,
}
: {
...body,
code: veriCode,
};
try {
const data = await require("POST", `/api/login/${
type === "password" ? "login_by_pswd" : "login_by_veri_code"
}`, {
body,
});
if (data.ret === -1) {
Toast.show({
icon: "fail",
content: data.msg,
position: "top",
});
return;
}
//登录
saveUserInfo(data, mobilePhone, regionCode);
signIn(data);
handleLogin({ isSignin: true, userToken: data.data.token });
router.push("/");
} catch (error) {
console.error(error);
}
};
//点击获取验证码
const handleVerification = async () => {
const { mobilePhone, regionCode, checked } = loginInfo;
if (!checked) {
Toast.show({
icon: "fail",
content: "请先阅读并同意《用户协议》和《隐私政策》后登录",
position: "top",
});
return;
}
//手机号校验
if (!mobilePhone.match(/^1[3456789]\d{9}$/)) {
Toast.show({
icon: "fail",
content: "手机号码格式错误",
position: "top",
});
return;
}
//开始倒计时
setIsCounting(true);
//对手机号进行RSA加密
const encrypt = new JSEncrypt();
encrypt.setPublicKey(process.env.NEXT_PUBLIC_RSA_KEY);
const mobile_phone = encrypt.encrypt(mobilePhone);
//发送短信验证码
try {
await fetch(`/api/veri_code/send`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
mobile_phone,
region_code: regionCode,
}),
});
} catch (error) {
console.error(error);
}
};
return (
<div className={`${styles.loginBox}`}>
<div className="mt-32 flex justify-between items-center px-2 text-gray-400 sticky top-0 z-10 bg-deepBg">
<Tabs
activeKey={tabItems[activeIndex].key}
onChange={(key) => {
const index = tabItems.findIndex((item) => item.key === key);
setActiveIndex(index);
swiperRef.current?.swipeTo(index);
}}
className={`w-full ${styles.customTabs}`}
>
{tabItems.map((item) => (
<Tabs.Tab
forceRender={false}
title={item.title}
key={item.key}
className="text-left"
/>
))}
</Tabs>
</div>
<Swiper
className="overflow-visible mt-6 "
direction="horizontal"
loop
indicator={() => null}
ref={swiperRef}
defaultIndex={activeIndex}
onIndexChange={(index) => {
setActiveIndex(index);
}}
>
<Swiper.Item className="px-10">
<div className="border-2 border-[#2c2b2f] rounded-2xl p-4">
<div className="flex flex-row flex-nowrap items-center mb-4">
<p className="text-base text-white mr-4">
+{loginInfo.regionCode}
</p>
<Input
clearable
placeholder="请输入手机号"
// disabled={true}
type="number"
maxLength={11}
onChange={(value) =>
setLoginInfo({ ...loginInfo, mobilePhone: value })
}
value={loginInfo.mobilePhone}
style={{ "--color": "#FFFFFF", "--font-size": "16px" }}
/>
</div>
<Divider />
<div className="flex flex-row flex-nowrap items-center">
<p className="text-base text-white mr-4 whitespace-nowrap">
密码
</p>
<Input
clearable
placeholder="请输入密码"
onChange={(value) => setLoginInfo({ ...loginInfo, password: value})}
value={loginInfo.password}
type="password"
style={{ "--color": "#FFFFFF", "--font-size": "16px" }}
/>
</div>
<Divider />
<div className="flex flex-row flex-nowrap items-center">
<p className="text-base text-white mr-4 whitespace-nowrap">
验证码
</p>
<Input
placeholder="请输入验证码"
onChange={(value) => setVeriCode(value)}
value={veriCode}
type="number"
style={{
"--placeholder-color": "#FFFFFF80",
"--font-size": "16px",
}}
/>
<Button
shape="rounded"
size="mini"
disabled={isCounting}
onClick={handleVerification}
style={{ "--background-color": "#FF669E", color: "#FFFFFF" }}
className="whitespace-nowrap"
>
{isCounting ? `(${seconds})重新发送` : "获取验证码"}
</Button>
</div>
</div>
<LoginBtn
loginInfo={loginInfo}
setLoginInfo={setLoginInfo}
handleSubmit={handleSubmit}
type={activeIndex ? "password" : "mobile"}
/>
</Swiper.Item>
<Swiper.Item className="px-10">
<div className="border-2 border-[#2c2b2f] rounded-2xl p-4">
<div className="flex flex-row flex-nowrap items-center mb-4">
<p className="text-base text-white mr-4">
+{loginInfo.regionCode}
</p>
<Input
clearable
placeholder="请输入手机号"
// disabled={true}
type="number"
maxLength={11}
onChange={(value) =>
setLoginInfo({ ...loginInfo, mobilePhone: value })
}
value={loginInfo.mobilePhone}
style={{ "--color": "#FFFFFF", "--font-size": "16px" }}
/>
</div>
<Divider />
<div className="flex flex-row flex-nowrap items-center">
<p className="text-base text-white mr-4 whitespace-nowrap">
密码
</p>
<Input
clearable
placeholder="请输入密码"
onChange={(value) =>
setLoginInfo({ ...loginInfo, password: value })
}
value={loginInfo.password}
type="password"
style={{
"--placeholder-color": "#FFFFFF80",
"--font-size": "16px",
}}
/>
</div>
</div>
<LoginBtn
loginInfo={loginInfo}
setLoginInfo={setLoginInfo}
handleSubmit={handleSubmit}
type={activeIndex ? "password" : "mobile"}
/>
</Swiper.Item>
</Swiper>
</div>
);
}
const LoginBtn = ({ loginInfo, setLoginInfo, type, handleSubmit }) => {
const router = useRouter();
useEffect(() => {
console.log("loginInfo", loginInfo);
}, []);
return (
<div className="mt-16">
<div className="flex items-center">
<Checkbox
value={loginInfo?.checked}
onChange={(value) => setLoginInfo({ ...loginInfo, checked: value })}
style={{
"--icon-size": "14px",
"--font-size": "14px",
"--gap": "6px",
}}
></Checkbox>
<span className="text-[#FFFFFF80] font-medium text-xs ml-2">
我确认已满18周岁并同意
<span
onClick={() =>
router.push(
`${process.env.NEXT_PUBLIC_WEB_URL}/doc/useragreement`
)
}
className="text-[#FF669E] text-xs"
>
用户协议
</span>
<span
onClick={() =>
router.push(
`${process.env.NEXT_PUBLIC_WEB_URL}/doc/useragreement`
)
}
className="text-[#FF669E] text-xs"
>
隐私政策
</span>
</span>
</div>
<Button
shape="rounded"
size="middle"
block
onClick={() => handleSubmit(type)}
style={{ "--background-color": "#FF669E", color: "#FFFFFF" }}
className="mt-2"
>
登录
</Button>
</div>
);
};
const mapDispatchToProps = {
handleLogin,
};
export default connect(null, mapDispatchToProps)(Login);