Compare commits
2 Commits
436deb44dd
...
12c6f0759b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12c6f0759b | ||
|
|
87b0b49e14 |
@ -36,7 +36,7 @@ const props = withDefaults(
|
|||||||
const emits = defineEmits(['loaded'])
|
const emits = defineEmits(['loaded'])
|
||||||
|
|
||||||
const OSS_URL = import.meta.env.VITE_OSS_URL
|
const OSS_URL = import.meta.env.VITE_OSS_URL
|
||||||
const bg = `url(${OSS_URL}/urm/urm_load_bg.png) 100%/cover no-repeat`
|
// const bg = `url(${OSS_URL}/urm/urm_load_bg.png) 100%/cover no-repeat`
|
||||||
|
|
||||||
const { size, loadProgress, loadSize, loaded, loadText, init } = useLoad()
|
const { size, loadProgress, loadSize, loaded, loadText, init } = useLoad()
|
||||||
const simulationLoadProgress = ref('0%') // 模拟进度
|
const simulationLoadProgress = ref('0%') // 模拟进度
|
||||||
@ -113,7 +113,7 @@ const progress = computed(() =>
|
|||||||
.load {
|
.load {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: v-bind(bg);
|
background-color: $theme-color-bg;
|
||||||
padding: 0 160px;
|
padding: 0 160px;
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
|
|||||||
@ -4,11 +4,7 @@
|
|||||||
"/subject/book_shelf_top.svg",
|
"/subject/book_shelf_top.svg",
|
||||||
"/subject/book_shelf_bottom.svg",
|
"/subject/book_shelf_bottom.svg",
|
||||||
|
|
||||||
"/urm/bg_home.svg",
|
|
||||||
"/urm/bg_login.svg",
|
|
||||||
"/urm/book_bg.svg",
|
"/urm/book_bg.svg",
|
||||||
"/urm/mail_bg.svg",
|
|
||||||
"/urm/main_bg.svg",
|
|
||||||
"/urm/urm_load_bg.png",
|
"/urm/urm_load_bg.png",
|
||||||
"/urm/subject_target_dia_bg.svg",
|
"/urm/subject_target_dia_bg.svg",
|
||||||
|
|
||||||
@ -110,10 +106,6 @@
|
|||||||
|
|
||||||
"/urm/task-yilingqu.png",
|
"/urm/task-yilingqu.png",
|
||||||
"/urm/task-kelingqu.png",
|
"/urm/task-kelingqu.png",
|
||||||
"/urm/task-normal.png",
|
"/urm/task-normal.png"
|
||||||
"/urm/task_achievement.svg",
|
|
||||||
"/urm/task_day.svg",
|
|
||||||
"/urm/task_parent.svg",
|
|
||||||
"/urm/task_lead.svg"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import hud from '@/utils/hud'
|
|||||||
import sass from '@/utils/sass'
|
import sass from '@/utils/sass'
|
||||||
|
|
||||||
const OSS_URL = import.meta.env.VITE_OSS_URL
|
const OSS_URL = import.meta.env.VITE_OSS_URL
|
||||||
const bg = `url(${OSS_URL}/urm/bg_login.svg) 100%/cover no-repeat`
|
|
||||||
const logo = sass.getLogo()
|
const logo = sass.getLogo()
|
||||||
const qr = sass.getQrCode()
|
const qr = sass.getQrCode()
|
||||||
const url = window.location.href
|
const url = window.location.href
|
||||||
@ -110,12 +109,7 @@ defineOptions({
|
|||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div class="page">
|
||||||
class="page"
|
|
||||||
:style="{
|
|
||||||
background: bg,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<BackBar v-if="showBack" class="backBar" />
|
<BackBar v-if="showBack" class="backBar" />
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<img class="logo" :src="logo" @click="onEnter" />
|
<img class="logo" :src="logo" @click="onEnter" />
|
||||||
@ -166,6 +160,7 @@ defineOptions({
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
background-color: $theme-color-bg;
|
||||||
|
|
||||||
.backBar {
|
.backBar {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
|||||||
@ -12,7 +12,6 @@ import globalApi from '@/api/global'
|
|||||||
import userApi from '@/api/system/user'
|
import userApi from '@/api/system/user'
|
||||||
|
|
||||||
const OSS_URL = import.meta.env.VITE_OSS_URL
|
const OSS_URL = import.meta.env.VITE_OSS_URL
|
||||||
const bg = `url(${OSS_URL}/urm/bg_login.svg) 100%/cover no-repeat`
|
|
||||||
const logo = sass.getLogo()
|
const logo = sass.getLogo()
|
||||||
const ipGirl = `${OSS_URL}/urm/ip_girl.png`
|
const ipGirl = `${OSS_URL}/urm/ip_girl.png`
|
||||||
|
|
||||||
@ -138,12 +137,7 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div class="page">
|
||||||
class="page"
|
|
||||||
:style="{
|
|
||||||
background: bg,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<div v-if="step === 0" class="step0" @click="step += 1">
|
<div v-if="step === 0" class="step0" @click="step += 1">
|
||||||
<img class="logo" :src="logo" />
|
<img class="logo" :src="logo" />
|
||||||
<img class="ip" :src="ipGirl" />
|
<img class="ip" :src="ipGirl" />
|
||||||
@ -257,6 +251,7 @@ onMounted(() => {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding: $space-2_h $space-6;
|
padding: $space-2_h $space-6;
|
||||||
|
background-color: $theme-color-bg;
|
||||||
|
|
||||||
.step0 {
|
.step0 {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
@ -19,7 +19,6 @@ const handleClick = (buttonId: any) => {
|
|||||||
const OSS_URL = import.meta.env.VITE_OSS_URL
|
const OSS_URL = import.meta.env.VITE_OSS_URL
|
||||||
const xxlIcon = `${OSS_URL}/icon/module_xxl.png`
|
const xxlIcon = `${OSS_URL}/icon/module_xxl.png`
|
||||||
const xzyIcon = `${OSS_URL}/icon/module_bjxzy.jpg`
|
const xzyIcon = `${OSS_URL}/icon/module_bjxzy.jpg`
|
||||||
const bg = `url(${OSS_URL}/urm/bg_login.svg) 100%/cover no-repeat`
|
|
||||||
const title_size = 68
|
const title_size = 68
|
||||||
const title_height = 112
|
const title_height = 112
|
||||||
</script>
|
</script>
|
||||||
@ -51,7 +50,7 @@ const title_height = 112
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: v-bind(bg);
|
background-color: $theme-color-bg;
|
||||||
gap: 250px;
|
gap: 250px;
|
||||||
|
|
||||||
.box {
|
.box {
|
||||||
|
|||||||
@ -96,12 +96,7 @@ const startDoWork = () => {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div class="uncomplete">
|
||||||
class="uncomplete"
|
|
||||||
:style="{
|
|
||||||
'background-image': `url(${OSS_URL}/urm/mail_bg.svg)`,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
class="book"
|
class="book"
|
||||||
:style="{
|
:style="{
|
||||||
@ -238,6 +233,7 @@ const startDoWork = () => {
|
|||||||
position: relative;
|
position: relative;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
|
background-color: $theme-color-bg;
|
||||||
|
|
||||||
.book {
|
.book {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|||||||
@ -1,645 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import SingleChoice from '@c/question/answer/SingleChoice.vue'
|
|
||||||
import Judgment from '@c/question/answer/Judgment.vue'
|
|
||||||
import JudgmentCloze from '@c/question/answer/JudgmentCloze.vue'
|
|
||||||
import MultipleChoice from '@c/question/answer/MultipleChoice.vue'
|
|
||||||
import ClozeTest from '@c/question/answer/ClozeTest.vue'
|
|
||||||
import Other from '@c/question/answer/Other.vue'
|
|
||||||
import HandDrawn from '@c/question/answer/HandDrawn.vue'
|
|
||||||
import Input from '@c/question/answer/Input.vue'
|
|
||||||
import { useAudio, TypeSet } from '@/hooks'
|
|
||||||
import homeWorkApi from '@/api/system/homeWork'
|
|
||||||
import EchartsBar from '@/page/system/homework/reports/components/EchartsBar.vue'
|
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
data: any
|
|
||||||
idx: number
|
|
||||||
reportFlag: number
|
|
||||||
uploadFile: any
|
|
||||||
beforeUpload: any
|
|
||||||
isDeadline: boolean
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const emit = defineEmits(['submit', 'handleTime', 'showPic', 'beforeUpload', 'uploadError'])
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
const component = computed(() => {
|
|
||||||
console.log(props.data)
|
|
||||||
if (props.data?.autoMarkFlag === 0) {
|
|
||||||
// 不可自动批改
|
|
||||||
if (props.data?.titleChannelTypeName === '填空题') {
|
|
||||||
// 不可自动批改的填空题
|
|
||||||
return HandDrawn
|
|
||||||
}
|
|
||||||
if (props.data?.subjectId === 3 && props.data?.titleChannelTypeName === '解答题') {
|
|
||||||
// 数学解答题
|
|
||||||
return HandDrawn
|
|
||||||
}
|
|
||||||
return Other
|
|
||||||
}
|
|
||||||
if (props.data?.fillBlankNum) {
|
|
||||||
// 填空题
|
|
||||||
return Input
|
|
||||||
}
|
|
||||||
// 选项为多个选项组时完形填空
|
|
||||||
if (props.data?.optionList?.length > 1) {
|
|
||||||
if (['判断', '判断题'].includes(props.data?.titleChannelTypeName)) {
|
|
||||||
return JudgmentCloze
|
|
||||||
}
|
|
||||||
return ClozeTest
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.data?.titleChannelTypeName === '多选题') {
|
|
||||||
return MultipleChoice
|
|
||||||
}
|
|
||||||
|
|
||||||
if (['判断', '判断题'].includes(props.data?.titleChannelTypeName)) {
|
|
||||||
return Judgment
|
|
||||||
}
|
|
||||||
|
|
||||||
// 其他题型有optionList时为单选题,否则是应用题
|
|
||||||
return props.data?.optionList?.[0]?.length ? SingleChoice : Other
|
|
||||||
})
|
|
||||||
|
|
||||||
// 图片预览
|
|
||||||
const imgDia = reactive({
|
|
||||||
show: false,
|
|
||||||
url: '',
|
|
||||||
})
|
|
||||||
function handleShow(url: string) {
|
|
||||||
if (url) {
|
|
||||||
imgDia.show = true
|
|
||||||
imgDia.url = url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const resultClass = computed(() => {
|
|
||||||
return (data: any) => {
|
|
||||||
if (!data)
|
|
||||||
return {
|
|
||||||
class: 'info',
|
|
||||||
text: 0 + '分',
|
|
||||||
// icon: 'emoji_info',
|
|
||||||
}
|
|
||||||
if (data.titleStatus === 2) {
|
|
||||||
return {
|
|
||||||
class: 'wait',
|
|
||||||
text: '待批改',
|
|
||||||
// icon: 'emoji_info',
|
|
||||||
}
|
|
||||||
} else if (!data.markFlag && !data.submitAnswer && !data.submitAnswerPic) {
|
|
||||||
return {
|
|
||||||
class: 'empty',
|
|
||||||
text: '未作答',
|
|
||||||
icon: 'emoji_empty',
|
|
||||||
}
|
|
||||||
} else if (data.markFlag) {
|
|
||||||
return {
|
|
||||||
class: 'success',
|
|
||||||
text: '答对啦',
|
|
||||||
icon: 'emoji_success',
|
|
||||||
}
|
|
||||||
} else if (!data.markFlag && data.answerScore === 0) {
|
|
||||||
return {
|
|
||||||
class: 'error',
|
|
||||||
text: '答错了',
|
|
||||||
icon: 'emoji_fail',
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
class: 'info',
|
|
||||||
text: data.answerScore + '分',
|
|
||||||
// icon: 'emoji_info',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// 给子题目添加序号
|
|
||||||
const filterTitle = computed(() => {
|
|
||||||
return (title: string | undefined, cidx?: number) => {
|
|
||||||
if (title) {
|
|
||||||
const t = title.replace(/http:\/\//gi, 'https://')
|
|
||||||
// .replace(/contenteditable/g, 'no-contenteditable');
|
|
||||||
if (cidx !== undefined) {
|
|
||||||
return `(${cidx + 1})` + t
|
|
||||||
} else {
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description: 音频
|
|
||||||
*/
|
|
||||||
// 音频是否正在播放
|
|
||||||
const isAudioPlay = ref(false)
|
|
||||||
// 音频播放器
|
|
||||||
let audioPlayer: anyObj = {}
|
|
||||||
// 播放音频
|
|
||||||
function playAudio() {
|
|
||||||
if (isAudioPlay.value) {
|
|
||||||
audioPlayer.pause()
|
|
||||||
} else {
|
|
||||||
audioPlayer.src = props.data?.voiceUrl
|
|
||||||
audioPlayer.play()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 填空题 同步答案
|
|
||||||
const quizList = ref<anyObj[]>([])
|
|
||||||
function inputChange(val: string, idx: number) {
|
|
||||||
quizList.value[idx].innerText = val
|
|
||||||
}
|
|
||||||
|
|
||||||
const jobStuStatistics = ref<anyObj>([])
|
|
||||||
|
|
||||||
const rightRef = ref()
|
|
||||||
const echartsRef = ref()
|
|
||||||
const echartsData = reactive<{ xData: string[]; seriesVal: anyObj[] }>({
|
|
||||||
xData: [],
|
|
||||||
seriesVal: [],
|
|
||||||
})
|
|
||||||
watch(
|
|
||||||
() => props.data?.jobStuTittleId,
|
|
||||||
async nv => {
|
|
||||||
if (nv) {
|
|
||||||
// 切换题目时初始化音频 初始化右边区域滑动条
|
|
||||||
audioPlayer?.pause?.()
|
|
||||||
rightRef.value && (rightRef.value.scrollTop = 0)
|
|
||||||
await nextTick()
|
|
||||||
TypeSet([document.querySelector('.question-item')])
|
|
||||||
// 题目里所有图片添加点击放大查看事件
|
|
||||||
document.querySelectorAll('.dowork .left-title img').forEach((i: anyObj) => {
|
|
||||||
i?.addEventListener('click', () => {
|
|
||||||
handleShow(i?.src)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
document.querySelectorAll('.dowork .choice-box img').forEach((i: anyObj) => {
|
|
||||||
i?.addEventListener('click', (e: any) => {
|
|
||||||
handleShow(i?.src)
|
|
||||||
e?.stopPropagation?.()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
document.querySelectorAll('.dowork .answer-box-main img').forEach((i: anyObj) => {
|
|
||||||
i?.addEventListener('click', () => {
|
|
||||||
handleShow(i?.src)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
document.querySelectorAll('.dowork .analyze-title img').forEach((i: anyObj) => {
|
|
||||||
i?.addEventListener('click', () => {
|
|
||||||
handleShow(i?.src)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
if (props.reportFlag) {
|
|
||||||
const { data: res } = await homeWorkApi.getJobStatistics({
|
|
||||||
titleId: props.data.id,
|
|
||||||
jobId: props.data.jobId,
|
|
||||||
stuId: route.query.stuId || '',
|
|
||||||
})
|
|
||||||
|
|
||||||
jobStuStatistics.value = res.data?.map((i: anyObj) => i.scStuList) || []
|
|
||||||
echartsData.xData = []
|
|
||||||
echartsData.seriesVal = []
|
|
||||||
res.data?.forEach((i: anyObj) => {
|
|
||||||
echartsData.xData.push(i.intervalValue + '分')
|
|
||||||
echartsData.seriesVal.push({
|
|
||||||
value: i.num,
|
|
||||||
itemStyle: {
|
|
||||||
color: i.curFlag ? '#FE8200' : '#7FADFF',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
nextTick(() => {
|
|
||||||
echartsRef.value?.drawGraph()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 填空题 同步答案
|
|
||||||
if (props.data?.fillBlankNum) {
|
|
||||||
quizList.value = []
|
|
||||||
document
|
|
||||||
.querySelectorAll('.quizPutTag[contenteditable]')
|
|
||||||
.forEach((i: anyObj, idx: number) => {
|
|
||||||
i.setAttribute('contenteditable', false)
|
|
||||||
i.innerText = props.data.submitAnswer?.split(',')?.[idx] || ''
|
|
||||||
quizList.value.push(i)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ immediate: true },
|
|
||||||
)
|
|
||||||
const xlDia = reactive<{ show: boolean; data: anyObj[] }>({
|
|
||||||
show: false,
|
|
||||||
data: [],
|
|
||||||
})
|
|
||||||
|
|
||||||
function handleBarClick(params: anyObj) {
|
|
||||||
const arr = jobStuStatistics.value[params.dataIndex]
|
|
||||||
if (arr?.length) {
|
|
||||||
xlDia.show = true
|
|
||||||
xlDia.data = arr.map((i: anyObj) => ({
|
|
||||||
id: i.id,
|
|
||||||
name: i.stuName,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
;(() => {
|
|
||||||
nextTick(() => {
|
|
||||||
audioPlayer = useAudio({
|
|
||||||
autoplay: false,
|
|
||||||
})
|
|
||||||
audioPlayer.on('play', () => {
|
|
||||||
isAudioPlay.value = true
|
|
||||||
})
|
|
||||||
audioPlayer.on('pause', () => {
|
|
||||||
isAudioPlay.value = false
|
|
||||||
})
|
|
||||||
audioPlayer.on('ended', () => {
|
|
||||||
isAudioPlay.value = false
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})()
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
audioPlayer?.destroy?.()
|
|
||||||
isAudioPlay.value = false
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="question-item">
|
|
||||||
<div class="left hidden_scroll">
|
|
||||||
<div class="left-subType">{{ idx }}. ({{ data?.score }}分) {{ data?.channelTypeName }}</div>
|
|
||||||
<div v-if="data?.title" class="left-title" v-html="filterTitle(data?.title)"></div>
|
|
||||||
<div v-if="data?.voiceUrl" class="left-voice">
|
|
||||||
<Horn :active="isAudioPlay" @click="playAudio"></Horn>
|
|
||||||
</div>
|
|
||||||
<div v-if="data?.children?.length" class="left-children">
|
|
||||||
<template v-for="(c, idx) in data?.children" :key="c.id">
|
|
||||||
<div class="left-title" v-html="filterTitle(c?.title, idx)"></div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div ref="rightRef" class="right hidden_scroll">
|
|
||||||
<!-- 批改结构 -->
|
|
||||||
<div class="result-box">
|
|
||||||
<div v-if="isDeadline && reportFlag" :class="['result', resultClass(data).class]">
|
|
||||||
<div>我的答案</div>
|
|
||||||
<div class="emoji-box">
|
|
||||||
<div class="emoji">
|
|
||||||
<Icon
|
|
||||||
v-if="resultClass(data).icon"
|
|
||||||
:name="resultClass(data).icon || ''"
|
|
||||||
width="48"
|
|
||||||
></Icon>
|
|
||||||
</div>
|
|
||||||
{{ resultClass(data).text }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- 答题区域 -->
|
|
||||||
|
|
||||||
<component
|
|
||||||
:is="component"
|
|
||||||
:reportFlag="Number(reportFlag)"
|
|
||||||
class="answer-com"
|
|
||||||
:titleId="data?.id"
|
|
||||||
:optionList="data?.optionList || undefined"
|
|
||||||
:answer="data?.answer"
|
|
||||||
:submitAnswer="data?.submitAnswer"
|
|
||||||
:submitAnswerPic="data?.submitAnswerPic"
|
|
||||||
:submitAnswerPicOriginal="data?.submitAnswerPicOriginal"
|
|
||||||
:isAndroid="data?.isAndroid"
|
|
||||||
:markAnswerPic="data?.markAnswerPic"
|
|
||||||
:markAnswer="data?.markAnswer"
|
|
||||||
:fillBlankNum="data?.fillBlankNum"
|
|
||||||
:isDeadline="isDeadline"
|
|
||||||
:uploadLoading="data?.uploadLoading"
|
|
||||||
:uploadFile="uploadFile"
|
|
||||||
:beforeUpload="beforeUpload"
|
|
||||||
@submit="
|
|
||||||
emit('submit', {
|
|
||||||
...$event,
|
|
||||||
id: data?.jobStuTittleId,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
@handleTime="emit('handleTime', $event)"
|
|
||||||
@showPic="handleShow"
|
|
||||||
@inputChange="inputChange"
|
|
||||||
>
|
|
||||||
<template #markAnswer>
|
|
||||||
<div v-if="isDeadline && reportFlag && data?.markAnswer" class="markAnswerPic">
|
|
||||||
<div class="answer-box">
|
|
||||||
<div class="answer-box-title">批改说明</div>
|
|
||||||
</div>
|
|
||||||
<div class="text-box">
|
|
||||||
{{ data?.markAnswer }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</component>
|
|
||||||
|
|
||||||
<!-- 答案与解析 -->
|
|
||||||
<template v-if="isDeadline && reportFlag">
|
|
||||||
<div v-if="echartsData.xData.length" class="answer-box">
|
|
||||||
<div class="answer-box-title">答题统计</div>
|
|
||||||
<div class="answer-box-echarts">
|
|
||||||
<EchartsBar
|
|
||||||
ref="echartsRef"
|
|
||||||
:data="echartsData"
|
|
||||||
yName="(人)"
|
|
||||||
@handleBarClick="handleBarClick"
|
|
||||||
></EchartsBar>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="data?.answer" class="answer-box">
|
|
||||||
<div class="answer-box-title">参考答案</div>
|
|
||||||
<div class="answer-box-main" v-html="data?.answer"></div>
|
|
||||||
</div>
|
|
||||||
<!-- 暂时隐藏 data.titleAnalyzeJson -->
|
|
||||||
<div
|
|
||||||
v-if="data?.titleAnalyze || data?.titleAnalyzePicture"
|
|
||||||
class="answer-box"
|
|
||||||
style="margin-top: 60px"
|
|
||||||
>
|
|
||||||
<div class="answer-box-title">解析</div>
|
|
||||||
<div v-if="data?.titleAnalyze" class="answer-box-main analyze analyze-title">
|
|
||||||
<div class="titleAnalyze" v-html="data?.titleAnalyze"></div>
|
|
||||||
</div>
|
|
||||||
<div v-if="data?.titleAnalyzePicture" class="answer-box-main analyze analyze-pic">
|
|
||||||
<div
|
|
||||||
v-for="i in data.titleAnalyzePicture.split(',')"
|
|
||||||
:key="i"
|
|
||||||
class="titleAnalyzePicture"
|
|
||||||
@click="handleShow(i)"
|
|
||||||
>
|
|
||||||
<img :src="i" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<ImgView v-model="imgDia.show" :url="imgDia.url"></ImgView>
|
|
||||||
<XLDia v-model="xlDia.show" title="对应学员">
|
|
||||||
<div class="stu_list">
|
|
||||||
<div v-for="i in xlDia.data" :key="i.id" class="stu_list-item">
|
|
||||||
{{ i.name }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</XLDia>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.question-item {
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
padding: 2.67vh;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
&::before {
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
bottom: 2.67vh;
|
|
||||||
transform: translate(-50%);
|
|
||||||
content: '';
|
|
||||||
display: block;
|
|
||||||
width: 6px;
|
|
||||||
height: calc(100% - 64px);
|
|
||||||
border-radius: 0.5vh;
|
|
||||||
background: repeating-linear-gradient(
|
|
||||||
to bottom,
|
|
||||||
#cdd5ff,
|
|
||||||
#cdd5ff 8px,
|
|
||||||
transparent 10px,
|
|
||||||
transparent 20px /* 线条间隔 */
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
.left {
|
|
||||||
width: calc(50% - 3px);
|
|
||||||
height: 100%;
|
|
||||||
padding-right: 48px;
|
|
||||||
|
|
||||||
// word-break: break-all;
|
|
||||||
word-break: normal;
|
|
||||||
word-wrap: break-word;
|
|
||||||
overflow-y: auto;
|
|
||||||
|
|
||||||
&-subType {
|
|
||||||
color: #666;
|
|
||||||
font-family: 'OPPO Sans';
|
|
||||||
font-size: 2.67vh;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 4vh; /* 150% */
|
|
||||||
margin-bottom: 2vh;
|
|
||||||
}
|
|
||||||
&-title {
|
|
||||||
padding-left: 40px;
|
|
||||||
color: $font-color;
|
|
||||||
font-family: 'OPPO Sans';
|
|
||||||
font-size: 2.67vh;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
// line-height: 48px; /* 150% */
|
|
||||||
line-height: 1.8em;
|
|
||||||
// overflow-x: hidden;
|
|
||||||
}
|
|
||||||
&-voice {
|
|
||||||
padding-left: 40px;
|
|
||||||
margin-top: 2vh;
|
|
||||||
}
|
|
||||||
&-children {
|
|
||||||
padding-left: 40px;
|
|
||||||
color: $font-color;
|
|
||||||
font-family: 'OPPO Sans';
|
|
||||||
font-size: 2.67vh;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 4vh; /* 150% */
|
|
||||||
// &-item {
|
|
||||||
// display: flex;
|
|
||||||
// flex-wrap: wrap;
|
|
||||||
// }
|
|
||||||
margin-top: 1.33vh;
|
|
||||||
.left-title {
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.right {
|
|
||||||
width: calc(50% - 3px);
|
|
||||||
height: 100%;
|
|
||||||
padding-left: 48px;
|
|
||||||
padding-right: 40px;
|
|
||||||
padding-top: 1.33vh;
|
|
||||||
.result-box {
|
|
||||||
width: 100%;
|
|
||||||
height: 4vh;
|
|
||||||
margin-bottom: 2vh;
|
|
||||||
}
|
|
||||||
.result {
|
|
||||||
padding: 0 20px 0 32px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
position: relative;
|
|
||||||
color: $font-color;
|
|
||||||
font-family: $font-special;
|
|
||||||
font-size: 2.67vh;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 4vh; /* 150% */
|
|
||||||
border-radius: 0.33vh;
|
|
||||||
&::before {
|
|
||||||
content: '';
|
|
||||||
width: 12px;
|
|
||||||
height: 100%;
|
|
||||||
flex-shrink: 0;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
background-color: #669cfe;
|
|
||||||
border-radius: 0 1vh 1vh 0;
|
|
||||||
}
|
|
||||||
&.error {
|
|
||||||
background: linear-gradient(90deg, rgba(255, 191, 191, 0) 0%, #ffbfbf 100%);
|
|
||||||
}
|
|
||||||
&.success {
|
|
||||||
background: linear-gradient(90deg, rgba(221, 255, 140, 0) 0%, #ddff8c 100%);
|
|
||||||
}
|
|
||||||
&.empty {
|
|
||||||
background: linear-gradient(90deg, rgba(255, 191, 191, 0) 0%, #ffbfbf 100%);
|
|
||||||
}
|
|
||||||
&.wait {
|
|
||||||
background: linear-gradient(90deg, rgba(190, 213, 254, 0) 0%, #bed5fe 100%);
|
|
||||||
}
|
|
||||||
&.info {
|
|
||||||
background: linear-gradient(90deg, rgba(190, 213, 254, 0) 0%, #bed5fe 100%);
|
|
||||||
}
|
|
||||||
.emoji-box {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
.emoji {
|
|
||||||
width: 48px;
|
|
||||||
height: 4vh;
|
|
||||||
margin-right: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.answer-com {
|
|
||||||
// max-height: calc(100% - 100px);
|
|
||||||
}
|
|
||||||
.answer-box {
|
|
||||||
margin-top: 4vh;
|
|
||||||
&-title {
|
|
||||||
padding: 0 20px 0 32px;
|
|
||||||
position: relative;
|
|
||||||
color: $font-color;
|
|
||||||
// font-family: $font-special;
|
|
||||||
font-family: 'OPPO Sans';
|
|
||||||
font-size: 2.67vh;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 4vh; /* 150% */
|
|
||||||
border-radius: 0.33vh;
|
|
||||||
margin-bottom: 2vh;
|
|
||||||
&::before {
|
|
||||||
content: '';
|
|
||||||
width: 12px;
|
|
||||||
height: 100%;
|
|
||||||
flex-shrink: 0;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
background-color: #669cfe;
|
|
||||||
border-radius: 0 1vh 1vh 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&-echarts {
|
|
||||||
height: 28.33vh;
|
|
||||||
}
|
|
||||||
&-main {
|
|
||||||
color: $font-color;
|
|
||||||
font-family: 'OPPO Sans';
|
|
||||||
font-size: 2.67vh;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 4vh; /* 150% */
|
|
||||||
padding-left: 32px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%;
|
|
||||||
&.analyze {
|
|
||||||
// padding-left: 0;
|
|
||||||
width: 100%;
|
|
||||||
&.analyze-pic {
|
|
||||||
margin-top: 2vh;
|
|
||||||
img {
|
|
||||||
max-width: 100%;
|
|
||||||
max-height: 66.67vh;
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
.titleAnalyzePicture {
|
|
||||||
margin-bottom: 1vh;
|
|
||||||
&:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.analyze-title {
|
|
||||||
margin-top: 2vh;
|
|
||||||
|
|
||||||
.titleAnalyze {
|
|
||||||
color: $font-color;
|
|
||||||
font-family: 'OPPO Sans';
|
|
||||||
font-size: 2.67vh;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 4vh; /* 150% */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.text-box {
|
|
||||||
// color: $font-color;
|
|
||||||
// font-family: $font-special;
|
|
||||||
font-size: 2.67vh;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 4vh; /* 150% */
|
|
||||||
padding-left: 48px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.xl-dia {
|
|
||||||
:deep(.xl-dia-main) {
|
|
||||||
padding: 2.33vh;
|
|
||||||
}
|
|
||||||
.stu_list {
|
|
||||||
display: flex;
|
|
||||||
align-items: start;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
height: 100%;
|
|
||||||
@include hidden_scroll;
|
|
||||||
&-item {
|
|
||||||
border-radius: 3.5vh;
|
|
||||||
border: 0.17vh solid $font-color;
|
|
||||||
padding: 1.33vh 32px;
|
|
||||||
color: $font-color;
|
|
||||||
text-align: center;
|
|
||||||
font-family: OPPOSans;
|
|
||||||
font-size: 2.67vh;
|
|
||||||
margin: 1vh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,584 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import homeWorkApi from '@/api/system/homeWork'
|
|
||||||
import globalApi from '@/api/global'
|
|
||||||
import { debounce, XMessage } from '@/hooks'
|
|
||||||
// import Question from './components/Question.vue'
|
|
||||||
import dayjs from 'dayjs'
|
|
||||||
import { useRoute, useRouter, onBeforeRouteLeave } from 'vue-router'
|
|
||||||
import { $XXL, getDurationText } from '@/utils'
|
|
||||||
import { loading } from '@/components/mj/mj-loading'
|
|
||||||
import { userStore } from '@/stores'
|
|
||||||
import type { IQuestion } from '@/types/question'
|
|
||||||
import { globalBack } from '@/router'
|
|
||||||
import { dialog } from '@/components/mj/mj-dialog'
|
|
||||||
defineOptions({
|
|
||||||
name: 'HomeWorkDoWork',
|
|
||||||
})
|
|
||||||
|
|
||||||
type PaperInfo = {
|
|
||||||
name: string
|
|
||||||
costTime: number
|
|
||||||
currentTitleId: string
|
|
||||||
}
|
|
||||||
|
|
||||||
const { token } = userStore()
|
|
||||||
|
|
||||||
const router = useRouter()
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
const pageLoading = ref(true)
|
|
||||||
// 是否为答题页面 0 答题 1 查看作业结果
|
|
||||||
const reportFlag = ref(0)
|
|
||||||
|
|
||||||
// 题目列表
|
|
||||||
interface WorkQuestion extends IQuestion {
|
|
||||||
/** 还未做过这道题 */
|
|
||||||
unLook: boolean
|
|
||||||
/** 上传中 */
|
|
||||||
uploadLoading: boolean
|
|
||||||
}
|
|
||||||
const paperList = ref<WorkQuestion[]>([])
|
|
||||||
// 试卷信息
|
|
||||||
const paperInfo = ref<PaperInfo>({} as PaperInfo)
|
|
||||||
const isDeadline = ref(false)
|
|
||||||
|
|
||||||
// 当前所在题目
|
|
||||||
const activeIdx = ref(-1)
|
|
||||||
const indicatorRef = ref()
|
|
||||||
|
|
||||||
// 切换题目
|
|
||||||
async function switchQuestion(idx?: number) {
|
|
||||||
if (idx === activeIdx.value) return
|
|
||||||
// 最后一题的索引
|
|
||||||
const lastIdx = paperList.value.length - 1
|
|
||||||
// 是否为点击下一题
|
|
||||||
let nextQuestion = false
|
|
||||||
if (idx !== undefined) {
|
|
||||||
activeIdx.value = idx
|
|
||||||
} else if (activeIdx.value < lastIdx) {
|
|
||||||
// 点击下一题
|
|
||||||
activeIdx.value++
|
|
||||||
nextQuestion = true
|
|
||||||
} else if (activeIdx.value === lastIdx) {
|
|
||||||
if (!reportFlag.value) {
|
|
||||||
// 提交作业
|
|
||||||
if (paperList.value.some(i => i.uploadLoading)) {
|
|
||||||
return XMessage.error('上传中,请稍后~')
|
|
||||||
}
|
|
||||||
let tips = '请确认是否提交本次作业'
|
|
||||||
if (paperList.value.some(i => !i.submitAnswer && !i.submitAnswerPic)) {
|
|
||||||
tips = '还有题目未作答,确定要提交答案吗?'
|
|
||||||
}
|
|
||||||
dialog.show({
|
|
||||||
content: tips,
|
|
||||||
ok: handleSubmitPaper,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!reportFlag.value) {
|
|
||||||
paperList.value[activeIdx.value].unLook = false
|
|
||||||
|
|
||||||
// 因为服务器返回的题目顺序是打乱的,所以上报当前正在查看的题目已经米有意义
|
|
||||||
// 每次切换题目调一次接口
|
|
||||||
// const { id, jobStuId } = paperList.value[activeIdx.value]
|
|
||||||
|
|
||||||
// submitCurrentTitleId({
|
|
||||||
// jobStuId,
|
|
||||||
// currentTitleId: id,
|
|
||||||
// costTime: paperInfo.value.costTime * 1000
|
|
||||||
// })
|
|
||||||
}
|
|
||||||
|
|
||||||
// 点击下一题时下方往后滑动
|
|
||||||
await nextTick()
|
|
||||||
if (nextQuestion && indicatorRef.value) {
|
|
||||||
indicatorRef.value.scrollLeft += 104
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const debounceChange = debounce(switchQuestion, 200)
|
|
||||||
|
|
||||||
function getResourceType() {
|
|
||||||
return route.query.resource
|
|
||||||
}
|
|
||||||
|
|
||||||
// 上传前
|
|
||||||
function beforeUpload(rawFile: any) {
|
|
||||||
const fileType = ['image/jpeg', 'image/png']
|
|
||||||
if (!fileType.includes(rawFile.type)) {
|
|
||||||
XMessage.error('上传格式不正确,只支持JPG/PNG')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (rawFile.size / 1024 / 1024 > 5) {
|
|
||||||
XMessage.error('图片过大')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
// 上传
|
|
||||||
async function uploadFile(options: anyObj) {
|
|
||||||
const currentTitleId = paperList.value[activeIdx.value].jobStuTittleId
|
|
||||||
paperList.value[activeIdx.value].uploadLoading = true
|
|
||||||
|
|
||||||
try {
|
|
||||||
let path = ''
|
|
||||||
if (options.isAndroid) {
|
|
||||||
if (options.submitAnswerPicOriginal) {
|
|
||||||
const res = await $XXL.uploadFile({
|
|
||||||
url: `${import.meta.env.VITE_SERVER_URL}/sysFileInfo/tenUploadAll`,
|
|
||||||
header: {
|
|
||||||
Authorization: token,
|
|
||||||
},
|
|
||||||
filePath: options.file,
|
|
||||||
fileKey: 'file',
|
|
||||||
})
|
|
||||||
path = res.url
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const formData = new FormData()
|
|
||||||
formData.append('file', options.file)
|
|
||||||
const { data: res } = await globalApi.uploadFile(formData)
|
|
||||||
path = res.data.filePath
|
|
||||||
}
|
|
||||||
if (options.submitAnswerPicOriginal) {
|
|
||||||
handleSubmitSingle({
|
|
||||||
submitAnswerPic: path,
|
|
||||||
submitAnswerPicOriginal: options.submitAnswerPicOriginal,
|
|
||||||
id: currentTitleId,
|
|
||||||
isAndroid: options.isAndroid || 0,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
if (options.isAndroid) {
|
|
||||||
handleSubmitSingle({
|
|
||||||
submitAnswerPic: '',
|
|
||||||
submitAnswerPicOriginal: '',
|
|
||||||
id: currentTitleId,
|
|
||||||
isAndroid: 1,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
handleSubmitSingle({ filePath: path, id: currentTitleId })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
XMessage.error('网络异常,请检查网络~')
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentIdx = paperList.value.findIndex(i => i.jobStuTittleId === currentTitleId)
|
|
||||||
if (currentIdx !== -1) {
|
|
||||||
paperList.value[currentIdx].uploadLoading = false
|
|
||||||
paperList.value[currentIdx].isAndroid = options.isAndroid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 提交每道题目
|
|
||||||
async function handleSubmitSingle(answer: any) {
|
|
||||||
if (reportFlag.value) return
|
|
||||||
const params: anyObj = {
|
|
||||||
id: answer.id,
|
|
||||||
isAndroid: answer.isAndroid,
|
|
||||||
}
|
|
||||||
if (answer.filePath) {
|
|
||||||
// 添加图片
|
|
||||||
const idx = paperList.value.findIndex(i => i.jobStuTittleId === answer.id)
|
|
||||||
if (idx !== -1) {
|
|
||||||
const safeUrl = String(answer.filePath || '')
|
|
||||||
.replace(/[`]/g, '')
|
|
||||||
.replace(/http:\/\//gi, 'https://')
|
|
||||||
.trim()
|
|
||||||
const q = paperList.value[idx]
|
|
||||||
const isHandDrawn =
|
|
||||||
q?.autoMarkFlag === 0 &&
|
|
||||||
(q?.titleChannelTypeName === '填空题' ||
|
|
||||||
(q?.subjectId === 3 && q?.titleChannelTypeName === '解答题'))
|
|
||||||
const submitPic = isHandDrawn
|
|
||||||
? safeUrl
|
|
||||||
: q.submitAnswerPic || ''
|
|
||||||
? `${q.submitAnswerPic},${safeUrl}`
|
|
||||||
: safeUrl
|
|
||||||
|
|
||||||
paperList.value[idx].submitAnswerPic = submitPic
|
|
||||||
params.submitAnswerPic = submitPic
|
|
||||||
}
|
|
||||||
} else if (answer.submitAnswer) {
|
|
||||||
paperList.value[activeIdx.value].submitAnswer = answer.submitAnswer
|
|
||||||
params.submitAnswer = answer.submitAnswer
|
|
||||||
} else if (answer.delUrl) {
|
|
||||||
// 删除图片
|
|
||||||
const picList = paperList.value[activeIdx.value].submitAnswerPic.split(',') || []
|
|
||||||
const idx = picList.indexOf(answer.delUrl)
|
|
||||||
picList.splice(idx, 1)
|
|
||||||
params.submitAnswerPic = picList.join(',')
|
|
||||||
paperList.value[activeIdx.value].submitAnswerPic = params.submitAnswerPic
|
|
||||||
} else if (answer.submitAnswerPic) {
|
|
||||||
// 替换图片
|
|
||||||
const idx = paperList.value.findIndex(i => i.jobStuTittleId === answer.id)
|
|
||||||
paperList.value[idx].submitAnswerPic = answer.submitAnswerPic
|
|
||||||
paperList.value[idx].submitAnswerPicOriginal = answer.submitAnswerPicOriginal
|
|
||||||
params.submitAnswerPic = answer.submitAnswerPic
|
|
||||||
params.submitAnswerPicOriginal = answer.submitAnswerPicOriginal
|
|
||||||
} else if (answer.submitAnswerPic === '' && answer.isAndroid) {
|
|
||||||
// 清空图片
|
|
||||||
const idx = paperList.value.findIndex(i => i.jobStuTittleId === answer.id)
|
|
||||||
paperList.value[idx].submitAnswerPic = ''
|
|
||||||
paperList.value[idx].submitAnswerPicOriginal = ''
|
|
||||||
params.submitAnswerPic = ''
|
|
||||||
params.submitAnswerPicOriginal = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
// 单选题直接跳转下一题
|
|
||||||
if (answer.toNext && activeIdx.value < paperList.value.length - 1) setTimeout(switchQuestion, 50)
|
|
||||||
|
|
||||||
await homeWorkApi.submitAnswer(params)
|
|
||||||
}
|
|
||||||
|
|
||||||
let timeout: any
|
|
||||||
function startTime() {
|
|
||||||
clearInterval(timeout)
|
|
||||||
timeout = setInterval(() => {
|
|
||||||
paperInfo.value.costTime++
|
|
||||||
}, 1000)
|
|
||||||
}
|
|
||||||
const timeFilter = computed(() => getDurationText(paperInfo.value.costTime))
|
|
||||||
|
|
||||||
function handleTime(type: 0 | 1) {
|
|
||||||
if (type) {
|
|
||||||
startTime()
|
|
||||||
} else {
|
|
||||||
clearInterval(timeout)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleSubmitPaper() {
|
|
||||||
clearInterval(timeout)
|
|
||||||
const { jobId, jobStuId } = paperList.value[activeIdx.value]
|
|
||||||
|
|
||||||
loading.show('正在提交中...')
|
|
||||||
try {
|
|
||||||
await homeWorkApi.submitPaper({
|
|
||||||
id: jobStuId,
|
|
||||||
jobId,
|
|
||||||
costTime: paperInfo.value.costTime * 1000,
|
|
||||||
})
|
|
||||||
// 标记为可以离开
|
|
||||||
leave.value = true
|
|
||||||
// 成功后跳转
|
|
||||||
router.replace('/system/homeWork/reports/' + jobId)
|
|
||||||
} finally {
|
|
||||||
loading.hide()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 题目是否有提交过答案
|
|
||||||
function hasSubmitAnswer(question: WorkQuestion) {
|
|
||||||
return question.submitAnswer || question.submitAnswerPic || question.submitAnswerPicOriginal
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取试卷信息
|
|
||||||
async function getPaper(options: any) {
|
|
||||||
// 请求试卷数据
|
|
||||||
const { data } = await homeWorkApi.getJobTittle(options)
|
|
||||||
paperInfo.value = {
|
|
||||||
name: data.paperName,
|
|
||||||
costTime: data.costTime / 1000,
|
|
||||||
currentTitleId: data.currentTitleId,
|
|
||||||
}
|
|
||||||
const oldQuestions = data.tjobStuVoList
|
|
||||||
// 是否到了作业的截止时间
|
|
||||||
isDeadline.value = dayjs().isAfter(data.submitEndTime)
|
|
||||||
if (!reportFlag.value) {
|
|
||||||
// 将已经答过的题目放在前面
|
|
||||||
const answeredQuestions: WorkQuestion[] = []
|
|
||||||
const unansweredQuestions: WorkQuestion[] = []
|
|
||||||
let idx = 0
|
|
||||||
for (const question of oldQuestions) {
|
|
||||||
if (hasSubmitAnswer(question)) {
|
|
||||||
answeredQuestions.push(question)
|
|
||||||
idx += 1
|
|
||||||
} else {
|
|
||||||
question.unLook = true
|
|
||||||
unansweredQuestions.push(question)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
paperList.value = [...answeredQuestions, ...unansweredQuestions]
|
|
||||||
return Math.min(idx, paperList.value.length - 1)
|
|
||||||
} else {
|
|
||||||
paperList.value = oldQuestions
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const leave = ref(true)
|
|
||||||
onBeforeRouteLeave((to, _from, next) => {
|
|
||||||
if (leave.value || to.path === '/login' || reportFlag.value) {
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
next(false)
|
|
||||||
|
|
||||||
dialog.show({
|
|
||||||
content: '还没有提交作业,是否确认退出?',
|
|
||||||
ok: () => {
|
|
||||||
leave.value = true
|
|
||||||
globalBack()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
const options: anyObj = route.query || {}
|
|
||||||
if (!options?.id) {
|
|
||||||
// 没有试卷的id
|
|
||||||
globalBack()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 是否为报告
|
|
||||||
reportFlag.value = Number(options.reportFlag)
|
|
||||||
|
|
||||||
// 请求数据
|
|
||||||
pageLoading.value = true
|
|
||||||
const currentIdx = await getPaper(options)
|
|
||||||
pageLoading.value = false
|
|
||||||
|
|
||||||
// 数据记载完毕后,就不准直接离开
|
|
||||||
leave.value = false
|
|
||||||
|
|
||||||
// 切换题目
|
|
||||||
switchQuestion(options?.idx ? Number(options?.idx) : currentIdx)
|
|
||||||
|
|
||||||
if (!reportFlag.value) {
|
|
||||||
startTime()
|
|
||||||
}
|
|
||||||
|
|
||||||
await nextTick()
|
|
||||||
if (indicatorRef.value) {
|
|
||||||
indicatorRef.value.scrollLeft = ((options?.idx ? Number(options?.idx) : currentIdx) - 1) * 104
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置屏幕常亮
|
|
||||||
$XXL.setScreenOn(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
paperInfo.value.costTime = 0
|
|
||||||
activeIdx.value = 0
|
|
||||||
paperList.value = []
|
|
||||||
paperInfo.value = {} as PaperInfo
|
|
||||||
|
|
||||||
// showQuesImgDia.value = false;
|
|
||||||
// quesImg.value = '';
|
|
||||||
// showDia.value = false
|
|
||||||
// leaveFlag = false;
|
|
||||||
clearInterval(timeout)
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="dowork g-main-bg">
|
|
||||||
<div class="title-box">
|
|
||||||
<div class="title" :data-content="paperInfo.name">
|
|
||||||
{{ paperInfo.name }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="top">
|
|
||||||
<BackBar />
|
|
||||||
|
|
||||||
<div v-if="!pageLoading" class="top-timeout">
|
|
||||||
<Icon name="clock" width="48"></Icon>
|
|
||||||
{{ timeFilter }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="!pageLoading" class="main">
|
|
||||||
<div class="main-box">
|
|
||||||
<div class="book-box">
|
|
||||||
<div class="book">
|
|
||||||
<Question
|
|
||||||
:data="paperList[activeIdx]"
|
|
||||||
:idx="activeIdx + 1"
|
|
||||||
:is-homework="true"
|
|
||||||
:total="paperList.length"
|
|
||||||
:reportFlag="reportFlag"
|
|
||||||
:isDeadline="isDeadline"
|
|
||||||
:resource-type="getResourceType()"
|
|
||||||
:uploadFile="uploadFile"
|
|
||||||
:beforeUpload="beforeUpload"
|
|
||||||
@submit="handleSubmitSingle"
|
|
||||||
@handleTime="handleTime"
|
|
||||||
></Question>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="bottom">
|
|
||||||
<div ref="indicatorRef" :class="{ indicator: true, hidden_scroll: true }">
|
|
||||||
<div
|
|
||||||
v-for="(i, idx) in paperList"
|
|
||||||
v-show="!i.unLook"
|
|
||||||
:key="i.id"
|
|
||||||
:class="{
|
|
||||||
'indicator-item': true,
|
|
||||||
pointer: true,
|
|
||||||
active: activeIdx === idx,
|
|
||||||
success: isDeadline && reportFlag && i.markFlag,
|
|
||||||
error: isDeadline && reportFlag && !i.markFlag && i.titleStatus === 3,
|
|
||||||
empty: !i.submitAnswer && !i.submitAnswerPic,
|
|
||||||
}"
|
|
||||||
@click="switchQuestion(idx)"
|
|
||||||
>
|
|
||||||
{{ idx + 1 }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Btn
|
|
||||||
v-if="!reportFlag"
|
|
||||||
class="btn"
|
|
||||||
type="success"
|
|
||||||
width="192"
|
|
||||||
height="80"
|
|
||||||
size="32"
|
|
||||||
@click="debounceChange()"
|
|
||||||
>
|
|
||||||
{{ activeIdx < paperList.length - 1 ? '下一题' : '提交' }}
|
|
||||||
</Btn>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<mj-loading v-else v-model="pageLoading" :fullscreen="true" text="正在加载试卷中..." />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<style scoped lang="scss">
|
|
||||||
$filter: brightness(0) saturate(0) invert(0) sepia(0) hue-rotate(30deg);
|
|
||||||
.dowork {
|
|
||||||
position: relative;
|
|
||||||
height: 100vh;
|
|
||||||
overflow: hidden;
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
.title-box {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
top: 2.92vh;
|
|
||||||
.title {
|
|
||||||
color: #fff;
|
|
||||||
text-shadow: 0 0.17vh 0.17vh rgba(0, 0, 0, 0.25);
|
|
||||||
font-family: $font-special;
|
|
||||||
font-size: 4vh;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 6vh; /* 150% */
|
|
||||||
text-align: center;
|
|
||||||
letter-spacing: 10px;
|
|
||||||
@include font-stroke($font-color, 6px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.top {
|
|
||||||
padding: 2.67vh 64px 0;
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
&-timeout {
|
|
||||||
color: $font-color;
|
|
||||||
text-align: center;
|
|
||||||
text-shadow: 0 0.17vh 0.17vh rgba(0, 0, 0, 0.25);
|
|
||||||
font-family: $font-special;
|
|
||||||
font-size: 2.67vh;
|
|
||||||
height: 4vh;
|
|
||||||
line-height: 4vh;
|
|
||||||
display: flex;
|
|
||||||
.x-icon {
|
|
||||||
margin-right: 10px;
|
|
||||||
filter: $filter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.main {
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 0 32px;
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
&-box {
|
|
||||||
position: relative;
|
|
||||||
top: 10.5vh;
|
|
||||||
}
|
|
||||||
.book-box {
|
|
||||||
position: relative;
|
|
||||||
height: 80vh;
|
|
||||||
padding-top: 1.25vh;
|
|
||||||
}
|
|
||||||
.book {
|
|
||||||
box-sizing: border-box;
|
|
||||||
position: relative;
|
|
||||||
z-index: auto;
|
|
||||||
height: 80vh;
|
|
||||||
flex-shrink: 0;
|
|
||||||
border-radius: 3.33vh;
|
|
||||||
padding-bottom: 5.83vh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.bottom {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
padding: 0 32px;
|
|
||||||
.indicator {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
overflow-x: auto;
|
|
||||||
padding: 0.83vh 0;
|
|
||||||
|
|
||||||
&-item {
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 80px;
|
|
||||||
height: 6.67vh;
|
|
||||||
background: #bed5fe;
|
|
||||||
// border: 4px solid #fff;
|
|
||||||
border-radius: 50%;
|
|
||||||
margin-right: 24px;
|
|
||||||
color: $font-color;
|
|
||||||
text-align: center;
|
|
||||||
font-family: $font-special;
|
|
||||||
font-size: 3.33vh;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 6.67vh; /* 150% */
|
|
||||||
text-align: center;
|
|
||||||
flex-shrink: 0;
|
|
||||||
box-shadow: 0 0.33vh 0.33vh 0 rgba(0, 0, 0, 0.25);
|
|
||||||
&:last-child {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
&.error {
|
|
||||||
background: #febebf;
|
|
||||||
border-color: #f00809;
|
|
||||||
color: #f00809;
|
|
||||||
}
|
|
||||||
&.success {
|
|
||||||
background: #ddff8c;
|
|
||||||
border-color: #8ecc00;
|
|
||||||
color: #8ecc00;
|
|
||||||
}
|
|
||||||
&.empty {
|
|
||||||
background: #cccccc;
|
|
||||||
color: #ffffff;
|
|
||||||
border-color: #aaa;
|
|
||||||
}
|
|
||||||
&.active {
|
|
||||||
background: #ebf1fd;
|
|
||||||
border: 0.33vh solid #7eacfe;
|
|
||||||
line-height: 6vh;
|
|
||||||
|
|
||||||
&.empty {
|
|
||||||
color: #7eacfe;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.btn {
|
|
||||||
margin-left: 62px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,473 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import dayjs from 'dayjs'
|
|
||||||
import { rateType } from '@/hooks'
|
|
||||||
import homeWorkApi from '@/api/system/homeWork'
|
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
import { userStore } from '@/stores'
|
|
||||||
import { storeToRefs } from 'pinia'
|
|
||||||
|
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
const OSS_URL = import.meta.env.VITE_OSS_URL
|
|
||||||
|
|
||||||
const { userInfo } = storeToRefs(userStore())
|
|
||||||
|
|
||||||
const colorConfig = {
|
|
||||||
// 字体颜色 progerss_border progress_bg progress_active
|
|
||||||
primary: ['#2D39BC', '#2D39BC', '#6B73E5', '#90FCF7'],
|
|
||||||
success: ['#2F8A5B', '#27A363', '#25BF6E', '#1FFF8A'],
|
|
||||||
error: ['#BC2D37', '#B22731', '#CB3741', '#FF3542'],
|
|
||||||
warning: ['#a13c00', '#C08129', '#CC8A33', '#FFFE36'],
|
|
||||||
}
|
|
||||||
|
|
||||||
const rateConfig = computed(() => {
|
|
||||||
return (rate: number) => {
|
|
||||||
const r = Number(rate) * 100
|
|
||||||
return {
|
|
||||||
color: colorConfig[rateType.value(r) || 'primary'][0],
|
|
||||||
border: colorConfig[rateType.value(r) || 'primary'][1],
|
|
||||||
bg: colorConfig[rateType.value(r) || 'primary'][2],
|
|
||||||
activeBg: colorConfig[rateType.value(r) || 'primary'][3],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// 筛选状态
|
|
||||||
const activeStatus = ref('')
|
|
||||||
function handleChangeStatus() {
|
|
||||||
refreshJobList()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 作业科目列表
|
|
||||||
const classList = ref<any[]>([])
|
|
||||||
async function getClassList() {
|
|
||||||
const { data: res } = await homeWorkApi.getSubjectList({ statusList: [1, 2, 3], historyFlag: 1 })
|
|
||||||
if (res.code === 200) {
|
|
||||||
classList.value = res.data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 当前选中的科目
|
|
||||||
const activeClass = ref<number>(0)
|
|
||||||
function handleSelectClass(_item: any) {
|
|
||||||
refreshJobList()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 作业列表
|
|
||||||
const jobList = ref<any[]>([])
|
|
||||||
const page = reactive({
|
|
||||||
pageSize: 4,
|
|
||||||
pageNum: 1,
|
|
||||||
total: -1,
|
|
||||||
})
|
|
||||||
const listLoading = ref(false)
|
|
||||||
async function getJobList() {
|
|
||||||
const { data: res } = await homeWorkApi.getJobList({
|
|
||||||
statusList: activeStatus.value ? (activeStatus.value === '3' ? [3] : [1, 2]) : [1, 2, 3],
|
|
||||||
...page,
|
|
||||||
subjectId: activeClass.value,
|
|
||||||
historyFlag: 1,
|
|
||||||
})
|
|
||||||
if (res.code === 200) {
|
|
||||||
jobList.value = res.rows
|
|
||||||
page.total = res.total
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const pageError = ref(false)
|
|
||||||
async function changePage(type: 0 | 1) {
|
|
||||||
pageError.value = false
|
|
||||||
const { total, pageNum, pageSize } = page
|
|
||||||
if (type) {
|
|
||||||
if (pageNum * pageSize >= total) return
|
|
||||||
page.pageNum++
|
|
||||||
} else {
|
|
||||||
if (pageNum <= 1) return
|
|
||||||
page.pageNum--
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
listLoading.value = true
|
|
||||||
jobList.value = []
|
|
||||||
await getJobList()
|
|
||||||
} catch {
|
|
||||||
pageError.value = true
|
|
||||||
}
|
|
||||||
listLoading.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
async function refreshJobList() {
|
|
||||||
pageError.value = false
|
|
||||||
listLoading.value = true
|
|
||||||
jobList.value = []
|
|
||||||
page.pageNum = 1
|
|
||||||
try {
|
|
||||||
await getJobList()
|
|
||||||
} catch {
|
|
||||||
pageError.value = true
|
|
||||||
}
|
|
||||||
listLoading.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
const pageLoading = ref(false)
|
|
||||||
onActivated(async () => {
|
|
||||||
pageLoading.value = true
|
|
||||||
listLoading.value = true
|
|
||||||
jobList.value = []
|
|
||||||
page.pageNum = 1
|
|
||||||
try {
|
|
||||||
await getClassList()
|
|
||||||
if (classList.value.length) {
|
|
||||||
if (classList.value.every(i => i.id !== activeClass.value)) {
|
|
||||||
activeClass.value = classList.value[0]?.id
|
|
||||||
}
|
|
||||||
await getJobList()
|
|
||||||
}
|
|
||||||
} catch {}
|
|
||||||
listLoading.value = false
|
|
||||||
pageLoading.value = false
|
|
||||||
})
|
|
||||||
|
|
||||||
function toReports(item: any) {
|
|
||||||
router.push(`/system/homeWork/reports/${item.id}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 是否到截止时间
|
|
||||||
const isDeadline = computed(() => (time: string) => dayjs().isAfter(time))
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="uncomplete">
|
|
||||||
<div class="top">
|
|
||||||
<BackBar leftText="作业记录" />
|
|
||||||
<Select
|
|
||||||
v-model="activeStatus"
|
|
||||||
:selectList="[
|
|
||||||
{ label: '全部', value: '' },
|
|
||||||
{ label: '已批改', value: '3' },
|
|
||||||
{ label: '未批改', value: '2' },
|
|
||||||
]"
|
|
||||||
@selectChange="handleChangeStatus"
|
|
||||||
></Select>
|
|
||||||
</div>
|
|
||||||
<div v-if="classList.length" class="main">
|
|
||||||
<div class="main-sidebar">
|
|
||||||
<Sidebar
|
|
||||||
v-model="activeClass"
|
|
||||||
:config="classList"
|
|
||||||
:prop="{ name: 'subject' }"
|
|
||||||
@handle-change="handleSelectClass"
|
|
||||||
></Sidebar>
|
|
||||||
</div>
|
|
||||||
<div class="list-box">
|
|
||||||
<template v-if="jobList.length">
|
|
||||||
<div class="list hidden_scroll">
|
|
||||||
<div ref="scrollRef">
|
|
||||||
<div v-for="i in jobList" :key="i.id" class="list-item pointer" @click="toReports(i)">
|
|
||||||
<div class="list-item-info">
|
|
||||||
<div class="list-item-title">{{ i.name }}</div>
|
|
||||||
<div class="list-item-name">{{ i.paperName }}</div>
|
|
||||||
<div class="list-item-time">
|
|
||||||
{{
|
|
||||||
i.createTime && `发布时间:${dayjs(i.createTime).format('YYYY/MM/DD HH:mm')}`
|
|
||||||
}}
|
|
||||||
|
|
||||||
{{
|
|
||||||
i.submitEndTime &&
|
|
||||||
`截止时间:${dayjs(i.submitEndTime).format('YYYY/MM/DD HH:mm')}`
|
|
||||||
}}
|
|
||||||
|
|
||||||
{{ i.markTime && `批改时间:${dayjs(i.markTime).format('YYYY/MM/DD HH:mm')}` }}
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="i.status === 3 && isDeadline(i.submitEndTime)"
|
|
||||||
class="list-item-rate"
|
|
||||||
:style="{ color: rateConfig(i.scoreRate).color }"
|
|
||||||
>
|
|
||||||
得分率:{{ i.scoreRate }}%
|
|
||||||
<ProgressBar
|
|
||||||
:rate="i.scoreRate / 100"
|
|
||||||
width="196"
|
|
||||||
v-bind="rateConfig(i.scoreRate)"
|
|
||||||
></ProgressBar>
|
|
||||||
<div class="reward-list">
|
|
||||||
<div v-if="i.addDiamond" class="reward-list-item">
|
|
||||||
<ItemPopover
|
|
||||||
:data="{
|
|
||||||
iconName: 'reward_diamond',
|
|
||||||
rewardName: '钻石',
|
|
||||||
personalCount: userInfo.diamond,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<Icon name="diamond_nobg" width="48" @click.stop="false"></Icon>
|
|
||||||
</ItemPopover>
|
|
||||||
<span class="text">+{{ i.addDiamond }}</span>
|
|
||||||
</div>
|
|
||||||
<div v-if="i.addExp" class="reward-list-item">
|
|
||||||
<ItemPopover
|
|
||||||
:data="{
|
|
||||||
iconName: 'reward_exp',
|
|
||||||
rewardName: '经验值',
|
|
||||||
personalCount: userInfo.experience,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<Icon name="exp_nobg" width="48" @click.stop="false"></Icon>
|
|
||||||
</ItemPopover>
|
|
||||||
<span class="text">+{{ i.addExp }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
:class="{ 'list-item-status': true, active: i.status === 3 }"
|
|
||||||
:style="{
|
|
||||||
'background-image':
|
|
||||||
i.status === 3
|
|
||||||
? `url(${OSS_URL}/urm/homeWork/corrected.svg)`
|
|
||||||
: `url(${OSS_URL}/urm/homeWork/uncorrected.svg)`,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
{{ i.status === 3 ? '已批改' : '未批改' }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="page">
|
|
||||||
<Btn v-if="page.pageNum > 1" width="162" height="80" tilt @click="changePage(0)">
|
|
||||||
上一页
|
|
||||||
</Btn>
|
|
||||||
<div>{{ page.pageNum }}/{{ Math.ceil(page.total / page.pageSize) }}</div>
|
|
||||||
<Btn
|
|
||||||
v-if="page.pageNum * page.pageSize < page.total"
|
|
||||||
type="success"
|
|
||||||
width="162"
|
|
||||||
height="80"
|
|
||||||
tilt
|
|
||||||
@click="changePage(1)"
|
|
||||||
>
|
|
||||||
下一页
|
|
||||||
</Btn>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<Empty
|
|
||||||
v-else
|
|
||||||
class="empty"
|
|
||||||
:type="listLoading ? 'loading' : pageError ? 'network' : 'learn'"
|
|
||||||
:content="pageError ? '网络出问题了啦' : '暂无作业记录,快去做作业吧!'"
|
|
||||||
></Empty>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Empty
|
|
||||||
v-else
|
|
||||||
class="empty"
|
|
||||||
:type="pageLoading ? 'loading' : 'learn'"
|
|
||||||
content="暂无作业记录,快去做作业吧!"
|
|
||||||
></Empty>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.uncomplete {
|
|
||||||
@include main_bg;
|
|
||||||
height: 100vh;
|
|
||||||
overflow: hidden;
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
.top {
|
|
||||||
padding: 3vh 64px 0;
|
|
||||||
// margin-bottom: 24px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: flex-end;
|
|
||||||
// &-menu {
|
|
||||||
// display: flex;
|
|
||||||
// justify-content: space-between;
|
|
||||||
// align-items: center;
|
|
||||||
// // flex: 1;
|
|
||||||
// padding-left: 80px;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
.main {
|
|
||||||
// display: flex;
|
|
||||||
height: calc(100% - 130px);
|
|
||||||
padding-left: 64px;
|
|
||||||
padding-right: 64px;
|
|
||||||
padding-top: 2.67vh;
|
|
||||||
position: relative;
|
|
||||||
&-sidebar {
|
|
||||||
position: absolute;
|
|
||||||
left: 64px;
|
|
||||||
top: 2.67vh;
|
|
||||||
height: calc(100% - 66px);
|
|
||||||
width: 272px;
|
|
||||||
}
|
|
||||||
.list-box {
|
|
||||||
height: 100%;
|
|
||||||
margin-left: 310px;
|
|
||||||
overflow: hidden;
|
|
||||||
.page {
|
|
||||||
margin-top: 0.83vh;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
color: #666;
|
|
||||||
font-family: $font-special;
|
|
||||||
font-size: 2.67vh;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 4vh;
|
|
||||||
justify-content: flex-end;
|
|
||||||
:deep(.x-button) {
|
|
||||||
box-shadow: none;
|
|
||||||
margin: 0 40px;
|
|
||||||
&.primary {
|
|
||||||
margin-right: 80px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.list {
|
|
||||||
height: calc(100% - 90px);
|
|
||||||
overflow-y: auto;
|
|
||||||
&-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
border-radius: 1.67vh;
|
|
||||||
background: #fff;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
margin-bottom: 1.33vh;
|
|
||||||
&:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
&-info {
|
|
||||||
width: calc(100% - 230px);
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
&-title {
|
|
||||||
// display: inline-block;
|
|
||||||
padding: 0 8px;
|
|
||||||
height: 6.67vh;
|
|
||||||
line-height: 6.67vh;
|
|
||||||
background: #7effff;
|
|
||||||
margin-bottom: 2vh;
|
|
||||||
width: 500px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
text-align: center;
|
|
||||||
// max-width: 100%;
|
|
||||||
color: $font-color;
|
|
||||||
font-family: $font-special;
|
|
||||||
font-size: 2.5vh;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
border-top-left-radius: 20px;
|
|
||||||
@include single_ellipsis;
|
|
||||||
}
|
|
||||||
&-name {
|
|
||||||
color: #666;
|
|
||||||
font-family: 'OPPO Sans';
|
|
||||||
font-size: 2.33vh;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 3.5vh; /* 150% */
|
|
||||||
padding-left: 32px;
|
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 1.33vh;
|
|
||||||
@include single_ellipsis;
|
|
||||||
}
|
|
||||||
&-time {
|
|
||||||
color: #999;
|
|
||||||
font-family: 'OPPO Sans';
|
|
||||||
font-size: 2vh;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 3vh; /* 150% */
|
|
||||||
padding-left: 32px;
|
|
||||||
padding-bottom: 2vh;
|
|
||||||
}
|
|
||||||
&-status {
|
|
||||||
width: 216px;
|
|
||||||
height: 8vh;
|
|
||||||
// background: url('~@/static/unity/schoolJob/uncorrected.svg') no-repeat;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: 100%;
|
|
||||||
color: $font-color;
|
|
||||||
font-family: $font-special;
|
|
||||||
font-size: 2.67vh;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 8vh; /* 150% */
|
|
||||||
text-align: center;
|
|
||||||
// &.active {
|
|
||||||
// background: url('~@/static/unity/schoolJob/corrected.svg') no-repeat;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
&-rate {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
position: absolute;
|
|
||||||
left: 516px;
|
|
||||||
top: 1.83vh;
|
|
||||||
font-family: $font-special;
|
|
||||||
font-size: 2.33vh;
|
|
||||||
line-height: 3vh;
|
|
||||||
.progress-bar {
|
|
||||||
margin-left: 12px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.reward-list {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
margin-left: 12px;
|
|
||||||
&-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin: 0 8px;
|
|
||||||
// color: $font-color;
|
|
||||||
font-family: $font-special;
|
|
||||||
font-size: 2.33vh;
|
|
||||||
.x-icon {
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.empty {
|
|
||||||
height: calc(100% - 156px);
|
|
||||||
position: relative;
|
|
||||||
.box {
|
|
||||||
position: absolute;
|
|
||||||
top: 22.08vh;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
img {
|
|
||||||
width: 320px;
|
|
||||||
height: 26.67vh;
|
|
||||||
}
|
|
||||||
.text {
|
|
||||||
color: #afbfcf;
|
|
||||||
text-align: center;
|
|
||||||
font-family: $font-special;
|
|
||||||
font-size: 2.33vh;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 3.5vh; /* 150% */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.info-time {
|
|
||||||
color: #999;
|
|
||||||
font-family: 'PingFang SC';
|
|
||||||
font-size: 1.67vh;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 2.5vh; /* 150% */
|
|
||||||
margin-top: 0.5vh;
|
|
||||||
span {
|
|
||||||
margin-right: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,506 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import LoadIcon from '@/page/system/mailbox/components/LoadIcon.vue'
|
|
||||||
import homeWorkApi from '@/api/system/homeWork'
|
|
||||||
import { debounce } from '@/hooks'
|
|
||||||
import dayjs from 'dayjs'
|
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
import { $XXL } from '@/utils'
|
|
||||||
|
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
const OSS_URL = import.meta.env.VITE_OSS_URL
|
|
||||||
|
|
||||||
// 作业科目列表
|
|
||||||
const classList = ref<any[]>([])
|
|
||||||
async function getClassList() {
|
|
||||||
const { data: res } = await homeWorkApi.getSubjectList({ statusList: [0] })
|
|
||||||
if (res.code === 200) {
|
|
||||||
classList.value = res.data.map((i: anyObj) => ({ ...i, warning: true }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 当前选中的科目
|
|
||||||
const activeClass = ref('')
|
|
||||||
async function handleSelectClass(_item: any) {
|
|
||||||
jobList.value = []
|
|
||||||
await getJobList()
|
|
||||||
handleSelectJob(jobList.value[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
// 作业列表
|
|
||||||
const jobList = ref<any[]>([])
|
|
||||||
const listLoading = ref(false)
|
|
||||||
const page = reactive({
|
|
||||||
pageSize: 10,
|
|
||||||
pageNum: 1,
|
|
||||||
total: -1,
|
|
||||||
})
|
|
||||||
async function getJobList() {
|
|
||||||
try {
|
|
||||||
listLoading.value = true
|
|
||||||
const res = await homeWorkApi.getJobList({
|
|
||||||
statusList: [0],
|
|
||||||
...page,
|
|
||||||
subjectId: activeClass.value,
|
|
||||||
})
|
|
||||||
jobList.value.push(...res.rows)
|
|
||||||
page.total = res.total
|
|
||||||
} finally {
|
|
||||||
listLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const bookSidebarRef = ref()
|
|
||||||
async function handleScroll(event: any) {
|
|
||||||
if (jobList.value.length >= page.total) return
|
|
||||||
if (listLoading.value) return
|
|
||||||
if (
|
|
||||||
event.target.scrollTop + event.target.offsetHeight >=
|
|
||||||
bookSidebarRef.value?.offsetHeight - 50
|
|
||||||
) {
|
|
||||||
page.pageNum++
|
|
||||||
try {
|
|
||||||
await getJobList()
|
|
||||||
} catch {
|
|
||||||
page.pageNum--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const debounceScroll = debounce(handleScroll, 200, false)
|
|
||||||
|
|
||||||
// 当前作业
|
|
||||||
const jobData = ref<any>({})
|
|
||||||
|
|
||||||
function handleSelectJob(item: any) {
|
|
||||||
jobData.value = item
|
|
||||||
}
|
|
||||||
|
|
||||||
const pageLoading = ref(false)
|
|
||||||
const pageError = ref(false)
|
|
||||||
|
|
||||||
onActivated(async () => {
|
|
||||||
pageLoading.value = true
|
|
||||||
listLoading.value = true
|
|
||||||
jobList.value = []
|
|
||||||
page.pageNum = 1
|
|
||||||
$XXL.setScreenOn(1)
|
|
||||||
try {
|
|
||||||
await getClassList()
|
|
||||||
if (classList.value.length) {
|
|
||||||
if (classList.value.every(i => i.id !== activeClass.value)) {
|
|
||||||
activeClass.value = classList.value[0]?.id
|
|
||||||
}
|
|
||||||
await getJobList()
|
|
||||||
handleSelectJob(jobList.value[0])
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
pageError.value = true
|
|
||||||
} finally {
|
|
||||||
pageLoading.value = false
|
|
||||||
listLoading.value = false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
function to(url: string) {
|
|
||||||
router.push(url)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div
|
|
||||||
class="uncomplete"
|
|
||||||
:style="{
|
|
||||||
'background-image': `url(${OSS_URL}/urm/mail_bg.svg)`,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="book"
|
|
||||||
:style="{
|
|
||||||
'background-image': `url(${OSS_URL}/urm/book_bg.svg)`,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<template v-if="jobList.length">
|
|
||||||
<div class="book-sidebar-box">
|
|
||||||
<div class="book-sidebar hidden_scroll" @scroll="debounceScroll">
|
|
||||||
<div ref="bookSidebarRef" class="scroll-box">
|
|
||||||
<div
|
|
||||||
v-for="i in jobList"
|
|
||||||
:key="i.id"
|
|
||||||
:class="{
|
|
||||||
'book-sidebar-item': true,
|
|
||||||
pointer: true,
|
|
||||||
active: jobData.id === i.id,
|
|
||||||
}"
|
|
||||||
:style="{
|
|
||||||
'background-image':
|
|
||||||
jobData.id === i.id
|
|
||||||
? `url(${OSS_URL}/urm/mailbox_sidebar_active_bg.svg)`
|
|
||||||
: i.readStatus
|
|
||||||
? `url(${OSS_URL}/urm/mailbox_sidebar_ready_bg.svg)`
|
|
||||||
: `url(${OSS_URL}/urm/mailbox_sidebar_bg.svg)`,
|
|
||||||
}"
|
|
||||||
@click="handleSelectJob(i)"
|
|
||||||
>
|
|
||||||
<!-- <div class="icon"></div> -->
|
|
||||||
<Icon
|
|
||||||
:name="jobData.id === i.id ? 'homeWork_job-item-active' : 'homeWork_job-item'"
|
|
||||||
class="icon"
|
|
||||||
width="84"
|
|
||||||
></Icon>
|
|
||||||
<div class="info">
|
|
||||||
<div class="info-title">{{ i.name }}</div>
|
|
||||||
<div class="info-time">
|
|
||||||
{{ dayjs(i.createTime).format('YYYY/MM/DD HH:mm') }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<LoadIcon v-if="listLoading" style="margin-top: 0"></LoadIcon>
|
|
||||||
</div>
|
|
||||||
<div class="book-main">
|
|
||||||
<div class="info flex-end">
|
|
||||||
<div class="info-title">{{ jobData.name }}</div>
|
|
||||||
<div class="info-time">
|
|
||||||
发布日期:
|
|
||||||
{{ dayjs(jobData.createTime).format('YYYY/MM/DD HH:mm') }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="info center">
|
|
||||||
<div class="info-name">
|
|
||||||
<span style="color: #999">来自:</span>{{ jobData.teacherName }}
|
|
||||||
</div>
|
|
||||||
<div class="info-time">
|
|
||||||
截止日期:
|
|
||||||
{{ dayjs(jobData.submitEndTime).format('YYYY/MM/DD HH:mm') }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="content">{{ jobData.content }}</div>
|
|
||||||
<div class="tab">
|
|
||||||
<div
|
|
||||||
class="tab-icon"
|
|
||||||
:style="{
|
|
||||||
'background-image': `url(${OSS_URL}/urm/homeWork/tab.svg)`,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<Icon class="icon" name="homeWork_paper" width="32"></Icon>
|
|
||||||
组卷
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="content paper">{{ jobData.paperName }}</div>
|
|
||||||
<div class="btn-box">
|
|
||||||
<mj-button
|
|
||||||
width="386"
|
|
||||||
height="108"
|
|
||||||
size="48"
|
|
||||||
:type="jobData.currentTitleId ? 'success' : 'primary'"
|
|
||||||
@click="to(`/system/homeWork/doWork?id=${jobData.id}&reportFlag=${0}`)"
|
|
||||||
>
|
|
||||||
{{ jobData.currentTitleId ? '继续答题' : '开始答题' }}
|
|
||||||
</mj-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<Empty
|
|
||||||
v-else
|
|
||||||
class="empty"
|
|
||||||
:type="listLoading ? 'loading' : 'task'"
|
|
||||||
content="还没有作业哦~"
|
|
||||||
></Empty>
|
|
||||||
</div>
|
|
||||||
<div class="top">
|
|
||||||
<BackBar leftText="作业" />
|
|
||||||
<div class="top-history">
|
|
||||||
<mj-button type="success" tilt @click="to(`/system/homeWork/statistics`)"
|
|
||||||
>统计分析</mj-button
|
|
||||||
>
|
|
||||||
<mj-button
|
|
||||||
type="success"
|
|
||||||
tilt
|
|
||||||
@click="to(`/system/homeWork/wrong?subjectId=${activeClass}`)"
|
|
||||||
>错题本</mj-button
|
|
||||||
>
|
|
||||||
<!-- <Icon
|
|
||||||
name="homeWork_temporary"
|
|
||||||
width="120"
|
|
||||||
class="pointer"
|
|
||||||
@click="to(`/system/homeWork/temporary`)"
|
|
||||||
></Icon> -->
|
|
||||||
<Icon
|
|
||||||
name="homeWork_hisJob"
|
|
||||||
width="120"
|
|
||||||
class="pointer"
|
|
||||||
@click="to(`/system/homeWork/history`)"
|
|
||||||
></Icon>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="classList.length" class="main">
|
|
||||||
<div class="main-sidebar">
|
|
||||||
<Sidebar
|
|
||||||
v-model="activeClass"
|
|
||||||
:config="classList"
|
|
||||||
:prop="{ name: 'subject' }"
|
|
||||||
@handle-change="handleSelectClass"
|
|
||||||
></Sidebar>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Empty
|
|
||||||
v-else
|
|
||||||
class="empty"
|
|
||||||
:type="pageLoading ? 'loading' : 'task'"
|
|
||||||
content="还没有作业哦~"
|
|
||||||
></Empty>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.uncomplete {
|
|
||||||
height: 100vh;
|
|
||||||
overflow: hidden;
|
|
||||||
box-sizing: border-box;
|
|
||||||
position: relative;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: cover;
|
|
||||||
|
|
||||||
.book {
|
|
||||||
box-sizing: border-box;
|
|
||||||
position: fixed;
|
|
||||||
width: 133.5vh;
|
|
||||||
height: 94vh;
|
|
||||||
left: 300px;
|
|
||||||
top: 6.67vh;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: top left;
|
|
||||||
background-size: 100% 100%;
|
|
||||||
padding: 10vh 11.67vh 6.33vh 9vh;
|
|
||||||
display: flex;
|
|
||||||
z-index: 2;
|
|
||||||
&-sidebar {
|
|
||||||
width: 36.67vh;
|
|
||||||
height: 100%;
|
|
||||||
flex-shrink: 0;
|
|
||||||
&-item {
|
|
||||||
padding: 0.83vh;
|
|
||||||
display: flex;
|
|
||||||
height: 9.67vh;
|
|
||||||
flex-shrink: 0;
|
|
||||||
margin-bottom: 1.33vh;
|
|
||||||
background-color: #fff;
|
|
||||||
box-shadow: 0 0.33vh 0.17vh 0 rgba(0, 0, 0, 0.15);
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: 26.17vh center;
|
|
||||||
background-size: 11.17vh, 13vh;
|
|
||||||
border: 0.5vh solid transparent;
|
|
||||||
|
|
||||||
&.unready {
|
|
||||||
background-color: #dcdff5;
|
|
||||||
}
|
|
||||||
&.active {
|
|
||||||
background-color: #fff;
|
|
||||||
border-color: rgb(255, 240, 31);
|
|
||||||
// background-position:
|
|
||||||
// center,
|
|
||||||
// 314px center;
|
|
||||||
// background-size: 100%, 134px, 156px;
|
|
||||||
}
|
|
||||||
&:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
.icon {
|
|
||||||
width: 7vh;
|
|
||||||
height: 7vh;
|
|
||||||
flex-shrink: 0;
|
|
||||||
background: #1c2a3a;
|
|
||||||
margin-right: 1.33vh;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info {
|
|
||||||
width: calc(100% - 8.33vh);
|
|
||||||
&-title {
|
|
||||||
color: $font-color;
|
|
||||||
font-family: $font-text;
|
|
||||||
font-size: 2.67vh;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 4vh; /* 150% */
|
|
||||||
@include single_ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-time {
|
|
||||||
font-size: 2vh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&-main {
|
|
||||||
margin-left: 5.83vh;
|
|
||||||
padding-top: 1.33vh;
|
|
||||||
width: calc(100% - 3.33vh);
|
|
||||||
.info {
|
|
||||||
padding: 1.33vh 0;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
&.flex-end {
|
|
||||||
align-items: end;
|
|
||||||
border-bottom: 0.17vh solid rgba(0, 0, 0, 0.45);
|
|
||||||
}
|
|
||||||
&.center {
|
|
||||||
align-items: center;
|
|
||||||
border-bottom: 0.08vh solid rgba(0, 0, 0, 0.45);
|
|
||||||
}
|
|
||||||
&-title {
|
|
||||||
color: $font-color;
|
|
||||||
font-family: $font-special;
|
|
||||||
font-size: 2.67vh;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 1.5;
|
|
||||||
width: 41.67vh;
|
|
||||||
margin-right: 1vh;
|
|
||||||
@include single_ellipsis;
|
|
||||||
}
|
|
||||||
&-name {
|
|
||||||
color: #999;
|
|
||||||
font-family: 'PingFang SC';
|
|
||||||
font-size: 2.67vh;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 1.2;
|
|
||||||
// width: calc(100% - 400px);
|
|
||||||
width: 41.67vh;
|
|
||||||
margin-right: 1vh;
|
|
||||||
@include single_ellipsis;
|
|
||||||
}
|
|
||||||
&-time {
|
|
||||||
font-size: 2vh;
|
|
||||||
|
|
||||||
line-height: 3vh; /* 150% */
|
|
||||||
@include single_ellipsis;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.content {
|
|
||||||
color: #333;
|
|
||||||
font-family: $font-text;
|
|
||||||
font-size: 2.67vh;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 4vh; /* 150% */
|
|
||||||
margin: 2.67vh 0;
|
|
||||||
text-indent: 2em;
|
|
||||||
height: 20vh;
|
|
||||||
// @include multiple_ellipsis(5);
|
|
||||||
@include hidden_scroll;
|
|
||||||
&.paper {
|
|
||||||
height: 8vh;
|
|
||||||
@include multiple_ellipsis(2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.tab {
|
|
||||||
// padding-top: 32px;
|
|
||||||
&-icon {
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 19vh;
|
|
||||||
height: 4.33vh;
|
|
||||||
background-position: top left;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: 100%;
|
|
||||||
padding: 0.17vh 0 0.17vh 4.67vh;
|
|
||||||
color: $font-color;
|
|
||||||
font-family: 'PingFang SC';
|
|
||||||
font-size: 2.67vh;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 4vh; /* 150% */
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
.icon {
|
|
||||||
// width: 32px;
|
|
||||||
// height: 32px;
|
|
||||||
margin-right: 1.33vh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.btn-box {
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding-top: 1.33vh;
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
|
|
||||||
.x-button {
|
|
||||||
font-family: $font-text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.btn {
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding-top: 1.5vh;
|
|
||||||
width: 32.17vh;
|
|
||||||
height: 9vh;
|
|
||||||
flex-shrink: 0;
|
|
||||||
border-radius: 5vh;
|
|
||||||
background-size: 100%;
|
|
||||||
box-shadow: 0 0.5vh 0.17vh 0 rgba(0, 0, 0, 0.25);
|
|
||||||
text-align: center;
|
|
||||||
color: $font-color;
|
|
||||||
text-align: center;
|
|
||||||
font-family: $font-text;
|
|
||||||
font-size: 4vh;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 6vh; /* 150% */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.empty {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.top {
|
|
||||||
padding: 3vh 5.33vh 0;
|
|
||||||
}
|
|
||||||
.top-history {
|
|
||||||
z-index: 2;
|
|
||||||
position: absolute;
|
|
||||||
top: 1.67vh;
|
|
||||||
right: 64px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
.mj-button {
|
|
||||||
margin: 0 16px 0.67vh;
|
|
||||||
font-family: $font-text;
|
|
||||||
}
|
|
||||||
.x-icon {
|
|
||||||
margin-left: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.main {
|
|
||||||
height: calc(100% - 13vh);
|
|
||||||
padding-left: 64px;
|
|
||||||
padding-right: 20px;
|
|
||||||
position: relative;
|
|
||||||
&-sidebar {
|
|
||||||
position: absolute;
|
|
||||||
left: 64px;
|
|
||||||
top: 1vh;
|
|
||||||
height: calc(100% - 2.5vh);
|
|
||||||
width: 22.92vh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.empty {
|
|
||||||
height: calc(100% - 25vh);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.info-time {
|
|
||||||
color: #999;
|
|
||||||
font-family: 'PingFang SC';
|
|
||||||
font-size: 1.67vh;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 2.5vh; /* 150% */
|
|
||||||
margin-top: 0.5vh;
|
|
||||||
span {
|
|
||||||
margin-right: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,261 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import dayjs from 'dayjs'
|
|
||||||
import { rateType } from '@/hooks'
|
|
||||||
import { colorConfig } from './hooks'
|
|
||||||
import EchartsPie from './EchartsPie.vue'
|
|
||||||
import EchartsBar from './EchartsBar.vue'
|
|
||||||
import { dialog } from '@/components/mj/mj-dialog'
|
|
||||||
|
|
||||||
const props = withDefaults(
|
|
||||||
defineProps<{
|
|
||||||
params: anyObj
|
|
||||||
jobAnalyse: anyObj
|
|
||||||
}>(),
|
|
||||||
{
|
|
||||||
jobAnalyse: () => ({}),
|
|
||||||
pageLoading: false,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
const difficultyColor = [
|
|
||||||
{ name: '容易', color: '#8FCC00' },
|
|
||||||
{ name: '较易', color: '#48A3FE' },
|
|
||||||
{ name: '普通', color: '#FFD04D' },
|
|
||||||
{ name: '较难', color: '#FF8200' },
|
|
||||||
{ name: '困难', color: '#FF5151' },
|
|
||||||
]
|
|
||||||
|
|
||||||
const report = computed(() => props.jobAnalyse.jobReportVo)
|
|
||||||
const analyze = computed(() =>
|
|
||||||
props.jobAnalyse.paperChanelAnalyzeList.map((i: anyObj) => {
|
|
||||||
return {
|
|
||||||
value: i.titleCount,
|
|
||||||
name: i.chanelTypeName,
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
const difficultyPie = computed(() =>
|
|
||||||
props.jobAnalyse.paperDifficultyAnalyzeList.map((i: anyObj) => {
|
|
||||||
return {
|
|
||||||
value: i.titleCount,
|
|
||||||
name: i.difficultyName,
|
|
||||||
itemStyle: {
|
|
||||||
color: difficultyColor.find(item => item.name === i.difficultyName)?.color || '#8FCC00',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
const difficultyBar = computed(() => {
|
|
||||||
const xData: string[] = []
|
|
||||||
const seriesVal: anyObj[] = []
|
|
||||||
props.jobAnalyse.paperDifficultyAnalyzeList.forEach((i: anyObj) => {
|
|
||||||
xData.push(`${i?.difficultyName}\n(${i?.score}分)`)
|
|
||||||
seriesVal.push({
|
|
||||||
value: i.score,
|
|
||||||
itemStyle: {
|
|
||||||
color: difficultyColor.find(item => item.name === i.difficultyName)?.color || '#8FCC00',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
return {
|
|
||||||
xData,
|
|
||||||
seriesVal,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
function showWorkPoints() {
|
|
||||||
dialog.show({
|
|
||||||
title: '作业内容',
|
|
||||||
content: report.value?.jobContent,
|
|
||||||
hideCancel: true,
|
|
||||||
hideClose: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="analyse">
|
|
||||||
<div v-if="jobAnalyse.jobReportVo" class="analyse-main">
|
|
||||||
<div class="analyse-info analyse-item">
|
|
||||||
<div class="analyse-item-title">作业信息</div>
|
|
||||||
<div class="analyse-info-main">
|
|
||||||
<p>
|
|
||||||
<span>作业名称:</span>
|
|
||||||
<span class="job-title">{{ report?.jobName }}</span>
|
|
||||||
<Icon name="report_detail" class="pointer" width="42" @click="showWorkPoints"></Icon>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<span>发布日期:</span>
|
|
||||||
{{ dayjs(report?.releaseTime).format('YYYY/MM/DD HH:mm') }}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<span>截止日期:</span>
|
|
||||||
{{ dayjs(report?.submitEndTime).format('YYYY/MM/DD HH:mm') }}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<span>总 分:</span>
|
|
||||||
{{ report?.totalScore }}分
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<span>作答时长:</span>
|
|
||||||
{{ report?.costTimeInfo }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="analyse-structure analyse-item">
|
|
||||||
<div class="analyse-item-title">试卷结构</div>
|
|
||||||
<div class="echarts-pie">
|
|
||||||
<div class="echarts-pie-item">
|
|
||||||
<EchartsPie :data="analyze" title="题型分布"></EchartsPie>
|
|
||||||
</div>
|
|
||||||
<div class="echarts-pie-item">
|
|
||||||
<EchartsPie :data="difficultyPie" title="难度分布"></EchartsPie>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="echarts-bar">
|
|
||||||
<EchartsBar :data="difficultyBar" title="试卷难易度显示"></EchartsBar>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="analyse-rank analyse-item">
|
|
||||||
<el-table
|
|
||||||
v-if="jobAnalyse.stuRankVoList?.length"
|
|
||||||
:data="jobAnalyse.stuRankVoList"
|
|
||||||
:row-class-name="({ row }) => (row.selfFlag ? 'self' : '')"
|
|
||||||
>
|
|
||||||
<el-table-column label="序号" align="center" minWidth="30%">
|
|
||||||
<template #default="{ row }">
|
|
||||||
{{ row.rankNum ? row.rankNum : '' }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="stuName" label="姓名" align="center" minWidth="30%" />
|
|
||||||
<el-table-column label="得分" align="center" minWidth="40%">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<span
|
|
||||||
:style="{
|
|
||||||
color: colorConfig[rateType((Number(row.scoreRate) || 0) * 100)],
|
|
||||||
}"
|
|
||||||
>{{ row.markScore }}</span
|
|
||||||
>/{{ row.scoreRate }}%
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- <Empty v-else class="empty" :type="pageLoading ? 'loading' : 'network'"></Empty> -->
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.analyse {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
line-height: 1.5;
|
|
||||||
&-main {
|
|
||||||
display: grid;
|
|
||||||
grid-template-areas:
|
|
||||||
'a b'
|
|
||||||
'c b';
|
|
||||||
grid-template-columns: 563px 895px;
|
|
||||||
grid-template-rows: 430px 585px;
|
|
||||||
gap: 24px;
|
|
||||||
}
|
|
||||||
&-item {
|
|
||||||
border-radius: 3.33vh;
|
|
||||||
border: 0.67vh solid #fff;
|
|
||||||
background: linear-gradient(179deg, #fff 0.69%, #dfebf7 99.53%);
|
|
||||||
box-shadow: 0 0.33vh 0.67vh 0 rgba(0, 0, 0, 0.25);
|
|
||||||
|
|
||||||
&-title {
|
|
||||||
color: $font-color;
|
|
||||||
font-family: $font-special;
|
|
||||||
font-size: 3.33vh;
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 2vh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&-info {
|
|
||||||
grid-area: a;
|
|
||||||
padding: 1.33vh;
|
|
||||||
&-main {
|
|
||||||
color: $font-color;
|
|
||||||
font-family: OPPOSans;
|
|
||||||
font-size: 2.33vh;
|
|
||||||
p {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 1.67vh;
|
|
||||||
line-height: 3.5vh;
|
|
||||||
.job-title {
|
|
||||||
width: calc(100% - 185px);
|
|
||||||
@include single_ellipsis;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&-structure {
|
|
||||||
grid-area: b;
|
|
||||||
padding: 1.33vh 44px;
|
|
||||||
.echarts-pie {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
height: 35vh;
|
|
||||||
margin-bottom: 4.17vh;
|
|
||||||
&-item {
|
|
||||||
height: 100%;
|
|
||||||
width: 50%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.echarts-bar {
|
|
||||||
height: 37.25vh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&-rank {
|
|
||||||
grid-area: c;
|
|
||||||
overflow-y: hidden;
|
|
||||||
:deep(.el-table) {
|
|
||||||
// border-radius: 3.33vh;
|
|
||||||
height: 100%;
|
|
||||||
background: transparent;
|
|
||||||
.cell {
|
|
||||||
line-height: 1.5;
|
|
||||||
color: $font-color;
|
|
||||||
font-size: 2.33vh;
|
|
||||||
padding: 0 4px;
|
|
||||||
}
|
|
||||||
th.el-table__cell {
|
|
||||||
background: #ccdeff;
|
|
||||||
font-family: $font-special;
|
|
||||||
padding: 1.5vh 0;
|
|
||||||
}
|
|
||||||
.el-table__cell {
|
|
||||||
&::after {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
content: '';
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
height: 0.17vh;
|
|
||||||
background: repeating-linear-gradient(
|
|
||||||
to right,
|
|
||||||
#deedfc,
|
|
||||||
#deedfc 8px,
|
|
||||||
transparent 8px,
|
|
||||||
transparent 16px /* 线条间隔 */
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tr {
|
|
||||||
background: transparent;
|
|
||||||
td {
|
|
||||||
font-family: OPPOSans;
|
|
||||||
padding: 1.5vh 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.el-table__row.self {
|
|
||||||
background: #fff7e0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,116 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import homeWorkApi from '@/api/system/homeWork'
|
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
params: anyObj
|
|
||||||
isDeadline: boolean
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
const list = ref<anyObj>([])
|
|
||||||
async function getData() {
|
|
||||||
const { data } = await homeWorkApi.getJobTittle({ ...props.params, reportFlag: 1 })
|
|
||||||
list.value = data.tjobStuVoList
|
|
||||||
}
|
|
||||||
|
|
||||||
function toWorkDetail(idx: string) {
|
|
||||||
const query: anyObj = {
|
|
||||||
reportFlag: 1,
|
|
||||||
idx,
|
|
||||||
...props.params,
|
|
||||||
}
|
|
||||||
|
|
||||||
router.push({
|
|
||||||
path: `/system/homeWork/doWork`,
|
|
||||||
query,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const pageLoading = ref(false)
|
|
||||||
onMounted(async () => {
|
|
||||||
pageLoading.value = true
|
|
||||||
try {
|
|
||||||
await getData()
|
|
||||||
} catch {}
|
|
||||||
pageLoading.value = false
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="detail">
|
|
||||||
<div v-if="list.length" class="detail-main">
|
|
||||||
<div class="detail-main-title">答题情况</div>
|
|
||||||
<div class="detail-main-list">
|
|
||||||
<div
|
|
||||||
v-for="(i, idx) in list"
|
|
||||||
:key="i.jobId"
|
|
||||||
:class="{
|
|
||||||
'detail-main-list-item': true,
|
|
||||||
pointer: true,
|
|
||||||
success: isDeadline && i.titleStatus === 3 && i.markFlag,
|
|
||||||
error: isDeadline && i.titleStatus === 3 && !i.markFlag,
|
|
||||||
}"
|
|
||||||
@click="toWorkDetail(idx)"
|
|
||||||
>
|
|
||||||
{{ idx + 1 }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Empty v-else class="empty" :type="pageLoading ? 'loading' : 'network'"></Empty>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.detail {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
line-height: 1.5;
|
|
||||||
|
|
||||||
&-main {
|
|
||||||
border-radius: 3.33vh;
|
|
||||||
border: 0.67vh solid #fff;
|
|
||||||
background: linear-gradient(179deg, #fdfdf9 0.69%, #dfebf7 99.53%);
|
|
||||||
box-shadow: 0 0.33vh 0.67vh 0 rgba(0, 0, 0, 0.25);
|
|
||||||
height: 100%;
|
|
||||||
&-title {
|
|
||||||
color: $font-color;
|
|
||||||
font-family: $font-special;
|
|
||||||
font-size: 4vh;
|
|
||||||
margin: 3.33vh 0;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
&-list {
|
|
||||||
overflow-y: auto;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(7, 160px);
|
|
||||||
grid-template-rows: 160px;
|
|
||||||
grid-auto-rows: 160px;
|
|
||||||
gap: 40px;
|
|
||||||
justify-content: center;
|
|
||||||
height: calc(100% - 160px);
|
|
||||||
&-item {
|
|
||||||
border-radius: 1vh;
|
|
||||||
border: 0.33vh solid #9fa5aa;
|
|
||||||
background: #e4e9ec;
|
|
||||||
text-align: center;
|
|
||||||
line-height: 13.33vh;
|
|
||||||
color: #666;
|
|
||||||
font-family: $font-special;
|
|
||||||
font-size: 4vh;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
&.success {
|
|
||||||
color: #8fcc00;
|
|
||||||
background-color: #ddff8c;
|
|
||||||
border-color: #8fcc00;
|
|
||||||
}
|
|
||||||
&.error {
|
|
||||||
color: #f00a0a;
|
|
||||||
background-color: #febebf;
|
|
||||||
border-color: #f00a0a;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,129 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { $font_color } from '@/styles/variate'
|
|
||||||
import * as echarts from 'echarts'
|
|
||||||
|
|
||||||
const props = withDefaults(
|
|
||||||
defineProps<{
|
|
||||||
data: {
|
|
||||||
xData: string[]
|
|
||||||
seriesVal: anyObj[]
|
|
||||||
}
|
|
||||||
title?: string
|
|
||||||
yName?: string
|
|
||||||
}>(),
|
|
||||||
{},
|
|
||||||
)
|
|
||||||
|
|
||||||
const emit = defineEmits(['handleBarClick'])
|
|
||||||
|
|
||||||
const widthRate = window.screen.width / 1920
|
|
||||||
|
|
||||||
const chartContainer = ref<HTMLDivElement | null>(null)
|
|
||||||
|
|
||||||
let myChart = reactive<anyObj>({})
|
|
||||||
|
|
||||||
const option = reactive<anyObj>({
|
|
||||||
title: {
|
|
||||||
text: '',
|
|
||||||
textStyle: {
|
|
||||||
color: $font_color,
|
|
||||||
fontSize: widthRate * 28,
|
|
||||||
fontWeight: 'normal',
|
|
||||||
},
|
|
||||||
top: 0,
|
|
||||||
left: 'center',
|
|
||||||
},
|
|
||||||
xAxis: {
|
|
||||||
data: [],
|
|
||||||
axisLabel: {
|
|
||||||
show: true,
|
|
||||||
textStyle: {
|
|
||||||
fontSize: widthRate * 22, // 修改字体大小
|
|
||||||
color: $font_color,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
axisLine: {
|
|
||||||
show: true,
|
|
||||||
lineStyle: {
|
|
||||||
color: '#8A94FD70',
|
|
||||||
width: widthRate * 4,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
yAxis: {
|
|
||||||
type: 'value',
|
|
||||||
minInterval: 1,
|
|
||||||
// name: '(人)',
|
|
||||||
nameLocation: 'end',
|
|
||||||
// nameGap: widthRate * -35,
|
|
||||||
nameTextStyle: {
|
|
||||||
color: $font_color,
|
|
||||||
fontSize: widthRate * 22,
|
|
||||||
},
|
|
||||||
axisLabel: {
|
|
||||||
show: true,
|
|
||||||
textStyle: {
|
|
||||||
fontSize: widthRate * 22, // 修改字体大小
|
|
||||||
color: $font_color,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
axisLine: {
|
|
||||||
show: true,
|
|
||||||
lineStyle: {
|
|
||||||
color: 'rgba(138, 148, 253, 0.25)',
|
|
||||||
width: widthRate * 4,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
splitLine: {
|
|
||||||
lineStyle: {
|
|
||||||
type: [5, 10],
|
|
||||||
color: 'rgba(138, 148, 253, 0.25)',
|
|
||||||
width: 2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
series: {
|
|
||||||
data: [],
|
|
||||||
type: 'bar',
|
|
||||||
barMaxWidth: widthRate * 80,
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
position: 'top',
|
|
||||||
fontSize: widthRate * 20,
|
|
||||||
color: $font_color,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
async function drawGraph() {
|
|
||||||
await nextTick()
|
|
||||||
if (myChart) {
|
|
||||||
myChart.dispose?.()
|
|
||||||
myChart = echarts.init(chartContainer.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
option.xAxis.data = props.data?.xData
|
|
||||||
option.series.data = props.data?.seriesVal
|
|
||||||
option.title.text = props.title
|
|
||||||
option.yAxis.name = props.yName || ''
|
|
||||||
myChart.setOption(option)
|
|
||||||
myChart.off('click').on('click', async (params: anyObj) => {
|
|
||||||
emit('handleBarClick', params)
|
|
||||||
})
|
|
||||||
myChart.resize()
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
drawGraph()
|
|
||||||
})
|
|
||||||
defineExpose({ drawGraph })
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div ref="chartContainer" class="echarts-bar"></div>
|
|
||||||
</template>
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.echarts-bar {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,101 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { $font_color } from '@/styles/variate'
|
|
||||||
import * as echarts from 'echarts'
|
|
||||||
|
|
||||||
const props = withDefaults(
|
|
||||||
defineProps<{
|
|
||||||
data: anyObj[]
|
|
||||||
title: string
|
|
||||||
}>(),
|
|
||||||
{},
|
|
||||||
)
|
|
||||||
|
|
||||||
const widthRate = window.screen.width / 1920
|
|
||||||
|
|
||||||
const chartContainer = ref<HTMLDivElement | null>(null)
|
|
||||||
|
|
||||||
let myChart = reactive<anyObj>({})
|
|
||||||
|
|
||||||
const option = reactive<anyObj>({
|
|
||||||
title: {
|
|
||||||
text: '',
|
|
||||||
textStyle: {
|
|
||||||
color: $font_color,
|
|
||||||
fontSize: widthRate * 28,
|
|
||||||
fontWeight: 'normal',
|
|
||||||
},
|
|
||||||
top: 0,
|
|
||||||
left: 'center',
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
bottom: 0,
|
|
||||||
left: 'center',
|
|
||||||
icon: 'circle',
|
|
||||||
itemWidth: widthRate * 8,
|
|
||||||
textStyle: {
|
|
||||||
fontSize: widthRate * 20,
|
|
||||||
color: '#666',
|
|
||||||
},
|
|
||||||
data: [],
|
|
||||||
},
|
|
||||||
|
|
||||||
series: {
|
|
||||||
type: 'pie',
|
|
||||||
radius: ['30%', '70%'],
|
|
||||||
avoidLabelOverlap: false,
|
|
||||||
legendHoverLink: false,
|
|
||||||
emphasis: {
|
|
||||||
label: {
|
|
||||||
show: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
position: 'outer',
|
|
||||||
alignTo: 'labelLine',
|
|
||||||
formatter: (params: anyObj) => Math.round(params.percent) + '%',
|
|
||||||
color: 'inherit',
|
|
||||||
fontSize: widthRate * 22,
|
|
||||||
distanceToLabelLine: widthRate * -50,
|
|
||||||
},
|
|
||||||
labelLine: {
|
|
||||||
length: widthRate * 22,
|
|
||||||
length2: widthRate * 75,
|
|
||||||
maxSurfaceAngle: 80,
|
|
||||||
},
|
|
||||||
labelLayout: {
|
|
||||||
verticalAlign: 'bottom',
|
|
||||||
},
|
|
||||||
data: [],
|
|
||||||
// width: '85%',
|
|
||||||
// height: '85%',
|
|
||||||
left: 'center',
|
|
||||||
top: 'center',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
async function drawGraph() {
|
|
||||||
await nextTick()
|
|
||||||
if (myChart) {
|
|
||||||
myChart.dispose?.() // 清除上一次已经画过得图,这样才能实时才到最新数据的图
|
|
||||||
myChart = echarts.init(chartContainer.value)
|
|
||||||
}
|
|
||||||
option.series.data = props.data
|
|
||||||
option.legend.data = props.data
|
|
||||||
option.title.text = props.title
|
|
||||||
myChart.setOption(option)
|
|
||||||
myChart.resize()
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
drawGraph()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div ref="chartContainer" class="echarts-pie"></div>
|
|
||||||
</template>
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.echarts-pie {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,180 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import homeWorkApi from '@/api/system/homeWork'
|
|
||||||
import { colorConfig } from './hooks'
|
|
||||||
import { rateType } from '@/hooks'
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
params: anyObj
|
|
||||||
}>()
|
|
||||||
|
|
||||||
// const width_rate = window.screen.width / 1920;
|
|
||||||
|
|
||||||
const data = ref<anyObj[]>([])
|
|
||||||
async function getData() {
|
|
||||||
const { data: res } = await homeWorkApi.getKnowledgeAnalyse(props.params)
|
|
||||||
data.value = res.data
|
|
||||||
currentType.value = res.data[0].type
|
|
||||||
}
|
|
||||||
const typeConfig = ['薄弱', '合格', '良好', '优秀']
|
|
||||||
const dataList = computed(() =>
|
|
||||||
data.value.map(({ type }) => ({
|
|
||||||
value: type,
|
|
||||||
label: typeConfig[type],
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
const tableData = computed(
|
|
||||||
() => data.value.find(i => i.type === currentType.value)?.knowledgeAnalyseVos,
|
|
||||||
)
|
|
||||||
const currentType = ref(0)
|
|
||||||
|
|
||||||
const pageLoading = ref(false)
|
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
pageLoading.value = true
|
|
||||||
|
|
||||||
try {
|
|
||||||
await getData()
|
|
||||||
} catch {}
|
|
||||||
pageLoading.value = false
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="knowledge">
|
|
||||||
<div v-if="data.length" class="knowledge-main">
|
|
||||||
<div class="knowledge-main-select">
|
|
||||||
<SelectTab v-model="currentType" :dataList="dataList"></SelectTab>
|
|
||||||
</div>
|
|
||||||
<div class="knowledge-main-table">
|
|
||||||
<el-table :data="tableData">
|
|
||||||
<el-table-column label="知识点名称" prop="name" align="center" minWidth="53%">
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="满分"
|
|
||||||
prop="fullScore"
|
|
||||||
align="center"
|
|
||||||
minWidth="23.5%"
|
|
||||||
></el-table-column>
|
|
||||||
<el-table-column label="得分" align="center" minWidth="23.5%">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<span
|
|
||||||
:style="{
|
|
||||||
color: colorConfig[rateType((Number(row.scoreRate.replace('%', '')) || 0) * 100)],
|
|
||||||
}"
|
|
||||||
>{{ row?.score }}/{{ row?.scoreRate }}</span
|
|
||||||
>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Empty
|
|
||||||
v-else
|
|
||||||
class="empty"
|
|
||||||
:type="pageLoading ? 'loading' : 'knowledge'"
|
|
||||||
content="暂无知识点数据~"
|
|
||||||
></Empty>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.knowledge {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
&-main {
|
|
||||||
border-radius: 3.33vh;
|
|
||||||
border: 0.67vh solid #fff;
|
|
||||||
background: linear-gradient(179deg, #fdfdf9 0.69%, #dfebf7 99.53%);
|
|
||||||
box-shadow: 0 0.33vh 0.67vh 0 rgba(0, 0, 0, 0.25);
|
|
||||||
padding: 4.33vh 48px;
|
|
||||||
height: 100%;
|
|
||||||
&-select {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
:deep(.select-list) {
|
|
||||||
display: inline-flex;
|
|
||||||
margin-bottom: 5vh;
|
|
||||||
.data-item {
|
|
||||||
width: 298px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&-table {
|
|
||||||
height: calc(100% - 100px);
|
|
||||||
:deep(.el-table) {
|
|
||||||
box-sizing: border-box;
|
|
||||||
border: 0.33vh #659cfe solid;
|
|
||||||
border-radius: 3.33vh;
|
|
||||||
background: transparent;
|
|
||||||
max-height: 100%;
|
|
||||||
@include hidden_scroll;
|
|
||||||
|
|
||||||
.cell {
|
|
||||||
line-height: 1.5;
|
|
||||||
color: $font-color;
|
|
||||||
font-size: 2.33vh;
|
|
||||||
padding: 0 4px;
|
|
||||||
@include single_ellipsis;
|
|
||||||
}
|
|
||||||
th.el-table__cell {
|
|
||||||
background: #ccdeff;
|
|
||||||
font-family: $font-special;
|
|
||||||
padding: 1.5vh 0;
|
|
||||||
}
|
|
||||||
.el-table__cell {
|
|
||||||
height: 7.5vh;
|
|
||||||
color: $font-color;
|
|
||||||
font-family: OPPOSans;
|
|
||||||
font-size: 2.33vh;
|
|
||||||
position: relative;
|
|
||||||
border: none;
|
|
||||||
.cell {
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
&::after {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
content: '';
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
height: 0.17vh;
|
|
||||||
background: repeating-linear-gradient(
|
|
||||||
to right,
|
|
||||||
#deedfc,
|
|
||||||
#deedfc 8px,
|
|
||||||
transparent 8px,
|
|
||||||
transparent 16px /* 线条间隔 */
|
|
||||||
);
|
|
||||||
}
|
|
||||||
&::before {
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
content: '';
|
|
||||||
display: block;
|
|
||||||
width: 2px;
|
|
||||||
height: 100%;
|
|
||||||
background: repeating-linear-gradient(
|
|
||||||
to bottom,
|
|
||||||
#deedfc,
|
|
||||||
#deedfc 8px,
|
|
||||||
transparent 8px,
|
|
||||||
transparent 16px /* 线条间隔 */
|
|
||||||
);
|
|
||||||
}
|
|
||||||
&:last-child {
|
|
||||||
&::before {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tr {
|
|
||||||
background: transparent;
|
|
||||||
td {
|
|
||||||
font-family: OPPOSans;
|
|
||||||
padding: 1.5vh 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
export const colorConfig = {
|
|
||||||
primary: '#2D39BC',
|
|
||||||
success: '#2F8A5B',
|
|
||||||
error: '#B22731',
|
|
||||||
warning: '#CC8A33',
|
|
||||||
}
|
|
||||||
@ -1,117 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import dayjs from 'dayjs'
|
|
||||||
import Analyse from './components/Analyse.vue'
|
|
||||||
import Detail from './components/Detail.vue'
|
|
||||||
import Knowledge from './components/Knowledge.vue'
|
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import homeWorkApi from '@/api/system/homeWork'
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
const params = {
|
|
||||||
id: route.params.id,
|
|
||||||
stuId: route.query?.stuId || '',
|
|
||||||
}
|
|
||||||
|
|
||||||
// 侧边栏
|
|
||||||
const activeType = ref('1')
|
|
||||||
const sidebarConfig = ref<anyObj[]>([])
|
|
||||||
const isDeadline = ref(false)
|
|
||||||
|
|
||||||
const jobAnalyse = ref<anyObj>({})
|
|
||||||
async function getJobAnalyse() {
|
|
||||||
const { data: res } = await homeWorkApi.getJobAnalyse(params)
|
|
||||||
jobAnalyse.value = res.data
|
|
||||||
isDeadline.value = dayjs().isAfter(jobAnalyse.value.jobReportVo.submitEndTime)
|
|
||||||
const detailCmp = { id: '1', name: '答题详情', c: markRaw(Detail) }
|
|
||||||
if (isDeadline.value) {
|
|
||||||
sidebarConfig.value = [
|
|
||||||
{ id: '0', name: '整体分析', c: markRaw(Analyse) },
|
|
||||||
detailCmp,
|
|
||||||
{ id: '2', name: '知识点分析', c: markRaw(Knowledge) },
|
|
||||||
]
|
|
||||||
activeType.value = '0'
|
|
||||||
} else {
|
|
||||||
sidebarConfig.value = [detailCmp]
|
|
||||||
activeType.value = '1'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const COMPONENT = computed(() => {
|
|
||||||
return sidebarConfig.value.find(i => i.id === activeType.value)?.c
|
|
||||||
})
|
|
||||||
|
|
||||||
const pageLoading = ref(false)
|
|
||||||
onMounted(async () => {
|
|
||||||
pageLoading.value = true
|
|
||||||
try {
|
|
||||||
await getJobAnalyse()
|
|
||||||
} catch {}
|
|
||||||
pageLoading.value = false
|
|
||||||
})
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'HomeWorkReports',
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="reports">
|
|
||||||
<div class="top">
|
|
||||||
<BackBar :leftText="(route.query?.name as string) || ' '" />
|
|
||||||
</div>
|
|
||||||
<div v-if="!pageLoading" class="main">
|
|
||||||
<div class="main-sidebar">
|
|
||||||
<Sidebar v-model="activeType" :config="sidebarConfig"></Sidebar>
|
|
||||||
</div>
|
|
||||||
<div class="main-box">
|
|
||||||
<KeepAlive>
|
|
||||||
<component
|
|
||||||
:is="COMPONENT"
|
|
||||||
:params="params"
|
|
||||||
:jobAnalyse="jobAnalyse"
|
|
||||||
:isDeadline="isDeadline"
|
|
||||||
></component>
|
|
||||||
</KeepAlive>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Empty v-else class="empty" :type="pageLoading ? 'loading' : 'network'"></Empty>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.reports {
|
|
||||||
@include main_bg;
|
|
||||||
height: 100vh;
|
|
||||||
overflow: hidden;
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
.top {
|
|
||||||
padding: 3vh 64px 0;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main {
|
|
||||||
height: calc(100% - 130px);
|
|
||||||
padding-left: 64px;
|
|
||||||
padding-right: 64px;
|
|
||||||
position: relative;
|
|
||||||
&-sidebar {
|
|
||||||
position: absolute;
|
|
||||||
left: 64px;
|
|
||||||
top: 3.67vh;
|
|
||||||
height: calc(100% - 66px);
|
|
||||||
width: 272px;
|
|
||||||
}
|
|
||||||
&-box {
|
|
||||||
width: 1482px;
|
|
||||||
height: 86.67vh;
|
|
||||||
margin-left: 310px;
|
|
||||||
overflow: hidden;
|
|
||||||
:deep(.empty) {
|
|
||||||
height: calc(100% - 150px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,463 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import * as echarts from 'echarts'
|
|
||||||
import dayjs from '@/plugins/dayjs'
|
|
||||||
import Card from '@/page/subject/components/Card.vue'
|
|
||||||
import { $font_color } from '@/styles/variate'
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
data: {
|
|
||||||
level?: number
|
|
||||||
stability?: number
|
|
||||||
jobScoringAbilityDataVoList?: {
|
|
||||||
level: number
|
|
||||||
scoreRate: number
|
|
||||||
submitDate: string
|
|
||||||
}[]
|
|
||||||
}
|
|
||||||
table?: anyObj[]
|
|
||||||
tableLoading: boolean
|
|
||||||
type: number
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const emit = defineEmits(['update:type'])
|
|
||||||
/*
|
|
||||||
level 级别:0-优秀 1-良 2-中 3-弱
|
|
||||||
stability 稳定性 0-稳定 1-相对波动
|
|
||||||
jobScoringAbilityDataVoList {
|
|
||||||
level 级别:0-优秀 1-良 2-中 3-弱
|
|
||||||
scoreRate 得分率
|
|
||||||
submitDate 日期
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
const levelConfig: anyObj = {
|
|
||||||
0: { label: '优秀' },
|
|
||||||
1: { label: '良好' },
|
|
||||||
2: { label: '合格' },
|
|
||||||
3: { label: '薄弱' },
|
|
||||||
}
|
|
||||||
|
|
||||||
const stabilityConfig: anyObj = {
|
|
||||||
0: { label: '稳定' },
|
|
||||||
1: { label: '相对波动' },
|
|
||||||
}
|
|
||||||
|
|
||||||
const configColor = {
|
|
||||||
success: '#2F8A5B',
|
|
||||||
primary: '#2D39BC',
|
|
||||||
warning: '#A13C00',
|
|
||||||
error: '#B22731',
|
|
||||||
}
|
|
||||||
// 正确率的颜色
|
|
||||||
function getColor(rate: number) {
|
|
||||||
return rate === 100 ? 'success' : rate >= 80 ? 'primary' : rate >= 60 ? 'warning' : 'error'
|
|
||||||
}
|
|
||||||
|
|
||||||
// 最近得分情况 0-零分 1-失分 2-满分
|
|
||||||
const recentScoreType = [
|
|
||||||
{ label: '零分', color: $font_color },
|
|
||||||
{ label: '失分', color: '#B22731' },
|
|
||||||
{ label: '满分', color: '#2F8A5B' },
|
|
||||||
]
|
|
||||||
const lossFrequency = computed(() => {
|
|
||||||
return (item: anyObj) => {
|
|
||||||
if (item) {
|
|
||||||
const { lostPointsNum, jobNum } = item
|
|
||||||
if (lostPointsNum / jobNum >= 1) {
|
|
||||||
return '每次'
|
|
||||||
} else if (lostPointsNum / jobNum >= 0.5) {
|
|
||||||
return '高频'
|
|
||||||
} else if (lostPointsNum / jobNum < 0.5) {
|
|
||||||
return '低频'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// type 类型 0-薄弱 1-合格 2-良好 3-优秀
|
|
||||||
const typeConfig = [
|
|
||||||
{
|
|
||||||
label: '薄弱',
|
|
||||||
value: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '合格',
|
|
||||||
value: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '良好',
|
|
||||||
value: 2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '优秀',
|
|
||||||
value: 3,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
const widthRate = window.screen.width / 1920
|
|
||||||
|
|
||||||
const echartRef = ref<HTMLDivElement | null>(null)
|
|
||||||
|
|
||||||
let myChart = reactive<anyObj>({})
|
|
||||||
|
|
||||||
const option = reactive<anyObj>({
|
|
||||||
legend: {
|
|
||||||
show: true,
|
|
||||||
data: ['我的得分能力'],
|
|
||||||
textStyle: {
|
|
||||||
color: '#666',
|
|
||||||
fontSize: widthRate * 24,
|
|
||||||
fontFamily: 'OPPOSans',
|
|
||||||
padding: [0, 8],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
xAxis: {
|
|
||||||
data: [],
|
|
||||||
boundaryGap: false,
|
|
||||||
axisLabel: {
|
|
||||||
show: true,
|
|
||||||
textStyle: {
|
|
||||||
fontSize: widthRate * 22, // 修改字体大小
|
|
||||||
color: $font_color,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
axisLine: {
|
|
||||||
show: true,
|
|
||||||
lineStyle: {
|
|
||||||
color: '#31BCFF',
|
|
||||||
width: widthRate * 4,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
axisTick: {
|
|
||||||
alignWithLabel: true,
|
|
||||||
},
|
|
||||||
splitLine: {
|
|
||||||
show: true,
|
|
||||||
interval: 0,
|
|
||||||
lineStyle: {
|
|
||||||
type: 'solid',
|
|
||||||
color: '#C3ECFF',
|
|
||||||
width: widthRate * 4,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
yAxis: {
|
|
||||||
type: 'value',
|
|
||||||
|
|
||||||
minInterval: 1,
|
|
||||||
nameLocation: 'end',
|
|
||||||
nameTextStyle: {
|
|
||||||
color: $font_color,
|
|
||||||
fontSize: widthRate * 22,
|
|
||||||
},
|
|
||||||
axisLabel: {
|
|
||||||
show: true,
|
|
||||||
textStyle: {
|
|
||||||
fontSize: widthRate * 22, // 修改字体大小
|
|
||||||
color: $font_color,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
axisLine: {
|
|
||||||
show: false,
|
|
||||||
lineStyle: {
|
|
||||||
color: '#C3ECFF',
|
|
||||||
width: widthRate * 4,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
splitLine: {
|
|
||||||
lineStyle: {
|
|
||||||
type: 'solid',
|
|
||||||
color: '#C3ECFF',
|
|
||||||
width: widthRate * 4,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
data: [],
|
|
||||||
name: '我的得分能力',
|
|
||||||
type: 'line',
|
|
||||||
lineStyle: {
|
|
||||||
color: '#00AEFF',
|
|
||||||
width: widthRate * 4,
|
|
||||||
},
|
|
||||||
symbol: 'circle',
|
|
||||||
symbolSize: widthRate * 12,
|
|
||||||
itemStyle: {
|
|
||||||
color: '#fff',
|
|
||||||
borderColor: '#00AEFF',
|
|
||||||
borderWidth: widthRate * 4,
|
|
||||||
},
|
|
||||||
markLine: {
|
|
||||||
symbol: ['none', 'triangle'],
|
|
||||||
data: [
|
|
||||||
{
|
|
||||||
yAxis: 85,
|
|
||||||
lineStyle: {
|
|
||||||
type: [10, 15],
|
|
||||||
color: '#F3B0B4',
|
|
||||||
width: widthRate * 5,
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
formatter: '优秀',
|
|
||||||
color: '#F3B0B4',
|
|
||||||
fontSize: widthRate * 30,
|
|
||||||
padding: [0, widthRate * 16],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
yAxis: 70,
|
|
||||||
lineStyle: {
|
|
||||||
type: [10, 15],
|
|
||||||
color: '#F3B0B4',
|
|
||||||
width: widthRate * 5,
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
formatter: '良好',
|
|
||||||
color: '#F3B0B4',
|
|
||||||
fontSize: widthRate * 30,
|
|
||||||
padding: [0, widthRate * 16],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
yAxis: 60,
|
|
||||||
lineStyle: {
|
|
||||||
type: [10, 15],
|
|
||||||
color: '#F3B0B4',
|
|
||||||
width: widthRate * 5,
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
formatter: '合格',
|
|
||||||
color: '#F3B0B4',
|
|
||||||
fontSize: widthRate * 30,
|
|
||||||
padding: [0, widthRate * 16],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
animation: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
function drawGraph() {
|
|
||||||
if (myChart) {
|
|
||||||
myChart.dispose?.() // 清除上一次已经画过得图,这样才能实时才到最新数据的图
|
|
||||||
myChart = echarts.init(echartRef.value)
|
|
||||||
}
|
|
||||||
option.xAxis.data =
|
|
||||||
props.data.jobScoringAbilityDataVoList?.map(i => dayjs(i.submitDate).format('MM/DD')) || []
|
|
||||||
option.series[0].data = props.data.jobScoringAbilityDataVoList?.map(i => i.scoreRate / 100) || []
|
|
||||||
myChart.setOption(option)
|
|
||||||
myChart.resize()
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.data,
|
|
||||||
() => {
|
|
||||||
nextTick(() => {
|
|
||||||
drawGraph()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{ immediate: true },
|
|
||||||
)
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="single-analysis hidden_scroll">
|
|
||||||
<div class="score-ability">
|
|
||||||
<div class="title" data-content="得分能力">得分能力</div>
|
|
||||||
<template v-if="data.jobScoringAbilityDataVoList?.length">
|
|
||||||
<div ref="echartRef" class="chartContainer"></div>
|
|
||||||
<div>
|
|
||||||
<div class="cards">
|
|
||||||
<Card
|
|
||||||
width="404"
|
|
||||||
height="160"
|
|
||||||
title="得分能力综合评估"
|
|
||||||
:text="levelConfig[data.level || 0].label"
|
|
||||||
></Card>
|
|
||||||
<Card
|
|
||||||
type="warning"
|
|
||||||
width="404"
|
|
||||||
height="160"
|
|
||||||
title="得分能力稳定性"
|
|
||||||
:text="stabilityConfig[data.stability || 0].label"
|
|
||||||
></Card>
|
|
||||||
</div>
|
|
||||||
<p class="tips">
|
|
||||||
注:得分能力是通过均分、得分率等数据综合计算得出,反映学生对该科目知识掌握程度和答题能力
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<Empty v-else type="learn" style="height: 600px"></Empty>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="knowledge-table">
|
|
||||||
<div class="title" data-content="知识点分析">知识点分析</div>
|
|
||||||
<div style="text-align: center">
|
|
||||||
<SelectTab
|
|
||||||
:modelValue="type"
|
|
||||||
:dataList="typeConfig"
|
|
||||||
@update:modelValue="emit('update:type', $event)"
|
|
||||||
></SelectTab>
|
|
||||||
</div>
|
|
||||||
<el-table v-if="table?.length" :data="table">
|
|
||||||
<el-table-column label="知识点名称" prop="knowledgeName" align="center" minWidth="42.8%">
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="失分频率"
|
|
||||||
prop="lostFrequency"
|
|
||||||
sortable
|
|
||||||
align="center"
|
|
||||||
minWidth="22%"
|
|
||||||
>
|
|
||||||
<template #default="{ row }">
|
|
||||||
{{ lossFrequency(row) }} ({{ row.lostPointsNum }}/{{ row.jobNum }})
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="得分率" prop="scoreRate" sortable align="center" minWidth="17.6%">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<span :style="{ color: configColor[getColor(row.scoreRate / 100)] }">
|
|
||||||
{{ row.scoreRate / 100 }}%
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="最近情况" prop="recentScoreType" align="center" minWidth="17.6%">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<span :style="{ color: recentScoreType[row.recentScoreType].color }">
|
|
||||||
{{ recentScoreType[row.recentScoreType].label }}
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
<Empty v-else :type="tableLoading ? 'loading' : 'learn'" style="height: 600px"></Empty>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.single-analysis {
|
|
||||||
line-height: 1.5;
|
|
||||||
height: calc(100%);
|
|
||||||
.title {
|
|
||||||
color: #fff;
|
|
||||||
font-family: $font-special;
|
|
||||||
font-size: 3vh;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
text-align: center;
|
|
||||||
@include font-stroke($font-color, 4px);
|
|
||||||
}
|
|
||||||
.score-ability {
|
|
||||||
.chartContainer {
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 4vh;
|
|
||||||
height: 60.83vh;
|
|
||||||
}
|
|
||||||
.cards {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
.xxl-card {
|
|
||||||
margin: 0 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.tips {
|
|
||||||
width: 1208px;
|
|
||||||
height: 9.67vh;
|
|
||||||
border-radius: 1.67vh;
|
|
||||||
background: #fff;
|
|
||||||
box-shadow: 0 0.33vh 0.17vh 0 rgba(0, 0, 0, 0.15);
|
|
||||||
color: #ff8d3b;
|
|
||||||
font-family: OPPOSans;
|
|
||||||
font-size: 2.67vh;
|
|
||||||
padding: 1vh 32px 0;
|
|
||||||
margin: 5vh auto 6vh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.knowledge-table {
|
|
||||||
min-height: calc(100% + 210px);
|
|
||||||
:deep(.select-list) {
|
|
||||||
display: inline-flex;
|
|
||||||
margin: 4vh auto;
|
|
||||||
.data-item {
|
|
||||||
width: 298px;
|
|
||||||
&.active {
|
|
||||||
background: linear-gradient(269deg, #86e6ff 2.25%, #9effda 51.93%, #5fdcfd 99.65%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
:deep(.el-table) {
|
|
||||||
box-sizing: border-box;
|
|
||||||
border: 0.33vh #31bcff solid;
|
|
||||||
border-radius: 3.33vh;
|
|
||||||
background: transparent;
|
|
||||||
|
|
||||||
.cell {
|
|
||||||
line-height: 1.5;
|
|
||||||
color: $font-color;
|
|
||||||
font-size: 2.33vh;
|
|
||||||
padding: 0 4px;
|
|
||||||
// @include single_ellipsis;
|
|
||||||
}
|
|
||||||
th.el-table__cell {
|
|
||||||
background: #c2ebff;
|
|
||||||
font-family: $font-special;
|
|
||||||
padding: 1.5vh 0;
|
|
||||||
}
|
|
||||||
.el-table__cell {
|
|
||||||
height: 7.5vh;
|
|
||||||
color: $font-color;
|
|
||||||
font-family: OPPOSans;
|
|
||||||
font-size: 2.33vh;
|
|
||||||
position: relative;
|
|
||||||
border: none;
|
|
||||||
background-color: #fff;
|
|
||||||
.cell {
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
&::after {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
content: '';
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
height: 0.17vh;
|
|
||||||
background: repeating-linear-gradient(
|
|
||||||
to right,
|
|
||||||
#deedfc,
|
|
||||||
#deedfc 8px,
|
|
||||||
transparent 8px,
|
|
||||||
transparent 16px /* 线条间隔 */
|
|
||||||
);
|
|
||||||
}
|
|
||||||
&::before {
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
content: '';
|
|
||||||
display: block;
|
|
||||||
width: 2px;
|
|
||||||
height: 100%;
|
|
||||||
background: repeating-linear-gradient(
|
|
||||||
to bottom,
|
|
||||||
#deedfc,
|
|
||||||
#deedfc 8px,
|
|
||||||
transparent 8px,
|
|
||||||
transparent 16px /* 线条间隔 */
|
|
||||||
);
|
|
||||||
}
|
|
||||||
&:last-child {
|
|
||||||
&::before {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tr {
|
|
||||||
background: transparent;
|
|
||||||
td {
|
|
||||||
font-family: OPPOSans;
|
|
||||||
padding: 1.5vh 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,148 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
const props = defineProps<{
|
|
||||||
wholeAnalysis: anyObj
|
|
||||||
}>()
|
|
||||||
// 整体
|
|
||||||
const wholeConfig: anyObj = {
|
|
||||||
0: {
|
|
||||||
label: '优秀',
|
|
||||||
icon: 'analysisStar_0',
|
|
||||||
},
|
|
||||||
1: {
|
|
||||||
label: '良好',
|
|
||||||
icon: 'analysisStar_1',
|
|
||||||
},
|
|
||||||
2: {
|
|
||||||
label: '合格',
|
|
||||||
icon: 'analysisStar_2',
|
|
||||||
},
|
|
||||||
3: {
|
|
||||||
label: '薄弱',
|
|
||||||
icon: 'analysisStar_3',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
// -1-不展示 0-退步 1-稳定 2-进步
|
|
||||||
|
|
||||||
const statusConfig: anyObj = {
|
|
||||||
0: {
|
|
||||||
label: '退步',
|
|
||||||
bg: '#FFD9D9',
|
|
||||||
color: '#F00A0A',
|
|
||||||
},
|
|
||||||
1: {
|
|
||||||
label: '稳定',
|
|
||||||
bg: '#E6E7FF',
|
|
||||||
color: '#7A7FF5',
|
|
||||||
},
|
|
||||||
2: {
|
|
||||||
label: '进步',
|
|
||||||
bg: '#F4FFDA',
|
|
||||||
color: '#8FCC00',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// // 过滤掉-1
|
|
||||||
// const analysisComputed = computed(() =>
|
|
||||||
// props.wholeAnalysis.map((i: anyObj) => {
|
|
||||||
// return {
|
|
||||||
// level: i?.level || 0,
|
|
||||||
// jobStatisticalDataVoList:
|
|
||||||
// i?.jobStatisticalDataVoList || [],
|
|
||||||
// };
|
|
||||||
// }),
|
|
||||||
// );
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="whole hidden_scroll">
|
|
||||||
<div v-for="i in wholeAnalysis" :key="i.level" class="whole-item">
|
|
||||||
<Icon :name="wholeConfig[i.level || 0].icon" size="120"> </Icon>
|
|
||||||
<div class="whole-item-right">
|
|
||||||
<template v-if="i.jobStatisticalDataVoList?.length">
|
|
||||||
<div
|
|
||||||
v-for="item in i.jobStatisticalDataVoList"
|
|
||||||
:key="item.subjectId"
|
|
||||||
class="whole-item-right-item"
|
|
||||||
>
|
|
||||||
<div class="name">{{ item.subjectName }}</div>
|
|
||||||
<div
|
|
||||||
v-if="statusConfig[item.status]"
|
|
||||||
class="status"
|
|
||||||
:style="{
|
|
||||||
color: statusConfig[item.status].color,
|
|
||||||
background: statusConfig[item.status].bg,
|
|
||||||
border: `3px solid ${statusConfig[item.status].color}`,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
{{ statusConfig[item.status].label }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<div v-else class="empty">暂无</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.whole {
|
|
||||||
height: calc(100% - 48px);
|
|
||||||
margin-top: 4vh;
|
|
||||||
line-height: 1.5;
|
|
||||||
&-item {
|
|
||||||
display: flex;
|
|
||||||
margin: 2vh 0;
|
|
||||||
.x-icon {
|
|
||||||
margin-right: 40px;
|
|
||||||
}
|
|
||||||
&-right {
|
|
||||||
flex: 1;
|
|
||||||
border-radius: 1.67vh;
|
|
||||||
background: rgba(0, 0, 0, 0.1);
|
|
||||||
min-height: 10.67vh;
|
|
||||||
font-family: $font-special;
|
|
||||||
font-size: 3.33vh;
|
|
||||||
padding: 2vh;
|
|
||||||
display: grid;
|
|
||||||
// grid-template-columns: repeat(2, 1fr);
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(546px, 1fr));
|
|
||||||
grid-template-rows: 80px;
|
|
||||||
grid-auto-rows: 80px;
|
|
||||||
gap: 24px;
|
|
||||||
justify-content: center;
|
|
||||||
&-item {
|
|
||||||
// width: 546px;
|
|
||||||
display: flex;
|
|
||||||
padding: 0.83vh 32px;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
border-radius: 1.67vh;
|
|
||||||
background: #fff;
|
|
||||||
box-shadow: 0 0.33vh 0.17vh 0 rgba(0, 0, 0, 0.15);
|
|
||||||
|
|
||||||
.name {
|
|
||||||
flex: 1;
|
|
||||||
color: $font-color;
|
|
||||||
}
|
|
||||||
.status {
|
|
||||||
width: 100px;
|
|
||||||
height: 3.83vh;
|
|
||||||
border-radius: 3.33vh;
|
|
||||||
border: 0.25vh solid black;
|
|
||||||
font-family: OPPOSans;
|
|
||||||
font-size: 2.17vh;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty {
|
|
||||||
color: #fff;
|
|
||||||
text-shadow: 0 0.17vh 0.17vh rgba(0, 0, 0, 0.45);
|
|
||||||
// margin: 10px auto;
|
|
||||||
text-align: center;
|
|
||||||
line-height: 6.67vh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,171 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { userStore } from '@/stores'
|
|
||||||
import { storeToRefs } from 'pinia'
|
|
||||||
import homeWorkApi from '@/api/system/homeWork'
|
|
||||||
import Whole from './Whole.vue'
|
|
||||||
import SingleAnalysis from './SingleAnalysis.vue'
|
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
|
|
||||||
const OSS_URL = import.meta.env.VITE_OSS_URL
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
let paramsStuId = route.query?.stuId || ''
|
|
||||||
const leftText = computed(() => route.query?.name || ' ')
|
|
||||||
|
|
||||||
const { userInfo } = storeToRefs(userStore())
|
|
||||||
|
|
||||||
const wholeAnalysis = ref<anyObj[]>([])
|
|
||||||
async function getAnalysis() {
|
|
||||||
const { data: res } = await homeWorkApi.getStatisticalAnalysis({
|
|
||||||
stuId: paramsStuId || userInfo.value.smartUserIdentifier,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (res.code === 200) {
|
|
||||||
wholeAnalysis.value = res.data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// const { subjectList, getSubjectAllList } = subjectStore();
|
|
||||||
const activeClass = ref('')
|
|
||||||
const classList = ref([{ id: '', name: '整体' }])
|
|
||||||
async function getClassList() {
|
|
||||||
const { data: res } = await homeWorkApi.getAnalyseSubjectList({
|
|
||||||
stuId: paramsStuId || userInfo.value.smartUserIdentifier,
|
|
||||||
})
|
|
||||||
if (res.code === 200) {
|
|
||||||
classList.value.push(...res.data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const singleAnalysis = ref<any>({})
|
|
||||||
async function getAnalysisSubject() {
|
|
||||||
const { data: res } = await homeWorkApi.getStatisticalSubject({
|
|
||||||
sourceType: 0,
|
|
||||||
stuId: paramsStuId || userInfo.value.smartUserIdentifier,
|
|
||||||
subjectId: activeClass.value,
|
|
||||||
})
|
|
||||||
if (res.code === 200) {
|
|
||||||
singleAnalysis.value = res.data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const type = ref(0)
|
|
||||||
const knowledgeAnalysis = ref<{ loading: boolean; table: anyObj[] }[]>([])
|
|
||||||
async function getKnowledgeSubject() {
|
|
||||||
if (knowledgeAnalysis.value[type.value]?.loading) return
|
|
||||||
if (Array.isArray(knowledgeAnalysis.value[type.value]?.table)) return
|
|
||||||
const curType = type.value
|
|
||||||
knowledgeAnalysis.value[curType] = {
|
|
||||||
loading: true,
|
|
||||||
table: [],
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const { data: res } = await homeWorkApi.getKnowledgeSubject({
|
|
||||||
sourceType: 0,
|
|
||||||
stuId: paramsStuId || userInfo.value.smartUserIdentifier,
|
|
||||||
subjectId: activeClass.value,
|
|
||||||
size: 300,
|
|
||||||
// orderByColumn: 'lostFrequency',
|
|
||||||
// isAsc: 'asc',
|
|
||||||
type: type.value,
|
|
||||||
})
|
|
||||||
if (res.code === 200) {
|
|
||||||
knowledgeAnalysis.value[curType].table = res.rows || []
|
|
||||||
}
|
|
||||||
} catch {}
|
|
||||||
knowledgeAnalysis.value[curType].loading = false
|
|
||||||
}
|
|
||||||
watch(type, () => getKnowledgeSubject())
|
|
||||||
watch(activeClass, () => {
|
|
||||||
knowledgeAnalysis.value = []
|
|
||||||
getKnowledgeSubject()
|
|
||||||
})
|
|
||||||
|
|
||||||
const loading = ref(false)
|
|
||||||
async function handleSelectClass(item: anyObj) {
|
|
||||||
if (item.id === '') {
|
|
||||||
getAnalysis()
|
|
||||||
} else {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
await getAnalysisSubject()
|
|
||||||
} catch {}
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onActivated(async () => {
|
|
||||||
paramsStuId = route.query?.stuId || ''
|
|
||||||
classList.value = [{ id: '', name: '整体' }]
|
|
||||||
activeClass.value = ''
|
|
||||||
getClassList()
|
|
||||||
getAnalysis()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="homework_statistics">
|
|
||||||
<div class="top">
|
|
||||||
<BackBar :leftText="leftText" />
|
|
||||||
</div>
|
|
||||||
<div class="main">
|
|
||||||
<div class="main_sidebar">
|
|
||||||
<Sidebar
|
|
||||||
v-model="activeClass"
|
|
||||||
:config="classList"
|
|
||||||
@handle-change="handleSelectClass"
|
|
||||||
></Sidebar>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="main_book"
|
|
||||||
:style="{ 'background-image': `url(${OSS_URL}/urm/homeWork/wrong_bg.svg)` }"
|
|
||||||
>
|
|
||||||
<template v-if="!loading">
|
|
||||||
<Whole v-if="activeClass === ''" :wholeAnalysis="wholeAnalysis"></Whole>
|
|
||||||
<SingleAnalysis
|
|
||||||
v-else
|
|
||||||
v-model:type="type"
|
|
||||||
:data="singleAnalysis"
|
|
||||||
:table="knowledgeAnalysis[type]?.table"
|
|
||||||
:tableLoading="knowledgeAnalysis[type]?.loading"
|
|
||||||
></SingleAnalysis>
|
|
||||||
</template>
|
|
||||||
<Empty v-else type="loading" style="height: 900px"></Empty>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.homework_statistics {
|
|
||||||
padding: 2.67vh 64px;
|
|
||||||
@include main_bg;
|
|
||||||
position: relative;
|
|
||||||
width: 100vw;
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
box-sizing: border-box;
|
|
||||||
line-height: 1.5;
|
|
||||||
.top {
|
|
||||||
position: absolute;
|
|
||||||
// top: 32;
|
|
||||||
// left: 64;
|
|
||||||
}
|
|
||||||
.main {
|
|
||||||
// margin-top: 66px;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
padding-top: 2.83vh;
|
|
||||||
display: flex;
|
|
||||||
&_sidebar {
|
|
||||||
padding-top: 5.5vh;
|
|
||||||
width: 275px;
|
|
||||||
margin-right: 50px;
|
|
||||||
}
|
|
||||||
&_book {
|
|
||||||
width: 1470px;
|
|
||||||
height: 90.25vh;
|
|
||||||
flex-shrink: 0;
|
|
||||||
background-size: 100%;
|
|
||||||
padding: 8.67vh 64px 3.17vh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,278 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import dayjs from 'dayjs'
|
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
import { userStore } from '@/stores'
|
|
||||||
import { XMessage, debounce } from '@/hooks'
|
|
||||||
import homeWorkApi from '@/api/system/homeWork'
|
|
||||||
import LoadIcon from '@/page/system/mailbox/components/LoadIcon.vue'
|
|
||||||
import sass from '@/utils/sass'
|
|
||||||
|
|
||||||
const router = useRouter()
|
|
||||||
const { token } = userStore()
|
|
||||||
const OSS_URL = import.meta.env.VITE_OSS_URL
|
|
||||||
const itemBg = `url(${OSS_URL}/icon/homeWork_temporary_item_bg.svg)`
|
|
||||||
|
|
||||||
const subjectList: any = ref([])
|
|
||||||
|
|
||||||
const page = reactive({
|
|
||||||
pageSize: 11,
|
|
||||||
pageNum: 1,
|
|
||||||
total: 0,
|
|
||||||
})
|
|
||||||
const jobList: any = ref([])
|
|
||||||
const uploadLoading = ref(false)
|
|
||||||
const listLoading = ref(false) // 列表加载
|
|
||||||
const moreLoading = ref(false) // 加载更多
|
|
||||||
const uploadMsg = ref({
|
|
||||||
submitUrl: '',
|
|
||||||
subjectId: '',
|
|
||||||
clientType: 0,
|
|
||||||
})
|
|
||||||
function toLook(item: any) {
|
|
||||||
router.push(`/system/homeWork/temporaryHomeWork/${item.id}`)
|
|
||||||
}
|
|
||||||
function handleSelectClass() {
|
|
||||||
initList()
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleBeforeUpload(rawFile: any) {
|
|
||||||
const fileType = ['image/jpeg', 'image/png']
|
|
||||||
if (!fileType.includes(rawFile.type)) {
|
|
||||||
XMessage.error('上传格式不正确,只支持JPG/PNG')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (rawFile.size / 1024 / 1024 > 10) {
|
|
||||||
XMessage.error('图片过大')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
uploadLoading.value = true
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
async function handleUploadSuccess(res: any, _file: any) {
|
|
||||||
uploadMsg.value.submitUrl = res.data.filePath
|
|
||||||
uploadLoading.value = false
|
|
||||||
try {
|
|
||||||
const { data } = await homeWorkApi.uploadTemporaryJob(uploadMsg.value)
|
|
||||||
if (data.code === 200) {
|
|
||||||
initList()
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function handleUploadError() {
|
|
||||||
XMessage.error('上传失败,请重新上传~')
|
|
||||||
uploadLoading.value = false
|
|
||||||
}
|
|
||||||
const debounceScroll = debounce(handleScroll, 200, false)
|
|
||||||
const scrollBoxRef = ref()
|
|
||||||
async function handleScroll(event: any) {
|
|
||||||
if (jobList.value.length >= page.total) return
|
|
||||||
if (moreLoading.value) return
|
|
||||||
if (event.target.scrollTop + event.target.offsetHeight >= scrollBoxRef.value?.offsetHeight - 50) {
|
|
||||||
page.pageNum++
|
|
||||||
try {
|
|
||||||
moreLoading.value = true
|
|
||||||
await initTemporaryJobList()
|
|
||||||
} catch {
|
|
||||||
page.pageNum--
|
|
||||||
}
|
|
||||||
moreLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async function initList() {
|
|
||||||
if (listLoading.value) return
|
|
||||||
listLoading.value = true
|
|
||||||
jobList.value = []
|
|
||||||
page.pageNum = 1
|
|
||||||
try {
|
|
||||||
await initTemporaryJobList()
|
|
||||||
} catch {
|
|
||||||
console.log('网络出问题了啦~')
|
|
||||||
}
|
|
||||||
listLoading.value = false
|
|
||||||
}
|
|
||||||
// 获取学科对应临时作业
|
|
||||||
async function initTemporaryJobList() {
|
|
||||||
try {
|
|
||||||
const { data } = await homeWorkApi.temporaryJobList({
|
|
||||||
clientType: 0,
|
|
||||||
subjectId: uploadMsg.value.subjectId,
|
|
||||||
...page,
|
|
||||||
})
|
|
||||||
page.total = data.total
|
|
||||||
for (let i in data.rows) {
|
|
||||||
jobList.value.push(data.rows[i])
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 获取学科
|
|
||||||
async function getSubjectList() {
|
|
||||||
try {
|
|
||||||
const { data } = await homeWorkApi.getTemporarySubjectList()
|
|
||||||
subjectList.value = data.rows
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onMounted(async () => {
|
|
||||||
await getSubjectList()
|
|
||||||
uploadMsg.value.subjectId = subjectList.value[0].id
|
|
||||||
initList()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="temporary">
|
|
||||||
<div class="top">
|
|
||||||
<BackBar leftText="临时作业" />
|
|
||||||
</div>
|
|
||||||
<div class="main">
|
|
||||||
<div class="main_sidebar">
|
|
||||||
<Sidebar
|
|
||||||
v-model="uploadMsg.subjectId"
|
|
||||||
:config="subjectList"
|
|
||||||
@handle-change="handleSelectClass"
|
|
||||||
></Sidebar>
|
|
||||||
</div>
|
|
||||||
<div class="main_answer">
|
|
||||||
<Empty v-if="listLoading" class="empty" type="loading"></Empty>
|
|
||||||
<div v-else class="main_answer_list hidden_scroll" @scroll="debounceScroll">
|
|
||||||
<div ref="scrollBoxRef" class="content">
|
|
||||||
<div>
|
|
||||||
<el-upload
|
|
||||||
class="pointer"
|
|
||||||
action="/api/main/sysFileInfo/tenUploadAll"
|
|
||||||
:data="{ type: 1 }"
|
|
||||||
:headers="{
|
|
||||||
Authorization: token,
|
|
||||||
'X-Tenant-Id': sass.platform.tenantId,
|
|
||||||
}"
|
|
||||||
accept="image/*"
|
|
||||||
:before-upload="handleBeforeUpload"
|
|
||||||
:on-success="handleUploadSuccess"
|
|
||||||
:on-error="handleUploadError"
|
|
||||||
:show-file-list="false"
|
|
||||||
:disabled="uploadLoading"
|
|
||||||
>
|
|
||||||
<Icon name="add_book" size="80" />
|
|
||||||
<span class="text">{{ uploadLoading ? '上传中' : '上传图片' }}</span>
|
|
||||||
</el-upload>
|
|
||||||
</div>
|
|
||||||
<div v-for="(i, index) in jobList" :key="index" class="item pointer" @click="toLook(i)">
|
|
||||||
<img :src="i.submitUrl" alt="" class="sub_answer" />
|
|
||||||
<span class="sub_date">{{ dayjs(i.submitTime).format('YYYY/MM/DD HH:mm') }}</span>
|
|
||||||
<div v-if="i.status === 1" class="result">已批改</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<LoadIcon v-if="moreLoading" class="load-icon"></LoadIcon>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.temporary {
|
|
||||||
@include main_bg;
|
|
||||||
padding: 2.67vh 64px;
|
|
||||||
height: 100vh;
|
|
||||||
overflow: hidden;
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
.top {
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
.main {
|
|
||||||
display: flex;
|
|
||||||
padding-top: 8.67vh;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
&_sidebar {
|
|
||||||
padding-top: 2vh;
|
|
||||||
width: 214px;
|
|
||||||
margin-right: 98px;
|
|
||||||
}
|
|
||||||
&_answer {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
padding: 2.5vh;
|
|
||||||
border-radius: 3.33vh;
|
|
||||||
border: 0.67vh solid #fff;
|
|
||||||
background: linear-gradient(179.32deg, #fdfdf9 0.69%, #dfebf7 99.53%);
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
&_list {
|
|
||||||
height: 100%;
|
|
||||||
.content {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
:deep(.el-upload) {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
margin-right: 24px;
|
|
||||||
width: 328px;
|
|
||||||
height: 33.5vh;
|
|
||||||
border-radius: 1.67vh;
|
|
||||||
background: #f2f3ff;
|
|
||||||
box-shadow: 0 0.33vh 0.33vh 0 #00000026;
|
|
||||||
.text {
|
|
||||||
font-family: $font-special;
|
|
||||||
margin-top: 2vh;
|
|
||||||
display: block;
|
|
||||||
font-size: 2.33vh;
|
|
||||||
line-height: 3.5vh;
|
|
||||||
color: #80b5fe;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.item {
|
|
||||||
position: relative;
|
|
||||||
padding: 2vh 24px 2.67vh 24px;
|
|
||||||
width: 332px;
|
|
||||||
height: 33.83vh;
|
|
||||||
margin: 0 24px 2vh 0;
|
|
||||||
border-radius: 1.67vh;
|
|
||||||
background-image: v-bind(itemBg);
|
|
||||||
background-size: 100%;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: right bottom;
|
|
||||||
|
|
||||||
.sub_answer {
|
|
||||||
width: 284px;
|
|
||||||
height: 23.67vh;
|
|
||||||
border-radius: 0.83vh;
|
|
||||||
}
|
|
||||||
.sub_date {
|
|
||||||
display: inline-block;
|
|
||||||
margin-top: 2vh;
|
|
||||||
font-size: 2.33vh;
|
|
||||||
line-height: 3.5vh;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
&:nth-child(4n) {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
.result {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
padding: 0.67vh 14px;
|
|
||||||
color: #2f8a5b;
|
|
||||||
font-family: $font-special;
|
|
||||||
font-size: 2.33vh;
|
|
||||||
line-height: 3.5vh;
|
|
||||||
text-align: center;
|
|
||||||
background-color: #a2fb6e;
|
|
||||||
border-radius: 0 1.67vh 0 1.67vh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,314 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import homeWorkApi from '@/api/system/homeWork'
|
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
const OSS_URL = import.meta.env.VITE_OSS_URL
|
|
||||||
const activeBg = `url(${OSS_URL}/urm/homeWork/homeWork_temporary_active_bg.svg)`
|
|
||||||
const nonactiveBg = `url(${OSS_URL}/urm/homeWork/homeWork_temporary_nonactive_bg.svg)`
|
|
||||||
const activeNav = ref({
|
|
||||||
name: '原图',
|
|
||||||
type: 0,
|
|
||||||
url: '',
|
|
||||||
})
|
|
||||||
const nav = ref([
|
|
||||||
{
|
|
||||||
name: '原图',
|
|
||||||
type: 0,
|
|
||||||
url: '',
|
|
||||||
},
|
|
||||||
])
|
|
||||||
const loading = ref(false)
|
|
||||||
const dragging = ref(false) // 是否处于拖拽状态
|
|
||||||
const imgArea = ref()
|
|
||||||
const img = ref()
|
|
||||||
const movableData = ref({
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
scale: 1,
|
|
||||||
})
|
|
||||||
const scaleData = ref(1) // 另外存一份放大倍率
|
|
||||||
const distance = ref({
|
|
||||||
startDistance: 0,
|
|
||||||
endDistance: 0,
|
|
||||||
})
|
|
||||||
function clickAnswerStatus(i: any) {
|
|
||||||
activeNav.value = i
|
|
||||||
movableData.value = {
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
scale: 1,
|
|
||||||
}
|
|
||||||
loading.value = true
|
|
||||||
setTimeout(() => {
|
|
||||||
onImageLoad()
|
|
||||||
}, 150)
|
|
||||||
}
|
|
||||||
// 图片加载
|
|
||||||
function onImageLoad() {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
// 放大
|
|
||||||
function tapEnlarge() {
|
|
||||||
if (movableData.value.scale < 2) {
|
|
||||||
movableData.value.scale += 0.1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 缩小
|
|
||||||
function tapLessen() {
|
|
||||||
if (movableData.value.scale > 0.6) {
|
|
||||||
movableData.value.scale -= 0.1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function touchTwo(e: anyObj[], mode: 'startDistance' | 'endDistance') {
|
|
||||||
// 双指缩放
|
|
||||||
const one = {
|
|
||||||
x: e[0].pageX, // 第一根手指的横坐标
|
|
||||||
y: e[0].pageY, // 第一根手指的纵坐标
|
|
||||||
} // 第一根手指的横坐标
|
|
||||||
const two = {
|
|
||||||
x: e[1].pageX, // 第二根手指的横坐标
|
|
||||||
y: e[1].pageY, // 第二根手指的纵坐标
|
|
||||||
}
|
|
||||||
|
|
||||||
distance.value[mode] = getDistance(one, two)
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDistance(start: anyObj, stop: anyObj) {
|
|
||||||
return Math.sqrt(
|
|
||||||
Math.pow(Math.abs(start.x - stop.x), 2) + Math.pow(Math.abs(start.y - stop.y), 2),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const startDrag = (event: anyObj) => {
|
|
||||||
if (event.touches?.length === 2) {
|
|
||||||
// 两个触点
|
|
||||||
touchTwo(event.touches, 'startDistance')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
dragging.value = true
|
|
||||||
const x = (event.touches ? event.touches[0].pageX : event.pageX) - movableData.value.x
|
|
||||||
const y = (event.touches ? event.touches[0].pageY : event.pageY) - movableData.value.y
|
|
||||||
|
|
||||||
const move = (event: anyObj) => {
|
|
||||||
if (event.touches?.length === 2) {
|
|
||||||
// 两个触点
|
|
||||||
touchTwo(event.touches, 'endDistance')
|
|
||||||
const scale2 =
|
|
||||||
scaleData.value + distance.value.endDistance / distance.value.startDistance - 1.01 // 减去原本的1倍 0.01是防止小数计算误差
|
|
||||||
movableData.value.scale = scale2 > 2 ? 2 : scale2 < 0.5 ? 0.5 : scale2 // 最大2倍 最小0.5倍
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (dragging.value) {
|
|
||||||
movableData.value.x = (event.touches ? event.touches[0].pageX : event.pageX) - x
|
|
||||||
movableData.value.y = (event.touches ? event.touches[0].pageY : event.pageY) - y
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const stopDrag = () => {
|
|
||||||
distance.value.startDistance = distance.value.endDistance = 0
|
|
||||||
scaleData.value = movableData.value.scale
|
|
||||||
dragging.value = false
|
|
||||||
document.removeEventListener('mousemove', move)
|
|
||||||
document.removeEventListener('touchmove', move)
|
|
||||||
document.removeEventListener('mouseup', stopDrag)
|
|
||||||
document.removeEventListener('touchend', stopDrag)
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener('mousemove', move)
|
|
||||||
document.addEventListener('touchmove', move)
|
|
||||||
document.addEventListener('mouseup', stopDrag)
|
|
||||||
document.addEventListener('touchend', stopDrag)
|
|
||||||
}
|
|
||||||
// 获取详情
|
|
||||||
async function getTemporaryJobDetail() {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const { data } = await homeWorkApi.temporaryJobDetail(route.params.id)
|
|
||||||
if (data.data.status === 1) {
|
|
||||||
nav.value = [
|
|
||||||
{
|
|
||||||
name: '原图',
|
|
||||||
type: 0,
|
|
||||||
url: data.data.submitUrl,
|
|
||||||
},
|
|
||||||
{ name: '批改图', type: 1, url: data.data.markUrl },
|
|
||||||
]
|
|
||||||
} else {
|
|
||||||
nav.value = [
|
|
||||||
{
|
|
||||||
name: '原图',
|
|
||||||
type: 0,
|
|
||||||
url: data.data.submitUrl,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
activeNav.value = nav.value[0]
|
|
||||||
loading.value = false
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err)
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onUnmounted(() => {
|
|
||||||
document.removeEventListener('mousemove', () => {})
|
|
||||||
document.removeEventListener('mouseup', () => {})
|
|
||||||
document.removeEventListener('touchmove', () => {})
|
|
||||||
document.removeEventListener('touchend', () => {})
|
|
||||||
})
|
|
||||||
onActivated(() => {
|
|
||||||
getTemporaryJobDetail()
|
|
||||||
movableData.value = {
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
scale: 1,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="sinle_temporary">
|
|
||||||
<div class="top">
|
|
||||||
<BackBar />
|
|
||||||
</div>
|
|
||||||
<div class="main">
|
|
||||||
<div
|
|
||||||
class="main_answer"
|
|
||||||
:style="{
|
|
||||||
'background-image': `url(${OSS_URL}/urm/homeWork/homeWork_temporary_single_bg.svg)`,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<div class="nav">
|
|
||||||
<div
|
|
||||||
v-for="i in nav"
|
|
||||||
:key="i.type"
|
|
||||||
class="nav_item pointer"
|
|
||||||
:style="{
|
|
||||||
'background-image': i.type === activeNav.type ? activeBg : nonactiveBg,
|
|
||||||
color: i.type === activeNav.type ? '' : '#666',
|
|
||||||
}"
|
|
||||||
@click="clickAnswerStatus(i)"
|
|
||||||
>
|
|
||||||
{{ i.name }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Empty v-if="loading" class="empty" type="loading" content=""></Empty>
|
|
||||||
<div v-else class="area">
|
|
||||||
<div
|
|
||||||
ref="imgArea"
|
|
||||||
:style="{
|
|
||||||
transform: `translate(${movableData.x}px, ${movableData.y}px) scale(${movableData.scale})`,
|
|
||||||
}"
|
|
||||||
class="area-content"
|
|
||||||
@mousedown.prevent="startDrag"
|
|
||||||
@touchstart.prevent="startDrag"
|
|
||||||
>
|
|
||||||
<img ref="img" :src="activeNav.url" alt="" @load="onImageLoad" />
|
|
||||||
</div>
|
|
||||||
<div class="tool">
|
|
||||||
<Icon
|
|
||||||
name="homeWork_temporary_fangda"
|
|
||||||
width="48"
|
|
||||||
class="pointer tool_fangda"
|
|
||||||
@click="tapEnlarge"
|
|
||||||
></Icon>
|
|
||||||
<Icon
|
|
||||||
name="homeWork_temporary_suoxiao"
|
|
||||||
width="48"
|
|
||||||
class="pointer"
|
|
||||||
@click="tapLessen"
|
|
||||||
></Icon>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.sinle_temporary {
|
|
||||||
@include main_bg;
|
|
||||||
height: 100vh;
|
|
||||||
overflow: hidden;
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
.top {
|
|
||||||
padding: 3vh 64px 0;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: flex-end;
|
|
||||||
}
|
|
||||||
.main {
|
|
||||||
padding: 0 64px;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
&_answer {
|
|
||||||
position: relative;
|
|
||||||
width: 1792px;
|
|
||||||
height: 85.25vh;
|
|
||||||
background-size: 100%;
|
|
||||||
|
|
||||||
.nav {
|
|
||||||
position: absolute;
|
|
||||||
top: -4.75vh;
|
|
||||||
width: calc(100% - 394px);
|
|
||||||
margin-left: 197px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
font-family: $font-special;
|
|
||||||
font-size: 3.33vh;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 5vh;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
&_item {
|
|
||||||
width: 300px;
|
|
||||||
height: 7.67vh;
|
|
||||||
line-height: 7.67vh;
|
|
||||||
border-radius: 3.33vh 3.33vh 0 0;
|
|
||||||
background-size: 100%;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
&:first-child {
|
|
||||||
margin-right: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.area {
|
|
||||||
position: absolute;
|
|
||||||
top: 9.58vh;
|
|
||||||
left: 48px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: calc(100% - 96px);
|
|
||||||
height: 71.92vh;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.tool {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 2vh;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 188px;
|
|
||||||
height: 5.33vh;
|
|
||||||
border-radius: 4.5vh;
|
|
||||||
background: rgba(0, 0, 0, 0.45);
|
|
||||||
&_fangda {
|
|
||||||
margin-right: 32px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.area-content {
|
|
||||||
width: 825px;
|
|
||||||
height: 71.92vh;
|
|
||||||
}
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
max-height: 100%;
|
|
||||||
object-fit: contain;
|
|
||||||
cursor: grab;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,557 +0,0 @@
|
|||||||
<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>
|
|
||||||
@ -242,12 +242,7 @@ onUnmounted(() => {
|
|||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div class="uncomplete">
|
||||||
class="uncomplete"
|
|
||||||
:style="{
|
|
||||||
'background-image': `url(${OSS_URL}/urm/mail_bg.svg)`,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<div v-if="!initLoading" class="hidden_scroll">
|
<div v-if="!initLoading" class="hidden_scroll">
|
||||||
<div
|
<div
|
||||||
class="book"
|
class="book"
|
||||||
@ -463,6 +458,7 @@ onUnmounted(() => {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
|
background-color: $theme-color-bg;
|
||||||
.book {
|
.book {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@include book_style;
|
@include book_style;
|
||||||
|
|||||||
@ -122,12 +122,11 @@ function handleGetDynamic(item: any) {
|
|||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.dynamic-list {
|
.dynamic-list {
|
||||||
width: 127.33vh;
|
width: 127.33vh;
|
||||||
// 1240 +168 +120
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 86vh;
|
top: 85vh;
|
||||||
left: 4vh;
|
left: 1vh;
|
||||||
height: 10.83vh;
|
height: 10.83vh;
|
||||||
&-item,
|
&-item,
|
||||||
.day,
|
.day,
|
||||||
@ -229,7 +228,7 @@ function handleGetDynamic(item: any) {
|
|||||||
}
|
}
|
||||||
.achievement-reward {
|
.achievement-reward {
|
||||||
width: 97.17vh;
|
width: 97.17vh;
|
||||||
margin-left: 15vh;
|
margin-left: 14vh;
|
||||||
.bg-slider {
|
.bg-slider {
|
||||||
width: 78.33vh;
|
width: 78.33vh;
|
||||||
left: 9.33vh;
|
left: 9.33vh;
|
||||||
|
|||||||
@ -476,7 +476,7 @@ onUnmounted(() => {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
background: $blue-gradient;
|
background: $theme-color-bg;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
padding-left: $space-6;
|
padding-left: $space-6;
|
||||||
@ -493,6 +493,7 @@ onUnmounted(() => {
|
|||||||
$right_w: 131.92vh;
|
$right_w: 131.92vh;
|
||||||
.right {
|
.right {
|
||||||
width: $right_w;
|
width: $right_w;
|
||||||
|
margin-left: 2vh;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
.button-allReward {
|
.button-allReward {
|
||||||
|
|||||||
@ -1,8 +1,11 @@
|
|||||||
// 主题
|
// 主题
|
||||||
|
$white-gradient: #fdfdf8; // 白系背景
|
||||||
|
$blue-gradient: #7CABFF;// 蓝色背景
|
||||||
|
|
||||||
$theme-color: #ffe60f; // 品牌色/主操作按钮/按钮移入/文字链
|
$theme-color: #ffe60f; // 品牌色/主操作按钮/按钮移入/文字链
|
||||||
$theme-minor-color: #fffbd2; // 品牌色/主操作按钮/按钮移入/文字链
|
$theme-minor-color: #fffbd2; // 品牌色/主操作按钮/按钮移入/文字链
|
||||||
// 主题色背景
|
// 主题色背景
|
||||||
$theme-color-bg: #D4E0F5;
|
$theme-color-bg: $blue-gradient; // #b1c3e3;
|
||||||
|
|
||||||
// 辅助颜色
|
// 辅助颜色
|
||||||
$success-color: #07cc89;
|
$success-color: #07cc89;
|
||||||
@ -79,8 +82,6 @@ $border-input-color: #e8eaec;
|
|||||||
$gradient: linear-gradient(180deg, #4de798 0%, #a2fb6e 100%); // 主渐变
|
$gradient: linear-gradient(180deg, #4de798 0%, #a2fb6e 100%); // 主渐变
|
||||||
$gray_bg: $theme-color-bg; // 灰系背景
|
$gray_bg: $theme-color-bg; // 灰系背景
|
||||||
$grey-gradient: $theme-color-bg; // 灰系背景
|
$grey-gradient: $theme-color-bg; // 灰系背景
|
||||||
$white-gradient: $theme-color-bg; // 白系背景
|
|
||||||
$blue-gradient: #7CABFF;// 蓝色背景
|
|
||||||
|
|
||||||
$page-fill: #fafafa;
|
$page-fill: #fafafa;
|
||||||
$bg-fill: #fff;
|
$bg-fill: #fff;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user