5 Revize edd6c78228 ... 7dcdc4f8b1

Autor SHA1 Zpráva Datum
  bding 7dcdc4f8b1 3.5 准备发版 před 3 měsíci
  bding c1b1ba36e1 修改伟丽遗留bug před 3 měsíci
  bding 98268aa3bc md5加密方式 před 3 měsíci
  bding b3882ca5fb md5 před 3 měsíci
  bding 31287ef6f9 更换了微信扫码登录的方案 před 3 měsíci

+ 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 }
     })
 }

+ 220 - 68
src/Login2p/Login2p.tsx

@@ -5,14 +5,19 @@ import useRequest from '@ahooksjs/use-request'
 // @ts-ignore
 import wx from 'weixin-js-sdk'
 
-import { Col, Row, Card, Input, Button, Select, Form, message } from 'antd'
-import { WechatOutlined } from '@ant-design/icons'
+import { Col, Row, Divider, Input, Button, Select, Form, message, Checkbox, TabsProps, Tabs } from 'antd'
+import { WechatOutlined, SafetyCertificateOutlined } from '@ant-design/icons'
+
+import { ReactComponent as LoginMobile } from 'assets/login_mobile.svg'
+import { ReactComponent as LoginPassword } from 'assets/login_passwode.svg'
 
 import { Login2pService } from './Login2p.service'
 import { useLogin2p } from './Login2pContext'
+import SetUpPassword from './components/SetUpPassword'
 import { useMedia } from 'Context/Media/MediaContext'
 import styles from '../styles/NewPage.module.scss'
 
+import type { CheckboxProps } from 'antd'
 const { Option } = Select
 
 const Login2p: React.FC = () => {
@@ -23,19 +28,33 @@ const Login2p: React.FC = () => {
   const wechatToken = new URLSearchParams(useLocation().search).get('smtoken')
   const next = new URLSearchParams(useLocation().search).get('next')
   const [isDisabled, setIsDisabled] = useState<boolean>(false)
-  const [captchaBtnText, setCaptchaBtnText] = useState<string>('发送验证码')
+  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)
   const colSpanBlank = { xs: 1, sm: 1, md: 4, lg: 4, xl: 8 }
   const colSpanForm = { xs: 22, sm: 22, md: 16, lg: 16, xl: 8 }
-  const colPadding = media.isSmallMax === media.isBigMax ? '150px 0px 0px ' : '20px 0px'
+  const colPadding = media.isSmallMax === media.isBigMax ? '20px 0px 0px ' : '20px 0px'
   const phoneAreaList = [
     { id: 86, optionName: '中国(+86)', regText: /^1\d{10}$/ },
     { id: 853, optionName: '中国澳门(+853)', regText: /^6\d{7}$/ },
     { 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}$/ }
   ]
@@ -60,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)
   }
 
@@ -83,37 +116,93 @@ const Login2p: React.FC = () => {
     }, 1000)
     setTimeout(() => {
       setIsDisabled(false)
-      setCaptchaBtnText('发送验证码')
+      setCaptchaBtnText('获取验证码')
       clearInterval(captchaCountdown)
     }, 60000)
     getLoginCode(phoneArea.toString(), form.getFieldValue('phone_number'))
   }
-
+  // 是否选择了我是作者
+  const onCheckboxChange: CheckboxProps['onChange'] = e => {
+    setIsSelectAuthor(e.target.checked)
+  }
   React.useEffect(checkLogin, [])
 
