tiefen_space_h5/app/login/page.js

540 lines
16 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,
Image,
} from "antd-mobile";
import { useRouter } from "next/navigation";
import styles from "./index.module.scss";
import { JSEncrypt } from "jsencrypt";
import { handleLogin } from "@/store/actions";
import { saveUserInfo, get, save } from "@/utils/storeInfo";
import { connect } from "react-redux";
import { cryptoPassword } from "@/utils/crypto";
import requireAPI from "@/utils/requireAPI";
import { signOut, signIn, checkAuth } from "@/utils/auth";
import OwnInput from "@/components/OwnInput";
/*
params格式
{
mid: item.mid,
}
*/
const tabItems = [
{ key: "veri_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 [deviceType, setDeviceType] = useState("");
const [loginInfo, setLoginInfo] = useState({
mobilePhone: "",
regionCode: "86",
password: "",
checked: false,
});
const router = useRouter();
const swiperRef = useRef(null);
useEffect(() => {
const userAgent = navigator.userAgent;
//区分设备类型
if (/Android/i.test(userAgent)) {
setDeviceType("Android");
} else if (/iPhone|iPad|iPod/i.test(userAgent)) {
setDeviceType("ios");
} else {
setDeviceType("pc");
}
handleLogin({ isSignin: false, userToken: null });
checkAuth().then((res) => {
const account = get("account");
if (res && account) {
router.replace("/");
}
});
}, []);
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,
veri_code: veriCode,
};
try {
const data = await requireAPI(
"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 });
if (get("firstLogin") == null) {
save("firstLogin", 1);
} else {
save("firstLogin", 0);
}
router.push(
!data?.data?.is_enabled && type != "password"
? "/my/setting/editPassword?is_enabled=" + data?.data?.is_enabled
: "/"
);
} 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 {
const data = await fetch(`/api/veri_code/send`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
mobile_phone,
region_code: regionCode,
}),
});
if (data.ret === -1) {
Toast.show({
icon: "fail",
content: data.msg,
position: "top",
});
return;
}
} catch (error) {
// console.error(error);
}
};
return (
<div className="lg:flex flex-col items-center ">
<div
className={`lg:max-w-[450px] overflow-hidden pt-20 flex flex-col items-center `}
>
<Image
src={process.env.NEXT_PUBLIC_WEB_ASSETS_URL + "/images/slogan.png"}
alt=""
className="w-[90%]"
/>
<div className="mt-6 flex justify-between items-center px-2 text-[#ffffff80] 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
allowTouchMove={false}
indicator={() => null}
ref={swiperRef}
defaultIndex={activeIndex}
onIndexChange={(index) => {
setActiveIndex(index);
}}
>
<Swiper.Item className="px-6">
<div className="border-2 border-[#2c2b2f] rounded-2xl p-3">
<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}
name="phone_number"
type="number"
maxLength={11}
onChange={(value) =>
setLoginInfo({ ...loginInfo, mobilePhone: value })
}
value={loginInfo.mobilePhone}
style={{
"--color": "#FFFFFF",
"--font-size": "16px",
"--placeholder-color": "#FFFFFF80",
}}
/> */}
<OwnInput
clearable={true}
placeholder="请输入手机号"
// disabled={true}
name="phone_number"
type="number"
maxLength={11}
onChange={(value) => {
setLoginInfo({
...loginInfo,
mobilePhone: value,
});
}}
value={loginInfo.mobilePhone}
/>
</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}
name="veriCode"
type="number"
style={{
"--placeholder-color": "#FFFFFF80",
"--font-size": "16px",
}}
/> */}
<OwnInput
placeholder="请输入验证码"
// disabled={true}
name="veriCode"
type="number"
onChange={setVeriCode}
value={veriCode}
/>
<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-6">
<div className="border-2 border-[#2c2b2f] rounded-2xl p-3">
<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"
name="phone_number"
maxLength={11}
onChange={(value) =>
setLoginInfo({ ...loginInfo, mobilePhone: value })
}
value={loginInfo.mobilePhone}
style={{
"--color": "#FFFFFF",
"--font-size": "16px",
"--placeholder-color": "#FFFFFF80",
}}
/> */}
<OwnInput
clearable={true}
placeholder="请输入手机号"
// disabled={true}
name="phone_number"
type="number"
maxLength={11}
onChange={(value) => {
setLoginInfo({
...loginInfo,
mobilePhone: value,
});
}}
value={loginInfo.mobilePhone}
/>
</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="请输入密码"
name="password"
onChange={(value) =>
setLoginInfo({ ...loginInfo, password: value })
}
value={loginInfo.password}
type="password"
style={{
"--placeholder-color": "#FFFFFF80",
"--font-size": "16px",
}}
/> */}
<OwnInput
clearable={true}
placeholder="请输入密码"
// disabled={true}
name="password"
type="password"
onChange={(value) => {
setLoginInfo({ ...loginInfo, password: value });
}}
value={loginInfo.password}
/>
</div>
</div>
<div className="flex justify-end">
<span
onClick={() =>
router.push(
`/my/setting/editPassword?forgetPassword=true&phone=${loginInfo.mobilePhone}`
)
}
className="text-[#FF669E] text-xs mt-2 text-right"
>
忘记密码
</span>
</div>
<LoginBtn
loginInfo={loginInfo}
setLoginInfo={setLoginInfo}
handleSubmit={handleSubmit}
type={activeIndex ? "password" : "mobile"}
/>
</Swiper.Item>
</Swiper>
</div>
{deviceType != "ios" && <BottomBox />}
</div>
);
}
const LoginBtn = ({ loginInfo, setLoginInfo, type, handleSubmit }) => {
const router = useRouter();
useEffect(() => {}, []);
return (
<div className="mt-16">
<div className="flex items-center justify-center">
<Checkbox
value={loginInfo?.checked}
onChange={(value) => setLoginInfo({ ...loginInfo, checked: value })}
style={{
"--icon-size": "18px",
"--font-size": "18px",
"--gap": "6px",
}}
></Checkbox>
<span className="text-[#FFFFFF80] font-medium text-xs ml-2">
我已阅读并同意
<span
onClick={() =>
router.push(
`/webView/${encodeURIComponent("/doc/useragreement")}`
)
}
className="text-[#FF669E] text-xs"
>
用户协议
</span>
<span
onClick={() =>
router.push(
`/webView/${encodeURIComponent("/doc/privatypolicy")}`
)
}
className="text-[#FF669E] text-xs"
>
隐私政策
</span>
</span>
</div>
<Button
shape="rounded"
size="middle"
block
onClick={() => handleSubmit(type)}
style={{
"--background-color": "#FF669E",
color: "#FFFFFF",
marginTop: "1rem",
}}
className="mt-4 py-2"
>
登录
</Button>
</div>
);
};
const BottomBox = () => {
const router = useRouter();
return (
<div className="w-full p-6 ">
<div className="p-3 rounded-md flex justify-between items-center bg-[#ffffff17]">
<div>
<p className="">铁粉空间APP可以下载啦</p>
<p className="text-xs text-[#FFFFFF80]">
立即下载APP体验更好的服务
</p>
</div>
<div
className="px-2 py-1 rounded-full bg-[#FF669E]"
onClick={() => router.push("https://tiefen.fun")}
>
下载APP
</div>
</div>
</div>
);
};
const mapDispatchToProps = {
handleLogin,
};
export default connect(null, mapDispatchToProps)(Login);