Files
yimo-resource-blog/pages/task/detail.vue

159 lines
10 KiB
Vue
Raw Normal View History

2026-09-16 16:49:25 +08:00
<template>
<view class="page" v-if="task.id">
<view class="summary-card">
<image v-if="task.coverUrl" class="cover" :src="resolveUrl(task.coverUrl)" mode="aspectFill"></image>
<view class="summary-main">
<view class="title">{{ task.taskName }}</view>
<view class="tags"><text class="tag">{{ task.cycleType === 'DAILY' ? '每日任务' : '单次任务' }}</text><text class="points">+{{ task.rewardPoints }}积分</text></view>
</view>
</view>
<view class="section">
<view class="section-title">任务说明</view>
<view class="paragraph">{{ task.description || '完成任务后上传相关截图,平台审核通过后积分自动到账。' }}</view>
</view>
<view v-if="task.operationGuide" class="section">
<view class="section-title">操作步骤</view>
<view class="paragraph guide">{{ task.operationGuide }}</view>
</view>
<view v-if="exampleImages.length" class="section">
<view class="section-title">正确截图示例</view>
<view class="image-grid"><image v-for="(url,index) in exampleImages" :key="url" class="example-image" :src="resolveUrl(url)" mode="aspectFill" @click="previewExamples(index)"></image></view>
</view>
<view v-if="submission" class="section status-section">
<view class="section-title">当前状态</view>
<view class="status-line"><text :class="['status-badge', submissionStatusClass]">{{ submissionStatusText }}</text><text class="submit-time">{{ submission.lastSubmitTime }}</text></view>
<view v-if="submission.rejectReason" class="reject-reason">{{ submission.status === 3 ? '撤销原因:' : '未通过原因:' }}{{ submission.rejectReason }}</view>
<view v-if="currentAttachments.length" class="old-images">
<image v-for="(item,index) in currentAttachments" :key="item.id" class="old-image" :src="item.previewUrl" mode="aspectFill" @click="previewOld(index)"></image>
</view>
</view>
<view v-if="task.canSubmit" class="section">
<view class="section-title">上传完成截图</view>
<view class="upload-tip">最多3张,单张不超过5MB;截图将保存到七牛云用于人工审核</view>
<view class="image-grid upload-grid">
<view v-for="(item,index) in uploads" :key="item.objectKey" class="upload-item">
<image class="upload-image" :src="item.localPath" mode="aspectFill" @click="previewUploads(index)"></image>
<text class="remove cuIcon-close" @click.stop="removeUpload(index)"></text>
</view>
<view v-if="uploads.length < 3" class="choose-image" @click="chooseImages"><text class="cuIcon-cameraadd"></text><view>上传截图</view></view>
</view>
<textarea v-model="remark" class="remark" maxlength="300" placeholder="可补充说明微信昵称、群名称等信息(选填)"></textarea>
</view>
<view class="bottom-space"></view>
<view class="bottom-bar">
<button v-if="task.canSubmit" class="submit-btn" :disabled="submitting || uploads.length === 0" @click="submit">{{ submitting ? '正在提交...' : (task.currentStatus === 2 ? '重新提交审核' : '提交审核') }}</button>
<button v-else class="submit-btn disabled" disabled>{{ buttonText }}</button>
</view>
</view>
</template>
<script>
import config from '@/config'
import { getPointTask, getMyPointTaskSubmission, submitPointTask, resubmitPointTask } from '@/api/app/index.js'
import { uploadPointTaskImage } from '@/utils/qiniu.js'
export default {
data() { return { id: null, selectedSubmissionId: null, task: {}, submission: null, uploads: [], remark: '', submitting: false } },
computed: {
exampleImages() { return String(this.task.exampleImages || '').split(',').map(v => v.trim()).filter(Boolean) },
currentAttachments() { return this.submission && this.submission.attachments ? this.submission.attachments.filter(v => v.isCurrent === 1) : [] },
submissionStatusText() { return this.submission ? ({ 0: '审核中', 1: '审核通过', 2: '审核未通过', 3: '奖励已撤销' }[this.submission.status] || '未知状态') : '' },
submissionStatusClass() { return this.submission ? ({ 0: 'pending', 1: 'approved', 2: 'rejected', 3: 'revoked' }[this.submission.status] || '') : '' },
buttonText() {
if (this.task.currentStatus === 0) return '审核中'
if (this.task.currentStatus === 1) return this.task.cycleType === 'DAILY' ? '今日已完成' : '任务已完成'
if (this.task.currentStatus === 3) return '奖励已撤销'
return '暂不可提交'
}
},
onLoad(options) { this.id = Number(options.id); this.selectedSubmissionId = options.submissionId ? Number(options.submissionId) : null; this.load() },
onPullDownRefresh() { this.load() },
methods: {
load() {
getPointTask(this.id).then(res => {
this.task = res.data || {}
this.submission = null
const submissionId = this.selectedSubmissionId || this.task.currentSubmissionId
if (submissionId) return getMyPointTaskSubmission(submissionId)
}).then(res => { if (res && res.data) { this.submission = res.data; this.remark = this.submission.remark || '' } }).finally(() => uni.stopPullDownRefresh())
},
resolveUrl(url) {
if (!url || /^(https?:)?\/\//i.test(url)) return url
return config.baseUrl + (url.charAt(0) === '/' ? url : '/' + url)
},
chooseImages() {
uni.chooseImage({ count: 3 - this.uploads.length, sizeType: ['compressed'], sourceType: ['album', 'camera'], success: async res => {
const files = res.tempFiles || (res.tempFilePaths || []).map(path => ({ path: path, size: 0 }))
for (let i = 0; i < files.length; i++) {
if (Number(files[i].size || 0) > 5 * 1024 * 1024) { uni.showToast({ title: '单张截图不能超过5MB', icon: 'none' }); continue }
try {
uni.showLoading({ title: '上传第' + (i + 1) + '张' })
const uploaded = await uploadPointTaskImage(files[i])
this.uploads.push(uploaded)
} catch (e) {
uni.showToast({ title: (e && e.message) || '截图上传失败', icon: 'none' })
} finally { uni.hideLoading() }
}
} })
},
removeUpload(index) { this.uploads.splice(index, 1) },
previewExamples(index) { uni.previewImage({ current: this.resolveUrl(this.exampleImages[index]), urls: this.exampleImages.map(this.resolveUrl) }) },
previewUploads(index) { uni.previewImage({ current: this.uploads[index].localPath, urls: this.uploads.map(v => v.localPath) }) },
previewOld(index) { uni.previewImage({ current: this.currentAttachments[index].previewUrl, urls: this.currentAttachments.map(v => v.previewUrl) }) },
submit() {
if (!this.uploads.length || this.submitting) return
this.submitting = true
const data = { attachmentKeys: this.uploads.map(v => v.objectKey), remark: String(this.remark || '').trim() }
const action = this.task.currentStatus === 2 && this.task.currentSubmissionId ? resubmitPointTask(this.task.currentSubmissionId, data) : submitPointTask(this.task.id, data)
action.then(() => {
uni.showModal({ title: '提交成功', content: '截图已提交,请耐心等待后台审核。', showCancel: false })
this.uploads = []
this.load()
}).finally(() => { this.submitting = false })
}
}
}
</script>
<style scoped>
.page { min-height: 100vh; padding: 26rpx; box-sizing: border-box; background: #f5f7fb; }
.summary-card, .section { margin-bottom: 22rpx; padding: 28rpx; border-radius: 22rpx; background: #fff; box-shadow: 0 8rpx 24rpx rgba(34, 48, 79, .05); }
.summary-card { display: flex; align-items: center; }
.cover { width: 132rpx; height: 132rpx; flex-shrink: 0; border-radius: 18rpx; background: #eef2fa; }
.summary-main { flex: 1; min-width: 0; margin-left: 24rpx; }
.title { font-size: 35rpx; font-weight: 700; color: #242933; }
.tags { display: flex; align-items: center; margin-top: 20rpx; }
.tag { padding: 6rpx 15rpx; border-radius: 16rpx; color: #5576e9; background: #edf1ff; font-size: 22rpx; }
.points { margin-left: 16rpx; color: #ff8721; font-size: 29rpx; font-weight: 700; }
.section-title { margin-bottom: 20rpx; color: #282d36; font-size: 30rpx; font-weight: 650; }
.paragraph { color: #626a76; font-size: 26rpx; line-height: 42rpx; }
.guide { white-space: pre-wrap; }
.image-grid { display: flex; flex-wrap: wrap; gap: 16rpx; }
.example-image, .upload-item, .choose-image { width: 190rpx; height: 190rpx; border-radius: 14rpx; }
.example-image, .upload-image { background: #edf0f5; }
.upload-item { position: relative; overflow: hidden; }
.upload-image { width: 100%; height: 100%; }
.remove { position: absolute; top: 8rpx; right: 8rpx; width: 38rpx; height: 38rpx; border-radius: 50%; color: #fff; background: rgba(0,0,0,.58); text-align: center; line-height: 38rpx; font-size: 24rpx; }
.choose-image { display: flex; flex-direction: column; align-items: center; justify-content: center; border: 2rpx dashed #cbd2df; color: #8790a0; font-size: 23rpx; }
.choose-image .cuIcon-cameraadd { margin-bottom: 10rpx; font-size: 52rpx; color: #667ff1; }
.upload-tip { margin: -5rpx 0 22rpx; color: #9ba2ae; font-size: 22rpx; line-height: 34rpx; }
.remark { width: 100%; height: 150rpx; margin-top: 24rpx; padding: 20rpx; box-sizing: border-box; border-radius: 14rpx; background: #f7f8fb; font-size: 25rpx; }
.status-line { display: flex; align-items: center; justify-content: space-between; }
.status-badge { padding: 8rpx 18rpx; border-radius: 18rpx; font-size: 24rpx; }
.status-badge.pending { color: #df8b16; background: #fff3df; }
.status-badge.approved { color: #24925d; background: #e8f7ef; }
.status-badge.rejected { color: #dc5151; background: #ffeded; }
.status-badge.revoked { color: #777f8c; background: #eff0f3; }
.submit-time { color: #9da4af; font-size: 22rpx; }
.reject-reason { margin-top: 20rpx; padding: 18rpx; border-radius: 12rpx; color: #d65050; background: #fff5f5; font-size: 24rpx; line-height: 36rpx; }
.old-images { display: flex; flex-wrap: wrap; gap: 15rpx; margin-top: 22rpx; }
.old-image { width: 150rpx; height: 150rpx; border-radius: 12rpx; }
.bottom-space { height: 130rpx; }
.bottom-bar { position: fixed; z-index: 10; left: 0; right: 0; bottom: 0; padding: 18rpx 28rpx calc(18rpx + env(safe-area-inset-bottom)); background: #fff; box-shadow: 0 -7rpx 25rpx rgba(20, 35, 66, .08); }
.submit-btn { height: 84rpx; border: 0; border-radius: 42rpx; color: #fff; background: linear-gradient(90deg, #567cf4, #735ff1); font-size: 29rpx; line-height: 84rpx; }
.submit-btn.disabled { color: #969da8; background: #e7e9ee; }
</style>