123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523 |
- "use client";
- import { getWheelRunApi, WheelsType } from "@/api/cashWheel";
- import { useRouter } from "@/i18n/routing";
- import useWheelStore from "@/stores/useWheelStore";
- import { percentage, timeFormat } from "@/utils/methods";
- import { LuckyWheel } from "@lucky-canvas/react";
- import NumberFlow from "@number-flow/react";
- import { Mask, ProgressBar } from "antd-mobile";
- import Image from "next/image";
- import { FC, forwardRef, memo, useEffect, useImperativeHandle, useRef, useState } from "react";
- import animation from "../animations.module.scss";
- import styles from "./style.module.scss";
- type Props = {
- onAfterHandler?: () => void;
- };
- export type WheelModalProps = {
- onClose: () => void;
- onOpen: (value: WheelsType) => void;
- };
- /**
- * 轮盘组件
- */
- const blocks = [
- {
- padding: "0",
- imgs: [
- {
- src: "/wheels/wheel.png",
- width: "100%",
- height: "100%",
- rotate: true,
- },
- ],
- },
- ];
- const prizes = [
- {
- fonts: [
- {
- text: "5000",
- top: "20%",
- fontColor: "#a47716",
- fontWeight: "bold",
- type: "bonus",
- fontSize: "0.1528rem",
- },
- ],
- },
- {
- fonts: [{ text: "", top: "20%", fontColor: "#a47716", fontWeight: "bold", type: "empty" }],
- imgs: [
- {
- src: "/wheels/prizes-empty.png",
- top: "20%",
- width: "0.2778rem",
- },
- ],
- },
- {
- fonts: [
- {
- text: "50",
- top: "20%",
- fontColor: "#a47716",
- fontWeight: "bold",
- type: "bonus",
- fontSize: "0.1528rem",
- },
- ],
- },
- {
- fonts: [
- { text: "", top: "30%", fontColor: "#a47716", fontWeight: "bold", type: "balance" },
- ],
- imgs: [
- {
- src: "/wheels/prizes-money.png",
- top: "20%",
- width: "0.3472rem",
- },
- ],
- },
- {
- fonts: [
- {
- text: "1000",
- top: "20%",
- fontColor: "#a47716",
- fontWeight: "bold",
- type: "bonus",
- fontSize: "0.1528rem",
- },
- ],
- },
- {
- imgs: [
- {
- src: "/wheels/prizes-empty.png",
- top: "20%",
- width: "0.2778rem",
- height: "0.2778rem",
- },
- ],
- },
- {
- fonts: [
- {
- text: "1",
- top: "20%",
- fontColor: "#a47716",
- fontWeight: "bold",
- type: "bonus",
- fontSize: "0.1528rem",
- },
- ],
- },
- {
- fonts: [
- {
- text: "",
- top: "20%",
- fontColor: "#a47716",
- fontWeight: "bold",
- type: "balance",
- fontSize: "0.1528rem",
- },
- ],
- imgs: [
- {
- src: "/wheels/prizes-money.png",
- top: "20%",
- width: "0.3472rem",
- },
- ],
- },
- ];
- const defaultConfig = {
- offsetDegree: 20,
- };
- /**
- * type: isRotate 是否可旋转, 分享页面不需要旋转, 而是跳转
- */
- export interface WheelProps {
- isRotate: boolean;
- onRotateEnd?: () => void;
- onRotateBefore?: () => void;
- onRotateDisable?: () => void;
- }
- const noop = () => {};
- export const WheelClient: FC<WheelProps> = (props) => {
- const { isRotate, onRotateEnd = noop, onRotateBefore = noop, onRotateDisable = noop } = props;
- const { statusWheel, currentWheel, setWheel } = useWheelStore((state) => ({
- statusWheel: state.status,
- currentWheel: state.currentWheel,
- setWheel: state.setWheel,
- }));
- // 选中 dom
- const activeRef = useRef<HTMLImageElement | null>(null);
- const wheelRef = useRef<any>();
- /*是否旋转*/
- const rotating = useRef<boolean>(false);
- const router = useRouter();
- const [isWin, setIsWin] = useState(false);
- const [buttonText, setButtonText] = useState<number>(0); // 0 -> false 1:true
- // 当前中奖
- const currentWin = useRef<any>({});
- const startRotate = (key: string) => {
- if (!isRotate) {
- router.push("/register");
- return;
- }
- // 正在旋转中
- if (rotating.current) return;
- // 如果是没有旋转次数
- if (currentWheel.can === 0) {
- onRotateDisable();
- }
- if (statusWheel !== 1) return;
- // 开始旋转中
- wheelRef.current?.play();
- // 点击抽奖按钮会触发star回调
- rotating.current = true;
- // 开始旋转前回调
- onRotateBefore();
- getWheelRunApi({ activity_id: currentWheel.id! })
- .then((res) => {
- setTimeout(() => {
- wheelRef.current?.stop(res.data.index);
- currentWin.current = res.data;
- }, 2000);
- })
- .catch(() => {
- wheelRef.current?.init();
- });
- };
- const endRotate = (prize: any) => {
- activeRef.current!.style.display = "block";
- if (currentWin.current.amount > 0) {
- setIsWin(true);
- setButtonText(currentWin.current.amount);
- } else {
- setButtonText(currentWheel.can || 0);
- setIsWin(false);
- }
- setTimeout(() => {
- setWheel().then((data) => {
- rotating.current = false;
- if (activeRef.current) {
- activeRef.current.style.display = "none";
- }
- onRotateEnd && onRotateEnd();
- setButtonText(data?.activate.can || 0);
- });
- // 重置状态
- setIsWin(false);
- }, 2000);
- };
- useEffect(() => {
- setButtonText(currentWheel.can || 0);
- }, [currentWheel.can]);
- return (
- <div className={"relative w-[100%] transform"}>
- <Image
- src="/wheels/aura.png"
- alt=""
- width={750}
- height={300}
- className={`absolute -z-[1] h-[100%] ${animation.floatAnimation}`}
- />
- <div className={"absolute h-[3.0833rem] w-[100%] overflow-hidden"}>
- <Image
- width={750}
- height={300}
- src="/wheels/bg-light.png"
- className={`absolute -top-[0.2778rem] left-0 -z-[1] ${animation.rotateAnimation}`}
- alt=""
- />
- </div>
- <Image
- width={750}
- height={300}
- alt=""
- src={"/wheels/wheel-bg.png"}
- className={"mx-auto h-[5.6rem] w-[100%] object-cover"}
- />
- <Image
- width={750}
- height={300}
- alt=""
- src={"/wheels/title.png"}
- className={"absolute left-[13%] top-[29%] z-10 w-[72%]"}
- />
- <Image
- width={750}
- height={300}
- alt=""
- src={"/wheels/bg-buttom.png"}
- className={
- "absolute bottom-[0.35rem] left-[54%] z-20 w-[1.7361rem] -translate-x-1/2"
- }
- />
- {/*定位到中心圆*/}
- <div className={"absolute bottom-[19.1%] z-10 h-[2.68rem] w-[100%]"}>
- <div className={"relative flex h-[100%] w-[100%] justify-center"}>
- <img
- src="/wheels/light-1.png"
- alt=""
- className={`absolute h-[100%] ${animation.flashingAnimation}`}
- />
- <img
- src="/wheels/light-2.png"
- alt=""
- className={`absolute h-[100%] ${animation.antiFlashingAnimation}`}
- />
- {/*<img*/}
- {/* src="/wheels/light-3.png"*/}
- {/* alt=""*/}
- {/* className={`absolute h-[100%] ${styles.closeFlashing}`}*/}
- {/*/>*/}
- <img
- ref={activeRef}
- src="/wheels/active-bg.png"
- className={`absolute z-10 ml-[0] mt-[0.159rem] hidden h-[1.3rem] ${animation.activeAnimation}`}
- alt=""
- />
- <div className={"relative h-[100%] w-[2.7rem] rounded-[50%] p-[0.14rem]"}>
- <LuckyWheel
- ref={wheelRef}
- width="2.42rem"
- height="2.42rem"
- blocks={blocks}
- defaultConfig={defaultConfig}
- prizes={prizes}
- onEnd={(prize: any) => endRotate(prize)}
- />
- <div
- className={
- "absolute bottom-[50%] left-0 z-[888] w-[100%] " +
- " translate-y-1/2"
- }
- >
- <div
- className={"relative flex h-[1.8rem] justify-center"}
- onClick={() => startRotate("desktop")}
- >
- <Image
- src={"/wheels/pointer.png"}
- className={"h-[1.8rem]" + " object-contain"}
- width={200}
- height={150}
- alt={"start"}
- />
- <div
- className={
- "absolute bottom-[50%] translate-y-1/2 text-center" +
- " text-[#ffdb0e]" +
- " text-[0.2222rem] font-black"
- }
- >
- <p className={"-mt-[0.2778rem] h-[0.22rem]"}>
- {buttonText > 0 && isWin ? "+" : ""}
- </p>
- <NumberFlow value={buttonText} />
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- );
- };
- export const LeftListClient = () => {
- const allHistory: any[] = Array.from({ length: 100 }).map((item) => ({
- phone_number: `55****${Math.floor(Math.random() * 10000)
- .toString()
- .padStart(4, "0")}`,
- receive_time: Date.now(),
- }));
- return (
- <>
- <div className={`${styles.winList} ${styles.swipernoswiping} ${styles.type2}`}>
- {allHistory &&
- allHistory.length > 0 &&
- allHistory?.map((item, index) => {
- return (
- <div className={styles.item} key={index}>
- <span className={`${styles.name} ${styles.omitWrap}`}>
- {item.phone_number}
- </span>
- <span className={styles.tipText}>
- {timeFormat(item.receive_time, "br", undefined, true)}
- </span>
- <div className={styles.value}>
- <span className={styles.addCash}>+100</span>
- <span className={styles.unit}> R$</span>
- </div>
- </div>
- );
- })}
- </div>
- </>
- );
- };
- const DetailClient = (props: { onUnload: () => void }) => {
- const { onUnload } = props;
- const router = useRouter();
- const { totalCount, currentWheel, setWheel } = useWheelStore((state) => ({
- statusWheel: state.status,
- currentWheel: state.currentWheel,
- setWheel: state.setWheel,
- totalCount: state.totalCount,
- }));
- const [count, setCount] = useState(0);
- const goPage = () => {
- router.push("/cashWheel");
- onUnload();
- };
- useEffect(() => {
- setCount(currentWheel.amount || 0);
- }, [currentWheel.amount]);
- return (
- <div className={`${styles.cashMain} ${styles.cashMain} ${styles.type1}`}>
- <div className={styles.haveCash}>
- <img src="/wheel/cash.png" alt="" className={styles.cashImg} />
- <div className={styles.cash}>
- <NumberFlow value={count} prefix={"R$ "} trend={+1} />
- </div>
- <span className={styles.withdraw}>
- <img src="/wheel/pix.png" alt="" /> SACAR{" "}
- </span>
- </div>
- <div className={styles.progress}>
- <div className={styles.num}> {percentage(count, 100)}%</div>
- {/*<div className={styles.bar}>*/}
- {/* <span style={{ width: "calc(97.15% - 1rem)" }}></span>*/}
- {/*</div>*/}
- <ProgressBar
- percent={percentage(count, 100)}
- style={{
- "--fill-color": "#fb8b05",
- "--track-width": "0.0694rem",
- }}
- />
- </div>
- <div className={styles.needCash}>
- Ainda e necessário{" "}
- <span className={styles.needCashNum}>
- {" "}
- <NumberFlow value={totalCount - count} trend={-1} />
- </span>{" "}
- para realizar do saque{" "}
- </div>
- <div
- className={
- "h-[0.34rem] w-[100%] rounded-[0.0694rem] bg-[#fb8b05] text-[#fff]" +
- " flex items-center justify-center"
- }
- onClick={goPage}
- >
- Reivindique mais para sacar
- </div>
- <div className={"mt-[10px] h-[2.0833rem] w-[100%] overflow-hidden"}>
- <LeftListClient />
- </div>
- </div>
- );
- };
- const WheelModal = forwardRef<WheelModalProps, Props>(function RedPacketModal(props, ref) {
- const [visible, setVisible] = useState(false);
- const [detailsVisible, setDetailsVisible] = useState(false);
- useImperativeHandle(ref, () => {
- return {
- onClose: () => setVisible(false),
- onOpen: () => {
- setVisible(true);
- },
- };
- });
- const onRotateEnd = () => {
- setVisible(false);
- setDetailsVisible(true);
- };
- const onUnload = () => {
- setDetailsVisible(false);
- };
- return (
- <>
- <Mask
- visible={detailsVisible}
- getContainer={null}
- destroyOnClose={true}
- opacity="thick"
- >
- <div className={"absolute top-[18%] z-50 w-[100%] px-[0.1389rem]"}>
- <div className={"rounded-[0.0694rem] bg-[#232327FF] p-[0.0694rem]"}>
- <div className={"flex items-center"}>
- <div className={"flex flex-1"}>
- <Image
- src={"/wheels/prizes-money.png"}
- width={40}
- height={40}
- alt={"moeny"}
- ></Image>
- <span className={"ml-[0.0694rem]"}>Receba R$ 100 de graca</span>
- </div>
- <span
- className={"iconfont icon-guanbi"}
- onClick={() => setDetailsVisible(false)}
- ></span>
- </div>
- <DetailClient onUnload={onUnload} />
- </div>
- </div>
- </Mask>
- <Mask visible={visible} destroyOnClose={true} getContainer={null} opacity="thick">
- <div
- className={"absolute right-[0.2083rem] top-[18%] z-50"}
- onClick={() => setVisible(false)}
- >
- <span className={"iconfont icon-guanbi"}></span>
- </div>
- <div className={"absolute top-[5%] w-[100%]"}>
- <WheelClient isRotate={true} onRotateEnd={onRotateEnd} />
- </div>
- </Mask>
- </>
- );
- });
- export default memo(WheelModal);
|