+  const items: TabsProps['items'] = [
+    {
+      key: '1',
+      label: <span>验证码登录</span>,
+      children: <></>
+    },
+
+    {
+      key: '2',
+      label: <span>密码登录</span>,
+      children: <></>
+    }
+  ]
+  // 点击了tabs的change
+  const onChange = (key: string) => {
+    setTabsAct(key)
+  }
+
+  // 阅读协议
+  const handleOpenWord = (path: string) => {
+    window.open(path)
+  }
+  // 点击了设置、修改密码
+  const setUpPasswordClickHandlern = () => {
+    setIsSetPassword(true)
+  }
   return (
-    <Row gutter={24} style={{ width: '100%' }}>
-      {/*0-780px*/}
-      {/*780px-1280px*/}
-      {/*1280px-max*/}
-      <Col {...colSpanBlank} />
-      <Col
-        {...colSpanForm}
-        style={{
-          padding: colPadding,
-          fontSize: '18px'
-        }}
-      >
-        <Card>
-          <Form form={form} onFinish={handleLoginegByCode}>
-            <div className={styles['login-title']}>验证码登录</div>
-            <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!' }]}>
-                <Input
-                  addonBefore={
+    <>
+      <div style={{ paddingTop: !wechatToken ? 0 : 130 }} className={styles['login-content-bg-img']}>
+        {!wechatToken && !media.isSmallMax && (
+          <img
+            src="https://hzstatic.hzinsights.com/yx_xcx/web/login_log.png"
+            alt=""
+            className={styles['login-log-img']}
+          />
+        )}
+        <div className={`${media.isSmallMax ? styles['login-content-box-mobile'] : styles['login-content-box']}`}>
+          {!wechatToken ? (
+            <>
+              {!media.isSmallMax ? (
+                <>
+                  <div>
+                    <Checkbox onChange={onCheckboxChange} value={isSelectAuthor}>
+                      我是专栏作者
+                    </Checkbox>
+                  </div>
+                  {!isSelectAuthor ? (
+                    <div className={styles['login-title']}>验证码登录</div>
+                  ) : (
+                    <Tabs defaultActiveKey={'1'} items={items} onChange={onChange} style={{ marginTop: 10 }} />
+                  )}
+                </>
+              ) : (
+                <div className={styles['login-title']}>验证码登录</div>
+              )}
+            </>
+          ) : (
+            <div className={styles['login-title']}>绑定手机号</div>
+          )}
+          <div className={styles['login-columncenter-content']}>
+            <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!'
+                    }
+                  ]}
+                >
+                  <div className={styles['input-content']}>
                     <Select
                       defaultValue={phoneArea}
+                      className="select-input"
                       onChange={value => {
                         setPhoneArea(value)
                       }}
@@ -124,49 +213,112 @@ const Login2p: React.FC = () => {
                         </Option>
                       ))}
                     </Select>
-                  }
-                  placeholder="请输入手机号"
-                />
+                    <Input
+                      className="mobile-phone-input"
+                      prefix={<LoginMobile style={{ width: 15, marginRight: 10 }} />}
+                      placeholder="请输入手机号"
+                    />
+                  </div>
+                </Form.Item>
               </Form.Item>
-            </Form.Item>
-            <Form.Item style={{ marginBottom: '5px' }}>
-              <p style={{ lineHeight: '24px', fontSize: '16px' }}>验证码</p>
-              <Form.Item>
-                <Form.Item
-                  name={'code'}
-                  style={{ width: '65%', display: 'inline-block' }}
-                  rules={[{ required: true, message: 'Please input Captcha!' }]}
-                >
-                  <Input placeholder="请输入验证码" />
+              {tabsAct === '1' || !isSelectAuthor ? (
+                <Form.Item style={{ marginBottom: '5px' }}>
+                  {/* <p style={{ lineHeight: '24px', fontSize: '16px' }}>验证码</p> */}
+                  <Form.Item name={'code'} rules={[{ required: true, message: 'Please input Captcha!' }]}>
+                    <div className={styles['input-content']}>
+                      <Input
+                        className="mobile-code-input"
+                        prefix={
+                          <SafetyCertificateOutlined style={{ color: '#E37318', fontSize: 20, marginRight: 10 }} />
+                        }
+                        placeholder="请输入验证码"
+                      />
+                      <Button
+                        onClick={getVCodeData}
+                        style={{ width: 160, marginLeft: '12px' }}
+                        loading={loadingVCode}
+                        disabled={isDisabled}
+                        className="custom-style-bottom-code"
+                      >
+                        {captchaBtnText}
+                      </Button>
+                    </div>
+                  </Form.Item>
+                </Form.Item>
+              ) : (
+                <Form.Item style={{ marginBottom: '-19px' }}>
+                  {/* <p style={{ lineHeight: '24px', fontSize: '16px' }}>验证码</p> */}
+                  <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: 40 }}
+                          prefix={<LoginPassword style={{ width: 20, marginRight: 10 }} />}
+                          placeholder="请输入密码"
+                        />
+                      </Form.Item>
+                    </div>
+                    <div className={styles['input-set-password-forget']}>
+                      <span onClick={setUpPasswordClickHandlern}>设置密码/忘记密码</span>
+                    </div>
+                  </Form.Item>
                 </Form.Item>
