mirror of
https://codeup.aliyun.com/67762337eccfc218f6110e0e/vue/learning-system-portal.git
synced 2025-12-18 15:26:45 +08:00
458 lines
14 KiB
Vue
458 lines
14 KiB
Vue
<template>
|
||
<div class="aiAbstract">
|
||
<div class="ai-left" v-if="sections.length > 0">
|
||
<div class="left-title">{{courseInfo.name}}</div>
|
||
<ul class="ai-list" >
|
||
<template v-for="c in chapterList">
|
||
<li class="ai-item" v-for="(item, index) in c.children" @click="handleSelectVideo(item, index+1)" :class="{'active': currentVideo.id === item.id}" :key="item.id">
|
||
<div class="ai-item-title">{{item.chapterName}}-{{'视频' + (index + 1)}}</div>
|
||
</li>
|
||
</template>
|
||
</ul>
|
||
</div>
|
||
<div class="ai-right">
|
||
<div class="right-title">
|
||
<h3>{{videoName}}</h3>
|
||
<div>
|
||
<el-button type="primary" @click="handleSummaryStatus">{{courseInfo.aiAbstract == 1 ? '下架' : '上架'}}本课程AI摘要</el-button>
|
||
</div>
|
||
</div>
|
||
<div class="ai-content">
|
||
<div class="videoBox">
|
||
<videoPlayer
|
||
:src="blobUrl"
|
||
:blobId="blobId"
|
||
style="height: auto;"> </videoPlayer>
|
||
<div class="video-content">
|
||
<h4>视频摘要</h4>
|
||
<p>
|
||
{{videoSummary}}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<div class="videoOperation">
|
||
<div class="opera-title">
|
||
<h4>课程摘要</h4>
|
||
<div class="opera-btn">
|
||
<el-button v-if="type == 1 || type == 3" type="primary" plain round size="mini" @click="retrySummaryTxt">重新生成</el-button>
|
||
<el-button v-if="type == 1" type="primary" plain round size="mini" @click="type = 4">编辑</el-button>
|
||
<el-button v-if="type == 4" plain round size="mini" @click="type = 1">取消</el-button>
|
||
<el-button v-if="type == 4" type="primary" plain round size="mini" @click="updateSummary">确认</el-button>
|
||
</div>
|
||
</div>
|
||
<div class="opera-content" v-show="type != 4">
|
||
<span v-show="type == 1">{{ aiAbstract }}</span>
|
||
<p v-show="type == 2" style="color: rgba(207, 207, 207, 1);text-align: center;margin-top: 48%;">AI 摘要重新生成中,过程可能耗时较长,<br>无需在此等待哦~</p>
|
||
<img v-show="type == 3" src="@/assets/images/course/generationFailed.png" alt="" width="150" height="159" style="display: flex;margin: 35% auto 0 auto;">
|
||
</div>
|
||
<el-input v-show="type == 4"
|
||
type="textarea"
|
||
placeholder="请输入内容"
|
||
autosize
|
||
v-model="aiAbstract">
|
||
</el-input>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import { mapGetters, mapMutations } from "vuex";
|
||
import videoPlayer from "@/components/VideoPlayer/index.vue";
|
||
import apiStudy from "@/api/modules/courseStudy.js";
|
||
import cookies from "vue-cookies";
|
||
import apiAiVideo from "@/api/modules/courseAiVideo.js";
|
||
import apiCourse from '@/api/modules/course.js';
|
||
import { encrypt } from "@/utils/jsencrypt.js";
|
||
export default {
|
||
name: 'aiAbstract',
|
||
// ai播放器相关
|
||
components: {
|
||
videoPlayer
|
||
},
|
||
data() {
|
||
return {
|
||
chapterList: [],
|
||
videoName: '',
|
||
currentVideo: {},
|
||
aiAbstract: '',
|
||
type: '1', // 1: 正常 2: 生成中 3: 错误 4: 编辑中
|
||
courseId: '',
|
||
courseInfo: {},
|
||
sections: [],
|
||
blobId: '',
|
||
blobUrl: '',
|
||
videoSummary: '',
|
||
summaryTimer: null
|
||
}
|
||
},
|
||
beforeDestroy() {
|
||
// 组件销毁前清除定时器
|
||
if (this.summaryTimer) {
|
||
clearInterval(this.summaryTimer);
|
||
this.summaryTimer = null;
|
||
}
|
||
},
|
||
created() {
|
||
this.courseId = this.$route.query.id;
|
||
console.log(this.courseId);
|
||
this.getCourseInfo();
|
||
this.getCourseSummary();
|
||
},
|
||
mounted() {
|
||
},
|
||
methods: {
|
||
...mapMutations({
|
||
SET_selectableLang: 'video/SET_selectableLang',
|
||
SET_courseInfo: 'video/SET_courseInfo',
|
||
}),
|
||
// 上下架状态
|
||
handleSummaryStatus() {
|
||
apiCourse.benchAiSet({courseList:[{id: this.courseId, aiAbstract: this.courseInfo.aiAbstract == 1 ? 0 : 1}]}).then(res => {
|
||
if(res.status === 200){
|
||
this.$message.success(this.courseInfo.aiAbstract == 1 ? '下架成功!' : '上架成功!');
|
||
this.getCourseInfo();
|
||
}else{
|
||
this.$message.error(this.courseInfo.aiAbstract == 1 ? '下架失败!' : '上架失败!');
|
||
}
|
||
})
|
||
},
|
||
// 下载摘要 - 不需要
|
||
downloadSummary() {
|
||
apiAiVideo.downloadSummaryTxt(this.courseId)
|
||
.then((rs) => {
|
||
console.log('下载',rs);
|
||
// 1. 创建 Blob 的临时 URL
|
||
const url = URL.createObjectURL(rs);
|
||
|
||
// 2. 创建隐藏的 <a> 标签
|
||
const a = document.createElement('a');
|
||
a.href = url;
|
||
a.download = '课程摘要.txt'; // 设置下载的文件名
|
||
a.style.display = 'none';
|
||
|
||
// 3. 添加到文档中并触发点击
|
||
document.body.appendChild(a);
|
||
a.click();
|
||
|
||
// 4. 清理
|
||
setTimeout(() => {
|
||
document.body.removeChild(a);
|
||
URL.revokeObjectURL(url); // 释放 URL 对象
|
||
}, 100);
|
||
})
|
||
},
|
||
updateSummary() {
|
||
apiAiVideo.updateVideoSummary({
|
||
courseId: this.courseId,
|
||
summaryContent: this.aiAbstract,
|
||
})
|
||
.then((rs) => {
|
||
if (rs.rspCode == '0000') {
|
||
this.type = '1';
|
||
this.getCourseSummary();
|
||
}
|
||
})
|
||
},
|
||
retrySummaryTxt() {
|
||
this.type = '2';
|
||
apiAiVideo.retrySummaryTxt({
|
||
courseId: this.courseId,
|
||
})
|
||
.then((rs) => {
|
||
if (rs.rspCode == '0000') {
|
||
this.type = '1';
|
||
this.getCourseSummary();
|
||
} else {
|
||
this.type = '3';
|
||
}
|
||
})
|
||
},
|
||
getVideoSummary(id) {
|
||
apiAiVideo.selectById(id)
|
||
.then((rs) => {
|
||
this.videoSummary = ''
|
||
if (rs.rspCode == '0000') {
|
||
this.videoSummary = rs.data.briefSummary || '';
|
||
} else {
|
||
this.$message.error('获取视频摘要失败!');
|
||
}
|
||
})
|
||
.catch((err) => {
|
||
this.videoSummary = ''
|
||
this.$message.error('获取视频摘要失败!');
|
||
})
|
||
},
|
||
getCourseSummary() {
|
||
apiAiVideo.getCourse({
|
||
courseId: this.courseId,
|
||
})
|
||
.then((rs) => {
|
||
// 任务状态 courseSummaryTaskStatus 0-未开始 1-执行中 2-执行完成 3-执行失败 4-重试中
|
||
// 页面状态 type 1: 正常 2: 生成中 3: 错误 4: 编辑中
|
||
let taskStatus = rs.data.courseSummaryTaskStatus;
|
||
|
||
// 统一清除可能存在的旧定时器
|
||
if (this.summaryTimer) {
|
||
clearInterval(this.summaryTimer);
|
||
this.summaryTimer = null;
|
||
}
|
||
|
||
if (taskStatus == 1 || taskStatus == 4) {
|
||
this.type = 2;
|
||
// 设置新定时器,每隔1秒调用一次
|
||
this.summaryTimer = setInterval(() => {
|
||
this.getCourseSummary();
|
||
}, 5000);
|
||
} else if (taskStatus == 3) {
|
||
this.type = 3;
|
||
} else {
|
||
this.type = 1;
|
||
}
|
||
this.aiAbstract = rs.data.summaryContent || '';
|
||
|
||
})
|
||
.catch(() => {
|
||
// 请求失败时也清除定时器
|
||
if (this.summaryTimer) {
|
||
clearInterval(this.summaryTimer);
|
||
this.summaryTimer = null;
|
||
}
|
||
})
|
||
},
|
||
createPlayUrl(fid, u) {
|
||
let nowDate = new Date();
|
||
let ctime = parseInt(nowDate.getTime() / 1000);
|
||
let beforeUrl = parseInt(nowDate.getTime() / 1000) + "/" + fid;
|
||
let urlSign = encodeURIComponent(encrypt(beforeUrl));
|
||
cookies.set("PLAYSIGN_TIME", ctime); //写客户端的cookie保存
|
||
//以下判断是为了区分本地环境和服务器环境
|
||
if (process.env.NODE_ENV == "development") {
|
||
this.blobUrl = process.env.VUE_APP_FILE_BASE_URL + u;
|
||
} else {
|
||
this.blobUrl =
|
||
process.env.VUE_APP_BASE_API +
|
||
"/xboe/m/course/cware/resource?sign=" +
|
||
urlSign;
|
||
}
|
||
},
|
||
handleSelectVideo(item, index) {
|
||
console.log('选择了该视频', item);
|
||
this.SET_selectableLang(item?.boeaiSubtitleRspList);
|
||
this.videoName = item.chapterName ? item.chapterName + "-视频" + index : this.courseInfo.name;
|
||
this.currentVideo = item;
|
||
let curriculumData = {};
|
||
if (item.content.startsWith("{")) {
|
||
curriculumData = JSON.parse(item.content);
|
||
} else {
|
||
curriculumData.url = item.content;
|
||
}
|
||
this.getVideoSummary(item.id);
|
||
this.blobId = item.id;
|
||
this.createPlayUrl(item.contentRefId, curriculumData.url);
|
||
},
|
||
getCourseInfo() {
|
||
apiStudy.studyIndexPost({
|
||
cid: this.courseId,
|
||
addView: false,
|
||
audiences: '',
|
||
})
|
||
.then((rs) => {
|
||
if (rs.status == 200) {
|
||
this.courseInfo = rs.result.course || {};
|
||
this.SET_courseInfo(this.courseInfo);
|
||
const contents = rs.result.contents?.filter(item => item.contentType == 10) || [];
|
||
this.sections = rs.result.sections || [];
|
||
if (this.sections.length > 0) {
|
||
let treeList = [];
|
||
let hasOne = false;
|
||
this.sections.forEach(section => {
|
||
let treeNode = {
|
||
...section,
|
||
children: [],
|
||
};
|
||
contents.forEach(item => {
|
||
if (section.id == item.csectionId) {
|
||
item.chapterName = section.name || '';
|
||
treeNode.children.push(item);
|
||
if (!hasOne) {
|
||
this.handleSelectVideo(item, 1);
|
||
hasOne = true;
|
||
}
|
||
}
|
||
})
|
||
treeList.push(treeNode);
|
||
})
|
||
this.chapterList = treeList;
|
||
} else {
|
||
this.handleSelectVideo(contents[0], 1);
|
||
}
|
||
console.log('33333',this.courseInfo, this.courseInfo.name);
|
||
this.aiAbstract = this.courseInfo.summaryContent
|
||
if (contents.length == 0) {
|
||
$this.$message.error("课程内容已删除或课程已不再使用");
|
||
return;
|
||
}
|
||
if (!rs.result.course.enabled) {
|
||
$this.$message.error(
|
||
"十分抱歉,此课程已停用,如需使用,请联系管理员。"
|
||
);
|
||
return;
|
||
}
|
||
}
|
||
|
||
})
|
||
}
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.aiAbstract{
|
||
height: 100%;
|
||
display: flex;
|
||
padding: 10px;
|
||
justify-content: space-between;
|
||
gap: 15px;
|
||
background: #f4f7fa;
|
||
.ai-left{
|
||
padding: 9px 10px;
|
||
width: 24%;
|
||
border-radius: 10px;
|
||
background: rgba(255, 255, 255, 1);
|
||
.left-title{
|
||
background: rgba(239, 244, 252, 1);
|
||
padding: 15px;
|
||
text-align: center;
|
||
color: rgba(75, 92, 118, 1);
|
||
font-family: Noto Sans SC;
|
||
font-size: 16px;
|
||
font-weight: 400;
|
||
line-height: 23px;
|
||
letter-spacing: 0px;
|
||
}
|
||
.ai-list{
|
||
margin: 0;
|
||
.ai-item{
|
||
cursor: pointer;
|
||
padding: 15px;
|
||
text-align: center;
|
||
color: rgba(75, 92, 118, 1);
|
||
font-family: Noto Sans SC;
|
||
font-size: 16px;
|
||
font-weight: 400;
|
||
line-height: 23px;
|
||
letter-spacing: 0px;
|
||
padding: 17px 31px;
|
||
border-bottom: 1px solid rgba(240, 240, 240, 1);
|
||
text-align: left;
|
||
&:hover{
|
||
background: rgba(240, 240, 240, 1);
|
||
}
|
||
&.active{
|
||
color: rgba(64, 158, 255, 1);
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
.ai-right{
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
border-radius: 10px;
|
||
background: rgba(255, 255, 255, 1);
|
||
.right-title{
|
||
display: flex;
|
||
padding: 0 28px;
|
||
height: 76px;
|
||
min-height: 76px;
|
||
border-bottom: 1px solid rgba(229, 231, 235, 1);
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
h3{
|
||
color: rgba(17, 24, 39, 1);
|
||
font-family: Noto Sans SC;
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
line-height: 26px;
|
||
margin: 0;
|
||
}
|
||
}
|
||
.ai-content{
|
||
flex: 1;
|
||
padding: 24px 30px;
|
||
display: flex;
|
||
gap: 30px;
|
||
|
||
.videoBox{
|
||
width: 55%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 24px;
|
||
.video-content{
|
||
flex: 1;
|
||
h4{
|
||
margin: 0 0 10px 0;
|
||
color: rgba(17, 24, 39, 1);
|
||
font-family: Noto Sans SC;
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
line-height: 23px;
|
||
}
|
||
p{
|
||
color: rgba(17, 24, 39, 1);
|
||
font-family: Noto Sans SC;
|
||
font-size: 14px;
|
||
font-weight: 400;
|
||
line-height: 20px;
|
||
}
|
||
}
|
||
}
|
||
.videoOperation{
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 15px;
|
||
.opera-title{
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
h4{
|
||
margin: 0;
|
||
color: rgba(17, 24, 39, 1);
|
||
font-family: Noto Sans SC;
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
line-height: 23px;
|
||
}
|
||
.opera-btn{
|
||
display: flex;
|
||
}
|
||
}
|
||
.opera-content{
|
||
flex: 1;
|
||
border-radius: 7px;
|
||
background: rgba(249, 250, 251, 1);
|
||
padding: 15px;
|
||
color: rgba(17, 24, 39, 1);
|
||
font-family: Noto Sans SC;
|
||
font-size: 14px;
|
||
font-weight: 400;
|
||
line-height: 20px;
|
||
letter-spacing: 0px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
::v-deep .el-textarea{
|
||
flex: 1;
|
||
}
|
||
::v-deep .el-textarea__inner{
|
||
height: 100%!important;
|
||
}
|
||
</style>
|