123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584 |
- "use client";
- import { receiveRedPacketApi } from "@/api/promo";
- import { useRouter } from "@/i18n/routing";
- import { getToken } from "@/utils/Cookies";
- import { Mask } from "antd-mobile";
- import clsx from "clsx";
- import { useTranslations } from "next-intl";
- import Image from "next/image";
- import {
- forwardRef,
- Fragment,
- memo,
- useEffect,
- useImperativeHandle,
- useRef,
- useState,
- } from "react";
- import styles from "./redpacked.module.scss";
- const randomX = (len: number) => {
- return Math.floor(Math.random() * len);
- };
- const mockData = Array(500)
- .fill(0)
- .map((item) => {
- return {
- phone: `55****${(randomX(99) + "").padEnd(2, "0")}`,
- num: `${(Math.random() * 20).toFixed(2)}`,
- time: "11:00",
- };
- });
- function getRandom(min: number, max: number) {
- const floatRandom = Math.random();
- const difference = max - min;
- // 介于 0 和差值之间的随机数
- const random = Math.round(difference * floatRandom);
- return random + min;
- }
- /**
- * @description 描述
- */
- type DescProps = {
- onClose: () => void;
- };
- const Desc = (props: DescProps) => {
- const { onClose } = props;
- const [activeTab, setActiveTab] = useState(0);
- const tabs = [{ text: "Vezes de evento participado" }, { text: "Consulta de registo levado" }];
- return (
- <div className={"absolute top-[30%] w-[100%]"}>
- <div className={"absolute -top-[1.3rem] left-1/2 w-[3.3rem] -translate-x-1/2"}>
- <img src="/9f/red-header.png" alt="" />
- <div
- className={
- "h-[0.2rem] w-[0.2rem] " + " absolute bottom-[20px] right-[30px]" + " "
- }
- onClick={onClose}
- >
- <Image src={"/9f/close.png"} alt={"close"} width={25} height={25} />
- </div>
- </div>
- <div
- className={"mx-auto w-[2.9rem] bg-[#ff9417] px-[0.16rem] pb-[0.12rem] pt-[0.44rem]"}
- >
- <img src="/9f/red-title.png" alt="" className={"mx-auto w-[90%]"} />
- <div className={"mt-[0.0694rem] rounded-[3px] bg-primary-color"}>
- <div className={"flex h-[0.54rem] justify-between text-center text-[0.15rem]"}>
- {tabs.map((item, index) => {
- return (
- <Fragment key={index}>
- <span
- onClick={() => setActiveTab(index)}
- className={`flex h-[100%] items-center ${index === activeTab ? "bg-[#8b3500] text-[#5f2600]" : ""}`}
- >
- {item.text}
- </span>
- </Fragment>
- );
- })}
- </div>
- {/* 动态内容 */}
- <div className={"h-[3rem] overflow-y-scroll p-[0.1rem]"}>
- <div
- className={
- "flex items-center rounded-[0.1rem] bg-[#d45300] p-[10px] font-bold"
- }
- >
- <Image
- className={"h-[0.43rem] w-[0.7rem]"}
- width={80}
- height={40}
- src="/9f/wallet.png"
- alt=""
- />
- <div className={"text-center"}>
- <h2> Tempo regressivo </h2>
- <p className={"text-[#fe0]"}>Começa amanhã às 11:00</p>
- </div>
- </div>
- {!activeTab ? (
- <>
- <Image
- src={"/9f/6xtime.png"}
- className={"mt-[0.1rem] h-[0.7rem] w-[100%] rounded-[0.1rem]"}
- width={600}
- height={100}
- alt={"time"}
- ></Image>
- <Image
- src={"/9f/3xtime.png"}
- className={"mt-[0.1rem] h-[0.55rem] w-[100%] rounded-[0.1rem]"}
- width={600}
- height={80}
- alt={"time"}
- ></Image>
- </>
- ) : (
- <>
- <div
- className={"mt-[0.1rem] rounded-[0.1rem] bg-[#d45300] p-[10px]"}
- >
- <p className={"text-[#fe0]"}>Registro persoal</p>
- <div
- className={
- "grid grid-cols-3 text-center" +
- " items-center text-[0.1rem]"
- }
- >
- <span>Nome do jogo</span>
- <span>Coleta cumulativa</span>
- <span>Participação total</span>
- </div>
- <div
- className={
- "grid grid-cols-3 text-center" +
- " items-center text-[0.12rem] font-bold"
- }
- >
- <span>55****26</span>
- <span>R$ 100.00</span>
- <span>1</span>
- </div>
- </div>
- <div
- className={"mt-[0.1rem] rounded-[0.1rem] bg-[#d45300] p-[10px]"}
- >
- <p className={"text-[#fe0]"}>Lista dos vencedores</p>
- <div
- className={
- "grid grid-cols-3 text-center" +
- " items-center text-[0.1rem]"
- }
- >
- <span>ID do papel</span>
- <span>Valor obtido</span>
- <span>Hora obtida</span>
- </div>
- <div className={`h-[1rem] overflow-hidden`}>
- {mockData.map((item, index) => {
- return (
- <div
- key={index}
- className={`grid grid-cols-3 items-center text-center text-[0.12rem] font-bold ${styles.scrollAnimation}`}
- >
- <span>{item.phone}</span>
- <span>R$ {item.num}</span>
- <span>{item.time}</span>
- </div>
- );
- })}
- </div>
- </div>
- </>
- )}
- <ul className={"mt-[0.1rem] text-[0.1rem]"}>
- <li>
- ·Cada sessão de chuva de dinheiro é distribuída gratuitamente por
- R$100.000.
- </li>
- <li>
- ·Valor máximo de queda em dinheiro: Cada sessão de chuva de dinheiro
- é distribuída gratuitamente.
- </li>
- <li>·Membros recarregados podem reivindicar gratuitamente.</li>
- <li>
- ·O dinheiro recebido pode ser utilizado para jogar ou sacar
- diretamente.
- </li>
- <li>
- ·Quanto for maior o nível de associação VIP, será maior o valor
- recebido.
- </li>
- </ul>
- </div>
- </div>
- </div>
- </div>
- );
- };
- // 红包掉落动画像下雨
- const FallAnimation1 = (props: any) => {
- const { onClose } = props;
- const fallContentRef = useRef<HTMLDivElement>(null);
- const isActive = useRef(true);
- const getRandom = (min: number, max: number) => {
- return Math.random() * (max - min) + min;
- };
- const createMoneyElement = (xPos: number) => {
- if (!fallContentRef.current || !isActive.current) return;
- const money = document.createElement('div');
-
- // 基础样式
- money.style.cssText = `
- position: fixed;
- left: ${xPos}px;
- top: -100px;
- width: 60px;
- height: 60px;
- pointer-events: none;
- `;
- // 创建图片
- const img = document.createElement('img');
- img.src = `/9f/money${Math.floor(Math.random() * 3) + 1}.png`;
- img.style.cssText = `
- width: 100%;
- height: 100%;
- object-fit: contain;
- `;
-
- money.appendChild(img);
- fallContentRef.current.appendChild(money);
- // 使用 requestAnimationFrame 确保元素已添加到 DOM 后再添加动画
- requestAnimationFrame(() => {
- const scale = getRandom(0.4, 1);
- const duration = getRandom(8, 12);
- const delay = getRandom(0, 5);
-
- money.style.cssText += `
- transform: scale(${scale});
- z-index: ${Math.floor(scale * 100)};
- animation: ${styles.fall} ${duration}s linear infinite,
- ${styles.sway} ${duration/2}s ease-in-out infinite alternate;
- animation-delay: ${delay}s;
- `;
- });
- // 当动画完成一次循环后,重新设置位置
- setInterval(() => {
- if (isActive.current) {
- money.style.left = `${getRandom(0, window.innerWidth)}px`;
- }
- }, 12000);
- };
- useEffect(() => {
- console.log('FallAnimation1 mounted'); // 调试日志
- if (!fallContentRef.current) return;
- // 设置容器样式
- fallContentRef.current.style.cssText = `
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- height: 100vh;
- z-index: 1000;
- pointer-events: none;
- overflow: hidden;
- background: transparent;
- `;
- // 分批创建元素
- const totalElements = 300;
- const batchSize = 20;
- let created = 0;
- const createBatch = () => {
- if (!isActive.current) return;
- for (let i = 0; i < batchSize && created < totalElements; i++) {
- const xPos = getRandom(0, window.innerWidth);
- createMoneyElement(xPos);
- created++;
- }
- if (created < totalElements) {
- setTimeout(createBatch, 200);
- }
- };
- createBatch();
- return () => {
- console.log('FallAnimation1 unmounting'); // 调试日志
- isActive.current = false;
- };
- }, []);
- return (
- <div
- ref={fallContentRef}
- style={{
- position: 'fixed',
- top: 0,
- left: 0,
- width: '100%',
- height: '100vh',
- zIndex: 1000,
- }}
- onClick={(e) => {
- e.stopPropagation();
- onClose?.();
- }}
- />
- );
- };
- const HbyInfoDetail = (props: any) => {
- const { iconImg, onCloseHby } = props;
- const router = useRouter();
- const token = getToken();
- const handler = () => {
- if (!token) {
- router.push("/login");
- }
- onCloseHby();
- };
- return (
- <div
- className={`absolute left-1/2 top-[50%] w-[90%] -translate-x-1/2 -translate-y-1/2 ${styles.promoRules}`}
- >
- {/* <Image src={"/hby/close.png"} alt={"close"} width={25} height={25} onClick={onCloseHby} className={styles.closeIcon}/> */}
- <div onClick={onCloseHby} className={styles.closeIcon}></div>
- <Image src={iconImg} onClick={handler} alt={"detail"} width={672} height={1044} />
- {/* <div className={`h-[0.15rem] text-[#ffd800] text-[0.20rem] text-center ${styles.promoTitle}`}>Dinheiro como chuva</div>
- <div className={styles.titleWrap}>
- <span>R$200.00</span>
- <span> por vez, </span>
- <span>Máx queda </span>
- <span>R$7.777</span>
- </div>
- <div className={styles.tips}>
- <img src="/hby/tip-icon.png" alt="tips" className={styles.tipsIcon}/>
- <div className={styles.tipsTime}>Começa às 23:00</div>
- </div>
- <div className={styles.times1}>
- <img src="/hby/time1.png"/>
- </div>
- <div className={styles.times2}>
- <img src="/hby/time2.png"/>
- </div>
- <ul className={styles.rulelist}>
- <li className={styles.ruleItem}>
- Cada sessão de chuva de dinheiro é distribuída gratuitamente com <span>R$200.000</span>
- </li>
- <li className={styles.ruleItem}>
- Valor máximo de queda em dinheiro:Cada sessäo de chuva de dinheiro é distribuida gratuitamente com
- </li>
- <li className={styles.ruleItem}>
- Membros recarregados podem reivindicar gratuitamente
- </li>
- <li className={styles.ruleItem}>
- O dinheiro recebido pode ser utilizado para jogar ou sacado diretamente
- </li>
- <li className={styles.ruleItem}>
- Quanto maior o nivel de associacäo VP, maior o valor recebido
- </li>
- </ul> */}
- </div>
- );
- };
- const HbyInfo = (props: any) => {
- const { iconImg, onCloseHby, onReciveRed } = props;
- if (!iconImg) return;
- return (
- <div
- className={`absolute left-1/2 top-[50%] -translate-x-1/2 -translate-y-1/2 ${styles.redclose}`}
- >
- {/* <Image src={"/hby/close.png"} alt={"close"} width={20} height={20} onClick={onCloseHby} className={styles.closeIcon}/> */}
- <div onClick={onCloseHby} className={styles.closeIcon}></div>
- <img
- src={iconImg}
- alt={"icon"}
- width={559}
- height={687}
- onClick={onReciveRed}
- className={styles.redIcon}
- />
- {/* <div className={styles.title}>Chuva de dinheiro</div>
- <div className={styles.desc}>
- <ul className={styles.desclist}>
- <li className={styles.descitem}> Membros recarregados podem reivindicar gratuitamente. </li>
- <div className={styles.line}></div>
- <li className={styles.descitem}> Valor máximo de queda em dinheiro: R$7.777 </li>
- </ul>
- </div>
- <div className={styles.openBtn} onClick={onReciveRed}>AGARRAR</div> */}
- </div>
- // <div data-v-f333135e="" className={`absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 ${styles.redopen}`}>
- // <Image src={"/hby/close.png"} alt={"close"} width={20} height={20} onClick={onCloseHby} className={styles.closeIcon}/>
- // <div className={styles.title}>Chuva de dinheiro</div>
- // <div className={styles.cash}>1.96</div>
- // <div className={styles.tips}>Valor máximo de queda em dinheiro:Cada sessäo de chuva de dinheiro é </div>
- // </div>
- );
- };
- const HbyInfo2 = (props: any) => {
- const { iconImg, onCloseHby, redAmount } = props;
- const hbyInfoClass = clsx(
- `${styles.redopen} absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 bg-no-repeat bg-cover`
- );
- const t = useTranslations("packetsPopup");
- return (
- <div
- className={hbyInfoClass}
- style={{ background: `url(${iconImg})`, backgroundSize: "100% 100%" }}
- >
- <Image
- src={"/hby/close.png"}
- alt={"close"}
- width={30}
- height={30}
- onClick={onCloseHby}
- className={styles.closeIcon}
- />
- <div className={styles.title}>{t("title")}</div>
- <div className={styles.cash}>{redAmount}</div>
- <div className={styles.tips}>
- {redAmount > 0 ? t("receiveSuccess") : t("receiveWarring")}
- </div>
- </div>
- );
- };
- type Props = {
- onAfterHandler?: () => void;
- };
- export type RedPacketModalProps = {
- onClose: () => void;
- onOpen: (source: any[], index?: number) => void;
- };
- /**
- * @description 红包的三种状态
- * is_start 可领取 展示红包领取组件
- * is_receive 已领取 展示领取情组件
- * is_end 可展示 展示���明页
- */
- enum Status {
- is_start,
- is_receive,
- is_end,
- }
- const RedPacketModal = forwardRef<RedPacketModalProps, Props>(function RedPacketModal(props, ref) {
- const { onAfterHandler } = props;
- const [visible, setVisible] = useState(false);
- const [iconLists, setIconLists] = useState<any>([]);
- // 初始状态为is_end, 展示活动详情页
- const [status, setStatus] = useState<Status>(Status.is_end);
- const packetCurrent = useRef<any>({});
- const packets = useRef<any[]>([]);
- const [redAmount, setRedAmount] = useState<any>(1);
- const activeIndex = useRef<number>(0);
- const token = getToken();
- const element = useRef<HTMLElement | null>(null);
- useImperativeHandle(ref, () => {
- return {
- onClose: () => setVisible(false),
- onOpen: (source, index?: number) => {
- packets.current = source;
- if (index !== null && index !== undefined) {
- activeIndex.current = index;
- }
- getRedPacketInfo().then((res) => {
- setVisible(true);
- });
- },
- };
- });
- const getRedPacketInfo = async () => {
- try {
- let actList = packets.current;
- // 是否开始但是没领过的红包
- let packetsFilter = actList
- .filter((aItem: any) => {
- return aItem.can_receive && aItem.is_start && !aItem.is_receive;
- })
- .sort((pre: any, next: any) => pre.end_time - next.end_time);
- // 有可领取红包
- if (packetsFilter.length > 0) {
- let current = packetsFilter[activeIndex.current];
- let iconList = JSON.parse(current.icon);
- // 红包
- packetCurrent.current = current;
- // 有可领取红包
- setIconLists(iconList);
- if (!token) {
- setStatus(Status.is_end);
- } else {
- setStatus(Status.is_start);
- }
- } else {
- // 无可领取红包
- // 展示最近可领红包详情
- let packets = actList.sort((pre: any, next: any) => pre.end_time - next.end_time);
- packetCurrent.current = packets[0];
- setIconLists(JSON.parse(packets[0].icon));
- // 无可领取红包展示详情
- setStatus(Status.is_end);
- }
- } catch (error) {
- console.log("redPacketInfo===>error:", error);
- }
- };
- const onReciveRed = async () => {
- try {
- let paramsData = {
- id: packetCurrent.current?.id,
- index: packetCurrent.current?.index,
- };
- let receiveRedPacketInfo = await receiveRedPacketApi(paramsData);
- let redNum = receiveRedPacketInfo.data;
- if (onAfterHandler) {
- onAfterHandler();
- }
- setStatus(Status.is_receive);
- setRedAmount(redNum?.amount);
- } catch (error) {
- console.log("redPacketInfo===>error:", error);
- }
- };
- useEffect(() => {
- element.current = document.getElementById("app");
- }, []);
- return (
- <Mask visible={visible} destroyOnClose={true} getContainer={element.current}>
- <FallAnimation1 onClose={() => setVisible(false)} />
- {/*<FallAnimation1 onClose={() => setVisible(false)} />*/}
- {status === Status.is_start ? (
- <HbyInfo
- onCloseHby={() => setVisible(false)}
- onReciveRed={onReciveRed}
- iconImg={iconLists[1]}
- />
- ) : status === Status.is_receive ? (
- <HbyInfo2
- onCloseHby={() => setVisible(false)}
- redAmount={redAmount}
- iconImg={iconLists[2]}
- />
- ) : (
- <HbyInfoDetail onCloseHby={() => setVisible(false)} iconImg={iconLists[0]} />
- )}
- </Mask>
- );
- });
- export default memo(RedPacketModal);
|