zhejiushizhuce il y a 2 ans
Parent
commit
86479d1bbb

+ 6 - 0
src/api/config.js

@@ -15,4 +15,10 @@ export const APP_ROUTES="game/routes";
 export const APP_PAGE_MODULES="game/pageModules";
 //game more
 export const APP_GAME_LIST="game/gameList";
+//login
+export const APP_LOGIN="game/login";
+//Reg
+export const APP_REGISTER="game/register";
+//logout
+export const APP_LOGOUT="game/logout";
 

+ 1 - 0
src/api/request.js

@@ -5,6 +5,7 @@ import axios from "axios";
 
 const service = axios.create({
     baseURL: BASE_API,
+    // withCredentials: true,
     timeout: 10000, // Request timeout
 });
 

BIN
src/assets/logo/logo_small3.png


+ 4 - 4
src/components/Logo/index.jsx

@@ -9,15 +9,15 @@ import PropTypes from 'prop-types';
 import {useThemeProvider} from "@contexts/themeContext";
 import logo_dark from '@assets/logo/logo.png';
 import logo_light from '@assets/logo/logo_w.png';
-import logo_small from '@assets/logo/logo_small.png';
+import logo_small from '@assets/logo/logo_small3.png';
 
 const Logo = ({size = 'md'}) => {
     const {theme,isMobile} = useThemeProvider();
-    // const logourl=isMobile()?logo_small:(theme=='dark'?logo_dark:logo_light);
-    const logourl=(theme=='dark'?logo_dark:logo_light);
+    const logourl=isMobile()?logo_small:(theme=='dark'?logo_dark:logo_light);
+    // const logourl=(theme=='dark'?logo_dark:logo_light);
     return (
         <NavLink className={`${styles.logo} ${styles[size]}`} to="/">
-            <img className={styles.logo_img} src={logourl}/>
+            <img className={`${isMobile()?styles.logo_img_m:styles.logo_img}`} src={logourl}/>
         </NavLink>
     )
 }

+ 4 - 0
src/components/Logo/styles.module.scss

@@ -20,6 +20,10 @@
         width: 85%;
         height: auto;
     }
