feat: 新增直播功能及相关组件

实现以下功能:
1. 在首页展示直播信息,支持点击进入直播间。
2. 添加获取直播流的API调用,动态更新直播数据。
3. 优化直播视图组件,调整样式以提升用户体验。
4. 代码格式化和注释优化,提升可读性。
This commit is contained in:
yindongqi 2025-08-05 17:36:59 +08:00
parent d24052d24b
commit 0d5a7d6d5d
4 changed files with 7301 additions and 7598 deletions

View File

@ -23,6 +23,7 @@
"exif-js": "^2.3.0", "exif-js": "^2.3.0",
"font-awesome": "^4.7.0", "font-awesome": "^4.7.0",
"mint-ui": "^2.2.13", "mint-ui": "^2.2.13",
"qnweb-rtc": "^4.3.1",
"qrcodejs2": "^0.0.2", "qrcodejs2": "^0.0.2",
"regenerator-runtime": "^0.14.1", "regenerator-runtime": "^0.14.1",
"simple-peer": "^9.11.1", "simple-peer": "^9.11.1",

View File

@ -11,6 +11,15 @@
<img src="../../static/lead/8.png" class="img8" v-if="step == 8"/> <img src="../../static/lead/8.png" class="img8" v-if="step == 8"/>
<img src="../../static/lead/9.png" class="img9" v-if="step == 9"/> <img src="../../static/lead/9.png" class="img9" v-if="step == 9"/>
</div> --> </div> -->
<div class="cover" v-if="haveLive">
<div class="live_box" v-for="live in liveData" @click="goLive(live.room)">
<div>{{ live.title }}</div>
<br />
<div class="stream_info">
<p>開始時間{{ live.start_time }}</p>
</div>
</div>
</div>
<common-top></common-top> <common-top></common-top>
<div class="all"> <div class="all">
<div class="top"> <div class="top">
@ -47,8 +56,8 @@
<p class="kechenginfoR_bt">{{ item.name }}</p> <p class="kechenginfoR_bt">{{ item.name }}</p>
<p class="kechenginfoR_xbt">{{ item.description }}</p> <p class="kechenginfoR_xbt">{{ item.description }}</p>
<div class="kechenginfoR_bot"> <div class="kechenginfoR_bot">
<div class="kechenginfoR_botL"><img src="../../static/img/yanjing.png" alt="" <div class="kechenginfoR_botL"><img src="../../static/img/yanjing.png" alt="" class="yanjing">{{
class="yanjing">{{ item.page_view }}</div> item.page_view }}</div>
<div class="kechenginfoR_botR" @click="goCourseDetail(item.id)">进入课程</div> <div class="kechenginfoR_botR" @click="goCourseDetail(item.id)">进入课程</div>
</div> </div>
</div> </div>
@ -72,7 +81,9 @@ export default {
return { return {
company: {}, company: {},
course: [], course: [],
step: 0 step: 0,
haveLive: false,
liveData: [],
} }
}, },
created() { created() {
@ -80,6 +91,7 @@ export default {
mounted() { mounted() {
this.getPageData(); this.getPageData();
this.getLiveStreams();
}, },
methods: { methods: {
goCourseDetail(id) { goCourseDetail(id) {
@ -124,6 +136,22 @@ export default {
}, },
err => { }) err => { })
}, },
getLiveStreams() {
this.getData("/Membervideo/getLiveStreams", { token: getStore("token") }).then(
(data) => {
if (data.code == 1) {
if (data.data.length > 0) {
this.haveLive = true;
this.liveData = data.data;
}
}
},
(err) => {}
);
},
goLive(room) {
this.$router.push({ path: "/liveview?roomName=" + room });
},
nextLead() { nextLead() {
if (this.step == 9) { if (this.step == 9) {
this.step = 0 this.step = 0
@ -295,4 +323,25 @@ export default {
} }
} }
.cover {
left: 0;
top: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.live_box {
background-color: white;
width: 25rem;
box-shadow: 2px 2px 2px 1px rgba(0, 0, 0, 0.2);
margin: 3rem;
padding: 1rem;
border-radius: 10px;
cursor: pointer;
// display: flex;
// flex-direction: column;
}
</style> </style>

View File

@ -62,7 +62,7 @@
{{ loginInfo.member_realname }}_{{ loginInfo.member_passport }} {{ loginInfo.member_realname }}_{{ loginInfo.member_passport }}
</div> </div>
<div id="teacherCamera" class="teacherCamera" ref="teacherCamera"></div> <div id="teacherCamera" class="teacherCamera" ref="teacherCamera"></div>
<div class="custom-controls" v-show="showControlBar"> <!-- <div class="custom-controls" v-show="showControlBar">
<div class="teacherAudioList" v-show="teacherAudioList.length > 0"> <div class="teacherAudioList" v-show="teacherAudioList.length > 0">
<div v-for="(teacherAudio, index) in teacherAudioList" class="teacherAudio"> <div v-for="(teacherAudio, index) in teacherAudioList" class="teacherAudio">
{{ teacherAudio.name }} {{ teacherAudio.name }}
@ -192,7 +192,7 @@
/> />
</svg> </svg>
</div> </div>
</div> </div> -->
</div> </div>
<div class="menubar"> <div class="menubar">
<div class="menubar-one"> <div class="menubar-one">
@ -309,7 +309,7 @@ import { getStore } from "@/utils/storage";
import { mapState } from "vuex"; import { mapState } from "vuex";
import { MessageBox } from "mint-ui"; import { MessageBox } from "mint-ui";
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
QNRTC.setLogLevel(QNLogLevel.NONE); // QNRTC.setLogLevel(QNLogLevel.NONE);
export default { export default {
components: { Back }, components: { Back },
data() { data() {
@ -467,11 +467,16 @@ export default {
} }
videoTracks[index].play(this.liveContent); videoTracks[index].play(this.liveContent);
// 使 setTimeout
setTimeout(() => {
const videos = document.querySelectorAll( const videos = document.querySelectorAll(
"video.qnrtc-video-player.qnrtc-stream-player" "video.qnrtc-video-player.qnrtc-stream-player"
); );
// controls // controls
for (let i = 0; i < videos.length; i++) { for (let i = 0; i < videos.length; i++) {
videos[i].controls = true;
//
videos[i].style.pointerEvents = "auto";
// //
videos[i].disablePictureInPicture = true; videos[i].disablePictureInPicture = true;
// //
@ -486,6 +491,7 @@ export default {
this.isVisible = true; this.isVisible = true;
}, 10000); }, 10000);
} }
}, 100);
} else if (videoTracks[index].tag === "video") { } else if (videoTracks[index].tag === "video") {
const teacherCameraElement = this.$refs.teacherCamera; const teacherCameraElement = this.$refs.teacherCamera;
// <video> // <video>

14793
yarn.lock

File diff suppressed because it is too large Load Diff