-                <Button onClick={getVCodeData} style={{ width: '35%' }} loading={loadingVCode} disabled={isDisabled}>
-                  {captchaBtnText}
+              )}
+              <Form.Item>
+                <Button type="primary" block htmlType="submit" className="custom-style-bottom">
+                  登录
                 </Button>
               </Form.Item>
-            </Form.Item>
-            <Form.Item>
-              <Button type="primary" block htmlType="submit">
-                验证码登录
-              </Button>
-            </Form.Item>
-            <Form.Item style={{ textAlign: 'center' }}>
-              <Button
-                icon={<WechatOutlined style={{ color: '#60C84D' }} />}
-                onClick={() =>
-                  login2p.loginWechatLaunch(
-                    `${process.env.REACT_APP_URL}login2p/wechat?next=${next ? next : encodeURIComponent('/')}`
-                  )
-                }
-                shape="circle"
-                size="large"
-                style={{ paddingTop: '7px' }}
-              ></Button>
-            </Form.Item>
-          </Form>
-        </Card>
-      </Col>
-      <Col {...colSpanBlank} />
-    </Row>
+              {!wechatToken && (
+                <>
+                  <div className={styles['custom-style-divider']}>
+                    <Divider className={styles['divider-line']}>
+                      <span className={styles['divider-text']}>其他登录方式</span>
+                    </Divider>
+                  </div>
+
+                  <Form.Item style={{ textAlign: 'center' }}>
+                    <Button
+                      icon={<WechatOutlined style={{ color: '#60C84D', fontSize: 38 }} />}
+                      onClick={() =>
+                        login2p.loginWechatLaunch(
+                          `${process.env.REACT_APP_URL}login2p/wechat?next=${next ? next : encodeURIComponent('/')}`
+                        )
+                      }
+                      shape="circle"
+                      size="large"
+                      style={{ paddingTop: '7px', width: 64, height: 64 }}
+                    ></Button>
+                  </Form.Item>
+                  <div className="buymodel-checkbox-wrapper m-t-md">
+                    <span>登录即代表您阅读并同意</span>
+                    <span className="buymodel-text-orange" onClick={handleOpenWord.bind(this, '/material/license')}>
+                      《研选平台服务协议》
+                    </span>
+                    <span className="buymodel-text-orange" onClick={handleOpenWord.bind(this, '/material/policy')}>
+                      《研选平台隐私政策》
+                    </span>
+                  </div>
+                </>
+              )}
+            </Form>
+          </div>
+        </div>
+      </div>
+      <SetUpPassword
+        visible={isSetPassword}
+        isBindMobile={isBindMobile}
+        encryptionPhone={encryptionPhone}
+        phoneAreaCode={phoneArea}
+        onCancelPassword={() => {
+          setIsSetPassword(false)
+        }}
+      />
+    </>
   )
 }
 

