Selaa lähdekoodia

Merge branch 'xudh'

PigTech 2 vuotta sitten
vanhempi
sitoutus
b8d5f69bfc

+ 3 - 0
src/api/config.js

@@ -29,6 +29,8 @@ export const APP_REGISTER=BASE_PREFIX+"register";
 export const APP_LOGOUT=BASE_PREFIX+"logout";
 //changePass
 export const APP_CHANGE_PASS=BASE_PREFIX+"changePass";
+//forgetPass
+export const APP_FORGET_PASS=BASE_PREFIX+"forgetPass";
 //changeInfo
 export const APP_CHANGE_INFO=BASE_PREFIX+"changeInfo";
 //增加/game/changePhone /game/changeEmail
@@ -49,6 +51,7 @@ export const APP_CHANGE_PAY_PASS=BASE_PREFIX+"changePayPass";
 export const APP_WITHDRAW_RECORD=BASE_PREFIX+"withDraw/record";
 export const APP_WITHDRAW_SET_INFO=BASE_PREFIX+"withDraw/withDrawInfo";
 export const APP_WITHDRAW_PAYOUT=BASE_PREFIX+"withDraw/payout";
+export const APP_FORGET_PAY_PASS=BASE_PREFIX+"forgetPayPass";
 //withdraw
 //userinfo
 export const APP_GAME_INFO=BASE_PREFIX+"info";

+ 38 - 1
src/features/api.js

@@ -17,7 +17,7 @@ import {
     APP_FAVO_ADD,
     APP_FAVO_GET,
     APP_FAVO_REMOVE,
-    APP_FIRSTPAY_CONFIG,
+    APP_FIRSTPAY_CONFIG, APP_FORGET_PASS, APP_FORGET_PAY_PASS,
     APP_GAME_ACTIVITY,
     APP_GAME_CARD_INFO,
     APP_GAME_INFO,
@@ -855,4 +855,41 @@ export const reqProtectGet=createAsyncThunk(
         return response;
     }
 )
+//withdraw forget pass
+export const reqForgetPayPass=createAsyncThunk(
+    APP_FORGET_PAY_PASS,
+    async (arg, thunkAPI) => {
+        // {phone,code,newpassword,renewpassword}
+        let params={
+            url:APP_FORGET_PAY_PASS,
+            data:{
+                phone:arg.phone,
+                code:arg.code,
+                newpassword:arg.newpassword,
+                renewpassword:arg.renewpassword,
+            }
+        }
+        const response=await Http.post(params);
+        thunkAPI.dispatch(updateUserInfo(response));
+        return response;
+    }
+)
+//forgetPass
+export const reqForgetPass=createAsyncThunk(
+    APP_FORGET_PASS,
+    async (arg,thunkAPI)=>{
+        let params={
+            url:APP_FORGET_PASS,
+            data:{
+                phone:arg.phone,
+                code:arg.code,
+                newpassword:arg.newpassword,
+                renewpassword:arg.renewpassword,
+                country:arg.country
+            }
+        }
+        const response = await Http.post(params);
+        return response;
+    },
+)
 //api

+ 16 - 3
src/features/mainSlice.js

@@ -12,13 +12,25 @@ import {
 } from "@constants/routes";
 import {REQ_STATUS} from "@api/config";
 import {
-    buildReqReducer, reqBindSMS, reqChangeEmail,
+    buildReqReducer,
+    reqBindSMS,
+    reqChangeEmail,
     reqChangeInfo,
-    reqChangePass, reqChangePhone, reqCustomLeaveMsg, reqCustomService, reqFavoGet, reqGameCardInfo, reqGameInfo,
+    reqChangePass,
+    reqChangePhone,
+    reqCustomLeaveMsg,
+    reqCustomService,
+    reqFavoGet, reqForgetPass,
+    reqForgetPayPass,
+    reqGameCardInfo,
+    reqGameInfo,
     reqGetRoutes,
     reqLogin,
     reqLogout,
-    reqRegister, reqSendCode, reqSMSLogin, reqUpdateFavoInfo
+    reqRegister,
+    reqSendCode,
+    reqSMSLogin,
+    reqUpdateFavoInfo
 } from "@features/api";
 import {toast} from 'react-toastify';
 import {getHttpMsg, localStorageGet, localStorageSet} from "@utils/helpers";
