返回 AiToEarn
articleInfo.tsx
根目录 / project / aitoearn-electron / src / views / task / components / articleInfo.tsx
1 import { Button, Modal, Tag, Divider, Row, Col, message } from 'antd';
2 import { Task, TaskTypeName, TaskArticle } from '@@/types/task';
3 import { PubType } from '@@/publish/PublishEnum';
4 import { forwardRef, useImperativeHandle, useState } from 'react';
5 import { taskApi } from '@/api/task';
6 import styles from './articleInfo.module.scss';
7 import {
8 ClockCircleOutlined,
9 TeamOutlined,
10 TagOutlined,
11 DollarOutlined,
12 CopyOutlined,
13 } from '@ant-design/icons';
14 import ChooseAccountModule from '@/views/publish/components/ChooseAccountModule/ChooseAccountModule';
15 import { PlatType } from '@@/AccountEnum';
16
17 const FILE_BASE_URL = import.meta.env.VITE_APP_FILE_HOST;
18
19 export interface TaskInfoRef {
20 init: (pubRecord: Task<TaskArticle>) => Promise<void>;
21 }
22
23 interface TaskInfoProps {
24 onTaskApplied?: () => void;
25 }
26
27 const Com = forwardRef<TaskInfoRef, TaskInfoProps>((props: any, ref) => {
28 const [isModalOpen, setIsModalOpen] = useState(false);
29 const [taskInfo, setTaskInfo] = useState<Task<TaskArticle> | null>();
30 const [downloading, setDownloading] = useState(false);
31 const [taskRecord, setTaskRecord] = useState<{
32 _id: string;
33 createTime: string;
34 isFirstTimeSubmission: boolean;
35 status: string;
36 taskId: string;
37 } | null>(null);
38
39 const { onTaskApplied } = props;
40
41 async function init(inTaskInfo: Task<TaskArticle>) {
42 setTaskInfo(inTaskInfo);
43 setIsModalOpen(true);
44 }
45
46 useImperativeHandle(ref, () => ({
47 init: init,
48 }));
49
50 /**
51 * 接受任务
52 */
53 async function taskApply() {}
54
55 /**
56 * 完成任务
57 */
58 async function taskDone() {
59 console.log('完成任务', taskInfo, taskRecord);
60 if (!taskInfo || !taskRecord) {
61 message.error('任务信息不完整,无法完成任务');
62 return;
63 }
64
65 try {
66 const res = await taskApi.taskDone(taskRecord._id, {
67 submissionUrl: taskInfo.title,
68 screenshotUrls: [taskInfo.imageUrl],
69 qrCodeScanResult: taskInfo.title,
70 });
71 message.success('任务发布成功!');
72 } catch (error) {
73 message.error('完成任务失败,请稍后再试');
74 }
75 }
76
77 const handleCancel = () => {
78 setIsModalOpen(false);
79 };
80
81 // 复制任务ID
82 const copyTaskId = (id: string) => {
83 navigator.clipboard
84 .writeText(id)
85 .then(() => {
86 message.success('任务ID已复制到剪贴板');
87 })
88 .catch(() => {
89 message.error('复制失败,请手动复制');
90 });
91 };
92
93 // 账户选择弹框显示隐藏状态
94 const [chooseAccountOpen, setChooseAccountOpen] = useState(false);
95 const [accountListChoose, setAccountListChoose] = useState<any[]>([]);
96
97 return (
98 <>
99 <ChooseAccountModule
100 open={chooseAccountOpen}
101 onClose={() => !downloading && setChooseAccountOpen(false)}
102 platChooseProps={{
103 choosedAccounts: accountListChoose,
104 pubType: PubType.ARTICLE,
105 allowPlatSet: new Set([PlatType.Douyin]),
106 }}
107 onPlatConfirm={async (aList) => {
108 console.log('账号:', aList);
109 setAccountListChoose(aList);
110 console.log('选择的账号:', aList);
111 // 这里可以添加发布文章的逻辑
112 setChooseAccountOpen(false);
113 }}
114 />
115
116 <Modal
117 title={null}
118 open={isModalOpen}
119 onCancel={handleCancel}
120 footer={null}
121 width={800}
122 className={styles.taskInfoModal}
123 destroyOnClose
124 >
125 {taskInfo ? (
126 <div className={styles.taskInfoContainer}>
127 <div className={styles.taskInfoHeader}>
128 <h2 className={styles.taskTitle}>
129 {taskInfo.title}
130 <span className={styles.taskId}>
131 ID: {taskInfo._id}
132 <CopyOutlined
133 className={styles.copyIcon}
134 onClick={() => copyTaskId(taskInfo._id)}
135 />
136 </span>
137 </h2>
138 <Tag color="#a66ae4" className={styles.taskTag}>
139 {(taskInfo.dataInfo as any)?.type || '文章任务'}
140 </Tag>
141 </div>
142
143 <div className={styles.taskInfoContent}>
144 <Row gutter={24}>
145 <Col span={12}>
146 <div className={styles.articleContainer}>
147 <div className={styles.taskImageContainer}>
148 <img
149 src={`${FILE_BASE_URL}${taskInfo.imageUrl}`}
150 alt={taskInfo.title}
151 className={styles.taskImage}
152 />
153 </div>
154 <div className={styles.articleCaption}>文章封面预览</div>
155 </div>
156 </Col>
157
158 <Col span={12}>
159 <div className={styles.taskDetails}>
160 <div className={styles.taskDetail}>
161 <TagOutlined className={styles.detailIcon} />
162 <span className={styles.detailLabel}>任务类型:</span>
163 <span className={styles.detailValue}>
164 {TaskTypeName.get(taskInfo.type) || '文章任务'}
165 </span>
166 </div>
167
168 <div className={styles.taskDetail}>
169 <ClockCircleOutlined className={styles.detailIcon} />
170 <span className={styles.detailLabel}>截止时间:</span>
171 <span className={styles.detailValue}>
172 {taskInfo.deadline || '2025/03/17'}
173 </span>
174 </div>
175
176 <div className={styles.taskDetail}>
177 <TeamOutlined className={styles.detailIcon} />
178 <span className={styles.detailLabel}>招募人数:</span>
179 <span className={styles.detailValue}>
180 {taskInfo.maxRecruits || 0}
181 </span>
182 </div>
183
184 <div className={styles.taskDetail}>
185 <DollarOutlined className={styles.detailIcon} />
186 <span className={styles.detailLabel}>任务奖励:</span>
187 <span className={styles.detailValue}>
188 ¥{taskInfo.reward || 0}
189 </span>
190 </div>
191
192 <div className={styles.taskDetail}>
193 <span className={styles.detailLabel}>任务要求:</span>
194 <span className={styles.detailValue}>
195 {taskInfo.requirement || '不许删文'}
196 </span>
197 </div>
198 </div>
199 </Col>
200 </Row>
201
202 <Divider />
203
204 <div className={styles.taskDescription}>
205 <h3 className={styles.sectionTitle}>任务描述</h3>
206 <div
207 className={styles.descriptionContent}
208 dangerouslySetInnerHTML={{
209 __html: taskInfo.description || '暂无描述',
210 }}
211 />
212 </div>
213
214 {taskInfo.dataInfo && (
215 <>
216 <Divider />
217 <div className={styles.articleDetails}>
218 <h3 className={styles.sectionTitle}>文章详情</h3>
219 <div className={styles.articleInfo}>
220 <div className={styles.articleInfoItem}>
221 <span className={styles.articleInfoLabel}>
222 文章标题:
223 </span>
224 <span className={styles.articleInfoValue}>
225 {taskInfo.dataInfo.title || '未知'}
226 </span>
227 </div>
228
229 <div className={styles.articleInfoItem}>
230 <span className={styles.articleInfoLabel}>
231 文章描述:
232 </span>
233 <span className={styles.articleInfoValue}>
234 {taskInfo.dataInfo.desc || '暂无描述'}
235 </span>
236 </div>
237 </div>
238 </div>
239 </>
240 )}
241 </div>
242
243 <div className={styles.taskInfoFooter}>
244 <div className={styles.taskStatus}>
245 <Tag color="#a66ae4">进行中</Tag>
246 </div>
247 <div className={styles.taskActions}>
248 <Button className={styles.cancelButton} onClick={handleCancel}>
249 取消
250 </Button>
251 <Button
252 type="primary"
253 className={styles.applyButton}
254 onClick={taskApply}
255 >
256 接受任务
257 </Button>
258 </div>
259 </div>
260 </div>
261 ) : (
262 <div className={styles.noDataContainer}>暂无数据</div>
263 )}
264 </Modal>
265 </>
266 );
267 });
268
269 Com.displayName = 'ArticleInfo';
270
271 export default Com;
272
272 lines Plain Text