+    .logo_img_m{
+        width: 66%;
+        height: auto;
+    }
 
     &.sm {
         font-size: 19px;

+ 1 - 1
src/components/SmallLoading/styles.module.scss

@@ -4,7 +4,7 @@
   justify-content: center;
   flex: 1;
   height: 100%;
-  align-self: stretch;
+  //align-self: stretch;
   box-sizing: border-box;
   svg{
     height: 40%;

+ 0 - 1
src/components/Spring.jsx

@@ -52,7 +52,6 @@ const Spring = ({children, index = 1, className, type = 'fade', ...props}) => {
         slideUp,
         zoom
     }
-
     return (
         <animated.div className={className ? className : ''} style={spring[type]} ref={ref}>
             {children}

+ 83 - 0
src/components/SubScreen/index.jsx

@@ -0,0 +1,83 @@
+import PropTypes from "prop-types";
+import styles from "./styles.module.scss";
+import SvgIcon from "@components/SvgIcon";
+import {animated,easings} from "@react-spring/web";
+import {useState,useEffect} from "react";
+import {useSpring} from "@react-spring/core";
+const i18n=global.i18n;
+
+const SubScreen=({open,onClose,children,route,renderComp})=>{
+    const [subOpen,setSubOpen]=useState(open);
+    const duration=230;
+    const delayClose=()=>{
+        if (onClose)onClose();
+    }
+    const close=()=>{
+        setSubOpen(false);
+        setTimeout(function () {
+            delayClose();
+        },duration);
+    }
+    useEffect(()=>{
+        // extra.close=close;
+    },[])
+    const showAni=useSpring({
+        from: {transform: 'translateX(100%) translateZ(0px)'},
+        transform:subOpen?'translateX(0%) translateZ(0px)':'translateX(100%) translateZ(0px)',
+        config: {
+            easing: easings.linear,
+            duration:duration
+        },
+        // onResolve: () => {
+        //     if (!subOpen){
+        //         if (onClose)onClose();
+        //     }
+        // },
+    });
+    const renderHeader=()=>{
+        let title=route?route.title:"";
+        return (
+            <div className={styles.header}>
+                <button className={styles.close} onClick={()=>{
+                    close();
+                }}>
+                    <SvgIcon className={styles.arrow} herf={"icon_Arrow"}/>
+                </button>
+                <div className={styles.hcontent}>
+                    {i18n.t(title)}
+                </div>
+            </div>
+        )
+    }
+    const renderBar=()=>{
+        return (
+            <div id={'subbar'} className={styles.appbar}>
+                {renderHeader()}
+            </div>
+        )
+    }
+    const renderContent=()=>{
+        // addExtraChildProps(children);
+        // let list=children.length==null?[children]:children;
+        // // {children}
+        return (
+            <div className={styles.content}>
+                {/*{children}*/}
+                {renderComp({close})}
+            </div>
+        )
+    }
+    return (
+        <animated.div className={styles.subscreen} style={showAni}>
+            {renderBar()}
+            {renderContent()}
+        </animated.div>
+    )
+}
+SubScreen.propTypes = {
+    open: PropTypes.bool.isRequired,
+    onClose: PropTypes.func.isRequired,
+    children: PropTypes.node.isRequired
+}
+
+export default SubScreen

+ 57 - 0
src/components/SubScreen/styles.module.scss

@@ -0,0 +1,57 @@
+.subscreen{
+  display: flex;
+  position: relative;
+  width: 100%;
+  height: 100%;
+  background: var(--body);
+  & .appbar{
+    display: flex;
+    flex-direction: column;
+    position: fixed;
+    z-index: 100;
+    align-items: center;
+    justify-content: flex-start;
+    //background:var(--widget);
+    width: 100%;
+    min-height: 3.75rem;
+  }
+  & .content{
+    width: 100%;
+    overflow: auto;
+    margin-top: 3.75rem;
+  }
+}
+.header{
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  width:100%;
+  background: var(--widget);
+  border-bottom: 1px solid var(--border);
+  box-shadow:var(--widget-shadow);
+  position:relative;
+  height:3.75rem;
+  padding: 0px 10px;
+  & .close{
+    display: flex;
+    align-items: center;
+    position:fixed;
+    height: 3.75rem;
+    width: 2rem;
+    & .arrow{
+      width: 1.4rem;
+      height: 1.8em;
+      transform: rotate(-180deg);
+      fill: var(--text);
+    }
+  }
+  & .hcontent {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    flex: 1;
+    font-size: 1rem;
+    font-weight: 600;
+    color: var(--header);
+  }
+}

+ 10 - 5
src/components/PasswordInput.jsx → src/components/UInput/PasswordInput.jsx

@@ -1,8 +1,12 @@
 // hooks
 import {useState, useEffect} from 'react';
+import styles from './styles.module.scss';
+import SvgIcon from "@components/SvgIcon";
 
 const PasswordInput = ({innerRef, ...props}) => {
     const [isPasswordVisible, setIsPasswordVisible] = useState(false);
+    const wrapClassName=props.className;
+    props.className=props.inputClass?props.inputClass:"";
 
     const togglePasswordVisibility = e => {
         e.preventDefault();
@@ -14,16 +18,17 @@ const PasswordInput = ({innerRef, ...props}) => {
     }, [props.value]);
 
     return (
-        <div className="field-wrapper">
-            <input className="field"
+        <div className={`input-control ${wrapClassName}`}>
+            <input
                    type={isPasswordVisible ? 'text' : 'password'}
+                   autocomplete="off"
                    ref={innerRef}
                    {...props}/>
-            <button className="field-btn" onClick={togglePasswordVisibility} aria-label="Toggle password visibility">
-                <i className={`icon icon-eye${isPasswordVisible ? '-slash' : '' }`}/>
+            <button className={styles.eye_btn} onClick={togglePasswordVisibility} aria-label="Toggle password visibility">
+                <SvgIcon className={styles.icon} herf={`${isPasswordVisible?'icon_Hide':'icon_View'}`}></SvgIcon>
             </button>
         </div>
     )
 }
 
-export default PasswordInput
+export default PasswordInput

+ 29 - 0
src/components/UInput/PhoneInput.jsx

@@ -0,0 +1,29 @@
+import styles from "./styles.module.scss";
+import {useEffect, useState} from "react";
+
+const PhoneInput = ({innerRef, ...props}) => {
+    const [country, setCountry] = useState("55");
+    const wrapClassName=props.className;
+    props.className=props.inputClass?props.inputClass:"";
+    useEffect(() => {
+        if (props.onChangeCountry!=null)props.onChangeCountry(country);
+    }, [country]);
+    return (
+        <div className={`input-control ${wrapClassName}`}>
+            <div className={styles.l_phone}>
+                <div className={styles.country}>
+                    <div>{"+"+country}</div>
+                </div>
+                <div className={styles.line}></div>
+                <input
+                    type="tel"
+                    ref={innerRef}
+                    {...props}
+                />
+            </div>
+        </div>
+    )
+}
+
+export default PhoneInput
+

+ 26 - 0
src/components/UInput/styles.module.scss

@@ -0,0 +1,26 @@
+.eye_btn{
+  & .icon{
+    font-size: .75rem;
+    width: 1.4em;
+    height: 1.4em;
+    fill: var(--text);
+  }
+}
+
+.l_phone{
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  gap:.5rem;
+  width: 100%;
+  height: 100%;
+  & .line{
+    height: .875rem;
+    width: 1px;
+    background-color: var(--text);
+  }
+  & .country{
+    font-size: .75rem;
+    color: var(--header);
+  }
+}

+ 4 - 0
src/constants/localkey.js

@@ -0,0 +1,4 @@
+const BASE="SG_";
+const LOCAL_USER_INFO="USER_INFO";
+const LOCAL_IS_LOGIN="IS_LOGIN";
+export {LOCAL_USER_INFO,LOCAL_IS_LOGIN};

+ 61 - 1
src/features/api.js

@@ -1,5 +1,13 @@
 import {createAsyncThunk} from "@reduxjs/toolkit";
-import {APP_GAME_LIST, APP_PAGE_MODULES, APP_ROUTES, REQ_STATUS} from "@api/config";
+import {
+    APP_GAME_LIST,
+    APP_LOGIN,
+    APP_LOGOUT,
+    APP_PAGE_MODULES,
+    APP_REGISTER,
+    APP_ROUTES,
+    REQ_STATUS
+} from "@api/config";
 import Http from "@api/request";
 import {updateGameList} from "@features/pageSlice";
 
@@ -56,4 +64,56 @@ export const reqGameList=createAsyncThunk(
         return response;
     },
 )
+//login
+export const reqLogin=createAsyncThunk(
+    APP_LOGIN,
+    async (arg,thunkAPI)=>{
+        //https://api.ouro777.com/game/login?country=55&account=12345678911&password=123456
+        let lang=global.i18n.getCurrLang();
+        let data={
+            country:arg.country,
+            account:arg.account,
+            password:arg.password,
+            lang:lang
+        }
+        let params={
+            url:APP_LOGIN,
+            data:data
+        }
+        const response = await Http.post(params);
+        return response;
+    },
+)
+//register
+export const reqRegister=createAsyncThunk(
+    APP_REGISTER,
+    async (arg,thunkAPI)=>{
+        //https://api.ouro777.com/game/register?country=55&phone=12345678911&password=123456&&repassword=123456
+        let lang=global.i18n.getCurrLang();
+        let data={
+            country:arg.country,
+            phone:arg.account,
+            password:arg.password,
+            repassword:arg.repassword,
+            lang:lang
+        }
+        let params={
+            url:APP_REGISTER,
+            data:data
+        }
+        const response = await Http.post(params);
+        return response;
+    },
+)
+//logout
+export const reqLogout=createAsyncThunk(
+    APP_LOGOUT,
+    async (arg,thunkAPI)=>{
+        let params={
+            url:APP_LOGOUT,
+        }
+        const response = await Http.get(params);
+        return response;
+    },
+)
 //api

+ 117 - 4
src/features/mainSlice.js

@@ -8,8 +8,11 @@ import {
     ROUTE_TYPE_POP_OR_FULL, SIDE_TYPE_NOMAL
 } from "@constants/routes";
 import {REQ_STATUS} from "@api/config";
-import {buildReqReducer, reqGetRoutes} from "@features/api";
-import {dark} from "@mui/material/styles/createPalette";
+import {buildReqReducer, reqGetRoutes, reqLogin, reqLogout, reqRegister} from "@features/api";
+import {toast} from 'react-toastify';
+import {localStorageGet, localStorageSet} from "@utils/helpers";
+import {LOCAL_IS_LOGIN, LOCAL_USER_INFO} from "@constants/localkey";
+const i18n=global.i18n;
 const generateNavs=(routes)=>{
     const navs=[];
     const allNavs=[];
@@ -42,6 +45,9 @@ const generateRoutes=(routes,asyncRoutes=[])=>{
                     route.plv=plevel;
                     route.ppath=ppath;
                     route.lv=plevel+key+"_";
+                    // if (route.path==="#/signin"||route.path==="#/signup"){
+                    //     route.component="Login";
+                    // }
                     allMap[route.path]=route;
                     let type=route.type;
                     if (type===ROUTE_TYPE_IN||type===ROUTE_TYPE_FULL)inRoutes.push(route);
@@ -118,6 +124,85 @@ const reducerRoutes=(state,action)=>{
 const routesStatus=(state,status,arg)=>{
     state.status=status;
 }
+//login
+const getLocalUserInfo=()=>{
+    let user={};
+    let value=localStorageGet(LOCAL_USER_INFO,null);
+    if (value&&value!=""){
+        user=JSON.parse(value);
+    }
+    return user;
+}
+const getLocalIsLogin=()=>{
+    let islogin=localStorageGet(LOCAL_IS_LOGIN,0);
+    islogin=Number(islogin);
+    return islogin;
+}
+const getMsg=(payload)=>{
+    let result=payload.result;
+    if (result==null)result=payload.msg;
+    let msg="";
+    if (result!=null){
+        let key=result[0];
+        let def=result[1];
+        if (i18n.exists(key))msg=i18n.t(key);
+        else msg=def;
+    }
+    return msg;
+}
+const parseLogin=(state,action,arg)=>{
+    let payload=action.payload;
+    let code=payload.code;
+    let msg=getMsg(payload);
+    //success
+    if (code==200){
+        let data=payload.data;
+        state.userInfo=data;
+        state.isLogin=1;
+        localStorageSet(LOCAL_USER_INFO,JSON.stringify(data));
+        localStorageSet(LOCAL_IS_LOGIN,1);
+        toast.success(msg);
+    }
+    else
+    {
+        toast.error(msg);
+    }
+    let callback=arg.callback;
+    if (callback!=null)callback(code);
+}
+const reducerLogin=(state,action,arg)=>{
+    // toast.success("success");
+    parseLogin(state,action,arg);
+}
+const loginStatus=(state,status,arg)=>{
+    state.loginStatus.login=status;
+}
+const reducerReg=(state,action,arg)=>{
+    parseLogin(state,action,arg);
+}
+const regStatus=(state,status,arg)=>{
+    state.loginStatus.register=status;
+}
+const reducerLogout=(state,action,arg)=>{
+    let payload=action.payload;
+    let code=payload.code;
+    let msg=getMsg(payload);
+    if (code==200){
+        state.userInfo={};
+        state.isLogin=0;
+        localStorageSet(LOCAL_USER_INFO,"");
+        localStorageSet(LOCAL_IS_LOGIN,0);
+        toast.success(msg);
+    }
+    else
+    {
+        toast.error(msg);
+    }
+}
+const logoutStatus=(state,status,arg)=>{
+    state.loginStatus.logout=status;
+}
+//login
 export const Main=createSlice({
     name: 'main',
     initialState: {
@@ -126,17 +211,33 @@ export const Main=createSlice({
         asyncRoutes:[],
         styles:{},
         blocks:{},
-        ...generateRoutes(routes)
+        ...generateRoutes(routes),
+        loginStatus:{
+            login:REQ_STATUS.NONE,
+            register:REQ_STATUS.NONE,
+            logout:REQ_STATUS.NONE,
+        },
+        userInfo:getLocalUserInfo(),
+        isLogin:getLocalIsLogin()
     },
     reducers: {
+        logout:(state, action)=>{
+            state.userInfo={};
+            state.isLogin=0;
+            localStorageSet(LOCAL_USER_INFO,"");
+            localStorageSet(LOCAL_IS_LOGIN,0);
+        }
     },
     extraReducers: (builder) => {
         // Add reducers for additional action types here, and handle loading state as needed
         // reducerRoutes(builder);
         buildReqReducer(builder,reqGetRoutes,reducerRoutes,routesStatus);
+        buildReqReducer(builder,reqLogin,reducerLogin,loginStatus);
+        buildReqReducer(builder,reqRegister,reducerReg,regStatus);
+        buildReqReducer(builder,reqLogout,reducerLogout,logoutStatus);
     },
 })
-export const {} = Main.actions;
+export const {logout} = Main.actions;
 //数据获取
 export const getFetchStatus=(state)=>{
     return state.main.status;
@@ -207,6 +308,18 @@ export const isOutRoute=(path)=>(state)=>{
     if (route)return route.type===ROUTE_TYPE_OUTLINK;
     return false;
 }
+//login
+export const getLoginStatus=(state)=>{
+    return state.main.loginStatus.login;
+}
+export const getRegStatus=(state)=>{
+    return state.main.loginStatus.register;
+}
+export const isLogined=(state)=>{
+    // console.log(state.main.isLogin)
+    return state.main.isLogin;
+    // return 1;
+}
 //数据获取
 export default Main.reducer;
 

+ 1 - 1
src/hooks/useScrollLock.js

@@ -14,4 +14,4 @@ const useScrollLock = () => {
     return setIsLocked
 }
 
-export default useScrollLock
+export default useScrollLock

+ 1 - 1
src/layout/BottomNav/styles.module.scss

@@ -7,7 +7,7 @@
     bottom: 0;
     left: 0;
     right: 0;
-    z-index: 1000;
+    z-index: var(--zindex-in);
     height: 4.125rem;
     display: flex;
     align-items: center;

+ 1 - 1
src/layout/BreadCrumb/index.jsx

@@ -8,7 +8,7 @@ import {useThemeProvider} from "@contexts/themeContext";
 import SvgIcon from "@components/SvgIcon";
 const i18n=global.i18n;
 const BreadCrumb=()=>{
-    const {isMobile,showHeader,setShowHeader} = useThemeProvider();
+    const {isMobile} = useThemeProvider();
     const {pathname} = useLocation();
     const chain=useSelector(getRouteChain(pathname));
     const currRoute=useSelector(getRoute(pathname));

+ 48 - 21
src/layout/Navbar/index.jsx

@@ -24,7 +24,7 @@ import {useSpring} from "@react-spring/core";
 import {useWindowSize} from "react-use";
 import ResizeObserver from "resize-observer-polyfill";
 import {useSelector} from "react-redux";
-import {allTopNavs, getRoute} from "@features/mainSlice";
+import {allTopNavs, getRoute, getRouteChain, isLogin, isLogined} from "@features/mainSlice";
 const i18n=global.i18n;
 
 const StyledToggleGroup= styled(ToggleButtonGroup)`
@@ -120,6 +120,7 @@ const DesktopNavToggle=()=>{
 
 const DesktopSignInUp=()=>{
     const navigate=useNavigate();
+    const isLogin=useSelector(isLogined);
     return (
         <div className="d-flex align-items-center justify-content-end">
             <IconButton aria-label="search">
@@ -127,12 +128,18 @@ const DesktopSignInUp=()=>{
                     <use xlinkHref={`${symbol}#icon_Search`} />
                 </svg>
             </IconButton>
-            <Button  className={`m-button ${styles.btn_sign_in}`} onClick={()=>{
-                navigate("#/signin");
-            }}>{i18n.t('sign_in')}</Button>
-            <Button className={`${styles.btn_sign_up}`} onClick={()=>{
-                navigate("#/signup");
-            }}>{i18n.t('sign_up')}</Button>
+            {
+                !isLogin?(
+                    <>
+                        <Button  className={`m-button ${styles.btn_sign_in}`} onClick={()=>{
+                            navigate("#/signin");
+                        }}>{i18n.t('sign_in')}</Button>
+                        <Button className={`${styles.btn_sign_up}`} onClick={()=>{
+                            navigate("#/signup");
+                        }}>{i18n.t('sign_up')}</Button>
+                    </>
+                ):(<></>)
+            }
             <Button  className="m-button">
                 <svg className={"ls-icon"} style={{width:'20px',height:'20px'}}>
                     <use xlinkHref={`${symbol}#icon_Chat`} />
@@ -161,14 +168,23 @@ const DesktopNav = () => {
 }
 const MobileSignInUp=()=>{
     const navigate=useNavigate();
+    const isLogin=useSelector(isLogined);
     return (
         <div className="d-flex align-items-center justify-content-end">
-            <Button  className={`m-button ${styles.btn_sign_in} ${styles.btn_sign_in_mobile}`} onClick={()=>{
-                navigate("#/signin");
-            }}>{i18n.t('sign_in')}</Button>
-            <Button  className={`${styles.btn_sign_up} ${styles.btn_sign_up_mobile}`} onClick={()=>{
-                navigate("#/signup");
-            }}>{i18n.t('sign_up')}</Button>
+            {
+                !isLogin?(
+                    <>
+                        <Button  className={`m-button ${styles.btn_sign_in} ${styles.btn_sign_in_mobile}`} onClick={()=>{
+                            navigate("#/signin");
+                        }}>{i18n.t('sign_in')}</Button>
+                        <Button  className={`${styles.btn_sign_up} ${styles.btn_sign_up_mobile}`} onClick={()=>{
+                            navigate("#/signup");
+                        }}>{i18n.t('sign_up')}</Button>
+                    </>
+                ):(
+                    <></>
+                )
+            }
             {/*<Button  className="m-button">*/}
             {/*    <svg className={"ls-icon"} style={{width:'1.4rem',height:'1.4rem'}}>*/}
             {/*        <use xlinkHref={`${symbol}#icon_Language`} />*/}
@@ -181,7 +197,7 @@ const MobileNav=()=>{
     return (
         <div
             className={`d-flex align-items-center justify-content-between ${styles.header} ${styles.header_mobile}`}>
-            <div className={`d-flex flex-row flex-1`}>
+            <div className={`d-flex flex-row flex-1`} style={{maxWidth:"55%"}}>
                 <SidebarTrigger/>
                 <Logo size="sm"/>
             </div>
@@ -196,6 +212,9 @@ const PageHeader = () => {
     const navigate=useNavigate();
     const barRef = useRef(null);
     const currRoute=useSelector(getRoute(pathname));
+    const chain=useSelector(getRouteChain(pathname));
+    const {isMobile,showHeader,setShowHeader} = useThemeProvider();
+    const [showMHeader, setShowMHeader] = useState(true);
     let lastscroll=0;
     const checkNavActive=(lv)=>{
         if (currRoute&&currRoute.lv)return currRoute.lv.startsWith(lv);
@@ -207,17 +226,18 @@ const PageHeader = () => {
         else return '0px';
     }
     const handleScroll=()=>{
+        if (!barRef.showHeader)return;
         let scrollTop = document.documentElement.scrollTop; //滚动条滚动高度
         let dt=scrollTop-lastscroll;
         lastscroll=scrollTop;
         let show=false;
         if (dt>20){
             show=false;
-            setShowHeader(show);
+            setShowMHeader(show);
         }
         else if (dt<-20){
             show=true;
-            setShowHeader(show);
+            setShowMHeader(show);
         }
     }
     const changeHeaderHeight=()=>{
@@ -247,13 +267,20 @@ const PageHeader = () => {
                 resizeObserver.unobserve(barRef.current);
             }
         };
-    }, []);
+    }, [pathname]);
+    useEffect(()=>{
+        setShowMHeader(showHeader);
+        barRef.showHeader=showHeader;
+    },[showHeader]);
+
+    useEffect(()=>{
+        let len=chain.length;
+        setShowHeader(len<=1);
+    },[pathname]);
 
-    const {isMobile,showHeader,setShowHeader} = useThemeProvider();
-    // const [showHeader, setShowHeader] = useState(true);
     const headerAni=useSpring({
         from: {transform: 'translateY(-100%) translateZ(0px)'},
-        transform:showHeader?'translateY(0%) translateZ(0px)':'translateY(-100%) translateZ(0px)',
+        transform:showMHeader?'translateY(0%) translateZ(0px)':'translateY(-100%) translateZ(0px)',
         config: {
             easing: easings.easeOutBack,
             duration:300
@@ -308,7 +335,7 @@ const PageHeader = () => {
     }
 
     return (
-        <div id={'appbar'} ref={barRef} className={`${styles.appbar} ${isMobile()?styles.mobile:''}`}>
+        <div id={'appbar'} ref={barRef} className={`${styles.appbar} ${isMobile()?styles.mobile:''} ${isMobile()&&!showHeader?styles.hide_header:''}`}>
             {
                 isMobile()?renderMobile():renderDesktop()
             }

+ 8 - 4
src/layout/Navbar/styles.module.scss

@@ -2,7 +2,7 @@
   display: flex;
   flex-direction: column;
   position: fixed;
-  z-index: 1100;
+  z-index: var(--zindex-in);
   align-items: center;
   justify-content: flex-start;
   background:var(--widget);
@@ -12,6 +12,9 @@
     background:transparent;
     height: 8.5rem;
   }
+  &.hide_header{
+    height:3.75rem;
+  }
 }
 .header{
   height:100%;
@@ -25,7 +28,7 @@
     padding-left:270px;
   }
   &_mobile{
-    position:relative;
+    position:fixed;
     height:3.75rem;
     padding: 0px 10px;
     z-index: 2;
@@ -33,12 +36,13 @@
 }
 .header_room{
   display: flex;
-  position: relative;
+  position: fixed;
   height: 4.75rem !important;
   background-color:var(--widget);
   z-index:1;
   padding: 0.75rem;
-  margin-top: -1px;
+  //margin-top: -1px;
+  top: 3.75rem;
   width: 100%;
   overflow-x: auto;
   overflow-y: hidden;

+ 48 - 3
src/layout/Routes/RouteWrapper.jsx

@@ -1,11 +1,30 @@
-import React, {useMemo, useState} from 'react';
+import React, {useMemo, useState, useEffect, useRef} from 'react';
 import Popup from "@components/Popup";
 import { useNavigate } from 'react-router-dom';
 import {ROUTE_TYPE_FULL, ROUTE_TYPE_IN, ROUTE_TYPE_POP, ROUTE_TYPE_POP_OR_FULL} from "@constants/routes";
+import {useThemeProvider} from "@contexts/themeContext";
+import SubScreen from "@components/SubScreen";
+import useScrollLock from "@hooks/useScrollLock";
+import {useLocation} from "react-router";
 const i18n=global.i18n;
 const RouteWrapper = (props) => {
+    const {isMobile} = useThemeProvider();
     const { Comp, route} = props;
     const navigate = useNavigate()
+    const {pathname,hash} = useLocation();
+    const setLock=(isLocked)=>{
+        if (isLocked) {
+            document.documentElement.classList.add('no-scroll');
+        } else {
+            document.documentElement.classList.remove('no-scroll');
+        }
+    }
+    useEffect(() => {
+        if (route){
+            if (route.type===ROUTE_TYPE_IN||route.type===ROUTE_TYPE_FULL)setLock(false);
+            else setLock(true);
+        }
+    }, [pathname,hash]);
     /** useMemo 缓存query,避免每次生成生的query */
     const renderIn=()=>{
         let isexit=false;
@@ -23,7 +42,8 @@ const RouteWrapper = (props) => {
     }
     const renderPop=()=>{
         let comp=(<></>);
-        if (Comp!=null)comp=Comp;
+        // if (Comp!=null)comp=Comp;
+        if (Comp!=null)comp=<Comp pageID={route.id} />;
         return(
             <Popup pageID={route.id} open={true} onClose={handleClose}>
                 <div className="pop_content">
@@ -32,13 +52,38 @@ const RouteWrapper = (props) => {
             </Popup>
         )
     }
+    const renderSub=()=>{
+        // let comp=(<></>);
+        const renderComp=(extra)=>{
+            if (Comp!=null){
+                return (<Comp pageID={route.id} {...extra} />)
+            }
+            else return (<></>);
+        }
+        // if (Comp!=null)comp=<Comp pageID={route.id} />;
+        // if (Comp!=null)comp=Comp;
+        return (
+            <SubScreen  route={route} onClose={handleClose} open={true} renderComp={renderComp}>
+                {/*{comp}*/}
+            </SubScreen>
+        )
+    }
+    const renderPopOrFull=()=>{
+        return (
+            <>
+                {
+                    isMobile()?renderSub():renderPop()
+                }
+            </>
+        )
+    }
     const renderComp=()=>{
         let comp=(<></>);
         if (route.type===ROUTE_TYPE_FULL||route.type===ROUTE_TYPE_IN){
             comp=renderIn();
         }
         else if (route.type===ROUTE_TYPE_POP_OR_FULL){
-            comp=renderPop();
+            comp=renderPopOrFull();
         }
         else if (route.type===ROUTE_TYPE_POP){
             comp=renderPop();

+ 38 - 6
src/layout/Sidebar/index.jsx

@@ -49,9 +49,19 @@ import spin_2 from "@assets/activity/spin_2.png";
 import spin_3 from "@assets/activity/spin_3.png";
 import imgrefer from "@assets/activity/rio_refer.png";
 import imgrefer_f from "@assets/activity/rio_refer_f.png";
-import {useSelector} from "react-redux";
-import {allNavs, getBlockColumn, getRoute, getRouteStyles, isFullRoute, nBlocks} from "@features/mainSlice";
+import {useDispatch, useSelector} from "react-redux";
 import {
+    allNavs,
+    getBlockColumn,
+    getRoute,
+    getRouteStyles,
+    isFullRoute,
+    isLogined,
+    logout,
+    nBlocks
+} from "@features/mainSlice";
+import {
+    ROUTE_TYPE_OUTLINK,
     ROUTE_TYPE_POP,
     ROUTE_TYPE_POP_OR_FULL,
     SIDE_TYPE_GRID,
@@ -59,6 +69,7 @@ import {
     SIDE_TYPE_SPECIAL
 } from "@constants/routes";
 import SvgIcon from "@components/SvgIcon";
+import {reqLogout} from "@features/api";
 const drawWidth = 250;
 const drawMobieWidth=250;
 const i18n=global.i18n;
@@ -144,6 +155,8 @@ const Sidebar = () => {
     const currRoute = useSelector(getRoute(pathname));
     const blocksColumn=useSelector(getBlockColumn);
     const routeStyles=useSelector(getRouteStyles);
+    const isLogin=useSelector(isLogined);
+    const dispatch = useDispatch();
     const handleActEnter = () => {
         if (open)return;
         setIsActHovered(true);
@@ -200,10 +213,11 @@ const Sidebar = () => {
         return lv===link.lv;
     }
     const renderIconLink=(link,olStyle)=>{
-        let ispop=link.type===ROUTE_TYPE_POP||link.type===ROUTE_TYPE_POP_OR_FULL;
+        let ispop=link.type===ROUTE_TYPE_POP||link.type===ROUTE_TYPE_POP_OR_FULL||link.type===ROUTE_TYPE_OUTLINK;
         let linkIcon=(<SvgIcon className={"icon"} herf={link.icon}/>);
+        let target=link.type===ROUTE_TYPE_OUTLINK?"_blank":"";
         let linkA=ispop?(
-            <a href={link.path}>
+            <a href={link.path} target={target}>
                 {linkIcon}
             </a>
         ):(
@@ -250,7 +264,7 @@ const Sidebar = () => {
 
     const renderRLink=(link)=>{
         let isActive=checkPath(link);
-        let ispop=link.type===ROUTE_TYPE_POP||link.type===ROUTE_TYPE_POP_OR_FULL;
+        let ispop=link.type===ROUTE_TYPE_POP||link.type===ROUTE_TYPE_POP_OR_FULL||link.type===ROUTE_TYPE_OUTLINK;
         let clname=`${isActive?'active':''} h4`;
         let linktxt=(
             <>
@@ -258,7 +272,8 @@ const Sidebar = () => {
                 {i18n.t(`${link.title}`)}
             </>
         )
-        return ispop?(<a className={clname} href={link.path}>{linktxt}</a>):(<NavLink className={clname} to={link.path}>{linktxt}</NavLink>)
+        let target=link.type===ROUTE_TYPE_OUTLINK?"_blank":"";
+        return ispop?(<a className={clname} target={target} href={link.path}>{linktxt}</a>):(<NavLink className={clname} to={link.path}>{linktxt}</NavLink>)
     }
     const renderSingleLink=(link)=>{
         let styles=getOLStyles(link.style);
@@ -362,9 +377,26 @@ const Sidebar = () => {
             </StyledBlock>
         );
     }
+    const renderLogout=()=>{
+        return (
+            <>
+                {
+                    isLogin?(
+                        <StyledButton open={open} className="h4" onClick={()=>{
+                            dispatch(reqLogout());
+                        }}>
+                            <SvgIcon className={"icon"} herf={"icon_Logout"}/>
+                            {open?i18n.t("links.log_out"):""}
+                        </StyledButton>
+                    ):(<></>)
+                }
+            </>
+        )
+    }
     const renderSuport=()=>{
         return (
             <StyledBlock open={open}>
+                {renderLogout()}
                 <MTooltip disableHoverListener={open} title={i18n.t("links.live_support")} arrow placement="right">
                     <StyledButton open={open} className="h4" fill={"#3bc117"}>
                         <SvgIcon className={"icon"} herf={"icon_Support"}/>

+ 8 - 3
src/locales/index.js

@@ -8,12 +8,14 @@ import pt from './languages/pt_BR.json';
 
 const resources = {
     en: {
-        translation: en
+        key:"en_US",
+        translation: en,
     },
     // zh: {
     //     translation: zh
     // }
     pt:{
+        key:"pt_BR",
         translation:pt
     }
 }
@@ -44,6 +46,9 @@ i18n
         // 初始化
         i18n.setLocalLanguage(language);
     });
-
-
+i18n.getCurrLang=()=>{
+    let res=resources[i18n.language];
+    if (res)return res.key;
+    return "";
+}
 export default i18n;

+ 43 - 1
src/locales/languages/en_US.json

@@ -12,7 +12,8 @@
     "bonus": "Bonus",
     "live_support": "Live Support",
     "quest": "QUEST",
-    "spin": "SPIN"
+    "spin": "SPIN",
+    "log_out": "Logout"
   },
   "tab": {
     "withdraw": "Withdraw",
@@ -55,5 +56,46 @@
   "game_search": {
     "input_ph": "Game name",
     "cancel": "Cancel"
+  },
+  "login": {
+    "login_title": "Sign In",
+    "phone_ph": "Phone Number",
+    "login_pwd": "Login Password",
+    "forgot": "Forgot your password?",
+    "sign_in_btn": "Sign In",
+    "reg_new_des": "New to 24680?",
+    "reg_new_btn": "Create account",
+    "reg_title": "Sign Up",
+    "re_pwd": "Confirm login password",
+    "sign_up_btn": "Sign Up",
+    "login_des": "Already have an account?",
+    "account": {
+      "empty": "Please enter username!"
+    },
+    "password": {
+      "empty": "Please enter password!"
+    },
+    "success": "Registration completed successfully!",
+    "notfound": "Your account was not found, please register or try again!"
+  },
+  "logout": {
+    "success": "You have exited successfully."
+  },
+  "reg": {
+    "account": {
+      "empty": "Please enter username!"
+    },
+    "password": {
+      "notsame": "Passwords entered twice are inconsistent, please enter again!"
+    },
+    "fail": {
+      "email_exist": "The email already exists, change it and try to register again!",
+      "phone_exist": "The phone number already exists, change it and try to register again!",
+      "suspend": "Registration suspended",
+      "stoplogin": "Login Pause",
+      "account_exist": "The account already exists, change another account name and try to register again!",
+      "ipmax": "Same IP cannot register multiple accounts"
+    },
+    "success": "Registration completed successfully!"
   }
 }

+ 3 - 6
src/pages/Login.jsx

@@ -1,13 +1,10 @@
 // components
-import AuthLayout from '@components/AuthLayout';
 import LoginForm from '@widgets/LoginForm';
 
-const Login = () => {
+const Login = ({...props}) => {
     return (
-        <AuthLayout title="Login">
-            <LoginForm/>
-        </AuthLayout>
+        <LoginForm {...props} />
     );
 }
 
-export default Login
+export default Login

+ 2 - 0
src/pages/index.jsx

@@ -1,7 +1,9 @@
 import {lazy} from "react";
 const ModulePage=lazy(()=>import('@pages/ModulePage'));
 const Home=lazy(()=>import('@pages/Home'));
+const Login=lazy(()=>import("@pages/Login"))
 export default {
     ModulePage:ModulePage,
     Home:Home,
+    login:Login,
 }

+ 54 - 1
src/styles/_elements.scss

@@ -56,8 +56,9 @@
 }
 
 .field {
-    height: 40px;
+    height: 2.75rem;
     width: 100%;
+    background-color: var(--nav-item-bg);
     border: 1px solid var(--border);
     padding: 0 12px;
     border-radius: 4px;
@@ -662,3 +663,55 @@ a {
 .recharts-tooltip-wrapper {
     z-index: 10;
 }
+
+
+//add new
+.input-control{
+    position: relative;
+    display: flex;
+    align-items: center;
+    padding: 0 .75rem;
+    height: 2.875rem;
+    border: 1px solid transparent;
+    background-color: var(--nav-item-bg);
+    border-radius: var(--border-radius);
+    opacity: 1;
+    &:focus-within{
+        border: 1px solid var(--highlight);
+    }
+    &--error {
+        border-color: var(--red);
+    }
+    input{
+        flex: 1;
+        width: 100%;
+        height: 100%;
+        min-width: 1rem;
+        padding: 0;
+        border: none;
+        background-color: transparent;
+        color: var(--header);
+        font-weight: 600;
+        &::placeholder {
+            font-weight: 500;
+            color: var(--header);
+            opacity: 0.6;
+        }
+    }
+}
+.button-green{
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 100%;
+    min-height: 2.75rem;
+    color: #fff;
+    background-color: #1d803ab3;
+    background-image: conic-gradient(from 1turn, rgba(88, 175, 16, 1), rgba(29, 128, 58, 1));
+    user-select: none;
+    border-radius: var(--border-radius);
+    font-weight: 700;
+    cursor: pointer;
+    transition: transform .2s cubic-bezier(.36,.66,.04,1);
+    text-transform: capitalize;
+}

+ 1 - 1
src/styles/_global.scss

@@ -79,7 +79,7 @@ body {
 }
 .app-hash{
     position: fixed;
-    z-index: 1000;
+    z-index: var(--zindex-second);
     inset: 0px;
 }
 .pop{

+ 6 - 1
src/styles/_vars.scss

@@ -25,7 +25,8 @@ $width-desktop-big-max:1564px;
     --header-dark: #fff;
     --btn-text-light: #3E4237;
     --nav-arrow: #A2C0D4;
-    --accent: #FDCA40;
+    //--accent: #FDCA40;
+    --accent: #3BC117;
     --azure: #0496FF;
     --blue: #2662F0;
     --green: #13783e;
@@ -63,6 +64,10 @@ $width-desktop-big-max:1564px;
     --grid-num:7;
     --side-open:250px;
     --side-close:65px;
+    --zindex-in:500;
+    --zindex-second:1000;
+    --zindex-drawer:1200;
+    --zindex-popup:1300;
 }
 
 //@media screen and (min-width: 414px) {

+ 9 - 1
src/utils/helpers.js

@@ -116,4 +116,12 @@ export const modifyCardNumber = (cardNumber) => {
     const modifiedCardNumber = maskedDigits + lastDigits.slice(-4);
 
     return modifiedCardNumber;
-}
+}
+export const localStorageGet=(key,defaultValue="")=>{
+    let value=localStorage.getItem(key);
+    if (value==null)value=defaultValue;
+    return value;
+}
+export const localStorageSet=(key,value)=>{
+    localStorage.setItem(key,value);
+}

+ 2 - 2
src/widgets/ChangePassword.jsx

@@ -1,6 +1,6 @@
 // components
 import Spring from '@components/Spring';
-import PasswordInput from '@components/PasswordInput';
+import PasswordInput from '@components/UInput/PasswordInput';
 import ResetPasswordPopup from '@components/ResetPasswordPopup';
 import {Controller, useForm} from 'react-hook-form';
 import {toast} from 'react-toastify';
@@ -95,4 +95,4 @@ const ChangePassword = () => {
     )
 }
 
-export default ChangePassword
+export default ChangePassword

+ 0 - 82
src/widgets/LoginForm.jsx

@@ -1,82 +0,0 @@
-// components
-import PasswordInput from '@components/PasswordInput';
-import BasicCheckbox from '@ui/BasicCheckbox';
-import ResetPasswordPopup from '@components/ResetPasswordPopup';
-
-// hooks
-import {useForm, Controller} from 'react-hook-form';
-import {useNavigate} from 'react-router-dom';
-import {useState} from 'react';
-
-// utils
-import classNames from 'classnames';
-
-const LoginForm = () => {
-    const [open, setOpen] = useState(false);
-    const {register, handleSubmit, formState: {errors}, control} = useForm({
-        defaultValues: {
-            email: '',
-            password: '',
-            rememberMe: false
-        }
-    });
-    const navigate = useNavigate();
-
-    const onSubmit = () => navigate('/');
-
-    const handleResetPassword = e => {
-        e.preventDefault();
-        setOpen(true);
-    }
-
-    return (
-        <>
-            <h1>Account login</h1>
-            <form>
-                <div className="d-flex flex-column g-10" style={{margin: '20px 0 30px'}}>
-                    <div className="d-flex flex-column g-20">
-                        <input className={classNames('field', {'field--error': errors.email})}
-                               type="text"
-                               placeholder="Login"
-                               {...register('email', {required: true, pattern: /^\S+@\S+$/i})}/>
-                        <Controller control={control}
-                                    name="password"
-                                    rules={{required: true}}
-                                    render={({field: {ref, onChange, value}, fieldState: {error}}) => (
-                                        <PasswordInput
-                                            className={classNames('field', {'field--error': error})}
-                                            value={value}
-                                            onChange={e => onChange(e.target.value)}
-                                            placeholder="Password"
-                                            innerRef={ref}/>
-                                    )}
-                        />
-                    </div>
-                    <div className="d-flex align-items-center g-10">
-                        <Controller control={control}
-                                    name="rememberMe"
-                                    render={({field: {ref, onChange, value}}) => (
-                                        <BasicCheckbox id="rememberMe"
-                                                       checked={value}
-                                                       onChange={e => onChange(e.target.checked)}
-                                                       innerRef={ref}/>
-                                    )}
-                        />
-                        <label htmlFor="rememberMe">Remember me</label>
-                    </div>
-                </div>
-                <div className="d-flex justify-content-between align-items-center">
-                    <button className="btn btn--sm" type="submit" onClick={handleSubmit(onSubmit)}>
-                        Submit
-                    </button>
-                    <button className="text-button text-button--sm" onClick={handleResetPassword}>
-                        Reset password
-                    </button>
-                </div>
-            </form>
-            <ResetPasswordPopup open={open} onClose={() => setOpen(false)}/>
-        </>
-    )
-}
-
-export default LoginForm

+ 229 - 0
src/widgets/LoginForm/index.jsx

@@ -0,0 +1,229 @@
+// components
+import PasswordInput from '@components/UInput/PasswordInput';
+import BasicCheckbox from '@ui/BasicCheckbox';
+import ResetPasswordPopup from '@components/ResetPasswordPopup';
+
+// hooks
+import {useForm, Controller} from 'react-hook-form';
+import {useNavigate} from 'react-router-dom';
+import React, {useState} from 'react';
+import styles from './styles.module.scss';
+import logo_dark from '@assets/logo/logo.png';
+import logo_light from '@assets/logo/logo_w.png';
+
+// utils
+import classNames from 'classnames';
+import {useThemeProvider} from "@contexts/themeContext";
+import Spring from "@components/Spring";
+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} 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} from "@features/api";
+
+const i18n=global.i18n;
+const TYPE_LOGIN="login";
+const TYPE_REGISTER="register";
+
+
+const LoginForm = ({close}) => {
+    const {theme,isMobile} = useThemeProvider();
+    const [showType,setShowType]=useState(TYPE_LOGIN);
+    const [open, setOpen] = useState(false);
+    const [country, setCountry] = useState("55");
+    const logourl=(theme=='dark'?logo_dark:logo_light);
+    const loginStatus=useSelector(getLoginStatus);
+    const regStatus=useSelector(getRegStatus);
+    const dispatch = useDispatch();
+    const {register, handleSubmit, formState: {errors}, control} = useForm({
+        defaultValues: {
+            // country:'55',
+            account: '',
+            password: '',
+            email:"",
+            repassword:"",
+            lang:""
+        }
+    });
+    const {pathname,hash} = useLocation();
+    useEffect(() => {
+        if (hash==="#/signin"){
+            setShowType(TYPE_LOGIN);
+        }
+        else if (hash==="#/signup"){
+            setShowType(TYPE_REGISTER);
+        }
+    }, [pathname,hash]);
+    const onCallback=(code)=>{
+        if (code==200){
+            if (close)close();
+        }
+    }
+    const onSubmitLogin=(data)=>{
+        data.country=country;
+        data.callback=onCallback;
+        dispatch(reqLogin(data));
+    }
+    const onSubmitReg=(data)=>{
+        data.country=country;
+        data.callback=onCallback;
+        dispatch(reqRegister(data));
+    }
+
+    const renderLoading=()=>{
+        return (<SmallLoading fill={"#fff"}/>)
+    }
+    const renderLogo=()=>{
+        return (
+            <div className={styles.l_logo}>
+                <img className={styles.logo_img} src={logourl}/>
+            </div>
+        )
+    }
+    const renderLoginForm=()=>{
+        return (
+            <Spring type="slideUp" >
+                <div className={styles.l_title}>{i18n.t("login.login_title")}</div>
+                <form autocomplete="off" className={styles.l_form} onSubmit={handleSubmit((data) => {
+                    onSubmitLogin(data);
+                })}>
+                    <Controller control={control}
+                                name="account"
+                                rules={{required: true}}
+                                render={({field: {ref, onChange, value}, fieldState: {error}}) => (
+                                    <PhoneInput
+                                        className={classNames({'input-control--error': error})}
+                                        value={value}
+                                        onChange={e => onChange(e.target.value)}
+                                        onChangeCountry={(country)=>{
+                                            setCountry(country);
+                                        }}
+                                        placeholder={i18n.t('login.phone_ph')}
+                                        innerRef={ref}/>
+                                )}
+                    />
+                    <Controller control={control}
+                                name="password"
+                                rules={{required: true}}
+                                render={({field: {ref, onChange, value}, fieldState: {error}}) => (
+                                    <PasswordInput
+                                        className={classNames({'input-control--error': error})}
+                                        value={value}
+                                        onChange={e => onChange(e.target.value)}
+                                        placeholder={i18n.t('login.login_pwd')}
+                                        innerRef={ref}/>
+                                )}
+                    />
+                    <div className={styles.l_forgot}>
+                        <button className={styles.forgot_btn}>
+                            {i18n.t("login.forgot")}
+                        </button>
+                    </div>
+                    <button className={"button-green"}>
+                        {
+                            loginStatus===REQ_STATUS.LOADING?renderLoading():i18n.t("login.sign_in_btn")
+                        }
+                    </button>
+                    {/*<div className={styles.l_reg_new}>*/}
+                    {/*    <div>{i18n.t("login.reg_new_des")}</div>*/}
+                    {/*    <div className={styles.reg_btn} onClick={()=>{*/}
+                    {/*        setShowType(TYPE_REGISTER);*/}
+                    {/*    }}>*/}
+                    {/*        {i18n.t("login.reg_new_btn")}*/}
+                    {/*    </div>*/}
+                    {/*</div>*/}
+                </form>
+            </Spring>
+        )
+    }
+    const renderRegisterForm=()=>{
+        return (
+            <Spring type="slideUp">
+                <div className={styles.l_title}>{i18n.t("login.reg_title")}</div>
+                <form autocomplete="off" className={styles.l_form} onSubmit={handleSubmit((data) => {
+                    onSubmitReg(data);
+                })}>
+                    <Controller control={control}
+                                name="account"
+                                rules={{required: true}}
+                                render={({field: {ref, onChange, value}, fieldState: {error}}) => (
+                                    <PhoneInput
+                                        className={classNames({'input-control--error': error})}
+                                        value={value}
+                                        onChange={e => onChange(e.target.value)}
+                                        onChangeCountry={(country)=>{
+                                            setCountry(country);
+                                        }}
+                                        placeholder={i18n.t('login.phone_ph')}
+                                        innerRef={ref}/>
+                                )}
+                    />
+                    <Controller control={control}
+                                name="password"
+                                rules={{required: true}}
+                                render={({field: {ref, onChange, value}, fieldState: {error}}) => (
+                                    <PasswordInput
+                                        className={classNames({'input-control--error': error})}
+                                        value={value}
+                                        onChange={e => onChange(e.target.value)}
+                                        placeholder={i18n.t('login.login_pwd')}
+                                        innerRef={ref}/>
+                                )}
+                    />
+                    <Controller control={control}
+                                name="repassword"
+                                rules={{required: true}}
+                                render={({field: {ref, onChange, value}, fieldState: {error}}) => (
+                                    <PasswordInput
+                                        className={classNames({'input-control--error': error})}
+                                        value={value}
+                                        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>
+                    {/*<div className={styles.l_reg_new}>*/}
+                    {/*    <div>{i18n.t("login.login_des")}</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();
+        }
+        return (
+            <div className={styles.l_form_container}>
+                {form}
+            </div>
+        )
+    }
+    return (
+        <div className={styles.container}>
+            {renderLogo()}
+            {renderForm()}
+        </div>
+    )
+}
+
+export default LoginForm;

+ 67 - 0
src/widgets/LoginForm/styles.module.scss

@@ -0,0 +1,67 @@
+.container{
+  width: 100%;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  position: relative;
+  background-color: var(--widget-bg);
+  & .l_logo{
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 100%;
+    min-height: 10rem;
+    padding: 1rem;
+    & .logo_img{
+      width: 60%;
+      height: auto;
+    }
+  }
+  & .l_form_container{
+    width: 100%;
+    height: auto;
+    padding: 2rem;
+    backdrop-filter: blur(7px);
+    display: flex;
+    flex-direction: column;
+  }
+}
+.l_title{
+  display: flex;
+  align-items: center;
+  height: 1.25rem;
+  line-height: 1.25rem;
+  color: var(--header);
+  font-size: 1rem;
+  margin: 0;
+  font-weight: 700;
+  text-transform: capitalize;
+}
+.l_form{
+  display: flex;
+  flex-direction: column;
+  margin-top: 1.5rem;
+  gap: 1rem;
+}
+.l_forgot{
+  display: flex;
+  align-items: center;
+  justify-content: flex-end;
+  & .forgot_btn{
+    font-weight: 600;
+    color: var(--header);
+  }
+}
+.l_reg_new{
+  display: flex;
+  align-items: center;
+  height: 1rem;
+  color: var(--text);
+  & .reg_btn{
+    margin-left: .75rem;
+    cursor: pointer;
+    font-size: 1rem;
+    font-weight: 700;
+    color: var(--primary-color);
+  }
+}

+ 2 - 2
src/widgets/PrivacyPolicy/index.jsx

@@ -4,7 +4,7 @@ import styles from './styles.module.scss';
 // components
 import Spring from '@components/Spring';
 import Switch from '@ui/Switch';
-import PasswordInput from '@components/PasswordInput';
+import PasswordInput from '@components/UInput/PasswordInput';
 
 // hooks
 import {useState} from 'react';
@@ -56,4 +56,4 @@ const PrivacyPolicy = () => {
     )
 }
 
-export default PrivacyPolicy
+export default PrivacyPolicy

+ 2 - 2
src/widgets/SignUpForm.jsx

@@ -1,5 +1,5 @@
 // components
-import PasswordInput from '@components/PasswordInput';
+import PasswordInput from '@components/UInput/PasswordInput';
 import Spring from '@components/Spring';
 import {Fragment} from 'react';
 import {toast} from 'react-toastify';
@@ -82,4 +82,4 @@ const SignUpForm = ({standalone = true}) => {
     )
 }
 
-export default SignUpForm
+export default SignUpForm