328 lines
8.9 KiB
TypeScript
328 lines
8.9 KiB
TypeScript
import { history, Link, useRequest } from '@umijs/max';
|
||
import { Button, Col, Form, Input, message, Popover, Progress, Row, Select, Space } from 'antd';
|
||
import type { Store } from 'antd/es/form/interface';
|
||
import type { FC } from 'react';
|
||
import { useEffect, useState } from 'react';
|
||
import type { StateType } from './service';
|
||
import { fakeRegister } from './service';
|
||
import useStyles from './style.style';
|
||
import { registerUser } from '@/services/user/api';
|
||
import { CommonResponse } from '@/types/common';
|
||
|
||
const FormItem = Form.Item;
|
||
const { Option } = Select;
|
||
|
||
const passwordProgressMap: {
|
||
ok: 'success';
|
||
pass: 'normal';
|
||
poor: 'exception';
|
||
} = {
|
||
ok: 'success',
|
||
pass: 'normal',
|
||
poor: 'exception',
|
||
};
|
||
const Register: FC = () => {
|
||
const { styles } = useStyles();
|
||
const [count, setCount]: [number, any] = useState(0);
|
||
const [open, setVisible]: [boolean, any] = useState(false);
|
||
const [prefix, setPrefix]: [string, any] = useState('86');
|
||
const [popover, setPopover]: [boolean, any] = useState(false);
|
||
const confirmDirty = false;
|
||
let interval: number | undefined;
|
||
|
||
const passwordStatusMap = {
|
||
ok: (
|
||
<div className={styles.success}>
|
||
<span>强度:强</span>
|
||
</div>
|
||
),
|
||
pass: (
|
||
<div className={styles.warning}>
|
||
<span>强度:中</span>
|
||
</div>
|
||
),
|
||
poor: (
|
||
<div className={styles.error}>
|
||
<span>强度:太短</span>
|
||
</div>
|
||
),
|
||
};
|
||
|
||
const [form] = Form.useForm();
|
||
useEffect(
|
||
() => () => {
|
||
clearInterval(interval);
|
||
},
|
||
[interval],
|
||
);
|
||
const onGetCaptcha = () => {
|
||
let counts = 59;
|
||
setCount(counts);
|
||
interval = window.setInterval(() => {
|
||
counts -= 1;
|
||
setCount(counts);
|
||
if (counts === 0) {
|
||
clearInterval(interval);
|
||
}
|
||
}, 1000);
|
||
};
|
||
const getPasswordStatus = () => {
|
||
const value = form.getFieldValue('password');
|
||
if (value && value.length > 9) {
|
||
return 'ok';
|
||
}
|
||
if (value && value.length > 5) {
|
||
return 'pass';
|
||
}
|
||
return 'poor';
|
||
};
|
||
const { loading: submitting, run: register } = useRequest(
|
||
(params) => registerUser(params, { skipErrorHandler: true }),
|
||
{
|
||
manual: true,
|
||
onSuccess: (data, params) => {
|
||
console.log('注册成功 - data:', data, 'params:', params);
|
||
const response = data as CommonResponse<string>;
|
||
if (response?.code === 200) {
|
||
message.success('注册成功!');
|
||
const formValues = params[0] as any;
|
||
history.push({
|
||
pathname: `/user/register-result?account=${formValues?.email || formValues?.username || ''}`,
|
||
});
|
||
} else {
|
||
message.error(response?.message || '注册失败,请重试');
|
||
}
|
||
},
|
||
},
|
||
);
|
||
|
||
const onFinish = (values: Store) => {
|
||
// 将表单数据映射为后端需要的格式
|
||
const registerParams = {
|
||
username: values.username,
|
||
nickname: values.nickname,
|
||
password: values.password,
|
||
email: values.email || '',
|
||
phone: values.phone || '',
|
||
};
|
||
register(registerParams);
|
||
};
|
||
const checkConfirm = (_: any, value: string) => {
|
||
const promise = Promise;
|
||
if (value && value !== form.getFieldValue('password')) {
|
||
return promise.reject('两次输入的密码不匹配!');
|
||
}
|
||
return promise.resolve();
|
||
};
|
||
const checkPassword = (_: any, value: string) => {
|
||
const promise = Promise;
|
||
// 没有值的情况
|
||
if (!value) {
|
||
setVisible(!!value);
|
||
return promise.reject('请输入密码!');
|
||
}
|
||
// 有值的情况
|
||
if (!open) {
|
||
setVisible(!!value);
|
||
}
|
||
setPopover(!popover);
|
||
if (value.length < 6) {
|
||
return promise.reject('');
|
||
}
|
||
if (value && confirmDirty) {
|
||
form.validateFields(['confirm']);
|
||
}
|
||
return promise.resolve();
|
||
};
|
||
const changePrefix = (value: string) => {
|
||
setPrefix(value);
|
||
};
|
||
const renderPasswordProgress = () => {
|
||
const value = form.getFieldValue('password');
|
||
const passwordStatus = getPasswordStatus();
|
||
return value && value.length ? (
|
||
<div className={styles[`progress-${passwordStatus}`]}>
|
||
<Progress
|
||
status={passwordProgressMap[passwordStatus]}
|
||
strokeWidth={6}
|
||
percent={value.length * 10 > 100 ? 100 : value.length * 10}
|
||
showInfo={false}
|
||
/>
|
||
</div>
|
||
) : null;
|
||
};
|
||
return (
|
||
<div className={styles.main}>
|
||
<h3>注册</h3>
|
||
<Form form={form} name="UserRegister" onFinish={onFinish}>
|
||
<FormItem
|
||
name="username"
|
||
rules={[
|
||
{ required: true, message: '请输入用户名!' },
|
||
{ max: 50, message: '用户名过长' },
|
||
]}
|
||
>
|
||
<Input size="large" placeholder="用户名" />
|
||
</FormItem>
|
||
<FormItem
|
||
name="nickname"
|
||
rules={[
|
||
{ required: true, message: '请输入昵称!' },
|
||
{ max: 50, message: '昵称过长' },
|
||
]}
|
||
>
|
||
<Input size="large" placeholder="昵称" />
|
||
</FormItem>
|
||
<Popover
|
||
getPopupContainer={(node) => {
|
||
if (node && node.parentNode) {
|
||
return node.parentNode as HTMLElement;
|
||
}
|
||
return node;
|
||
}}
|
||
content={
|
||
open && (
|
||
<div
|
||
style={{
|
||
padding: '4px 0',
|
||
}}
|
||
>
|
||
{passwordStatusMap[getPasswordStatus()]}
|
||
{renderPasswordProgress()}
|
||
<div
|
||
style={{
|
||
marginTop: 10,
|
||
}}
|
||
>
|
||
<span>请至少输入 6 个字符。请不要使用容易被猜到的密码。</span>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
overlayStyle={{
|
||
width: 240,
|
||
}}
|
||
placement="right"
|
||
open={open}
|
||
>
|
||
<FormItem
|
||
name="password"
|
||
className={
|
||
form.getFieldValue('password') &&
|
||
form.getFieldValue('password').length > 0 &&
|
||
styles.password
|
||
}
|
||
rules={[
|
||
{
|
||
validator: checkPassword,
|
||
},
|
||
]}
|
||
>
|
||
<Input size="large" type="password" placeholder="至少6位密码,区分大小写" />
|
||
</FormItem>
|
||
</Popover>
|
||
<FormItem
|
||
name="confirm"
|
||
rules={[
|
||
{
|
||
required: true,
|
||
message: '确认密码',
|
||
},
|
||
{
|
||
validator: checkConfirm,
|
||
},
|
||
]}
|
||
>
|
||
<Input size="large" type="password" placeholder="确认密码" />
|
||
</FormItem>
|
||
<FormItem
|
||
name="email"
|
||
/* rules={[
|
||
{
|
||
required: true,
|
||
message: '请输入邮箱地址!',
|
||
},
|
||
{
|
||
type: 'email',
|
||
message: '邮箱地址格式错误!',
|
||
},
|
||
]} */
|
||
>
|
||
<Input size="large" placeholder="邮箱" />
|
||
</FormItem>
|
||
<FormItem
|
||
name="phone"
|
||
/* rules={[
|
||
{
|
||
required: true,
|
||
message: '请输入手机号!',
|
||
},
|
||
{
|
||
pattern: /^\d{11}$/,
|
||
message: '手机号格式错误!',
|
||
},
|
||
]} */
|
||
>
|
||
<Space.Compact style={{ width: '100%' }}>
|
||
<Select
|
||
size="large"
|
||
value={prefix}
|
||
onChange={changePrefix}
|
||
style={{
|
||
width: '30%',
|
||
}}
|
||
>
|
||
<Option value="86">+86</Option>
|
||
<Option value="87">+87</Option>
|
||
</Select>
|
||
|
||
<Input size="large" placeholder="手机号" />
|
||
</Space.Compact>
|
||
</FormItem>
|
||
{/* <Row gutter={8}>
|
||
<Col span={16}>
|
||
<FormItem
|
||
name="captcha"
|
||
rules={[
|
||
{
|
||
required: true,
|
||
message: '请输入验证码!',
|
||
},
|
||
]}
|
||
>
|
||
<Input size="large" placeholder="验证码" />
|
||
</FormItem>
|
||
</Col>
|
||
<Col span={8}>
|
||
<Button
|
||
size="large"
|
||
disabled={!!count}
|
||
className={styles.getCaptcha}
|
||
onClick={onGetCaptcha}
|
||
>
|
||
{count ? `${count} s` : '获取验证码'}
|
||
</Button>
|
||
</Col>
|
||
</Row> */}
|
||
<FormItem>
|
||
<div className={styles.footer}>
|
||
<Button
|
||
size="large"
|
||
loading={submitting}
|
||
className={styles.submit}
|
||
type="primary"
|
||
htmlType="submit"
|
||
>
|
||
<span>注册</span>
|
||
</Button>
|
||
<Link to="/user/login">
|
||
<span>使用已有账户登录</span>
|
||
</Link>
|
||
</div>
|
||
</FormItem>
|
||
</Form>
|
||
</div>
|
||
);
|
||
};
|
||
export default Register;
|