+ 82 - 18
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)
@@ -43,7 +59,8 @@ const Login2pProvider: React.FC<ContextProviderProps> = ({ children }: ContextPr
     if (inviteCode) {
       sessionStorage.setItem('invite_code', inviteCode)
     }
-    history.push(next)
+    window.location.href = next
+    // history.push(next)
   }
 
   const logout = (): void => {
@@ -54,6 +71,7 @@ const Login2pProvider: React.FC<ContextProviderProps> = ({ children }: ContextPr
     history.go(0)
   }
   const loginWechatLaunch = (redirectURI: string) => {
+    const windowFeatures = 'left=500,top3100,width=500,height=500'
     if (isWechat) {
       window.location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${
         process.env.REACT_APP_WECHART_APPID_NEW
@@ -61,11 +79,24 @@ const Login2pProvider: React.FC<ContextProviderProps> = ({ children }: ContextPr
         redirectURI
       )}&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect`
     } else {
-      window.location.href = `https://open.weixin.qq.com/connect/qrconnect?appid=${
-        process.env.REACT_APP_WECHAT_OPEN_APPID_NEW
-      }&redirect_uri=${encodeURIComponent(
-        redirectURI
-      )}&response_type=code&scope=snsapi_login&state=STATE#wechat_redirect`
+      // 创建 BroadcastChannel 实例
+      const channel = new BroadcastChannel('myChannel')
+
+      // 监听广播通道的消息
+      channel.addEventListener('message', event => {
+        loginWechat(event.data.code, decodeURIComponent(event.data.next ? event.data.next : '/')).then(r =>
+          console.log(r)
+        )
+      })
+      window.open(
+        `https://open.weixin.qq.com/connect/qrconnect?appid=${
+          process.env.REACT_APP_WECHAT_OPEN_APPID_NEW
+        }&redirect_uri=${encodeURIComponent(
+          redirectURI
+        )}&response_type=code&scope=snsapi_login&state=STATE#wechat_redirect`,
+        'mozillaWindow',
+        windowFeatures
+      )
     }
   }
   const loginWechat = async (code: string, next: string) =>
@@ -79,19 +110,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(data.data.Msg)
+    }
+  }
 
