修改智慧教师三分屏

This commit is contained in:
yangjun 2024-11-13 17:30:23 +08:00
parent ea0acda5a2
commit bf38afc422
3 changed files with 1119 additions and 1 deletions

View File

@ -72,7 +72,7 @@
<a-form-item label="评价类型">
<j-dict-select-tag
placeholder="请选择评价类型" v-model:value="queryParam.col15"
:options="[{ value: '一般听课表', label: '一般听课表'}, { value: '线上听课表', label: '线上听课表'},{ value: '同行评价表', label: '同行评价表'},{ value: '老版评价表', label: '老版评价表'}]"
:options="[{ value: '一般听课表', label: '一般听课表'}, { value: '线上听课表', label: '线上听课表'},{ value: '同行评价表', label: '同行评价表'},{ value: '老版评价表', label: '老版评价表'},{ value: '思政评价表', label: '思政评价表'}]"
/>
</a-form-item>
</a-col>

View File

@ -0,0 +1,566 @@
<template>
<div id="siteMain">
<div id="maxSite">
<a-layout>
<!-- 页头 -->
<headerPage/>
<!-- 主体部分 -->
<a-layout-content>
<div style="width:100%;height: 100%;margin-top: 1rem;" v-if="!isError">
<a-row :gutter="[16,16]">
<a-col :xs="{ span: 24 }" :sm="{ span: 19 }" :lg="{ span: 19 }">
<div>
<a-card class="videoCardMain" style="width:100%">
<template #title>
<span class="" style="border-radius: 5px;">
{{ ktangInfo.kcmc || ' ' }}
</span>
<span style="float: right;margin-left: 10px;background: #1c84c6;color: #fff;padding: 9px;border-radius: 5px;font-size: 16px;" @click="funpingjia(ktangInfo)">填写评价表</span>
<div style="font-size: 12px;">
{{ ktangInfo.zc || ' ' }}&nbsp;&nbsp;{{ ktangInfo.skjs || ' ' }}&nbsp;&nbsp;学分{{ ktangInfo.xf || ' ' }}
&nbsp;&nbsp;课程性质{{ ktangInfo.kcxz || ' ' }}
&nbsp;&nbsp;开课单位{{ ktangInfo.kkdw || ' ' }}
&nbsp;&nbsp;节次{{ ktangInfo.hh || ' ' }}
</div>
<div style="font-size: 12px;text-wrap: wrap;width: 100%;word-wrap: break-word;" hidden>
课程介绍{{ ktangInfo?.zyJxdg?.kcjs }}
</div>
</template>
<div style="padding: 1rem;">
<div style="font-size: 16px;float: left;">{{ mainVideoCardBoxTitle || '' }}</div>
<!-- <div style="float: right;">
AI识别出勤率{{ calcPercentage((ktangInfo?.detectionMain?.averageNum || 0),(ktangInfo?.jiaoshirongliang?.jsrl || 0))}}&nbsp;&nbsp;
<template v-if="ktangInfo?.jiaoshirongliang?.jsrl">本教室容量{{ktangInfo?.jiaoshirongliang?.jsrl}}座位</template>
</div> -->
<div style="float: right;">
<span>本教室容量{{ktangInfo?.jiaoshirongliang?.jsrl || ' '}}座位</span>
<span style="margin-left:15px;">选课人数{{ ktangInfo.xkrs || ' ' }}</span>
<span style="margin-left:15px;">AI识别出勤人数: <a @click="handleZqrs(ktangInfo)">{{ ktangInfo?.kcDetectionDetailed?.num||'-' }}</a></span>
<span style="margin-left:15px;">
<a-tooltip placement="topRight">
<template #title>
<span>平台分别在三个时间节点上课后10分钟课中50分钟下课前10分钟抓取学生全景图片进行AI人流量识别当前显示的人数为最近一个时间的人数识别</span>
</template>
<!-- <Icon icon="ant-design:question-circle-outlined" /> -->
<span class="helpClass">?</span>
</a-tooltip>
</span>
</div>
<bVideo ref="mainVideo" videoId="mainVideo" :videoOption="{ autoplay: true }" @load-end="mainVideoLoadEnd"/>
<div class="jxDiv">
<a-space>
<div v-if="ktangInfo?.zyJxdg?.filePath">
教学大纲:
<a-button type="primary" size="small" style="margin-left:10px;" @click="openPdf(ktangInfo?.zyJxdg?.pdfPath)">预览</a-button>
<a-button type="primary" size="small" style="margin-left:10px;" @click="downloadFile(ktangInfo?.zyJxdg?.filePath)">下载</a-button>
</div>
<div v-if="ktangInfo?.zyJxdg?.filePath">
教学日历:
<a-button type="primary" size="small" style="margin-left:10px;" @click="openPdf(ktangInfo?.zyJxdg?.jxrlPdfPath)">预览</a-button>
<a-button type="primary" size="small" style="margin-left:10px;" @click="downloadFile(ktangInfo?.zyJxdg?.jxrlFilePath)">下载</a-button>
</div>
</a-space>
</div>
<div style="width: 100%;margin-top:10px;">
<a-textarea style="width: calc(100% - 7rem);height:120px;float: left;" v-model:value="model.notes" placeholder="您可以填写听课笔记"></a-textarea>
<div style="width: 7rem;height: 100%;float: right;">
<a-button type="primary" style="width: 7rem;height: 100%;" @click="openAllSuiBi">查看听课笔记</a-button>
<a-button type="primary" style="width: 7rem;height: 100%;margin-top: 10px;" @click="saveSuibi">保存听课笔记</a-button>
<a-button type="primary" style="width: 7rem;height: 100%;margin-top: 10px;" @click="handleBaocuo(ktangInfo)">报错</a-button>
</div>
</div>
</div>
</a-card>
</div>
</a-col>
<a-col :xs="{ span: 24 }" :sm="{ span: 5 }" :lg="{ span: 5 }">
<a-row>
<a-col :span="24" v-for="(item,index) of tableData" style="text-align: center;" :key="'col-'+index">
<a-card>
<div>
<div style="margin-bottom: .5rem;">
<span class="smallTxt miniButton" :class="item.id == curentPlayerVideo.id?'activte':''" @click="changeLive(item)">
<i class="fa-solid fa-right-left"></i>{{ item?.xm }}
</span>
<span class="smallTxt miniButton miniButtonRight" v-show="!item.isShow" @click="(item.isShow = true,initVideo('other-'+item.id))"><i class="fa-solid fa-film"></i>显示缩略窗口</span>
<span class="smallTxt miniButton miniButtonRight" v-show="item.isShow" @click="item.isShow = false"><i class="fa-solid fa-film"></i>隐藏缩略窗口</span>
</div>
<div style="height: 2rem;">&nbsp;</div>
<div v-show="item.isShow" :key="'other-div-'+index">
<!-- <bVideo :key="'other-'+index" :ref="el=> bVideoRefs['other-'+item.id] = el" :videoId="'other-'+item.id" :src="'http://127.0.0.1/hls/a.m3u8'" :videoOption="{ autoplay: true, controls: false }" @load-end="loadEnd"/> -->
<bVideo :key="'other-'+index" :ref="el=> bVideoRefs['other-'+item.id] = el" :videoId="'other-'+item.id" :src="item.pullUrl" :videoOption="{ autoplay: true, controls: false }" @load-end="loadEnd"/>
</div>
</div>
</a-card>
</a-col>
</a-row>
</a-col>
</a-row>
</div>
<div v-else style="height: 100%;display: flex;justify-content: center;align-items: center;">
<a-empty>
<template #description>
<span v-if="!playStatus">
没有找到直播间
</span>
<span v-if="playStatus">
{{ getSysConfig()?.videoPlayErrTitle || '直播间暂无内容' }}
</span>
</template>
<!-- <a-button type="primary" @="">关闭</a-button> -->
</a-empty>
</div>
</a-layout-content>
</a-layout>
</div>
</div>
<KcErrorreportIndexModal ref="kcErrorreportIndexModal"></KcErrorreportIndexModal>
<addModalPage ref="tingKeZuJiAddModal"/>
<a-modal title="查看听课笔记" width="800px" :visible="tkbjVisible" :okButtonProps="{ class: { 'jee-hidden': true } }" @cancel="() => tkbjVisible = false" cancelText="关闭">
<div style="white-space:normal; word-break:break-all;overflow:hidden;">
<a-table tableLayout="fixed" :dataSource="suibiList" :scroll="{ x: true }" :pagination="false">
<a-table-column width="160px" title="填写时间" key="createTime" data-index="createTime" />
<a-table-column title="内容" key="notes" data-index="notes" />
</a-table>
</div>
</a-modal>
<KcDetectionMainModal @register="registerModal" ></KcDetectionMainModal>
</template>
<script lang="ts" setup name="zhihuijiaoshiIndexPage">
import { defHttp } from '/@/utils/http/axios';
import { ref, reactive, onMounted, createVNode, h } from 'vue';
import headerPage from '/@/views/site/common/header.vue';
import bVideo from '/@/views/site/common/video/videojs/video.vue';
import addModalPage from '/@/views/site/tingKeZuJi/components/addModal.vue';
import { nextTick } from 'vue';
import { useRoute } from 'vue-router'
import { getUserId } from '/@/views/site/utils/index';
import KcErrorreportIndexModal from '/@/views/kc/kcErrorreport/components/KcErrorreportIndexZbModal.vue'
import videojs from "video.js";
import { getSysConfig } from '/@/views/site/utils/index';
import { useMessage } from '/@/hooks/web/useMessage';
import { baseApiUrl, getFileAccessHttpUrl } from '/@/utils/common/compUtils';
import KcDetectionMainModal from '/@/views/kc/detection/components/KcDetectionMainModal.vue'
import { useModal } from '/@/components/Modal';
import { Modal } from 'ant-design-vue';
import { CloseOutlined } from '@ant-design/icons-vue';
const { createMessage, createInfoModal, createErrorModal } = useMessage();
const mainVideo = ref<any>();
const bVideoRefs = ref<any>([]);
const kcErrorreportIndexModal = ref();
const tingKeZuJiAddModal = ref<any>({});
const curentPlayerVideo = ref<any>({});
const kcCardBoxTitle = ref<any>('');
const ktangInfo = ref<any>({});
// const jxdgInfo = ref<any>({});
const mainVideoCardBoxTitle = ref<any>('');
const tableData = ref<Recordable>([])
const suibiList = ref<Recordable>([])
const isError = ref(false);
const tkbjVisible = ref(false);
const playStatus = ref(false);
const model = reactive<Record<string, any>>({ notes:'' });
//model
const [registerModal, { openModal }] = useModal();
const route = useRoute();
enum Api {
list = '/jiaoshi/kcZhihuijiaoshi/list',
querySuibi = '/kc/kcKetangSuibi/list',
addSuibi = '/kc/kcKetangSuibi/add',
// editSuibi = '/kc/kcKetangSuibi/edit'
getKtangInfo = '/ktgl/kcKetangbiao/queryAllDataById',
savePlayLog = '/jiaoshi/kcZhihuijiaoshiAccessLog/savePlayLog',
changeAvyLiveByJsbhsApi = '/httpinterface/runAvyApiByJsbhs',
}
/**
* 列表接口
* @param params
*/
const list = (params) => defHttp.get({ url: Api.list, params });
const querySuibi = (params) => defHttp.get({ url: Api.querySuibi, params });
const addSuibi = (params) => defHttp.post({ url: Api.addSuibi, params });
const getKtangInfo = (params) => defHttp.get({ url: Api.getKtangInfo, params });
const savePlayLog = (params) => defHttp.post({ url: Api.savePlayLog, params, }, { isTransformResponse: false });
const changeAvyLiveByJsbhsApi = (params) => defHttp.get({ url: Api.changeAvyLiveByJsbhsApi, params,timeout: 9000000 });
// const editSuibi = (params) => defHttp.post({ url: Api.editSuibi, params });
onMounted(() => {
if(route.query.id){
model.notes = '';
//,
list({ pageSize: -1, sfyx: '0', jsbh: route.query.id }).then(res => {
let list = (res?.records) ?? [];
tableData.value = list;
tableData.value.forEach(x => x.isShow = true);//
let zjData = tableData.value.find(x => x.xm == '教师近景') || {};
nextTick(() => {
if(zjData){
changeLive(zjData);
}
savePlayLogFn(zjData);
calcPlayStatus(zjData);
tableData.value.forEach(x => x.isShow = false);//
})
});
getSuibi();
getKcxx();
//
isError.value = false;
}else{
isError.value = true;
}
});
function handleBaocuo(item) {
kcErrorreportIndexModal.value.disableSubmit = false;
kcErrorreportIndexModal.value.add(item);
}
const listTkjlApi = (params) => defHttp.get({ url: '/kcTingke/kcTingke/findTingKeZuJiBytingketimeAndUserId', params });
function funpingjia(record) {
console.log('11111--------->',record)
console.log('222--------->',record.szkc)
var item = { ketangbiaoid: record.id };
let userid = getUserId();
listTkjlApi({ userid: userid, ketangbiaoid: item.ketangbiaoid }).then((res) => {
var list = res;
console.log(`🚀 ~ file: list.vue:106 ~ listTkjlApi ~ list:`, list);
if (list.length > 0) {
var score = list[0].score;
if (score) {
Modal.error({
icon: createVNode({}),
content: h('div', { style: 'height:200px;text-align:center;' }, [
h('icon', { style: 'font-size:80px;font-weight:600;color:red;' }, createVNode(CloseOutlined)),
h('p', { style: 'font-size:22px;font-weight:600;color:black;' }, '已经对此课程进行评价,不可重复评价!'),
]),
okText: 'OK',
width: '500px',
});
} else {
// tingKeZuJiAddModal.value.view(item);
tingKeZuJiAddModal.value.view({ ketangbiaoid: route.query.ktId,szkc:record.szkc })
}
} else {
// tingKeZuJiAddModal.value.view(item);
tingKeZuJiAddModal.value.view({ ketangbiaoid: route.query.ktId,szkc:record.szkc })
}
});
}
/**
* 子页加载完成后回调
* @param player
*/
function loadEnd(player){
nextTick(() => {
player.on('play',() => {
setTimeout(() => {
player.pause();
},2000);
})
})
}
function initVideo(key){
nextTick(() => {
let ref = bVideoRefs.value[key];
ref.init();
})
}
function changeLive(item:any){
let url = item.pullUrl
let mainVideo = document.querySelector<any>('#mainVideo');
// mainVideo?.player?.src([{ type:'application/x-mpegURL',src: 'http://127.0.0.1/live_hls/a.m3u8' }])
mainVideo?.player?.src([{ type:'application/x-mpegURL',src: url }])
mainVideoCardBoxTitle.value = item.jsmc+" "+item.xm;
console.log(`🚀 --------------------------------------------------------------------------------🚀`);
console.log(`🚀 ~ file: viewPage.vue:164 ~ changeLive ~ mainVideo?.player:`, mainVideo?.player);
console.log(`🚀 --------------------------------------------------------------------------------🚀`);
// let buttonEl = mainVideo?.player.el().querySelector('.changeDefinitionBtn');
//URL
// if(buttonEl){
// buttonEl.dataset.url = url;
// }
curentPlayerVideo.value = item;
// nextTick(() => {
//
//mainVideo?.player?.play();
// })
}
function mainVideoLoadEnd(player){
setTimeout(() => {
setTimeout(() => {
player.muted(false);
//player.play();
},50);
},50);
}
function openLive(item: any){
changeAvyLiveByJsbhsApi({ jsbhs:item.jsbh, type: 1 }).then(res => {
let content = '';
res.forEach(x => {
// content += x.jsmc + "-" + x.xm
content += x.jsmc
let text = '';
if(x.resText){
if(x.resText.includes('ok')){
text = '播放失败,开启直播间成功,请稍后再试'
}else{
text = '播放失败,开启直播间失败,请联系管理员。'
}
}
content += " " + text + "<br/>"
});
// if(content.includes('')){
// //12
// setTimeout(() => {
// createInfoModal({ width:'50%', title: '',content })
// }, 12*1000);
// }else{
createInfoModal({ width:'50%', title: '结果',content })
// }
}).catch(e => {
console.error(e);
//loading.value = false;
//createInfoModal({title: '',content:e})
})
}
function getSuibi(){
//
let param = {
userId: getUserId(),
ketangbiaoId: route.query.ktId,
column: 'createTime',
order: 'desc',
};
querySuibi(param).then(res => {
Object.assign(model, (res.records??[{}])[0]);
// model = (res.records??[{}])[0];
});
}
function getKcxx(){
//
//route.query.ktId
let param = {
id: route.query.ktId
}
getKtangInfo(param).then(res => {
console.log(res);
ktangInfo.value = res;
kcCardBoxTitle.value = res.kcmc;
// defHttp.get({ url: '/zyJxdg/zyJxdg/getKcjsJxdg', params: { rwbh: res.rwbh,xqxn: res.xnxq } }).then((res) => {
// if(res){
// jxdgInfo.value = res;
// }
// });
});
}
function savePlayLogFn(item){
savePlayLog({ playUrl: item.pullUrl, jxlId: item.jxlId, jxlName: item.jxlName, jsbh: route.query.id, jsmc: item.jsmc, ketangbiaoId: route.query.ktId,ketangbiaoName: ktangInfo.value.kcmc })
}
//
function calcPlayStatus(item){
if(item.pullUrl){
videojs.xhr.get(item.pullUrl,(err, resp, body) => {
if(err){
playStatus.value = false;
isError.value = true;
openLive(item);
}else{
playStatus.value = true;
isError.value = false;
}
})
}else {
console.log(1111111);
playStatus.value = true;
isError.value = true;
}
}
function saveSuibi(){
//addSuibi editSuibi
// if(model.value.id){
// editSuibi({...model.value, })
// }else{
addSuibi({ notes: model.notes, userId: getUserId(), ketangbiaoId: route.query.ktId })
// }
}
function openAllSuiBi(){
//
tkbjVisible.value = true;
//
let param = {
userId: getUserId(),
ketangbiaoId: route.query.ktId,
column: 'createTime',
order: 'desc',
pageSize: '-1',
};
querySuibi(param).then(res => {
// model = (res.records??[{}])[0];
suibiList.value = res.records??[]
}).finally(() => {
//
});
}
function calcPercentage(averageNum, zrs) {
if(averageNum === 0 || zrs === 0){
return '0%';
}else{
return (averageNum / zrs * 100).toFixed(2) + '%';
}
}
function openPdf(miniUrl) {
let url2 = getFileAccessHttpUrl(miniUrl)
let url = baseApiUrl+"/generic/web/viewer.html?file="+encodeURIComponent(url2);
window.open(url,"_blank");
}
function downloadFile(miniUrl) {
let url = getFileAccessHttpUrl(miniUrl);
window.open(url,"_blank");
}
function handleZqrs(record){
console.log(`🚀 ~ handleZqrs ~ record:`, record)
console.log(`🚀 ~ handleZqrs ~ zqrsList:`, record.zqrsList)
var a = record.detectionMain;
if(record.zqrsList){
a.detectionDetailedList = record.zqrsList
handleZqrsDetail(a);
}else{
createMessage.warning("暂无数据!")
}
}
function handleZqrsDetail(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
showFooter: false,
});
}
</script>
<style lang="less" scoped>
.jxDiv {
padding-top: .5rem;
}
#siteMain {
// font-size: ;
height: 100%;
background: #f3f3f4;
#maxSite {
//
max-width: 1170px;
//
margin: 0 auto;
.rowGutter{
margin-top: 1rem;
margin-bottom: 1rem;
}
.ant-layout-header {
color: #fff;
background: #1ab394;
}
.ant-layout-footer {
line-height: 1.5;
background: #FFF;
}
.ant-layout-sider {
color: #fff;
line-height: 120px;
background: #3ba0e9;
}
.ant-layout-content {
min-height: 120px;
color: #000;
/*line-height: 120px;*/
background: #f3f3f4;
}
}
}
/**暗黑模式特殊配色*/
[data-theme='dark'] #siteMain #maxSite {
.ant-layout-header, .ant-layout-footer {
background: #6aa0c7;
}
.ant-layout-content {
background: #107bcb;
}
.ant-layout-sider {
background: #3499ec;
}
}
.videoMax{
width: 25%;
}
.videoCardMain {
:deep(.ant-card-body) {
padding: 0;
}
}
/* 隐藏video 进度条 */
video::-webkit-media-controls-timeline {
display: none;
}
.smallTxt {
font-size: .8rem;
}
.activte {
background: #1ab394;
color: #FFF;
}
.miniButton {
float: left;
padding: 0.3rem;
border-radius: 5px;
}
.miniButtonRight {
float: right
}
.helpClass{
font-size: 16px;
font-weight: 700;
/* background: #1c84c6; */
color: #1c84c6;
border-radius: 5px;
padding: 1px 8px;
border: 1px #1c84c6 solid;
cursor: pointer;
}
</style>

