|
@@ -0,0 +1,789 @@
|
|
|
+<template>
|
|
|
+ <div class="record-info">
|
|
|
+ <Loading v-show="isLoad" bgColor="rgba(0, 0, 0, 0.3)"></Loading>
|
|
|
+ <div class="testTitle">
|
|
|
+ <span class="logoutIcon" @click="quitRec">
|
|
|
+ <svg-icon icon-class="logout" />
|
|
|
+ </span>
|
|
|
+ <span class="testTitleText">{{$t("studentWeb.courseContent.classRecord")}}</span>
|
|
|
+ </div>
|
|
|
+ <div style="margin-top: 44px; padding: 10px 15px;">
|
|
|
+ <h2 class="event-title" v-if="recordInfo">{{ 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;">
|
|
|
+ <div class="record-head" v-if="recordInfo">
|
|
|
+ <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 v-if="courseNow">
|
|
|
+ <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') }}
|
|
|
+ <template v-if="courseNow.className.length">
|
|
|
+ <span class="base-info-text" v-for="(item, index) in courseNow.className" :key="index" style="margin-right: 10px;">{{ item.name }}</span>
|
|
|
+ </template>
|
|
|
+ </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 class="count-box">
|
|
|
+ <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 v-if="hasVideo" v-show="isShowVd" class="video-player-box">
|
|
|
+ <video style="width: 100%;" id="recordVideo" 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 === 'WrkSpaceEnd' && 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>
|
|
|
+ <Modal v-model="showNote" :title="$t('studentWeb.courseContent.mynotes')" width="800">
|
|
|
+ <img :src="item" alt="" v-for="(item, index) in recordInfo.myNote" :key="index" style="width: 100%">
|
|
|
+ </Modal>
|
|
|
+ <Modal v-model="showWorks" :title="$t('studentWeb.courseContent.mynotes')" width="800">
|
|
|
+ <myWorks :recordInfo="recordInfo" :rcvData="myWorks" />
|
|
|
+ </Modal>
|
|
|
+
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import videojs from "video.js";
|
|
|
+import "videojs-markers";
|
|
|
+
|
|
|
+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 {
|
|
|
+ name: "ClassRecord",
|
|
|
+ components: {
|
|
|
+ Loading,
|
|
|
+ DataCount, ShowQues, PopQues, Buzr, Push,
|
|
|
+ StuReceive, ReceiveBack, Pick, Exam, myWorks,
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ isLoad: false,
|
|
|
+ player: undefined,
|
|
|
+ pageList: [], //课件
|
|
|
+ markers: [], //打点
|
|
|
+ sokratesRecords: {}, //原始数据
|
|
|
+ curPage: 1, //当前课件页码
|
|
|
+ currentfilterType: "",
|
|
|
+ openHtexViewer: false,
|
|
|
+ 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无法播放媒体源时显示的默认信息。
|
|
|
+ },
|
|
|
+ recordInfo: {},
|
|
|
+ baseData: undefined, //base.json
|
|
|
+ pushData: [], //push.json
|
|
|
+ irsData: [], //irs.json
|
|
|
+ taskData: [], //task.json
|
|
|
+ fnEvents: [], //功能事件
|
|
|
+ events: [], //事件ID
|
|
|
+ hiTeachEvent: [], //需要解析的事件信息
|
|
|
+ isShowVd: true,
|
|
|
+ hasVideo: true,
|
|
|
+ nowStuInfo: undefined, //当前学生的信息
|
|
|
+ haveInteraction: true,
|
|
|
+ courseNow: undefined,
|
|
|
+ showNote: false,
|
|
|
+ filtertype: {
|
|
|
+ push: 0, //推送
|
|
|
+ task: 0, //任务
|
|
|
+ irs: 0, //互动
|
|
|
+ exam: 0, //测验
|
|
|
+ },
|
|
|
+ showPageList: [],
|
|
|
+ myWorks: [],
|
|
|
+ showWorks: false,
|
|
|
+ attendType: 2,
|
|
|
+ attentColor: ['', '#19be6b', '#EB3941', '#EB3941', '#EB3941', '#EB3941'],
|
|
|
+ }
|
|
|
+ },
|
|
|
+ created() {
|
|
|
+ this.hiTeachEvent = this.$GLOBAL.HI_TEACH_EVENT()
|
|
|
+ this.events = Object.keys(this.hiTeachEvent)
|
|
|
+ this.fnEvents = this.events.filter(key => this.hiTeachEvent[key].type === 'fn')
|
|
|
+ this.recordInfo = this.$route.params.record
|
|
|
+ this.courseNow = this.$route.params.courseNow
|
|
|
+ if (!this.recordInfo) {
|
|
|
+ this.$router.go(-1)
|
|
|
+ } else {
|
|
|
+ this.recordInfo.startTime = this.dateFormat(this.recordInfo.startTime)
|
|
|
+ let sec = this.recordInfo.duration % 60
|
|
|
+ let min = parseInt(this.recordInfo.duration / 60)
|
|
|
+ let mins = min >= 60 ? min % 60 : min
|
|
|
+ let hour = parseInt(min / 60)
|
|
|
+ this.recordInfo.time = `${hour < 10 ? ('0' + hour) : hour}:${mins < 10 ? ('0' + mins) : mins}:${sec < 10 ? ('0' + sec) : sec}`
|
|
|
+ this.getPageList()
|
|
|
+ }
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ // this.getVideo()
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ getVideo() {
|
|
|
+ var that = this
|
|
|
+ this.player = videojs(document.getElementById("recordVideo"), 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
|
|
|
+ },
|
|
|
+ // 根据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.$store.state.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.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 === 'WrkSpaceEnd' || 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
|
|
|
+ }
|
|
|
+ })
|
|
|
+ this.getVideo()
|
|
|
+ },
|
|
|
+ // 点击互动记录页面tag
|
|
|
+ toVideo(page, e) {
|
|
|
+ this.curPage = page
|
|
|
+ //页面滚动
|
|
|
+ /* let dataLoacation = this.$refs["datawrap"].getPosition()
|
|
|
+ let pageLocaltion = this.$refs["pagewrap"].getPosition()
|
|
|
+ let y = e.pageY - 770 + pageLocaltion.scrollTop + dataLoacation.scrollTop
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.$refs["pagewrap"].scrollTo(
|
|
|
+ {
|
|
|
+ x: 0,
|
|
|
+ y: 0
|
|
|
+ }
|
|
|
+ )
|
|
|
+ this.$refs["datawrap"].scrollTo(
|
|
|
+ {
|
|
|
+ x: 0,
|
|
|
+ y: y
|
|
|
+ }
|
|
|
+ )
|
|
|
+ }) */
|
|
|
+ //视频时间定位
|
|
|
+ let pageInfo = this.markers.find(item => {
|
|
|
+ return item.page === page
|
|
|
+ })
|
|
|
+ if (pageInfo) {
|
|
|
+ this.player.currentTime(pageInfo.time)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 点击课件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 {
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 点击视频切片
|
|
|
+ getCurPage(page) {
|
|
|
+ this.curPage = page
|
|
|
+ // this.mapJson = require('./data/' + page + '.json')
|
|
|
+ // this.$refs["datawrap"].scrollIntoView('#page' + page, 500)
|
|
|
+ this.player.play()
|
|
|
+ },
|
|
|
+ //查看电子笔记
|
|
|
+ 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'))
|
|
|
+ // }
|
|
|
+ },
|
|
|
+ //下载电子笔记
|
|
|
+ async loadNote() {
|
|
|
+ if (this.recordInfo.eNote) {
|
|
|
+ // 已经有授权,在查看文件时不需要再次获取授权
|
|
|
+ let blobData = this.recordInfo.eNote
|
|
|
+ const downloadRes = async () => {
|
|
|
+ let response = await fetch(blobData); // 内容转变成blob地址
|
|
|
+ let blob = await response.blob(); // 创建隐藏的可下载链接
|
|
|
+ let objectUrl = window.URL.createObjectURL(blob);
|
|
|
+ let a = document.createElement('a');
|
|
|
+ a.href = objectUrl;
|
|
|
+ a.download = "电子笔记.pdf";
|
|
|
+ a.click()
|
|
|
+ a.remove();
|
|
|
+ }
|
|
|
+ downloadRes();
|
|
|
+ } else {
|
|
|
+ this.$Message.warning("暂无电子笔记")
|
|
|
+ }
|
|
|
+ },
|
|
|
+ openViewer(item) {
|
|
|
+ this.$hevueImgPreview(item)
|
|
|
+ },
|
|
|
+ // 筛选
|
|
|
+ showFile(type) {
|
|
|
+ this.currentfilterType = (type === "all" ? "" : type)
|
|
|
+ },
|
|
|
+ getTime(time) {
|
|
|
+ let sec = parseInt(time % 60)
|
|
|
+ let min = parseInt(time / 60)
|
|
|
+ let mins = min >= 60 ? min % 60 : min
|
|
|
+ let hour = parseInt(min / 60)
|
|
|
+ return `${hour < 10 ? ('0' + hour) : hour}:${mins < 10 ? ('0' + mins) : mins}:${sec < 10 ? ('0' + sec) : sec}`
|
|
|
+ },
|
|
|
+ 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;
|
|
|
+ },
|
|
|
+ quitRec() {
|
|
|
+ this.$router.go(-1)
|
|
|
+ },
|
|
|
+ //返回顶部
|
|
|
+ handleToTop() {
|
|
|
+ document.getElementsByClassName("class-record")[0].scrollIntoView()
|
|
|
+ /* this.$refs['pagewrap'].scrollTo(
|
|
|
+ {
|
|
|
+ y: '0'
|
|
|
+ },
|
|
|
+ 300
|
|
|
+ ) */
|
|
|
+ },
|
|
|
+ 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
|
|
|
+ }
|
|
|
+ },
|
|
|
+ downloadFile(url, taskInfo) {
|
|
|
+ const downloadRes = async () => {
|
|
|
+ let response = await fetch(url); // 内容转变成blob地址
|
|
|
+ let blob = await response.blob(); // 创建隐藏的可下载链接
|
|
|
+ let objectUrl = window.URL.createObjectURL(blob);
|
|
|
+ let a = document.createElement('a');
|
|
|
+ a.href = objectUrl;
|
|
|
+ let fileName = url.substring(url.lastIndexOf('/' + 1), url.lastIndexOf('?'))
|
|
|
+ a.download = `${taskInfo.jobName}-${taskInfo.seatID}-${fileName}`
|
|
|
+ a.click()
|
|
|
+ a.remove();
|
|
|
+ }
|
|
|
+ downloadRes();
|
|
|
+ },
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ curImg() {
|
|
|
+ if (this.pageList[this.curPage - 1]) {
|
|
|
+ return this.pageList[this.curPage - 1].img
|
|
|
+ } else {
|
|
|
+ return ""
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="less" scoped>
|
|
|
+@import "./newClassRecord.less";
|
|
|
+</style>
|
|
|
+<style lang="less">
|
|
|
+.record-left {
|
|
|
+ .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;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.record-info {
|
|
|
+ .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;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|