对接注册和退出登录
This commit is contained in:
@ -1,18 +1,74 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
//导入logo图片
|
||||
import LogoImg from '@/assets/img/logo.png';
|
||||
// 引入样式文件
|
||||
import './Header.less';
|
||||
//导入菜单组件
|
||||
import HeaderMenu from './HeaderMenu';
|
||||
const Header: React.FC = (props) => {
|
||||
import { connect, history } from 'umi';
|
||||
import type { UserModelState } from '@/models/user';
|
||||
import type { ConnectProps, Dispatch } from 'umi';
|
||||
import { Button, Dropdown, message, Modal } from 'antd';
|
||||
import { DownOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
|
||||
interface PageProps extends ConnectProps {
|
||||
user: UserModelState; // dva model状态
|
||||
dispatch: Dispatch; // dva dispatch方法
|
||||
}
|
||||
const Header: React.FC<PageProps> = ({ user, dispatch }) => {
|
||||
useEffect(() => {
|
||||
if (user.token) {
|
||||
dispatch({
|
||||
type: 'user/fetchUserInfo',
|
||||
});
|
||||
}
|
||||
}, [user.token]);
|
||||
const handleMenuClick = (e: any) => {
|
||||
if (e.key === 'logout') {
|
||||
Modal.confirm({
|
||||
title: '确定退出登录吗?',
|
||||
icon: <ExclamationCircleOutlined />,
|
||||
content: '退出登录后,您将需要重新登录',
|
||||
onOk() {
|
||||
dispatch({
|
||||
type: 'user/logout',
|
||||
}).then(() => {
|
||||
sessionStorage.clear();
|
||||
message.success('退出登录成功');
|
||||
history.push('/index');
|
||||
});
|
||||
},
|
||||
onCancel() {
|
||||
return;
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="header-container">
|
||||
<div className="header">
|
||||
<img className="logo" src={LogoImg} alt="logo" />
|
||||
<HeaderMenu />
|
||||
{user.userInfo?.fullName && (
|
||||
<Dropdown
|
||||
trigger={['hover']}
|
||||
menu={{
|
||||
items: [
|
||||
{
|
||||
key: 'logout',
|
||||
label: '退出登录',
|
||||
},
|
||||
],
|
||||
onClick: handleMenuClick,
|
||||
}}
|
||||
>
|
||||
<Button type="link">
|
||||
{`${user.userInfo?.fullName}`}
|
||||
<DownOutlined />
|
||||
</Button>
|
||||
</Dropdown>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default Header;
|
||||
export default connect(({ user }: { user: UserModelState }) => ({ user }))(Header);
|
||||
|
@ -4,6 +4,7 @@ import help from './en-US/help';
|
||||
import policy from './en-US/policy';
|
||||
import register from './en-US/register';
|
||||
import home from './en-US/home';
|
||||
import login from './en-US/login';
|
||||
|
||||
export default {
|
||||
'menu.首页': 'Home',
|
||||
@ -39,19 +40,6 @@ export default {
|
||||
"加载更多":"Load More",
|
||||
"登录/注册":"Login/Register",
|
||||
|
||||
// Login page
|
||||
"login.title": "E-Bidding Platform",
|
||||
"login.tab.supplier": "Supplier",
|
||||
"login.tab.expert": "Expert",
|
||||
"login.tab.agent": "Bidding Agent",
|
||||
"login.username.placeholder": "Please enter username",
|
||||
"login.password.placeholder": "Please enter password",
|
||||
"login.remember": "Remember password",
|
||||
"login.forgot": "Forgot password?",
|
||||
"login.button": "Login",
|
||||
"login.register.tip": "Don't have an account?",
|
||||
"login.register.action": "Register Now",
|
||||
"login.back.home": "Back to Home",
|
||||
|
||||
// Help Center module
|
||||
...help,
|
||||
@ -70,4 +58,7 @@ export default {
|
||||
|
||||
// Home module
|
||||
...home,
|
||||
|
||||
// Login page
|
||||
...login,
|
||||
};
|
||||
|
16
src/locales/en-US/login.ts
Normal file
16
src/locales/en-US/login.ts
Normal file
@ -0,0 +1,16 @@
|
||||
export default {
|
||||
// Login page
|
||||
"login.title": "E-Bidding Platform",
|
||||
"login.tab.supplier": "Supplier",
|
||||
"login.tab.expert": "Expert",
|
||||
"login.tab.agent": "Bidding Agent",
|
||||
"login.username.placeholder": "Please enter username",
|
||||
"login.password.placeholder": "Please enter password",
|
||||
"login.remember": "Remember me",
|
||||
"login.forgot": "Forgot password?",
|
||||
"login.button": "Login",
|
||||
"login.register.tip": "Don't have an account?",
|
||||
"login.register.action": "Register now",
|
||||
"login.back.home": "Back to home",
|
||||
"login.captcha.placeholder": "Please enter verification code",
|
||||
};
|
@ -4,6 +4,7 @@ import help from './zh-CN/help';
|
||||
import policy from './zh-CN/policy';
|
||||
import register from './zh-CN/register';
|
||||
import home from './zh-CN/home';
|
||||
import login from './zh-CN/login';
|
||||
|
||||
export default {
|
||||
'menu.首页': '首页',
|
||||
@ -39,19 +40,6 @@ export default {
|
||||
"加载更多":"加载更多",
|
||||
"登录/注册":"登录/注册",
|
||||
|
||||
// 登录页文案
|
||||
"login.title": "电子招投标平台",
|
||||
"login.tab.supplier": "供应商",
|
||||
"login.tab.expert": "专家",
|
||||
"login.tab.agent": "招标代理",
|
||||
"login.username.placeholder": "请输入用户名",
|
||||
"login.password.placeholder": "请输入密码",
|
||||
"login.remember": "记住密码",
|
||||
"login.forgot": "忘记密码?",
|
||||
"login.button": "登录",
|
||||
"login.register.tip": "还没有账号?",
|
||||
"login.register.action": "立即注册",
|
||||
"login.back.home": "返回首页",
|
||||
|
||||
// 帮助中心模块
|
||||
...help,
|
||||
@ -70,4 +58,7 @@ export default {
|
||||
|
||||
// Home module
|
||||
...home,
|
||||
|
||||
// Login page
|
||||
...login,
|
||||
};
|
||||
|
16
src/locales/zh-CN/login.ts
Normal file
16
src/locales/zh-CN/login.ts
Normal file
@ -0,0 +1,16 @@
|
||||
export default {
|
||||
// 登录页文案
|
||||
"login.title": "中远海运",
|
||||
"login.tab.supplier": "供应商",
|
||||
"login.tab.expert": "专家",
|
||||
"login.tab.agent": "招标代理",
|
||||
"login.username.placeholder": "请输入用户名",
|
||||
"login.password.placeholder": "请输入密码",
|
||||
"login.remember": "记住密码",
|
||||
"login.forgot": "忘记密码?",
|
||||
"login.button": "登录",
|
||||
"login.register.tip": "还没有账号?",
|
||||
"login.register.action": "立即注册",
|
||||
"login.back.home": "返回首页",
|
||||
"login.captcha.placeholder": "请输入验证码",
|
||||
};
|
4
src/models/index.ts
Normal file
4
src/models/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import user from './user';
|
||||
export default {
|
||||
user,
|
||||
};
|
103
src/models/user.ts
Normal file
103
src/models/user.ts
Normal file
@ -0,0 +1,103 @@
|
||||
import type { Effect, Reducer } from 'umi';
|
||||
|
||||
import { getUserinfo, Logout } from '@/servers/api/login';
|
||||
|
||||
export type SupplierUser = {
|
||||
email?: string;
|
||||
mobile?: string;
|
||||
name?: string;
|
||||
sex?: string;
|
||||
status?: number;
|
||||
userId?: string;
|
||||
userName?: string;
|
||||
};
|
||||
|
||||
export type User = {
|
||||
userId: string;
|
||||
userName: string;
|
||||
userType: string;
|
||||
};
|
||||
|
||||
export type UserInfo = {
|
||||
userId: string;
|
||||
fullName: string;
|
||||
loginName: string;
|
||||
userType: string;
|
||||
authorityList: { roleId: string, roleName: string, roleCode: string, roleScope: string }[];
|
||||
};
|
||||
|
||||
export type UserModelState = {
|
||||
supplierUser?: SupplierUser;
|
||||
user?: User;
|
||||
token?: string;
|
||||
userInfo?: UserInfo;
|
||||
};
|
||||
|
||||
export type UserModelType = {
|
||||
namespace: 'user';
|
||||
state: UserModelState;
|
||||
effects: {
|
||||
fetchUserInfo: Effect;
|
||||
logout: Effect;
|
||||
};
|
||||
reducers: {
|
||||
saveLoginUser: Reducer<UserModelState>;
|
||||
saveUserInfo: Reducer<UserModelState>;
|
||||
clearUserInfo: Reducer;
|
||||
};
|
||||
};
|
||||
|
||||
const UserModel: UserModelType = {
|
||||
namespace: 'user',
|
||||
|
||||
state: {
|
||||
supplierUser: {}, // 登录返回的供应商用户信息
|
||||
user: undefined, // 登录返回的用户信息
|
||||
token: sessionStorage.getItem('token') || '', // 登录返回的token
|
||||
userInfo: undefined, // 请求userInfo返回的用户信息
|
||||
},
|
||||
|
||||
effects: {
|
||||
*fetchUserInfo(_, { call, put }) {
|
||||
const response = yield call(getUserinfo);
|
||||
yield put({
|
||||
type: 'saveUserInfo',
|
||||
payload: response,
|
||||
});
|
||||
},
|
||||
*logout(_, { call, put }) {
|
||||
yield call(Logout);
|
||||
yield put({
|
||||
type: 'clearUserInfo'
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
reducers: {
|
||||
clearUserInfo(state) {
|
||||
return {
|
||||
...state,
|
||||
userInfo: undefined,
|
||||
token: '',
|
||||
supplierUser: {},
|
||||
user: {},
|
||||
};
|
||||
},
|
||||
saveLoginUser(state, action) {
|
||||
return {
|
||||
...state,
|
||||
supplierUser: action.payload.supplierUser || {},
|
||||
user: action.payload.user || {},
|
||||
token: action.payload.token || '',
|
||||
};
|
||||
},
|
||||
saveUserInfo(state, action) {
|
||||
return {
|
||||
...state,
|
||||
userInfo: action.payload || {},
|
||||
};
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
export default UserModel;
|
@ -31,70 +31,43 @@
|
||||
}
|
||||
.noticeList {
|
||||
padding: 10px 0;
|
||||
.noticeTitle {
|
||||
font-size: 22px;
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
align-items: center;
|
||||
.noticeName {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: @main-color;
|
||||
padding: 10px 7px;
|
||||
color: @main-danger-color;
|
||||
width: 50px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
.noticeItem {
|
||||
background-color: #fff;
|
||||
padding: 24px;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0px 1px 4px 0px rgba(0, 0, 0, 0.05);
|
||||
height: 249px;
|
||||
.title {
|
||||
margin: 14px 0;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
// 超过两行显示省略号
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
height: 63px;
|
||||
}
|
||||
.content{
|
||||
font-size: 16px;
|
||||
color: @main-text-color-2;
|
||||
// 超过两行显示省略号
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.button{
|
||||
color: @main-color;
|
||||
font-size: 16px;
|
||||
margin-top: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.header {
|
||||
margin-right: 20px;
|
||||
flex: 1;
|
||||
width: 0;
|
||||
.cardTitle {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.time {
|
||||
font-size: 14px;
|
||||
.cardTitleText {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: @main-color;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
.cardTitleTime {
|
||||
color: @main-text-color-2;
|
||||
}
|
||||
.type {
|
||||
display: inline-block;
|
||||
background-color: @main-color;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
padding: 4px 8px;
|
||||
border-radius: 20px;
|
||||
&.primary {
|
||||
background-color: rgba(59, 130, 246, 0.1);
|
||||
color: #3b82f6;
|
||||
}
|
||||
&.danger {
|
||||
background-color: rgba(255, 77, 77, 0.1);
|
||||
color: #ff4d4d;
|
||||
}
|
||||
}
|
||||
}
|
||||
.cardContent {
|
||||
color: @main-text-color-2;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -163,7 +136,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.search {
|
||||
.search{
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Card, Row, Col, Tabs, Table, Button, Space } from 'antd';
|
||||
import { useIntl, Link, history } from 'umi';
|
||||
import { useIntl, Link, history, connect } from 'umi';
|
||||
import './index.less';
|
||||
import IconFont from '@/components/IconFont/IconFont';
|
||||
import Search from '../announce/Search';
|
||||
@ -12,7 +12,8 @@ import { getAboutUs } from '@/servers/api/about';
|
||||
// 获取通知公告
|
||||
import { getNoticeList } from '@/servers/api/notice';
|
||||
import { filterHtmlTag } from '@/utils/utils';
|
||||
const IndexPage: React.FC = () => {
|
||||
const IndexPage: React.FC<any> = ({ user }) => {
|
||||
const token = user.token;
|
||||
const intl = useIntl();
|
||||
const [noticeLoading, setNoticeLoading] = useState(false);
|
||||
// 友情链接
|
||||
@ -186,7 +187,29 @@ const IndexPage: React.FC = () => {
|
||||
return (
|
||||
<div>
|
||||
<img className="banner" src={require('@/assets/img/banner.jpg')} alt="" />
|
||||
|
||||
{/* 通知列表 */}
|
||||
<div className="noticeList layout-content-main">
|
||||
<div className="noticeName">通知公告</div>
|
||||
{noticeList.map((item) => (
|
||||
<div className="noticeItem" key={item.id}>
|
||||
<div className="cardTitle">
|
||||
<span
|
||||
className="cardTitleText"
|
||||
onClick={() => {
|
||||
history.push({
|
||||
pathname: '/announce/announceInfo',
|
||||
search: '?id=' + item.id,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{item.title}
|
||||
</span>
|
||||
<span className="cardTitleTime">{item.publishTime}</span>
|
||||
</div>
|
||||
<p className="cardContent" dangerouslySetInnerHTML={{ __html: item.content }} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="loginType">
|
||||
<div className="layout-content-main">
|
||||
<Row gutter={20}>
|
||||
@ -242,49 +265,6 @@ const IndexPage: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 通知列表 */}
|
||||
<div className="noticeList">
|
||||
<div className="layout-content-main">
|
||||
<div className="noticeTitle">
|
||||
通知公告
|
||||
</div>
|
||||
<Row gutter={32}>
|
||||
{noticeList.map((item) => (
|
||||
<Col span={6} key={item.id}>
|
||||
<div className="noticeItem" >
|
||||
<div className='header'>
|
||||
<span className='time'>{item.publishTime}</span>
|
||||
<span className={`type ${item.columnType === 'notice' ? 'primary' : 'danger'}`}>{item.columnType === 'notice' ? '公告' : '通知'}</span>
|
||||
</div>
|
||||
<div className='title'>{item[intl.formatMessage({ id: 'notice.data.title' }) as keyof typeof item]}</div>
|
||||
<div className='content'>{filterHtmlTag(item[intl.formatMessage({ id: 'notice.data.content' }) as keyof typeof item] || '')}</div>
|
||||
<div className="button" onClick={() => {
|
||||
history.push({
|
||||
pathname: '/announce/announceInfo',
|
||||
search: '?id=' + item.id,
|
||||
});
|
||||
}}>{intl.formatMessage({ id: 'notice.detail' })}></div>
|
||||
{/* <div className="cardTitle">
|
||||
<span
|
||||
className="cardTitleText"
|
||||
onClick={() => {
|
||||
history.push({
|
||||
pathname: '/announce/announceInfo',
|
||||
search: '?id=' + item.id,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{item.title}
|
||||
</span>
|
||||
<span className="cardTitleTime">{item.time}</span>
|
||||
</div> */}
|
||||
{/* <p className="cardContent">{item.content}</p> */}
|
||||
</div>
|
||||
</Col>
|
||||
))}
|
||||
</Row>
|
||||
</div>
|
||||
</div>
|
||||
<div className="layout-content-main announce">
|
||||
<Row gutter={40}>
|
||||
<Col span={5}>
|
||||
@ -393,4 +373,6 @@ const IndexPage: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default IndexPage;
|
||||
export default connect(({ user }: any) => ({
|
||||
user,
|
||||
}))(IndexPage);
|
||||
|
@ -1,26 +1,57 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Form, Input, Button, Checkbox, Tabs, message } from 'antd';
|
||||
import { UserOutlined, LockOutlined, EyeInvisibleOutlined, EyeTwoTone, HomeOutlined } from '@ant-design/icons';
|
||||
import { history, useIntl } from 'umi';
|
||||
import {
|
||||
UserOutlined,
|
||||
LockOutlined,
|
||||
EyeInvisibleOutlined,
|
||||
EyeTwoTone,
|
||||
HomeOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { history, useIntl, connect } from 'umi';
|
||||
import type { ConnectProps, Dispatch } from 'umi';
|
||||
import './login.less';
|
||||
import { getCaptcha, supplierLogin, getUserinfo, findMenuList } from '@/servers/api/login';
|
||||
import { encryptWithRsa } from '@/utils/encryptWithRsa';
|
||||
import type { UserModelState } from '@/models/user';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
const LoginPage: React.FC = () => {
|
||||
interface PageProps extends ConnectProps {
|
||||
user: UserModelState; // dva model状态
|
||||
dispatch: Dispatch; // dva dispatch方法
|
||||
}
|
||||
const LoginPage: React.FC<PageProps> = ({ user, dispatch }) => {
|
||||
const [activeKey, setActiveKey] = useState('supplier');
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const intl = useIntl();
|
||||
|
||||
const onFinish = (values: any) => {
|
||||
const [captchaImg, setCaptchaImg] = useState('');
|
||||
const [captchaKey, setCaptchaKey] = useState('');
|
||||
const onFinish = async (values: any) => {
|
||||
setLoading(true);
|
||||
console.log('登录信息:', values);
|
||||
// 这里添加登录逻辑
|
||||
setTimeout(() => {
|
||||
try {
|
||||
const params = {
|
||||
...values,
|
||||
password: encryptWithRsa(values.password, false),
|
||||
encryptValue: encryptWithRsa(values.identifying),
|
||||
};
|
||||
const loginRes = await supplierLogin(params);
|
||||
if (loginRes.code === 200) {
|
||||
sessionStorage.setItem('token', loginRes.data.token);
|
||||
//存入供应商用户id
|
||||
sessionStorage.setItem('userId', loginRes.data.supplierUser.userId);
|
||||
sessionStorage.setItem('currentUser', JSON.stringify(loginRes.data));
|
||||
dispatch({
|
||||
type: 'user/saveLoginUser',
|
||||
payload: loginRes.data,
|
||||
})
|
||||
message.success('登录成功');
|
||||
history.push('/index');
|
||||
} else {
|
||||
message.error(loginRes.message || '登录失败');
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
message.success('登录成功');
|
||||
history.push('/index');
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
|
||||
const handleTabChange = (key: string) => {
|
||||
@ -30,7 +61,7 @@ const LoginPage: React.FC = () => {
|
||||
|
||||
// 根据当前选中的Tab决定跳转到哪个注册页面
|
||||
const handleRegister = () => {
|
||||
switch(activeKey) {
|
||||
switch (activeKey) {
|
||||
case 'supplier':
|
||||
history.push('/register/supplier');
|
||||
break;
|
||||
@ -52,42 +83,55 @@ const LoginPage: React.FC = () => {
|
||||
return (
|
||||
<div className="register-link">
|
||||
{intl.formatMessage({ id: 'login.register.tip' })}
|
||||
<a onClick={handleRegister}>
|
||||
{intl.formatMessage({ id: 'login.register.action' })}
|
||||
</a>
|
||||
<a onClick={handleRegister}>{intl.formatMessage({ id: 'login.register.action' })}</a>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 获取验证码
|
||||
const fetchCaptcha = async () => {
|
||||
const res = await getCaptcha();
|
||||
if (res.code === 200) {
|
||||
setCaptchaImg(res.data.base64Image);
|
||||
setCaptchaKey(res.data.code);
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
fetchCaptcha();
|
||||
}, [activeKey]);
|
||||
return (
|
||||
<div className='login-page'>
|
||||
<div className='login-container'>
|
||||
<div className='back-home'>
|
||||
<div className="login-page">
|
||||
<div className="login-container">
|
||||
<div className="back-home">
|
||||
<a onClick={() => history.push('/index')}>
|
||||
<HomeOutlined /> {intl.formatMessage({ id: 'login.back.home' })}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className='login-title'>{intl.formatMessage({ id: 'login.title' })}</div>
|
||||
<div className="login-title">{intl.formatMessage({ id: 'login.title' })}</div>
|
||||
|
||||
<div className="login-tab-container">
|
||||
{/* <div className="login-tab-container">
|
||||
<Tabs activeKey={activeKey} onChange={handleTabChange} className='login-tabs'>
|
||||
<TabPane tab={intl.formatMessage({ id: 'login.tab.supplier' })} key="supplier" />
|
||||
<TabPane tab={intl.formatMessage({ id: 'login.tab.expert' })} key="expert" />
|
||||
<TabPane tab={intl.formatMessage({ id: 'login.tab.agent' })} key="agent" />
|
||||
</Tabs>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
<Form
|
||||
form={form}
|
||||
name="login"
|
||||
className='login-form'
|
||||
className="login-form"
|
||||
initialValues={{ remember: false }}
|
||||
onFinish={onFinish}
|
||||
>
|
||||
<Form.Item
|
||||
name="username"
|
||||
rules={[{ required: true, message: intl.formatMessage({ id: 'login.username.placeholder' }) + '!' }]}
|
||||
name="account"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({ id: 'login.username.placeholder' }) + '!',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input
|
||||
prefix={<UserOutlined className="site-form-item-icon" />}
|
||||
@ -98,18 +142,48 @@ const LoginPage: React.FC = () => {
|
||||
|
||||
<Form.Item
|
||||
name="password"
|
||||
rules={[{ required: true, message: intl.formatMessage({ id: 'login.password.placeholder' }) + '!' }]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({ id: 'login.password.placeholder' }) + '!',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input.Password
|
||||
prefix={<LockOutlined className="site-form-item-icon" />}
|
||||
placeholder={intl.formatMessage({ id: 'login.password.placeholder' })}
|
||||
iconRender={visible => (visible ? <EyeTwoTone /> : <EyeInvisibleOutlined />)}
|
||||
iconRender={(visible) => (visible ? <EyeTwoTone /> : <EyeInvisibleOutlined />)}
|
||||
size="large"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="identifying"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({ id: 'login.captcha.placeholder' }) + '!',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input
|
||||
placeholder={intl.formatMessage({ id: 'login.captcha.placeholder' })}
|
||||
size="large"
|
||||
maxLength={6}
|
||||
autoComplete="off"
|
||||
prefix={null}
|
||||
suffix={
|
||||
<img
|
||||
src={`data:image/png;base64,${captchaImg}`}
|
||||
alt="验证码"
|
||||
style={{ cursor: 'pointer', height: 32, verticalAlign: 'middle' }}
|
||||
onClick={fetchCaptcha}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
<div className='login-options'>
|
||||
<div className="login-options">
|
||||
<Form.Item name="remember" valuePropName="checked" noStyle>
|
||||
<Checkbox>{intl.formatMessage({ id: 'login.remember' })}</Checkbox>
|
||||
</Form.Item>
|
||||
@ -120,7 +194,13 @@ const LoginPage: React.FC = () => {
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit" className="login-form-button" loading={loading} size="large">
|
||||
<Button
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
className="login-form-button"
|
||||
loading={loading}
|
||||
size="large"
|
||||
>
|
||||
{intl.formatMessage({ id: 'login.button' })}
|
||||
</Button>
|
||||
{renderRegisterLink()}
|
||||
@ -131,4 +211,4 @@ const LoginPage: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default LoginPage;
|
||||
export default connect(({ user }: { user: UserModelState }) => ({ user }))(LoginPage);
|
||||
|
73
src/servers/api/login.ts
Normal file
73
src/servers/api/login.ts
Normal file
@ -0,0 +1,73 @@
|
||||
import request from '@/utils/request';
|
||||
|
||||
/**
|
||||
* 验证码
|
||||
*/
|
||||
export async function getCaptcha() {
|
||||
return request('/v1/login/getCaptcha', {
|
||||
method: 'GET'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 招标代理
|
||||
*/
|
||||
export async function accountLogin (data: API.LoginSupplier) {
|
||||
return request('/v1/login/accountLogin', {
|
||||
method: 'POST',
|
||||
data
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 专家
|
||||
*/
|
||||
export async function expertLogin (data: API.LoginSupplier) {
|
||||
return request('/v1/login/expertLogin', {
|
||||
method: 'POST',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 供应商
|
||||
*/
|
||||
export async function supplierLogin (data: API.LoginSupplier) {
|
||||
return request('/v1/login/accountLogin/supplier', {
|
||||
method: 'POST',
|
||||
data
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
export async function getUserinfo() {
|
||||
return request('/v1/userinfo/get', {
|
||||
method: 'GET'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出
|
||||
*/
|
||||
export async function Logout() {
|
||||
return request('/v1/login/logout', {
|
||||
method: 'POST'
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 路由
|
||||
*/
|
||||
export async function findMenuList(data: any) {
|
||||
return request('/v1/menu/findMenuList', {
|
||||
method: 'POST',
|
||||
data
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 部门
|
||||
*/
|
||||
export async function queryUserOrgAll() {
|
||||
return request('/org/queryUserOrgAll', {
|
||||
method: 'GET'
|
||||
});
|
||||
}
|
7
src/servers/api/typings.d.ts
vendored
7
src/servers/api/typings.d.ts
vendored
@ -368,4 +368,11 @@ declare namespace API {
|
||||
thumbnail: string;
|
||||
url: string;
|
||||
}
|
||||
//登录
|
||||
interface LoginSupplier {
|
||||
account: string;
|
||||
password: string;
|
||||
identifying: string;
|
||||
encryptValue: string;
|
||||
}
|
||||
}
|
||||
|
21
src/utils/encryptWithRsa.ts
Normal file
21
src/utils/encryptWithRsa.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import JSEncrypt from 'jsencrypt';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
const PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
|
||||
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvLBkALIYR/x9Rv5TiXQGWAXTzraN/He80r9gQovSQ5oTP8qllL9+Oc1LdTijPFRsddHWg37umvFliwhmukU1NT+o2loGcKpyMHFkc/UPNjQLvd+YFR4nYhgP8l+dmRNOtQWawOt5dbksRKTghMjA+FKT2+itMsawSs1+Ic+zoIwIDAQAB
|
||||
-----END PUBLIC KEY-----`;
|
||||
|
||||
export function encryptWithRsa(value: string, type: boolean = true, publicKey: string = PUBLIC_KEY): string {
|
||||
const nowStr = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||||
|
||||
const content = type? `${value}_${nowStr}`: value;
|
||||
const encryptor = new JSEncrypt();
|
||||
encryptor.setPublicKey(publicKey);
|
||||
const encrypted = encryptor.encrypt(content);
|
||||
if (!encrypted) {
|
||||
throw new Error('RSA 加密失败');
|
||||
}
|
||||
return encrypted;
|
||||
}
|
||||
|
||||
|
@ -57,24 +57,20 @@ const request = extend({
|
||||
credentials: 'include' // 默认请求是否带上cookie
|
||||
});
|
||||
// request拦截器, 改变url 或 options.
|
||||
request.interceptors.request.use(async (url, options) => {
|
||||
if (
|
||||
options.method === 'post' ||
|
||||
options.method === 'put' ||
|
||||
options.method === 'delete' ||
|
||||
options.method === 'get'
|
||||
) {
|
||||
//如果是获取token的url,则不加token
|
||||
// headers = {
|
||||
// Authorization: getUserToken() == null ? null : getUserToken(),
|
||||
// JwtToken: getSessionUserData() == null ? null : getSessionUserData().userId,
|
||||
// ...options.headers
|
||||
// }
|
||||
// options.headers = headers;
|
||||
}
|
||||
request.interceptors.request.use((url: string, options: any) => {
|
||||
// 获取 token
|
||||
const token = sessionStorage.getItem('token');
|
||||
const userId = sessionStorage.getItem('userId');
|
||||
return {
|
||||
url,
|
||||
options: { ...options },
|
||||
options: {
|
||||
...options,
|
||||
headers: {
|
||||
...(options.headers || {}),
|
||||
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
||||
...(userId ? { Mall3Check: `${userId}` } : {}),
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
//response拦截
|
||||
|
@ -1,13 +1,15 @@
|
||||
import React from 'react';
|
||||
import { Redirect } from 'umi';
|
||||
import { message } from 'antd';
|
||||
import { connect, Redirect, history } from 'umi';
|
||||
import { message, Modal } from 'antd';
|
||||
// 权限校验
|
||||
export default (props: any) => {
|
||||
if (localStorage.getItem('token')) {
|
||||
return <div>{props.children}</div>;
|
||||
const AuthWrapper = ({ user, children }: any) => {
|
||||
if (user.token) {
|
||||
return <div>{children}</div>;
|
||||
} else {
|
||||
// 提示后跳转
|
||||
message.error('请先登录');
|
||||
return <Redirect to="/login" />;
|
||||
}
|
||||
};
|
||||
|
||||
export default connect(({ user }: any) => ({
|
||||
user,
|
||||
}))(AuthWrapper);
|
||||
|
Reference in New Issue
Block a user