返回 AiToEarn
index.tsx
1 /*
2 * @Author: nevin
3 * @Date: 2025-02-10 22:20:15
4 * @LastEditTime: 2025-03-25 16:01:56
5 * @LastEditors: nevin
6 * @Description: 评论页面 reply
7 */
8 import { icpCreatorList, WorkData } from '@/icp/reply';
9 import {
10 Popconfirm,
11 Tabs,
12 Tooltip,
13 Card,
14 Typography,
15 Divider,
16 Spin,
17 } from 'antd';
18 import { useCallback, useRef, useState } from 'react';
19 import AccountSidebar from '../account/components/AccountSidebar/AccountSidebar';
20 import ReplyWorks, { ReplyWorksRef } from './components/replyWorks';
21 import AddAutoRun, { AddAutoRunRef } from './components/addAutoRun';
22 import CommentList, { CommentListRef } from './components/commentList';
23 import { QuestionCircleOutlined } from '@ant-design/icons';
24 import styles from './reply.module.scss';
25 import AutoRun from './autoRun';
26 import OneKeyReply, { OneKeyReplyRef } from './components/oneKeyReply';
27 import Masonry from 'react-masonry-css';
28 import { useInView } from 'react-intersection-observer';
29
30 export default function Page() {
31 const [wordList, setWordList] = useState<WorkData[]>([]);
32 const [activeAccountId, setActiveAccountId] = useState<number>(-1);
33 const [pageInfo, setPageInfo] = useState<{
34 count: number;
35 hasMore: boolean;
36 pcursor?: string;
37 }>({
38 count: 0,
39 hasMore: false,
40 });
41 const Ref_ReplyWorks = useRef<ReplyWorksRef>(null);
42 const Ref_AddAutoRun = useRef<AddAutoRunRef>(null);
43 const Ref_CommentList = useRef<CommentListRef>(null);
44 const Ref_OneKeyReply = useRef<OneKeyReplyRef>(null);
45 const [isLoadingMore, setIsLoadingMore] = useState(false);
46 const { Text } = Typography;
47
48 // 使用 react-intersection-observer 创建一个观察器
49 const { ref: loadMoreRef, inView } = useInView({
50 threshold: 0.5,
51 triggerOnce: false,
52 });
53
54 // 监听 inView 变化,当元素可见时加载更多
55 const loadMorePosts = useCallback(async () => {
56 if (!pageInfo.hasMore || isLoadingMore) return;
57 setIsLoadingMore(true);
58 try {
59 await getCreatorList(activeAccountId);
60 } finally {
61 setIsLoadingMore(false);
62 }
63 }, [pageInfo.hasMore, isLoadingMore, activeAccountId]);
64
65 // 监听 inView 变化
66 if (inView && pageInfo.hasMore && !isLoadingMore) {
67 loadMorePosts();
68 }
69
70 async function getCreatorList(accountId: number) {
71 if (accountId === -1) return;
72 const res = await icpCreatorList(accountId, pageInfo.pcursor);
73 setPageInfo(res.pageInfo);
74 setWordList([...wordList, ...res.list]);
75 }
76
77 /**
78 * 打开作品评论
79 * @param data
80 */
81 function openReplyWorks(data: WorkData) {
82 Ref_ReplyWorks.current?.init(activeAccountId, data);
83 }
84
85 /**
86 * 打开评论列表
87 * @param data
88 */
89 function openCommentList(data: WorkData) {
90 Ref_CommentList.current?.init(activeAccountId, data);
91 }
92
93 /**
94 * 打开创建自动任务
95 * @param data
96 */
97 function openAddAutoRun(data: WorkData) {
98 Ref_AddAutoRun.current?.init(activeAccountId, data);
99 }
100
101 // 计算断点值,用于响应式布局
102 const breakpointColumnsObj = {
103 default: 6,
104 2270: 5,
105 1939: 4,
106 1600: 3,
107 1200: 2,
108 900: 2,
109 600: 1,
110 };
111
112 return (
113 <div className={styles.account}>
114 <AccountSidebar
115 activeAccountId={activeAccountId}
116 onAccountChange={useCallback((info) => {
117 setWordList([]);
118 setActiveAccountId(info.id);
119 getCreatorList(info.id);
120 }, [])}
121 />
122
123 <div className="w-full p-4 text-gray-500">
124 <Tabs defaultActiveKey="1" className="w-full">
125 <Tabs.TabPane tab="作品列表" key="1">
126 {activeAccountId === -1 ? (
127 <div className="flex items-center justify-center h-[300px]">
128 <Tooltip title="请先在左侧侧边栏选择账户">
129 <QuestionCircleOutlined className="mr-2 text-3xl" />
130 </Tooltip>
131 点击左侧账户
132 </div>
133 ) : (
134 <div className="p-4">
135 <Masonry
136 breakpointCols={breakpointColumnsObj}
137 className={styles.myMasonryGrid}
138 columnClassName={styles.myMasonryGridColumn}
139 >
140 {wordList.map((item) => (
141 <div key={item.dataId} className={styles.masonryItem}>
142 <Card
143 hoverable
144 className={styles.cardContainer}
145 cover={<img alt="example" src={item.coverUrl} />}
146 actions={[
147 <Tooltip key="comment-list" title="评论列表">
148 <span
149 className=" cursor-pointer"
150 onClick={() => openCommentList(item)}
151 >
152 评论列表
153 </span>
154 </Tooltip>,
155 <Tooltip key="reply" title="评论作品">
156 <span
157 className=" cursor-pointer"
158 onClick={() => openReplyWorks(item)}
159 >
160 评论作品
161 </span>
162 </Tooltip>,
163 <Tooltip key="onekey" title="一键评论">
164 <Popconfirm
165 title="确认进行一键评论"
166 onConfirm={() => {
167 Ref_OneKeyReply.current?.init(
168 activeAccountId,
169 item,
170 );
171 }}
172 okText="是"
173 cancelText="否"
174 >
175 <span className=" cursor-pointer">一键评论</span>
176 </Popconfirm>
177 </Tooltip>,
178 <Tooltip key="auto" title="自动评论">
179 <span
180 className="cursor-pointer"
181 onClick={() => openAddAutoRun(item)}
182 >
183 自动评论
184 </span>
185 </Tooltip>,
186 ]}
187 >
188 <Card.Meta
189 title={item.title || '无标题'}
190 description={
191 <Text type="secondary">
192 {item.desc || '暂无描述'}
193 </Text>
194 }
195 />
196 </Card>
197 </div>
198 ))}
199 </Masonry>
200
201 {/* 加载更多区域 */}
202 <div ref={loadMoreRef} className={styles.loadMoreArea}>
203 {isLoadingMore && (
204 <div className={styles.loadingMore}>
205 <Spin size="small" />
206 <span style={{ marginLeft: 8 }}>加载中...</span>
207 </div>
208 )}
209
210 {!pageInfo.hasMore && wordList.length > 0 && (
211 <div className={styles.noMoreData}>
212 <Divider plain>没有更多数据了</Divider>
213 </div>
214 )}
215 </div>
216 </div>
217 )}
218 </Tabs.TabPane>
219 <Tabs.TabPane tab="自动任务" key="2">
220 <div style={{ width: '100%' }}>
221 <AutoRun />
222 </div>
223 </Tabs.TabPane>
224 </Tabs>
225 </div>
226
227 <ReplyWorks ref={Ref_ReplyWorks} />
228 <AddAutoRun ref={Ref_AddAutoRun} />
229 <CommentList ref={Ref_CommentList} />
230 <OneKeyReply ref={Ref_OneKeyReply} />
231 </div>
232 );
233 }
234
234 lines Plain Text