year 1 mēnesi atpakaļ
vecāks
revīzija
6ee82df02b

+ 1 - 0
src/api/home.ts

@@ -321,6 +321,7 @@ export interface EmailNoticeRep {
     status: number;
     attach: EmailAttachItem[];
     create_at: number;
+    image?: string;
 }
 
 export const getReplayGamesApi = (props: SearchProps) => {

+ 143 - 39
src/app/[locale]/(navbar)/emailDetail/page.tsx

@@ -1,51 +1,155 @@
+"use client";
+import { EmailNoticeRep } from "@/api/home";
+import GlobalNotify from "@/components/ModalPopup/GlobalNotifyModal";
+import { ClaimActiveErrorMap } from "@/enums";
+import { server } from "@/utils/client";
+import { formatAmount } from "@/utils/index";
+import { timeFormat } from "@/utils/methods";
+import { Toast } from "antd-mobile";
+import { useTranslations } from "next-intl";
+import { useSearchParams } from "next/navigation";
+import React from "react";
 import styles from "./page.module.scss";
+
+const getEmailNotices = () => {
+    return server.request<EmailNoticeRep[]>({
+        url: "/v1/api/user/mail/getMailList",
+        method: "POST",
+        data: {},
+    });
+};
+const changeStatus = (params: { ids: number[]; status: 1 | 0 }) => {
+    return server.request<EmailNoticeRep[]>({
+        url: "/v1/api/user/mail/setStatus",
+        method: "POST",
+        data: params,
+    });
+};
+///v1/api/user/mail/setStatus
+const receiveGift = (ids: number[]) => {
+    return server.request<any>({
+        url: "/v1/api/user/mail/claimMailReward",
+        method: "POST",
+        data: { ids },
+    });
+};
+
+const CoinMap = new Map([
+    [1, "Solad"],
+    [2, "GOLD"],
+    [3, "SILVER"],
+]);
+
 const EmailDetail = () => {
+    const t = useTranslations();
+    const searchParams = useSearchParams();
+    const [data, setData] = React.useState<EmailNoticeRep[]>([]);
+    const [amount, setAmount] = React.useState<any>({});
+    const [visible, setVisible] = React.useState<boolean>(false);
+
+    React.useEffect(() => {
+        getData();
+    }, []);
+
+    const curData = React.useMemo(() => {
+        const id = Number(searchParams.get("id"));
+        const res = data?.filter((item) => {
+            return item.id === id;
+        });
+        if (!res?.length) return {} as EmailNoticeRep;
+        return res[0];
+    }, [data, searchParams]);
+
+    const getData = async () => {
+        const id = Number(searchParams.get("id"));
+        changeStatus({
+            ids: [id],
+            status: 1,
+        });
+        const res = await getEmailNotices();
+        if (res?.code === 200) {
+            setData(res.data);
+        }
+    };
+    const doReceive = async () => {
+        Toast.show({ icon: "loading" });
+        try {
+            const res = await receiveGift([curData.id]);
+
+            if (res.code === 200 && res?.data?.code === 0) {
+                const amountObj: any = {};
+                if (res?.data?.reward) {
+                    res?.data?.reward.forEach((item: any) => {
+                        amountObj[`coin_${item.coin_type}`] = formatAmount(item.amount);
+                    });
+                }
+                if (res?.data?.extra_reward) {
+                    res?.data?.extra_reward.forEach((item: any) => {
+                        amountObj[`coin_${item.coin_type}`] = formatAmount(
+                            new BigNumber(amountObj[`coin_${item.coin_type}`] || 0)
+                                .plus(item.amount)
+                                .toString()
+                        );
+                    });
+                }
+                setAmount(amountObj);
+                setVisible(true);
+            } else {
+                throw new Error(ClaimActiveErrorMap.get(res.data.code) || t(`code.400`));
+            }
+        } finally {
+            getData();
+        }
+    };
+
     return (
         <div className={styles.emailDetail}>
-            <div className={styles.title}>
-                O texto do título do e-mail é exibido aqui. Se o texto for longo
-            </div>
-            <div className={styles.date}>27/03/25 14:22:30</div>
+            <div className={styles.title}>{curData?.title}</div>
+            <div className={styles.date}>{timeFormat(curData?.create_at)}</div>
             <div className={styles.content}>
-                <img
-                    src="https://img1.baidu.com/it/u=2076262900,1204832320&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=678"
-                    alt=""
-                    className={styles.img}
-                />
-                <div
-                    className={styles.contentText}
-                    dangerouslySetInnerHTML={{
-                        __html: `Texto do e-mail:\r\n
-                    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean euismod bibendum
-                    laoreet. Proin gravida dolor sit amet lacus accumsan et viverra justo commodo.
-                    Proin sodales pulvinar sic tempor. Sociis natoque penatibus et magnis dis
-                    parturient montes, nascetur ridiculus mus. Nam fermentum, nulla luctus pharetra
-                    vulputate, felis tellus mollis orci, sed rhoncus pronin sapien nunc accuan eget.`,
-                    }}
-                ></div>
+                {curData?.image && <img src={curData?.image} alt="" className={styles.img} />}
+                {curData?.content && (
+                    <div
+                        className={styles.contentText}
+                        dangerouslySetInnerHTML={{
+                            __html: `${curData?.content}`,
+                        }}
+                    ></div>
+                )}
             </div>
             <div className={styles.more}>&lt;Pular texto&gt;</div>
-            <div className={styles.giftBox}>
-                <div className={styles.giftItem}>
-                    <img src="/notice/gift2.png" alt="" />
-                    <div className={styles.giftDesc}>
-                        <div>O nome da recompensa é mostrado aqui</div>
-                        <div>prêmio R$1</div>
-                    </div>
-                </div>
-                <div className={styles.giftItem}>
-                    <img src="/notice/gift2.png" alt="" />
-                    <div className={styles.giftDesc}>
-                        <div>O nome da recompensa é mostrado aqui</div>
-                        <div>prêmio R$1</div>
+            {curData.status !== 2 && (
+                <div className={styles.giftBox}>
+                    {!!curData?.attach?.length &&
+                        curData.attach.map((item, idx) => {
+                            return (
+                                <div
+                                    className={styles.giftItem}
+                                    key={`${item.coin_type}_${item.amount}`}
+                                >
+                                    <img src="/notice/gift2.png" alt="" />
+                                    <div className={styles.giftDesc}>
+                                        <div>O nome da recompensa é mostrado aqui</div>
+                                        <div>prêmio R${item.amount.toString()}</div>
+                                    </div>
+                                </div>
+                            );
+                        })}
+
+                    <div className={styles.btnBox}>
+                        <button onClick={doReceive}>
+                            <span>Receber</span>
+                        </button>
                     </div>
                 </div>
-                <div className={styles.btnBox}>
-                    <button>
-                        <span>Receber</span>
-                    </button>
-                </div>
-            </div>
+            )}
+
+            <GlobalNotify
+                amount={amount}
+                visible={visible}
+                onChange={() => setVisible(false)}
+                deraction={5000}
+            ></GlobalNotify>
         </div>
     );
 };

+ 33 - 7
src/app/[locale]/(navbar)/notification/NotificationClient.tsx

@@ -6,12 +6,23 @@ import {
     updateUserNoticeApi,
 } from "@/api/home";
 import Tabs from "@/components/Tabs";
+import { useRouter } from "@/i18n/routing";
+import { server } from "@/utils/client";
 import { useTranslations } from "next-intl";
+import { useSearchParams } from "next/navigation";
 import { FC, useState } from "react";
 import actions from "./actions";
 import Email from "./components/Email";
 import { default as Notices } from "./components/Notices";
 
+const getEmailNotices = () => {
+    return server.request<EmailNoticeRep[]>({
+        url: "/v1/api/user/mail/getMailList",
+        method: "POST",
+        data: {},
+    });
+};
+
 interface Props {
     systemNotices: GlobalNoticeRep[];
     userNotices: GlobalNoticeRep[];
@@ -28,9 +39,14 @@ const setReadData = (data: GlobalNoticeRep[], key: number) => {
     return newNotices;
 };
 const NotificationClient: FC<Props> = (props) => {
+    const searchParams = useSearchParams();
+    const router = useRouter();
     const [systemNotices, setSystemNotices] = useState(props.systemNotices);
     const [userNotices, setUserNotices] = useState(props.userNotices);
     const [emailNotices, setEmailNotices] = useState(props.emailNotices || []);
+
+    const [actKey, setActKey] = useState(searchParams.get("type") || "1");
+
     const t = useTranslations("ProfilePage");
     const handler = async (active: string, key: string) => {
         if (key === "system") {
@@ -46,10 +62,8 @@ const NotificationClient: FC<Props> = (props) => {
             updateUserNoticeApi(+active).then((r) => {});
         }
         if (key === "email") {
-            // const isRead = userNotices.filter((item) => item.status === 0);
-            // if (!isRead) return;
-            // setUserNotices(setReadData(userNotices, +active));
-            // updateUserNoticeApi(+active).then((r) => {});
+            const res = await getEmailNotices();
+            setEmailNotices(res?.data || []);
         }
 
         await actions();
@@ -58,6 +72,7 @@ const NotificationClient: FC<Props> = (props) => {
     const defaultTabs = [
         {
             id: 1,
+            key: "1",
             name: (
                 <div className="flex items-center text-[.14rem]">
                     <i className="iconfont icon-laba mr-[.02rem] text-[.14rem]"></i>
@@ -69,6 +84,7 @@ const NotificationClient: FC<Props> = (props) => {
         },
         {
             id: 2,
+            key: "2",
             name: (
                 <div className="flex items-center text-[.14rem]">
                     <i className="iconfont icon-yonghu mr-[.02rem] text-[.16rem]"></i>
@@ -80,6 +96,7 @@ const NotificationClient: FC<Props> = (props) => {
         },
         {
             id: 3,
+            key: "3",
             name: (
                 <div className="flex items-center text-[.14rem]">
                     <i className="iconfont icon-duanxinguanli mr-[.02rem] text-[.18rem]"></i>
@@ -87,12 +104,21 @@ const NotificationClient: FC<Props> = (props) => {
                 </div>
             ),
             content: emailNotices.reduce(
-                (count, notice) => count + (notice.status === 0 ? 0 : 1),
+                (count, notice) => count + (notice.status === 0 ? 1 : 0),
                 0
             ),
-            render: <Email data={emailNotices}></Email>,
+            render: <Email data={emailNotices} handler={handler}></Email>,
         },
     ];
-    return <Tabs items={defaultTabs} />;
+    return (
+        <Tabs
+            items={defaultTabs}
+            actKey={actKey}
+            onChange={(key) => {
+                setActKey(key);
+                router.replace(`/notification?type=${key}`);
+            }}
+        />
+    );
 };
 export default NotificationClient;

+ 143 - 22
src/app/[locale]/(navbar)/notification/components/Email.tsx

@@ -1,33 +1,148 @@
+"use client";
 import { EmailNoticeRep } from "@/api/home";
 import Empty from "@/components/Empty";
+import GlobalNotify from "@/components/ModalPopup/GlobalNotifyModal";
+import { ClaimActiveErrorMap } from "@/enums";
+import { useRouter } from "@/i18n/routing";
+import { server } from "@/utils/client";
+import { formatAmount } from "@/utils/index";
+import { timeFormat } from "@/utils/methods";
+import { Toast } from "antd-mobile";
+import { useTranslations } from "next-intl";
+import React from "react";
 import styles from "./style.module.scss";
+
 interface Props {
     data: EmailNoticeRep[];
+    handler?: (active: string, key: string) => void;
+}
+
+enum Status {
+    DELETE = -1,
+    UNREAD = 0,
+    READ = 1,
+    RECEIVED = 2,
 }
 
-const Email: React.FC<Props> = ({ data }) => {
-    console.log(data);
+const changeStatus = (params: { ids: number[]; status: 1 | -1 }) => {
+    return server.request<EmailNoticeRep[]>({
+        url: "/v1/api/user/mail/setStatus",
+        method: "POST",
+        data: params,
+    });
+};
+const receiveGift = (ids: number[]) => {
+    return server.request<any>({
+        url: "/v1/api/user/mail/claimMailReward",
+        method: "POST",
+        data: { ids },
+    });
+};
+
+const Email: React.FC<Props> = ({ data, handler }) => {
+    const t = useTranslations();
+    const router = useRouter();
+    const [amount, setAmount] = React.useState<any>({});
+    const [visible, setVisible] = React.useState<boolean>(false);
+
+    const goDetail = (item: EmailNoticeRep) => {
+        router.push(`/emailDetail?id=${item.id}`);
+    };
+    const doRefresh = () => {
+        if (typeof handler === "function") {
+            handler("refresh", "email");
+        }
+    };
+
+    const doDeleteRead = async () => {
+        const needSetArr: number[] = [];
+        data.filter((item) => {
+            if (item.status === Status.READ && !item?.attach?.length) {
+                needSetArr.push(item.id);
+            }
+            if (item.status === Status.RECEIVED) {
+                needSetArr.push(item.id);
+            }
+        });
+
+        if (!needSetArr?.length) return;
+        try {
+            Toast.show({ icon: "loading" });
+            await changeStatus({
+                ids: needSetArr,
+                status: -1,
+            });
+            Toast.show({ icon: "success" });
+        } finally {
+            doRefresh();
+        }
+    };
+
+    const doAllReceive = async () => {
+        const needSetArr: number[] = [];
+        data.filter((item) => {
+            if (item.status !== Status.RECEIVED && item?.attach?.length) {
+                needSetArr.push(item.id);
+            }
+        });
+
+        if (!needSetArr?.length) return;
+        try {
+            Toast.show({ icon: "loading" });
+            const res = await receiveGift(needSetArr);
+            if (res.code === 200 && res?.data?.code === 0) {
+                const amountObj: any = {};
+                if (res?.data?.reward) {
+                    res?.data?.reward.forEach((item: any) => {
+                        amountObj[`coin_${item.coin_type}`] = formatAmount(item.amount);
+                    });
+                }
+                if (res?.data?.extra_reward) {
+                    res?.data?.extra_reward.forEach((item: any) => {
+                        amountObj[`coin_${item.coin_type}`] = formatAmount(
+                            new BigNumber(amountObj[`coin_${item.coin_type}`] || 0)
+                                .plus(item.amount)
+                                .toString()
+                        );
+                    });
+                }
+                setAmount(amountObj);
+                setVisible(true);
+            } else {
+                throw new Error(ClaimActiveErrorMap.get(res.data.code) || t(`code.400`));
+            }
+        } finally {
+            doRefresh();
+        }
+    };
+
     return (
         <div className={styles.emailBox}>
             <div className={styles.content}>
-                <div className={styles.emailItem}>
-                    <div className={styles.title}>
-                        <div className="text-[#c6d4e1]">
-                            232322323223232232322323223232232322323223232232322323223232232322323223232232322323223232232322323223232232322323223232232322323223232232322323223232232322323223232232322323223232
-                        </div>
-                        <div className="mt-[.04rem] text-[.1rem] text-[#5d7284]">202112323</div>
-                    </div>
-                    <img className={styles.gift} src="/notice/gift.png" alt="" />
-                    <div className={styles.doGet}>Nova!!</div>
-                </div>
-                <div className={styles.emailItem}>
-                    <div className={styles.title}>
-                        <div className="text-[#c6d4e1]">
-                            232322323223232232322323223232232322323223232232322323223232232322323223232232322323223232232322323223232232322323223232232322323223232232322323223232232322323223232232322323223232
-                        </div>
-                        <div className="mt-[.04rem] text-[.1rem] text-[#5d7284]">202112323</div>
-                    </div>
-                </div>
+                {!!data.length &&
+                    data.map((item) => {
+                        return (
+                            <div
+                                key={item.id}
+                                className={styles.emailItem}
+                                onClick={() => goDetail(item)}
+                            >
+                                <div className={styles.title}>
+                                    <div className="text-[#c6d4e1]">{item.title}</div>
+                                    <div className="mt-[.04rem] text-[.1rem] text-[#5d7284]">
+                                        {timeFormat(item.create_at)}
+                                    </div>
+                                </div>
+                                {item?.attach?.length && item.status !== Status.RECEIVED && (
+                                    <img className={styles.gift} src="/notice/gift.png" alt="" />
+                                )}
+                                {item.status === Status.UNREAD && (
+                                    <div className={styles.doGet}>Nova!!</div>
+                                )}
+                            </div>
+                        );
+                    })}
+
                 {!data?.length && (
                     <div className="flex h-[100%] items-center justify-center">
                         <Empty></Empty>
@@ -42,10 +157,16 @@ const Email: React.FC<Props> = ({ data }) => {
                     Os e-mails recebidos sao limpos após expirar.
                 </div>
                 <div className={styles.btns}>
-                    <button>Excluir leitura</button>
-                    <button>Uma peça para pegar</button>
+                    <button onClick={doDeleteRead}>Excluir leitura</button>
+                    <button onClick={doAllReceive}>Uma peça para pegar</button>
                 </div>
             </div>
+            <GlobalNotify
+                amount={amount}
+                visible={visible}
+                onChange={() => setVisible(false)}
+                deraction={5000}
+            ></GlobalNotify>
         </div>
     );
 };

+ 11 - 1
src/components/Tabs/index.tsx

@@ -10,6 +10,8 @@ interface Item {
 }
 interface Props {
     items: Item[];
+    actKey?: string;
+    onChange?: (key: string) => void;
 }
 // <Badge
 //   className={"flex items-center justify-center text-[20px]"}
@@ -25,7 +27,13 @@ interface Props {
 //   {tab.name}
 // </Badge>
 const Transactions: FC<PropsWithChildren<Props>> = (props) => {
-    const { items } = props;
+    const { items, actKey, onChange } = props;
+
+    const activeKeyChange = (key: string) => {
+        if (typeof onChange === "function") {
+            onChange(key);
+        }
+    };
     return (
         <Tabs
             style={{
@@ -35,6 +43,8 @@ const Transactions: FC<PropsWithChildren<Props>> = (props) => {
                 // @ts-ignore
                 "--adm-color-border": "none",
             }}
+            activeKey={actKey}
+            onChange={activeKeyChange}
         >
             {items.map((tab) => {
                 return (