| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527 |
- <template>
- <div class="app-container" v-loading.fullscreen.lock="loading">
- <!-- 直播回放开关区域 -->
- <div class="switch-area">
- <span class="switch-label">直播回放:</span>
- <el-switch
- v-model="replayForm.isPlaybackOpen"
- @change="handlePlaybackSwitch"
- />
- <span class="switch-desc" style="color: #9c9c9c;margin-left: 10px">开启回放,直播结束后学员可查看回放视频</span>
- </div>
- <!-- 回放模式区域 -->
- <div class="playback-mode" v-if="replayForm.isPlaybackOpen">
- <div class="mode-title">回放模式:</div>
- <div class="mode-option">
- <el-radio v-model="replayForm.playbackMode" label="1">模式一:以直播间全部直播视频作为回看</el-radio>
- </div>
- <!-- <div class="mode-option">-->
- <!-- <el-radio v-model="playbackMode" label="2">模式二:以直播间最后一次直播视频作为回看</el-radio>-->
- <!-- </div>-->
- <!-- <div class="mode-option">-->
- <!-- <el-radio v-model="playbackMode" label="3">模式三:自主选择本直播间或素材库任一视频作为回看</el-radio>-->
- <!-- </div>-->
- </div>
- <!-- 回放有效期区域 -->
- <div class="validity-period" v-if="replayForm.isPlaybackOpen">
- <div class="period-title">回放有效期:</div>
- <div class="period-option">
- <el-radio @change="updateForm" v-model="replayForm.validityType" label="permanent">永久有效</el-radio>
- </div>
- <div class="period-option">
- <el-radio v-model="replayForm.validityType" label="days">
- 直播结束后
- <el-input
- v-model.number="replayForm.validDays"
- type="number"
- @change="updateForm"
- :min="1"
- placeholder="天数"
- />
- 天有效
- <el-tooltip
- content="以直播实际结束时间为准"
- placement="top"
- effect="light"
- >
- <i class="el-icon-question" style="color: #999; margin-left: 5px" />
- </el-tooltip> </el-radio>
- </div>
- <div class="period-desc">
- 到期后,学员无法观看回放内容
- </div>
- <!-- 倍速播放区域 -->
- <div class="speed-play">
- <div class="speed-label">倍速播放/快进:</div>
- <el-radio @change="updateForm" v-model="replayForm.isSpeedAllowed" label="1">允许</el-radio>
- <el-radio @change="updateForm" v-model="replayForm.isSpeedAllowed" label="0">禁止</el-radio>
- <span class="speed-desc">禁止时,课程未学完学员不可倍速播放、拖动进度条</span>
- <span class="pro-tag">专业版</span>
- </div>
- </div>
- <!-- 回放内容区域 -->
- <div class="playback-content">
- <div>
- <span>回放内容:</span>
- <!-- <el-button-->
- <!-- class="upload-btn"-->
- <!-- type="text"-->
- <!-- icon="el-icon-plus"-->
- <!-- @click="handleUploadVideo"-->
- <!-- >上传视频</el-button>-->
- <!-- <span class="upload-tip">上传视频大小不可超过5GB</span>-->
- </div>
- <!-- 视频列表 -->
- <el-table
- :data="videoList"
- border
- style="width: 100%; margin-top: 10px"
- >
- <el-table-column prop="videoName" label="视频名称">
- <template slot-scope="scope">
- <div class="video-item">
- <div class="video-info">
- <div class="video-name">{{ scope.row.videoName }}</div>
- </div>
- </div>
- </template>
- </el-table-column>
- <el-table-column prop="duration" label="时长" />
- <el-table-column prop="status" label="状态">
- <template slot-scope="scope">
- <span v-if="scope.row.status === '回放中'">
- <span class="status-dot" />{{ scope.row.status }}
- </span>
- <span v-else>{{ scope.row.status }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="updateTime" label="更新时间" />
- <el-table-column label="视频地址" prop="videoUrl" >
- <template slot-scope="scope">
- <el-tooltip
- :content="scope.row.videoUrl"
- placement="top"
- effect="dark"
- >
- <a
- :href="scope.row.videoUrl"
- target="_blank"
- class="video-url-container"
- rel="noopener noreferrer"
- >
- {{
- scope.row.videoUrl.length > 32
- ? scope.row.videoUrl.substring(0, 32) + '...'
- : scope.row.videoUrl
- }}
- <i class="el-icon-external-link video-url-icon"></i>
- </a>
- </el-tooltip>
- </template>
- </el-table-column>
- <!-- <el-table-column label="操作">-->
- <!-- <template slot-scope="scope">-->
- <!--<!– <el-button–>-->
- <!--<!– type="text"–>-->
- <!--<!– size="small"–>-->
- <!--<!– @click="handleCut(scope.row)"–>-->
- <!--<!– >剪切</el-button>–>-->
- <!--<!– <el-button–>-->
- <!--<!– type="text"–>-->
- <!--<!– size="small"–>-->
- <!--<!– @click="handleGenerateAiSummary(scope.row)"–>-->
- <!--<!– >生成AI速览</el-button>–>-->
- <!--<!– <el-button–>-->
- <!--<!– type="text"–>-->
- <!--<!– size="small"–>-->
- <!--<!– @click="handleGenerateSubtitle(scope.row)"–>-->
- <!--<!– >生成字幕</el-button>–>-->
- <!--<!– <el-button–>-->
- <!--<!– type="text"–>-->
- <!--<!– size="small"–>-->
- <!--<!– @click="handlePlaybackSegment(scope.row)"–>-->
- <!--<!– >回放片段</el-button>–>-->
- <!-- </template>-->
- <!-- </el-table-column>-->
- </el-table>
- </div>
- </div>
- </template>
- <script>
- import {getLive, updateLive,} from '@/api/live/live'
- import {getLiveVideoByLiveId,} from '@/api/live/liveVideo'
- export default {
- name: "LiveReplay",
- data() {
- return {
- loading: true,
- replayForm:{
- isPlaybackOpen: false, // 直播回放开关状态
- playbackMode: "1", // 回放模式,默认模式一
- validityType: "days", // 回放有效期类型,默认天数
- validDays: 7, // 有效天数
- isSpeedAllowed: "1", // 是否允许倍速播放,默认允许
- },
- videoList: [
- {
- },
- ], // 视频列表数据,实际需从接口获取
- liveId: null,
- liveInfo: null,
- };
- },
- watch: {
- // 监听路由的 query 参数变化
- '$route.query': {
- handler(newQuery) {
- if (this.$route.params.liveId) {
- this.liveId = this.$route.params.liveId;
- }else {
- this.liveId = this.$route.query.liveId;
- }
- if(this.liveId == null) {
- return;
- }
- this.getLive();
- },
- // 初始化时立即执行一次
- immediate: true
- }
- },
- created() {
- // if (this.$route.params.liveId) {
- // this.liveId = this.$route.params.liveId;
- // }else {
- // this.liveId = this.$route.query.liveId;
- // }
- // if(this.liveId == null) {
- // this.$message.error("页面错误,请联系管理员");
- // return;
- // }
- // this.getLive();
- },
- methods: {
- getLiveVideo() {
- getLiveVideoByLiveId(this.liveId).then(res => {
- let dataEntity =
- {
- duration: "00:00",
- status: "回放中",
- updateTime: "2025-09-08 14:28:24",
- };
- //将秒数转为时分秒
- dataEntity.duration = this.convertSeconds(res.data.duration);
- dataEntity.updateTime = res.data.updateTime;
- dataEntity.videoUrl = res.data.videoUrl;
- dataEntity.videoName = this.extractFileName(dataEntity.videoUrl)
- this.videoList.push(dataEntity);
- });
- },
- /**
- * 提取文件名称核心方法
- * 逻辑:通过split('/')分割路径,取最后一个非空元素作为文件名称
- */
- extractFileName(data) {
- try {
- this.errorMsg = '';
- const trimmedUrl = data.trim();
- // 用'/'分割路径,过滤空字符串(避免路径末尾有'/'导致的空元素)
- const pathSegments = trimmedUrl.split('/').filter(segment => segment);
- if (pathSegments.length === 0) {
- throw new Error('输入的路径格式无效,请检查后重新输入');
- }
- // 最后一个分段即为文件名称
- const fileName = pathSegments[pathSegments.length - 1];
- // 简单校验是否为常见视频文件格式(可选,根据需求调整)
- const videoExtensions = ['mp4', 'mov', 'avi', 'flv', 'mkv'];
- const fileExtension = fileName.split('.').pop()?.toLowerCase();
- if (!fileExtension || !videoExtensions.includes(fileExtension)) {
- this.errorMsg = '提示:提取到的文件可能不是常见视频格式,请注意校验';
- }
- return fileName;
- } catch (err) {
- this.errorMsg = err.message;
- return '';
- }
- },
- convertSeconds(data) {
- // 确保输入是有效的数字
- const totalSeconds = Math.max(0, parseInt(data) || 0);
- // 计算时、分、秒
- const hours = Math.floor(totalSeconds / 3600);
- const minutes = Math.floor((totalSeconds % 3600) / 60);
- const seconds = totalSeconds % 60;
- // 格式化每个部分为两位数
- return `${this.padWithZero(hours)}:${this.padWithZero(minutes)}:${this.padWithZero(seconds)}`;
- },
- padWithZero(num) {
- // 将数字转换为两位数格式
- return num.toString().padStart(2, '0');
- },
- getLive() {
- getLive(this.liveId).then(res => {
- this.liveInfo = res.data;
- if(res.data.liveConfig){
- this.replayForm = JSON.parse(res.data.liveConfig);
- this.getLiveVideo();
- }else{
- this.resetForm();
- }
- this.loading = false;
- });
- },
- resetForm() {
- this.replayForm={
- isPlaybackOpen: false, // 直播回放开关状态
- playbackMode: "1", // 回放模式,默认模式一
- validityType: "days", // 回放有效期类型,默认天数
- validDays: 7, // 有效天数
- isSpeedAllowed: "1", // 是否允许倍速播放,默认允许
- }
- },
- // 直播回放开关变更处理
- handlePlaybackSwitch(val) {
- if (this.liveInfo.liveType != 1 && this.liveInfo.liveType != 3) {
- this.replayForm.isPlaybackOpen = !val;
- this.$message.error("直播回放开关仅支持直播");
- return;
- }
- if (!this.liveInfo.finishTime) {
- this.replayForm.isPlaybackOpen = !val;
- this.$message.error("直播未结束");
- return;
- }
- this.replayForm.isPlaybackOpen = val;
- this.updateForm()
- },
- updateForm(){
- let param = {
- liveId: this.liveId,
- startTime:this.liveInfo.startTime,
- finishTime:this.liveInfo.finishTime,
- status: 4,
- liveConfig: JSON.stringify(this.replayForm)
- };
- updateLive(param).then(res => {
- if (res.code != 200) {
- this.$message.error(res.msg);
- }
- });
- },
- // 上传视频处理
- handleUploadVideo() {
- // 模拟上传视频逻辑,实际需调用上传组件或接口
- this.$message.info("点击了上传视频按钮,实际需对接上传逻辑");
- },
- // 剪切视频处理
- handleCut(video) {
- this.$message.info(`点击了剪切视频,视频名称:${video.videoName}`);
- // 实际剪切逻辑
- },
- // 生成AI速览处理
- handleGenerateAiSummary(video) {
- this.$message.info(`点击了生成AI速览,视频名称:${video.videoName}`);
- // 实际生成AI速览逻辑
- },
- // 生成字幕处理
- handleGenerateSubtitle(video) {
- this.$message.info(`点击了生成字幕,视频名称:${video.videoName}`);
- // 实际生成字幕逻辑
- },
- // 回放片段处理
- handlePlaybackSegment(video) {
- this.$message.info(`点击了回放片段,视频名称:${video.videoName}`);
- // 实际回放片段逻辑
- },
- },
- };
- </script>
- <style scoped>
- .live-playback-setting {
- font-family: "Microsoft Yahei", sans-serif;
- color: #333;
- background-color: #fff;
- padding: 20px;
- border-radius: 4px;
- box-shadow: 0 0 8px rgba(0, 0, 0, 0.1);
- }
- .switch-area {
- display: flex;
- align-items: center;
- margin-bottom: 20px;
- }
- .switch-area .switch-label {
- margin-right: 10px;
- font-size: 14px;
- }
- .switch-area .switch-desc {
- color: #999;
- font-size: 12px;
- }
- .playback-mode {
- background-color: #f9fafc;
- padding: 15px;
- border-radius: 4px;
- margin-bottom: 20px;
- }
- .playback-mode .mode-title {
- font-size: 14px;
- margin-bottom: 10px;
- }
- .playback-mode .mode-option {
- display: flex;
- align-items: center;
- margin-bottom: 8px;
- }
- .playback-mode .mode-option .el-radio {
- margin-left: 2%;
- }
- .playback-mode .mode-option label {
- font-size: 13px;
- }
- .validity-period {
- background-color: #f9fafc;
- padding: 15px;
- border-radius: 4px;
- margin-bottom: 20px;
- }
- .validity-period .period-title {
- font-size: 14px;
- margin-bottom: 10px;
- }
- .validity-period .period-option {
- display: flex;
- align-items: center;
- margin-bottom: 8px;
- }
- .validity-period .period-option .el-radio {
- margin-left: 2%;
- }
- .validity-period .period-option label {
- font-size: 13px;
- }
- .validity-period .period-desc {
- color: #999;
- font-size: 12px;
- margin-top: 5px;
- margin-left: 2%;
- }
- .validity-period .day-input {
- width: 60px;
- height: 28px;
- border: 1px solid #ddd;
- border-radius: 4px;
- padding: 0 8px;
- margin: 0 5px;
- }
- .speed-play {
- display: flex;
- align-items: center;
- margin-top: 15px;
- }
- .speed-play .speed-label {
- font-size: 13px;
- margin-right: 10px;
- }
- .speed-play .el-radio {
- margin-right: 5px;
- }
- .speed-play .speed-desc {
- color: #999;
- font-size: 12px;
- margin-left: 10px;
- }
- .pro-tag {
- display: inline-block;
- background-color: #007bff;
- color: #fff;
- font-size: 10px;
- padding: 2px 5px;
- border-radius: 3px;
- margin-left: 5px;
- vertical-align: middle;
- }
- .playback-content {
- margin-top: 20px;
- }
- .playback-content .upload-btn {
- display: inline-block;
- background-color: #fff;
- border: 1px dashed #ddd;
- color: #007bff;
- padding: 8px 15px;
- border-radius: 4px;
- cursor: pointer;
- font-size: 13px;
- margin-bottom: 15px;
- }
- .playback-content .upload-btn:hover {
- border-color: #007bff;
- }
- .playback-content .upload-tip {
- font-size: 12px;
- color: #999;
- margin-left: 10px;
- }
- .video-item {
- display: flex;
- align-items: center;
- }
- .video-cover {
- width: 80px;
- height: 45px;
- border-radius: 4px;
- margin-right: 10px;
- }
- .video-info .video-name {
- font-size: 13px;
- margin-bottom: 3px;
- }
- .video-info .video-desc {
- font-size: 12px;
- color: #999;
- }
- .video-duration,
- .video-source,
- .video-status,
- .video-update-time {
- color: #666;
- }
- .video-status .status-dot {
- display: inline-block;
- width: 8px;
- height: 8px;
- background-color: #007bff;
- border-radius: 50%;
- margin-right: 5px;
- vertical-align: middle;
- }
- .video-operation .el-button {
- color: #007bff;
- font-size: 12px;
- margin-right: 10px;
- }
- </style>
|