diff --git a/app/(root)/authenticate/components/register.tsx b/app/(root)/authenticate/components/register.tsx index 35d6e14..5985bd7 100644 --- a/app/(root)/authenticate/components/register.tsx +++ b/app/(root)/authenticate/components/register.tsx @@ -15,7 +15,6 @@ export default function Register({ userName, setCode, setPassword, - setAccount, setConfirmPassword, setUserName }: RegisterProps){ @@ -86,11 +85,9 @@ export interface RegisterProps { password: string; confirmPassword: string; passwordRobustness: boolean[]; - valide: boolean; userName: string; setCode: (val: string) => void; setPassword: (val: string) => void, - setAccount: (val: string) => void, setConfirmPassword: (val: string) => void, setUserName: (val:string) => void; } diff --git a/app/(root)/authenticate/hooks/useRegister.ts b/app/(root)/authenticate/hooks/useRegister.ts new file mode 100644 index 0000000..15ac114 --- /dev/null +++ b/app/(root)/authenticate/hooks/useRegister.ts @@ -0,0 +1,22 @@ +import { useState } from "react" + +export const useRegister = ( +) => { + const [code, setCode] = useState(''); + const [password, setPassword] = useState(''); + const [confirmPassword, setConfirmPassword] = useState(''); + const [passwordRobustness, setRobustness] = useState([]); + const [userName, setUsername] = useState(''); + return { + code, + setCode, + password, + setPassword, + confirmPassword, + setConfirmPassword, + passwordRobustness, + setRobustness, + userName, + setUsername + } +} \ No newline at end of file diff --git a/app/(root)/authenticate/hooks/useValide.ts b/app/(root)/authenticate/hooks/useValide.ts index b470ebe..1569361 100644 --- a/app/(root)/authenticate/hooks/useValide.ts +++ b/app/(root)/authenticate/hooks/useValide.ts @@ -1,6 +1,7 @@ -import { useEffect, useMemo, useState } from "react" +import { useCallback, useEffect, useMemo, useState } from "react" import { PageType } from "../page"; -import { z,ZodError } from 'zod'; +import { z,ZodError, ZodIssue } from 'zod'; +import { useUpdate, useUpdateEffect } from "ahooks"; const validateEmail = (val: string) => val.match(/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,10}$/i); const validatePhone = (val: string) => val.match(/^1[3-9]\d{9}$/i); @@ -42,26 +43,25 @@ export const useValide = ( props: Partial, ) => { const [valide, setValide] = useState(false); - const [errors, setErrors] = useState(); + const [errors, setErrors] = useState(); const valideData = ()=>{ if (pageType !== 'register'){ const schemaValideRes = schema[pageType].safeParse(props); if (!schemaValideRes.success){ - setErrors(schemaValideRes.error) + setErrors(schemaValideRes.error.errors) } setValide(schemaValideRes.success); return; } if (pageType === 'register'){ - console.log(props.password, props.confirmPassword) const schemaRes = schema['register'].safeParse(props) setValide(schemaRes.success && (props.password === props.confirmPassword)); if (!schemaRes.success){ - setErrors(schemaRes.error) + setErrors(schemaRes.error.errors) } return; } - } + }; return { valide,errors, setValide,setErrors, diff --git a/app/(root)/authenticate/layout.tsx b/app/(root)/authenticate/layout.tsx index c653baa..2322c40 100644 --- a/app/(root)/authenticate/layout.tsx +++ b/app/(root)/authenticate/layout.tsx @@ -1,11 +1,19 @@ +'use client' import React from "react"; +import { useMount } from "ahooks"; +import { useRouter } from "next/navigation"; export default function AuthenticateLayout({children}: { children: React.ReactNode; }) { + const router = useRouter(); + useMount(()=>{ + router.prefetch('/authenticate/login') + router.prefetch('/authenticate/register') + }) return (
-
+
{children}
diff --git a/app/(root)/authenticate/login/page.tsx b/app/(root)/authenticate/login/page.tsx new file mode 100644 index 0000000..509cdfa --- /dev/null +++ b/app/(root)/authenticate/login/page.tsx @@ -0,0 +1,77 @@ +'use client'; +import { useState } from "react"; +import { Login } from "../components/login"; +import { useAtom } from "jotai"; +import { accountAtom } from "@/app/store"; +import { Button, Card, CardBody, CardFooter, Checkbox, Input } from "@nextui-org/react"; +import Link from "next/link"; +import IOC from "@/providers"; +import { UserAPI } from "@/interface/userAPI"; +import { Message } from "@/components/message"; +import { SetLoggedInState } from "@/interface/hooks"; +import { useRouter } from "next/navigation"; +export default function LoginPage(){ + const [password, setPassword] = useState(''); + const [account, setAccount] = useAtom(accountAtom); + const [policy, setPolicy] = useState(false); + const [loading, setLoading] = useState(false); + const router = useRouter(); + const login = () => { + setLoading(true); + UserAPI.login(account, password) + .then(( + [state, text] + )=>{ + if (state){ + Message.success('登陆成功'); + SetLoggedInState(true); + router.push('/dashboard') + } else { + Message.success(text); + } + }) + .finally(()=>{ + setLoading(false); + }) + } + return ( +
+ + +
+
+ + +
+ + 点击注册 + +
+
+ + 登录或注册即代表同意服务条款 + +
+
+ + + +
+
+ ) +} \ No newline at end of file diff --git a/app/(root)/authenticate/page.tsx b/app/(root)/authenticate/page.tsx index 84953e9..95c1c7d 100644 --- a/app/(root)/authenticate/page.tsx +++ b/app/(root)/authenticate/page.tsx @@ -1,81 +1,30 @@ 'use client' import { Button, Card, CardBody, CardFooter, Checkbox, Input } from '@nextui-org/react'; -import { KeyboardEvent, useEffect, useMemo, useState} from 'react'; +import { KeyboardEvent, useEffect, useState} from 'react'; import { useAccountDiscriminator, useValide } from './hooks/useValide'; import { useButton } from './hooks/useButton'; import IOC from '@/providers'; -import { Login } from './components/login'; -import Register from './components/register'; -import { Message } from '@/components/message'; import { useRouter } from 'next/navigation'; -import { SetLoggedInState } from '@/interface/hooks'; -import { UserAPI } from '@/interface/userAPI'; -import { useDebounceFn } from 'ahooks'; +import { useUpdateEffect } from 'ahooks'; +import { accountAtom } from '@/app/store'; +import { useAtom } from 'jotai'; export type Colors = "default" | "primary" | "secondary" | "success" | "warning" | "danger" | undefined; export type PageType = 'wait-check' | 'login' | 'register' export default function Page(){ - const [account, setAccount] = useState('') - const [password, setPassword] = useState(''); - const [confirmPassword, setConfirmPassword] = useState(''); - const [accountExists, setAccountExists] = useState(false); - const [code, setCode] = useState(''); - const [passwordRobustness, setPasswordRobustness] = useState(new Array(6).fill(false)); - const [userName, setUserName] = useState(''); - + const [account, setAccount] = useAtom(accountAtom) const [pageType, setPageType] = useState('wait-check'); const [policy, setPolicy] = useState(false); const [showErr, setShowErr] = useState(false); const {phone, email} = useAccountDiscriminator(account) - const {valide, errors, valideData} = useValide(pageType, { - account, - policyState: policy, - password, - confirmPassword, - checkCode: code, - userName, - passwordRobustness, - isPhone: phone, - isEmail: email, - }); + const {valide, valideData} = useValide(pageType, { account, policyState: policy, isPhone: phone, isEmail: email }); const {color, disabled, loading, setLoading, buttonMessage} = useButton({pageType, valide }); const router = useRouter(); - const fns = useMemo(() => { - return [ - (val: string) => val.length > 5, - (val: string) => /[A-Z]/.test(val), - (val: string) => /[a-z]/.test(val), - (val: string) => /[0-9]/.test(val), - (val: string) => /\W/.test(val), - (val: string) => val.length < 20 - ] - }, []) - useEffect(()=>{ - if (pageType !== 'wait-check' && account.length === 0){ - setPageType('wait-check'); - } - }, [pageType, account]) useEffect(()=>{ valideData() - }, [ - account, - policy, - password, - confirmPassword, - code, - userName, - passwordRobustness, - phone, - email, - pageType - ]); - useEffect(()=>{ - setPasswordRobustness( - [...fns.map(fn => fn(password))] - ) - }, [password, fns]) + }, [ account, policy, phone, email, pageType ]); const checkAccountExistsByEmail = () => { return IOC.user.checkEmail(account) .then((exists:boolean) => exists) @@ -86,7 +35,13 @@ export default function Page(){ .then((exists:boolean) => exists) .catch(() => false); } - const checkAccountExists = useDebounceFn(()=>{ + const onChangeAccount = (account: string) => { + setAccount(account); + setPageType("wait-check"); + } + + const accountCheck = () => { + setLoading(true); if (email){ checkAccountExistsByEmail() .then((exists) => { @@ -94,94 +49,40 @@ export default function Page(){ }) .finally(() => setLoading(false)); } - }, {wait: 1000}) - useEffect(()=>{ - checkAccountExists.run(); - }, [account]); - const invoke = () => { - const checkAccount = () => { - setLoading(true); - if (email){ - checkAccountExistsByEmail() - .then((exists) => { - setShowErr(!exists) - }) - .finally(() => setLoading(false)); - } - if (phone){ - checkAccountExistsByPhone() - .then((exists) => { - if (!exists){ - setPageType('register') - return; - } - setPageType('login') - }) - .finally(()=>{ - setLoading(false); - }) - } - } - const login = () => { - setLoading(true) - UserAPI.login(account, password) - .then((r) => { - const [state, text] = r; - if (state) { - Message.success("登录成功"); - SetLoggedInState(true); - router.push("/dashboard"); - } else { - Message.error(text); - setLoading(false) + if (phone){ + checkAccountExistsByPhone() + .then((exists) => { + if (!exists){ + setPageType('register') + return; } + setPageType('login') + }) + .finally(()=>{ + setLoading(false); }) } - const reg = () => { - setLoading(true) - if (phone){ - IOC.user.createUser({ - phone: account, - password, - username: userName, - code - }) - .then(() => { - Message.success('注册成功, 请登录'); - setPageType('login'); - setPassword(''); - return; - }) - .finally(() => setLoading(false)); - } - } - const obj:Recordvoid> = { - 'login': login, - 'register': reg, - 'wait-check': checkAccount - } - return obj[pageType] } const onEnter = (e:KeyboardEvent) => { if (!disabled && e.code.toLowerCase().includes('enter')){ e.preventDefault(); - invoke()(); + accountCheck() } } - - const onChangeAccount = (account: string) => { - setAccount(account); - setPageType("wait-check"); - } + useUpdateEffect(()=>{ + if (pageType !== 'wait-check'){ + router.push(`/authenticate/${pageType}`); + } + }, [pageType]); return (
- +
}
- { - pageType !== 'wait-check' ? pageType === 'login' ? - : - pageType === 'register' ? - : null : null - } - + 登录或注册即代表同意服务条款
- diff --git a/app/(root)/authenticate/register/page.tsx b/app/(root)/authenticate/register/page.tsx new file mode 100644 index 0000000..7a3290c --- /dev/null +++ b/app/(root)/authenticate/register/page.tsx @@ -0,0 +1,151 @@ +'use client' + +import { accountAtom } from "@/app/store"; +import { Button, Card, CardBody, CardFooter, CardHeader, Checkbox, Input, Link } from "@nextui-org/react"; +import { useAtom } from "jotai"; +import Register from "../components/register"; +import { useEffect, useMemo, useState } from "react"; +import { useRegister } from "../hooks/useRegister"; +import { useMount, useUpdateEffect } from "ahooks"; +import { useButton } from "../hooks/useButton"; +import { useValide } from "../hooks/useValide"; +import { IoChevronBackSharp } from "react-icons/io5"; +import IOC from "@/providers"; +import { Message } from "@/components/message"; +import { useRouter } from "next/navigation"; + +const isEmail = (val: string): boolean => /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/gim.test(val); +const isPhone = (val: string): boolean => /^[0-9]{7,32}$/gim.test(val); + +export default function RegisterPage(){ + const [account, setAccount] = useAtom(accountAtom); + + const { + code, + password, + confirmPassword, + passwordRobustness, + userName, + setCode, + setPassword, + setConfirmPassword, + setUsername, + setRobustness, + } = useRegister(); + const [policy, setPolicy] = useState(false); + const accountType = useMemo(() => isPhone(account) ? 'phone' : isEmail(account) ? 'email' : '', [account]); + const [loading, setLoading] = useState(false); + const router = useRouter(); + const {valide, valideData, errors} = useValide('register', { + password, + confirmPassword, + checkCode: code, + passwordRobustness, + pageType: 'register', + isEmail: accountType === 'email', + isPhone: accountType === 'phone', + account, + accountExists: false, + userName, + policyState: policy + }) + useEffect(()=>{ + valideData() + }, [account, policy, password, confirmPassword, code, userName, passwordRobustness]); + const {buttonMessage, color, disabled} = useButton({ + pageType: 'register', + valide, + }) + const fns = useMemo(() => { + return [ + (val: string) => val.length > 5, + (val: string) => /[A-Z]/.test(val), + (val: string) => /[a-z]/.test(val), + (val: string) => /[0-9]/.test(val), + (val: string) => /\W/.test(val), + (val: string) => val.length < 20 + ] + }, []) + useMount(()=>{ + setRobustness( + Array.from({length: fns.length}, ()=>false) + ) + }) + useUpdateEffect(()=>{ + setRobustness( + [...fns.map(fn => fn(password))] + ) + },[password,fns, setRobustness]) + + const reg = () => { + if (errors && errors.length){ + const [err] = errors; + Message.error(err.message); + return; + } + IOC.user.createUser({ + username: userName, + phone: account, + password, + code + }) + .then(({data}) => data) + .then(() => { + Message.success('注册成功, 请登录'); + router.replace('/authenticate/login'); + setPassword(''); + }) + .finally(() => setLoading(false)); + } + + return ( + + + +
+ + + +
+
+ +
+ + + + 登录或注册即代表同意服务条款 + +
+
+ + + +
+ + ) +} \ No newline at end of file diff --git a/app/(root)/layout.tsx b/app/(root)/layout.tsx index a8b1f5f..9575858 100644 --- a/app/(root)/layout.tsx +++ b/app/(root)/layout.tsx @@ -46,7 +46,7 @@ export default function RootLayout({children,}: { } } } gutter={-15}/> - + {children} diff --git a/app/layout.tsx b/app/layout.tsx index db3bb40..33e6273 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -39,7 +39,7 @@ export default function Root(
- + {children}
diff --git a/app/store.ts b/app/store.ts index d99771a..f742469 100644 --- a/app/store.ts +++ b/app/store.ts @@ -15,4 +15,5 @@ export const profile = atom(null); export const verify = atom(true); export const uploadStack = atom([]); export const currentFolderId = atom('1'); -export const folderId = atom([{id: '1', name: 'root'}]) \ No newline at end of file +export const folderId = atom([{id: '1', name: 'root'}]) +export const accountAtom = atom(''); \ No newline at end of file