判断视频是否完成,自动播放下一个视频

main
king 1 year ago
parent 331e7dc95f
commit 0054b7741d
  1. 2
      src/api/curriculum.ts
  2. 7
      src/api/request.ts
  3. 15
      src/api/user.ts
  4. 3
      src/app.tsx
  5. 2
      src/components/video/video.tsx
  6. 7
      src/hooks/unique_ident.ts
  7. 62
      src/pages/business/curHistory/curHistory.tsx
  8. 4
      src/pages/business/userInfo/userInfo.tsx
  9. 3
      src/pages/business/videoInfo/components/catalogue.tsx
  10. 4
      src/pages/business/videoInfo/components/course.tsx
  11. 58
      src/pages/business/videoInfo/videoInfo.tsx
  12. 21
      src/pages/manage/depAdmin/depAdmin.tsx

@ -85,7 +85,7 @@ export const curriculum = {
delCur(dep_id: number, course_id: number) {
return request(`/api/v1/department/assign/${dep_id}?course_id=${course_id}`, "DELETE")
},
/** 部门课程进度 */
/** 课程进度 */
course() {
return request<Course>(`/api/v1/user/courses`, "GET")
},

@ -43,6 +43,7 @@ export const ERROR_STATUS: Record<number | string, string> = {
'INVALID_DATA': '服务器响应异常~',
'OVERSTEP': '请求越界~'
}
let isOverdue = false
export function request<T = unknown>(
url: string,
@ -70,6 +71,9 @@ export function request<T = unknown>(
})
option.url += parameter
}
if (isOverdue) {
isOverdue = false
}
data && (option.data = data)
return new Promise<T>((resolve, reject) => {
Taro.request<T>({
@ -80,6 +84,8 @@ export function request<T = unknown>(
if (data?.code === 0 && res.statusCode === 200) {
resolve(data.data || [])
} else if (res.statusCode === 401) {
if (!isOverdue) {
isOverdue = true
Taro.showModal({
title: "登录过期,需重新登陆",
showCancel: false,
@ -87,6 +93,7 @@ export function request<T = unknown>(
confirm && Taro.reLaunch({url: '/pages/login/login'})
}
})
}
} else {
Taro.showToast({title: data.msg || ERROR_STATUS[res.statusCode] || '请求错误~', icon: 'error'})
reject(null)

@ -35,6 +35,16 @@ interface LearningRecord {
user_course_records: LearnRecord[]
}
interface CourseRecord {
course: Curriculum
data: Record<number, userRecord>
}
interface HourCourse{
courseHour:Record<number, Hour>,
date:Record<number, { start:number,end:number} >
duration:Record<number, number>
}
export const userApi = {
login(code: string) {
@ -64,6 +74,9 @@ export const userApi = {
return request<Record<number, userRecord>>(`/api/v1/course/${category_id}/record`, "GET")
},
courseRecord(course_id: number) {
return request<Record<number, userRecord>>(`/api/v1/course/${course_id}/info`, "GET")
return request<CourseRecord>(`/api/v1/course/${course_id}/info`, "GET")
},
hourCourse(course_id: number, unique_ident: number) {
return request<HourCourse>(`/api/v1/course/${course_id}/info/${unique_ident}`, "GET")
}
}

@ -1,6 +1,7 @@
import Taro, {useDidShow, useDidHide} from '@tarojs/taro'
import './app.scss'
import {CustomWrapper} from "@tarojs/components";
import unique_ident from "@/hooks/unique_ident";
function updateApp() {
if (Taro.canIUse('getUpdateManager')) {
@ -38,6 +39,8 @@ function App(props) {
if (!token) {
Taro.reLaunch({url: '/pages/login/login'})
}
unique_ident.put()
unique_ident.del()
})

@ -35,8 +35,8 @@ const HVideo: FC<HVideoOptions> = (opt: HVideoOptions) => {
}
opt.setTime((time: number) => {
video.stop()
video.seek(time)
video.play()
})
function onEnded() {

@ -31,8 +31,13 @@ function put(duration?: number, start_date?: number, upload = true) {
}
}
function del() {
Taro.removeStorageSync(KEY)
}
export default {
set,
put
put,
del
}

@ -9,44 +9,66 @@ const CurHistory = () => {
const [show, setShow] = useState(false)
const {course_id, name} = Taro.getCurrentInstance().preloadData as { course_id: number, name: string }
const [data, setData] = useState<userRecord[]>([])
const [course, setCourse] = useState<Curriculum | null>(null)
const [hours, setHours] = useState<Hour[] | null>(null)
const [durations, setDuration] = useState<Record<number, number> | null>(null)
const [time, setTime] = useState<Record<number, { start: number, end: number }> | null>(null)
Taro.useLoad(() => {
Taro.setNavigationBarTitle({title: name})
userApi.courseRecord(course_id).then(res => {
setData(Object.values(res))
setData(Object.values(res.data))
setCourse(res.course)
})
})
function setHour() {
async function setHour(unique_ident: number) {
const res = await userApi.hourCourse(course_id, unique_ident)
setHours(Object.values(res.courseHour))
setDuration(res.duration)
setTime(res.date)
setShow(true)
}
function percent(duration: number): number {
if (!course?.course_duration) return 0
return Number((duration / course.course_duration * 100).toFixed(2))
}
function getTime(id: number): string {
const start = formatDateTime(new Date(time?.[id].start || 0), 'MM/dd HH:mm')
const end = formatDateTime(new Date(time?.[id].end || 0), 'MM/dd HH:mm')
return `${start} - ${end}`
}
function hourPercent(id: number, duration: number): number {
const viewingTime = durations?.[id] || 0
console.log(viewingTime,duration)
return Number((viewingTime / duration * 100).toFixed(2))
}
return (
<View>
<View className={styles.cur}>
<Image src={''} mode='aspectFit'/>
<Image src={course?.thumb || ''} mode='widthFix'/>
<View>{name}</View>
</View>
<View className={styles.record}>
{
data.map(d => <View onClick={() => setHour()} className={styles.recordItem}>
<View>{formatDateTime(new Date(d.start_at), "MM-dd HH:ss")} - {formatDateTime(new Date(d.end_at), "MM-dd HH:ss")}</View>
<View>{formatMinute(d.duration)}</View>
{data.map(d => <View onClick={() => setHour(d.unique_ident)} className={styles.recordItem}>
<View>{formatDateTime(new Date(d.start_at), "MM/dd HH:ss")} - {formatDateTime(new Date(d.end_at), "MM/dd HH:ss")}</View>
<View>{formatMinute(d.duration)}</View>
<Progress
percent={d.duration}
percent={percent(d.duration)}
showInfo
borderRadius={10}
active
duration={10}
strokeWidth={10}
color={'#45D4A8'}
/>
</View>)
}
</View>)}
</View>
<PageContainer
@ -54,12 +76,16 @@ const CurHistory = () => {
round
onAfterLeave={() => setShow(false)}>
{show && <View className={styles.hourRecord}>
<View>
<View onClick={() => setHour()} className={styles.recordItem}>
<View>2023-6-19</View>
{
hours?.map(d => <View>
<View className={styles.recordItem}>
<View>{d.title}</View>
<View>{getTime(d.id)}</View>
<Progress
percent={10}
percent={hourPercent(d.id, d.duration)}
showInfo
duration={10}
borderRadius={10}
active
strokeWidth={10}
@ -67,6 +93,8 @@ const CurHistory = () => {
/>
</View>
</View>
)
}
</View>}
</PageContainer>
</View>

@ -2,7 +2,7 @@ import {FC, useState} from "react";
import {Profile} from '@/store'
import avatar from "@/static/img/avatar.png"
import PopPut from "@/components/popPut/popPut";
import {Button, CustomWrapper, Input, View} from "@tarojs/components";
import {Button, Input, View} from "@tarojs/components";
import Taro from "@tarojs/taro";
import {userApi} from "@/api";
import styles from './userInfo.module.scss'
@ -70,11 +70,9 @@ const List = () => {
const userInfo: FC = () => {
return (
<CustomWrapper>
<Profile.Provider>
<List/>
</Profile.Provider>
</CustomWrapper>
)
}

@ -23,7 +23,8 @@ const Catalogue: FC<Props> = ({data, setHors, id}) => {
const [current, setCurrent] = useState(1)
function jumCurHistory() {
Taro.navigateTo({url: `/pages/business/curHistory/curHistory?id=${id}&name=${data?.course.title}`})
Taro.preload({course_id: id, name: data?.course.title})
Taro.navigateTo({url: `/pages/business/curHistory/curHistory`})
}
function tabChange({tab}: OnChangOpt) {

@ -55,7 +55,6 @@ const Course: FC<Props> = ({id, courseId, preview, curEnd}: Props) => {
/** 进入断点 */
function onBreakpoint(breakpoint: number) {
console.log(breakpoint)
if (breakpoint !== time) {
setFrequency(count?.[breakpoint] || 1)
}
@ -162,7 +161,8 @@ const Course: FC<Props> = ({id, courseId, preview, curEnd}: Props) => {
const index = old.indexOf(time)
old.splice(index, 1)
setBreakpoint(old)
Taro.showToast({title: '全部通过'})
Taro.showToast({title: '答题正确', duration: 1000})
seek(time)
init()
} else {
Taro.showToast({title: '错误', icon: 'error'})

@ -6,6 +6,8 @@ import {Profile} from '@/store'
import Catalogue from "./components/catalogue";
import Course from "./components/course";
import Taro from "@tarojs/taro";
import unique_ident from "@/hooks/unique_ident";
const VideoInfo: FC = () => {
const {id, depId} = Taro.getCurrentInstance().preloadData as { id: number, depId: number | null }
@ -18,6 +20,10 @@ const VideoInfo: FC = () => {
if (res) {
setData(res)
}
/** 用于自动播放 判断当前课程是否完成 */
if (playId) {
currentVideo()
}
}
function setHors(is_complete: boolean, play_id: number) {
@ -29,12 +35,62 @@ const VideoInfo: FC = () => {
getData()
})
/** 播放下一个视频 */
function playNext() {
const flats: Hour[] = Object.values(data?.hours || {}).flat(Infinity) as Hour[]
if (playId === flats.at(-1)?.id) {
Taro.showModal({title: '当前课程结束'})
return;
}
for (const [index, flat] of flats.entries()) {
if (flat.id === playId) {
const next = flats[index + 1]
if (next) {
Taro.showModal({
title: '是否播放下一个视频',
success({confirm}) {
if (confirm) {
setPlayId(next.id)
setPreview(false)
}
}
})
}
return
}
}
}
/**
*
*/
function currentVideo() {
const courseHourRecordsFinish = data?.learn_hour_records.find(d => d.id === playId)?.courseHourRecordsFinish
if (typeof courseHourRecordsFinish === 'number') {
if (courseHourRecordsFinish === 1) {
playNext()
} else {
Taro.showModal({
title: '有考卷还未完成',
content: '未完成考卷不能播放下一个视频',
confirmText: '考试',
success({confirm}) {
confirm && Taro.navigateTo({url: `/pages/business/test/test?testId=${playId}`})
}
})
}
}
}
Taro.useDidShow(() => {
if (data) {
getData()
getData().then()
}
})
Taro.useUnload(unique_ident.put)
return (
<Profile.Provider>
<View className='content'>

@ -156,13 +156,19 @@ const DepAdmin: FC = () => {
}
function userManagesSheet(user: User) {
Taro.showActionSheet({
itemList: [
const itemList = [
'修改',
'删除',
'设置为管理员',
"日学习记录"
],
"日学习记录",
]
if (user.role_type === 1) {
itemList.push('取消管理员')
} else if (user.role_type === 0) {
itemList.push('设置为管理员')
}
Taro.showActionSheet({
itemList,
success({tapIndex}) {
switch (tapIndex) {
case 0:
@ -172,10 +178,10 @@ const DepAdmin: FC = () => {
delUser(user.id)
break
case 2:
setRoleType(user)
Taro.navigateTo({url: `/pages/manage/college/college?id=${user.id}&name=${user.name}`})
break
case 3:
Taro.navigateTo({url: `/pages/manage/college/college?id=${user.id}&name=${user.name}`})
setRoleType(user)
break
}
}
@ -235,6 +241,7 @@ const DepAdmin: FC = () => {
leftImage={d.avatar}
title={d.name}
onClick={() => userManagesSheet(d)}
content={['学员', '管理员', '超级管理员'][d.role_type]}
/>)
}

Loading…
Cancel
Save