|
@@ -85,17 +85,28 @@
|
|
|
loop
|
|
loop
|
|
|
autoplay
|
|
autoplay
|
|
|
width="100%"
|
|
width="100%"
|
|
|
- :src="videoUrl"
|
|
|
|
|
|
|
+ muted
|
|
|
|
|
+ playsinline
|
|
|
@click.prevent
|
|
@click.prevent
|
|
|
@contextmenu.prevent
|
|
@contextmenu.prevent
|
|
|
class="custom-video"
|
|
class="custom-video"
|
|
|
- style="display: block; background: #000; height: 45vh;"
|
|
|
|
|
- ></video>
|
|
|
|
|
|
|
+ style="display: block; background: #000; height: 40vh;"
|
|
|
|
|
+ >
|
|
|
|
|
+ <source :src="videoUrl" type="video/mp4">
|
|
|
|
|
+ </video>
|
|
|
|
|
+ <!-- 自定义进度条容器 -->
|
|
|
|
|
+ <div ref="progressBar" class="progress-container">
|
|
|
|
|
+ <div id="progressBar" class="progress-bar"></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 时间显示(可选) -->
|
|
|
|
|
+ <div ref="elapsedTime" class="time-display">
|
|
|
|
|
+ 已播放:<span id="elapsedTime">00:00:00</span>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
<div style="border-radius: 5px; overflow: hidden;" v-else>
|
|
<div style="border-radius: 5px; overflow: hidden;" v-else>
|
|
|
<img :src="require('@/assets/images/videoNotStart.png')" style="width: 100%; height: 45vh;">
|
|
<img :src="require('@/assets/images/videoNotStart.png')" style="width: 100%; height: 45vh;">
|
|
|
</div>
|
|
</div>
|
|
|
-
|
|
|
|
|
</div>
|
|
</div>
|
|
|
<!-- 底部导航栏 -->
|
|
<!-- 底部导航栏 -->
|
|
|
<div style="display: flex; justify-content: space-around; padding: 15px 0; background: #fff; border-top: 1px solid #f0f0f0;">
|
|
<div style="display: flex; justify-content: space-around; padding: 15px 0; background: #fff; border-top: 1px solid #f0f0f0;">
|
|
@@ -280,6 +291,11 @@ export default {
|
|
|
{value:'30',label:'30分钟',key:'30'},
|
|
{value:'30',label:'30分钟',key:'30'},
|
|
|
{value:'60',label:'1小时',key:'60'},
|
|
{value:'60',label:'1小时',key:'60'},
|
|
|
],
|
|
],
|
|
|
|
|
+ videoDuration: 0,
|
|
|
|
|
+ startTime: null,
|
|
|
|
|
+ processInterval: null,
|
|
|
|
|
+ // ... 其他数据
|
|
|
|
|
+ chatScrollTop: 0 // 保存聊天滚动位置
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
created() {
|
|
created() {
|
|
@@ -328,40 +344,61 @@ export default {
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
mounted() {
|
|
mounted() {
|
|
|
|
|
+ this.$nextTick(() => {
|
|
|
|
|
+ this.restoreChatScrollPosition();
|
|
|
|
|
+ });
|
|
|
this.getEchartsTables();
|
|
this.getEchartsTables();
|
|
|
- if (this.videoUrl.length > 0) {
|
|
|
|
|
- const video = this.$refs.videoPlayer;
|
|
|
|
|
- video.play()
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // 添加滚动事件监听器
|
|
|
|
|
+ this.$nextTick(() => {
|
|
|
|
|
+ if (this.$refs.manageRightRef && this.$refs.manageRightRef.wrap) {
|
|
|
|
|
+ this.$refs.manageRightRef.wrap.addEventListener('scroll', this.saveChatScrollPosition);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ },
|
|
|
|
|
+ // 使用 deactivated 和 activated 钩子替代 beforeDestroy 和 destroyed
|
|
|
|
|
+ deactivated() {
|
|
|
|
|
+ this.saveChatScrollPosition();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
|
|
+ activated() {
|
|
|
|
|
+ this.$nextTick(() => {
|
|
|
|
|
+ this.restoreChatScrollPosition();
|
|
|
|
|
+ });
|
|
|
|
|
+ this.$nextTick(() => {
|
|
|
|
|
+ const video = this.$refs.videoPlayer;
|
|
|
|
|
+ if (video != null) {
|
|
|
|
|
+ video.play();
|
|
|
|
|
+ this.initVideoPlayer(this.liveInfo.startTime)
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
},
|
|
},
|
|
|
methods: {
|
|
methods: {
|
|
|
- // 切换音量状态
|
|
|
|
|
- toggleVolume() {
|
|
|
|
|
- const video = this.$refs.videoPlayer;
|
|
|
|
|
- if (video.volume > 0) {
|
|
|
|
|
- // 保存当前音量并静音
|
|
|
|
|
- this.lastVolume = video.volume;
|
|
|
|
|
- video.volume = 0;
|
|
|
|
|
- this.isMuted = true;
|
|
|
|
|
- } else {
|
|
|
|
|
- // 恢复之前的音量
|
|
|
|
|
- video.volume = this.lastVolume || this.initialVolume;
|
|
|
|
|
- this.isMuted = false;
|
|
|
|
|
|
|
+ // 保存聊天滚动位置
|
|
|
|
|
+ saveChatScrollPosition() {
|
|
|
|
|
+ if (this.$refs.manageRightRef && this.$refs.manageRightRef.wrap) {
|
|
|
|
|
+ this.chatScrollTop = this.$refs.manageRightRef.wrap.scrollHeight - this.$refs.manageRightRef.wrap.clientHeight;
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 恢复聊天滚动位置
|
|
|
|
|
+ restoreChatScrollPosition() {
|
|
|
|
|
+ if (this.$refs.manageRightRef && this.$refs.manageRightRef.wrap) {
|
|
|
|
|
+ this.$refs.manageRightRef.wrap.scrollTop = this.chatScrollTop;
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
getLive(){
|
|
getLive(){
|
|
|
getLive(this.liveId).then(res => {
|
|
getLive(this.liveId).then(res => {
|
|
|
- this.loading = false
|
|
|
|
|
if (res.code == 200) {
|
|
if (res.code == 200) {
|
|
|
if (res.data.isAudit != 1) {
|
|
if (res.data.isAudit != 1) {
|
|
|
this.$message.error("当前直播间未经审核");
|
|
this.$message.error("当前直播间未经审核");
|
|
|
|
|
+ this.loading = false
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
this.isAudit = true
|
|
this.isAudit = true
|
|
|
this.status = res.data.status
|
|
this.status = res.data.status
|
|
|
if (res.data.status != 2) {
|
|
if (res.data.status != 2) {
|
|
|
this.$message.error("当前直播间未直播");
|
|
this.$message.error("当前直播间未直播");
|
|
|
|
|
+ this.loading = false
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
if (res.data.liveType == 1) {
|
|
if (res.data.liveType == 1) {
|
|
@@ -372,13 +409,91 @@ export default {
|
|
|
} else {
|
|
} else {
|
|
|
this.liveType = 2
|
|
this.liveType = 2
|
|
|
this.videoUrl = res.data.videoUrl;
|
|
this.videoUrl = res.data.videoUrl;
|
|
|
|
|
+ this.$nextTick(() => {
|
|
|
|
|
+ const video = this.$refs.videoPlayer;
|
|
|
|
|
+ video.play()
|
|
|
|
|
+ this.initVideoPlayer(res.data.startTime)
|
|
|
|
|
+ })
|
|
|
}
|
|
}
|
|
|
|
|
+ this.loading = false
|
|
|
} else {
|
|
} else {
|
|
|
this.$message.error(res.msg)
|
|
this.$message.error(res.msg)
|
|
|
|
|
+ this.loading = false
|
|
|
}
|
|
}
|
|
|
this.liveInfo = res.data
|
|
this.liveInfo = res.data
|
|
|
})
|
|
})
|
|
|
},
|
|
},
|
|
|
|
|
+ initVideoPlayer: function (startTime) {
|
|
|
|
|
+ const video = this.$refs.videoPlayer;
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // 1. 初始化开播时间
|
|
|
|
|
+ startTime = new Date(startTime).getTime();
|
|
|
|
|
+ this.startTime = startTime;
|
|
|
|
|
+ // 2. 监听视频元数据加载完成(获取视频时长)
|
|
|
|
|
+ video.addEventListener('loadedmetadata', () => {
|
|
|
|
|
+ this.videoDuration = video.duration; // 获取视频时长(秒)
|
|
|
|
|
+
|
|
|
|
|
+ // 初始化视频播放位置
|
|
|
|
|
+ this.updateVideoPosition(video);
|
|
|
|
|
+
|
|
|
|
|
+ // 启动实时进度更新(每秒刷新一次)
|
|
|
|
|
+ this.processInterval = setInterval(this.updateProgress, 1000);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ },
|
|
|
|
|
+ updateVideoPosition(video){
|
|
|
|
|
+ const currentTime = new Date().getTime(); // 当前时间戳(毫秒)
|
|
|
|
|
+ const elapsedTime = currentTime - this.startTime; // 已流逝时间(毫秒)
|
|
|
|
|
+
|
|
|
|
|
+ if (elapsedTime < 0) {
|
|
|
|
|
+ // 未开播:视频停在初始位置
|
|
|
|
|
+ video.currentTime = 0;
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 已开播:计算视频循环后的位置(流逝时间 % 视频时长)
|
|
|
|
|
+ const elapsedSeconds = elapsedTime / 1000; // 转换为秒
|
|
|
|
|
+ const videoPosition = elapsedSeconds % this.videoDuration; // 视频内的播放位置(秒)
|
|
|
|
|
+
|
|
|
|
|
+ // 设置视频播放位置
|
|
|
|
|
+ video.currentTime = videoPosition;
|
|
|
|
|
+ },
|
|
|
|
|
+ updateProgress() {
|
|
|
|
|
+ const progressBar = this.$refs.progressBar;
|
|
|
|
|
+ const elapsedTimeEl = this.$refs.elapsedTime;
|
|
|
|
|
+ if (!this.videoDuration) return; // 视频时长未加载时不更新
|
|
|
|
|
+
|
|
|
|
|
+ const currentTime = new Date().getTime();
|
|
|
|
|
+ const elapsedTime = currentTime - this.startTime; // 总流逝时间(毫秒)
|
|
|
|
|
+
|
|
|
|
|
+ if (elapsedTime < 0) {
|
|
|
|
|
+ // 未开播状态
|
|
|
|
|
+ progressBar.style.width = '0%';
|
|
|
|
|
+ elapsedTimeEl.textContent = '00:00:00';
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 计算进度百分比(基于视频循环)
|
|
|
|
|
+ const elapsedSeconds = elapsedTime / 1000;
|
|
|
|
|
+ const videoPosition = elapsedSeconds % this.videoDuration; // 当前在视频中的位置
|
|
|
|
|
+ const progressPercent = (videoPosition / this.videoDuration) * 100; // 进度百分比
|
|
|
|
|
+
|
|
|
|
|
+ // 更新进度条宽度
|
|
|
|
|
+ progressBar.style.width = `${progressPercent}%`;
|
|
|
|
|
+
|
|
|
|
|
+ // 格式化总流逝时间为“时:分:秒”并显示
|
|
|
|
|
+ elapsedTimeEl.textContent = this.formatTime(elapsedTime);
|
|
|
|
|
+ },
|
|
|
|
|
+ formatTime(ms) {
|
|
|
|
|
+ const totalSeconds = Math.floor(ms / 1000);
|
|
|
|
|
+ const hours = Math.floor(totalSeconds / 3600);
|
|
|
|
|
+ const minutes = Math.floor((totalSeconds % 3600) / 60);
|
|
|
|
|
+ const seconds = totalSeconds % 60;
|
|
|
|
|
+
|
|
|
|
|
+ // 补零处理(确保两位数)
|
|
|
|
|
+ return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
|
|
|
|
|
+ },
|
|
|
// ... 其他方法 ...
|
|
// ... 其他方法 ...
|
|
|
timeChange(val) {
|
|
timeChange(val) {
|
|
|
this.searchQuery.timeOptions = val
|
|
this.searchQuery.timeOptions = val
|
|
@@ -469,7 +584,6 @@ export default {
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
handleClick(tab) {
|
|
handleClick(tab) {
|
|
|
-
|
|
|
|
|
},
|
|
},
|
|
|
getList() {
|
|
getList() {
|
|
|
this.resetParams()
|
|
this.resetParams()
|
|
@@ -564,10 +678,7 @@ export default {
|
|
|
})
|
|
})
|
|
|
},
|
|
},
|
|
|
manageRightScroll() {
|
|
manageRightScroll() {
|
|
|
- let max = this.$refs.manageRightRef.wrap.scrollHeight - this.$refs.manageRightRef.wrap.clientHeight
|
|
|
|
|
- let current = this.$refs.manageRightRef.wrap.scrollTop
|
|
|
|
|
- console.log("manageRightMax", max);
|
|
|
|
|
- console.log("manageRight", current)
|
|
|
|
|
|
|
+ this.saveChatScrollPosition();
|
|
|
},
|
|
},
|
|
|
changeUserState(u) {
|
|
changeUserState(u) {
|
|
|
// 修改状态
|
|
// 修改状态
|
|
@@ -668,6 +779,7 @@ export default {
|
|
|
liveId: this.liveId,
|
|
liveId: this.liveId,
|
|
|
userId: this.userId
|
|
userId: this.userId
|
|
|
})
|
|
})
|
|
|
|
|
+ clearInterval(this.processInterval)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
</script>
|
|
</script>
|
|
@@ -738,6 +850,37 @@ export default {
|
|
|
}
|
|
}
|
|
|
.custom-video {
|
|
.custom-video {
|
|
|
pointer-events: none !important; /* 完全禁止鼠标交互,避免悬停时显示工具栏 */
|
|
pointer-events: none !important; /* 完全禁止鼠标交互,避免悬停时显示工具栏 */
|
|
|
|
|
+ outline: none !important; /* 移除焦点轮廓 */
|
|
|
|
|
+}
|
|
|
|
|
+/* 额外的兼容性隐藏 */
|
|
|
|
|
+.custom-video::-webkit-media-controls {
|
|
|
|
|
+ display: none !important;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+.custom-video::-webkit-media-controls-panel {
|
|
|
|
|
+ display: none !important;
|
|
|
|
|
+}
|
|
|
|
|
+/* 进度条容器 */
|
|
|
|
|
+.progress-container {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: 6px;
|
|
|
|
|
+ background: #eee;
|
|
|
|
|
+ border-radius: 3px;
|
|
|
|
|
+ margin: 10px 0;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 进度条填充部分 */
|
|
|
|
|
+.progress-bar {
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ background: #42b983; /* Vue 绿色主题示例 */
|
|
|
|
|
+ border-radius: 3px;
|
|
|
|
|
+ width: 0%; /* 初始进度为 0 */
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 时间显示样式 */
|
|
|
|
|
+.time-display {
|
|
|
|
|
+ color: #333;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+}
|
|
|
</style>
|
|
</style>
|