ai视频一期功能提交

This commit is contained in:
sunli_tydic
2025-11-20 17:30:31 +08:00
parent 003fece291
commit b31f02bf23
11 changed files with 368 additions and 131 deletions

View File

@@ -14,9 +14,8 @@
/>
<div class="language-selector">
<span class="language-label">语言</span>
<el-select v-model="selectedLanguage" class="language-select" @change="changeLanguage">
<el-option label="中文" value="zh"></el-option>
<el-option label="英文" value="en"></el-option>
<el-select v-model="selectedLanguage" class="language-select" @change="changeLanguage" placeholder="请选择语言">
<el-option v-for="lang in selectableLang" :key="lang.srclang" :label="lang.label" :value="lang.srclang"></el-option>
</el-select>
</div>
</div>
@@ -24,15 +23,15 @@
<!-- 内容展示区域 -->
<div class="content-container">
<!-- 动态渲染内容块 -->
<div v-for="(item, index) in contentList" :key="index" class="content-item" :class="{'active': activeIndex === index}">
<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>
{{ item.timestamp }}
{{ formatTime(item.start) }}
</div>
</div>
<el-card class="content-text">
<div v-html="item.highlightedContent || item.content"></div>
<el-card class="content-text" @click.native="scrollToTime(item.start)">
<div v-html="item.highlightedContent || item.text"></div>
</el-card>
</div>
</div>
@@ -40,35 +39,148 @@
</template>
<script>
import { mapGetters, mapMutations } from 'vuex'
export default {
name: 'ai-script',
data () {
data() {
return {
searchKeyword: '',
selectedLanguage: 'zh',
originalContentList: [
{
timestamp: '00:00:04',
content: '从功能上来看,整个平台是涵盖了四个核心的环节,第一个是我们的智课,第一个环节,通过我们的大模型能力将我们企业现有的材料上传之后,能够智能生成培训课件那培训课件。可以智能的转化成我们的培训视频已有的培训课件可以实现快速上架,第二个是学习的阶段,微信形式微课形式的一个线上学习的一个模式,通过岗位地图引导在线上进行视频学习,在移动端的时候,配合我们的智能数字导师可以进行线上微课的一个授课以及智能的互动问答解答。'
},
{
timestamp: '00:10:04',
content: '第三个就是考核的模式,也就是学习后的一个考核,通过我们的智能出题来生成我们课件对应的试题,那在进行学习完成之后,完成岗位的考试进行实时判卷,同时也能看到每个考核它的整个错题集的情况。最后就是我们的发证认证,所有的任务都会配合他对应的一个认证资格的认证,一人一证一码,随时可以查询他的证书情况和他的成绩培训情况。'
},
{
timestamp: '00:13:04',
content: '第三个就是考核的模式,也就是学习后的一个考核,通过我们的智能出题来生成我们课件对应的试题,那在进行学习完成之后,完成岗位的考试进行实时判卷,同时也能看到每个考核它的整个错题集的情况。最后就是我们的发证认证,所有的任务都会配合他对应的一个认证资格的认证,一人一证一码,随时可以查询他的证书情况和他的成绩培训情况。'
}
],
selectedLanguage: 'zh-CN',
originalContentList: [],
contentList: [], // 用于显示的内容列表
activeIndex: 0,
isUserScrolling: false, // 用户是否正在滚动
userScrollTimeout: null // 滚动超时计时器
}
},
computed: {
...mapGetters([
'currentTime',
'selectableLang'
]),
},
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.contentList = this.originalContentList.map(item => ({ ...item }));
// 初始化时根据语言选择显示内容
this.changeLanguage(this.selectedLanguage)
},
methods: {
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('跳转到时间点:', 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()) {
@@ -80,7 +192,7 @@ export default {
const keyword = this.searchKeyword.trim();
// 过滤包含关键词的内容
const filteredList = this.originalContentList.filter(item =>
item.content.includes(keyword)
item.text.includes(keyword)
);
if (filteredList.length === 0) {
@@ -94,7 +206,7 @@ export default {
// 对搜索到的内容进行关键词高亮处理
this.contentList = filteredList.map(item => ({
...item,
highlightedContent: this.highlightKeyword(item.content, keyword)
highlightedContent: this.highlightKeyword(item.text, keyword)
}));
console.log(this.contentList)
}
@@ -107,8 +219,24 @@ export default {
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
console.log('切换语言:', this.selectedLanguage)
// 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() {
// 当输入框内容变化时,如果为空则重置显示所有内容
@@ -185,7 +313,7 @@ export default {
display: flex;
flex-direction: column;
gap: 20px;
max-height: 530px;
max-height: 410px;
overflow-y: auto;
padding: 0 20px;
}
@@ -220,6 +348,7 @@ export default {
}
.content-text {
cursor: pointer;
line-height: 1.6;
font-size: 14px;
color: rgba(102, 102, 102, 1);