提交 93dc694f authored 作者: 王鹏飞's avatar 王鹏飞

chore: update

上级 f1424aff
...@@ -9,7 +9,7 @@ const userInfo = userStore.user ...@@ -9,7 +9,7 @@ const userInfo = userStore.user
const logout = async () => { const logout = async () => {
await userStore.logout() await userStore.logout()
location.href = `${import.meta.env.VITE_LOGIN_URL}?rd=${encodeURIComponent(location.href)}` location.href = `${import.meta.env.VITE_LOGIN_URL}?rd=${encodeURIComponent(location.origin)}`
} }
</script> </script>
......
<script setup lang="ts"> <script setup lang="ts">
import type { PaperQuestionType } from '@/types' import type { PaperQuestionType } from '@/types'
import { ElMessage } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { ArrowLeftBold } from '@element-plus/icons-vue' import { ArrowLeftBold } from '@element-plus/icons-vue'
import { cachePaper, submitPaper } from '../api' import { cachePaper, submitPaper } from '../api'
...@@ -64,6 +64,32 @@ function handleNext() { ...@@ -64,6 +64,32 @@ function handleNext() {
} }
// 提交 // 提交
function handleSubmit() { function handleSubmit() {
// 未作答的数量
const unansweredNum = questionList.reduce((result, item) => {
if (item.children && item.children.length) {
item.children.forEach(item => {
!item.user_answer && result++
})
} else {
!item.user_answer && result++
}
return result
}, 0)
if (unansweredNum) {
ElMessageBox.confirm(`您还有${unansweredNum}道题没有作答`, '请确认', {
confirmButtonText: '确认提交',
cancelButtonText: '继续作答',
type: 'warning'
}).then(() => {
handleSubmitPaper()
})
} else {
handleSubmitPaper()
}
}
// 提交试卷
function handleSubmitPaper() {
submitLoading = true submitLoading = true
clearInterval(timer) clearInterval(timer)
submitPaper({ submitPaper({
...@@ -81,8 +107,8 @@ function handleSubmit() { ...@@ -81,8 +107,8 @@ function handleSubmit() {
submitLoading = false submitLoading = false
}) })
} }
// 自动提交 // 提交缓存试卷
function handleAutoSubmit() { function handleSubmitCachePaper() {
if (disabled) return if (disabled) return
cachePaper({ cachePaper({
course_id: courseId, course_id: courseId,
...@@ -92,7 +118,7 @@ function handleAutoSubmit() { ...@@ -92,7 +118,7 @@ function handleAutoSubmit() {
question: JSON.stringify(genSubmitQuestion(questionList)) question: JSON.stringify(genSubmitQuestion(questionList))
}) })
} }
const timer = setInterval(handleAutoSubmit, 5000) const timer = setInterval(handleSubmitCachePaper, 5000)
onUnmounted(() => { onUnmounted(() => {
clearInterval(timer) clearInterval(timer)
}) })
...@@ -122,13 +148,18 @@ function genSubmitQuestion(questionList: PaperQuestionType[]) { ...@@ -122,13 +148,18 @@ function genSubmitQuestion(questionList: PaperQuestionType[]) {
<div class="course-exam-card-bd"> <div class="course-exam-card-bd">
<div class="course-exam-left"> <div class="course-exam-left">
<div class="course-exam-scroll"> <div class="course-exam-scroll">
<CourseExamQuestion :question="currentQuestion" :status="status" :index="questionIndex + 1"> <CourseExamQuestion
:question="currentQuestion"
:status="status"
:index="questionIndex + 1"
v-if="questionLength"
>
<template #index>{{ questionIndex + 1 }}/{{ questionLength }}</template> <template #index>{{ questionIndex + 1 }}/{{ questionLength }}</template>
</CourseExamQuestion> </CourseExamQuestion>
</div> </div>
<div class="course-exam-buttons"> <div class="course-exam-buttons">
<el-button size="large" :disabled="questionIndex === 0" @click="handlePrev">上一题</el-button> <el-button size="large" :disabled="questionIndex === 0" @click="handlePrev">上一题</el-button>
<el-button size="large" :disabled="questionIndex === questionLength - 1" @click="handleNext"> <el-button size="large" :disabled="questionIndex >= questionLength - 1" @click="handleNext">
下一题 下一题
</el-button> </el-button>
</div> </div>
......
...@@ -5,6 +5,7 @@ const router = useRouter() ...@@ -5,6 +5,7 @@ const router = useRouter()
const courseId = $ref<string>(inject('courseId')) const courseId = $ref<string>(inject('courseId'))
const semesterId = $ref<string>(inject('semesterId')) const semesterId = $ref<string>(inject('semesterId'))
const paperId = $ref<string>(inject('paperId')) const paperId = $ref<string>(inject('paperId'))
const paperTitle = $ref<string>(inject('paperTitle'))
const type = $ref<string>(inject('type')) const type = $ref<string>(inject('type'))
// 所有试题 // 所有试题
const questionList = $ref<PaperQuestionType[]>(inject('questionList')) const questionList = $ref<PaperQuestionType[]>(inject('questionList'))
...@@ -34,7 +35,7 @@ function genClass(data: PaperQuestionType) { ...@@ -34,7 +35,7 @@ function genClass(data: PaperQuestionType) {
function handleClick(index: number) { function handleClick(index: number) {
router.push({ router.push({
path: '/course/exam', path: '/course/exam',
query: { course_id: courseId, semester_id: semesterId, paper_id: paperId, type, index } query: { course_id: courseId, semester_id: semesterId, paper_id: paperId, type, index, paper_title: paperTitle }
}) })
} }
</script> </script>
...@@ -106,7 +107,7 @@ function handleClick(index: number) { ...@@ -106,7 +107,7 @@ function handleClick(index: number) {
border: 2px solid #ccc; border: 2px solid #ccc;
color: #666; color: #666;
&:nth-child(5n) { &:nth-child(10n) {
margin-right: 0; margin-right: 0;
} }
} }
...@@ -140,6 +141,11 @@ function handleClick(index: number) { ...@@ -140,6 +141,11 @@ function handleClick(index: number) {
color: #666; color: #666;
border: 2px solid #ccc; border: 2px solid #ccc;
} }
.is-error {
color: #666;
border: 2px solid #c01540;
}
.is-review { .is-review {
color: #fff; color: #fff;
background-color: blue; background-color: blue;
...@@ -160,8 +166,4 @@ function handleClick(index: number) { ...@@ -160,8 +166,4 @@ function handleClick(index: number) {
.is-info.is-success { .is-info.is-success {
color: #fff; color: #fff;
} }
.is-error {
color: #666;
border: 2px solid #c01540;
}
</style> </style>
...@@ -21,7 +21,7 @@ const props = defineProps<Props>() ...@@ -21,7 +21,7 @@ const props = defineProps<Props>()
const targetUrl = computed(() => { const targetUrl = computed(() => {
const info = props.data.info const info = props.data.info
if (props.data.resource_type === 3 || props.data.resource_type === 9) { if (props.data.resource_type === 3 || props.data.resource_type === 9) {
return `/course/exam?course_id=${courseId}&semester_id=${semesterId}&paper_id=${props.data.resource_id}&type=2` return `/course/exam?course_id=${courseId}&semester_id=${semesterId}&paper_id=${props.data.resource_id}&type=2&paper_title=${props.data.name}`
} else if (['pptx', 'doc', 'docx', 'xls', 'xlsx'].includes(info.type)) { } else if (['pptx', 'doc', 'docx', 'xls', 'xlsx'].includes(info.type)) {
return `https://view.officeapps.live.com/op/view.aspx?src=${info.url}` return `https://view.officeapps.live.com/op/view.aspx?src=${info.url}`
} else { } else {
...@@ -70,7 +70,7 @@ function downloadFile(data: CourseResourceType) { ...@@ -70,7 +70,7 @@ function downloadFile(data: CourseResourceType) {
</a> </a>
</p> </p>
<router-link <router-link
:to="`/course/exam/result?course_id=${courseId}&semester_id=${semesterId}&paper_id=${data.resource_id}&type=2`" :to="`/course/exam/result?course_id=${courseId}&semester_id=${semesterId}&paper_id=${data.resource_id}&type=2&paper_title=${data.name}`"
target="_blank" target="_blank"
style="margin: 0 20px" style="margin: 0 20px"
v-if="data.paper_status === 2 || data.paper_status === 3" v-if="data.paper_status === 2 || data.paper_status === 3"
......
...@@ -41,7 +41,7 @@ const section = $computed(() => { ...@@ -41,7 +41,7 @@ const section = $computed(() => {
// 当前节 // 当前节
const resource = $computed(() => { const resource = $computed(() => {
return section?.resources.find(item => item.resource_id === resourceId) return section?.resources.find(item => item.resource_id === resourceId && item.resource_type === 2)
}) })
// 资源视频列表 // 资源视频列表
const videoList = $computed<CourseResourceType[]>(() => { const videoList = $computed<CourseResourceType[]>(() => {
...@@ -70,40 +70,41 @@ const currentPlayList = $computed<PlayItemType[]>(() => { ...@@ -70,40 +70,41 @@ const currentPlayList = $computed<PlayItemType[]>(() => {
}) })
// 获取视频信息 // 获取视频信息
function fetchInfo() { async function fetchInfo() {
if (!resource?.info.source_id) return if (!resource?.info.source_id) throw '资源不存在'
return getCoursePlayInfo({ source_id: resource.info.source_id }).then(res => { const res = await getCoursePlayInfo({ source_id: resource.info.source_id })
playList = res.data.playing_list playList = res.data.playing_list
changeSrc(currentPlayList[0])
})
} }
// 获取视频记录 // 获取视频记录
function fetchVideoRecords() { async function fetchVideoRecords() {
if (!resource) return if (!resource) throw '资源不存在'
getVideoRecords({ const res = await getVideoRecords({
chapter_id: chapterId, chapter_id: chapterId,
course_id: courseId, course_id: courseId,
section_id: sectionId, section_id: sectionId,
semester_id: semesterId, semester_id: semesterId,
video_id: resourceId video_id: resourceId
}).then(res => {
const { detail = {} } = res.data
progress.current_playing_time = detail.current_playing_time ? parseFloat(detail.current_playing_time) : 0
progress.max_playing_time = detail.max_playing_time ? parseFloat(detail.max_playing_time) : 0
progress.valid_playing_time = detail.valid_playing_time ? parseFloat(detail.valid_playing_time) : 0
progress.watchedTimePoint = []
if (videoJsPlayer && progress.current_playing_time) {
videoJsPlayer.currentTime(progress.current_playing_time)
}
}) })
const { detail = {} } = res.data
progress.current_playing_time = detail.current_playing_time ? parseFloat(detail.current_playing_time) : 0
progress.max_playing_time = detail.max_playing_time ? parseFloat(detail.max_playing_time) : 0
progress.valid_playing_time = detail.valid_playing_time ? parseFloat(detail.valid_playing_time) : 0
progress.watchedTimePoint = []
} }
watchEffect(async () => { watchEffect(async () => {
if (!resource) return
await fetchVideoRecords()
await fetchInfo() await fetchInfo()
fetchVideoRecords() changeSrc(currentPlayList[0])
nextTick(() => {
if (videoJsPlayer && progress.current_playing_time) {
videoJsPlayer.currentTime(progress.current_playing_time)
console.log('seek', progress.current_playing_time)
}
})
}) })
const throttledFn = throttle( const throttledFn = throttle(
() => { () => {
if (progress.watchedTimePoint.length < 5) return
uploadVideoRecords({ uploadVideoRecords({
chapter_id: chapterId, chapter_id: chapterId,
course_id: courseId, course_id: courseId,
...@@ -118,27 +119,17 @@ const throttledFn = throttle( ...@@ -118,27 +119,17 @@ const throttledFn = throttle(
// 清空已经上传过的观看时间点 // 清空已经上传过的观看时间点
progress.watchedTimePoint = [] progress.watchedTimePoint = []
}, },
5000, 5000
{ leading: false } // { leading: false }
) )
let watchedTime = 0
function onTimeUpdate() { function onTimeUpdate() {
console.log('onTimeUpdate')
if (!videoJsPlayer) return if (!videoJsPlayer) return
const time = Math.floor(videoJsPlayer.currentTime() ?? 0) const time = Math.floor(videoJsPlayer.currentTime() ?? 0)
// 更新当前播放时间 if (!time) return
progress.current_playing_time = time console.log('onTimeUpdate', time)
// 观看的最大点
progress.max_playing_time = Math.max(time, progress.max_playing_time)
// 观看时间点
const hasTimePoint = progress.watchedTimePoint.includes(time)
if (!hasTimePoint) {
progress.watchedTimePoint.push(time)
}
// 更新观看累计时长 // 更新观看累计时长
if (time !== watchedTime) { if (time !== progress.current_playing_time) {
watchedTime = time
// // 增加跳过片头时间 // // 增加跳过片头时间
// if (this.isSkip && !this.progress.pt) { // if (this.isSkip && !this.progress.pt) {
// this.progress.pt = this.skipTime + 20 // this.progress.pt = this.skipTime + 20
...@@ -147,6 +138,14 @@ function onTimeUpdate() { ...@@ -147,6 +138,14 @@ function onTimeUpdate() {
progress.valid_playing_time = progress.valid_playing_time || 20 progress.valid_playing_time = progress.valid_playing_time || 20
progress.valid_playing_time++ progress.valid_playing_time++
} }
// 更新当前播放时间
progress.current_playing_time = time
// 观看的最大点
progress.max_playing_time = Math.max(time, progress.max_playing_time)
// 观看时间点
const hasTimePoint = progress.watchedTimePoint.includes(time)
!hasTimePoint && progress.watchedTimePoint.push(time)
throttledFn() throttledFn()
} }
...@@ -162,15 +161,17 @@ const isAutoPlayNext = useStorage('isAutoPlayNext', false) ...@@ -162,15 +161,17 @@ const isAutoPlayNext = useStorage('isAutoPlayNext', false)
let isReady = $ref<boolean>(false) let isReady = $ref<boolean>(false)
let videoJsPlayer = $ref<VideoJsPlayer | null>() let videoJsPlayer = $ref<VideoJsPlayer | null>()
const onReady = (player: VideoJsPlayer) => { const onReady = (player: VideoJsPlayer) => {
videoJsPlayer = player
isReady = true isReady = true
videoJsPlayer = player
videoJsPlayer.currentTime(progress.current_playing_time)
console.log('seek', progress.current_playing_time)
} }
function changeSrc(data: PlayItemType) { function changeSrc(data: PlayItemType) {
// src = { src: data.PlayURL, type: 'application/x-mpegURL' } // src = { src: data.PlayURL, type: 'application/x-mpegURL' }
src = { src: data.PlayURL, type: 'video/mp4' } src = { src: data.PlayURL, type: 'video/mp4' }
} }
function changeResource(data: CourseResourceType) { function changeResource(data: CourseResourceType) {
throttledFn && throttledFn.cancel() throttledFn && throttledFn.flush()
resourceId = data.resource_id resourceId = data.resource_id
} }
</script> </script>
......
...@@ -81,9 +81,9 @@ function downloadFile(data: CourseResourceType) { ...@@ -81,9 +81,9 @@ function downloadFile(data: CourseResourceType) {
// 跳转链接 // 跳转链接
function targetUrl(resource: CourseResourceType, section: CourseSectionType, chapter: CourseChapterType) { function targetUrl(resource: CourseResourceType, section: CourseSectionType, chapter: CourseChapterType) {
if (resource.resource_type === 3 || resource.resource_type === 9) { if (resource.resource_type === 3 || resource.resource_type === 9) {
return `/course/exam?course_id=${courseId}&semester_id=${semesterId}&paper_id=${resource.resource_id}&type=2` return `/course/exam?course_id=${courseId}&semester_id=${semesterId}&paper_id=${resource.resource_id}&type=2&paper_title=${resource.name}`
} }
return `/course/player?course_id=${courseId}&chapter_id=${chapter.id}&section_id=${section.id}&resource_id=${resource.resource_id}&semester_id=${semesterId}` return `/course/player?course_id=${courseId}&chapter_id=${chapter.id}&section_id=${section.id}&resource_id=${resource.resource_id}&semester_id=${semesterId}&paper_title=${resource.name}`
} }
</script> </script>
<template> <template>
...@@ -102,7 +102,7 @@ function targetUrl(resource: CourseResourceType, section: CourseSectionType, cha ...@@ -102,7 +102,7 @@ function targetUrl(resource: CourseResourceType, section: CourseSectionType, cha
</router-link> </router-link>
</p> </p>
<router-link <router-link
:to="`/course/exam/result?course_id=${courseId}&semester_id=${semesterId}&paper_id=${resource.resource_id}&type=2`" :to="`/course/exam/result?course_id=${courseId}&semester_id=${semesterId}&paper_id=${resource.resource_id}&type=2&paper_title=${resource.name}`"
target="_blank" target="_blank"
style="margin: 0 20px" style="margin: 0 20px"
v-if="resource.paper_status === 2 || resource.paper_status === 3" v-if="resource.paper_status === 2 || resource.paper_status === 3"
......
...@@ -14,13 +14,13 @@ const semesterId = $ref(query.semester_id as string) ...@@ -14,13 +14,13 @@ const semesterId = $ref(query.semester_id as string)
<div class="course-exam-item" :key="data.id"> <div class="course-exam-item" :key="data.id">
<p> <p>
<router-link <router-link
:to="`/course/exam?course_id=${courseId}&semester_id=${semesterId}&paper_id=${data.id}&type=1`" :to="`/course/exam?course_id=${courseId}&semester_id=${semesterId}&paper_id=${data.id}&type=1&paper_title=${data.paper_title}`"
target="_blank" target="_blank"
>{{ data.paper_title }}</router-link >{{ data.paper_title }}</router-link
> >
</p> </p>
<router-link <router-link
:to="`/course/exam/result?course_id=${courseId}&semester_id=${semesterId}&paper_id=${data.id}&type=1`" :to="`/course/exam/result?course_id=${courseId}&semester_id=${semesterId}&paper_id=${data.id}&type=1&paper_title=${data.paper_title}`"
target="_blank" target="_blank"
v-if="data.paper_status === 2 || data.paper_status === 3" v-if="data.paper_status === 2 || data.paper_status === 3"
> >
......
...@@ -7,6 +7,7 @@ const route = useRoute() ...@@ -7,6 +7,7 @@ const route = useRoute()
const courseId = $ref(route.query.course_id as string) const courseId = $ref(route.query.course_id as string)
const semesterId = $ref(route.query.semester_id as string) const semesterId = $ref(route.query.semester_id as string)
const paperId = $ref(route.query.paper_id as string) const paperId = $ref(route.query.paper_id as string)
const paperTitle = $ref(route.query.paper_title as string)
const type = $ref(parseInt(route.query.type as string)) const type = $ref(parseInt(route.query.type as string))
const detail = reactive({ status: 1 }) const detail = reactive({ status: 1 })
let questionList = $ref<PaperQuestionType[]>([]) let questionList = $ref<PaperQuestionType[]>([])
...@@ -26,8 +27,12 @@ function fetchInfo() { ...@@ -26,8 +27,12 @@ function fetchInfo() {
onMounted(() => { onMounted(() => {
fetchInfo() fetchInfo()
}) })
const title = computed(() => {
if (paperTitle) return paperTitle
return type === 1 ? '课程考试' : '课后作业'
})
</script> </script>
<template> <template>
<CourseExamCard :status="detail.status" @update="fetchInfo" v-loading="loading"></CourseExamCard> <CourseExamCard :status="detail.status" :title="title" @update="fetchInfo" v-loading="loading"></CourseExamCard>
</template> </template>
...@@ -10,6 +10,8 @@ const semesterId = $ref(route.query.semester_id as string) ...@@ -10,6 +10,8 @@ const semesterId = $ref(route.query.semester_id as string)
provide('semesterId', semesterId) provide('semesterId', semesterId)
const paperId = $ref(route.query.paper_id as string) const paperId = $ref(route.query.paper_id as string)
provide('paperId', paperId) provide('paperId', paperId)
const paperTitle = $ref(route.query.paper_title as string)
provide('paperTitle', paperTitle)
const type = $ref(parseInt(route.query.type as string)) const type = $ref(parseInt(route.query.type as string))
provide('type', type) provide('type', type)
......
...@@ -85,7 +85,7 @@ function downloadFile(data: CollectionType) { ...@@ -85,7 +85,7 @@ function downloadFile(data: CollectionType) {
} }
function targetUrl(item: CollectionType) { function targetUrl(item: CollectionType) {
if (item.type === 5) { if (item.type === 5) {
return `/course/exam?course_id=${item.course_id}&semester_id=${item.semester_id}&paper_id=${item.info.id}&type=2` return `/course/exam?course_id=${item.course_id}&semester_id=${item.semester_id}&paper_id=${item.info.id}&type=2&paper_title=${item.info.paper_title}`
} }
return `/course/player?course_id=${item.course_id}&chapter_id=${item.chapter_id}&section_id=${item.section_id}&resource_id=${item.source_id}&semester_id=${item.semester_id}` return `/course/player?course_id=${item.course_id}&chapter_id=${item.chapter_id}&section_id=${item.section_id}&resource_id=${item.source_id}&semester_id=${item.semester_id}`
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论