+  // 初始密码登录
+  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',
@@ -130,6 +193,7 @@ const Login2pProvider: React.FC<ContextProviderProps> = ({ children }: ContextPr
         loginWechatLaunch: loginWechatLaunch,
         loginWechat: loginWechat,
         login2pByCode: login2pByCode,
+        initPassword: initPassword,
         getUserDetail: getUserDetail,
         getUserDetailLoading: getUserDetailLoading,
         loginEnd: loginEnd

+ 10 - 1
src/Login2p/Login2pWechatLanding.tsx

@@ -11,8 +11,17 @@ const Login2pWechatLanding: React.FC = () => {
   console.log(next)
   const login2p = useLogin2p()
   React.useEffect(() => {
+    // 创建 BroadcastChannel 实例
+
+    const channel = new BroadcastChannel('myChannel')
+
     if (code) {
-      login2p.loginWechat(code, decodeURIComponent(next ? next : '/')).then(r => console.log(r))
+      // 向广播通道发送消息
+      channel.postMessage({ code, next })
+      // 关闭频道
+      channel.close()
+      window.close()
+      // login2p.loginWechat(code, decodeURIComponent(next ? next : '/')).then(r => console.log(r))
     }
   }, [])
 

+ 202 - 0
src/Login2p/components/SetUpPassword.tsx

@@ -0,0 +1,202 @@
+import React, { useEffect, useState } from 'react'
+import useRequest from '@ahooksjs/use-request'
+import { useHistory, useLocation } from 'react-router-dom'
+import { Col, Row, Divider, Input, Button, Select, Form, message, Modal, TabsProps, Tabs } from 'antd'
+
+import styles from 'styles/NewPage.module.scss'
+import { SafetyCertificateOutlined, ExclamationCircleFilled } from '@ant-design/icons'
+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
+  isBindMobile: number
+  encryptionPhone: string
+  phoneAreaCode: number
+  onCancelPassword: () => void
+  onRefresh?: () => void
+}
+
+/**
+ * 编辑专栏信息
+ */
+const SetUpPassword: React.FC<ISetUpPasswordProps> = props => {
+  const { Option } = Select
+  const { visible, isBindMobile, encryptionPhone, phoneAreaCode, onCancelPassword, onRefresh } = props
+  const [form] = Form.useForm()
+  const login2p = useLogin2p()
+  const next = new URLSearchParams(useLocation().search).get('next')
+  const wechatToken = new URLSearchParams(useLocation().search).get('smtoken')
+  const [phoneArea, setPhoneArea] = useState<number>(86)
+
+  const [isDisabled, setIsDisabled] = useState<boolean>(false)
+  const [captchaBtnText, setCaptchaBtnText] = useState<string>('获取验证码')
+
+  const phoneAreaList = [
+    { id: 86, optionName: '中国(+86)', regText: /^1\d{10}$/ },
+    { id: 853, optionName: '中国澳门(+853)', regText: /^6\d{7}$/ },
+    { 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: 44, optionName: '英国(+44)', regText: /^\+?[0-9,\-,\s]{5,20}$/ },
+    { id: 886, optionName: '中国台湾(+886)', regText: /^[0][9]\d{8}$/ }
+  ]
+  const { run: getLoginCode, loading: loadingVCode } = useRequest(Login2pService.getVCode, {
+    manual: true,
+    formatResult: response => response.data,
+    onSuccess: result => message.success('验证码请求成功!'),
+    onError: e => message.error('验证码请求失败~')
+  })
+
+  // 点击了登录
+  const handleLoginegByCode = (value: any) => {
+    // 至少 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')) && !encryptionPhone) {
+      message.warning('格式错误,请输入正确的手机号码')
+      return
+    }
+    setIsDisabled(true)
+    let countdown = 60
+    const captchaCountdown = setInterval(() => {
+      setCaptchaBtnText(`${countdown--}s`)
+    }, 1000)
+    setTimeout(() => {
+      setIsDisabled(false)
+      setCaptchaBtnText('获取验证码')
+      clearInterval(captchaCountdown)
+    }, 60000)
+    getLoginCode(
+      phoneArea.toString() || phoneAreaCode.toString(),
+      form.getFieldValue('phone_number') || encryptionPhone
+    )
+  }
+  // 关闭弹框事件
+  const closeModalHandler = () => {
+    onCancelPassword()
+  }
+  return (
+    <Modal open={visible} closable={false} destroyOnClose={true} maskClosable={false} footer={null} width={600}>
+      {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}>
+            {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, marginRight: 10 }} />} placeholder="请输入手机号" />
+                  </div>
+                </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']}>
+                  <Input
+                    prefix={<SafetyCertificateOutlined style={{ color: '#E37318', fontSize: 20, marginRight: 10 }} />}
+                    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: 40 }}
+                      prefix={<LoginPassword style={{ width: 20, marginRight: 10 }} />}
+                      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

+ 29 - 0
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(true)
+  }
   return (
     <div className={styles['newpage-header']} style={{ minWidth: isShowMobileMenu ? 'auto' : '1024px' }}>
       {isShowMobileMenu ? (
@@ -362,6 +372,11 @@ const NewPage: React.FC = props => {
                   <div className="person-line person-center-link" onClick={handleToMy}>
                     个人中心
                   </div>
+                  {login2p.userInfo && login2p.userInfo.IsAuthor && (
+                    <div className="person-line person-center-link" onClick={handlePassword}>
+                      {login2p.userInfo?.IsSetPassword ? '修改登录密码' : '设置登录密码'}
+                    </div>
+                  )}
                   <div className="person-line person-exit" onClick={login2p.logout}>
                     退出
                   </div>
@@ -378,6 +393,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 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

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

@@ -0,0 +1,111 @@
+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 = () => {
+    form.resetFields()
+    onCancelPassword()
+  }
+  return (
+    <Modal open={visible} closable={false} destroyOnClose={true} maskClosable={false} footer={null} width={600}>
+      <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: 40 }}
+                      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: 40 }}
+                      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={600}>
+      <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, marginRight: 10 }} />}
+                    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: 40 }}
+                      prefix={<LoginPassword style={{ width: 20, marginRight: 10 }} />}
+                      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

