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

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) { delCur(dep_id: number, course_id: number) {
return request(`/api/v1/department/assign/${dep_id}?course_id=${course_id}`, "DELETE") return request(`/api/v1/department/assign/${dep_id}?course_id=${course_id}`, "DELETE")
}, },
/** 部门课程进度 */ /** 课程进度 */
course() { course() {
return request<Course>(`/api/v1/user/courses`, "GET") return request<Course>(`/api/v1/user/courses`, "GET")
}, },

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

@ -35,6 +35,16 @@ interface LearningRecord {
user_course_records: LearnRecord[] 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 = { export const userApi = {
login(code: string) { login(code: string) {
@ -64,6 +74,9 @@ export const userApi = {
return request<Record<number, userRecord>>(`/api/v1/course/${category_id}/record`, "GET") return request<Record<number, userRecord>>(`/api/v1/course/${category_id}/record`, "GET")
}, },
courseRecord(course_id: number) { 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 Taro, {useDidShow, useDidHide} from '@tarojs/taro'
import './app.scss' import './app.scss'
import {CustomWrapper} from "@tarojs/components"; import {CustomWrapper} from "@tarojs/components";
import unique_ident from "@/hooks/unique_ident";
function updateApp() { function updateApp() {
if (Taro.canIUse('getUpdateManager')) { if (Taro.canIUse('getUpdateManager')) {
@ -38,6 +39,8 @@ function App(props) {
if (!token) { if (!token) {
Taro.reLaunch({url: '/pages/login/login'}) 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) => { opt.setTime((time: number) => {
video.stop()
video.seek(time) video.seek(time)
video.play()
}) })
function onEnded() { function onEnded() {

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

@ -9,44 +9,66 @@ const CurHistory = () => {
const [show, setShow] = useState(false) const [show, setShow] = useState(false)
const {course_id, name} = Taro.getCurrentInstance().preloadData as { course_id: number, name: string } const {course_id, name} = Taro.getCurrentInstance().preloadData as { course_id: number, name: string }
const [data, setData] = useState<userRecord[]>([]) 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.useLoad(() => {
Taro.setNavigationBarTitle({title: name}) Taro.setNavigationBarTitle({title: name})
userApi.courseRecord(course_id).then(res => { 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) 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 ( return (
<View> <View>
<View className={styles.cur}> <View className={styles.cur}>
<Image src={''} mode='aspectFit'/> <Image src={course?.thumb || ''} mode='widthFix'/>
<View>{name}</View> <View>{name}</View>
</View> </View>
<View className={styles.record}> <View className={styles.record}>
{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>
data.map(d => <View onClick={() => setHour()} className={styles.recordItem}> <View>{formatMinute(d.duration)}</View>
<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 <Progress
percent={d.duration} percent={percent(d.duration)}
showInfo showInfo
borderRadius={10} borderRadius={10}
active active
duration={10}
strokeWidth={10} strokeWidth={10}
color={'#45D4A8'} color={'#45D4A8'}
/> />
</View>) </View>)}
}
</View> </View>
<PageContainer <PageContainer
@ -54,12 +76,16 @@ const CurHistory = () => {
round round
onAfterLeave={() => setShow(false)}> onAfterLeave={() => setShow(false)}>
{show && <View className={styles.hourRecord}> {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 <Progress
percent={10} percent={hourPercent(d.id, d.duration)}
showInfo showInfo
duration={10}
borderRadius={10} borderRadius={10}
active active
strokeWidth={10} strokeWidth={10}
@ -67,6 +93,8 @@ const CurHistory = () => {
/> />
</View> </View>
</View> </View>
)
}
</View>} </View>}
</PageContainer> </PageContainer>
</View> </View>

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

@ -23,7 +23,8 @@ const Catalogue: FC<Props> = ({data, setHors, id}) => {
const [current, setCurrent] = useState(1) const [current, setCurrent] = useState(1)
function jumCurHistory() { 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) { function tabChange({tab}: OnChangOpt) {

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

@ -6,6 +6,8 @@ import {Profile} from '@/store'
import Catalogue from "./components/catalogue"; import Catalogue from "./components/catalogue";
import Course from "./components/course"; import Course from "./components/course";
import Taro from "@tarojs/taro"; import Taro from "@tarojs/taro";
import unique_ident from "@/hooks/unique_ident";
const VideoInfo: FC = () => { const VideoInfo: FC = () => {
const {id, depId} = Taro.getCurrentInstance().preloadData as { id: number, depId: number | null } const {id, depId} = Taro.getCurrentInstance().preloadData as { id: number, depId: number | null }
@ -18,6 +20,10 @@ const VideoInfo: FC = () => {
if (res) { if (res) {
setData(res) setData(res)
} }
/** 用于自动播放 判断当前课程是否完成 */
if (playId) {
currentVideo()
}
} }
function setHors(is_complete: boolean, play_id: number) { function setHors(is_complete: boolean, play_id: number) {
@ -29,12 +35,62 @@ const VideoInfo: FC = () => {
getData() 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(() => { Taro.useDidShow(() => {
if (data) { if (data) {
getData() getData().then()
} }
}) })
Taro.useUnload(unique_ident.put)
return ( return (
<Profile.Provider> <Profile.Provider>
<View className='content'> <View className='content'>

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

Loading…
Cancel
Save