改用 ahooks

This commit is contained in:
xmdhs 2023-09-26 01:13:20 +08:00
parent 7376cffe61
commit a9cc09dcae
No known key found for this signature in database
GPG Key ID: E809D6D43DEFCC95
5 changed files with 107 additions and 51 deletions

View File

@ -15,12 +15,12 @@
"@marsidev/react-turnstile": "^0.3.1",
"@mui/icons-material": "^5.14.9",
"@mui/material": "^5.14.10",
"ahooks": "^3.7.8",
"immer": "^10.0.2",
"jotai": "^2.4.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.16.0",
"swr": "^2.2.2"
"react-router-dom": "^6.16.0"
},
"devDependencies": {
"@types/node": "^20.6.2",

View File

@ -20,6 +20,9 @@ dependencies:
'@mui/material':
specifier: ^5.14.10
version: 5.14.10(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.21)(react-dom@18.2.0)(react@18.2.0)
ahooks:
specifier: ^3.7.8
version: 3.7.8(react@18.2.0)
immer:
specifier: ^10.0.2
version: 10.0.2
@ -35,9 +38,6 @@ dependencies:
react-router-dom:
specifier: ^6.16.0
version: 6.16.0(react-dom@18.2.0)(react@18.2.0)
swr:
specifier: ^2.2.2
version: 2.2.2(react@18.2.0)
devDependencies:
'@types/node':
@ -873,6 +873,10 @@ packages:
resolution: {integrity: sha512-z/G02d+59gyyUb7KYhKi9jOhicek6QD2oMaotUyG+lUkybpXoV49dY9bj7Ah5Q+y7knK2jU67UTX9FyfGzaxQg==}
dev: true
/@types/js-cookie@2.2.7:
resolution: {integrity: sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==}
dev: false
/@types/json-schema@7.0.13:
resolution: {integrity: sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==}
dev: true
@ -1070,6 +1074,29 @@ packages:
hasBin: true
dev: true
/ahooks-v3-count@1.0.0:
resolution: {integrity: sha512-V7uUvAwnimu6eh/PED4mCDjE7tokeZQLKlxg9lCTMPhN+NjsSbtdacByVlR1oluXQzD3MOw55wylDmQo4+S9ZQ==}
dev: false
/ahooks@3.7.8(react@18.2.0):
resolution: {integrity: sha512-e/NMlQWoCjaUtncNFIZk3FG1ImSkV/JhScQSkTqnftakRwdfZWSw6zzoWSG9OMYqPNs2MguDYBUFFC6THelWXA==}
engines: {node: '>=8.0.0'}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
dependencies:
'@babel/runtime': 7.22.15
'@types/js-cookie': 2.2.7
ahooks-v3-count: 1.0.0
dayjs: 1.11.10
intersection-observer: 0.12.2
js-cookie: 2.2.1
lodash: 4.17.21
react: 18.2.0
resize-observer-polyfill: 1.5.1
screenfull: 5.2.0
tslib: 2.6.2
dev: false
/ajv@6.12.6:
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
dependencies:
@ -1155,10 +1182,6 @@ packages:
supports-color: 7.2.0
dev: true
/client-only@0.0.1:
resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
dev: false
/clsx@2.0.0:
resolution: {integrity: sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==}
engines: {node: '>=6'}
@ -1216,6 +1239,10 @@ packages:
/csstype@3.1.2:
resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==}
/dayjs@1.11.10:
resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==}
dev: false
/debug@4.3.4:
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
engines: {node: '>=6.0'}
@ -1603,6 +1630,10 @@ packages:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
dev: true
/intersection-observer@0.12.2:
resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==}
dev: false
/is-arrayish@0.2.1:
resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
dev: false
@ -1655,6 +1686,10 @@ packages:
react: 18.2.0
dev: false
/js-cookie@2.2.1:
resolution: {integrity: sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==}
dev: false
/js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
dev: false
@ -1711,6 +1746,10 @@ packages:
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
dev: true
/lodash@4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
dev: false
/loose-envify@1.4.0:
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
hasBin: true
@ -1940,6 +1979,10 @@ packages:
resolution: {integrity: sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==}
dev: false
/resize-observer-polyfill@1.5.1:
resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==}
dev: false
/resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
@ -1985,6 +2028,11 @@ packages:
loose-envify: 1.4.0
dev: false
/screenfull@5.2.0:
resolution: {integrity: sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==}
engines: {node: '>=0.10.0'}
dev: false
/semver@7.5.4:
resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==}
engines: {node: '>=10'}
@ -2055,16 +2103,6 @@ packages:
engines: {node: '>= 0.4'}
dev: false
/swr@2.2.2(react@18.2.0):
resolution: {integrity: sha512-CbR41AoMD4TQBQw9ic3GTXspgfM9Y8Mdhb5Ob4uIKXhWqnRLItwA5fpGvB7SmSw3+zEjb0PdhiEumtUvYoQ+bQ==}
peerDependencies:
react: ^16.11.0 || ^17.0.0 || ^18.0.0
dependencies:
client-only: 0.0.1
react: 18.2.0
use-sync-external-store: 1.2.0(react@18.2.0)
dev: false
/text-table@0.2.0:
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
dev: true
@ -2090,6 +2128,10 @@ packages:
typescript: 5.2.2
dev: true
/tslib@2.6.2:
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
dev: false
/type-check@0.4.0:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'}
@ -2114,14 +2156,6 @@ packages:
punycode: 2.3.0
dev: true
/use-sync-external-store@1.2.0(react@18.2.0):
resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
dependencies:
react: 18.2.0
dev: false
/vite@4.4.9(@types/node@20.6.2):
resolution: {integrity: sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==}
engines: {node: ^14.18.0 || >=16.0.0}

