zhejiushizhuce 2 жил өмнө
parent
commit
ceb5ef7d7e

+ 1 - 0
package.json

@@ -38,6 +38,7 @@
     "dayjs": "^1.11.7",
     "fontfaceobserver": "^2.3.0",
     "gsap": "^3.12.5",
+    "html2canvas": "^1.4.1",
     "http-proxy-middleware": "^3.0.0",
     "i18next": "^23.8.2",
     "i18next-browser-languagedetector": "^7.2.0",

+ 6 - 0
src/Global.js

@@ -138,4 +138,10 @@ global.setLocalAreaID=(value)=>{
     localStorageSet(LOCAL_AREA_ID,value+"");
     window.location.reload();
 }
+global.showSaveAccount=(info)=>{
+    if (global.saveAccount)global.saveAccount.show(info);
+}
+global.hideSaveAccount=()=>{
+    if (global.saveAccount)global.saveAccount.hide();
+}
 //wd pass

+ 2 - 1
src/components/UInput/PhoneInput.jsx

@@ -147,6 +147,7 @@ const PhoneInput = ({innerRef, ...props}) => {
             <MuiDrawer open={showCountrySelect}
                        anchor={"bottom"}
                        sx={{
+                           zIndex:1300,
                            "& .MuiDrawer-paper": {
                                height: "50%",
                                boxSizing: "border-box",
@@ -154,7 +155,7 @@ const PhoneInput = ({innerRef, ...props}) => {
                                backgroundColor: 'var(--widget);',
                                boxShadow: 'var(--widget-shadow);',
                                borderTopLeftRadius: '1rem',
-                               borderTopRightRadius: '1rem'
+                               borderTopRightRadius: '1rem',
                            },
                        }}
                        variant="temporary"

+ 4 - 4
src/db/area.js

@@ -5,25 +5,25 @@ import img4 from '@assets/area/EUR.rect.png';
 const area=[
     {
         id:1,
-        name:"areaSelect.brazil",
+        name:"Brasil",
         icon:img1,
         code:"br"
     },
     {
         id:2,
-        name:"areaSelect.mexico",
+        name:"México",
         icon:img2,
         code:"mx"
     },
     {
         id:3,
-        name:"areaSelect.russia",
+        name:"Россия",
         icon:img3,
         code:"ru"
     },
     {
         id:4,
-        name:"areaSelect.europe",
+        name:"Europe",
         icon:img4,
         code:"eu"
     }

+ 26 - 8
src/features/api.js

@@ -147,17 +147,22 @@ export const reqLogin=createAsyncThunk(
         let data={
             // country:arg.country,
             // account:arg.account,
+            type:arg.type,
             password:arg.password,
             lang:lang
         }
         if (type===APP_LOGIN_TYPE.ID){
             data.account=arg.account;
-            data.type=APP_LOGIN_TYPE.ID;
         }
-        else
-        {
+        else if (type===APP_LOGIN_TYPE.PHONE){
             data.country=arg.country;
-            data.account=arg.account;
+            data.account=arg.phone;
+        }
+        else if (type===APP_LOGIN_TYPE.MAIL){
+            data.account=arg.email;
+        }
+        else if (type===APP_LOGIN_TYPE.GUEST){
+            delete data.password;
         }
         let params={
             url:APP_LOGIN,
@@ -178,18 +183,31 @@ export const reqRegister=createAsyncThunk(
         let data={
             // country:arg.country,
             // phone:arg.account,
+            type:arg.type,
             password:arg.password,
             repassword:arg.repassword,
             lang:lang
         }
         if (type===APP_LOGIN_TYPE.ID){
             data.account=arg.account;
-            data.type=APP_LOGIN_TYPE.ID;
         }
-        else
-        {
+        else if (type===APP_LOGIN_TYPE.PHONE){
             data.country=arg.country;
-            data.phone=arg.account;
+            data.phone=arg.phone;
+        }
+        else if (type===APP_LOGIN_TYPE.SMS){
+            data.country=arg.country;
+            data.phone=arg.phone;
+            data.code=arg.code;
+            delete data.password;
+            delete data.repassword;
+        }
+        else if (type===APP_LOGIN_TYPE.MAIL){
+            data.email=arg.email;
+        }
+        else if (type===APP_LOGIN_TYPE.GUEST){
+            delete data.password;
+            delete data.repassword;
         }
         let act=localStorageGet(LOCAL_WHEEL_INVITE_CODE);
         if (act!=null&&act!==""){

+ 14 - 3
src/features/mainSlice.js

@@ -253,6 +253,8 @@ const reducerRoutes=(state,action)=>{
         let firstBonus=0;
         let openRelief=0;
         let accountLogin=1;
+        let loginOpen="";
+        let registerOpen="";
         let area=global.getLocalAreaID();
         //sport 配置
         let bbConf=null;
@@ -283,11 +285,12 @@ const reducerRoutes=(state,action)=>{
                 bbConf=conf.bb;
                 if(userInfo!=null)userInfo.bb=bbConf;
             }
-            if (!!conf.accountLogin)accountLogin=conf.accountLogin;
+            if (!!conf.loginOpen)loginOpen=conf.loginOpen;
+            if (!!conf.registerOpen)registerOpen=conf.registerOpen;
         }
         //favorites
         // let favorites=getFavorites(json.favorites);
-        Object.assign(state, {routes:state.routes,asyncRoutes:data,styles,blocks,...generData,isLogin:isLogin,userInfo,hallServerUrl,promoteInstall,guest,upgradeBonus,popWheel,popBindPhone,firstBonus,openRelief,bbConf,accountLogin,area});
+        Object.assign(state, {routes:state.routes,asyncRoutes:data,styles,blocks,...generData,isLogin:isLogin,userInfo,hallServerUrl,promoteInstall,guest,upgradeBonus,popWheel,popBindPhone,firstBonus,openRelief,bbConf,accountLogin,area,loginOpen,registerOpen});
         // Object.assign(state, {routes:state.routes,asyncRoutes:data,styles:styles,blocks:blocks,...generData,isLogin:isLogin,userInfo:userInfo,hallServerUrl:hallServerUrl,promoteInstall:promoteInstall,guest:guest,upgradeBonus:upgradeBonus});
     }
 }
@@ -614,7 +617,9 @@ export const Main=createSlice({
         //sport conf
         bbConf:null,
         accountLogin:0,
-        area:0
+        area:0,
+        loginOpen:"",
+        registerOpen:""
     },
     reducers: {
         logout:(state, action)=>{
@@ -955,6 +960,12 @@ export const getUserNick=(state)=>{
     }
     return nick;
 }
+export const getLoginOpen=(state)=>{
+    return state.main.loginOpen;
+}
+export const getRegisterOpen=(state)=>{
+    return state.main.registerOpen;
+}
 //数据获取
 export default Main.reducer;
 

+ 2 - 0
src/layout/Layout.jsx

@@ -38,6 +38,7 @@ import WProtect from "@widgets/WProtect";
 import LoadingCircular from "@components/Loading/LoadingCircular";
 import WFirstPay from "@widgets/WFirstPay";
 import WAreaSelect from "@widgets/WAreaSelect";
+import WSaveAccount from "@widgets/WSaveAccount";
 const PageNotFound = lazy(() => import('@pages/PageNotFound'));
 
 const Layout=({...props})=>{
@@ -134,6 +135,7 @@ const Layout=({...props})=>{
                 <WBindPrompt/>
                 <WProtect/>
                 <WFirstPay/>
+                <WSaveAccount/>
             </>
         )
     }

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

@@ -113,19 +113,23 @@
     "success": "Login successful, welcome back!",
     "notfound": "Your account was not found, please register or try again!",
     "login_type": {
-      "password": "Password",
-      "otp": "SMS",
-      "account": "Account"
+      "id": "Account",
+      "phone": "Phone",
+      "sms": "SMS",
+      "mail": "Mail",
+      "guest": "Guest"
     },
     "otp_ph": "Verification Code",
-    "guest_login": "Guest Login",
+    "guest_login": "Quick Login",
+    "guest_reg": "Quick Registration",
     "btn_otp": "Send Code",
     "forget_title": "Reset Password",
     "back_to_login": "Back to",
     "account_ph": "User Account",
     "reg_type": {
       "phone": "Phone"
-    }
+    },
+    "mail_ph": "Email"
   },
   "logout": {
     "success": "You have exited successfully."
@@ -419,7 +423,9 @@
   "form_errors": {
     "phone_55": "Enter a correct 11-digit phone number.",
     "pass": "The password must not be less than 6 characters.",
-    "account": "The account number must not be less than 6 characters."
+    "account": "The account number must not be less than 6 characters.",
+    "email": "Enter a valid email address",
+    "phone": "Enter a correct phone number."
   },
   "turnplate": {
     "expires_in": "Expires in",
@@ -660,6 +666,12 @@
   "areaCodeSelect": {
     "title": "Area Code Selection"
   },
+  "saveAccount": {
+    "title": "Save Account",
+    "account": "Account",
+    "password": "Password",
+    "tips": "Please save your account in time to avoid loss"
+  },
   "web": {
     "activity": {
       "redpack_not_available": "There is no RedPack",

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

@@ -110,19 +110,23 @@
     "success": "Inicio de sesión exitoso, ¡bienvenido de nuevo!",
     "notfound": "Tu cuenta no fue encontrada, por favor regístrate o intenta nuevamente!",
     "login_type": {
-      "password": "Contraseña",
-      "otp": "SMS",
-      "account": "Cuenta"
+      "id": "Cuenta",
+      "phone": "Teléfono",
+      "sms": "SMS",
+      "mail": "Correo",
+      "guest": "Rápido"
     },
     "otp_ph": "Código de verificación",
-    "guest_login": "Acceso de usuario invitado",
+    "guest_login": "Inicio de sesión rápido",
+    "guest_reg": "Registro rápido",
     "btn_otp": "Verificar",
     "forget_title": "Restablecer la contraseña",
     "back_to_login": "Volver a",
     "account_ph": "Cuenta de usuario",
     "reg_type": {
       "phone": "Teléfono"
-    }
+    },
+    "mail_ph": "Correo electrónico"
   },
   "logout": {
     "success": "Has salido exitosamente."
@@ -409,7 +413,9 @@
   "form_errors": {
     "phone_55": "Ingresa un número de teléfono correcto de 11 dígitos.",
     "pass": "La contraseña no debe tener menos de 6 caracteres",
-    "account": "El número de cuenta no debe tener menos de 6 caracteres."
+    "account": "El número de cuenta no debe tener menos de 6 caracteres.",
+    "email": "Introduzca una dirección de correo electrónico válida",
+    "phone": "Introduzca un número de teléfono correcto."
   },
   "turnplate": {
     "expires_in": "Expira en",
@@ -649,6 +655,12 @@
   "areaCodeSelect": {
     "title": "Selección de código de área"
   },
+  "saveAccount": {
+    "title": "Guardar cuenta",
+    "account": "Cuenta",
+    "password": "Contraseña",
+    "tips": "Guarde su cuenta a tiempo para evitar pérdidas."
+  },
   "web": {
     "activity": {
       "redpack_not_available": "No hay RedPack",

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

@@ -110,19 +110,23 @@
     "success": "Login bem-sucedido, bem-vindo de volta!",
     "notfound": "Sua conta não foi encontrada, por favor registre-se ou tente novamente!",
     "login_type": {
-      "password": "Senha",
-      "otp": "SMS",
-      "account": "Conta"
+      "id": "Conta",
+      "phone": "Telefone",
+      "sms": "SMS",
+      "mail": "E-mail",
+      "guest": "Rápido"
     },
     "otp_ph": "Código de verificação",
-    "guest_login": "Login como convidado",
+    "guest_login": "Login rápido",
+    "guest_reg": "Registro rápido",
     "btn_otp": "Verificar",
     "forget_title": "Redefinir senha",
     "back_to_login": "Voltar ao",
     "account_ph": "Conta de usuário",
     "reg_type": {
       "phone": "Telefone"
-    }
+    },
+    "mail_ph": "E-mail"
   },
   "logout": {
     "success": "Você saiu com sucesso."
@@ -409,7 +413,9 @@
   "form_errors": {
     "phone_55": "Digite um número de telefone correto de 11 dígitos.",
     "pass": "A senha não deve ter menos de 6 caracteres",
-    "account": "O número da conta não deve ter menos de 6 caracteres."
+    "account": "O número da conta não deve ter menos de 6 caracteres.",
+    "email": "Insira um endereço de e-mail válido",
+    "phone": "Digite um número de telefone correto."
   },
   "turnplate": {
     "expires_in": "Expira em",
@@ -649,6 +655,12 @@
   "areaCodeSelect": {
     "title": "Seleção de código de área"
   },
+  "saveAccount": {
+    "title": "Salvar conta",
+    "account": "Conta",
+    "password": "Senha",
+    "tips": "Por favor, salve sua conta a tempo para evitar perdas"
+  },
   "web": {
     "activity": {
       "redpack_not_available": "Não há RedPack",

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

@@ -110,19 +110,23 @@
     "success": "Успешный вход, добро пожаловать!",
     "notfound": "Ваш аккаунт не найден, пожалуйста, зарегистрируйтесь или попробуйте снова!",
     "login_type": {
-      "password": "Пароль",
-      "otp": "SMS",
-      "account": "Счет"
+      "id": "Счет",
+      "phone": "Телефон",
+      "sms": "SMS",
+      "mail": "Почта",
+      "guest": "Гость"
     },
     "otp_ph": "Проверочный код",
-    "guest_login": "Гостевой вход",
+    "guest_login": "Быстрый вход",
+    "guest_reg": "Быстрая регистрация",
     "btn_otp": "Отправить",
     "forget_title": "Сброс пароля",
     "back_to_login": "Вернуться к",
     "account_ph": "Учетная запись пользователя",
     "reg_type": {
       "phone": "Телефон"
-    }
+    },
+    "mail_ph": "Электронная почта"
   },
   "logout": {
     "success": "Вход успешно выполнен."
@@ -409,7 +413,9 @@
   "form_errors": {
     "phone_55": "Введите корректный 11-значный номер телефона.",
     "pass": "Пароль должен содержать не менее 6 символов",
-    "account": "Номер счета не должен быть короче 6 символов."
+    "account": "Номер счета не должен быть короче 6 символов.",
+    "email": "Введите действительный адрес электронной почты",
+    "phone": "Введите правильный номер телефона."
   },
   "turnplate": {
     "expires_in": "Истекает через",
@@ -649,6 +655,12 @@
   "areaCodeSelect": {
     "title": "Выбор кода города"
   },
+  "saveAccount": {
+    "title": "Сохранить аккаунт",
+    "account": "Счет",
+    "password": "Пароль",
+    "tips": "Пожалуйста, сохраните свой аккаунт вовремя, чтобы избежать потери."
+  },
   "web": {
     "activity": {
       "redpack_not_available": "Нет RedPack",

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

@@ -110,19 +110,23 @@
     "success": "登入成功,歡迎回來!",
     "notfound": "未找到您的帳戶,請註冊或重試!",
     "login_type": {
-      "password": "密碼",
-      "otp": "驗證碼",
-      "account": "帳戶"
+      "id": "帳戶",
+      "phone": "電話",
+      "sms": "驗證碼",
+      "mail": "郵件",
+      "guest": "客人"
     },
     "otp_ph": "驗證碼",
-    "guest_login": "訪客登入",
+    "guest_login": "快速登入",
+    "guest_reg": "快速註冊",
     "btn_otp": "驗證碼",
     "forget_title": "重新設密碼",
     "back_to_login": "返回",
     "account_ph": "使用者帳戶",
     "reg_type": {
       "phone": "電話"
-    }
+    },
+    "mail_ph": "電子郵件"
   },
   "logout": {
     "success": "您已成功退出。"
@@ -409,7 +413,9 @@
   "form_errors": {
     "phone_55": "請輸入正確的11位電話號碼。",
     "pass": "密碼不得少於6個字符",
-    "account": "帳號不得少於6個字元。"
+    "account": "帳號不得少於6個字元。",
+    "email": "輸入有效的電子郵件地址",
+    "phone": "輸入正確的電話號碼。"
   },
   "turnplate": {
     "expires_in": "到期時間",
@@ -649,6 +655,12 @@
   "areaCodeSelect": {
     "title": "區號選擇"
   },
+  "saveAccount": {
+    "title": "儲存帳戶",
+    "account": "帳戶",
+    "password": "密碼",
+    "tips": "請及時保存您的帳戶,以免造成損失"
+  },
   "web": {
     "activity": {
       "redpack_not_available": "沒有紅包",

+ 4 - 0
src/utils/helpers.js

@@ -469,3 +469,7 @@ export const getDomainSuffix=()=>{
 
     return suffix;
 }
+export const validateEmail=(value)=>{
+    const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+    return emailPattern.test(value);
+}

+ 372 - 110
src/widgets/LoginForm/index.jsx

@@ -21,8 +21,8 @@ import {toast} from 'react-toastify';
 import {useDispatch, useSelector} from "react-redux";
 import {
     getAccountLogin,
-    getChangePassStatus,
-    getLoginStatus,
+    getChangePassStatus, getLoginOpen,
+    getLoginStatus, getRegisterOpen,
     getRegStatus,
     getSendCodeStatus,
     getSendCodeTS,
@@ -35,7 +35,7 @@ import {REQ_STATUS} from "@api/config";
 import {reqForgetPass, reqLogin, reqRegister, reqSendCode, reqSMSLogin} from "@features/api";
 import {ROUTE_PATH} from "@constants/routes";
 import {APP_LOGIN_TYPE, APP_THEME} from "@constants/app";
-import {validateCountryPhone} from "@utils/helpers";
+import {validateCountryPhone, validateEmail} from "@utils/helpers";
 import {CircularProgress} from "@mui/material";
 import area from "@db/area";
 import SvgIcon from "@components/SvgIcon";
@@ -45,12 +45,17 @@ const TYPE_LOGIN="login";
 const TYPE_REGISTER="register";
 const TYPE_FORGET_PASS="forgetPassword";
 
-const LOGIN_TYPE_PASS=0;
-const LOGIN_TYPE_OTP=1;
-const LOGIN_TYPE_ACOUNT=2;
+const LOGIN_TYPE_PHONE=0;
+const LOGIN_TYPE_SMS=1;
+const LOGIN_TYPE_ID=2;
+const LOGIN_TYPE_MAIL=3;
+const LOGIN_TYPE_GUEST=4;
 
 const REG_TYPE_PHONE=0;
-const REG_TYPE_ACCOUNT=1;
+const REG_TYPE_SMS=1;
+const REG_TYPE_ID=2;
+const REG_TYPE_MAIL=3;
+const REG_TYPE_GUEST=4;
 const objInfo={
     cdTimer:null,
     inSubmit:false
@@ -67,7 +72,7 @@ const LoginForm = ({close}) => {
     const forgetStatus=useSelector(getChangePassStatus);
     const dispatch = useDispatch();
     const navigate=useNavigate();
-    const [loginType,setLoginType]=useState(LOGIN_TYPE_PASS);
+    const [loginType,setLoginType]=useState(LOGIN_TYPE_PHONE);
     const {register, handleSubmit, formState: {errors}, control} = useForm({
         defaultValues: {
             // country:'55',
@@ -79,10 +84,7 @@ const LoginForm = ({close}) => {
             code:"",
             phone:"",
             newpassword:"",
-            renewpassword:"",
-            acc_account:"",
-            acc_password:"",
-            acc_repassword:""
+            renewpassword:""
         }
     });
     const {pathname,hash} = useLocation();
@@ -92,6 +94,12 @@ const LoginForm = ({close}) => {
     const smsLoginStatus=useSelector(getSMSLoginStatus);
     const isAccountLogin=useSelector(getAccountLogin);
     const [regType,setRegType]=useState(REG_TYPE_PHONE);
+    const loginOpen=useSelector(getLoginOpen);
+    const registerOpen=useSelector(getRegisterOpen);
+    //登录类型列表
+    const [logTypeList,setLogTypeList]=useState([]);
+    //注册类型列表
+    const [regTypeList,setRegTypeList]=useState([]);
     const onSendCode=(e)=>{
         if (otpCD>0)return;
         if (otpStatus===REQ_STATUS.LOADING)return;
@@ -124,6 +132,30 @@ const LoginForm = ({close}) => {
         data.callback=onCallback;
         dispatch(reqSMSLogin(data));
     }
+    const parseLogTypeList=(openType,stype)=>{
+        let subs=openType.split(",");
+        let list=[];
+        for(let i=0;i<subs.length;i++){
+            let type=subs[i];
+            let obj={};
+            if (type===APP_LOGIN_TYPE.ID)stype===TYPE_LOGIN?obj.type=LOGIN_TYPE_ID:obj.type=REG_TYPE_ID;
+            else if (type===APP_LOGIN_TYPE.SMS)stype===TYPE_LOGIN?obj.type=LOGIN_TYPE_SMS:obj.type=REG_TYPE_SMS;
+            else if (type===APP_LOGIN_TYPE.PHONE)stype===TYPE_LOGIN?obj.type=LOGIN_TYPE_PHONE:obj.type=REG_TYPE_PHONE;
+            else if (type===APP_LOGIN_TYPE.MAIL)stype===TYPE_LOGIN?obj.type=LOGIN_TYPE_MAIL:obj.type=REG_TYPE_MAIL;
+            else if (type===APP_LOGIN_TYPE.GUEST) stype===TYPE_LOGIN?obj.type=LOGIN_TYPE_GUEST:obj.type=REG_TYPE_GUEST;
+            obj.label="login.login_type."+type;
+            list.push(obj);
+        }
+        list.sort((a,b)=>{
+            return a.type-b.type;
+        })
+        return list;
+    }
+    const getPhoneError=()=>{
+        if (country==="55")return i18n.t("form_errors.phone_" + country);
+        else return i18n.t("form_errors.phone");
+    }
+    //effect_______
     useEffect(()=>{
        return ()=>{
            global.authLockPath=null;
@@ -153,6 +185,21 @@ const LoginForm = ({close}) => {
             },1000)
         }
     },[otpTimeStamp])
+    useEffect(()=>{
+        let list=parseLogTypeList(loginOpen,TYPE_LOGIN);
+        if (list&&list.length>0){
+            setLogTypeList(list);
+            setLoginType(list[0].type);
+        }
+    },[loginOpen])
+    useEffect(()=>{
+        let list=parseLogTypeList(registerOpen,TYPE_REGISTER);
+        if (list&&list.length>0){
+            setRegTypeList(list);
+            setRegType(list[0].type);
+        }
+    },[registerOpen])
+    //effect_______
     const navigatRoute=(route)=>{
         navigate(route,{replace:true});
     }
@@ -194,38 +241,12 @@ const LoginForm = ({close}) => {
         dispatch(reqForgetPass(data));
         objInfo.inSubmit=true;
     }
-    const onSubmitAccountLogin=(data)=>{
-        if (loginStatus===REQ_STATUS.LOADING)return;
-        if (objInfo.inSubmit)return;
-        // acc_account:"",
-        //     acc_password:"",
-        //     acc_repassword:""
-        //data.type=APP_LOGIN_TYPE.ID;
-        let ndata={
-            account:data.acc_account,
-            password:data.acc_password,
-            type:APP_LOGIN_TYPE.ID
-        }
-        ndata.callback=onCallback;
-        dispatch(reqLogin(ndata));
-        objInfo.inSubmit=true;
-    }
-    const onSubmitAccountReg=(data)=>{
-        if (regStatus===REQ_STATUS.LOADING)return;
-        if (objInfo.inSubmit)return;
-        let ndata={
-            account:data.acc_account,
-            password:data.acc_password,
-            repassword:data.acc_repassword,
-            type:APP_LOGIN_TYPE.ID
-        }
-        ndata.callback=onCallback;
-        dispatch(reqRegister(ndata));
-        objInfo.inSubmit=true;
-    }
     const validatePhone=(value)=>{
         return validateCountryPhone(country,value);
     }
+    const validateMail=(value)=>{
+        return validateEmail(value);
+    }
     const getAreaItem=()=>{
         let areaID=global.getLocalAreaID();
         let item=null;
@@ -277,24 +298,52 @@ const LoginForm = ({close}) => {
             </div>
         )
     }
-    const renderGuestLogin = () => {
-        // return (
-        //     <button type={"button"} className={styles.guest_btn}>
-        //         {i18n.t("login.guest_login")}
-        //     </button>
-        // )
+    // const renderGuestLogin = () => {
+    //     let subs=loginOpen.split(",");
+    //     let isGuest=subs.indexOf(APP_LOGIN_TYPE.GUEST)!==-1;
+    //     if (!isGuest)return (<></>);
+    //     return (
+    //         <button type={"button"} className={styles.guest_btn}>
+    //             {i18n.t("login.guest_login")}
+    //         </button>
+    //     )
+    // }
+    const renderTypeTabs=(type)=>{
+        let list=type===TYPE_LOGIN?logTypeList:regTypeList;
+        if (list.length<=1)return (<></>);
+        let ltype=type===TYPE_LOGIN?loginType:regType;
+        return (
+            <div className={styles.l_type_tabs}>
+                {
+                    list.map((item,index)=>{
+                        return (
+                            <button
+                                key={index}
+                                className={`${styles.l_type_btn} ${ltype === item.type ? styles.active : ''}`}
+                                onClick={() => {
+                                    if(type===TYPE_LOGIN)setLoginType(item.type);
+                                    else if (type===TYPE_REGISTER)setRegType(item.type);
+                                }}>
+                                <span>{i18n.t(item.label)}</span>
+                            </button>
+                        )
+                    })
+                }
+            </div>
+        )
     }
     //login__________
     const renderPassLogin = () => {
         return (
             <form key={"pass"} autoComplete="off" className={styles.l_form} onSubmit={handleSubmit((data) => {
+                data.type=APP_LOGIN_TYPE.PHONE;
                 onSubmitLogin(data);
             })}>
                 <Controller control={control}
-                            name="account"
+                            name="phone"
                             rules={{required: true}}
                             render={({field: {ref, onChange, value}, fieldState: {error}}) => {
-                                let message = error ? i18n.t("form_errors.phone_" + country) : "";
+                                let message = error ? getPhoneError() : "";
                                 return (<PhoneInput
                                     key={"account"}
                                     className={classNames({'input-control--error': error})}
@@ -333,7 +382,7 @@ const LoginForm = ({close}) => {
                         loginStatus === REQ_STATUS.LOADING ? renderLoading() : i18n.t("login.sign_in_btn")
                     }
                 </button>
-                {renderGuestLogin()}
+                {/*{renderGuestLogin()}*/}
                 {renderLoginToReg()}
             </form>
         )
@@ -347,7 +396,7 @@ const LoginForm = ({close}) => {
                             name="phone"
                             rules={{required: true}}
                             render={({field: {ref, onChange, value}, fieldState: {error}}) => {
-                                let message = error ? i18n.t("form_errors.phone_" + country) : "";
+                                let message = error ? getPhoneError() : "";
                                 return (<PhoneInput
                                     key={"phone"}
                                     className={classNames({'input-control--error': error})}
@@ -400,7 +449,7 @@ const LoginForm = ({close}) => {
                         smsLoginStatus === REQ_STATUS.LOADING ? renderLoading() : i18n.t("login.sign_in_btn")
                     }
                 </button>
-                {renderGuestLogin()}
+                {/*{renderGuestLogin()}*/}
                 {renderLoginToReg()}
             </form>
         )
@@ -408,10 +457,12 @@ const LoginForm = ({close}) => {
     const renderAccountLogin=()=>{
         return (
             <form key={"account"} autoComplete="off" className={styles.l_form} onSubmit={handleSubmit((data) => {
-                onSubmitAccountLogin(data);
+                // onSubmitAccountLogin(data);
+                data.type=APP_LOGIN_TYPE.ID;
+                onSubmitLogin(data);
             })}>
                 <Controller control={control}
-                            name="acc_account"
+                            name="account"
                             rules={{required: true, minLength: 6}}
                             render={({field: {ref, onChange, value}, fieldState: {error}}) => {
                                 return (
@@ -420,6 +471,7 @@ const LoginForm = ({close}) => {
                                             className={`input-control ${classNames({'input-control--error': error})}`}>
                                             <input
                                                 type={'text'}
+                                                value={value}
                                                 autoComplete="off"
                                                 ref={ref}
                                                 onChange={e => onChange(e.target.value)}
@@ -434,7 +486,7 @@ const LoginForm = ({close}) => {
                             }}
                 />
                 <Controller control={control}
-                            name="acc_password"
+                            name="password"
                             rules={{required: true, minLength: 6}}
                             render={({field: {ref, onChange, value}, fieldState: {error}}) => (
                                 <PasswordInput
@@ -451,18 +503,93 @@ const LoginForm = ({close}) => {
                         loginStatus === REQ_STATUS.LOADING ? renderLoading() : i18n.t("login.sign_in_btn")
                     }
                 </button>
-                {renderGuestLogin()}
+                {/*{renderGuestLogin()}*/}
+                {renderLoginToReg()}
+            </form>
+        )
+    }
+    const renderMailLogin=()=>{
+        return (
+            <form key={"mail"} autoComplete="off" className={styles.l_form} onSubmit={handleSubmit((data) => {
+                // onSubmitAccountLogin(data);
+                data.type=APP_LOGIN_TYPE.MAIL;
+                onSubmitLogin(data);
+            })}>
+                <Controller control={control}
+                            name="email"
+                            rules={{required: true, validate: validateMail}}
+                            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'}
+                                                value={value}
+                                                autoComplete="off"
+                                                ref={ref}
+                                                onChange={e => onChange(e.target.value)}
+                                                placeholder={i18n.t('login.mail_ph')}
+                                            />
+                                        </div>
+                                        {
+                                            error?(<div className="input-prompt-error">{i18n.t('form_errors.email')}</div>):(<></>)
+                                        }
+                                    </div>
+                                )
+                            }}
+                />
+                <Controller control={control}
+                            name="password"
+                            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('login.login_pwd')}
+                                    innerRef={ref}/>
+                            )}
+                />
+                <button className={"button-green"}>
+                    {
+                        loginStatus === REQ_STATUS.LOADING ? renderLoading() : i18n.t("login.sign_in_btn")
+                    }
+                </button>
+                {/*{renderGuestLogin()}*/}
+                {renderLoginToReg()}
+            </form>
+        )
+    }
+    const renderGuestLogin=()=>{
+        return (
+            <form key={"guest"} autoComplete="off" className={styles.l_form} onSubmit={handleSubmit((data) => {
+                // onSubmitAccountLogin(data);
+                data.type=APP_LOGIN_TYPE.GUEST;
+                onSubmitLogin(data);
+            })}>
+                <button className={"button-green"}>
+                    {
+                        loginStatus === REQ_STATUS.LOADING ? renderLoading() : i18n.t("login.guest_login")
+                    }
+                </button>
                 {renderLoginToReg()}
             </form>
         )
     }
     const renderLoginType = () => {
-        if (loginType === LOGIN_TYPE_PASS) {
+        control._names.mount.clear();
+        if (loginType === LOGIN_TYPE_PHONE) {
             return renderPassLogin();
-        } else if (loginType === LOGIN_TYPE_OTP){
+        } else if (loginType === LOGIN_TYPE_SMS){
             return renderOTPLogin();
-        } else if (loginType === LOGIN_TYPE_ACOUNT){
+        } else if (loginType === LOGIN_TYPE_ID){
             return renderAccountLogin();
+        } else if (loginType ===LOGIN_TYPE_MAIL){
+            return renderMailLogin();
+        } else if (loginType === LOGIN_TYPE_GUEST){
+            return renderGuestLogin();
         }
         return (<></>);
     }
@@ -470,30 +597,7 @@ const LoginForm = ({close}) => {
         return (
             <Spring type="slideUp">
                 <div className={styles.l_title}>{i18n.t("login.login_title")}</div>
-                <div className={styles.l_type_tabs}>
-                    <button className={`${styles.l_type_btn} ${loginType === LOGIN_TYPE_PASS ? styles.active : ''}`}
-                            onClick={() => {
-                                setLoginType(LOGIN_TYPE_PASS)
-                            }}>
-                        <span>{i18n.t("login.login_type.password")}</span>
-                    </button>
-                    <button className={`${styles.l_type_btn} ${loginType === LOGIN_TYPE_OTP ? styles.active : ''}`}
-                            onClick={() => {
-                                setLoginType(LOGIN_TYPE_OTP)
-                            }}>
-                        <span>{i18n.t("login.login_type.otp")}</span>
-                    </button>
-                    {
-                        isAccountLogin?(
-                            <button className={`${styles.l_type_btn} ${loginType === LOGIN_TYPE_ACOUNT ? styles.active : ''}`}
-                                    onClick={() => {
-                                        setLoginType(LOGIN_TYPE_ACOUNT)
-                                    }}>
-                                <span>{i18n.t("login.login_type.account")}</span>
-                            </button>
-                        ):(<></>)
-                    }
-                </div>
+                {renderTypeTabs(TYPE_LOGIN)}
                 {
                     // loginType===LOGIN_TYPE_PASS?renderPassLogin():renderOTPLogin()
                     renderLoginType()
@@ -521,16 +625,27 @@ const LoginForm = ({close}) => {
             </div>
         )
     }
+    // const renderGuestReg = () => {
+    //     let subs=registerOpen.split(",");
+    //     let isGuest=subs.indexOf(APP_LOGIN_TYPE.GUEST)!==-1;
+    //     if (!isGuest)return (<></>);
+    //     return (
+    //         <button type={"button"} className={styles.guest_btn}>
+    //             {i18n.t("login.guest_reg")}
+    //         </button>
+    //     )
+    // }
     const renderRegPhone = () => {
         return (
             <form key={"reg_phone"} autoComplete="off" className={styles.l_form} onSubmit={handleSubmit((data) => {
+                data.type=APP_LOGIN_TYPE.PHONE;
                 onSubmitReg(data);
             })}>
                 <Controller control={control}
-                            name="account"
+                            name="phone"
                             rules={{required: true, validate: validatePhone}}
                             render={({field: {ref, onChange, value}, fieldState: {error}}) => {
-                                let message = error ? i18n.t("form_errors.phone_" + country) : "";
+                                let message = error ? getPhoneError() : "";
                                 return (<PhoneInput
                                     className={classNames({'input-control--error': error})}
                                     value={value}
@@ -574,6 +689,7 @@ const LoginForm = ({close}) => {
                         regStatus === REQ_STATUS.LOADING ? renderLoading() : i18n.t("login.sign_up_btn")
                     }
                 </button>
+                {/*{renderGuestReg()}*/}
                 {renderRegToLogin()}
             </form>
         )
@@ -582,10 +698,12 @@ const LoginForm = ({close}) => {
         return (
             <form key={"reg_account"} autoComplete="off" className={styles.l_form} onSubmit={handleSubmit((data) => {
                 // onSubmitReg(data);
-                onSubmitAccountReg(data);
+                // onSubmitAccountReg(data);
+                data.type=APP_LOGIN_TYPE.ID;
+                onSubmitReg(data);
             })}>
                 <Controller control={control}
-                            name="acc_account"
+                            name="account"
                             rules={{required: true, minLength: 6}}
                             render={({field: {ref, onChange, value}, fieldState: {error}}) => {
                                 return (
@@ -594,6 +712,7 @@ const LoginForm = ({close}) => {
                                             className={`input-control ${classNames({'input-control--error': error})}`}>
                                             <input
                                                 type={'text'}
+                                                value={value}
                                                 autoComplete="off"
                                                 ref={ref}
                                                 onChange={e => onChange(e.target.value)}
@@ -608,7 +727,7 @@ const LoginForm = ({close}) => {
                             }}
                 />
                 <Controller control={control}
-                            name="acc_password"
+                            name="password"
                             rules={{required: true, minLength: 6}}
                             render={({field: {ref, onChange, value}, fieldState: {error}}) => (
                                 <PasswordInput
@@ -621,7 +740,7 @@ const LoginForm = ({close}) => {
                             )}
                 />
                 <Controller control={control}
-                            name="acc_repassword"
+                            name="repassword"
                             rules={{required: true, minLength: 6}}
                             render={({field: {ref, onChange, value}, fieldState: {error}}) => (
                                 <PasswordInput
@@ -638,42 +757,185 @@ const LoginForm = ({close}) => {
                         regStatus === REQ_STATUS.LOADING ? renderLoading() : i18n.t("login.sign_up_btn")
                     }
                 </button>
+                {/*{renderGuestReg()}*/}
+                {renderRegToLogin()}
+            </form>
+        )
+    }
+    const renderRegSms=()=>{
+        return (
+            <form key={"reg_sms"} autoComplete="off" className={styles.l_form} onSubmit={handleSubmit((data) => {
+                data.type=APP_LOGIN_TYPE.SMS;
+                onSubmitReg(data);
+            })}>
+                <Controller control={control}
+                            name="phone"
+                            rules={{required: true, validate: validatePhone}}
+                            render={({field: {ref, onChange, value}, fieldState: {error}}) => {
+                                let message = error ? getPhoneError() : "";
+                                return (<PhoneInput
+                                    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="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"}>
+                    {
+                        regStatus === REQ_STATUS.LOADING ? renderLoading() : i18n.t("login.sign_up_btn")
+                    }
+                </button>
+                {/*{renderGuestReg()}*/}
+                {renderRegToLogin()}
+            </form>
+        )
+    }
+    const renderRegMail=()=>{
+        return (
+            <form key={"reg_mail"} autoComplete="off" className={styles.l_form} onSubmit={handleSubmit((data) => {
+                // onSubmitReg(data);
+                // onSubmitAccountReg(data);
+                data.type=APP_LOGIN_TYPE.MAIL;
+                onSubmitReg(data);
+            })}>
+                <Controller control={control}
+                            name="email"
+                            rules={{required: true, validate: validateMail}}
+                            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'}
+                                                value={value}
+                                                autoComplete="off"
+                                                ref={ref}
+                                                onChange={e => onChange(e.target.value)}
+                                                placeholder={i18n.t('login.mail_ph')}
+                                            />
+                                        </div>
+                                        {
+                                            error?(<div className="input-prompt-error">{i18n.t('form_errors.email')}</div>):(<></>)
+                                        }
+                                    </div>
+                                )
+                            }}
+                />
+                <Controller control={control}
+                            name="password"
+                            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('login.login_pwd')}
+                                    innerRef={ref}/>
+                            )}
+                />
+                <Controller control={control}
+                            name="repassword"
+                            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('login.re_pwd')}
+                                    innerRef={ref}/>
+                            )}
+                />
+                <button className={"button-green"}>
+                    {
+                        regStatus === REQ_STATUS.LOADING ? renderLoading() : i18n.t("login.sign_up_btn")
+                    }
+                </button>
+                {/*{renderGuestReg()}*/}
+                {renderRegToLogin()}
+            </form>
+        )
+    }
+    const renderRegGuest=()=>{
+        return (
+            <form key={"reg_guest"} autoComplete="off" className={styles.l_form} onSubmit={handleSubmit((data) => {
+                data.type=APP_LOGIN_TYPE.MAIL;
+                onSubmitReg(data);
+            })}>
+                <button className={"button-green"}>
+                    {
+                        regStatus === REQ_STATUS.LOADING ? renderLoading() : i18n.t("login.guest_reg")
+                    }
+                </button>
                 {renderRegToLogin()}
             </form>
         )
     }
     const renderRegType=()=>{
+        control._names.mount.clear();
         if (regType===REG_TYPE_PHONE){
             return renderRegPhone();
         }
-        else if (regType===REG_TYPE_ACCOUNT){
+        else if (regType===REG_TYPE_ID){
             return renderRegAccount();
         }
+        else if (regType===REG_TYPE_SMS){
+            return renderRegSms();
+        }
+        else if(regType===REG_TYPE_MAIL){
+            return renderRegMail();
+        }
+        else if (regType===REG_TYPE_GUEST){
+            return renderRegGuest();
+        }
         return (<></>);
     }
     const renderRegisterForm = () => {
         return (
             <Spring type="slideUp">
                 <div className={styles.l_title}>{i18n.t("login.reg_title")}</div>
-                {
-                    isAccountLogin?(
-                        <div className={styles.l_type_tabs}>
-                            <button className={`${styles.l_type_btn} ${regType === REG_TYPE_PHONE ? styles.active : ''}`}
-                                    onClick={() => {
-                                        setRegType(REG_TYPE_PHONE);
-                                    }}>
-                                <span>{i18n.t("login.reg_type.phone")}</span>
-                            </button>
-                            <button
-                                className={`${styles.l_type_btn} ${regType === REG_TYPE_ACCOUNT ? styles.active : ''}`}
-                                onClick={() => {
-                                    setRegType(REG_TYPE_ACCOUNT)
-                                }}>
-                                <span>{i18n.t("login.login_type.account")}</span>
-                            </button>
-                        </div>
-                    ):(<></>)
-                }
+                {renderTypeTabs(TYPE_REGISTER)}
                 {renderRegType()}
             </Spring>
         )
@@ -691,7 +953,7 @@ const LoginForm = ({close}) => {
                                 name="phone"
                                 rules={{required: true}}
                                 render={({field: {ref, onChange, value}, fieldState: {error}}) => {
-                                    let message = error ? i18n.t("form_errors.phone_" + country) : "";
+                                    let message = error ? getPhoneError() : "";
                                     return (<PhoneInput
                                         key={"phone"}
                                         className={classNames({'input-control--error': error})}

+ 2 - 1
src/widgets/LoginForm/styles.module.scss

@@ -123,7 +123,8 @@
   flex-direction: row;
   align-items: center;
   justify-content: center;
-  color: var(--text);
+  color: var(--primary-color);
+  font-weight: 700;
 }
 .area_btn{
   display: flex;

+ 106 - 0
src/widgets/WSaveAccount/index.jsx

@@ -0,0 +1,106 @@
+import React, {useEffect, useState} from "react";
+import Popup from "@components/Popup";
+import styles from "./styles.module.scss";
+import SvgIcon from "@components/SvgIcon";
+import {CopyToClipboard} from "react-copy-to-clipboard";
+import {toast} from "react-toastify";
+import {stringFormat} from "@utils/helpers";
+import html2canvas from "html2canvas";
+import Fade from "@mui/material/Fade";
+import Grow from "@mui/material/Grow";
+import Modal from "@mui/material/Modal";
+const i18n=global.i18n;
+const WSaveAccount = () => {
+    const [isShow, setIsShow] = useState(false);
+    const [account, setAccount] = useState("");
+    const [password, setPassword] = useState("");
+    const show=(info)=>{
+        if (info.account!=null)setAccount(info.account);
+        if (info.password!=null)setPassword(info.password);
+        setIsShow(true);
+    }
+    const hide=()=>{
+        setIsShow(false);
+    }
+    const handleScreenshot = () => {
+        const style = document.createElement('style');
+        document.head.appendChild(style);
+        style.sheet?.insertRule('body > div:last-child img { display: inline-block; }');
+        // 获取需要截图的元素
+        const element = document.getElementById('saveAccount');
+
+        // 使用 html2canvas 捕获元素并生成图片
+        html2canvas(element,{
+        }).then(canvas => {
+            // 创建一个链接元素
+            const link = document.createElement('a');
+            link.href = canvas.toDataURL('image/png'); // 获取图片数据 URL
+            link.download = 'accountInfo.png'; // 设置下载的文件名
+            link.click(); // 触发下载
+        });
+    };
+    useEffect(()=>{
+        const saveAccount={
+            show,
+            hide
+        };
+        global.saveAccount=saveAccount;
+        return ()=>{
+        }
+    },[])
+    return (
+        <Modal open={isShow} onClose={hide} closeAfterTransition>
+            <Fade in={isShow} timeout={500}>
+                <div className="popup-container">
+                    <Grow in={isShow} timeout={500}>
+                        <div id={"saveAccount"} className={`popup card card-padded`} style={{minHeight: 'unset'}}>
+                            <button className="popup_close" onClick={hide} aria-label="Close popup">
+                                <i className="icon-xmark"/>
+                            </button>
+                            <div className={styles.container}>
+                                <span className={styles.title}>
+                                    {i18n.t("saveAccount.title")}
+                                </span>
+                                <div className={styles.item_con}>
+                                    <span className={styles.title}>{i18n.t("saveAccount.account")}</span>
+                                    <div className={"input-control"}>
+                                        <div className={styles.item}>
+                                            <span>{account}</span>
+                                            <CopyToClipboard text={account} onCopy={() => {
+                                                toast.success(i18n.t("copy_success"));
+                                            }}>
+                                                <button>
+                                                    <SvgIcon className={styles.icon} herf={"icon_Copy"}></SvgIcon>
+                                                </button>
+                                            </CopyToClipboard>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div className={styles.item_con}>
+                                    <span className={styles.title}>{i18n.t("saveAccount.password")}</span>
+                                    <div className={"input-control"}>
+                                        <div className={styles.item}>
+                                            <span>{password}</span>
+                                            <CopyToClipboard text={password} onCopy={() => {
+                                                toast.success(i18n.t("copy_success"));
+                                            }}>
+                                                <button>
+                                                    <SvgIcon className={styles.icon} herf={"icon_Copy"}></SvgIcon>
+                                                </button>
+                                            </CopyToClipboard>
+                                        </div>
+                                    </div>
+                                </div>
+                                <button className={"button-green"} onClick={() => {
+                                    handleScreenshot();
+                                }}>{i18n.t("saveAccount.title")}</button>
+                                <span className={styles.tips}>{i18n.t("saveAccount.tips")}</span>
+                            </div>
+                        </div>
+                    </Grow>
+                </div>
+            </Fade>
+        </Modal>
+    )
+}
+export default WSaveAccount;

+ 48 - 0
src/widgets/WSaveAccount/styles.module.scss

@@ -0,0 +1,48 @@
+.container {
+  display: flex;
+  flex-direction: column;
+  width: 100%;
+  gap: 1.2rem;
+  pointer-events: auto;
+  & .title{
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    justify-content: center;
+    color: var(--header);
+    font-weight: 600;
+    font-size: 1rem;
+  }
+  .item_con{
+    display: flex;
+    flex-direction: column;
+    gap: 0.5rem;
+    & .title{
+      display: flex;
+      justify-content: flex-start;
+      font-size: 1rem;
+      color: var(--text);
+    }
+    & .item{
+      display: flex;
+      flex-direction: row;
+      align-items: center;
+      justify-content: space-between;
+      width: 100%;
+      font-weight: 600;
+      color: var(--header);
+      & .icon{
+        width: 1.4rem;
+        height: 1.4rem;
+        fill: var(--text);
+      }
+    }
+  }
+  & .tips{
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    justify-content: center;
+    color: var(--coin-yellow);
+  }
+}