Login.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. /* eslint-disable jsx-a11y/anchor-has-content */
  2. import React,{useState,useEffect} from "react"
  3. import "../assets/scss/login.scss"
  4. import { AxiosError, AxiosResponse } from 'axios'
  5. import {ReactComponent as User} from "../assets/svg/user.svg"
  6. import {ReactComponent as Key} from "../assets/svg/key.svg"
  7. import {ReactComponent as EyesOpen} from "../assets/svg/eyes-open.svg"
  8. import {ReactComponent as EyesClose} from "../assets/svg/eyes-close.svg"
  9. import { ReactComponent as Eweima } from "../assets/svg/erweima.svg"
  10. import {ReactComponent as Lock} from "../assets/svg/lock.svg"
  11. import logo from "../assets/logo.jpg"
  12. import Footer from "../components/Footer"
  13. import {useNavigate,Link} from "react-router-dom"
  14. import {api} from "../utils/api"
  15. import CONSTS from "../shared/index"
  16. import {message} from "antd"
  17. export default function Login(){
  18. const [username,setUsername]=useState<string>()
  19. const [password,setPassword]=useState<string|number>()
  20. const [isVisible,setVisible]=useState<boolean>(false)
  21. const [rememberMe,setRemember]=useState<boolean>(false)
  22. const [captcha,setCode] = useState<string>("")
  23. const [providers,setProviders] = useState<Array<object>>([])
  24. const [imageCaptcha,setImage] = useState<string>('')
  25. const [state,setSta] = useState<string>('')
  26. const [qrScan,setqrScan] = useState<string>("")
  27. const [isqrCode,setisqrCode] = useState<boolean>(false)
  28. let captchaType = ""
  29. let loginType="normal"
  30. const navigate = useNavigate()
  31. const onUserNameChange=(e:React.FormEvent<HTMLInputElement>)=>setUsername(((e.target) as HTMLInputElement).value)
  32. const onPasswordChange=(e:React.FormEvent<HTMLInputElement>)=>setPassword(((e.target) as HTMLInputElement).value)
  33. const onRememberChange=(e:React.FormEvent<HTMLInputElement>)=>setRemember(((e.target) as HTMLInputElement).checked)
  34. const onCodeChange = (e:React.FormEvent<HTMLInputElement>)=>setCode(((e.target) as HTMLInputElement).value)
  35. //点击show后切换visible
  36. const show=()=>{
  37. setVisible(!isVisible)
  38. }
  39. //获得验证码
  40. const getImageCaptcha=():void=>{
  41. api.getImageCaptcha({state,captcha:captchaType})
  42. .then((res:AxiosResponse)=>{
  43. res=res.data
  44. setImage(res.data.image);
  45. })
  46. .catch((err:AxiosError)=>Promise.reject(err))
  47. }
  48. const getUrl=(value:string):string=>{
  49. let params = window.location.search.substring(1).split('&')
  50. for(let i =0; i< params.length; i++){
  51. let param = params[i].split('=')
  52. if(param[0]=== value){
  53. return param[1]
  54. }
  55. }
  56. return ''
  57. }
  58. const congressLogin=(congress:string)=>{
  59. api.congress({congress:congress})
  60. .then((response: AxiosResponse)=>{
  61. const res=response.data
  62. if(res.code !== 0){
  63. console.log(res.msg)
  64. }else{
  65. //设置用户token信息
  66. api.auth(res.data)
  67. api.navigate({})
  68. }
  69. })
  70. .catch((err:AxiosError)=>{
  71. console.log(err.message)
  72. Promise.reject(err)
  73. })
  74. }
  75. //初始化
  76. useEffect(()=>{
  77. if(getUrl(CONSTS.REDIRECT_URI) !==''){
  78. api.setRedirectUri(getUrl(CONSTS.REDIRECT_URI))
  79. }
  80. if(getUrl(CONSTS.CONGRESS)){
  81. congressLogin(getUrl(CONSTS.CONGRESS))
  82. }
  83. //init socails,state
  84. api.clear()
  85. api.get({remember_me:localStorage.getItem(CONSTS.REMEMBER)})
  86. .then((response:AxiosResponse)=>{
  87. const res=response.data
  88. if(res.code !== 0){
  89. console.log(res.msg)
  90. }else{
  91. if(res.data.state){
  92. setProviders(res.data.socials.providers)
  93. setSta(res.data.state)
  94. setqrScan(res.data.socials.qrScan)
  95. captchaType = res.data.captcha
  96. if(captchaType !== 'NONE'){
  97. //初始化图像验证码
  98. api.getImageCaptcha({state,captcha:captchaType})
  99. .then((res:AxiosResponse)=>{
  100. res=res.data
  101. setImage(res.data.image)
  102. })
  103. }
  104. }
  105. }
  106. })
  107. .catch((err:AxiosError)=>{
  108. Promise.reject(err)
  109. })
  110. },[])
  111. const socialauth=(provider:string): void =>{
  112. //clear User
  113. localStorage.setItem('user',JSON.stringify({}))
  114. api.authorize(provider)
  115. .then((res:AxiosResponse)=>{
  116. // debugger
  117. window.location.href = res.data.data
  118. })
  119. }
  120. const login=()=>{
  121. if(username===''|| password===''|| captcha===''){
  122. return;
  123. }
  124. localStorage.setItem(CONSTS.REMEMBER,rememberMe === true?"true":"false")
  125. api.login(
  126. {
  127. authType:loginType,
  128. state:state,
  129. username,
  130. password,
  131. captcha,
  132. mobile:null,
  133. otpCaptcha:null,
  134. remeberMe:rememberMe,
  135. })
  136. .then((response:AxiosResponse)=>{
  137. const res=response.data
  138. if(res.code!== 0){
  139. message.error("登录失败!")
  140. getImageCaptcha()
  141. }else{
  142. //清空路由复用信息?
  143. api.auth(res.data)
  144. api.navigate({})
  145. navigate("/dashboard/home")
  146. }
  147. })
  148. .catch((err:AxiosError)=>{
  149. console.log(err.message)
  150. Promise.reject(err)
  151. })
  152. }
  153. const switchTab=(index:number):void=>{
  154. if(index===1){
  155. setisqrCode(false)
  156. }
  157. if(index===2){
  158. setisqrCode(true)
  159. localStorage.setItem('user',JSON.stringify({}))
  160. api.scanqrcode(qrScan)
  161. .then((response:AxiosResponse)=>{
  162. const res=response.data
  163. if(res.code === 0){
  164. if(qrScan === 'workweixin'){
  165. qrScanWorkweixin(res.data)
  166. }else if(qrScan === 'dingtalk'){
  167. qrScanDingtalk(res.data)
  168. }else if(qrScan === 'feishu'){
  169. qrScanFeishu(res.data)
  170. }
  171. }
  172. })
  173. .catch((err:AxiosError)=>{
  174. Promise.reject(err)
  175. })
  176. }
  177. }
  178. const qrScanWorkweixin=(data: any) => {
  179. //see doc https://developer.work.weixin.qq.com/document/path/91025
  180. // @ts-ignore
  181. let wwLogin = new WwLogin({
  182. id: 'div_qrcodelogin',
  183. appid: data.clientId,
  184. agentid: data.agentId,
  185. redirect_uri: encodeURIComponent(data.redirectUri),
  186. state: data.state,
  187. href: 'data:text/css;base64,LmltcG93ZXJCb3ggLnFyY29kZSB7d2lkdGg6IDI1MHB4O30NCi5pbXBvd2VyQm94IC50aXRsZSB7ZGlzcGxheTogbm9uZTt9DQouaW1wb3dlckJveCAuaW5mbyB7d2lkdGg6IDI1MHB4O30NCi5zdGF0dXNfaWNvbiB7ZGlzcGxheTpub25lfQ0KLmltcG93ZXJCb3ggLnN0YXR1cyB7dGV4dC1hbGlnbjogY2VudGVyO30='
  188. });
  189. }
  190. const qrScanFeishu=(data: any)=> {
  191. //see doc https://open.feishu.cn/document/common-capabilities/sso/web-application-sso/qr-sdk-documentation
  192. //remove old div
  193. var qrcodeDiv = document.querySelector('#div_qrcodelogin');
  194. qrcodeDiv?.childNodes.forEach(function (value, index, array) {
  195. qrcodeDiv?.removeChild(value);
  196. });
  197. // @ts-ignore
  198. let fsredirectUri = `https://passport.feishu.cn/suite/passport/oauth/authorize?client_id=${data.clientId}&redirect_uri=${encodeURIComponent(
  199. data.redirectUri
  200. )}&response_type=code&state=${data.state}`;
  201. // @ts-ignore
  202. var redirectUri = fsredirectUri;
  203. // @ts-ignore
  204. let QRLoginObj = QRLogin({
  205. id: 'div_qrcodelogin',
  206. goto: redirectUri,
  207. width: '300',
  208. height: '300',
  209. style: 'border: 0;'
  210. });
  211. }
  212. const qrScanDingtalk=(data: any)=> {
  213. //see doc https://open.dingtalk.com/document/isvapp-server/scan-qr-code-to-log-on-to-third-party-websites
  214. var url = encodeURIComponent(data.redirectUri);
  215. var gotodingtalk = encodeURIComponent(
  216. `https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid=${data.clientId}&response_type=code&scope=snsapi_login&state=${data.state}&redirect_uri=${url}`
  217. );
  218. // @ts-ignore
  219. let ddredirect_uri = `https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid=${data.clientId}&response_type=code&scope=snsapi_login&state=${data.state}&redirect_uri=${data.redirectUri}`;
  220. // @ts-ignore
  221. var obj = DDLogin({
  222. id: 'div_qrcodelogin', //这里需要你在自己的页面定义一个HTML标签并设置id,例如<div id="login_container"></div>或<span id="login_container"></span>
  223. goto: gotodingtalk, //请参考注释里的方式
  224. style: 'border:none;background-color:#FFFFFF;',
  225. width: '360',
  226. height: '400'
  227. });
  228. }
  229. return(
  230. <div>
  231. <header>
  232. <div className= 'title'>
  233. <img src={logo} alt="" />
  234. Max
  235. <span>Key</span>
  236. 单点登录认证系统
  237. </div>
  238. </header>
  239. <form className="form-signin" method="post">
  240. <h3>业界领先的IAM身份管理和认证产品</h3>
  241. <div className="login-type">
  242. <span onClick = {()=>switchTab(1)} className={!isqrCode?'activeOne':''}>
  243. <i><User/></i>
  244. 账号登录
  245. </span>
  246. <span onClick = {()=>switchTab(2)} className={isqrCode?'activeTwo':''}>
  247. <i><Eweima/></i>
  248. 扫码登录
  249. </span>
  250. </div>
  251. {!isqrCode
  252. ?<>
  253. <div className="username">
  254. <label htmlFor="username" className="label">
  255. <User/>
  256. </label>
  257. <input
  258. value={username}
  259. onChange={onUserNameChange}
  260. type="text"
  261. name="username"
  262. id="username"
  263. className="form-control"
  264. placeholder="用户名"
  265. />
  266. </div>
  267. <div className="password">
  268. <label htmlFor="inputPassword" className="label">
  269. <Key/>
  270. </label>
  271. <input
  272. value={password}
  273. onChange={onPasswordChange}
  274. type={isVisible?'text':'password'}
  275. name="password"
  276. id="inputPassword"
  277. className="form-control"
  278. placeholder="密码" required/>
  279. <EyesOpen
  280. style={{
  281. display:isVisible?'block':'none'
  282. }}
  283. className="eyes-img"
  284. onClick={show}
  285. />
  286. <EyesClose
  287. style={{
  288. display:isVisible?'none':'block'
  289. }}
  290. className="eyes-img"
  291. onClick={show}/>
  292. </div>
  293. <div className="check">
  294. <label htmlFor="check" className="label">
  295. <Lock />
  296. </label>
  297. <input
  298. type="text"
  299. id="check"
  300. placeholder="验证码"
  301. className="form-control"
  302. value={captcha}
  303. onChange={onCodeChange}
  304. />
  305. <div className="code">
  306. <img src = {imageCaptcha} alt="" onClick={getImageCaptcha}/>
  307. </div>
  308. </div>
  309. <div className="remember">
  310. <input type="checkbox" id="remember" checked={rememberMe} onChange={onRememberChange}/>
  311. <label htmlFor="remember">记住登录</label>
  312. <Link to="/passport/forgot" replace={true} >忘记密码</Link>
  313. </div>
  314. <button className="btn" type="button" onClick={login} >登录</button>
  315. <div className="otherLogin">
  316. <>
  317. <span>其他登录方式</span>
  318. {
  319. providers.map((prod:any)=>
  320. <img src= {prod.icon} alt="" onClick={()=>socialauth(prod.provider)} key={prod.id}/>
  321. )
  322. }
  323. </>
  324. </div>
  325. </>
  326. :<div>
  327. <div id='div_qrcodelogin'></div>
  328. </div>
  329. }
  330. </form>
  331. <Footer/>
  332. </div>
  333. )
  334. }