返回 AiToEarn
carTask.tsx
根目录 / project / aitoearn-electron / src / views / task / carTask.tsx
1 /*
2 * @Author: nevin
3 * @Date: 2025-02-10 22:20:15
4 * @LastEditTime: 2025-03-24 14:28:05
5 * @LastEditors: nevin
6 * @Description: 任务
7 */
8 import { Button, Tag, Tooltip, Spin } from 'antd';
9 import { useState, useEffect, useRef } from 'react';
10 import { Task, TaskProduct, TaskType } from '@@/types/task';
11 import { taskApi } from '@/api/task';
12 import { TaskInfoRef } from './components/popInfo';
13 import TaskInfo from './components/carInfo';
14 import { InfoCircleOutlined } from '@ant-design/icons';
15 import styles from './task.module.scss';
16
17 const FILE_BASE_URL = import.meta.env.VITE_APP_FILE_HOST;
18
19 export default function Page() {
20 const [taskList, setTaskList] = useState<Task<TaskProduct>[]>([]);
21 const [pageInfo, setPageInfo] = useState({
22 pageSize: 9,
23 pageNo: 1,
24 totalCount: 0,
25 });
26 const [loading, setLoading] = useState(false);
27 const [hasMore, setHasMore] = useState(true);
28
29 const Ref_TaskInfo = useRef<TaskInfoRef>(null);
30
31 async function getTaskList(isLoadMore = false) {
32 setLoading(true);
33 try {
34 const res = await taskApi.getTaskList<TaskProduct>({
35 ...pageInfo,
36 type: TaskType.PRODUCT,
37 });
38
39 if (isLoadMore) {
40 setTaskList((prev) => [...prev, ...res.items]);
41 } else {
42 setTaskList(res.items);
43 }
44
45 setPageInfo((prev) => ({
46 ...prev,
47 totalCount: (res as any).totalCount,
48 }));
49
50 // 检查是否还有更多数据
51 setHasMore(pageInfo.pageNo * pageInfo.pageSize < (res as any).totalCount);
52 } catch (error) {
53 console.error('获取任务列表失败', error);
54 } finally {
55 setLoading(false);
56 }
57 }
58
59 useEffect(() => {
60 getTaskList();
61 }, []);
62
63 // 加载更多数据
64 const loadMore = () => {
65 setPageInfo((prev) => ({
66 ...prev,
67 pageNo: prev.pageNo + 1,
68 }));
69 getTaskList(true);
70 };
71
72 // 计算佣金比例
73 const calculateCommissionRate = (price: number, reward: number) => {
74 if (!price || price === 0) return 0;
75 return Math.round((reward / price) * 100);
76 };
77
78 // 计算节省金额
79 const calculateSavings = (price: number, reward: number) => {
80 return reward.toFixed(2);
81 };
82
83 // 刷新任务列表的函数
84 const refreshTaskList = () => {
85 setPageInfo({
86 pageSize: 9,
87 pageNo: 1,
88 totalCount: 0,
89 });
90 getTaskList();
91 };
92
93 return (
94 <div className={styles.taskListContainer}>
95 <TaskInfo ref={Ref_TaskInfo} onTaskApplied={refreshTaskList} />
96
97 <div className={styles.productGridContainer}>
98 <div className={styles.productGrid}>
99 {taskList.map((task) => {
100 const price = task.dataInfo?.price || 0;
101 const reward = task.reward || 0;
102 const commissionRate = calculateCommissionRate(price, reward);
103 const savings = calculateSavings(price, reward);
104
105 return (
106 <div key={task._id} className={styles.productCard}>
107 <div className={styles.productContent}>
108 <div className={styles.imageContainer}>
109 <img
110 src={`${FILE_BASE_URL}${task.imageUrl}`}
111 alt={task.title}
112 className={styles.productImage}
113 />
114 </div>
115
116 <div className={styles.productInfo}>
117 <h3 className={styles.productTitle}>
118 {task.dataInfo?.title || task.title}
119 </h3>
120 <div className={styles.priceRow}>
121 <div
122 style={{
123 display: 'flex',
124 flexDirection: 'row',
125 alignItems: 'center',
126 justifyContent: 'space-between',
127 }}
128 >
129 <span className={styles.price}>
130 ¥{task.dataInfo?.price || '暂无价格'}
131 </span>
132 <div className={styles.infoItem}>
133 <span className={styles.infoLabel}>已售:</span>
134 <span className={styles.infoValue}>
135 {task.dataInfo?.sales || 0}
136 </span>
137 </div>
138 </div>
139
140 <div
141 className={styles.discountTag}
142 style={{ margin: '8px 0' }}
143 >
144 <Tag color="#ff4d4f">高佣{commissionRate}%</Tag>
145 <Tag color="#ff4d4f">赚¥{savings}</Tag>
146 </div>
147 </div>
148
149 <div className={styles.infoRow}>
150 <div className={styles.infoItem}>
151 <span className={styles.infoLabel}>带货等级:</span>
152 <span className={styles.infoValue}>LV03及以上</span>
153 </div>
154 <div className={styles.infoItem}>
155 <span className={styles.infoLabel}>招募人数:</span>
156 <span className={styles.infoValue}>
157 {task.maxRecruits}
158 </span>
159 </div>
160 </div>
161 </div>
162 </div>
163
164 <div className={styles.taskFooter}>
165 <div className={styles.taskTips}>
166 新号首次报名,奖励1-100元,立即到账
167 <Tooltip title="新用户首次完成任务可获得随机奖励">
168 <InfoCircleOutlined className={styles.infoIcon} />
169 </Tooltip>
170 </div>
171 <Button
172 disabled={task.isAccepted}
173 type="primary"
174 className={styles.applyButton}
175 onClick={() => Ref_TaskInfo.current?.init(task)}
176 >
177 报名
178 </Button>
179 </div>
180 </div>
181 );
182 })}
183 </div>
184
185 {loading && (
186 <div className={styles.loadingContainer}>
187 <Spin />
188 </div>
189 )}
190
191 {!loading && hasMore && (
192 <div className={styles.loadMoreContainer}>
193 <Button onClick={loadMore}>查看更多项目...</Button>
194 </div>
195 )}
196 </div>
197 </div>
198 );
199 }
200
200 lines Plain Text