-
-
Notifications
You must be signed in to change notification settings - Fork 319
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
138 additions
and
61 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import request from '@/utils/request'; | ||
|
||
// 用户登录 | ||
export const login = async <T>(params: T) => { | ||
return request.post('/user/login', params, { showLoading: false }); | ||
}; | ||
|
||
// 发送验证码 | ||
export const sendEmail = async (params: { email: string }) => { | ||
return request.post('/user/sendEmail', params, { showLoading: false }); | ||
}; | ||
|
||
// 邮箱注册 | ||
export const regist = async (params: { userName: string; code?: number; userPwd: string }) => { | ||
return request.post('/user/regist', params, { showLoading: false }); | ||
}; | ||
|
||
// 获取用户信息 | ||
export const getUserInfo = async () => { | ||
return request.get('/user/info', {}, { showLoading: false }); | ||
}; | ||
|
||
// 获取用户头像 | ||
export const getUserAvatar = () => { | ||
// TODO 等后续接入微信扫码后生成用户头像 | ||
return Promise.resolve({ | ||
avatar: '', | ||
}); | ||
}; | ||
|
||
// 搜索用户 | ||
export const searchUser = (keyword: string) => { | ||
return request.post(`/user/search`, { keyword }, { showLoading: false }); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,32 +1,77 @@ | ||
import { useEffect, useState } from 'react'; | ||
import type { FormProps } from 'antd'; | ||
import { Button, Form, Input } from 'antd'; | ||
import { Button, Form, Input, InputNumber, Space } from 'antd'; | ||
import { useNavigate } from 'react-router-dom'; | ||
import { login } from '@/api'; | ||
import { login, sendEmail, regist } from '@/api/user'; | ||
import storage from '@/utils/storage'; | ||
import { usePageStore } from '@/stores/pageStore'; | ||
import { LockOutlined, UserOutlined } from '@ant-design/icons'; | ||
import { LockOutlined, SafetyOutlined, UserOutlined } from '@ant-design/icons'; | ||
import style from './index.module.less'; | ||
type FieldType = { | ||
userName: string; | ||
code?: number; | ||
userPwd: string; | ||
}; | ||
export default function Login() { | ||
const [type, setType] = useState('login'); | ||
const [count, setCount] = useState(0); | ||
const [loading1, setLoading1] = useState(false); | ||
const [loading2, setLoading2] = useState(false); | ||
const navigate = useNavigate(); | ||
const [form] = Form.useForm(); | ||
const saveUserInfo = usePageStore((state) => state.saveUserInfo); | ||
|
||
// 类型切换 | ||
const onChange = () => { | ||
setType(type == 'login' ? 'regist' : 'login'); | ||
}; | ||
|
||
// 生成验证码 | ||
const handleCreateCode = () => { | ||
form.validateFields(['userName']).then(async ({ userName }) => { | ||
setLoading1(true); | ||
try { | ||
await sendEmail({ email: userName }); | ||
setCount(60); | ||
setLoading1(false); | ||
} catch (error) { | ||
setLoading1(false); | ||
} | ||
}); | ||
}; | ||
|
||
useEffect(() => { | ||
const timer = setTimeout(() => { | ||
if (count > 0) { | ||
setCount(count - 1); | ||
} | ||
}, 1000); | ||
|
||
// 清理函数 | ||
return () => clearTimeout(timer); | ||
}, [count]); | ||
|
||
// 登录或注册 | ||
const onFinish: FormProps<FieldType>['onFinish'] = async (values: FieldType) => { | ||
const res = await login<FieldType>(values); | ||
if (res.token) { | ||
storage.set('token', res.token); | ||
saveUserInfo(res); | ||
if (location.search) { | ||
const params = new URLSearchParams(location.search); | ||
setTimeout(() => { | ||
const url = new URL(params.get('callback') as string); | ||
navigate(url.pathname || '/projects'); | ||
}); | ||
} else { | ||
navigate('/projects'); | ||
setLoading2(true); | ||
try { | ||
const res = type === 'login' ? await login<FieldType>(values) : await regist(values); | ||
setLoading2(false); | ||
if (res.token) { | ||
storage.set('token', res.token); | ||
saveUserInfo(res); | ||
if (location.search) { | ||
const params = new URLSearchParams(location.search); | ||
setTimeout(() => { | ||
const url = new URL(params.get('callback') as string); | ||
navigate(url.pathname || '/projects'); | ||
}); | ||
} else { | ||
navigate('/projects'); | ||
} | ||
} | ||
} catch (error) { | ||
setLoading2(false); | ||
} | ||
}; | ||
return ( | ||
|
@@ -36,10 +81,14 @@ export default function Login() { | |
<img src="/imgs/login-bg.png" /> | ||
</div> | ||
<div className={style.form}> | ||
<div className={style.title}> | ||
<img src="/imgs/mars-logo.png" width={45} /> | ||
<span>Marsview</span> | ||
</div> | ||
{type === 'login' ? ( | ||
<div className={style.title}> | ||
<img src="/imgs/mars-logo.png" width={45} /> | ||
<span>Marsview</span> | ||
</div> | ||
) : ( | ||
<div className={style.title}>账号注册</div> | ||
)} | ||
<Form | ||
name="basic" | ||
layout="vertical" | ||
|
@@ -48,20 +97,45 @@ export default function Login() { | |
initialValues={{ userName: '[email protected]', userPwd: 'marsview' }} | ||
autoComplete="off" | ||
size="large" | ||
form={form} | ||
> | ||
<Form.Item<FieldType> name="userName" rules={[{ required: true, message: '请输入邮箱' }]}> | ||
<Input prefix={<UserOutlined />} /> | ||
<Form.Item<FieldType> | ||
name="userName" | ||
rules={[ | ||
{ required: true, message: '请输入邮箱' }, | ||
{ pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, message: '请输入正确的邮箱' }, | ||
]} | ||
> | ||
<Input prefix={<UserOutlined />} placeholder="请输入个人邮箱" /> | ||
</Form.Item> | ||
|
||
{type === 'regist' && ( | ||
<Form.Item> | ||
<Space> | ||
<Form.Item<FieldType> name="code" noStyle rules={[{ required: true, message: '请输入验证码' }]}> | ||
<InputNumber prefix={<SafetyOutlined />} style={{ width: '100%' }} placeholder="验证码" /> | ||
</Form.Item> | ||
<Button type="primary" onClick={handleCreateCode} disabled={count > 0} loading={loading1}> | ||
{count > 0 ? count + 's' : '获取验证码'} | ||
</Button> | ||
</Space> | ||
</Form.Item> | ||
)} | ||
|
||
<Form.Item<FieldType> style={{ marginTop: 32 }} name="userPwd" rules={[{ required: true, message: '请输入密码' }]}> | ||
<Input.Password prefix={<LockOutlined />} /> | ||
</Form.Item> | ||
|
||
<Form.Item style={{ marginTop: 40 }}> | ||
<Button type="primary" block htmlType="submit"> | ||
<Button type="primary" block htmlType="submit" loading={loading2}> | ||
登录 | ||
</Button> | ||
</Form.Item> | ||
<Form.Item style={{ marginTop: 40 }}> | ||
<div style={{ textAlign: 'center' }}> | ||
<a onClick={onChange}>{type === 'login' ? '没有账号?去注册' : '已有账号?去登录'}</a> | ||
</div> | ||
</Form.Item> | ||
</Form> | ||
</div> | ||
</div> | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters