544 lines
14 KiB
Vue
544 lines
14 KiB
Vue
<script setup lang="ts">
|
||
import ShowRewards from './components/show-rewards.vue'
|
||
import RewardDia from './components/RewardDia.vue'
|
||
import TaskList from './components/task-list.vue'
|
||
import DynamicList from './components/dynamic-list.vue'
|
||
import { useSound } from '@/hooks'
|
||
import taskApi from '@/api/task'
|
||
import router from '@/router'
|
||
import { storeToRefs } from 'pinia'
|
||
import { taskStore, userStore } from '@/stores'
|
||
import hud from '@/utils/hud'
|
||
import {
|
||
task_type_day,
|
||
task_type_week,
|
||
task_type_achievement,
|
||
task_type_parent,
|
||
task_status_available,
|
||
task_status_claimed,
|
||
task_type_lead,
|
||
task_status_ing,
|
||
task_id_word_test,
|
||
task_category_word_review,
|
||
task_type_newbie,
|
||
} from '.'
|
||
import { taskService } from '@/api/service/task'
|
||
import { useLeadTaskStore } from '@/stores/lead-task'
|
||
import obj from '@/utils/obj'
|
||
import { only_last_id } from '@/api/only-last'
|
||
|
||
const OSS_URL = import.meta.env.VITE_OSS_URL
|
||
|
||
const useTaskStore = taskStore()
|
||
const { status } = storeToRefs(useTaskStore)
|
||
const { setStatus } = useTaskStore
|
||
|
||
const loading = ref(false) // 加载loading
|
||
const showBigDia = ref(false) // 查看奖励弹框
|
||
const showItem = ref<anyObj[]>([]) // 查看奖品数据
|
||
const showRewardDia = ref(false) // 领取奖励弹框
|
||
const rewardList = ref<anyObj[]>([]) // 领取奖励数据
|
||
const isWordListReward = ref(false) // dia弹框是打开的任务奖励 还是活跃度奖励
|
||
|
||
const { userInfo } = storeToRefs(userStore())
|
||
const activeType = ref('')
|
||
|
||
const { taskList } = storeToRefs(useLeadTaskStore())
|
||
|
||
const classList = computed(() => {
|
||
let list = [] as any
|
||
if (status.value.newbieStatus) {
|
||
list.push({
|
||
id: task_type_newbie,
|
||
name: '新手',
|
||
warning: status.value.newbieStatus,
|
||
})
|
||
}
|
||
// 如果是校内学员
|
||
if (userInfo.value.isTaskStudent) {
|
||
list.push({
|
||
id: task_type_lead,
|
||
name: '导学',
|
||
warning: status.value.leadStatus,
|
||
})
|
||
}
|
||
list = list.concat([
|
||
{
|
||
id: task_type_day,
|
||
name: '日常',
|
||
warning: status.value.dailyStatus,
|
||
},
|
||
// {
|
||
// id: task_type_week,
|
||
// name: '周常',
|
||
// warning: status.value.weekStatus,
|
||
// },
|
||
{
|
||
id: task_type_achievement,
|
||
name: '成就',
|
||
warning: status.value.archiveStatus,
|
||
},
|
||
{
|
||
id: task_type_parent,
|
||
name: '家长',
|
||
warning: status.value.parentStatus,
|
||
},
|
||
])
|
||
return list
|
||
})
|
||
|
||
const oneKeyBg = `url(${OSS_URL}/urm/task_one_key.svg) 100%/cover no-repeat`
|
||
|
||
const taskData = ref<{
|
||
dynamicCount: number
|
||
taskDynamicVoList: any[]
|
||
taskList: anyObj[]
|
||
}>({
|
||
dynamicCount: 0, // 活跃度/成就点数
|
||
taskDynamicVoList: [], // 任务奖励列表
|
||
taskList: [], // 任务列表
|
||
})
|
||
|
||
const showAllReward = computed(() => {
|
||
let flag = false
|
||
flag = taskData.value.taskDynamicVoList?.some(item => item.status === task_status_available)
|
||
if (!flag) {
|
||
flag = taskData.value.taskList.some(item => item.status === task_status_available)
|
||
}
|
||
return flag
|
||
})
|
||
|
||
// 是否显示导学的【任务记录】按钮
|
||
const showLeadRecords = computed(() => {
|
||
return activeType.value === task_type_lead
|
||
})
|
||
|
||
function init(showLoading = false) {
|
||
loading.value = showLoading
|
||
let fn = taskApi.getUserDailyTask
|
||
switch (activeType.value) {
|
||
case task_type_lead:
|
||
// 获取日常任务
|
||
fn = taskService.getProcessingTasks
|
||
break
|
||
case task_type_week:
|
||
// 获取周长任务
|
||
fn = taskApi.getUserWeekTask
|
||
break
|
||
case task_type_achievement:
|
||
// 获取成就任务
|
||
fn = taskApi.getUserAchievementTask
|
||
break
|
||
case task_type_parent:
|
||
// 获取家长任务
|
||
fn = taskApi.getParentOrTeacherTask
|
||
break
|
||
case task_type_newbie:
|
||
// 获取新手任务
|
||
fn = taskApi.getNewbieTask
|
||
break
|
||
}
|
||
|
||
fn({ [only_last_id]: 'task-list' })
|
||
.then(data => {
|
||
clearData()
|
||
|
||
// 日常任务、周常任务进行筛选同类任务,只展示第一个
|
||
if (activeType.value === task_type_day || activeType.value === task_type_week) {
|
||
data.taskList = data.taskList || []
|
||
if (activeType.value === task_type_day) {
|
||
// 每日任务,增加一个单词测评
|
||
data.taskList.push({
|
||
taskId: task_id_word_test,
|
||
taskType: task_type_day,
|
||
status: task_status_ing,
|
||
name: '单词测评',
|
||
display: true,
|
||
rewardList: [
|
||
{
|
||
rewardId: null,
|
||
rewardType: 'currency',
|
||
rewardCode: null,
|
||
rewardCount: '',
|
||
propertyVo: null,
|
||
clothingVo: null,
|
||
personalityVo: null,
|
||
userRewardMappingVo: null,
|
||
showUser: null,
|
||
userHaveCount: null,
|
||
},
|
||
],
|
||
})
|
||
// 如果是单词复习,显示明显一点的背景
|
||
for (const item of data.taskList) {
|
||
if (item.taskCategory === task_category_word_review) {
|
||
item.bg = 'kelingqu'
|
||
break
|
||
}
|
||
}
|
||
}
|
||
data.taskList = filterFn(
|
||
data.taskList.sort((a: anyObj, b: anyObj) => a.taskId - b.taskId),
|
||
).sort(sortData)
|
||
}
|
||
|
||
if (activeType.value === task_type_parent) {
|
||
// 家长悬赏
|
||
taskData.value.taskList = data
|
||
}
|
||
|
||
for (const key in taskData.value) {
|
||
if (activeType.value !== task_type_parent) {
|
||
// 其他任务
|
||
;(taskData.value as any)[key] = data[key]
|
||
}
|
||
}
|
||
if (activeType.value === task_type_achievement) {
|
||
// 成就任务
|
||
taskData.value.taskList = filterFn(taskData.value.taskList)
|
||
taskData.value.taskList.sort(sortData)
|
||
} else if (activeType.value === task_type_lead) {
|
||
// taskData.value.taskList.sort(sortData)
|
||
// 导学任务
|
||
taskList.value = taskData.value.taskList as any
|
||
for (const task of taskData.value.taskList) {
|
||
try {
|
||
task.config = JSON.parse(task.config)
|
||
} catch {
|
||
task.config = {}
|
||
}
|
||
}
|
||
|
||
if (taskList.value.length) {
|
||
// 设置任务的完成优先级
|
||
let maxPriority = -1
|
||
// 优先完成的任务,索引
|
||
let firstTaskIdx = -1
|
||
// 优先完成的任务,插入的位置
|
||
let firstInsertIdx = -1
|
||
for (let i = 0; i < taskList.value.length; i++) {
|
||
const task = taskList.value[i]
|
||
if (task.status !== task_status_ing) {
|
||
firstInsertIdx = i + 1
|
||
continue
|
||
}
|
||
task.first = false
|
||
if (task.priority > maxPriority) {
|
||
maxPriority = task.priority
|
||
firstTaskIdx = i
|
||
}
|
||
}
|
||
if (firstTaskIdx !== -1) {
|
||
// 如果找到是第一个任务
|
||
const firstItem = taskList.value[firstTaskIdx]
|
||
// 跟firstItem同一级别的,都设置为first = true
|
||
for (const task of taskList.value) {
|
||
if (task.status !== task_status_ing) {
|
||
continue
|
||
}
|
||
if (task.priority === firstItem.priority) {
|
||
task.first = true
|
||
}
|
||
}
|
||
// const element = taskList.value.splice(firstTaskIdx, 1)[0]
|
||
// taskList.value.unshift(element)
|
||
obj.moveElement(taskList.value, firstTaskIdx, firstInsertIdx)
|
||
}
|
||
}
|
||
}
|
||
})
|
||
.finally(() => {
|
||
loading.value = false
|
||
})
|
||
}
|
||
|
||
function sortData(a: anyObj, b: anyObj) {
|
||
if (a.status === task_status_available) {
|
||
// 可以领取的放前面
|
||
return -1
|
||
} else if (b.status === task_status_available) {
|
||
return 1
|
||
} else if (a.status === task_status_claimed && a.completeProportionCount >= a.proportionSum) {
|
||
// 已领取的放最后
|
||
return 1
|
||
} else if (b.status === task_status_claimed && b.completeProportionCount >= b.proportionSum) {
|
||
return -1
|
||
} else {
|
||
// 不能领取且没有完成比例的放最后面
|
||
if (a.needProportion && !b.needProportion) {
|
||
return -1
|
||
} else if (!a.needProportion && b.needProportion) {
|
||
return 1
|
||
} else {
|
||
// 其他不能领取按照完成比例排序
|
||
return (
|
||
b.completeProportionCount / b.proportionSum - a.completeProportionCount / a.proportionSum
|
||
)
|
||
}
|
||
}
|
||
}
|
||
|
||
function filterFn(list: anyObj[]): anyObj[] {
|
||
// 筛选任务 每个任务类型只展示第一个未完成的
|
||
const filterStrObj: anyObj = {}
|
||
const listData: anyObj[] = []
|
||
|
||
list.forEach(item => {
|
||
if (
|
||
!filterStrObj[item.taskCategory] ||
|
||
item.taskCategory === 'Medal' ||
|
||
(item.taskCategory === 'WordLevelThrough' && activeType.value === task_type_achievement)
|
||
) {
|
||
if (item.name.includes('单词闯关入门获得')) {
|
||
if (!filterStrObj.StarPickerNormal) {
|
||
if (!item.status) {
|
||
filterStrObj.StarPickerNormal = 'StarPickerNormal'
|
||
}
|
||
listData.push(item)
|
||
}
|
||
return
|
||
} else if (item.name.includes('单词闯关进阶获得')) {
|
||
if (!filterStrObj.StarPickerHard) {
|
||
if (!item.status) {
|
||
filterStrObj.StarPickerHard = 'StarPickerHard'
|
||
}
|
||
listData.push(item)
|
||
}
|
||
return
|
||
}
|
||
if (item.status !== 2) {
|
||
filterStrObj[item.taskCategory] = item.taskCategory || ''
|
||
}
|
||
listData.push(item)
|
||
}
|
||
})
|
||
return listData
|
||
}
|
||
|
||
function clearData() {
|
||
taskData.value = {
|
||
dynamicCount: 0,
|
||
taskDynamicVoList: [],
|
||
taskList: [],
|
||
}
|
||
}
|
||
|
||
function typeChange(item: any) {
|
||
activeType.value = item.id
|
||
init(true)
|
||
}
|
||
|
||
// 显示学员的导学任务记录
|
||
function getLeadRecords() {
|
||
router.push('/task/user-task-records')
|
||
}
|
||
|
||
const allReceiveLoading = ref(false)
|
||
function allReceive() {
|
||
hud.load({
|
||
task: async () => {
|
||
try {
|
||
// 一键领取
|
||
allReceiveLoading.value = true
|
||
let data
|
||
if (activeType.value === task_type_lead) {
|
||
data = await taskService.batchClaimReward()
|
||
} else {
|
||
data = await taskApi.openAllReward({
|
||
taskType: activeType.value,
|
||
})
|
||
}
|
||
claimReward(data)
|
||
} finally {
|
||
allReceiveLoading.value = false
|
||
}
|
||
},
|
||
option: '领取',
|
||
hideSuccess: true,
|
||
})
|
||
}
|
||
|
||
// 领取完奖励
|
||
function claimReward(list: any) {
|
||
if (!list.length) return
|
||
setStatus()
|
||
init()
|
||
rewardList.value = list
|
||
showRewardDia.value = true // 领取奖励弹框出现
|
||
}
|
||
|
||
// 点击任务奖励
|
||
function clickTaskReward(item: any) {
|
||
isWordListReward.value = true
|
||
showItem.value = item
|
||
showBigDia.value = true
|
||
}
|
||
|
||
// 点击活跃度奖励
|
||
function clickDynamicReward(item: any) {
|
||
isWordListReward.value = false
|
||
showItem.value = item
|
||
showBigDia.value = true
|
||
}
|
||
|
||
// 奖励弹窗音效
|
||
watch(showRewardDia, nv => {
|
||
if (nv) {
|
||
useSound(`${OSS_URL}/sound/receive_reward.mp3`)
|
||
}
|
||
})
|
||
|
||
onActivated(() => {
|
||
// activeType.value = userInfo.value.isTaskStudent ? task_type_lead : task_type_day
|
||
activeType.value = classList.value[0].id
|
||
init(true)
|
||
setStatus()
|
||
})
|
||
onUnmounted(() => {
|
||
clearData()
|
||
})
|
||
</script>
|
||
<template>
|
||
<div class="task-content">
|
||
<div class="left">
|
||
<BackBar leftText="任务" class="back-bar" />
|
||
<Sidebar
|
||
:model-value="activeType"
|
||
:config="classList as any"
|
||
class="left-vbar"
|
||
@handle-change="typeChange"
|
||
></Sidebar>
|
||
</div>
|
||
<div v-if="taskData.taskList?.length > 0 && !loading" class="right">
|
||
<task-list
|
||
:data="taskData.taskList"
|
||
:type="activeType"
|
||
@click-reward="clickTaskReward"
|
||
@claim-reward="claimReward"
|
||
/>
|
||
<div v-if="showAllReward" class="button-allReward">
|
||
<mj-button
|
||
:width="230"
|
||
:size="32"
|
||
type="success"
|
||
:loading="allReceiveLoading"
|
||
@click="allReceive"
|
||
>
|
||
一键领取
|
||
</mj-button>
|
||
</div>
|
||
<dynamic-list
|
||
v-if="taskData.taskDynamicVoList?.length"
|
||
:data="taskData"
|
||
:type="activeType"
|
||
@click-reward="clickDynamicReward"
|
||
@claim-reward="claimReward"
|
||
/>
|
||
</div>
|
||
<div v-else class="empty-box">
|
||
<Icon v-if="loading" name="global_loading" loading :size="80"></Icon>
|
||
<Empty v-else content="暂无任务" type="goods"></Empty>
|
||
</div>
|
||
|
||
<mj-button
|
||
v-if="showLeadRecords"
|
||
tilt
|
||
type="warn"
|
||
:class="{
|
||
'button-lead-records': true,
|
||
'not-empty': taskData.taskList?.length,
|
||
'no-all-reward': !showAllReward,
|
||
}"
|
||
:width="230"
|
||
:size="32"
|
||
@click="getLeadRecords"
|
||
>
|
||
任务记录
|
||
</mj-button>
|
||
|
||
<show-rewards
|
||
v-model="showBigDia"
|
||
:isAchievement="activeType === task_type_achievement"
|
||
:dynamic="taskData.dynamicCount"
|
||
:item="showItem"
|
||
:isWordListReward="isWordListReward"
|
||
/>
|
||
<RewardDia v-if="showRewardDia" v-model="showRewardDia" :rewardList="rewardList"></RewardDia>
|
||
</div>
|
||
</template>
|
||
|
||
<style lang="scss" scoped>
|
||
.task-content {
|
||
-webkit-transform: translate3d(0, 0, 0);
|
||
-moz-transform: translate3d(0, 0, 0);
|
||
-ms-transform: translate3d(0, 0, 0);
|
||
transform: translate3d(0, 0, 0);
|
||
width: 100%;
|
||
height: 100%;
|
||
position: relative;
|
||
background: $theme-color-bg;
|
||
display: flex;
|
||
gap: 10px;
|
||
padding-left: $space-6;
|
||
.left {
|
||
width: 20vh;
|
||
.back-bar {
|
||
margin: 2.67vh 0 3.33vh 0;
|
||
}
|
||
.left-vbar {
|
||
top: 11.67vh;
|
||
overflow: visible;
|
||
}
|
||
}
|
||
$right_w: 131.92vh;
|
||
.right {
|
||
width: $right_w;
|
||
margin-left: 2vh;
|
||
position: relative;
|
||
}
|
||
.button-allReward {
|
||
width: 19.33vh;
|
||
height: 6.67vh;
|
||
flex-shrink: 0;
|
||
position: absolute;
|
||
right: 0.5vh;
|
||
top: 11.67vh;
|
||
border-radius: 3.33vh 0 0 3.33vh;
|
||
// box-shadow: 0px 6px 4px 0px rgba(0, 0, 0, 0.35);
|
||
cursor: pointer;
|
||
|
||
.mj-button {
|
||
background: v-bind(oneKeyBg);
|
||
border-radius: 0;
|
||
box-shadow: none;
|
||
:deep(img) {
|
||
display: none;
|
||
}
|
||
}
|
||
}
|
||
|
||
.button-lead-records {
|
||
position: absolute;
|
||
right: $space-6;
|
||
top: 3.2vh;
|
||
|
||
&.not-empty {
|
||
left: 116vh;
|
||
top: 11.5vh;
|
||
|
||
&.no-all-reward {
|
||
left: 137vh;
|
||
}
|
||
}
|
||
}
|
||
|
||
.empty-box {
|
||
width: 70%;
|
||
height: 70%;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
}
|
||
}
|
||
</style>
|