Explorar el Código

添加游戏介绍

ith5 hace 3 meses
padre
commit
c99d85ec41

+ 18 - 0
src/api/gift/index.ts

@@ -0,0 +1,18 @@
+import request from "../../http/request";
+
+export const getVipCustomersGiftListApi = (params) => {
+  return request({
+    url: "/vip_customers_gift/list",
+    method: "POST",
+    data: params,
+  });
+};
+
+// 领取礼包
+export const receiveVipCustomersGiftApi = (params) => {
+  return request({
+    url: "/vip_customers_gift/receive",
+    method: "POST",
+    data: params,
+  });
+};

+ 13 - 0
src/api/log/index.ts

@@ -0,0 +1,13 @@
+import request from "../../http/request";
+import { getSystemInfo } from "../../utils";
+/**
+ * 下载日志
+ */
+export const downLogApi = (params) => {
+  const os = getSystemInfo();
+  return request({
+    url: "/logs/download-log",
+    method: "POST",
+    data: { os, ...params },
+  });
+};

+ 26 - 1
src/api/user/index.ts

@@ -1,5 +1,5 @@
 import request from "../../http/request";
-
+import { getSystemInfo } from "../../utils";
 /**
  * 用户信息
  * @returns
@@ -10,3 +10,28 @@ export const getUserInfoApi = (userId?: any) => {
     method: "POST",
   });
 };
+
+/**
+ * vip用户信息
+ */
+
+export const getVipUserInfoApi = (params) => {
+  const os = getSystemInfo();
+  return request({
+    url: "/users/vip-info",
+    method: "POST",
+    data: { os, ...params },
+  });
+};
+
+/**
+ * vip用户角色信息
+ */
+export const getVipUserRoleListApi = (params) => {
+  const os = getSystemInfo();
+  return request({
+    url: "/users/vip-role-list",
+    method: "POST",
+    data: { os, ...params },
+  });
+};

BIN
src/asset/img/b1.jpg


BIN
src/asset/img/b2.jpg


+ 134 - 61
src/components/game-intro/index.tsx

