2026-05-11 15:15:28 +08:00

544 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>