year пре 3 недеља
родитељ
комит
9675705130

+ 10 - 0
src/api/user.ts

@@ -126,6 +126,8 @@ export interface UserInfoRep {
     user_phone?: string;
     // 正在进行中的游戏
     play_list?: GameListRep[];
+
+    avatar?: number;
 }
 // 前台用户获取信息
 export const getUserInfoApi = () => {
@@ -415,3 +417,11 @@ export const cleanBounsApi = (is_clear: 0 | 1) => {
         },
     });
 };
+
+export const changeUserInfo = (data: { nick_name: string; avatar: number }) => {
+    ///v1/api/user/player/setUserInfo
+    return server.post<any>({
+        url: "/v1/api/user/player/setUserInfo",
+        data,
+    });
+};

+ 2 - 1
src/app/[locale]/(TabBar)/deposit/component/withdraw/index.tsx

@@ -7,6 +7,7 @@ import { ChannelEnum, ChannelEnumMap } from "@/enums";
 import useGame from "@/hooks/useGame";
 import { useRouter } from "@/i18n/routing";
 import { useSystemStore } from "@/stores/useSystemStore";
+import { useUserInfoStore } from "@/stores/useUserInfoStore";
 import { useWalletStore } from "@/stores/useWalletStore";
 import { isEmail } from "@/utils";
 import { server } from "@/utils/client";
