index.tsx 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. import { Button, message, Radio, Spin } from 'antd';
  2. import React, { useCallback, useEffect, useState } from 'react';
  3. import { useModel, useRequest } from 'umi';
  4. import { getCode, getDingKey, ddlogin, phoneLogin, getNoteCode } from '@/services/login';
  5. import styles from './style.less';
  6. import { CopyrightOutlined, SwapRightOutlined } from '@ant-design/icons';
  7. import { useBase64 } from '@/Hook/useBase64'
  8. import Bg from './components/bg';
  9. import { history } from 'umi'
  10. import { api } from '@/services/api'
  11. /**
  12. * 退出登录,并且将当前的 url 保存
  13. */
  14. const loginOut = async () => {
  15. sessionStorage.removeItem('Admin-Token')
  16. // window.location.href = '/';
  17. };
  18. const Login: React.FC<{}> = () => {
  19. const { decode } = useBase64()
  20. const { setInitialState, initialState } = useModel('@@initialState');
  21. const codeRes = useRequest((phone) => getCode(phone), { manual: true, formatResult: (res: any) => res })//钉钉验证码
  22. const noteCode = useRequest((phone) => getNoteCode(phone), { manual: true, formatResult: (res: any) => res })//短信验证码
  23. const getKey = useRequest(() => getDingKey(), { manual: true, formatResult: (res: any) => res })//获取服务器KEY
  24. const Ddlogin = useRequest((params: any) => ddlogin(params), { manual: true, formatResult: (res: any) => res })//用钉钉登录
  25. const phone_login = useRequest((params: any) => phoneLogin(params), { manual: true, formatResult: (res: any) => res })//用手机登录
  26. let phone = decode('phone')
  27. let code = decode('code')
  28. const [values, setValues] = useState<{ phone: string, code: string }>({ phone: phone || '', code: code || '' })
  29. const [isLogin, setIsLogin] = useState<boolean>(false)
  30. const [videoMenu, setVideoMenu] = useState<{ visible: boolean, left?: number, top?: number }>({ visible: false, left: 0, top: 0 })
  31. const [show, setShow] = useState<boolean>(false)
  32. const [tab, setTab] = useState<1 | 2>(1)
  33. const [APPID,] = useState<string>('dingwfmhucedfluenpuj')//测试 dingdsfuoevmawlyyrjx 线上dingwfmhucedfluenpuj
  34. const [REDIRECT_URI,] = useState<string>(encodeURIComponent(location.href?.split('?')[0]))
  35. const [companyList, setCompanyList] = useState<any[]>([])
  36. const [loading, setLoading] = useState<boolean>(false)
  37. const [isMobile, setIsMobile] = useState<boolean>(false)
  38. const [m, setM] = useState<any>(0)//倒计时,0可以点击
  39. const [codeType, setCodeType] = useState(1)//1钉钉验证码 2短信验证码
  40. // 获取TOKEN
  41. useEffect(() => {
  42. let hash = window.location.hash
  43. if (hash?.includes('token')) {
  44. let token = hash?.split('token=')[1]
  45. sessionStorage.setItem('Admin-Token', token)
  46. window.location.href = '/';
  47. }
  48. }, [])
  49. // 获取运行环境
  50. useEffect(() => {
  51. let u = navigator.userAgent
  52. let isPhone = !!u.match(/AppleWebKit.*Mobile.*/) || u.indexOf('iPad') > -1
  53. if (isPhone) {
  54. setTab(1)
  55. setIsMobile(isPhone) // 判断是否在移动端
  56. }
  57. }, [])
  58. // 获取公司列表
  59. useEffect(() => {
  60. let data = initialState?.currentUser?.companyList
  61. if (data && Object.keys(data).length > 0) {
  62. setCompanyList(() => data as any)
  63. }
  64. }, [initialState?.currentUser?.companyList])
  65. //显示隐藏登录
  66. let handleShow = useCallback((props?: boolean) => {
  67. if (props && !show) {
  68. console.log('显示')
  69. setShow(true)
  70. }
  71. if (!props && show) {
  72. console.log('关闭')
  73. setShow(false)
  74. }
  75. }, [show])
  76. // 钉钉登录
  77. useEffect(() => {
  78. if (!sessionStorage.getItem('Admin-Token')) {
  79. let query = history?.location?.query
  80. if (query?.code) {
  81. Ddlogin.run({ code: query?.code, state: query?.state, stateKey: sessionStorage.getItem('DDK') }).then((msg: any) => {
  82. if (msg.code === 200) {
  83. sessionStorage.setItem('Admin-Token', msg?.data?.token)
  84. let companyInfo = msg?.data?.companyRelationInfo?.filter((item: { companyId: number }) => item.companyId !== 4 && item.companyId !== 3)
  85. setIsLogin(false)
  86. if (companyInfo?.length === 0) {
  87. sessionStorage.removeItem('Admin-Token')
  88. message.error('登录失败,请用趣程运营平台账号登录')
  89. return
  90. } else if (companyInfo?.length === 1) {
  91. setCompanyHandle(msg?.data?.companyRelationInfo[0].companyId)
  92. } else {
  93. setCompanyList(companyInfo)
  94. }
  95. // message.success('登录成功!');
  96. return;
  97. } else {
  98. setIsLogin(false)
  99. message.success('2秒后刷新页面,请重新扫码登录')
  100. setTimeout(() => {
  101. window.open(location?.origin, '_parent')
  102. }, 2000)
  103. }
  104. })
  105. } else {
  106. if (tab === 2) {
  107. let state = ''
  108. getKey.run().then((res) => {
  109. if (res?.code === 200) {
  110. let strArr = res?.data?.split('_')
  111. sessionStorage.setItem('DDK', strArr[0])
  112. state = strArr[1]
  113. }
  114. });
  115. (window as any).DDLogin({
  116. id: "login_container",
  117. goto: encodeURIComponent(`https://oapi.dingtalk.com/connect/qrconnect?appid=${APPID}&response_type=code&scope=snsapi_login&state=STATE&redirect_uri=${REDIRECT_URI}`),
  118. style: "border:none;padding:0 0 20px 0;background-color:rgba(255,255,255,0.7);",
  119. width: "100%",
  120. height: "400"
  121. });
  122. const handleMessage = (event: { data: any; origin: any; }) => {
  123. // 获取loginTempCode
  124. const loginTempCode = event.data;
  125. // 获取消息来源
  126. const origin = event.origin;
  127. // 拼接 url
  128. const url = `https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid=${APPID}&response_type=code&scope=snsapi_login&state=${encodeURIComponent(state)}&redirect_uri=${REDIRECT_URI}&loginTmpCode=${loginTempCode}`
  129. // 如果来源为https://login.dingtalk.com,则在当前窗口打开回调链接
  130. if (origin === 'https://login.dingtalk.com') {
  131. window.open(encodeURI(url), '_parent')
  132. }
  133. }
  134. // 监听iframe的消息
  135. if (typeof window.addEventListener != 'undefined') {
  136. window.addEventListener('message', handleMessage, false);
  137. } else if (typeof (window as any).attachEvent != 'undefined') {
  138. (window as any).attachEvent('onmessage', handleMessage);
  139. }
  140. }
  141. }
  142. }
  143. }, [tab, sessionStorage.getItem('Admin-Token')])
  144. // 选择公司
  145. const setCompanyHandle = (companyId: number) => {
  146. setLoading(() => true)
  147. fetch(api + `/erp/user/chooseCompany/${companyId}`, {
  148. method: 'PUT',
  149. headers: { ['Authorization']: 'Bearer ' + sessionStorage.getItem('Admin-Token') }
  150. }).then(res => res.json()).then((res: any) => {
  151. setLoading(() => false)
  152. if (res?.code === 200) {
  153. // 验证服务器是否升级
  154. fetch(api + '/erp/config/sysVersion/preserve', {
  155. headers: {
  156. 'Authorization': 'Bearer ' + res?.data?.token
  157. }
  158. }).then(res => res.json()).then(js => {
  159. if (js?.data?.configValue === 'true') {
  160. message.error('版本更新中....请关注大群公告!!!!', 5, () => {
  161. sessionStorage.removeItem('Admin-Token')
  162. window.location.href = '/';
  163. })
  164. } else {
  165. sessionStorage.setItem('Admin-Token', res?.data?.token)
  166. window.location.href = '/';
  167. // setTimeout(() => {
  168. // refresh();
  169. // }, 0);
  170. }
  171. })
  172. }
  173. }).catch(() => setLoading(() => false))
  174. }
  175. // 更换账户
  176. const logOut = () => {
  177. setInitialState({ ...initialState, currentUser: undefined });
  178. loginOut();
  179. }
  180. // 获取手机钉钉验证码
  181. const getPhoneCode = () => {
  182. if (values) {
  183. setM(30)
  184. codeRes.run(values?.phone).then(res => {
  185. if (res.code === 200) {
  186. message.success('获取成功,请查看你的钉钉消息!验证码将发送到钉钉')
  187. timeOut(30)
  188. } else {
  189. timeOut(0)
  190. }
  191. }).catch(() => {
  192. timeOut(0)
  193. })
  194. } else {
  195. message.error('请输入正确的手机号!!!')
  196. }
  197. }
  198. // 获取手机短信验证码
  199. const getPhoneNoteCode = () => {
  200. if (values) {
  201. setM(30)
  202. noteCode.run(values?.phone).then(res => {
  203. if (res.code === 200) {
  204. message.success('获取成功,请查看你的短信消息!验证码将以短信的形式发送到您的手机!')
  205. timeOut(30)
  206. } else {
  207. timeOut(0)
  208. }
  209. }).catch(() => timeOut(0))
  210. } else {
  211. message.error('请输入正确的手机号!!!')
  212. }
  213. }
  214. // 倒计时
  215. const timeOut = useCallback((num: number) => {
  216. let timer: any = null
  217. if (num > 0) {
  218. timer = setTimeout(() => {
  219. setM(num - 1)
  220. timeOut(num - 1)
  221. clearTimeout(timer)
  222. timer = null
  223. }, 1000)
  224. } else {
  225. setM(0)
  226. }
  227. }, [])
  228. // 手机登录
  229. const phoneSubmit = () => {
  230. if (values?.code) {
  231. phone_login.run(values).then(res => {
  232. try {
  233. if (res.code === 200) {
  234. setIsLogin(false)
  235. sessionStorage.setItem('Admin-Token', res?.data?.token)
  236. let companyInfo = res?.data?.companyRelationInfo?.filter((item: { companyId: number }) => item.companyId !== 4 && item.companyId !== 3)
  237. if (companyInfo?.length === 0) {
  238. sessionStorage.removeItem('Admin-Token')
  239. message.error('登录失败,请用趣程运营平台账号登录')
  240. return
  241. } else if (companyInfo?.length === 1) {
  242. setCompanyHandle(res?.data?.companyRelationInfo[0].companyId)
  243. } else {
  244. setCompanyList(companyInfo)
  245. }
  246. // message.success('登录成功!');
  247. return;
  248. } else {
  249. setIsLogin(false)
  250. // codeRes.run()
  251. }
  252. // 如果失败去设置用户错误信息
  253. } catch (error) {
  254. setIsLogin(false)
  255. message.error('登录失败,请重试!');
  256. }
  257. })
  258. }
  259. }
  260. return (
  261. <>
  262. {
  263. <>
  264. {/* {isOk?} */}
  265. <div className={`${styles.container}`} id='login'>
  266. <div className={styles.content}>
  267. {sessionStorage.getItem('Admin-Token') && companyList?.length > 0 ? <div className={`${styles.company} ${isMobile ? '' : show ? '' : styles.login_hide}`}>
  268. <h1>趣程广告投放管理</h1>
  269. <div className={styles.companyAccount}>
  270. <h3 className={styles.title}>请选择公司账户登录</h3>
  271. <div className={styles.chooseTableBlock}>
  272. <Spin spinning={loading}>
  273. {
  274. companyList?.map((item: any) => <div className={styles.acTableLine} key={item?.companyId} onClick={() => { setCompanyHandle(item.companyId) }}>
  275. <div className={styles.actname}>{item?.companyInfo?.companyName}</div>
  276. <div className={styles.right}> <div className={styles.actcha}>{item?.powerLevel === 999 ? '超级管理员' : item?.powerLevel === 100 ? '系统管理员' : item?.powerLevel === 99 ? '管理员' : '普通用户'}</div> <SwapRightOutlined className={styles.iconRight} /></div>
  277. </div>)
  278. }
  279. </Spin>
  280. </div>
  281. <div className={styles.button}>
  282. <Button type="link" onClick={logOut}>更换账号</Button>
  283. </div>
  284. </div>
  285. </div> : <div className={`${styles.main} ${isMobile ? '' : show ? '' : styles.login_hide}`} id='login_main'>
  286. <h1>趣程广告投放管理</h1>
  287. {<div className={styles.tabs}>
  288. {/* <span onClick={() => { setTab(1) }} style={tab === 1 ? { color: localStorage.getItem('color') || '#24DB95' } : {}}>手机</span>
  289. <span onClick={() => { setTab(2) }} style={tab === 2 ? { color: localStorage.getItem('color') || '#24DB95' } : {}}>钉钉</span> */}
  290. </div>}
  291. {
  292. tab === 1 ? <>
  293. <div>
  294. <input
  295. placeholder='Phone'
  296. value={values?.phone}
  297. onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
  298. let value = e.target.value
  299. if (value === '背景') {
  300. setVideoMenu({ visible: true })
  301. }
  302. setValues({ ...values, phone: value })
  303. }}
  304. />
  305. </div>
  306. <div>
  307. <input
  308. placeholder='Code'
  309. style={{ width: '60%' }}
  310. onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
  311. let value = e.target.value
  312. setValues({ ...values, code: value })
  313. }}
  314. onKeyUp={(event: React.KeyboardEvent<HTMLInputElement>) => {
  315. if (event.key === 'Enter') {
  316. phoneSubmit()
  317. }
  318. }}
  319. />
  320. <button
  321. className={styles.btn}
  322. style={!m ? { background: localStorage.getItem('color') || '#24DB95' } : { background: '#999' }}
  323. onClick={codeType === 1 ? getPhoneCode : getPhoneNoteCode}
  324. disabled={!!m}
  325. >
  326. {!m ? '获取验证码' : m + '秒再次获取'}
  327. </button>
  328. </div>
  329. <Radio.Group value={codeType} onChange={(e) => {
  330. let v = e.target.value
  331. setCodeType(v)
  332. }}>
  333. <Radio value={1} style={{ color: '#fff' }}>钉钉验证码</Radio>
  334. <Radio value={2} style={{ color: '#fff' }}>手机验证码</Radio>
  335. </Radio.Group>
  336. <button
  337. onClick={phoneSubmit}
  338. disabled={isLogin}
  339. style={{ background: localStorage.getItem('color') || '#24DB95' }}
  340. >登录</button>
  341. </> :
  342. <div id='login_container' />
  343. }
  344. </div>}
  345. </div>
  346. {/**背景 */}
  347. {!isMobile && <Bg data={videoMenu} open={setVideoMenu} show={handleShow} isShow={show} />}
  348. <footer className={`${styles.footer} ${show ? '' : styles.footer_hide}`} onClick={() => { setVideoMenu({ visible: !videoMenu.visible }) }}>
  349. <a >
  350. 趣程
  351. </a>
  352. <span>Copyright<span><CopyrightOutlined /></span>趣程 </span>
  353. </footer>
  354. </div>
  355. </>
  356. }
  357. </>
  358. );
  359. };
  360. export default Login;