|
@@ -0,0 +1,862 @@
|
|
|
+<template>
|
|
|
+ <div class="layout-content-wrap record-view">
|
|
|
+ <Loading v-show="isLoad" bgColor="rgba(0, 0, 0, 0.3)"></Loading>
|
|
|
+ <div class="layout-left-wrap list-block-box" v-show="getSidebarisOpen">
|
|
|
+ <template v-if="recordList.length">
|
|
|
+ <Scroll :on-reach-bottom="handleReachTop" class="scroll-list">
|
|
|
+ <div v-for="(item, index) in recordList" :key="index" @click="toClassRecord(item, index)"
|
|
|
+ :class="['list-item', {'list-item-selected': nowIndex === index}]">
|
|
|
+ <ul>
|
|
|
+ <li class="list-item-info">
|
|
|
+ <RcdPoster class="record-poster-wrap" :poster="item.CoverImage"></RcdPoster>
|
|
|
+ <div>
|
|
|
+ <p class="list-item-title">
|
|
|
+ <span>{{ item.name }}</span>
|
|
|
+ <!-- <span style="margin-left: 20px">老师:{{ item.teacher }}</span> -->
|
|
|
+ </p>
|
|
|
+ <p class="list-item-time">
|
|
|
+ <span>{{ item.startTime }}</span>
|
|
|
+ <span>{{ $t('studentWeb.baseInfo.duration') }}:{{ item.time }}</span>
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+ </li>
|
|
|
+ <span class="upload">
|
|
|
+ <span v-show="item.myNote.length" @click.capture.stop="uploadNote(item, true)">
|
|
|
+ <Icon custom="iconfont icon-myNote" size="20" :title="$t('studentWeb.courseContent.mynotes')" />
|
|
|
+ </span>
|
|
|
+ <span style="margin-left: 15px;" @click.capture.stop="uploadNote(item)">
|
|
|
+ <Icon custom="iconfont icon-activityT" size="20" :title="$t('studentWeb.courseContent.notes')" />
|
|
|
+ </span>
|
|
|
+ </span>
|
|
|
+ </ul>
|
|
|
+ </div>
|
|
|
+ </Scroll>
|
|
|
+ </template>
|
|
|
+ <div v-else style="margin-top: 15px; font-size: 15px; text-align: center;">{{ $t('studentWeb.courseContent.noClassRecord') }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="layout-right-wrap record-view-right" :class="{'no-bar-content': getSidebarisOpen}" v-if="recordInfo">
|
|
|
+ <!-- <div style="margin-top: 44px; padding: 10px 15px;"> -->
|
|
|
+ <h2 class="event-title">{{ recordInfo.name }}</h2>
|
|
|
+ <div style="text-align: right; margin-bottom: 5px;">
|
|
|
+ <!-- <Button type="primary" @click="showWorks = true" :disabled="!myWorks.length">
|
|
|
+ <Icon custom="iconfont icon-zuopin" size="16" />
|
|
|
+ {{ $t('studentWeb.courseContent.myWorks') }}
|
|
|
+ </Button> -->
|
|
|
+ <!-- <Button type="primary" @click="showNote = true" v-if="recordInfo.myNote.length">
|
|
|
+ <Icon custom="iconfont icon-myNote" size="17" />
|
|
|
+ {{ $t('studentWeb.courseContent.mynotes') }}
|
|
|
+ </Button> -->
|
|
|
+ <Button type="primary" @click="viewENote">
|
|
|
+ <Icon custom="iconfont icon-activityT" />
|
|
|
+ {{ $t('studentWeb.courseContent.notes') }}
|
|
|
+ </Button>
|
|
|
+ <Button type="primary" @click="isShowVd = !isShowVd" v-show="hasVideo">
|
|
|
+ <Icon :type="isShowVd ? 'md-podium' : 'logo-youtube'" />
|
|
|
+ {{ isShowVd ? $t('cusMgt.rcd.dataCount') : $t('cusMgt.rcd.videoData') }}
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ <div class="record-content">
|
|
|
+ <div class="record-left">
|
|
|
+ <vuescroll>
|
|
|
+ <div style="padding-right: 10px; height: 100%;">
|
|
|
+ <div class="record-head">
|
|
|
+ <div>
|
|
|
+ <p style="margin-left: 20px;">
|
|
|
+ <Icon type="ios-contact-outline" style="font-weight: bold;" class="base-info-icon" />{{ $t('studentWeb.baseInfo.teacher') }}
|
|
|
+ <span class="base-info-text">{{ recordInfo.tmdname }}</span>
|
|
|
+ </p>
|
|
|
+ <template>
|
|
|
+ <p style="margin-left: 20px;">
|
|
|
+ <svg-icon class="base-info-icon" icon-class="course" />{{ $t('studentWeb.baseInfo.subjectName') }}
|
|
|
+ <span class="base-info-text">{{ courseNow.name }}</span>
|
|
|
+ </p>
|
|
|
+ <p style="margin-left: 20px;">
|
|
|
+ <Icon custom="iconfont icon-mingdan" class="base-info-icon" />{{ $t('studentWeb.baseInfo.stuList') }}
|
|
|
+ <span class="base-info-text">{{ courseNow.listName }}</span>
|
|
|
+ </p>
|
|
|
+ </template>
|
|
|
+ <p style="margin-left: 20px;">
|
|
|
+ <Icon type="md-timer" class="base-info-icon" />{{ $t('studentWeb.baseInfo.duration') }}:
|
|
|
+ <span class="base-info-text">{{ recordInfo.time }}</span>
|
|
|
+ </p>
|
|
|
+ <p style="margin-left: 20px;">
|
|
|
+ <svg-icon icon-class="time" class="base-info-icon" />{{$t('studentWeb.baseInfo.classTime')}}:
|
|
|
+ <span class="base-info-text">{{ recordInfo.startTime }}</span>
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <i-circle :percent="100" :size="100" :stroke-width="10" :stroke-color="attentColor[attendType]">
|
|
|
+ <p class="attend-type">{{ $t(`studentWeb.hiteachNote.dataCount.attendTypeList[${attendType}]`) }}</p>
|
|
|
+ </i-circle>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div v-if="hasVideo" v-show="isShowVd" class="video-player-box">
|
|
|
+ <video style="width: 100%;" :id="'recordVideo' + recordInfo.id" class="video-js vjs-default-skin" type="video/mp4"></video>
|
|
|
+ </div>
|
|
|
+ <div v-show="!isShowVd" class="video-player-box" style="padding:25px 0px">
|
|
|
+ <Alert v-show="!hasVideo" class="no-video-tips" type="warning" show-icon>
|
|
|
+ {{$t('cusMgt.rcd.noVideo')}}
|
|
|
+ </Alert>
|
|
|
+ <DataCount :nowStuInfo="nowStuInfo" :rcdInfo="baseData" v-if="baseData"></DataCount>
|
|
|
+ </div>
|
|
|
+ <div class="courseware-wrap">
|
|
|
+ <!-- <DrawHTEX :mapJson="mapJson"></DrawHTEX> -->
|
|
|
+ <img :src="curImg" alt="" class="course-cur-img">
|
|
|
+ <div class="page-wrap">
|
|
|
+ <Page :total="pageList.length" :current="curPage" :page-size="1" size="small" @on-change="getCurHTEX" />
|
|
|
+ <!-- <Icon v-if="pageList.length" type="md-qr-scanner" class="full-screen-icon" @click="viewHtex" /> -->
|
|
|
+ </div>
|
|
|
+ <!-- <span class="cur-page-tag">{{ curPage }}</span> -->
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </vuescroll>
|
|
|
+ </div>
|
|
|
+ <div class="record-right">
|
|
|
+ <div>
|
|
|
+ <h2 class="title-rect-name">
|
|
|
+ {{ $t("studentWeb.hiteachNote.classInteractionRecord") }}
|
|
|
+ </h2>
|
|
|
+ <div style="width: 100%; display: flex; justify-content: space-between;">
|
|
|
+ <div>
|
|
|
+ <!-- <Button type="primary">时间</Button>
|
|
|
+ <Button type="primary">页次</Button> -->
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <Button type="warning" @click="filterFn('all')">{{ $t("cusMgt.rcd.filter1") }}</Button>
|
|
|
+ <Button type="warning" :disabled="!filtertype.push" @click="filterFn('push')">{{ $t("cusMgt.rcd.filter2") }}({{ filtertype.push }})</Button>
|
|
|
+ <Button type="warning" :disabled="!filtertype.task" @click="filterFn('task')">{{ $t("cusMgt.rcd.filter3") }}({{ filtertype.task }})</Button>
|
|
|
+ <Button type="warning" :disabled="!filtertype.irs" @click="filterFn('irs')">{{ $t("cusMgt.rcd.filter4") }}({{ filtertype.irs }})</Button>
|
|
|
+ <Button type="warning" :disabled="!filtertype.exam" @click="filterFn('exam')">{{ $t("cusMgt.rcd.filter5") }}({{ filtertype.exam }})</Button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="dec">
|
|
|
+ <div class="message-area" v-if="haveInteraction">
|
|
|
+ <vuescroll ref="datawrap">
|
|
|
+ <div style="margin-bottom: 50px;">
|
|
|
+ <div v-for="(items, index) in showPageList" :key="index" :id="'page' + (items.page)">
|
|
|
+ <div v-if="items.pageData.length" class="message-box">
|
|
|
+ <div class="message-page">
|
|
|
+ <div @click="toVideo(index + 1, $event)">
|
|
|
+ <img :src="items.img" @click="openViewer(items.img)">
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="message-record">
|
|
|
+ <template v-if="items.pageData.length">
|
|
|
+ <div v-for="event in items.pageData" :key="event.Time">
|
|
|
+ <!-- 即问即答 -->
|
|
|
+ <div v-if="currentfilterType === '' || currentfilterType === 'ShowAnsLoad'">
|
|
|
+ <ShowQues class="event-item student-event" v-if="event.Event === 'PopQuesLoad' || event.Event === 'ReAtmpAnsStrt'" :nowStuInfo="nowStuInfo" :evtType="event.Event" :irsData="event.data"></ShowQues>
|
|
|
+ <PopQues class="event-item" v-if="event.Event === 'PopQuesLoad' || event.Event === 'ReAtmpAnsStrt'" :evtType="event.Event" :irsData="event.data"></PopQues>
|
|
|
+ </div>
|
|
|
+ <!-- 抢权 -->
|
|
|
+ <Buzr class="event-item student-event" v-if="event.Event === 'BuzrAns' && baseData" :buzrData="event.data" :students="baseData.student"></Buzr>
|
|
|
+ <!-- 推送 -->
|
|
|
+ <Push class="event-item" v-if="event.Event === 'FastPgPush' && (currentfilterType === '' || currentfilterType === 'doc')" :pushData="event.data"></Push>
|
|
|
+ <!-- 作品收集 -->
|
|
|
+ <StuReceive class="student-event event-item" v-if="event.Event === 'WrkSpaceLoad' && baseData" :nowStuInfo="nowStuInfo" :rcvData="event.data" :recordInfo="recordInfo" :students="baseData.student"></StuReceive>
|
|
|
+ <!-- 老师收集的所有作品目前不展示,只展示老师回贴的学生作品 -->
|
|
|
+ <!-- <Receive :recordInfo="recordInfo" class="student-event event-item" v-if="event.Event === 'WrkCmp' && baseData" :rcvData="event.data" :students="baseData.student"></Receive> -->
|
|
|
+ <ReceiveBack class="event-item" v-if="event.Event === 'WrkCmp' && event.data && baseData" :recordInfo="recordInfo" :rcvData="event.data" :students="baseData.student"></ReceiveBack>
|
|
|
+ <!-- 随机挑人 -->
|
|
|
+ <Pick class="event-item student-event" v-if="event.Event === 'PickupResult' && baseData" :pickData="event.data" :students="baseData.student"></Pick>
|
|
|
+ <!-- 课中评测 -->
|
|
|
+ <Exam class="student-event event-item" :examInfo="event.data" :recordInfo="recordInfo" v-if="event.Event === 'SPQStrt'"></Exam>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </vuescroll>
|
|
|
+ </div>
|
|
|
+ <div v-else class="no-interaction">
|
|
|
+ {{ $t("studentWeb.hiteachNote.noContent") }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!-- </div> -->
|
|
|
+ </div>
|
|
|
+ <Modal v-model="showNote" :title="$t('studentWeb.courseContent.mynotes')" width="800">
|
|
|
+ <div v-if="recordInfo">
|
|
|
+ <img :src="item" alt="" v-for="(item, index) in recordInfo.myNote" :key="index" style="width: 100%">
|
|
|
+ </div>
|
|
|
+ </Modal>
|
|
|
+ <Modal v-model="showWorks" :title="$t('studentWeb.courseContent.mynotes')" width="800" v-if="recordInfo">
|
|
|
+ <myWorks :recordInfo="recordInfo" :rcvData="myWorks" />
|
|
|
+ </Modal>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import { mapGetters, mapState } from 'vuex'
|
|
|
+import BlobTool from "@/utils/blobTool.js"
|
|
|
+import videojs from "video.js";
|
|
|
+import "videojs-markers";
|
|
|
+
|
|
|
+import RcdPoster from '../../../view/homepage/RcdPoster.vue';
|
|
|
+import Loading from '@/common/Loading.vue';
|
|
|
+import DataCount from './newDataCount.vue';
|
|
|
+import ShowQues from './ShowQues.vue';
|
|
|
+import PopQues from '@/view/classrecord/eventchart/PopQues.vue';
|
|
|
+import Buzr from './Buzr.vue';
|
|
|
+import Push from '@/view/classrecord/eventchart/Push.vue';
|
|
|
+import StuReceive from './StuReceive.vue';
|
|
|
+import ReceiveBack from './ReceiveBack.vue';
|
|
|
+import Pick from './Pick.vue';
|
|
|
+import Exam from './Exam.vue';
|
|
|
+import myWorks from './myWorks.vue';
|
|
|
+
|
|
|
+export default {
|
|
|
+ components: {
|
|
|
+ RcdPoster,
|
|
|
+ Loading,
|
|
|
+ DataCount, ShowQues, PopQues, Buzr, Push,
|
|
|
+ StuReceive, ReceiveBack, Pick, Exam, myWorks,
|
|
|
+ },
|
|
|
+ data () {
|
|
|
+ return {
|
|
|
+ MyNo: "10",
|
|
|
+ MyName: this.$t('studentWeb.courseContent.classRecord'),
|
|
|
+ isLoad: false,
|
|
|
+ recordList: [],
|
|
|
+ continuationToken: null,
|
|
|
+ sasInfo: undefined,
|
|
|
+ nowIndex: -1,
|
|
|
+
|
|
|
+ pageList: [], //课件
|
|
|
+ showPageList: [],
|
|
|
+ markers: [], //打点
|
|
|
+ playerOptions: {
|
|
|
+ height: "450px",
|
|
|
+ controlBar: {
|
|
|
+ children: [// 写在这里,会在播放条上显示出来,并且是按照写的顺序显示位置。
|
|
|
+ { name: "playToggle" }, //播放暂停按钮
|
|
|
+ { name: "currentTimeDisplay" }, //当前播放时间
|
|
|
+ { name: "progressControl" }, //播放进度条
|
|
|
+ { name: "durationDisplay" }, //总时间
|
|
|
+ {
|
|
|
+ name: "playbackRateMenuButton",
|
|
|
+ playbackRates: [0.5, 1, 1.5, 2, 2.5]
|
|
|
+ }, //播放速率
|
|
|
+ {
|
|
|
+ name: "volumePanel", //音量控制
|
|
|
+ inline: false, //不使用水平方式
|
|
|
+ },
|
|
|
+ { name: "FullscreenToggle" }, //全屏
|
|
|
+ { name: "DashBoardEchart" }
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ /* html5: {
|
|
|
+ nativeControlsForTouch: true,
|
|
|
+ }, */
|
|
|
+ // inactivityTimeout: 1,
|
|
|
+ // nativeVideoTracks: false,
|
|
|
+ // playbackRates: [0.5, 1.0, 1.25, 2.0], //播放速度
|
|
|
+ // autoplay: false, //如果true,浏览器准备好时开始回放。
|
|
|
+ controls: true, //控制条
|
|
|
+ preload: 'auto', //视频预加载
|
|
|
+ muted: false, //默认情况下将会消除任何音频。
|
|
|
+ // loop: false, //导致视频一结束就重新开始。
|
|
|
+ // language: 'zh-CN',
|
|
|
+ // aspectRatio: '16:9', // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")
|
|
|
+ //fluid: true, // 当true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。
|
|
|
+ sources: [{
|
|
|
+ src: ""
|
|
|
+ }],
|
|
|
+ // notSupportedMessage: '此视频暂无法播放,请稍后再试' //允许覆盖Video.js无法播放媒体源时显示的默认信息。
|
|
|
+ },
|
|
|
+ curPage: 1, //当前课件页码
|
|
|
+ sokratesRecords: {}, //原始数据
|
|
|
+ recordInfo: undefined,
|
|
|
+ baseData: undefined, //base.json
|
|
|
+ pushData: [], //push.json
|
|
|
+ irsData: [], //irs.json
|
|
|
+ taskData: [], //task.json
|
|
|
+ fnEvents: [], //功能事件
|
|
|
+ events: [], //事件ID
|
|
|
+ hiTeachEvent: [], //需要解析的事件信息
|
|
|
+ isShowVd: true,
|
|
|
+ hasVideo: true,
|
|
|
+ showNote: false,
|
|
|
+ showWorks: false,
|
|
|
+ attendType: 2,
|
|
|
+ attentColor: ['', '#19be6b', '#EB3941', '#EB3941', '#EB3941', '#EB3941'],
|
|
|
+ nowStuInfo: undefined, //当前学生的信息
|
|
|
+ filtertype: {
|
|
|
+ push: 0, //推送
|
|
|
+ task: 0, //任务
|
|
|
+ irs: 0, //互动
|
|
|
+ exam: 0, //测验
|
|
|
+ },
|
|
|
+ myWorks: [],
|
|
|
+ haveInteraction: true,
|
|
|
+ player: undefined,
|
|
|
+ currentfilterType: "",
|
|
|
+ }
|
|
|
+ },
|
|
|
+ created () {
|
|
|
+ this.$emit("onNavNo", this.MyNo);
|
|
|
+ this.$emit("onNavName", this.MyName);
|
|
|
+ this.$store.commit("ToggleSidebar", true);
|
|
|
+ this.hiTeachEvent = this.$GLOBAL.HI_TEACH_EVENT()
|
|
|
+ this.events = Object.keys(this.hiTeachEvent)
|
|
|
+ this.fnEvents = this.events.filter(key => this.hiTeachEvent[key].type === 'fn')
|
|
|
+ },
|
|
|
+ mounted () {
|
|
|
+ // if(this.getNowCourse) {
|
|
|
+ this.getSas()
|
|
|
+ this.getRecordList()
|
|
|
+ // }
|
|
|
+ // this.getRecordList()
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ async getSas() {
|
|
|
+ let code = this.courseNow.scope === "school" ? this.courseNow.school : this.courseNow.roster.teacherId
|
|
|
+ this.sasInfo = await this.$tools.getBlobSas(code)
|
|
|
+ },
|
|
|
+ getRecordList() {
|
|
|
+ let param = {
|
|
|
+ tmdid: this.courseNow.roster.teacherId,
|
|
|
+ scope: this.courseNow.scope, //school:传school,private:传tmdid
|
|
|
+ school: "",
|
|
|
+ category: [],
|
|
|
+ subjectId: [],
|
|
|
+ courseId: this.courseNow.id,
|
|
|
+ grade: [],
|
|
|
+ doubleGreen: false,
|
|
|
+ quality: false,
|
|
|
+ DESC: "startTime",
|
|
|
+ pageCount: 10, //返回六条数据(分页)
|
|
|
+ today: false,
|
|
|
+ continuationToken: this.continuationToken, //返回的有值的话,下次查询就要用这个值
|
|
|
+ groupIds: [this.courseNow.list],
|
|
|
+ }
|
|
|
+ param.scope === "school" ? param.school = this.courseNow.school : param.tmdid = this.courseNow.roster.teacherId
|
|
|
+ this.$api.studentWeb.getClassRecord(param).then(res => {
|
|
|
+ if(res.lessonRecords && res.lessonRecords.length) {
|
|
|
+ let newList = res.lessonRecords.map(item => {
|
|
|
+ item.startTime = this.dateFormat(item.startTime)
|
|
|
+ let sec = item.duration % 60
|
|
|
+ let min = parseInt(item.duration / 60)
|
|
|
+ let mins = min >= 60 ? min % 60 : min
|
|
|
+ let hour = parseInt(min / 60)
|
|
|
+ item.time = `${hour < 10 ? ('0' + hour) : hour}:${mins < 10 ? ('0' + mins) : mins}:${sec < 10 ? ('0' + sec) : sec}`
|
|
|
+ item.eNote = `${this.sasInfo.url}/${this.sasInfo.name}/records/${item.id}/Note.pdf?${this.sasInfo.sas}`
|
|
|
+ item.CoverImage = `${this.sasInfo.url}/${this.sasInfo.name}/records/${item.id}/Record/CoverImage.jpg?${this.sasInfo.sas}`
|
|
|
+ item.myNote = []
|
|
|
+ return item
|
|
|
+ })
|
|
|
+ this.recordList.push.apply(this.recordList, newList)
|
|
|
+ this.continuationToken = res.continuationToken
|
|
|
+ this.getTrainFiles()
|
|
|
+ }
|
|
|
+ }).finally(() => {
|
|
|
+ })
|
|
|
+ },
|
|
|
+ // 查找活动的视频和文件
|
|
|
+ getTrainFiles() {
|
|
|
+ let blobTool = new BlobTool(this.sasInfo.url, this.sasInfo.name, "?" + this.sasInfo.sas, this.courseNow.scope)
|
|
|
+ let promiseArr = []
|
|
|
+ let recList = [...this.recordList]
|
|
|
+ recList.forEach((item, index) => {
|
|
|
+ promiseArr.push(new Promise((r, j) => {
|
|
|
+ blobTool.listBlob({
|
|
|
+ prefix: `records/${item.id}/Clients/${this.userInfo.sub}/Task/NoteSelfNote`
|
|
|
+ }).then(
|
|
|
+ res => {
|
|
|
+ let imgList = []
|
|
|
+ res.blobList.forEach(list => {
|
|
|
+ imgList.push(`${list.url}?${this.sasInfo.sas}`)
|
|
|
+ })
|
|
|
+ this.recordList[index].myNote = imgList
|
|
|
+ r(res)
|
|
|
+ },
|
|
|
+ err => {
|
|
|
+
|
|
|
+ }
|
|
|
+ )
|
|
|
+ }))
|
|
|
+ })
|
|
|
+ Promise.all(promiseArr).then(res => {
|
|
|
+ if(this.$route.query.aId) {
|
|
|
+ let info = this.recordList.findIndex(item => {
|
|
|
+ return item.id === this.$route.query.aId
|
|
|
+ })
|
|
|
+ if(info != -1) {
|
|
|
+ this.toClassRecord(this.recordList[info], info)
|
|
|
+ } else {
|
|
|
+ this.$Message.warning("未找到课堂记录")
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ this.toClassRecord(this.recordList[0], 0)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ handleReachTop () {
|
|
|
+ return new Promise(resolve => {
|
|
|
+ setTimeout(() => {
|
|
|
+ if(this.continuationToken) {
|
|
|
+ this.getRecordList()
|
|
|
+ } else {
|
|
|
+ this.$Message.warning("没有更多数据")
|
|
|
+ }
|
|
|
+ resolve()
|
|
|
+ }, 2000);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ toClassRecord(item, index) {
|
|
|
+ this.recordInfo = []
|
|
|
+ this.nowIndex = index
|
|
|
+ this.recordInfo = {...item}
|
|
|
+ this.getPageList()
|
|
|
+
|
|
|
+ ////螢幕寬度<767px時,直接關掉sidebar
|
|
|
+ if (window.innerWidth <= 1024) {
|
|
|
+ this.$store.commit("ToggleSidebar", false);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ uploadNote(info, mine) {
|
|
|
+ // mine:我的笔记
|
|
|
+ if(mine) {
|
|
|
+ this.$tools.generatePdfByImgs(info.myNote, `${info.name}-${this.$t('studentWeb.courseContent.mynotes')}`)
|
|
|
+ } else {
|
|
|
+ this.$tools.doDownloadByUrl(info.eNote, info.name)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ //查看电子笔记
|
|
|
+ async viewENote() {
|
|
|
+ let eNote
|
|
|
+ if (this.recordInfo.eNote) {
|
|
|
+ eNote = this.recordInfo.eNote
|
|
|
+ } else {
|
|
|
+ // let sasInfo = {}
|
|
|
+ // let blobInfo = this.recordInfo.scope === 'school' ? this.$store.state.user.schoolProfile : this.$store.state.user.userProfile
|
|
|
+ // sasInfo.sas = '?' + blobInfo.blob_sas
|
|
|
+ // sasInfo.name = this.recordInfo.scope ? this.$store.state.userInfo.schoolCode : this.$store.state.userInfo.TEAMModelId
|
|
|
+ // sasInfo.url = blobInfo.blob_uri.slice(0, blobInfo.blob_uri.lastIndexOf(sasInfo.name) - 1)
|
|
|
+ let sasInfo = await this.$tools.getBlobSas(this.recordInfo.scope === 'school' ? this.recordInfo.school : this.recordInfo.tmdid)
|
|
|
+ eNote = `${sasInfo.url}/${sasInfo.name}/records/${this.recordInfo.id}/Note.pdf?${sasInfo.sas}`
|
|
|
+ }
|
|
|
+ window.open('/web/viewer.html?file=' + encodeURIComponent(eNote))
|
|
|
+ // if (this.recordInfo.eNote) {
|
|
|
+ // window.open('/web/viewer.html?file=' + encodeURIComponent(this.recordInfo.eNote))
|
|
|
+ // } else {
|
|
|
+ // this.$Message.warning(this.$t('cusMgt.rcd.noNote'))
|
|
|
+ // }
|
|
|
+ },
|
|
|
+ // 根据SokratesRecords.json处理page数据
|
|
|
+ async getPageList() {
|
|
|
+ this.isLoad = true
|
|
|
+ this.pageList = []
|
|
|
+ this.showPageList = []
|
|
|
+ this.markers = []
|
|
|
+ let sas = await this.$tools.getBlobSas(this.recordInfo.scope === 'school' ? this.recordInfo.school : this.recordInfo.tmdid)
|
|
|
+ this.recordInfo.eNote = `${sas.url}/${sas.name}/records/${this.recordInfo.id}/Note.pdf?${sas.sas}`
|
|
|
+ // 如果只会存在一个视频,文件名是否可以固定?
|
|
|
+ this.playerOptions.sources[0].src = `${sas.url}/${sas.name}/records/${this.recordInfo.id}/Record/CourseRecord.mp4?${sas.sas}`
|
|
|
+ // let url = `${sas.url}/${sas.name}/records/${this.recordInfo.id}/Sokrates/SokratesRecords.json?${sas.sas}` //后面会根据TimeLine.json处理
|
|
|
+ // 这里需要兼容原来没有TimeLine.json的课例(优先度读timeLine.json,没有则读SokratesRecords.json)
|
|
|
+ let url = `${sas.url}/${sas.name}/records/${this.recordInfo.id}/IES/TimeLine.json?${sas.sas}`
|
|
|
+ let hasTimeLine = true
|
|
|
+ let dataErr = false
|
|
|
+ let pgids = []
|
|
|
+ let pageEvents = []
|
|
|
+ try {
|
|
|
+ let res = await this.$tools.getFile(url)
|
|
|
+ this.sokratesRecords = JSON.parse(res)
|
|
|
+ pgids = this.sokratesRecords.PgIdList || []
|
|
|
+ pageEvents = this.sokratesRecords.events || []
|
|
|
+ } catch (e) {
|
|
|
+ hasTimeLine = false
|
|
|
+ }
|
|
|
+ //读取 timeLine.json 失败,则读取 SokratesRecords.json
|
|
|
+ if (!hasTimeLine) {
|
|
|
+ url = `${sas.url}/${sas.name}/records/${this.recordInfo.id}/Sokrates/SokratesRecords.json?${sas.sas}`
|
|
|
+ try {
|
|
|
+ let res = await this.$tools.getFile(url)
|
|
|
+ let resJson = JSON.parse(res)
|
|
|
+ // 处理成timeLine数据格式
|
|
|
+ let pageidEvent = resJson.find(item => item.Event == 'PgidList')
|
|
|
+ pgids = pageidEvent && pageidEvent.PgIdList ? pageidEvent.PgIdList : []
|
|
|
+ pageEvents = resJson.filter(item => this.events.includes(item.Event))
|
|
|
+ this.sokratesRecords = {
|
|
|
+ events: pageEvents,
|
|
|
+ PgIdList: pgids
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ //timeLine 和 SokratesRecords都没有找到
|
|
|
+ dataErr = true
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 数据异常
|
|
|
+ if (dataErr) {
|
|
|
+ this.$Message.error(this.$t('cusMgt.rcd.dataErr'))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ //获取Push.json、IRS.json、Task.json、Base.json数据
|
|
|
+ try {
|
|
|
+ let pushUrl = `${sas.url}/${sas.name}/records/${this.recordInfo.id}/IES/Push.json?${sas.sas}`
|
|
|
+ this.pushData = JSON.parse(await this.$tools.getFile(pushUrl) || '[]')
|
|
|
+ this.pushData.forEach(item => {
|
|
|
+ item.pageUrl = `${sas.url}/${sas.name}/records/${this.recordInfo.id}${item.pageMeta}?${sas.sas}`
|
|
|
+ })
|
|
|
+ } catch (e) {
|
|
|
+ this.pushData = []
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ let irsUrl = `${sas.url}/${sas.name}/records/${this.recordInfo.id}/IES/IRS.json?${sas.sas}`
|
|
|
+ this.irsData = JSON.parse(await this.$tools.getFile(irsUrl) || '[]')
|
|
|
+ } catch (e) {
|
|
|
+ this.irsData = []
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ let taskUrl = `${sas.url}/${sas.name}/records/${this.recordInfo.id}/IES/Task.json?${sas.sas}`
|
|
|
+ this.taskData = JSON.parse(await this.$tools.getFile(taskUrl) || '[]')
|
|
|
+ } catch (e) {
|
|
|
+ this.taskData = []
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ let baseUrl = `${sas.url}/${sas.name}/records/${this.recordInfo.id}/IES/base.json?${sas.sas}`
|
|
|
+ this.baseData = JSON.parse(await this.$tools.getFile(baseUrl) || '{}')
|
|
|
+ this.baseData.student.forEach((item, index) => {
|
|
|
+ if (item.id === this.userInfo.sub) {
|
|
|
+ this.nowStuInfo = item
|
|
|
+ this.nowStuInfo.index = index
|
|
|
+ }
|
|
|
+ })
|
|
|
+ if(this.nowStuInfo) {
|
|
|
+ let nowClient = this.baseData.report.clientSummaryList.find(item => {
|
|
|
+ return this.nowStuInfo.seatID === item.seatID
|
|
|
+ })
|
|
|
+ this.attendType = nowClient ? nowClient.attendState : 2
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ this.baseData = undefined
|
|
|
+ }
|
|
|
+
|
|
|
+ //这里需要判断录制开始的pageid
|
|
|
+ let startInfo = pageEvents.find(item => item.Event === 'EzsStartRecord')
|
|
|
+ let startId = startInfo ? startInfo.Pgid : ''
|
|
|
+ let startIndex = 0
|
|
|
+ if (startId) {
|
|
|
+ startIndex = pgids.findIndex(item => item === startId)
|
|
|
+ }
|
|
|
+ pgids = pgids.slice(startIndex)
|
|
|
+
|
|
|
+ let havePage = 0
|
|
|
+ let myTask = []
|
|
|
+ pgids.forEach((item, index) => {
|
|
|
+ let page = {}
|
|
|
+ page.id = item
|
|
|
+ page.img = `${sas.url}/${sas.name}/records/${this.recordInfo.id}/Memo/${item}.jpg?${sas.sas}`
|
|
|
+ page.page = index + 1
|
|
|
+ //当前页面对应的sokrates
|
|
|
+ page.pageData = pageEvents.filter(record => record.Pgid === item && this.fnEvents.includes(record.Event))
|
|
|
+ havePage += (page.pageData.length ? 1 : 0)
|
|
|
+ page.pageData.forEach(e => {
|
|
|
+ e.pageIndex = index
|
|
|
+ e.eventName = this.hiTeachEvent[e.Event]?.text
|
|
|
+ let rlt = this.hiTeachEvent[e.Event]?.relation
|
|
|
+ e.relation = rlt
|
|
|
+ switch (rlt) {
|
|
|
+ case 'irs':
|
|
|
+ this.filtertype.irs += 1
|
|
|
+ e.data = this.irsData.find(i => i.pageID == e.Pgid)
|
|
|
+ break
|
|
|
+ case 'push':
|
|
|
+ this.filtertype.push += 1
|
|
|
+ e.data = this.pushData.find(p => p.pageId == e.Pgid || p.pageID == e.Pgid)
|
|
|
+ break
|
|
|
+ case 'task':
|
|
|
+ this.filtertype.task += 1
|
|
|
+ e.data = this.taskData.find(t => t.pageID == e.Pgid)
|
|
|
+ myTask.push(e.data)
|
|
|
+ break
|
|
|
+ case 'timeline':
|
|
|
+ e.data = this._.cloneDeep(e)
|
|
|
+ break
|
|
|
+ case 'exam':
|
|
|
+ this.filtertype.exam += 1
|
|
|
+ e.data = this._.cloneDeep(e)
|
|
|
+ break
|
|
|
+ default:
|
|
|
+ break
|
|
|
+ }
|
|
|
+ })
|
|
|
+ this.pageList.push(page)
|
|
|
+ })
|
|
|
+ if(this.baseData) {
|
|
|
+ myTask.forEach(item => {
|
|
|
+ if(item && item.clientWorks.length) {
|
|
|
+ item.clientWorks.forEach(works => {
|
|
|
+ let owner = this.baseData.student.find(stu => stu.seatID == works.seatID && stu.id === this.$store.state.userInfo.sub)
|
|
|
+ if(owner) {
|
|
|
+ this.myWorks.push({
|
|
|
+ jobName: item.jobName,
|
|
|
+ collateType: item.collateType,
|
|
|
+ blobFiles: works.blobFiles
|
|
|
+ })
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ this.showPageList = [...this.pageList]
|
|
|
+ this.haveInteraction = havePage != 0
|
|
|
+ let pageEvent = pageEvents.filter(item => item.Event === 'PopQuesLoad' || item.Event === 'ReAtmpAnsStrt' || item.Event === 'FastPgPush' || item.Event === 'WrkSpaceLoad' || item.Event === 'SPQStrt')
|
|
|
+ this.markers = pageEvent.map((item, index) => {
|
|
|
+ return {
|
|
|
+ time: item.Time,
|
|
|
+ text: `${this.$t('cusMgt.rcd.di')}${index + 1}${this.$t('cusMgt.rcd.page')}`,
|
|
|
+ page: index + 1
|
|
|
+ }
|
|
|
+ })
|
|
|
+ if(this.hasVideo) {
|
|
|
+ this.getVideo()
|
|
|
+ } else {
|
|
|
+ this.isLoad = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ getVideo() {
|
|
|
+ var that = this
|
|
|
+ this.player = videojs(document.getElementById('recordVideo' + this.recordInfo.id), this.playerOptions, function () {
|
|
|
+ this.on('error', (e) => {
|
|
|
+ that.hasVideo = false
|
|
|
+ that.isShowVd = false
|
|
|
+ })
|
|
|
+ })
|
|
|
+ //时间切片
|
|
|
+ /* this.player.markers({
|
|
|
+ markerStyle: {
|
|
|
+ width: "16px",
|
|
|
+ height: "16px",
|
|
|
+ top: "-22px",
|
|
|
+ "display": "inline-block",
|
|
|
+ "border-radius": "50%",
|
|
|
+ "font-size": "12px",
|
|
|
+ "line-height": "16px",
|
|
|
+ "background-color": "orange"
|
|
|
+ },
|
|
|
+ breakOverlay: {
|
|
|
+ display: false,
|
|
|
+ displayTime: 4,
|
|
|
+ style: {
|
|
|
+ "z-index": "6",
|
|
|
+ width: "100%",
|
|
|
+ height: "10%",
|
|
|
+ "background-color": "rgba(200,250,10,0.6)",
|
|
|
+ color: "white",
|
|
|
+ "font-size": "16px",
|
|
|
+ },
|
|
|
+ text: function (marker) {
|
|
|
+ return that.$t('system.compt.cusWare') + marker.text;
|
|
|
+ },
|
|
|
+ },
|
|
|
+ markerTip: {
|
|
|
+ display: false,
|
|
|
+ text: function (marker) {
|
|
|
+ return marker.text;
|
|
|
+ },
|
|
|
+ },
|
|
|
+ markers: that.markers,
|
|
|
+
|
|
|
+ //标记点击事件
|
|
|
+ onMarkerClick: function (marker) {
|
|
|
+
|
|
|
+ },
|
|
|
+ //视频播放到标记点触发的时间
|
|
|
+ onMarkerReached: function (marker) {
|
|
|
+ let mkDoms = document.getElementsByClassName('vjs-marker ')
|
|
|
+ for (let index in mkDoms) {
|
|
|
+ if (mkDoms[index].dataset) {
|
|
|
+ if (parseInt(mkDoms[index].dataset.markerTime) <= marker.time) {
|
|
|
+ mkDoms[index].style.backgroundColor = '#1CC0F3'
|
|
|
+ mkDoms[index].classList.add('vjs-marker-active')
|
|
|
+ } else {
|
|
|
+ mkDoms[index].style.backgroundColor = 'orange'
|
|
|
+ mkDoms[index].classList.remove('vjs-marker-active')
|
|
|
+ }
|
|
|
+ // mkDoms[index].innerHTML = this.markers[index].page
|
|
|
+ }
|
|
|
+ }
|
|
|
+ that.getCurPage(marker.page)
|
|
|
+ },
|
|
|
+ }); */
|
|
|
+
|
|
|
+ this.isLoad = false
|
|
|
+ },
|
|
|
+ // 点击视频切片
|
|
|
+ getCurPage(page) {
|
|
|
+ this.curPage = page
|
|
|
+ // this.mapJson = require('./data/' + page + '.json')
|
|
|
+ // this.$refs["datawrap"].scrollIntoView('#page' + page, 500)
|
|
|
+ this.player.play()
|
|
|
+ },
|
|
|
+ // 点击课件page
|
|
|
+ getCurHTEX(page) {
|
|
|
+ this.curPage = page
|
|
|
+ // this.mapJson = require('./data/' + page + '.json')
|
|
|
+ //视频时间定位
|
|
|
+ let pageInfo = this.markers.find(item => {
|
|
|
+ return item.page === page
|
|
|
+ })
|
|
|
+ if (pageInfo) {
|
|
|
+ this.player.currentTime(pageInfo.time)
|
|
|
+ if (!this.openHtexViewer) {
|
|
|
+ this.player.play()
|
|
|
+ } else {
|
|
|
+ this.player.pause()
|
|
|
+ }
|
|
|
+ //互动记录滚动
|
|
|
+ // this.$refs["datawrap"].scrollIntoView('#page' + page, 500)
|
|
|
+ } else {
|
|
|
+ }
|
|
|
+ },
|
|
|
+ filterFn(type) {
|
|
|
+ this.showPageList = []
|
|
|
+ if(type === 'all') {
|
|
|
+ this.showPageList = [...this.pageList]
|
|
|
+ } else {
|
|
|
+ this.isLoad = true
|
|
|
+ this.pageList.forEach(item => {
|
|
|
+ if(item.pageData.length) {
|
|
|
+ let filterArr = item.pageData.filter(data => {
|
|
|
+ return data.relation === type
|
|
|
+ })
|
|
|
+ if(filterArr.length) {
|
|
|
+ this.showPageList.push({
|
|
|
+ id: item.id,
|
|
|
+ img: item.img,
|
|
|
+ page: item.page,
|
|
|
+ pageData: filterArr
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ this.isLoad = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ dateFormat(timestamp) {
|
|
|
+ var date = new Date(timestamp)
|
|
|
+ var Y = date.getFullYear() + '-'
|
|
|
+ var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
|
|
|
+ var D = (date.getDate() < 10 ? '0' + (date.getDate()) : date.getDate()) + ' '
|
|
|
+ var H = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ":"
|
|
|
+ var Min = (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes())
|
|
|
+ var S = (date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()) + " "
|
|
|
+ return Y + M + D + H + Min;
|
|
|
+ },
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ ...mapGetters(["getNowCourse", "getSidebarisOpen",]),
|
|
|
+ ...mapState({
|
|
|
+ userInfo: state => state.userInfo,
|
|
|
+ }),
|
|
|
+ curImg() {
|
|
|
+ if (this.pageList[this.curPage - 1]) {
|
|
|
+ return this.pageList[this.curPage - 1].img
|
|
|
+ } else {
|
|
|
+ return ""
|
|
|
+ }
|
|
|
+ },
|
|
|
+ courseNow() {
|
|
|
+ return this.getNowCourse || JSON.parse(decodeURIComponent(localStorage.course, "utf-8"))
|
|
|
+ },
|
|
|
+ },
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="less" scoped>
|
|
|
+@import "./RecordView.less";
|
|
|
+</style>
|
|
|
+
|
|
|
+<style lang="less">
|
|
|
+.record-view-right {
|
|
|
+ .video-js .vjs-big-play-button {
|
|
|
+ top: 50%;
|
|
|
+ left: 50%;
|
|
|
+ margin-left: -20px;
|
|
|
+ margin-top: -20px;
|
|
|
+ display: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ .video-js .vjs-control-bar {
|
|
|
+ display: flex;
|
|
|
+ }
|
|
|
+
|
|
|
+ .vjs-marker::after {
|
|
|
+ content: "";
|
|
|
+ height: 0px;
|
|
|
+ width: 0px;
|
|
|
+ border: 3px transparent solid;
|
|
|
+ display: block;
|
|
|
+ position: absolute;
|
|
|
+ bottom: -10px;
|
|
|
+ z-index: -1;
|
|
|
+ border-right: 8px solid transparent;
|
|
|
+ border-top: 15px solid orange;
|
|
|
+ border-left: 8px solid transparent;
|
|
|
+ }
|
|
|
+ .vjs-marker-active::after {
|
|
|
+ border-right: 8px solid transparent !important;
|
|
|
+ border-top: 15px solid #1cc0f3 !important;
|
|
|
+ border-left: 8px solid transparent !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ .vjs-marker:hover {
|
|
|
+ z-index: 101;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ .ivu-btn-primary {
|
|
|
+ background-color: #24b880;
|
|
|
+ border-color: #24b880;
|
|
|
+
|
|
|
+ &:not(:last-child) {
|
|
|
+ margin-right: 15px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .ivu-btn-warning {
|
|
|
+ color: #515a6e;
|
|
|
+ background-color: #FEE49E;
|
|
|
+ border-color: #FEE49E;
|
|
|
+
|
|
|
+ &:not(:last-child) {
|
|
|
+ margin-right: 15px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .ivu-btn-primary[disabled],
|
|
|
+ .ivu-btn-primary[disabled]:hover {
|
|
|
+ background-color: #24b880;
|
|
|
+ border-color: #24b880;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ivu-btn-warning[disabled],
|
|
|
+ .ivu-btn-warning[disabled]:hover {
|
|
|
+ color: #c5c8ce;
|
|
|
+ background-color: #FEE49E;
|
|
|
+ border-color: #FEE49E;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.owner-student-client-icon {
|
|
|
+ font-size: 30px;
|
|
|
+ padding: 5px;
|
|
|
+ color: #ffffff;
|
|
|
+ border-radius: 50%;
|
|
|
+ background: #de7320;
|
|
|
+}
|
|
|
+
|
|
|
+.scroll-list {
|
|
|
+ height: 100%;
|
|
|
+
|
|
|
+ .ivu-scroll-container {
|
|
|
+ height: 100% !important;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|