浏览代码

Merge branch 'master' of ssh://git.s64.io:10022/xuduhao/web2468

PigTech 2 年之前
父节点
当前提交
4555b65233
共有 71 个文件被更改,包括 1293 次插入 和 34 次删除
  1. 14 3
      src/App.js
  2. 24 0
      src/Global.js
  3. 3 0
      src/api/config.js
  4. 2 0
      src/app/store.js
  5. 二进制
      src/assets/bindPrompt/bg.png
  6. 二进制
      src/assets/bindPrompt/btn.png
  7. 二进制
      src/assets/bindPrompt/connect_now_en.png
  8. 二进制
      src/assets/bindPrompt/connect_now_es.png
  9. 二进制
      src/assets/bindPrompt/connect_now_pt.png
  10. 二进制
      src/assets/bindPrompt/connect_now_ru.png
  11. 二进制
      src/assets/firstPay/bglight.png
  12. 二进制
      src/assets/firstPay/button.png
  13. 二进制
      src/assets/firstPay/en.png
  14. 二进制
      src/assets/firstPay/es.png
  15. 二进制
      src/assets/firstPay/pack.png
  16. 二进制
      src/assets/firstPay/percent.png
  17. 二进制
      src/assets/firstPay/pt.png
  18. 二进制
      src/assets/firstPay/ru.png
  19. 二进制
      src/assets/protect/p_bg.png
  20. 二进制
      src/assets/protect/p_btn.png
  21. 二进制
      src/assets/protect/p_close.png
  22. 二进制
      src/assets/tabicon/deposit.png
  23. 二进制
      src/assets/welfare/bind.png
  24. 二进制
      src/assets/welfare/first_en.png
  25. 二进制
      src/assets/welfare/first_es.png
  26. 二进制
      src/assets/welfare/first_pay.png
  27. 二进制
      src/assets/welfare/first_pt.png
  28. 二进制
      src/assets/welfare/first_ru.png
  29. 二进制
      src/assets/welfare/fundos_en.png
  30. 二进制
      src/assets/welfare/fundos_es.png
  31. 二进制
      src/assets/welfare/fundos_pt.png
  32. 二进制
      src/assets/welfare/fundos_ru.png
  33. 二进制
      src/assets/welfare/leftb.png
  34. 二进制
      src/assets/welfare/leftb_1.png
  35. 二进制
      src/assets/welfare/protect.png
  36. 二进制
      src/assets/welfare/veri_en.png
  37. 二进制
      src/assets/welfare/veri_es.png
  38. 二进制
      src/assets/welfare/veri_pt.png
  39. 二进制
      src/assets/welfare/veri_ru.png
  40. 2 1
      src/components/GameItem/styles.module.scss
  41. 1 1
      src/components/GameTabs/index.jsx
  42. 33 0
      src/components/Loading/LoadingCircular.jsx
  43. 10 1
      src/components/LoadingScreen.jsx
  44. 7 1
      src/constants/app.js
  45. 2 1
      src/constants/routes.js
  46. 4 1
      src/contexts/themeContext.js
  47. 27 1
      src/features/api.js
  48. 43 2
      src/features/mainSlice.js
  49. 5 2
      src/features/paySlice.js
  50. 63 0
      src/features/protectSlice.js
  51. 9 6
      src/layout/BottomNav/index.jsx
  52. 6 0
      src/layout/BottomNav/styles.module.scss
  53. 10 0
      src/layout/Layout.jsx
  54. 14 0
      src/locales/languages/en_US.json
  55. 14 0
      src/locales/languages/es_MX.json
  56. 14 0
      src/locales/languages/pt_BR.json
  57. 14 0
      src/locales/languages/ru_RU.json
  58. 14 0
      src/locales/languages/zh_TW.json
  59. 14 0
      src/styles/_global.scss
  60. 1 0
      src/styles/_vars.scss
  61. 5 0
      src/widgets/WBindPhone/index.jsx
  62. 57 0
      src/widgets/WBindPrompt/index.jsx
  63. 78 0
      src/widgets/WBindPrompt/styles.module.scss
  64. 132 0
      src/widgets/WFirstPay/index.jsx
  65. 171 0
      src/widgets/WFirstPay/styles.module.scss
  66. 1 1
      src/widgets/WGame/WDGame/WDGame.jsx
  67. 112 0
      src/widgets/WProtect/index.jsx
  68. 104 0
      src/widgets/WProtect/styles.module.scss
  69. 18 13
      src/widgets/WSecurity/index.jsx
  70. 202 0
      src/widgets/WWelfarebar/index.jsx
  71. 63 0
      src/widgets/WWelfarebar/styles.module.scss

+ 14 - 3
src/App.js

@@ -44,11 +44,11 @@ import Layout from "@layout/Layout";
 import {
     getBFP,
     getFetchStatus,
-    getHallServerUrl, getKickState, getOpenPay, getPromoteInstall,getPopWheel,
+    getHallServerUrl, getKickState, getOpenPay, getPromoteInstall, getPopWheel,
     getUserID,
     getUserInfo,
     isLogined,
-    updateCurrGame, updateOpenPay
+    updateCurrGame, updateOpenPay, getIsGuest, getPopBindPhone
 } from '@features/mainSlice';
 import {useDispatch, useSelector} from "react-redux";
 import {REQ_STATUS} from "@api/config";
@@ -56,7 +56,7 @@ import {
     reqAgentCheck, reqCheckinCheck,
     reqCustomService, reqFavoGet, reqFirstPayConfig, reqGameActivity, reqGameInfo,
     reqGetRoutes,
-    reqMailCheck,
+    reqMailCheck, reqProtectCheck,
     reqRedpackCheck, reqRedpackConfig,
     reqTurnplateIndex,
     reqWithdrawBaseInfo
@@ -103,6 +103,8 @@ const App = () => {
     const promoteInstall=useSelector(getPromoteInstall);
     const popWheel=useSelector(getPopWheel);
     const openPay=useSelector(getOpenPay);
+    const guest=useSelector(getIsGuest);
+    const popBindPhone=useSelector(getPopBindPhone);
     const muiTheme = createTheme({
         direction: direction,
     });
@@ -181,6 +183,7 @@ const App = () => {
             //Mail红点屏蔽
             dispatch(reqMailCheck());
             dispatch(reqFavoGet());
+            checkOpenBindPrompt();
         }
         else
         {
@@ -265,6 +268,14 @@ const App = () => {
             }
         },time);
     }
