判断题 && 取消答题次数和容错

main
king 1 year ago
parent 223ef62419
commit 702cee4693
  1. 4
      .env
  2. 2
      src/api/curriculum.ts
  3. 9
      src/components/topic/judge.tsx
  4. 5
      src/components/topic/multi.tsx
  5. 7
      src/components/topic/shortAnswer.tsx
  6. 3
      src/components/topic/topic.scss
  7. 13
      src/components/video/video.tsx
  8. 144
      src/pages/business/videoInfo/components/course.tsx
  9. 2
      types/topic.d.ts

@ -1,5 +1,5 @@
#TARO_APP_API=https://yjx.dev.yaojiankang.top TARO_APP_API=https://yjx.dev.yaojiankang.top
TARO_APP_API=https://playedu.yaojiankang.top #TARO_APP_API=https://playedu.yaojiankang.top
TARO_APP_LGOIN=true TARO_APP_LGOIN=true

@ -28,8 +28,6 @@ export interface HourPlayData {
url: string url: string
/** 断点题 */ /** 断点题 */
hourExamQuestions: Record<number, (ShareSubject | Multi)[]> hourExamQuestions: Record<number, (ShareSubject | Multi)[]>
/** 次数 */
fault_count: Record<number, number>[]
/** 断点 */ /** 断点 */
timeList: number[] timeList: number[]
hour_test?: { id: number } hour_test?: { id: number }

@ -6,10 +6,9 @@ interface Props {
data: ShareSubject data: ShareSubject
onAnswer: (isAnswer: boolean) => void onAnswer: (isAnswer: boolean) => void
validate: boolean validate: boolean
frequency?: number
} }
const Judge: FC<Props> = ({data, onAnswer, validate, frequency}) => { const Judge: FC<Props> = ({data, onAnswer, validate}) => {
const [rightAnswer, setRightAnswer] = useState<string | null>(null) //答案 const [rightAnswer, setRightAnswer] = useState<string | null>(null) //答案
const rightKey = data.right_answer === 'true' ? 'correct' : 'error' // 正确答案数组 const rightKey = data.right_answer === 'true' ? 'correct' : 'error' // 正确答案数组
const [error, setError] = useState(false) const [error, setError] = useState(false)
@ -43,19 +42,19 @@ const Judge: FC<Props> = ({data, onAnswer, validate, frequency}) => {
color={validate ? 'correct' !== rightKey ? 'red' : '#45d4a8' : '#45d4a8'} color={validate ? 'correct' !== rightKey ? 'red' : '#45d4a8' : '#45d4a8'}
disabled={validate ? 'correct' !== rightAnswer : false} disabled={validate ? 'correct' !== rightAnswer : false}
> >
<Text></Text> <Text>{data.right_value}</Text>
</Radio> </Radio>
<Radio <Radio
value={'error'} value={'error'}
color={validate ? 'error' !== rightKey ? 'red' : '#45d4a8' : '#45d4a8'} color={validate ? 'error' !== rightKey ? 'red' : '#45d4a8' : '#45d4a8'}
disabled={validate ? 'error' !== rightAnswer : false} disabled={validate ? 'error' !== rightAnswer : false}
className='option'> className='option'>
<Text></Text> <Text>{data.error_value}</Text>
</Radio> </Radio>
</RadioGroup> </RadioGroup>
{error && frequency == 0 && <View className='mt-3'> {error && <View className='mt-3'>
<View className='right_answer'>{data.right_answer ? '正确' : '错误'}</View> <View className='right_answer'>{data.right_answer ? '正确' : '错误'}</View>
<View className='font-weight my-3'></View> <View className='font-weight my-3'></View>
<View>{data.analysis}</View> <View>{data.analysis}</View>

@ -6,10 +6,9 @@ interface Props {
data: Multi data: Multi
onAnswer: (isAnswer: boolean) => void onAnswer: (isAnswer: boolean) => void
validate: boolean validate: boolean
frequency?: number
} }
const Multi: FC<Props> = ({data, onAnswer, validate, frequency}) => { const Multi: FC<Props> = ({data, onAnswer, validate}) => {
const [rightAnswer, setRightAnswer] = useState<string[]>([]) //答案 const [rightAnswer, setRightAnswer] = useState<string[]>([]) //答案
const rightKey = data?.right_answer?.split(',') || [] // 正确答案数组 const rightKey = data?.right_answer?.split(',') || [] // 正确答案数组
@ -90,7 +89,7 @@ const Multi: FC<Props> = ({data, onAnswer, validate, frequency}) => {
} }
</View> </View>
{error && frequency == 0 && <View className='mt-3'> {error && <View className='mt-3'>
<View className='right_answer'>{data.right_answer}</View> <View className='right_answer'>{data.right_answer}</View>
<View className='font-weight my-3'></View> <View className='font-weight my-3'></View>
<View>{data.analysis}</View> <View>{data.analysis}</View>

@ -6,10 +6,9 @@ interface Props {
data: ShareSubject data: ShareSubject
onAnswer: (isAnswer: boolean) => void onAnswer: (isAnswer: boolean) => void
validate: boolean validate: boolean
frequency?: number
} }
const ShortAnswer: FC<Props> = ({data, onAnswer, validate, frequency}) => { const ShortAnswer: FC<Props> = ({data, onAnswer, validate}) => {
const [value, setValue] = useState<string>('') const [value, setValue] = useState<string>('')
useEffect(() => { useEffect(() => {
@ -36,10 +35,10 @@ const ShortAnswer: FC<Props> = ({data, onAnswer, validate, frequency}) => {
onInput={(e) => setValue(e.detail.value)}/> onInput={(e) => setValue(e.detail.value)}/>
{frequency == 0 && <View className='mt-3'> <View className='mt-3'>
<View className='font-weight my-3'></View> <View className='font-weight my-3'></View>
<View>{data.analysis}</View> <View>{data.analysis}</View>
</View>} </View>
</View> </View>
) )

@ -35,9 +35,6 @@
border-top: 1px solid #F5F8F7; border-top: 1px solid #F5F8F7;
height: 60rpx; height: 60rpx;
padding: 20rpx 0; padding: 20rpx 0;
display: flex;
align-items: center;
justify-content: space-between;
} }
.upAndDown { .upAndDown {

@ -41,7 +41,6 @@ const HVideo: FC<HVideoOptions> = (opt: HVideoOptions) => {
/** 判断是否进入断点 */ /** 判断是否进入断点 */
opt.breakpoint.forEach(d => { opt.breakpoint.forEach(d => {
if (time < d + deviation && time > d - deviation) { if (time < d + deviation && time > d - deviation) {
video?.pause() video?.pause()
video?.seek(d - deviation) video?.seek(d - deviation)
if (process.env.TARO_ENV === 'h5') { if (process.env.TARO_ENV === 'h5') {
@ -51,17 +50,6 @@ const HVideo: FC<HVideoOptions> = (opt: HVideoOptions) => {
} }
} }
video?.exitFullScreen() video?.exitFullScreen()
// if (opt.preview) {
// Taro.showModal({
// title: '是否答题',
// success({confirm, cancel}) {
// confirm && opt.onBreakpoint(d)
// cancel && video?.play()
// }
// })
// return;
// }
opt.onBreakpoint(d) opt.onBreakpoint(d)
return return
} }
@ -131,7 +119,6 @@ const HVideo: FC<HVideoOptions> = (opt: HVideoOptions) => {
onPlay={onPlay} onPlay={onPlay}
onPause={onPause} onPause={onPause}
/> />
{/*<View style={{position: 'absolute', top: '100px', color: '#fff'}}>1221</View>*/}
</> </>
) )
} }

@ -1,12 +1,10 @@
import {ScrollView, Swiper, SwiperItem, Text, View} from "@tarojs/components"; import {ScrollView, Text, View} from "@tarojs/components";
import {FC, useEffect, useState} from "react"; import {FC, useEffect, useState} from "react";
import HVideo from "@/components/video/video"; import HVideo from "@/components/video/video";
import {CurEndParam, curriculum, HourPlayData} from "@/api"; import {CurEndParam, curriculum, HourPlayData} from "@/api";
import {Profile} from '@/store' import {Profile} from '@/store'
import Taro from "@tarojs/taro"; import Taro from "@tarojs/taro";
import Multi from "@/components/topic/multi";
import Judge from "@/components/topic/judge"; import Judge from "@/components/topic/judge";
import ShortAnswer from "@/components/topic/shortAnswer";
import unique_ident from "@/hooks/unique_ident"; import unique_ident from "@/hooks/unique_ident";
import MyButton from "@/components/button/MyButton"; import MyButton from "@/components/button/MyButton";
import {formatMinute} from "@/utils/time"; import {formatMinute} from "@/utils/time";
@ -21,21 +19,16 @@ interface Props {
let seek: (time: number) => void let seek: (time: number) => void
let record: boolean[] = [] //答案记录
const Course: FC<Props> = ({id, courseId, preview, curEnd}) => { const Course: FC<Props> = ({id, courseId, preview, curEnd}) => {
const [breakpoint, setBreakpoint] = useState<number[]>([]) // 断点 const [breakpoint, setBreakpoint] = useState<number[]>([]) // 断点
const [show, setShow] = useState(false) // 题 const [show, setShow] = useState(false) // 题
const [data, setData] = useState<HourPlayData | null>(null) const [data, setData] = useState<HourPlayData | null>(null)
const [examAll, setExamAll] = useState<Record<number, (ShareSubject | Multi)[]>>([]) // 题库 const [examAll, setExamAll] = useState<Record<number, (ShareSubject | Multi)[]>>([]) // 题库
const [index, setIndex] = useState(0)
const [time, setTime] = useState<number>(0) // 进入断点的时间 const [time, setTime] = useState<number>(0) // 进入断点的时间
const [validate, setValidate] = useState(false) // 开启验证 const [validate, setValidate] = useState(false) // 开启验证
const [newRecord, setNewRecord] = useState<boolean[]>([]) const [record, setRecord] = useState<boolean[]>([]) // 考题记录
const [faultCount, setFaultCount] = useState<Record<number, number> | null>(null)
const [frequency, setFrequency] = useState<number>(1) // 次数
const [testId, setTestId] = useState<number | null>(null) const [testId, setTestId] = useState<number | null>(null)
const [startRecording, setStartRecording] = useState<CurEndParam | null>(null) // 视频开始记录 const [startRecording, setStartRecording] = useState<CurEndParam | null>(null) // 视频开始记录
const [topicRecord, setRecord] = useState<number[]>([]) // 考题正确记录
const {user} = Profile.useContainer() const {user} = Profile.useContainer()
async function onEnded() { async function onEnded() {
@ -66,9 +59,6 @@ const Course: FC<Props> = ({id, courseId, preview, curEnd}) => {
/** 进入断点 */ /** 进入断点 */
function onBreakpoint(breakpoint: number) { function onBreakpoint(breakpoint: number) {
if (breakpoint !== time) {
setFrequency(faultCount?.[breakpoint] || 1)
}
setTime(breakpoint) setTime(breakpoint)
setShow(true) setShow(true)
} }
@ -82,9 +72,7 @@ const Course: FC<Props> = ({id, courseId, preview, curEnd}) => {
setData(res) setData(res)
setBreakpoint(res.timeList) setBreakpoint(res.timeList)
setExamAll(res.hourExamQuestions || []) setExamAll(res.hourExamQuestions || [])
setFaultCount(res.fault_count?.[0] || {})
setTestId(res?.hour_test?.id || null) setTestId(res?.hour_test?.id || null)
setRecord([])
setStartRecording({ setStartRecording({
duration: 0, duration: 0,
@ -109,55 +97,19 @@ const Course: FC<Props> = ({id, courseId, preview, curEnd}) => {
getData() getData()
}, [id]) }, [id])
/** 统计答案 */
function onAnswer(isAnswer: boolean) {
record.push(isAnswer)
setNewRecord(record)
}
function init(show = true) { function init(show = true) {
record = []
show && setShow(false) show && setShow(false)
setValidate(false) setValidate(false)
setNewRecord([]) setRecord([])
setTime(0) setTime(0)
setIndex(0)
}
/** 再来一次 */
function onceMore() {
const oldTime: number = time
init(false)
setTimeout(() => {
setTime(oldTime)
})
} }
/** 重新看 */
function again() {
if (examAll?.[time]?.length) {
const {id: question_id, question_type} = examAll?.[time]?.[0]
curriculum.answerRecord(id, {
is_pass: false,
user_id: user?.id!,
time: time,
question_id,
question_type
}).then(res => {
seek(res.time)
})
init()
}
setShow(false)
setIndex(0)
}
useEffect(() => { useEffect(() => {
if (!newRecord.length) return; if (!record.length) return;
setFrequency(frequency - 1) // 减少一次学习记录 const pass = record.every(d => d)
const pass = newRecord.every(d => d)
/** 考题正确 */ /** 考题正确 */
if (pass) {
const {id: question_id, question_type} = examAll?.[time]?.[0] const {id: question_id, question_type} = examAll?.[time]?.[0]
curriculum.answerRecord(id, { curriculum.answerRecord(id, {
is_pass: pass, is_pass: pass,
@ -173,33 +125,11 @@ const Course: FC<Props> = ({id, courseId, preview, curEnd}) => {
old.splice(index, 1) old.splice(index, 1)
setBreakpoint(old) setBreakpoint(old)
setRecord([...topicRecord, time]) if (pass) {
seek(time) seek(time)
Taro.showToast({title: '答题正确', duration: 2000})
init() init()
} else {
if ((frequency - 1) !== 0) {
Taro.showModal({
title: '考试未通过',
content: '剩余考试次数:' + (frequency - 1),
confirmText: '重考',
success({confirm}) {
confirm && onceMore()
} }
}) }, [record])
} else {
Taro.showModal({
title: '考试未通过',
content: '请重新学习',
success({confirm}) {
confirm && again()
}
})
}
}
}, [newRecord])
function videoSeek(fn: (time: number) => void) { function videoSeek(fn: (time: number) => void) {
seek = fn seek = fn
@ -220,73 +150,31 @@ const Course: FC<Props> = ({id, courseId, preview, curEnd}) => {
<CustomPageContainer <CustomPageContainer
show={show} show={show}
position='bottom' position='bottom'
onAfterLeave={again}
> >
<View> <View>
<View className='text-center mt-2 text-muted'> <View className='text-center mt-2 text-muted'>
<Text className='mr-2'>{formatMinute(time)}</Text> <Text className='mr-2'>{formatMinute(time)}</Text>
<Text>{index + 1}/{examAll?.[time]?.length}</Text>
</View> </View>
<Swiper {examAll?.[time]?.slice(0, 1)?.map((d) =>
style={{height: "60vh"}} <ScrollView style='height:60vh' scrollY key={d.id}>
snapToEdge
current={index}
onChange={(e) => setIndex(e.detail.current ?? index)}>
{examAll?.[time]?.map((d) =>
<SwiperItem key={d.id}>
<ScrollView style='height:70vh' scrollY>
{d.question_type === 1 &&
<Multi
frequency={frequency}
data={d as Multi}
onAnswer={onAnswer}
validate={validate}
/>}
{d.question_type === 2 && {d.question_type === 2 &&
<Judge <Judge
frequency={frequency}
data={d as ShareSubject}
validate={validate}
onAnswer={onAnswer}
/>}
{d.question_type === 3 &&
<ShortAnswer
frequency={frequency}
data={d as ShareSubject} data={d as ShareSubject}
onAnswer={onAnswer}
validate={validate} validate={validate}
onAnswer={(isAnswer) => setRecord([isAnswer])}
/>} />}
</ScrollView> </ScrollView>
</SwiperItem>
)} )}
</Swiper>
<View> <View>
<View className='upAndDown'>
<View>
{
index !== 0 &&
<MyButton type='default' size='mini' width={150}
onClick={() => setIndex(index - 1)}></MyButton>
}
</View>
<View>
{
(index + 1) !== examAll?.[time]?.length &&
<MyButton size='mini' width={150} type='default'
onClick={() => setIndex(index + 1)}></MyButton>
}
</View>
</View>
<View className='statistics'> <View className='statistics'>
<View>{frequency}</View>
{ {
(index + 1) === examAll?.[time]?.length && <View> record.length > 0
{!validate && <MyButton width={150} fillet onClick={() => setValidate(true)}></MyButton>} ? <MyButton fillet onClick={() => {
{frequency > 0 && validate && <MyButton width={150} fillet onClick={onceMore}></MyButton>} init();
{frequency === 0 && validate && <MyButton width={150} fillet onClick={again}></MyButton>} seek(time)
</View> }}></MyButton>
: <MyButton fillet onClick={() => setValidate(true)}></MyButton>
} }
</View> </View>
</View> </View>

2
types/topic.d.ts vendored

@ -8,6 +8,8 @@ interface ShareSubject {
analysis: string //课题解析 analysis: string //课题解析
course_id: number course_id: number
hour_id: number hour_id: number
right_value:string
error_value:string
} }
/** 选择题 */ /** 选择题 */

Loading…
Cancel
Save