View File

@ -0,0 +1,552 @@
<template>
<div id="siteMain">
<div id="maxSite">
<a-layout>
<!-- 页头 -->
<headerPage/>
<!-- 主体部分 -->
<a-layout-content>
<div style="width:100%;height: 100%;margin-top: 1rem;background-color: white;padding: 10px;" v-if="!isError">
<a-row :gutter="[16,16]">
<a-col>
<div>
<span style="border-radius: 5px;font-size: 16px;">
{{ ktangInfo.kcmc || ' ' }}
</span>
<span style="float: right;margin-left: 10px;background: #1c84c6;color: #fff;padding: 9px;border-radius: 5px;font-size: 16px;" @click="funpingjia(ktangInfo)">填写评价表</span>
<div style="font-size: 12px;">
{{ ktangInfo.zc || ' ' }}&nbsp;&nbsp;{{ ktangInfo.skjs || ' ' }}&nbsp;&nbsp;学分{{ ktangInfo.xf || ' ' }}
&nbsp;&nbsp;课程性质{{ ktangInfo.kcxz || ' ' }}
&nbsp;&nbsp;开课单位{{ ktangInfo.kkdw || ' ' }}
&nbsp;&nbsp;节次{{ ktangInfo.hh || ' ' }}
</div>
</div>
<a-divider />
<div style="margin-top: 20px;">
<div style="font-size: 16px;float: left;">{{ mainVideoCardBoxTitle || '' }}</div>
<div style="float: right;">
<span>本教室容量{{ktangInfo?.jiaoshirongliang?.jsrl || ' '}}座位</span>
<span style="margin-left:15px;">选课人数{{ ktangInfo.xkrs || ' ' }}</span>
<span style="margin-left:15px;">AI识别出勤人数: <a @click="handleZqrs(ktangInfo)">{{ ktangInfo?.kcDetectionDetailed?.num||'-' }}</a></span>
<span style="margin-left:15px;">
<a-tooltip placement="topRight">
<template #title>
<span>平台分别在三个时间节点上课后10分钟课中50分钟下课前10分钟抓取学生全景图片进行AI人流量识别当前显示的人数为最近一个时间的人数识别</span>
</template>
<span class="helpClass">?</span>
</a-tooltip>
</span>
</div>
</div>
</a-col>
<a-col :span="18" style="padding-right: 0px;">
<bVideo ref="mainVideo" videoId="mainVideo" :videoOption="{ autoplay: true }" @load-end="mainVideoLoadEnd"/>
</a-col>
<a-col :span="6" style="padding-left: 0px;padding-right: 3px">
<a-row>
<a-col :span="24" v-for="(item,index) of tableData" style="text-align: center;" :key="'col-'+index" v-show="item.id != curentPlayerVideo.id">
<div>
<div :key="'other-div-'+index">
<bVideo :key="'other-'+index" :ref="el=> bVideoRefs['other-'+item.id] = el" :videoId="'other-'+item.id" @click="changeLive(item)" :src="item.pullUrl" :videoOption="{ autoplay: true, controls: false }" @load-end="loadEnd"/>
</div>
</div>
</a-col>
</a-row>
</a-col>
<a-col :span="24" style="padding-right: 0px;">
<div style="width: 100%;margin-top:10px;">
<a-textarea style="width: calc(100% - 7rem);height:120px;float: left;" v-model:value="model.notes" placeholder="您可以填写听课笔记"></a-textarea>
<div style="width: 7rem;height: 100%;float: right;">
<a-button type="primary" style="width: 7rem;height: 100%;" @click="openAllSuiBi">查看听课笔记</a-button>
<a-button type="primary" style="width: 7rem;height: 100%;margin-top: 10px;" @click="saveSuibi">保存听课笔记</a-button>
<a-button type="primary" style="width: 7rem;height: 100%;margin-top: 10px;" @click="handleBaocuo(ktangInfo)">报错</a-button>
</div>
</div>
</a-col>
</a-row>
</div>
<div v-else style="height: 100%;display: flex;justify-content: center;align-items: center;">
<a-empty>
<template #description>
<span v-if="!playStatus">
没有找到直播间
</span>
<span v-if="playStatus">
{{ getSysConfig()?.videoPlayErrTitle || '直播间暂无内容' }}
</span>
</template>
<!-- <a-button type="primary" @="">关闭</a-button> -->
</a-empty>
</div>
</a-layout-content>
</a-layout>
</div>
</div>
<KcErrorreportIndexModal ref="kcErrorreportIndexModal"></KcErrorreportIndexModal>
<addModalPage ref="tingKeZuJiAddModal"/>
<a-modal title="查看听课笔记" width="800px" :visible="tkbjVisible" :okButtonProps="{ class: { 'jee-hidden': true } }" @cancel="() => tkbjVisible = false" cancelText="关闭">
<div style="white-space:normal; word-break:break-all;overflow:hidden;">
<a-table tableLayout="fixed" :dataSource="suibiList" :scroll="{ x: true }" :pagination="false">
<a-table-column width="160px" title="填写时间" key="createTime" data-index="createTime" />
<a-table-column title="内容" key="notes" data-index="notes" />
</a-table>
</div>
</a-modal>
<KcDetectionMainModal @register="registerModal" ></KcDetectionMainModal>
</template>
<script lang="ts" setup name="zhihuijiaoshiIndexPage">
import { defHttp } from '/@/utils/http/axios';
import { ref, reactive, onMounted, createVNode, h } from 'vue';
import headerPage from '/@/views/site/common/header.vue';
import bVideo from '/@/views/site/common/video/videojs/video.vue';
import addModalPage from '/@/views/site/tingKeZuJi/components/addModal.vue';
import { nextTick } from 'vue';
import { useRoute } from 'vue-router'
import { getUserId } from '/@/views/site/utils/index';
import KcErrorreportIndexModal from '/@/views/kc/kcErrorreport/components/KcErrorreportIndexZbModal.vue'
import videojs from "video.js";
import { getSysConfig } from '/@/views/site/utils/index';
import { useMessage } from '/@/hooks/web/useMessage';
import { baseApiUrl, getFileAccessHttpUrl } from '/@/utils/common/compUtils';
import KcDetectionMainModal from '/@/views/kc/detection/components/KcDetectionMainModal.vue'
import { useModal } from '/@/components/Modal';
import { Modal } from 'ant-design-vue';
import { CloseOutlined } from '@ant-design/icons-vue';
const { createMessage, createInfoModal, createErrorModal } = useMessage();
const mainVideo = ref<any>();
const bVideoRefs = ref<any>([]);
const kcErrorreportIndexModal = ref();
const tingKeZuJiAddModal = ref<any>({});
const curentPlayerVideo = ref<any>({});
const kcCardBoxTitle = ref<any>('');
const ktangInfo = ref<any>({});
// const jxdgInfo = ref<any>({});
const mainVideoCardBoxTitle = ref<any>('');
const tableData = ref<Recordable>([])
const suibiList = ref<Recordable>([])
const isError = ref(false);
const tkbjVisible = ref(false);
const playStatus = ref(false);
const model = reactive<Record<string, any>>({ notes:'' });
//model
const [registerModal, { openModal }] = useModal();
const route = useRoute();
enum Api {
list = '/jiaoshi/kcZhihuijiaoshi/list',
querySuibi = '/kc/kcKetangSuibi/list',
addSuibi = '/kc/kcKetangSuibi/add',
// editSuibi = '/kc/kcKetangSuibi/edit'
getKtangInfo = '/ktgl/kcKetangbiao/queryAllDataById',
savePlayLog = '/jiaoshi/kcZhihuijiaoshiAccessLog/savePlayLog',
changeAvyLiveByJsbhsApi = '/httpinterface/runAvyApiByJsbhs',
}
/**
* 列表接口
* @param params
*/
const list = (params) => defHttp.get({ url: Api.list, params });
const querySuibi = (params) => defHttp.get({ url: Api.querySuibi, params });
const addSuibi = (params) => defHttp.post({ url: Api.addSuibi, params });
const getKtangInfo = (params) => defHttp.get({ url: Api.getKtangInfo, params });
const savePlayLog = (params) => defHttp.post({ url: Api.savePlayLog, params, }, { isTransformResponse: false });
const changeAvyLiveByJsbhsApi = (params) => defHttp.get({ url: Api.changeAvyLiveByJsbhsApi, params,timeout: 9000000 });
// const editSuibi = (params) => defHttp.post({ url: Api.editSuibi, params });
onMounted(() => {
if(route.query.id){
model.notes = '';
//,
list({ pageSize: -1, sfyx: '0', jsbh: route.query.id }).then(res => {
let list = (res?.records) ?? [];
tableData.value = list;
tableData.value.forEach(x => x.isShow = true);//
let zjData = tableData.value.find(x => x.xm == '教师近景') || {};
nextTick(() => {
if(zjData){
changeLive(zjData);
}
savePlayLogFn(zjData);
calcPlayStatus(zjData);
tableData.value.forEach(x => x.isShow = false);//
})
});
getSuibi();
getKcxx();
//
isError.value = false;
}else{
isError.value = true;
}
});
function handleBaocuo(item) {
kcErrorreportIndexModal.value.disableSubmit = false;
kcErrorreportIndexModal.value.add(item);
}
const listTkjlApi = (params) => defHttp.get({ url: '/kcTingke/kcTingke/findTingKeZuJiBytingketimeAndUserId', params });
function funpingjia(record) {
console.log('11111--------->',record)
console.log('222--------->',record.szkc)
var item = { ketangbiaoid: record.id };
let userid = getUserId();
listTkjlApi({ userid: userid, ketangbiaoid: item.ketangbiaoid }).then((res) => {
var list = res;
console.log(`🚀 ~ file: list.vue:106 ~ listTkjlApi ~ list:`, list);
if (list.length > 0) {
var score = list[0].score;
if (score) {
Modal.error({
icon: createVNode({}),
content: h('div', { style: 'height:200px;text-align:center;' }, [
h('icon', { style: 'font-size:80px;font-weight:600;color:red;' }, createVNode(CloseOutlined)),
h('p', { style: 'font-size:22px;font-weight:600;color:black;' }, '已经对此课程进行评价,不可重复评价!'),
]),
okText: 'OK',
width: '500px',
});
} else {
// tingKeZuJiAddModal.value.view(item);
tingKeZuJiAddModal.value.view({ ketangbiaoid: route.query.ktId,szkc:record.szkc })
}
} else {
// tingKeZuJiAddModal.value.view(item);
tingKeZuJiAddModal.value.view({ ketangbiaoid: route.query.ktId,szkc:record.szkc })
}
});
}
/**
* 子页加载完成后回调
* @param player
*/
function loadEnd(player){
// nextTick(() => {
// player.on('play',() => {
// setTimeout(() => {
// player.pause();
// },2000);
// })
// })
}
function mainVideoLoadEnd(player){
setTimeout(() => {
setTimeout(() => {
player.muted(false);
},50);
},50);
// player.muted(false);
// nextTick(() => {
// player.on('play',() => {
// // setTimeout(() => {
// // player.muted(false);
// player.play();
// // },2000);
// })
// })
}
function loadEnd2(player){
nextTick(() => {
player.on('play',() => {
setTimeout(() => {
player.pause();
},2000);
})
})
}
function initVideo(key){
nextTick(() => {
let ref = bVideoRefs.value[key];
ref.init();
})
}
function changeLive(item:any){
let url = item.pullUrl
let mainVideo = document.querySelector<any>('#mainVideo');
// mainVideo?.player?.src([{ type:'application/x-mpegURL',src: 'http://127.0.0.1/live_hls/a.m3u8' }])
mainVideo?.player?.src([{ type:'application/x-mpegURL',src: url }])
mainVideoCardBoxTitle.value = item.jsmc+" "+item.xm;
console.log(`🚀 --------------------------------------------------------------------------------🚀`);
console.log(`🚀 ~ file: viewPage.vue:164 ~ changeLive ~ mainVideo?.player:`, mainVideo?.player);
console.log(`🚀 --------------------------------------------------------------------------------🚀`);
// let buttonEl = mainVideo?.player.el().querySelector('.changeDefinitionBtn');
//URL
// if(buttonEl){
// buttonEl.dataset.url = url;
// }
curentPlayerVideo.value = item;
// nextTick(() => {
//
//mainVideo?.player?.play();
// })
}
function openLive(item: any){
changeAvyLiveByJsbhsApi({ jsbhs:item.jsbh, type: 1 }).then(res => {
let content = '';
res.forEach(x => {
// content += x.jsmc + "-" + x.xm
content += x.jsmc
let text = '';
if(x.resText){
if(x.resText.includes('ok')){
text = '播放失败,开启直播间成功,请稍后再试'
}else{
text = '播放失败,开启直播间失败,请联系管理员。'
}
}
content += " " + text + "<br/>"
});
// if(content.includes('')){
// //12
// setTimeout(() => {
// createInfoModal({ width:'50%', title: '',content })
// }, 12*1000);
// }else{
createInfoModal({ width:'50%', title: '结果',content })
// }
}).catch(e => {
console.error(e);
//loading.value = false;
//createInfoModal({title: '',content:e})
})
}
function getSuibi(){
//
let param = {
userId: getUserId(),
ketangbiaoId: route.query.ktId,
column: 'createTime',
order: 'desc',
};
querySuibi(param).then(res => {
Object.assign(model, (res.records??[{}])[0]);
// model = (res.records??[{}])[0];
});
}
function getKcxx(){
//
//route.query.ktId
let param = {
id: route.query.ktId
}
getKtangInfo(param).then(res => {
console.log(res);
ktangInfo.value = res;
kcCardBoxTitle.value = res.kcmc;
// defHttp.get({ url: '/zyJxdg/zyJxdg/getKcjsJxdg', params: { rwbh: res.rwbh,xqxn: res.xnxq } }).then((res) => {
// if(res){
// jxdgInfo.value = res;
// }
// });
});
}
function savePlayLogFn(item){
savePlayLog({ playUrl: item.pullUrl, jxlId: item.jxlId, jxlName: item.jxlName, jsbh: route.query.id, jsmc: item.jsmc, ketangbiaoId: route.query.ktId,ketangbiaoName: ktangInfo.value.kcmc })
}
//
function calcPlayStatus(item){
if(item.pullUrl){
videojs.xhr.get(item.pullUrl,(err, resp, body) => {
if(err){
playStatus.value = false;
isError.value = true;
openLive(item);
}else{
playStatus.value = true;
isError.value = false;
}
})
}else {
console.log(1111111);
playStatus.value = true;
isError.value = true;
}
}
function saveSuibi(){
//addSuibi editSuibi
// if(model.value.id){
// editSuibi({...model.value, })
// }else{
addSuibi({ notes: model.notes, userId: getUserId(), ketangbiaoId: route.query.ktId })
// }
}
function openAllSuiBi(){
//
tkbjVisible.value = true;
//
let param = {
userId: getUserId(),
ketangbiaoId: route.query.ktId,
column: 'createTime',
order: 'desc',
pageSize: '-1',
};
querySuibi(param).then(res => {
// model = (res.records??[{}])[0];
suibiList.value = res.records??[]
}).finally(() => {
//
});
}
function calcPercentage(averageNum, zrs) {
if(averageNum === 0 || zrs === 0){
return '0%';
}else{
return (averageNum / zrs * 100).toFixed(2) + '%';
}
}
function openPdf(miniUrl) {
let url2 = getFileAccessHttpUrl(miniUrl)
let url = baseApiUrl+"/generic/web/viewer.html?file="+encodeURIComponent(url2);
window.open(url,"_blank");
}
function downloadFile(miniUrl) {
let url = getFileAccessHttpUrl(miniUrl);
window.open(url,"_blank");
}
function handleZqrs(record){
console.log(`🚀 ~ handleZqrs ~ record:`, record)
console.log(`🚀 ~ handleZqrs ~ zqrsList:`, record.zqrsList)
var a = record.detectionMain;
if(record.zqrsList){
a.detectionDetailedList = record.zqrsList
handleZqrsDetail(a);
}else{
createMessage.warning("暂无数据!")
}
}
function handleZqrsDetail(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
showFooter: false,
});
}
</script>
<style lang="less" scoped>
.jxDiv {
padding-top: .5rem;
}
#siteMain {
// font-size: ;
height: 100%;
background: #f3f3f4;
#maxSite {
//
max-width: 1170px;
//
margin: 0 auto;
.rowGutter{
margin-top: 1rem;
margin-bottom: 1rem;
}
.ant-layout-header {
color: #fff;
background: #1ab394;
}
.ant-layout-footer {
line-height: 1.5;
background: #FFF;
}
.ant-layout-sider {
color: #fff;
line-height: 120px;
background: #3ba0e9;
}
.ant-layout-content {
min-height: 120px;
color: #000;
/*line-height: 120px;*/
background: #f3f3f4;
}
}
}
/**暗黑模式特殊配色*/
[data-theme='dark'] #siteMain #maxSite {
.ant-layout-header, .ant-layout-footer {
background: #6aa0c7;
}
.ant-layout-content {
background: #107bcb;
}
.ant-layout-sider {
background: #3499ec;
}
}
.videoMax{
width: 25%;
}
.videoCardMain {
:deep(.ant-card-body) {
padding: 0;
}
}
/* 隐藏video 进度条 */
video::-webkit-media-controls-timeline {
display: none;
}
.smallTxt {
font-size: .8rem;
}
.activte {
background: #1ab394;
color: #FFF;
}
.miniButton {
float: left;
padding: 0.3rem;
border-radius: 5px;
}
.miniButtonRight {
float: right
}
.helpClass{
font-size: 16px;
font-weight: 700;
/* background: #1c84c6; */
color: #1c84c6;
border-radius: 5px;
padding: 1px 8px;
border: 1px #1c84c6 solid;
cursor: pointer;
}
</style>