@@ -43,7 +44,7 @@ const Withdraw = () => {
     const [amount, setAmount] = React.useState<string>("");
     const { wallet } = useWalletStore();
     const isStrictMode = useSystemStore((state) => state.identity_verify.withdraw === 1);
-
+    const userInfo = useUserInfoStore((state) => state.userInfo);
     // const canWithdrawRef = React.useRef<ModalProps>(null);
     const formInstanceRef = React.useRef<any>(null);
     const scoreRef = React.useRef<ModalProps>(null);

+ 23 - 5
src/app/[locale]/(TabBar)/profile/ProfileHeader.tsx

@@ -10,6 +10,7 @@ import {
 } from "@/components/ModalPopup/WalletDescribeModal";
 import TipsModal, { ModalProps } from "@/components/TipsModal";
 import Vip from "@/components/Vip";
+import { HeaderImageMap } from "@/enums";
 import useGame from "@/hooks/useGame";
 import { Link, useRouter } from "@/i18n/routing";
 import { usePollingStore } from "@/stores/usePollingStore";
@@ -25,6 +26,7 @@ import { useTranslations } from "next-intl";
 import Image from "next/image";
 import { Fragment, useEffect, useMemo, useRef, useState } from "react";
 import { shallow } from "zustand/shallow";
+import ChangeAvatar from "./component/ChangeAvatar";
 
 type Props = {
     userInfo: UserInfoRep;
@@ -397,9 +399,10 @@ export const ProfileHeader = () => {
     // }));
     const router = useRouter();
     const [isShowNoBounsWarn, setIsShowNoBounsWarn] = useState(false);
+    const [isShowAvatar, setIsShowAvatar] = useState(false);
     const [isShowed, setIsShowed] = useState(false);
 
-    const { data: userInfo } = useRequest<any, any>(userInfoApi, {
+    const { data: userInfo, run: refreshUserInfo } = useRequest<any, any>(userInfoApi, {
         pollingErrorRetryCount: 1,
     });
 
@@ -455,7 +458,8 @@ export const ProfileHeader = () => {
         refresh && refresh();
     };
 
-    const doCopyUsreId = () => {
+    const doCopyUsreId = (evt: any) => {
+        (evt as any).stopPropagation();
         copyText(`${userInfo.data.user_phone}`);
         Toast.show({ icon: "success", content: tc("SummaryPage.copySuc"), maskClickable: false });
     };
@@ -464,10 +468,13 @@ export const ProfileHeader = () => {
         <>
             <div className={"userContent"}>
                 <div className={"userInfo"}>
-                    <div>
+                    <div onClick={() => setIsShowAvatar(true)}>
                         <div className={"bgImg"}>
                             <Image
-                                src={"/img/avatar.webp"}
+                                src={
+                                    HeaderImageMap.get(userInfo?.data?.avatar)?.img ||
+                                    "/img/avatar.webp"
+                                }
                                 className={
                                     "h-[100%] w-[100%] rounded-[50%] border-[2px] border-[#ccc]"
                                 }
@@ -475,6 +482,7 @@ export const ProfileHeader = () => {
                                 width={120}
                                 height={120}
                             />
+
                             <Vip
                                 style={{
                                     position: "absolute",
@@ -486,7 +494,9 @@ export const ProfileHeader = () => {
                             ></Vip>
                         </div>
                         <div>
-                            <span className="font-bold">{t("Conta")}</span>
+                            <span className="font-bold">
+                                {userInfo?.data?.nick_name || t("Conta")}
+                            </span>
                             <div className="flex items-center">
                                 <span className="phone text-[#7b939a]">
                                     {userInfo?.data?.user_phone || ""}
@@ -525,6 +535,14 @@ export const ProfileHeader = () => {
                 onClose={() => setIsShowNoBounsWarn(false)}
                 onConfirm={doConfirm}
             ></NoBounsWarn>
+            <ChangeAvatar
+                visible={isShowAvatar}
+                useInfo={userInfo?.data}
+                onClose={() => {
+                    refreshUserInfo();
+                    setIsShowAvatar(false);
+                }}
+            ></ChangeAvatar>
         </>
     );
 };

+ 86 - 0
src/app/[locale]/(TabBar)/profile/component/ChangeAvatar/index.module.scss

@@ -0,0 +1,86 @@
+.Dialog {
+    width: 80%;
+    position: absolute;
+    left: 50%;
+    top: 50%;
+    transform: translate3d(-50%, -50%, 0);
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    &Container {
+        width: 100%;
+        position: relative;
+        height: 100%;
+        background-color: #1f2830;
+        border-radius: 0.1rem;
+        overflow: hidden;
+        opacity: 0.2;
+        transition: all 0.3s;
+        padding-bottom: 0.1rem;
+        &.visible {
+            opacity: 1;
+        }
+    }
+    .close {
+        position: absolute;
+        right: 0.1rem;
+        top: 0.05rem;
+        z-index: 1;
+        padding: 0.05rem 0.05rem 0.1rem 0.1rem;
+        overflow: hidden;
+    }
+
+    .content {
+        padding: 0.15rem;
+    }
+    .title {
+        color: #11de68;
+        font-size: 0.16rem;
+        font-weight: 700;
+        text-align: center;
+    }
+    .curHeader {
+        padding: 0.2rem 0 0.1rem 0;
+        text-align: center;
+        img {
+            width: 0.8rem;
+            height: 0.8rem;
+            border-radius: 50%;
+            display: inline-block;
+        }
+    }
+    .imgs {
+        display: grid;
+        grid-template-columns: repeat(4, 1fr);
+        grid-column-gap: 0.1rem;
+        grid-row-gap: 0.15rem;
+        padding: 0.3rem 0 0.1rem 0;
+    }
+    .imgItem {
+        aspect-ratio: 1/1;
+        border-radius: 50%;
+        border: 2px solid transparent;
+        position: relative;
+        img {
+            border-radius: 50%;
+        }
+        &.active {
+            border: 2px solid #11de68;
+            .check {
+                display: block;
+            }
+        }
+        .check {
+            display: none;
+            position: absolute;
+            right: 0;
+            bottom: 0;
+            border-radius: 50%;
+            background: #11de68;
+            width: 0.2rem;
+            height: 0.2rem;
+            text-align: center;
+            line-height: 0.2rem;
+        }
+    }
+}

+ 122 - 0
src/app/[locale]/(TabBar)/profile/component/ChangeAvatar/index.tsx

@@ -0,0 +1,122 @@
+import { HeaderImageMap } from "@/enums";
+import { Mask, Toast } from "antd-mobile";
+// import { CheckCircleOutline } from "antd-mobile-icons";
+import { changeUserInfo, UserInfoRep } from "@/api/user";
+import clsx from "clsx";
+import { useTranslations } from "next-intl";
+import React from "react";
+import styles from "./index.module.scss";
+
+interface Props {
+    visible: boolean;
+    useInfo: UserInfoRep;
+    onClose?: () => void;
+}
+
+interface ImgItem {
+    key: number;
+    img: string;
+}
+
+const ChangeAvatar: React.FC<Props> = ({ visible = false, useInfo, onClose }) => {
+    const t = useTranslations("ProfilePage");
+
+    const [innerVisible, setInnerVisible] = React.useState<boolean>(false);
+    const [curAvatar, setCurAvatar] = React.useState(useInfo?.avatar || 1);
+
+    const imgs: ImgItem[] = React.useMemo(() => {
+        const result: ImgItem[] = [];
+        for (let [key, value] of HeaderImageMap.entries()) {
+            result.push({
+                img: value.img,
+                key: Number(key),
+            });
+        }
+        return result;
+    }, []);
+
+    React.useEffect(() => {
+        setInnerVisible(visible);
+    }, [visible]);
+
+    React.useEffect(() => {
+        setCurAvatar(useInfo?.avatar || 1);
+    }, [useInfo]);
+
+    const doChangeAvatar = async () => {
+        const loadToast = Toast.show({
+            icon: "loading",
+            duration: 0,
+        });
+        try {
+            const res = await changeUserInfo({
+                avatar: curAvatar,
+                nick_name: useInfo?.nick_name || "",
+            });
+            if (res.code === 200) {
+                Toast.show({ icon: "success", content: t("success") });
+                close();
+            }
+        } catch {
+            loadToast.close();
+        }
+    };
+
+    const close = () => {
+        if (typeof onClose === "function") {
+            onClose();
+        }
+    };
+
+    return (
+        <Mask visible={visible} onMaskClick={close}>
+            <div className={styles.Dialog}>
+                <div
+                    className={clsx(styles.DialogContainer, {
+                        [styles.visible]: innerVisible,
+                    })}
+                >
+                    <div className={styles.content}>
+                        <div className={styles.title}>Selecione um Avatar</div>
+                        <div className={styles.curHeader}>
+                            <img src={HeaderImageMap.get(curAvatar)?.img} alt="" />
+                        </div>
+                        <div className="text-center text-[.12rem]">Alterar sua foto de perfil</div>
+
+                        <div className={styles.imgs}>
+                            {imgs.map((item) => {
+                                return (
+                                    <div
+                                        key={item.key}
+                                        className={clsx(styles.imgItem, {
+                                            [styles.active]: curAvatar == item.key,
+                                        })}
+                                        onClick={() => {
+                                            setCurAvatar(item.key);
+                                        }}
+                                    >
+                                        <img src={item.img} alt="" />
+                                        <span className={styles.check}>
+                                            <i className="iconfont icon-gouxuan1 text-[.1rem]"></i>
+                                        </span>
+                                    </div>
+                                );
+                            })}
+                        </div>
+                        <div
+                            className="mt-[.1rem] rounded-[.2rem] bg-[#11de68] py-[.08rem] text-center text-[.18rem] font-bold text-[#12171a]"
+                            onClick={doChangeAvatar}
+                        >
+                            Claro
+                        </div>
+                    </div>
+                    <div className={clsx(styles.close)} onClick={close}>
+                        <i className={clsx("iconfont icon-guanbi relative z-10 text-[.14rem]")}></i>
+                    </div>
+                </div>
+            </div>
+        </Mask>
+    );
+};
+
+export default ChangeAvatar;

+ 13 - 11
src/app/[locale]/(doings)/rank/detail/page.tsx

@@ -117,20 +117,22 @@ const Page = () => {
 
     return (
         <div className="relative h-[100%] overflow-auto" id="outContainer">
-            <div
-                onClick={() => {
-                    setModalType(-1);
-                    historyRef.current?.onOpen();
-                }}
-                className="absolute right-[0px] top-[320px] z-[1] rounded-[.08rem_0_0_.08rem] bg-[rgba(255,255,255,.2)] px-[.1rem] py-[.04rem]"
-            >
-                <i className="iconfont icon-tubiaozhizuomoban- mr-[.06rem]"></i>
-                <span>Histórico</span>
-            </div>
             <div className={styles.header}>
                 <RankSwiper data={rankData} areaId={areaId} onChange={swiperChange}></RankSwiper>
             </div>
-            <div id="middleContainer" className={styles.middleContainerRef}></div>
+
+            <div id="middleContainer" className={styles.middleContainerRef}>
+                <div
+                    onClick={() => {
+                        setModalType(-1);
+                        historyRef.current?.onOpen();
+                    }}
+                    className="absolute right-[0px] top-[.7rem] z-[1] rounded-[.08rem_0_0_.08rem] bg-[rgba(255,255,255,.2)] px-[.1rem] py-[.04rem]"
+                >
+                    <i className="iconfont icon-tubiaozhizuomoban- mr-[.06rem]"></i>
+                    <span>Histórico</span>
+                </div>
+            </div>
             {!initLoading && (
                 <>
                     <MiddleContainer

+ 4 - 8
src/app/[locale]/(enter)/components/Form/index.tsx

@@ -1,5 +1,5 @@
 "use client";
-import { loginApi, registerApi, userInfoApi } from "@/api/login";
+import { loginApi, registerApi } from "@/api/login";
 import ButtonOwn from "@/components/ButtonOwn";
 import MobileField from "@/components/Fields/MobileField";
 import { useEventPoint } from "@/hooks/useEventPoint";
@@ -217,13 +217,9 @@ const FormComponent: FC<Props> = (props) => {
             if (loginResult?.code === 200) {
                 eventLogin();
                 setCookies("Token", loginResult.data.token as string);
-                // loginAction(loginResult.data.token)
-                const result = await userInfoApi();
-                if (result.code === 200) {
-                    setUserInfo(result.data);
-                    resolve(result);
-                    return result;
-                }
+                setUserInfo(loginResult.data);
+                resolve(loginResult);
+                return loginResult;
             } else {
                 reject();
             }

+ 2 - 1
src/app/[locale]/(navbar)/withdraw/WithdrawWidget.tsx

@@ -146,6 +146,7 @@ const WithdrawWidget: FC<Props> = (props) => {
     const t = useTranslations();
     const { channels, wallet } = props;
     const { getGameUrl } = useGame();
+    // const userInfo = useUserInfoStore((state) => state.userInfo);
 
     const isStrictMode = useSystemStore((state) => state.identity_verify.withdraw === 1);
 
@@ -173,10 +174,10 @@ const WithdrawWidget: FC<Props> = (props) => {
         }));
 
     const userInfo = useUserInfoStore((state) => state.userInfo);
+
     const initParams = {
         channel: "",
         amount: "",
-
         passport: userInfo.passport,
         user_name: userInfo.user_name,
     };

+ 6 - 0
src/enums/index.tsx

@@ -141,4 +141,10 @@ export const HeaderImageMap = new Map<number, Record<string, any>>([
             img: "/user_header/h_7.webp",
         },
     ],
+    [
+        8,
+        {
+            img: "/user_header/h_8.webp",
+        },
+    ],
 ]);

+ 30 - 37
tsconfig.json

@@ -1,40 +1,33 @@
 {
-  "compilerOptions": {
-    "lib": [
-      "dom",
-      "dom.iterable",
-      "esnext"
+    "compilerOptions": {
+        "lib": ["dom", "dom.iterable", "esnext"],
+        "target": "es2015",
+        "allowJs": true,
+        "skipLibCheck": true,
+        "strict": true,
+        "noEmit": true,
+        "esModuleInterop": true,
+        "module": "esnext",
+        "moduleResolution": "bundler",
+        "resolveJsonModule": true,
+        "isolatedModules": true,
+        "jsx": "preserve",
+        "incremental": true,
+        "plugins": [
+            {
+                "name": "next"
+            }
+        ],
+        "paths": {
+            "@/*": ["./src/*"]
+        }
+    },
+    "include": [
+        "next-env.d.ts",
+        "**/*.ts",
+        "**/*.tsx",
+        ".next/types/**/*.ts",
+        "build/types/**/*.ts"
     ],
-    "allowJs": true,
-    "skipLibCheck": true,
-    "strict": true,
-    "noEmit": true,
-    "esModuleInterop": true,
-    "module": "esnext",
-    "moduleResolution": "bundler",
-    "resolveJsonModule": true,
-    "isolatedModules": true,
-    "jsx": "preserve",
-    "incremental": true,
-    "plugins": [
-      {
-        "name": "next"
-      }
-    ],
-    "paths": {
-      "@/*": [
-        "./src/*"
-      ]
-    }
-  },
-  "include": [
-    "next-env.d.ts",
-    "**/*.ts",
-    "**/*.tsx",
-    ".next/types/**/*.ts",
-    "build/types/**/*.ts"
-  ],
-  "exclude": [
-    "node_modules"
-  ]
+    "exclude": ["node_modules"]
 }