preview.vue 11 KB

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