Procházet zdrojové kódy

feat: 活动红点

year před 2 měsíci
rodič
revize
11b0b92551

binární
public/promo/category.png


+ 2 - 0
src/api/home.ts

@@ -237,6 +237,8 @@ export interface BannerRep {
      * sort 排序
      */
     sort?: number;
+
+    is_badge?: boolean;
 }
 
 export interface Content {

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

@@ -1,36 +1,155 @@
+"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 Promo = async () => {
-    const promotion = await getPromotionApi();
-    if (!promotion.length) return <Empty />;
+
+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 = () => {
+    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]">
+                <div
+                    className={clsx(styles.categoryItem, {
+                        [styles.active]: act === 0,
+                    })}
+                    onClick={() => doChange(0)}
+                >
+                    <Image alt="category" src="/promo/category.png" width={16} height={16}></Image>
+                    <div className="ml-[.04rem]">Abrangente</div>
+                </div>
+                {category.map((item: any) => {
+                    return (
+                        <div
+                            key={item.id}
+                            className={clsx(styles.categoryItem, styles.small, {
+                                [styles.active]: act === item.id,
+                            })}
+                            onClick={() => doChange(item.id)}
+                        >
+                            {item.name}
+                            {!!item.badge_count && (
+                                <div className={styles.categoryItemNum}>{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%;
+    }
+}

+ 6 - 3
src/components/Footer/index.tsx

@@ -162,9 +162,8 @@ const Footer: FC = () => {
                                                     maxWidth: "1000%",
                                                     position: "relative",
                                                 }}
+                                                alt=""
                                             />
-                                            {/* <i className="icon-afiliado"></i>
-                                            <i className="icon-rs"></i> */}
                                         </div>
                                     ) : (
                                         <Badge
@@ -180,7 +179,11 @@ const Footer: FC = () => {
                                             ></span>
                                         </Badge>
                                     )}
-                                    {index == 3 && <b className="icon-hot"></b>}
+                                    {index == 3 && (
+                                        <div className="absolute -top-[0px] right-[4px] flex h-[.16rem] w-[.16rem] items-center justify-center rounded-[50%] bg-[#ff0000] text-[.1rem] text-[#fff]">
+                                            23
+                                        </div>
+                                    )}
                                 </div>
                                 <label>{t(item.label)}</label>
                             </Link>