645 lines
13 KiB
Vue
645 lines
13 KiB
Vue
|
|
<template>
|
|||
|
|
<div class="task-center">
|
|||
|
|
<!-- 导航栏 -->
|
|||
|
|
<nav class="navbar">
|
|||
|
|
<div class="nav-left">
|
|||
|
|
<el-button
|
|||
|
|
type="text"
|
|||
|
|
@click="$router.go(-1)"
|
|||
|
|
class="back-btn"
|
|||
|
|
>
|
|||
|
|
<el-icon><ArrowLeft /></el-icon>
|
|||
|
|
返回
|
|||
|
|
</el-button>
|
|||
|
|
</div>
|
|||
|
|
<div class="nav-center">
|
|||
|
|
<h1 class="nav-title">任务中心</h1>
|
|||
|
|
</div>
|
|||
|
|
<div class="nav-right">
|
|||
|
|
<el-button
|
|||
|
|
type="text"
|
|||
|
|
@click="$router.push('/points-history')"
|
|||
|
|
class="points-btn"
|
|||
|
|
>
|
|||
|
|
<el-icon><Coin /></el-icon>
|
|||
|
|
{{ userPoints }}
|
|||
|
|
</el-button>
|
|||
|
|
</div>
|
|||
|
|
</nav>
|
|||
|
|
|
|||
|
|
<!-- 任务统计 -->
|
|||
|
|
<div class="task-stats">
|
|||
|
|
<div class="stats-card">
|
|||
|
|
<div class="stat-item">
|
|||
|
|
<div class="stat-icon">
|
|||
|
|
<el-icon><Trophy /></el-icon>
|
|||
|
|
</div>
|
|||
|
|
<div class="stat-info">
|
|||
|
|
<div class="stat-value">{{ completedTasks }}</div>
|
|||
|
|
<div class="stat-label">已完成</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="stat-item">
|
|||
|
|
<div class="stat-icon">
|
|||
|
|
<el-icon><Clock /></el-icon>
|
|||
|
|
</div>
|
|||
|
|
<div class="stat-info">
|
|||
|
|
<div class="stat-value">{{ pendingTasks }}</div>
|
|||
|
|
<div class="stat-label">进行中</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="stat-item">
|
|||
|
|
<div class="stat-icon">
|
|||
|
|
<el-icon><Star /></el-icon>
|
|||
|
|
</div>
|
|||
|
|
<div class="stat-info">
|
|||
|
|
<div class="stat-value">{{ totalRewards }}</div>
|
|||
|
|
<div class="stat-label">总积分</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 任务分类 -->
|
|||
|
|
<div class="task-categories">
|
|||
|
|
<div class="category-tabs">
|
|||
|
|
<div
|
|||
|
|
v-for="category in categories"
|
|||
|
|
:key="category.key"
|
|||
|
|
:class="['category-tab', { active: activeCategory === category.key }]"
|
|||
|
|
@click="activeCategory = category.key"
|
|||
|
|
>
|
|||
|
|
<el-icon>{{ category.icon }}</el-icon>
|
|||
|
|
<span>{{ category.name }}</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 任务列表 -->
|
|||
|
|
<div class="task-list">
|
|||
|
|
<div
|
|||
|
|
v-for="task in filteredTasks"
|
|||
|
|
:key="task.id"
|
|||
|
|
:class="['task-item', {
|
|||
|
|
completed: task.status === 'completed',
|
|||
|
|
claimed: task.status === 'claimed'
|
|||
|
|
}]"
|
|||
|
|
>
|
|||
|
|
<div class="task-icon">
|
|||
|
|
<el-icon :size="24">{{ getTaskIcon(task.type) }}</el-icon>
|
|||
|
|
</div>
|
|||
|
|
<div class="task-content">
|
|||
|
|
<div class="task-title">{{ task.title }}</div>
|
|||
|
|
<div class="task-desc">{{ task.description }}</div>
|
|||
|
|
<div class="task-progress" v-if="task.progress !== undefined">
|
|||
|
|
<el-progress
|
|||
|
|
:percentage="(task.progress / task.target) * 100"
|
|||
|
|
:show-text="false"
|
|||
|
|
:stroke-width="4"
|
|||
|
|
/>
|
|||
|
|
<span class="progress-text">{{ task.progress }}/{{ task.target }}</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="task-reward">
|
|||
|
|
<div class="reward-points">+{{ task.points }}</div>
|
|||
|
|
<el-button
|
|||
|
|
v-if="task.status === 'pending'"
|
|||
|
|
type="primary"
|
|||
|
|
size="small"
|
|||
|
|
@click="doTask(task)"
|
|||
|
|
:loading="task.loading"
|
|||
|
|
>
|
|||
|
|
{{ getTaskButtonText(task) }}
|
|||
|
|
</el-button>
|
|||
|
|
<el-button
|
|||
|
|
v-else-if="task.status === 'completed'"
|
|||
|
|
type="success"
|
|||
|
|
size="small"
|
|||
|
|
@click="claimReward(task)"
|
|||
|
|
:loading="task.loading"
|
|||
|
|
>
|
|||
|
|
领取奖励
|
|||
|
|
</el-button>
|
|||
|
|
<el-tag
|
|||
|
|
v-else-if="task.status === 'claimed'"
|
|||
|
|
type="success"
|
|||
|
|
size="small"
|
|||
|
|
>
|
|||
|
|
已领取
|
|||
|
|
</el-tag>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 空状态 -->
|
|||
|
|
<div v-if="filteredTasks.length === 0" class="empty-state">
|
|||
|
|
<el-icon :size="60" color="#c0c4cc"><DocumentRemove /></el-icon>
|
|||
|
|
<p>暂无任务</p>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script setup>
|
|||
|
|
import { ref, reactive, computed, onMounted } from 'vue'
|
|||
|
|
import { useRouter } from 'vue-router'
|
|||
|
|
import { useUserStore } from '@/stores/user'
|
|||
|
|
import { ElMessage } from 'element-plus'
|
|||
|
|
import {
|
|||
|
|
ArrowLeft,
|
|||
|
|
Coin,
|
|||
|
|
Trophy,
|
|||
|
|
Clock,
|
|||
|
|
Star,
|
|||
|
|
Calendar,
|
|||
|
|
ShoppingCart,
|
|||
|
|
Share,
|
|||
|
|
User,
|
|||
|
|
CreditCard,
|
|||
|
|
Present,
|
|||
|
|
DocumentRemove
|
|||
|
|
} from '@element-plus/icons-vue'
|
|||
|
|
import api from '@/utils/api'
|
|||
|
|
|
|||
|
|
const router = useRouter()
|
|||
|
|
const userStore = useUserStore()
|
|||
|
|
|
|||
|
|
// 响应式数据
|
|||
|
|
const userPoints = ref(0)
|
|||
|
|
const activeCategory = ref('daily')
|
|||
|
|
const tasks = ref([])
|
|||
|
|
const loading = ref(false)
|
|||
|
|
|
|||
|
|
// 任务分类
|
|||
|
|
const categories = ref([
|
|||
|
|
{ key: 'daily', name: '每日任务', icon: Calendar },
|
|||
|
|
{ key: 'shopping', name: '购物任务', icon: ShoppingCart },
|
|||
|
|
{ key: 'social', name: '社交任务', icon: Share },
|
|||
|
|
{ key: 'profile', name: '完善资料', icon: User },
|
|||
|
|
{ key: 'special', name: '特殊任务', icon: Present }
|
|||
|
|
])
|
|||
|
|
|
|||
|
|
// 计算属性
|
|||
|
|
const filteredTasks = computed(() => {
|
|||
|
|
return tasks.value.filter(task => task.category === activeCategory.value)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
const completedTasks = computed(() => {
|
|||
|
|
return tasks.value.filter(task => task.status === 'claimed').length
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
const pendingTasks = computed(() => {
|
|||
|
|
return tasks.value.filter(task => task.status === 'pending' || task.status === 'completed').length
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
const totalRewards = computed(() => {
|
|||
|
|
return tasks.value
|
|||
|
|
.filter(task => task.status === 'claimed')
|
|||
|
|
.reduce((total, task) => total + task.points, 0)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 方法
|
|||
|
|
/**
|
|||
|
|
* 获取任务图标
|
|||
|
|
*/
|
|||
|
|
const getTaskIcon = (type) => {
|
|||
|
|
const iconMap = {
|
|||
|
|
purchase: ShoppingCart,
|
|||
|
|
share: Share,
|
|||
|
|
profile: User,
|
|||
|
|
transfer: CreditCard,
|
|||
|
|
invite: User,
|
|||
|
|
review: Star
|
|||
|
|
}
|
|||
|
|
return iconMap[type] || Present
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取任务按钮文本
|
|||
|
|
*/
|
|||
|
|
const getTaskButtonText = (task) => {
|
|||
|
|
const textMap = {
|
|||
|
|
purchase: '去购买',
|
|||
|
|
share: '去分享',
|
|||
|
|
profile: '去完善',
|
|||
|
|
transfer: '去转账',
|
|||
|
|
invite: '去邀请',
|
|||
|
|
review: '去评价'
|
|||
|
|
}
|
|||
|
|
return textMap[task.type] || '去完成'
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 执行任务
|
|||
|
|
*/
|
|||
|
|
const doTask = async (task) => {
|
|||
|
|
task.loading = true
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
switch (task.type) {
|
|||
|
|
case 'purchase':
|
|||
|
|
router.push('/shop')
|
|||
|
|
break
|
|||
|
|
case 'share':
|
|||
|
|
await shareApp()
|
|||
|
|
break
|
|||
|
|
case 'profile':
|
|||
|
|
router.push('/profile')
|
|||
|
|
break
|
|||
|
|
case 'transfer':
|
|||
|
|
router.push('/transfers')
|
|||
|
|
break
|
|||
|
|
case 'invite':
|
|||
|
|
await showInviteDialog()
|
|||
|
|
break
|
|||
|
|
case 'review':
|
|||
|
|
router.push('/orders')
|
|||
|
|
break
|
|||
|
|
default:
|
|||
|
|
// 直接完成任务
|
|||
|
|
await completeTask(task.id)
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
ElMessage.error('操作失败,请重试')
|
|||
|
|
} finally {
|
|||
|
|
task.loading = false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 分享应用
|
|||
|
|
*/
|
|||
|
|
const shareApp = async () => {
|
|||
|
|
if (navigator.share) {
|
|||
|
|
try {
|
|||
|
|
await navigator.share({
|
|||
|
|
title: '融互通 - 资金互助平台',
|
|||
|
|
text: '发现一个很棒的资金互助平台,快来看看吧!',
|
|||
|
|
url: window.location.origin
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 完成分享任务
|
|||
|
|
await completeTask('share_app')
|
|||
|
|
ElMessage.success('分享成功!')
|
|||
|
|
} catch (error) {
|
|||
|
|
if (error.name !== 'AbortError') {
|
|||
|
|
ElMessage.error('分享失败')
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// 复制链接到剪贴板
|
|||
|
|
try {
|
|||
|
|
await navigator.clipboard.writeText(window.location.origin)
|
|||
|
|
await completeTask('share_app')
|
|||
|
|
ElMessage.success('链接已复制到剪贴板!')
|
|||
|
|
} catch (error) {
|
|||
|
|
ElMessage.error('复制失败')
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 显示邀请对话框
|
|||
|
|
*/
|
|||
|
|
const showInviteDialog = async () => {
|
|||
|
|
// 这里可以实现邀请功能
|
|||
|
|
ElMessage.info('邀请功能开发中...')
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 完成任务
|
|||
|
|
*/
|
|||
|
|
const completeTask = async (taskId) => {
|
|||
|
|
try {
|
|||
|
|
const response = await api.post(`/tasks/${taskId}/complete`)
|
|||
|
|
|
|||
|
|
// 更新任务状态
|
|||
|
|
const task = tasks.value.find(t => t.id === taskId)
|
|||
|
|
if (task) {
|
|||
|
|
task.status = 'completed'
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ElMessage.success('任务完成!')
|
|||
|
|
} catch (error) {
|
|||
|
|
ElMessage.error('任务完成失败')
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 领取奖励
|
|||
|
|
*/
|
|||
|
|
const claimReward = async (task) => {
|
|||
|
|
task.loading = true
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const response = await api.post(`/tasks/${task.id}/claim`)
|
|||
|
|
|
|||
|
|
// 更新任务状态和用户积分
|
|||
|
|
task.status = 'claimed'
|
|||
|
|
userPoints.value += task.points
|
|||
|
|
|
|||
|
|
ElMessage.success(`获得 ${task.points} 积分!`)
|
|||
|
|
} catch (error) {
|
|||
|
|
ElMessage.error('领取失败,请重试')
|
|||
|
|
} finally {
|
|||
|
|
task.loading = false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取任务列表
|
|||
|
|
*/
|
|||
|
|
const getTasks = async () => {
|
|||
|
|
loading.value = true
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const response = await api.get('/tasks')
|
|||
|
|
tasks.value = response.data.map(task => ({
|
|||
|
|
...task,
|
|||
|
|
loading: false
|
|||
|
|
}))
|
|||
|
|
} catch (error) {
|
|||
|
|
// 如果API不存在,使用模拟数据
|
|||
|
|
tasks.value = [
|
|||
|
|
{
|
|||
|
|
id: 'first_purchase',
|
|||
|
|
category: 'shopping',
|
|||
|
|
type: 'purchase',
|
|||
|
|
title: '首次购买',
|
|||
|
|
description: '在积分商城完成首次购买',
|
|||
|
|
points: 50,
|
|||
|
|
status: 'pending',
|
|||
|
|
loading: false
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 'share_app',
|
|||
|
|
category: 'social',
|
|||
|
|
type: 'share',
|
|||
|
|
title: '分享应用',
|
|||
|
|
description: '分享应用给朋友',
|
|||
|
|
points: 20,
|
|||
|
|
status: 'pending',
|
|||
|
|
loading: false
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 'complete_profile',
|
|||
|
|
category: 'profile',
|
|||
|
|
type: 'profile',
|
|||
|
|
title: '完善个人资料',
|
|||
|
|
description: '完善头像、姓名等个人信息',
|
|||
|
|
points: 30,
|
|||
|
|
status: 'pending',
|
|||
|
|
progress: 2,
|
|||
|
|
target: 5,
|
|||
|
|
loading: false
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 'first_transfer',
|
|||
|
|
category: 'special',
|
|||
|
|
type: 'transfer',
|
|||
|
|
title: '首次转账',
|
|||
|
|
description: '完成首次转账操作',
|
|||
|
|
points: 100,
|
|||
|
|
status: 'completed',
|
|||
|
|
loading: false
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
} finally {
|
|||
|
|
loading.value = false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取用户积分
|
|||
|
|
*/
|
|||
|
|
const getUserPoints = async () => {
|
|||
|
|
try {
|
|||
|
|
const response = await api.get('/user/points')
|
|||
|
|
userPoints.value = response.data.points
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('获取用户积分失败:', error)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 生命周期
|
|||
|
|
onMounted(() => {
|
|||
|
|
getTasks()
|
|||
|
|
getUserPoints()
|
|||
|
|
})
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style scoped>
|
|||
|
|
.task-center {
|
|||
|
|
min-height: 100vh;
|
|||
|
|
background: #f5f7fa;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.navbar {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
padding: 0 16px;
|
|||
|
|
height: 56px;
|
|||
|
|
background: white;
|
|||
|
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.nav-left,
|
|||
|
|
.nav-right {
|
|||
|
|
flex: 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.nav-right {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: flex-end;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.back-btn,
|
|||
|
|
.points-btn {
|
|||
|
|
color: #333;
|
|||
|
|
font-size: 14px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.points-btn {
|
|||
|
|
color: #ff6b35;
|
|||
|
|
font-weight: bold;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.nav-title {
|
|||
|
|
margin: 0;
|
|||
|
|
font-size: 18px;
|
|||
|
|
font-weight: 500;
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.task-stats {
|
|||
|
|
padding: 16px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stats-card {
|
|||
|
|
background: white;
|
|||
|
|
border-radius: 12px;
|
|||
|
|
padding: 20px;
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-around;
|
|||
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stat-item {
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
align-items: center;
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stat-icon {
|
|||
|
|
width: 40px;
|
|||
|
|
height: 40px;
|
|||
|
|
border-radius: 50%;
|
|||
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
color: white;
|
|||
|
|
margin-bottom: 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stat-value {
|
|||
|
|
font-size: 20px;
|
|||
|
|
font-weight: bold;
|
|||
|
|
color: #333;
|
|||
|
|
margin-bottom: 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stat-label {
|
|||
|
|
font-size: 12px;
|
|||
|
|
color: #666;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.task-categories {
|
|||
|
|
padding: 0 16px 16px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.category-tabs {
|
|||
|
|
display: flex;
|
|||
|
|
background: white;
|
|||
|
|
border-radius: 12px;
|
|||
|
|
padding: 4px;
|
|||
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.category-tab {
|
|||
|
|
flex: 1;
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
align-items: center;
|
|||
|
|
padding: 12px 8px;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
transition: all 0.3s;
|
|||
|
|
font-size: 12px;
|
|||
|
|
color: #666;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.category-tab.active {
|
|||
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|||
|
|
color: white;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.category-tab .el-icon {
|
|||
|
|
margin-bottom: 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.task-list {
|
|||
|
|
padding: 0 16px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.task-item {
|
|||
|
|
background: white;
|
|||
|
|
border-radius: 12px;
|
|||
|
|
padding: 16px;
|
|||
|
|
margin-bottom: 12px;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|||
|
|
transition: all 0.3s;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.task-item.completed {
|
|||
|
|
border-left: 4px solid #67c23a;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.task-item.claimed {
|
|||
|
|
opacity: 0.6;
|
|||
|
|
border-left: 4px solid #909399;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.task-icon {
|
|||
|
|
width: 48px;
|
|||
|
|
height: 48px;
|
|||
|
|
border-radius: 50%;
|
|||
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
color: white;
|
|||
|
|
margin-right: 16px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.task-content {
|
|||
|
|
flex: 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.task-title {
|
|||
|
|
font-size: 16px;
|
|||
|
|
font-weight: 500;
|
|||
|
|
color: #333;
|
|||
|
|
margin-bottom: 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.task-desc {
|
|||
|
|
font-size: 14px;
|
|||
|
|
color: #666;
|
|||
|
|
margin-bottom: 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.task-progress {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.task-progress .el-progress {
|
|||
|
|
flex: 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.progress-text {
|
|||
|
|
font-size: 12px;
|
|||
|
|
color: #666;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.task-reward {
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.reward-points {
|
|||
|
|
font-size: 14px;
|
|||
|
|
font-weight: bold;
|
|||
|
|
color: #ff6b35;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.empty-state {
|
|||
|
|
text-align: center;
|
|||
|
|
padding: 60px 20px;
|
|||
|
|
color: #666;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.empty-state p {
|
|||
|
|
margin-top: 16px;
|
|||
|
|
font-size: 14px;
|
|||
|
|
}
|
|||
|
|
</style>
|