Procházet zdrojové kódy

Merge branch 'feature-year0506' of bcwin/site_front into v1.4

year před 2 měsíci
rodič
revize
27d2f73bc6

+ 12 - 1
messages/br.json

@@ -177,7 +177,18 @@
         "gratis": "Grátis",
         "compensation": "Compensaçao",
 
-        "carteira": "TRANSFERIR PARA A CARTEIRA"
+        "carteira": "TRANSFERIR PARA A CARTEIRA",
+        "bounsTitle1": "Nenhum modo de recompensa",
+        "bonusDesc4": "No modo de recompensa após aabertura.",
+        "bonusDesc5": "Não pode ganhar nenhum bônus.",
+        "bonusDesc6": "Esvaziar os b'nus existentes e asnecessidades de missão.",
+        "boundsBtn": "Ouro de cor esvaziado",
+        "cancel": "cancelar",
+        "sure": "A certeza",
+        "clean": "Não ativar o modo de bônus?",
+        "switch": "Esvaziar o bônus ou não? ",
+        "bouns6001": "Condições não satisfeitas",
+        "bouns6002": "No Bouns está aberto, não é possível receber"
     },
     "DepositPage": {
         "Montante": "Montante",

binární
public/promo/category.png


binární
public/windialog/bg.png


binární
public/windialog/bottom.png


binární
public/windialog/br.png


binární
public/windialog/btn.png


binární
public/windialog/title.png


binární
public/windialog/top.png


+ 3 - 1
src/api/home.ts

@@ -237,6 +237,8 @@ export interface BannerRep {
      * sort 排序
      */
     sort?: number;
+
+    is_badge?: boolean;
 }
 
 export interface Content {
@@ -314,7 +316,7 @@ export interface GlobalNoticeRep extends Omit<NoticeRep, "content"> {
     send_user_time: number;
 }
 export const getGlobalNoticeApi = () => {
-    return server.post<NoticeRep[], { summery: { unread: number } }>({
+    return server.post<NoticeRep[], { summery: { unread: number; promotion_count: number } }>({
         url: "/v1/api/front/notice_list",
         data: {},
     });

+ 19 - 0
src/api/user.ts

@@ -135,6 +135,7 @@ export const getUserInfoApi = () => {
 };
 
 export interface Wallet {
+    is_open_no_bonus?: boolean;
     /**
      * 用户头像
      */
@@ -389,3 +390,21 @@ export const getUserDepositApi = () => {
         url: "/v1/api/user/user_deposit_latest",
     });
 };
+
+export const toggleUserBounsApi = (is_open: 1 | 0) => {
+    return server.post<any>({
+        url: "/v1/api/user/bonus/openNoBonus",
+        data: {
+            is_open,
+        },
+    });
+};
+
+export const cleanBounsApi = (is_clear: 0 | 1) => {
+    return server.post<any>({
+        url: "/v1/api/user/bonus/clearBonus",
+        data: {
+            is_clear,
+        },
+    });
+};

+ 2 - 0
src/app/[locale]/(TabBar)/[[...share]]/@actionWidget/Dialog.tsx

@@ -1,6 +1,7 @@
 import DialogImg from "@/dialog/img";
 import DialogPay from "@/dialog/pay";
 import DialogPwaInstall from "@/dialog/pwaInstall";
+import DialogReceiveGift from "@/dialog/receive";
 import DialogRedPacket from "@/dialog/redPacket";
 import DialogSign from "@/dialog/sign";
 import DialogText from "@/dialog/text";
@@ -16,6 +17,7 @@ const Dialogs = () => {
             <DialogText></DialogText>
             <DialogRedPacket></DialogRedPacket>
             <DialogPwaInstall></DialogPwaInstall>
+            <DialogReceiveGift></DialogReceiveGift>
             {/* <DialogPromation></DialogPromation> */}
         </>
     );

+ 26 - 4
src/app/[locale]/(TabBar)/promo/layout.tsx

@@ -1,18 +1,40 @@
+"use client";
 import HeaderBack from "@/components/HeaderBack";
-import { getTranslations } from "next-intl/server";
+import { useRouter } from "@/i18n/routing";
+import { useTranslations } from "next-intl";
 import { ReactNode } from "react";
 
-export default async function Layout({
+export default function Layout({
     children,
     params: { locale },
 }: {
     children: ReactNode;
     params: { locale: string };
 }) {
-    const t = await getTranslations("Sidebar");
+    const t = useTranslations("Sidebar");
+    const router = useRouter();
+
+    const goRecord = () => {
+        router.push({
+            pathname: "/promo/record",
+        });
+    };
+
     return (
         <>
-            <HeaderBack useBg={true} showBack={true} title={t("promocoes")} />
+            <HeaderBack
+                useBg={true}
+                showBack={true}
+                title={t("promocoes")}
+                // Right={() => (
+                //     <div
+                //         onClick={goRecord}
+                //         className="whitespace-nowrap text-[.12rem] text-[#00eaff]"
+                //     >
+                //         Recorde de prêmios
+                //     </div>
+                // )}
+            />
             <main className={"main-header"}>{children}</main>
         </>
     );

+ 144 - 19
src/app/[locale]/(TabBar)/promo/page.tsx

@@ -1,36 +1,161 @@
+"use client";
+
 import { BannerRep } from "@/api/home";
+import Box from "@/components/Box";
 import Empty from "@/components/Empty";
-import { server } from "@/utils/server";
+import Loading from "@/components/Loading";
+import { server } from "@/utils/client";
+import clsx from "clsx";
+import Image from "next/image";
+import React from "react";
+import styles from "./style.module.scss"; // Import the CSS modul
 
-import Box from "@/components/Box";
-const getPromotionApi = async () => {
+const getPromotionApi = async (category_id?: number) => {
     return server
         .request<BannerRep[]>({
             url: "/v1/api/front/activity_promotion",
             method: "post",
+            data: { category_id },
+        })
+        .then((res) => {
+            if (res.code === 200) return res.data;
+            return [];
+        });
+};
+
+const getPromotionCategoryApi = async () => {
+    return server
+        .request<any>({
+            url: "/v1/api/front/activity_promotion_category",
+            method: "post",
+        })
+        .then((res) => {
+            if (res.code === 200) return res.data;
+            return [];
+        });
+};
+
+const registePromoClick = (activity_id: number) => {
+    return server
+        .request({
+            url: "/v1/api/front/activity_promotion_click",
+            method: "post",
+            data: { activity_id },
         })
         .then((res) => {
             if (res.code === 200) return res.data;
             return [];
         });
 };
-const Promo = async () => {
-    const promotion = await getPromotionApi();
-    if (!promotion.length) return <Empty />;
+
+const Promo = () => {
+    const [category, setCategory] = React.useState<any>([]);
+    const [promotion, setPromotion] = React.useState<BannerRep[]>([]);
+    const [act, setAct] = React.useState(0);
+    const [isLoading, setIsLoading] = React.useState<boolean>(false);
+
+    React.useEffect(() => {
+        getCategoryList();
+    }, []);
+
+    React.useEffect(() => {
+        if (act === 0) {
+            getPromotionList();
+        } else {
+            getPromotionList(act);
+        }
+    }, [act]);
+
+    const getCategoryList = async () => {
+        const category = await getPromotionCategoryApi();
+        setCategory(category || []);
+    };
+    const getPromotionList = async (id?: number) => {
+        try {
+            setIsLoading(true);
+            const promotion = await getPromotionApi(id);
+            setPromotion(promotion || []);
+        } finally {
+            setIsLoading(false);
+        }
+    };
+
+    const doChange = (id: number) => {
+        setAct(id);
+    };
+
+    const registePromo = (id?: number) => {
+        if (!id) return;
+        registePromoClick(id);
+    };
+
+    if (!category.length && !isLoading) {
+        return <Empty />;
+    }
+
     return (
-        <>
-            {promotion.map((item, index) => (
-                <Box key={item.id} action={item.action_type} actionData={item.action_params}>
-                    <img
-                        width={0}
-                        height={0}
-                        src={item.content!}
-                        alt={"Image"}
-                        className={"h-[1.16rem] w-[100%] rounded-[0.0833rem]"}
-                    />
-                </Box>
-            ))}
-        </>
+        <div className="flex h-[100%] items-stretch">
+            <div className="w-[1.2rem] overflow-auto p-[.1rem] pr-[0px]">
+                {category.map((item: any) => {
+                    return (
+                        <div
+                            key={item.id}
+                            className={clsx(styles.categoryItem, {
+                                [styles.active]: act === item.id,
+                                [styles.small]: item.id,
+                            })}
+                            onClick={() => doChange(item.id)}
+                        >
+                            {item.id != 0 && item.name}
+                            {item.id === 0 && (
+                                <>
+                                    <Image
+                                        alt="category"
+                                        src="/promo/category.png"
+                                        width={16}
+                                        height={16}
+                                    ></Image>
+                                    <div className="ml-[.04rem]">Abrangente</div>
+                                </>
+                            )}
+
+                            {!!item.badge_count && (
+                                <div className={styles.categoryItemNum}>
+                                    {item.badge_count > 99 ? `99+` : item.badge_count}
+                                </div>
+                            )}
+                        </div>
+                    );
+                })}
+            </div>
+            <div className="flex-1 overflow-auto">
+                {!isLoading &&
+                    promotion.map((item, index) => (
+                        <Box
+                            key={item.id}
+                            action={item.action_type}
+                            actionData={item.action_params}
+                            className={styles.promoItem}
+                            onBeforeHandler={() => registePromo(item.id)}
+                        >
+                            <img
+                                width={0}
+                                height={0}
+                                src={item.content!}
+                                alt={"Image"}
+                                className={"h-[1.16rem] w-[100%] rounded-[0.0833rem]"}
+                            />
+                            {item.is_badge && <div className={styles.promoItemNum}></div>}
+                        </Box>
+                    ))}
+                {isLoading && (
+                    <div className="flex h-[100%] items-center justify-center">
+                        <Loading></Loading>
+                    </div>
+                )}
+                {!promotion.length && <Empty />}
+            </div>
+        </div>
     );
 };
 

+ 283 - 242
src/app/[locale]/(TabBar)/promo/style.module.scss

@@ -1,296 +1,337 @@
 @keyframes smoothscroll {
-  0% {
-    transform: translateY(0);
-  }
+    0% {
+        transform: translateY(0);
+    }
 
-  100% {
-   transform: translateY(-20rem);
-  }
+    100% {
+        transform: translateY(-20rem);
+    }
 }
 
 .scrollAnimation {
-  padding: 3px 0;
-  animation: smoothscroll 30s linear infinite;
+    padding: 3px 0;
+    animation: smoothscroll 30s linear infinite;
 }
 
-
 /* From Uiverse.io by vamsidevendrakumar */
 .card {
-  width: 300px;
-  height: 200px;
-  perspective: 1000px;
+    width: 300px;
+    height: 200px;
+    perspective: 1000px;
 }
 
 .cardInner {
-  width: 100%;
-  height: 100%;
-  position: relative;
-  transform-style: preserve-3d;
-  transition: transform 0.999s;
+    width: 100%;
+    height: 100%;
+    position: relative;
+    transform-style: preserve-3d;
+    transition: transform 0.999s;
 }
 
 .card:hover .cardInner {
-  transform: rotateY(180deg);
+    transform: rotateY(180deg);
 }
 
 .cardFront,
 .cardBack {
-  position: absolute;
-  width: 100%;
-  height: 100%;
-  backface-visibility: hidden;
+    position: absolute;
+    width: 100%;
+    height: 100%;
+    backface-visibility: hidden;
 }
 
 .cardFront {
-  background-color: #6A2C70;
-  color: #fff;
-  display: flex;
-  align-items: center;
-  border: 10px solid #6A2C70;
-  border-radius: 10px;
-  justify-content: center;
-  font-size: 24px;
-  transform: rotateY(0deg);
+    background-color: #6a2c70;
+    color: #fff;
+    display: flex;
+    align-items: center;
+    border: 10px solid #6a2c70;
+    border-radius: 10px;
+    justify-content: center;
+    font-size: 24px;
+    transform: rotateY(0deg);
 }
 
 .cardBack {
-  background-color: #F08A5D;
-  color: #fff;
-  display: flex;
-  align-items: center;
-  border: 10px solid #F08A5D;
-  border-radius: 10px;
-  justify-content: center;
-  font-size: 24px;
-  transform: rotateY(180deg);
+    background-color: #f08a5d;
+    color: #fff;
+    display: flex;
+    align-items: center;
+    border: 10px solid #f08a5d;
+    border-radius: 10px;
+    justify-content: center;
+    font-size: 24px;
+    transform: rotateY(180deg);
 }
 
 .packetWrapper {
-  position: absolute;
-  top: 0;
-  left: 0;
-  transform: translateY(-100%);
-  animation: down 8s linear infinite;
-  font-size: 0;
+    position: absolute;
+    top: 0;
+    left: 0;
+    transform: translateY(-100%);
+    animation: down 8s linear infinite;
+    font-size: 0;
 }
 
-.packet{
-  width: 100%;
-  height: 100%;
-  position: relative;
-  transition: all 3s ease;
-  transform: rotate(0);
+.packet {
+    width: 100%;
+    height: 100%;
+    position: relative;
+    transition: all 3s ease;
+    transform: rotate(0);
 }
 
 @keyframes down {
-  0% {
-    transform: translateY(-100%);
-  }
+    0% {
+        transform: translateY(-100%);
+    }
 
-  100% {
-    transform: translateY(100vh);
-  }
+    100% {
+        transform: translateY(100vh);
+    }
 }
 
+.promoRules {
+    // background:url('/hby/hby_bg.png') no-repeat;
+    background-size: 100% 100%;
 
-.promoRules{
-  // background:url('/hby/hby_bg.png') no-repeat;
-  background-size: 100% 100%;
-
-  .promoTitle{
-    font-weight:400;
-    font-weight:700;
-  }
-  .titleWrap{
-    font-size:.18rem;
-    text-align: center;
-    color:#ffdf7f;
-    margin:0.1rem 0;
-  }
-  .tips{
-    margin:0 auto;
-    width:2.5rem;
-    height:0.6rem;
-    background: url('/hby/tip-bg.png') no-repeat;
-    background-size: 100% auto;
-    display: flex;
-    justify-content: center;
-    line-height:0.48rem;
-    .tipsIcon{
-      display: inline-block;
-      width:0.40rem;
-      height:0.25rem;
-      margin:0.11rem 0 0;
+    .promoTitle {
+        font-weight: 400;
+        font-weight: 700;
     }
-    .tipsTime{
-      color:#3ab54c;
-      font-size:.18rem;
-      font-weight:700;
-      margin:0 0 0 0.1rem;
+    .titleWrap {
+        font-size: 0.18rem;
+        text-align: center;
+        color: #ffdf7f;
+        margin: 0.1rem 0;
+    }
+    .tips {
+        margin: 0 auto;
+        width: 2.5rem;
+        height: 0.6rem;
+        background: url("/hby/tip-bg.png") no-repeat;
+        background-size: 100% auto;
+        display: flex;
+        justify-content: center;
+        line-height: 0.48rem;
+        .tipsIcon {
+            display: inline-block;
+            width: 0.4rem;
+            height: 0.25rem;
+            margin: 0.11rem 0 0;
+        }
+        .tipsTime {
+            color: #3ab54c;
+            font-size: 0.18rem;
+            font-weight: 700;
+            margin: 0 0 0 0.1rem;
+        }
     }
-  }
 
-  .times1{
-    margin:0.14rem auto;
-  }
+    .times1 {
+        margin: 0.14rem auto;
+    }
 
-  .rulelist{
-    width:90%;
-    font-family: 'Arial-BoldMT, Arial';
-    color:#fff8bb;
-    font-size:0.1rem;
-    line-height:0.14rem;
-    list-style-type: disc;
-    transform: scale(0.96);
-    margin:0.08rem 0 0 .1rem;
-    .ruleItem{
-      list-style: disc;
+    .rulelist {
+        width: 90%;
+        font-family: "Arial-BoldMT, Arial";
+        color: #fff8bb;
+        font-size: 0.1rem;
+        line-height: 0.14rem;
+        list-style-type: disc;
+        transform: scale(0.96);
+        margin: 0.08rem 0 0 0.1rem;
+        .ruleItem {
+            list-style: disc;
+        }
+    }
+    .closeIcon {
+        position: absolute;
+        width: 30px;
+        height: 30px;
+        right: 0.12rem;
+        top: 0.1rem;
+        cursor: pointer;
     }
-  }
-  .closeIcon{
-    position: absolute;
-    width: 30px;
-    height:30px;
-    right:0.12rem;
-    top:0.1rem;
-    cursor: pointer;
-  }
 }
 
+.redclose {
+    display: flex;
+    -webkit-box-pack: start;
+    -ms-flex-pack: start;
+    justify-content: flex-start;
+    -webkit-box-align: center;
+    -ms-flex-align: center;
+    align-items: center;
+    -webkit-box-orient: vertical;
+    -webkit-box-direction: normal;
+    -ms-flex-direction: column;
+    flex-direction: column;
 
-.redclose{
-  display: flex;
-  -webkit-box-pack: start;
-  -ms-flex-pack: start;
-  justify-content: flex-start;
-  -webkit-box-align: center;
-  -ms-flex-align: center;
-  align-items: center;
-  -webkit-box-orient: vertical;
-  -webkit-box-direction: normal;
-  -ms-flex-direction: column;
-  flex-direction: column;
-
-  width: 2.09rem;
-  height: 2.575rem;
-  // background: url('/hby/red-bg1.png') no-repeat;
-  // background-size: 2.09rem 2.575rem;
-  z-index: 120;
-  .closeIcon{
-    position: absolute;
-    right:0;
-    top:0;
-    width:40px;
-    height:40px;
-    cursor: pointer;
-  }
-  .redIcon{
-    cursor: pointer;
-  }
-  .title{
-    color: #ffd800;
-    font-size: .18rem;
-    text-align: center;
-    font-family:' Arial-BoldMT,Arial';
-    font-weight: 700;
-    width: 1.8rem;
-    height: .25rem;
-    margin-top: .24rem
-  }
+    width: 2.09rem;
+    height: 2.575rem;
+    // background: url('/hby/red-bg1.png') no-repeat;
+    // background-size: 2.09rem 2.575rem;
+    z-index: 120;
+    .closeIcon {
+        position: absolute;
+        right: 0;
+        top: 0;
+        width: 40px;
+        height: 40px;
+        cursor: pointer;
+    }
+    .redIcon {
+        cursor: pointer;
+    }
+    .title {
+        color: #ffd800;
+        font-size: 0.18rem;
+        text-align: center;
+        font-family: " Arial-BoldMT,Arial";
+        font-weight: 700;
+        width: 1.8rem;
+        height: 0.25rem;
+        margin-top: 0.24rem;
+    }
 
-  .desc{
-    color: #fff;
-    font-size: .13rem;
-    font-family: 'Arial-BoldMT,Arial';
-    width: 1.6rem;
-    height: 1rem;
-    margin-left: .22rem;
-    .desclist{
-      list-style-type: disc;
-      font-size:0.1rem;
-      transform: scale(0.96);
-      font-weight: 400;
-      .line{
-        margin-top: .09rem;
-        margin-left: -.14rem;
+    .desc {
+        color: #fff;
+        font-size: 0.13rem;
+        font-family: "Arial-BoldMT,Arial";
         width: 1.6rem;
-        height: .005rem;
-        background: hsla(0,0%,100%,.2)
-      }
-      li{
-        margin-top: .08rem;
-        list-style: disc;
-      }
+        height: 1rem;
+        margin-left: 0.22rem;
+        .desclist {
+            list-style-type: disc;
+            font-size: 0.1rem;
+            transform: scale(0.96);
+            font-weight: 400;
+            .line {
+                margin-top: 0.09rem;
+                margin-left: -0.14rem;
+                width: 1.6rem;
+                height: 0.005rem;
+                background: hsla(0, 0%, 100%, 0.2);
+            }
+            li {
+                margin-top: 0.08rem;
+                list-style: disc;
+            }
+        }
     }
-  }
 
-  .openBtn {
-    cursor: pointer;
-    margin: .06rem auto 0;
-    width: 1rem;
-    height: .3rem;
-    line-height: .3rem;
-    font-family: 'Arial-BoldMT,Arial';
-    color: #613e00;
-    font-size: .115rem;
-    font-weight: 700;
-    text-align: center
-  }
-  
+    .openBtn {
+        cursor: pointer;
+        margin: 0.06rem auto 0;
+        width: 1rem;
+        height: 0.3rem;
+        line-height: 0.3rem;
+        font-family: "Arial-BoldMT,Arial";
+        color: #613e00;
+        font-size: 0.115rem;
+        font-weight: 700;
+        text-align: center;
+    }
 }
 
-.redopen{
-  display: flex;
-  -webkit-box-pack: start;
-  -ms-flex-pack: start;
-  justify-content: flex-start;
-  -webkit-box-align: center;
-  -ms-flex-align: center;
-  align-items: center;
-  -webkit-box-orient: vertical;
-  -webkit-box-direction: normal;
-  -ms-flex-direction: column;
-  flex-direction: column;
-
-  width: 2.11rem;
-  height: 2.555rem;
-  // background: url('/hby/red-bg2.png') no-repeat;
-  // background-size: 2.11rem 2.555rem;
-  z-index: 120;
-  .closeIcon{
-    position: absolute;
-    right:0;
-    top:0;
-    cursor: pointer;
-  }
-  .title{
-    color: #925c00;
-    font-size: .16rem;
-    text-align: center;
-    font-family: 'Arial-BoldMT,Arial';
-    width: 1.595rem;
-    margin-top: .4rem
-  }
-  .cash{
-    position:absolute;
-    top:40%;
-    color: #8c0c04;
-    text-align: center;
-    font-family: "Arial-BoldMT,Arial";
-    font-weight: 700;
-    font-size: .55rem
-  }
-  .tips{
-    margin-top: .3rem;
-    width: 1.545rem;
-    color: #ffd800;
-    text-align: center;
-    font-family:" Arial-BoldMT,Arial";
-    font-size: .1rem;
-    line-height:.12rem;
-    transform: scale(0.96);
-  }
+.redopen {
+    display: flex;
+    -webkit-box-pack: start;
+    -ms-flex-pack: start;
+    justify-content: flex-start;
+    -webkit-box-align: center;
+    -ms-flex-align: center;
+    align-items: center;
+    -webkit-box-orient: vertical;
+    -webkit-box-direction: normal;
+    -ms-flex-direction: column;
+    flex-direction: column;
 
+    width: 2.11rem;
+    height: 2.555rem;
+    // background: url('/hby/red-bg2.png') no-repeat;
+    // background-size: 2.11rem 2.555rem;
+    z-index: 120;
+    .closeIcon {
+        position: absolute;
+        right: 0;
+        top: 0;
+        cursor: pointer;
+    }
+    .title {
+        color: #925c00;
+        font-size: 0.16rem;
+        text-align: center;
+        font-family: "Arial-BoldMT,Arial";
+        width: 1.595rem;
+        margin-top: 0.4rem;
+    }
+    .cash {
+        position: absolute;
+        top: 40%;
+        color: #8c0c04;
+        text-align: center;
+        font-family: "Arial-BoldMT,Arial";
+        font-weight: 700;
+        font-size: 0.55rem;
+    }
+    .tips {
+        margin-top: 0.3rem;
+        width: 1.545rem;
+        color: #ffd800;
+        text-align: center;
+        font-family: " Arial-BoldMT,Arial";
+        font-size: 0.1rem;
+        line-height: 0.12rem;
+        transform: scale(0.96);
+    }
 }
 
-
+.categoryItem {
+    border: 1px solid #3aa3c5;
+    border-radius: 0.06rem;
+    box-shadow: 0 0 0.1rem #3aa3c5 inset;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    position: relative;
+    padding: 0.1rem 0;
+    font-size: 0.12rem;
+    margin-bottom: 0.1rem;
+    &.active {
+        border: 1px solid #e53fff;
+        box-shadow: 0 0 0.1rem #e53fff inset;
+    }
+    &.small {
+        margin-left: 0.1rem;
+        margin-right: 0.1rem;
+    }
+    .categoryItemNum {
+        position: absolute;
+        right: 0.05rem;
+        top: 0.05rem;
+        background: #ff0000;
+        width: 0.17rem;
+        height: 0.17rem;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        color: #fff;
+        font-size: 0.1rem;
+        border-radius: 50%;
+        line-height: 1;
+    }
+}
+.promoItem {
+    position: relative;
+    .promoItemNum {
+        position: absolute;
+        left: 0.2rem;
+        top: 0.2rem;
+        background: #ff0000;
+        width: 0.1rem;
+        height: 0.1rem;
+        border-radius: 50%;
+    }
+}

+ 5 - 3
src/app/[locale]/(doings)/rechargeproxy/page.tsx

@@ -231,9 +231,11 @@ const Page = () => {
             <div className={styles.container}>
                 <div className={styles.top}>
                     <img className={styles.img} src="/proxyRecharge/top.png" alt="" />
-                    {/* <div className={styles.recharge} onClick={goDeposit}>
-                        RECARGA
-                    </div> */}
+                    {act === 0 && (
+                        <div className={styles.recharge} onClick={goDeposit}>
+                            RECARGA
+                        </div>
+                    )}
                 </div>
                 <div className={styles.content}>
                     <div className={styles.tip}>

+ 16 - 9
src/components/Footer/index.tsx

@@ -69,12 +69,15 @@ const Footer: FC = () => {
         router.push(path);
     };
 
-    const { unread, userUnred, setNotices, setUserUnread } = useGlobalNoticeStore((state) => ({
-        unread: state.unread,
-        setNotices: state.setNotices,
-        setUserUnread: state.setUserUnread,
-        userUnred: state.userUnred,
-    }));
+    const { unread, userUnred, setNotices, setUserUnread, promotion_count, setPromotionCount } =
+        useGlobalNoticeStore((state) => ({
+            unread: state.unread,
+            setNotices: state.setNotices,
+            setUserUnread: state.setUserUnread,
+            setPromotionCount: state.setPromotionCount,
+            userUnred: state.userUnred,
+            promotion_count: state.promotion_count,
+        }));
 
     const setWallet = useWalletStore((state) => state.setWallet);
     const { run } = useRequest(getGlobalNoticeApi, {
@@ -84,6 +87,7 @@ const Footer: FC = () => {
         pollingWhenHidden: false,
         onSuccess: (data) => {
             setNotices(data?.data || [], data?.summery.unread || 0);
+            setPromotionCount(data.summery.promotion_count || 0);
         },
     });
 
@@ -162,9 +166,8 @@ const Footer: FC = () => {
                                                     maxWidth: "1000%",
                                                     position: "relative",
                                                 }}
+                                                alt=""
                                             />
-                                            {/* <i className="icon-afiliado"></i>
-                                            <i className="icon-rs"></i> */}
                                         </div>
                                     ) : (
                                         <Badge
@@ -180,7 +183,11 @@ const Footer: FC = () => {
                                             ></span>
                                         </Badge>
                                     )}
-                                    {index == 3 && <b className="icon-hot"></b>}
+                                    {index == 3 && !!promotion_count && (
+                                        <div className="absolute -top-[0px] right-[4px] flex h-[.16rem] w-[.16rem] items-center justify-center rounded-[50%] bg-[#ff0000] text-[.1rem] text-[#fff]">
+                                            {promotion_count > 99 ? `99+` : promotion_count}
+                                        </div>
+                                    )}
                                 </div>
                                 <label>{t(item.label)}</label>
                             </Link>

+ 9 - 3
src/components/HeaderBack/index.tsx

@@ -20,6 +20,7 @@ export interface HeaderBackProps {
     headerRender?: () => ReactNode;
     className?: string;
     useBg?: Boolean;
+    Right?: () => ReactNode; // 右侧自定义渲染,优先级高于childre
 }
 
 const HeaderBack: FC<PropsWithChildren<HeaderBackProps>> = ({
@@ -28,6 +29,7 @@ const HeaderBack: FC<PropsWithChildren<HeaderBackProps>> = ({
     children,
     className,
     useBg = false,
+    Right,
 }) => {
     const t = useTranslations("HeaderBack");
 
@@ -69,6 +71,7 @@ const HeaderBack: FC<PropsWithChildren<HeaderBackProps>> = ({
     };
 
     const cls = clsx(styles.headerBack, className, useBg && styles.useBg);
+
     return (
         <div className={cls}>
             <div className={styles.left}>
@@ -78,9 +81,12 @@ const HeaderBack: FC<PropsWithChildren<HeaderBackProps>> = ({
                 {(title || selfTitle) && <span>{title || selfTitle}</span>}
             </div>
             <div className={styles.content}>{children}</div>
-            <span className={styles.right} onClick={() => goPage("home")}>
-                <span className={iconClassName2}></span>
-            </span>
+            {!Right && (
+                <span className={styles.right} onClick={() => goPage("home")}>
+                    <span className={iconClassName2}></span>
+                </span>
+            )}
+            {Right && Right()}
         </div>
     );
 };

+ 84 - 2
src/components/ModalPopup/WalletDescribeModal/index.tsx

@@ -1,7 +1,8 @@
-import { Wallet } from "@/api/user";
+import { Wallet, cleanBounsApi, toggleUserBounsApi } from "@/api/user";
 import { percentage } from "@/utils/methods";
-import { ProgressBar } from "antd-mobile";
+import { Dialog, ProgressBar, Switch, Toast } from "antd-mobile";
 import { useTranslations } from "next-intl";
+import React from "react";
 import "./style.scss";
 
 const Progress = ({ percent, textColor = "#fff" }: { percent: number; textColor?: string }) => {
@@ -69,7 +70,56 @@ export const BalanceContent = (props: { wallet: Wallet }) => {
 
 export const BonusContent = (props: { wallet: Wallet; handleAcquire?: any }) => {
     const { wallet, handleAcquire } = props;
+    const [switchValue, setSwitchValue] = React.useState<boolean>(wallet.is_open_no_bonus || false);
+    const [isLoading, setIsLoading] = React.useState<boolean>(false);
     const t = useTranslations("ProfilePage");
+
+    const changeValue = async (value: boolean) => {
+        const confirmRes = await Dialog.confirm({
+            content: <div className="text-[#fff]">{t("switch")}</div>,
+            confirmText: t("sure"),
+            cancelText: t("cancel"),
+            bodyStyle: {
+                backgroundColor: "#373737",
+                color: "#fff",
+            },
+            bodyClassName: "customConfirm",
+        });
+        if (!confirmRes) return;
+        setIsLoading(true);
+        try {
+            const res = await toggleUserBounsApi(value ? 1 : 0);
+            if (!(res?.code && res?.data?.code === 0)) {
+                throw new Error("error");
+            }
+            setSwitchValue(value);
+        } finally {
+            setIsLoading(false);
+        }
+    };
+
+    const doHandle = async () => {
+        const confirmRes = await Dialog.confirm({
+            content: <div className="text-[#fff]">{t("clean")}</div>,
+            confirmText: t("sure"),
+            cancelText: t("cancel"),
+            bodyStyle: {
+                backgroundColor: "#373737",
+                color: "#fff",
+            },
+            bodyClassName: "customConfirm",
+        });
+        if (!confirmRes) return;
+        const res = await cleanBounsApi(1);
+        if (res?.code && [6001, 6002].includes(res?.data?.code)) {
+            const str = t(`bouns${res?.data?.code}`);
+            Toast.show({
+                content: str,
+                icon: "fail",
+            });
+        }
+    };
+
     return (
         <div>
             <WalletContent
@@ -84,6 +134,38 @@ export const BonusContent = (props: { wallet: Wallet; handleAcquire?: any }) =>
                 <li className="mt-[.06rem]">{t("bonusDesc2")}</li>
                 <li className="mt-[.06rem]">{t("bonusDesc3")}</li>
             </ul>
+            <div className="mt-[.15rem] rounded-[8px] border-[1px] border-[#e540ff] bg-[#2f4cc8] shadow-[0_-10px_25px_#e641ff_inset]">
+                <div className="flex flex-row items-center justify-between border-b-[1px] border-[#e540ff] p-[8px]">
+                    <span className="text-[#fff]">{t("bounsTitle1")}</span>
+                    <Switch
+                        loading={isLoading}
+                        style={{
+                            "--checked-color": "#a195d2",
+                            "--height": "24px",
+                            "--width": "48px",
+                        }}
+                        checked={switchValue}
+                        onChange={changeValue}
+                    ></Switch>
+                </div>
+                <ul
+                    className={
+                        "ml-[0.1389rem] list-decimal pl-[0.1rem] pt-[.1rem] text-[0.12rem] text-[#fff]"
+                    }
+                >
+                    <li className="mt-[.06rem]">{t("bonusDesc4")}</li>
+                    <li className="mt-[.06rem]">{t("bonusDesc5")}</li>
+                    <li className="mt-[.06rem]">{t("bonusDesc6")}</li>
+                </ul>
+                <div className="mt-[.1rem] flex items-center justify-center pb-[.2rem]">
+                    <a
+                        onClick={doHandle}
+                        className="rounded-[20px] border-[1px] border-[#46e3ff] bg-[#4234a8] px-[.1rem] py-[.06rem] text-[#00f6ff] shadow-[0_0_25px_#435cb9_inset]"
+                    >
+                        {t("boundsBtn")}
+                    </a>
+                </div>
+            </div>
             {handleAcquire && (
                 <a
                     className={`carteira-box ${wallet.is_point_transfer ? "active" : ""}`}

+ 17 - 0
src/components/ModalPopup/WalletDescribeModal/style.scss

@@ -70,3 +70,20 @@
         }
     }
 }
+.customConfirm {
+    background-color: #373737 !important;
+    .adm-dialog-action-row {
+        flex-direction: row-reverse;
+        border-top: 0.5px solid #8f8f8f !important;
+        button {
+            color: #fff !important;
+            &:nth-child(1) {
+                border-right: none !important;
+                border-left: 0.5px solid #8f8f8f !important;
+                color: red !important;
+            }
+        }
+    }
+    .adm-dialog-footer {
+    }
+}

+ 4 - 0
src/dialog/auto.ts

@@ -100,6 +100,10 @@ const AutoShowDialog = () => {
                 continue;
             }
 
+            // await dialogManage.showDialog("ReceiveGift", curData); //周返现
+            // await dialogManage.showDialog('WheelSection', curData);  //轮盘
+            // await dialogManage.showDialog('SignInSection', curData);  //签到
+
             try {
                 switch (curData.content_type) {
                     // 图片展示

+ 117 - 0
src/dialog/receive/index.module.scss

@@ -0,0 +1,117 @@
+.container {
+    position: absolute;
+    left: 0;
+    top: 50%;
+    transform: translateY(-50%);
+    width: 100%;
+    height: 50%;
+    height: 4rem;
+    z-index: 2;
+    .top {
+        height: 0.28rem;
+        position: relative;
+    }
+    .title {
+        position: absolute;
+        left: 50%;
+        bottom: -0.3rem;
+        transform: translateX(-50%);
+    }
+    .close {
+        position: absolute;
+        right: 0.2rem;
+        bottom: 0.4rem;
+        z-index: 2;
+        border: 1px solid #fff;
+        border-radius: 50%;
+        width: 0.24rem;
+        height: 0.24rem;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        i {
+            font-size: 0.12rem;
+        }
+    }
+    .topImg {
+        width: 100%;
+    }
+    .bottomImg {
+        width: 98%;
+        margin: 0 auto;
+    }
+    .content {
+        height: 0;
+        overflow: hidden;
+        transition:
+            height 0.5s ease-in-out,
+            padding 0.5s ease-in-out;
+        background-color: #f4e3ea;
+        border-left: 10px solid #e49ece;
+        border-right: 10px solid #e49ece;
+        position: relative;
+        margin: 0 0.3rem;
+        box-sizing: border-box;
+        display: flex;
+        flex-direction: column;
+        justify-content: space-between;
+        align-items: center;
+        padding: 0 0.12rem;
+        &::before {
+            content: "";
+            position: absolute;
+            left: 5px;
+            top: 0;
+            bottom: 0;
+            width: 1px;
+            background-color: #e7acd3;
+        }
+        &::after {
+            content: "";
+            position: absolute;
+            right: 5px;
+            top: 0;
+            bottom: 0;
+            width: 1px;
+            background-color: #e7acd3;
+        }
+        &.active {
+            height: 2.5rem;
+            padding: 0.2rem 0.12rem;
+        }
+    }
+    .text {
+        color: #9f56cb;
+        font-size: 0.15rem;
+    }
+    .textTitle {
+        margin-bottom: 0.06rem;
+    }
+    .btn {
+        width: 1.8rem;
+        height: 0.5rem;
+        background-image: url("/windialog/btn.png");
+        background-size: 100% 100%;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        color: #da0a01;
+        font-size: 0.18rem;
+        font-weight: 700;
+    }
+    .bottom {
+        position: relative;
+    }
+    .coin1 {
+        position: absolute;
+        right: 0.06rem;
+        bottom: 0.1rem;
+        width: 0.4rem;
+    }
+    .coin2 {
+        position: absolute;
+        left: 0.1rem;
+        bottom: 0.4rem;
+        width: 0.15rem;
+    }
+}

+ 73 - 0
src/dialog/receive/index.tsx

@@ -0,0 +1,73 @@
+"use client";
+import { useRouter } from "@/i18n/routing";
+import { Mask } from "antd-mobile";
+import clsx from "clsx";
+import React from "react";
+import dialogManage from "../manager";
+import styles from "./index.module.scss";
+
+const ReceiveGift = () => {
+    const KeyName = "ReceiveGift";
+    const router = useRouter();
+    const [visible, setVisible] = React.useState(false);
+    const [isActive, setIsActive] = React.useState(false);
+    const [data, setData] = React.useState<any>({});
+
+    React.useEffect(() => {
+        dialogManage.registerDialog({
+            key: KeyName,
+            show(data) {
+                setData(data);
+                setVisible(true);
+                setTimeout(() => {
+                    setIsActive(true);
+                }, 100);
+            },
+        });
+    }, []);
+    const closeClick = () => {
+        setIsActive(false);
+        //关闭等待动画播放完毕
+        setTimeout(() => {
+            dialogManage.hideDialog(KeyName);
+            setVisible(false);
+        }, 600);
+    };
+    const goCashback = () => {
+        router.push("/cashback");
+    };
+
+    return (
+        <Mask visible={visible} onMaskClick={closeClick}>
+            <div className={styles.container}>
+                <div className={styles.top}>
+                    <img src="/windialog/top.png" alt="" className={styles.topImg} />
+                    <img src="/windialog/title.png" alt="" className={styles.title} />
+                    <div className={styles.close} onClick={closeClick}>
+                        <i className={clsx("iconfont icon-guanbi")}></i>
+                    </div>
+                </div>
+                <div className={clsx(styles.content, { [styles.active]: isActive })}>
+                    <div className={styles.text}>
+                        <div className={styles.textTitle}>Caros jogadores,</div>
+                        <div>
+                            no momento vocês têm recompensas de cashback semanais (
+                            {data?.content?.value || 0}) que não foram reivindicadas. Por favor,
+                            verifiquem-nas com antecedência.
+                        </div>
+                    </div>
+                    <div className={styles.btn} onClick={goCashback}>
+                        VÁ AGORA
+                    </div>
+                </div>
+                <div className={styles.bottom}>
+                    <img src="/windialog/bottom.png" alt="" className={styles.bottomImg} />
+                    <img src="/windialog/br.png" alt="" className={styles.coin1} />
+                    <img src="/windialog/br.png" alt="" className={styles.coin2} />
+                </div>
+            </div>
+        </Mask>
+    );
+};
+
+export default ReceiveGift;

+ 5 - 0
src/stores/useGlobalNoticeStore.ts

@@ -7,6 +7,7 @@ interface State {
     unread: number; // 未读数量
     userUnred: number; // 未读数量
     notices: NoticeRep[]; // 总数据
+    promotion_count: number;
 }
 
 interface Action {
@@ -15,6 +16,7 @@ interface Action {
     setNotices: (notices: NoticeRep[], unread: number) => void;
     setReadNotices: (id: number) => Promise<Result<any>>;
     setUserUnread: (value: number) => void;
+    setPromotionCount: (value: number) => void;
 }
 
 const initialState: State = {
@@ -22,6 +24,7 @@ const initialState: State = {
     unread: 0,
     userUnred: 0,
     notices: [],
+    promotion_count: 0,
 };
 
 export const useGlobalNoticeStore = create<State & Action>()((set, get) => {
@@ -38,6 +41,8 @@ export const useGlobalNoticeStore = create<State & Action>()((set, get) => {
         setNotices: (notices, unread: number) => set((state) => ({ ...state, notices, unread })),
 
         setUserUnread: (value: number) => set((state) => ({ ...state, userUnred: value })),
+        setPromotionCount: (value: number) =>
+            set((state) => ({ ...state, promotion_count: value })),
         setReadNotices: (id: number) => {
             return updateGlobalNoticeApi(id);
         },

+ 0 - 1
src/utils/client/index.ts

@@ -17,7 +17,6 @@ const server = new Request({
             return config;
         },
         responseInterceptorCatch: async (err) => {
-            console.log(2233, err);
             const { response }: any = err;
             if (!response) return err;
             // if (response.status !== 200) return Promise.reject(response.message);