zhejiushizhuce 2 gadi atpakaļ
vecāks
revīzija
1b55c028d1

+ 1 - 1
package.json

@@ -50,7 +50,7 @@
     "react-big-calendar": "^1.6.8",
     "react-calendar": "^4.0.0",
     "react-copy-to-clipboard": "^5.1.0",
-    "react-countup": "^6.4.1",
+    "react-countup": "^6.5.3",
     "react-dom": "^18.2.0",
     "react-ga4": "^2.1.0",
     "react-grid-layout": "^1.3.4",

+ 7 - 0
public/index.html

@@ -37,5 +37,12 @@
   <body>
     <noscript>You need to enable JavaScript to run this app.</noscript>
     <div id="root"></div>
+    <script type="text/javascript">
+        window.addEventListener("load", function(){
+          progressier.add({
+            id: "pDy0w3lGo3j9gL2zwK"
+          });
+        });
+    </script>
   </body>
 </html>

+ 4 - 2
src/App.js

@@ -80,6 +80,8 @@ const App = () => {
         swisscbt.load().then(() => {
 
         })
+        new FontFaceObserver("Roboto").load();
+        new FontFaceObserver("Roboto-1").load();
     }
     useEffect(() => {
         // scroll to top on route change
@@ -94,6 +96,8 @@ const App = () => {
             dispatch(connectHall());
             //withdraw
             dispatch(reqWithdrawBaseInfo());
+            //turnplate
+            dispatch(reqTurnplateIndex());
         }
         else
         {
@@ -103,8 +107,6 @@ const App = () => {
         reqPayConfig(dispatch,userInfo);
         //customer
         dispatch(reqCustomService());
-        //turnplate
-        dispatch(reqTurnplateIndex());
         // var client = new Colyseus.Client('wss://wstest.ouro777.com:10000');
         // console.log(client.joinOrCreate)
     },[userID])

+ 1 - 1
src/components/Popup.jsx

@@ -38,4 +38,4 @@ Popup.propTypes = {
     popupClass: PropTypes.string
 }
 
-export default Popup
+export default Popup

+ 6 - 0
src/features/turnplateSlice.js

@@ -33,4 +33,10 @@ export const {} = Turnplate.actions;
 export const getTurnplate=(state)=>{
     return state.turnplate.turnplate;
 }
+export const getRecord=(state)=>{
+    return state.turnplate.record;
+}
+export const getMyRecord=(state)=>{
+    return state.turnplate.myRecord;
+}
 export default Turnplate.reducer;

BIN
src/fonts/roboto/KFOlCnqEu92Fr1MmWUlfBBc4AMP6lQ.woff2


BIN
src/fonts/roboto/KFOmCnqEu92Fr1Mu4mxKKTU1Kg.woff2


+ 8 - 0
src/fonts/roboto/style.css

@@ -0,0 +1,8 @@
+@font-face {
+    font-family: 'Roboto';
+    src: url('../../fonts/roboto/KFOlCnqEu92Fr1MmWUlfBBc4AMP6lQ.woff2')
+}
+@font-face {
+    font-family: 'Roboto-1';
+    src: url('../../fonts/roboto/KFOmCnqEu92Fr1Mu4mxKKTU1Kg.woff2')
+}

+ 40 - 1
src/layout/Navbar/index.jsx

@@ -248,11 +248,47 @@ const PageHeader = () => {
     const {isMobile,showHeader,setShowHeader} = useThemeProvider();
     const [showMHeader, setShowMHeader] = useState(true);
     let lastscroll=0;
+    //body node
+    const bodyNode = document.body;
+    // 配置观察器选项
+    const config = { childList: true, subtree: true }; // 观察子节点的变化,并包括子树中的所有节点
+
+// 创建一个 MutationObserver 实例并传入回调函数
+    const observer = new MutationObserver((mutationsList, observer) => {
+        // 迭代每一个变化
+        for(const mutation of mutationsList) {
+            if (mutation.type === 'childList') {
+                // 如果是子节点变化
+                mutation.addedNodes.forEach(node => {
+                    // console.log('一个节点被添加到了 body 中。');
+                    // 这里可以进行相应的操作
+                    // if (node.className==="progressier-install-banner"){
+                    //     const root = document.querySelector('#root');
+                    //     if (root){
+                    //         root.classList.add("progressier_bar_open");
+                    //     }
+                    // }
+                });
+                mutation.removedNodes.forEach(node => {
+                    // console.log('一个节点从 body 中移除了。');
+                    // 这里可以进行相应的操作
+                    // if (node.className==="progressier-install-banner"){
+                    //     const root = document.querySelector('#root');
+                    //     if (root){
+                    //         root.classList.remove("progressier_bar_open");
+                    //     }
+                    // }
+                });
+            }
+        }
+    });
+
     const checkNavActive=(lv)=>{
         if (currRoute&&currRoute.lv)return currRoute.lv.startsWith(lv);
         else return false;
     }
     const getHeaderHeight = () => {
+        // console.log(document.querySelector('div.progressier-install-banner'))
         const header = document.querySelector('#appbar');
         if (header!=null)return `${header.offsetHeight || 0}px`
         else return '0px';
@@ -288,6 +324,7 @@ const PageHeader = () => {
         // 这里的代码会在组件挂载时执行
         // console.log('Component mounted');
         window.addEventListener('scroll', handleScroll);
+        observer.observe(bodyNode, config);
         if (barRef.current) {
             resizeObserver.observe(barRef.current);
         }
@@ -298,8 +335,10 @@ const PageHeader = () => {
             if (barRef.current) {
                 resizeObserver.unobserve(barRef.current);
             }
+            observer.disconnect();
         };
-    }, [pathname]);
+    }, []);
+    // }, [pathname]);
     useEffect(()=>{
         setShowMHeader(showHeader);
         barRef.showHeader=showHeader;

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

@@ -291,6 +291,31 @@
   },
   "turnplate": {
     "expires_in": "Expires in",
-    "invite_tips": "Invite friends to help with withdrawals"
+    "invite_tips": "Invite friends to help with withdrawals",
+    "cash_btn": "WITHDRAW",
+    "need_tip": "You still need",
+    "need_tip_1": "to make the withdrawal",
+    "record": {
+      "tab_0": "Report",
+      "tab_1": "My Reference"
+    },
+    "rule": {
+      "title": "Rules",
+      "rule_1": "1. You can start withdrawing money when the accumulated amount reaches 100BRL (maximum reward amount is 1,000BRL, there is no limit on times).",
+      "rule_2": "2. Inviting friends to register on 6TT.CC will help you withdraw money quickly.",
+      "rule_3": "3. Each user will receive one free spin per day.",
+      "rule_4": "4. Each user's activity is valid for 3 days and can participate in the activity multiple times.",
+      "rule_5": "5. If you get an extra bonus, you can play for free and win more real money.",
+      "rule_6": "6. If no withdrawal is made within 3 days, the funds will be lost."
+    },
+    "info_box": {
+      "title": "You will soon be able to withdraw",
+      "tips": "Total money to be paid soon",
+      "step_title": "Payment methods",
+      "step_0": "The payment request has been submitted",
+      "step_1": "You still need {0} to make the withdrawal",
+      "step_2": "{0} R$ will be paid into your Pix account",
+      "btn_invite": "Invite friends to help with"
+    }
   }
 }

+ 1 - 0
src/style.scss

@@ -8,3 +8,4 @@
 @import "styles/rtl";
 @import "fonts/icomoon/style.css";
 @import "fonts/swisscbt/style.css";
+@import "fonts/roboto/style.css";

+ 4 - 0
src/styles/_elements.scss

@@ -493,6 +493,10 @@ a {
         position: absolute;
         top: 20px;
         right: 20px;
+        i{
+            font-size: 1.4rem;
+            color: var(--header);
+        }
     }
 }
 

