| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354 |
- <template>
- <div class="my-task-page">
- <!-- 返回按钮 -->
- <div class="header">
- <van-icon name="arrow-left" @click="goBack" />
- <span>{{ $t('home.taskCenter') }}</span>
- <span></span>
- </div>
- <!-- 状态Tab -->
- <div class="status-tabs">
- <div
- class="tab-item"
- v-for="item in statusTabs"
- :key="item.value"
- :class="{ active: status === item.value }"
- @click="status = item.value"
- >
- {{ item.label }}
- </div>
- </div>
- <!-- 任务列表 -->
- <div class="task-list">
- <van-pull-refresh v-model="refreshing" @refresh="onRefresh">
- <van-list
- v-model:loading="loading"
- :finished="finished"
- :finished-text="$t('common.noData')"
- @load="loadMore"
- >
- <div
- class="task-card"
- v-for="item in taskList"
- :key="item.id"
- @click="goDetail(item)"
- >
- <div class="task-img">
- <img :src="item.task?.cover || item.task?.imageMaterial?.[0] || defaultIcon" />
- </div>
- <div class="task-info">
- <div class="task-title">{{ item.taskTitle || item.task?.title }}</div>
- <div class="task-meta">
- <span class="time">{{ formatTime(item.createdAt) }}</span>
- <span class="status" :class="getStatusClass(item.status)">
- {{ getStatusText(item.status) }}
- </span>
- </div>
- <div class="task-price">+{{ formatReward(item.rewardAmount || item.task?.rewardAmount) }} USDT</div>
- </div>
- </div>
- </van-list>
- </van-pull-refresh>
- </div>
- </div>
- </template>
- <script setup lang="ts">
- import { ref, computed, watch, onMounted } from "vue";
- import { useRouter } from "vue-router";
- import { useI18n } from "vue-i18n";
- import dayjs from "dayjs";
- import { requestMyTaskList } from "@/api/task";
- import defaultIcon from "@/assets/images/common/no-data.svg";
- const { t } = useI18n();
- const router = useRouter();
- const status = ref<number>(-99);
- const taskList = ref<TaskApplyInfo[]>([]);
- const loading = ref(false);
- const finished = ref(false);
- const refreshing = ref(false);
- const page = ref(1);
- const pageSize = 10;
- // 后端状态: -2=已放弃 -1=审核失败 0=进行中 1=待审核 2=已完成 5=已打回
- const statusTabs = computed(() => [
- { label: t('taskStatus.all'), value: -99 },
- { label: t('taskStatus.inProgress'), value: 0 },
- { label: t('taskStatus.pendingReview'), value: 1 },
- { label: t('taskStatus.completed'), value: 2 },
- { label: t('taskStatus.returnBack'), value: 5 },
- { label: t('taskStatus.rejected'), value: -1 }
- ]);
- // 后端状态: -2=已放弃 -1=审核失败 0=进行中 1=待审核 2=已完成 5=已打回
- const getStatusClass = (s: number) => {
- const map: Record<number, string> = {
- [-2]: 'abandoned',
- [-1]: 'rejected',
- 0: 'in-progress',
- 1: 'pending',
- 2: 'completed',
- 5: 'return-back'
- };
- return map[s] || '';
- };
- const getStatusText = (s: number) => {
- const map: Record<number, string> = {
- [-2]: t('taskStatus.abandoned'),
- [-1]: t('taskStatus.rejected'),
- 0: t('taskStatus.inProgress'),
- 1: t('taskStatus.pendingReview'),
- 2: t('taskStatus.completed'),
- 5: t('taskStatus.returnBack')
- };
- return map[s] || '';
- };
- const formatTime = (time: string | number) => {
- if (!time) return '';
- // 如果是时间戳(秒)
- if (typeof time === 'number') {
- return dayjs.unix(time).format('YYYY-MM-DD HH:mm');
- }
- return dayjs(time).format('YYYY-MM-DD HH:mm');
- };
- // 格式化奖励金额
- const formatReward = (amount: number | string | undefined) => {
- if (amount === undefined || amount === null) return '0.00';
- const num = typeof amount === 'string' ? parseFloat(amount) : amount;
- if (num > 100) {
- return (num / 100).toFixed(2);
- }
- return num.toFixed(2);
- };
- const getTaskList = async (isRefresh = false) => {
- if (isRefresh) {
- page.value = 1;
- finished.value = false;
- }
- try {
- const res = await requestMyTaskList({
- current: page.value,
- size: pageSize,
- status: status.value === -99 ? undefined : status.value
- });
- if (res.code === 200) {
- const list = res.data?.list || [];
- if (isRefresh) {
- taskList.value = list;
- } else {
- taskList.value = [...taskList.value, ...list];
- }
- if (list.length < pageSize) {
- finished.value = true;
- }
- }
- } catch (e) {
- // 模拟数据
- const mockStatuses = status.value ? [status.value] : [1, 2, 3, 4];
- const mockList = Array(pageSize).fill(null).map((_, i) => ({
- id: (page.value - 1) * pageSize + i + 1,
- taskId: i + 1,
- status: mockStatuses[Math.floor(Math.random() * mockStatuses.length)],
- createdAt: dayjs().subtract(i, 'day').toISOString(),
- task: {
- id: i + 1,
- title: `Task ${(page.value - 1) * pageSize + i + 1} - Complete social media interaction`,
- price: (Math.random() * 10 + 2).toFixed(2),
- imageMaterial: []
- }
- })) as TaskApplyInfo[];
- if (isRefresh) {
- taskList.value = mockList;
- } else {
- taskList.value = [...taskList.value, ...mockList];
- }
- if (page.value >= 2) {
- finished.value = true;
- }
- }
- loading.value = false;
- refreshing.value = false;
- };
- const onRefresh = () => {
- getTaskList(true);
- };
- const loadMore = () => {
- page.value++;
- getTaskList();
- };
- const goBack = () => {
- router.back();
- };
- const goDetail = (item: TaskApplyInfo) => {
- if (item.status === 0 || item.status === 5 || item.status === 3 || item.status === -1) {
- // 进行中(0)、已拒绝(3/-1)或已打回(5) - 跳转提交页
- router.push(`/task/submit/${item.id}`);
- } else {
- // 其他状态 - 跳转详情页
- router.push(`/task/detail/${item.taskId}`);
- }
- };
- watch(status, () => {
- getTaskList(true);
- });
- onMounted(() => {
- getTaskList(true);
- });
- </script>
- <style lang="scss" scoped>
- .my-task-page {
- min-height: 100vh;
- background: #121212;
- padding-bottom: 80px;
- }
- .header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 16px;
- color: #fff;
- font-size: 16px;
- font-weight: 600;
- .van-icon {
- font-size: 20px;
- }
- }
- .status-tabs {
- display: flex;
- gap: 8px;
- padding: 0 16px 16px;
- overflow-x: auto;
- &::-webkit-scrollbar {
- display: none;
- }
- .tab-item {
- flex-shrink: 0;
- padding: 8px 14px;
- background: rgba(255, 255, 255, 0.05);
- border-radius: 16px;
- font-size: 12px;
- color: rgba(255, 255, 255, 0.6);
- &.active {
- background: linear-gradient(135deg, #ffc300 0%, #ff9500 100%);
- color: #000;
- font-weight: 600;
- }
- }
- }
- .task-list {
- padding: 0 16px;
- }
- .task-card {
- display: flex;
- background: rgba(255, 255, 255, 0.05);
- border-radius: 12px;
- overflow: hidden;
- margin-bottom: 12px;
- .task-img {
- width: 90px;
- height: 90px;
- flex-shrink: 0;
- img {
- width: 100%;
- height: 100%;
- object-fit: cover;
- }
- }
- .task-info {
- flex: 1;
- padding: 12px;
- display: flex;
- flex-direction: column;
- justify-content: space-between;
- .task-title {
- font-size: 14px;
- color: #fff;
- line-height: 1.3;
- display: -webkit-box;
- -webkit-line-clamp: 2;
- -webkit-box-orient: vertical;
- overflow: hidden;
- }
- .task-meta {
- display: flex;
- align-items: center;
- gap: 12px;
- .time {
- font-size: 11px;
- color: rgba(255, 255, 255, 0.4);
- }
- .status {
- font-size: 11px;
- padding: 2px 8px;
- border-radius: 10px;
- &.in-progress {
- background: rgba(33, 150, 243, 0.2);
- color: #2196f3;
- }
- &.pending {
- background: rgba(255, 152, 0, 0.2);
- color: #ff9800;
- }
- &.completed {
- background: rgba(76, 175, 80, 0.2);
- color: #4caf50;
- }
- &.rejected {
- background: rgba(244, 67, 54, 0.2);
- color: #f44336;
- }
- &.abandoned {
- background: rgba(158, 158, 158, 0.2);
- color: #9e9e9e;
- }
- &.return-back {
- background: rgba(255, 152, 0, 0.2);
- color: #ff9800;
- }
- }
- }
- .task-price {
- font-size: 15px;
- font-weight: bold;
- color: #ffc300;
- }
- }
- }
- </style>
|