@@ -1,78 +1,151 @@
-import { Image, Swiper, Button } from "@nutui/nutui-react-taro";
-import { Video, View } from "@tarojs/components";
-const GameIntro = () => {
+import { Button, Image } from "@nutui/nutui-react-taro";
+import { View, ScrollView } from "@tarojs/components";
+import { downLogApi } from "../../api/log";
+const GameIntro = (props) => {
+  const { vipUserInfo } = props;
+  console.log(vipUserInfo);
+
+  // 下载游戏
+  const handleDown = async (item: any) => {
+    {
+      // 下载功能
+      try {
+        await downLogApi({
+          user_name: vipUserInfo.user_name,
+          phone: vipUserInfo.phone,
+          server_id: vipUserInfo.server_id,
+          game_id: vipUserInfo.download_list[0].to_game_id,
+        });
+        // 非微信内置浏览器,直接跳转下载
+        var schemeUrl = vipUserInfo.download_list[0].open_url; // App 的 URL Scheme
+        var downloadUrl = vipUserInfo.download_list[0].url; // 你的下载页链接
+
+        console.log("schemeUrl", schemeUrl);
+        console.log("downloadUrl", downloadUrl);
+
+        if (schemeUrl) {
+          var timer: ReturnType<typeof setTimeout> | undefined = undefined;
+          var hasApp = false;
+
+          // 监听 visibilitychange,如果页面切到后台,说明 App 被唤起了
+          document.addEventListener("visibilitychange", function () {
+            if (document.hidden) {
+              hasApp = true;
+              clearTimeout(timer); // 停止 fallback
+            }
+          });
+
+          // 尝试唤起 App
+          window.location.href = schemeUrl;
+
+          // 定时器:如果 1.5 秒后还没切后台,说明 App 没安装 → 跳下载页
+          timer = setTimeout(function () {
+            if (!hasApp) {
+              window.location.href = downloadUrl;
+            }
+          }, 1500);
+        } else {
+          window.location.href = downloadUrl;
+        }
+      } catch (error) {
+        console.log(error);
+      }
+    }
+  };
   return (
-    <View className="absolute w-[100%] h-[100vh] left-[0px] top-[0px] bg-[#606cd2] relative">
-      <Swiper indicator>
-        <Swiper.Item>
-          <Video
-            autoplay={true}
-            loop={true}
-            showFullscreenBtn={false}
-            showPlayBtn={false}
-            showCenterPlayBtn={false}
-            enableProgressGesture={false}
-            showMuteBtn={false}
-            muted={true}
-            showProgress={false}
-            controls={false}
-            poster="https://upload.yunfanyouxi.com/nbc/nativeMaterial/32/936a44ec19d63d6640b78cdba5be365a281de8.mp4"
-            src="https://upload.yunfanyouxi.com/nbc/nativeMaterial/32/936a44ec19d63d6640b78cdba5be365a281de8.mp4"
-          />
-        </Swiper.Item>
-        <Swiper.Item>
+    <ScrollView scrollY style={props.style} className="pb-[20px]">
+      <View>
+        <View className="p-[10px] flex items-center">
           <Image
-            mode="scaleToFill"
-            src="https://storage.360buyimg.com/jdc-article/NutUItaro34.jpg"
+            src={vipUserInfo?.download_list[0].icon}
+            width={70}
+            height={70}
+            className="rounded-[10px] overflow-hidden"
           />
-        </Swiper.Item>
-      </Swiper>
-      <View className="w-[94%] p-[10px] mx-auto mt-[-10px] rounded-[5px] relative z-10 left-[0px] top-[0px] bg-[#fff]">
-        <View className="flex items-center">
-          <View className="w-[100px] h-[100px] rounded-[10px] overflow-hidden">
-            <Image
-              mode="scaleToFill"
-              src="https://nie.res.netease.com/r/pic/20250724/10d06793-87aa-48c5-a51f-1bd9934f5705.png"
-            />
-          </View>
-          <View className="ml-[10px] text-[16px] font-[800] text-[#333]">
-            <View>美丽大冒险</View>
-            <View className="text-[12px] text-[#999]">
-              美丽大冒险,是一款全名抑制游戏
+          <View className="ml-[10px]">
+            <View className="text-[18px] font-[800] text-[#9c9dee]">
+              {vipUserInfo?.download_list[0].title}
+            </View>
+            <View className="text-[12px] text-[#949494]">
+              {vipUserInfo?.download_list[0].subtitle2}
+            </View>
+            <View className="flex flex-wrap">
+              {vipUserInfo?.download_list[0].tags.map((item) => (
+                <View className="text-[12px] px-[5px] py-[2px] mb-[5px] text-[#7ac0a4] bg-[#e8f5f0] mr-[5px]">
+                  {item}
+                </View>
+              ))}
             </View>
           </View>
         </View>
-        <View className="flex items-center text-[12px] mt-[10px]  flex-wrap">
-          <View className="p-[4px] bg-[#e8ebff] text-[#333] rounded-[5px] mr-[10px] mb-[10px]">
-            万众期待
-          </View>
-          <View className="p-[4px] bg-[#f8f8f8] text-[#333] rounded-[5px] mr-[10px] mb-[10px]">
-            角色模拟
+        <View className="flex justify-between text-center">
+          <View className="px-[20px] w-[24%] border-r-[1px] border-r-solid border-r-[#e0e0e0]">
+            <View className="text-[14px] font-[800] text-[#333]">5.0</View>
+            <View className="text-[12px] text-[#949494]">游戏评分</View>
           </View>
-          <View className="p-[4px] bg-[#f8f8f8] text-[#333] rounded-[5px] mr-[10px] mb-[10px]">
-            角色模拟
+          <View className="px-[20px] w-[24%] border-r-[1px] border-r-solid border-r-[#e0e0e0]">
+            <View className="text-[14px] font-[800] text-[#333]">90万+</View>
+            <View className="text-[12px] text-[#949494]">下载次数</View>
           </View>
-          <View className="p-[4px] bg-[#f8f8f8] text-[#333] rounded-[5px] mr-[10px] mb-[10px]">
-            角色模拟
+          <View className="px-[20px] w-[24%] border-r-[1px] border-r-solid border-r-[#e0e0e0]">
+            <View className="text-[14px] font-[800] text-[#333]">1.3G</View>
+            <View className="text-[12px] text-[#949494]">大小</View>
           </View>
-          <View className="p-[4px] bg-[#f8f8f8] text-[#333] rounded-[5px] mr-[10px] mb-[10px]">
-            角色模拟
+          <View className="px-[20px] w-[24%]">
+            <View className="text-[14px] font-[800] text-[#333]">16+</View>
+            <View className="text-[12px] text-[#949494]">年龄分级</View>
           </View>
-          <View className="p-[4px] bg-[#f8f8f8] text-[#333] rounded-[5px] mr-[10px] mb-[10px]">
-            角色模拟
-          </View>
-          <View className="p-[4px] bg-[#f8f8f8] text-[#333] rounded-[5px] mr-[10px] mb-[10px]">
-            角色模拟
+        </View>
+        <View className="w-full h-[427px] p-[10px]">
+          <ScrollView className="w-full h-full" scrollX showScrollbar={false}>
+            <View className="flex">
+              <View
+                style={{
+                  width: "250px",
+                  height: "427px",
+                  marginRight: "10px",
+                }}
+              >
+                <img
+                  src="http://192.168.10.5:8181/uploads20250917/b1.jpg"
+                  width="250px"
+                  height="427px"
+                  style={{ objectFit: "cover", borderRadius: "10px" }}
+                />
+              </View>
+              <View
+                style={{
+                  width: "250px",
+                  height: "427px",
+                  marginRight: "10px",
+                }}
+              >
+                <img
+                  src="http://192.168.10.5:8181/uploads20250917/b2.jpg"
+                  width="250px"
+                  height="427px"
+                  style={{ objectFit: "cover", borderRadius: "10px" }}
+                />
+              </View>
+            </View>
+          </ScrollView>
+          <View className="text-[12px] text-[#949494]">
+            版本号:1.0.0 更新时间:2025-09-19
           </View>
-          <View className="p-[4px] bg-[#f8f8f8] text-[#333] rounded-[5px] mr-[10px] mb-[10px]">
-            角色模拟
+          <View className="px-[10px]">
+            <Button
+              block
+              size="large"
+              type="primary"
+              className="p-[10px] bg-[#fff]"
+              onClick={handleDown}
+            >
+              启动游戏
+            </Button>
           </View>
         </View>
-        <Button size="large" type="primary" block className="mt-[80px]">
-          下载游戏
-        </Button>
       </View>
-    </View>
+    </ScrollView>
   );
 };
 

+ 87 - 74
src/components/post-list/index.tsx

@@ -1,4 +1,4 @@
-import { Text, View } from "@tarojs/components";
+import { Text, View, ScrollView } from "@tarojs/components";
 import { Heart, HeartFill, Plus } from "@nutui/icons-react-taro";
 import { Message } from "@nutui/icons-react-taro";
 import {
@@ -8,6 +8,7 @@ import {
   ImagePreview,
   Toast,
 } from "@nutui/nutui-react-taro";
+import { createPortal } from "react-dom";
 import MoreButton from "../fllower-button";
 import { circlePostListApi } from "../../api/post";
 import {
@@ -23,8 +24,7 @@ import { Dialog } from "@nutui/nutui-react-taro";
 import { navigateTo } from "@tarojs/taro";
 
 interface PostListProps {
-  onLoadMore?: () => void;
-  onRefresh?: () => void;
+  style?: any;
 }
 
 export interface PostListRef {
@@ -34,7 +34,7 @@ export interface PostListRef {
   loading: boolean;
 }
 
-const PostList = forwardRef<PostListRef, PostListProps>((props, ref) => {
+const PostList = forwardRef<PostListRef, PostListProps>(({ style }, ref) => {
   const [postList, setPostList] = useState<any[]>([]);
   const [showPreview, setShowPreview] = useState(false);
   const [init, setInit] = useState(0);
@@ -43,11 +43,16 @@ const PostList = forwardRef<PostListRef, PostListProps>((props, ref) => {
   const [currentPage, setCurrentPage] = useState(1);
   const [hasMore, setHasMore] = useState(true);
   const [loading, setLoading] = useState(false);
+  const [refreshing, setRefreshing] = useState(false);
   const pageSize = 10;
 
   const getPostList = useCallback(
     async (page = 1, isRefresh = false) => {
-      setLoading(true);
+      if (isRefresh) {
+        setRefreshing(true);
+      } else {
+        setLoading(true);
+      }
       try {
         const res: any = await circlePostListApi({
           circleId: 1,
@@ -57,12 +62,6 @@ const PostList = forwardRef<PostListRef, PostListProps>((props, ref) => {
 
         if (res.code === 200) {
           const newList = res.data.list || [];
-          console.log("API Response:", {
-            page,
-            newListLength: newList.length,
-            hasMore: res.data.has_more,
-            isRefresh,
-          });
 
           if (isRefresh || page === 1) {
             setPostList(newList);
@@ -75,13 +74,13 @@ const PostList = forwardRef<PostListRef, PostListProps>((props, ref) => {
           setCurrentPage(page);
         }
       } catch (error) {
-        console.error("获取帖子列表失败:", error);
         Toast.show("load_error", {
           content: "加载失败,请重试",
           duration: 2,
         });
       } finally {
         setLoading(false);
+        setRefreshing(false);
       }
     },
     [pageSize]
@@ -94,12 +93,20 @@ const PostList = forwardRef<PostListRef, PostListProps>((props, ref) => {
 
   // 上拉加载更多
   const handleLoadMore = useCallback(async () => {
-    console.log("handleLoadMore called", { hasMore, currentPage, loading });
-    if (hasMore && !loading) {
-      console.log("Loading next page:", currentPage + 1);
+    if (hasMore && !loading && !refreshing) {
       await getPostList(currentPage + 1, false);
     }
-  }, [hasMore, currentPage, getPostList, loading]);
+  }, [hasMore, currentPage, getPostList, loading, refreshing]);
+
+  // ScrollView 滚动到底部事件
+  const handleScrollToLower = useCallback(() => {
+    handleLoadMore();
+  }, [handleLoadMore]);
+
+  // ScrollView 下拉刷新事件
+  const handleRefresherRefresh = useCallback(async () => {
+    await handleRefresh();
+  }, [handleRefresh]);
 
   const handleMoreClick = (postIndex: number) => {
     const data = [...postList];
@@ -130,30 +137,18 @@ const PostList = forwardRef<PostListRef, PostListProps>((props, ref) => {
   // 关注/不关注
   const handleFollowClick = (followeeId: number, is_followed) => {
     if (is_followed) {
-      Dialog.open("followed_dialog", {
-        title: "提示",
-        content: "确定取消关注吗?",
-        onConfirm: () => {
-          unfollowApi({ followee_id: followeeId }).then(() => {
-            Toast.show("follow_success", {
-              content: "取消关注成功",
-              duration: 2,
-              lockScroll: true,
-            });
-            const data = [...postList];
-            data.forEach((item: any) => {
-              item.is_followed = false;
-            });
-            setPostList(data);
-            Dialog.close("followed_dialog");
-          });
-        },
-        onCancel: () => {
-          Dialog.close("followed_dialog");
-        },
-        onClose: () => {
-          Dialog.close("followed_dialog");
-        },
+      unfollowApi({ followee_id: followeeId }).then(() => {
+        Toast.show("follow_success", {
+          content: "取消关注成功",
+          duration: 2,
+          lockScroll: true,
+        });
+        const data = [...postList];
+        data.forEach((item: any) => {
+          item.is_followed = false;
+        });
+        setPostList(data);
+        Dialog.close("followed_dialog");
       });
     } else {
       followApi({ followee_id: followeeId }).then(() => {
@@ -166,7 +161,6 @@ const PostList = forwardRef<PostListRef, PostListProps>((props, ref) => {
         data.forEach((item: any) => {
           item.is_followed = true;
         });
-
         setPostList(data);
       });
     }
@@ -181,11 +175,20 @@ const PostList = forwardRef<PostListRef, PostListProps>((props, ref) => {
 
   useEffect(() => {
     getPostList(1, true);
-  }, []);
+  }, [getPostList]);
 
   return (
     <>
-      <View className="post-list bg-[#f8f8f8]">
+      <ScrollView
+        className="post-list bg-[#f8f8f8]"
+        scrollY
+        refresherEnabled
+        refresherTriggered={refreshing}
+        onRefresherRefresh={handleRefresherRefresh}
+        onScrollToLower={handleScrollToLower}
+        lowerThreshold={50}
+        style={style}
+      >
         {postList.map((item: any, index: number) => {
           return (
             <View
@@ -213,7 +216,7 @@ const PostList = forwardRef<PostListRef, PostListProps>((props, ref) => {
                     <View className="text-[14px] font-[800] text-[#9c9dee]">
                       {item.user.nickname || item.user.username || "微信用户"}
                     </View>
-                    {item.user.is_official ? (
+                    {item.user.is_officia ? (
                       <View className="text-[12px] text-[#949494]">
                         《心动女友》官方账号
                       </View>
@@ -230,12 +233,10 @@ const PostList = forwardRef<PostListRef, PostListProps>((props, ref) => {
                       handleFollowClick(item.user_id, item.is_followed);
                     }}
                   >
-                    {item.is_followed ? <></> : <Plus color="#1874d0" />}
+                    {!item.is_followed && <Plus color="#1874d0" />}
                     <Text>{item.is_followed ? "已关注" : "关注"}</Text>
                   </View>
-                  {item.user.is_official ? (
-                    <></>
-                  ) : (
+                  {!item.user.is_official && (
                     <MoreButton
                       postIndex={index}
                       followeeId={item.user_id}
@@ -252,14 +253,30 @@ const PostList = forwardRef<PostListRef, PostListProps>((props, ref) => {
                 }
               >
                 <View className="text-[14px] text-[#333] pt-[20px]">
-                  {strSlice(item.content, 100)}
-                  {item.content.length > 100 && (
-                    <Text className="text-[#1874d0]">全文</Text>
+                  {item.type === "1" ? (
+                    <>
+                      {strSlice(item.content, 100)}
+                      {item.content.length > 100 && (
+                        <Text className="text-[#1874d0]">全文</Text>
+                      )}
+                    </>
+                  ) : (
+                    <>
+                      <View
+                        className="text-[14px] text-[#333]"
+                        dangerouslySetInnerHTML={{
+                          __html: strSlice(item.content, 100),
+                        }}
+                      ></View>
+                      {item.content.length > 100 && (
+                        <Text className="text-[#1874d0]">全文</Text>
+                      )}
+                    </>
                   )}
                 </View>
                 <View className="mt-[10px]">
                   <Grid
-                    columns={item.media_url.length < 3 ? 2 : 3} // 这里,如果图片小于3张,则columns为2,如果图片大于等于3张,则columns为3
+                    columns={item.media_url.length < 3 ? 2 : 3}
                     gap={7}
                     style={
                       {
@@ -274,17 +291,12 @@ const PostList = forwardRef<PostListRef, PostListProps>((props, ref) => {
                         <GridItem>
                           <Image
                             src={ite.src}
-                            mode={
-                              item.media_url.length === 1
-                                ? "scaleToFill"
-                                : "scaleToFill"
-                            }
+                            mode="scaleToFill"
                             onClick={(e) => {
                               e.preventDefault();
                               e.stopPropagation();
                               setMediaUrl(item.media_url);
                               setInit(index + 1);
-
                               setShowPreview(true);
                             }}
                           />
@@ -306,13 +318,11 @@ const PostList = forwardRef<PostListRef, PostListProps>((props, ref) => {
                       e.stopPropagation();
                       handleInteraction(
                         item.id,
-                        item.interaction_type === "praise"
-                          ? "dispraise"
-                          : "praise"
+                        item.is_praised ? "dispraise" : "praise"
                       );
                     }}
                   >
-                    {item.interaction_type === "praise" ? (
+                    {item.is_praised ? (
                       <HeartFill size={16} color="#ec4342" />
                     ) : (
                       <Heart size={16} color="#949494" />
@@ -348,18 +358,21 @@ const PostList = forwardRef<PostListRef, PostListProps>((props, ref) => {
             <Text className="text-[14px] text-[#999]">没有更多了</Text>
           </View>
         )}
-
-        <ImagePreview
-          images={mediaUrl}
-          visible={showPreview}
-          defaultValue={init}
-          onClose={() => setShowPreview(false)}
-          indicator
-        />
-        <Toast id="follow_success" />
-        <Toast id="load_error" />
-        <Dialog id="followed_dialog" />
-      </View>
+      </ScrollView>
+      <Toast id="follow_success" />
+      <Toast id="load_error" />
+      <Dialog id="followed_dialog" />
+      {showPreview &&
+        createPortal(
+          <ImagePreview
+            images={mediaUrl}
+            visible={showPreview}
+            defaultValue={init}
+            onClose={() => setShowPreview(false)}
+            indicator
+          />,
+          document.body
+        )}
     </>
   );
 });

+ 1 - 1
src/config/index.ts

@@ -1,7 +1,7 @@
 // 环境配置
 const config = {
   development: {
-    TARO_ACT_HOST: "http://localhost:3000",
+    TARO_ACT_HOST: "http://192.168.10.5:3000",
     API_BASE_URL: "/api",
   },
   production: {

+ 1 - 1
src/pages/act/index.scss

@@ -1,5 +1,5 @@
 .act-content {
-  height: calc(100vh - 70px);
+  height: calc(100vh);
   width: 100%;
   border: none;
   padding-top: 40px;

+ 3 - 0
src/pages/detail/index.scss

@@ -0,0 +1,3 @@
+.content-detail img {
+  width: 100%;
+}

+ 62 - 49
src/pages/detail/index.tsx

@@ -3,6 +3,7 @@ import { pxTransform, useLoad, navigateBack } from "@tarojs/taro";
 import { useState } from "react";
 import { ArrowLeft, Plus } from "@nutui/icons-react-taro";
 import { Text } from "@tarojs/components";
+import "./index.scss";
 import {
   Dialog,
   Image,
@@ -125,58 +126,70 @@ const Detail = () => {
             </View>
           </View>
         </View>
-        <Swiper
-          height={pxTransform(300)}
-          defaultValue={0}
-          onChange={(e) => {
-            setCurrentIndex(e.detail.current);
-          }}
-          indicator={
-            <View
-              style={{
-                display: "flex",
-                flexDirection: "row",
-                justifyContent: "center",
-                alignItems: "center",
-                position: "absolute",
-                left: "85%",
-                bottom: pxTransform(10),
-                width: pxTransform(46),
-                height: pxTransform(22),
-                backgroundColor: "rgba(0, 0, 0, 0.33)",
-                borderRadius: pxTransform(22),
-                textAlign: "center",
-                fontSize: pxTransform(14),
-                zIndex: 1,
-              }}
-            >
-              <Text style={{ color: "#fff" }}>
-                {currentIndex + 1}/{postData.media_url.length}
-              </Text>
-            </View>
-          }
-        >
-          {postData.media_url.map((item, index) => (
-            <Swiper.Item key={item}>
-              <Image
-                width="100%"
-                height="100%"
-                onClick={() => {
-                  setShowPreview(true);
-                  setInit(index + 1);
+        {postData.type === "1" ? (
+          <Swiper
+            height={pxTransform(300)}
+            defaultValue={0}
+            onChange={(e) => {
+              setCurrentIndex(e.detail.current);
+            }}
+            indicator={
+              <View
+                style={{
+                  display: "flex",
+                  flexDirection: "row",
+                  justifyContent: "center",
+                  alignItems: "center",
+                  position: "absolute",
+                  left: "85%",
+                  bottom: pxTransform(10),
+                  width: pxTransform(46),
+                  height: pxTransform(22),
+                  backgroundColor: "rgba(0, 0, 0, 0.33)",
+                  borderRadius: pxTransform(22),
+                  textAlign: "center",
+                  fontSize: pxTransform(14),
+                  zIndex: 1,
                 }}
-                mode="aspectFill"
-                src={item.src}
-              />
-            </Swiper.Item>
-          ))}
-        </Swiper>
-        <View className="p-[10px]">
-          <View className="text-[14px] text-[#333]">{postData.content}</View>
-          <CommentList postId={postData.id} />
+              >
+                <Text style={{ color: "#fff" }}>
+                  {currentIndex + 1}/{postData.media_url.length}
+                </Text>
+              </View>
+            }
+          >
+            {postData.media_url.map((item, index) => (
+              <Swiper.Item key={item}>
+                <Image
+                  width="100%"
+                  height="100%"
+                  onClick={() => {
+                    setShowPreview(true);
+                    setInit(index + 1);
+                  }}
+                  mode="aspectFill"
+                  src={item.src}
+                />
+              </Swiper.Item>
+            ))}
+          </Swiper>
+        ) : (
+          <></>
+        )}
+        <View className="p-[10px] content-detail">
+          {postData.type === "1" ? (
+            <View className="text-[14px] text-[#333]">{postData.content}</View>
+          ) : (
+            <View
+              className="text-[14px] text-[#333]"
+              dangerouslySetInnerHTML={{ __html: postData.content }}
+            ></View>
+          )}
+
+          {/* <CommentList postId={postData.id} /> */}
         </View>
       </ScrollView>
-      <CommentListInteraction postData={postData} />
+      {/* <CommentListInteraction postData={postData} /> */}
       <ImagePreview
         images={postData.media_url}
         visible={showPreview}

+ 57 - 21
src/pages/gift/index.tsx

@@ -1,9 +1,45 @@
 import { NavBar } from "@nutui/nutui-react-taro";
 import { View } from "@tarojs/components";
 import { ArrowLeft } from "@nutui/icons-react-taro";
-import { navigateBack } from "@tarojs/taro";
+import { getStorageSync, navigateBack } from "@tarojs/taro";
+import {
+  getVipCustomersGiftListApi,
+  receiveVipCustomersGiftApi,
+} from "../../api/gift";
+import { useEffect, useState } from "react";
 
 const Gift = () => {
+  const [giftList, setGiftList] = useState([]);
+
+  const getGiftList = async () => {
+    // 圈子id 从本地存储中获取
+    const mainId = getStorageSync("vipInfo").game_id;
+    if (!mainId) {
+      return;
+    }
+
+    const res: any = await getVipCustomersGiftListApi({ main_id: mainId });
+    if (res.code === 200) {
+      setGiftList(res.data);
+    }
+  };
+
+  const getGift = async (item) => {
+    const res: any = await receiveVipCustomersGiftApi(item);
+    if (res.code === 200) {
+      const data = [...giftList];
+      data.forEach((ite: any) => {
+        if (ite.id === item.id) {
+          ite.is_received = true;
+        }
+      });
+      setGiftList(data);
+    }
+  };
+
+  useEffect(() => {
+    getGiftList();
+  }, []);
   return (
     <View>
       <NavBar
@@ -22,30 +58,30 @@ const Gift = () => {
         礼包中心
       </NavBar>
       <View className="pt-[60px]">
-        <View className="flex items-center justify-between px-[10px] border-b-[1px] border-[#f2f2f2] pb-[10px] mb-[10px]">
-          <View className="text-[14px]">
-            <View>每日签到礼包</View>
-            <View className="text-[12px] text-[#999]">
-              钻石x50,强化合金x10000
+        {giftList.map((item: any) => (
+          <View className="flex items-center justify-between px-[10px] border-b-[1px] border-[#f2f2f2] pb-[10px]">
+            <View className="text-[14px]">
+              <View>{item.gift_name}</View>
+              <View className="text-[12px] text-[#999]">{item.gift_desc}</View>
             </View>
-          </View>
 
-          <View className="text-center bg-[#f2f2f2] py-[4px] w-[60px] rounded-[10px] text-[14px] text-[#6069d9] font-[800]">
-            领 取
-          </View>
-        </View>
-        <View className="flex items-center justify-between px-[10px] border-b-[1px] border-[#f2f2f2] pb-[10px]">
-          <View className="text-[14px]">
-            <View>每日签到礼包</View>
-            <View className="text-[12px] text-[#999]">
-              钻石x50,强化合金x10000
+            <View
+              className={`font-[800] text-center py-[4px] w-[60px] rounded-[10px] text-[14px] ${
+                item.is_received
+                  ? "text-[#999] bg-[#f2f2f2]"
+                  : "text-[#fff] bg-[#5c64ac]"
+              }`}
+              onClick={() => {
+                if (item.is_received) {
+                  return;
+                }
+                getGift(item);
+              }}
+            >
+              {item.is_received ? "已领取" : "领取"}
             </View>
           </View>
-
-          <View className="text-center bg-[#f2f2f2] py-[4px] w-[60px] rounded-[10px] text-[14px]">
-            领 取
-          </View>
-        </View>
+        ))}
       </View>
     </View>
   );

+ 12 - 17
src/pages/index/index.scss

@@ -1,8 +1,6 @@
 .index {
   font-size: 24rpx;
-  margin-bottom: 30px;
   background-color: #f8f8f8;
-  overflow-y: scroll;
   .header {
     width: 100%;
     height: 100px;
@@ -26,15 +24,15 @@
   background-repeat: no-repeat;
   color: #fff;
   width: 100%;
-  // &::before {
-  //   content: "";
-  //   position: absolute;
-  //   top: 0;
-  //   left: 0;
-  //   width: 100%;
-  //   height: 100%;
-  //   background-color: rgba(0, 0, 0, 0.5);
-  // }
+  &::before {
+    content: "";
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    background-color: rgba(0, 0, 0, 0.5);
+  }
 }
 
 .index-content {
@@ -44,17 +42,14 @@
 // 页面滚动容器
 .page-scroll {
   width: 100%;
+  padding-top: 80px;
 }
 
 // 页面内容容器
 .page-content {
   position: relative;
-  min-height: calc(100vh + 130px); // 最小高度 = 视窗高度 + 蓝色区域高度
-}
-
-// 可滚动的横幅区域
-.scrollable-banner {
-  // 这个区域可以正常滚动,不需要特殊样式
+  min-height: calc(100vh - 80px); // 最小高度 = 视窗高度 + 蓝色区域高度
+  padding-top: 80px;
 }
 
 // PostList容器样式

+ 124 - 166
src/pages/index/index.tsx

@@ -1,206 +1,164 @@
-import { View, ScrollView } from "@tarojs/components";
-import { Gift, Plus, User } from "@nutui/icons-react-taro";
-import { navigateTo } from "@tarojs/taro";
-import PostList, { PostListRef } from "../../components/post-list";
+import { View } from "@tarojs/components";
+import { Gift, Home, Plus, User } from "@nutui/icons-react-taro";
+import { navigateTo, redirectTo } from "@tarojs/taro";
+import PostList from "../../components/post-list";
 
 import "./index.scss";
-import { Image, Sticky, TabPane, Tabs } from "@nutui/nutui-react-taro";
+import {
+  Image,
+  NoticeBar,
+  Tabbar,
+  TabPane,
+  Tabs,
+} from "@nutui/nutui-react-taro";
 import { useEffect, useRef, useState } from "react";
 import { getCircleInfoApi } from "../../api/circle";
 import GameIntro from "../../components/game-intro";
+import { getVipUserInfoApi, getVipUserRoleListApi } from "../../api/user";
 
 function Index() {
   const [circleData, setCircleData] = useState<any>(null);
   const [tab1value, setTab1value] = useState<string | number>("1");
   const containerTopRef = useRef<HTMLDivElement>(null);
-  const [scrollTop, setScrollTop] = useState(0);
-  const [scrollEnabled, setScrollEnabled] = useState(false);
-  const postListRef = useRef<PostListRef>(null);
+  const [vipUserInfo, setVipUserInfo] = useState<any>(null);
   useEffect(() => {
     getCircleInfoApi({ id: 1 }).then((res: any) => {
       setCircleData(res.data);
     });
-    // getUserInfo();
+    getVipUserRoleList();
   }, []);
 
-  const handleUpperThreshold = () => {
-    console.log("handleUpperThreshold");
-  };
-  const handleScroll = (e: any) => {
-    if (e.detail.scrollTop === 0 && scrollEnabled) {
-      containerTopRef.current?.scrollTo({
-        top: 50,
-        behavior: "smooth",
-      });
-      disableScroll();
-    } else {
-      enableScroll();
-    }
-  };
+  // 获取vip用户角色信息
+  const getVipUserRoleList = async () => {
+    const res: any = await getVipUserRoleListApi({});
 
-  const handleScrollToLower = () => {
-    console.log("handleScrollToLower - 触发加载更多");
-    if (
-      postListRef.current &&
-      postListRef.current.hasMore &&
-      !postListRef.current.loading
-    ) {
-      postListRef.current.loadMore();
+    if (res.code === 200 && res.data.length > 0) {
+      getVipUserInfo(res.data[0].role_id);
     }
   };
 
-  // 禁用滚动的函数
-  const disableScroll = () => {
-    setScrollEnabled(false);
-  };
-
-  // 启用滚动的函数
-  const enableScroll = () => {
-    setScrollEnabled(true);
+  // 获取vip用户信息
+  const getVipUserInfo = async (role_id: number) => {
+    const res: any = await getVipUserInfoApi({ role_id });
+    if (res.code === 200) {
+      setVipUserInfo(res.data);
+    }
   };
 
-  // 监听滚动事件
-  useEffect(() => {
-    containerTopRef.current?.addEventListener("scroll", (e) => {
-      console.log("e.target?.scrollTop", e.target?.scrollTop);
-      if (e.target?.scrollTop > 60) {
-        setScrollEnabled(true);
-      } else {
-        setScrollEnabled(false);
-      }
-    });
-    return () => {
-      containerTopRef.current?.removeEventListener("scroll", (e) => {
-        if (e.target?.scrollTop > 60) {
-          setScrollEnabled(true);
-        } else {
-          setScrollEnabled(false);
-        }
-      });
-    };
-  }, []);
-
   return (
-    <View
-      style={{ height: "calc(100vh)" }}
-      ref={containerTopRef}
-      className="index"
-    >
+    <View ref={containerTopRef} className="index">
       {/* 头部导航栏 - 移出page-content容器 */}
-      <View className="index-header bg-[#6069d9] fixed top-0 left-0 right-0 z-20 h-[50px] w-full flex items-center justify-between px-[10px]">
-        <View className="flex items-center relative z-40">
-          <View className="w-[30px] h-[30px] bg-[#fff] rounded-[50%] overflow-hidden">
-            <Image
-              src={circleData?.icon_url}
-              width="100%"
-              height="100%"
-              mode="aspectFill"
-            />
-          </View>
-          <View>
-            <View className="text-[13px] font-[800] text-[#fff] ml-[10px]">
-              {circleData?.name}
+      <View
+        className="index-header bg-[#6069d9] fixed top-0 left-0 right-0 z-20 h-[70px] w-full px-[10px]"
+        style={{
+          backgroundImage: `url(${circleData?.icon_url})`,
+          backgroundSize: "cover",
+          backgroundPosition: "center",
+          backgroundRepeat: "no-repeat",
+        }}
+      >
+        <View className="flex items-center justify-between">
+          <View className="flex items-center relative z-40 py-[10px]">
+            <View className="w-[45px] h-[45px] bg-[#fff] rounded-[50%] overflow-hidden">
+              <Image
+                src={circleData?.icon_url}
+                width="100%"
+                height="100%"
+                mode="aspectFill"
+              />
             </View>
-            <View className="text-[12px] text-[#ccc] ml-[10px]">
-              3400人在线
+            <View>
+              <View className="text-[16px] font-[800] text-[#fff] ml-[10px]">
+                {circleData?.name}
+              </View>
+              <View className="text-[12px] text-[#ccc] ml-[10px]">
+                3400人在线
+              </View>
             </View>
           </View>
+          <View className="flex items-center">
+            {/* <View
+              onClick={() => {
+                navigateTo({ url: "/pages/self/index" });
+              }}
+            >
+              <User size={22} />
+            </View> */}
+          </View>
         </View>
-        <View className="flex items-center">
+      </View>
+
+      {/* 页面内容 - 使用ScrollView控制整个页面滚动 */}
+
+      <View className="page-content">
+        <View className="flex items-center justify-around w-[100%]">
           <View
+            className="text-[#fff] w-[28%] h-[30px] flex items-center justify-center text-center text-[12px]  rounded-[5px] font-[800] px-[10px] bg-[#5258acf0] "
             onClick={() => {
-              navigateTo({ url: "/pages/self/index" });
+              navigateTo({ url: "/pages/act/index" });
             }}
           >
-            <User size={20} />
+            积分中心
           </View>
-        </View>
-      </View>
-
-      {/* 页面内容 - 使用ScrollView控制整个页面滚动 */}
-      <View
-        className="page-scroll"
-        // scrollY
-      >
-        <View className="page-content">
-          <View className="scrollable-banner h-[100px] pb-[10px] bg-[#6069d9] pt-[60px] flex items-center justify-around">
-            <View
-              className="text-[#fff] w-[28%] h-[30px] flex items-center justify-center text-center text-[12px]  rounded-[5px] font-[800] px-[10px] bg-[#5258acf0] "
-              onClick={() => {
-                navigateTo({ url: "/pages/act/index" });
-              }}
-            >
-              积分中心
-            </View>
-            <View
-              className="text-[#fff] w-[28%] h-[30px] flex items-center justify-center text-center text-[12px] rounded-[5px]  font-[800] px-[10px] bg-[#5258acf0]"
-              onClick={() => {
-                navigateTo({ url: "/pages/gift/index" });
-              }}
-            >
-              <Gift size={16} />
-              礼包中心
-            </View>
-            <View
-              className="text-[#fff] w-[28%] h-[30px] flex items-center justify-center text-center text-[12px] rounded-[5px]  font-[800] px-[10px] bg-[#5258acf0]"
-              onClick={() => {
-                navigateTo({ url: "/pages/publish/index" });
-              }}
-            >
-              <Plus size={20} />
-              发布帖子
-            </View>
+          <View
+            className="text-[#fff] w-[28%] h-[30px] flex items-center justify-center text-center text-[12px] rounded-[5px]  font-[800] px-[10px] bg-[#5258acf0]"
+            onClick={() => {
+              navigateTo({ url: "/pages/gift/index" });
+            }}
+          >
+            <Gift size={16} />
+            礼包中心
           </View>
-          <Sticky threshold={50} className="relative">
-            <Tabs
-              align="left"
-              value={tab1value}
-              onChange={(value) => {
-                setTab1value(value);
-              }}
-            >
-              <TabPane title="游戏介绍">
-                <ScrollView
-                  upperThreshold={50}
-                  lowerThreshold={50}
-                  onScrollToUpper={handleUpperThreshold}
-                  onScrollToLower={handleScrollToLower}
-                  onScroll={handleScroll}
-                  scrollY
-                  style={{ height: "calc(100vh)" }}
-                >
-                  <GameIntro />
-                </ScrollView>
-              </TabPane>
-              <TabPane title="最新">
-                <ScrollView
-                  upperThreshold={50}
-                  lowerThreshold={50}
-                  // onScrollToUpper={handleUpperThreshold}
-                  onScrollToLower={handleScrollToLower}
-                  onScroll={handleScroll}
-                  scrollY
-                  className={scrollEnabled ? "" : "scroll-disabled"}
-                  style={{
-                    height: "calc(100vh)",
-                  }}
-                >
-                  <PostList ref={postListRef} />
-                </ScrollView>
+          {/* <View
+            className="text-[#fff] w-[28%] h-[30px] flex items-center justify-center text-center text-[12px] rounded-[5px]  font-[800] px-[10px] bg-[#5258acf0]"
+            onClick={() => {
+              navigateTo({ url: "/pages/publish/index" });
+            }}
+          >
+            <Plus size={20} />
+            发布帖子
+          </View> */}
+        </View>
+        {vipUserInfo?.notice && <NoticeBar content={vipUserInfo.notice} />}
+        <View style={{ height: "calc(100vh - 180px)" }}>
+          <Tabs
+            align="left"
+            autoHeight
+            value={tab1value}
+            onChange={(value) => {
+              setTab1value(value);
+            }}
+          >
+            {vipUserInfo?.download_list && (
+              <TabPane title="游戏介绍" value="0">
+                <GameIntro
+                  style={{ height: "calc(100vh - 260px)" }}
+                  vipUserInfo={vipUserInfo}
+                />
               </TabPane>
-            </Tabs>
-          </Sticky>
+            )}
+
+            <TabPane title="官方咨讯" value="1">
+              <PostList style={{ height: "calc(100vh - 260px)" }} />
+            </TabPane>
+          </Tabs>
+        </View>
+        <View className="bg-[#fff] fixed bottom-[0px] left-[0px] right-[0px] w-full">
+          <Tabbar
+            defaultValue={0}
+            onSwitch={(value) => {
+              if (value === 1) {
+                redirectTo({ url: "/pages/self/index" });
+              } else {
+                redirectTo({ url: "/pages/index/index" });
+              }
+            }}
+          >
+            <Tabbar.Item title="首页" icon={<Home width={20} height={20} />} />
+            <Tabbar.Item title="我的" icon={<User width={20} height={20} />} />
+          </Tabbar>
         </View>
       </View>
-
-      {/* <View
-        className="fixed bottom-[50px] right-[20px] w-[50px] h-[50px] bg-[#6069d9] rounded-[50%] z-10 justify-center items-center flex "
-        onClick={() => {
-          navigateTo({ url: "/pages/publish/index" });
-        }}
-      >
-        <Plus color="#fff" size={50} style={{ marginLeft: "-2px" }} />
-      </View> */}
     </View>
   );
 }

+ 11 - 1
src/pages/login/index.tsx

@@ -27,9 +27,19 @@ const LoginIndex = () => {
   const smsLogin = async () => {
     await smsLoginApi({ mobile, code }).then((res: any) => {
       const { data } = res;
-      const { token, username, mobile, is_vip, vip_code } = data;
+      const {
+        token,
+        username,
+        mobile,
+        is_vip,
+        vip_code,
+        vip_info,
+        circle_info,
+      } = data;
       setStorageSync("Authorization", token);
       setStorageSync("User", { username, mobile, is_vip, vip_code });
+      setStorageSync("vipInfo", vip_info);
+      setStorageSync("circleInfo", circle_info);
       redirectTo({ url: "/pages/index/index" });
     });
   };

+ 33 - 18
src/pages/self/index.tsx

@@ -3,24 +3,29 @@ import {
   ArrowLeft,
   Heart,
   HeartFill,
+  Home,
   Message,
   Notice,
   Plus,
   Setting,
+  User,
 } from "@nutui/icons-react-taro";
 import {
   Badge,
+  Button,
   Dialog,
   Grid,
   GridItem,
   Image,
   ImagePreview,
   NavBar,
+  Tabbar,
   Tabs,
   Toast,
 } from "@nutui/nutui-react-taro";
 import {
   navigateTo,
+  redirectTo,
   getSystemInfoSync,
   useDidShow,
   navigateBack,
@@ -213,12 +218,7 @@ const Self = () => {
   return (
     <>
       <NavBar
-        back={
-          <>
-            <ArrowLeft />
-            返回
-          </>
-        }
+        back={<></>}
         onBackClick={(e) => {
           e.preventDefault();
           navigateBack({ delta: 1 });
@@ -257,18 +257,18 @@ const Self = () => {
                       navigateTo({ url: "/pages/self/setting/index" });
                     }}
                   />
-                  <Badge top="2" right="8" value={userInfo.notification_count}>
+                  {/* <Badge top="2" right="8" value={userInfo.notification_count}>
                     <Notice
                       onClick={() => {
                         navigateTo({ url: "/pages/message/index" });
                       }}
                     />
-                  </Badge>
+                  </Badge> */}
                 </>
               )}
             </View>
           </View>
-          <View className="flex items-center justify-between text-[14px] mt-[10px]">
+          {/* <View className="flex items-center justify-between text-[14px] mt-[10px]">
             <View className="flex">
               <View className="flex">
                 <Text className="text-[#949494]">
@@ -306,10 +306,10 @@ const Self = () => {
                 </View>
               )}
             </View>
-          </View>
+          </View> */}
         </View>
         <View className="mt-[10px]">
-          <Tabs
+          {/* <Tabs
             className="tabs-self"
             align="left"
             value={tab1value}
@@ -367,7 +367,7 @@ const Self = () => {
                     </View>
                     <View className="mt-[10px]">
                       <Grid
-                        columns={item.media_url.length < 3 ? 2 : 3} // 这里,如果图片小于3张,则columns为2,如果图片大于等于3张,则columns为3
+                        columns={item.media_url.length < 3 ? 2 : 3} 
                         gap={7}
                         style={
                           {
@@ -429,7 +429,7 @@ const Self = () => {
                   </View>
                 ))}
 
-                {/* 错误状态 */}
+              
                 {error && (
                   <View className="p-[20px] text-center">
                     <View className="text-[#ff4d4f] mb-[10px]">{error}</View>
@@ -442,28 +442,28 @@ const Self = () => {
                   </View>
                 )}
 
-                {/* 加载更多状态 */}
+              
                 {loadingMore && (
                   <View className="p-[20px] text-center text-[#999]">
                     加载更多...
                   </View>
                 )}
 
-                {/* 没有更多数据 */}
+              
                 {!hasMore && postList.length > 0 && !loading && !refreshing && (
                   <View className="p-[20px] text-center text-[#999]">
                     没有更多内容了
                   </View>
                 )}
 
-                {/* 空状态 */}
+              
                 {!loading && !refreshing && postList.length === 0 && !error && (
                   <View className="p-[40px] text-center text-[#999]">
                     暂无帖子
                   </View>
                 )}
 
-                {/* 初始加载状态 */}
+                
                 {loading && postList.length === 0 && (
                   <View className="p-[40px] text-center text-[#999]">
                     加载中...
@@ -471,7 +471,7 @@ const Self = () => {
                 )}
               </ScrollView>
             </Tabs.TabPane>
-          </Tabs>
+          </Tabs> */}
         </View>
         <ImagePreview
           images={mediaUrl}
@@ -481,6 +481,21 @@ const Self = () => {
           indicator
         />
       </View>
+      <View className="bg-[#fff] fixed bottom-[0px] left-[0px] right-[0px] w-full">
+        <Tabbar
+          defaultValue={1}
+          onSwitch={(value) => {
+            if (value === 1) {
+              redirectTo({ url: "/pages/self/index" });
+            } else {
+              redirectTo({ url: "/pages/index/index" });
+            }
+          }}
+        >
+          <Tabbar.Item title="首页" icon={<Home width={20} height={20} />} />
+          <Tabbar.Item title="我的" icon={<User width={20} height={20} />} />
+        </Tabbar>
+      </View>
     </>
   );
 };

+ 8 - 2
src/utils/index.ts

@@ -1,4 +1,4 @@
-import { navigateTo, getStorageSync } from "@tarojs/taro";
+import { navigateTo, getStorageSync, getSystemInfoSync } from "@tarojs/taro";
 const verifyPhone = (phone: string) => {
   return /^1[3-9]\d{9}$/.test(phone);
 };
@@ -41,4 +41,10 @@ const timeFormat = (time: string) => {
   }
 };
 
-export { verifyPhone, isLogin, strSlice, timeFormat };
+// 获取安卓/ios的系统
+const getSystemInfo = () => {
+  const systemInfo: any = getSystemInfoSync();
+  return systemInfo.system === "AndroidOS" ? "android" : "ios";
+};
+
+export { verifyPhone, isLogin, strSlice, timeFormat, getSystemInfo };