ith5 4 月之前
父节点
当前提交
7d0ea9c26b

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

@@ -0,0 +1,13 @@
+import request from "../../http/request";
+
+/**
+ * 获取未读通知数量
+ * @param
+ * @returns
+ */
+export const getUnreadCountApi = () => {
+  return request({
+    url: `/notifications/unread-count`,
+    method: "POST",
+  });
+};

+ 12 - 0
src/api/post/index.ts

@@ -48,3 +48,15 @@ export const createPostApi = (params) => {
     data: params,
   });
 };
+
+/**
+ * 根据用户ID获取帖子列表
+ */
+
+export const getUserPostListApi = (userId?: any, params?: any) => {
+  return request({
+    url: userId ? `/posts/user/${userId}` : `/posts/user/`,
+    method: "POST",
+    data: params,
+  });
+};

+ 12 - 0
src/api/user/index.ts

@@ -0,0 +1,12 @@
+import request from "../../http/request";
+
+/**
+ * 用户信息
+ * @returns
+ */
+export const getUserInfoApi = (userId?: any) => {
+  return request({
+    url: userId ? `/users/info/${userId}` : `/users/info`,
+    method: "POST",
+  });
+};

+ 2 - 0
src/app.config.ts

@@ -1,5 +1,7 @@
 export default defineAppConfig({
   pages: [
+    "pages/message/index",
+    "pages/self/index",
     "pages/index/index",
     "pages/report/index",
     "pages/detail/index",

二进制
src/asset/img/comment-icon.png


二进制
src/asset/img/fllow-icon.png


二进制
src/asset/img/like-icon.png


+ 19 - 5
src/pages/index/index.tsx

@@ -1,8 +1,9 @@
 import { View, Text } from "@tarojs/components";
-import { Mail } from "@nutui/icons-react-taro";
-import TabbarCom from "../../components/tabbar";
-
+import { Mail, Plus } from "@nutui/icons-react-taro";
+// import TabbarCom from "../../components/tabbar";
+import { navigateTo } from "@tarojs/taro";
 import PostList from "../../components/post-list";
+
 import "./index.scss";
 
 function Index() {
@@ -22,7 +23,12 @@ function Index() {
             <View className="text-[14px] font-[800] text-[#333]">组队开黑</View>
             <View className="text-[12px] text-[#949494]">寻找队友</View>
           </View>
-          <View className="w-[22%] pt-[20rpx] pb-[20rpx] pl-[10rpx] border-[1rpx] border-solid border-[#e0e0e0]  rounded-[5px] shadow-[0_35px_60px_-15px_rgba(0,0,0,0.3)]">
+          <View
+            className="w-[22%] pt-[20rpx] pb-[20rpx] pl-[10rpx] border-[1rpx] border-solid border-[#e0e0e0]  rounded-[5px] shadow-[0_35px_60px_-15px_rgba(0,0,0,0.3)]"
+            onClick={() => {
+              navigateTo({ url: "/pages/self/index" });
+            }}
+          >
             <View className="text-[14px] font-[800] text-[#333]">个人中心</View>
             <View className="text-[12px] text-[#949494]">我的信息</View>
           </View>
@@ -47,7 +53,15 @@ function Index() {
       <View className="mt-[10px]">
         <PostList />
       </View>
-      <TabbarCom />
+      {/* <TabbarCom /> */}
+      <View
+        className="fixed bottom-[50px] right-[20px] w-[50px] h-[50px] bg-[#1874d0] rounded-[50%] z-10 justify-center items-center flex "
+        onClick={() => {
+          navigateTo({ url: "/pages/publish/index" });
+        }}
+      >
+        <Plus color="#fff" size={20} />
+      </View>
     </View>
   );
 }

+ 3 - 0
src/pages/message/index.config.ts

@@ -0,0 +1,3 @@
+export default {
+  navigationBarTitleText: "消息",
+};

+ 27 - 0
src/pages/message/index.tsx

@@ -0,0 +1,27 @@
+import { Badge, Cell } from "@nutui/nutui-react-taro";
+import { ArrowRight } from "@nutui/icons-react-taro";
+import { View } from "@tarojs/components";
+
+const Message = () => {
+  return (
+    <>
+      <Cell
+        title={
+          <>
+            <Badge right="0" dot>
+              赞和收藏
+            </Badge>
+          </>
+        }
+        extra={
+          <View className="flex items-center">
+            <ArrowRight />
+          </View>
+        }
+      />
+      <Cell title="评论" extra={<ArrowRight />} />
+    </>
+  );
+};
+
+export default Message;

+ 3 - 0
src/pages/self/index.config.ts

@@ -0,0 +1,3 @@
+export default {
+  navigationBarTitleText: "个人中心",
+};

+ 7 - 0
src/pages/self/index.scss

@@ -0,0 +1,7 @@
+.tabs-self {
+  --nutui-tabs-titles-background-color: #fff;
+  --nutui-tabs-titles-item-color: #7d7d7d;
+  --nutui-tabs-titles-item-active-color: #333;
+  --nutui-tabs-tab-line-color: #333;
+  --nutui-tabs-tabpane-padding: 0;
+}

+ 372 - 0
src/pages/self/index.tsx

@@ -0,0 +1,372 @@
+import { ScrollView, Text, View } from "@tarojs/components";
+import {
+  Heart,
+  HeartFill,
+  Message,
+  Notice,
+  Plus,
+} from "@nutui/icons-react-taro";
+import {
+  Badge,
+  Grid,
+  GridItem,
+  Image,
+  ImagePreview,
+  Tabs,
+} from "@nutui/nutui-react-taro";
+import { navigateTo, getSystemInfoSync } from "@tarojs/taro";
+import "./index.scss";
+import { useState, useEffect, useCallback } from "react";
+import { strSlice } from "../../utils";
+import { interactionApi } from "../../api/interaction";
+import { getUserPostListApi } from "../../api/post";
+import { getUserInfoApi } from "../../api/user";
+const Self = () => {
+  const [tab1value, setTab1value] = useState<string | number>("0");
+  const [scrollHeight, setScrollHeight] = useState<number>(0);
+  const [postList, setPostList] = useState<any[]>([]);
+  const [loading, setLoading] = useState<boolean>(false);
+  const [refreshing, setRefreshing] = useState<boolean>(false);
+  const [loadingMore, setLoadingMore] = useState<boolean>(false);
+  const [hasMore, setHasMore] = useState<boolean>(true);
+  const [error, setError] = useState<string>("");
+  const [mediaUrl, setMediaUrl] = useState<any[]>([]);
+  const [showPreview, setShowPreview] = useState(false);
+  const [init, setInit] = useState(0);
+  const [userInfo, setUserInfo] = useState<any>({});
+  const [pageConfig, setPageConfig] = useState({
+    page: 1,
+    page_size: 10,
+    count: 0,
+    has_more: false,
+  });
+
+  // 计算滚动区域高度
+  useEffect(() => {
+    const systemInfo = getSystemInfoSync();
+    // 屏幕高度 - 状态栏 - 导航栏 - 用户信息区域 - 标签栏
+    const height = systemInfo.windowHeight - 44 - 50 - 100;
+    setScrollHeight(height);
+  }, []);
+
+  // 点赞
+  const handleInteraction = async (postId: number, type) => {
+    const res: any = await interactionApi({
+      target_type: "post",
+      target_id: postId,
+      interaction_type: type,
+    });
+    if (res.code === 200) {
+      const data = [...postList];
+      data.forEach((item: any) => {
+        if (item.id === postId) {
+          item.interaction_type = type;
+          item.like_count =
+            type === "praise" ? item.like_count + 1 : item.like_count - 1;
+        }
+      });
+      setPostList(data);
+    }
+  };
+  // 获取用户信息
+  const getUserInfo = async () => {
+    const res: any = await getUserInfoApi();
+    if (res.code === 200) {
+      setUserInfo(res.data);
+    }
+  };
+
+  // 获取帖子列表
+  const getPostList = async (
+    isRefresh: boolean = false,
+    isLoadMore: boolean = false
+  ) => {
+    try {
+      // 设置对应的加载状态
+      if (isRefresh) {
+        setRefreshing(true);
+      } else if (isLoadMore) {
+        setLoadingMore(true);
+      } else {
+        setLoading(true);
+      }
+
+      setError(""); // 清除错误信息
+
+      const currentPage = isRefresh ? 1 : pageConfig.page;
+      const requestParams = {
+        ...pageConfig,
+        page: currentPage,
+      };
+
+      const res: any = await getUserPostListApi(null, requestParams);
+
+      if (res.code === 200) {
+        const newData = res.data.list || [];
+
+        setPageConfig({
+          page: res.data.page,
+          page_size: res.data.page_size,
+          count: res.data.count,
+          has_more: res.data.has_more,
+        });
+
+        // 根据操作类型更新帖子列表
+        if (isRefresh || currentPage === 1) {
+          setPostList(newData);
+        } else if (isLoadMore) {
+          setPostList((prev) => [...prev, ...newData]);
+        } else {
+          setPostList(newData);
+        }
+
+        setHasMore(res.data.has_more);
+      } else {
+        setError(res.message || "获取帖子列表失败");
+      }
+    } catch (err: any) {
+      console.error("获取帖子列表失败:", err);
+      setError(err.message || "网络错误,请稍后重试");
+    } finally {
+      // 清除所有加载状态
+      setLoading(false);
+      setRefreshing(false);
+      setLoadingMore(false);
+    }
+  };
+
+  // 初始加载
+  useEffect(() => {
+    getPostList();
+    getUserInfo();
+  }, []);
+
+  // 加载更多
+  const loadMore = useCallback(() => {
+    if (hasMore && !loadingMore && !loading && !refreshing) {
+      getPostList(false, true);
+    }
+  }, [hasMore, loadingMore, loading, refreshing]);
+
+  // 下拉刷新
+  const onRefresh = useCallback(() => {
+    getPostList(true, false);
+  }, []);
+  return (
+    <View className="min-h-screen bg-[#f7f8fa]">
+      <View className="p-[10px] bg-[#fff]">
+        <View className="flex items-center justify-between">
+          <View className="flex items-center">
+            <View className="w-[50px] h-[50px] bg-[#1874d0] rounded-[50%] overflow-hidden">
+              {userInfo.avatar_url ? (
+                <Image src={userInfo.avatar_url} mode="aspectFill" />
+              ) : (
+                <Image src={userInfo.avatar_url} mode="aspectFill" />
+              )}
+            </View>
+            <View className="flex-1 ml-[10px]">
+              <View className="text-[16px] font-bold">
+                {userInfo.nickname || userInfo.username}
+              </View>
+            </View>
+          </View>
+          <Badge top="2" right="8" value={userInfo.notification_count}>
+            <Notice
+              onClick={() => {
+                navigateTo({ url: "/pages/message/index" });
+              }}
+            />
+          </Badge>
+        </View>
+        <View className="flex items-center justify-between text-[14px] mt-[10px]">
+          <View className="flex">
+            <View className="flex">
+              <Text className="text-[#949494]">关注我</Text>
+              <Text className="ml-[5px]">{userInfo.follow_count}</Text>
+            </View>
+            <View className="flex ml-[20px]">
+              <Text className="text-[#949494]">获得互动</Text>
+              <Text className="ml-[5px]">{userInfo.interaction_count}</Text>
+            </View>
+          </View>
+          <View
+            className="bg-[#f7f7f7] w-[70px] h-[30px] rounded-[15px] flex items-center justify-around"
+            onClick={() => {
+              navigateTo({ url: "/pages/publish/index" });
+            }}
+          >
+            <View className="flex items-center">
+              <Plus />
+              <Text>发布</Text>
+            </View>
+          </View>
+        </View>
+      </View>
+      <View className="mt-[10px]">
+        <Tabs
+          className="tabs-self"
+          align="left"
+          value={tab1value}
+          onChange={(value) => {
+            setTab1value(value);
+          }}
+        >
+          <Tabs.TabPane title="帖子">
+            <ScrollView
+              scrollY
+              style={{ height: `${scrollHeight}px` }}
+              onScrollToLower={loadMore}
+              refresherEnabled
+              refresherTriggered={refreshing}
+              onRefresherRefresh={onRefresh}
+              lowerThreshold={50}
+              className="bg-[#f7f8fa]"
+            >
+              {postList.map((item) => (
+                <View
+                  className="post-list-item bg-[#fff] p-[10px] mt-[10px]"
+                  onClick={() =>
+                    navigateTo({ url: `/pages/detail/index?id=${item.id}` })
+                  }
+                >
+                  <View className="flex items-center justify-between">
+                    <View className="flex items-center">
+                      <View className="w-[80rpx] h-[80rpx] rounded-[50%] bg-[#000] overflow-hidden bg-[url('https://m.360buyimg.com/mobilecms/s750x366_jfs/t1/26597/30/4870/174583/5c35c5d2Ed55eedc6/50e27870c25e7a82.png')] bg-cover bg-center"></View>
+                      <View className="ml-[10rpx]">
+                        <View className="text-[14px] font-[800] text-[#9c9dee]">
+                          {item.user.nickname || "微信用户"}
+                        </View>
+                        {item.user.is_official ? (
+                          <View className="text-[12px] text-[#949494]">
+                            《心动女友》官方账号
+                          </View>
+                        ) : (
+                          <></>
+                        )}
+                      </View>
+                    </View>
+                  </View>
+                  <View className="text-[14px] text-[#333] pt-[10px]">
+                    {strSlice(item.content, 100)}
+                    {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
+                      gap={7}
+                      style={
+                        {
+                          "--nutui-grid-item-content-padding": "0px",
+                          "--nutui-grid-item-content-margin": "0px",
+                        } as any
+                      }
+                    >
+                      {item.media_url.map((ite: any, index: number) => {
+                        return (
+                          <GridItem>
+                            <Image
+                              src={ite.src}
+                              mode="aspectFill"
+                              height="100"
+                              onClick={(e) => {
+                                e.stopPropagation();
+                                setMediaUrl(item.media_url);
+                                setInit(index + 1);
+
+                                setShowPreview(true);
+                              }}
+                            />
+                          </GridItem>
+                        );
+                      })}
+                    </Grid>
+                  </View>
+                  <View className="mt-[10px] pl-[20px] pr-[20px] flex items-center justify-between">
+                    <View className="flex items-center">
+                      <Message size={16} color="#949494" />
+                      <Text className="ml-[5px] text-[16px] text-[#949494]">
+                        {item.comment_count}
+                      </Text>
+                    </View>
+                    <View
+                      className="flex items-center"
+                      onClick={() => {
+                        handleInteraction(
+                          item.id,
+                          item.interaction_type === "praise"
+                            ? "dispraise"
+                            : "praise"
+                        );
+                      }}
+                    >
+                      {item.interaction_type === "praise" ? (
+                        <HeartFill size={16} color="#ec4342" />
+                      ) : (
+                        <Heart size={16} color="#949494" />
+                      )}
+                      <Text className="ml-[5px] text-[16px] text-[#949494]">
+                        {item.like_count}
+                      </Text>
+                    </View>
+                  </View>
+                </View>
+              ))}
+
+              {/* 错误状态 */}
+              {error && (
+                <View className="p-[20px] text-center">
+                  <View className="text-[#ff4d4f] mb-[10px]">{error}</View>
+                  <View
+                    className="text-[#1874d0] underline cursor-pointer"
+                    onClick={() => getPostList()}
+                  >
+                    点击重试
+                  </View>
+                </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]">
+                  加载中...
+                </View>
+              )}
+            </ScrollView>
+          </Tabs.TabPane>
+        </Tabs>
+      </View>
+      <ImagePreview
+        images={mediaUrl}
+        visible={showPreview}
+        defaultValue={init}
+        onClose={() => setShowPreview(false)}
+        indicator
+      />
+    </View>
+  );
+};
+
+export default Self;