binární
src/assets/login_bg.png


binární
src/assets/login_bg_phone.png


+ 3 - 0
src/assets/login_mobile.svg

@@ -0,0 +1,3 @@
+<svg width="18" height="24" viewBox="0 0 18 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M9.00082 21.7502C9.60072 21.7502 10.1257 21.2251 10.1257 20.625C10.1257 20.0248 9.60014 19.5 9.00082 19.5C8.4015 19.5 7.87562 20.0251 7.87562 20.625C7.87562 21.2249 8.40063 21.7502 9.00082 21.7502ZM14.6251 0H3.37507C1.72513 0 0.375 1.34984 0.375 3.00007V20.9999C0.375 22.6499 1.72513 24 3.37507 24H14.6251C16.2751 24 17.6252 22.6499 17.6252 20.9999V3.00007C17.6252 1.34984 16.2751 0 14.6251 0ZM16.125 20.9999C16.1239 21.3973 15.9656 21.7782 15.6845 22.0591C15.4034 22.3401 15.0225 22.4983 14.6251 22.4992H3.37507C2.97756 22.4983 2.59659 22.34 2.3155 22.0589C2.03442 21.7778 1.8761 21.3969 1.87518 20.9993V18.7501H16.125V20.9999ZM16.125 17.2499H1.87518V5.24991H16.125V17.2499ZM16.125 3.74857H1.87518V3.00007C1.87602 2.60251 2.03431 2.22146 2.3154 1.94031C2.59649 1.65917 2.97751 1.50081 3.37507 1.49989H14.6251C15.0227 1.50081 15.4037 1.65917 15.6848 1.94031C15.9659 2.22146 16.1242 2.60251 16.125 3.00007V3.74857Z" fill="#E37318"/>
+</svg>

+ 4 - 0
src/assets/login_passwode.svg

@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M18.5179 8.16094H17.7585V6.04922C17.7585 2.97656 15.1757 0.480469 12.0046 0.480469C8.82178 0.480469 6.23897 2.97422 6.23897 6.04922V8.16094H5.47959C3.52021 8.16094 1.91943 9.74531 1.91943 11.7117V19.9852C1.91943 21.9375 3.5085 23.5219 5.47959 23.5219H18.5179C20.4772 23.5219 22.078 21.9375 22.078 19.9852V11.6953C22.0804 9.74297 20.4913 8.16094 18.5179 8.16094ZM8.16084 6.04922C8.16084 4.03828 9.88584 2.4 12.0069 2.4C14.121 2.4 15.8413 4.03594 15.8413 6.04922V8.01328H8.16084V6.04922ZM20.1608 19.9828C20.1608 20.8734 19.4249 21.6 18.5202 21.6H5.48193C4.57725 21.6 3.84131 20.8758 3.84131 19.9828V11.7094C3.84131 10.8117 4.57725 10.0805 5.48193 10.0805H18.5202C19.4249 10.0805 20.1608 10.8047 20.1608 11.6977V19.9828Z" fill="#E37318"/>
+<path d="M12 12.9609C11.4727 12.9609 11.0391 13.3922 11.0391 13.9219V17.7609C11.0391 18.2883 11.4703 18.7219 12 18.7219C12.5273 18.7219 12.9609 18.2906 12.9609 17.7609V13.9195C12.9609 13.3922 12.5273 12.9609 12 12.9609Z" fill="#E37318"/>
+</svg>

+ 13 - 1
src/index.tsx

