271 lines
5.7 KiB
Plaintext
271 lines
5.7 KiB
Plaintext
<template>
|
|
<view class="page">
|
|
<view class="-status-bar"></view>
|
|
<view class="header">
|
|
<view class="coins-card">
|
|
<uni-icons type="wallet-filled" size="36" color="#FFB300"></uni-icons>
|
|
<view class="coins-info">
|
|
<text class="coins-num">{{ coins }}</text>
|
|
<text class="coins-label">我的金币</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<scroll-view direction="vertical" :show-scrollbar="false" style="flex:1;">
|
|
<view class="task-item" v-for="(item, idx) in tasks" :key="item.id">
|
|
<view class="task-icon">
|
|
<uni-icons :type="item.icon" size="30" color="#fff"></uni-icons>
|
|
</view>
|
|
<view class="task-main">
|
|
<text class="task-title">{{ item.title }}</text>
|
|
<text class="task-desc">{{ item.desc }}</text>
|
|
<view class="task-progress" v-if="item.status != 2">
|
|
<view class="bar">
|
|
<view class="bar-inner" :style="{ width: progressWidth(item) }"></view>
|
|
</view>
|
|
<text class="bar-text">{{ item.current }}/{{ item.target }}</text>
|
|
</view>
|
|
</view>
|
|
<view class="task-reward">+{{ item.reward }}</view>
|
|
<button class="task-btn" :class="btnClass(item.status)" :disabled="item.status == 2"
|
|
@click="onReceive(item)">
|
|
{{ btnText(item.status) }}
|
|
</button>
|
|
</view>
|
|
|
|
<view v-if="tasks.length === 0 && !loading" class="empty">
|
|
<text class="empty-text">暂无任务</text>
|
|
</view>
|
|
</scroll-view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup lang="uts">
|
|
import Api from '@/common/api-service.uts'
|
|
|
|
type ITask = {
|
|
id : number
|
|
title : string
|
|
desc : string
|
|
icon : string
|
|
reward : number
|
|
target : number
|
|
current : number
|
|
status : number
|
|
}
|
|
|
|
const coins = ref<number>(0)
|
|
const loading = ref<boolean>(false)
|
|
const tasks = reactive<ITask[]>([])
|
|
|
|
const btnText = (status : number) : string => {
|
|
if (status == 2) return '已领取'
|
|
if (status == 1) return '领取'
|
|
return '去完成'
|
|
}
|
|
const btnClass = (status : number) : string => {
|
|
if (status == 2) return 'done'
|
|
if (status == 1) return 'ready'
|
|
return 'todo'
|
|
}
|
|
const progressWidth = (item : ITask) : string => {
|
|
const pct = item.target > 0 ? (item.current / item.target * 100) : 0
|
|
return Math.min(100, pct) + '%'
|
|
}
|
|
|
|
function loadData() {
|
|
if (loading.value) return
|
|
loading.value = true
|
|
Api.task.list()
|
|
.then((res : UTSJSONObject) => {
|
|
coins.value = (res.get('coins') as number) ?? 0
|
|
const list = (res.get('list') as Array<ITask>) ?? []
|
|
tasks.splice(0, tasks.length)
|
|
list.forEach((t : ITask) => tasks.push(t))
|
|
})
|
|
.catch(() => {
|
|
uni.showToast({ title: '加载失败', icon: 'none' })
|
|
})
|
|
.finally(() => { loading.value = false })
|
|
}
|
|
|
|
function onReceive(item : ITask) {
|
|
if (item.status == 2) return
|
|
if (item.status == 0) {
|
|
uni.showToast({ title: '任务未完成', icon: 'none' })
|
|
return
|
|
}
|
|
uni.showLoading({ title: '领取中...', mask: true })
|
|
Api.task.receive(item.id)
|
|
.then((res : UTSJSONObject) => {
|
|
item.status = 2
|
|
coins.value = (res.get('coins_left') as number) ?? coins.value
|
|
uni.hideLoading()
|
|
uni.showToast({ title: '领取成功 +' + item.reward + '金币', icon: 'none' })
|
|
})
|
|
.catch((e : any) => {
|
|
uni.hideLoading()
|
|
const msg = (e as UTSJSONObject)?.get('message') as string
|
|
uni.showToast({ title: msg ?? '领取失败', icon: 'none' })
|
|
})
|
|
}
|
|
|
|
onMounted(() => { loadData() })
|
|
onShow(() => { loadData() })
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
.page {
|
|
flex: 1;
|
|
background-color: #f7f7f7;
|
|
}
|
|
|
|
.-status-bar {
|
|
height: var(--status-bar-height);
|
|
background: linear-gradient(135deg, #FF6B6B 0%, #FF8E53 100%);
|
|
}
|
|
|
|
.header {
|
|
background: linear-gradient(135deg, #FF6B6B 0%, #FF8E53 100%);
|
|
padding-bottom: 30rpx;
|
|
}
|
|
|
|
.coins-card {
|
|
display: flex;
|
|
flex-flow: row nowrap;
|
|
align-items: center;
|
|
margin: 24rpx 30rpx 0;
|
|
padding: 30rpx;
|
|
background-color: rgba(255, 255, 255, 0.18);
|
|
border-radius: 20rpx;
|
|
}
|
|
|
|
.coins-info {
|
|
display: flex;
|
|
flex-direction: column;
|
|
margin-left: 20rpx;
|
|
}
|
|
|
|
.coins-num {
|
|
font-size: 44rpx;
|
|
font-weight: bold;
|
|
color: #fff;
|
|
}
|
|
|
|
.coins-label {
|
|
font-size: 24rpx;
|
|
color: #ffe;
|
|
margin-top: 4rpx;
|
|
}
|
|
|
|
.task-item {
|
|
display: flex;
|
|
flex-flow: row nowrap;
|
|
align-items: center;
|
|
margin: 20rpx 30rpx 0;
|
|
padding: 28rpx 24rpx;
|
|
background-color: #fff;
|
|
border-radius: 18rpx;
|
|
box-shadow: 0 6rpx 18rpx rgba(0, 0, 0, 0.04);
|
|
}
|
|
|
|
.task-icon {
|
|
width: 84rpx;
|
|
height: 84rpx;
|
|
border-radius: 20rpx;
|
|
background: linear-gradient(135deg, #FF8E53 0%, #FF6B6B 100%);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-right: 20rpx;
|
|
}
|
|
|
|
.task-main {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.task-title {
|
|
font-size: 30rpx;
|
|
font-weight: 600;
|
|
color: #333;
|
|
}
|
|
|
|
.task-desc {
|
|
font-size: 22rpx;
|
|
color: #999;
|
|
margin-top: 4rpx;
|
|
}
|
|
|
|
.task-progress {
|
|
display: flex;
|
|
flex-flow: row nowrap;
|
|
align-items: center;
|
|
margin-top: 10rpx;
|
|
}
|
|
|
|
.bar {
|
|
flex: 1;
|
|
height: 12rpx;
|
|
border-radius: 6rpx;
|
|
background-color: #f0f0f0;
|
|
overflow: hidden;
|
|
margin-right: 12rpx;
|
|
}
|
|
|
|
.bar-inner {
|
|
height: 100%;
|
|
background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%);
|
|
border-radius: 6rpx;
|
|
}
|
|
|
|
.bar-text {
|
|
font-size: 20rpx;
|
|
color: #bbb;
|
|
}
|
|
|
|
.task-reward {
|
|
font-size: 26rpx;
|
|
font-weight: bold;
|
|
color: #FF9500;
|
|
margin-right: 16rpx;
|
|
}
|
|
|
|
.task-btn {
|
|
height: 60rpx;
|
|
padding: 0 28rpx;
|
|
line-height: 60rpx;
|
|
border-radius: 30rpx;
|
|
font-size: 26rpx;
|
|
border: none;
|
|
}
|
|
|
|
.task-btn.ready {
|
|
background: linear-gradient(135deg, #FF6B6B 0%, #FF8E53 100%);
|
|
color: #fff;
|
|
}
|
|
|
|
.task-btn.todo {
|
|
background-color: #f0f0f0;
|
|
color: #999;
|
|
}
|
|
|
|
.task-btn.done {
|
|
background-color: #f0f0f0;
|
|
color: #ccc;
|
|
}
|
|
|
|
.empty {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 120rpx 0;
|
|
}
|
|
|
|
.empty-text {
|
|
font-size: 28rpx;
|
|
color: #bbb;
|
|
}
|
|
</style>
|