|
|
@@ -0,0 +1,354 @@
|
|
|
+import { Text, View, ScrollView } from "@tarojs/components";
|
|
|
+import { Heart, HeartFill, Plus } from "@nutui/icons-react-taro";
|
|
|
+import { Message } from "@nutui/icons-react-taro";
|
|
|
+import {
|
|
|
+ Grid,
|
|
|
+ GridItem,
|
|
|
+ Image,
|
|
|
+ ImagePreview,
|
|
|
+ Toast,
|
|
|
+ PullToRefresh,
|
|
|
+ Sticky,
|
|
|
+} from "@nutui/nutui-react-taro";
|
|
|
+import MoreButton from "../fllower-button";
|
|
|
+import { circlePostListApi } from "../../api/post";
|
|
|
+import { useEffect, useState, useCallback, useRef } from "react";
|
|
|
+import { strSlice } from "../../utils/index";
|
|
|
+import { followApi, interactionApi, unfollowApi } from "../../api/interaction";
|
|
|
+import { Dialog } from "@nutui/nutui-react-taro";
|
|
|
+import { navigateTo } from "@tarojs/taro";
|
|
|
+
|
|
|
+const PostList = () => {
|
|
|
+ const [postList, setPostList] = useState<any[]>([]);
|
|
|
+ const [showPreview, setShowPreview] = useState(false);
|
|
|
+ const [init, setInit] = useState(0);
|
|
|
+ const [mediaUrl, setMediaUrl] = useState<any[]>([]);
|
|
|
+ const containerTopRef = useRef(null);
|
|
|
+ // 分页相关状态
|
|
|
+ const [currentPage, setCurrentPage] = useState(1);
|
|
|
+ const [hasMore, setHasMore] = useState(true);
|
|
|
+ const [loading, setLoading] = useState(false);
|
|
|
+ const pageSize = 10;
|
|
|
+
|
|
|
+ const getPostList = useCallback(
|
|
|
+ async (page = 1, isRefresh = false) => {
|
|
|
+ setLoading(true);
|
|
|
+ try {
|
|
|
+ const res: any = await circlePostListApi({
|
|
|
+ circleId: 1,
|
|
|
+ page,
|
|
|
+ pageSize: pageSize,
|
|
|
+ });
|
|
|
+
|
|
|
+ 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);
|
|
|
+ } else {
|
|
|
+ setPostList((prev) => [...prev, ...newList]);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 判断是否还有更多数据
|
|
|
+ setHasMore(res.data.has_more);
|
|
|
+ setCurrentPage(page);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error("获取帖子列表失败:", error);
|
|
|
+ Toast.show("load_error", {
|
|
|
+ content: "加载失败,请重试",
|
|
|
+ duration: 2,
|
|
|
+ });
|
|
|
+ } finally {
|
|
|
+ setLoading(false);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ [pageSize]
|
|
|
+ );
|
|
|
+
|
|
|
+ // 下拉刷新
|
|
|
+ const handleRefresh = useCallback(async () => {
|
|
|
+ await getPostList(1, true);
|
|
|
+ }, [getPostList]);
|
|
|
+
|
|
|
+ // 上拉加载更多
|
|
|
+ const handleLoadMore = useCallback(async () => {
|
|
|
+ console.log("handleLoadMore called", { hasMore, currentPage, loading });
|
|
|
+ if (hasMore && !loading) {
|
|
|
+ console.log("Loading next page:", currentPage + 1);
|
|
|
+ await getPostList(currentPage + 1, false);
|
|
|
+ }
|
|
|
+ }, [hasMore, currentPage, getPostList, loading]);
|
|
|
+
|
|
|
+ const handleMoreClick = (postIndex: number) => {
|
|
|
+ const data = [...postList];
|
|
|
+ data.splice(postIndex, 1);
|
|
|
+ setPostList(data);
|
|
|
+ };
|
|
|
+
|
|
|
+ // 点赞
|
|
|
+ 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 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");
|
|
|
+ },
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ followApi({ followee_id: followeeId }).then(() => {
|
|
|
+ Toast.show("follow_success", {
|
|
|
+ content: "关注成功",
|
|
|
+ duration: 2,
|
|
|
+ lockScroll: true,
|
|
|
+ });
|
|
|
+ const data = [...postList];
|
|
|
+ data.forEach((item: any) => {
|
|
|
+ item.is_followed = true;
|
|
|
+ });
|
|
|
+
|
|
|
+ setPostList(data);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ };
|
|
|
+ useEffect(() => {
|
|
|
+ getPostList(1, true);
|
|
|
+ }, []);
|
|
|
+
|
|
|
+ return (
|
|
|
+ // <ScrollView
|
|
|
+ // scrollY
|
|
|
+ // className="post-list-scroll"
|
|
|
+ // style={{ height: "calc(100vh - 100px)" }}
|
|
|
+ // onScrollToLower={handleLoadMore}
|
|
|
+ // lowerThreshold={200}
|
|
|
+ // >
|
|
|
+ <View>
|
|
|
+ {/* <PullToRefresh onRefresh={handleRefresh}> */}
|
|
|
+ <View className="post-list bg-[#f8f8f8]">
|
|
|
+ {postList.map((item: any, index: number) => {
|
|
|
+ return (
|
|
|
+ <View
|
|
|
+ key={item.id}
|
|
|
+ className="post-list-item bg-[#fff] p-[10px] mb-[10px]"
|
|
|
+ >
|
|
|
+ <View
|
|
|
+ className="flex items-center justify-between"
|
|
|
+ onClick={() => {
|
|
|
+ navigateTo({
|
|
|
+ url: `/pages/self/index?id=${item.user_id}`,
|
|
|
+ });
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <View className="flex items-center">
|
|
|
+ <View
|
|
|
+ className="w-[40px] h-[40px] rounded-[50%] bg-[#000] overflow-hidden bg-cover bg-center"
|
|
|
+ style={{
|
|
|
+ backgroundImage: item.user.avatar_url
|
|
|
+ ? `url('${item.user.avatar_url}')`
|
|
|
+ : "none",
|
|
|
+ }}
|
|
|
+ ></View>
|
|
|
+ <View className="ml-[5px]">
|
|
|
+ <View className="text-[14px] font-[800] text-[#9c9dee]">
|
|
|
+ {item.user.nickname || item.user.username || "微信用户"}
|
|
|
+ </View>
|
|
|
+ {item.user.is_official ? (
|
|
|
+ <View className="text-[12px] text-[#949494]">
|
|
|
+ 《心动女友》官方账号
|
|
|
+ </View>
|
|
|
+ ) : (
|
|
|
+ <></>
|
|
|
+ )}
|
|
|
+ </View>
|
|
|
+ </View>
|
|
|
+ <View className="flex items-center">
|
|
|
+ <View
|
|
|
+ className="flex items-center justify-center mr-[12px] text-[12px] bg-[#f8f8f8] text-[#949494] rounded-[20px] h-[25px] w-[60px] text-center"
|
|
|
+ onClick={(e) => {
|
|
|
+ e.stopPropagation();
|
|
|
+ handleFollowClick(item.user_id, item.is_followed);
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ {item.is_followed ? <></> : <Plus color="#1874d0" />}
|
|
|
+ <Text>{item.is_followed ? "已关注" : "关注"}</Text>
|
|
|
+ </View>
|
|
|
+ {item.user.is_official ? (
|
|
|
+ <></>
|
|
|
+ ) : (
|
|
|
+ <MoreButton
|
|
|
+ postIndex={index}
|
|
|
+ followeeId={item.user_id}
|
|
|
+ postId={item.id}
|
|
|
+ postItem={item}
|
|
|
+ onClick={handleMoreClick}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ </View>
|
|
|
+ </View>
|
|
|
+ <View
|
|
|
+ onClick={() =>
|
|
|
+ navigateTo({ url: `/pages/detail/index?id=${item.id}` })
|
|
|
+ }
|
|
|
+ >
|
|
|
+ <View className="text-[14px] text-[#333] pt-[20px]">
|
|
|
+ {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",
|
|
|
+ "--nutui-grid-border-color": "transparent",
|
|
|
+ } as any
|
|
|
+ }
|
|
|
+ >
|
|
|
+ {item.media_url.map((ite: any, index: number) => {
|
|
|
+ return (
|
|
|
+ <GridItem>
|
|
|
+ <Image
|
|
|
+ src={ite.src}
|
|
|
+ mode={
|
|
|
+ item.media_url.length === 1
|
|
|
+ ? "scaleToFill"
|
|
|
+ : "scaleToFill"
|
|
|
+ }
|
|
|
+ onClick={(e) => {
|
|
|
+ e.preventDefault();
|
|
|
+ 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={(e) => {
|
|
|
+ e.stopPropagation();
|
|
|
+ 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>
|
|
|
+ </View>
|
|
|
+ );
|
|
|
+ })}
|
|
|
+
|
|
|
+ {/* 空状态提示 */}
|
|
|
+ {postList.length === 0 && !loading && (
|
|
|
+ <View className="flex flex-col items-center justify-center py-[50px]">
|
|
|
+ <Text className="text-[#999] text-[14px]">暂无帖子</Text>
|
|
|
+ </View>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* 加载更多提示 */}
|
|
|
+ {hasMore && (
|
|
|
+ <View className="p-[20px] text-center">
|
|
|
+ <Text className="text-[14px] text-[#999]">
|
|
|
+ {loading ? "加载中..." : "上拉加载更多"}
|
|
|
+ </Text>
|
|
|
+ </View>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {!hasMore && postList.length > 0 && (
|
|
|
+ <View className="p-[20px] text-center">
|
|
|
+ <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>
|
|
|
+ {/* </PullToRefresh> */}
|
|
|
+ </View>
|
|
|
+ // </ScrollView>
|
|
|
+ );
|
|
|
+};
|
|
|
+
|
|
|
+export default PostList;
|