@@ -633,6 +645,7 @@ export const Main=createSlice({
         buildReqReducer(builder,reqSendCode,reducerSendCode,sendCodeStatus);
         buildReqReducer(builder,reqBindSMS,reducerBindSMS,bindSMSStatus);
         buildReqReducer(builder,reqSMSLogin,reducerSMSLogin,smsLoginStatus);
+        buildReqReducer(builder,reqForgetPass,reducerChangePass,changePassStatus);
     },
 })
 export const {logout,updateUserInfo,updateUserScore,updateAddScore,updateGameLike,updateCurrGame,updateServerKick,updateOpenPay,updateGameRoute,updateGameCardInfo} = Main.actions;

+ 6 - 1
src/features/paySlice.js

@@ -3,7 +3,7 @@ import {REQ_STATUS} from "@api/config";
 import {
     buildReqReducer,
     reqChangePayPass,
-    reqFirstPayConfig,
+    reqFirstPayConfig, reqForgetPayPass,
     reqGetAccountsPayInfo,
     reqPaymentOrder,
     reqPayOrderList,
@@ -235,6 +235,7 @@ export const Pay=createSlice({
             WithdrawInfo:REQ_STATUS.NONE,
             WithdrawSetInfo:REQ_STATUS.NONE,
             WithdrawPayout:REQ_STATUS.NONE,
+            ForgetPayPass:REQ_STATUS.NONE,
         },
         config:[],
         payconfig:null,
@@ -277,6 +278,7 @@ export const Pay=createSlice({
         buildReqReducer(builder,reqWithdrawRecord,reducerWithdrawRecord,withdrawRecordStatus);
         buildReqReducer(builder,reqWithdrawSetInfo,reducerWithdrawSetInfo,withdrawSetInfoStatus);
         buildReqReducer(builder,reqWithdrawPayout,reducerWithdrawPayout,withdrawPayoutStatus);
+        buildReqReducer(builder,reqForgetPayPass,reducerChangePayPass,changePayPassStatus);
     },
 })
 export const {updatePayFinish,setChangePayPassType} = Pay.actions;
@@ -378,6 +380,9 @@ export const getShowBonus=(state)=>{
 export const getPayConfigStatus=(state)=>{
     return state.pay.status.RechargeConfig;
 }
+export const getForgetPayPassStatus=(state)=>{
+    return state.pay.status.ForgetPayPass;
+}
 export default Pay.reducer;
 
 export const reqPayConfig=(dispatch,userInfo)=>{

+ 6 - 2
src/locales/languages/en_US.json

@@ -118,7 +118,9 @@
     },
     "otp_ph": "Verification Code",
     "guest_login": "Guest Login",
-    "btn_otp": "Send Code"
+    "btn_otp": "Send Code",
+    "forget_title": "Reset Password",
+    "back_to_login": "Back to"
   },
   "logout": {
     "success": "You have exited successfully."
@@ -340,7 +342,9 @@
         "btn_withdraw": "Withdraw Now",
         "err_not_same": "The password input is inconsistent, please re-enter it."
       },
-      "pass_box_title": "Enter the withdrawal password"
+      "pass_box_title": "Enter the withdrawal password",
+      "btn_forgot": "Forgot your password?",
+      "otp_title": "Phone verification code"
     },
     "record": {
       "title": "Withdrawal Record",

+ 6 - 2
src/locales/languages/es_MX.json

@@ -115,7 +115,9 @@
     },
     "otp_ph": "Código de verificación",
     "guest_login": "Acceso de usuario invitado",
-    "btn_otp": "Verificar"
+    "btn_otp": "Verificar",
+    "forget_title": "Restablecer la contraseña",
+    "back_to_login": "Volver a"
   },
   "logout": {
     "success": "Has salido exitosamente."
@@ -335,7 +337,9 @@
         "btn_withdraw": "Retirar Ahora",
         "err_not_same": "La contraseña ingresada es inconsistente, por favor ingrésala nuevamente."
       },
