Przeglądaj źródła

md5加密方式

bding 8 miesięcy temu
rodzic
commit
98268aa3bc

+ 24 - 0
package-lock.json

@@ -62,6 +62,7 @@
         "jest": "^27.4.3",
         "jest-resolve": "^27.4.2",
         "jest-watch-typeahead": "^1.0.0",
+        "js-md5": "^0.8.3",
         "jspdf": "^2.5.1",
         "jwt-decode": "^3.1.2",
         "localforage": "^1.9.0",
@@ -116,6 +117,7 @@
       },
       "devDependencies": {
         "@types/file-saver": "^2.0.4",
+        "@types/js-md5": "^0.7.2",
         "@types/jspdf": "^2.0.0",
         "@types/lodash": "^4.14.182",
         "@types/md5": "^2.3.2",
@@ -4559,6 +4561,12 @@
       "version": "2.2.7",
       "license": "MIT"
     },
+    "node_modules/@types/js-md5": {
+      "version": "0.7.2",
+      "resolved": "https://registry.npmmirror.com/@types/js-md5/-/js-md5-0.7.2.tgz",
+      "integrity": "sha512-IwcFIov1JbiEToo3DgrVEwkMUR1BVlVyLjk245AzGHyyOlyddDFIeSIZO+WlhTortOa4P1PdCS8cDrTl7RW1HA==",
+      "dev": true
+    },
     "node_modules/@types/json-schema": {
       "version": "7.0.15",
       "license": "MIT"
@@ -12966,6 +12974,11 @@
       "version": "2.2.1",
       "license": "MIT"
     },
