对接注册和退出登录
This commit is contained in:
@ -1,13 +1,18 @@
|
|||||||
export default {
|
export default {
|
||||||
dev: {
|
dev: {
|
||||||
// '/api/wfap/v1/audit/bill/find/by/procid': {
|
'/api/v1': {
|
||||||
// target: 'http://10.242.37.148:8891/',//审批单 dev环境自动审批,暂时用不到
|
target: 'http://10.0.0.10:18030',// 茂
|
||||||
// changeOrigin: true,
|
// target: 'http://10.0.0.14:18030',// 李
|
||||||
// pathRewrite: { '^': '' },
|
changeOrigin: true,
|
||||||
// },
|
pathRewrite: { '^/api/v1': '/v1' },
|
||||||
|
},
|
||||||
'/api': {
|
'/api': {
|
||||||
// target: 'http://10.0.0.125:18012',//测试环境
|
// target: 'http://10.242.37.148:18022',//
|
||||||
target: 'http://10.0.0.10:18013',//连接天宫的ng
|
target: 'http://10.0.0.10:18013',// 茂
|
||||||
|
// target: 'http://10.0.0.125:18012',// 测试
|
||||||
|
// target: 'http://10.0.0.14:18012',// 李
|
||||||
|
// target: 'http://10.0.0.14:18030',// 李
|
||||||
|
// target: 'http://10.0.0.46:18013',// 袁
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
pathRewrite: { '^/api': '' },
|
pathRewrite: { '^/api': '' },
|
||||||
},
|
},
|
||||||
|
@ -70,9 +70,11 @@
|
|||||||
"array-move": "3.0.1",
|
"array-move": "3.0.1",
|
||||||
"axios": "0.21.1",
|
"axios": "0.21.1",
|
||||||
"classnames": "2.3.1",
|
"classnames": "2.3.1",
|
||||||
|
"dayjs": "^1.11.13",
|
||||||
"dva": "2.4.1",
|
"dva": "2.4.1",
|
||||||
"echarts": "^5.2.2",
|
"echarts": "^5.2.2",
|
||||||
"echarts-for-react": "^3.0.2",
|
"echarts-for-react": "^3.0.2",
|
||||||
|
"jsencrypt": "^3.3.2",
|
||||||
"lodash": "4.17.21",
|
"lodash": "4.17.21",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"omit.js": "2.0.2",
|
"omit.js": "2.0.2",
|
||||||
|
10
pnpm-lock.yaml
generated
10
pnpm-lock.yaml
generated
@ -50,6 +50,9 @@ dependencies:
|
|||||||
classnames:
|
classnames:
|
||||||
specifier: 2.3.1
|
specifier: 2.3.1
|
||||||
version: 2.3.1
|
version: 2.3.1
|
||||||
|
dayjs:
|
||||||
|
specifier: ^1.11.13
|
||||||
|
version: 1.11.13
|
||||||
dva:
|
dva:
|
||||||
specifier: 2.4.1
|
specifier: 2.4.1
|
||||||
version: 2.4.1(react-dom@16.14.0)(react@16.14.0)
|
version: 2.4.1(react-dom@16.14.0)(react@16.14.0)
|
||||||
@ -59,6 +62,9 @@ dependencies:
|
|||||||
echarts-for-react:
|
echarts-for-react:
|
||||||
specifier: ^3.0.2
|
specifier: ^3.0.2
|
||||||
version: 3.0.2(echarts@5.6.0)(react@16.14.0)
|
version: 3.0.2(echarts@5.6.0)(react@16.14.0)
|
||||||
|
jsencrypt:
|
||||||
|
specifier: ^3.3.2
|
||||||
|
version: 3.3.2
|
||||||
lodash:
|
lodash:
|
||||||
specifier: 4.17.21
|
specifier: 4.17.21
|
||||||
version: 4.17.21
|
version: 4.17.21
|
||||||
@ -10263,6 +10269,10 @@ packages:
|
|||||||
- utf-8-validate
|
- utf-8-validate
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/jsencrypt@3.3.2:
|
||||||
|
resolution: {integrity: sha512-arQR1R1ESGdAxY7ZheWr12wCaF2yF47v5qpB76TtV64H1pyGudk9Hvw8Y9tb/FiTIaaTRUyaSnm5T/Y53Ghm/A==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/jsesc@2.5.2:
|
/jsesc@2.5.2:
|
||||||
resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==}
|
resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
@ -1,18 +1,74 @@
|
|||||||
import React from 'react';
|
import React, { useEffect } from 'react';
|
||||||
//导入logo图片
|
//导入logo图片
|
||||||
import LogoImg from '@/assets/img/logo.png';
|
import LogoImg from '@/assets/img/logo.png';
|
||||||
// 引入样式文件
|
// 引入样式文件
|
||||||
import './Header.less';
|
import './Header.less';
|
||||||
//导入菜单组件
|
//导入菜单组件
|
||||||
import HeaderMenu from './HeaderMenu';
|
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 (
|
return (
|
||||||
<div className="header-container">
|
<div className="header-container">
|
||||||
<div className="header">
|
<div className="header">
|
||||||
<img className="logo" src={LogoImg} alt="logo" />
|
<img className="logo" src={LogoImg} alt="logo" />
|
||||||
<HeaderMenu />
|
<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>
|
||||||
</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 policy from './en-US/policy';
|
||||||
import register from './en-US/register';
|
import register from './en-US/register';
|
||||||
import home from './en-US/home';
|
import home from './en-US/home';
|
||||||
|
import login from './en-US/login';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
'menu.首页': 'Home',
|
'menu.首页': 'Home',
|
||||||
@ -39,19 +40,6 @@ export default {
|
|||||||
"加载更多":"Load More",
|
"加载更多":"Load More",
|
||||||
"登录/注册":"Login/Register",
|
"登录/注册":"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 Center module
|
||||||
...help,
|
...help,
|
||||||
@ -70,4 +58,7 @@ export default {
|
|||||||
|
|
||||||
// Home module
|
// Home module
|
||||||
...home,
|
...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 policy from './zh-CN/policy';
|
||||||
import register from './zh-CN/register';
|
import register from './zh-CN/register';
|
||||||
import home from './zh-CN/home';
|
import home from './zh-CN/home';
|
||||||
|
import login from './zh-CN/login';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
'menu.首页': '首页',
|
'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,
|
...help,
|
||||||
@ -70,4 +58,7 @@ export default {
|
|||||||
|
|
||||||
// Home module
|
// Home module
|
||||||
...home,
|
...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 {
|
.noticeList {
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
.noticeTitle {
|
display: flex;
|
||||||
font-size: 22px;
|
gap: 20px;
|
||||||
|
align-items: center;
|
||||||
|
.noticeName {
|
||||||
|
font-size: 18px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: @main-color;
|
color: @main-danger-color;
|
||||||
padding: 10px 7px;
|
width: 50px;
|
||||||
|
line-height: 20px;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
.noticeItem {
|
.noticeItem {
|
||||||
background-color: #fff;
|
margin-right: 20px;
|
||||||
padding: 24px;
|
flex: 1;
|
||||||
border-radius: 16px;
|
width: 0;
|
||||||
box-shadow: 0px 1px 4px 0px rgba(0, 0, 0, 0.05);
|
.cardTitle {
|
||||||
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 {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
.cardTitleText {
|
||||||
.time {
|
font-size: 18px;
|
||||||
font-size: 14px;
|
font-weight: 600;
|
||||||
|
color: @main-color;
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.cardTitleTime {
|
||||||
color: @main-text-color-2;
|
color: @main-text-color-2;
|
||||||
}
|
}
|
||||||
.type {
|
}
|
||||||
display: inline-block;
|
.cardContent {
|
||||||
background-color: @main-color;
|
color: @main-text-color-2;
|
||||||
color: #fff;
|
text-overflow: ellipsis;
|
||||||
font-size: 12px;
|
overflow: hidden;
|
||||||
padding: 4px 8px;
|
white-space: nowrap;
|
||||||
border-radius: 20px;
|
margin-bottom: 0;
|
||||||
&.primary {
|
|
||||||
background-color: rgba(59, 130, 246, 0.1);
|
|
||||||
color: #3b82f6;
|
|
||||||
}
|
|
||||||
&.danger {
|
|
||||||
background-color: rgba(255, 77, 77, 0.1);
|
|
||||||
color: #ff4d4d;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -163,7 +136,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.search {
|
.search{
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Card, Row, Col, Tabs, Table, Button, Space } from 'antd';
|
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 './index.less';
|
||||||
import IconFont from '@/components/IconFont/IconFont';
|
import IconFont from '@/components/IconFont/IconFont';
|
||||||
import Search from '../announce/Search';
|
import Search from '../announce/Search';
|
||||||
@ -12,7 +12,8 @@ import { getAboutUs } from '@/servers/api/about';
|
|||||||
// 获取通知公告
|
// 获取通知公告
|
||||||
import { getNoticeList } from '@/servers/api/notice';
|
import { getNoticeList } from '@/servers/api/notice';
|
||||||
import { filterHtmlTag } from '@/utils/utils';
|
import { filterHtmlTag } from '@/utils/utils';
|
||||||
const IndexPage: React.FC = () => {
|
const IndexPage: React.FC<any> = ({ user }) => {
|
||||||
|
const token = user.token;
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const [noticeLoading, setNoticeLoading] = useState(false);
|
const [noticeLoading, setNoticeLoading] = useState(false);
|
||||||
// 友情链接
|
// 友情链接
|
||||||
@ -186,7 +187,29 @@ const IndexPage: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<img className="banner" src={require('@/assets/img/banner.jpg')} alt="" />
|
<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="loginType">
|
||||||
<div className="layout-content-main">
|
<div className="layout-content-main">
|
||||||
<Row gutter={20}>
|
<Row gutter={20}>
|
||||||
@ -242,49 +265,6 @@ const IndexPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</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">
|
<div className="layout-content-main announce">
|
||||||
<Row gutter={40}>
|
<Row gutter={40}>
|
||||||
<Col span={5}>
|
<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 { Form, Input, Button, Checkbox, Tabs, message } from 'antd';
|
||||||
import { UserOutlined, LockOutlined, EyeInvisibleOutlined, EyeTwoTone, HomeOutlined } from '@ant-design/icons';
|
import {
|
||||||
import { history, useIntl } from 'umi';
|
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 './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 { TabPane } = Tabs;
|
||||||
|
interface PageProps extends ConnectProps {
|
||||||
const LoginPage: React.FC = () => {
|
user: UserModelState; // dva model状态
|
||||||
|
dispatch: Dispatch; // dva dispatch方法
|
||||||
|
}
|
||||||
|
const LoginPage: React.FC<PageProps> = ({ user, dispatch }) => {
|
||||||
const [activeKey, setActiveKey] = useState('supplier');
|
const [activeKey, setActiveKey] = useState('supplier');
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
const [captchaImg, setCaptchaImg] = useState('');
|
||||||
const onFinish = (values: any) => {
|
const [captchaKey, setCaptchaKey] = useState('');
|
||||||
|
const onFinish = async (values: any) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
console.log('登录信息:', values);
|
try {
|
||||||
// 这里添加登录逻辑
|
const params = {
|
||||||
setTimeout(() => {
|
...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);
|
setLoading(false);
|
||||||
message.success('登录成功');
|
}
|
||||||
history.push('/index');
|
|
||||||
}, 1000);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTabChange = (key: string) => {
|
const handleTabChange = (key: string) => {
|
||||||
@ -30,7 +61,7 @@ const LoginPage: React.FC = () => {
|
|||||||
|
|
||||||
// 根据当前选中的Tab决定跳转到哪个注册页面
|
// 根据当前选中的Tab决定跳转到哪个注册页面
|
||||||
const handleRegister = () => {
|
const handleRegister = () => {
|
||||||
switch(activeKey) {
|
switch (activeKey) {
|
||||||
case 'supplier':
|
case 'supplier':
|
||||||
history.push('/register/supplier');
|
history.push('/register/supplier');
|
||||||
break;
|
break;
|
||||||
@ -52,42 +83,55 @@ const LoginPage: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="register-link">
|
<div className="register-link">
|
||||||
{intl.formatMessage({ id: 'login.register.tip' })}
|
{intl.formatMessage({ id: 'login.register.tip' })}
|
||||||
<a onClick={handleRegister}>
|
<a onClick={handleRegister}>{intl.formatMessage({ id: 'login.register.action' })}</a>
|
||||||
{intl.formatMessage({ id: 'login.register.action' })}
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
// 获取验证码
|
||||||
|
const fetchCaptcha = async () => {
|
||||||
|
const res = await getCaptcha();
|
||||||
|
if (res.code === 200) {
|
||||||
|
setCaptchaImg(res.data.base64Image);
|
||||||
|
setCaptchaKey(res.data.code);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
fetchCaptcha();
|
||||||
|
}, [activeKey]);
|
||||||
return (
|
return (
|
||||||
<div className='login-page'>
|
<div className="login-page">
|
||||||
<div className='login-container'>
|
<div className="login-container">
|
||||||
<div className='back-home'>
|
<div className="back-home">
|
||||||
<a onClick={() => history.push('/index')}>
|
<a onClick={() => history.push('/index')}>
|
||||||
<HomeOutlined /> {intl.formatMessage({ id: 'login.back.home' })}
|
<HomeOutlined /> {intl.formatMessage({ id: 'login.back.home' })}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</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'>
|
<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.supplier' })} key="supplier" />
|
||||||
<TabPane tab={intl.formatMessage({ id: 'login.tab.expert' })} key="expert" />
|
<TabPane tab={intl.formatMessage({ id: 'login.tab.expert' })} key="expert" />
|
||||||
<TabPane tab={intl.formatMessage({ id: 'login.tab.agent' })} key="agent" />
|
<TabPane tab={intl.formatMessage({ id: 'login.tab.agent' })} key="agent" />
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div> */}
|
||||||
|
|
||||||
<Form
|
<Form
|
||||||
form={form}
|
form={form}
|
||||||
name="login"
|
name="login"
|
||||||
className='login-form'
|
className="login-form"
|
||||||
initialValues={{ remember: false }}
|
initialValues={{ remember: false }}
|
||||||
onFinish={onFinish}
|
onFinish={onFinish}
|
||||||
>
|
>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="username"
|
name="account"
|
||||||
rules={[{ required: true, message: intl.formatMessage({ id: 'login.username.placeholder' }) + '!' }]}
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: intl.formatMessage({ id: 'login.username.placeholder' }) + '!',
|
||||||
|
},
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
prefix={<UserOutlined className="site-form-item-icon" />}
|
prefix={<UserOutlined className="site-form-item-icon" />}
|
||||||
@ -98,18 +142,48 @@ const LoginPage: React.FC = () => {
|
|||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="password"
|
name="password"
|
||||||
rules={[{ required: true, message: intl.formatMessage({ id: 'login.password.placeholder' }) + '!' }]}
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: intl.formatMessage({ id: 'login.password.placeholder' }) + '!',
|
||||||
|
},
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<Input.Password
|
<Input.Password
|
||||||
prefix={<LockOutlined className="site-form-item-icon" />}
|
prefix={<LockOutlined className="site-form-item-icon" />}
|
||||||
placeholder={intl.formatMessage({ id: 'login.password.placeholder' })}
|
placeholder={intl.formatMessage({ id: 'login.password.placeholder' })}
|
||||||
iconRender={visible => (visible ? <EyeTwoTone /> : <EyeInvisibleOutlined />)}
|
iconRender={(visible) => (visible ? <EyeTwoTone /> : <EyeInvisibleOutlined />)}
|
||||||
size="large"
|
size="large"
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</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>
|
<Form.Item>
|
||||||
<div className='login-options'>
|
<div className="login-options">
|
||||||
<Form.Item name="remember" valuePropName="checked" noStyle>
|
<Form.Item name="remember" valuePropName="checked" noStyle>
|
||||||
<Checkbox>{intl.formatMessage({ id: 'login.remember' })}</Checkbox>
|
<Checkbox>{intl.formatMessage({ id: 'login.remember' })}</Checkbox>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@ -120,7 +194,13 @@ const LoginPage: React.FC = () => {
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<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' })}
|
{intl.formatMessage({ id: 'login.button' })}
|
||||||
</Button>
|
</Button>
|
||||||
{renderRegisterLink()}
|
{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;
|
thumbnail: string;
|
||||||
url: 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
|
credentials: 'include' // 默认请求是否带上cookie
|
||||||
});
|
});
|
||||||
// request拦截器, 改变url 或 options.
|
// request拦截器, 改变url 或 options.
|
||||||
request.interceptors.request.use(async (url, options) => {
|
request.interceptors.request.use((url: string, options: any) => {
|
||||||
if (
|
// 获取 token
|
||||||
options.method === 'post' ||
|
const token = sessionStorage.getItem('token');
|
||||||
options.method === 'put' ||
|
const userId = sessionStorage.getItem('userId');
|
||||||
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;
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
url,
|
url,
|
||||||
options: { ...options },
|
options: {
|
||||||
|
...options,
|
||||||
|
headers: {
|
||||||
|
...(options.headers || {}),
|
||||||
|
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
||||||
|
...(userId ? { Mall3Check: `${userId}` } : {}),
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
//response拦截
|
//response拦截
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Redirect } from 'umi';
|
import { connect, Redirect, history } from 'umi';
|
||||||
import { message } from 'antd';
|
import { message, Modal } from 'antd';
|
||||||
// 权限校验
|
// 权限校验
|
||||||
export default (props: any) => {
|
const AuthWrapper = ({ user, children }: any) => {
|
||||||
if (localStorage.getItem('token')) {
|
if (user.token) {
|
||||||
return <div>{props.children}</div>;
|
return <div>{children}</div>;
|
||||||
} else {
|
} else {
|
||||||
// 提示后跳转
|
|
||||||
message.error('请先登录');
|
|
||||||
return <Redirect to="/login" />;
|
return <Redirect to="/login" />;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default connect(({ user }: any) => ({
|
||||||
|
user,
|
||||||
|
}))(AuthWrapper);
|
||||||
|
Reference in New Issue
Block a user