Compare commits
33 Commits
master
...
master-202
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
932225ec49 | ||
|
|
1bf29238cd | ||
|
|
8f01fc7a8e | ||
|
|
7dc8c81223 | ||
|
|
4bc350d7b6 | ||
|
|
e6e60ae2e6 | ||
|
|
19618e3b90 | ||
|
|
4d4a93698f | ||
|
|
7e53ed11c2 | ||
|
|
85152e2e25 | ||
|
|
a1d5d0f079 | ||
|
|
99760c9940 | ||
|
|
3cfd025858 | ||
|
|
0cc4dd73fb | ||
|
|
ea6989cddc | ||
|
|
8e46ffb65a | ||
|
|
cbb36b429b | ||
|
|
a1b8c617e4 | ||
|
|
8168c8880d | ||
|
|
7d3ab9b6b6 | ||
|
|
27e76da571 | ||
|
|
82c765edd4 | ||
|
|
39d29c0dad | ||
|
|
311ec096eb | ||
|
|
9379aad28f | ||
|
|
fa1bcaf1c7 | ||
|
|
3ee4afb229 | ||
|
|
82dcfa6348 | ||
|
|
caa9b23766 | ||
|
|
4b4c0943d8 | ||
|
|
b31f02bf23 | ||
|
|
003fece291 | ||
|
|
986a47f22a |
@@ -1,14 +1,14 @@
|
||||
/* 案例模块的相关处理*/
|
||||
import ajax from '@/utils/xajax.js'
|
||||
import ajaxs from '@/api/ajax.js'
|
||||
import ajax from "@/utils/xajax.js";
|
||||
import ajaxs from "@/api/ajax.js";
|
||||
/**首页查询
|
||||
* pageSize
|
||||
* orderField
|
||||
* orderAsc
|
||||
*/
|
||||
const indexList = function (query) {
|
||||
return ajax.post('/xboe/m/boe/cases/case-random', query);
|
||||
}
|
||||
return ajax.post("/xboe/m/boe/cases/case-random", query);
|
||||
};
|
||||
/*
|
||||
*案例分页搜索查询 是否推荐
|
||||
*@param(String) keyWord 关键词
|
||||
@@ -20,11 +20,11 @@ const indexList = function (query) {
|
||||
* @param(String) majorDomain 专业领域
|
||||
*/
|
||||
const queryList = function (query) {
|
||||
return ajax.post('/xboe/m/boe/cases/queryList', query);
|
||||
}
|
||||
return ajax.post("/xboe/m/boe/cases/queryList", query);
|
||||
};
|
||||
const queryListV2 = function (query) {
|
||||
return ajax.postJson('/xboe/m/boe/cases/queryListV2', query);
|
||||
}
|
||||
return ajax.postJson("/xboe/m/boe/cases/queryListV2", query);
|
||||
};
|
||||
/* 案例分页搜索查询 是否置顶
|
||||
*@param(String) keyWord 关键词
|
||||
@param(Boolean) isTop 是否置顶 true或false
|
||||
@@ -33,32 +33,32 @@ const queryListV2 = function (query) {
|
||||
* @param(String) majorDomain 专业领域
|
||||
*/
|
||||
const isTopList = function (query) {
|
||||
return ajax.post('/xboe/m/boe/cases/isTopList', query);
|
||||
}
|
||||
return ajax.post("/xboe/m/boe/cases/isTopList", query);
|
||||
};
|
||||
/* 设置置顶 取消置顶
|
||||
@param(String) id
|
||||
@param(Integer) isTop 是否置顶 0:未置顶,1:已置顶
|
||||
*/
|
||||
const updateTop = function (id, isTop) {
|
||||
return ajax.get(`/xboe/m/boe/cases/updateTop?id=${id}&isTop=${isTop}`);
|
||||
}
|
||||
};
|
||||
/* 删除案例
|
||||
*param(String) id 案例的id
|
||||
*/
|
||||
const del = function (id) {
|
||||
return ajax.get('/xboe/m/boe/cases/delete?id=' + id);
|
||||
}
|
||||
return ajax.get("/xboe/m/boe/cases/delete?id=" + id);
|
||||
};
|
||||
/*案例详情
|
||||
@param(String) id 案例的id
|
||||
@param(Boolean) addView 是否增加浏览量
|
||||
*/
|
||||
const detail = function (id, addView) {
|
||||
let pars = 'id=' + id;
|
||||
let pars = "id=" + id;
|
||||
if (addView) {
|
||||
pars += '&addView=' + addView
|
||||
}
|
||||
return ajax.get('/xboe/m/boe/cases/detail?' + pars);
|
||||
pars += "&addView=" + addView;
|
||||
}
|
||||
return ajax.get("/xboe/m/boe/cases/detail?" + pars);
|
||||
};
|
||||
/*推荐
|
||||
*@param (String) id 案例的id
|
||||
*@param (String) title 案例的标题
|
||||
@@ -66,61 +66,65 @@ const detail = function (id, addView) {
|
||||
*@param (String) recommentThat 推荐说明
|
||||
*/
|
||||
const savaRecommend = function (data) {
|
||||
return ajax.post('/xboe/m/boe/recommend/save', data);
|
||||
}
|
||||
return ajax.post("/xboe/m/boe/recommend/save", data);
|
||||
};
|
||||
/* 推荐列表
|
||||
*没有参数
|
||||
*/
|
||||
const query = function () {
|
||||
return ajax.get('/xboe/m/boe/recommend/query');
|
||||
}
|
||||
return ajax.get("/xboe/m/boe/recommend/query");
|
||||
};
|
||||
/*
|
||||
*作者排行榜
|
||||
@param pageSize 长度
|
||||
*/
|
||||
const usernameList = function (pageSize) {
|
||||
return ajax.get(`/xboe/m/boe/cases/usernamelist?pageSize=${pageSize}`);
|
||||
}
|
||||
};
|
||||
/*
|
||||
人气榜
|
||||
@param pageSize 长度
|
||||
*/
|
||||
const queryPraises = function (pageSize) {
|
||||
return ajax.get(`/xboe/m/boe/cases/query-praises?pageSize=${pageSize}`);
|
||||
}
|
||||
};
|
||||
const queryPraisesNew = function (pageSize, rankType) {
|
||||
return ajax.get(`/xboe/m/boe/cases/queryPopularity?pageSize=${pageSize}&rankType=${rankType}`);
|
||||
}
|
||||
return ajax.get(
|
||||
`/xboe/m/boe/cases/queryPopularity?pageSize=${pageSize}&rankType=${rankType}`
|
||||
);
|
||||
};
|
||||
const queryRecommendRank = function (pageSize) {
|
||||
return ajax.get(`/xboe/m/boe/cases/queryRecommendRank?pageSize=${pageSize}`);
|
||||
}
|
||||
};
|
||||
/*
|
||||
好评榜
|
||||
@param pageSize 长度
|
||||
*/
|
||||
const queryComments = function (pageSize) {
|
||||
return ajax.get(`/xboe/m/boe/cases/query-comments?pageSize=${pageSize}`);
|
||||
}
|
||||
};
|
||||
const queryCommentsNew = function (pageSize, rankType) {
|
||||
return ajax.get(`/xboe/m/boe/cases/queryHighOpinion?pageSize=${pageSize}&rankType=${rankType}`);
|
||||
}
|
||||
return ajax.get(
|
||||
`/xboe/m/boe/cases/queryHighOpinion?pageSize=${pageSize}&rankType=${rankType}`
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 专业分类
|
||||
* */
|
||||
const majorTypes = function () {
|
||||
return ajax.get('/xboe/m/boe/cases/majorTypes');
|
||||
}
|
||||
return ajax.get("/xboe/m/boe/cases/majorTypes");
|
||||
};
|
||||
|
||||
/**
|
||||
* 详情新*/
|
||||
const details = function (id, addView) {
|
||||
let pars = 'id=' + id;
|
||||
let pars = "id=" + id;
|
||||
if (addView) {
|
||||
pars += '&addView=' + addView
|
||||
}
|
||||
return ajax.get('/xboe/m/boe/cases/details?' + pars);
|
||||
pars += "&addView=" + addView;
|
||||
}
|
||||
return ajax.get("/xboe/m/boe/cases/details?" + pars);
|
||||
};
|
||||
|
||||
/**
|
||||
* 二次查询
|
||||
@@ -129,8 +133,8 @@ const details = function (id, addView) {
|
||||
* }
|
||||
* */
|
||||
const ids = function (data) {
|
||||
return ajax.postJson('/xboe/m/boe/cases/ids', data);
|
||||
}
|
||||
return ajax.postJson("/xboe/m/boe/cases/ids", data);
|
||||
};
|
||||
|
||||
/**
|
||||
* 设置和取消优秀案例
|
||||
@@ -140,9 +144,8 @@ const ids = function (data) {
|
||||
* }
|
||||
* */
|
||||
const excellent = function (data) {
|
||||
return ajax.post('/xboe/m/boe/cases/excellent', data)
|
||||
}
|
||||
|
||||
return ajax.post("/xboe/m/boe/cases/excellent", data);
|
||||
};
|
||||
|
||||
/**
|
||||
*我的案例
|
||||
@@ -152,8 +155,8 @@ const excellent = function (data) {
|
||||
* }
|
||||
* */
|
||||
const mylist = function (query) {
|
||||
return ajax.post('/xboe/m/boe/cases/mylist', query);
|
||||
}
|
||||
return ajax.post("/xboe/m/boe/cases/mylist", query);
|
||||
};
|
||||
|
||||
/**
|
||||
* 导出
|
||||
@@ -163,21 +166,26 @@ const mylist = function (query) {
|
||||
* }
|
||||
* */
|
||||
const exportCases = function (query) {
|
||||
return ajax.post('/xboe/m/boe/cases/export', query);
|
||||
}
|
||||
return ajax.post("/xboe/m/boe/cases/export", query);
|
||||
};
|
||||
|
||||
/**
|
||||
* 导出案例后加的
|
||||
* */
|
||||
const exports = function (query) {
|
||||
return ajax.post('/xboe/m/boe/cases/exportCase', query, { responseType: 'blob' });
|
||||
}
|
||||
return ajax.post("/xboe/m/boe/cases/exportCase", query, {
|
||||
responseType: "blob",
|
||||
});
|
||||
};
|
||||
/**
|
||||
* @param
|
||||
* 记录查看开始时间
|
||||
* caseRecommendId读取的案例的id
|
||||
*/
|
||||
const startReadTimer = (caseRecommendId) => ajax.get(`/xboe/m/boe/cases/recommend/startRead?caseRecommendId=${caseRecommendId}`);
|
||||
const startReadTimer = (caseRecommendId) =>
|
||||
ajax.get(
|
||||
`/xboe/m/boe/cases/recommend/startRead?caseRecommendId=${caseRecommendId}`
|
||||
);
|
||||
|
||||
/**
|
||||
* @param
|
||||
@@ -187,34 +195,56 @@ const startReadTimer = (caseRecommendId) => ajax.get(`/xboe/m/boe/cases/recomm
|
||||
* browseDuration [浏览时间秒]
|
||||
* }
|
||||
*/
|
||||
const endReadTimer = (data)=> ajax.postJson('/xboe/m/boe/cases/recommend/recordBrowseDuration',data)
|
||||
const endReadTimer = (data) =>
|
||||
ajax.postJson("/xboe/m/boe/cases/recommend/recordBrowseDuration", data);
|
||||
|
||||
/**年份查询
|
||||
*
|
||||
*/
|
||||
const caseYears = function (query = {}) {
|
||||
return ajax.post('/xboe/m/boe/cases/caseYears', query);
|
||||
}
|
||||
return ajax.post("/xboe/m/boe/cases/caseYears", query);
|
||||
};
|
||||
//案例推荐榜单
|
||||
const getQueryRecommendRank = function (params) {
|
||||
return ajax.get(`/xboe/m/boe/cases/queryRecommendRank?pageSize=${params.pageSize}`)
|
||||
}
|
||||
return ajax.get(
|
||||
`/xboe/m/boe/cases/queryRecommendRank?pageSize=${params.pageSize}`
|
||||
);
|
||||
};
|
||||
//案例上榜
|
||||
const riseIntoRank = (data) => ajax.post(`/xboe/m/boe/cases/riseIntoRank?caseId=${data.caseId}`)
|
||||
const riseIntoRank = (data) =>
|
||||
ajax.post(`/xboe/m/boe/cases/riseIntoRank?caseId=${data.caseId}`);
|
||||
//取消上榜
|
||||
const cancelRiseIntoRank = (data) => ajax.post(`/xboe/m/boe/cases/cancelRiseIntoRank?caseId=${data.caseId}`)
|
||||
const cancelRiseIntoRank = (data) =>
|
||||
ajax.post(`/xboe/m/boe/cases/cancelRiseIntoRank?caseId=${data.caseId}`);
|
||||
//调整榜单
|
||||
const adjustRank = (data) => ajax.post(`/xboe/m/boe/cases/adjustRank?caseIds=${data.caseIds}`)
|
||||
const adjustRank = (data) =>
|
||||
ajax.post(`/xboe/m/boe/cases/adjustRank?caseIds=${data.caseIds}`);
|
||||
//收藏案例
|
||||
const queryFavoriteCaseOfIndex = (params) => ajax.get(`/xboe/m/boe/cases/queryFavoriteCaseOfIndex?pageIndex=${params.pageIndex}&pageSize=${params.pageSize}&orderField=${params.orderField}&orderAsc=${params.orderAsc}`)
|
||||
const queryFavoriteCaseOfIndex = (params) =>
|
||||
ajax.get(
|
||||
`/xboe/m/boe/cases/queryFavoriteCaseOfIndex?pageIndex=${params.pageIndex}&pageSize=${params.pageSize}&orderField=${params.orderField}&orderAsc=${params.orderAsc}`
|
||||
);
|
||||
//浏览记录
|
||||
const queryViewRecord = (data) => ajaxs.post(`/statApi/xboe/m/stat/userdynamic/list?pageIndex=${data.pageIndex}&pageSize=${data.pageSize}&contentType=${data.contentType}&aid=${data.aid}&hidden=${data.hidden}&eventKey=${data.eventKey}`)
|
||||
const queryViewRecord = (data) =>
|
||||
ajaxs.post(
|
||||
`/statApi/xboe/m/stat/userdynamic/list?pageIndex=${data.pageIndex}&pageSize=${data.pageSize}&contentType=${data.contentType}&aid=${data.aid}&hidden=${data.hidden}&eventKey=${data.eventKey}`
|
||||
);
|
||||
//浏览量TOP榜单
|
||||
const queryPopularityOfMajor = (params) => ajax.get(`/xboe/m/boe/cases/queryPopularityOfMajor?pageSize=${params.pageSize}&rankMonth=${params.rankMonth}&majorId=${params.majorId}`)
|
||||
const queryPopularityOfMajor = (params) =>
|
||||
ajax.get(
|
||||
`/xboe/m/boe/cases/queryPopularityOfMajor?pageSize=${params.pageSize}&rankMonth=${params.rankMonth}&majorId=${params.majorId}`
|
||||
);
|
||||
//浏览记录新
|
||||
const browsingHistory = (params) => ajax.get(`/xboe/m/boe/cases/browsingHistory?pageIndex=${params.pageIndex}&pageSize=5`)
|
||||
const browsingHistory = (params) =>
|
||||
ajax.get(
|
||||
`/xboe/m/boe/cases/browsingHistory?pageIndex=${params.pageIndex}&pageSize=5`
|
||||
);
|
||||
//获取所有的专业月份
|
||||
const queryAllTimePopularityOfMajor = () => ajax.get('/xboe/m/boe/cases/queryAllTimePopularityOfMajor')
|
||||
const queryAllTimePopularityOfMajor = () =>
|
||||
ajax.get("/xboe/m/boe/cases/queryAllTimePopularityOfMajor");
|
||||
// 获取会话ID
|
||||
const initChat = () => ajax.get("/xboe/m/boe/case/ai/initChat");
|
||||
|
||||
export default {
|
||||
queryAllTimePopularityOfMajor,
|
||||
getQueryRecommendRank,
|
||||
@@ -249,5 +279,6 @@ export default {
|
||||
exports,
|
||||
startReadTimer,
|
||||
endReadTimer,
|
||||
caseYears
|
||||
}
|
||||
caseYears,
|
||||
initChat,
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
**/
|
||||
import ajax from '@/utils/xajax.js'
|
||||
|
||||
import ajax2 from '../unionAjax.js';
|
||||
/**
|
||||
* 保存课程基本信息,新增和更新都是此方式
|
||||
* @param {Object} data
|
||||
@@ -446,6 +446,14 @@ const saveTip = function() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ai播放器相关 - 批量AI设置
|
||||
const benchAiSet=function(data){
|
||||
return ajax.postJson('/xboe/m/course/manage/benchAiSet',data);
|
||||
}
|
||||
const listByUser=function(data){
|
||||
return ajax2.get('/manageApi/admin/thirdApi/permission/listByUser?permissionType=PAGE');
|
||||
}
|
||||
export default {
|
||||
saveBase,
|
||||
submitCourse,
|
||||
@@ -489,6 +497,8 @@ export default {
|
||||
exportCourse,
|
||||
queryCrowd,
|
||||
ids,
|
||||
saveTip
|
||||
saveTip,
|
||||
benchAiSet,
|
||||
listByUser,
|
||||
|
||||
}
|
||||
|
||||
BIN
src/assets/images/case/stop.png
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
src/assets/images/course/courseAbstract.png
Normal file
|
After Width: | Height: | Size: 8.7 KiB |
BIN
src/assets/images/course/courseNew.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
src/assets/images/course/generationFailed.png
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
BIN
src/assets/images/course/languageIcon.png
Normal file
|
After Width: | Height: | Size: 478 B |
BIN
src/assets/images/course/noData.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
src/assets/images/course/selectLanguage.png
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
src/assets/images/course/wengaoTip.png
Normal file
|
After Width: | Height: | Size: 504 B |
@@ -97,3 +97,29 @@
|
||||
font-size: 14px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
// 已下架
|
||||
.custom-takeout{
|
||||
display: inline-block;
|
||||
padding: 3px 13px;
|
||||
border-radius: 20px;
|
||||
font-size: 12px;
|
||||
background: rgba(254, 249, 195, 1);
|
||||
color: rgba(133, 77, 14, 1);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
line-height: 17px;
|
||||
letter-spacing: 0px;
|
||||
}
|
||||
// 已上架
|
||||
.custom-putaway{
|
||||
display: inline-block;
|
||||
padding: 3px 13px;
|
||||
border-radius: 20px;
|
||||
font-size: 12px;
|
||||
background: rgba(220, 252, 231, 1);
|
||||
color: rgba(22, 101, 52, 1);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
line-height: 17px;
|
||||
letter-spacing: 0px;
|
||||
}
|
||||
439
src/components/Course/aiScript.vue
Normal file
@@ -0,0 +1,439 @@
|
||||
<template>
|
||||
<div class="ai-script">
|
||||
<!-- 搜索和语言选择区域 -->
|
||||
<div v-if="selectableLang && selectableLang.length > 0" class="search-container">
|
||||
<el-input
|
||||
v-model="searchKeyword"
|
||||
placeholder="请输入关键词查找文稿内容"
|
||||
class="search-input"
|
||||
prefix-icon="el-icon-search"
|
||||
@keyup.enter.native="searchContent"
|
||||
@input="handleInputChange"
|
||||
clearable
|
||||
native-type="text"
|
||||
/>
|
||||
<div class="language-selector">
|
||||
<span class="language-label">语言</span>
|
||||
<el-select v-model="selectedLanguage" class="language-select" @change="changeLanguage" placeholder="请选择语言">
|
||||
<el-option v-for="lang in selectableLang" :key="lang.srclang" :label="getSelectLabel(lang)" :value="lang.srclang"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 内容展示区域 -->
|
||||
<div v-if="selectableLang && selectableLang.length > 0" class="content-container">
|
||||
<!-- 动态渲染内容块 -->
|
||||
<div v-for="(item, index) in contentList" :key="index" class="content-item" :class="{'active': currentTime >= item.start && currentTime <= item.end}">
|
||||
<div class="timestamp">
|
||||
<div class="timestamp-text">
|
||||
<i class="el-icon-time"></i>
|
||||
{{ formatTime(item.start) }}
|
||||
</div>
|
||||
</div>
|
||||
<el-card class="content-text" @click.native="scrollToTime(item.start)">
|
||||
<div v-html="item.highlightedContent || item.text"></div>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="no-data">
|
||||
<img src="../../assets/images/course/noData.png" alt="">
|
||||
<span >生成中,过程可能耗时较长,<br>无需在此等待哦~</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapMutations } from 'vuex'
|
||||
export default {
|
||||
props: {
|
||||
// 视频链接对应的content Id
|
||||
blobId: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
isDrag:{
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
name: 'ai-script',
|
||||
data() {
|
||||
return {
|
||||
searchKeyword: '',
|
||||
selectedLanguage: 'zh-CN',
|
||||
originalContentList: [],
|
||||
contentList: [], // 用于显示的内容列表
|
||||
isUserScrolling: false, // 用户是否正在滚动
|
||||
userScrollTimeout: null // 滚动超时计时器
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'currentTime',
|
||||
'selectableLang',
|
||||
'duration'
|
||||
]),
|
||||
},
|
||||
mounted: function() {
|
||||
// 添加滚动事件监听,检测用户手动滚动
|
||||
const container = document.querySelector('.content-container');
|
||||
if (container) {
|
||||
container.addEventListener('scroll', this.handleUserScroll);
|
||||
}
|
||||
},
|
||||
|
||||
beforeDestroy: function() {
|
||||
// 清理事件监听和计时器
|
||||
const container = document.querySelector('.content-container');
|
||||
if (container) {
|
||||
container.removeEventListener('scroll', this.handleUserScroll);
|
||||
}
|
||||
if (this.userScrollTimeout) {
|
||||
clearTimeout(this.userScrollTimeout);
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
// 监听currentTime变化,自动滚动到当前激活项
|
||||
currentTime: function(newTime) {
|
||||
// 只有当用户没有手动滚动时才执行自动滚动
|
||||
if (!this.isUserScrolling) {
|
||||
this.$nextTick(function() {
|
||||
const activeElement = document.querySelector('.content-item.active');
|
||||
if (activeElement) {
|
||||
// 获取内容容器
|
||||
const container = document.querySelector('.content-container');
|
||||
|
||||
// 计算元素是否在可视区域内
|
||||
const containerRect = container.getBoundingClientRect();
|
||||
const elementRect = activeElement.getBoundingClientRect();
|
||||
|
||||
// 如果元素不在可视区域内,则滚动到可视区域
|
||||
if (elementRect.top < containerRect.top || elementRect.bottom > containerRect.bottom) {
|
||||
// 计算元素相对于容器的偏移量,而不是使用scrollIntoView
|
||||
// 这样只会滚动content-container内部,不会影响页面滚动
|
||||
|
||||
// 计算元素相对于容器的位置
|
||||
const elementOffsetTop = activeElement.offsetTop;
|
||||
const containerScrollTop = container.scrollTop;
|
||||
const containerHeight = container.clientHeight;
|
||||
const elementHeight = activeElement.clientHeight;
|
||||
|
||||
// 计算目标滚动位置,使元素居中显示
|
||||
// 考虑容器的内边距和元素本身的高度
|
||||
let targetScrollTop = elementOffsetTop - (containerHeight / 2) + (elementHeight / 2);
|
||||
|
||||
// 确保目标滚动位置不会小于0
|
||||
targetScrollTop = Math.max(0, targetScrollTop);
|
||||
|
||||
// 确保目标滚动位置不会导致元素超出容器底部
|
||||
const maxScrollTop = container.scrollHeight - containerHeight;
|
||||
targetScrollTop = Math.min(targetScrollTop, maxScrollTop);
|
||||
|
||||
// 使用requestAnimationFrame实现平滑滚动
|
||||
const startScrollTop = containerScrollTop;
|
||||
const distance = targetScrollTop - startScrollTop;
|
||||
const duration = 300; // 滚动持续时间,毫秒
|
||||
let startTime = null;
|
||||
|
||||
function animateScroll(currentTime) {
|
||||
if (!startTime) startTime = currentTime;
|
||||
const timeElapsed = currentTime - startTime;
|
||||
container.scrollTo({
|
||||
top: startScrollTop + distance - elementHeight - 120,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
|
||||
if (timeElapsed < duration) {
|
||||
requestAnimationFrame(animateScroll);
|
||||
}
|
||||
}
|
||||
|
||||
requestAnimationFrame(animateScroll);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 初始化时根据语言选择显示内容
|
||||
this.changeLanguage(this.selectedLanguage)
|
||||
|
||||
},
|
||||
methods: {
|
||||
// 动态获取选择框的标签
|
||||
getSelectLabel(lang) {
|
||||
if (lang.srclang == 'zh-CN') {
|
||||
return lang.label;
|
||||
}
|
||||
return `${lang.name} (${lang.label})`;
|
||||
},
|
||||
formatTime (time) {
|
||||
// 格式化时间为HH:MM:SS,如01:00:00
|
||||
const hours = Math.floor(time / 3600);
|
||||
const minutes = Math.floor((time % 3600) / 60);
|
||||
const seconds = Math.floor(time % 60);
|
||||
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
|
||||
},
|
||||
// 跳转到指定时间点
|
||||
scrollToTime(time) {
|
||||
// console.log('scrollToTime', time , this.blobId, localStorage.getItem('videoProgressData'), this.duration)
|
||||
if(!this.isDrag && this.duration){
|
||||
var t = localStorage.getItem('videoProgressData')
|
||||
var arr = t&&JSON.parse(t) || {}
|
||||
if(arr[this.blobId] < time/this.duration || !arr[this.blobId]){
|
||||
return
|
||||
}
|
||||
}
|
||||
console.log('跳转到时间点:', time);
|
||||
this.$emit('changeCurrentTime', time);
|
||||
// 设置用户滚动状态,避免自动滚动干扰
|
||||
this.isUserScrolling = true;
|
||||
if (this.userScrollTimeout) {
|
||||
clearTimeout(this.userScrollTimeout);
|
||||
}
|
||||
this.userScrollTimeout = setTimeout(() => {
|
||||
this.isUserScrolling = false;
|
||||
}, 3000);
|
||||
},
|
||||
// 处理用户滚动事件
|
||||
handleUserScroll: function() {
|
||||
this.isUserScrolling = true;
|
||||
|
||||
// 清除之前的计时器
|
||||
if (this.userScrollTimeout) {
|
||||
clearTimeout(this.userScrollTimeout);
|
||||
}
|
||||
|
||||
// 设置新的计时器,3秒后恢复自动滚动
|
||||
this.userScrollTimeout = setTimeout(() => {
|
||||
this.isUserScrolling = false;
|
||||
}, 3000);
|
||||
},
|
||||
|
||||
searchContent () {
|
||||
// 搜索功能实现
|
||||
if (!this.searchKeyword.trim()) {
|
||||
// 如果搜索关键词为空,显示所有内容
|
||||
this.contentList = this.originalContentList.map(item => ({ ...item }));
|
||||
return;
|
||||
}
|
||||
|
||||
const keyword = this.searchKeyword.trim();
|
||||
// 过滤包含关键词的内容
|
||||
const filteredList = this.originalContentList.filter(item =>
|
||||
item.text.includes(keyword)
|
||||
);
|
||||
|
||||
if (filteredList.length === 0) {
|
||||
// 如果没有搜索到内容,显示提示
|
||||
this.$message({
|
||||
message: '未找到相关内容',
|
||||
type: 'info'
|
||||
});
|
||||
this.contentList = this.originalContentList.map(item => ({ ...item }));
|
||||
} else {
|
||||
// 对搜索到的内容进行关键词高亮处理
|
||||
this.contentList = filteredList.map(item => ({
|
||||
...item,
|
||||
highlightedContent: this.highlightKeyword(item.text, keyword)
|
||||
}));
|
||||
console.log(this.contentList)
|
||||
}
|
||||
},
|
||||
highlightKeyword(content, keyword) {
|
||||
// 对关键词进行转义,防止正则表达式特殊字符的影响
|
||||
const escapedKeyword = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
// 使用正则表达式全局匹配关键词并添加高亮标记
|
||||
const regex = new RegExp(`(${escapedKeyword})`, 'gi');
|
||||
return content.replace(regex, '<span style="color: rgba(6, 125, 255, 1); background: rgba(6, 125, 255, 0.1);">$1</span>');
|
||||
},
|
||||
changeLanguage (event) {
|
||||
// this.selectedLanguage = event
|
||||
this.selectableLang.forEach(item => {
|
||||
if (item.srclang === event) {
|
||||
console.log('当前语言:', item)
|
||||
if (!item.originalContentList) {
|
||||
try {
|
||||
item.originalContentList = JSON.parse(item.subtitleData)
|
||||
} catch (error) {
|
||||
console.error('ai文稿格式有问题!')
|
||||
}
|
||||
}
|
||||
this.originalContentList = item.originalContentList || []
|
||||
// 初始化时显示所有内容
|
||||
this.contentList = this.originalContentList.map(item => ({ ...item }));
|
||||
console.log('ai文稿数据:', this.originalContentList)
|
||||
}
|
||||
})
|
||||
console.log('切换语言:', event)
|
||||
},
|
||||
handleInputChange() {
|
||||
// 当输入框内容变化时,如果为空则重置显示所有内容
|
||||
if (!this.searchKeyword.trim()) {
|
||||
this.contentList = this.originalContentList.map(item => ({ ...item }));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ai-script {
|
||||
padding: 15px 0;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.search-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin: 0 20px 15px 20px;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
::v-deep .el-input__inner {
|
||||
border-color: #2688FF;
|
||||
}
|
||||
|
||||
::v-deep .el-input__inner:focus {
|
||||
border-color: #1a6fe0;
|
||||
box-shadow: 0 0 0 2px rgba(38, 136, 255, 0.2);
|
||||
}
|
||||
|
||||
::v-deep .el-input__prefix {
|
||||
left: 5px;
|
||||
}
|
||||
|
||||
::v-deep .el-input__icon {
|
||||
color: #2688FF;
|
||||
}
|
||||
|
||||
.language-selector {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.language-label {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.language-select {
|
||||
width: 90px;
|
||||
}
|
||||
|
||||
|
||||
.content-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
max-height: 410px;
|
||||
overflow-y: auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.content-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 7px;
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
padding: 5px 0;
|
||||
.timestamp-text{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
border-radius: 12px;
|
||||
padding: 2px 12px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
}
|
||||
|
||||
.content-text {
|
||||
cursor: pointer;
|
||||
line-height: 1.6;
|
||||
font-size: 14px;
|
||||
color: rgba(102, 102, 102, 1);
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
background: rgba(250, 250, 250, 1);
|
||||
line-height: 22px;
|
||||
letter-spacing: 0.28px;
|
||||
}
|
||||
|
||||
.active {
|
||||
.timestamp-text{
|
||||
color: rgba(6, 125, 255, 1);
|
||||
background: rgba(6, 125, 255, 0.1);
|
||||
}
|
||||
.content-text{
|
||||
border: 1px solid rgba(116, 182, 255, 1);
|
||||
box-shadow: 0px 0px 7px 0px rgba(6, 125, 255, 0.24);
|
||||
background: rgba(250, 250, 250, 1);
|
||||
}
|
||||
}
|
||||
:deep(.el-card__body) {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
:deep(.el-card.is-hover-shadow:focus, .el-card.is-hover-shadow:hover) {
|
||||
box-shadow: 0 2px 12px 0 rgba(38, 136, 255, 0.2);
|
||||
}
|
||||
:deep(.el-input__inner) {
|
||||
border-radius: 4px!important;
|
||||
}
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.search-container {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.language-selector {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
.no-data{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding-top: 60px;
|
||||
color: rgba(0, 0, 0, 0.34);
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 21px;
|
||||
letter-spacing: 0.26px;
|
||||
text-align: center;
|
||||
img{
|
||||
width: 96px;
|
||||
height: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -208,6 +208,44 @@
|
||||
placeholder="请尽量填写课程简介,用于列表中显示,可以让用户更容易了解课程信息">
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<!-- ai播放器相关 -->
|
||||
<el-form-item v-show="aiPermission" label="AI设置">
|
||||
<div style="margin-top: 7px;">
|
||||
<div style="display: flex; align-items: center;gap: 5px;">
|
||||
<el-switch v-model="courseInfo.aiSet" :active-value="1" :inactive-value="0"></el-switch>
|
||||
<el-tooltip class="item" effect="dark" :content="aiSetTip" placement="top">
|
||||
<i class="el-icon-question" style="margin-left: 5px; color: #909399; cursor: pointer;"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div v-show="courseInfo.aiSet==1" style="margin-left: -20px;">
|
||||
<div style="display: flex; justify-content: space-between;;align-items: center;gap: 5px;margin: 10px 0;">
|
||||
<div style="display: flex; align-items: center;gap: 5px;">
|
||||
<span>AI摘要</span>
|
||||
<el-switch v-model="courseInfo.aiAbstract" :active-value="1" :inactive-value="0"></el-switch>
|
||||
<el-tooltip class="item" effect="dark" :content="aiAbstractTip" placement="top">
|
||||
<i class="el-icon-question" style="margin-left: 5px; color: #909399; cursor: pointer;"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center;gap: 5px;">
|
||||
<span>AI文稿</span>
|
||||
<el-switch v-model="courseInfo.aiDraft" :active-value="1" :inactive-value="0"></el-switch>
|
||||
<el-tooltip class="item" effect="dark" :content="aiDraftTip" placement="top">
|
||||
<i class="el-icon-question" style="margin-left: 5px; color: #909399; cursor: pointer;"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center;gap: 5px;margin: 10px 0;margin-left: -30px;">
|
||||
<span>AI翻译语种</span>
|
||||
<el-select v-model="courseInfo.languageCode" placeholder="请选择" multiple filterable style="width: 240px;">
|
||||
<el-option v-for="item in selectAllLang" :key="item.key" :label="item.label" :value="item.srclang"> </el-option>
|
||||
</el-select>
|
||||
<el-tooltip class="item" effect="dark" :content="aiTranslateTip" placement="top">
|
||||
<i class="el-icon-question" style="margin-left: 5px; color: #909399; cursor: pointer;"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="14">
|
||||
<div @click="checkCourse"><weikeContent ref="weikeContent" :reset="weikeReset" :contents="contentInfo.list" :course="courseInfo" min-height="644px"></weikeContent></div>
|
||||
@@ -390,6 +428,44 @@
|
||||
placeholder="请尽量填写课程简介,用于列表中显示,可以让用户更容易了解课程信息">
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<!-- ai播放器相关 -->
|
||||
<el-form-item v-show="aiPermission" label="AI设置">
|
||||
<div style="margin-top: 7px;">
|
||||
<div style="display: flex; align-items: center;gap: 5px;">
|
||||
<el-switch v-model="courseInfo.aiSet" :active-value="1" :inactive-value="0"></el-switch>
|
||||
<el-tooltip class="item" effect="dark" :content="aiSetTip" placement="top">
|
||||
<i class="el-icon-question" style="margin-left: 5px; color: #909399; cursor: pointer;"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div v-show="courseInfo.aiSet==1" style="margin-left: -20px;">
|
||||
<div style="display: flex;align-items: center;gap: 80px;margin: 20px 0;">
|
||||
<div style="display: flex; align-items: center;gap: 5px;">
|
||||
<span>AI摘要</span>
|
||||
<el-switch v-model="courseInfo.aiAbstract" :active-value="1" :inactive-value="0"></el-switch>
|
||||
<el-tooltip class="item" effect="dark" :content="aiAbstractTip" placement="top">
|
||||
<i class="el-icon-question" style="margin-left: 5px; color: #909399; cursor: pointer;"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center;gap: 5px;">
|
||||
<span>AI文稿</span>
|
||||
<el-switch v-model="courseInfo.aiDraft" :active-value="1" :inactive-value="0"></el-switch>
|
||||
<el-tooltip class="item" effect="dark" :content="aiDraftTip" placement="top">
|
||||
<i class="el-icon-question" style="margin-left: 5px; color: #909399; cursor: pointer;"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center;gap: 5px;margin: 20px 0;margin-left: -30px;">
|
||||
<span>AI翻译语种</span>
|
||||
<el-select v-model="courseInfo.languageCode" placeholder="请选择" multiple filterable style="flex:1">
|
||||
<el-option v-for="item in selectAllLang" :key="item.key" :label="item.label" :value="item.srclang"> </el-option>
|
||||
</el-select>
|
||||
<el-tooltip class="item" effect="dark" :content="aiTranslateTip" placement="top">
|
||||
<i class="el-icon-question" style="margin-left: 5px; color: #909399; cursor: pointer;"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<!-- v-if="!weike.onlyRequired" -->
|
||||
<!-- <el-form-item label="课程描述">
|
||||
<WxEditor v-model="courseInfo.overview" :minHeight="50"></WxEditor>
|
||||
@@ -600,13 +676,23 @@ export default {
|
||||
highlightStyle: {},
|
||||
guidanceElements: [],
|
||||
isFirstCreate: false, // 标记是否为首次创建
|
||||
selectedOrg: {
|
||||
orgId: null,
|
||||
name: ''
|
||||
},
|
||||
aiPermission: false, // ai播放器权限
|
||||
aiSetTip: '是否将课程进行AI处理', //提示信息
|
||||
aiAbstractTip: '一键提炼课程视频核心要点,助力学员课前高效掌握重点,快速筛选学习资源', // 提示信息
|
||||
aiDraftTip: '分段展示视频内容并精准同步时间轴,实现视频进度与文稿双向定位,学习内容触手可及', //提示信息
|
||||
aiTranslateTip: '智能转换视频字幕与语音为多语种,支持全球学员按需切换语言,打破学习边界', // 提示信息
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getSceneData();
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['resOwnerMap', 'sysTypeMap','userInfo','identity']),
|
||||
// ai播放器相关
|
||||
...mapGetters(['resOwnerMap', 'sysTypeMap','userInfo','identity', 'selectAllLang']),
|
||||
catalogTree() {
|
||||
let treeList = [];
|
||||
let $this = this;
|
||||
@@ -632,6 +718,8 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// ai播放器相关
|
||||
this.getAiPermission();
|
||||
let extendFlag=this.$route.query.f; //是否是管理端过来的
|
||||
this.extendRefId=this.$route.query.refId;
|
||||
this.extendRefType=this.$route.query.refType;
|
||||
@@ -656,6 +744,16 @@ export default {
|
||||
handleTagHelp(){
|
||||
this.checkAndShowGuidance();
|
||||
},
|
||||
getAiPermission() {
|
||||
apiCourse.listByUser({}).then(res => {
|
||||
console.log('res', res);
|
||||
if(res.code === 200){
|
||||
let index = res.data.findIndex(item => item.permissionCode === 'KjbAiSetCode');
|
||||
this.aiPermission = index !== -1;
|
||||
console.log('index', index, this.aiPermission);
|
||||
}
|
||||
})
|
||||
},
|
||||
// 关键字的更改
|
||||
changeKeywords(option){
|
||||
if(option.target.value){
|
||||
@@ -895,7 +993,7 @@ export default {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.initAiData();
|
||||
} else {
|
||||
//console.log(editData,'editData');
|
||||
this.weikeReset = editData.id;
|
||||
@@ -1082,6 +1180,30 @@ export default {
|
||||
this.courseCoverurl = '';
|
||||
this.courseInfo.coverImg = '';
|
||||
},
|
||||
// ai播放器相关
|
||||
// 初始化ai数据
|
||||
initAiData() {
|
||||
if (!this.aiPermission) {
|
||||
// 如果ai设置为空则给默认值 - 会看成新增状态
|
||||
if(this.courseInfo.aiSet === null || this.courseInfo.aiSet === '' || this.courseInfo.aiSet === undefined){
|
||||
this.courseInfo.languageCode = ['zh-CN', 'en-US'];
|
||||
}
|
||||
return;
|
||||
}
|
||||
// 如果ai设置为空则给默认值 - 会看成新增状态
|
||||
if(this.courseInfo.aiSet === null || this.courseInfo.aiSet === '' || this.courseInfo.aiSet === undefined){
|
||||
this.courseInfo.isAddAI = 1; //暂时是否是新增
|
||||
this.courseInfo.aiSet = 1;
|
||||
this.courseInfo.aiAbstract = 1;
|
||||
this.courseInfo.aiDraft = 1;
|
||||
this.courseInfo.aiTranslate = 1;
|
||||
this.courseInfo.languageStatus = 1;
|
||||
this.courseInfo.languageCode = ['zh-CN', 'en-US'];
|
||||
} else {
|
||||
// 获取ai设置信息
|
||||
this.courseInfo.isAddAI = 0;
|
||||
}
|
||||
},
|
||||
//获取课程信息
|
||||
async getDetail(id) {
|
||||
this.curCourseId = id;
|
||||
@@ -1105,7 +1227,8 @@ export default {
|
||||
this.contentInfo.list = result.contents;
|
||||
this.sectionInfo.list = result.sections;
|
||||
this.courseTeachers = result.teachers; //课程的老师信息
|
||||
|
||||
// ai播放器相关
|
||||
this.initAiData()
|
||||
if(!this.courseInfo.orgId){
|
||||
//根据课程创建者获取机构id
|
||||
apiUser.getOrgSimpleByUserId(result.course.sysCreateAid).then(ors=>{
|
||||
|
||||
@@ -119,7 +119,35 @@
|
||||
</div>
|
||||
<div class="player-time">{{ currentTimeFormat }} / {{ fullTimeFormat }}</div>
|
||||
</div>
|
||||
<!-- ai播放器相关 -->
|
||||
<div class="player-controls-bottom-right">
|
||||
<div v-if="isAiTranslate" class="player-controls-btn box-aiTranslate">
|
||||
<div v-show="isSubtitle" class="player-controls-btn cursor-pointer btn-speed">
|
||||
<span>{{!currentLang ? 'AI翻译' : currentLangLabel}}</span>
|
||||
<div class="speed-control">
|
||||
<ul class="speed-control-list">
|
||||
<li
|
||||
v-for="item in selectableLang"
|
||||
:key="item.srclang"
|
||||
@click="changeLang(item)"
|
||||
:data-value="item.srclang"
|
||||
class="one-line-ellipsis"
|
||||
:title="item.label"
|
||||
:class="{'current': currentLang === item.srclang}"
|
||||
>{{ item.label }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="isSubtitle" style="margin-top: -3px;">|</div>
|
||||
<div class="player-controls-btn" style="display: flex;gap: 0.3rem;align-items: center;">
|
||||
<span>字幕</span>
|
||||
<el-switch
|
||||
@change="toggleSubtitle"
|
||||
v-model="isSubtitle">
|
||||
</el-switch>
|
||||
</div>
|
||||
<div style="margin-top: -3px;">|</div>
|
||||
</div>
|
||||
<div class="player-controls-btn cursor-pointer btn-speed">
|
||||
<span>{{currentSpeed === 1 ? '倍速' : `${currentSpeed}x`}}</span>
|
||||
<div class="speed-control">
|
||||
@@ -224,6 +252,7 @@
|
||||
import volumeBar from "@/components/VideoPlayer/volume-bar.vue";
|
||||
import progressBar from "@/components/VideoPlayer/progress-bar.vue";
|
||||
import playerBarrageScreen from "@/components/VideoPlayer/player-barrage-screen.vue";
|
||||
import { mapGetters, mapMutations } from 'vuex';
|
||||
|
||||
export default {
|
||||
name: "barrage-videoplayer",
|
||||
@@ -301,12 +330,24 @@ export default {
|
||||
fullTimeFormat: "00:00:00", // 视频总长度的文字
|
||||
barrageTimelineStart: 0, // 弹幕时间轴的起始时间点(手动调整进度条触发更新)
|
||||
isInit:false, // 是否初始化过
|
||||
// ai播放器相关
|
||||
isSubtitle: true, // 是否开启字幕
|
||||
currentLangLabel:'', // 当前字幕语言
|
||||
};
|
||||
},
|
||||
// ai播放器相关
|
||||
computed: {
|
||||
...mapGetters(['selectableLang','currentLang','courseInfo']),
|
||||
isAiTranslate () {
|
||||
return this.courseInfo?.aiSet == 1 && this.courseInfo?.aiTranslate == 1 && this.selectableLang && this.selectableLang.length > 0;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
||||
// ai播放器相关
|
||||
this.SET_currentLang('');
|
||||
},
|
||||
mounted() {
|
||||
console.log('---',this.isAiTranslate,this.courseInfo,'courseInfo');
|
||||
this.videoDom = this.$refs.video;
|
||||
this.videoDom.focus({preventScroll: true});
|
||||
let speedValue=localStorage.getItem('boe_video_speed');
|
||||
@@ -317,7 +358,8 @@ export default {
|
||||
}
|
||||
|
||||
setInterval(() => {
|
||||
console.log('当前状态:',this.currentProgress,this.isDrag,this.videoDom.currentTime , this.videoDom.duration)
|
||||
this.SET_duration(this.videoDom.duration);
|
||||
console.log('当前状态:',localStorage.getItem('videoProgressData'),this.currentProgress,this.isDrag,this.videoDom.currentTime , this.videoDom.duration)
|
||||
// 视频播放时本地记录视频实时播放时长,视频设置了禁止拖动时执行
|
||||
if(!this.isDrag){
|
||||
var time = localStorage.getItem('videoProgressData')
|
||||
@@ -372,6 +414,7 @@ export default {
|
||||
//if()
|
||||
//console.log(this.videoDom.readyState,'this.videoDom.readyState');
|
||||
}, 1000);
|
||||
|
||||
// 视频dom监听器,用于控制鼠标的显示
|
||||
this.videoDom.addEventListener("mousemove", () => {
|
||||
this.isCursorStatic = false;
|
||||
@@ -411,6 +454,13 @@ export default {
|
||||
// });
|
||||
},
|
||||
methods: {
|
||||
// ai播放器相关
|
||||
...mapMutations({
|
||||
SET_currentLang: 'video/SET_currentLang',
|
||||
SET_currentTime: 'video/SET_currentTime',
|
||||
SET_selectableLang: 'video/SET_selectableLang',
|
||||
SET_duration: 'video/SET_duration',
|
||||
}),
|
||||
//当视频由于需要缓冲下一帧而停止,解决一直计时的问题
|
||||
onWaiting(){
|
||||
console.log('触发了onWairing');
|
||||
@@ -624,6 +674,8 @@ export default {
|
||||
},
|
||||
onAudioTimeUpdate() {
|
||||
const currentTime = this.$refs.video.currentTime;
|
||||
// ai播放器相关
|
||||
this.SET_currentTime(currentTime)
|
||||
this.$emit('onTimeUpdate', currentTime);
|
||||
},
|
||||
/**
|
||||
@@ -641,9 +693,77 @@ export default {
|
||||
this.$emit('onFullscreen',false);//全屏
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
/** ai播放器相关
|
||||
* 切换字幕
|
||||
*/
|
||||
toggleSubtitle(value) {
|
||||
if (this.videoDom && this.videoDom.textTracks && this.videoDom.textTracks.length >0) {
|
||||
if (!value) {
|
||||
// 关闭字幕
|
||||
this.videoDom.textTracks[this.videoDom.textTracks.length - 1].mode = 'hidden';
|
||||
} else {
|
||||
// 打开字幕
|
||||
this.videoDom.textTracks[this.videoDom.textTracks.length - 1].mode = 'showing';
|
||||
}
|
||||
}
|
||||
},
|
||||
/** ai播放器相关
|
||||
* 切换字幕语言
|
||||
*/
|
||||
changeLang(item = {}) {
|
||||
this.SET_currentLang(item.srclang);
|
||||
this.currentLangLabel = item.label;
|
||||
console.log("changeLang",item);
|
||||
// 先移除所有字幕轨道
|
||||
Array.from(this.videoDom.querySelectorAll('track')).forEach(t => t.remove());
|
||||
if(!item.vttContent){
|
||||
console.log("字幕内容为空!")
|
||||
return;
|
||||
}
|
||||
if(!item.srcUrl){
|
||||
try{
|
||||
const blob = new Blob([item.vttContent], { type: 'text/vtt' });
|
||||
item.srcUrl = URL.createObjectURL(blob);
|
||||
}catch(e){
|
||||
console.log("字幕格式错误",e)
|
||||
}
|
||||
}
|
||||
const trackEl = document.createElement('track');
|
||||
trackEl.kind = 'subtitles';
|
||||
trackEl.srclang = item.srclang;
|
||||
trackEl.label = item.label;
|
||||
trackEl.src = item.srcUrl;
|
||||
trackEl.default = true; // 确保字幕默认启用
|
||||
|
||||
// 使用箭头函数保持this上下文
|
||||
trackEl.addEventListener('load', () => {
|
||||
console.log('字幕加载成功!');
|
||||
// console.log('#########Track cues:', trackEl.track.cues);
|
||||
});
|
||||
|
||||
trackEl.addEventListener('error', () => {
|
||||
console.error('字幕加载失败!');
|
||||
});
|
||||
|
||||
// 确保视频已加载到可添加轨道的状态
|
||||
if (this.videoDom.readyState >= 1) {
|
||||
this.videoDom.appendChild(trackEl);
|
||||
this.videoDom.textTracks[this.videoDom.textTracks.length - 1].mode = 'showing';
|
||||
} else {
|
||||
this.videoDom.addEventListener('loadedmetadata', () => {
|
||||
this.videoDom.appendChild(trackEl);
|
||||
this.videoDom.textTracks[this.videoDom.textTracks.length - 1].mode = 'showing';
|
||||
}, { once: true });
|
||||
}
|
||||
},
|
||||
seekToTime(time) {
|
||||
if (!this.videoDom) return;
|
||||
this.videoDom.currentTime = time + 0.01;
|
||||
this.isPlaying = true;
|
||||
this.videoDom.play();
|
||||
},
|
||||
|
||||
},
|
||||
watch: {
|
||||
currentVolume: function () {
|
||||
@@ -668,9 +788,37 @@ export default {
|
||||
// }
|
||||
// },
|
||||
src: function () {
|
||||
// 当视频地址变更时,重载视频
|
||||
// 当视频地址变更时,先重置字幕再重载视频
|
||||
this.isPlaying = false;
|
||||
// 重置字幕相关状态
|
||||
this.SET_currentLang('');
|
||||
this.currentLangLabel = '';
|
||||
|
||||
// 移除所有现有字幕轨道元素
|
||||
Array.from(this.videoDom.querySelectorAll('track')).forEach(t => t.remove());
|
||||
|
||||
// 更彻底地清除字幕:重置所有textTracks
|
||||
Array.from(this.videoDom.textTracks).forEach(track => {
|
||||
track.mode = 'hidden';
|
||||
// 尝试移除所有cues(浏览器支持的话)
|
||||
if (track.cues) {
|
||||
while (track.cues.length > 0) {
|
||||
track.cues.remove(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 重载视频
|
||||
this.videoDom.load();
|
||||
this.isPlaying = false
|
||||
|
||||
// 如果有默认语言且支持AI翻译,重新设置字幕
|
||||
// if (this.isAiTranslate && this.selectableLang && this.selectableLang.length > 0) {
|
||||
// // 找到默认语言或第一个可用语言
|
||||
// const defaultLang = this.selectableLang.find(lang => lang.srclang === 'zh-CN') || this.selectableLang[0];
|
||||
// if (defaultLang) {
|
||||
// this.changeLang(defaultLang);
|
||||
// }
|
||||
// }
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -907,6 +1055,12 @@ export default {
|
||||
color: #fff;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.box-aiTranslate{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
@media (device-width: 100vw) {
|
||||
.player-controls-btn .player-controls-icon {
|
||||
/* height: 26px; */
|
||||
@@ -918,4 +1072,22 @@ export default {
|
||||
height: 100px;
|
||||
}
|
||||
}
|
||||
video::cue {
|
||||
/* color: #fff; */
|
||||
/* background-color: transparent; */
|
||||
/* font-size: 0.85em; */
|
||||
/* font-family: 'Arial', sans-serif;
|
||||
-webkit-text-stroke: 4px #000;
|
||||
text-stroke: 4px #000; */
|
||||
|
||||
/* text-shadow:
|
||||
2px 2px 0 #000,
|
||||
-2px 2px 0 #000,
|
||||
2px -2px 0 #000,
|
||||
-2px -2px 0 #000,
|
||||
0 2px 0 #000,
|
||||
2px 0 0 #000,
|
||||
0 -2px 0 #000,
|
||||
-2px 0 0 #000; */
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -117,6 +117,8 @@ export const iframes=[
|
||||
{title:'嵌入测试', path:'/iframe/index',hidden:false,component:'portal/iframe'},
|
||||
{title:'课件管理', path:'/iframe/course/coursewares',hidden:false,component:'course/Courseware'},
|
||||
{title:'课程管理', path:'/iframe/course/manages',hidden:false,component:'course/ManageList'},
|
||||
{title:'ai摘要', path:'/iframe/course/aiAbstract',hidden:false,component:'course/aiSet/aiAbstract'},
|
||||
{title:'ai翻译', path:'/iframe/course/aiTranslate',hidden:false,component:'course/aiSet/aiTranslate'},
|
||||
{title:'考试试题管理', path:'/iframe/exam/questions',hidden:false,component:'exam/Question'},
|
||||
{title:'查看答卷', path:'/iframe/exam/viewanswer',hidden:false,component:'exam/viewAnswer'},
|
||||
{title:'考试试卷管理', path:'/iframe/exam/papers',hidden:false,component:'exam/TestPaper'},
|
||||
|
||||
@@ -28,5 +28,12 @@ const getters = {
|
||||
studyTaskCount:state => state.user.studyTaskCount,
|
||||
praisesUnicom:state =>state.pdf.praisesUnicom,
|
||||
favoritesUnicom:state =>state.pdf.favoritesUnicom,
|
||||
// ai播放器相关
|
||||
selectAllLang:state => state.video.selectAllLang,
|
||||
selectableLang:state => state.video.selectableLang,
|
||||
currentLang:state => state.video.currentLang,
|
||||
currentTime:state => state.video.currentTime,
|
||||
courseInfo:state => state.video.courseInfo,
|
||||
duration:state => state.video.duration,
|
||||
}
|
||||
export default getters
|
||||
|
||||
@@ -12,6 +12,7 @@ import resOwner from './modules/resOwner'
|
||||
import majorType from './modules/majorType'
|
||||
import orgDomain from './modules/orgDomain'
|
||||
import pdf from './modules/pdf'
|
||||
import video from './modules/video' // ai播放器相关
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
@@ -27,7 +28,8 @@ const store = new Vuex.Store({
|
||||
resOwner,
|
||||
majorType,
|
||||
orgDomain,
|
||||
pdf
|
||||
pdf,
|
||||
video
|
||||
},
|
||||
getters
|
||||
})
|
||||
|
||||
171
src/store/modules/video.js
Normal file
@@ -0,0 +1,171 @@
|
||||
// ai播放器相关
|
||||
|
||||
/**
|
||||
*
|
||||
selectAllLang: [
|
||||
{
|
||||
key: 'ZH_CN',
|
||||
srclang: 'zh-CN',
|
||||
label: '中文',
|
||||
name: '中文',
|
||||
},
|
||||
{
|
||||
key: 'EN_US',
|
||||
srclang: 'en-US',
|
||||
label: '英语',
|
||||
name: 'English',
|
||||
},
|
||||
{
|
||||
key: 'JA_JP',
|
||||
srclang: 'ja-JP',
|
||||
label: '日语',
|
||||
name: '日本語',
|
||||
},
|
||||
{
|
||||
key: 'KO_KR',
|
||||
srclang: 'ko-KR',
|
||||
label: '韩语',
|
||||
name: '한국어',
|
||||
},
|
||||
{
|
||||
key: 'FR_FR',
|
||||
srclang: 'fr-FR',
|
||||
label: '法语',
|
||||
name: 'français',
|
||||
},
|
||||
{
|
||||
key: 'DE_DE',
|
||||
srclang: 'de-DE',
|
||||
label: '德语',
|
||||
name: 'Deutsch',
|
||||
},
|
||||
{
|
||||
key: 'ES_ES',
|
||||
srclang: 'es-ES',
|
||||
label: '西班牙语',
|
||||
name: 'español',
|
||||
},
|
||||
{
|
||||
key: 'RU_RU',
|
||||
srclang: 'ru-RU',
|
||||
label: '俄语',
|
||||
name: 'русский',
|
||||
},
|
||||
{
|
||||
key: 'PT_BR',
|
||||
srclang: 'pt-BR',
|
||||
label: '葡萄牙语',
|
||||
name: 'português',
|
||||
},
|
||||
{
|
||||
key: 'IT_IT',
|
||||
srclang: 'it-IT',
|
||||
label: '意大利语',
|
||||
name: 'italiano',
|
||||
},
|
||||
{
|
||||
key: 'AR_SA',
|
||||
srclang: 'ar-SA',
|
||||
label: '阿拉伯语',
|
||||
name: 'العربية',
|
||||
},
|
||||
{
|
||||
key: 'TH_TH',
|
||||
srclang: 'th-TH',
|
||||
label: '泰语',
|
||||
name: 'ไทย',
|
||||
},
|
||||
{
|
||||
key: 'VI_VN',
|
||||
srclang: 'vi-VN',
|
||||
label: '越南语',
|
||||
name: 'tiếng Việt',
|
||||
},
|
||||
{
|
||||
key: 'ID_ID',
|
||||
srclang: 'id-ID',
|
||||
label: '印度尼西亚语',
|
||||
name: 'Bahasa Indonesia',
|
||||
},
|
||||
{
|
||||
key: 'HI_IN',
|
||||
srclang: 'hi-IN',
|
||||
label: '印地语',
|
||||
name: 'हिन्दी',
|
||||
}
|
||||
], // 全部语言列表
|
||||
*/
|
||||
|
||||
const state = {
|
||||
selectAllLang: [
|
||||
{
|
||||
key: 'ZH_CN',
|
||||
srclang: 'zh-CN',
|
||||
label: '中文',
|
||||
name: '中文',
|
||||
},
|
||||
{
|
||||
key: 'EN_US',
|
||||
srclang: 'en-US',
|
||||
label: '英语',
|
||||
name: 'English',
|
||||
},
|
||||
{
|
||||
key: 'VI_VN',
|
||||
srclang: 'vi-VN',
|
||||
label: '越南语',
|
||||
name: 'tiếng Việt',
|
||||
},
|
||||
{
|
||||
key: 'ES_ES',
|
||||
srclang: 'es-ES',
|
||||
label: '西班牙语',
|
||||
name: 'español',
|
||||
},
|
||||
], // 一期语言列表
|
||||
selectableLang: [], // 可选语言列表+字幕信息
|
||||
currentLang: '', // 当前选中语言
|
||||
currentTime: -1, // 当前视频时间
|
||||
courseInfo: {},
|
||||
duration: 0, // 视频时长
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
SET_currentLang: (state, lang) => {
|
||||
state.currentLang = lang
|
||||
},
|
||||
SET_selectableLang: (state, list = []) => {
|
||||
let selectableLang = []
|
||||
list.forEach(item => {
|
||||
let selectItem = state.selectAllLang.find(selectItem => selectItem.srclang === item.language)
|
||||
if (selectItem) {
|
||||
selectableLang.push({
|
||||
...item,
|
||||
...selectItem,
|
||||
})
|
||||
}
|
||||
})
|
||||
state.selectableLang = selectableLang
|
||||
},
|
||||
SET_currentTime: (state, time) => {
|
||||
state.currentTime = time
|
||||
},
|
||||
SET_courseInfo: (state, info) => {
|
||||
state.courseInfo = info
|
||||
},
|
||||
SET_duration: (state, duration) => {
|
||||
state.duration = duration
|
||||
},
|
||||
}
|
||||
|
||||
const actions = {
|
||||
|
||||
}
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state,
|
||||
mutations,
|
||||
actions
|
||||
}
|
||||
|
||||
@@ -40,6 +40,9 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 给 body 添加特定类名,用于限制样式作用域
|
||||
document.body.classList.add('contributor-conference-colleage-active');
|
||||
|
||||
let list = colleage.colleage;
|
||||
list.forEach(element => {
|
||||
let temp = element.org.split('/')
|
||||
@@ -47,39 +50,44 @@ export default {
|
||||
});
|
||||
this.colleageList = list;
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时移除类名
|
||||
document.body.classList.remove('contributor-conference-colleage-active');
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang='scss'>
|
||||
// 使用 body.contributor-conference-colleage-active 类名限制样式只在该页面生效
|
||||
@media screen and (min-width: 1440px) and (max-width: 1600px){
|
||||
body{
|
||||
body.contributor-conference-colleage-active{
|
||||
zoom: 79%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-colleage-active #bg{
|
||||
width: 1900px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1366px) and (max-width: 1440px){
|
||||
body{
|
||||
body.contributor-conference-colleage-active{
|
||||
zoom: 74%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-colleage-active #bg{
|
||||
width: 1869px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1280px) and (max-width: 1366px){
|
||||
body{
|
||||
body.contributor-conference-colleage-active{
|
||||
zoom: 72%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-colleage-active #bg{
|
||||
width: 1874px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1280px){
|
||||
body{
|
||||
body.contributor-conference-colleage-active{
|
||||
zoom: 67%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-colleage-active #bg{
|
||||
width: 1885px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,41 +49,49 @@ export default {
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// 给 body 添加特定类名,用于限制样式作用域
|
||||
document.body.classList.add('contributor-conference-course-active');
|
||||
|
||||
this.courseList = course.course;
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时移除类名
|
||||
document.body.classList.remove('contributor-conference-course-active');
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang='scss'>
|
||||
// 使用 body.contributor-conference-course-active 类名限制样式只在该页面生效
|
||||
@media screen and (min-width: 1440px) and (max-width: 1600px){
|
||||
body{
|
||||
body.contributor-conference-course-active{
|
||||
zoom: 79%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-course-active #bg{
|
||||
width: 1900px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1366px) and (max-width: 1440px){
|
||||
body{
|
||||
body.contributor-conference-course-active{
|
||||
zoom: 74%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-course-active #bg{
|
||||
width: 1869px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1280px) and (max-width: 1366px){
|
||||
body{
|
||||
body.contributor-conference-course-active{
|
||||
zoom: 72%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-course-active #bg{
|
||||
width: 1874px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1280px){
|
||||
body{
|
||||
body.contributor-conference-course-active{
|
||||
zoom: 67%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-course-active #bg{
|
||||
width: 1885px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,6 +98,9 @@ export default {
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
// 给 body 添加特定类名,用于限制样式作用域
|
||||
document.body.classList.add('course-detail-page-active');
|
||||
|
||||
this.id = this.$route.params.id
|
||||
let list = course.course;
|
||||
let pid = this.id
|
||||
@@ -124,45 +127,50 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时移除类名
|
||||
document.body.classList.remove('course-detail-page-active');
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang='scss'>
|
||||
// 使用 body.course-detail-page-active 类名限制样式只在该页面生效
|
||||
@media screen and (min-width: 1440px) and (max-width: 1600px){
|
||||
body{
|
||||
body.course-detail-page-active{
|
||||
zoom: 79%;
|
||||
}
|
||||
#bg{
|
||||
body.course-detail-page-active #bg{
|
||||
width: 1900px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1366px) and (max-width: 1440px){
|
||||
body{
|
||||
body.course-detail-page-active{
|
||||
zoom: 74%;
|
||||
}
|
||||
#bg{
|
||||
body.course-detail-page-active #bg{
|
||||
width: 1869px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1280px) and (max-width: 1366px){
|
||||
body{
|
||||
body.course-detail-page-active{
|
||||
zoom: 72%;
|
||||
}
|
||||
#bg{
|
||||
body.course-detail-page-active #bg{
|
||||
width: 1874px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1280px){
|
||||
body{
|
||||
body.course-detail-page-active{
|
||||
zoom: 67%;
|
||||
}
|
||||
#bg{
|
||||
body.course-detail-page-active #bg{
|
||||
width: 1885px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
body {
|
||||
body.course-detail-page-active {
|
||||
background-color: #e0ded9;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -123,6 +123,9 @@ export default {
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
// 给 body 添加特定类名,用于限制样式作用域
|
||||
document.body.classList.add('contributor-conference-project-detail-active');
|
||||
|
||||
this.id = this.$route.params.id;
|
||||
let list = project.project;
|
||||
let pid = this.id;
|
||||
@@ -154,45 +157,50 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时移除类名
|
||||
document.body.classList.remove('contributor-conference-project-detail-active');
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang='scss'>
|
||||
// 使用 body.contributor-conference-project-detail-active 类名限制样式只在该页面生效
|
||||
@media screen and (min-width: 1440px) and (max-width: 1600px){
|
||||
body{
|
||||
body.contributor-conference-project-detail-active{
|
||||
zoom: 79%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-project-detail-active #bg{
|
||||
width: 1900px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1366px) and (max-width: 1440px){
|
||||
body{
|
||||
body.contributor-conference-project-detail-active{
|
||||
zoom: 74%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-project-detail-active #bg{
|
||||
width: 1869px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1280px) and (max-width: 1366px){
|
||||
body{
|
||||
body.contributor-conference-project-detail-active{
|
||||
zoom: 72%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-project-detail-active #bg{
|
||||
width: 1874px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1280px){
|
||||
body{
|
||||
body.contributor-conference-project-detail-active{
|
||||
zoom: 67%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-project-detail-active #bg{
|
||||
width: 1885px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
body {
|
||||
body.contributor-conference-project-detail-active {
|
||||
background-color: #e0ded9;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -52,6 +52,9 @@ export default {
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// 给 body 添加特定类名,用于限制样式作用域
|
||||
document.body.classList.add('contributor-conference-example-active');
|
||||
|
||||
let list = example.example
|
||||
for(let i = 0; i<list.length; i++){
|
||||
if(list[i].content.length > 200){
|
||||
@@ -60,40 +63,45 @@ export default {
|
||||
}
|
||||
this.exampleList = list
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时移除类名
|
||||
document.body.classList.remove('contributor-conference-example-active');
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss'>
|
||||
// 使用 body.contributor-conference-example-active 类名限制样式只在该页面生效
|
||||
@media screen and (min-width: 1440px) and (max-width: 1600px){
|
||||
body{
|
||||
body.contributor-conference-example-active{
|
||||
zoom: 79%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-example-active #bg{
|
||||
width: 1900px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1366px) and (max-width: 1440px){
|
||||
body{
|
||||
body.contributor-conference-example-active{
|
||||
zoom: 74%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-example-active #bg{
|
||||
width: 1869px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1280px) and (max-width: 1366px){
|
||||
body{
|
||||
body.contributor-conference-example-active{
|
||||
zoom: 72%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-example-active #bg{
|
||||
width: 1874px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1280px){
|
||||
body{
|
||||
body.contributor-conference-example-active{
|
||||
zoom: 67%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-example-active #bg{
|
||||
width: 1885px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -383,41 +383,49 @@ export default {
|
||||
// if (res && res.code == "0") {
|
||||
// console.log(res);
|
||||
// }
|
||||
},
|
||||
mounted() {
|
||||
// 给 body 添加特定类名,用于限制样式作用域
|
||||
document.body.classList.add('contributor-conference-index-active');
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时移除类名
|
||||
document.body.classList.remove('contributor-conference-index-active');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
</script>
|
||||
<style lang='scss'>
|
||||
// 使用 body.contributor-conference-index-active 类名限制样式只在该页面生效
|
||||
@media screen and (min-width: 1440px) and (max-width: 1600px){
|
||||
body{
|
||||
body.contributor-conference-index-active{
|
||||
zoom: 79%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-index-active #bg{
|
||||
width: 1900px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1366px) and (max-width: 1440px){
|
||||
body{
|
||||
body.contributor-conference-index-active{
|
||||
zoom: 74%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-index-active #bg{
|
||||
width: 1869px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1280px) and (max-width: 1366px){
|
||||
body{
|
||||
body.contributor-conference-index-active{
|
||||
zoom: 72%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-index-active #bg{
|
||||
width: 1874px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1280px){
|
||||
body{
|
||||
body.contributor-conference-index-active{
|
||||
zoom: 67%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-index-active #bg{
|
||||
width: 1885px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,39 +111,48 @@ export default {
|
||||
this.$router.back('/contributor/index')
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 给 body 添加特定类名,用于限制样式作用域
|
||||
document.body.classList.add('contributor-conference-project-active');
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时移除类名
|
||||
document.body.classList.remove('contributor-conference-project-active');
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang='scss'>
|
||||
// 使用 body.contributor-conference-project-active 类名限制样式只在该页面生效
|
||||
@media screen and (min-width: 1440px) and (max-width: 1600px){
|
||||
body{
|
||||
body.contributor-conference-project-active{
|
||||
zoom: 79%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-project-active #bg{
|
||||
width: 1900px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1366px) and (max-width: 1440px){
|
||||
body{
|
||||
body.contributor-conference-project-active{
|
||||
zoom: 74%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-project-active #bg{
|
||||
width: 1869px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1280px) and (max-width: 1366px){
|
||||
body{
|
||||
body.contributor-conference-project-active{
|
||||
zoom: 72%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-project-active #bg{
|
||||
width: 1874px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1280px){
|
||||
body{
|
||||
body.contributor-conference-project-active{
|
||||
zoom: 67%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-project-active #bg{
|
||||
width: 1885px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,9 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 给 body 添加特定类名,用于限制样式作用域
|
||||
document.body.classList.add('contributor-conference-teacher-active');
|
||||
|
||||
let list = teacher.teacher;
|
||||
list.forEach(element => {
|
||||
let temp = element.org.split('/')
|
||||
@@ -51,38 +54,43 @@ export default {
|
||||
this.teacherList = list
|
||||
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时移除类名
|
||||
document.body.classList.remove('contributor-conference-teacher-active');
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang='scss'>
|
||||
// 使用 body.contributor-conference-teacher-active 类名限制样式只在该页面生效
|
||||
@media screen and (min-width: 1440px) and (max-width: 1600px){
|
||||
body{
|
||||
body.contributor-conference-teacher-active{
|
||||
zoom: 79%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-teacher-active #bg{
|
||||
width: 1900px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1366px) and (max-width: 1440px){
|
||||
body{
|
||||
body.contributor-conference-teacher-active{
|
||||
zoom: 74%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-teacher-active #bg{
|
||||
width: 1869px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1280px) and (max-width: 1366px){
|
||||
body{
|
||||
body.contributor-conference-teacher-active{
|
||||
zoom: 72%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-teacher-active #bg{
|
||||
width: 1874px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1280px){
|
||||
body{
|
||||
body.contributor-conference-teacher-active{
|
||||
zoom: 67%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-teacher-active #bg{
|
||||
width: 1885px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,9 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 给 body 添加特定类名,用于限制样式作用域
|
||||
document.body.classList.add('contributor-conference-2024-colleage-active');
|
||||
|
||||
let list = colleage.colleage;
|
||||
list.forEach(element => {
|
||||
let temp = element.org.split('/')
|
||||
@@ -49,39 +52,44 @@ export default {
|
||||
});
|
||||
this.colleageList = list;
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时移除类名
|
||||
document.body.classList.remove('contributor-conference-2024-colleage-active');
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang='scss'>
|
||||
// 使用 body.contributor-conference-2024-colleage-active 类名限制样式只在该页面生效
|
||||
@media screen and (min-width: 1440px) and (max-width: 1600px){
|
||||
body{
|
||||
body.contributor-conference-2024-colleage-active{
|
||||
zoom: 79%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-colleage-active #bg{
|
||||
width: 1900px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1366px) and (max-width: 1440px){
|
||||
body{
|
||||
body.contributor-conference-2024-colleage-active{
|
||||
zoom: 74%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-colleage-active #bg{
|
||||
width: 1869px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1280px) and (max-width: 1366px){
|
||||
body{
|
||||
body.contributor-conference-2024-colleage-active{
|
||||
zoom: 72%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-colleage-active #bg{
|
||||
width: 1874px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1280px){
|
||||
body{
|
||||
body.contributor-conference-2024-colleage-active{
|
||||
zoom: 67%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-colleage-active #bg{
|
||||
width: 1885px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,41 +50,49 @@ export default {
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// 给 body 添加特定类名,用于限制样式作用域
|
||||
document.body.classList.add('contributor-conference-2024-course-active');
|
||||
|
||||
this.courseList = course.course;
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时移除类名
|
||||
document.body.classList.remove('contributor-conference-2024-course-active');
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang='scss'>
|
||||
// 使用 body.contributor-conference-2024-course-active 类名限制样式只在该页面生效
|
||||
@media screen and (min-width: 1440px) and (max-width: 1600px){
|
||||
body{
|
||||
body.contributor-conference-2024-course-active{
|
||||
zoom: 79%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-course-active #bg{
|
||||
width: 1900px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1366px) and (max-width: 1440px){
|
||||
body{
|
||||
body.contributor-conference-2024-course-active{
|
||||
zoom: 74%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-course-active #bg{
|
||||
width: 1869px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1280px) and (max-width: 1366px){
|
||||
body{
|
||||
body.contributor-conference-2024-course-active{
|
||||
zoom: 72%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-course-active #bg{
|
||||
width: 1874px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1280px){
|
||||
body{
|
||||
body.contributor-conference-2024-course-active{
|
||||
zoom: 67%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-course-active #bg{
|
||||
width: 1885px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,6 +98,9 @@ export default {
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
// 给 body 添加特定类名,用于限制样式作用域
|
||||
document.body.classList.add('contributor-conference-2024-course-detail-active');
|
||||
|
||||
this.id = this.$route.params.id
|
||||
let list = course.course;
|
||||
let pid = this.id
|
||||
@@ -124,45 +127,50 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时移除类名
|
||||
document.body.classList.remove('contributor-conference-2024-course-detail-active');
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang='scss'>
|
||||
// 使用 body.contributor-conference-2024-course-detail-active 类名限制样式只在该页面生效
|
||||
@media screen and (min-width: 1440px) and (max-width: 1600px){
|
||||
body{
|
||||
body.contributor-conference-2024-course-detail-active{
|
||||
zoom: 79%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-course-detail-active #bg{
|
||||
width: 1900px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1366px) and (max-width: 1440px){
|
||||
body{
|
||||
body.contributor-conference-2024-course-detail-active{
|
||||
zoom: 74%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-course-detail-active #bg{
|
||||
width: 1869px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1280px) and (max-width: 1366px){
|
||||
body{
|
||||
body.contributor-conference-2024-course-detail-active{
|
||||
zoom: 72%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-course-detail-active #bg{
|
||||
width: 1874px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1280px){
|
||||
body{
|
||||
body.contributor-conference-2024-course-detail-active{
|
||||
zoom: 67%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-course-detail-active #bg{
|
||||
width: 1885px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
body {
|
||||
body.contributor-conference-2024-course-detail-active {
|
||||
background-color: #e0ded9;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -129,6 +129,9 @@ export default {
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
// 给 body 添加特定类名,用于限制样式作用域
|
||||
document.body.classList.add('contributor-conference-2024-project-detail-active');
|
||||
|
||||
this.id = this.$route.params.id;
|
||||
let list = project.project;
|
||||
let pid = this.id;
|
||||
@@ -160,45 +163,50 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时移除类名
|
||||
document.body.classList.remove('contributor-conference-2024-project-detail-active');
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang='scss'>
|
||||
// 使用 body.contributor-conference-2024-project-detail-active 类名限制样式只在该页面生效
|
||||
@media screen and (min-width: 1440px) and (max-width: 1600px){
|
||||
body{
|
||||
body.contributor-conference-2024-project-detail-active{
|
||||
zoom: 79%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-project-detail-active #bg{
|
||||
width: 1900px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1366px) and (max-width: 1440px){
|
||||
body{
|
||||
body.contributor-conference-2024-project-detail-active{
|
||||
zoom: 74%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-project-detail-active #bg{
|
||||
width: 1869px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1280px) and (max-width: 1366px){
|
||||
body{
|
||||
body.contributor-conference-2024-project-detail-active{
|
||||
zoom: 72%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-project-detail-active #bg{
|
||||
width: 1874px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1280px){
|
||||
body{
|
||||
body.contributor-conference-2024-project-detail-active{
|
||||
zoom: 67%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-project-detail-active #bg{
|
||||
width: 1885px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
body {
|
||||
body.contributor-conference-2024-project-detail-active {
|
||||
background-color: #e0ded9;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -52,6 +52,9 @@ export default {
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// 给 body 添加特定类名,用于限制样式作用域
|
||||
document.body.classList.add('contributor-conference-2024-example-active');
|
||||
|
||||
let list = example.example
|
||||
for(let i = 0; i<list.length; i++){
|
||||
if(list[i].content.length > 200){
|
||||
@@ -60,40 +63,45 @@ export default {
|
||||
}
|
||||
this.exampleList = list
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时移除类名
|
||||
document.body.classList.remove('contributor-conference-2024-example-active');
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss'>
|
||||
// 使用 body.contributor-conference-2024-example-active 类名限制样式只在该页面生效
|
||||
@media screen and (min-width: 1440px) and (max-width: 1600px){
|
||||
body{
|
||||
body.contributor-conference-2024-example-active{
|
||||
zoom: 79%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-example-active #bg{
|
||||
width: 1900px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1366px) and (max-width: 1440px){
|
||||
body{
|
||||
body.contributor-conference-2024-example-active{
|
||||
zoom: 74%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-example-active #bg{
|
||||
width: 1869px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1280px) and (max-width: 1366px){
|
||||
body{
|
||||
body.contributor-conference-2024-example-active{
|
||||
zoom: 72%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-example-active #bg{
|
||||
width: 1874px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1280px){
|
||||
body{
|
||||
body.contributor-conference-2024-example-active{
|
||||
zoom: 67%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-example-active #bg{
|
||||
width: 1885px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,6 +408,14 @@ export default {
|
||||
// if (res && res.code == "0") {
|
||||
// console.log(res);
|
||||
// }
|
||||
},
|
||||
mounted() {
|
||||
// 给 body 添加特定类名,用于限制样式作用域
|
||||
document.body.classList.add('contributor-conference-2024-index-active');
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时移除类名
|
||||
document.body.classList.remove('contributor-conference-2024-index-active');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -425,35 +433,36 @@ export default {
|
||||
font-family: 'biaoti2';
|
||||
src: url('../../assets/fonts/KMQfP4.ttf');
|
||||
}
|
||||
// 使用 body.contributor-conference-2024-index-active 类名限制样式只在该页面生效
|
||||
@media screen and (min-width: 1440px) and (max-width: 1600px){
|
||||
body{
|
||||
body.contributor-conference-2024-index-active{
|
||||
zoom: 79%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-index-active #bg{
|
||||
width: 1900px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1366px) and (max-width: 1440px){
|
||||
body{
|
||||
body.contributor-conference-2024-index-active{
|
||||
zoom: 74%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-index-active #bg{
|
||||
width: 1869px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1280px) and (max-width: 1366px){
|
||||
body{
|
||||
body.contributor-conference-2024-index-active{
|
||||
zoom: 72%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-index-active #bg{
|
||||
width: 1874px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1280px){
|
||||
body{
|
||||
body.contributor-conference-2024-index-active{
|
||||
zoom: 67%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-index-active #bg{
|
||||
width: 1885px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,39 +139,48 @@ export default {
|
||||
this.$router.back('/contributor_2024/index')
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 给 body 添加特定类名,用于限制样式作用域
|
||||
document.body.classList.add('contributor-conference-2024-project-active');
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时移除类名
|
||||
document.body.classList.remove('contributor-conference-2024-project-active');
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang='scss'>
|
||||
// 使用 body.contributor-conference-2024-project-active 类名限制样式只在该页面生效
|
||||
@media screen and (min-width: 1440px) and (max-width: 1600px){
|
||||
body{
|
||||
body.contributor-conference-2024-project-active{
|
||||
zoom: 79%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-project-active #bg{
|
||||
width: 1900px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1366px) and (max-width: 1440px){
|
||||
body{
|
||||
body.contributor-conference-2024-project-active{
|
||||
zoom: 74%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-project-active #bg{
|
||||
width: 1869px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1280px) and (max-width: 1366px){
|
||||
body{
|
||||
body.contributor-conference-2024-project-active{
|
||||
zoom: 72%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-project-active #bg{
|
||||
width: 1874px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1280px){
|
||||
body{
|
||||
body.contributor-conference-2024-project-active{
|
||||
zoom: 67%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-project-active #bg{
|
||||
width: 1885px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,9 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 给 body 添加特定类名,用于限制样式作用域
|
||||
document.body.classList.add('contributor-conference-2024-teacher-active');
|
||||
|
||||
let list = teacher.teacher;
|
||||
list.forEach(element => {
|
||||
let temp = element.org.split('/')
|
||||
@@ -51,38 +54,43 @@ export default {
|
||||
this.teacherList = list
|
||||
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时移除类名
|
||||
document.body.classList.remove('contributor-conference-2024-teacher-active');
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang='scss'>
|
||||
// 使用 body.contributor-conference-2024-teacher-active 类名限制样式只在该页面生效
|
||||
@media screen and (min-width: 1440px) and (max-width: 1600px){
|
||||
body{
|
||||
body.contributor-conference-2024-teacher-active{
|
||||
zoom: 79%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-teacher-active #bg{
|
||||
width: 1900px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1366px) and (max-width: 1440px){
|
||||
body{
|
||||
body.contributor-conference-2024-teacher-active{
|
||||
zoom: 74%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-teacher-active #bg{
|
||||
width: 1869px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1280px) and (max-width: 1366px){
|
||||
body{
|
||||
body.contributor-conference-2024-teacher-active{
|
||||
zoom: 72%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-teacher-active #bg{
|
||||
width: 1874px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1280px){
|
||||
body{
|
||||
body.contributor-conference-2024-teacher-active{
|
||||
zoom: 67%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-2024-teacher-active #bg{
|
||||
width: 1885px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,9 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 给 body 添加特定类名,用于限制样式作用域
|
||||
document.body.classList.add('contributor-conference-new-colleage-active');
|
||||
|
||||
let list = colleage.colleage;
|
||||
list.forEach(element => {
|
||||
let temp = element.org.split('/')
|
||||
@@ -47,39 +50,44 @@ export default {
|
||||
});
|
||||
this.colleageList = list;
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时移除类名
|
||||
document.body.classList.remove('contributor-conference-new-colleage-active');
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang='scss'>
|
||||
// 使用 body.contributor-conference-new-colleage-active 类名限制样式只在该页面生效
|
||||
@media screen and (min-width: 1440px) and (max-width: 1600px){
|
||||
body{
|
||||
body.contributor-conference-new-colleage-active{
|
||||
zoom: 79%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-colleage-active #bg{
|
||||
width: 1900px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1366px) and (max-width: 1440px){
|
||||
body{
|
||||
body.contributor-conference-new-colleage-active{
|
||||
zoom: 74%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-colleage-active #bg{
|
||||
width: 1869px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1280px) and (max-width: 1366px){
|
||||
body{
|
||||
body.contributor-conference-new-colleage-active{
|
||||
zoom: 72%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-colleage-active #bg{
|
||||
width: 1874px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1280px){
|
||||
body{
|
||||
body.contributor-conference-new-colleage-active{
|
||||
zoom: 67%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-colleage-active #bg{
|
||||
width: 1885px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,41 +50,49 @@ export default {
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// 给 body 添加特定类名,用于限制样式作用域
|
||||
document.body.classList.add('contributor-conference-new-course-active');
|
||||
|
||||
this.courseList = course.course;
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时移除类名
|
||||
document.body.classList.remove('contributor-conference-new-course-active');
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang='scss'>
|
||||
// 使用 body.contributor-conference-new-course-active 类名限制样式只在该页面生效
|
||||
@media screen and (min-width: 1440px) and (max-width: 1600px){
|
||||
body{
|
||||
body.contributor-conference-new-course-active{
|
||||
zoom: 79%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-course-active #bg{
|
||||
width: 1900px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1366px) and (max-width: 1440px){
|
||||
body{
|
||||
body.contributor-conference-new-course-active{
|
||||
zoom: 74%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-course-active #bg{
|
||||
width: 1869px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1280px) and (max-width: 1366px){
|
||||
body{
|
||||
body.contributor-conference-new-course-active{
|
||||
zoom: 72%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-course-active #bg{
|
||||
width: 1874px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1280px){
|
||||
body{
|
||||
body.contributor-conference-new-course-active{
|
||||
zoom: 67%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-course-active #bg{
|
||||
width: 1885px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,6 +98,9 @@ export default {
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
// 给 body 添加特定类名,用于限制样式作用域
|
||||
document.body.classList.add('contributor-conference-new-course-detail-active');
|
||||
|
||||
this.id = this.$route.params.id
|
||||
let list = course.course;
|
||||
let pid = this.id
|
||||
@@ -124,45 +127,50 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时移除类名
|
||||
document.body.classList.remove('contributor-conference-new-course-detail-active');
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang='scss'>
|
||||
// 使用 body.contributor-conference-new-course-detail-active 类名限制样式只在该页面生效
|
||||
@media screen and (min-width: 1440px) and (max-width: 1600px){
|
||||
body{
|
||||
body.contributor-conference-new-course-detail-active{
|
||||
zoom: 79%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-course-detail-active #bg{
|
||||
width: 1900px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1366px) and (max-width: 1440px){
|
||||
body{
|
||||
body.contributor-conference-new-course-detail-active{
|
||||
zoom: 74%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-course-detail-active #bg{
|
||||
width: 1869px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1280px) and (max-width: 1366px){
|
||||
body{
|
||||
body.contributor-conference-new-course-detail-active{
|
||||
zoom: 72%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-course-detail-active #bg{
|
||||
width: 1874px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1280px){
|
||||
body{
|
||||
body.contributor-conference-new-course-detail-active{
|
||||
zoom: 67%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-course-detail-active #bg{
|
||||
width: 1885px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
body {
|
||||
body.contributor-conference-new-course-detail-active {
|
||||
background-color: #e0ded9;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -129,6 +129,9 @@ export default {
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
// 给 body 添加特定类名,用于限制样式作用域
|
||||
document.body.classList.add('contributor-conference-new-project-detail-active');
|
||||
|
||||
this.id = this.$route.params.id;
|
||||
let list = project.project;
|
||||
let pid = this.id;
|
||||
@@ -160,45 +163,50 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时移除类名
|
||||
document.body.classList.remove('contributor-conference-new-project-detail-active');
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang='scss'>
|
||||
// 使用 body.contributor-conference-new-project-detail-active 类名限制样式只在该页面生效
|
||||
@media screen and (min-width: 1440px) and (max-width: 1600px){
|
||||
body{
|
||||
body.contributor-conference-new-project-detail-active{
|
||||
zoom: 79%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-project-detail-active #bg{
|
||||
width: 1900px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1366px) and (max-width: 1440px){
|
||||
body{
|
||||
body.contributor-conference-new-project-detail-active{
|
||||
zoom: 74%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-project-detail-active #bg{
|
||||
width: 1869px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1280px) and (max-width: 1366px){
|
||||
body{
|
||||
body.contributor-conference-new-project-detail-active{
|
||||
zoom: 72%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-project-detail-active #bg{
|
||||
width: 1874px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1280px){
|
||||
body{
|
||||
body.contributor-conference-new-project-detail-active{
|
||||
zoom: 67%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-project-detail-active #bg{
|
||||
width: 1885px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
body {
|
||||
body.contributor-conference-new-project-detail-active {
|
||||
background-color: #e0ded9;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -52,6 +52,9 @@ export default {
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// 给 body 添加特定类名,用于限制样式作用域
|
||||
document.body.classList.add('contributor-conference-new-example-active');
|
||||
|
||||
let list = example.example
|
||||
for(let i = 0; i<list.length; i++){
|
||||
if(list[i].content.length > 200){
|
||||
@@ -60,40 +63,45 @@ export default {
|
||||
}
|
||||
this.exampleList = list
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时移除类名
|
||||
document.body.classList.remove('contributor-conference-new-example-active');
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss'>
|
||||
// 使用 body.contributor-conference-new-example-active 类名限制样式只在该页面生效
|
||||
@media screen and (min-width: 1440px) and (max-width: 1600px){
|
||||
body{
|
||||
body.contributor-conference-new-example-active{
|
||||
zoom: 79%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-example-active #bg{
|
||||
width: 1900px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1366px) and (max-width: 1440px){
|
||||
body{
|
||||
body.contributor-conference-new-example-active{
|
||||
zoom: 74%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-example-active #bg{
|
||||
width: 1869px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1280px) and (max-width: 1366px){
|
||||
body{
|
||||
body.contributor-conference-new-example-active{
|
||||
zoom: 72%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-example-active #bg{
|
||||
width: 1874px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1280px){
|
||||
body{
|
||||
body.contributor-conference-new-example-active{
|
||||
zoom: 67%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-example-active #bg{
|
||||
width: 1885px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,6 +401,14 @@ export default {
|
||||
// if (res && res.code == "0") {
|
||||
// console.log(res);
|
||||
// }
|
||||
},
|
||||
mounted() {
|
||||
// 给 body 添加特定类名,用于限制样式作用域
|
||||
document.body.classList.add('contributor-conference-new-index-active');
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时移除类名
|
||||
document.body.classList.remove('contributor-conference-new-index-active');
|
||||
}
|
||||
|
||||
|
||||
@@ -419,35 +427,36 @@ export default {
|
||||
font-family: 'biaoti2';
|
||||
src: url('../../assets/fonts/KMQfP4.ttf');
|
||||
}
|
||||
// 使用 body.contributor-conference-new-index-active 类名限制样式只在该页面生效
|
||||
@media screen and (min-width: 1440px) and (max-width: 1600px){
|
||||
body{
|
||||
body.contributor-conference-new-index-active{
|
||||
zoom: 79%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-index-active #bg{
|
||||
width: 1900px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1366px) and (max-width: 1440px){
|
||||
body{
|
||||
body.contributor-conference-new-index-active{
|
||||
zoom: 74%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-index-active #bg{
|
||||
width: 1869px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1280px) and (max-width: 1366px){
|
||||
body{
|
||||
body.contributor-conference-new-index-active{
|
||||
zoom: 72%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-index-active #bg{
|
||||
width: 1874px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1280px){
|
||||
body{
|
||||
body.contributor-conference-new-index-active{
|
||||
zoom: 67%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-index-active #bg{
|
||||
width: 1885px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,39 +183,48 @@ export default {
|
||||
this.$router.back('/contributornew/index')
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 给 body 添加特定类名,用于限制样式作用域
|
||||
document.body.classList.add('contributor-conference-new-project-active');
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时移除类名
|
||||
document.body.classList.remove('contributor-conference-new-project-active');
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang='scss'>
|
||||
// 使用 body.contributor-conference-new-project-active 类名限制样式只在该页面生效
|
||||
@media screen and (min-width: 1440px) and (max-width: 1600px){
|
||||
body{
|
||||
body.contributor-conference-new-project-active{
|
||||
zoom: 79%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-project-active #bg{
|
||||
width: 1900px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1366px) and (max-width: 1440px){
|
||||
body{
|
||||
body.contributor-conference-new-project-active{
|
||||
zoom: 74%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-project-active #bg{
|
||||
width: 1869px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1280px) and (max-width: 1366px){
|
||||
body{
|
||||
body.contributor-conference-new-project-active{
|
||||
zoom: 72%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-project-active #bg{
|
||||
width: 1874px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1280px){
|
||||
body{
|
||||
body.contributor-conference-new-project-active{
|
||||
zoom: 67%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-project-active #bg{
|
||||
width: 1885px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,9 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 给 body 添加特定类名,用于限制样式作用域
|
||||
document.body.classList.add('contributor-conference-new-teacher-active');
|
||||
|
||||
let list = teacher.teacher;
|
||||
list.forEach(element => {
|
||||
let temp = element.org.split('/')
|
||||
@@ -51,38 +54,43 @@ export default {
|
||||
this.teacherList = list
|
||||
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时移除类名
|
||||
document.body.classList.remove('contributor-conference-new-teacher-active');
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang='scss'>
|
||||
// 使用 body.contributor-conference-new-teacher-active 类名限制样式只在该页面生效
|
||||
@media screen and (min-width: 1440px) and (max-width: 1600px){
|
||||
body{
|
||||
body.contributor-conference-new-teacher-active{
|
||||
zoom: 79%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-teacher-active #bg{
|
||||
width: 1900px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1366px) and (max-width: 1440px){
|
||||
body{
|
||||
body.contributor-conference-new-teacher-active{
|
||||
zoom: 74%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-teacher-active #bg{
|
||||
width: 1869px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1280px) and (max-width: 1366px){
|
||||
body{
|
||||
body.contributor-conference-new-teacher-active{
|
||||
zoom: 72%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-teacher-active #bg{
|
||||
width: 1874px !important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1280px){
|
||||
body{
|
||||
body.contributor-conference-new-teacher-active{
|
||||
zoom: 67%;
|
||||
}
|
||||
#bg{
|
||||
body.contributor-conference-new-teacher-active #bg{
|
||||
width: 1885px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,14 +64,19 @@
|
||||
</div>
|
||||
</div>
|
||||
<el-row :gutter="20" style="margin-top:10px">
|
||||
<el-col :span="4">
|
||||
<!-- ai播放器相关 -->
|
||||
<el-col :span="24">
|
||||
<!-- <el-button icon="el-icon-folder" type="primary" size="small">导出</el-button> -->
|
||||
<el-button class="Create-coures" type="primary" @click="addNewCourse()" icon="el-icon-plus">新建课程</el-button>
|
||||
<el-button v-if="aiPermission" type="primary" @click="setLanguage()" icon="el-icon-connection" :disabled="selectedCourses.length === 0">设置语种</el-button>
|
||||
<el-button v-if="aiPermission" type="primary" @click="enableAI()" icon="el-icon-switch-button" :disabled="selectedCourses.length === 0">开启AI处理</el-button>
|
||||
</el-col >
|
||||
</el-row>
|
||||
</div>
|
||||
<div style="margin-right:30px;">
|
||||
<el-table style="margin:10px 32px 10px 22px;" :data="pageData" border stripe>
|
||||
<!-- ai播放器相关 -->
|
||||
<el-table style="margin:10px 32px 10px 22px;" :data="pageData" border stripe @selection-change="handleSelectionChange">
|
||||
<el-table-column v-if="aiPermission" type="selection" width="55"></el-table-column>
|
||||
<el-table-column label="序号" type="index" width="50"></el-table-column>
|
||||
<el-table-column v-if="forChoose" label="选择" width="80">
|
||||
<template slot-scope="scope" v-if="scope.row.published">
|
||||
@@ -107,7 +112,7 @@
|
||||
{{ courseType(scope.row.type)}}
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
<el-table-column label="状态" prop="status" width="120px">
|
||||
<el-table-column label="状态" prop="status" width="100">
|
||||
<template slot-scope="scope">
|
||||
<!-- 1,未提交 2.已提交 = 未审核 5 已审核 -->
|
||||
<span v-if="scope.row.status == 1">未提交</span>
|
||||
@@ -116,26 +121,30 @@
|
||||
<span v-if="scope.row.status == 3">审核未通过</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="是否发布" width="130px">
|
||||
<el-table-column label="是否发布" width="80">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.published == true ? '已发布' : '未发布' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建人" prop="sysCreateBy"></el-table-column>
|
||||
<el-table-column label="创建时间" prop="sysCreateTime" width="230px" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="是否停用" width="130px">
|
||||
<el-table-column label="创建时间" prop="sysCreateTime" width="150" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="是否停用" width="80">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.enabled == true ? '启用' : '停用' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="是否置顶" width="130px">
|
||||
<el-table-column label="是否置顶" width="80">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.isTop == true ? '置顶' : '未置顶' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审核人" prop="auditUser" width="80"></el-table-column>
|
||||
<el-table-column label="审核时间" prop="auditTime" width="150" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="操作" width="180px" fixed="right">
|
||||
<template slot-scope="scope" class="btn-gl">
|
||||
<!-- 20240621 修改scope.row.isPermission = fasle 时不展示操作按钮-->
|
||||
<!-- ai播放器相关 -->
|
||||
<el-button v-if="aiPermission && scope.row.isPermission && scope.row.status != 2" type="text" size="mini" @click="setAI(scope.row)">AI设置</el-button>
|
||||
<el-button type="text" size="mini" v-if="scope.row.isPermission && scope.row.status == 5 && !scope.row.published" @click="releaseData(scope.row)">发布</el-button>
|
||||
<el-button v-if="scope.row.isPermission && pageManage && scope.row.published" @click="showStudent(scope.row)" type="text" size="mini">学员</el-button>
|
||||
<el-button v-if="scope.row.isPermission && !forChoose && scope.row.published" @click="showManageStudy(scope.row)" type="text" size="mini">管理</el-button>
|
||||
@@ -287,6 +296,236 @@
|
||||
<div>
|
||||
<course-form ref="courseForm" @submitSuccess="searchData" @close="searchData"></course-form>
|
||||
</div>
|
||||
|
||||
<!-- ai播放器相关 -->
|
||||
<!-- 设置语种弹框 -->
|
||||
<el-dialog
|
||||
title="AI翻译"
|
||||
:visible.sync="languageSetting.dlgShow"
|
||||
width="500px"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<div style="margin-bottom: 20px;">
|
||||
<div style="margin-bottom: 15px;">请选择课程所支持语种</div>
|
||||
<el-select
|
||||
v-model="languageSetting.languageCode"
|
||||
multiple
|
||||
placeholder="请选择语种"
|
||||
style="width: 100%;"
|
||||
>
|
||||
<el-option
|
||||
v-for="lang in selectAllLang"
|
||||
:key="lang.srclang"
|
||||
:label="lang.label"
|
||||
:value="lang.srclang"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div style="color: #ff4d4f; font-size: 12px;">
|
||||
注:仅支持对已开启AI处理的课程进行批量语种设置;所选的课程中有{{languageSetting.aiSetNoNum}}个未开启AI处理的课程,以上配置仅对{{languageSetting.aiSetNum}}个已开启AI处理的课程生效。
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="languageSetting.dlgShow = false">取消</el-button>
|
||||
<el-button type="primary" @click="confirmLanguageSetting">确认</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 开启AI处理弹框 -->
|
||||
<el-dialog
|
||||
title="开启AI处理"
|
||||
:visible.sync="aiProcessSetting.dlgShow"
|
||||
width="400px"
|
||||
>
|
||||
<div class="ai-process-dialog">
|
||||
<!-- AI处理状态 -->
|
||||
<div class="form-item">
|
||||
<span class="form-label">
|
||||
<el-tooltip class="item" effect="dark" :content="aiSetTip" placement="top">
|
||||
<i class="el-icon-question"></i>
|
||||
</el-tooltip>
|
||||
AI处理:
|
||||
</span>
|
||||
<span class="status-text">
|
||||
{{ aiProcessSetting.aiSet === 1 ? '开启' : '关闭' }}
|
||||
</span>
|
||||
<el-switch
|
||||
v-model="aiProcessSetting.aiSet"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
></el-switch>
|
||||
</div>
|
||||
|
||||
<div v-show="aiProcessSetting.aiSet === 1">
|
||||
<!-- 是否生成AI摘要 -->
|
||||
<div class="form-item">
|
||||
<span class="form-label">
|
||||
<el-tooltip class="item" effect="dark" :content="aiAbstractTip" placement="top">
|
||||
<i class="el-icon-question"></i>
|
||||
</el-tooltip>
|
||||
AI摘要:
|
||||
</span>
|
||||
<span class="status-text">
|
||||
{{ aiProcessSetting.aiAbstract === 1 ? '开启' : '关闭' }}
|
||||
</span>
|
||||
<el-switch
|
||||
v-model="aiProcessSetting.aiAbstract"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
>
|
||||
</el-switch>
|
||||
</div>
|
||||
|
||||
<!-- 是否生成AI文稿 -->
|
||||
<div class="form-item">
|
||||
<span class="form-label">
|
||||
<el-tooltip class="item" effect="dark" :content="aiDraftTip" placement="top">
|
||||
<i class="el-icon-question"></i>
|
||||
</el-tooltip>
|
||||
AI文稿:
|
||||
</span>
|
||||
<span class="status-text">
|
||||
{{ aiProcessSetting.aiDraft === 1 ? '开启' : '关闭' }}
|
||||
</span>
|
||||
<el-switch
|
||||
v-model="aiProcessSetting.aiDraft"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
>
|
||||
</el-switch>
|
||||
</div>
|
||||
|
||||
<!-- 课程支持语种选择 -->
|
||||
<div class="form-item" style="flex-flow: column;align-items: baseline;">
|
||||
<span class="form-label">
|
||||
<el-tooltip class="item" effect="dark" :content="aiTranslateTip" placement="top">
|
||||
<i class="el-icon-question"></i>
|
||||
</el-tooltip>
|
||||
AI翻译语种:
|
||||
</span>
|
||||
<el-select
|
||||
v-model="aiProcessSetting.languageCode"
|
||||
multiple
|
||||
style="width: 100%;"
|
||||
placeholder="请选择语种"
|
||||
>
|
||||
<el-option
|
||||
v-for="lang in selectAllLang"
|
||||
:key="lang.srclang"
|
||||
:label="lang.label"
|
||||
:value="lang.srclang"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 提示信息 -->
|
||||
<div class="tips">
|
||||
<span>注:已跳过{{aiProcessSetting.aiSetNum}}个已开启AI处理的课程,仅更新剩余{{aiProcessSetting.aiSetNoNum}}个</span>
|
||||
</div>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="aiProcessSetting.dlgShow = false">取消</el-button>
|
||||
<el-button type="primary" @click="confirmAiProcess">确认</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<!-- AI设置弹框 -->
|
||||
<el-dialog
|
||||
title="AI设置"
|
||||
:visible.sync="aiSetting.dlgShow"
|
||||
width="500px"
|
||||
>
|
||||
<div class="ai-setting-dialog">
|
||||
|
||||
<!-- AI功能状态 -->
|
||||
<div class="form-item">
|
||||
<span class="form-label">
|
||||
<el-tooltip class="item" effect="dark" content="是否将课程进行AI处理" placement="top">
|
||||
<i class="el-icon-question"></i>
|
||||
</el-tooltip>
|
||||
AI功能状态:
|
||||
</span>
|
||||
<span class="status-text">
|
||||
{{ aiSetting.aiSet === 1 ? '已开放' : '未开放' }}
|
||||
</span>
|
||||
<el-switch
|
||||
v-model="aiSetting.aiSet"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
></el-switch>
|
||||
</div>
|
||||
<div v-show="aiSetting.aiSet === 1">
|
||||
<!-- AI摘要状态 -->
|
||||
<div class="form-item">
|
||||
<span class="form-label">AI摘要状态:</span>
|
||||
<span :class="aiSetting.aiAbstract === 1 ? 'custom-putaway' : 'custom-takeout'">
|
||||
{{ aiSetting.aiAbstract === 1 ? '已上架' : '已下架' }}
|
||||
</span>
|
||||
<div class="action-buttons">
|
||||
<el-button type="text" @click="changeAIKey('aiAbstract')">
|
||||
{{ aiSetting.aiAbstract === 1 ? '下架' : '上架' }}
|
||||
</el-button>
|
||||
<el-button v-show="false" type="text" >编辑</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- AI文稿状态 -->
|
||||
<div class="form-item">
|
||||
<span class="form-label">AI文稿状态:</span>
|
||||
<span :class="aiSetting.aiDraft === 1 ? 'custom-putaway' : 'custom-takeout'">
|
||||
{{ aiSetting.aiDraft === 1 ? '已上架' : '已下架' }}
|
||||
</span>
|
||||
<div class="action-buttons">
|
||||
<el-button type="text" @click="changeAIKey('aiDraft')">
|
||||
{{ aiSetting.aiDraft === 1 ? '下架' : '上架' }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- AI翻译状态 -->
|
||||
<div class="form-item">
|
||||
<span class="form-label">AI翻译状态:</span>
|
||||
<span :class="aiSetting.aiTranslate === 1 ? 'custom-putaway' : 'custom-takeout'">
|
||||
{{ aiSetting.aiTranslate === 1 ? '已上架' : '已下架' }}
|
||||
</span>
|
||||
<div class="action-buttons">
|
||||
<el-button type="text" @click="changeAIKey('aiTranslate')">
|
||||
{{ aiSetting.aiTranslate === 1 ? '下架' : '上架' }}
|
||||
</el-button>
|
||||
<el-button v-show="false" type="text" >编辑</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 支持语种 -->
|
||||
<div class="form-item" style="align-items: flex-start;">
|
||||
<span class="form-label" style="white-space: nowrap;">支持语种:</span>
|
||||
<div class="languages-list" v-show="false">
|
||||
<div v-for="lang in aiSetting.languageCode" :key="lang" class="language-tag">
|
||||
{{ getLanguageName(lang) }}
|
||||
<span class="custom-takeout">已下架</span>
|
||||
</div>
|
||||
</div>
|
||||
<el-select
|
||||
v-model="aiSetting.languageCode"
|
||||
multiple
|
||||
style="width: 100%;"
|
||||
placeholder="请选择语种"
|
||||
>
|
||||
<el-option
|
||||
v-for="lang in selectAllLang"
|
||||
:key="lang.srclang"
|
||||
:label="lang.label"
|
||||
:value="lang.srclang"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="aiSetting.dlgShow = false">取消</el-button>
|
||||
<el-button type="primary" @click="confirmAISetting">确认</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -299,7 +538,7 @@ import auditCourse2 from '@/components/Course/auditCourse2.vue';
|
||||
import adminPage from '@/components/Administration/adminPage.vue';
|
||||
import apiResowner from '../../api/modules/resowner.js';
|
||||
import apiType from '../../api/modules/type.js'
|
||||
import {courseType} from '../../utils/tools.js';
|
||||
import {courseType, deepCopy} from '../../utils/tools.js';
|
||||
import apiCourse from '../../api/modules/course.js';
|
||||
// import {resOwnerIndexName,sysTypeIndexName} from '@/utils/type.js';
|
||||
import { mapGetters,mapActions } from 'vuex';
|
||||
@@ -307,8 +546,9 @@ import apiUserbasic from "@/api/boe/userbasic.js"
|
||||
export default {
|
||||
name: 'manageCourse',
|
||||
components: {courseForm, manager, auditCourse1, auditCourse2,adminPage},
|
||||
// ai播放器相关
|
||||
computed: {
|
||||
...mapGetters(['resOwnerMap','sysTypeMap','userInfo']),
|
||||
...mapGetters(['resOwnerMap','sysTypeMap','userInfo', 'selectAllLang']),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -392,9 +632,38 @@ export default {
|
||||
},
|
||||
extendRefId:'',
|
||||
extendRefType:'',
|
||||
// ai播放器相关
|
||||
aiPermission: false,
|
||||
selectedCourses: [], //已选课程
|
||||
languageSetting: { // 设置语种弹框
|
||||
dlgShow: false,
|
||||
languageCode: ['zh-CN', 'en-US'] // 默认选中的语种
|
||||
},
|
||||
aiProcessSetting: { // 开启AI处理弹框
|
||||
dlgShow: false,
|
||||
aiSet: 1,
|
||||
aiAbstract: 1,
|
||||
aiDraft: 1,
|
||||
languageCode: ['zh-CN', 'en-US'] // 默认选中的语种
|
||||
},
|
||||
aiSetting: { // AI设置弹框
|
||||
dlgShow: false,
|
||||
courseId: '',
|
||||
aiSet: 1,
|
||||
aiAbstract: 1, // 1:上架 0:下架
|
||||
aiDraft: 1, // 1:上架 0:下架
|
||||
aiTranslate: 1, // 1:上架 0:下架
|
||||
languageCode: ['zh-CN', 'en-US', 'vi-VN'] // 支持的语种
|
||||
},
|
||||
aiSetTip: '是否将课程进行AI处理', //提示信息
|
||||
aiAbstractTip: '一键提炼课程视频核心要点,助力学员课前高效掌握重点,快速筛选学习资源', // 提示信息
|
||||
aiDraftTip: '分段展示视频内容并精准同步时间轴,实现视频进度与文稿双向定位,学习内容触手可及', //提示信息
|
||||
aiTranslateTip: '智能转换视频字幕与语音为多语种,支持全球学员按需切换语言,打破学习边界', // 提示信息
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
// ai播放器相关
|
||||
this.getAiPermission();
|
||||
this.getAudiences()
|
||||
let chooseFlag=this.$route.query.f;
|
||||
this.extendRefId=this.$route.query.refId;
|
||||
@@ -885,6 +1154,150 @@ export default {
|
||||
saveNewCatalogZhang() {
|
||||
this.catalogs.addNewZhang = false;
|
||||
},
|
||||
|
||||
|
||||
// ai播放器相关
|
||||
getLanguageName(lang){
|
||||
return this.selectAllLang.find(item => item.srclang === lang)?.label || '';
|
||||
},
|
||||
handleSelectionChange(val){
|
||||
this.selectedCourses = val;
|
||||
console.log(val);
|
||||
},
|
||||
// 获取选中课程的AI信息
|
||||
getAIInfoByList(list = []) {
|
||||
let selectNum = 0; // 选中的课程数量
|
||||
let aiSetNum = 0; // 已设置AI的课程数量
|
||||
let aiSetNoNum = 0; // 未设置AI的课程数量
|
||||
list.forEach(item => {
|
||||
if(item.aiSet === 1){
|
||||
aiSetNum++;
|
||||
}else{
|
||||
aiSetNoNum++;
|
||||
}
|
||||
});
|
||||
return {
|
||||
selectNum,
|
||||
aiSetNum,
|
||||
aiSetNoNum
|
||||
}
|
||||
},
|
||||
// AI设置
|
||||
setAI(row) {
|
||||
console.log('row', row);
|
||||
this.aiSetting = {
|
||||
dlgShow: true,
|
||||
...row
|
||||
};
|
||||
},
|
||||
changeAIKey(key) {
|
||||
this.aiSetting[key] = this.aiSetting[key] === 1 ? 0 : 1;
|
||||
},
|
||||
// 确认AI设置
|
||||
confirmAISetting() {
|
||||
const item = deepCopy(this.aiSetting);
|
||||
item.languageStatus = item.aiSet;
|
||||
item.languageCode = item.languageCode || [];
|
||||
if (!item.languageCode.includes('zh-CN')) {
|
||||
item.languageCode.unshift('zh-CN'); // 默认添加中文 去重
|
||||
}
|
||||
this._benchAiSet([item], (res) => {
|
||||
this.$message.success('AI设置保存成功');
|
||||
this.aiSetting.dlgShow = false;
|
||||
// 可以选择是否刷新列表数据
|
||||
this.searchData();
|
||||
}, (res) => {
|
||||
this.$message.error('AI设置保存失败!');
|
||||
})
|
||||
},
|
||||
setLanguage() {
|
||||
if (this.selectedCourses.length > 0) {
|
||||
this.languageSetting = {...{
|
||||
dlgShow: true,
|
||||
languageCode: ['zh-CN', 'en-US'] // 默认选中的语种
|
||||
}, ...this.getAIInfoByList(this.selectedCourses)}
|
||||
}
|
||||
},
|
||||
enableAI() {
|
||||
// 开启AI处理按钮点击事件
|
||||
if (this.selectedCourses.length > 0) {
|
||||
this.aiProcessSetting = {...{
|
||||
dlgShow: true,
|
||||
aiSet: 1,
|
||||
aiAbstract: 1,
|
||||
aiDraft: 1,
|
||||
languageCode: ['zh-CN', 'en-US'] // 默认选中的语种
|
||||
}, ...this.getAIInfoByList(this.selectedCourses)}
|
||||
}
|
||||
},
|
||||
// 批量设置语种 - 确认
|
||||
confirmLanguageSetting() {
|
||||
const courseList = deepCopy(this.selectedCourses);
|
||||
let languageCode = deepCopy(this.languageSetting.languageCode || []);
|
||||
if (!languageCode.includes('zh-CN')) {
|
||||
languageCode.unshift('zh-CN'); // 默认添加中文 去重
|
||||
}
|
||||
courseList.forEach(item => {
|
||||
item.languageCode = languageCode;
|
||||
item.aiTranslate = item.aiSet;
|
||||
item.languageStatus = item.aiSet;
|
||||
})
|
||||
this._benchAiSet(courseList, (res) => {
|
||||
this.$message.success('设置语种成功!');
|
||||
this.languageSetting.dlgShow = false;
|
||||
// 可以选择是否刷新列表数据
|
||||
this.searchData();
|
||||
}, (res) => {
|
||||
this.$message.error('设置语种失败!');
|
||||
})
|
||||
},
|
||||
|
||||
// 批量开启AI处理 - 确认
|
||||
confirmAiProcess() {
|
||||
// 获取AI处理配置
|
||||
let { aiSet, aiAbstract, aiDraft, languageCode } = this.aiProcessSetting;
|
||||
const courseList = deepCopy(this.selectedCourses);
|
||||
languageCode = languageCode || [];
|
||||
if (!languageCode.includes('zh-CN')) {
|
||||
languageCode.unshift('zh-CN'); // 默认添加中文 去重
|
||||
}
|
||||
courseList.forEach(item => {
|
||||
item.aiSet = aiSet;
|
||||
item.aiAbstract = aiAbstract;
|
||||
item.aiDraft = aiDraft;
|
||||
item.aiTranslate = aiSet;
|
||||
item.languageStatus = aiSet;
|
||||
item.languageCode = languageCode;
|
||||
})
|
||||
this._benchAiSet(courseList, (res) => {
|
||||
this.$message.success('开启AI处理成功!');
|
||||
this.aiProcessSetting.dlgShow = false;
|
||||
// 可以选择是否刷新列表数据
|
||||
this.searchData();
|
||||
}, (res) => {
|
||||
this.$message.error('开启AI处理失败!');
|
||||
})
|
||||
},
|
||||
|
||||
_benchAiSet(courseList, successCB, failCB) {
|
||||
apiCourse.benchAiSet({courseList}).then(res => {
|
||||
if(res.status === 200){
|
||||
successCB && successCB(res);
|
||||
}else{
|
||||
failCB && failCB(res);
|
||||
}
|
||||
})
|
||||
},
|
||||
getAiPermission() {
|
||||
apiCourse.listByUser({}).then(res => {
|
||||
console.log('res', res);
|
||||
if(res.code === 200){
|
||||
let index = res.data.findIndex(item => item.permissionCode === 'KjbAiSetCode');
|
||||
this.aiPermission = index !== -1;
|
||||
console.log('index', index, this.aiPermission);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -953,4 +1366,22 @@ export default {
|
||||
.el-dialog__body {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.form-item{
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.tips {
|
||||
color: #f56c6c;
|
||||
font-size: 12px;
|
||||
margin: 10px 0;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.languages-list{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
245
src/views/course/aiSet/aiAbstract.vue
Normal file
@@ -0,0 +1,245 @@
|
||||
<template>
|
||||
<div class="aiAbstract">
|
||||
<div class="ai-left">
|
||||
<div class="left-title">{{courseName}}</div>
|
||||
<ul class="ai-list">
|
||||
<li class="ai-item" v-for="(item, index) in videoList" @click="currentVideo = item" :class="{'active': currentVideo.id === item.id}" :key="index">
|
||||
<div class="ai-item-title">{{item.name}}</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="ai-right">
|
||||
<div class="right-title">
|
||||
<h3>{{currentVideo.name}}</h3>
|
||||
<div>
|
||||
<el-button type="primary" @click="status = 1">下架本课程AI摘要</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ai-content">
|
||||
<div class="videoBox">
|
||||
<videoPlayer :src="testUrl" style="height: auto;"> </videoPlayer>
|
||||
<div class="video-content">
|
||||
<h4>视频摘要</h4>
|
||||
<p>
|
||||
{{currentVideo.aiAbstract}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="videoOperation">
|
||||
<div class="opera-title">
|
||||
<h4>课程摘要</h4>
|
||||
<div class="opera-btn">
|
||||
<el-button type="primary" plain round size="mini" @click="status = 2">重新生成</el-button>
|
||||
<el-button type="primary" plain round size="mini" @click="status = 4">编辑</el-button>
|
||||
<el-button plain round size="mini" @click="status = 3">取消</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="opera-content" v-show="status != 4">
|
||||
<span v-show="status == 1">{{ aiAbstract }}</span>
|
||||
<p v-show="status == 2" style="color: rgba(207, 207, 207, 1);text-align: center;margin-top: 48%;">AI 摘要重新生成中,过程可能耗时较长,<br>无需在此等待哦~</p>
|
||||
<img v-show="status == 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="status == 4"
|
||||
type="textarea"
|
||||
placeholder="请输入内容"
|
||||
autosize
|
||||
v-model="aiAbstract">
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import videoPlayer from "@/components/VideoPlayer/index.vue";
|
||||
export default {
|
||||
name: 'aiAbstract',
|
||||
// ai播放器相关
|
||||
components: {
|
||||
videoPlayer
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
courseName: '企业经营法则--课程单元',
|
||||
videoList: [
|
||||
{
|
||||
id: 1,
|
||||
name: '1. 开源节流1',
|
||||
aiAbstract: '人工智能(AI)是让计算机模拟人类智能的技术,核心包括机器学习、深度学习等。主要分为弱 AI(专注特定任务)和强 AI(通用智能)两类。应用涵盖医疗诊断、自动驾驶、语音助手等多个领域。它通过数据学习模式,实现预测和适应能力,正在改变生活方式和工作方式。未来发展需平衡创新与伦理考量,确保对人类社会有益。'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '2. 企业经营法则总述',
|
||||
aiAbstract: '本课程将介绍企业经营法则的总述,包括企业经营的基本原理、经营策略、经营模式等。'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '3. 企业经营法则总述',
|
||||
aiAbstract: '本课程将介绍企业经营法则的总述,包括企业经营的基本原理、经营策略、经营模式等。'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '4. 企业经营法则总述',
|
||||
aiAbstract: '本课程将介绍企业经营法则的总述,包括企业经营的基本原理、经营策略、经营模式等。'
|
||||
},
|
||||
],
|
||||
testUrl: 'https://vjs.zencdn.net/v/oceans.mp4',
|
||||
currentVideo: {},
|
||||
aiAbstract: '人工智能(AI)是让计算机模拟人类智能的技术,核心包括机器学习、深度学习等。主要分为弱 AI(专注特定任务)和强 AI(通用智能)两类。应用涵盖医疗诊断、自动驾驶、语音助手等多个领域。它通过数据学习模式,实现预测和适应能力,正在改变生活方式和工作方式。未来发展需平衡创新与伦理考量,确保对人类社会有益。',
|
||||
status: '1', // 1: 正常 2: 生成中 3: 错误 4: 编辑中
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.currentVideo = this.videoList[0];
|
||||
},
|
||||
methods: {}
|
||||
};
|
||||
</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;
|
||||
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>
|
||||
388
src/views/course/aiSet/aiTranslate.vue
Normal file
@@ -0,0 +1,388 @@
|
||||
<template>
|
||||
<div class="aiTranslate">
|
||||
<div class="ai-left">
|
||||
<div class="left-title">{{courseName}}</div>
|
||||
<ul class="ai-list">
|
||||
<li class="ai-item" v-for="(item, index) in videoList" @click="currentVideo = item" :class="{'active': currentVideo.id === item.id}" :key="index">
|
||||
<div class="ai-item-title">{{item.name}}</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="ai-right">
|
||||
<div class="right-title">
|
||||
<h3>{{currentVideo.name}}</h3>
|
||||
<div>
|
||||
<el-button type="primary" @click="status = 1">下架本课程AI翻译</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ai-content">
|
||||
<div class="videoBox">
|
||||
<videoPlayer :src="testUrl" style="height: auto;"> </videoPlayer>
|
||||
<div class="video-content">
|
||||
<div class="select-lang">
|
||||
<img src="@/assets/images/course/languageIcon.png" alt="" width="10" height="10">
|
||||
<span>本课程支持语种:</span>
|
||||
<el-button type="primary" @click="setLanguage()" icon="el-icon-connection">设置语种</el-button>
|
||||
</div>
|
||||
<div class="lang-content">
|
||||
<div class="lang-item" v-for="(item, index) in selectedLang" :key="index">
|
||||
<span>{{ item.label }}</span>
|
||||
<span :class="item.aiTranslate == 1 ? 'custom-putaway' : 'custom-takeout'">{{ item.aiTranslate == 1 ? '已上架' : '已下架' }}</span>
|
||||
<el-button type="text" @click="item.aiTranslate = item.aiTranslate == 1 ? 0 : 1">{{ item.aiTranslate == 1 ? '下架' : '上架' }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="videoOperation">
|
||||
<div class="opera-title">
|
||||
<span>目标语种</span>
|
||||
<el-select v-model="value" placeholder="请选择目标语种" style="flex: 1;">
|
||||
<el-option
|
||||
v-for="item in selectAllLang"
|
||||
:key="item.srclang"
|
||||
:label="item.label"
|
||||
:value="item.srclang"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
<el-button type="primary" @click="status = 2">加载字幕</el-button>
|
||||
</div>
|
||||
<div class="opera-content">
|
||||
<div class="bg-gray" v-show="status != 4">
|
||||
<p v-show="status == 1" v-html="aiTranslate"></p>
|
||||
<p v-show="status == 2" style="color: rgba(207, 207, 207, 1);text-align: center;margin-top: 48%;">AI 翻译重新生成中,过程可能耗时较长,<br>无需在此等待哦~</p>
|
||||
<img v-show="status == 3" src="@/assets/images/course/generationFailed.png" alt="" width="150" height="159" style="display: flex;margin: 35% auto 0 auto;">
|
||||
<img v-show="status == 5" src="@/assets/images/course/selectLanguage.png" alt="" width="112" height="130" style="display: flex;margin: 35% auto 0 auto;">
|
||||
</div>
|
||||
<el-input v-show="status == 4"
|
||||
type="textarea"
|
||||
placeholder="请输入内容"
|
||||
autosize
|
||||
v-model="aiTranslate">
|
||||
</el-input>
|
||||
<div class="opera-btn">
|
||||
<el-button v-show="status == 1" type="primary" plain round size="mini" @click="updateDialogVisible = true">重新生成</el-button>
|
||||
<el-button v-show="status == 1" type="primary" plain round size="mini" @click="status = 4">编辑</el-button>
|
||||
<el-button v-show="status == 4" plain round size="mini" @click="status = 1">取消</el-button>
|
||||
<el-button v-show="status == 4" type="primary" plain round size="mini" @click="status = 1">确认</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-dialog
|
||||
title="确认同步更新吗"
|
||||
:visible.sync="updateDialogVisible"
|
||||
width="420px"
|
||||
style="border-radius: 10px;"
|
||||
center>
|
||||
<p style="text-align: center;">系统将根据当前最新中文内容重新生成其他语种的翻译,您此前对翻译的修改将会丢失!</p>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="updateDialogVisible = false">取 消</el-button>
|
||||
<el-button style="margin-left: 60px;" type="primary" @click="updateDialogVisible = false">确认同步更新</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
<el-dialog
|
||||
title="AIf翻译"
|
||||
:visible.sync="selectDialogVisible"
|
||||
width="500px"
|
||||
class="select-dialog">
|
||||
<div class="select-dialog-content">
|
||||
<p>请选择该课程所支持语种</p>
|
||||
<el-select v-model="selectLang" placeholder="请选择目标语种" style="width: 100%;" multiple>
|
||||
<el-option
|
||||
v-for="item in selectAllLang"
|
||||
:key="item.srclang"
|
||||
:label="item.label"
|
||||
:value="item.srclang"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="selectDialogVisible = false">取 消</el-button>
|
||||
<el-button style="" type="primary" @click="selectDialogVisible = false">确 认</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import videoPlayer from "@/components/VideoPlayer/index.vue";
|
||||
import { mapGetters } from 'vuex';
|
||||
export default {
|
||||
name: 'aiTranslate',
|
||||
// ai播放器相关
|
||||
components: {
|
||||
videoPlayer
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
courseName: '企业经营法则--课程单元',
|
||||
videoList: [
|
||||
{
|
||||
id: 1,
|
||||
name: '1. 开源节流1',
|
||||
aiTranslate: '人工智能(AI)是让计算机模拟人类智能的技术,核心包括机器学习、深度学习等。主要分为弱 AI(专注特定任务)和强 AI(通用智能)两类。应用涵盖医疗诊断、自动驾驶、语音助手等多个领域。它通过数据学习模式,实现预测和适应能力,正在改变生活方式和工作方式。未来发展需平衡创新与伦理考量,确保对人类社会有益。'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '2. 企业经营法则总述',
|
||||
aiTranslate: '本课程将介绍企业经营法则的总述,包括企业经营的基本原理、经营策略、经营模式等。'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '3. 企业经营法则总述',
|
||||
aiTranslate: '本课程将介绍企业经营法则的总述,包括企业经营的基本原理、经营策略、经营模式等。'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '4. 企业经营法则总述',
|
||||
aiTranslate: '本课程将介绍企业经营法则的总述,包括企业经营的基本原理、经营策略、经营模式等。'
|
||||
},
|
||||
],
|
||||
testUrl: 'https://vjs.zencdn.net/v/oceans.mp4',
|
||||
currentVideo: {},
|
||||
aiTranslate: `
|
||||
00:00:01/00:00:03
|
||||
Hello everyone in the audience
|
||||
|
||||
00:00:03/00:00:05
|
||||
today I want to share with you the topic of
|
||||
|
||||
00:00:05/00:00:09
|
||||
"The Development History and Future Prospects of Computer Technology -
|
||||
`,
|
||||
status: '1', // 1: 正常 2: 生成中 3: 错误 4: 编辑中 5: 未选择语种
|
||||
selectedLang: [
|
||||
{
|
||||
label: '英文',
|
||||
srclang: 'en',
|
||||
aiTranslate: 1,
|
||||
},
|
||||
{
|
||||
label: '中文',
|
||||
srclang: 'zh',
|
||||
aiTranslate: 1,
|
||||
},
|
||||
{
|
||||
label: '日文',
|
||||
srclang: 'ja',
|
||||
aiTranslate: 1,
|
||||
},
|
||||
{
|
||||
label: '韩文',
|
||||
srclang: 'ko',
|
||||
aiTranslate: 1,
|
||||
},
|
||||
{
|
||||
label: '法文',
|
||||
srclang: 'fr',
|
||||
aiTranslate: 0,
|
||||
},
|
||||
],
|
||||
updateDialogVisible: false,
|
||||
selectDialogVisible: false,
|
||||
selectLang: [],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['selectAllLang']),
|
||||
},
|
||||
mounted() {
|
||||
this.currentVideo = this.videoList[0];
|
||||
},
|
||||
methods: {
|
||||
setLanguage(){
|
||||
this.selectDialogVisible = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.aiTranslate{
|
||||
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;
|
||||
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;
|
||||
.select-lang{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
color: rgba(107, 114, 128, 1);
|
||||
font-family: Noto Sans SC;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 17px;
|
||||
}
|
||||
.lang-content{
|
||||
display: flex;
|
||||
margin-top: 25px;
|
||||
align-items: center;
|
||||
gap: 25px 60px;
|
||||
flex-wrap: wrap;
|
||||
.lang-item{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
color: rgba(96, 98, 102, 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;
|
||||
gap: 15px;
|
||||
>span{
|
||||
color: rgba(75, 92, 118, 1);
|
||||
font-family: Noto Sans SC;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
.opera-content{
|
||||
position: relative;
|
||||
flex: 1;
|
||||
color: rgba(17, 24, 39, 1);
|
||||
font-family: Noto Sans SC;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
letter-spacing: 0px;
|
||||
white-space: pre-wrap;
|
||||
.bg-gray{
|
||||
border-radius: 7px;
|
||||
background: rgba(249, 250, 251, 1);
|
||||
height: 100%;
|
||||
padding: 15px;
|
||||
}
|
||||
.opera-btn{
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
right: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
::v-deep .el-textarea{
|
||||
height: 100%!important;
|
||||
}
|
||||
::v-deep .el-textarea__inner{
|
||||
height: 100%!important;
|
||||
}
|
||||
::v-deep .el-dialog {
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.select-dialog{
|
||||
::v-deep .el-dialog__header{
|
||||
border-bottom: 1px solid rgba(229, 231, 235, 1);
|
||||
}
|
||||
.select-dialog-content{
|
||||
p{
|
||||
color: rgba(17, 24, 39, 1);
|
||||
font-family: Noto Sans SC;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -19,22 +19,27 @@
|
||||
<span>案例专家</span>
|
||||
<div class="window-control-btn">
|
||||
<el-button
|
||||
style="color:#96999f;margin-right: 6px;"
|
||||
style="color: #96999f; margin-right: 6px"
|
||||
type="text"
|
||||
@click="minimizeWindow"
|
||||
>
|
||||
<i class="el-icon-minus"></i>
|
||||
</el-button>
|
||||
<el-button
|
||||
type="text"
|
||||
style="color:#96999f;"
|
||||
>
|
||||
<img v-if="!dialogFullscreen" @click="onbigWindowClick" :src="openImg" alt="" style="width: 17px">
|
||||
<i v-else @click="onRestoreWindowClick" class="el-icon-copy-document"></i>
|
||||
<el-button type="text" style="color: #96999f">
|
||||
<img
|
||||
v-if="!dialogFullscreen"
|
||||
@click="onbigWindowClick"
|
||||
:src="openImg"
|
||||
alt=""
|
||||
style="width: 17px"
|
||||
/>
|
||||
<i
|
||||
v-else
|
||||
@click="onRestoreWindowClick"
|
||||
class="el-icon-copy-document"
|
||||
></i>
|
||||
</el-button>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content-wrapper" v-show="isLan">
|
||||
@@ -48,11 +53,31 @@
|
||||
@scroll="handleScroll"
|
||||
@wheel="handleWheel"
|
||||
>
|
||||
<div class="message-text" v-for="(item, index) in messageList" :key="index">
|
||||
<messages :messageData="item" @update:messageData="val=> $set(messageList, index, val)" :suggestions="suggestions" @getMinWindow="minimizeWindow" :isFirstMessage="item.isFirstMessage"></messages>
|
||||
<div
|
||||
class="message-text"
|
||||
v-for="(item, index) in messageList"
|
||||
:key="index"
|
||||
>
|
||||
<messages
|
||||
:messageData="item"
|
||||
@update:messageData="(val) => $set(messageList, index, val)"
|
||||
:suggestions="suggestions"
|
||||
@getMinWindow="minimizeWindow"
|
||||
:isFirstMessage="item.isFirstMessage"
|
||||
></messages>
|
||||
</div>
|
||||
<div class="message-suggestions" v-if="messageList.length > 0 && messageList[messageList.length-1].textCompleted">
|
||||
<div class="suggestion-item" v-for="(item, index) in suggestions" :key="index">
|
||||
<div
|
||||
class="message-suggestions"
|
||||
v-if="
|
||||
messageList.length > 0 &&
|
||||
messageList[messageList.length - 1].textCompleted
|
||||
"
|
||||
>
|
||||
<div
|
||||
class="suggestion-item"
|
||||
v-for="(item, index) in suggestions"
|
||||
:key="index"
|
||||
>
|
||||
<a @click="sendSuggestions(item)"> {{ item }} →</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -75,6 +100,8 @@
|
||||
@update-suggestions="updateSuggestions"
|
||||
@new-conversation="startNewConversation"
|
||||
:disabled="isLoading"
|
||||
:stopShow="stopShow"
|
||||
:sendShow="sendShow"
|
||||
class="input-area-wrapper"
|
||||
ref="sendMessage"
|
||||
/>
|
||||
@@ -95,7 +122,7 @@
|
||||
class="window-control-btn"
|
||||
@click.stop="onMinimizedWindowClick"
|
||||
>
|
||||
<img :src="openImg" alt="" style="width: 17px">
|
||||
<img :src="openImg" alt="" style="width: 17px" />
|
||||
</el-button>
|
||||
|
||||
<el-button
|
||||
@@ -121,47 +148,48 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import messages from './components/messages.vue'
|
||||
import sendMessage from './components/sendMessage.vue'
|
||||
import openImg from './components/open.png'
|
||||
import { mapState } from "vuex";
|
||||
import messages from "./components/messages.vue";
|
||||
import sendMessage from "./components/sendMessage.vue";
|
||||
import openImg from "./components/open.png";
|
||||
import LanServiceChecker from "@/components/LanServiceChecker.vue";
|
||||
export default {
|
||||
name: 'CaseExpertDialog',
|
||||
name: "CaseExpertDialog",
|
||||
props: {
|
||||
dialogVisible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
components: {
|
||||
LanServiceChecker,
|
||||
messages,
|
||||
sendMessage
|
||||
sendMessage,
|
||||
},
|
||||
directives: {
|
||||
draggable: {
|
||||
bind(el, binding, vnode) {
|
||||
vnode.context.$nextTick(() => {
|
||||
const dialogEl = el.querySelector('.el-dialog');
|
||||
const dialogEl = el.querySelector(".el-dialog");
|
||||
if (!dialogEl) return;
|
||||
|
||||
const headerEl = dialogEl.querySelector('.dialog-title');
|
||||
const headerEl = dialogEl.querySelector(".dialog-title");
|
||||
if (!headerEl) return;
|
||||
|
||||
// 检查是否有保存的位置状态
|
||||
const savedPosition = sessionStorage.getItem('aiCallDialogPosition');
|
||||
const savedPosition = sessionStorage.getItem("aiCallDialogPosition");
|
||||
if (savedPosition) {
|
||||
const { left, top } = JSON.parse(savedPosition);
|
||||
dialogEl.style.left = left + 'px';
|
||||
dialogEl.style.top = top + 'px';
|
||||
dialogEl.style.left = left + "px";
|
||||
dialogEl.style.top = top + "px";
|
||||
} else {
|
||||
// 设置初始样式
|
||||
dialogEl.style.position = 'fixed';
|
||||
dialogEl.style.top = '100px';
|
||||
dialogEl.style.left = (window.innerWidth - dialogEl.offsetWidth) / 2 + 'px';
|
||||
dialogEl.style.position = "fixed";
|
||||
dialogEl.style.top = "100px";
|
||||
dialogEl.style.left =
|
||||
(window.innerWidth - dialogEl.offsetWidth) / 2 + "px";
|
||||
}
|
||||
dialogEl.style.margin = '0';
|
||||
dialogEl.style.margin = "0";
|
||||
|
||||
let isDragging = false;
|
||||
let startX = 0;
|
||||
@@ -170,10 +198,10 @@ export default {
|
||||
let startTop = 0;
|
||||
const startDrag = (event) => {
|
||||
// 只有在标题栏上按下鼠标才开始拖动
|
||||
if (event.target.closest('.resize-handle')) {
|
||||
if (event.target.closest(".resize-handle")) {
|
||||
return; // 如果点击的是resize-handle,则不触发拖动
|
||||
}
|
||||
if (event.target.closest('.window-control-btn')) {
|
||||
if (event.target.closest(".window-control-btn")) {
|
||||
return; // 如果点击的是控制按钮,则不触发拖动
|
||||
}
|
||||
// 全屏状态下拖动为正常弹窗大小
|
||||
@@ -191,8 +219,8 @@ export default {
|
||||
event.stopPropagation();
|
||||
|
||||
// 添加全局事件监听
|
||||
document.addEventListener('mousemove', handleMouseMove);
|
||||
document.addEventListener('mouseup', stopDrag);
|
||||
document.addEventListener("mousemove", handleMouseMove);
|
||||
document.addEventListener("mouseup", stopDrag);
|
||||
};
|
||||
|
||||
const handleMouseMove = (event) => {
|
||||
@@ -201,8 +229,26 @@ export default {
|
||||
const deltaX = event.clientX - startX;
|
||||
const deltaY = event.clientY - startY;
|
||||
|
||||
dialogEl.style.left = (startLeft + deltaX) + 'px';
|
||||
dialogEl.style.top = (startTop + deltaY) + 'px';
|
||||
// 计算新位置
|
||||
let newLeft = startLeft + deltaX;
|
||||
let newTop = startTop + deltaY;
|
||||
|
||||
// 获取对话框尺寸
|
||||
const dialogWidth = dialogEl.offsetWidth;
|
||||
const dialogHeight = dialogEl.offsetHeight;
|
||||
|
||||
// 限制拖拽范围在浏览器可视区域内
|
||||
// 左边界
|
||||
newLeft = Math.max(-500, newLeft);
|
||||
// 右边界
|
||||
newLeft = Math.min(window.innerWidth - dialogWidth, newLeft);
|
||||
// 上边界
|
||||
newTop = Math.max(0, newTop);
|
||||
// 下边界 (确保标题栏始终可见,标题栏高度约为80px)
|
||||
newTop = Math.min(window.innerHeight - 80, newTop);
|
||||
|
||||
dialogEl.style.left = newLeft + "px";
|
||||
dialogEl.style.top = newTop + "px";
|
||||
};
|
||||
|
||||
const stopDrag = () => {
|
||||
@@ -210,114 +256,113 @@ export default {
|
||||
// 保存当前位置到 sessionStorage
|
||||
const currentPosition = {
|
||||
left: parseInt(dialogEl.style.left),
|
||||
top: parseInt(dialogEl.style.top)
|
||||
top: parseInt(dialogEl.style.top),
|
||||
};
|
||||
// sessionStorage.setItem('aiCallDialogPosition', JSON.stringify(currentPosition));
|
||||
// 移除全局事件监听
|
||||
document.removeEventListener('mousemove', handleMouseMove);
|
||||
document.removeEventListener('mouseup', stopDrag);
|
||||
document.removeEventListener("mousemove", handleMouseMove);
|
||||
document.removeEventListener("mouseup", stopDrag);
|
||||
};
|
||||
|
||||
// 为标题栏绑定拖动事件
|
||||
headerEl.addEventListener('mousedown', startDrag);
|
||||
headerEl.addEventListener("mousedown", startDrag);
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
resizeable: {
|
||||
bind(el, binding, vnode) {
|
||||
// 确保元素已插入DOM
|
||||
vnode.context.$nextTick(() => {
|
||||
const dialogEl = el.querySelector('.el-dialog');
|
||||
const dialogEl = el.querySelector(".el-dialog");
|
||||
if (!dialogEl) return;
|
||||
|
||||
// 检查是否有保存的尺寸状态
|
||||
const savedSize = sessionStorage.getItem('aiCallDialogSize');
|
||||
const savedSize = sessionStorage.getItem("aiCallDialogSize");
|
||||
if (savedSize) {
|
||||
const { width, height, left, top } = JSON.parse(savedSize);
|
||||
dialogEl.style.width = width + 'px';
|
||||
dialogEl.style.height = height + 'px';
|
||||
dialogEl.style.left = left + 'px';
|
||||
dialogEl.style.top = top + 'px';
|
||||
dialogEl.style.width = width + "px";
|
||||
dialogEl.style.height = height + "px";
|
||||
dialogEl.style.left = left + "px";
|
||||
dialogEl.style.top = top + "px";
|
||||
} else {
|
||||
// 设置初始样式
|
||||
dialogEl.style.position = 'fixed';
|
||||
dialogEl.style.top = '100px';
|
||||
dialogEl.style.left = (window.innerWidth - dialogEl.offsetWidth) / 2 + 'px';
|
||||
dialogEl.style.position = "fixed";
|
||||
dialogEl.style.top = "100px";
|
||||
dialogEl.style.left =
|
||||
(window.innerWidth - dialogEl.offsetWidth) / 2 + "px";
|
||||
}
|
||||
|
||||
// 创建拖拽手柄
|
||||
const createHandle = (direction) => {
|
||||
const handle = document.createElement('div');
|
||||
const handle = document.createElement("div");
|
||||
handle.className = `resize-handle ${direction}`;
|
||||
handle.style.position = 'absolute';
|
||||
handle.style.zIndex = '10';
|
||||
handle.style.position = "absolute";
|
||||
handle.style.zIndex = "10";
|
||||
|
||||
switch (direction) {
|
||||
case 'left':
|
||||
case 'right':
|
||||
handle.style.width = '6px';
|
||||
handle.style.height = '100%';
|
||||
handle.style.top = '0';
|
||||
handle.style.cursor = 'ew-resize';
|
||||
case "left":
|
||||
case "right":
|
||||
handle.style.width = "6px";
|
||||
handle.style.height = "100%";
|
||||
handle.style.top = "0";
|
||||
handle.style.cursor = "ew-resize";
|
||||
break;
|
||||
case 'top':
|
||||
case 'bottom':
|
||||
handle.style.width = '100%';
|
||||
handle.style.height = '6px';
|
||||
handle.style.left = '0';
|
||||
handle.style.cursor = 'ns-resize';
|
||||
case "top":
|
||||
case "bottom":
|
||||
handle.style.width = "100%";
|
||||
handle.style.height = "6px";
|
||||
handle.style.left = "0";
|
||||
handle.style.cursor = "ns-resize";
|
||||
break;
|
||||
case 'top-left':
|
||||
case 'top-right':
|
||||
case 'bottom-left':
|
||||
case 'bottom-right':
|
||||
handle.style.width = '10px';
|
||||
handle.style.height = '10px';
|
||||
handle.style.zIndex = '20';
|
||||
case "top-left":
|
||||
case "top-right":
|
||||
case "bottom-left":
|
||||
case "bottom-right":
|
||||
handle.style.width = "10px";
|
||||
handle.style.height = "10px";
|
||||
handle.style.zIndex = "20";
|
||||
break;
|
||||
}
|
||||
|
||||
switch (direction) {
|
||||
case 'left':
|
||||
handle.style.left = '0';
|
||||
case "left":
|
||||
handle.style.left = "0";
|
||||
break;
|
||||
case 'right':
|
||||
handle.style.right = '0';
|
||||
case "right":
|
||||
handle.style.right = "0";
|
||||
break;
|
||||
case 'top':
|
||||
handle.style.top = '0';
|
||||
case "top":
|
||||
handle.style.top = "0";
|
||||
break;
|
||||
case 'bottom':
|
||||
handle.style.bottom = '0';
|
||||
case "bottom":
|
||||
handle.style.bottom = "0";
|
||||
break;
|
||||
case 'top-left':
|
||||
handle.style.top = '0';
|
||||
handle.style.left = '0';
|
||||
handle.style.cursor = 'nw-resize';
|
||||
case "top-left":
|
||||
handle.style.top = "0";
|
||||
handle.style.left = "0";
|
||||
handle.style.cursor = "nw-resize";
|
||||
break;
|
||||
case 'top-right':
|
||||
handle.style.top = '0';
|
||||
handle.style.right = '0';
|
||||
handle.style.cursor = 'ne-resize';
|
||||
case "top-right":
|
||||
handle.style.top = "0";
|
||||
handle.style.right = "0";
|
||||
handle.style.cursor = "ne-resize";
|
||||
break;
|
||||
case 'bottom-left':
|
||||
handle.style.bottom = '0';
|
||||
handle.style.left = '0';
|
||||
handle.style.cursor = 'sw-resize';
|
||||
case "bottom-left":
|
||||
handle.style.bottom = "0";
|
||||
handle.style.left = "0";
|
||||
handle.style.cursor = "sw-resize";
|
||||
break;
|
||||
case 'bottom-right':
|
||||
handle.style.bottom = '0';
|
||||
handle.style.right = '0';
|
||||
handle.style.cursor = 'se-resize';
|
||||
case "bottom-right":
|
||||
handle.style.bottom = "0";
|
||||
handle.style.right = "0";
|
||||
handle.style.cursor = "se-resize";
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// 防止拖拽手柄的事件冒泡到标题栏
|
||||
handle.addEventListener('mousedown', (e) => {
|
||||
handle.addEventListener("mousedown", (e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
});
|
||||
|
||||
dialogEl.appendChild(handle);
|
||||
@@ -326,19 +371,19 @@ export default {
|
||||
|
||||
// 创建8个拖拽手柄
|
||||
const handles = {
|
||||
left: createHandle('left'),
|
||||
right: createHandle('right'),
|
||||
top: createHandle('top'),
|
||||
bottom: createHandle('bottom'),
|
||||
topLeft: createHandle('top-left'),
|
||||
topRight: createHandle('top-right'),
|
||||
bottomLeft: createHandle('bottom-left'),
|
||||
bottomRight: createHandle('bottom-right')
|
||||
left: createHandle("left"),
|
||||
right: createHandle("right"),
|
||||
top: createHandle("top"),
|
||||
bottom: createHandle("bottom"),
|
||||
topLeft: createHandle("top-left"),
|
||||
topRight: createHandle("top-right"),
|
||||
bottomLeft: createHandle("bottom-left"),
|
||||
bottomRight: createHandle("bottom-right"),
|
||||
};
|
||||
|
||||
// 添加拖拽事件处理
|
||||
let isResizing = false;
|
||||
let resizeDirection = '';
|
||||
let resizeDirection = "";
|
||||
let startX = 0;
|
||||
let startY = 0;
|
||||
let startWidth = 0;
|
||||
@@ -363,8 +408,8 @@ export default {
|
||||
startTop = parseInt(dialogEl.style.top) || 0;
|
||||
|
||||
// 添加全局事件监听
|
||||
document.addEventListener('mousemove', handleMouseMove);
|
||||
document.addEventListener('mouseup', stopResize);
|
||||
document.addEventListener("mousemove", handleMouseMove);
|
||||
document.addEventListener("mouseup", stopResize);
|
||||
};
|
||||
|
||||
const handleMouseMove = (event) => {
|
||||
@@ -376,71 +421,71 @@ export default {
|
||||
let newWidth, newHeight, newLeft, newTop;
|
||||
|
||||
switch (resizeDirection) {
|
||||
case 'right':
|
||||
case "right":
|
||||
newWidth = Math.max(400, startWidth + deltaX);
|
||||
dialogEl.style.width = newWidth + 'px';
|
||||
dialogEl.style.width = newWidth + "px";
|
||||
break;
|
||||
case 'left':
|
||||
case "left":
|
||||
newWidth = Math.max(400, startWidth - deltaX);
|
||||
newLeft = startLeft + startWidth - newWidth;
|
||||
dialogEl.style.width = newWidth + 'px';
|
||||
dialogEl.style.left = newLeft + 'px';
|
||||
dialogEl.style.width = newWidth + "px";
|
||||
dialogEl.style.left = newLeft + "px";
|
||||
break;
|
||||
case 'bottom':
|
||||
case "bottom":
|
||||
newHeight = Math.max(600, startHeight + deltaY);
|
||||
dialogEl.style.height = newHeight + 'px';
|
||||
dialogEl.style.height = newHeight + "px";
|
||||
break;
|
||||
case 'top':
|
||||
case "top":
|
||||
// 当窗口高度达到最小值时,不再调整高度和位置
|
||||
if (startHeight - deltaY >= 600) {
|
||||
newHeight = startHeight - deltaY;
|
||||
newTop = startTop + deltaY;
|
||||
dialogEl.style.height = newHeight + 'px';
|
||||
dialogEl.style.top = newTop + 'px';
|
||||
dialogEl.style.height = newHeight + "px";
|
||||
dialogEl.style.top = newTop + "px";
|
||||
}
|
||||
break;
|
||||
case 'bottom-right':
|
||||
case "bottom-right":
|
||||
newWidth = Math.max(400, startWidth + deltaX);
|
||||
newHeight = Math.max(600, startHeight + deltaY);
|
||||
dialogEl.style.width = newWidth + 'px';
|
||||
dialogEl.style.height = newHeight + 'px';
|
||||
dialogEl.style.width = newWidth + "px";
|
||||
dialogEl.style.height = newHeight + "px";
|
||||
break;
|
||||
case 'bottom-left':
|
||||
case "bottom-left":
|
||||
newWidth = Math.max(400, startWidth - deltaX);
|
||||
newHeight = Math.max(600, startHeight + deltaY);
|
||||
newLeft = startLeft + startWidth - newWidth;
|
||||
dialogEl.style.width = newWidth + 'px';
|
||||
dialogEl.style.left = newLeft + 'px';
|
||||
dialogEl.style.height = newHeight + 'px';
|
||||
dialogEl.style.width = newWidth + "px";
|
||||
dialogEl.style.left = newLeft + "px";
|
||||
dialogEl.style.height = newHeight + "px";
|
||||
break;
|
||||
case 'top-right':
|
||||
case "top-right":
|
||||
// 当窗口高度达到最小值时,不再调整高度和位置
|
||||
if (startHeight - deltaY >= 600) {
|
||||
newHeight = startHeight - deltaY;
|
||||
newTop = startTop + deltaY;
|
||||
newWidth = Math.max(400, startWidth + deltaX);
|
||||
dialogEl.style.height = newHeight + 'px';
|
||||
dialogEl.style.top = newTop + 'px';
|
||||
dialogEl.style.width = newWidth + 'px';
|
||||
dialogEl.style.height = newHeight + "px";
|
||||
dialogEl.style.top = newTop + "px";
|
||||
dialogEl.style.width = newWidth + "px";
|
||||
}
|
||||
break;
|
||||
case 'top-left':
|
||||
case "top-left":
|
||||
// 当窗口高度达到最小值时,不再调整高度和位置
|
||||
if (startHeight - deltaY >= 600) {
|
||||
newHeight = startHeight - deltaY;
|
||||
newTop = startTop + deltaY;
|
||||
newWidth = Math.max(400, startWidth - deltaX);
|
||||
newLeft = startLeft + startWidth - newWidth;
|
||||
dialogEl.style.height = newHeight + 'px';
|
||||
dialogEl.style.top = newTop + 'px';
|
||||
dialogEl.style.width = newWidth + 'px';
|
||||
dialogEl.style.left = newLeft + 'px';
|
||||
dialogEl.style.height = newHeight + "px";
|
||||
dialogEl.style.top = newTop + "px";
|
||||
dialogEl.style.width = newWidth + "px";
|
||||
dialogEl.style.left = newLeft + "px";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
let doc = document.querySelector('.welcome-message')
|
||||
let sendBox = document.querySelector('.input-area-wrapper');
|
||||
let doc = document.querySelector(".welcome-message");
|
||||
let sendBox = document.querySelector(".input-area-wrapper");
|
||||
// sendBox 的高度
|
||||
if (doc && sendBox) {
|
||||
doc.style.height = `calc(${dialogEl.style.height} - ${sendBox.offsetHeight}px - 120px)`;
|
||||
@@ -449,50 +494,71 @@ export default {
|
||||
|
||||
const stopResize = () => {
|
||||
isResizing = false;
|
||||
resizeDirection = '';
|
||||
resizeDirection = "";
|
||||
|
||||
// 保存当前尺寸和位置到 sessionStorage
|
||||
const currentSize = {
|
||||
width: parseInt(dialogEl.style.width),
|
||||
height: parseInt(dialogEl.style.height),
|
||||
left: parseInt(dialogEl.style.left),
|
||||
top: parseInt(dialogEl.style.top)
|
||||
top: parseInt(dialogEl.style.top),
|
||||
};
|
||||
sessionStorage.setItem('aiCallDialogSize', JSON.stringify(currentSize));
|
||||
sessionStorage.setItem(
|
||||
"aiCallDialogSize",
|
||||
JSON.stringify(currentSize)
|
||||
);
|
||||
|
||||
// 移除全局事件监听
|
||||
document.removeEventListener('mousemove', handleMouseMove);
|
||||
document.removeEventListener('mouseup', stopResize);
|
||||
document.removeEventListener("mousemove", handleMouseMove);
|
||||
document.removeEventListener("mouseup", stopResize);
|
||||
};
|
||||
|
||||
// 为每个手柄绑定事件
|
||||
handles.left.addEventListener('mousedown', (e) => startResize('left', e));
|
||||
handles.right.addEventListener('mousedown', (e) => startResize('right', e));
|
||||
handles.top.addEventListener('mousedown', (e) => startResize('top', e));
|
||||
handles.bottom.addEventListener('mousedown', (e) => startResize('bottom', e));
|
||||
handles.topLeft.addEventListener('mousedown', (e) => startResize('top-left', e));
|
||||
handles.topRight.addEventListener('mousedown', (e) => startResize('top-right', e));
|
||||
handles.bottomLeft.addEventListener('mousedown', (e) => startResize('bottom-left', e));
|
||||
handles.bottomRight.addEventListener('mousedown', (e) => startResize('bottom-right', e));
|
||||
handles.left.addEventListener("mousedown", (e) =>
|
||||
startResize("left", e)
|
||||
);
|
||||
handles.right.addEventListener("mousedown", (e) =>
|
||||
startResize("right", e)
|
||||
);
|
||||
handles.top.addEventListener("mousedown", (e) =>
|
||||
startResize("top", e)
|
||||
);
|
||||
handles.bottom.addEventListener("mousedown", (e) =>
|
||||
startResize("bottom", e)
|
||||
);
|
||||
handles.topLeft.addEventListener("mousedown", (e) =>
|
||||
startResize("top-left", e)
|
||||
);
|
||||
handles.topRight.addEventListener("mousedown", (e) =>
|
||||
startResize("top-right", e)
|
||||
);
|
||||
handles.bottomLeft.addEventListener("mousedown", (e) =>
|
||||
startResize("bottom-left", e)
|
||||
);
|
||||
handles.bottomRight.addEventListener("mousedown", (e) =>
|
||||
startResize("bottom-right", e)
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState('app', ['showAICallMinimized']),
|
||||
...mapState("app", ["showAICallMinimized"]),
|
||||
showMinimizedWindow() {
|
||||
// 只有在Vuex状态为true时才显示最小化窗口
|
||||
return this.showAICallMinimized;
|
||||
}
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialogFullscreen: false,
|
||||
isLan: true,
|
||||
openImg,
|
||||
AIContent: '',
|
||||
AIContent: "",
|
||||
isLoading: false,
|
||||
windowState: 'maximized', // 'maximized' 或 'minimized'
|
||||
stopShow: false,
|
||||
sendShow: true,
|
||||
windowState: "maximized", // 'maximized' 或 'minimized'
|
||||
messageList: [
|
||||
{
|
||||
typing: true,
|
||||
@@ -501,14 +567,14 @@ export default {
|
||||
text: `<p><b>您好!我是京东方案例智能问答助手,随时为您服务。</b></p>
|
||||
<p>我可以帮您快速查找和解读平台内的各类案例内容。只需输入您想了解的问题或关键词,我会从案例库中精准匹配相关信息,并提供清晰的解答。每条回答都会附上来源链接,方便您随时查阅原始案例全文。</p>
|
||||
<p>我还会根据您的提问,智能推荐相关延伸问题,助您更高效地探索知识、解决问题。</p>
|
||||
<p>现在,欢迎随时向我提问,开启高效的知识查询体验吧!</p>`
|
||||
}
|
||||
<p>现在,欢迎随时向我提问,开启高效的知识查询体验吧!</p>`,
|
||||
},
|
||||
],
|
||||
suggestions: [],
|
||||
isAutoScroll: true, // 是否自动滚动
|
||||
// 添加一个标志位,用于标识组件是否已经初始化完成
|
||||
isComponentReady: false
|
||||
}
|
||||
isComponentReady: false,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
// 组件挂载完成后,标记为已准备就绪
|
||||
@@ -519,22 +585,24 @@ export default {
|
||||
watch: {
|
||||
dialogVisible: {
|
||||
handler(newVal) {
|
||||
console.log('dialogVisible发生变化');
|
||||
console.log("dialogVisible发生变化");
|
||||
console.log(newVal);
|
||||
console.log(this.windowState);
|
||||
if (newVal) {
|
||||
this.$nextTick(() => {
|
||||
// 获取对话框元素
|
||||
const dialogEl = document.querySelector('.case-expert-dialog .el-dialog');
|
||||
const dialogEl = document.querySelector(
|
||||
".case-expert-dialog .el-dialog"
|
||||
);
|
||||
if (dialogEl) {
|
||||
// 检查是否有保存的尺寸状态
|
||||
const savedSize = sessionStorage.getItem('aiCallDialogSize');
|
||||
const savedSize = sessionStorage.getItem("aiCallDialogSize");
|
||||
if (savedSize) {
|
||||
const { width, height, left, top } = JSON.parse(savedSize);
|
||||
dialogEl.style.width = width + 'px';
|
||||
dialogEl.style.height = height + 'px';
|
||||
dialogEl.style.left = left + 'px';
|
||||
dialogEl.style.top = top + 'px';
|
||||
dialogEl.style.width = width + "px";
|
||||
dialogEl.style.height = height + "px";
|
||||
dialogEl.style.left = left + "px";
|
||||
dialogEl.style.top = top + "px";
|
||||
}
|
||||
|
||||
// 检查是否有保存的位置状态
|
||||
@@ -546,11 +614,11 @@ export default {
|
||||
// }
|
||||
}
|
||||
|
||||
let doc = document.querySelector('.welcome-message')
|
||||
let sendBox = document.querySelector('.input-area-wrapper');
|
||||
let doc = document.querySelector(".welcome-message");
|
||||
let sendBox = document.querySelector(".input-area-wrapper");
|
||||
// 只有在没有保存的尺寸状态时才使用默认值
|
||||
if (doc && sendBox) {
|
||||
const savedSize = sessionStorage.getItem('aiCallDialogSize');
|
||||
const savedSize = sessionStorage.getItem("aiCallDialogSize");
|
||||
if (!savedSize) {
|
||||
doc.style.height = `calc(600px - ${sendBox.offsetHeight}px - 120px)`;
|
||||
} else {
|
||||
@@ -561,7 +629,7 @@ export default {
|
||||
});
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
immediate: true,
|
||||
},
|
||||
messageList: {
|
||||
handler() {
|
||||
@@ -572,23 +640,28 @@ export default {
|
||||
});
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onbigWindowClick() {
|
||||
console.log('放大');
|
||||
console.log("放大");
|
||||
// 保存当前非全屏状态的尺寸和位置(仅当当前不是全屏状态时)
|
||||
if (!this.dialogFullscreen) {
|
||||
const dialogEl = document.querySelector('.case-expert-dialog .el-dialog');
|
||||
const dialogEl = document.querySelector(
|
||||
".case-expert-dialog .el-dialog"
|
||||
);
|
||||
if (dialogEl) {
|
||||
const normalSize = {
|
||||
width: parseInt(dialogEl.style.width) || dialogEl.offsetWidth,
|
||||
height: parseInt(dialogEl.style.height) || dialogEl.offsetHeight,
|
||||
left: parseInt(dialogEl.style.left) || dialogEl.offsetLeft,
|
||||
top: parseInt(dialogEl.style.top) || dialogEl.offsetTop
|
||||
top: parseInt(dialogEl.style.top) || dialogEl.offsetTop,
|
||||
};
|
||||
sessionStorage.setItem('aiCallDialogNormalSize', JSON.stringify(normalSize));
|
||||
sessionStorage.setItem(
|
||||
"aiCallDialogNormalSize",
|
||||
JSON.stringify(normalSize)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -597,16 +670,18 @@ export default {
|
||||
|
||||
// 设置全屏尺寸和位置
|
||||
this.$nextTick(() => {
|
||||
const dialogEl = document.querySelector('.case-expert-dialog .el-dialog');
|
||||
const dialogEl = document.querySelector(
|
||||
".case-expert-dialog .el-dialog"
|
||||
);
|
||||
if (dialogEl) {
|
||||
dialogEl.style.width = '100vw';
|
||||
dialogEl.style.height = '100vh';
|
||||
dialogEl.style.left = '0px';
|
||||
dialogEl.style.top = '0px';
|
||||
dialogEl.style.width = "100vw";
|
||||
dialogEl.style.height = "100vh";
|
||||
dialogEl.style.left = "0px";
|
||||
dialogEl.style.top = "0px";
|
||||
|
||||
// 更新消息容器高度
|
||||
const messageContainer = document.querySelector('.welcome-message');
|
||||
const inputArea = document.querySelector('.input-area-wrapper');
|
||||
const messageContainer = document.querySelector(".welcome-message");
|
||||
const inputArea = document.querySelector(".input-area-wrapper");
|
||||
if (messageContainer && inputArea) {
|
||||
messageContainer.style.height = `calc(100vh - ${inputArea.offsetHeight}px - 120px)`;
|
||||
}
|
||||
@@ -614,45 +689,49 @@ export default {
|
||||
});
|
||||
},
|
||||
onRestoreWindowClick() {
|
||||
console.log('缩小');
|
||||
console.log("缩小");
|
||||
this.dialogFullscreen = false;
|
||||
|
||||
this.$nextTick(() => {
|
||||
const dialogEl = document.querySelector('.case-expert-dialog .el-dialog');
|
||||
const dialogEl = document.querySelector(
|
||||
".case-expert-dialog .el-dialog"
|
||||
);
|
||||
if (dialogEl) {
|
||||
// 从 sessionStorage 中获取保存的正常窗口尺寸和位置
|
||||
const savedNormalSize = sessionStorage.getItem('aiCallDialogNormalSize');
|
||||
const savedSize = sessionStorage.getItem('aiCallDialogSize');
|
||||
const savedPosition = sessionStorage.getItem('aiCallDialogPosition');
|
||||
const savedNormalSize = sessionStorage.getItem(
|
||||
"aiCallDialogNormalSize"
|
||||
);
|
||||
const savedSize = sessionStorage.getItem("aiCallDialogSize");
|
||||
const savedPosition = sessionStorage.getItem("aiCallDialogPosition");
|
||||
|
||||
if (savedNormalSize) {
|
||||
// 使用之前保存的正常尺寸
|
||||
const { width, height, left, top } = JSON.parse(savedNormalSize);
|
||||
dialogEl.style.width = width + 'px';
|
||||
dialogEl.style.height = height + 'px';
|
||||
dialogEl.style.left = left + 'px';
|
||||
dialogEl.style.top = top + 'px';
|
||||
dialogEl.style.width = width + "px";
|
||||
dialogEl.style.height = height + "px";
|
||||
dialogEl.style.left = left + "px";
|
||||
dialogEl.style.top = top + "px";
|
||||
} else if (savedSize) {
|
||||
// 回退到通用保存的尺寸
|
||||
const { width, height, left, top } = JSON.parse(savedSize);
|
||||
dialogEl.style.width = width + 'px';
|
||||
dialogEl.style.height = height + 'px';
|
||||
dialogEl.style.left = left + 'px';
|
||||
dialogEl.style.top = top + 'px';
|
||||
dialogEl.style.width = width + "px";
|
||||
dialogEl.style.height = height + "px";
|
||||
dialogEl.style.left = left + "px";
|
||||
dialogEl.style.top = top + "px";
|
||||
} else {
|
||||
// 如果没有保存的尺寸,则使用默认值
|
||||
dialogEl.style.width = '800px';
|
||||
dialogEl.style.height = '600px';
|
||||
dialogEl.style.left = (window.innerWidth - 800) / 2 + 'px';
|
||||
dialogEl.style.top = '100px';
|
||||
dialogEl.style.width = "800px";
|
||||
dialogEl.style.height = "600px";
|
||||
dialogEl.style.left = (window.innerWidth - 800) / 2 + "px";
|
||||
dialogEl.style.top = "100px";
|
||||
}
|
||||
|
||||
// 应用相应的消息容器高度
|
||||
this.$nextTick(() => {
|
||||
const messageContainer = document.querySelector('.welcome-message');
|
||||
const inputArea = document.querySelector('.input-area-wrapper');
|
||||
const messageContainer = document.querySelector(".welcome-message");
|
||||
const inputArea = document.querySelector(".input-area-wrapper");
|
||||
if (messageContainer && inputArea) {
|
||||
const dialogHeight = dialogEl.style.height || '600px';
|
||||
const dialogHeight = dialogEl.style.height || "600px";
|
||||
messageContainer.style.height = `calc(${dialogHeight} - ${inputArea.offsetHeight}px - 120px)`;
|
||||
}
|
||||
});
|
||||
@@ -662,33 +741,33 @@ onRestoreWindowClick(){
|
||||
|
||||
// / 关闭最小化窗口
|
||||
closeMinimizedWindow() {
|
||||
this.$store.commit('app/SET_SHOW_AI_CALL_MINIMIZED', false);
|
||||
this.$store.commit('app/SET_SHOW_AI_CALL', false);
|
||||
this.windowState = 'maximized';
|
||||
this.$store.commit("app/SET_SHOW_AI_CALL_MINIMIZED", false);
|
||||
this.$store.commit("app/SET_SHOW_AI_CALL", false);
|
||||
this.windowState = "maximized";
|
||||
},
|
||||
getMinWidow(vis) {
|
||||
// this.showAICallMinimized = vis
|
||||
this.windowState = 'minimized';
|
||||
this.windowState = "minimized";
|
||||
},
|
||||
onClose() {
|
||||
console.log('关闭弹窗')
|
||||
console.log("关闭弹窗");
|
||||
// 清除保存的状态
|
||||
sessionStorage.removeItem('aiCallDialogSize');
|
||||
sessionStorage.removeItem("aiCallDialogSize");
|
||||
// sessionStorage.removeItem('aiCallDialogPosition');
|
||||
this.$emit('close')
|
||||
this.dialogFullscreen=false
|
||||
this.$emit("close");
|
||||
this.dialogFullscreen = false;
|
||||
// 可以在这里执行其他逻辑
|
||||
},
|
||||
|
||||
minimizeWindow() {
|
||||
console.log(131);
|
||||
|
||||
this.windowState = 'minimized';
|
||||
this.$store.commit('app/SET_SHOW_AI_CALL_MINIMIZED', true);
|
||||
this.windowState = "minimized";
|
||||
this.$store.commit("app/SET_SHOW_AI_CALL_MINIMIZED", true);
|
||||
},
|
||||
|
||||
maximizeWindow() {
|
||||
this.windowState = 'maximized';
|
||||
this.windowState = "maximized";
|
||||
},
|
||||
|
||||
getLastUserMessage() {
|
||||
@@ -696,18 +775,20 @@ closeMinimizedWindow() {
|
||||
for (let i = this.messageList.length - 1; i >= 0; i--) {
|
||||
if (!this.messageList[i].isBot) {
|
||||
// 移除HTML标签只返回纯文本
|
||||
const tempDiv = document.createElement('div');
|
||||
const tempDiv = document.createElement("div");
|
||||
tempDiv.innerHTML = this.messageList[i].text;
|
||||
return tempDiv.textContent || tempDiv.innerText || '';
|
||||
return tempDiv.textContent || tempDiv.innerText || "";
|
||||
}
|
||||
}
|
||||
return '';
|
||||
return "";
|
||||
},
|
||||
|
||||
// 处理加载状态
|
||||
handleLoading(status) {
|
||||
console.log('handleLoading---'+status);
|
||||
console.log("handleLoading---" + status);
|
||||
this.isLoading = status;
|
||||
this.sendShow = !status;
|
||||
this.stopShow = status;
|
||||
},
|
||||
|
||||
// 更新消息
|
||||
@@ -716,16 +797,16 @@ closeMinimizedWindow() {
|
||||
// 这里不需要额外的操作
|
||||
},
|
||||
updateSuggestions(arr) {
|
||||
this.suggestions = arr
|
||||
this.suggestions = arr;
|
||||
},
|
||||
// 处理建议
|
||||
sendSuggestions(item) {
|
||||
// this.suggestions = []
|
||||
this.AIContent = item
|
||||
this.AIContent = item;
|
||||
setTimeout(() => {
|
||||
this.$refs.sendMessage.handleSend()
|
||||
this.AIContent = ''
|
||||
},500)
|
||||
this.$refs.sendMessage.handleSend();
|
||||
this.AIContent = "";
|
||||
}, 500);
|
||||
},
|
||||
startNewConversation() {
|
||||
// 重置对话时,先标记组件为未准备就绪状态
|
||||
@@ -738,10 +819,10 @@ closeMinimizedWindow() {
|
||||
text: `<p><b>您好!我是京东方案例智能问答助手,随时为您服务。</b></p>
|
||||
<p>我可以帮您快速查找和解读平台内的各类案例内容。只需输入您想了解的问题或关键词,我会从案例库中精准匹配相关信息,并提供清晰的解答。每条回答都会附上来源链接,方便您随时查阅原始案例全文。</p>
|
||||
<p>我还会根据您的提问,智能推荐相关延伸问题,助您更高效地探索知识、解决问题。</p>
|
||||
<p>现在,欢迎随时向我提问,开启高效的知识查询体验吧!</p>`
|
||||
}
|
||||
<p>现在,欢迎随时向我提问,开启高效的知识查询体验吧!</p>`,
|
||||
},
|
||||
];
|
||||
this.AIContent = '';
|
||||
this.AIContent = "";
|
||||
this.isLoading = false;
|
||||
|
||||
// 在下一个 tick 中重新标记为准备就绪
|
||||
@@ -754,7 +835,8 @@ closeMinimizedWindow() {
|
||||
handleScroll(event) {
|
||||
const element = event.target;
|
||||
// 判断是否滚动到底部
|
||||
const isAtBottom = element.scrollHeight - element.scrollTop <= element.clientHeight + 1;
|
||||
const isAtBottom =
|
||||
element.scrollHeight - element.scrollTop <= element.clientHeight + 1;
|
||||
|
||||
// 如果滚动到底部,则开启自动滚动
|
||||
// 如果离开底部,则关闭自动滚动
|
||||
@@ -764,7 +846,8 @@ closeMinimizedWindow() {
|
||||
// 滚动到底部
|
||||
scrollToBottom() {
|
||||
if (this.isAutoScroll && this.$refs.messageContainer) {
|
||||
this.$refs.messageContainer.scrollTop = this.$refs.messageContainer.scrollHeight;
|
||||
this.$refs.messageContainer.scrollTop =
|
||||
this.$refs.messageContainer.scrollHeight;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -783,7 +866,10 @@ closeMinimizedWindow() {
|
||||
if (delta < 0 && element.scrollTop === 0) {
|
||||
// 向上滚动且已在顶部,阻止默认行为
|
||||
event.preventDefault();
|
||||
} else if (delta > 0 && element.scrollHeight - element.scrollTop <= element.clientHeight) {
|
||||
} else if (
|
||||
delta > 0 &&
|
||||
element.scrollHeight - element.scrollTop <= element.clientHeight
|
||||
) {
|
||||
// 向下滚动且已在底部,阻止默认行为
|
||||
event.preventDefault();
|
||||
} else {
|
||||
@@ -797,13 +883,13 @@ closeMinimizedWindow() {
|
||||
onMinimizedWindowClick() {
|
||||
// 当点击最小化窗口时,如果dialogVisible为false,则通过事件通知父组件显示对话框
|
||||
if (!this.dialogVisible) {
|
||||
this.$emit('restore');
|
||||
this.$emit("restore");
|
||||
}
|
||||
// 然后将窗口状态设置为最大化
|
||||
this.windowState = 'maximized';
|
||||
}
|
||||
}
|
||||
}
|
||||
this.windowState = "maximized";
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -857,7 +943,6 @@ closeMinimizedWindow() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.message-suggestions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -966,7 +1051,9 @@ closeMinimizedWindow() {
|
||||
}
|
||||
|
||||
@keyframes loading {
|
||||
0%, 80%, 100% {
|
||||
0%,
|
||||
80%,
|
||||
100% {
|
||||
transform: scale(0);
|
||||
}
|
||||
40% {
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
<img :src="messageData.isLike == 1 ? require('@/assets/images/case/zan-yes.svg') : require('@/assets/images/case/zan.svg')"
|
||||
@click="toggleStar(1)" alt="zan" class="zan_img">
|
||||
</el-tooltip>
|
||||
<el-tooltip class="item" effect="dark" content="点踩" placement="bottom">
|
||||
<el-popover placement="bottom" trigger="manual" v-model="caiDialogShow">
|
||||
<div class="feedback-class">
|
||||
<span>反馈</span>
|
||||
@@ -58,11 +59,11 @@
|
||||
<div style="text-align: right; margin: 0">
|
||||
<el-button style="margin-top: 10px;" type="primary" size="mini" @click="handleSure">确定</el-button>
|
||||
</div>
|
||||
<!-- 修改为根据 isLike 值判断状态,使用 == 而不是 === -->
|
||||
<img slot="reference"
|
||||
:src="messageData.isLike == -1 ? require('@/assets/images/case/cai-yes.svg') : require('@/assets/images/case/cai.svg')"
|
||||
@click="toggleStar(-1)" alt="cai" class="zan_img" style="margin-left:10px">
|
||||
</el-popover>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -195,6 +196,7 @@ export default {
|
||||
} else {
|
||||
newIsLike = type;
|
||||
if (type === -1) {
|
||||
this.feedbackText = ''
|
||||
this.caiDialogShow = true;
|
||||
} else {
|
||||
this.caiDialogShow = false;
|
||||
@@ -221,6 +223,10 @@ export default {
|
||||
},
|
||||
// 确定
|
||||
handleSure() {
|
||||
if(this.feedbackText == ''){
|
||||
this.$message({ type: "error", message: "请输入反馈内容"});
|
||||
return;
|
||||
}
|
||||
let params = {
|
||||
docId: this.messageData.docId,
|
||||
feedback: this.feedbackText
|
||||
|
||||
@@ -1,123 +1,245 @@
|
||||
<template>
|
||||
<div class="input-area">
|
||||
<el-input v-model="inputContent" type="textarea" class="input-placeholder" placeholder="有问题,尽管问"
|
||||
@keyup.enter.native.prevent="handleSend" :disabled="disabled" :autosize="{ minRows: 2, maxRows: 4 }"
|
||||
resize="none"></el-input>
|
||||
<el-input
|
||||
v-model="inputContent"
|
||||
type="textarea"
|
||||
class="input-placeholder"
|
||||
placeholder="有问题,尽管问"
|
||||
@keyup.enter.native.prevent="handleSend"
|
||||
:disabled="disabled"
|
||||
:autosize="{ minRows: 2, maxRows: 4 }"
|
||||
resize="none"
|
||||
></el-input>
|
||||
<div class="action-buttons">
|
||||
<el-button type="primary" size="small" class="start-btn" @click="handleNewConversation">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
class="start-btn"
|
||||
@click="handleNewConversation"
|
||||
>
|
||||
+ 开启新对话
|
||||
</el-button>
|
||||
<el-button type="text" class="send-btn" @click="handleSend" :disabled="disabled">
|
||||
<el-button
|
||||
type="text"
|
||||
class="send-btn"
|
||||
@click="handleSend"
|
||||
v-if="sendShow"
|
||||
>
|
||||
<i class="el-icon-s-promotion"></i>
|
||||
</el-button>
|
||||
<el-button
|
||||
type="text"
|
||||
class="send-btn"
|
||||
@click="handleStop"
|
||||
v-if="stopShow"
|
||||
>
|
||||
<img class="stop" src="@/assets/images/case/stop.png" alt="" />
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { aiChat } from '@/api/boe/aiChat.js'
|
||||
import { aiChat } from "@/api/boe/aiChat.js";
|
||||
import apiCase from "@/api/modules/cases.js";
|
||||
|
||||
export default {
|
||||
name: 'SendMessage',
|
||||
name: "SendMessage",
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
default: "",
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
default: false,
|
||||
},
|
||||
sendShow: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
stopShow: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
messageList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
default: () => [],
|
||||
},
|
||||
suggestions: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
inputContent: this.value,
|
||||
conversationId: '' // 会话ID
|
||||
}
|
||||
currentAbortController: null, // 添加abort controller引用
|
||||
isStopped: false, // 添加停止状态
|
||||
typingTimer: null, // 添加这一行
|
||||
thinkTypingTimer: null, // 添加这一行
|
||||
conversationId: "",
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
value(newVal) {
|
||||
this.inputContent = newVal
|
||||
}
|
||||
this.inputContent = newVal;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.getConversationId();
|
||||
},
|
||||
|
||||
methods: {
|
||||
getConversationId() {
|
||||
apiCase.initChat().then((res) => {
|
||||
if (res.status == 200) {
|
||||
const { result } = res;
|
||||
// console.log(result);
|
||||
this.conversationId = result;
|
||||
sessionStorage.setItem("conversationId", this.conversationId);
|
||||
}
|
||||
});
|
||||
},
|
||||
handleSend(event) {
|
||||
// 阻止事件的默认行为和冒泡
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
console.log('preventDefault');
|
||||
console.log("preventDefault");
|
||||
}
|
||||
console.log('handleSend');
|
||||
if (!this.inputContent.trim() || this.disabled) return
|
||||
console.log("handleSend");
|
||||
if (!this.inputContent.trim() || this.disabled) return;
|
||||
// 重置停止状态,确保可以正常发送新消息
|
||||
this.isStopped = false;
|
||||
|
||||
// 添加用户消息到列表
|
||||
const userMessage = {
|
||||
isBot: false,
|
||||
text: this.inputContent
|
||||
text: this.inputContent,
|
||||
};
|
||||
this.messageList.push(userMessage);
|
||||
|
||||
// 显示加载状态
|
||||
this.$emit('loading', true);
|
||||
this.$emit("loading", true);
|
||||
|
||||
// 调用AI聊天接口 (暂时注释掉SSE,使用模拟数据)
|
||||
this.callAIChat(this.inputContent);
|
||||
|
||||
// 清空输入框
|
||||
this.inputContent = ''
|
||||
this.inputContent = "";
|
||||
},
|
||||
handleStop() {
|
||||
console.log(this.accumulatedContent, "accumulatedContent");
|
||||
// 设置停止状态
|
||||
this.isStopped = true;
|
||||
// 清除打字机定时器
|
||||
if (this.typingTimer) {
|
||||
clearInterval(this.typingTimer);
|
||||
this.typingTimer = null;
|
||||
}
|
||||
|
||||
if (this.thinkTypingTimer) {
|
||||
clearInterval(this.thinkTypingTimer);
|
||||
this.thinkTypingTimer = null;
|
||||
}
|
||||
if (this.conversationId) {
|
||||
// const stopAbortController = new AbortController();
|
||||
fetch(
|
||||
`/systemapi/xboe/m/boe/case/ai/stop?conversationId=${this.conversationId}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
// signal: stopAbortController.signal,
|
||||
}
|
||||
)
|
||||
.then((response) => {
|
||||
if (response.ok) {
|
||||
console.log("成功发送停止请求"); // 通知父组件需要重新获取会话ID
|
||||
this.$emit("need-new-conversation-id");
|
||||
} else {
|
||||
console.error("停止请求失败:", response.status);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("停止请求出错:", error);
|
||||
});
|
||||
}
|
||||
// 终止当前正在进行的聊天请求
|
||||
if (this.currentAbortController) {
|
||||
this.currentAbortController.abort();
|
||||
}
|
||||
|
||||
// 立即发送加载完成事件,停止按钮状态切换
|
||||
this.$emit("loading", false);
|
||||
// 发送按钮状态更新事件
|
||||
this.$emit("update-button-state", {
|
||||
sendShow: true,
|
||||
stopShow: false,
|
||||
});
|
||||
},
|
||||
|
||||
// 真实的SSE实现(暂时注释掉)
|
||||
callAIChat(question) {
|
||||
// 重置停止状态
|
||||
this.isStopped = false;
|
||||
// 开始请求时显示加载状态
|
||||
this.$emit("loading", true);
|
||||
// 创建AbortController实例
|
||||
this.currentAbortController = new AbortController();
|
||||
|
||||
// 创建新的AI消息对象
|
||||
const aiMessage = {
|
||||
docId: '',
|
||||
docId: "",
|
||||
isBot: true,
|
||||
text: '',
|
||||
text: "",
|
||||
status: null,
|
||||
thinkText: '',
|
||||
thinkText: "",
|
||||
caseRefers: [], // 添加caseRefers字段
|
||||
textCompleted: false // 添加文字处理完成状态,默认为false
|
||||
textCompleted: false, // 添加文字处理完成状态,默认为false
|
||||
};
|
||||
this.messageList.push(aiMessage);
|
||||
let hasFinished = false; // 新增
|
||||
|
||||
// 构造请求参数
|
||||
const requestData = {
|
||||
conversationId: this.conversationId,
|
||||
query: question
|
||||
query: question,
|
||||
};
|
||||
// 创建POST请求
|
||||
fetch('/systemapi/xboe/m/boe/case/ai/chat', {
|
||||
method: 'POST',
|
||||
fetch("/systemapi/xboe/m/boe/case/ai/chat", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"accept": "text/event-stream",
|
||||
"Content-Type": "application/json",
|
||||
accept: "text/event-stream",
|
||||
},
|
||||
body: JSON.stringify(requestData)
|
||||
}).then(r => {
|
||||
return r
|
||||
}).then(response => {
|
||||
body: JSON.stringify(requestData),
|
||||
signal: this.currentAbortController.signal, // 添加signal
|
||||
})
|
||||
.then((r) => {
|
||||
return r;
|
||||
})
|
||||
.then((response) => {
|
||||
// 处理流式响应
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder('utf-8');
|
||||
let buffer = '';
|
||||
let accumulatedContent = ''; // 累积的内容用于打字机效果
|
||||
let accumulatedThinkContent = ''; // 累积的思考内容
|
||||
const decoder = new TextDecoder("utf-8");
|
||||
let buffer = "";
|
||||
let accumulatedContent = ""; // 累积的内容用于打字机效果
|
||||
let accumulatedThinkContent = ""; // 累积的思考内容
|
||||
let inThinkSection = false; // 是否在思考部分
|
||||
let typingTimer = null; // 打字机定时器
|
||||
let thinkTypingTimer = null; // 思考内容打字机定时器
|
||||
|
||||
// 逐字显示文本的函数
|
||||
const typeText = (message, fullContent) => {
|
||||
// 如果已经停止,则直接显示全部内容
|
||||
if (this.isStopped) {
|
||||
message.text = fullContent;
|
||||
this.$emit("update-message", message);
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果已有定时器在运行,先清除它
|
||||
if (typingTimer) {
|
||||
clearInterval(typingTimer);
|
||||
@@ -136,11 +258,19 @@ export default {
|
||||
const typingSpeed = 30; // 每个字符的间隔时间(毫秒)
|
||||
|
||||
typingTimer = setInterval(() => {
|
||||
// 如果已停止,立即停止渲染并保持当前内容
|
||||
if (this.isStopped) {
|
||||
clearInterval(typingTimer);
|
||||
typingTimer = null;
|
||||
// 保持当前已渲染的内容,不显示剩余内容
|
||||
this.$emit("update-message", message);
|
||||
return;
|
||||
}
|
||||
// 计算下一个要显示的字符索引
|
||||
const nextIndex = message.text.length + 1;
|
||||
if (nextIndex <= targetLength) {
|
||||
message.text = fullContent.substring(0, nextIndex);
|
||||
this.$emit('update-message', message);
|
||||
this.$emit("update-message", message);
|
||||
} else {
|
||||
clearInterval(typingTimer);
|
||||
typingTimer = null;
|
||||
@@ -157,6 +287,17 @@ export default {
|
||||
|
||||
// 逐字显示思考内容的函数
|
||||
const typeThinkText = (message, fullThinkContent) => {
|
||||
// 如果已经停止,则直接显示当前已渲染的内容并清理定时器
|
||||
if (this.isStopped) {
|
||||
// 清理可能存在的定时器
|
||||
if (thinkTypingTimer) {
|
||||
clearInterval(thinkTypingTimer);
|
||||
thinkTypingTimer = null;
|
||||
}
|
||||
// 保持当前已渲染的内容,不显示完整内容
|
||||
this.$emit("update-message", message);
|
||||
return;
|
||||
}
|
||||
// 如果已有定时器在运行,先清除它
|
||||
if (thinkTypingTimer) {
|
||||
clearInterval(thinkTypingTimer);
|
||||
@@ -178,11 +319,19 @@ export default {
|
||||
const typingSpeed = 20; // 每个字符的间隔时间(毫秒)
|
||||
|
||||
thinkTypingTimer = setInterval(() => {
|
||||
// 如果已停止,立即停止渲染并保持当前内容
|
||||
if (this.isStopped) {
|
||||
clearInterval(thinkTypingTimer);
|
||||
thinkTypingTimer = null;
|
||||
// 保持当前已渲染的内容,不显示剩余内容
|
||||
this.$emit("update-message", message);
|
||||
return;
|
||||
}
|
||||
// 计算下一个要显示的字符索引
|
||||
const nextIndex = message.thinkText.length + 1;
|
||||
if (nextIndex <= targetLength) {
|
||||
message.thinkText = fullThinkContent.substring(0, nextIndex);
|
||||
this.$emit('update-message', message);
|
||||
this.$emit("update-message", message);
|
||||
} else {
|
||||
clearInterval(thinkTypingTimer);
|
||||
thinkTypingTimer = null;
|
||||
@@ -197,12 +346,26 @@ export default {
|
||||
|
||||
// 读取流数据
|
||||
const read = () => {
|
||||
reader.read().then(({ done, value }) => {
|
||||
// 如果已停止,直接返回
|
||||
if (this.isStopped || hasFinished) {
|
||||
this.$emit("loading", false);
|
||||
return;
|
||||
}
|
||||
|
||||
reader
|
||||
.read()
|
||||
.then(({ done, value }) => {
|
||||
// 如果已停止,直接返回
|
||||
if (this.isStopped || hasFinished) {
|
||||
this.$emit("loading", false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (done) {
|
||||
// 当流结束时,等待打字机效果完成
|
||||
const waitForTyping = () => {
|
||||
if (!typingTimer) {
|
||||
this.$emit('loading', false);
|
||||
this.$emit("loading", false);
|
||||
} else {
|
||||
setTimeout(waitForTyping, 100);
|
||||
}
|
||||
@@ -215,42 +378,61 @@ export default {
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
|
||||
// 按行分割数据
|
||||
const lines = buffer.split('\n');
|
||||
const lines = buffer.split("\n");
|
||||
buffer = lines.pop(); // 保留不完整的行
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.startsWith('data:')) {
|
||||
// 如果已停止,跳出循环
|
||||
if (this.isStopped) {
|
||||
this.$emit("loading", false);
|
||||
return;
|
||||
}
|
||||
if (line.startsWith("data:")) {
|
||||
try {
|
||||
const jsonData = JSON.parse(line.substring(5));
|
||||
// 根据status处理不同类型的数据
|
||||
switch (jsonData.status) {
|
||||
case 0: // 引用文件
|
||||
// 处理引用文件信息
|
||||
if (jsonData.fileRefer && jsonData.fileRefer.caseRefers) {
|
||||
aiMessage.caseRefers = jsonData.fileRefer.caseRefers;
|
||||
if (
|
||||
jsonData.fileRefer &&
|
||||
jsonData.fileRefer.caseRefers
|
||||
) {
|
||||
aiMessage.caseRefers =
|
||||
jsonData.fileRefer.caseRefers;
|
||||
// 更新父组件的messageList
|
||||
this.$emit('update-message', aiMessage);
|
||||
this.$emit("update-message", aiMessage);
|
||||
}
|
||||
// 从响应中获取并保存conversationId
|
||||
if (jsonData.conversationId) {
|
||||
this.conversationId = jsonData.conversationId;
|
||||
sessionStorage.setItem('conversationId', jsonData.conversationId);
|
||||
}
|
||||
// if (jsonData.conversationId) {
|
||||
// this.conversationId = jsonData.conversationId;
|
||||
// sessionStorage.setItem(
|
||||
// "conversationId",
|
||||
// jsonData.conversationId
|
||||
// );
|
||||
// }
|
||||
break;
|
||||
|
||||
case 1: // 流式对话内容
|
||||
if (!this.isStopped) {
|
||||
// 处理
|
||||
const content = jsonData.content;
|
||||
aiMessage.hasThink = false;
|
||||
if (content.startsWith('<think>')) {
|
||||
aiMessage.hasThink = true
|
||||
if (content.startsWith("<think>")) {
|
||||
aiMessage.hasThink = true;
|
||||
inThinkSection = true;
|
||||
accumulatedThinkContent = content.replace('<think>', '');
|
||||
accumulatedThinkContent = content.replace(
|
||||
"<think>",
|
||||
""
|
||||
);
|
||||
// 使用打字机效果显示think内容
|
||||
typeThinkText(aiMessage, accumulatedThinkContent);
|
||||
} else if (content.startsWith('</think>')) {
|
||||
} else if (content.startsWith("</think>")) {
|
||||
inThinkSection = false;
|
||||
accumulatedThinkContent += content.replace('</think>', '');
|
||||
accumulatedThinkContent += content.replace(
|
||||
"</think>",
|
||||
""
|
||||
);
|
||||
// 使用打字机效果显示think内容
|
||||
typeThinkText(aiMessage, accumulatedThinkContent);
|
||||
} else if (inThinkSection) {
|
||||
@@ -262,13 +444,21 @@ export default {
|
||||
accumulatedContent += content;
|
||||
// 如果thinkText已经显示完整,则继续使用打字机效果显示内容
|
||||
if (aiMessage.hasThink) {
|
||||
if (aiMessage.thinkText.length >= accumulatedThinkContent.length) {
|
||||
if (
|
||||
aiMessage.thinkText.length >=
|
||||
accumulatedThinkContent.length
|
||||
) {
|
||||
typeText(aiMessage, accumulatedContent);
|
||||
}
|
||||
} else {
|
||||
typeText(aiMessage, accumulatedContent);
|
||||
}
|
||||
|
||||
}
|
||||
// 发送事件更新按钮状态:显示停止按钮
|
||||
this.$emit("update-button-state", {
|
||||
sendShow: false,
|
||||
stopShow: true,
|
||||
});
|
||||
}
|
||||
// 不在这里直接更新,让打字机效果处理更新
|
||||
break;
|
||||
@@ -276,88 +466,153 @@ export default {
|
||||
case 2: // 回答完成
|
||||
// 不再在这里设置textCompleted状态
|
||||
// 更新父组件的messageList
|
||||
this.$emit('update-message', aiMessage);
|
||||
// 只有在未停止状态下才更新
|
||||
if (!this.isStopped) {
|
||||
// 更新父组件的messageList
|
||||
this.$emit("update-message", aiMessage);
|
||||
// 发送事件更新按钮状态:显示停止按钮
|
||||
this.$emit("update-button-state", {
|
||||
sendShow: false,
|
||||
stopShow: true,
|
||||
});
|
||||
}
|
||||
// 从响应中获取并保存conversationId
|
||||
|
||||
break;
|
||||
|
||||
case 3: // 返回建议
|
||||
// 只有在未停止状态下才处理建议
|
||||
if (!this.isStopped) {
|
||||
// 这里可以处理建议问题
|
||||
this.$emit('update-suggestions', jsonData.suggestions);
|
||||
this.$emit(
|
||||
"update-suggestions",
|
||||
jsonData.suggestions
|
||||
);
|
||||
// 发送事件更新按钮状态:显示停止按钮
|
||||
this.$emit("update-button-state", {
|
||||
sendShow: false,
|
||||
stopShow: true,
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case 4: // 交互完成
|
||||
aiMessage.status = 4
|
||||
// 只有在未停止状态下才处理完成状态
|
||||
if (!this.isStopped) {
|
||||
aiMessage.status = 4;
|
||||
|
||||
// 从响应中获取并保存conversationId
|
||||
this.$emit('loading', false);
|
||||
// this.$emit("loading", false);
|
||||
// 检查文本是否已经完全显示,如果是则设置textCompleted为true
|
||||
if (isTextDisplayCompleted(aiMessage, accumulatedContent)) {
|
||||
if (
|
||||
isTextDisplayCompleted(
|
||||
aiMessage,
|
||||
accumulatedContent
|
||||
)
|
||||
) {
|
||||
// aiMessage.textCompleted = true;
|
||||
this.$emit('update-message', aiMessage);
|
||||
this.$emit("update-message", aiMessage);
|
||||
// 发送事件更新按钮状态:显示发送按钮
|
||||
hasFinished = true; // 标记完成
|
||||
this.$emit("update-button-state", {
|
||||
sendShow: true,
|
||||
stopShow: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 9:
|
||||
if (jsonData.docId) {
|
||||
aiMessage.docId = jsonData.docId
|
||||
this.$emit('update-message', aiMessage);
|
||||
// 只有在未停止状态下才处理docId
|
||||
if (!this.isStopped && jsonData.docId) {
|
||||
aiMessage.docId = jsonData.docId;
|
||||
this.$emit("update-message", aiMessage);
|
||||
}
|
||||
console.log(jsonData)
|
||||
break
|
||||
// 显示发送按钮,隐藏停止按钮
|
||||
// this.$emit("loading", false);
|
||||
console.log(jsonData);
|
||||
// 发送事件更新按钮状态:显示发送按钮
|
||||
hasFinished = true; // 标记完成
|
||||
this.$emit("update-button-state", {
|
||||
sendShow: true,
|
||||
stopShow: false,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
if (jsonData.docId) {
|
||||
aiMessage.docId = jsonData.docId
|
||||
this.$emit('update-message', aiMessage);
|
||||
if (!this.isStopped && jsonData.docId) {
|
||||
aiMessage.docId = jsonData.docId;
|
||||
this.$emit("update-message", aiMessage);
|
||||
}
|
||||
break
|
||||
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('解析SSE数据错误:', error);
|
||||
console.error("解析SSE数据错误:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 继续读取
|
||||
// 继续读取(除非已停止)
|
||||
if (!this.isStopped) {
|
||||
read();
|
||||
}).catch(error => {
|
||||
console.error('SSE连接错误:', error);
|
||||
}
|
||||
// 继续读取
|
||||
// read();
|
||||
})
|
||||
.catch((error) => {
|
||||
// 处理AbortError
|
||||
if (error.name === "AbortError") {
|
||||
console.log("请求已被用户取消");
|
||||
// aiMessage.text = "请求已被取消";
|
||||
aiMessage.textCompleted = true;
|
||||
this.$emit("update-message", aiMessage);
|
||||
} else {
|
||||
console.error("SSE连接错误:", error);
|
||||
// 出错时也设置文字处理完成状态
|
||||
if (typingTimer) {
|
||||
clearInterval(typingTimer);
|
||||
typingTimer = null;
|
||||
}
|
||||
aiMessage.textCompleted = true;
|
||||
this.$emit('loading', false);
|
||||
aiMessage.text = '当前无法获取回答,请稍后重试';
|
||||
aiMessage.text = "当前无法获取回答,请稍后重试";
|
||||
// 更新父组件的messageList
|
||||
this.$emit('update-message', aiMessage);
|
||||
this.$emit("update-message", aiMessage);
|
||||
}
|
||||
this.$emit("loading", false);
|
||||
});
|
||||
};
|
||||
|
||||
// 开始读取数据
|
||||
// 继续读取(除非已停止)
|
||||
if (!this.isStopped) {
|
||||
read();
|
||||
}).catch(error => {
|
||||
console.error('请求失败:', error);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
// 处理AbortError
|
||||
if (error.name === "AbortError") {
|
||||
console.log("请求已被用户取消");
|
||||
aiMessage.text = "请求已被取消";
|
||||
aiMessage.textCompleted = true;
|
||||
this.$emit("update-message", aiMessage);
|
||||
} else {
|
||||
console.error("请求失败:", error);
|
||||
// 出错时也设置文字处理完成状态
|
||||
aiMessage.textCompleted = true;
|
||||
this.$emit('loading', false);
|
||||
aiMessage.text = '当前无法获取回答,请稍后重试';
|
||||
aiMessage.text = "当前无法获取回答,请稍后重试";
|
||||
// 更新父组件的messageList
|
||||
this.$emit('update-message', aiMessage);
|
||||
this.$emit("update-message", aiMessage);
|
||||
}
|
||||
// 只有在非停止状态下才发送 loading 事件
|
||||
if (!this.isStopped) {
|
||||
this.$emit("loading", false);
|
||||
}
|
||||
});
|
||||
},
|
||||
handleNewConversation() {
|
||||
this.conversationId = ''
|
||||
this.$emit('new-conversation')
|
||||
}
|
||||
}
|
||||
}
|
||||
// this.conversationId = "";
|
||||
this.$emit("new-conversation");
|
||||
this.getConversationId();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.input-area {
|
||||
background-color: white;
|
||||
@@ -405,5 +660,10 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
.stop {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -203,8 +203,25 @@
|
||||
<div class="couresstartTime">
|
||||
<span v-if="cinfo.type == 30 && cinfo.startTime">开课时间:{{ cinfo.startTime }}</span>
|
||||
</div>
|
||||
|
||||
<div class="course-info">
|
||||
<!-- ai播放器相关 -->
|
||||
<div class="course-info" style="align-items: center;">
|
||||
<el-popover
|
||||
placement="top-start"
|
||||
:width="cinfo.summaryContent && cinfo.summaryContent.length > 200 ? '402' : '253'"
|
||||
trigger="hover"
|
||||
popper-class="course-popover"
|
||||
>
|
||||
<div class="course-popover-content">
|
||||
<h4>课程摘要</h4>
|
||||
<div v-if="cinfo.summaryContent" class="course-popover-content-text" >{{ cinfo.summaryContent }}</div>
|
||||
<div v-else class="course-popover-noContent" >
|
||||
<img src="../../../assets/images/course/noData.png" alt="">
|
||||
<span>暂无数据</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <img slot="reference" src="../../../assets/images/course/courseAbstract.png" alt="摘要" style="width: 94px;height: 44px;margin-left: -10px;"> -->
|
||||
<img v-show="cinfo.aiSet == 1 && cinfo.aiAbstract == 1" slot="reference" src="../../../assets/images/course/courseAbstract.png" alt="摘要" style="width: 94px;height: 44px;margin-left: -10px;">
|
||||
</el-popover>
|
||||
<div class="course-info-user" style="max-width: 100px;" v-if="cinfo.teacher">
|
||||
<el-tooltip :content="cinfo.teacher" placement="bottom" effect="light">
|
||||
<span class="course-info-author">{{ cinfo.teacher }}</span>
|
||||
@@ -214,13 +231,13 @@
|
||||
<span class="course-info-studys">{{ formatNum(cinfo.studies) }}人学习</span>
|
||||
</div>
|
||||
<div class="course-info-score">
|
||||
<div style="display: flex;">
|
||||
<div style="display: flex; align-items: center;">
|
||||
<interactBar :type="1" nodeWidth="20px" :data="cinfo" :courseExclusive="true" :comments="false"
|
||||
:praises="false" :shares="false" :views="false"></interactBar>
|
||||
<div v-if="cinfo.score">
|
||||
<span class="course-score-value">{{ toScore(cinfo.score) }}分</span>
|
||||
<span class="course-score-value" style="white-space: nowrap;">{{ toScore(cinfo.score) }}分</span>
|
||||
</div>
|
||||
<div v-else class="course-score-no">未评分</div>
|
||||
<div v-else class="course-score-no" style="white-space: nowrap;">未评分</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2655,3 +2672,43 @@ a.custom2 {
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<!-- ai播放器相关 -->
|
||||
<style lang="scss">
|
||||
.course-popover{
|
||||
border-radius: 12px;
|
||||
box-shadow: 0px 0px 16px 0px rgba(0, 0, 0, 0.12);
|
||||
.course-popover-content{
|
||||
h4{
|
||||
margin: 0 0 7px 0;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 17px;
|
||||
letter-spacing: 0.3px;
|
||||
color: rgba(0, 0, 0, 1);
|
||||
}
|
||||
.course-popover-content-text{
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
letter-spacing: 0.26px;
|
||||
color: rgba(102, 102, 102, 1);
|
||||
}
|
||||
.course-popover-noContent{
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
color: rgba(0, 0, 0, 0.34);
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 14px;
|
||||
letter-spacing: 0.26px;
|
||||
padding-bottom: 20px;
|
||||
img{
|
||||
width: 96px;
|
||||
height: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -373,6 +373,30 @@
|
||||
></i
|
||||
>我的笔记
|
||||
</div>
|
||||
<!-- ai播放器相关 开发阶段先放开 v-if="courseInfo.aiDraft == 1" -->
|
||||
<div
|
||||
@click="heartabthree"
|
||||
v-if="courseInfo.aiSet == 1 && courseInfo.aiDraft == 1 && contentData.contentType == 10"
|
||||
:class="tab == 3 ? 'control-tab-active' : ' '"
|
||||
style="position: relative"
|
||||
>
|
||||
<i
|
||||
class="el-icon-document"
|
||||
style="margin-right: 9px; margin-left: 9px"
|
||||
></i
|
||||
>AI文稿
|
||||
<img
|
||||
src="@/assets/images/course/wengaoTip.png"
|
||||
alt=""
|
||||
style="
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
right: -14px;
|
||||
width: 15px;
|
||||
height: 14px;
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 课程单元 -->
|
||||
<div class="course-units" v-if="tab == 1">
|
||||
@@ -625,6 +649,11 @@
|
||||
:score="courseInfo.score"
|
||||
></my-note>
|
||||
</div>
|
||||
<!-- ai播放器相关 -->
|
||||
<!-- ai文稿 -->
|
||||
<div class="ai-script" v-if="contentData.contentType == 10 && tab == 3">
|
||||
<ai-script ref="aiscript" :blobId="blobId" :isDrag="curriculumData.isDrag" @changeCurrentTime="changeCurrentTime"></ai-script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="course-infobox">
|
||||
@@ -637,6 +666,16 @@
|
||||
>
|
||||
内容简介<span class=""></span>
|
||||
</div>
|
||||
<!-- ai播放器相关 -->
|
||||
<div
|
||||
@click="coutab(4)"
|
||||
v-if="courseInfo.aiSet == 1 && courseInfo.aiAbstract == 1"
|
||||
style="position: relative;"
|
||||
:class="courestab == 4 ? 'course-info-tab-active' : ''"
|
||||
>
|
||||
AI摘要<span class=""></span>
|
||||
<img style="position: absolute;top: -18px;right: -23px;width: 36px;height: 24px;" src="@/assets/images/course/courseNew.png" alt="">
|
||||
</div>
|
||||
<div
|
||||
@click="coutab(2)"
|
||||
:class="courestab == 2 ? 'course-info-tab-active' : ''"
|
||||
@@ -709,6 +748,15 @@
|
||||
></note-comments>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ai播放器相关 -->
|
||||
<div
|
||||
v-show="courestab == 4"
|
||||
style="padding-left: 17px; padding-top: 20px;background-color: #fff;"
|
||||
>
|
||||
<div style="padding: 30px;line-height: 24px;letter-spacing: 0.3px;color: rgba(102, 102, 102, 1);">
|
||||
{{ courseInfo.summaryContent || '' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="course-teacher">
|
||||
<div class="cteacher-top">
|
||||
@@ -799,7 +847,8 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { mapGetters } from "vuex";
|
||||
// ai播放器相关
|
||||
import { mapGetters, mapMutations } from "vuex";
|
||||
import followButton from "@/components/Follow/button.vue";
|
||||
import portalHeader from "@/components/PortalHeader.vue";
|
||||
import portalFooter from "@/components/PortalFooter.vue";
|
||||
@@ -836,6 +885,8 @@ import exam from "@/components/Course/exam";
|
||||
import homework from "@/components/Course/homework";
|
||||
import assess from "@/components/Course/assess";
|
||||
import myNote from "../../components/Course/myNote.vue";
|
||||
// ai播放器相关
|
||||
import aiScript from "../../components/Course/aiScript.vue";
|
||||
import apiFollow from "@/api/phase2/userfollow.js";
|
||||
import apiMessage from "@/api/system/message.js";
|
||||
// import Vue from 'vue';
|
||||
@@ -855,6 +906,7 @@ export default {
|
||||
audioPlayer,
|
||||
videoPlayer,
|
||||
myNote,
|
||||
aiScript,
|
||||
noteComments,
|
||||
portalFooter,
|
||||
followButton,
|
||||
@@ -968,7 +1020,8 @@ export default {
|
||||
this.loadData();
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(["userInfo"]),
|
||||
// ai播放器相关
|
||||
...mapGetters(["userInfo", 'selectableLang']),
|
||||
catalogTree() {
|
||||
let treeList = [];
|
||||
this.completed = [];
|
||||
@@ -1004,6 +1057,16 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// ai播放器相关
|
||||
// 处理从AI文稿组件传递过来的时间跳转事件
|
||||
changeCurrentTime(time) {
|
||||
console.log(time,'time')
|
||||
this.$refs.myVideoPlayer && this.$refs.myVideoPlayer.seekToTime(time);
|
||||
},
|
||||
...mapMutations({
|
||||
SET_selectableLang: 'video/SET_selectableLang',
|
||||
SET_courseInfo: 'video/SET_courseInfo',
|
||||
}),
|
||||
handleOpen(key, path) {
|
||||
if (this.isFalse) {
|
||||
this.defaultOpeneds = [key];
|
||||
@@ -1165,6 +1228,10 @@ export default {
|
||||
this.curriculumData.url = r.content;
|
||||
}
|
||||
this.$refs.mynote.showVideoTimeBtn(true);
|
||||
// ai播放器相关 - 视频类型加载ai相关功能
|
||||
if (r.contentType == 10) {
|
||||
this.handleAIVideo(r.boeaiSubtitleRspList, r);
|
||||
}
|
||||
this.createPlayUrl(r.contentRefId, this.curriculumData.url);
|
||||
} else if (r.contentType == 40) {
|
||||
// if (r.content != '' && r.content.indexOf('.pdf') == -1) {
|
||||
@@ -1327,6 +1394,16 @@ export default {
|
||||
localStorage.setItem("videoProgressData", JSON.stringify(arr));
|
||||
}
|
||||
},
|
||||
// ai播放器相关 - 视频处理
|
||||
handleAIVideo(list = [], r) {
|
||||
console.log('触发了-----------list', list);
|
||||
this.SET_selectableLang(list);
|
||||
this.SET_courseInfo(this.courseInfo);
|
||||
if (this.courseInfo.aiSet && this.courseInfo.aiAbstract == 1 && this.courseInfo.summaryContent) {
|
||||
this.coutab(4);
|
||||
}
|
||||
console.log("ai处理", this.selectableLang);
|
||||
},
|
||||
isShowTime() {
|
||||
if (this.isContentTypeTwo != this.contentData.contentType) {
|
||||
return;
|
||||
@@ -2290,6 +2367,10 @@ export default {
|
||||
heartabtwo() {
|
||||
this.tab = 2;
|
||||
},
|
||||
// ai播放器相关
|
||||
heartabthree() {
|
||||
this.tab = 3;
|
||||
},
|
||||
handleAudioTimeUpdate(currentTime) {
|
||||
// if(this.contentStudysLength.length == 0){
|
||||
let params = {
|
||||
|
||||