016d69e51e292625f82f66e1aed85f2f.txt 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
  1. <template>
  2. <div class="video-container">
  3. <h1 class="video-title">视频管理</h1>
  4. <!-- 搜索和操作区域 -->
  5. <div class="video-operations">
  6. <el-input
  7. v-model="searchQuery"
  8. placeholder="搜索视频名称"
  9. class="search-input"
  10. clearable
  11. @clear="handleSearch"
  12. @keyup.enter.native="handleSearch"
  13. >
  14. <el-button slot="append" icon="el-icon-search" @click="handleSearch" />
  15. </el-input>
  16. <el-button
  17. type="primary"
  18. icon="el-icon-upload"
  19. @click="showUploadDialog"
  20. >
  21. 上传视频
  22. </el-button>
  23. </div>
  24. <!-- 视频列表表格 -->
  25. <el-table
  26. :data="filteredVideos"
  27. border
  28. style="width: 100%"
  29. v-loading="loading"
  30. >
  31. <el-table-column prop="id" label="ID" width="80" />
  32. <el-table-column prop="upload_user" label="上传员工" />
  33. <el-table-column prop="name" label="视频名称" />
  34. <el-table-column label="封面">
  35. <template slot-scope="{ row }">
  36. <img v-if="row.video_img" :src="row.video_img" class="video-cover" />
  37. </template>
  38. </el-table-column>
  39. <el-table-column label="视频地址">
  40. <template slot-scope="{ row }">
  41. <el-link :href="row.video_address" target="_blank">查看视频</el-link>
  42. </template>
  43. </el-table-column>
  44. <el-table-column prop="create_time" label="创建时间" width="180" />
  45. <el-table-column prop="update_time" label="更新时间" width="180" />
  46. <el-table-column label="操作" width="180">
  47. <template slot-scope="{ row }">
  48. <el-button size="small" @click="handleEdit(row)">编辑</el-button>
  49. <el-button size="small" type="danger" @click="handleDelete(row)">删除</el-button>
  50. </template>
  51. </el-table-column>
  52. </el-table>
  53. <!-- 分页 -->
  54. <div class="pagination-container">
  55. <el-pagination
  56. @size-change="handleSizeChange"
  57. @current-change="handleCurrentChange"
  58. :current-page="currentPage"
  59. :page-sizes="[10, 20, 50, 100]"
  60. :page-size="pageSize"
  61. layout="total, sizes, prev, pager, next, jumper"
  62. :total="total"
  63. />
  64. </div>
  65. <!-- 上传视频对话框 -->
  66. <el-dialog
  67. title="上传视频"
  68. :visible.sync="uploadDialogVisible"
  69. width="50%"
  70. :close-on-click-modal="false"
  71. >
  72. <el-form
  73. ref="uploadForm"
  74. :model="uploadForm"
  75. :rules="uploadRules"
  76. label-width="100px"
  77. >
  78. <el-form-item label="视频名称" prop="name">
  79. <el-input v-model="uploadForm.name" />
  80. </el-form-item>
  81. <el-form-item label="视频封面" prop="coverUrl">
  82. <el-upload
  83. class="cover-uploader"
  84. action=""
  85. :show-file-list="false"
  86. :auto-upload="false"
  87. :on-change="handleCoverChange"
  88. :before-upload="beforeCoverUpload"
  89. >
  90. <img v-if="uploadForm.coverUrl" :src="uploadForm.coverUrl" class="cover-image" />
  91. <i v-else class="el-icon-plus cover-uploader-icon"></i>
  92. <div class="el-upload__tip">建议尺寸16:9,不超过2MB</div>
  93. <!-- 增加上传状态提示 -->
  94. <div v-if="coverUploading" class="el-upload__tip">封面上传中...</div>
  95. <div v-if="coverUploadError" class="el-upload__tip" style="color: red;">{{ coverUploadError }}</div>
  96. </el-upload>
  97. </el-form-item>
  98. <el-form-item label="视频文件" prop="video">
  99. <el-upload
  100. class="video-uploader"
  101. drag
  102. action=""
  103. :auto-upload="false"
  104. :on-change="handleVideoChange"
  105. :before-upload="beforeVideoUpload"
  106. :file-list="videoFileList"
  107. :limit="1"
  108. >
  109. <i class="el-icon-upload"></i>
  110. <div class="el-upload__text">将视频拖到此处,或<em>点击上传</em></div>
  111. <div class="el-upload__tip">
  112. 支持MP4/AVI/MOV/WMV格式,不超过100MB
  113. <el-progress
  114. v-if="uploadProgress > 0"
  115. :percentage="uploadProgress"
  116. :stroke-width="2"
  117. style="margin-top: 10px;"
  118. />
  119. </div>
  120. </el-upload>
  121. </el-form-item>
  122. </el-form>
  123. <span slot="footer" class="dialog-footer">
  124. <el-button @click="closeUploadDialog">取 消</el-button>
  125. <el-button
  126. type="primary"
  127. @click="handleUpload"
  128. >
  129. 确 定
  130. </el-button>
  131. </span>
  132. </el-dialog>
  133. <!-- 编辑对话框 -->
  134. <el-dialog
  135. title="编辑视频信息"
  136. :visible.sync="editDialogVisible"
  137. width="30%"
  138. >
  139. <!-- <el-form :model="editForm" ref="editForm" label-width="100px">
  140. <el-form-item label="视频名称" prop="name">
  141. <el-input v-model="editForm.name" />
  142. </el-form-item>
  143. </el-form> -->
  144. <el-form
  145. ref="uploadForm"
  146. :model="uploadForm"
  147. :rules="uploadRules"
  148. label-width="100px"
  149. >
  150. <el-form-item label="视频名称" prop="name">
  151. <el-input v-model="editForm.name" />
  152. </el-form-item>
  153. <el-form-item label="视频封面" prop="coverUrl">
  154. <el-upload
  155. class="cover-uploader"
  156. action=""
  157. :show-file-list="false"
  158. :auto-upload="false"
  159. :on-change="edithandleCoverChange"
  160. :before-upload="beforeCoverUpload"
  161. >
  162. <img v-if="editForm.video_img" :src="editForm.video_img" class="cover-image" />
  163. <i v-else class="el-icon-plus cover-uploader-icon"></i>
  164. <div class="el-upload__tip">建议尺寸16:9,不超过2MB</div>
  165. <!-- 增加上传状态提示 -->
  166. <div v-if="coverUploading" class="el-upload__tip">封面上传中...</div>
  167. <div v-if="coverUploadError" class="el-upload__tip" style="color: red;">{{ coverUploadError }}</div>
  168. </el-upload>
  169. </el-form-item>
  170. <el-form-item label="视频文件" prop="video">
  171. <el-upload
  172. class="video-uploader"
  173. drag
  174. action=""
  175. :auto-upload="false"
  176. :on-change="edithandleVideoChange"
  177. :before-upload="beforeVideoUpload"
  178. :file-list="videoFileList"
  179. :limit="1"
  180. >
  181. <i class="el-icon-upload"></i>
  182. <div class="el-upload__text">将视频拖到此处,或<em>点击上传</em></div>
  183. <div class="el-upload__tip">
  184. 支持MP4/AVI/MOV/WMV格式,不超过100MB
  185. <el-progress
  186. v-if="uploadProgress > 0"
  187. :percentage="uploadProgress"
  188. :stroke-width="2"
  189. style="margin-top: 10px;"
  190. />
  191. </div>
  192. </el-upload>
  193. </el-form-item>
  194. </el-form>
  195. <span slot="footer" class="dialog-footer">
  196. <el-button @click="editDialogVisible = false">取 消</el-button>
  197. <el-button type="primary" @click="submitEdit">确 定</el-button>
  198. </span>
  199. </el-dialog>
  200. </div>
  201. </template>
  202. <script>
  203. import { userApi } from '@/api/index.js';
  204. export default {
  205. name: "VideoManager",
  206. data() {
  207. return {
  208. videoList: [],
  209. loading: false,
  210. searchQuery: '',
  211. currentPage: 1,
  212. pageSize: 10,
  213. total: 0,
  214. // 上传相关
  215. uploadDialogVisible: false,
  216. uploadLoading: false,
  217. uploadProgress: 0,
  218. coverUploading: false, // 新增:封面上传状态
  219. coverUploadError: '', // 新增:封面上传错误信息
  220. uploadForm: {
  221. name: '',
  222. coverUrl: '',
  223. coverPath: '', // 新增:存储服务器返回的封面路径
  224. video: null,
  225. videoUrl: ''
  226. },
  227. videoFileList: [],
  228. // 编辑相关
  229. editDialogVisible: false,
  230. editForm: {
  231. id: '',
  232. name: ''
  233. },
  234. is_image:'',
  235. // 验证规则
  236. uploadRules: {
  237. name: [{ required: true, message: '请输入视频名称', trigger: 'blur' }],
  238. coverUrl: [{ required: true, message: '请上传视频封面', trigger: 'change' }],
  239. video: [{ required: true, message: '请上传视频文件', trigger: 'change' }]
  240. }
  241. };
  242. },
  243. computed: {
  244. filteredVideos() {
  245. return this.videoList.filter(item =>
  246. item.name.toLowerCase().includes(this.searchQuery.toLowerCase())
  247. );
  248. },
  249. // 新增计算属性:检查表单是否有效
  250. isUploadFormValid() {
  251. return (
  252. this.uploadForm.name &&
  253. this.uploadForm.coverPath &&
  254. this.uploadForm.videoUrl &&
  255. !this.coverUploading
  256. );
  257. }
  258. },
  259. created() {
  260. this.fetchVideoList();
  261. },
  262. methods: {
  263. // 获取视频列表
  264. async fetchVideoList() {
  265. this.loading = true;
  266. try {
  267. const res = await userApi.getVideoList({
  268. page: this.currentPage,
  269. size: this.pageSize
  270. });
  271. this.videoList = res.data.data.list || [];
  272. this.total = res.data.data.total || 0;
  273. } catch (error) {
  274. this.$message.error('获取视频列表失败');
  275. console.error(error);
  276. } finally {
  277. this.loading = false;
  278. }
  279. },
  280. // 搜索
  281. handleSearch() {
  282. this.currentPage = 1;
  283. this.fetchVideoList();
  284. },
  285. // 分页
  286. handleSizeChange(val) {
  287. this.pageSize = val;
  288. this.fetchVideoList();
  289. },
  290. handleCurrentChange(val) {
  291. this.currentPage = val;
  292. this.fetchVideoList();
  293. },
  294. // 上传相关方法
  295. showUploadDialog() {
  296. this.uploadDialogVisible = true;
  297. this.resetUploadForm();
  298. },
  299. closeUploadDialog() {
  300. this.uploadDialogVisible = false;
  301. this.resetUploadForm();
  302. },
  303. resetUploadForm() {
  304. this.uploadForm = {
  305. name: '',
  306. coverUrl: '',
  307. coverPath: '',
  308. video: null,
  309. videoUrl: ''
  310. };
  311. this.videoFileList = [];
  312. this.uploadProgress = 0;
  313. this.coverUploading = false;
  314. this.coverUploadError = '';
  315. this.$refs.uploadForm?.resetFields();
  316. },
  317. beforeCoverUpload(file) {
  318. const isImage = ['image/jpeg', 'image/png'].includes(file.type);
  319. const isLt2M = file.size / 1024 / 1024 < 2;
  320. if (!isImage) {
  321. this.$message.error('封面必须是JPG/PNG图片!');
  322. return false;
  323. }
  324. if (!isLt2M) {
  325. this.$message.error('封面大小不能超过2MB!');
  326. return false;
  327. }
  328. return true;
  329. },
  330. // 修改handleCoverChange方法
  331. async handleCoverChange(file) {
  332. if (!this.beforeCoverUpload(file.raw)) {
  333. return;
  334. }
  335. this.coverUploading = true;
  336. this.coverUploadError = '';
  337. // 显示本地预览
  338. this.uploadForm.coverUrl = URL.createObjectURL(file.raw);
  339. // 创建FormData并上传
  340. const formData = new FormData();
  341. formData.append('file', file.raw);
  342. // 添加请求配置
  343. // const config = {
  344. // headers: {
  345. // 'Content-Type': 'multipart/form-data'
  346. // },
  347. // timeout: 30000 // 30秒超时
  348. // };
  349. const res = await userApi.uploadImage(formData);
  350. console.log('封面上传响应:', res);
  351. console.log('封面上传响应:', res.data);
  352. // 适配不同的返回格式
  353. if (res.data.code === 200) {
  354. this.uploadForm.coverPath = res.data.filePath;
  355. this.$message.success('封面上传成功');
  356. } else {
  357. throw new Error(res.message || '封面上传失败');
  358. }
  359. },
  360. async edithandleCoverChange(file) {
  361. if (!this.beforeCoverUpload(file.raw)) {
  362. return;
  363. }
  364. this.coverUploading = true;
  365. this.coverUploadError = '';
  366. // 显示本地预览
  367. this.uploadForm.coverUrl = URL.createObjectURL(file.raw);
  368. // 创建FormData并上传
  369. const formData = new FormData();
  370. formData.append('file', file.raw);
  371. // 添加请求配置
  372. const res = await userApi.uploadImage(formData);
  373. console.log('封面上传响应:', res);
  374. console.log('封面上传响应:', res.data);
  375. console.log('封面上传响应:', res.data.filePath);
  376. // 适配不同的返回格式
  377. if (res.data.code === 200) {
  378. this.editForm.video_img = res.data.filePath;
  379. this.is_image = res.data.filePath;
  380. this.$message.success('封面上传成功');
  381. } else {
  382. throw new Error(res.message || '封面上传失败');
  383. }
  384. },
  385. beforeVideoUpload(file) {
  386. const validExts = ['mp4', 'avi', 'mov', 'wmv'];
  387. const validMimes = [
  388. 'video/mp4',
  389. 'video/quicktime',
  390. 'video/x-msvideo',
  391. 'video/x-ms-wmv'
  392. ];
  393. const fileExt = file.name.split('.').pop().toLowerCase();
  394. const isValidExt = validExts.includes(fileExt);
  395. const isValidMime = validMimes.includes(file.type);
  396. const isLt100M = file.size / 1024 / 1024 < 100;
  397. if (!isValidExt) {
  398. this.$message.error(`仅支持 ${validExts.join(', ')} 格式!`);
  399. return false;
  400. }
  401. if (!isValidMime) {
  402. this.$message.error('文件类型不匹配!');
  403. return false;
  404. }
  405. if (!isLt100M) {
  406. this.$message.error('视频不能超过100MB!');
  407. return false;
  408. }
  409. return true;
  410. },
  411. // 修改handleVideoChange方法
  412. async handleVideoChange(file) {
  413. if (!this.beforeVideoUpload(file.raw)) {
  414. this.videoFileList = [];
  415. return;
  416. }
  417. this.videoFileList = [file];
  418. this.uploadProgress = 0;
  419. try {
  420. const formData = new FormData();
  421. formData.append('file', file.raw, file.name);
  422. // 添加错误处理
  423. try {
  424. const res = await userApi.uploadVideo(formData);
  425. console.log('视频上传响应:', res.data);
  426. console.log('视频上传响应:', res.data.data);
  427. if (res.data.code === 200) {
  428. this.uploadForm.videoUrl = res.data.data.url;
  429. this.$message.success('视频上传成功');
  430. } else {
  431. throw new Error(res.message || '上传失败');
  432. }
  433. } catch (error) {
  434. // 捕获并显示详细的错误信息
  435. let errorMsg = error.message;
  436. if (error.response) {
  437. errorMsg = error.response.data?.message || errorMsg;
  438. }
  439. throw new Error(`视频上传失败: ${errorMsg}`);
  440. }
  441. } catch (error) {
  442. this.$message.error(error.message);
  443. // this.resetVideoUpload();
  444. console.error('视频上传错误详情:', error);
  445. }
  446. },
  447. // 修改handleVideoChange方法
  448. async edithandleVideoChange(file) {
  449. console.log('编辑视频上传文件111:', file);
  450. if (!this.beforeVideoUpload(file.raw)) {
  451. this.videoFileList = [];
  452. return;
  453. }
  454. this.videoFileList = [file];
  455. this.uploadProgress = 0;
  456. try {
  457. const formData = new FormData();
  458. formData.append('file', file.raw, file.name);
  459. // 添加错误处理
  460. try {
  461. const res = await userApi.uploadVideo(formData);
  462. console.log('视频上传响应:', res.data);
  463. console.log('视频上传响应:', res.data.data);
  464. if (res.data.code === 200) {
  465. this.editForm.video_address = res.data.data.url;
  466. this.$message.success('视频上传成功');
  467. } else {
  468. throw new Error(res.message || '上传失败');
  469. }
  470. } catch (error) {
  471. // 捕获并显示详细的错误信息
  472. let errorMsg = error.message;
  473. if (error.response) {
  474. errorMsg = error.response.data?.message || errorMsg;
  475. }
  476. throw new Error(`视频上传失败: ${errorMsg}`);
  477. }
  478. } catch (error) {
  479. this.$message.error(error.message);
  480. // this.resetVideoUpload();
  481. console.error('视频上传错误详情:', error);
  482. }
  483. },
  484. // 提交上传
  485. // 提交上传
  486. async handleUpload() {
  487. console.log('提交上传表单:', this.uploadForm);
  488. try {
  489. // 验证表单
  490. // await this.$refs.uploadForm.validate();
  491. // 检查视频是否已上传
  492. if (!this.uploadForm.videoUrl) {
  493. this.$message.error('请先上传视频文件');
  494. return;
  495. }
  496. this.uploadLoading = true;
  497. // 如果用户没有上传封面,则从视频中截取一帧作为封面
  498. if (!this.uploadForm.coverPath) {
  499. try {
  500. // 创建视频元素来截取帧
  501. const video = document.createElement('video');
  502. video.crossOrigin = 'anonymous';
  503. video.src = this.uploadForm.videoUrl;
  504. // 等待视频加载完成
  505. await new Promise((resolve, reject) => {
  506. video.addEventListener('loadeddata', resolve);
  507. video.addEventListener('error', reject);
  508. video.load();
  509. });
  510. // 等待视频可以播放
  511. await new Promise(resolve => {
  512. video.addEventListener('canplay', resolve);
  513. });
  514. // 跳转到视频的10%位置(通常这里会有有意义的画面)
  515. video.currentTime = video.duration * 0.1;
  516. // 等待seek完成
  517. await new Promise(resolve => {
  518. video.addEventListener('seeked', resolve);
  519. });
  520. // 创建canvas来截取帧
  521. const canvas = document.createElement('canvas');
  522. canvas.width = video.videoWidth;
  523. canvas.height = video.videoHeight;
  524. const ctx = canvas.getContext('2d');
  525. ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
  526. // 将canvas转换为blob
  527. const blob = await new Promise(resolve => {
  528. canvas.toBlob(resolve, 'image/jpeg', 0.8);
  529. });
  530. // 创建FormData并上传截取的封面
  531. const formData = new FormData();
  532. formData.append('file', blob, 'auto_capture.jpg');
  533. const config = {
  534. headers: {
  535. 'Content-Type': 'multipart/form-data'
  536. },
  537. timeout: 30000
  538. };
  539. const res = await userApi.uploadImage(formData, config);
  540. if (res.data.code === 200) {
  541. this.uploadForm.coverPath = res.data.filePath;
  542. this.uploadForm.coverUrl = URL.createObjectURL(blob);
  543. this.$message.success('已自动截取视频帧作为封面');
  544. } else {
  545. throw new Error(res.message || '封面截取失败');
  546. }
  547. } catch (error) {
  548. console.error('截取视频封面失败:', error);
  549. this.$message.error('自动截取封面失败,请手动上传封面');
  550. return;
  551. }
  552. }
  553. const userInfo = JSON.parse(localStorage.getItem('userInfo'));
  554. const user = userInfo.employeeName + '(' + userInfo.employeeUsername + ')';
  555. // 提交视频信息
  556. const res = await userApi.addVideoList({
  557. upload_user: user,
  558. name: this.uploadForm.name,
  559. video_img: this.uploadForm.coverPath,
  560. video_address: this.uploadForm.videoUrl
  561. });
  562. if (res.data.code === 1 || res.data.code === 200) {
  563. this.$message.success('上传成功');
  564. this.closeUploadDialog();
  565. this.fetchVideoList();
  566. } else {
  567. throw new Error(res.message || '提交失败');
  568. }
  569. } catch (error) {
  570. console.error('上传失败:', error);
  571. this.$message.error(error.message || '上传失败');
  572. } finally {
  573. this.uploadLoading = false;
  574. }
  575. },
  576. // 编辑相关
  577. handleEdit(row) {
  578. this.editForm = { ...row };
  579. this.editDialogVisible = true;
  580. },
  581. async submitEdit() {
  582. if(!this.is_image){
  583. console.log('is_image:', this.is_image);
  584. try {
  585. // 创建视频元素来截取帧
  586. const video = document.createElement('video');
  587. video.crossOrigin = 'anonymous';
  588. video.src = this.editForm.video_address;
  589. // 等待视频加载完成
  590. await new Promise((resolve, reject) => {
  591. video.addEventListener('loadeddata', resolve);
  592. video.addEventListener('error', reject);
  593. video.load();
  594. });
  595. // 等待视频可以播放
  596. await new Promise(resolve => {
  597. video.addEventListener('canplay', resolve);
  598. });
  599. // 跳转到视频的10%位置(通常这里会有有意义的画面)
  600. video.currentTime = video.duration * 0.1;
  601. // 等待seek完成
  602. await new Promise(resolve => {
  603. video.addEventListener('seeked', resolve);
  604. });
  605. // 创建canvas来截取帧
  606. const canvas = document.createElement('canvas');
  607. canvas.width = video.videoWidth;
  608. canvas.height = video.videoHeight;
  609. const ctx = canvas.getContext('2d');
  610. ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
  611. // 将canvas转换为blob
  612. const blob = await new Promise(resolve => {
  613. canvas.toBlob(resolve, 'image/jpeg', 0.8);
  614. });
  615. // 创建FormData并上传截取的封面
  616. const formData = new FormData();
  617. formData.append('file', blob, 'auto_capture.jpg');
  618. // const config = {
  619. // headers: {
  620. // 'Content-Type': 'multipart/form-data'
  621. // },
  622. // timeout: 30000
  623. // };
  624. const res = await userApi.uploadImage(formData);
  625. if (res.data.code === 200) {
  626. this.editForm.video_img = res.data.filePath;
  627. this.$message.success('已自动截取视频帧作为封面');
  628. } else {
  629. throw new Error(res.message || '封面截取失败');
  630. }
  631. } catch (error) {
  632. console.error('截取视频封面失败:', error);
  633. this.$message.error('自动截取封面失败,请手动上传封面');
  634. return;
  635. }
  636. }
  637. try {
  638. const res = await userApi.editVideoList(this.editForm);
  639. if (res.data.code === 200) {
  640. this.$message.success('修改成功');
  641. this.editDialogVisible = false;
  642. this.fetchVideoList();
  643. this.is_image = '';
  644. }
  645. } catch (error) {
  646. this.$message.error('修改失败');
  647. }
  648. },
  649. // 删除
  650. async handleDelete(row) {
  651. try {
  652. await this.$confirm('确认删除该视频?', '提示', { type: 'warning' });
  653. const res = await userApi.deleteVideoList({ id: row.id });
  654. if (res.code === 200) {
  655. this.$message.success('删除成功');
  656. this.fetchVideoList();
  657. }
  658. } catch (error) {
  659. if (error !== 'cancel') {
  660. this.$message.error('删除失败');
  661. }
  662. }
  663. }
  664. }
  665. };
  666. </script>
  667. <style scoped>
  668. .video-container {
  669. padding: 20px;
  670. }
  671. .video-title {
  672. margin-bottom: 20px;
  673. font-size: 24px;
  674. font-weight: bold;
  675. }
  676. .video-operations {
  677. display: flex;
  678. justify-content: space-between;
  679. margin-bottom: 20px;
  680. }
  681. .search-input {
  682. width: 300px;
  683. }
  684. .pagination-container {
  685. margin-top: 20px;
  686. text-align: right;
  687. }
  688. .video-cover {
  689. max-width: 120px;
  690. max-height: 80px;
  691. }
  692. .cover-uploader {
  693. border: 1px dashed #d9d9d9;
  694. border-radius: 6px;
  695. cursor: pointer;
  696. position: relative;
  697. overflow: hidden;
  698. width: 178px;
  699. height: 100px;
  700. }
  701. .cover-uploader:hover {
  702. border-color: #409EFF;
  703. }
  704. .cover-uploader-icon {
  705. font-size: 28px;
  706. color: #8c939d;
  707. width: 178px;
  708. height: 100px;
  709. line-height: 100px;
  710. text-align: center;
  711. }
  712. .cover-image {
  713. width: 178px;
  714. height: 100px;
  715. display: block;
  716. }
  717. .video-uploader {
  718. width: 100%;
  719. }
  720. .el-upload__tip {
  721. font-size: 12px;
  722. color: #606266;
  723. margin-top: 7px;
  724. }
  725. </style>