+    "node_modules/js-md5": {
+      "version": "0.8.3",
+      "resolved": "https://registry.npmmirror.com/js-md5/-/js-md5-0.8.3.tgz",
+      "integrity": "sha512-qR0HB5uP6wCuRMrWPTrkMaev7MJZwJuuw4fnwAzRgP4J4/F8RwtodOKpGp4XpqsLBFzzgqIO42efFAyz2Et6KQ=="
+    },
     "node_modules/js-tokens": {
       "version": "4.0.0",
       "license": "MIT"
@@ -23131,6 +23144,12 @@
     "@types/js-cookie": {
       "version": "2.2.7"
     },
+    "@types/js-md5": {
+      "version": "0.7.2",
+      "resolved": "https://registry.npmmirror.com/@types/js-md5/-/js-md5-0.7.2.tgz",
+      "integrity": "sha512-IwcFIov1JbiEToo3DgrVEwkMUR1BVlVyLjk245AzGHyyOlyddDFIeSIZO+WlhTortOa4P1PdCS8cDrTl7RW1HA==",
+      "dev": true
+    },
     "@types/json-schema": {
       "version": "7.0.15"
     },
@@ -28367,6 +28386,11 @@
     "js-cookie": {
       "version": "2.2.1"
     },
+    "js-md5": {
+      "version": "0.8.3",
+      "resolved": "https://registry.npmmirror.com/js-md5/-/js-md5-0.8.3.tgz",
+      "integrity": "sha512-qR0HB5uP6wCuRMrWPTrkMaev7MJZwJuuw4fnwAzRgP4J4/F8RwtodOKpGp4XpqsLBFzzgqIO42efFAyz2Et6KQ=="
+    },
     "js-tokens": {
       "version": "4.0.0"
     },

+ 2 - 0
package.json

@@ -56,6 +56,7 @@
     "jest": "^27.4.3",
     "jest-resolve": "^27.4.2",
     "jest-watch-typeahead": "^1.0.0",
+    "js-md5": "^0.8.3",
     "jspdf": "^2.5.1",
     "jwt-decode": "^3.1.2",
     "localforage": "^1.9.0",
@@ -139,6 +140,7 @@
   },
   "devDependencies": {
     "@types/file-saver": "^2.0.4",
+    "@types/js-md5": "^0.7.2",
     "@types/jspdf": "^2.0.0",
     "@types/lodash": "^4.14.182",
     "@types/md5": "^2.3.2",

+ 23 - 1
src/Login2p/Login2p.service.ts

@@ -2,6 +2,8 @@ import NewAxiosInstanceFunc from 'Service/NewAxiosInstance'
 import { INewResponse } from 'Service/INewResponse'
 
 export interface IPrivateEquityUser {
+  IsSetPassword: boolean
+  LoginErrCode: number
   Authorization: string // Token
   CompanyName: string
   CompanyPointsNum: number // 研选剩余点数
@@ -45,13 +47,21 @@ export const Login2pService = {
   postLogin2p: (
     Mobile: string,
     VCode: string,
+    LoginType?: string,
+    Password?: string,
     Token?: string,
     InviteShareCode?: string
   ): INewResponse<IPrivateEquityUser> =>
     NewAxiosInstanceFunc({
       url: '/user/login',
       method: 'post',
-      data: { Mobile, VCode, Token, InviteShareCode }
+      data: { Mobile, VCode, LoginType, Password, Token, InviteShareCode }
+    }),
+  postLoginResetPassword: (Mobile: string, VCode: string, Password?: string): INewResponse<IPrivateEquityUser> =>
+    NewAxiosInstanceFunc({
+      url: '/user/reset_pass',
+      method: 'post',
+      data: { Mobile, VCode, Password }
     }),
   getUserDetail: (token: string | undefined): INewResponse<IPrivateEquityUser> =>
     NewAxiosInstanceFunc({
@@ -70,5 +80,17 @@ export const Login2pService = {
       url: '/wechat/bindMobile',
       method: 'post',
       data: { Mobile, VCode }
+    }),
+  postSetPassword: (Mobile: string, VCode: string, Password: string): INewResponse =>
+    NewAxiosInstanceFunc({
+      url: '/user/set_pass',
+      method: 'post',
+      data: { Mobile, VCode, Password }
+    }),
+  postUpdatePassword: (OldPassword: string, NewPassword: string): INewResponse =>
+    NewAxiosInstanceFunc({
+      url: '/user/update_pass',
+      method: 'post',
+      data: { OldPassword, NewPassword }
     })
 }

+ 57 - 9
src/Login2p/Login2p.tsx

@@ -29,6 +29,10 @@ const Login2p: React.FC = () => {
   const next = new URLSearchParams(useLocation().search).get('next')
   const [isDisabled, setIsDisabled] = useState<boolean>(false)
   const [isSelectAuthor, setIsSelectAuthor] = useState<boolean>(false)
+  const [isSetPassword, setIsSetPassword] = useState<boolean>(false)
+  const [isBindMobile, setIsBindMobile] = useState<number>(0)
+  const [encryptionPhone, setEncryptionPhone] = useState<string>('')
+
   const [captchaBtnText, setCaptchaBtnText] = useState<string>('获取验证码')
   const [tabsAct, setTabsAct] = useState<string>('1')
   const [phoneArea, setPhoneArea] = useState<number>(86)
@@ -41,8 +45,16 @@ const Login2p: React.FC = () => {
     { id: 852, optionName: '中国香港(+852)', regText: /^[569]\d{3}\-?\d{4}$/ },
     { id: 61, optionName: '澳大利亚(+61)', regText: /^4\d{8}$/ },
     { id: 65, optionName: '新加坡(+65)', regText: /^[89]\d{7}$/ },
-    { id: 1, optionName: '美国(+001)', regText: /^[2-9]\d{2}[2-9](?!11)\d{6}$/ },
-    { id: 81, optionName: '日本(+81)', regText: /^\d{1,4}[ \-]?\d{1,4}[ \-]?\d{4}$/ },
+    {
+      id: 1,
+      optionName: '美国(+001)',
+      regText: /^[2-9]\d{2}[2-9](?!11)\d{6}$/
+    },
+    {
+      id: 81,
+      optionName: '日本(+81)',
+      regText: /^\d{1,4}[ \-]?\d{1,4}[ \-]?\d{4}$/
+    },
     { id: 44, optionName: '英国(+44)', regText: /^\+?[0-9,\-,\s]{5,20}$/ },
     { id: 886, optionName: '中国台湾(+886)', regText: /^[0][9]\d{8}$/ }
   ]
@@ -67,9 +79,23 @@ const Login2p: React.FC = () => {
       )
     }
   }
+  // 设置、修改密码
+  const setUpPasswordHandlern = () => {
+    setIsBindMobile(1)
+    setIsSetPassword(true)
+  }
   const handleLoginegByCode = (value: any) => {
+    setEncryptionPhone(value.phone_number)
     login2p
-      .login2pByCode(value.phone_number, value.code, next ? next : '/', wechatToken ? wechatToken : undefined)
+      .login2pByCode(
+        value.phone_number,
+        value.code,
+        tabsAct,
+        value.password,
+        next ? next : '/',
+        wechatToken ? wechatToken : undefined,
+        setUpPasswordHandlern
+      )
       .then(r => undefined)
   }
 
@@ -110,7 +136,7 @@ const Login2p: React.FC = () => {
 
     {
       key: '2',
-      label: <span>密码登</span>,
+      label: <span>密码登</span>,
       children: <></>
     }
   ]
@@ -123,7 +149,10 @@ const Login2p: React.FC = () => {
   const handleOpenWord = (path: string) => {
     window.open(path)
   }
-
+  // 点击了设置、修改密码
+  const setUpPasswordClickHandlern = () => {
+    setIsSetPassword(true)
+  }
   return (
     <>
       <div className={styles['login-content-bg-img']}>
@@ -153,7 +182,15 @@ const Login2p: React.FC = () => {
             <Form form={form} onFinish={handleLoginegByCode}>
               <Form.Item style={{ marginBottom: '5px' }}>
                 {/* <p style={{ lineHeight: '24px', fontSize: '16px' }}>手机号</p> */}
-                <Form.Item name="phone_number" rules={[{ required: true, message: 'Please input your phone number!' }]}>
+                <Form.Item
+                  name="phone_number"
+                  rules={[
+                    {
+                      required: true,
+                      message: 'Please input your phone number!'
+                    }
+                  ]}
+                >
                   <div className={styles['input-content']}>
                     <Select
                       defaultValue={phoneArea}
@@ -204,17 +241,20 @@ const Login2p: React.FC = () => {
                   <Form.Item>
                     <div className={styles['input-content']}>
                       <Form.Item
-                        name={'password'}
+                        name="password"
                         style={{ width: '100%', display: 'inline-block' }}
                         rules={[{ required: true, message: 'Please input Password!' }]}
                       >
-                        <Input
+                        <Input.Password
                           style={{ height: 60 }}
                           prefix={<LoginPassword style={{ width: 20 }} />}
                           placeholder="请输入密码"
                         />
                       </Form.Item>
                     </div>
+                    <div className={styles['input-set-password-forget']}>
+                      <span onClick={setUpPasswordClickHandlern}>设置密码/忘记密码</span>
+                    </div>
                   </Form.Item>
                 </Form.Item>
               )}
@@ -257,7 +297,15 @@ const Login2p: React.FC = () => {
           </div>
         </div>
       </div>
-      <SetUpPassword visible={false} />
+      <SetUpPassword
+        visible={isSetPassword}
+        isBindMobile={isBindMobile}
+        encryptionPhone={encryptionPhone}
+        phoneAreaCode={phoneArea}
+        onCancelPassword={() => {
+          setIsSetPassword(false)
+        }}
+      />
     </>
   )
 }

+ 61 - 12
src/Login2p/Login2pContext.tsx

@@ -8,6 +8,7 @@ import { Login2pService, IPrivateEquityUser } from './Login2p.service'
 import { getQueryString } from 'utils/getQueryString'
 import { checkWechat } from 'utils/wxConfig'
 import NewAxiosInstanceFunc from 'Service/NewAxiosInstance'
+import md5 from 'js-md5'
 
 const Login2pContexts = React.createContext<
   | {
@@ -19,10 +20,25 @@ const Login2pContexts = React.createContext<
       logout: () => void
       loginWechatLaunch: (redirectURI: string) => void
       loginWechat: (code: string, next: string) => Promise<any>
-      login2pByCode: (phone_number: string, code: string, next: string, token?: string) => Promise<any>
+      login2pByCode: (
+        phone_number: string,
+        code: string,
+        tabsAct: string,
+        password: string,
+        next: string,
+        token?: string,
+        setUpPasswordHandlern?: any
+      ) => Promise<any>
       getUserDetail: () => void
       getUserDetailLoading: boolean
       loginEnd: (access_token: string, next: string) => void
+      initPassword: (
+        phone_number: string,
+        code: string,
+        tabsAct: string,
+        password: string,
+        next: string
+      ) => Promise<any>
     }
   | undefined
 >(undefined)
@@ -93,19 +109,51 @@ const Login2pProvider: React.FC<ContextProviderProps> = ({ children }: ContextPr
         }
       })
       .catch(error => message.error('微信登录错误'))
+  // 验证码登录 || 密码登录
+  const login2pByCode = async (
+    phone_number: string,
+    code: string,
+    tabsAct: string,
+    password: string,
+    next: string,
+    token?: string,
+    setUpPasswordHandlern?: any
+  ) => {
+    const data = await Login2pService.postLogin2p(
+      phone_number,
+      code,
+      tabsAct,
+      password ? md5(password) : '',
+      token,
+      sessionStorage.getItem('invite_code') || inviteCode
+    )
 
-  const login2pByCode = async (phone_number: string, code: string, next: string, token?: string) =>
-    await Login2pService.postLogin2p(phone_number, code, token, sessionStorage.getItem('invite_code') || inviteCode)
-      .then(data => {
-        if (data.data.Data?.Authorization) {
-          setUserInfo(data.data.Data)
-          loginEnd(data.data.Data?.Authorization, next)
-        } else {
-          message.error(data.data.Msg)
-        }
-      })
-      .catch(error => message.error('验证码错误'))
+    if (data.data.Ret === 200) {
+      if (data.data.Data?.Authorization) {
+        setUserInfo(data.data.Data)
+        loginEnd(data.data.Data?.Authorization, next)
+        return
+      } else if (data.data.Data?.LoginErrCode > 0 && !data.data.Data?.Authorization) {
+        setUpPasswordHandlern()
+        return
+      } else {
+        message.error(data.data.Msg)
+      }
+    } else {
+      message.error(tabsAct === '1' ? '验证码错误' : '密码错误')
+    }
+  }
 
+  // 初始密码登录
+  const initPassword = async (phone_number: string, code: string, tabsAct: string, password: string, next: string) => {
+    const data = await Login2pService.postLoginResetPassword(phone_number, code, password ? md5(password) : '')
+    if (data.data.Ret === 200) {
+      if (data.data.Data?.Authorization) {
+        setUserInfo(data.data.Data)
+        loginEnd(data.data.Data?.Authorization, next)
+      }
+    }
+  }
   const { run: getUserDetail, loading: getUserDetailLoading } = useRequest(() => Login2pService.getUserDetail(jwt), {
     manual: true,
     cacheKey: 'userinfo',
@@ -144,6 +192,7 @@ const Login2pProvider: React.FC<ContextProviderProps> = ({ children }: ContextPr
         loginWechatLaunch: loginWechatLaunch,
         loginWechat: loginWechat,
         login2pByCode: login2pByCode,
+        initPassword: initPassword,
         getUserDetail: getUserDetail,
         getUserDetailLoading: getUserDetailLoading,
         loginEnd: loginEnd

+ 78 - 47
src/Login2p/components/SetUpPassword.tsx

@@ -8,11 +8,13 @@ import { SafetyCertificateOutlined, ExclamationCircleFilled } from '@ant-design/
 import { useLogin2p } from '../Login2pContext'
 import { ReactComponent as LoginMobile } from 'assets/login_mobile.svg'
 import { ReactComponent as LoginPassword } from 'assets/login_passwode.svg'
-
 import { Login2pService } from '../Login2p.service'
 interface ISetUpPasswordProps {
   visible: boolean
-  onClose?: () => void
+  isBindMobile: number
+  encryptionPhone: string
+  phoneAreaCode: number
+  onCancelPassword: () => void
   onRefresh?: () => void
 }
 
@@ -21,7 +23,7 @@ interface ISetUpPasswordProps {
  */
 const SetUpPassword: React.FC<ISetUpPasswordProps> = props => {
   const { Option } = Select
-  const { visible, onClose, onRefresh } = props
+  const { visible, isBindMobile, encryptionPhone, phoneAreaCode, onCancelPassword, onRefresh } = props
   const [form] = Form.useForm()
   const login2p = useLogin2p()
   const next = new URLSearchParams(useLocation().search).get('next')
@@ -48,20 +50,34 @@ const SetUpPassword: React.FC<ISetUpPasswordProps> = props => {
     onSuccess: result => message.success('验证码请求成功!'),
     onError: e => message.error('验证码请求失败~')
   })
-  const handleToClose = () => {
-    // onClose()
-  }
+
+  // 点击了登录
   const handleLoginegByCode = (value: any) => {
-    login2p
-      .login2pByCode(value.phone_number, value.code, next ? next : '/', wechatToken ? wechatToken : undefined)
-      .then(r => undefined)
+    // 至少 6 位字符,包含字母和数字
+    const passwordRegex = /^(?=.*[a-zA-Z])(?=.*\d)[a-zA-Z\d]{6,}$/
+
+    if (passwordRegex.test(value.password)) {
+      login2p.initPassword(
+        value.phone_number || encryptionPhone,
+        value.code || phoneAreaCode,
+        isBindMobile + '',
+        value.password,
+        next ? next : '/'
+      )
+    } else {
+      message.error('请设置6位以上数字字母组合')
+      return
+    }
   }
+
   const checkPhoneNumber = (phoneNumber: string) => {
     const findItem = phoneAreaList.find(item => item.id === phoneArea)
     return findItem ? findItem.regText.test(phoneNumber) : false
   }
+
+  // 手机验证码
   const getVCodeData = () => {
-    if (!checkPhoneNumber(form.getFieldValue('phone_number'))) {
+    if (!checkPhoneNumber(form.getFieldValue('phone_number')) && !encryptionPhone) {
       message.warning('格式错误,请输入正确的手机号码')
       return
     }
@@ -75,46 +91,56 @@ const SetUpPassword: React.FC<ISetUpPasswordProps> = props => {
       setCaptchaBtnText('获取验证码')
       clearInterval(captchaCountdown)
     }, 60000)
-    getLoginCode(phoneArea.toString(), form.getFieldValue('phone_number'))
+    getLoginCode(
+      phoneArea.toString() || phoneAreaCode.toString(),
+      form.getFieldValue('phone_number') || encryptionPhone
+    )
+  }
+  // 关闭弹框事件
+  const closeModalHandler = () => {
+    onCancelPassword()
   }
   return (
-    <Modal
-      open={visible}
-      closable={false}
-      onCancel={handleToClose}
-      destroyOnClose={true}
-      maskClosable={false}
-      footer={null}
-      width={800}
-    >
-      <div className={styles['set-up-password-text']}>
-        <ExclamationCircleFilled style={{ color: '#E37318', fontSize: 18, marginRight: 8 }} />
-        该账号尚未设置登录密码,请先设置登录密码
-      </div>
-      <div className={styles['login-content-box-set-up']}>
+    <Modal open={visible} closable={false} destroyOnClose={true} maskClosable={false} footer={null} width={800}>
+      {isBindMobile === 1 && (
+        <div className={styles['set-up-password-text']}>
+          <ExclamationCircleFilled style={{ color: '#E37318', fontSize: 18, marginRight: 8 }} />
+          该账号尚未设置登录密码,请先设置登录密码
+        </div>
+      )}
+
+      <div className={`${styles['login-content-bg-img']} ${styles['login-content-box-set-up']} `}>
         <div className="password-dlg-title">设置密码</div>
         <div className={styles['login-columncenter-content']}>
           <Form form={form} onFinish={handleLoginegByCode}>
-            <Form.Item style={{ marginBottom: '5px' }}>
-              <Form.Item name="phone_number" rules={[{ required: true, message: 'Please input your phone number!' }]}>
-                <div className={styles['input-content']}>
-                  <Select
-                    defaultValue={phoneArea}
-                    className="select-input"
-                    onChange={value => {
-                      setPhoneArea(value)
-                    }}
-                  >
-                    {phoneAreaList.map(item => (
-                      <Option value={item.id} key={item.id}>
-                        {item.optionName}
-                      </Option>
-                    ))}
-                  </Select>
-                  <Input prefix={<LoginMobile style={{ width: 15 }} />} placeholder="请输入手机号" />
-                </div>
+            {isBindMobile === 1 ? (
+              <div className={styles['encryption-phone-text']}>
+                +{phoneAreaCode} &nbsp;
+                {encryptionPhone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')}
+              </div>
+            ) : (
+              <Form.Item style={{ marginBottom: '5px' }}>
+                <Form.Item name="phone_number" rules={[{ required: true, message: 'Please input your phone number!' }]}>
+                  <div className={styles['input-content']}>
+                    <Select
+                      defaultValue={phoneArea}
+                      className="select-input"
+                      onChange={value => {
+                        setPhoneArea(value)
+                      }}
+                    >
+                      {phoneAreaList.map(item => (
+                        <Option value={item.id} key={item.id}>
+                          {item.optionName}
+                        </Option>
+                      ))}
+                    </Select>
+                    <Input prefix={<LoginMobile style={{ width: 15 }} />} placeholder="请输入手机号" />
+                  </div>
+                </Form.Item>
               </Form.Item>
-            </Form.Item>
+            )}
+
             <Form.Item style={{ marginBottom: '5px' }}>
               <Form.Item name={'code'} rules={[{ required: true, message: 'Please input Captcha!' }]}>
                 <div className={styles['input-content']}>
@@ -142,10 +168,10 @@ const SetUpPassword: React.FC<ISetUpPasswordProps> = props => {
                     style={{ width: '100%', display: 'inline-block' }}
                     rules={[{ required: true, message: 'Please input Password!' }]}
                   >
-                    <Input
+                    <Input.Password
                       style={{ height: 60 }}
                       prefix={<LoginPassword style={{ width: 20 }} />}
-                      placeholder="请输入密码"
+                      placeholder="请设置登录密码(6位以上数字字母组合)"
                     />
                   </Form.Item>
                 </div>
@@ -153,7 +179,12 @@ const SetUpPassword: React.FC<ISetUpPasswordProps> = props => {
             </Form.Item>
             <Form.Item>
               <div className="password-btn-box">
-                <Button htmlType="button" className="password-btn" style={{ color: '#faa12f' }}>
+                <Button
+                  onClick={closeModalHandler}
+                  htmlType="button"
+                  className="password-btn"
+                  style={{ color: '#faa12f' }}
+                >
                   取消
                 </Button>
                 <Button type="primary" htmlType="submit" className="password-btn">

+ 26 - 2
src/NewPageHeader.tsx

@@ -9,6 +9,8 @@ import OfficialImg from 'assets/official.png'
 import VipDay from 'assets/vipday.png'
 import VipMonth from 'assets/vipmonth.png'
 // import LogoImg from 'assets/logo.png'
+import MySetPassword from 'SetPassword/mySet.password'
+import ModifyPassword from 'SetPassword/modify.password'
 
 import { useLogin2p } from './Login2p/Login2pContext'
 import { useMedia } from 'Context/Media/MediaContext'
@@ -40,6 +42,8 @@ const NewPage: React.FC = props => {
   const [hoverKey, setHoverKey] = useState<string>('newest')
   const [searchWord, setSearchWord] = useState<string>()
   const [visibleApply, setVisibleApply] = useState(false)
+  const [visiblePassword, setVisiblePassword] = useState(false)
+  const [visibleModifyPassword, setVisibleModifyPassword] = useState(false)
   const [isShowMenu, setIsShowMenu] = useState<boolean>(false)
   const [isShowMobileMenu, setIsShowMobileMenu] = useState<boolean>(false) // 识别报告详情页+活动详情页,适配移动菜单
 
@@ -146,6 +150,12 @@ const NewPage: React.FC = props => {
   const handleOKApply = () => {
     setVisibleApply(false)
   }
+
+  // 设置修改密码
+  const handlePassword = () => {
+    // eslint-disable-next-line @typescript-eslint/no-unused-expressions
+    login2p.userInfo?.IsSetPassword ? setVisibleModifyPassword(true) : setVisiblePassword(false)
+  }
   return (
     <div className={styles['newpage-header']} style={{ minWidth: isShowMobileMenu ? 'auto' : '1024px' }}>
       {isShowMobileMenu ? (
@@ -362,8 +372,8 @@ const NewPage: React.FC = props => {
                   <div className="person-line person-center-link" onClick={handleToMy}>
                     个人中心
                   </div>
-                  <div className="person-line person-center-link" onClick={handleToMy}>
-                    设置登录密码
+                  <div className="person-line person-center-link" onClick={handlePassword}>
+                    {login2p.userInfo?.IsSetPassword ? '修改登录密码' : '设置登录密码'}
                   </div>
                   <div className="person-line person-exit" onClick={login2p.logout}>
                     退出
@@ -381,6 +391,20 @@ const NewPage: React.FC = props => {
         </Row>
       )}
       <ApplyPermission visible={visibleApply} onCloseModel={handleOKApply} />
+      <MySetPassword
+        visible={visiblePassword}
+        encryptionPhone={login2p.userInfo?.Mobile || ''}
+        phoneAreaCode={login2p.userInfo?.OutboundCountryCode || ''}
+        onCancelPassword={() => {
+          setVisiblePassword(false)
+        }}
+      />
+      <ModifyPassword
+        visible={visibleModifyPassword}
+        onCancelPassword={() => {
+          setVisibleModifyPassword(false)
+        }}
+      />
     </div>
   )
 }

+ 41 - 2
src/Service/NewAxiosInstance.ts

@@ -1,6 +1,11 @@
 import Axios, { AxiosError, AxiosInstance, AxiosResponse } from 'axios'
 import { getQueryString } from 'utils/getQueryString'
 import { Cookie } from 'utils/cookie'
+import { Modal } from 'antd'
+import { useHistory } from 'react-router-dom'
+import styles from 'styles/CommonGlobal.module.scss'
+
+// const history = useHistory()
 
 const fromString = getQueryString('From')
 if (fromString) {
@@ -17,7 +22,41 @@ if (Cookie.get('From') || fromString)
   NewAxiosInstanceFunc.defaults.headers['From'] = Cookie.get('From') || fromString || ''
 
 NewAxiosInstanceFunc.interceptors.response.use(
-  (response: AxiosResponse): AxiosResponse => response,
-  (error: AxiosError): Promise<AxiosError> => Promise.reject(error)
+  (response: AxiosResponse): AxiosResponse | Promise<AxiosResponse> => {
+    if (response.data?.Ret === 408) {
+      Modal.confirm({
+        title: '登录提示',
+        content: '您的登录状态已过期,请重新登录',
+        okText: '重新登录',
+        className: 'all-dlg-style',
+        centered: true,
+        onOk: close => {
+          localStorage.removeItem('smtoken')
+          window.location.href = '/login2p'
+        }
+      })
+      // eslint-disable-next-line @typescript-eslint/no-empty-function
+      return new Promise<AxiosResponse>(() => {}) as Promise<AxiosResponse>
+    } else if (response.data?.Ret === 401) {
+      Modal.confirm({
+        title: '下线提示',
+        content:
+          '您的账号在另一设备登录,当前设备已被迫下线。若不是您本人操作,请确保未泄露短信验证码或及时修改您的登录密码',
+        okText: '重新登录',
+        className: 'all-dlg-style',
+        centered: true,
+        onOk: close => {
+          localStorage.removeItem('smtoken')
+          window.location.href = '/login2p'
+        }
+      })
+      // eslint-disable-next-line @typescript-eslint/no-empty-function
+      return new Promise<AxiosResponse>(() => {}) as Promise<AxiosResponse>
+    }
+    return response
+  },
+  (error: AxiosError): Promise<AxiosError> => {
+    return Promise.reject(error)
+  }
 )
 export default NewAxiosInstanceFunc

+ 110 - 0
src/SetPassword/modify.password.tsx

@@ -0,0 +1,110 @@
+import React, { useState } from 'react'
+import useRequest from '@ahooksjs/use-request'
+import { Input, Button, Form, message, Modal } from 'antd'
+
+import styles from 'styles/NewPage.module.scss'
+import { SafetyCertificateOutlined } from '@ant-design/icons'
+import { ReactComponent as LoginPassword } from 'assets/login_passwode.svg'
+import { Login2pService } from 'Login2p/Login2p.service'
+import { useLogin2p } from 'Login2p/Login2pContext'
+import md5 from 'js-md5'
+interface ISetUpPasswordProps {
+  visible: boolean
+  onCancelPassword: () => void
+}
+
+/**
+ * 编辑专栏信息
+ */
+const SetUpPassword: React.FC<ISetUpPasswordProps> = props => {
+  const login2p = useLogin2p()
+  const { visible, onCancelPassword } = props
+  const [form] = Form.useForm()
+
+  // 点击了登录
+  const handleLoginegByCode = async (value: any) => {
+    // 至少 6 位字符,包含字母和数字
+    const passwordRegex = /^(?=.*[a-zA-Z])(?=.*\d)[a-zA-Z\d]{6,}$/
+
+    if (passwordRegex.test(value.new_password)) {
+      const res = await Login2pService.postUpdatePassword(md5(value.old_password), md5(value.new_password))
+      if (res.data.Ret === 200) {
+        message.success('操作成功')
+        login2p.getUserDetail()
+        closeModalHandler()
+      } else {
+        message.error(res.data.Msg)
+      }
+    } else {
+      message.error('请设置6位以上数字字母组合')
+      return
+    }
+  }
+
+  // 关闭弹框事件
+  const closeModalHandler = () => {
+    onCancelPassword()
+  }
+  return (
+    <Modal open={visible} closable={false} destroyOnClose={true} maskClosable={false} footer={null} width={800}>
+      <div className={`${styles['login-content-bg-img']} ${styles['login-content-box-set-up']} `}>
+        <div className="password-dlg-title">修改密码</div>
+        <div className={styles['login-columncenter-content']}>
+          <Form form={form} onFinish={handleLoginegByCode}>
+            <Form.Item style={{ marginBottom: '-19px' }}>
+              <Form.Item>
+                <div className={styles['input-content']}>
+                  <Form.Item
+                    name={'old_password'}
+                    style={{ width: '100%', display: 'inline-block' }}
+                    rules={[{ required: true, message: 'Please input Password!' }]}
+                  >
+                    <Input.Password
+                      style={{ height: 60 }}
+                      prefix={<LoginPassword style={{ width: 20 }} />}
+                      placeholder=" 请输入原密码(6位以上数字字母组合)"
+                    />
+                  </Form.Item>
+                </div>
+              </Form.Item>
+            </Form.Item>
+            <Form.Item style={{ marginBottom: '-19px' }}>
+              <Form.Item>
+                <div className={styles['input-content']}>
+                  <Form.Item
+                    name={'new_password'}
+                    style={{ width: '100%', display: 'inline-block' }}
+                    rules={[{ required: true, message: 'Please input Password!' }]}
+                  >
+                    <Input.Password
+                      style={{ height: 60 }}
+                      prefix={<LoginPassword style={{ width: 20 }} />}
+                      placeholder=" 请输入新密码(6位以上数字字母组合)"
+                    />
+                  </Form.Item>
+                </div>
+              </Form.Item>
+            </Form.Item>
+            <Form.Item>
+              <div className="password-btn-box">
+                <Button
+                  onClick={closeModalHandler}
+                  htmlType="button"
+                  className="password-btn"
+                  style={{ color: '#faa12f' }}
+                >
+                  取消
+                </Button>
+                <Button type="primary" htmlType="submit" className="password-btn">
+                  确定
+                </Button>
+              </div>
+            </Form.Item>
+          </Form>
+        </div>
+      </div>
+    </Modal>
+  )
+}
+
+export default SetUpPassword

+ 148 - 0
src/SetPassword/mySet.password.tsx

@@ -0,0 +1,148 @@
+import React, { useState } from 'react'
+import useRequest from '@ahooksjs/use-request'
+import { Input, Button, Form, message, Modal } from 'antd'
+
+import styles from 'styles/NewPage.module.scss'
+import { SafetyCertificateOutlined } from '@ant-design/icons'
+import { ReactComponent as LoginPassword } from 'assets/login_passwode.svg'
+import { Login2pService } from 'Login2p/Login2p.service'
+import { useLogin2p } from 'Login2p/Login2pContext'
+
+import md5 from 'js-md5'
+
+interface ISetUpPasswordProps {
+  visible: boolean
+  encryptionPhone: string
+  phoneAreaCode: string
+  onCancelPassword: () => void
+}
+
+/**
+ * 编辑专栏信息
+ */
+const SetUpPassword: React.FC<ISetUpPasswordProps> = props => {
+  const login2p = useLogin2p()
+  const { visible, encryptionPhone, phoneAreaCode, onCancelPassword } = props
+  const [form] = Form.useForm()
+
+  const [isDisabled, setIsDisabled] = useState<boolean>(false)
+  const [captchaBtnText, setCaptchaBtnText] = useState<string>('获取验证码')
+
+  const { run: getLoginCode, loading: loadingVCode } = useRequest(Login2pService.getVCode, {
+    manual: true,
+    formatResult: response => response.data,
+    onSuccess: result => message.success('验证码请求成功!'),
+    onError: e => message.error('验证码请求失败~')
+  })
+
+  // 点击了登录
+  const handleLoginegByCode = async (value: any) => {
+    // 至少 6 位字符,包含字母和数字
+    const passwordRegex = /^(?=.*[a-zA-Z])(?=.*\d)[a-zA-Z\d]{6,}$/
+
+    if (passwordRegex.test(value.password)) {
+      const res = await Login2pService.postSetPassword(encryptionPhone, value.code, md5(value.password))
+      if (res.data.Ret === 200) {
+        message.success('操作成功')
+        login2p.getUserDetail()
+        closeModalHandler()
+      } else {
+        message.error(res.data.Msg)
+      }
+    } else {
+      message.error('请设置6位以上数字字母组合')
+      return
+    }
+  }
+
+  // 手机验证码
+  const getVCodeData = () => {
+    if (!encryptionPhone) {
+      message.warning('格式错误,请输入正确的手机号码')
+      return
+    }
+    setIsDisabled(true)
+    let countdown = 60
+    const captchaCountdown = setInterval(() => {
+      setCaptchaBtnText(`${countdown--}s`)
+    }, 1000)
+    setTimeout(() => {
+      setIsDisabled(false)
+      setCaptchaBtnText('获取验证码')
+      clearInterval(captchaCountdown)
+    }, 60000)
+    getLoginCode(phoneAreaCode, encryptionPhone)
+  }
+  // 关闭弹框事件
+  const closeModalHandler = () => {
+    onCancelPassword()
+  }
+  return (
+    <Modal open={visible} closable={false} destroyOnClose={true} maskClosable={false} footer={null} width={800}>
+      <div className={`${styles['login-content-bg-img']} ${styles['login-content-box-set-up']} `}>
+        <div className="password-dlg-title">设置密码</div>
+        <div className={styles['login-columncenter-content']}>
+          <Form form={form} onFinish={handleLoginegByCode}>
+            <div className={styles['encryption-phone-text']}>
+              +{phoneAreaCode} &nbsp;
+              {encryptionPhone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')}
+            </div>
+            <Form.Item style={{ marginBottom: '5px' }}>
+              <Form.Item name={'code'} rules={[{ required: true, message: 'Please input Captcha!' }]}>
+                <div className={styles['input-content']}>
+                  <Input
+                    prefix={<SafetyCertificateOutlined style={{ color: '#E37318', fontSize: 20 }} />}
+                    placeholder="请输入验证码"
+                  />
+                  <Button
+                    onClick={getVCodeData}
+                    style={{ width: 160, marginLeft: '12px' }}
+                    loading={loadingVCode}
+                    disabled={isDisabled}
+                    className="custom-style-bottom"
+                  >
+                    {captchaBtnText}
+                  </Button>
+                </div>
+              </Form.Item>
+            </Form.Item>
+            <Form.Item style={{ marginBottom: '-19px' }}>
+              <Form.Item>
+                <div className={styles['input-content']}>
+                  <Form.Item
+                    name={'password'}
+                    style={{ width: '100%', display: 'inline-block' }}
+                    rules={[{ required: true, message: 'Please input Password!' }]}
+                  >
+                    <Input.Password
+                      style={{ height: 60 }}
+                      prefix={<LoginPassword style={{ width: 20 }} />}
+                      placeholder="请设置登录密码(6位以上数字字母组合)"
+                    />
+                  </Form.Item>
+                </div>
+              </Form.Item>
+            </Form.Item>
+            <Form.Item>
+              <div className="password-btn-box">
+                <Button
+                  onClick={closeModalHandler}
+                  htmlType="button"
+                  className="password-btn"
+                  style={{ color: '#faa12f' }}
+                >
+                  取消
+                </Button>
+                <Button type="primary" htmlType="submit" className="password-btn">
+                  确定
+                </Button>
+              </div>
+            </Form.Item>
+          </Form>
+        </div>
+      </div>
+    </Modal>
+  )
+}
+
+export default SetUpPassword

+ 14 - 0
src/styles/CommonGlobal.module.scss

@@ -687,4 +687,18 @@
       padding-bottom: 20px;
     }
   }
+  // 错误状态码的弹框
+  .all-dlg-style {
+    .ant-modal-confirm-content {
+      margin: 20px 0 30px;
+    }
+    .ant-modal-confirm-btns {
+      display: flex;
+      justify-content: center;
+    }
+    .ant-btn-primary {
+      margin-left: 30px;
+      background-color: #faa12f;
+    }
+  }
 }

+ 0 - 0
src/styles/ErrModal.scss


+ 24 - 3
src/styles/NewPage.module.scss

@@ -438,8 +438,16 @@
   justify-content: center;
   font-size: 18px;
   color: #999999;
+  margin-bottom: 10px;
 }
 
+.encryption-phone-text {
+  font-size: 30px;
+  font-weight: 600;
+  line-height: 50px;
+  color: #333;
+  margin-bottom: 20px;
+}
 .login-content-box {
   width: 800px;
   height: 600px;
@@ -449,9 +457,7 @@
   margin: 0 auto;
   box-sizing: border-box;
 }
-.login-content-box-set-up {
-  padding: 10px 100px 22px;
-}
+
 .login-content-bg-img {
   width: 100%;
   height: 100vh;
@@ -555,6 +561,21 @@
     }
   }
 }
+.input-set-password-forget {
+  text-align: right;
+  margin-top: -15px;
+  margin-bottom: 25px;
+  color: #999;
+  span {
+    cursor: pointer;
+  }
+}
+.login-content-box-set-up {
+  width: 75% ;
+  height: 100% ;
+  background: none ;
+  padding: 10px 100px 22px;
+}
 .input-content {
   display: flex;
   justify-content: space-between;

+ 3 - 0
src/types/md5.d.ts

@@ -0,0 +1,3 @@
+declare module 'js-md5' {
+    export default function md5(input: string): string;
+}

+ 5 - 1
tsconfig.json

@@ -15,7 +15,11 @@
     "resolveJsonModule": true,
     "isolatedModules": true,
     "noEmit": true,
-    "jsx": "react-jsx"
+    "jsx": "react-jsx",
+    "typeRoots": [
+      "node_modules/@types",
+      "src/types"
+    ]
   },
   "include": ["src","@types"]
 }