preview.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  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 {getLiveVideoByLiveIdAndType,addLiveVideo} 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. videoList: [
  89. ], // 视频列表数据,实际需从接口获取
  90. liveId: null,
  91. liveInfo: null,
  92. isPrivate:null,
  93. // 表单参数
  94. form: {
  95. uploadType: 1,
  96. isTranscode:0,
  97. transcodeFileKey:null
  98. },
  99. videoUrl:"",
  100. // 是否显示弹出层
  101. open: false,
  102. };
  103. },
  104. watch: {
  105. // 监听路由的 query 参数变化
  106. '$route.query': {
  107. handler(newQuery) {
  108. if (this.$route.params.liveId) {
  109. this.liveId = this.$route.params.liveId;
  110. }else {
  111. this.liveId = this.$route.query.liveId;
  112. }
  113. if(this.liveId == null) {
  114. return;
  115. }
  116. this.getLiveVideo();
  117. },
  118. // 初始化时立即执行一次
  119. immediate: true
  120. }
  121. },
  122. created() {
  123. // if (this.$route.params.liveId) {
  124. // this.liveId = this.$route.params.liveId;
  125. // }else {
  126. // this.liveId = this.$route.query.liveId;
  127. // }
  128. // if(this.liveId == null) {
  129. // this.$message.error("页面错误,请联系管理员");
  130. // return;g
  131. // }
  132. // this.getLive();
  133. },
  134. methods: {
  135. submitForm() {
  136. this.open = false;
  137. const doParam = {
  138. liveId: this.liveId,
  139. videoUrl: this.videoUrl,
  140. videoType: 3,
  141. duration: this.form.duration,
  142. }
  143. addLiveVideo(doParam).then(response => {
  144. if (response.code == 200) {
  145. this.$message.success("上传成功");
  146. this.getLiveVideo()
  147. } else {
  148. this.$message.warning(response.msg);
  149. }
  150. this.$refs.form.resetFields();
  151. })
  152. },
  153. handleVideoDuration(duration) {
  154. this.form.duration = duration;
  155. },
  156. handleVideoChange(videoUrl,lineOne){
  157. this.videoUrl = videoUrl;
  158. this.form.videoUrl = videoUrl;
  159. console.log(this.videoUrl)
  160. },
  161. getLiveVideo() {
  162. getLiveVideoByLiveIdAndType(this.liveId).then(res => {
  163. let dataEntity =
  164. {
  165. duration: "00:00",
  166. status: "回放中",
  167. updateTime: "2025-09-08 14:28:24",
  168. };
  169. //将秒数转为时分秒
  170. dataEntity.duration = this.convertSeconds(res.data.duration);
  171. dataEntity.updateTime = res.data.updateTime;
  172. dataEntity.videoUrl = res.data.videoUrl;
  173. dataEntity.videoName = this.extractFileName(dataEntity.videoUrl)
  174. this.videoList.push(dataEntity);
  175. });
  176. this.loading = false;
  177. },
  178. /**
  179. * 提取文件名称核心方法
  180. * 逻辑:通过split('/')分割路径,取最后一个非空元素作为文件名称
  181. */
  182. extractFileName(data) {
  183. try {
  184. this.errorMsg = '';
  185. const trimmedUrl = data.trim();
  186. // 用'/'分割路径,过滤空字符串(避免路径末尾有'/'导致的空元素)
  187. const pathSegments = trimmedUrl.split('/').filter(segment => segment);
  188. if (pathSegments.length === 0) {
  189. throw new Error('输入的路径格式无效,请检查后重新输入');
  190. }
  191. // 最后一个分段即为文件名称
  192. const fileName = pathSegments[pathSegments.length - 1];
  193. // 简单校验是否为常见视频文件格式(可选,根据需求调整)
  194. const videoExtensions = ['mp4', 'mov', 'avi', 'flv', 'mkv'];
  195. const fileExtension = fileName.split('.').pop()?.toLowerCase();
  196. if (!fileExtension || !videoExtensions.includes(fileExtension)) {
  197. this.errorMsg = '提示:提取到的文件可能不是常见视频格式,请注意校验';
  198. }
  199. return fileName;
  200. } catch (err) {
  201. this.errorMsg = err.message;
  202. return '';
  203. }
  204. },
  205. convertSeconds(data) {
  206. // 确保输入是有效的数字
  207. const totalSeconds = Math.max(0, parseInt(data) || 0);
  208. // 计算时、分、秒
  209. const hours = Math.floor(totalSeconds / 3600);
  210. const minutes = Math.floor((totalSeconds % 3600) / 60);
  211. const seconds = totalSeconds % 60;
  212. // 格式化每个部分为两位数
  213. return `${this.padWithZero(hours)}:${this.padWithZero(minutes)}:${this.padWithZero(seconds)}`;
  214. },
  215. padWithZero(num) {
  216. // 将数字转换为两位数格式
  217. return num.toString().padStart(2, '0');
  218. },
  219. getLive() {
  220. getLive(this.liveId).then(res => {
  221. this.liveInfo = res.data;
  222. if (res.data.liveType == 1) {
  223. this.getLiveVideo();
  224. }
  225. this.loading = false;
  226. });
  227. },
  228. resetForm() {
  229. this.replayForm={
  230. isPlaybackOpen: false, // 直播回放开关状态
  231. playbackMode: "1", // 回放模式,默认模式一
  232. validityType: "days", // 回放有效期类型,默认天数
  233. validDays: 7, // 有效天数
  234. isSpeedAllowed: "1", // 是否允许倍速播放,默认允许
  235. }
  236. },
  237. // 上传视频处理
  238. handleUploadVideo() {
  239. // 模拟上传视频逻辑,实际需调用上传组件或接口
  240. this.open = true;
  241. },
  242. },
  243. };
  244. </script>
  245. <style scoped>
  246. .live-playback-setting {
  247. font-family: "Microsoft Yahei", sans-serif;
  248. color: #333;
  249. background-color: #fff;
  250. padding: 20px;
  251. border-radius: 4px;
  252. box-shadow: 0 0 8px rgba(0, 0, 0, 0.1);
  253. }
  254. .switch-area {
  255. display: flex;
  256. align-items: center;
  257. margin-bottom: 20px;
  258. }
  259. .switch-area .switch-label {
  260. margin-right: 10px;
  261. font-size: 14px;
  262. }
  263. .switch-area .switch-desc {
  264. color: #999;
  265. font-size: 12px;
  266. }
  267. .playback-mode {
  268. background-color: #f9fafc;
  269. padding: 15px;
  270. border-radius: 4px;
  271. margin-bottom: 20px;
  272. }
  273. .playback-mode .mode-title {
  274. font-size: 14px;
  275. margin-bottom: 10px;
  276. }
  277. .playback-mode .mode-option {
  278. display: flex;
  279. align-items: center;
  280. margin-bottom: 8px;
  281. }
  282. .playback-mode .mode-option .el-radio {
  283. margin-left: 2%;
  284. }
  285. .playback-mode .mode-option label {
  286. font-size: 13px;
  287. }
  288. .validity-period {
  289. background-color: #f9fafc;
  290. padding: 15px;
  291. border-radius: 4px;
  292. margin-bottom: 20px;
  293. }
  294. .validity-period .period-title {
  295. font-size: 14px;
  296. margin-bottom: 10px;
  297. }
  298. .validity-period .period-option {
  299. display: flex;
  300. align-items: center;
  301. margin-bottom: 8px;
  302. }
  303. .validity-period .period-option .el-radio {
  304. margin-left: 2%;
  305. }
  306. .validity-period .period-option label {
  307. font-size: 13px;
  308. }
  309. .validity-period .period-desc {
  310. color: #999;
  311. font-size: 12px;
  312. margin-top: 5px;
  313. margin-left: 2%;
  314. }
  315. .validity-period .day-input {
  316. width: 60px;
  317. height: 28px;
  318. border: 1px solid #ddd;
  319. border-radius: 4px;
  320. padding: 0 8px;
  321. margin: 0 5px;
  322. }
  323. .speed-play {
  324. display: flex;
  325. align-items: center;
  326. margin-top: 15px;
  327. }
  328. .speed-play .speed-label {
  329. font-size: 13px;
  330. margin-right: 10px;
  331. }
  332. .speed-play .el-radio {
  333. margin-right: 5px;
  334. }
  335. .speed-play .speed-desc {
  336. color: #999;
  337. font-size: 12px;
  338. margin-left: 10px;
  339. }
  340. .pro-tag {
  341. display: inline-block;
  342. background-color: #007bff;
  343. color: #fff;
  344. font-size: 10px;
  345. padding: 2px 5px;
  346. border-radius: 3px;
  347. margin-left: 5px;
  348. vertical-align: middle;
  349. }
  350. .playback-content {
  351. margin-top: 20px;
  352. }
  353. .playback-content .upload-btn {
  354. display: inline-block;
  355. background-color: #fff;
  356. border: 1px dashed #ddd;
  357. color: #007bff;
  358. padding: 8px 15px;
  359. border-radius: 4px;
  360. cursor: pointer;
  361. font-size: 13px;
  362. margin-bottom: 15px;
  363. }
  364. .playback-content .upload-btn:hover {
  365. border-color: #007bff;
  366. }
  367. .playback-content .upload-tip {
  368. font-size: 12px;
  369. color: #999;
  370. margin-left: 10px;
  371. }
  372. .video-item {
  373. display: flex;
  374. align-items: center;
  375. }
  376. .video-cover {
  377. width: 80px;
  378. height: 45px;
  379. border-radius: 4px;
  380. margin-right: 10px;
  381. }
  382. .video-info .video-name {
  383. font-size: 13px;
  384. margin-bottom: 3px;
  385. }
  386. .video-info .video-desc {
  387. font-size: 12px;
  388. color: #999;
  389. }
  390. .video-duration,
  391. .video-source,
  392. .video-status,
  393. .video-update-time {
  394. color: #666;
  395. }
  396. .video-status .status-dot {
  397. display: inline-block;
  398. width: 8px;
  399. height: 8px;
  400. background-color: #007bff;
  401. border-radius: 50%;
  402. margin-right: 5px;
  403. vertical-align: middle;
  404. }
  405. .video-operation .el-button {
  406. color: #007bff;
  407. font-size: 12px;
  408. margin-right: 10px;
  409. }
  410. .video-url-container {
  411. cursor: pointer;
  412. white-space: nowrap;
  413. overflow: hidden;
  414. text-overflow: ellipsis;
  415. max-width: 100%;
  416. }
  417. </style>