+    //检查打开绑定提示
+    const checkOpenBindPrompt=()=>{
+        if (guest!==1)return;
+        if (popBindPhone!==1)return;
+        if (global.ispopBindPhone===1)return;
+        global.showBindPrompt();
+        global.ispopBindPhone=1;
+    }
     //app Effect
     //初始化
     useEffect(() => {

+ 24 - 0
src/Global.js

@@ -76,4 +76,28 @@ global.showBindPhone=()=>{
 global.hideBindPhone=()=>{
     if (global.bindPhone)global.bindPhone.hide();
 }
+global.showBindPrompt=()=>{
+    if (global.bindPrompt)global.bindPrompt.show();
+}
+global.hideBindPrompt=()=>{
+    if (global.bindPrompt)global.bindPrompt.hide();
+}
+global.showProtectReward=()=>{
+    if (global.protectReward)global.protectReward.show();
+}
+global.hideProtectReward=()=>{
+    if (global.protectReward)global.protectReward.hide();
+}
+global.showLoadingCircular=(showMask=false)=>{
+    if (global.loadingCircular)global.loadingCircular.show(showMask);
+}
+global.hideLoadingCircular=()=>{
+    if (global.loadingCircular)global.loadingCircular.hide();
+}
+global.showFirstPay=()=>{
+    if (global.firstPay)global.firstPay.show();
+}
+global.hideFirstPay=()=>{
+    if (global.firstPay)global.firstPay.hide();
+}
 //wd pass

+ 3 - 0
src/api/config.js

@@ -103,3 +103,6 @@ export const APP_BIND_SMS=BASE_PREFIX+"bindsms";
 export const APP_SMS_LOGIN=BASE_PREFIX+"smslogin";
 //apk
 export const APP_GAME_APK=BASE_PREFIX+"apk";
+//protect
+export const APP_PROTECT_CHECK=BASE_PREFIX+"protect/check";
+export const APP_PROTECT_GET=BASE_PREFIX+"protect/get";

+ 2 - 0
src/app/store.js

@@ -11,6 +11,7 @@ import RedPackReducer from '@features/redpackSlice';
 import AgentReducer from '@features/agentSlice';
 import ActivityReducer from '@features/activitySlice';
 import CheckInReducer from '@features/checkInSlice';
+import ProtectReducer from '@features/protectSlice';
 export default configureStore({
     reducer: {
         // todos: TodosReducer,
@@ -25,6 +26,7 @@ export default configureStore({
         agent:AgentReducer,
         activity:ActivityReducer,
         checkIn:CheckInReducer,
+        protect:ProtectReducer,
     },
     middleware:(getDefaultMiddleware => {
         let middlewares=getDefaultMiddleware();

二进制
src/assets/bindPrompt/bg.png


二进制
src/assets/bindPrompt/btn.png


二进制
src/assets/bindPrompt/connect_now_en.png


二进制
src/assets/bindPrompt/connect_now_es.png


二进制
src/assets/bindPrompt/connect_now_pt.png


二进制
src/assets/bindPrompt/connect_now_ru.png


二进制
src/assets/firstPay/bglight.png


二进制
src/assets/firstPay/button.png


二进制
src/assets/firstPay/en.png


二进制
src/assets/firstPay/es.png


二进制
src/assets/firstPay/pack.png


二进制
src/assets/firstPay/percent.png


二进制
src/assets/firstPay/pt.png


二进制
src/assets/firstPay/ru.png


二进制
src/assets/protect/p_bg.png


二进制
src/assets/protect/p_btn.png


二进制
src/assets/protect/p_close.png


二进制
src/assets/tabicon/deposit.png


二进制
src/assets/welfare/bind.png


二进制
src/assets/welfare/first_en.png


二进制
src/assets/welfare/first_es.png


二进制
src/assets/welfare/first_pay.png


二进制
src/assets/welfare/first_pt.png


二进制
src/assets/welfare/first_ru.png


二进制
src/assets/welfare/fundos_en.png


二进制
src/assets/welfare/fundos_es.png


二进制
src/assets/welfare/fundos_pt.png


二进制
src/assets/welfare/fundos_ru.png


二进制
src/assets/welfare/leftb.png


二进制
src/assets/welfare/leftb_1.png


二进制
src/assets/welfare/protect.png


二进制
src/assets/welfare/veri_en.png


二进制
src/assets/welfare/veri_es.png


二进制
src/assets/welfare/veri_pt.png


二进制
src/assets/welfare/veri_ru.png


+ 2 - 1
src/components/GameItem/styles.module.scss

@@ -5,7 +5,8 @@
   scroll-snap-align: start;
   width: 100%;
   //border-radius: var(--border-radius);
-  transition: all var(--transition);
+  //transition: all var(--transition);
+  transition: filter var(--transition),transform var(--transition);
   will-change: transform;
   & .game_img_wrap{
     display: flex;

+ 1 - 1
src/components/GameTabs/index.jsx

@@ -93,7 +93,7 @@ const GameTabs=({data})=>{
                         return (
                             <Fragment key={index}>
                                 <Suspense fallback={<SmallLoading/>}>
-                                    <Comp data={item}></Comp>
+                                    <Comp key={"Comp_"+tabIndex+"_"+index} data={item}></Comp>
                                 </Suspense>
                             </Fragment>
                         )

+ 33 - 0
src/components/Loading/LoadingCircular.jsx

@@ -0,0 +1,33 @@
+import React, {useEffect, useState} from "react";
+import {CircularProgress} from "@mui/material";
+
+const LoadingCircular=()=>{
+    const [isShow, setIsShow] = useState(false);
+    const [showMask,setShowMask]=useState(false);
+    const show=(showMask=false)=>{
+        setIsShow(true);
+        setShowMask(showMask);
+    }
+    const hide=()=>{
+        setIsShow(false);
+    }
+    useEffect(()=>{
+        const loadingCircular={
+            show,
+            hide
+        };
+        global.loadingCircular=loadingCircular;
+    },[])
+    return (
+        <>
+            {
+                isShow? (
+                    <div className={`loading-circular ${showMask?'loading-circular_mask':''}`}>
+                        <CircularProgress color="success" size={40} thickness={4}/>
+                    </div>
+                ):(<></>)
+            }
+        </>
+    )
+}
+export default LoadingCircular;

+ 10 - 1
src/components/LoadingScreen.jsx

@@ -5,12 +5,21 @@ import Spring from '@components/Spring';
 // assets
 // import ball from '@assets/logo.json';
 import logo from '@assets/logo.json';
+import {useEffect, useRef} from "react";
 
 const LoadingScreen = ({...props}) => {
+    const loadRef = useRef(null);
+    useEffect(()=>{
+        return ()=>{
+            if (loadRef.current!=null){
+                loadRef.current.destroy();
+            }
+        }
+    },[])
     return (
         <Spring {...props} className="d-flex align-items-center justify-content-center flex-1 w-100 h-100">
             {/*<Lottie animationData={ball} />*/}
-            <Lottie animationData={logo} />
+            <Lottie animationData={logo} lottieRef={loadRef}/>
         </Spring>
     )
 }

+ 7 - 1
src/constants/app.js

@@ -16,9 +16,15 @@ const APP_SERVER={
     SERVER_URL:"wss://wstest.ouro777.com:10000",
     ROOM_HALL:"hall",
 }
+const APP_WELFARE={
+    PROTECT:0,
+    FIRST_PAY:1,
+    BIND:2
+}
 export {
     APP_THEME,
     APP_GENDER,
     APP_CHANNEL,
-    APP_SERVER
+    APP_SERVER,
+    APP_WELFARE
 };

+ 2 - 1
src/constants/routes.js

@@ -39,7 +39,8 @@ const ROUTE_PATH={
     PRIVACY:"#/privacy",
     SERVICES:"#/services",
     KYC:"#/kyc",
-    CHECK_IN:"#/checkIn"
+    CHECK_IN:"#/checkIn",
+    SPORTS:"/sports"
 }
 
 export {

+ 4 - 1
src/contexts/themeContext.js

@@ -67,6 +67,7 @@ export const ThemeProvider = ({children}) => {
     const isLogin=useSelector(isLogined);
     const user=useSelector(getUserInfo);
     const {preLocation,gameID}=usePreviousRoute();
+    const [welfTabState,setWelfTabState]=useState(-1);
     const initWindowSize=()=>{
         if (isMobile())global.device="m";
         else global.device="d";
@@ -210,7 +211,9 @@ export const ThemeProvider = ({children}) => {
                 toggleSoundOpen,
                 //preroute hook
                 preLocation,
-                gameID
+                gameID,
+                welfTabState,
+                setWelfTabState
             }}>
             {children}
         </ThemeContext.Provider>

+ 27 - 1
src/features/api.js

@@ -31,7 +31,7 @@ import {
     APP_MAIL_TAKE,
     APP_PAGE_MODULES,
     APP_PAY_ORDER_LIST,
-    APP_PAYMENT_ORDER,
+    APP_PAYMENT_ORDER, APP_PROTECT_CHECK, APP_PROTECT_GET,
     APP_RECHARGE_CONFIG,
     APP_REDPACK_CHECK,
     APP_REDPACK_CONFIG,
@@ -829,4 +829,30 @@ export const reqSMSLogin=createAsyncThunk(
         return response;
     }
 )
+//protect
+export const reqProtectCheck=createAsyncThunk(
+    APP_PROTECT_CHECK,
+    async (arg,thunkAPI)=>{
+        let params={
+            url:APP_PROTECT_CHECK,
+        }
+        const response=await Http.get(params);
+        return response;
+    }
+)
+export const reqProtectGet=createAsyncThunk(
+    APP_PROTECT_GET,
+    async (arg,thunkAPI)=>{
+        let params={
+            url:APP_PROTECT_GET,
+        }
+        const response=await Http.get(params);
+        if (response&&response.code==200){
+            // let updateAddScore
+            let ProtectNum=Number(formatCash(arg.ProtectNum));
+            thunkAPI.dispatch(updateAddScore(ProtectNum));
+        }
+        return response;
+    }
+)
 //api

+ 43 - 2
src/features/mainSlice.js

@@ -23,6 +23,7 @@ import {
 import {toast} from 'react-toastify';
 import {getHttpMsg, localStorageGet, localStorageSet} from "@utils/helpers";
 import {
+    LOCAL_ACTIVITY_C,
     LOCAL_CUSTOM_LOCK,
     LOCAL_FAVO_IDS, LOCAL_GAME_URL,
     LOCAL_IS_LOGIN,
@@ -180,6 +181,7 @@ const reducerRoutes=(state,action)=>{
                 if(user.Phone!==""&&user.Phone.startsWith(user.RegisterLocation)){
                     hasPhone=1;
                 }
+                checkChannel(user);
             }
         }
         let conf=json.conf;
@@ -191,6 +193,8 @@ const reducerRoutes=(state,action)=>{
         let popBindPhone=0;
         let firstBonus=0;
         let openRelief=0;
+        //sport 配置
+        let bbConf=null;
         if (conf){
             if (!!conf.hall)hallServerUrl=conf.hall;
             if (!!conf.DOLLAR)global.DOLLAR=conf.DOLLAR;
@@ -213,10 +217,15 @@ const reducerRoutes=(state,action)=>{
             if (!!conf.firstBonus)firstBonus=conf.firstBonus;
             //显示救济浮标
             if (!!conf.openRelief)openRelief=conf.openRelief;
+            //sport 配置
+            if (!!conf.bb){
+                bbConf=conf.bb;
+                if(userInfo!=null)userInfo.bb=bbConf;
+            }
         }
         //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});
+        Object.assign(state, {routes:state.routes,asyncRoutes:data,styles,blocks,...generData,isLogin:isLogin,userInfo,hallServerUrl,promoteInstall,guest,upgradeBonus,popWheel,popBindPhone,firstBonus,openRelief,bbConf});
         // 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});
     }
 }
@@ -240,6 +249,20 @@ const getLocalIsLogin=()=>{
 const getMsg=(payload)=>{
     return getHttpMsg(payload);
 }
+//检查渠道
+const checkChannel=(userInfo)=>{
+    if (userInfo!=null&&userInfo.Channel!=null){
+        let Channel=parseInt(userInfo.Channel);
+        let localC=localStorageGet(LOCAL_ACTIVITY_C,"");
+        if (localC!=null&&localC!==""){
+            localC=parseInt(localC);
+            if (Channel!==localC){
+                localStorageSet(LOCAL_ACTIVITY_C,Channel+"");
+                window.location.replace(window.location.origin);
+            }
+        }
+    }
+}
 const parseLogin=(state,action,arg,isShowMsg=true)=>{
     let payload=action.payload;
     let code=payload.code;
@@ -247,6 +270,9 @@ const parseLogin=(state,action,arg,isShowMsg=true)=>{
     //success
     if (code==200){
         let data=payload.data;
+        if (data.bb==null){
+            if (state.userInfo!=null)data.bb=state.userInfo.bb;
+        }
         state.userInfo=data;
         state.isLogin=1;
         // localStorageSet(LOCAL_USER_INFO,JSON.stringify(data));
@@ -260,6 +286,7 @@ const parseLogin=(state,action,arg,isShowMsg=true)=>{
             //新注册的
             trackEvent("CompleteRegistration",{external_id:data.UserID})
         }
+        checkChannel(data);
     }
     else
     {
@@ -511,7 +538,9 @@ export const Main=createSlice({
         //是否是guest登录
         guest:0,
         //绑定奖励
-        upgradeBonus:0
+        upgradeBonus:0,
+        //sport conf
+        bbConf:null
     },
     reducers: {
         logout:(state, action)=>{
@@ -803,6 +832,18 @@ export const getIsGuest=(state)=>{
 export const getUpgradeBonus=(state)=>{
     return state.main.upgradeBonus;
 }
+export const getPopBindPhone=(state)=>{
+    return state.main.popBindPhone;
+}
+export const getFirstBonus=(state)=>{
+    return state.main.firstBonus;
+}
+export const getOpenRelief=(state)=>{
+    return state.main.openRelief;
+}
+export const getBBConf=(state)=>{
+    return state.main.bbConf;
+}
 //数据获取
 export default Main.reducer;
 

+ 5 - 2
src/features/paySlice.js

@@ -253,9 +253,9 @@ export const Pay=createSlice({
         updatePayFinish:((state, action) => {
             if (state.currPayOrder!=null){
                 state.currPayOrder.status="success";
+                //充值完成 message={Golds:12.xx,PayNum:10}
+                toast.success(i18n.t("deposit.order_info.success"));
             }
-            //充值完成 message={Golds:12.xx,PayNum:10}
-            toast.success(i18n.t("deposit.order_info.success"));
         }),
         setChangePayPassType:((state, action) => {
             state.changePayPassType=action.payload;
@@ -283,6 +283,9 @@ export const {updatePayFinish,setChangePayPassType} = Pay.actions;
 export const getConfig=(state)=>{
     return state.pay.config;
 }
+export const getOFirstPay=(state)=>{
+    return state.pay.firstconfig;
+}
 export const getFirstPay=(state)=>{
     let firstconfig=state.pay.firstconfig;
     let config=state.pay.config;

+ 63 - 0
src/features/protectSlice.js

@@ -0,0 +1,63 @@
+import {createSlice} from "@reduxjs/toolkit";
+import {REQ_STATUS} from "@api/config";
+import {buildReqReducer, reqProtectCheck, reqProtectGet} from "@features/api";
+import {getHttpMsg} from "@utils/helpers";
+import {toast} from "react-toastify";
+const reducerProtectCheck=(state,action,arg)=>{
+    let payload=action.payload;
+    if (payload&&payload.code===200){
+        let data=payload.data;
+        if (data!=null){
+            let info=data[0];
+            if (info!=null)state.protectInfo=info;
+        }
+    }
+}
+const protectCheckStatus=(state,status,arg)=>{
+    state.status.ProtectCheck=status;
+}
+
+const reducerProtectGet=(state,action,arg)=>{
+    let payload=action.payload;
+    let msg=getHttpMsg(payload);
+    if (payload&&payload.code===200){
+        let CurProtectTimes=state.protectInfo.CurProtectTimes!=null?Number(state.protectInfo.CurProtectTimes):0;
+        CurProtectTimes=CurProtectTimes+1;
+        state.protectInfo.CurProtectTimes=CurProtectTimes;
+        if (msg!=null&&msg!="")toast.success(msg);
+    }
+    else
+    {
+        if (msg!=null&&msg!="")toast.error(msg);
+    }
+    if (arg.callback!=null)arg.callback(payload);
+}
+const protectGetStatus=(state,status,arg)=>{
+    state.status.ProtectGet=status;
+}
+
+export const ProtectSlice = createSlice({
+    name: "protect",
+    initialState: {
+        protectInfo: {},
+        status:{
+            ProtectCheck:REQ_STATUS.NONE,
+            ProtectGet:REQ_STATUS.NONE,
+        }
+    },
+    reducers: {
+
+    },
+    extraReducers: (builder) => {
+        buildReqReducer(builder,reqProtectCheck,reducerProtectCheck,protectCheckStatus);
+        buildReqReducer(builder,reqProtectGet,reducerProtectGet,protectGetStatus);
+    }
+})
+export const {} = ProtectSlice.actions;
+export const getProtectInfo=(state)=>{
+    return state.protect.protectInfo;
+}
+export const getProtectGetStatus=(state)=>{
+    return state.protect.status.ProtectGet;
+}
+export default ProtectSlice.reducer;

+ 9 - 6
src/layout/BottomNav/index.jsx

@@ -22,6 +22,8 @@ import {ROUTE_PATH} from "@constants/routes";
 import {useSelector} from "react-redux";
 import {getMailNew} from "@features/mailSlice";
 import {checkIsStandalone} from "@utils/helpers";
+import {useThemeProvider} from "@contexts/themeContext";
+import {APP_THEME} from "@constants/app";
 const i18n=global.i18n;
 
 const BottomNav = () => {
@@ -30,7 +32,8 @@ const BottomNav = () => {
     const isStoreRoute = useStoreRoute();
     const {setCartOpen} = useShopProvider();
     const mailNew=useSelector(getMailNew);
-    const isStandalone=checkIsStandalone()
+    const isStandalone=checkIsStandalone();
+    const {theme} = useThemeProvider();
 
     // return (
     //     <div className={styles.container}>
@@ -83,7 +86,7 @@ const BottomNav = () => {
                 {/*    <use xlinkHref={`${ticon_gift}#gift`} />*/}
                 {/*</svg>*/}
                 {/*<TabGift className={styles.icon}/>*/}
-                <svg className={styles.bottomicon}>
+                <svg className={`${styles.bottomicon} ${theme===APP_THEME.LIGHT?styles.bottomicon_light:''}`}>
                     <use xlinkHref={`${symbol}#icon_Giftbox`}/>
                 </svg>
                 <span className={styles.bottomtxt}>{i18n.t("tab.promotion")}</span>
@@ -93,13 +96,13 @@ const BottomNav = () => {
                 {/* <use xlinkHref={`${symbol_m}#icon_Home_1`} />*/}
                 {/*</svg>*/}
                 {/*<TabWithdraw className={styles.icon}/>*/}
-                <svg className={styles.bottomicon}>
+                <svg className={`${styles.bottomicon} ${theme===APP_THEME.LIGHT?styles.bottomicon_light:''}`}>
                     <use xlinkHref={`${symbol}#icon_WithDraw`}/>
                 </svg>
                 <span className={styles.bottomtxt}>{i18n.t("tab.withdraw")}</span>
             </NavLink>
             <NavLink className={styles.button} style={{width:"25%"}} to={ROUTE_PATH.PAY}>
-                <img src={shopIcon} style={{width:"8rem",marginTop:"-2.4rem"}}/>
+                <img src={shopIcon} style={{width:"8rem",marginTop:"-2.3rem",stroke:"#24262b 4px"}}/>
 
                 <span className={`${styles.yellow_txt} ${styles.bottomtxt}`} style={{lineHeight:"1.5rem"}}>{i18n.t("tab.deposit")}</span>
             </NavLink>
@@ -111,7 +114,7 @@ const BottomNav = () => {
                 {/*    <use xlinkHref={`${symbol_m}#icon_Account`} />*/}
                 {/*</svg>*/}
                 {/*<TabMessage className={styles.icon}/>*/}
-                <svg className={styles.bottomicon}>
+                <svg className={`${styles.bottomicon} ${theme===APP_THEME.LIGHT?styles.bottomicon_light:''}`}>
                     <use xlinkHref={`${symbol}#icon_Mail`}/>
                 </svg>
                 <span className={styles.bottomtxt}>{i18n.t("tab.message")}</span>
@@ -121,7 +124,7 @@ const BottomNav = () => {
                 {/*    <use xlinkHref={`${symbol}#icon_Chat`} />*/}
                 {/*</svg>*/}
                 {/*<TabSett className={styles.icon}></TabSett>*/}
-                <svg className={styles.bottomicon}>
+                <svg className={`${styles.bottomicon} ${theme===APP_THEME.LIGHT?styles.bottomicon_light:''}`}>
                     <use xlinkHref={`${symbol}#icon_Support`}/>
                 </svg>
                 <span className={styles.bottomtxt}>{i18n.t("tab.service")}</span>

+ 6 - 0
src/layout/BottomNav/styles.module.scss

@@ -57,6 +57,12 @@
         g{
             fill: url(#gradient-vertical) #fffee7;
         }
+        &_light{
+            fill: url(#gradient-vertical-dark) #fffee7;
+            g{
+                fill: url(#gradient-vertical-dark) #fffee7;
+            }
+        }
     }
 
     & .icon_yellow{

+ 10 - 0
src/layout/Layout.jsx

@@ -32,6 +32,11 @@ import useEdgeGesture from "@hooks/useEdgeGesture";
 import MailNew from "@components/MailNew";
 import WInstallBox from "@widgets/WInstallBox";
 import WBindPhone from "@widgets/WBindPhone";
+import WBindPrompt from "@widgets/WBindPrompt";
+import WWelfarebar from "@widgets/WWelfarebar";
+import WProtect from "@widgets/WProtect";
+import LoadingCircular from "@components/Loading/LoadingCircular";
+import WFirstPay from "@widgets/WFirstPay";
 const PageNotFound = lazy(() => import('@pages/PageNotFound'));
 
 const Layout=({...props})=>{
@@ -84,6 +89,7 @@ const Layout=({...props})=>{
                     <Sidebar/>
                     <Navbar/>
                     {isMobile() && <BottomNav/>}
+                    <WWelfarebar/>
                 </div>
             </>
         )
@@ -124,6 +130,9 @@ const Layout=({...props})=>{
                 }}></WHallWheel>
                 {/*<WInstallBox/>*/}
                 <WBindPhone/>
+                <WBindPrompt/>
+                <WProtect/>
+                <WFirstPay/>
             </>
         )
     }
@@ -132,6 +141,7 @@ const Layout=({...props})=>{
             <>
                 <DropCoins/>
                 <MailNew/>
+                <LoadingCircular/>
             </>
         )
     }

+ 14 - 0
src/locales/languages/en_US.json

@@ -620,6 +620,20 @@
     "btn_submit": "SUBMIT",
     "tips": "Link your Mobile and get {0} chips reward"
   },
+  "bindPrompt": {
+    "title": "Binding reward",
+    "content": "After binding, you can login to the game via your mobile phone number, which is safe!",
+    "tips": "Play guest accounts. If you replace or reinstall your phone, you will lose your account. Immediate membership is recommended"
+  },
+  "protect": {
+    "title": "Relief Funds",
+    "des": "You can get it when your balance is less than ",
+    "tips": "Only {0} time a day",
+    "btn_get": "Get"
+  },
+  "firstPay": {
+    "only_need": "Only need"
+  },
   "web": {
     "activity": {
       "redpack_not_available": "There is no RedPack",

+ 14 - 0
src/locales/languages/es_MX.json

@@ -609,6 +609,20 @@
     "btn_submit": "ENVIAR",
     "tips": "Vincula tu móvil y obtén una recompensa de {0} fichas"
   },
+  "bindPrompt": {
+    "title": "Recompensa vinculante",
+    "content": "Después de la vinculación, podrás iniciar sesión en el juego a través de tu número de teléfono móvil, ¡lo cual es seguro!",
+    "tips": "Juega cuentas de invitados. Si reemplaza o reinstala su teléfono, perderá su cuenta. Se recomienda membresía inmediata"
+  },
+  "protect": {
+    "title": "Fondos de ayuda",
+    "des": "Puedes obtenerlo cuando tu saldo sea inferior a ",
+    "tips": "Sólo {0} vez al día",
+    "btn_get": "Obtener"
+  },
+  "firstPay": {
+    "only_need": "Solo necesito"
+  },
   "web": {
     "activity": {
       "redpack_not_available": "No hay RedPack",

+ 14 - 0
src/locales/languages/pt_BR.json

@@ -609,6 +609,20 @@
     "btn_submit": "ENVIAR",
     "tips": "Vincule seu celular e ganhe {0} fichas de recompensa"
   },
+  "bindPrompt": {
+    "title": "Recompensa vinculativa",
+    "content": "Após a vinculação, você pode fazer login no jogo usando seu número de celular, que é seguro!",
+    "tips": "Jogue contas de convidados. Se substituir ou reinstalar o telefone, você perderá sua conta. Recomenda-se adesão imediata"
+  },
+  "protect": {
+    "title": "Fundos de Socorro",
+    "des": "Você pode obtê-lo quando seu saldo for inferior a ",
+    "tips": "Apenas {0} vez por dia",
+    "btn_get": "Obter"
+  },
+  "firstPay": {
+    "only_need": "Só precisa"
+  },
   "web": {
     "activity": {
       "redpack_not_available": "Não há RedPack",

+ 14 - 0
src/locales/languages/ru_RU.json

@@ -609,6 +609,20 @@
     "btn_submit": "ПОДТВЕРДИТЬ",
     "tips": "Свяжите свой мобильный телефон и получите награду - {0} фишек"
   },
+  "bindPrompt": {
+    "title": "Обязательное вознаграждение",
+    "content": "После привязки вы сможете войти в игру через свой номер мобильного телефона, это безопасно!",
+    "tips": "Играйте в гостевые аккаунты. Если вы замените или переустановите свой телефон, вы потеряете свой аккаунт. Рекомендуется немедленное членство"
+  },
+  "protect": {
+    "title": "Фонды помощи",
+    "des": "Вы можете получить его, когда ваш баланс будет меньше ",
+    "tips": "Только {0} раз в день",
+    "btn_get": "Получать"
+  },
+  "firstPay": {
+    "only_need": "Нужно только"
+  },
   "web": {
     "activity": {
       "redpack_not_available": "Нет RedPack",

+ 14 - 0
src/locales/languages/zh_TW.json

@@ -609,6 +609,20 @@
     "btn_submit": "提交",
     "tips": "連接您的手機並獲得 {0} 積分獎勵"
   },
+  "bindPrompt": {
+    "title": "綁定獎勵",
+    "content": "綁定後即可透過手機號碼登入遊戲,安全!",
+    "tips": "玩訪客帳戶。如果您更換或重新安裝手機,您將遺失您的帳戶。建議立即加入會員"
+  },
+  "protect": {
+    "title": "救濟基金",
+    "des": "當您的餘額低於 ",
+    "tips": "每天僅限{0}次",
+    "btn_get": "獲得"
+  },
+  "firstPay": {
+    "only_need": "只需要"
+  },
   "web": {
     "activity": {
       "redpack_not_available": "沒有紅包",

+ 14 - 0
src/styles/_global.scss

@@ -230,5 +230,19 @@ input, input::placeholder {
     width: 100vw;
     height: 100vh;
 }
+.loading-circular{
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    justify-content: center;
+    position: fixed;
+    inset: 0px;
+    z-index: var(--zindex-effect);
+    background-color: rgba(0, 0, 0, 0);
+    transition: all var(--transition);
+    &_mask{
+        background-color: rgba(0, 0, 0, 0.3);
+    }
+}
 body.progressier-blurring > *:not(.no-blurring){-webkit-filter:none !important;filter: none !important;pointer-events:none;z-index: 888 !important;}
 

+ 1 - 0
src/styles/_vars.scss

@@ -65,6 +65,7 @@ $width-desktop-big-max:1564px;
     --side-open:250px;
     --side-close:65px;
     --zindex-in:500;
+    --zindex-welfare:600;
     --zindex-second:1000;
     --zindex-drawer:1200;
     --zindex-popup:1300;

+ 5 - 0
src/widgets/WBindPhone/index.jsx

@@ -14,6 +14,8 @@ import SmallLoading from "@components/SmallLoading";
 import {CircularProgress} from "@mui/material";
 import {toast} from "react-toastify";
 import {reqBindSMS, reqSendCode} from "@features/api";
+import {APP_WELFARE} from "@constants/app";
+import {useThemeProvider} from "@contexts/themeContext";
 const i18n=global.i18n;
 const objInfo={
     cdTimer:null
@@ -28,6 +30,7 @@ const WBindPhone=({})=>{
     const upgradBonus=useSelector(getUpgradeBonus);
     const regCoins=formatCash(upgradBonus,0);
     const dispatch=useDispatch();
+    const {setWelfTabState}=useThemeProvider();
     const {register, handleSubmit, formState: {errors}, control} = useForm({
         defaultValues: {
             phone: '',
@@ -39,9 +42,11 @@ const WBindPhone=({})=>{
     // /bindsms    {phone,code,password,repassword}
     const show=()=>{
         setIsShow(true);
+        setWelfTabState(APP_WELFARE.BIND);
     }
     const hide=()=>{
         setIsShow(false);
+        setWelfTabState(-1);
     }
     const validatePhone=(value)=>{
         return validateCountryPhone(country,value);

+ 57 - 0
src/widgets/WBindPrompt/index.jsx

@@ -0,0 +1,57 @@
+import {useEffect, useState} from "react";
+import {useSelector} from "react-redux";
+import {getUpgradeBonus} from "@features/mainSlice";
+import {formatCash} from "@utils/helpers";
+import Modal from "@mui/material/Modal";
+import Fade from "@mui/material/Fade";
+import styles from "./styles.module.scss";
+import Grow from "@mui/material/Grow";
+const i18n=global.i18n;
+const WBindPrompt=({})=>{
+    const [isShow, setIsShow] = useState(false);
+    const upgradBonus=useSelector(getUpgradeBonus);
+    const regCoins=formatCash(upgradBonus,0);
+    const show=()=>{
+        setIsShow(true);
+    }
+    const hide=()=>{
+        setIsShow(false);
+    }
+    const onOpenBind=()=>{
+        hide();
+        global.showBindPhone();
+    }
+    useEffect(()=>{
+        const bindPrompt={
+            show,
+            hide
+        };
+        global.bindPrompt=bindPrompt;
+        return ()=>{
+        }
+    },[])
+    return (
+        <Modal open={isShow} onClose={()=>{
+            hide()
+        }} closeAfterTransition>
+            <Fade in={isShow} timeout={500}>
+                <div className={styles.continer}>
+                    <Grow in={isShow} timeout={500}>
+                        <div className={styles.bind_con} onClick={onOpenBind}>
+                            <div className={styles.content}>
+                                <span className={styles.title}>{i18n.t("bindPrompt.title")}</span>
+                                <span className={styles.reward}>{global.DOLLAR+""+regCoins}</span>
+                                <span className={styles.des}>{i18n.t("bindPrompt.content")}</span>
+                                <button className={styles.btn_now}>
+                                    <img src={require('@assets/bindPrompt/connect_now_'+i18n.language+'.png')}/>
+                                </button>
+                            </div>
+                            <span className={styles.tips}>{i18n.t("bindPrompt.tips")}</span>
+                        </div>
+                    </Grow>
+                </div>
+            </Fade>
+        </Modal>
+    )
+}
+export default WBindPrompt;

+ 78 - 0
src/widgets/WBindPrompt/styles.module.scss

@@ -0,0 +1,78 @@
+$bg:"./../../assets/bindPrompt/bg.png";
+$btn:"./../../assets/bindPrompt/btn.png";
+.continer{
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  width: 100%;
+  height: 100%;
+  padding: 1rem;
+  pointer-events: none;
+  position: relative;
+}
+.bind_con{
+  display: flex;
+  flex-direction: column;
+  width: 100%;
+  max-width: 25.875rem;
+  background: url($bg);
+  aspect-ratio: 773/497;
+  background-size: 100% 100%;
+  pointer-events: auto;
+  align-items: center;
+  justify-content: space-between;
+  & .content{
+    display: flex;
+    flex-direction: column;
+    width: 100%;
+    align-items: center;
+    padding: 1.8rem 1rem 0rem 4rem;
+    transform: rotate(-6deg);
+    gap: 0.75rem;
+    & .title{
+      color: #6a005e;
+      font-weight: bold;
+      font-size: 1.1rem;
+    }
+    & .reward{
+      color: #3244c3;
+      font-weight: bold;
+      font-size: 2.3rem;
+    }
+    & .des{
+      color: #6a005e;
+      padding: 0rem 2.5rem;
+      font-size: 0.8rem;
+      line-height: 1rem;
+    }
+    & .btn_now{
+      display: flex;
+      flex-direction: row;
+      align-items: center;
+      justify-content: center;
+      background: url($btn);
+      width: 50%;
+      aspect-ratio: 248/85;
+      background-size: 100% 100%;
+      transform: rotate(5.5deg);
+      margin-top: -1rem;
+      img{
+        transform: rotate(-5.5deg);
+        height: 30%;
+        width: auto;
+      }
+    }
+  }
+  & .tips{
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    justify-content: center;
+    padding: 0.4rem 1rem 1.8rem 4rem;
+    color: #fff;
+    transform: rotate(-6deg);
+    line-height: 0.8rem;
+    font-size: 0.5rem;
+  }
+}

+ 132 - 0
src/widgets/WFirstPay/index.jsx

@@ -0,0 +1,132 @@
+import {useEffect, useState} from "react";
+import styles from "./styles.module.scss";
+import Modal from "@mui/material/Modal";
+import Fade from "@mui/material/Fade";
+import Grow from "@mui/material/Grow";
+import {useDispatch, useSelector} from "react-redux";
+import {getOFirstPay, getPaymentOrderStatus} from "@features/paySlice";
+import {ROUTE_PATH} from "@constants/routes";
+import {REQ_STATUS} from "@api/config";
+import {reqPaymentOrder} from "@features/api";
+import {useNavigate} from "react-router-dom";
+import {getUserInfo} from "@features/mainSlice";
+import SmallLoading from "@components/SmallLoading";
+import {useThemeProvider} from "@contexts/themeContext";
+import {APP_WELFARE} from "@constants/app";
+const i18n=global.i18n;
+
+const WFirstPay=()=>{
+    const [isShow, setIsShow] = useState(false);
+    const firstPayConf=useSelector(getOFirstPay);
+    const dispatch = useDispatch();
+    const navigate=useNavigate();
+    const user=useSelector(getUserInfo);
+    const orderStatus=useSelector(getPaymentOrderStatus);
+    const {setWelfTabState}=useThemeProvider();
+    const show=()=>{
+        setIsShow(true);
+        setWelfTabState(APP_WELFARE.FIRST_PAY);
+    }
+    const hide=()=>{
+        setIsShow(false);
+        setWelfTabState(-1);
+    }
+    useEffect(()=>{
+        const firstPay={
+            show,
+            hide
+        };
+        global.firstPay=firstPay;
+        return ()=>{
+        }
+    },[])
+    const onGetPaymentCallback=()=>{
+        hide();
+        navigate(ROUTE_PATH.PAY_ORDER);
+    }
+    const onGetPaymentOder=(data)=>{
+        if (orderStatus===REQ_STATUS.LOADING)return;
+        data.user=user;
+        data.callback=onGetPaymentCallback;
+        dispatch(reqPaymentOrder(data));
+    }
+    const renderMain=()=>{
+        if (firstPayConf==null)return (<></>);
+        let first_pay=firstPayConf.first_pay;
+        let give=0;
+        let money=0;
+        let total=0;
+        let precent="";
+        if (first_pay!=null){
+            give=Number(first_pay.give);
+            money=Number(first_pay.money);
+            total=give+money;
+            precent='+'+Math.floor(give*100/money)+'%';
+        }
+        return (
+            <div className={styles.f_main}>
+                <img className={styles.bglight} src={require('@assets/firstPay/bglight.png')}/>
+                <div className={styles.pack_con}>
+                    <div className={styles.percent_con}>
+                        <div data-title={precent} className={styles.text}>{precent}</div>
+                    </div>
+                    <div className={styles.bonus_con}>
+                        <div data-title={total} className={styles.bonus}>{total}</div>
+                    </div>
+                </div>
+            </div>
+        )
+    }
+    const renderPayBtn=()=>{
+        if (firstPayConf==null)return (<></>);
+        let first_pay=firstPayConf.first_pay;
+        let money=0;
+        let payType=0;
+        if (first_pay!=null){
+            money=Number(first_pay.money);
+            let gear=first_pay.gear;
+            if (gear!=null){
+                gear=JSON.parse(gear);
+                if (gear)gear=gear[0];
+                payType=gear.id;
+            }
+        }
+        return (
+            <button className={styles.f_btnpay} onClick={()=>{
+                onGetPaymentOder({
+                    price:money,
+                    goodsName:"firstpay",
+                    payType:payType,
+                    GiftsID:201
+                })
+            }}>
+                {
+                    orderStatus===REQ_STATUS.LOADING?(<SmallLoading fill={"#fff"}/>):(
+                        <>
+                            <span data-title={i18n.t("firstPay.only_need")}
+                                  className={styles.label}>{i18n.t("firstPay.only_need")}</span>
+                            <span data-title={global.DOLLAR + money}
+                                  className={styles.pay_num}>{global.DOLLAR + money}</span>
+                        </>
+                    )
+                }
+            </button>
+        )
+    }
+    return (
+        <Modal open={isShow} onClose={hide} closeAfterTransition>
+            <Fade in={isShow} timeout={500}>
+                <div className={styles.container}>
+                    <Grow in={isShow} timeout={500}>
+                        <div className={styles.f_con}>
+                            <img className={styles.title} src={require(`@assets/firstPay/${i18n.language}.png`)}/>
+                            {renderMain()}
+                            {renderPayBtn()}
+                        </div>
+                    </Grow>
+                </div>
+            </Fade>
+        </Modal>
+    )
+}
+export default WFirstPay;

+ 171 - 0
src/widgets/WFirstPay/styles.module.scss

@@ -0,0 +1,171 @@
+$bg:"./../../assets/firstPay/pack.png";
+$btnbg:"./../../assets/firstPay/button.png";
+$pbg:"./../../assets/firstPay/percent.png";
+.container{
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  width: 100%;
+  height: 100%;
+  padding: 1rem;
+  pointer-events: none;
+  position: relative;
+}
+.f_con{
+  display: flex;
+  flex-direction: column;
+  width: 100%;
+  max-width: 25.875rem;
+  pointer-events: auto;
+  align-items: center;
+  position:relative;
+  gap: 0.5rem;
+  & .title{
+    max-width: 90%;
+    height: auto;
+  }
+  & .f_main{
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    width: 80%;
+    aspect-ratio: 583/442;
+    height: auto;
+    position: relative;
+    & .bglight{
+      width: 127%;
+      position: absolute;
+      z-index: 1;
+      pointer-events: none;
+      animation: bg_light_ani 20s linear infinite;
+      transform: translateZ(0);
+    }
+    & .pack_con{
+      display: flex;
+      flex-direction: column;
+      position: absolute;
+      align-items: center;
+      justify-content: flex-end;
+      z-index: 2;
+      width: 100%;
+      aspect-ratio: 583/442;
+      background: url($bg);
+      background-size: 100% 100%;
+      & .percent_con{
+        display: flex;
+        flex-direction: row;
+        align-items: center;
+        justify-content: center;
+        position: absolute;
+        z-index: 2;
+        background: url($pbg);
+        background-size: 100% 100%;
+        width: 24%;
+        aspect-ratio: 137/136;
+        right: 16%;
+        bottom: 15%;
+        transform: rotate(-10deg);
+        & .text{
+          &::before{
+            content: attr(data-title);
+            position: absolute;
+            background-image: linear-gradient(to right,#ffd63c,#ffd63c, #fdfdd1);
+            background-clip: text;
+            -webkit-background-clip: text;
+            -webkit-text-fill-color: transparent;
+            -webkit-text-stroke: 0px #483be3;
+            font-weight: bold;
+            font-size: 1rem;
+            pointer-events: none;
+          }
+          font-weight: bold;
+          font-size: 1rem;
+          -webkit-text-stroke: 2px #483be3;
+          filter: drop-shadow(0 1px 1px #3615a0);
+        }
+      }
+      & .bonus_con{
+        display: flex;
+        flex-direction: row;
+        align-items: center;
+        justify-content: center;
+        width: 100%;
+        height: 21%;
+        & .bonus{
+          &::before{
+            content: attr(data-title);
+            position: absolute;
+            background-image: linear-gradient(to bottom,#fffd5a 0%,#fffd5a 30%,#fffdea 60%,#ffb643 70%);
+            background-clip: text;
+            -webkit-background-clip: text;
+            -webkit-text-fill-color: transparent;
+            -webkit-text-stroke: 0px #483be3;
+            font-weight: bold;
+            font-size: 1.8rem;
+            pointer-events: none;
+          }
+          font-weight: bold;
+          font-size: 1.8rem;
+          filter: drop-shadow(0 3px 1px #120519);
+        }
+      }
+    }
+  }
+  & .f_btnpay{
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    justify-content: center;
+    margin-top: 0.8rem;
+    width: 48%;
+    aspect-ratio: 347/91;
+    background: url($btnbg);
+    background-size: 100% 100%;
+    font-weight: bold;
+    font-size: 1.1rem;
+    gap: 0.5rem;
+    & .label{
+      margin-top: -0.3rem;
+      color: #fff;
+      -webkit-text-stroke: 4px #1c4014;
+      &::before{
+        content: attr(data-title);
+        position: absolute;
+        background-image: linear-gradient(to bottom,#fff,#fff);
+        background-clip: text;
+        -webkit-background-clip: text;
+        -webkit-text-fill-color: transparent;
+        -webkit-text-stroke: 0px #1c4014;
+        font-weight: bold;
+        font-size: 1.1rem;
+      }
+    }
+    & .pay_num{
+      margin-top: -0.3rem;
+      font-size: 1.2rem;
+      color: #f4eb00;
+      -webkit-text-stroke: 4px #830662;
+      &::before{
+        content: attr(data-title);
+        position: absolute;
+        background-image: linear-gradient(to bottom,#f4eb00,#f4eb00);
+        background-clip: text;
+        -webkit-background-clip: text;
+        -webkit-text-fill-color: transparent;
+        -webkit-text-stroke: 0px #830662;
+        font-weight: bold;
+        font-size: 1.2rem;
+      }
+    }
+  }
+}
+@keyframes bg_light_ani {
+  0%{
+    transform: rotate(0deg);
+  }
+  100%{
+    transform: rotate(1turn);
+  }
+}

+ 1 - 1
src/widgets/WGame/WDGame/WDGame.jsx

@@ -182,7 +182,7 @@ const WDGame=()=>{
         let play_num=gameInfo.play_num!=null?gameInfo.play_num:0;
         return (
             <div className={`${styles.game_area} ${movieMode?styles.movie_mode:''}`}>
-                <div className={`${styles.game_action_wrap} ${styles.game_action_wrap_top}`}></div>
+                {/*<div className={`${styles.game_action_wrap} ${styles.game_action_wrap_top}`}></div>*/}
                 <div className={styles.game_main}>
                     <div className={styles.game_iframe_wrap}>
                         {

+ 112 - 0
src/widgets/WProtect/index.jsx

@@ -0,0 +1,112 @@
+import {useEffect, useState} from "react";
+import Fade from "@mui/material/Fade";
+import Modal from "@mui/material/Modal";
+import styles from "./styles.module.scss";
+import Grow from "@mui/material/Grow";
+import SmallLoading from "@components/SmallLoading";
+import {useDispatch, useSelector} from "react-redux";
+import {getProtectGetStatus, getProtectInfo} from "@features/protectSlice";
+import {formatCash, stringFormat} from "@utils/helpers";
+import {REQ_STATUS} from "@api/config";
+import {reqProtectCheck, reqProtectGet} from "@features/api";
+import {APP_WELFARE} from "@constants/app";
+import {useThemeProvider} from "@contexts/themeContext";
+import {getUserInfo} from "@features/mainSlice";
+const i18n=global.i18n;
+const WProtect = () => {
+    const [isShow, setIsShow] = useState(false);
+    const protectInfo=useSelector(getProtectInfo);
+    const status=useSelector(getProtectGetStatus);
+    const dispatch = useDispatch();
+    const {setWelfTabState}=useThemeProvider();
+    const user=useSelector(getUserInfo);
+    const show=()=>{
+        setIsShow(true);
+        setWelfTabState(APP_WELFARE.PROTECT);
+        dispatch(reqProtectCheck());
+    }
+    const hide=()=>{
+        setIsShow(false);
+        setWelfTabState(-1);
+    }
+    const callback=(payload)=>{
+        if (payload&&payload.code===200){
+            hide();
+        }
+    }
+    const checkCanProtect=()=>{
+        if (user!=null&&protectInfo!=null){
+            let Score=Number(user.Score)*global.CASH_BASE;
+            let ProtectLimit=protectInfo.ProtectLimit!=null?parseInt(protectInfo.ProtectLimit):0;
+            let CurProtectTimes=protectInfo.CurProtectTimes!=null?parseInt(protectInfo.CurProtectTimes):0;
+            let ProtectTimes=protectInfo.ProtectTimes!=null?parseInt(protectInfo.ProtectTimes):0;
+            return (Score<ProtectLimit)&&(CurProtectTimes<ProtectTimes);
+        }
+        return false;
+    }
+    const onGetProtect=()=>{
+        if (status===REQ_STATUS.LOADING)return;
+        if (!checkCanProtect())return;
+        let ProtectNum=protectInfo.ProtectNum!=null?parseInt(protectInfo.ProtectNum):0;
+        dispatch(reqProtectGet({callback:callback,ProtectNum:ProtectNum}));
+    }
+    useEffect(()=>{
+        const protectReward={
+            show,
+            hide
+        };
+        global.protectReward=protectReward;
+        return ()=>{
+        }
+    },[])
+    const renderInfo=()=>{
+        let ProtectLimit=protectInfo.ProtectLimit?formatCash(Number(protectInfo.ProtectLimit),0):0;
+        let ProtectNum=protectInfo.ProtectNum?formatCash(Number(protectInfo.ProtectNum),0):0;
+        let ProtectTimes=protectInfo.ProtectTimes?parseInt(protectInfo.ProtectTimes):0;
+        return (
+            <div className={styles.p_info}>
+                <span className={styles.title}>{i18n.t("protect.title")}</span>
+                <div className={styles.des}>
+                    {i18n.t("protect.des")}
+                    <span className={styles.coin}>{global.DOLLAR + ProtectLimit}</span>
+                </div>
+                <div className={styles.rewad}>{global.DOLLAR + ProtectNum}</div>
+                <span className={styles.tips}>{stringFormat(i18n.t("protect.tips"),[ProtectTimes])}</span>
+            </div>
+        )
+    }
+    const renderGetBtn=()=>{
+        let isDisabled=!checkCanProtect();
+        return (
+            <button className={`${styles.p_get_btn} ${isDisabled?styles.p_get_btn_disabled:''}`} onClick={onGetProtect} disabled={isDisabled}>
+                {
+                    status===REQ_STATUS.LOADING?(<SmallLoading fill={"#fff"}/>): (
+                        <span className={styles.text}>
+                            {i18n.t("protect.btn_get")}
+                        </span>
+                    )
+                }
+            </button>
+        )
+    }
+    return (
+        <Modal open={isShow} onClose={hide} closeAfterTransition>
+            <Fade in={isShow} timeout={500}>
+                <div className={styles.container}>
+                    <Grow in={isShow} timeout={500}>
+                    <div className={styles.p_con}>
+                            <button className={styles.p_close} onClick={hide}>
+                                <img src={require("@assets/protect/p_close.png")}/>
+                            </button>
+                            <div className={styles.p_content}>
+                                {renderInfo()}
+                                {renderGetBtn()}
+                            </div>
+                        </div>
+                    </Grow>
+                </div>
+            </Fade>
+        </Modal>
+)
+}
+export default WProtect;

+ 104 - 0
src/widgets/WProtect/styles.module.scss

@@ -0,0 +1,104 @@
+$bg:"./../../assets/protect/p_bg.png";
+$btn_bg:"./../../assets/protect/p_btn.png";
+.container{
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  width: 100%;
+  height: 100%;
+  padding: 1rem;
+  pointer-events: none;
+  position: relative;
+}
+.p_con{
+  display: flex;
+  flex-direction: column;
+  width: 100%;
+  max-width: 25.875rem;
+  background: url($bg);
+  aspect-ratio: 719/571;
+  background-size: 100% 100%;
+  pointer-events: auto;
+  align-items: center;
+  position:relative;
+}
+.p_content{
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: space-between;
+  width: 100%;
+  height: 100%;
+}
+.p_info{
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: 1rem;
+  width: 100%;
+  height: 100%;
+  & .title{
+    margin-top: 15.5%;
+    font-size: 1.1rem;
+    font-weight: bold;
+    color: #f4eb90;
+  }
+  & .des{
+    font-weight: bold;
+    color: #fff;
+    width: 48%;
+    line-height: 1rem;
+    & .coin{
+      color: #ffcc00;
+    }
+  }
+  & .rewad{
+    margin-top: 12%;
+    font-size: 1.2rem;
+    font-weight: bold;
+    color: #fbd630;
+    text-shadow:
+            -1px -1px 0 #d82b2f,
+            1px -1px 0 #d82b2f,
+            -1px  1px 0 #d82b2f,
+            1px  1px 0 #d82b2f;
+  }
+  & .tips{
+    margin-top: -1rem;
+    font-weight: bold;
+    font-size: 0.75rem;
+    color: #2563c8;
+  }
+}
+.p_get_btn{
+  display: flex;
+  flex-direction: row;
+  justify-content: center;
+  align-items: center;
+  width: 38%;
+  background: url($btn_bg);
+  background-size: 100% 100%;
+  aspect-ratio: 275/105;
+  margin-left: 0.5rem;
+  margin-bottom: 0.6rem;
+  color: #116f02;
+  font-size: 1.1rem;
+  font-weight: bold;
+  & .text{
+    margin-top: -0.3rem;
+  }
+  &_disabled{
+    filter: brightness(60%);
+  }
+}
+.p_close{
+  position: absolute;
+  z-index: 2;
+  top: 1rem;
+  right: 2rem;
+  img{
+    width: 2.5rem;
+    height: auto;
+  }
+}

+ 18 - 13
src/widgets/WSecurity/index.jsx

@@ -15,6 +15,7 @@ const WSecurity = ({ close }) => {
     const navigate = useNavigate();
     const user=useSelector(getUserInfo);
     const dispatch = useDispatch();
+    const isShowPWD=user != null && user.Phone != null && user.Phone !== "";
     return (
         <div className={styles.container}>
             <Spring className={styles.content} type={"slideUp"}>
@@ -22,25 +23,29 @@ const WSecurity = ({ close }) => {
                     {i18n.t("security.title")}
                 </div>
                 <div className={styles.line}></div>
-                <div className={styles.item}>
-                    <div className={styles.item_title_con}>
-                        <PhonePassIcon className={styles.icon}></PhonePassIcon>
-                        <div className={styles.title}>{i18n.t("security.login_pass_title")}</div>
-                    </div>
-                    <div>{i18n.t("security.login_pass_des")}</div>
-                    <button className={`button-green ${styles.button}`} onClick={()=>{
-                        navigate(ROUTE_PATH.EDIT_PWD)
-                    }}>{i18n.t("security.login_pass_title")}</button>
-                </div>
+                {
+                    isShowPWD? (
+                        <div className={styles.item}>
+                            <div className={styles.item_title_con}>
+                                <PhonePassIcon className={styles.icon}></PhonePassIcon>
+                                <div className={styles.title}>{i18n.t("security.login_pass_title")}</div>
+                            </div>
+                            <div>{i18n.t("security.login_pass_des")}</div>
+                            <button className={`button-green ${styles.button}`} onClick={() => {
+                                navigate(ROUTE_PATH.EDIT_PWD)
+                            }}>{i18n.t("security.login_pass_title")}</button>
+                        </div>
+                    ):(<></>)
+                }
                 <div className={styles.item}>
                     <div className={styles.item_title_con}>
                         <WithdrawPassIcon className={styles.icon}></WithdrawPassIcon>
                         <div className={styles.title}>{i18n.t("security.withdraw_pass_title")}</div>
                     </div>
                     <div>{i18n.t("security.withdraw_pass_des")}</div>
-                    <button className={`button-green ${styles.button}`} onClick={()=>{
-                        let InsurePass=Number(user.InsurePass);
-                        let type=InsurePass;
+                    <button className={`button-green ${styles.button}`} onClick={() => {
+                        let InsurePass = Number(user.InsurePass);
+                        let type = InsurePass;
                         dispatch(setChangePayPassType(InsurePass));
                         navigate(ROUTE_PATH.CHANGE_PAY_PASS)
                     }}>{i18n.t("security.withdraw_pass_title")}</button>

+ 202 - 0
src/widgets/WWelfarebar/index.jsx

@@ -0,0 +1,202 @@
+import styles from './styles.module.scss';
+import {useEffect, useState} from "react";
+import {reqFirstPayConfig, reqProtectCheck} from "@features/api";
+import {useDispatch, useSelector} from "react-redux";
+import {getFirstBonus, getOpenRelief, getPopBindPhone, getUserInfo} from "@features/mainSlice";
+import {APP_WELFARE} from "@constants/app";
+import {getCurrPayOrder, getOFirstPay} from "@features/paySlice";
+import {getProtectInfo} from "@features/protectSlice";
+import {formatCash} from "@utils/helpers";
+import {useThemeProvider} from "@contexts/themeContext";
+import {useSidebar} from "@contexts/sidebarContext";
+import {useLocation} from "react-router-dom";
+import {ROUTE_PATH} from "@constants/routes";
+const i18n=global.i18n;
+const WelfareBtn=({children,open=false,pressDown=false,...props})=>{
+    const [bopen,setBopen]=useState(false);
+    const [show,setShow]=useState(false);
+    useEffect(()=>{
+        if (open){
+            setShow(true);
+            setTimeout(()=>{
+                setBopen(true);
+            },100)
+        }
+        else
+        {
+            setBopen(false);
+            setShow(false);
+        }
+    },[open])
+    return (
+        <>
+            {
+                show? (
+                    <button
+                        className={`${styles.wlf_btn} ${bopen ? styles.wlf_btn_open : styles.wlf_btn_close} ${pressDown ? styles.wlf_btn_press : ''}`}
+                        {...props}
+                    >
+                        {
+                            children == null ? (<></>) : children
+                        }
+                    </button>
+                ):(<></>)
+            }
+        </>
+    )
+}
+const WWelfarebar = () => {
+    const dispatch = useDispatch();
+    const user = useSelector(getUserInfo);
+    //0:1,1:1,2:1
+    // const [openState, setOpenState] = useState({});
+    const [openProtect, setOpenProtect] = useState(0);
+    const [openFirst, setOpenFirst] = useState(0);
+    const [openBind, setOpenBind] = useState(0);
+    const firstPayConf = useSelector(getOFirstPay);
+    const popBindPhone=useSelector(getPopBindPhone);
+    const firstBonus=useSelector(getFirstBonus);
+    const openRelief=useSelector(getOpenRelief);
+    const protectInfo=useSelector(getProtectInfo);
+    const orderInfo=useSelector(getCurrPayOrder);
+    const {welfTabState,isMobile}=useThemeProvider();
+    const {open} = useSidebar();
+    const {pathname} = useLocation();
+    const getWelfTabState=(key)=>{
+        if (welfTabState===key)return true;
+        return false;
+    }
+    const changeNewState=(key,state)=>{
+        // let newOpenState= Object.assign({},openState);
+        // newOpenState[key]=state;
+        // setOpenState(newOpenState);
+        // state=1;
+        switch (key){
+            case APP_WELFARE.PROTECT:
+                setOpenProtect(state);
+                break;
+            case APP_WELFARE.FIRST_PAY:
+                setOpenFirst(state);
+                break;
+            case APP_WELFARE.BIND:
+                setOpenBind(state);
+                break;
+        }
+    }
+    const checkProtect=()=>{
+        let state=0;
+        if (openRelief){
+            if (protectInfo!=null){
+                if (user.UserID>0){
+                    // let CurProtectTimes=protectInfo.CurProtectTimes!=null?parseInt(protectInfo.CurProtectTimes):0;
+                    // if (CurProtectTimes>0)state=1;
+                    state=1;
+                }
+            }
+        }
+        changeNewState(APP_WELFARE.PROTECT,state);
+    }
+    const checkProtectLimit=()=>{
+        if (protectInfo!=null){
+            let ProtectLimit=protectInfo.ProtectLimit;
+            if (ProtectLimit!=null){
+                ProtectLimit=Number(ProtectLimit);
+                ProtectLimit=Number(formatCash(ProtectLimit))
+                let Score=user.Score;
+                if (Score<=ProtectLimit){
+                    dispatch(reqProtectCheck());
+                }
+            }
+        }
+    }
+    const checkFirstPay=()=>{
+        let state=0;
+        if (firstBonus){
+            if (user.UserID>0){
+                if (firstPayConf!=null)state=1;
+            }
+        }
+        changeNewState(APP_WELFARE.FIRST_PAY,state);
+    }
+    const checkFirstPayStatus=()=>{
+        if (orderInfo!=null){
+            if (orderInfo.status==="success"){
+                dispatch(reqFirstPayConfig());
+            }
+        }
+    }
+    const checkBind=()=>{
+        let state=0;
+        if (popBindPhone){
+            if (user.UserID>0){
+                if (user.Phone==null||user.Phone===""){
+                    state=1;
+                }
+            }
+        }
+        changeNewState(APP_WELFARE.BIND,state);
+    }
+    useEffect(()=>{
+        let UserID=user.UserID;
+        if (UserID>0){
+            dispatch(reqProtectCheck());
+            dispatch(reqFirstPayConfig());
+        }
+    },[user.UserID]);
+    useEffect(() => {
+        checkProtectLimit();
+    }, [user.Score]);
+    useEffect(()=>{
+        checkFirstPay();
+    },[firstPayConf]);
+    useEffect(()=>{
+        checkBind();
+    },[popBindPhone,user.Phone]);
+    useEffect(()=>{
+        checkProtect();
+    },[protectInfo])
+    useEffect(()=>{
+        checkFirstPayStatus();
+    },[orderInfo])
+    const renderBar=()=>{
+        let extra='';
+        if (!isMobile()){
+            if (open)extra='sidebar-open';
+            else extra='sidebar-close';
+        }
+        let isClose=false;
+        if (pathname.startsWith(ROUTE_PATH.SPORTS)){
+            isClose=true;
+        }
+        return (
+            <div className={`${styles.container} ${extra}`}>
+                <div className={`${styles.content} ${isClose?styles.content_close:''}`}>
+                    <WelfareBtn key={"protect"} open={openProtect} pressDown={getWelfTabState(APP_WELFARE.PROTECT)}
+                                onClick={() => {
+                                    global.showProtectReward();
+                                }}>
+                        <img className={styles.wlf_protect} src={require("@assets/welfare/fundos_"+i18n.language+".png")}/>
+                    </WelfareBtn>
+                    <WelfareBtn key={"first_pay"} open={openFirst} pressDown={getWelfTabState(APP_WELFARE.FIRST_PAY)}
+                                onClick={() => {
+                                    global.showFirstPay();
+                                }}>
+                        <img className={styles.wlf_first} src={require("@assets/welfare/first_"+i18n.language+".png")}/>
+                    </WelfareBtn>
+                    <WelfareBtn key={"bind"} open={openBind} pressDown={getWelfTabState(APP_WELFARE.BIND)}
+                                onClick={() => {
+                                    global.showBindPhone();
+                                }}>
+                        <img className={styles.wlf_bind} src={require("@assets/welfare/veri_"+i18n.language+".png")}/>
+                    </WelfareBtn>
+                </div>
+            </div>
+        )
+    }
+    return (
+        <>
+            {renderBar()}
+        </>
+    )
+}
+export default WWelfarebar;

+ 63 - 0
src/widgets/WWelfarebar/styles.module.scss

@@ -0,0 +1,63 @@
+$leftb:"./../../assets/welfare/leftb.png";
+$leftb_1:"./../../assets/welfare/leftb_1.png";
+.container{
+  display: flex;
+  flex-direction: column;
+  position: fixed;
+  z-index: var(--zindex-welfare);
+  left: 0;
+  bottom: 12rem;
+}
+.content{
+  display: flex;
+  flex-direction: column;
+  justify-content: flex-end;
+  gap: 0.8rem;
+  transition: all var(--transition);
+  &_close{
+    margin-left: -100%;
+  }
+}
+.wlf_btn{
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  justify-content: center;
+  pointer-events: auto;
+  width: 5rem;
+  aspect-ratio: 144/162;
+  background: url($leftb);
+  background-size: 100% 100%;
+  &_press{
+    background: url($leftb_1);
+    background-size: 100% 100%;
+  }
+  opacity: 0;
+  transform: translateX(-100%);
+  &_open{
+    opacity: 1;
+    transform: translateX(0%);
+    transition: all .3s ease;
+  }
+  &_close{
+    opacity: 0;
+    transform: translateX(-100%);
+    transition: all .3s ease;
+  }
+}
+.wlf_protect{
+  width: 5rem;
+  height: auto;
+  margin-left: 0.5rem;
+  margin-top: 0.1rem;
+}
+.wlf_first{
+  width: 3.2rem;
+  height: auto;
+  margin-left: -0.5rem;
+}
+.wlf_bind{
+  width: 3.5rem;
+  height: auto;
+  margin-left: -0.5rem;
+}