+ 3 - 0
src/styles/_global.scss

@@ -201,3 +201,6 @@ input, input::placeholder {
     --grid-gap: .75rem;
     row-gap: 1.25rem;
 }
+.progressier_bar_open{
+    margin-top: 65px;
+}

+ 1 - 1
src/styles/_rtl.scss

@@ -22,4 +22,4 @@ html[dir="rtl"] {
         left: 20px;
         right: auto;
     }
-}
+}

+ 5 - 0
src/utils/helpers.js

@@ -226,3 +226,8 @@ export const validateBRPhone=(t)=>{
 export const validateCountryPhone=(country,phone)=>{
     return validateBRPhone(phone);
 }
+export const formatCash=(cash,fixed=2)=>{
+    cash=Number(cash);
+    cash=cash/global.CASH_BASE;
+    return cash.toFixed(fixed);
+}

+ 1 - 1
src/widgets/WCashWheel/WWheel/styles.module.scss

@@ -130,7 +130,7 @@
   flex-direction: row;
   background-color: var(--standard);
   border-radius: var(--border-radius);
-  padding: 0.5rem 1rem;
+  padding: 0.75rem 1rem;
   width: 100%;
   justify-content: space-between;
   align-items: center;

+ 216 - 0
src/widgets/WCashWheel/index.jsx

@@ -2,23 +2,239 @@ import styles from "./styles.module.scss";
 import WWheel from "@widgets/WCashWheel/WWheel/WWheel";
 import Spring from "@components/Spring";
 import cash_2 from "@assets/wheel/cash_2.png";
+import coin from "@assets/wheel/pix_2.png";
+import {useSelector} from "react-redux";
+import {getMyRecord, getRecord, getTurnplate} from "@features/turnplateSlice";
+import {formatCash, stringFormat} from "@utils/helpers";
+import React, {useEffect, useState} from "react";
+import {Autoplay, EffectCube,EffectCoverflow} from "swiper";
+import {Swiper, SwiperSlide} from "swiper/react";
+import styled from 'styled-components/macro';
+import CountUp from "react-countup";
+import Popup from "@components/Popup";
+const i18n=global.i18n;
+const StyledSwiper=styled(Swiper)`
+.swiper-wrapper{
+    transition-timing-function: cubic-bezier(1.00, 1.00, 0.00, 0.00);
+ }
+`
 const WCashWheel=({close})=>{
+    let turnplate=useSelector(getTurnplate);
+    if(turnplate==null)turnplate={collect_amount:0,total_amount:10000};
+    const record=useSelector(getRecord);
+    const myrecord=useSelector(getMyRecord);
+    const [recordIndex,setRecordIndex]=useState(0);
+    const recordLog=[record,myrecord];
+    const [swiper, setSwiper] = useState(null);
+    const [openInfoBox,setOpenInfoBox]=useState(false);
+    useEffect(() => {
+        if (swiper) {
+            swiper.update();
+        }
+    }, [swiper]);
+    useEffect(()=>{
+
+    },[recordIndex])
+    const formatPhone=(phone)=>{
+        let tstr=phone.substring(1,phone.length-2);
+        phone=phone.replace(tstr,'*'.repeat(tstr.length));
+        if (!phone.startsWith("55"))phone="55"+phone;
+        return phone;
+    }
     const renderCashMain=()=>{
+        let collect=turnplate.collect_amount;
+        collect=9556;
+        let total=turnplate.total_amount;
+        let left=total-collect;
+        let isEnableCash=collect>0;
+        let progress=((collect/total)*100).toFixed(2);
         return (
             <div className={styles.cash_main_con}>
                 <div className={styles.cash_main}>
                     <div className={styles.have_cash}>
                         <img className={styles.cash_img} src={cash_2}/>
+                        <CountUp
+                            start={0}
+                            end={Number(formatCash(collect))}
+                            duration={1}
+                            separator=" "
+                            decimals={2}
+                            prefix={global.DOLLAR+" "}
+                            startOnMount
+
+                        >
+                            {({ countUpRef, start }) => {
+                                if (countUpRef.current){
+                                    let current=countUpRef.current;
+                                    if (current.value!==collect){
+                                        start();
+                                        current.value=collect;
+                                    }
+                                }
+                                return (
+                                    <div ref={countUpRef} className={styles.cash_text}/>
+                                )
+                            }}
+                        </CountUp>
+                        {/*<div className={styles.cash_text}>{global.DOLLAR+" "+formatCash(collect)}</div>*/}
+                        <button className={styles.cash_btn} disabled={!isEnableCash} onClick={()=>{
+                            setOpenInfoBox(true);
+                        }}>
+                            <img className={styles.icon} src={coin}/>
+                            {i18n.t("turnplate.cash_btn")}
+                        </button>
+                    </div>
+                    <div className={styles.progress}>
+                        <div>{progress+"%"}</div>
+                        <div className={styles.bar}>
+                            <span className={styles.dot} style={{width:progress+"%"}}>
+                                <div className={styles.custor}></div>
+                            </span>
+                        </div>
+                    </div>
+                    <div className={styles.need_cash}>
+                        <div>{i18n.t("turnplate.need_tip")}</div>
+                        <div className={styles.need_txt}>{formatCash(left)}</div>
+                        <div>{i18n.t("turnplate.need_tip_1")}</div>
+                    </div>
+                </div>
+            </div>
+        )
+    }
+    const renderRecord=()=>{
+        let list=recordLog[recordIndex];
+        return (
+            <div className={styles.record_con}>
+                <div className={styles.record}>
+                    <div className={styles.record_tab}>
+                        <button disabled={recordIndex===0} onClick={()=>{
+                            setRecordIndex(0);
+                        }}>
+                            {i18n.t("turnplate.record.tab_0")}
+                        </button>
+                        <button disabled={recordIndex===1} onClick={()=>{
+                            setRecordIndex(1);
+                        }}>
+                            {i18n.t("turnplate.record.tab_1")}
+                        </button>
                     </div>
+                    {
+                        list==null||list.length===0?(<></>):(
+                            <StyledSwiper
+                                className={styles.swiper_list}
+                                onSwiper={setSwiper}
+                                direction={"vertical"}
+                                spaceBetween={0}
+                                slidesPerView={5}
+                                loop={true}
+                                autoplay={{delay: 0, disableOnInteraction: false}}
+                                effect="fade"
+                                fadeEffect={{crossFade: true}}
+                                modules={[Autoplay]}
+                                speed={800}
+                            >
+                                {
+                                    list.map((item,index)=>{
+                                        let phone=formatPhone(item.phone);
+                                        let name=item.name;
+                                        let amount=item.amount;
+                                        return (
+                                            <SwiperSlide key={index}>
+                                                <div className={styles.swiper_item}>
+                                                    <div>{phone}</div>
+                                                    <div className={styles.name}>{name}</div>
+                                                    <div className={styles.coins_con}>
+                                                        {"+"+amount}
+                                                        <div className={styles.unit}>{global.DOLLAR}</div>
+                                                    </div>
+                                                </div>
+                                            </SwiperSlide>
+                                        )
+                                    })
+                                }
+                            </StyledSwiper>
+                        )
+                    }
                 </div>
             </div>
         )
     }
+    const renderHelp=()=>{
+        let arr=Array.from({ length: 6 }, (_, index) => index+1);
+        return (
+            <div className={styles.rule_con}>
+                <div className={styles.rule}>
+                    <div className={styles.title}>{i18n.t("turnplate.rule.title")}</div>
+                    {
+                        arr.map((item,index)=>{
+                           return (
+                               <div key={index}>{i18n.t("turnplate.rule.rule_"+item)}</div>
+                           )
+                        })
+                    }
+                </div>
+            </div>
+        )
+    }
+    const renderInfoBox=()=>{
+        let collect=turnplate.collect_amount;
+        collect=9556;
+        let total=turnplate.total_amount;
+        let left=total-collect;
+        return (
+            <Popup open={openInfoBox} onClose={()=>{
+                setOpenInfoBox(false);
+            }}>
+                <div className={styles.info_box}>
+                    <div className={styles.title}>{i18n.t("turnplate.info_box.title")}</div>
+                    <div className={styles.content}>
+                        <div className={styles.cash_con}>
+                            <div className={styles.tips}>
+                                {i18n.t("turnplate.info_box.tips")}
+                            </div>
+                            <CountUp
+                                start={openInfoBox?0:Number(formatCash(collect))}
+                                end={Number(formatCash(collect))}
+                                duration={1}
+                                separator=" "
+                                decimals={2}
+                                suffix={" BRL"}
+
+                            >
+                                {({ countUpRef, start }) => {
+                                    return (
+                                        <div ref={(ref)=>{
+                                            countUpRef.current=ref;
+                                            if (openInfoBox)start();
+                                        }} className={styles.cash}/>
+                                    )
+                                }}
+                            </CountUp>
+                        </div>
+                        <div className={styles.step_con}>
+                            <div>{i18n.t("turnplate.info_box.step_title")}</div>
+                            <div className={styles.step_content}>
+                                <div className={`${styles.step_item} ${collect>0?styles.active:''}`}>{i18n.t("turnplate.info_box.step_0")}</div>
+                                <div className={`${styles.step_item} ${collect>0?styles.active:''}`}>{stringFormat(i18n.t("turnplate.info_box.step_1"),[formatCash(left)])}</div>
+                                <div className={`${styles.step_item} ${collect>=total?styles.active:''}`}>{stringFormat(i18n.t("turnplate.info_box.step_2"),[formatCash(total,0)])}</div>
+                            </div>
+                        </div>
+                        <button className={styles.invite_btn}>
+                            {i18n.t("turnplate.info_box.btn_invite")}
+                        </button>
+                    </div>
+                </div>
+            </Popup>
+        )
+    }
     return (
         <div className={styles.container}>
             <Spring type={"slideUp"} className={styles.content}>
                 {renderCashMain()}
                 <WWheel></WWheel>
+                {renderRecord()}
+                {renderHelp()}
+                {renderInfoBox()}
             </Spring>
         </div>
     )

+ 271 - 1
src/widgets/WCashWheel/styles.module.scss

@@ -20,19 +20,289 @@
 .cash_main{
   display: flex;
   flex-direction: column;
-  padding: 1rem;
+  padding: 1rem 1.9rem;
   align-items: center;
   justify-content: center;
   background-color: var(--standard);
   border-radius: 0.8rem;
+  gap: 1rem;
   & .have_cash{
     display: flex;
     align-items: center;
     justify-content: space-between;
+    width: 100%;
     & .cash_img{
       width: 4rem;
       height: auto;
     }
+    & .cash_text{
+      font-size: 2.2rem;
+      font-family: "Roboto";
+      color: #6ddf38;
+    }
+    & .cash_btn{
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      background: #efb423 linear-gradient(90deg, #49a319, #2b8b2f);
+      border-radius: .8rem;
+      padding: 0.4rem 0.7rem;
+      color: var(--header);
+      font-family: "Roboto";
+      gap: 0.25rem;
+      & .icon{
+        width: 1.2rem;
+        height: auto;
+      }
+      &:disabled{
+        opacity: .35;
+        cursor: default;
+      }
+    }
+  }
+  & .progress{
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: flex-end;
+    width: 100%;
+    font-size: 1rem;
+    font-weight: 600;
+    color: var(--header);
+    gap: 0.25rem;
+    & .bar{
+      width: 100%;
+      height: 0.6rem;
+      background: #6ddf39;
+      border-radius: 1rem;
+      position: relative;
+      & .dot{
+        display: flex;
+        flex-direction: row;
+        justify-content: flex-end;
+        align-items: center;
+        height: 100%;
+        & .custor{
+          background: #fff;
+          border-radius: 50%;
+          width: 100%;
+          max-width: 0.6rem;
+          aspect-ratio:1;
+        }
+      }
+    }
+  }
+  & .need_cash{
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    gap: 0.5rem;
+    font-size: 1rem;
+    color: var(--header);
+    & .need_txt{
+      color: #6ddf39;
+      font-family: "SWISSC-BT";
+    }
+  }
+}
+.record_con{
+  width: 100%;
+  margin-top: -0.5rem;
+  padding: 0rem 1rem;
+}
+.record{
+  display: flex;
+  flex-direction: column;
+  gap: 0.5rem;
+  position: relative;
+  height: 18rem;
+  & .record_tab{
+    display: flex;
+    align-items: center;
+    justify-content: space-around;
+    width: 100%;
+    border-radius: var(--border-radius);
+    background-color: var(--standard);
+    button{
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      color: var(--text);
+      padding: 1rem;
+      font-size: 1.1rem;
+      height: 100%;
+      width: 50%;
+      &:disabled{
+        color: var(--header);
+        font-weight: 600;
+        cursor: default;
+      }
+    }
+  }
+  & .swiper_list{
+    display: flex;
+    flex-direction: column;
+    gap: 1rem;
+    padding: 1rem;
+    width: 100%;
+    & .swiper_item{
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      color: var(--text);
+      font-size: 1rem;
+      font-weight: 500;
+      & .name{
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        max-width: 8rem;
+      }
+      & .coins_con{
+        display: flex;
+        align-items: center;
+        color: #6ddf39;
+        gap: 0.5rem;
+        & .unit{
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          background-color: #6ddf39;
+          width: 1.5rem;
+          height: 1.5rem;
+          border-radius: 50%;
+          font-family: "Roboto";
+          color: var(--header);
+        }
+      }
+    }
+  }
+}
+.rule_con{
+  width: 100%;
+  padding: 1rem;
+}
+.rule{
+  display: flex;
+  gap: 1rem;
+  flex-direction: column;
+  padding: 1rem;
+  color: var(--text);
+  background-color: var(--standard);
+  border-radius: var(--border-radius);
+  position: relative;
+  font-size: 1rem;
+  & .title{
+    display: flex;
+    width: 100%;
+    align-items: center;
+    justify-content: center;
+    font-size: 1.3rem;
+    font-weight: 600;
+  }
+}
+.info_box{
+  display: flex;
+  flex-direction: column;
+  gap: 1rem;
+  & .title{
+    color: var(--header);
+    font-size: 1rem;
+  }
+  & .content{
+    display: flex;
+    flex-direction: column;
+    gap: 1rem;
+    padding: 1rem;
+    border-radius: 0.8rem;
+    width: 100%;
+    background-color: var(--card-bg);
+    & .cash_con{
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      gap: 1rem;
+      & .tips{
+        font-size: 0.9rem;
+        color: #6ddf39;
+      }
+      & .cash{
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        color: #fd2626;
+        font-size: 2.8rem;
+        font-weight: 700;
+        font-family: "Roboto";
+        height: 2.8rem;
+      }
+    }
+    & .step_con{
+      display: flex;
+      flex-direction: column;
+      gap: 1rem;
+      font-size: 0.9rem;
+      color: var(--header);
+      & .step_content{
+        display: flex;
+        flex-direction: column;
+        position: relative;
+        padding: 1rem;
+        background-color: rgba(0, 0, 0, 0.1);
+        border-radius: 0.8rem;
+        gap: 0.5rem;
+        & .step_item{
+          display: flex;
+          align-items: center;
+          color: #4b4f5d;
+          font-size: 0.8rem;
+          z-index: 2;
+          &:before{
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            content: "";
+            width: 0.5rem;
+            height: 0.5rem;
+            border-radius: 50%;
+            background-color: #4b4f5d;
+            margin-right: 0.8rem;
+          }
+          &.active{
+            color: #6ddf39;
+            &:before{
+              content: "\e937";
+              font-size: 0.5rem;
+              text-align: center;
+              font-family: "icomoon";
+              width: 1rem;
+              height: 1rem;
+              border-radius: 50%;
+              background-color: #6ddf39;
+              margin-left: -0.25rem;
+              color:#fff;
+            }
+          }
+        }
+        &:after{
+          content: "";
+          position: absolute;
+          border-left: 0.08rem dotted #3cc517;
+          top: 1.6rem;
+          bottom: 1.6rem;
+          left: 1.2rem;
+        }
+      }
+    }
+    & .invite_btn{
+      width: 100%;
+      padding: 0.6rem;
+      border-radius: var(--border-radius);
+      color: var(--header);
+      font-size: 1.2rem;
+      font-weight: 600;
+      background: rgb(109, 223, 57);
+    }
   }
 }