Browse Source

免密登录 100%

master
wenlele 2 years ago
parent
commit
7629ba2dec
  1. 48
      web/client/src/layout/containers/layout/index.jsx
  2. 10
      web/client/src/sections/auth/actions/auth.js
  3. 186
      web/client/src/sections/auth/containers/login.jsx
  4. 197
      web/client/src/sections/humanAffairs/components/deleteModal.jsx
  5. 8
      web/config.js
  6. 2
      web/package.json

48
web/client/src/layout/containers/layout/index.jsx

@ -13,6 +13,9 @@ import { useLocation } from "react-router";
import { RouteTable } from '$utils'; import { RouteTable } from '$utils';
import { RouteRequest } from '@peace/utils'; import { RouteRequest } from '@peace/utils';
import Cookie from 'js-cookie'; import Cookie from 'js-cookie';
import { login, LOGIN_SUCCESS } from '../../../sections/auth/actions/auth';
import { error } from 'webpack-dev-server/lib/utils/colors';
NProgress.configure({ NProgress.configure({
@ -31,8 +34,8 @@ let requestUser = true
// const location111 = useLocation(); // const location111 = useLocation();
const LayoutContainer = props => { const LayoutContainer = props => {
const { const {
dispatch, msg, user, copyright, children, sections, clientWidth, clientHeight, dispatch, actions, msg, user, copyright, children, sections, clientWidth, clientHeight,
location, match, routes, history, socket, location, match, routes, history, socket,apiRoot
} = props } = props
const [collapsed, setCollapsed] = useState(false) const [collapsed, setCollapsed] = useState(false)
@ -205,32 +208,20 @@ const LayoutContainer = props => {
if (requestUser) { if (requestUser) {
requestUser = false; requestUser = false;
RouteRequest.get(RouteTable.getDomain).then(res => { RouteRequest.get(RouteTable.getDomain).then(res => {
console.log(res);
let token = Cookie.get('pepToken', { domain: res.root }); let token = Cookie.get('pepToken', { domain: res.root });
history.push('/humanAffairs/archivesCenter/personnelArchives/personnelFiles') dispatch(login({ token })).then(res => {
if (res.type == 'LOGIN_SUCCESS') {
dispatch(login(values.username, values.password)).then(res => { const data = res.payload?.user || {}
const data = res.payload.user history.push('/humanAffairs/archivesCenter/personnelArchives/personnelFiles')
localStorage.setItem('word', JSON.stringify(values.password)) localStorage.setItem('poms_open_sider', JSON.stringify(["archivesCenter"]))
dispatch(actions.layout.initWebSocket({ ioUrl: apiRoot, token: data.token, hrUserId: data.hrUserInfo && hrUserInfo.id })) localStorage.setItem('poms_selected_sider', JSON.stringify(["humanAffairs"]))
}) dispatch(actions.layout.initWebSocket({ ioUrl: apiRoot, token: data.token, hrUserId: data.hrUserInfo && hrUserInfo.id }))
} else {
// dispatch(getUserInfoByTokenUrl(token)).then(userRes => { redirectToLogin(true);
// if (userRes.success) { }
// sessionStorage.setItem('user', JSON.stringify(userRes.payload.data)); }, error => {
// dispatch({ redirectToLogin(true);
// type: 'INIT_AUTH', })
// payload: {
// user: userRes.payload.data
// }
// })
// // window.location.href = window.location.href;
// } else {
// redirectToLogin(true);
// }
// }, error => {
// redirectToLogin(true);
// })
}, error => { }, error => {
message.error('鉴权失败', 5); message.error('鉴权失败', 5);
redirectToLogin(true); redirectToLogin(true);
@ -378,7 +369,8 @@ function mapStateToProps (state) {
clientHeight: global.clientHeight, clientHeight: global.clientHeight,
msg: ajaxResponse.msg, msg: ajaxResponse.msg,
user: auth.user, user: auth.user,
socket: webSocket.socket socket: webSocket.socket,
apiRoot: global.apiRoot,
}; };
} }

10
web/client/src/sections/auth/actions/auth.js

@ -4,7 +4,7 @@ import { ApiTable, AxyRequest, EmisRequest } from '$utils'
import { Request } from '@peace/utils'; import { Request } from '@peace/utils';
export const INIT_AUTH = 'INIT_AUTH'; export const INIT_AUTH = 'INIT_AUTH';
export function initAuth(userData) { export function initAuth (userData) {
const sessionUser = JSON.parse(sessionStorage.getItem('hrUser')) const sessionUser = JSON.parse(sessionStorage.getItem('hrUser'))
const user = userData || sessionUser || {}; const user = userData || sessionUser || {};
if (user.authorized && !sessionUser) { if (user.authorized && !sessionUser) {
@ -21,11 +21,11 @@ export function initAuth(userData) {
export const REQUEST_LOGIN = 'REQUEST_LOGIN'; export const REQUEST_LOGIN = 'REQUEST_LOGIN';
export const LOGIN_SUCCESS = 'LOGIN_SUCCESS'; export const LOGIN_SUCCESS = 'LOGIN_SUCCESS';
export const LOGIN_ERROR = 'LOGIN_ERROR'; export const LOGIN_ERROR = 'LOGIN_ERROR';
export function login(username, password) { export function login ({ username, password, token }) {
return dispatch => { return dispatch => {
dispatch({ type: REQUEST_LOGIN }); dispatch({ type: REQUEST_LOGIN });
if (!username || !password) { if ((!username || !password) && !token) {
dispatch({ dispatch({
type: LOGIN_ERROR, type: LOGIN_ERROR,
payload: { error: '请输入账号名和密码' } payload: { error: '请输入账号名和密码' }
@ -43,7 +43,7 @@ export function login(username, password) {
// }, // },
// }); // });
return Request.post(ApiTable.login, { username, password, code: 'HR' }) return Request.post(ApiTable.login, { username, password, token, code: 'HR' })
.then(user => { .then(user => {
sessionStorage.setItem('hrUser', JSON.stringify(user)); sessionStorage.setItem('hrUser', JSON.stringify(user));
return dispatch({ return dispatch({
@ -63,7 +63,7 @@ export function login(username, password) {
} }
export const LOGOUT = 'LOGOUT'; export const LOGOUT = 'LOGOUT';
export function logout() { export function logout () {
const user = JSON.parse(sessionStorage.getItem('hrUser')) const user = JSON.parse(sessionStorage.getItem('hrUser'))
user && user.token ? user && user.token ?
Request.put(ApiTable.logout, { Request.put(ApiTable.logout, {

186
web/client/src/sections/auth/containers/login.jsx

@ -8,110 +8,110 @@ import { IconLock, IconUser } from '@douyinfe/semi-icons';
import '../style.less' import '../style.less'
const Login = props => { const Login = props => {
const { dispatch, user, error, actions, apiRoot, isRequesting } = props const { dispatch, user, error, actions, apiRoot, isRequesting } = props
const form = useRef(); const form = useRef();
useEffect(() => { useEffect(() => {
if (error) { if (error) {
Toast.error(error); Toast.error(error);
form.current.setValue('password', '') form.current.setValue('password', '')
} }
}, [error]) }, [error])
useEffect(() => { useEffect(() => {
if (user && user.authorized) { if (user && user.authorized) {
dispatch(push('/humanAffairs/archivesCenter/personnelArchives/personnelFiles')); dispatch(push('/humanAffairs/archivesCenter/personnelArchives/personnelFiles'));
localStorage.setItem('poms_open_sider', JSON.stringify(["archivesCenter"])) localStorage.setItem('poms_open_sider', JSON.stringify(["archivesCenter"]))
localStorage.setItem('poms_selected_sider', JSON.stringify(["humanAffairs"])) localStorage.setItem('poms_selected_sider', JSON.stringify(["humanAffairs"]))
} }
}, [user]) }, [user])
return ( return (
<div style={{ <div style={{
width: '100%', width: '100%',
height: '100%', height: '100%',
background: '#F0F4FF', background: '#F0F4FF',
}}> }}>
<div style={{ <div style={{
width: '57.64%', width: '57.64%',
height: '100%' height: '100%'
}}> }}>
<div className='zoomImage'> <div className='zoomImage'>
<img src="/assets/images/background/loginText.png" style={{ width: 587, height: 81, margin: "143px 0 0 69px" }} /> <img src="/assets/images/background/loginText.png" style={{ width: 587, height: 81, margin: "143px 0 0 69px" }} />
</div>
</div> </div>
</div>
<div style={{
width: '42.36%',
height: '100%',
padding: '45px 60px',
background: 'rgb(255 255 255 / 50%)',
backdropFilter: "saturate(100%) contrast(100%) blur(17px)",
position: 'absolute',
top: 0,
right: 0,
zIndex: "6",
textAlign: 'center',
display: 'flex',
justifyContent: 'center',
background: '#FFFFFF'
}}>
<div style={{ <div style={{
width: '42.36%', width: 388,
height: '100%', marginTop: "18.33%"
padding: '45px 60px',
background: 'rgb(255 255 255 / 50%)',
backdropFilter: "saturate(100%) contrast(100%) blur(17px)",
position: 'absolute',
top: 0,
right: 0,
zIndex: "6",
textAlign: 'center',
display: 'flex',
justifyContent: 'center',
background: '#FFFFFF'
}}> }}>
<div style={{ <div style={{ fontSize: 31, color: '#000000', marginBottom: 90 }}>
width: 388, FS-EIMS企业信息管理系统
marginTop: "18.33%" </div>
}}> <Form
<div style={{ fontSize: 31, color: '#000000', marginBottom: 90 }}> onSubmit={values => {
FS-EIMS企业信息管理系统 dispatch(login({ username: values.username, password: values.password })).then(res => {
</div> const data = res.payload.user
<Form localStorage.setItem('word', JSON.stringify(values.password))
onSubmit={values => { dispatch(actions.layout.initWebSocket({ ioUrl: apiRoot, token: data.token, hrUserId: data.hrUserInfo && hrUserInfo.id }))
dispatch(login(values.username, values.password)).then(res => { })
const data = res.payload.user }}
localStorage.setItem('word', JSON.stringify(values.password)) getFormApi={formApi => form.current = formApi}
dispatch(actions.layout.initWebSocket({ ioUrl: apiRoot, token: data.token, hrUserId: data.hrUserInfo && hrUserInfo.id })) >
}) <Form.Input
}} className='inputbgc'
getFormApi={formApi => form.current = formApi} field='username'
> noLabel={true}
<Form.Input label='用户名'
className='inputbgc' placeholder='请输入账号'
field='username' prefix={<IconUser style={{ color: '#717171', marginRight: 14, marginLeft: 8 }} />}
noLabel={true} style={{ background: '#FFFFFF', height: 46, marginBottom: 33, border: '1px solid rgb(185 211 239)', borderRadius: '4px' }}
label='用户名' />
placeholder='请输入账号' <Form.Input
prefix={<IconUser style={{ color: '#717171', marginRight: 14, marginLeft: 8 }} />} field='password'
style={{ background: '#FFFFFF', height: 46, marginBottom: 33, border: '1px solid rgb(185 211 239)', borderRadius: '4px' }} noLabel={true}
/> mode="password"
<Form.Input autoComplete=""
field='password' placeholder='请输入密码'
noLabel={true} label='密码'
mode="password" prefix={<IconLock style={{ color: '#717171', marginRight: 14, marginLeft: 8 }} />}
autoComplete="" style={{ background: '#FFFFFF', height: 46, border: '1px solid rgb(185 211 239)', borderRadius: '4px' }}
placeholder='请输入密码' />
label='密码' <img src="/assets/images/background/xiangqi.png" style={{ width: 112, height: 14, margin: "4px 0 0 278px" }} />
prefix={<IconLock style={{ color: '#717171', marginRight: 14, marginLeft: 8 }} />} <Button htmlType='submit' block theme="solid" loading={isRequesting} style={{ marginTop: 56, height: 46, backgroundColor: '#0F7EFB', borderRadius: '27px' }}>立即登录</Button>
style={{ background: '#FFFFFF', height: 46, border: '1px solid rgb(185 211 239)', borderRadius: '4px' }}
/>
<img src="/assets/images/background/xiangqi.png" style={{ width: 112, height: 14, margin: "4px 0 0 278px" }} />
<Button htmlType='submit' block theme="solid" loading={isRequesting} style={{ marginTop: 56, height: 46, backgroundColor: '#0F7EFB', borderRadius: '27px' }}>立即登录</Button>
</Form>
</div> </Form>
</div> </div>
</div >
); </div>
</div >
);
} }
function mapStateToProps(state) { function mapStateToProps (state) {
const { auth, global } = state; const { auth, global } = state;
return { return {
user: auth.user, user: auth.user,
error: auth.error, error: auth.error,
actions: global.actions, actions: global.actions,
apiRoot: global.apiRoot, apiRoot: global.apiRoot,
isRequesting: auth.isRequesting isRequesting: auth.isRequesting
} }
} }
export default connect(mapStateToProps)(Login); export default connect(mapStateToProps)(Login);

197
web/client/src/sections/humanAffairs/components/deleteModal.jsx

@ -3,115 +3,116 @@ import { connect } from "react-redux";
import { Modal, Form, Button, Upload, Toast } from "@douyinfe/semi-ui"; import { Modal, Form, Button, Upload, Toast } from "@douyinfe/semi-ui";
import { IconAlertCircle } from '@douyinfe/semi-icons'; import { IconAlertCircle } from '@douyinfe/semi-icons';
import cityData from './city.json'; import cityData from './city.json';
import { ApiTable, AxyRequest, EmisRequest } from '$utils'
import PerfectScrollbar from "perfect-scrollbar"; import PerfectScrollbar from "perfect-scrollbar";
import './style.less' import './style.less'
let Scrollbar; let Scrollbar;
function deleteModal (props) { function deleteModal (props) {
const { const {
close, close,
cancel, cancel,
visible, visible,
dispatch, dispatch,
actions, actions,
pepUserId pepUserId,
} = props; user
const { humanAffairs } = actions; } = props;
const form = useRef();// const { humanAffairs } = actions;
const form = useRef();//
const [idPhoto, setIdPhoto] = useState(); // const [idPhoto, setIdPhoto] = useState(); //
const [word, setWord] = useState(); // const [word, setWord] = useState(); //
// //
useEffect(() => { useEffect(() => {
setWord(JSON.parse(localStorage.getItem('word'))) setWord(JSON.parse(localStorage.getItem('word')))
}, []); }, []);
useEffect(() => {
const Project = document.getElementById("myForm");
if (Project) {
if (Project && Scrollbar) {
Scrollbar.update();
}
Scrollbar = new PerfectScrollbar("#myForm", {
suppressScrollX: true,
});
}
});
function handleOk () {
//
form.current
.validate()
.then((values) => {
if (word == values.word) {
dispatch(humanAffairs.delMember({ pepUserId: pepUserId, msg: '删除档案' })).then((res) => {//(PEP)
if (res.success) {
close();
}
})
}
else {
Toast.error('密码错误');
form.current.setValue('word', '')
}
useEffect(() => {
const Project = document.getElementById("myForm");
if (Project) {
if (Project && Scrollbar) {
Scrollbar.update();
}
Scrollbar = new PerfectScrollbar("#myForm", {
suppressScrollX: true,
});
}
});
function handleOk () {
//
form.current
.validate()
.then((values) => {
console.log(values);
let data = EmisRequest?.post(`verify/user/${user?.id}/pswd`, { pswd: values?.word }, { token: user?.token }).then(res => {
dispatch(humanAffairs.delMember({ pepUserId: pepUserId, msg: '删除档案' })).then((res) => {//(PEP)
if (res.success) {
close();
}
})
}, err => {
Toast.error(err?.response?.body?.message || '密码错误')
form.current.setValue('word', '')
}) })
} })
function handleCancel () { }
cancel(); function handleCancel () {
// cancel();
} //
return ( }
<> return (
<Modal <>
title={'警告'} <Modal
okText="确认删除" title={'警告'}
cancelText="取消" okText="确认删除"
visible={visible} cancelText="取消"
onOk={handleOk} visible={visible}
width={500} onOk={handleOk}
onCancel={handleCancel} width={500}
onCancel={handleCancel}
>
<div style={{ borderBottom: '1px solid #DCDEE0', margin: '0px -24px' }}></div>
<Form
// allowEmpty
labelPosition="left"
labelAlign="right"
labelWidth="116px"
onValueChange={(values, field) => {
console.log('values', values);
}}
getFormApi={(formApi) => (form.current = formApi)}
> >
<div style={{ borderBottom: '1px solid #DCDEE0', margin: '0px -24px' }}></div> <div style={{ padding: '20px 0px' }}>
<Form <div style={{ display: 'flex', alignItems: 'center' }}>
// allowEmpty <div>
labelPosition="left" <Form.Input
labelAlign="right" field="word"
labelWidth="116px" label='我的登录密码:'
onValueChange={(values, field) => { style={{ width: 334 }}
console.log('values', values); initValue={'' || ""}
}} placeholder="请输入登录密码"
getFormApi={(formApi) => (form.current = formApi)} mode="password"
> showClear
<div style={{ padding: '20px 0px' }}> rules={[{ required: true, message: "请输入登录密码" }]}
<div style={{ display: 'flex', alignItems: 'center' }}> />
<div> </div>
<Form.Input </div>
field="word" </div>
label='我的登录密码:' </Form>
style={{ width: 334 }} </Modal >
initValue={'' || ""} </>
placeholder="请输入登录密码" );
mode="password"
showClear
rules={[{ required: true, message: "请输入登录密码" }]}
/>
</div>
</div>
</div>
</Form>
</Modal >
</>
);
} }
function mapStateToProps (state) { function mapStateToProps (state) {
const { auth, global, members } = state; const { auth, global, members } = state;
return { return {
// loading: members.isRequesting, // loading: members.isRequesting,
user: auth.user, user: auth.user,
actions: global.actions, actions: global.actions,
apiRoot: global.apiRoot, apiRoot: global.apiRoot,
// members: members.data, // members: members.data,
}; };
} }
export default connect(mapStateToProps)(deleteModal); export default connect(mapStateToProps)(deleteModal);

8
web/config.js

@ -13,6 +13,7 @@ dev && console.log('\x1B[33m%s\x1b[0m', '请遵循并及时更新 readme.md,
args.option(['p', 'port'], '启动端口'); args.option(['p', 'port'], '启动端口');
args.option(['u', 'api-url'], 'webapi的URL'); args.option(['u', 'api-url'], 'webapi的URL');
args.option('apiHrUrl', 'webapi的URL 外网可访问'); args.option('apiHrUrl', 'webapi的URL 外网可访问');
args.option('apiEmisUrl', '企业管理 api');
args.option(['d', 'domain'], 'web domain'); args.option(['d', 'domain'], 'web domain');
@ -32,6 +33,7 @@ const flags = args.parse(process.argv);
const API_URL = process.env.API_URL || flags.apiUrl; const API_URL = process.env.API_URL || flags.apiUrl;
const API_HR_URL = process.env.API_HR_URL || flags.apiHrUrl; const API_HR_URL = process.env.API_HR_URL || flags.apiHrUrl;
const FS_HR_DOMAIN = process.env.FS_HR_DOMAIN || flags.domain; const FS_HR_DOMAIN = process.env.FS_HR_DOMAIN || flags.domain;
const API_EMIS_URL = process.env.API_EMIS_URL || flags.apiEmisUrl;
// 七牛 // 七牛
const ANXINCLOUD_QINIU_AK = process.env.ANXINCLOUD_QINIU_ACCESSKEY || flags.qnak; const ANXINCLOUD_QINIU_AK = process.env.ANXINCLOUD_QINIU_ACCESSKEY || flags.qnak;
@ -88,6 +90,12 @@ const product = {
}, },
domain: FS_HR_DOMAIN, domain: FS_HR_DOMAIN,
} }
}, {
entry: require('./middlewares/proxy').entry,
opts: {
host: API_EMIS_URL,
match: /^\/_emis\//,
}
}, { }, {
entry: require('./client').entry,// 静态信息 entry: require('./client').entry,// 静态信息
opts: {} opts: {}

2
web/package.json

@ -7,7 +7,7 @@
"test": "mocha", "test": "mocha",
"start-vite": "cross-env NODE_ENV=developmentVite npm run start-params", "start-vite": "cross-env NODE_ENV=developmentVite npm run start-params",
"start": "cross-env NODE_ENV=development npm run start-params", "start": "cross-env NODE_ENV=development npm run start-params",
"start-params": "node server -p 5700 -u http://localhost:4700 --apiHrUrl http://localhost:4700 -d localhost --qnak XuDgkao6cL0HidoMAPnA5OB10Mc_Ew08mpIfRJK5 --qnsk yewcieZLzKZuDfig0wLZ9if9jKp2P_1jd3CMJPSa --qnbkt dev-hr --qndmn http://rjkwed13l.hn-bkt.clouddn.com --wkys http://10.8.30.109:14000", "start-params": "node server -p 5700 -u http://localhost:4700 --apiHrUrl http://localhost:4700 -d localhost --apiEmisUrl http://localhost:4000 --qnak XuDgkao6cL0HidoMAPnA5OB10Mc_Ew08mpIfRJK5 --qnsk yewcieZLzKZuDfig0wLZ9if9jKp2P_1jd3CMJPSa --qnbkt dev-hr --qndmn http://rjkwed13l.hn-bkt.clouddn.com --wkys http://10.8.30.109:14000",
"deploy": "export NODE_ENV=production&& npm run build && node server", "deploy": "export NODE_ENV=production&& npm run build && node server",
"build-dev": "cross-env NODE_ENV=development&&webpack --config webpack.config.js", "build-dev": "cross-env NODE_ENV=development&&webpack --config webpack.config.js",
"build": "cross-env NODE_ENV=production&&webpack --config webpack.config.prod.js" "build": "cross-env NODE_ENV=production&&webpack --config webpack.config.prod.js"

Loading…
Cancel
Save