View File

@ -1,26 +1,40 @@
import { Turnstile } from '@marsidev/react-turnstile'
import Button from '@mui/material/Button'
import { useRef, useState, memo } from 'react'
import { useRef, useState, memo, forwardRef, useImperativeHandle } from 'react'
import type { TurnstileInstance } from '@marsidev/react-turnstile'
import useSWR from "swr";
import { ApiCaptcha } from '@/apis/model';
import Alert from '@mui/material/Alert';
import Skeleton from '@mui/material/Skeleton';
import { useRequest } from 'ahooks';
interface prop {
onSuccess: ((token: string) => void)
}
function CaptchaWidget({ onSuccess }: prop) {
const ref = useRef<TurnstileInstance>(null)
export type refType = {
reload: () => void
}
const CaptchaWidget = forwardRef<refType, prop>(({ onSuccess }, ref) => {
const Turnstileref = useRef<TurnstileInstance>(null)
const [key, setKey] = useState(1)
const { data, error, isLoading } = useSWR<ApiCaptcha>(import.meta.env.VITE_APIADDR + '/api/v1/captcha')
const { data, error, loading } = useRequest(() => fetch(import.meta.env.VITE_APIADDR + '/api/v1/captcha').then(v => v.json() as Promise<ApiCaptcha>))
useImperativeHandle(ref, () => {
return {
reload: () => {
setKey(key + 1)
}
}
})
if (error) {
console.warn(error)
return <Alert severity="warning">{String(error)}</Alert>
}
if (isLoading) {
if (loading) {
return <Skeleton variant="rectangular" width={300} height={65} />
}
if (data?.code != 0) {
@ -31,13 +45,14 @@ function CaptchaWidget({ onSuccess }: prop) {
onSuccess("ok")
return <></>
}
return (
<>
<Turnstile siteKey={data?.data.siteKey ?? ""} key={key} onSuccess={onSuccess} ref={ref} scriptOptions={{ async: true }} />
<Turnstile siteKey={data?.data.siteKey ?? ""} key={key} onSuccess={onSuccess} ref={Turnstileref} scriptOptions={{ async: true }} />
<Button onClick={() => setKey(key + 1)}></Button>
</>
)
}
})
const CaptchaWidgetMemo = memo(CaptchaWidget)

View File

@ -2,16 +2,11 @@ import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import CssBaseline from '@mui/material/CssBaseline';
import { SWRConfig } from 'swr'
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<CssBaseline>
<SWRConfig value={{
fetcher: (resource, init) => fetch(resource, init).then(res => res.json())
}}>
<App />
</SWRConfig>
</CssBaseline>
</React.StrictMode>
)

View File

@ -11,26 +11,41 @@ import Container from '@mui/material/Container';
import { Link as RouterLink } from "react-router-dom";
import { register } from '@/apis/apis'
import CheckInput, { refType } from '@/components/CheckInput'
import { useState } from 'react';
import { useRef, useState } from 'react';
import Alert from '@mui/material/Alert';
import Snackbar from '@mui/material/Snackbar';
import Loading from '@/components/Loading'
import { useNavigate } from "react-router-dom";
import CaptchaWidget from '@/components/CaptchaWidget';
import { useRequest } from 'ahooks';
import type { refType as CaptchaWidgetRef } from '@/components/CaptchaWidget'
export default function SignUp() {
const [regErr, setRegErr] = useState("");
const [loading, setLoading] = useState(false);
const navigate = useNavigate();
const [captchaToken, setCaptchaToken] = useState("");
const captchaRef = useRef<CaptchaWidgetRef>(null)
const checkList = React.useRef<Map<string, refType>>(new Map<string, refType>())
const { loading, run } = useRequest(register, {
manual: true,
onSuccess: () => {
navigate("/login")
},
onError: (e) => {
setRegErr(String(e))
console.warn(e)
captchaRef.current?.reload()
}
})
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
if (loading) return
setLoading(true)
if (loading) {
return
}
const data = new FormData(event.currentTarget);
const d = {
email: data.get('email')?.toString(),
@ -38,17 +53,14 @@ export default function SignUp() {
username: data.get("username")?.toString()
}
if (!Array.from(checkList.current.values()).map(v => v.verify()).reduce((p, v) => (p == true) && (v == true))) {
setLoading(false)
return
}
if (captchaToken == "") {
setLoading(false)
setRegErr("验证码无效")
}
register(d.email ?? "", d.username ?? "", d.password ?? "", captchaToken).
then(() => navigate("/login")).
catch(v => [setRegErr(String(v)), console.warn(v)]).
finally(() => setLoading(false))
run(d.email ?? "", d.username ?? "", d.password ?? "", captchaToken)
};
return (
@ -126,7 +138,7 @@ export default function SignUp() {
/>
</Grid>
<Grid item xs={12}>
<CaptchaWidget onSuccess={setCaptchaToken} />
<CaptchaWidget ref={captchaRef} onSuccess={setCaptchaToken} />
</Grid>
</Grid>
<Button