未验证 提交 704eb3fa 编写于 作者: 陈帅 提交者: GitHub

feat: use ProForm replace compents form (#7544)

*  feat: use ProForm replace compents form

* up version

* fix lint error
上级 e133575d
import { Request, Response } from 'express';
function getFakeCaptcha(req: Request, res: Response) {
const waitTime = (time: number = 100) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(true);
}, time);
});
};
async function getFakeCaptcha(req: Request, res: Response) {
await waitTime(2000);
return res.json('captcha-xxx');
}
// 代码中会兼容本地 service mock 以及部署站点的静态数据
export default {
// 支持值为 Object 和 Array
......@@ -77,8 +87,9 @@ export default {
address: 'Sidney No. 1 Lake Park',
},
],
'POST /api/login/account': (req: Request, res: Response) => {
'POST /api/login/account': async (req: Request, res: Response) => {
const { password, userName, type } = req.body;
await waitTime(2000);
if (password === 'ant.design' && userName === 'admin') {
res.send({
status: 'ok',
......
{
"name": "ant-design-pro",
"version": "4.2.2",
"version": "4.3.0",
"private": true,
"description": "An out-of-box UI solution for enterprise applications",
"scripts": {
......@@ -55,7 +55,7 @@
"dependencies": {
"@ant-design/icons": "^4.0.0",
"@ant-design/pro-descriptions": "^1.0.19",
"@ant-design/pro-form": "^1.0.2",
"@ant-design/pro-form": "^1.2.0",
"@ant-design/pro-layout": "^6.4.19",
"@ant-design/pro-table": "^2.9.5",
"@umijs/route-utils": "^1.0.33",
......
......@@ -4,6 +4,7 @@ import menu from './zh-CN/menu';
import pwa from './zh-CN/pwa';
import settingDrawer from './zh-CN/settingDrawer';
import settings from './zh-CN/settings';
import pages from './zh-CN/pages';
export default {
'navBar.lang': '语言',
......@@ -13,6 +14,7 @@ export default {
'app.preview.down.block': '下载此页面到本地项目',
'app.welcome.link.fetch-blocks': '获取全部区块',
'app.welcome.link.block-list': '基于 block 开发,快速构建标准页面',
...pages,
...globalHeader,
...menu,
...settingDrawer,
......
export default {
'pages.layouts.userLayout.title': 'Ant Design 是西湖区最具影响力的 Web 设计规范',
'pages.login.accountLogin.tab': '账户密码登录',
'pages.login.accountLogin.errorMessage': '错误的用户名和密码(admin/ant.design)',
'pages.login.username.placeholder': '用户名: admin or user',
'pages.login.username.required': '用户名是必填项!',
'pages.login.password.placeholder': '密码: ant.design',
'pages.login.password.required': '密码是必填项!',
'pages.login.phoneLogin.tab': '手机号登录',
'pages.login.phoneLogin.errorMessage': '验证码错误',
'pages.login.phoneNumber.placeholder': '请输入手机号!',
'pages.login.phoneNumber.required': '手机号是必填项!',
'pages.login.phoneNumber.invalid': '不合法的手机号!',
'pages.login.captcha.placeholder': '请输入验证码!',
'pages.login.captcha.required': '验证码是必填项!',
'pages.login.phoneLogin.getVerificationCode': '获取验证码',
'pages.getCaptchaSecondText': '秒后重新获取',
'pages.login.rememberMe': '自动登录',
'pages.login.forgotPassword': '忘记密码 ?',
'pages.login.submit': '提交',
'pages.login.loginWith': '其他登录方式 :',
'pages.login.registerAccount': '注册账户',
};
......@@ -4,6 +4,7 @@ import { history, Reducer, Effect } from 'umi';
import { fakeAccountLogin } from '@/services/login';
import { setAuthority } from '@/utils/authority';
import { getPageQuery } from '@/utils/utils';
import { message } from 'antd';
export interface StateType {
status?: 'ok' | 'error';
......@@ -41,6 +42,7 @@ const Model: LoginModelType = {
if (response.status === 'ok') {
const urlParams = new URL(window.location.href);
const params = getPageQuery();
message.success('🎉 🎉 🎉 登录成功!');
let { redirect } = params as { redirect: string };
if (redirect) {
const redirectUrlParams = new URL(redirect);
......
import { createContext } from 'react';
export interface LoginContextProps {
tabUtil?: {
addTab: (id: string) => void;
removeTab: (id: string) => void;
};
updateActive?: (activeItem: { [key: string]: string } | string) => void;
}
const LoginContext: React.Context<LoginContextProps> = createContext({});
export default LoginContext;
import { Button, Col, Input, Row, Form, message } from 'antd';
import React, { useState, useCallback, useEffect } from 'react';
import omit from 'omit.js';
import { FormItemProps } from 'antd/es/form/FormItem';
import { getFakeCaptcha } from '@/services/login';
import { FormattedMessage } from 'umi';
import ItemMap from './map';
import LoginContext, { LoginContextProps } from './LoginContext';
import styles from './index.less';
export type WrappedLoginItemProps = LoginItemProps;
export type LoginItemKeyType = keyof typeof ItemMap;
export interface LoginItemType {
UserName: React.FC<WrappedLoginItemProps>;
Password: React.FC<WrappedLoginItemProps>;
Mobile: React.FC<WrappedLoginItemProps>;
Captcha: React.FC<WrappedLoginItemProps>;
}
export interface LoginItemProps extends Partial<FormItemProps> {
name?: string;
style?: React.CSSProperties;
placeholder?: string;
buttonText?: React.ReactNode;
countDown?: number;
getCaptchaButtonText?: string;
getCaptchaSecondText?: string;
updateActive?: LoginContextProps['updateActive'];
type?: string;
defaultValue?: string;
customProps?: { [key: string]: unknown };
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
tabUtil?: LoginContextProps['tabUtil'];
}
const FormItem = Form.Item;
const getFormItemOptions = ({
onChange,
defaultValue,
customProps = {},
rules,
}: LoginItemProps) => {
const options: {
rules?: LoginItemProps['rules'];
onChange?: LoginItemProps['onChange'];
initialValue?: LoginItemProps['defaultValue'];
} = {
rules: rules || (customProps.rules as LoginItemProps['rules']),
};
if (onChange) {
options.onChange = onChange;
}
if (defaultValue) {
options.initialValue = defaultValue;
}
return options;
};
const LoginItem: React.FC<LoginItemProps> = (props) => {
const [count, setCount] = useState<number>(props.countDown || 0);
const [timing, setTiming] = useState(false);
// 这么写是为了防止restProps中 带入 onChange, defaultValue, rules props tabUtil
const {
onChange,
customProps,
defaultValue,
rules,
name,
getCaptchaButtonText,
getCaptchaSecondText,
updateActive,
type,
tabUtil,
...restProps
} = props;
const onGetCaptcha = useCallback(async (mobile: string) => {
const result = await getFakeCaptcha(mobile);
if (result === false) {
return;
}
message.success('获取验证码成功!验证码为:1234');
setTiming(true);
}, []);
useEffect(() => {
let interval: number = 0;
const { countDown } = props;
if (timing) {
interval = window.setInterval(() => {
setCount((preSecond) => {
if (preSecond <= 1) {
setTiming(false);
clearInterval(interval);
// 重置秒数
return countDown || 60;
}
return preSecond - 1;
});
}, 1000);
}
return () => clearInterval(interval);
}, [timing]);
if (!name) {
return null;
}
// get getFieldDecorator props
const options = getFormItemOptions(props);
const otherProps = restProps || {};
if (type === 'Captcha') {
const inputProps = omit(otherProps, ['onGetCaptcha', 'countDown']);
return (
<FormItem shouldUpdate noStyle>
{({ getFieldValue }) => (
<Row gutter={8}>
<Col span={16}>
<FormItem name={name} {...options}>
<Input {...customProps} {...inputProps} />
</FormItem>
</Col>
<Col span={8}>
<Button
disabled={timing}
className={styles.getCaptcha}
size="large"
onClick={() => {
const value = getFieldValue('mobile');
onGetCaptcha(value);
}}
>
{timing ? (
`${count} 秒`
) : (
<FormattedMessage
id="pages.login.phoneLogin.getVerificationCode"
defaultMessage="获取验证码"
/>
)}
</Button>
</Col>
</Row>
)}
</FormItem>
);
}
return (
<FormItem name={name} {...options}>
<Input {...customProps} {...otherProps} />
</FormItem>
);
};
const LoginItems: Partial<LoginItemType> = {};
Object.keys(ItemMap).forEach((key) => {
const item = ItemMap[key];
LoginItems[key] = (props: LoginItemProps) => (
<LoginContext.Consumer>
{(context) => (
<LoginItem
customProps={item.props}
rules={item.rules}
{...props}
type={key}
{...context}
updateActive={context.updateActive}
/>
)}
</LoginContext.Consumer>
);
});
export default LoginItems as LoginItemType;
import { Button, Form } from 'antd';
import { ButtonProps } from 'antd/es/button';
import React from 'react';
import classNames from 'classnames';
import styles from './index.less';
const FormItem = Form.Item;
interface LoginSubmitProps extends ButtonProps {
className?: string;
}
const LoginSubmit: React.FC<LoginSubmitProps> = ({ className, ...rest }) => {
const clsString = classNames(styles.submit, className);
return (
<FormItem>
<Button size="large" className={clsString} type="primary" htmlType="submit" {...rest} />
</FormItem>
);
};
export default LoginSubmit;
import React, { useEffect } from 'react';
import { Tabs } from 'antd';
import LoginContext, { LoginContextProps } from './LoginContext';
const { TabPane } = Tabs;
const generateId = (() => {
let i = 0;
return (prefix = '') => {
i += 1;
return `${prefix}${i}`;
};
})();
type TabPaneProps = Parameters<typeof Tabs.TabPane>[0];
interface LoginTabProps extends TabPaneProps {
tabUtil: LoginContextProps['tabUtil'];
active?: boolean;
}
const LoginTab: React.FC<LoginTabProps> = (props) => {
useEffect(() => {
const uniqueId = generateId('login-tab-');
const { tabUtil } = props;
if (tabUtil) {
tabUtil.addTab(uniqueId);
}
}, []);
const { children } = props;
return <TabPane {...props}>{props.active && children}</TabPane>;
};
const WrapContext: React.FC<TabPaneProps> & {
typeName: string;
} = (props) => (
<LoginContext.Consumer>
{(value) => <LoginTab tabUtil={value.tabUtil} {...props} />}
</LoginContext.Consumer>
);
// 标志位 用来判断是不是自定义组件
WrapContext.typeName = 'LoginTab';
export default WrapContext;
@import '~antd/es/style/themes/default.less';
.login {
:global {
.ant-tabs .ant-tabs-bar {
margin-bottom: 24px;
text-align: center;
border-bottom: 0;
}
}
.getCaptcha {
display: block;
width: 100%;
}
.icon {
margin-left: 16px;
color: rgba(0, 0, 0, 0.2);
font-size: 24px;
vertical-align: middle;
cursor: pointer;
transition: color 0.3s;
&:hover {
color: @primary-color;
}
}
.other {
margin-top: 24px;
line-height: 22px;
text-align: left;
.register {
float: right;
}
}
.prefixIcon {
color: @disabled-color;
font-size: @font-size-base;
}
.submit {
width: 100%;
margin-top: 24px;
}
}
import { Tabs, Form } from 'antd';
import React, { useState } from 'react';
import useMergeValue from 'use-merge-value';
import classNames from 'classnames';
import { FormInstance } from 'antd/es/form';
import { LoginParamsType } from '@/services/login';
import LoginContext from './LoginContext';
import LoginItem, { LoginItemProps } from './LoginItem';
import LoginSubmit from './LoginSubmit';
import LoginTab from './LoginTab';
import styles from './index.less';
export interface LoginProps {
activeKey?: string;
onTabChange?: (key: string) => void;
style?: React.CSSProperties;
onSubmit?: (values: LoginParamsType) => void;
className?: string;
from?: FormInstance;
children: React.ReactElement<typeof LoginTab>[];
}
interface LoginType extends React.FC<LoginProps> {
Tab: typeof LoginTab;
Submit: typeof LoginSubmit;
UserName: React.FunctionComponent<LoginItemProps>;
Password: React.FunctionComponent<LoginItemProps>;
Mobile: React.FunctionComponent<LoginItemProps>;
Captcha: React.FunctionComponent<LoginItemProps>;
}
const Login: LoginType = (props) => {
const { className } = props;
const [tabs, setTabs] = useState<string[]>([]);
const [active, setActive] = useState({});
const [type, setType] = useMergeValue('', {
value: props.activeKey,
onChange: props.onTabChange,
});
const TabChildren: React.ReactComponentElement<typeof LoginTab>[] = [];
const otherChildren: React.ReactElement<unknown>[] = [];
React.Children.forEach(
props.children,
(child: React.ReactComponentElement<typeof LoginTab> | React.ReactElement<unknown>) => {
if (!child) {
return;
}
if ((child.type as { typeName: string }).typeName === 'LoginTab') {
TabChildren.push(child as React.ReactComponentElement<typeof LoginTab>);
} else {
otherChildren.push(child);
}
},
);
return (
<LoginContext.Provider
value={{
tabUtil: {
addTab: (id) => {
setTabs([...tabs, id]);
},
removeTab: (id) => {
setTabs(tabs.filter((currentId) => currentId !== id));
},
},
updateActive: (activeItem) => {
if (!active) return;
if (active[type]) {
active[type].push(activeItem);
} else {
active[type] = [activeItem];
}
setActive(active);
},
}}
>
<div className={classNames(className, styles.login)}>
<Form
form={props.from}
onFinish={(values) => {
if (props.onSubmit) {
props.onSubmit(values as LoginParamsType);
}
}}
>
{tabs.length ? (
<React.Fragment>
<Tabs
destroyInactiveTabPane
animated={false}
className={styles.tabs}
activeKey={type}
onChange={(activeKey) => {
setType(activeKey);
}}
>
{TabChildren}
</Tabs>
{otherChildren}
</React.Fragment>
) : (
props.children
)}
</Form>
</div>
</LoginContext.Provider>
);
};
Login.Tab = LoginTab;
Login.Submit = LoginSubmit;
Login.UserName = LoginItem.UserName;
Login.Password = LoginItem.Password;
Login.Mobile = LoginItem.Mobile;
Login.Captcha = LoginItem.Captcha;
export default Login;
import { LockTwoTone, MailTwoTone, MobileTwoTone, UserOutlined } from '@ant-design/icons';
import React from 'react';
import styles from './index.less';
export default {
UserName: {
props: {
size: 'large',
id: 'userName',
prefix: (
<UserOutlined
style={{
color: '#1890ff',
}}
className={styles.prefixIcon}
/>
),
placeholder: 'admin',
},
rules: [
{
required: true,
message: 'Please enter username!',
},
],
},
Password: {
props: {
size: 'large',
prefix: <LockTwoTone className={styles.prefixIcon} />,
type: 'password',
id: 'password',
placeholder: '888888',
},
rules: [
{
required: true,
message: 'Please enter password!',
},
],
},
Mobile: {
props: {
size: 'large',
prefix: <MobileTwoTone className={styles.prefixIcon} />,
placeholder: 'mobile number',
},
rules: [
{
required: true,
message: 'Please enter mobile number!',
},
{
pattern: /^1\d{10}$/,
message: 'Wrong mobile number format!',
},
],
},
Captcha: {
props: {
size: 'large',
prefix: <MailTwoTone className={styles.prefixIcon} />,
placeholder: 'captcha',
},
rules: [
{
required: true,
message: 'Please enter Captcha!',
},
],
},
};
@import '~antd/es/style/themes/default.less';
.main {
width: 368px;
width: 328px;
margin: 0 auto;
@media screen and (max-width: @screen-sm) {
width: 95%;
max-width: 328px;
}
:global {
.@{ant-prefix}-tabs-nav-list {
margin: auto;
font-size: 16rpx;
}
}
.icon {
......@@ -24,16 +32,13 @@
margin-top: 24px;
line-height: 22px;
text-align: left;
.register {
float: right;
}
}
:global {
.antd-pro-login-submit {
width: 100%;
margin-top: 24px;
}
.prefixIcon {
color: @primary-color;
font-size: @font-size-base;
}
}
import { AlipayCircleOutlined, TaobaoCircleOutlined, WeiboCircleOutlined } from '@ant-design/icons';
import { Alert, Checkbox } from 'antd';
import {
AlipayCircleOutlined,
LockTwoTone,
MailTwoTone,
MobileTwoTone,
TaobaoCircleOutlined,
UserOutlined,
WeiboCircleOutlined,
} from '@ant-design/icons';
import { Alert, Space, message, Tabs } from 'antd';
import React, { useState } from 'react';
import { Link, connect, Dispatch, useIntl, FormattedMessage } from 'umi';
import ProForm, { ProFormCaptcha, ProFormCheckbox, ProFormText } from '@ant-design/pro-form';
import { connect, Dispatch, useIntl, FormattedMessage } from 'umi';
import { StateType } from '@/models/login';
import { LoginParamsType } from '@/services/login';
import { getFakeCaptcha, LoginParamsType } from '@/services/login';
import { ConnectState } from '@/models/connect';
import LoginForm from './components/Login';
import styles from './style.less';
import styles from './index.less';
const { Tab, UserName, Password, Mobile, Captcha, Submit } = LoginForm;
interface LoginProps {
dispatch: Dispatch;
userLogin: StateType;
......@@ -32,7 +39,6 @@ const LoginMessage: React.FC<{
const Login: React.FC<LoginProps> = (props) => {
const { userLogin = {}, submitting } = props;
const { status, type: loginType } = userLogin;
const [autoLogin, setAutoLogin] = useState(true);
const [type, setType] = useState<string>('account');
const intl = useIntl();
......@@ -45,131 +51,187 @@ const Login: React.FC<LoginProps> = (props) => {
};
return (
<div className={styles.main}>
<LoginForm activeKey={type} onTabChange={setType} onSubmit={handleSubmit}>
<Tab
key="account"
tab={intl.formatMessage({
id: 'pages.login.accountLogin.tab',
defaultMessage: '账户密码登录',
})}
>
{status === 'error' && loginType === 'account' && !submitting && (
<LoginMessage
content={intl.formatMessage({
id: 'pages.login.accountLogin.errorMessage',
defaultMessage: '账户或密码错误(admin/ant.design)',
})}
/>
)}
<UserName
name="userName"
placeholder={intl.formatMessage({
id: 'pages.login.username.placeholder',
defaultMessage: '用户名: admin or user',
<ProForm
initialValues={{
autoLogin: true,
}}
submitter={{
render: (_, dom) => dom.pop(),
submitButtonProps: {
loading: submitting,
size: 'large',
style: {
width: '100%',
},
},
}}
onFinish={async (values) => {
handleSubmit(values);
}}
>
<Tabs activeKey={type} onChange={setType}>
<Tabs.TabPane
key="account"
tab={intl.formatMessage({
id: 'pages.login.accountLogin.tab',
defaultMessage: '账户密码登录',
})}
rules={[
{
required: true,
message: (
<FormattedMessage
id="pages.login.username.required"
defaultMessage="请输入用户名!"
/>
),
},
]}
/>
<Password
name="password"
placeholder={intl.formatMessage({
id: 'pages.login.password.placeholder',
defaultMessage: '密码: ant.design',
<Tabs.TabPane
key="mobile"
tab={intl.formatMessage({
id: 'pages.login.phoneLogin.tab',
defaultMessage: '手机号登录',
})}
rules={[
{
required: true,
message: (
<FormattedMessage
id="pages.login.password.required"
defaultMessage="请输入密码!"
/>
),
},
]}
/>
</Tab>
<Tab
key="mobile"
tab={intl.formatMessage({
id: 'pages.login.phoneLogin.tab',
defaultMessage: '手机号登录',
})}
>
{status === 'error' && loginType === 'mobile' && !submitting && (
<LoginMessage
content={intl.formatMessage({
id: 'pages.login.phoneLogin.errorMessage',
defaultMessage: '验证码错误',
})}
/>
)}
<Mobile
name="mobile"
placeholder={intl.formatMessage({
id: 'pages.login.phoneNumber.placeholder',
defaultMessage: '手机号',
})}
rules={[
{
required: true,
message: (
<FormattedMessage
id="pages.login.phoneNumber.required"
defaultMessage="请输入手机号!"
/>
),
},
{
pattern: /^1\d{10}$/,
message: (
<FormattedMessage
id="pages.login.phoneNumber.invalid"
defaultMessage="手机号格式错误!"
/>
),
},
]}
/>
<Captcha
name="captcha"
placeholder={intl.formatMessage({
id: 'pages.login.captcha.placeholder',
defaultMessage: '验证码',
})}
countDown={120}
getCaptchaButtonText=""
getCaptchaSecondText={intl.formatMessage({
id: 'pages.getCaptchaSecondText',
defaultMessage: '',
</Tabs>
{status === 'error' && loginType === 'account' && !submitting && (
<LoginMessage
content={intl.formatMessage({
id: 'pages.login.accountLogin.errorMessage',
defaultMessage: '账户或密码错误(admin/ant.design)',
})}
rules={[
{
required: true,
message: (
<FormattedMessage
id="pages.login.captcha.required"
defaultMessage="请输入验证码!"
/>
),
},
]}
/>
</Tab>
<div>
<Checkbox checked={autoLogin} onChange={(e) => setAutoLogin(e.target.checked)}>
)}
{type === 'account' && (
<>
<ProFormText
name="userName"
fieldProps={{
size: 'large',
prefix: <UserOutlined className={styles.prefixIcon} />,
}}
placeholder={intl.formatMessage({
id: 'pages.login.username.placeholder',
defaultMessage: '用户名: admin or user',
})}
rules={[
{
required: true,
message: (
<FormattedMessage
id="pages.login.username.required"
defaultMessage="请输入用户名!"
/>
),
},
]}
/>
<ProFormText.Password
name="password"
fieldProps={{
size: 'large',
prefix: <LockTwoTone className={styles.prefixIcon} />,
}}
placeholder={intl.formatMessage({
id: 'pages.login.password.placeholder',
defaultMessage: '密码: ant.design',
})}
rules={[
{
required: true,
message: (
<FormattedMessage
id="pages.login.password.required"
defaultMessage="请输入密码!"
/>
),
},
]}
/>
</>
)}
{status === 'error' && loginType === 'mobile' && !submitting && (
<LoginMessage content="验证码错误" />
)}
{type === 'mobile' && (
<>
<ProFormText
fieldProps={{
size: 'large',
prefix: <MobileTwoTone className={styles.prefixIcon} />,
}}
name="mobile"
placeholder={intl.formatMessage({
id: 'pages.login.phoneNumber.placeholder',
defaultMessage: '手机号',
})}
rules={[
{
required: true,
message: (
<FormattedMessage
id="pages.login.phoneNumber.required"
defaultMessage="请输入手机号!"
/>
),
},
{
pattern: /^1\d{10}$/,
message: (
<FormattedMessage
id="pages.login.phoneNumber.invalid"
defaultMessage="手机号格式错误!"
/>
),
},
]}
/>
<ProFormCaptcha
fieldProps={{
size: 'large',
prefix: <MailTwoTone className={styles.prefixIcon} />,
}}
captchaProps={{
size: 'large',
}}
placeholder={intl.formatMessage({
id: 'pages.login.captcha.placeholder',
defaultMessage: '请输入验证码',
})}
captchaTextRender={(timing, count) =>
timing
? `${count} ${intl.formatMessage({
id: 'pages.getCaptchaSecondText',
defaultMessage: '获取验证码',
})}`
: intl.formatMessage({
id: 'pages.login.phoneLogin.getVerificationCode',
defaultMessage: '获取验证码',
})
}
name="captcha"
rules={[
{
required: true,
message: (
<FormattedMessage
id="pages.login.captcha.required"
defaultMessage="请输入验证码!"
/>
),
},
]}
onGetCaptcha={async (mobile) => {
const result = await getFakeCaptcha(mobile);
if (result === false) {
return;
}
message.success('获取验证码成功!验证码为:1234');
}}
/>
</>
)}
<div
style={{
marginBottom: 24,
}}
>
<ProFormCheckbox noStyle name="autoLogin">
<FormattedMessage id="pages.login.rememberMe" defaultMessage="自动登录" />
</Checkbox>
</ProFormCheckbox>
<a
style={{
float: 'right',
......@@ -178,19 +240,13 @@ const Login: React.FC<LoginProps> = (props) => {
<FormattedMessage id="pages.login.forgotPassword" defaultMessage="忘记密码" />
</a>
</div>
<Submit loading={submitting}>
<FormattedMessage id="pages.login.submit" defaultMessage="登录" />
</Submit>
<div className={styles.other}>
<FormattedMessage id="pages.login.loginWith" defaultMessage="其他登录方式" />
<AlipayCircleOutlined className={styles.icon} />
<TaobaoCircleOutlined className={styles.icon} />
<WeiboCircleOutlined className={styles.icon} />
<Link className={styles.register} to="/user/register">
<FormattedMessage id="pages.login.registerAccount" defaultMessage="注册账户" />
</Link>
</div>
</LoginForm>
</ProForm>
<Space className={styles.other}>
<FormattedMessage id="pages.login.loginWith" defaultMessage="其他登录方式" />
<AlipayCircleOutlined className={styles.icon} />
<TaobaoCircleOutlined className={styles.icon} />
<WeiboCircleOutlined className={styles.icon} />
</Space>
</div>
);
};
......
......@@ -23,6 +23,7 @@
}
},
"include": [
"mock/**/*",
"src/**/*",
"tests/**/*",
"test/**/*",
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册