preview.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. <template>
  2. <div class="app-container" v-loading.fullscreen.lock="loading">
  3. <!-- 直播回放开关区域 -->
  4. <!-- 回放内容区域 -->
  5. <div class="playback-content">
  6. <div>
  7. <span>预告内容:</span>
  8. <el-button class="upload-btn" type="normal" @click="handleUploadVideo">上传视频</el-button>
  9. <!-- <el-button-->
  10. <!-- class="upload-btn"-->
  11. <!-- type="text"-->
  12. <!-- icon="el-icon-plus"-->
  13. <!-- @click="handleUploadVideo"-->
  14. <!-- >上传视频</el-button>-->
  15. <!-- <span class="upload-tip">上传视频大小不可超过5GB</span>-->
  16. </div>
  17. <el-dialog title="添加直播预告" :visible.sync="open" width="900px" append-to-body>
  18. <el-form ref="form" :model="form" label-width="80px">
  19. <video-upload
  20. :type = "1"
  21. :isPrivate = "isPrivate"
  22. :fileKey.sync = "form.fileKey"
  23. :fileSize.sync = "form.fileSize"
  24. :videoUrl.sync="videoUrl"
  25. :fileName.sync="form.fileName"
  26. :line_2.sync="form.lineTwo"
  27. :line_1.sync="form.lineOne"
  28. :thumbnail.sync="form.thumbnail"
  29. :uploadType.sync="form.uploadType"
  30. :isTranscode.sync="form.isTranscode"
  31. :transcodeFileKey.sync="form.transcodeFileKey"
  32. @video-duration="handleVideoDuration"
  33. @change="handleVideoChange"
  34. ref="videoUpload"
  35. append-to-body
  36. />
  37. </el-form>
  38. <div slot="footer" class="dialog-footer">
  39. <el-button type="primary" @click="submitForm">确 定</el-button>
  40. <el-button @click="open = false">取 消</el-button>
  41. </div>
  42. </el-dialog>
  43. <!-- 视频列表 -->
  44. <el-table
  45. :data="videoList"
  46. border
  47. style="width: 100%; margin-top: 10px"
  48. >
  49. <el-table-column prop="duration" label="时长" />
  50. <el-table-column prop="updateTime" label="更新时间" />
  51. <el-table-column label="视频地址(可点击查看)" prop="videoUrl" >
  52. <template slot-scope="scope">
  53. <el-tooltip
  54. :content="scope.row.videoUrl"
  55. placement="top"
  56. effect="dark"
  57. >
  58. <a
  59. :href="scope.row.videoUrl"
  60. target="_blank"
  61. class="video-url-container"
  62. rel="noopener noreferrer"
  63. >
  64. {{
  65. scope.row.videoUrl.length > 32
  66. ? scope.row.videoUrl.substring(0, 32) + '...'
  67. : scope.row.videoUrl
  68. }}
  69. <i class="el-icon-external-link video-url-icon"></i>
  70. </a>
  71. </el-tooltip>
  72. </template>
  73. </el-table-column>
  74. </el-table>
  75. </div>
  76. </div>
  77. </template>
  78. <script>
  79. import { getLive,} from '@/api/live/live'
  80. import { addLiveVideo, getLiveVideoByLiveIdAndType} from '@/api/live/liveVideo'
  81. import VideoUpload from "@/components/LiveVideoUpload/index.vue";
  82. export default {
  83. name: "Preview",
  84. components: {VideoUpload},
  85. data() {
  86. return {
  87. loading: true,
  88. replayForm:{
  89. isPlaybackOpen: false, // 直播回放开关状态
  90. playbackMode: "1", // 回放模式,默认模式一
  91. validityType: "days", // 回放有效期类型,默认天数
  92. validDays: 7, // 有效天数
  93. isSpeedAllowed: "1", // 是否允许倍速播放,默认允许
  94. },
  95. videoList: [
  96. ], // 视频列表数据,实际需从接口获取
  97. liveId: null,
  98. liveInfo: null,
  99. isPrivate:null,
  100. // 表单参数
  101. form: {
  102. uploadType: 1,
  103. isTranscode:0,
  104. transcodeFileKey:null
  105. },
  106. videoUrl:"",
  107. // 是否显示弹出层
  108. open: false,
  109. };
  110. },
  111. watch: {
  112. // 监听路由的 query 参数变化
  113. '$route.query': {
  114. handler(newQuery) {
  115. if (this.$route.params.liveId) {
  116. this.liveId = this.$route.params.liveId;
  117. }else {
  118. this.liveId = this.$route.query.liveId;
  119. }
  120. if(this.liveId == null) {
  121. return;
  122. }
  123. this.getLiveVideo();
  124. },
  125. // 初始化时立即执行一次
  126. immediate: true
  127. }
  128. },
  129. created() {
  130. // if (this.$route.params.liveId) {
  131. // this.liveId = this.$route.params.liveId;
  132. // }else {
  133. // this.liveId = this.$route.query.liveId;
  134. // }
  135. // if(this.liveId == null) {
  136. // this.$message.error("页面错误,请联系管理员");
  137. // return;
  138. // }
  139. // this.getLive();
  140. },
  141. methods: {
  142. submitForm() {
  143. this.open = false;
  144. const doParam = {
  145. liveId: this.liveId,
  146. videoUrl: this.videoUrl,
  147. videoType: 3,
  148. duration: this.form.duration,
  149. fileSize: this.form.fileSize,
  150. }
  151. addLiveVideo(doParam).then(response => {
  152. if (response.code == 200) {
  153. this.$message.success("上传成功");
  154. } else {
  155. this.$message.warning(response.msg);
  156. }
  157. this.getLiveVideo()
  158. this.$refs.form.resetFields();
  159. })
  160. },
  161. handleVideoDuration(duration) {
  162. this.form.duration = duration;
  163. },
  164. handleVideoChange(videoUrl,lineOne){
  165. this.videoUrl = videoUrl;
  166. this.form.videoUrl = videoUrl;
  167. console.log(this.videoUrl)
  168. },
  169. getLiveVideo() {
  170. getLiveVideoByLiveIdAndType(this.liveId).then(res => {
  171. let dataEntity =
  172. {
  173. duration: "00:00",
  174. status: "回放中",
  175. updateTime: "2025-09-08 14:28:24",
  176. };
  177. //将秒数转为时分秒
  178. dataEntity.duration = this.convertSeconds(res.data.duration);
  179. dataEntity.updateTime = res.data.updateTime;
  180. dataEntity.videoUrl = res.data.videoUrl;
  181. dataEntity.videoName = this.extractFileName(dataEntity.videoUrl)
  182. this.videoList.push(dataEntity);
  183. });
  184. this.loading = false;
  185. },
  186. /**
  187. * 提取文件名称核心方法
  188. * 逻辑:通过split('/')分割路径,取最后一个非空元素作为文件名称
  189. */
  190. extractFileName(data) {
  191. try {
  192. this.errorMsg = '';
  193. const trimmedUrl = data.trim();
  194. // 用'/'分割路径,过滤空字符串(避免路径末尾有'/'导致的空元素)
  195. const pathSegments = trimmedUrl.split('/').filter(segment => segment);
  196. if (pathSegments.length === 0) {
  197. throw new Error('输入的路径格式无效,请检查后重新输入');
  198. }
  199. // 最后一个分段即为文件名称
  200. const fileName = pathSegments[pathSegments.length - 1];
  201. // 简单校验是否为常见视频文件格式(可选,根据需求调整)
  202. const videoExtensions = ['mp4', 'mov', 'avi', 'flv', 'mkv'];
  203. const fileExtension = fileName.split('.').pop()?.toLowerCase();
  204. if (!fileExtension || !videoExtensions.includes(fileExtension)) {
  205. this.errorMsg = '提示:提取到的文件可能不是常见视频格式,请注意校验';
  206. }
  207. return fileName;
  208. } catch (err) {
  209. this.errorMsg = err.message;
  210. return '';
  211. }
  212. },
  213. convertSeconds(data) {
  214. // 确保输入是有效的数字
  215. const totalSeconds = Math.max(0, parseInt(data) || 0);
  216. // 计算时、分、秒
  217. const hours = Math.floor(totalSeconds / 3600);
  218. const minutes = Math.floor((totalSeconds % 3600) / 60);
  219. const seconds = totalSeconds % 60;
  220. // 格式化每个部分为两位数
  221. return `${this.padWithZero(hours)}:${this.padWithZero(minutes)}:${this.padWithZero(seconds)}`;
  222. },
  223. padWithZero(num) {
  224. // 将数字转换为两位数格式
  225. return num.toString().padStart(2, '0');
  226. },
  227. getLive() {
  228. getLive(this.liveId).then(res => {
  229. this.liveInfo = res.data;
  230. if (res.data.liveType == 1) {
  231. this.getLiveVideo();
  232. }
  233. this.loading = false;
  234. });
  235. },
  236. resetForm() {
  237. this.replayForm={
  238. isPlaybackOpen: false, // 直播回放开关状态
  239. playbackMode: "1", // 回放模式,默认模式一
  240. validityType: "days", // 回放有效期类型,默认天数
  241. validDays: 7, // 有效天数
  242. isSpeedAllowed: "1", // 是否允许倍速播放,默认允许
  243. }
  244. },
  245. // 上传视频处理
  246. handleUploadVideo() {
  247. this.videoList = [];
  248. this.form = {
  249. uploadType: 1,
  250. isTranscode:0,
  251. transcodeFileKey:null,
  252. videoUrl: null,
  253. fileSize: null,
  254. };
  255. setTimeout(() => {
  256. this.$refs.videoUpload.reset();
  257. }, 100);
  258. // 模拟上传视频逻辑,实际需调用上传组件或接口
  259. this.open = true;
  260. },
  261. },
  262. };
  263. </script>
  264. <style scoped>
  265. .live-playback-setting {
  266. font-family: "Microsoft Yahei", sans-serif;
  267. color: #333;
  268. background-color: #fff;
  269. padding: 20px;
  270. border-radius: 4px;
  271. box-shadow: 0 0 8px rgba(0, 0, 0, 0.1);
  272. }
  273. .switch-area {
  274. display: flex;
  275. align-items: center;
  276. margin-bottom: 20px;
  277. }
  278. .switch-area .switch-label {
  279. margin-right: 10px;
  280. font-size: 14px;
  281. }
  282. .switch-area .switch-desc {
  283. color: #999;
  284. font-size: 12px;
  285. }
  286. .playback-mode {
  287. background-color: #f9fafc;
  288. padding: 15px;
  289. border-radius: 4px;
  290. margin-bottom: 20px;
  291. }
  292. .playback-mode .mode-title {
  293. font-size: 14px;
  294. margin-bottom: 10px;
  295. }
  296. .playback-mode .mode-option {
  297. display: flex;
  298. align-items: center;
  299. margin-bottom: 8px;
  300. }
  301. .playback-mode .mode-option .el-radio {
  302. margin-left: 2%;
  303. }
  304. .playback-mode .mode-option label {
  305. font-size: 13px;
  306. }
  307. .validity-period {
  308. background-color: #f9fafc;
  309. padding: 15px;
  310. border-radius: 4px;
  311. margin-bottom: 20px;
  312. }
  313. .validity-period .period-title {
  314. font-size: 14px;
  315. margin-bottom: 10px;
  316. }
  317. .validity-period .period-option {
  318. display: flex;
  319. align-items: center;
  320. margin-bottom: 8px;
  321. }
  322. .validity-period .period-option .el-radio {
  323. margin-left: 2%;
  324. }
  325. .validity-period .period-option label {
  326. font-size: 13px;
  327. }
  328. .validity-period .period-desc {
  329. color: #999;
  330. font-size: 12px;
  331. margin-top: 5px;
  332. margin-left: 2%;
  333. }
  334. .validity-period .day-input {
  335. width: 60px;
  336. height: 28px;
  337. border: 1px solid #ddd;
  338. border-radius: 4px;
  339. padding: 0 8px;
  340. margin: 0 5px;
  341. }
  342. .speed-play {
  343. display: flex;
  344. align-items: center;
  345. margin-top: 15px;
  346. }
  347. .speed-play .speed-label {
  348. font-size: 13px;
  349. margin-right: 10px;
  350. }
  351. .speed-play .el-radio {
  352. margin-right: 5px;
  353. }
  354. .speed-play .speed-desc {
  355. color: #999;
  356. font-size: 12px;
  357. margin-left: 10px;
  358. }
  359. .pro-tag {
  360. display: inline-block;
  361. background-color: #007bff;
  362. color: #fff;
  363. font-size: 10px;
  364. padding: 2px 5px;
  365. border-radius: 3px;
  366. margin-left: 5px;
  367. vertical-align: middle;
  368. }
  369. .playback-content {
  370. margin-top: 20px;
  371. }
  372. .playback-content .upload-btn {
  373. display: inline-block;
  374. background-color: #fff;
  375. border: 1px dashed #ddd;
  376. color: #007bff;
  377. padding: 8px 15px;
  378. border-radius: 4px;
  379. cursor: pointer;
  380. font-size: 13px;
  381. margin-bottom: 15px;
  382. }
  383. .playback-content .upload-btn:hover {
  384. border-color: #007bff;
  385. }
  386. .playback-content .upload-tip {
  387. font-size: 12px;
  388. color: #999;
  389. margin-left: 10px;
  390. }
  391. .video-item {
  392. display: flex;
  393. align-items: center;
  394. }
  395. .video-cover {
  396. width: 80px;
  397. height: 45px;
  398. border-radius: 4px;
  399. margin-right: 10px;
  400. }
  401. .video-info .video-name {
  402. font-size: 13px;
  403. margin-bottom: 3px;
  404. }
  405. .video-info .video-desc {
  406. font-size: 12px;
  407. color: #999;
  408. }
  409. .video-duration,
  410. .video-source,
  411. .video-status,
  412. .video-update-time {
  413. color: #666;
  414. }
  415. .video-status .status-dot {
  416. display: inline-block;
  417. width: 8px;
  418. height: 8px;
  419. background-color: #007bff;
  420. border-radius: 50%;
  421. margin-right: 5px;
  422. vertical-align: middle;
  423. }
  424. .video-operation .el-button {
  425. color: #007bff;
  426. font-size: 12px;
  427. margin-right: 10px;
  428. }
  429. .video-url-container {
  430. cursor: pointer;
  431. white-space: nowrap;
  432. overflow: hidden;
  433. text-overflow: ellipsis;
  434. max-width: 100%;
  435. }
  436. </style>