-      "pass_box_title": "Ingresa la contraseña de retiro"
+      "pass_box_title": "Ingresa la contraseña de retiro",
+      "btn_forgot": "¿Olvidaste tu contraseña?",
+      "otp_title": "Código de verificación del teléfono"
     },
     "record": {
       "title": "Registro de Retiro",

+ 6 - 2
src/locales/languages/pt_BR.json

@@ -115,7 +115,9 @@
     },
     "otp_ph": "Código de verificação",
     "guest_login": "Login como convidado",
-    "btn_otp": "Verificar"
+    "btn_otp": "Verificar",
+    "forget_title": "Redefinir senha",
+    "back_to_login": "Voltar ao"
   },
   "logout": {
     "success": "Você saiu com sucesso."
@@ -335,7 +337,9 @@
         "btn_withdraw": "Retirar Agora",
         "err_not_same": "A senha inserida é inconsistente, por favor insira novamente."
       },
-      "pass_box_title": "Digite a senha de retirada"
+      "pass_box_title": "Digite a senha de retirada",
+      "btn_forgot": "Esqueceu sua senha?",
+      "otp_title": "Código de verificação do telefone"
     },
     "record": {
       "title": "Registro de Retirada",

+ 6 - 2
src/locales/languages/ru_RU.json

@@ -115,7 +115,9 @@
     },
     "otp_ph": "Проверочный код",
     "guest_login": "Гостевой вход",
-    "btn_otp": "Отправить"
+    "btn_otp": "Отправить",
+    "forget_title": "Сброс пароля",
+    "back_to_login": "Вернуться к"
   },
   "logout": {
     "success": "Вход успешно выполнен."
@@ -335,7 +337,9 @@
         "btn_withdraw": "Вывести сейчас",
         "err_not_same": "Пароли не совпадают, пожалуйста, введите снова."
       },
-      "pass_box_title": "Введите пароль для вывода"
+      "pass_box_title": "Введите пароль для вывода",
+      "btn_forgot": "Забыли пароль?",
+      "otp_title": "Код подтверждения телефона"
     },
     "record": {
       "title": "История выводов",

+ 6 - 2
src/locales/languages/zh_TW.json

@@ -115,7 +115,9 @@
     },
     "otp_ph": "驗證碼",
     "guest_login": "訪客登入",
-    "btn_otp": "驗證碼"
+    "btn_otp": "驗證碼",
+    "forget_title": "重新設密碼",
+    "back_to_login": "返回"
   },
   "logout": {
     "success": "您已成功退出。"
@@ -335,7 +337,9 @@
         "btn_withdraw": "立即提現",
         "err_not_same": "輸入的密碼不一致,請重新輸入。"
       },
