import React, {Component} from 'react'
|
import PropTypes from 'prop-types'
|
import { Form, Input, Button, Checkbox, Select, Modal, message, AutoComplete, Tooltip } from 'antd'
|
import { UserOutlined, LockOutlined, QrcodeOutlined, RedoOutlined, CloseCircleOutlined } from '@ant-design/icons'
|
import md5 from 'md5'
|
import moment from 'moment'
|
|
import Api from '@/api'
|
import Utils from '@/utils/utils.js'
|
import options from '@/store/options.js'
|
import asyncLoadComponent from '@/utils/asyncLoadComponent'
|
import wxicon from '@/assets/img/wx-icon.png'
|
import './index.scss'
|
|
const { warning } = Modal
|
let LoginVerCodeTimer = null
|
const QrCode = asyncLoadComponent(() => import('@/components/qrcode'))
|
|
const CheckWrap = ({checked, prolType, onChange, prolCont}) => {
|
const change = (e) => {
|
onChange(e.target.checked)
|
|
if (prolType === 'once') {
|
localStorage.setItem(window.GLOB.sysSign + 'protocol', e.target.checked)
|
}
|
}
|
|
return (
|
<>
|
<Checkbox defaultChecked={checked} onChange={change} style={{marginRight: '8px'}}></Checkbox>
|
<span dangerouslySetInnerHTML={{__html: prolCont}}></span>
|
</>
|
)
|
}
|
|
class LoginTabForm extends Component {
|
static propTpyes = {
|
isDisabled: PropTypes.bool,
|
handleSubmit: PropTypes.func,
|
authLogin: PropTypes.func,
|
auth: PropTypes.bool,
|
authError: PropTypes.string,
|
langList: PropTypes.array,
|
loginWays: PropTypes.array
|
}
|
|
state = {
|
activeKey: 'uname_pwd',
|
scanId: '',
|
username: '',
|
password: '',
|
remember: true,
|
protocol: null,
|
delay: null,
|
loginWays: [],
|
smsId: '',
|
verdisabled: false,
|
hasScan: false,
|
timeout: false,
|
vispwd: window.GLOB.vispwd,
|
wayLabels: {},
|
dict: window.GLOB.dict,
|
users: [],
|
lang: sessionStorage.getItem('lang')
|
}
|
|
timer = null
|
splitTime = 0
|
scanParam = null
|
|
UNSAFE_componentWillMount () {
|
const { loginWays, prolType } = this.props
|
|
let protocol = null
|
|
if (prolType === 'once') {
|
protocol = localStorage.getItem(window.GLOB.sysSign + 'protocol') === 'true'
|
} else if (prolType === 'every_time') {
|
protocol = false
|
}
|
|
let remember = true
|
|
if (localStorage.getItem(window.GLOB.sysSign + 'remember') === 'false') {
|
remember = false
|
}
|
if (!window.GLOB.keepKey) {
|
remember = false
|
}
|
|
let smsId = ''
|
let hasScan = false
|
let _loginWays = []
|
let wayLabels = {app_scan: 'APP扫码', weixin_scan: '微信扫码', uname_pwd: '账号登录', sms_vcode: '短信登录'}
|
|
loginWays.forEach(item => {
|
item.label = window.GLOB.dict[item.type] || wayLabels[item.type]
|
wayLabels[item.type] = item.label
|
if (item.type === 'sms_vcode') {
|
smsId = item.smsId
|
_loginWays.push(item)
|
} else if (item.type === 'uname_pwd') {
|
_loginWays.push(item)
|
} else if (item.type === 'app_scan') {
|
_loginWays.push(item)
|
hasScan = true
|
} else if (item.type === 'weixin_scan') {
|
_loginWays.push(item)
|
hasScan = true
|
}
|
})
|
|
let activeKey = _loginWays[0].type
|
|
let users = localStorage.getItem(window.GLOB.sysSign + 'users')
|
let _user = null
|
|
if (users) {
|
try {
|
users = JSON.parse(window.decodeURIComponent(window.atob(users)))
|
} catch (e) {
|
users = []
|
}
|
} else {
|
users = []
|
}
|
|
if (users[0] && activeKey === 'uname_pwd') {
|
_user = users[0]
|
}
|
|
this.setState({
|
users: users,
|
username: _user ? _user.username : '',
|
password: _user ? _user.password : '',
|
smsId: smsId,
|
loginWays: _loginWays,
|
activeKey,
|
scanId: activeKey === 'app_scan' || activeKey === 'weixin_scan' ? Utils.getuuid() : '',
|
timeout: false,
|
remember,
|
protocol,
|
wayLabels,
|
hasScan
|
})
|
|
if (activeKey === 'app_scan' || activeKey === 'weixin_scan') {
|
this.splitTime = 0
|
this.timer = setTimeout(() => {
|
this.checkResult()
|
}, 10000)
|
}
|
}
|
|
checkResult = () => {
|
const { scanId, protocol, dict } = this.state
|
|
this.splitTime += 10000
|
this.scanParam = null
|
|
let _param = {
|
func: 'webapi_get_binding_key',
|
scan_type: 'pc',
|
id: scanId,
|
UserName: ''
|
}
|
|
_param.userid = sessionStorage.getItem('visitorUserID')
|
_param.LoginUID = sessionStorage.getItem('visitorLoginUID')
|
|
if (this.splitTime >= 180000) {
|
this.setState({
|
timeout: true
|
})
|
return
|
}
|
|
Api.getSystemConfig(_param).then(res => {
|
if (!res.status) {
|
message.warning(res.message)
|
return
|
} else if (res.thd_party_appid && res.thd_party_member_id && res.thd_party_openid) {
|
if (protocol === false) {
|
this.scanParam = {
|
...res,
|
scanId
|
}
|
message.warning(dict['protocol_check'] || '请阅读并同意协议!')
|
return
|
}
|
this.props.authLogin(res.thd_party_appid, res.thd_party_openid, res.thd_party_member_id, scanId)
|
} else {
|
this.timer = setTimeout(() => {
|
this.checkResult()
|
}, 10000)
|
}
|
})
|
}
|
|
handleConfirm = () => {
|
const { activeKey, protocol } = this.state
|
|
if (protocol === false) return Promise.reject()
|
|
// 表单提交时检查输入值是否正确
|
return new Promise((resolve, reject) => {
|
this.props.form.validateFieldsAndScroll((err, values) => {
|
if (!err) {
|
if (activeKey === 'uname_pwd') {
|
values.username = values.username.replace(/\t+|\v+|\s+/g, '')
|
values.password = values.password.replace(/\t+|\v+|\s+/g, '')
|
} else if (activeKey === 'sms_vcode') {
|
values.phone = values.phone.replace(/\t+|\v+|\s+/g, '')
|
values.vercode = values.vercode.replace(/\t+|\v+|\s+/g, '')
|
}
|
resolve({type: activeKey, ...values})
|
} else {
|
reject(err)
|
}
|
})
|
})
|
}
|
|
changelang = (val) => {
|
localStorage.setItem(window.location.href.split('#')[0] + 'lang', val)
|
sessionStorage.setItem('lang', val)
|
|
window.location.reload()
|
}
|
|
handleSubmit = e => {
|
const { activeKey, dict } = this.state
|
// 登录参数检验
|
e && e.preventDefault()
|
if (!this.props.auth) {
|
warning({
|
title: this.props.authError || dict['auth_tip'] || '系统未授权,请联系管理员。',
|
okText: dict['got_it'] || '知道了',
|
onOk() {},
|
onCancel() {}
|
})
|
return
|
}
|
|
if (activeKey === 'uname_pwd') {
|
if (!this.props.form.getFieldValue('username')) {
|
const wrap = document.getElementById('username')
|
const input = wrap ? wrap.getElementsByTagName('input')[0] : null
|
if (input) {
|
input.focus()
|
}
|
} else if (!this.props.form.getFieldValue('password')) {
|
const input = document.getElementById('password')
|
if (input) {
|
input.focus()
|
}
|
} else {
|
this.props.handleSubmit()
|
}
|
} else if (activeKey === 'sms_vcode') {
|
if (!this.props.form.getFieldValue('phone')) {
|
const input = document.getElementById('phone')
|
if (input) {
|
input.focus()
|
}
|
} else if (!this.props.form.getFieldValue('vercode')) {
|
const input = document.getElementById('vercode')
|
if (input) {
|
input.focus()
|
}
|
} else {
|
this.props.handleSubmit()
|
}
|
}
|
}
|
|
componentDidMount () {
|
const wrap = document.getElementById('username')
|
const input = wrap ? wrap.getElementsByTagName('input')[0] : null
|
if (input) {
|
input.focus()
|
}
|
}
|
|
onChangeTab = (key) => {
|
const { activeKey, loginWays } = this.state
|
|
if (key === 'scan') {
|
key = loginWays.filter(item => item.type === 'app_scan' || item.type === 'weixin_scan')[0].type
|
}
|
|
this.setState({activeKey: key, scanId: key === 'app_scan' || key === 'weixin_scan' ? Utils.getuuid() : ''})
|
|
if (activeKey === 'app_scan' || activeKey === 'weixin_scan') {
|
this.timer && clearTimeout(this.timer)
|
}
|
|
if (key === 'app_scan' || key === 'weixin_scan') {
|
this.splitTime = 0
|
this.setState({timeout: false})
|
this.timer = setTimeout(() => {
|
this.checkResult()
|
}, 10000)
|
}
|
}
|
|
reCode = () => {
|
this.splitTime = 0
|
|
this.setState({timeout: false, scanId: Utils.getuuid()})
|
|
this.timer = setTimeout(() => {
|
this.checkResult()
|
}, 10000)
|
}
|
|
getvercode = () => {
|
const { smsId, dict } = this.state
|
let _phone = this.props.form.getFieldValue('phone')
|
if (!_phone) {
|
message.warning(dict['phone_no_required'] || '请输入手机号!')
|
return
|
} else if (!/^1[3456789]\d{9}$/.test(_phone)) {
|
message.warning(dict['phone_error'] || '手机号格式错误,请重填!')
|
return
|
} else if (!sessionStorage.getItem('visitorUserID') || !sessionStorage.getItem('visitorLoginUID')) {
|
message.warning(dict['vercode_error'] || '未获取验证码设置,请稍后或刷新重试!')
|
return
|
}
|
|
let _param = {
|
func: 'mes_sms_send_code_sso',
|
send_type: 'login',
|
mob: _phone,
|
ID: smsId
|
}
|
_param.LText = 'minke'
|
_param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
|
_param.secretkey = md5(`${_param.LText}mingke${_param.timestamp}`)
|
|
_param.userid = sessionStorage.getItem('visitorUserID') || ''
|
_param.LoginUID = sessionStorage.getItem('visitorLoginUID') || ''
|
|
Api.getSystemConfig(_param).then(res => {
|
if (!res.status || !res.n_id) {
|
message.warning(res.message || '验证码获取失败!')
|
return
|
}
|
|
let param = {
|
func: 'MSN_sms_send_code',
|
send_type: 'login',
|
mob: _phone,
|
timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
|
ID: smsId,
|
n_id: res.n_id
|
}
|
|
param.LText = md5(`${_phone}mingke${window.GLOB.appkey}${param.timestamp}`)
|
param.secretkey = md5(`${param.LText}mingke${param.timestamp}`)
|
|
param.rduri = 'https://sso.mk9h.cn/webapi/dostars'
|
param.userid = 'bh0bapabtd45epsgra79segbch6c1ibk'
|
param.LoginUID = 'bh0bapabtd45epsgra79segbch6c1ibk'
|
|
this.setState({
|
verdisabled: true,
|
delay: 60
|
})
|
LoginVerCodeTimer = setTimeout(this.resetVerCodeDelay, 1000)
|
|
Api.genericInterface(param).then(res => {
|
if (res.status) {
|
|
} else {
|
if (LoginVerCodeTimer) {
|
clearTimeout(LoginVerCodeTimer)
|
}
|
this.setState({
|
verdisabled: false,
|
delay: null
|
})
|
message.warning(res.message)
|
}
|
}, (error) => {
|
if (error && error.ErrCode === 'LoginError') {
|
let param = {
|
func: 's_visitor_login',
|
timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
|
SessionUid: 'bh0bapabtd45epsgra79segbch6c1ibk',
|
TypeCharOne: 'pc',
|
appkey: '202004041613277377A6A2456D34A4948AE84'
|
}
|
|
param.LText = md5(window.btoa('bh0bapabtd45epsgra79segbch6c1ibk' + param.timestamp))
|
param.secretkey = md5(param.LText + 'mingke' + param.timestamp)
|
|
let params = {
|
url: 'https://sso.mk9h.cn/webapi/dologon',
|
method: 'post',
|
data: JSON.stringify(param)
|
}
|
|
Api.directRequest(params)
|
|
return
|
}
|
|
if (LoginVerCodeTimer) {
|
clearTimeout(LoginVerCodeTimer)
|
}
|
this.setState({
|
verdisabled: false,
|
delay: null
|
})
|
})
|
})
|
}
|
|
resetVerCodeDelay = () => {
|
const { delay } = this.state
|
if (delay && delay > 1) {
|
this.setState({delay: delay - 1})
|
LoginVerCodeTimer = setTimeout(this.resetVerCodeDelay, 1000)
|
} else {
|
this.setState({
|
verdisabled: false,
|
delay: null
|
})
|
}
|
}
|
|
rememberChange = (e) => {
|
let val = e.target.checked
|
|
localStorage.setItem(window.GLOB.sysSign + 'remember', val)
|
}
|
|
complete = (val) => {
|
const { users } = this.state
|
|
let user = users.filter(m => m.username === val)[0]
|
let password = user && user.password ? user.password : ''
|
|
this.props.form.setFieldsValue({password: password})
|
|
if (!password) {
|
const input = document.getElementById('password')
|
if (input) {
|
input.focus()
|
}
|
}
|
}
|
|
deleteUser = (e, val) => {
|
const { users } = this.state
|
|
e.stopPropagation()
|
|
let _users = users.filter(m => m.username !== val)
|
|
this.setState({users: _users})
|
|
localStorage.setItem(window.GLOB.sysSign + 'users', window.btoa(window.encodeURIComponent(JSON.stringify(_users))))
|
}
|
|
changeAgree = (val) => {
|
this.setState({protocol: val})
|
|
if (this.scanParam && val) {
|
this.props.authLogin(this.scanParam.thd_party_appid, this.scanParam.thd_party_openid, this.scanParam.thd_party_member_id, this.scanParam.scanId)
|
}
|
}
|
|
/**
|
* @description 组件销毁,清除state更新
|
*/
|
componentWillUnmount () {
|
this.setState = () => {
|
return
|
}
|
this.timer && clearTimeout(this.timer)
|
}
|
|
render() {
|
const { langList, isDisabled, prolType, prolCont } = this.props
|
const { getFieldDecorator } = this.props.form
|
const { activeKey, verdisabled, delay, loginWays, remember, scanId, timeout, hasScan, users, wayLabels, dict, lang, vispwd, protocol } = this.state
|
|
return (
|
<Form className="login-form" id="login-form" onSubmit={this.handleSubmit}>
|
<div className="login-way-title">{wayLabels[activeKey]}</div>
|
{hasScan && activeKey !== 'app_scan' && activeKey !== 'weixin_scan' ? <div className="scan-icon" onClick={() => this.onChangeTab('scan')}><QrcodeOutlined /></div> : null}
|
{activeKey === 'uname_pwd' ? <div className="form-item-wrap">
|
<Form.Item>
|
{getFieldDecorator('username', {
|
rules: [{ required: true, message: dict['username_required'] || '请输入用户名' }],
|
initialValue: this.state.username || '',
|
})(
|
<AutoComplete
|
className
|
dataSource={users.map((cell, i) => <AutoComplete.Option className="mk-user-option" value={cell.username} key={i}>
|
{cell.username}
|
<CloseCircleOutlined onClick={(e) => this.deleteUser(e, cell.username)}/>
|
</AutoComplete.Option>)}
|
filterOption={false}
|
onSelect={this.complete}
|
defaultActiveFirstOption={false}
|
defaultOpen={false}
|
optionLabelProp="value"
|
>
|
<Input
|
prefix={<UserOutlined style={{ color: 'rgba(0,0,0,.25)' }} />}
|
placeholder={dict['username'] || '用户名'}
|
autoComplete="off"
|
/>
|
</AutoComplete>
|
)}
|
</Form.Item>
|
<Form.Item>
|
{getFieldDecorator('password', {
|
initialValue: this.state.password || '',
|
rules: [
|
{
|
required: true,
|
message: dict['password_required'] || '请输入密码',
|
}
|
]
|
})(<Input.Password placeholder={dict['password'] || '密码'} visibilityToggle={vispwd} prefix={<LockOutlined style={{ color: 'rgba(0,0,0,.25)' }} />} />)}
|
</Form.Item>
|
{window.GLOB.keepKey ? <Form.Item className="minline">
|
{getFieldDecorator('remember', {
|
valuePropName: 'checked',
|
initialValue: remember,
|
})(<Checkbox onChange={this.rememberChange}>{dict['remember_me'] || '记住密码'}</Checkbox>)}
|
</Form.Item> : <div style={{height: '30px', float: 'left'}}></div>}
|
{langList && langList.length > 0 ? <Form.Item className="minline right">
|
{getFieldDecorator('lang', {
|
initialValue: lang,
|
})(
|
<Select
|
onChange={(value) => {this.changelang(value)}}
|
getPopupContainer={() => document.getElementById('login-form')}
|
>
|
{langList.map((item, index) => {
|
return <Select.Option key={index} value={item.Lang}>{item.LangName}</Select.Option>
|
})}
|
</Select>
|
)}
|
</Form.Item> : null}
|
{prolType ? <Form.Item className="proline">
|
<CheckWrap checked={protocol} onChange={(val) => this.setState({protocol: val})} prolCont={prolCont} prolType={prolType}/>
|
</Form.Item> : null}
|
<Form.Item className="btn-login">
|
{protocol === false ? <Tooltip overlayStyle={{maxWidth: 500}} placement="top" trigger="click" title={dict['protocol_check'] || '请阅读并同意协议!'}>
|
<Button type="primary" htmlType="submit" className="login-form-button disabled">
|
{dict['log_in'] || '登录'}
|
</Button>
|
</Tooltip> : <Button type="primary" htmlType="submit" className="login-form-button" disabled={isDisabled} loading={isDisabled}>
|
{dict['log_in'] || '登录'}
|
</Button>}
|
</Form.Item>
|
{window.GLOB.sysType === 'cloud' && options.cdomain.indexOf('mk9h') > -1 ? <Form.Item className="register-line">
|
<a href="http://www.minkesoft.com/signup" target="_blank" rel="noopener noreferrer" className="register">注册</a>
|
<a href="http://www.minkesoft.com/forgotPwd" target="_blank" rel="noopener noreferrer" className="forgot">忘记密码?</a>
|
</Form.Item> : null}
|
</div> : null}
|
{activeKey === 'sms_vcode' ? <div className="form-item-wrap">
|
<Form.Item>
|
{getFieldDecorator('phone', {
|
rules: [{ required: true, message: dict['phone_no_required'] || '请输入手机号' }],
|
initialValue: '',
|
})(
|
<Input
|
placeholder={dict['phone_no'] || '手机号'}
|
autoComplete="off"
|
/>
|
)}
|
</Form.Item>
|
<Form.Item className="vercode">
|
{getFieldDecorator('vercode', {
|
initialValue: '',
|
rules: [
|
{
|
required: true,
|
message: dict['vercode_required'] || '请输入验证码',
|
}
|
]
|
})(
|
<Input
|
addonAfter={
|
<Button type="link" className="vercode" size="small" disabled={verdisabled} onClick={this.getvercode}>
|
{delay ? `${delay}s` : dict['query_vercode'] || '获取验证码'}
|
</Button>
|
}
|
placeholder={dict['vercode'] || '验证码'}
|
autoComplete="off"
|
/>
|
)}
|
</Form.Item>
|
{langList && langList.length > 0 ? <Form.Item className="minline right">
|
{getFieldDecorator('lang', {
|
initialValue: lang,
|
})(
|
<Select
|
onChange={(value) => {this.changelang(value)}}
|
getPopupContainer={() => document.getElementById('login-form')}
|
>
|
{langList.map((item, index) => {
|
return <Select.Option key={index} value={item.Lang}>{item.LangName}</Select.Option>
|
})}
|
</Select>
|
)}
|
</Form.Item> : null}
|
{prolType ? <Form.Item className="proline">
|
<CheckWrap checked={protocol} onChange={(val) => this.setState({protocol: val})} prolCont={prolCont} prolType={prolType}/>
|
</Form.Item> : null}
|
<Form.Item className="btn-login">
|
{protocol === false ? <Tooltip overlayStyle={{maxWidth: 500}} placement="top" trigger="click" title={dict['protocol_check'] || '请阅读并同意协议!'}>
|
<Button type="primary" htmlType="submit" className="login-form-button disabled">
|
{dict['log_in'] || '登录'}
|
</Button>
|
</Tooltip> : <Button type="primary" htmlType="submit" className="login-form-button" disabled={isDisabled} loading={isDisabled}>
|
{dict['log_in'] || '登录'}
|
</Button>}
|
</Form.Item>
|
{window.GLOB.sysType === 'cloud' && options.cdomain.indexOf('mk9h') > -1 ? <Form.Item className="register-line">
|
<a href="http://www.minkesoft.com/signup" target="_blank" rel="noopener noreferrer" className="register">注册</a>
|
<a href="http://www.minkesoft.com/forgotPwd" target="_blank" rel="noopener noreferrer" className="forgot">忘记密码?</a>
|
</Form.Item> : null}
|
</div> : null}
|
{activeKey === 'weixin_scan' ? <div className="form-item-wrap">
|
<div className="form-scan-wrap">
|
<div className="qr-wrap">
|
{scanId ? <QrCode card={{qrWidth: 500, color: '#000000'}} value={`https://cloud.mk9h.cn/mob/mknotice.html?originurl=${window.btoa(window.GLOB.baseurl + 'mob/index.html#/wx/' + scanId)}`}/> : null}
|
{timeout ? <div className="qrcode-out">
|
<RedoOutlined onClick={this.reCode} />
|
{dict['code_expired'] || '二维码已失效。'}
|
</div> : null}
|
</div>
|
<img src={wxicon} alt=""/>{dict['wechat_scan'] || '请使用微信扫一扫登录'}
|
</div>
|
{prolType ? <Form.Item className="proline">
|
<CheckWrap checked={protocol} onChange={this.changeAgree} prolCont={prolCont} prolType={prolType}/>
|
</Form.Item> : null}
|
</div> : null}
|
{activeKey === 'app_scan' ? <div className="form-item-wrap">
|
<div className="form-scan-wrap">
|
<div className="qr-wrap">
|
{scanId ? <QrCode card={{qrWidth: 500, color: '#000000'}} value={`mkpcscan,${window.GLOB.appkey},${scanId}`}/> : null}
|
{timeout ? <div className="qrcode-out">
|
<RedoOutlined onClick={this.reCode} />
|
{dict['code_expired'] || '二维码已失效。'}
|
</div> : null}
|
</div>
|
{dict['client_scan'] || '请使用客户端扫一扫登录'}
|
</div>
|
{prolType ? <Form.Item className="proline">
|
<CheckWrap checked={protocol} onChange={this.changeAgree} prolCont={prolCont} prolType={prolType}/>
|
</Form.Item> : null}
|
</div> : null}
|
<div className={'login-ways ' + (activeKey === 'app_scan' || activeKey === 'weixin_scan' ? 'center' : '')}>
|
{loginWays.map(item => {
|
if (activeKey === item.type) return null
|
if (item.type === 'app_scan' && activeKey !== 'weixin_scan') return null
|
if (item.type === 'weixin_scan' && activeKey !== 'app_scan') return null
|
return (<span key={item.type} onClick={() => this.onChangeTab(item.type)}>{item.label}</span>)
|
})}
|
</div>
|
</Form>
|
)
|
}
|
}
|
|
export default Form.create()(LoginTabForm)
|