@@ -17,7 +17,19 @@ ReactDOM.render(
   <React.StrictMode>
     <React.Suspense fallback={<Spin />}>
       <StyleProvider hashPriority="high" transformers={[legacyLogicalPropertiesTransformer]}>
-        <ConfigProvider locale={zhCN}>
+        <ConfigProvider
+          locale={zhCN}
+          theme={{
+            token: {
+              // Seed Token,影响范围大
+              colorPrimary: '#f1a84a',
+              borderRadius: 2
+
+              // 派生变量,影响范围小
+              // colorBgContainer: '#ffffff'
+            }
+          }}
+        >
           <Router basename={process.env.REACT_APP_BASE_NAME}>
             <Login2pProvider>
               <MediaProvider>

+ 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


+ 248 - 18
src/styles/NewPage.module.scss

@@ -316,17 +316,17 @@
           }
         }
       }
-      .person-vip-card-wrapper{
+      .person-vip-card-wrapper {
         width: 222px;
         height: 87px;
         margin: 10px auto 0 auto;
         position: relative;
-        .person-vip-card-img{
+        .person-vip-card-img {
           width: 100%;
           height: 100%;
         }
-        .person-vip-timetext{
-          color:#F1A84A;
+        .person-vip-timetext {
+          color: #f1a84a;
           line-height: 20px;
           position: absolute;
           bottom: 15px;
@@ -364,14 +364,11 @@
   color: #ffffff;
 }
 .login-title {
-  color: rgba(0, 0, 0, 0.85);
-  font-weight: 600;
-  font-size: 20px;
-  line-height: 32px;
-  overflow: hidden;
-  white-space: nowrap;
-  text-overflow: ellipsis;
-  padding: 16px 24px 16px 0;
+  color: #faa12f;
+  font-weight: 500;
+  font-size: 16px;
+  line-height: 28px;
+  padding: 12px 0 26px 0;
 }
 .home-chart-wrapper {
   margin: 4vh auto;
@@ -423,6 +420,190 @@
   }
 }
 
+.custom-style-divider {
+  width: 224px;
+  margin: 0 auto;
+  color: #999999;
+  .divider-text {
+    color: #999999 !important;
+  }
+  .divider-line::before,
+  .divider-line::after {
+    border-top: 1px solid #999999 !important;
+  }
+}
+.set-up-password-text {
+  display: flex;
+  align-items: center;
+  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: 600px;
+  padding: 48px 72px 48px;
+  border-radius: 16px;
+  background-color: #fff;
+  margin: 0 auto;
+  box-sizing: border-box;
+}
+
+.login-content-bg-img {
+  width: 100%;
+  height: 100vh;
+  background: url('../assets/login_bg.png') no-repeat 100% 100%;
+  box-sizing: border-box;
+
+  .login-log-img {
+    display: block;
+    width: 408px;
+    height: 90px;
+    margin: 0 auto;
+    padding: 70px 20px 30px 0;
+  }
+  :global {
+    .ant-tabs-top > .ant-tabs-nav::before {
+      border: none;
+    }
+    .ant-tabs .ant-tabs-tab {
+      font-size: 16px;
+      color: #ffbd66;
+    }
+    .ant-tabs .ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn {
+      color: #faa12f;
+    }
+    .ant-tabs .ant-tabs-tab:hover {
+      color: #faa12f;
+    }
+    .ant-tabs .ant-tabs-ink-bar {
+      background: #faa12f;
+    }
+    .ant-tabs-top > .ant-tabs-nav::before {
+      border: none;
+    }
+    .ant-btn.ant-btn-circle.ant-btn-lg  {
+      border: 1px solid #DCDFE6;
+    }
+    .ant-checkbox-wrapper {
+      font-size: 16px;
+      color: #999999;
+    }
+    .ant-select .ant-select-arrow {
+      color: #333;
+      font-size: 14px;
+    }
+    .login-columncenter-content {
+      padding: 0 20px;
+      .columncenter-content-item {
+        border-bottom: 1px solid #f8f8fa;
+        padding: 20px 0;
+        &:last-child {
+          border-bottom: none;
+        }
+        .columncenter-title {
+          font-size: 16px;
+          line-height: 22px;
+          font-weight: bold;
+          color: #000000;
+          cursor: pointer;
+          &:hover {
+            color: #faa12f;
+          }
+        }
+      }
+      .detele-btn {
+        background: #fff0ed;
+        color: #d54941;
+      }
+      .update-btn {
+        background: #faa12f;
+        color: #ffffff;
+      }
+      .cancel-btn {
+        background: #e5efff;
+        color: #faa12f;
+      }
+    }
+    .select-input {
+      width: 160px !important;
+      height: 40px;
+      margin-right: 15px;
+    }
+    .custom-style-bottom,
+    .custom-style-bottom-code {
+      color: #fff;
+      background-color: #faa12f;
+      height: 40px;
+      border-radius: 4px;
+      box-shadow: none;
+    }
+    .ant-btn-default {
+      border-color: rgba(0, 0, 0, 0);
+    }
+    .password-btn-box {
+      display: flex;
+      justify-content: space-between;
+      .ant-btn-default {
+        border-color: #fff6e8;
+        border: 1px solid #faa12f;
+        color: #faa12f;
+      }
+    }
+    .password-btn {
+      width: 200px !important;
+      height: 40px;
+    }
+    .ant-select-selector,
+    .ant-input-outlined {
+      border: 1px solid #faa12f !important;
+      border-radius: 4px !important;
+    }
+    .buymodel-checkbox-wrapper {
+      text-align: center;
+    }
+    .buymodel-checkbox-wrapper span {
+      vertical-align: middle;
+    }
+    .buymodel-text-orange {
+      color: #faa12f;
+      cursor: pointer;
+    }
+    .password-dlg-title {
+      font-size: 22px;
+      font-weight: 600;
+      line-height: 28px;
+      color: #faa12f;
+      margin-bottom: 20px;
+    }
+  }
+}
+.input-set-password-forget {
+  text-align: right;
+  margin-top: -15px;
+  margin-bottom: 10px;
+  color: #999;
+  span {
+    cursor: pointer;
+  }
+}
+.login-content-box-set-up {
+  height: 100%;
+  background: none;
+  padding: 10px 52px 10px;
+}
+.input-content {
+  display: flex;
+  justify-content: space-between;
+}
 @media screen and (max-width: 1280px) {
   .newpage-header {
     padding: 0 30px !important;
@@ -457,10 +638,59 @@
   .newindex-content {
     padding: 0px;
   }
-  // .newpage-web-header {
-  //   display: none !important;
-  // }
-  // .newpage-mobile-header {
-  //   display: block;
-  // }
+  .login-content-bg-img {
+    width: 100%;
+    height: 100vh;
+    background: url('../assets/login_bg_phone.png') no-repeat 100% 100%;
+    background-size: cover;
+    :global {
+      .custom-style-bottom,
+      .mobile-phone-input,
+      .custom-style-bottom-code {
+        height: 44px !important;
+      }
+      .ant-input-outlined ,.ant-select-selector{
+        border:none !important;
+        border-radius:0 !important;
+      }
+      .custom-style-bottom-code {
+        background-color: #fff;
+        color: #faa12f ;
+        margin-left: 0 !important;
+        border-radius: 0;
+      }
+      .select-input {
+        width: 160px !important;
+        height: 44px !important;
+        margin-right: 0px !important;
+      }
+
+
+      .buymodel-checkbox-wrapper {
+        text-align: left !important;
+        border-radius: 4px 0 0 4px !important;
+        :nth-child(1) {
+          color: #999;
+        }
+      }
+    }
+    .input-content {
+      border-radius: 4px;
+      border: 1px solid #faa12f;
+      overflow: hidden;
+    }
+    .login-title {
+      padding: 0 0 10px 0;
+    }
+  }
+  .login-content-box-mobile {
+    width: 100%;
+    height: 100%;
+    padding: 220px 30px 0;
+    height: auto;
+    box-sizing: border-box;
+  }
+  .custom-style-divider {
+    padding-top: 130px;
+  }
 }

+ 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"]
 }