-      "pass_box_title": "輸入提現密碼"
+      "pass_box_title": "輸入提現密碼",
+      "btn_forgot": "忘記密碼了嗎?",
+      "otp_title": "手機驗證碼"
     },
     "record": {
       "title": "提現記錄",

+ 149 - 11
src/widgets/LoginForm/index.jsx

@@ -19,12 +19,19 @@ import PhoneInput from "@components/UInput/PhoneInput";
 import {animated,useSpring} from "@react-spring/web";
 import {toast} from 'react-toastify';
 import {useDispatch, useSelector} from "react-redux";
-import {getLoginStatus, getRegStatus, getSendCodeStatus, getSendCodeTS, getSMSLoginStatus} from "@features/mainSlice";
+import {
+    getChangePassStatus,
+    getLoginStatus,
+    getRegStatus,
+    getSendCodeStatus,
+    getSendCodeTS,
+    getSMSLoginStatus
+} from "@features/mainSlice";
 import {useLocation} from "react-router";
 import {useEffect} from 'react';
 import SmallLoading from "@components/SmallLoading";
 import {REQ_STATUS} from "@api/config";
-import {reqLogin, reqRegister, reqSendCode, reqSMSLogin} from "@features/api";
+import {reqForgetPass, reqLogin, reqRegister, reqSendCode, reqSMSLogin} from "@features/api";
 import {ROUTE_PATH} from "@constants/routes";
 import {APP_THEME} from "@constants/app";
 import {validateCountryPhone} from "@utils/helpers";
@@ -33,11 +40,13 @@ import {CircularProgress} from "@mui/material";
 const i18n=global.i18n;
 const TYPE_LOGIN="login";
 const TYPE_REGISTER="register";
+const TYPE_FORGET_PASS="forgetPassword";
 
 const LOGIN_TYPE_PASS=0;
 const LOGIN_TYPE_OTP=1;
 const objInfo={
-    cdTimer:null
+    cdTimer:null,
+    inSubmit:false
 }
 
 const LoginForm = ({close}) => {
@@ -48,6 +57,7 @@ const LoginForm = ({close}) => {
     const logourl=(theme===APP_THEME.DARK?logo_dark:logo_light);
     const loginStatus=useSelector(getLoginStatus);
     const regStatus=useSelector(getRegStatus);
+    const forgetStatus=useSelector(getChangePassStatus);
     const dispatch = useDispatch();
     const navigate=useNavigate();
     const [loginType,setLoginType]=useState(LOGIN_TYPE_PASS);
@@ -60,7 +70,9 @@ const LoginForm = ({close}) => {
             repassword:"",
             lang:"",
             code:"",
-            phone:""
+            phone:"",
+            newpassword:"",
+            renewpassword:""
         }
     });
     const {pathname,hash} = useLocation();
@@ -104,6 +116,7 @@ const LoginForm = ({close}) => {
        return ()=>{
            global.authLockPath=null;
            clearTimer();
+           objInfo.inSubmit=false;
        }
     },[])
     useEffect(() => {
@@ -133,6 +146,7 @@ const LoginForm = ({close}) => {
     }
     const onCallback=(code)=>{
         // global.authLockPath=null;
+        objInfo.inSubmit=false;
         if (code==200){
             if (global.authLockPath!=null){
                 let path=global.authLockPath;
@@ -146,15 +160,27 @@ const LoginForm = ({close}) => {
     }
     const onSubmitLogin=(data)=>{
         if (loginStatus===REQ_STATUS.LOADING)return;
+        if (objInfo.inSubmit)return;
         data.country=country;
         data.callback=onCallback;
         dispatch(reqLogin(data));
+        objInfo.inSubmit=true;
     }
     const onSubmitReg=(data)=>{
         if (regStatus===REQ_STATUS.LOADING)return;
+        if (objInfo.inSubmit)return;
         data.country=country;
         data.callback=onCallback;
         dispatch(reqRegister(data));
+        objInfo.inSubmit=true;
+    }
+    const onSubmitForget=(data)=>{
+        if (forgetStatus===REQ_STATUS.LOADING)return;
+        if (objInfo.inSubmit)return;
+        data.country=country;
+        data.callback=onCallback;
+        dispatch(reqForgetPass(data));
+        objInfo.inSubmit=true;
     }
     const validatePhone=(value)=>{
         return validateCountryPhone(country,value);
@@ -226,6 +252,13 @@ const LoginForm = ({close}) => {
                                     innerRef={ref}/>
                             )}
                 />
+                <div className={styles.l_forgot}>
+                    <button type={"button"} className={styles.forgot_btn} onClick={()=>{
+                        setShowType(TYPE_FORGET_PASS);
+                    }}>
+                        {i18n.t("login.forgot")}
+                    </button>
+                </div>
                 <button className={"button-green"}>
                     {
                         loginStatus === REQ_STATUS.LOADING ? renderLoading() : i18n.t("login.sign_in_btn")
@@ -396,13 +429,118 @@ const LoginForm = ({close}) => {
             </Spring>
         )
     }
-    const renderForm=()=>{
-        let form=(<></>);
-        if (showType===TYPE_LOGIN){
-            form=renderLoginForm();
-        }
-        else if (showType===TYPE_REGISTER){
-            form=renderRegisterForm();
+    const renderForgetForm=()=>{
+        return (
+            <Spring type="slideUp">
+                <div className={styles.l_title}>{i18n.t("login.forget_title")}</div>
+                <form key={"forget"} autoComplete="off" className={styles.l_form} onSubmit={handleSubmit((data) => {
+                    onSubmitForget(data);
+                })}>
+                    <Controller control={control}
+                                name="phone"
+                                rules={{required: true}}
+                                render={({field: {ref, onChange, value}, fieldState: {error}}) => {
+                                    let message = error ? i18n.t("form_errors.phone_" + country) : "";
+                                    return (<PhoneInput
+                                        key={"phone"}
+                                        className={classNames({'input-control--error': error})}
+                                        value={value}
+                                        message={message}
+                                        onChange={e => onChange(e.target.value)}
+                                        onChangeCountry={(country) => {
+                                            setCountry(country);
+                                        }}
+                                        placeholder={i18n.t('login.phone_ph')}
+                                        innerRef={ref}/>)
+                                }}
+                    />
+                    <Controller control={control}
+                                name="newpassword"
+                                rules={{required: true, minLength: 6}}
+                                render={({field: {ref, onChange, value}, fieldState: {error}}) => (
+                                    <PasswordInput
+                                        className={classNames({'input-control--error': error})}
+                                        value={value}
+                                        message={error ? i18n.t("form_errors.pass") : ''}
+                                        onChange={e => onChange(e.target.value)}
+                                        placeholder={i18n.t('user.password.newpassword')}
+                                        innerRef={ref}/>
+                                )}
+                    />
+                    <Controller control={control}
+                                name="renewpassword"
+                                rules={{required: true, minLength: 6}}
+                                render={({field: {ref, onChange, value}, fieldState: {error}}) => (
+                                    <PasswordInput
+                                        className={classNames({'input-control--error': error})}
+                                        value={value}
+                                        message={error ? i18n.t("form_errors.pass") : ''}
+                                        onChange={e => onChange(e.target.value)}
+                                        placeholder={i18n.t('user.password.renewpassword')}
+                                        innerRef={ref}/>
+                                )}
+                    />
+                    <Controller control={control}
+                                name="code"
+                                rules={{required: true}}
+                                render={({field: {ref, onChange, value}, fieldState: {error}}) => {
+                                    return (
+                                        <div className={`d-flex flex-column g-6`}>
+                                            <div
+                                                className={`input-control ${classNames({'input-control--error': error})}`}>
+                                                <input
+                                                    type={'text'}
+                                                    autoComplete="off"
+                                                    ref={ref}
+                                                    onChange={e => onChange(e.target.value)}
+                                                    placeholder={i18n.t('login.otp_ph')}
+                                                />
+                                                <button type={"button"}
+                                                        className={`${styles.otp_btn} ${otpCD > 0 ? styles.cd_timer : ''}`}
+                                                        onClick={onSendCode}>
+                                                    {
+                                                        otpStatus === REQ_STATUS.LOADING ? (
+                                                            <CircularProgress color="success" size={22}
+                                                                              thickness={5}/>) : (
+                                                            <>
+                                                                {
+                                                                    otpCD <= 0 ? i18n.t('login.btn_otp') : otpCD + "S"
+                                                                }
+                                                            </>
+                                                        )
+                                                    }
+                                                </button>
+                                            </div>
+                                        </div>
+                                    )
+                                }}
+                    />
+                    <button className={"button-green"}>
+                        {
+                            forgetStatus === REQ_STATUS.LOADING ? (
+                                <SmallLoading fill={"#fff"}/>) : i18n.t("login.forget_title")
+                        }
+                    </button>
+                    <div className={styles.l_reg_new}>
+                        <div>{i18n.t("login.back_to_login")}</div>
+                        <div className={styles.reg_btn} onClick={() => {
+                            setShowType(TYPE_LOGIN);
+                        }}>
+                            {i18n.t("login.sign_in_btn")}
+                        </div>
+                    </div>
+                </form>
+            </Spring>
+        )
+    }
+    const renderForm = () => {
+        let form = (<></>);
+        if (showType === TYPE_LOGIN) {
+            form = renderLoginForm();
+        } else if (showType === TYPE_REGISTER) {
+            form = renderRegisterForm();
+        } else if (showType === TYPE_FORGET_PASS) {
+            form = renderForgetForm();
         }
         return (
             <div className={styles.l_form_container}>

+ 26 - 3
src/widgets/WWithdraw/PassBox/WDPassBox.jsx

@@ -4,10 +4,9 @@ import Grow from "@mui/material/Grow";
 import styles from "./styles.module.scss";
 import Modal from "@mui/material/Modal";
 import MultiNumPassInput from "@components/UInput/MultiNumPassInput";
-import classNames from "classnames";
 import React, {useState} from "react";
 import {useDispatch, useSelector} from "react-redux";
-import {getWDBaseInfoStatus} from "@features/paySlice";
+import {getWDBaseInfoStatus, setChangePayPassType} from "@features/paySlice";
 import {REQ_STATUS} from "@api/config";
 import SmallLoading from "@components/SmallLoading";
 import {reqWithdrawBaseInfo} from "@features/api";
@@ -15,6 +14,8 @@ import {getHttpMsg} from "@utils/helpers";
 import {toast} from "react-toastify";
 import {useNavigate} from "react-router-dom";
 import {ROUTE_PATH} from "@constants/routes";
+import SvgIcon from "@components/SvgIcon";
+import {getUserInfo} from "@features/mainSlice";
 const i18n=global.i18n;
 
 const WDPassBox=({open,onClose,title,confirmLabel,onConfirm})=>{
@@ -26,9 +27,10 @@ const WDPassBox=({open,onClose,title,confirmLabel,onConfirm})=>{
     const status=useSelector(getWDBaseInfoStatus);
     const dispatch=useDispatch();
     const navigate=useNavigate();
+    const user=useSelector(getUserInfo);
     const callback=(data)=>{
         if (data==null)return;
-        if (data.code==200){
+        if (data.code===200){
             if (onClose)onClose();
             navigate(ROUTE_PATH.SAQUE);
         }
@@ -50,6 +52,20 @@ const WDPassBox=({open,onClose,title,confirmLabel,onConfirm})=>{
         // if (onClose)onClose();
         checkPass(pass)
     }
+    const onSevice=()=>{
+        if (onClose)onClose();
+        navigate(ROUTE_PATH.CUSTOMER);
+    }
+    const onForgotPassword=()=>{
+        let isBindPhone=user != null && user.Phone != null && user.Phone !== "";
+        if (onClose)onClose();
+        if (!isBindPhone){
+            global.showBindPhone();
+            return;
+        }
+        dispatch(setChangePayPassType(2));
+        navigate(ROUTE_PATH.CHANGE_PAY_PASS)
+    }
     return (
         <Modal open={open} onClose={onClose} closeAfterTransition>
             <Fade in={open} timeout={500}>
@@ -78,6 +94,13 @@ const WDPassBox=({open,onClose,title,confirmLabel,onConfirm})=>{
                                             status===REQ_STATUS.LOADING?(<SmallLoading fill={"#fff"} />):confirmLabel
                                         }
                                     </button>
+                                    <div className={styles.help_con}>
+                                        <button className={styles.btn_forget} onClick={onForgotPassword}>{i18n.t("withdraw.pass.btn_forgot")}</button>
+                                        <button className={styles.btn_service} onClick={onSevice}>
+                                            <SvgIcon className={styles.icon} herf={"icon_Support"}/>
+                                            {/*{i18n.t("withdraw.pass.btn_service")}*/}
+                                        </button>
+                                    </div>
                                 </div>
                             </div>
                         </Grow>

+ 23 - 0
src/widgets/WWithdraw/PassBox/styles.module.scss

@@ -8,5 +8,28 @@
     & .title{
       font-size: 1.1rem;
     }
+    & .help_con{
+      margin-top: -0.6rem;
+      width: 100%;
+      display: flex;
+      flex-direction: row;
+      align-items: center;
+      justify-content: space-between;
+      & .btn_forget{
+        text-decoration: underline;
+        text-underline-offset: 0.3rem;
+      }
+      & .btn_service{
+        display: flex;
+        flex-direction: row;
+        gap: 0.5rem;
+        align-items: center;
+        & .icon{
+          width: 1.4rem;
+          height: 1.4rem;
+          fill: #3bc117;
+        }
+      }
+    }
   }
 }

+ 122 - 7
src/widgets/WWithdraw/PassEdit/WDPassEdit.jsx

@@ -1,6 +1,6 @@
 import styles from "./styles.module.scss";
 import Spring from "@components/Spring";
-import React, {useState} from "react";
+import React, {useEffect, useState} from "react";
 import {Controller, useForm} from "react-hook-form";
 import classNames from "classnames";
 import MultiNumPassInput from "@components/UInput/MultiNumPassInput";
@@ -8,18 +8,23 @@ import {ReactComponent as SuccessIcon} from "@assets/singlesvg/succss.svg";
 import {useNavigate} from "react-router-dom";
 import {ROUTE_PATH} from "@constants/routes";
 import {useDispatch, useSelector} from "react-redux";
-import {getUserInfo} from "@features/mainSlice";
+import {getSendCodeStatus, getSendCodeTS, getUserInfo} from "@features/mainSlice";
 import {toast} from "react-toastify";
-import {reqChangePayPass} from "@features/api";
-import {getChangePayPassStatus, getChangePayPassType} from "@features/paySlice";
+import {reqChangePayPass, reqForgetPayPass, reqSendCode} from "@features/api";
+import {getChangePayPassStatus, getChangePayPassType, getForgetPayPassStatus} from "@features/paySlice";
 import {REQ_STATUS} from "@api/config";
 import SmallLoading from "@components/SmallLoading";
+import {CircularProgress} from "@mui/material";
 const i18n=global.i18n;
+const objInfo={
+    cdTimer:null
+}
 const WDPassEdit=({close})=>{
     const navigate=useNavigate();
     const dispatch=useDispatch();
     const user=useSelector(getUserInfo);
     const status=useSelector(getChangePayPassStatus);
+    //0 新设置 1 修改 2 忘记
     const editType=useSelector(getChangePayPassType);
     const [showType,setShowType]=useState(0)
     const passLen=6;
@@ -27,10 +32,14 @@ const WDPassEdit=({close})=>{
         defaultValues: {
             oldpassword:"",
             password: "",
-            repassword:""
+            repassword:"",
+            code:""
         }
     });
     const [focusIndex,setFocusIndex]=useState(editType===0?0:-1);
+    const otpTimeStamp=useSelector(getSendCodeTS);
+    const [otpCD,setOtpCD]=useState(-1);
+    const otpStatus=useSelector(getSendCodeStatus);
     const callback=(data)=>{
         if (data&&data.code===200){
             setShowType(1);
@@ -49,9 +58,64 @@ const WDPassEdit=({close})=>{
         if (editType===1){
             pdata.oldpassword=data.oldpassword;
         }
+        else if (editType===2){
+            let phone=getUserPhone();
+            if (phone==null||phone==="")return;
+            pdata.phone=phone;
+            pdata.code=data.code;
+        }
         pdata.callback=callback;
-        dispatch(reqChangePayPass(pdata));
+        if (editType===2)dispatch(reqForgetPayPass(pdata));
+        else dispatch(reqChangePayPass(pdata));
+    }
+    const clearTimer=()=>{
+        if (objInfo.cdTimer!=null){
+            clearInterval(objInfo.cdTimer);
+            objInfo.cdTimer=null;
+        }
+    }
+    const getUserPhone=()=>{
+        let phone=user.Phone;
+        if (phone==null||phone==="")return "";
+        let RegisterLocation=user.RegisterLocation;
+        if (RegisterLocation!=null)phone=phone.substring(RegisterLocation.length);
+        return phone;
     }
+    const onSendCode=(e)=>{
+        if (otpCD>0)return;
+        if (otpStatus===REQ_STATUS.LOADING)return;
+        let phone=getUserPhone();
+        if (phone==null||phone==="")return;
+        let params={phone:phone};
+        dispatch(reqSendCode(params));
+    }
+    const getLeftCD=()=>{
+        let now=Date.now();
+        let delta=now-otpTimeStamp;
+        delta=Math.floor(delta/1000);
+        delta=60-delta;
+        if (delta<0)return -1;
+        else return delta;
+    }
+    useEffect(()=>{
+        return ()=>{
+            clearTimer();
+        }
+    },[])
+    useEffect(()=>{
+        let left=getLeftCD();
+        setOtpCD(left);
+        clearTimer();
+        if (left>0){
+            objInfo.cdTimer=setInterval(()=>{
+                left=getLeftCD();
+                setOtpCD(left);
+                if (left<0){
+                    clearTimer();
+                }
+            },1000)
+        }
+    },[otpTimeStamp])
     const renderTitle=()=>{
         return (
             <div className={styles.title}>
@@ -140,11 +204,61 @@ const WDPassEdit=({close})=>{
             />
         )
     }
+    const renderOTPInput=()=>{
+        return (
+            <Controller control={control}
+                        name="code"
+                        rules={{required: true}}
+                        render={({field: {ref, onChange, value}, fieldState: {error}}) => {
+                            return (
+                                <div className={styles.password}>
+                                    <div>{i18n.t("withdraw.pass.otp_title")}</div>
+                                    <div
+                                        className={`input-control ${classNames({'input-control--error': error})}`}>
+                                        <input
+                                            type={'text'}
+                                            autoComplete="off"
+                                            ref={ref}
+                                            onChange={e => onChange(e.target.value)}
+                                            placeholder={i18n.t('login.otp_ph')}
+                                        />
+                                        <button type={"button"}
+                                                className={`${styles.otp_btn} ${otpCD > 0 ? styles.cd_timer : ''}`}
+                                                onClick={onSendCode}>
+                                            {
+                                                otpStatus === REQ_STATUS.LOADING ? (
+                                                    <CircularProgress color="success" size={22} thickness={5}/>) : (
+                                                    <>
+                                                        {
+                                                            otpCD <= 0 ? i18n.t('login.btn_otp') : otpCD + "S"
+                                                        }
+                                                    </>
+                                                )
+                                            }
+                                        </button>
+                                    </div>
+                                </div>
+                            )
+                        }}
+            />
+        )
+    }
+    const renderSubmitText=()=>{
+        if (status===REQ_STATUS.LOADING)return (<SmallLoading fill={"#fff"}/>);
+        if (editType===2){
+            return (i18n.t("withdraw.pass.btn_confirm"));
+        }
+        else
+        {
+            return (i18n.t("withdraw.pass.btn_submit"));
+        }
+    }
     const renderSubmit=()=>{
         return (
             <div className={styles.btn_con}>
                 <button className={"button-green"}>
-                    {status===REQ_STATUS.LOADING?(<SmallLoading fill={"#fff"}/>):(i18n.t("withdraw.pass.btn_submit"))}
+                    {/*{status===REQ_STATUS.LOADING?(<SmallLoading fill={"#fff"}/>):(i18n.t("withdraw.pass.btn_submit"))}*/}
+                    {renderSubmitText()}
                 </button>
             </div>
         )
@@ -156,6 +270,7 @@ const WDPassEdit=({close})=>{
             })}>
                 {renderTitle()}
                 {editType===1?renderOldPass():(<></>)}
+                {editType===2?renderOTPInput():(<></>)}
                 {renderPass()}
                 {renderRePass()}
                 {renderSubmit()}

+ 14 - 0
src/widgets/WWithdraw/PassEdit/styles.module.scss

@@ -61,4 +61,18 @@
     width: 100%;
   }
 }
+.otp_btn{
+  min-width: 3rem;
+  height: 2.5rem;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  justify-content: flex-end;
+  color: var(--primary-color);
+  text-align: right;
+  font-size: 1rem;
+  &.cd_timer{
+    color:var(--text);
+  }
+}