| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484 |
- <template>
- <a-layout>
- <a-layout-sider v-model:collapsed="collapsed" :trigger="null" collapsible collapsedWidth="0">
- <div class="classTitle">
- <div>{{ classDetail.courseName }}</div>
- </div>
- <a-menu
- v-model:openKeys="openKeys"
- v-model:selectedKeys="selectedKeys"
- theme="dark"
- mode="inline"
- @click="menuClick"
- >
- <a-sub-menu :key="String(idx + 1)" v-for="(item, idx) in classTimeList">
- <template #title>{{ item.name }}</template>
- <a-menu-item :key="itemc.id" v-for="(itemc, idxc) in item.classHours">
- <span class="mr-2">
- <CheckSquareOutlined v-if="selectedKeys.includes(itemc.id)" />
- <BorderOutlined v-else />
- </span>
- <span>{{ itemc.name }}</span>
- </a-menu-item>
- </a-sub-menu>
- </a-menu>
- </a-layout-sider>
- <a-layout>
- <a-layout-header style="padding: 0 20px">
- <div class="flex-between">
- <div>
- <menu-unfold-outlined
- v-if="collapsed"
- class="trigger"
- @click="() => (collapsed = !collapsed)"
- style="font-size: 30px; color: #fff"
- />
- <menu-fold-outlined
- v-else
- class="trigger"
- @click="() => (collapsed = !collapsed)"
- style="font-size: 30px; color: #fff"
- />
- </div>
- <div @click="isLike">
- <a-tooltip title="收藏" style="cursor: pointer" :getPopupContainer="(trigger) => trigger.parentElement">
- <heart-outlined :style="{ 'font-size': '20px', color: classDetail.isCollect ? '#ff4d4f' : '#fff' }" />
- </a-tooltip>
- </div>
- </div>
- </a-layout-header>
- <a-layout-content style="overflow-y: auto">
- <a-card :bordered="false">
- <div v-if="videoUrl">
- <video
- style="height: 900px; width: 100%; background-color: #000"
- ref="videoRef"
- controls
- Controlslist="nodownload noplaybackrate noremoteplayback disablePictureInPicture"
- @timeupdate="timeUpdate"
- :currentTime="currentTimenew"
- :poster="videoPoster"
- >
- <source :src="videoUrl" type="video/mp4" />
- <source :src="videoUrl" type="video/ogg" />
- 您的浏览器不支持 HTML5 video 标签。
- </video>
- </div>
- <rightMenu
- :idsObj="idsObj"
- :dataList="classTimeData"
- ref="rightNenuRef"
- @videoSpeed="videoSpeed"
- @videoStopTime="videoStopTime"
- :videoObj="videoObj"
- :classDetailParams="classDetailParams(2)"
- ></rightMenu>
- </a-card>
- <a-card :bordered="false" class="mt-3">
- <div>
- <div>
- <div v-for="(item, idx) in webCon">
- <a :href="item.href" target="_blank">{{ item.title }}</a>
- </div>
- </div>
- <a-divider />
- <div>
- 在小学和初中,我们已经接触过一些集合.例如,自然数的集合,同一平面内到一个定点的距离等于定长的点的集合(即圆)等.为了更有效地使用集合语言,我们需要进一步了解集合的有关知识.下面先从集合的含义开始.
- 看下面的例子:<br />
- (1)1~10之间的所有偶数;<br />
- (2)立德中学今年人学的全体高一学生;<br />
- (3)所有的正方形;<br />
- (4)到直线l的距离等于定长d的所有点<br />
- (5)方程x’-3x+2=0的所有实数根;<br />
- (6)地球上的四大洋.<br />
- 例(1)中,我们把1~10之间的每一个偶数作为元素,这些元素的全体就是一个集合;同样地,<br />
- 例(2)中,把立德中学今年人学的每一位高一学生作为元素,这些元素的全体也是一个集合.
- </div>
- </div>
- </a-card>
- <div style="display: flex; justify-content: center">
- <a-card :bordered="false" class="mt-2" style="width: 100%; padding-right: 30px">
- <a-tabs v-model:activeKey="tabsActiveKey">
- <a-tab-pane key="1" tab="讲义" style="height: 800px">
- <handouts
- :itemObj="itemObj"
- :hourId="classHourData.id"
- v-if="classHourData"
- :videoObj="videoObj"
- ></handouts>
- </a-tab-pane>
- <a-tab-pane key="2" tab="字幕" style="height: 800px">
- <subtitleBox :url="danmuObj.url" v-if="tabsActiveKey == 2" @videoSpeed="videoSpeed"></subtitleBox>
- </a-tab-pane>
- <a-tab-pane key="3" tab="笔记" style="min-height: 800px">
- <note
- :idsObj="idsObj"
- ref="noteRef"
- v-if="tabsActiveKey == 3"
- @videoSpeed="videoSpeed"
- @videoStopTime="videoStopTime"
- ></note>
- </a-tab-pane>
- <a-tab-pane key="4" tab="问答" style="min-height: 800px">
- <askDiv
- :idsObj="idsObj"
- ref="askDivRef"
- v-if="tabsActiveKey == 4"
- @videoSpeed="videoSpeed"
- @videoStopTime="videoStopTime"
- ></askDiv>
- </a-tab-pane>
- </a-tabs>
- </a-card>
- </div>
- <forumBtn :forumData="forumData" resourceType="0"></forumBtn>
- </a-layout-content>
- </a-layout>
- </a-layout>
- </template>
- <script setup name="classCentre">
- import classCentre from '@/api/student/classCentre'
- import rightMenu from './rightMenu.vue'
- import { useRoute, useRouter } from 'vue-router'
- import sysConfig from '@/config/index'
- import note from './note.vue'
- import askDiv from './ask.vue'
- import handouts from './handouts.vue'
- import subtitleBox from './subtitle.vue'
- import Broadcast from '@/utils/Broadcast.js'
- import {message} from "ant-design-vue";
- const route = useRoute()
- const router = useRouter()
- const classDetail = ref({})
- const classTimeList = ref([])
- const classTimeData = ref([])
- const openKeys = ref(['1'])
- const selectedKeys = ref([])
- const collapsed = ref(false)
- const videoRef = ref()
- const currentTimenew = ref(0)
- const idsObj = computed(() => {
- let item = findNodeByKey(classTimeList.value, selectedKeys.value[0], classTimeList.value[0])
- return {
- courseId: route.query.id,
- chapterId: selectedKeys.value[0],
- hourId: classHourData.value?.id,
- ...item
- }
- })
- function findNodeByKey(list, id, parent = null) {
- for (const item of list) {
- const itemWithParent = {
- ...item,
- parent: parent
- }
- if (itemWithParent.id === id) {
- return itemWithParent
- }
- if (item.classHours && Array.isArray(item.classHours)) {
- const found = findNodeByKey(item.classHours, id, itemWithParent)
- if (found) return found
- }
- }
- return null
- }
- const videoPoster = computed(() => {
- return classTimeData.value.find((r) => r.funcType == 0)?.url
- })
- const getClassData = () => {
- classCentre.addViewCount({ courseId: route.query.id })
- classCentre.courseDetail({ courseId: route.query.id }).then((data) => {
- classDetail.value = data
- classCentre.coursechapterList({ courseId: data.courseId }).then((data) => {
- classTimeList.value = data
- selectedKeys.value = selectedKeys.value[0] ? selectedKeys.value : [data[0]?.classHours[0].id]
- if (selectedKeys.value[0]) {
- menuClick()
- }
- })
- })
- }
- const getClassStatus = (e) => {
- classCentre
- .classGetStatus({
- examPaperId: e.relateId
- })
- .then((res) => {
- const idx = classTimeData.value.findIndex((item) => item.funcType == e.funcType)
- classTimeData.value[idx].status = res.isFinish
- classTimeData.value[idx].answerId = res.answerId
- })
- }
- const classHourData = ref()
- const rightNenuRef = ref()
- const menuClick = (e) => {
- rightNenuRef.value.onClose()
- if (e) {
- addClassPlan(1)
- }
- classCentre.courseTimeDetail({ id: e?.key ? e.key : selectedKeys.value[0] }).then((data) => {
- classHourData.value = data
- classTimeData.value = data.courseRelates.map((r) => {
- return {
- ...r,
- url: sysConfig.FILE_URL + r.url
- }
- })
- videoUrl.value = classTimeData.value.find((r) => r.funcType == 1)?.url
- nextTick(() => {
- videoRef.value.src = classTimeData.value.find((r) => r.funcType == 1)?.url
- videoStart()
- footprintClassAdd()
- getVideoTime()
- const funcType4Item = classTimeData.value.find((r) => r.funcType == 4)
- const funcType5Item = classTimeData.value.find((r) => r.funcType == 5)
- if (funcType4Item) {
- getClassStatus(funcType4Item)
- }
- if (funcType5Item) {
- getClassStatus(funcType5Item)
- }
- })
- })
- }
- const tabsActiveKey = ref('1')
- const noteRef = ref()
- const askDivRef = ref()
- const itemObj = computed(() => {
- let item = classTimeData.value.length > 0 ? classTimeData.value.find((r) => r.funcType == 2) : { url: '' }
- return item
- })
- const danmuObj = computed(() => {
- let item = classTimeData.value.length > 0 ? classTimeData.value.find((r) => r.funcType == 3) : { url: '' }
- return item
- })
- const videoUrl = ref('')
- const newsschedule = ref(0)
- const currTime = ref(null)
- const maxTime = ref(0)
- const initialtime = ref(0)
- const videoContext = ref()
- const timeStamp1 = ref()
- const allTime = ref()
- const biNum = ref()
- const currentTime = ref()
- const videoObj = computed(() => {
- return {
- initialtime: initialtime.value,
- currentTime: currTime.value
- }
- })
- const videoStart = () => {
- videoContext.value = videoRef.value
- if (initialtime.value > 0) {
- videoContext.value.currentTime = initialtime.value
- timeStamp1.value = initialtime.value
- }
- }
- const timeUpdate = (e) => {
- //获取当前播放时间 durationinitialtime
- if (timeStamp1.value != parseInt(e.target.currentTime)) {
- allTime.value = parseInt(e.target.duration) //视频总时长(秒)
- //对播放的时间进行整
- timeStamp1.value = parseInt(e.target.currentTime) //播放进度 (秒)
- biNum.value = Math.floor((timeStamp1.value / allTime.value) * 10000) / 100 //暂时没用到
- currentTime.value = e.target.currentTime
- if (e.srcElement.currentTime - currTime.value > 3) {
- addClassPlan(3)
- }
- if (currTime.value - e.srcElement.currentTime > 3) {
- addClassPlan(3)
- }
- currTime.value = e.srcElement.currentTime
- }
- }
- const videoJumpTime = ref(false)
- const videoSpeed = (e) => {
- videoJumpTime.value = true
- currentTimenew.value = parseFloat(e.startTime)
- }
- const forumData = computed(() => {
- let item = findNodeByKey(classTimeList.value, selectedKeys.value[0], classTimeList.value[0]) ?? {}
- if (!item.src) {
- item.src = classTimeData.value.find((r) => r.funcType == 1)?.url
- }
- return {
- id: classHourData.value?.id,
- title: item.parent?.name,
- videoUrl: btoa(encodeURIComponent(videoRef.value?.src ? videoRef.value.src : item.src)),
- courseId: route.query.id,
- chapterId: selectedKeys.value[0],
- courseName: classDetail.value?.courseName,
- chapterName: classHourData.value?.name
- }
- })
- const showPdf = ref(true)
- function errorHandler() {
- showPdf.value = false
- }
- const getVideoTime = () => {
- classCentre
- .theLastDetail({
- funcType: 1,
- type: 1,
- hourId: classHourData.value?.id
- })
- .then((data) => {
- if (data?.endTime) {
- initialtime.value = parseFloat(data.endTime) / 1000
- currentTimenew.value = parseFloat(data.endTime) / 1000
- }
- })
- }
- // 毫秒转换时分秒
- function msToHMS(ms) {
- if (typeof ms !== 'number' || isNaN(ms)) return '00:00:00'
- let totalSeconds = Math.floor(ms / 1000)
- let hours = String(Math.floor(totalSeconds / 3600)).padStart(2, '0')
- let minutes = String(Math.floor((totalSeconds % 3600) / 60)).padStart(2, '0')
- let seconds = String(totalSeconds % 60).padStart(2, '0')
- return `${hours}:${minutes}:${seconds}`
- }
- // 时分秒转换毫秒
- function hmsToMs(hms) {
- if (!hms) return 0
- const parts = hms.split(':').map((p) => parseFloat(p.replace(',', '.')))
- // 支持 hh:mm:ss 或 mm:ss
- let ms = 0
- if (parts.length === 3) {
- ms = (parts[0] * 3600 + parts[1] * 60 + parts[2]) * 1000
- } else if (parts.length === 2) {
- ms = (parts[0] * 60 + parts[1]) * 1000
- } else if (parts.length === 1) {
- ms = parts[0] * 1000
- }
- return ms
- }
- const nowTimesStr = Date.now()
- const outNowTimesStr = ref()
- const addClassPlan = (type) => {
- outNowTimesStr.value = Date.now()
- let currentTime = videoRef.value?.currentTime
- let progress = parseFloat((currentTime / videoRef.value?.duration) * 100)
- if (
- (progress || progress == 0) &&
- (initialtime.value || initialtime.value == 0) &&
- (currentTime || currentTime == 0)
- ) {
- classCentre.classPlanAdd({
- startTime: parseFloat(initialtime.value),
- endTime: Math.round(currentTime * 1000),
- progress: Math.round(progress),
- hourId: classHourData.value?.id,
- stayTime: outNowTimesStr.value - nowTimesStr,
- type: type,
- funcType: 1,
- ...classDetailParams.value(1)
- })
- }
- }
- const isLike = () => {
- console.log('收藏状态',classDetail.value.isCollect)
- classCentre
- .classCollectAdd(
- {
- courseId: route.query.id
- },
- classDetail.value.isCollect
- )
- .then(() => {
- if(classDetail.value.isCollect == false){
- message.success('添加收藏')
- }else{
- message.warn('取消收藏')
- }
- classCentre.courseDetail({ courseId: route.query.id }).then((data) => {
- classDetail.value = data
- })
- })
- }
- const classDetailParams = computed(() => {
- return (funcType)=>{
- return {
- hourName: classHourData.value?.name,
- chapterName: classHourData.value?.name,
- courseName: classDetail.value?.courseName,
- hourId: classHourData.value?.id,
- chapterId: selectedKeys.value[0],
- courseId: classDetail.value?.courseId,
- fileId: classHourData.value?.courseRelates.find((r) => r.funcType == funcType)?.relateId,
- fileName: classHourData.value?.courseRelates.find((r) => r.funcType == funcType)?.name,
- filePath: classHourData.value?.courseRelates.find((r) => r.funcType == funcType)?.url,
- extendName: classHourData.value?.courseRelates.find((r) => r.funcType == funcType)?.extendName,
- }
- }
- })
- //足迹
- const footprintClassAdd = () => {
- classCentre.footprintClassAdd({
- ...classDetailParams.value(1)
- })
- }
- // 监听消息
- const stopListening = Broadcast.on('getClassDetail', (event) => {
- if (event.type == 1) {
- getClassData()
- }
- })
- const videoStopTime = (callback) => {
- videoRef.value.pause()
- callback && callback(videoRef.value?.currentTime)
- }
- const webCon = ref([
- {
- title: '1.1 集合的概念',
- href: '/webEmpty'
- },
- {
- title: '1.2 集合间的基本关系',
- href: '/webEmpty'
- },
- {
- title: '1.3 集合的基本运算',
- href: '/webEmpty'
- },
- {
- title: '1.4 充分条件与必要条件',
- href: '/webEmpty'
- },
- {
- title: '1.5 全称量词与存在量词',
- href: '/webEmpty'
- }
- ])
- onMounted(() => {
- getClassData()
- })
- onBeforeUnmount(() => {
- stopListening()
- addClassPlan(1)
- })
- </script>
- <style scoped lang="less">
- .classTitle {
- height: 64px;
- width: 200px;
- font-size: 18px;
- color: #ccc;
- display: flex;
- justify-content: center;
- align-items: center;
- }
- .flex-between {
- display: flex;
- justify-content: space-between;
- }
- </style>
|