2026-05-09 10:34:51 +08:00

558 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 { useRouter, useRoute } from 'vue-router'
import RoundSplit from '@/page/subject/freeStudy/views/textbookDetail/RoundSplit.vue'
import LoadIcon from '@/page/system/mailbox/components/LoadIcon.vue'
import VideoFullBox from '@/page/subject/components/VideoFullBox/index.vue'
import homeWorkApi from '@/api/system/homeWork'
import { debounce, useAudio } from '@/hooks'
import { subjectStore } from '@/stores'
import { resource_type_analogy, resource_type_wrong_book } from '@/page/subject/doWork'
// import { userStore } from '@/stores';
const router = useRouter()
const route = useRoute()
// const { userInfo } = userStore();
const subjectId = ref(2)
const { getSubjectAllList } = subjectStore()
const { subjectList } = toRefs(subjectStore())
const subjectComputed = computed(() =>
subjectList.value.map(({ id, name }: anyObj) => ({ value: id, label: name })),
)
const subjectiveFlag = ref<0 | 1>(1)
const subjectiveConfig = reactive([
{ value: 1, label: '客观题' },
{ value: 0, label: '主观题' },
])
const correctFlag = ref<0 | 1>(0)
const correctConfig = reactive([
{ value: 0, label: '未订正' },
{ value: 1, label: '已订正' },
])
const unCorrectNum = ref(0)
async function getCorrectConfig() {
const { data: res } = await homeWorkApi.getWrongNum({
subjectId: subjectId.value,
subjectiveFlag: subjectiveFlag.value,
})
unCorrectNum.value = res.data.unCorrectNum
correctConfig[0].label = `未订正 ${res.data.unCorrectNum > 999 ? '999+' : res.data.unCorrectNum}`
correctConfig[1].label = `已订正 ${res.data.correctNum > 999 ? '999+' : res.data.correctNum}`
}
// 列表
const list = ref<anyObj[]>([])
const page = reactive({
size: 10,
current: 1,
total: 0,
})
const loading = ref(false)
async function getList() {
const { data: res } = await homeWorkApi.getWrongList({
subjectId: subjectId.value,
subjectiveFlag: subjectiveFlag.value,
correctFlag: correctFlag.value,
...page,
})
if (res.code !== 200) return
list.value.push(...(res.rows || []))
page.total = res.total
}
const scrollBoxRef = ref()
async function handleScroll(event: any) {
if (list.value.length >= page.total) return
if (loading.value) return
if (event.target.scrollTop + event.target.offsetHeight >= scrollBoxRef.value?.offsetHeight - 50) {
page.current++
try {
loading.value = true
await getList()
} catch {
page.current--
}
loading.value = false
}
}
const debounceScroll = debounce(handleScroll, 200, false)
const pageLoading = ref(false)
const emptyText = ref('暂无错题')
// 初始化列表
async function initList(pageSize = 0) {
if (pageLoading.value) return
pageLoading.value = true
list.value = []
page.current = 1
pageSize && (page.size = pageSize)
try {
await getList()
emptyText.value = correctFlag.value ? '暂无已订正错题' : '暂无未订正错题'
} catch {
emptyText.value = '网络出问题了啦~'
}
pageLoading.value = false
}
async function init(size = 0) {
getCorrectConfig()
await initList(size)
}
/*
** 音频
*/
// 播放器
let player: anyObj = {}
const activeAudio = ref('')
function handlePlay(item: anyObj) {
activeAudio.value = item.subjectTitleVo.id
player.src = item.subjectTitleVo.voiceUrl
player.play()
}
// 举一反三
function startTrain(item: anyObj) {
router.push({
path: '/subject/doWork',
query: {
resourceType: resource_type_analogy,
subjectId: subjectId.value,
titleId: item.id,
isHomework: resource_type_analogy,
},
})
}
const debounceStartTrain = debounce(startTrain, 500)
function handleStart() {
if (unCorrectNum.value <= 1) {
handleDiaOK()
} else {
showDia.value = true
}
}
// 订正错题弹窗
const showDia = ref(false)
const correctId = ref<string>('') // 被订正的主观题id
const correctNum = ref(1)
async function handleDiaOK() {
if (!correctId.value) {
router.push({
path: '/subject/doWork',
query: {
resourceType: resource_type_wrong_book,
num: correctNum.value,
subjectId: subjectId.value,
isHomework: resource_type_wrong_book,
},
})
} else {
await homeWorkApi.errorEdition(correctId.value)
await init(list.value.length)
showDia.value = false
page.size = 10
}
}
function changeCorrectNum(type: 0 | 1) {
if (type) {
if (correctNum.value >= 10) return
correctNum.value++
} else {
if (correctNum.value <= 1) return
correctNum.value--
}
}
// 主观题订正错题
function handleCorrection(item: anyObj) {
correctId.value = item.userErrorEditionId
showDia.value = true
}
function handleDiaClosed() {
correctId.value = ''
}
// 视频播放器
const videoDia = reactive<anyObj>({
show: false,
knowledge: {},
})
function playVideo(item: anyObj) {
// videoDia.videoList = list;
videoDia.show = true
videoDia.knowledge.id = item.id
videoDia.knowledge.name = item.name
}
// 图片预览弹窗
const imgDia = reactive({
show: false,
url: '',
})
function handleShow(url: string) {
imgDia.show = true
imgDia.url = url
}
onMounted(async () => {
player = useAudio({
autoplay: false,
})
if (subjectList.value?.length <= 0) {
await getSubjectAllList()
}
subjectId.value = subjectComputed.value[0].value
})
onActivated(async () => {
if (route.meta.subjectId) {
subjectId.value = Number(route.meta.subjectId as string)
}
pageLoading.value = false
loading.value = false
activeAudio.value = ''
correctFlag.value = 0
subjectiveFlag.value = 1
showDia.value = false
correctNum.value = 1
init()
})
onDeactivated(() => {
showDia.value = false
player.pause?.()
})
onUnmounted(() => {
player.destroy()
})
</script>
<template>
<div class="wrong-book">
<div class="top">
<BackBar leftText="错题本" />
<div class="top-select-tab">
<SelectTab v-model="subjectiveFlag" :dataList="subjectiveConfig" @change="init"></SelectTab>
</div>
<div class="top-select">
<Select v-model="subjectId" :selectList="subjectComputed" @selectChange="init"></Select>
</div>
</div>
<div class="main">
<div class="main-top">
<div class="main-top-select">
<SelectTab v-model="correctFlag" :dataList="correctConfig" @change="initList"></SelectTab>
</div>
</div>
<RoundSplit :length="88"></RoundSplit>
<div class="main-box">
<template v-if="list.length">
<div class="main-list hidden_scroll" @scroll="debounceScroll">
<div ref="scrollBoxRef" class="scroll-box QuesItemBox">
<template v-for="i in list" :key="i.id">
<WrongQuesItem
:correctFlag="correctFlag"
:subjectiveFlag="subjectiveFlag"
:data="i"
:time="i.createTime"
:activeAudio="activeAudio"
@startTrain="debounceStartTrain(i)"
@play="handlePlay(i)"
@playVideo="playVideo"
@showPic="handleShow"
>
<template #customBtn>
<Btn
v-if="subjectiveFlag === 1"
class="custom-btn"
width="192"
height="64"
size="32"
@click="debounceStartTrain(i)"
>
举一反三
</Btn>
<Btn
v-else-if="subjectiveFlag === 0 && correctFlag === 0"
class="custom-btn"
width="192"
height="64"
size="32"
@click="handleCorrection(i)"
>
订正错题
</Btn>
<div v-else></div>
</template>
</WrongQuesItem>
</template>
</div>
</div>
<LoadIcon v-if="loading" class="load-icon"></LoadIcon>
</template>
<Empty
v-else
class="empty"
:type="pageLoading ? 'loading' : 'learn'"
:content="emptyText"
></Empty>
</div>
</div>
<Btn
v-if="unCorrectNum && subjectiveFlag === 1"
class="start_train-btn"
type="success"
width="232"
height="80"
@click="handleStart"
>
订正错题
</Btn>
<mj-dialog
v-model="showDia"
hide-cancel
:ok-text="!correctId ? '开始' : '确定'"
title="订正错题"
:ok="handleDiaOK"
:closed="handleDiaClosed"
>
<div class="dia_body">
<template v-if="!correctId">
<div class="dia_body-title">系统将从您的错题本中随机选择题目</div>
<div class="dia_body-slider">
<div class="dia_body-slider-box">
<Icon name="slider_btn_reduce" width="56" @click="changeCorrectNum(0)"></Icon>
<el-slider
v-model="correctNum"
:show-tooltip="false"
:step="1"
:max="unCorrectNum > 10 ? 10 : unCorrectNum"
:min="1"
></el-slider>
<Icon name="slider_btn_add" width="56" @click="changeCorrectNum(1)"></Icon>
</div>
<div class="dia_body-slider-text">
数量{{ correctNum }}/{{ unCorrectNum > 10 ? 10 : unCorrectNum }}
</div>
</div>
</template>
<template v-else>
<div class="dia_body-title text">请确认该题已掌握订正后将移动至 已订正模块</div>
</template>
</div>
</mj-dialog>
<VideoFullBox
v-model:show="videoDia.show"
:knowledge="videoDia.knowledge"
:data="{
subjectId,
resourceType: 2,
origin: 2,
}"
></VideoFullBox>
<ImgView v-model="imgDia.show" :url="imgDia.url"></ImgView>
</div>
</template>
<style scoped lang="scss">
.wrong-book {
@include main_bg;
position: relative;
width: 100vw;
height: 100%;
overflow: hidden;
box-sizing: border-box;
line-height: 1.5;
// @include hidden_scroll;
.top {
width: 100%;
padding: 2.67vh 64px 0;
position: absolute;
top: 0;
left: 0;
z-index: 1;
margin-bottom: 2vh;
display: flex;
align-items: center;
justify-content: space-between;
z-index: auto;
&-select-tab {
flex: 1;
margin-left: 62px;
:deep(.select-list) {
width: 968px;
.data-item {
min-width: 50%;
&.active {
background: linear-gradient(269deg, #86e6ff 2.25%, #9effda 51.93%, #5fdcfd 99.65%);
backdrop-filter: blur(3px);
}
}
}
}
}
.main {
position: relative;
margin: 11.33vh auto 0;
background-size: 100%;
width: 1792px;
height: 86vh;
// padding: 91px 24px 24px;
&-top {
width: 100%;
height: 8.5vh;
border-radius: 1.67vh;
background: linear-gradient(269deg, #86e6ff 2.25%, #9effda 51.93%, #5fdcfd 99.65%);
display: flex;
align-items: center;
justify-content: flex-end;
padding: 0 32px;
&-select {
:deep(.select-list) {
border-radius: 4.5vh;
.data-item {
min-width: 258px;
width: 50%;
border-radius: 4.5vh;
&.active {
background: linear-gradient(90deg, #86e6ff 0%, #9effda 50%, #86e6ff 100%);
}
}
}
}
}
.round-split {
position: absolute;
width: calc(100% - 50px);
left: 50%;
transform: translate(-50%);
// z-index: 1;
top: 7.92vh;
:deep(.dot) {
background: linear-gradient(180deg, #798498 0%, #a1b1cb 100%);
}
}
&-box {
height: calc(100% - 110px);
border-radius: 1.67vh;
background: linear-gradient(180deg, #f0faff 0%, #fafdff 100%);
padding: 2.67vh;
}
&-list {
height: 100%;
// margin: 0 32px;
// .scroll-box {
// height: 100%;
//}
.custom-btn {
min-height: 5.67vh;
margin-left: 24px;
border-radius: 4.42vh;
border: 0.17vh solid #fff;
box-shadow: 0 0.17vh 0.17vh 0 rgba(0, 0, 0, 0.25);
color: $font-color;
.x-icon {
margin-left: 16px;
transform: rotate(180deg);
}
}
}
.custom_load_icon {
position: absolute;
bottom: 3.17vh;
left: 50%;
transform: translate(-50%);
}
.empty {
height: 75vh;
}
}
.start_train-btn {
position: absolute;
bottom: 13.33vh;
right: -35px;
z-index: 100;
:deep(.x-button-text) {
transform: translate(-8px);
}
}
}
.dia_body {
// height: 392px;
padding: 3.33vh 0;
text-align: center;
color: $font-color;
line-height: 1.5;
&-title {
text-align: center;
font-family: $font-special;
font-size: 2.67vh;
&.text {
width: 510px;
margin: 4.17vh auto 0;
}
}
&-slider {
margin-top: 3.33vh;
&-box {
display: flex;
align-items: center;
padding: 0 24px;
.x-icon {
margin: 0 32px;
flex-shrink: 0;
}
}
:deep(.el-slider) {
height: 4.67vh;
.el-slider__runway {
height: 3vh;
background: #9a9ea0;
border-radius: 2.5vh;
}
.el-slider__bar {
height: 3vh;
border-radius: 2.5vh;
background: linear-gradient(249deg, #4de798 26.9%, #a2fb6e 79.86%);
}
.el-slider__button-wrapper {
width: 56px;
height: 4.67vh;
background: radial-gradient(189.06% 189.06% at 49.22% 50.78%, #fff 0%, #becadf 100%);
border-radius: 50%;
filter: drop-shadow(0px 3.5px 3.5px rgba(0, 0, 0, 0.15));
top: -0.83vh;
}
.el-slider__button {
width: 56px;
height: 4.67vh;
border: none;
}
}
&-text {
margin-top: 1.33vh;
font-family: OPPOSans;
font-size: 2vh;
}
}
}
</style>