|
@@ -40,22 +40,30 @@
|
|
|
|
|
|
|
|
<!-- 今日任务 -->
|
|
<!-- 今日任务 -->
|
|
|
<view class="task-list" v-if="activeTab === 'today'">
|
|
<view class="task-list" v-if="activeTab === 'today'">
|
|
|
- <view class="task-item" v-for="task in todayTasks" :key="task.id" :class="{ completed: task.status === 'completed' }">
|
|
|
|
|
|
|
+ <view class="task-item" v-for="task in todayTasks" :key="task.id" :class="{ completed: task.status === 'completed', locked: task.status === 'locked' }">
|
|
|
<view class="task-left">
|
|
<view class="task-left">
|
|
|
<view class="task-title">
|
|
<view class="task-title">
|
|
|
{{ task.title }}
|
|
{{ task.title }}
|
|
|
<text v-if="task.memberOnly === 1" class="member-badge">会员任务</text>
|
|
<text v-if="task.memberOnly === 1" class="member-badge">会员任务</text>
|
|
|
<text v-if="task.category === '小游戏类'" class="minigame-badge">🎮</text>
|
|
<text v-if="task.category === '小游戏类'" class="minigame-badge">🎮</text>
|
|
|
|
|
+ <text v-if="task.actionType === 'cart' || task.actionType === 'order'" class="minigame-badge">🛒</text>
|
|
|
|
|
+ <text v-if="task.actionType === 'focus'" class="minigame-badge">⏱️</text>
|
|
|
|
|
+ <text v-if="task.actionType === 'survey'" class="minigame-badge">📝</text>
|
|
|
|
|
+ <text v-if="task.actionType === 'article'" class="minigame-badge">📖</text>
|
|
|
|
|
+ <text v-if="task.actionType === 'activity'" class="minigame-badge">🎯</text>
|
|
|
|
|
+ <text v-if="task.requireInput === 1" class="minigame-badge">✍️</text>
|
|
|
</view>
|
|
</view>
|
|
|
<view class="task-meta">
|
|
<view class="task-meta">
|
|
|
<text class="points">+{{ task.points }}分</text>
|
|
<text class="points">+{{ task.points }}分</text>
|
|
|
<text class="deadline">截止 {{ formatTime(task.deadline) }}</text>
|
|
<text class="deadline">截止 {{ formatTime(task.deadline) }}</text>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
+ <text v-if="task.status === 'locked'" class="lock-tip">🔒 需先完成前置任务后解锁</text>
|
|
|
</view>
|
|
</view>
|
|
|
- <view class="task-right" v-if="task.status === 'pending'">
|
|
|
|
|
- <button class="btn-complete" @click="completeTask(task)">完成</button>
|
|
|
|
|
|
|
+ <view class="task-right" v-if="task.status === 'pending' || task.status === 'in_progress'">
|
|
|
|
|
+ <button class="btn-complete" @click="handleTaskAction(task)">{{ getActionLabel(task) }}</button>
|
|
|
</view>
|
|
</view>
|
|
|
- <view class="completed-badge" v-else>已完成</view>
|
|
|
|
|
|
|
+ <view class="completed-badge" v-else-if="task.status === 'completed'">已完成</view>
|
|
|
|
|
+ <view class="lock-badge" v-else>🔒 未解锁</view>
|
|
|
</view>
|
|
</view>
|
|
|
<view class="empty" v-if="!loading && todayTasks.length === 0">
|
|
<view class="empty" v-if="!loading && todayTasks.length === 0">
|
|
|
<text>暂无今日任务</text>
|
|
<text>暂无今日任务</text>
|
|
@@ -101,6 +109,26 @@
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
|
|
+ <!-- 填写式完成弹窗 -->
|
|
|
|
|
+ <view class="modal-mask" v-if="showInputModal" @click="closeInputModal">
|
|
|
|
|
+ <view class="modal modal-large" @click.stop>
|
|
|
|
|
+ <view class="modal-title">完成任务</view>
|
|
|
|
|
+ <view class="modal-content">
|
|
|
|
|
+ <text class="input-tip">{{ activeInputTask && activeInputTask.title }}</text>
|
|
|
|
|
+ <textarea
|
|
|
|
|
+ v-model="inputContent"
|
|
|
|
|
+ class="input-textarea"
|
|
|
|
|
+ placeholder="请填写完成内容(如:今天完成了...)"
|
|
|
|
|
+ :maxlength="500"
|
|
|
|
|
+ ></textarea>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="modal-btns">
|
|
|
|
|
+ <button @click="closeInputModal">取消</button>
|
|
|
|
|
+ <button class="btn-primary" :disabled="!inputContent" @click="submitWithInput">提交完成</button>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
<!-- 创建任务弹窗 -->
|
|
<!-- 创建任务弹窗 -->
|
|
|
<view class="modal-mask" v-if="showAddModal" @click="showAddModal = false">
|
|
<view class="modal-mask" v-if="showAddModal" @click="showAddModal = false">
|
|
|
<view class="modal modal-large" @click.stop>
|
|
<view class="modal modal-large" @click.stop>
|
|
@@ -155,7 +183,7 @@
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
|
<script>
|
|
|
-import { getMyPendingTasks, acceptTask as acceptTaskApi, rejectTask as rejectTaskApi, getTodayTasks, completeTask as completeTaskApi, createTask as createTaskApi, getFamilyMemberList, getMyMembership } from '../../utils/api.js'
|
|
|
|
|
|
|
+import { getMyPendingTasks, acceptTask as acceptTaskApi, rejectTask as rejectTaskApi, getTodayTasks, completeTask as completeTaskApi, startTask as startTaskApi, createTask as createTaskApi, getFamilyMemberList, getMyMembership } from '../../utils/api.js'
|
|
|
import { parseDate } from '../../utils/format.js'
|
|
import { parseDate } from '../../utils/format.js'
|
|
|
|
|
|
|
|
export default {
|
|
export default {
|
|
@@ -174,6 +202,9 @@ export default {
|
|
|
showAddModal: false,
|
|
showAddModal: false,
|
|
|
showRejectModalFlag: false,
|
|
showRejectModalFlag: false,
|
|
|
rejectTaskId: null,
|
|
rejectTaskId: null,
|
|
|
|
|
+ showInputModal: false,
|
|
|
|
|
+ activeInputTask: null,
|
|
|
|
|
+ inputContent: '',
|
|
|
newTask: {
|
|
newTask: {
|
|
|
title: '',
|
|
title: '',
|
|
|
description: '',
|
|
description: '',
|
|
@@ -266,6 +297,98 @@ export default {
|
|
|
console.error('完成任务失败', e)
|
|
console.error('完成任务失败', e)
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
|
|
+ /** 按钮文案:按 action_type / start_required / require_input 分派 */
|
|
|
|
|
+ getActionLabel(task) {
|
|
|
|
|
+ if (task.startRequired === 1 && task.status !== 'in_progress') return '开始'
|
|
|
|
|
+ if (task.requireInput === 1) return '填写完成'
|
|
|
|
|
+ var at = task.actionType
|
|
|
|
|
+ if (at === 'minigame') return '开始游戏'
|
|
|
|
|
+ if (at === 'focus') return '开始专注'
|
|
|
|
|
+ if (at === 'survey') return '去填写'
|
|
|
|
|
+ if (at === 'article') return '去阅读'
|
|
|
|
|
+ if (at === 'activity') return '去报名'
|
|
|
|
|
+ if (at === 'cart' || at === 'order') return '去购物'
|
|
|
|
|
+ return '完成'
|
|
|
|
|
+ },
|
|
|
|
|
+ /** 统一任务动作入口:开始/联动跳转/填写/直接完成 */
|
|
|
|
|
+ handleTaskAction(task) {
|
|
|
|
|
+ var self = this
|
|
|
|
|
+ // 前置锁定任务不可操作
|
|
|
|
|
+ if (task.status === 'locked') {
|
|
|
|
|
+ uni.showToast({ title: '需先完成前置任务', icon: 'none' })
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ // 两阶段:先开始,再按 action_type 跳转联动页面
|
|
|
|
|
+ if (task.startRequired === 1 && task.status !== 'in_progress') {
|
|
|
|
|
+ uni.showLoading({ title: '开始中...' })
|
|
|
|
|
+ startTaskApi(task.id, this.currentMemberId).then(function(res) {
|
|
|
|
|
+ uni.hideLoading()
|
|
|
|
|
+ if (res && res.code === 200 && res.data) {
|
|
|
|
|
+ uni.showToast({ title: '已开始', icon: 'success' })
|
|
|
|
|
+ self.navigateByAction(task)
|
|
|
|
|
+ self.loadTodayTasks()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ uni.showToast({ title: (res && res.message) || '开始失败', icon: 'none' })
|
|
|
|
|
+ }
|
|
|
|
|
+ }).catch(function() {
|
|
|
|
|
+ uni.hideLoading()
|
|
|
|
|
+ uni.showToast({ title: '开始失败', icon: 'none' })
|
|
|
|
|
+ })
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ // 填写式完成
|
|
|
|
|
+ if (task.requireInput === 1) {
|
|
|
|
|
+ this.activeInputTask = task
|
|
|
|
|
+ this.inputContent = ''
|
|
|
|
|
+ this.showInputModal = true
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ // 已开始的两阶段任务或普通任务:直接完成
|
|
|
|
|
+ this.completeTask(task)
|
|
|
|
|
+ },
|
|
|
|
|
+ /** 按 action_type 跳转联动页面 */
|
|
|
|
|
+ navigateByAction(task) {
|
|
|
|
|
+ var at = task.actionType
|
|
|
|
|
+ var taskId = task.id
|
|
|
|
|
+ if (at === 'minigame') {
|
|
|
|
|
+ var code = 'schulte'
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (task.actionConfig) {
|
|
|
|
|
+ var cfg = JSON.parse(task.actionConfig)
|
|
|
|
|
+ if (cfg.gameCode) code = cfg.gameCode
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (e) {}
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/games/' + code + '?taskId=' + taskId })
|
|
|
|
|
+ } else if (at === 'focus') {
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/focus/index?taskId=' + taskId })
|
|
|
|
|
+ } else if (at === 'survey') {
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/assessment/survey-questionnaire?taskId=' + taskId })
|
|
|
|
|
+ } else if (at === 'article') {
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/article-center/article-detail?taskId=' + taskId })
|
|
|
|
|
+ } else if (at === 'activity') {
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/activity/activity-list?taskId=' + taskId })
|
|
|
|
|
+ } else if (at === 'cart' || at === 'order') {
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/shop/cart?taskId=' + taskId })
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ closeInputModal() {
|
|
|
|
|
+ this.showInputModal = false
|
|
|
|
|
+ this.activeInputTask = null
|
|
|
|
|
+ this.inputContent = ''
|
|
|
|
|
+ },
|
|
|
|
|
+ async submitWithInput() {
|
|
|
|
|
+ var task = this.activeInputTask
|
|
|
|
|
+ if (!task || !this.inputContent) return
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await completeTaskApi(task.id, this.currentMemberId, '', this.inputContent, 'text')
|
|
|
|
|
+ uni.showToast({ title: `获得 ${res.data.pointsEarned} 积分`, icon: 'success' })
|
|
|
|
|
+ this.closeInputModal()
|
|
|
|
|
+ this.loadTodayTasks()
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error('提交填写内容失败', e)
|
|
|
|
|
+ uni.showToast({ title: '提交失败', icon: 'none' })
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
acceptTask(task) {
|
|
acceptTask(task) {
|
|
|
if (task.memberOnly === 1 && !this.isMember) {
|
|
if (task.memberOnly === 1 && !this.isMember) {
|
|
|
uni.showModal({
|
|
uni.showModal({
|
|
@@ -556,6 +679,39 @@ export default {
|
|
|
font-size: 26rpx;
|
|
font-size: 26rpx;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+.lock-badge {
|
|
|
|
|
+ color: #B0B0B0;
|
|
|
|
|
+ font-size: 26rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.lock-tip {
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ margin-top: 8rpx;
|
|
|
|
|
+ font-size: 22rpx;
|
|
|
|
|
+ color: #B0B0B0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.task-item.locked {
|
|
|
|
|
+ opacity: 0.6;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.input-tip {
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ font-size: 26rpx;
|
|
|
|
|
+ color: #666;
|
|
|
|
|
+ margin-bottom: 16rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.input-textarea {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: 240rpx;
|
|
|
|
|
+ background: #F7F7F7;
|
|
|
|
|
+ border-radius: 16rpx;
|
|
|
|
|
+ padding: 20rpx;
|
|
|
|
|
+ font-size: 26rpx;
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
.minigame-badge {
|
|
.minigame-badge {
|
|
|
font-size: 28rpx;
|
|
font-size: 28rpx;
|
|
|
margin-left: 10rpx;
|
|
margin-left: 10rpx;
|