Parcourir la source

1.用户头像更新接口
2.视频稿件封面和视频文件更新接口

reghao il y a 1 mois
Parent
commit
048540be92

+ 2 - 3
src/components/VideoPreviewPlayer.vue

@@ -27,7 +27,6 @@ export default {
   watch: {
     // 监控 videoProp 对象的变化
     videoProp(newVal) {
-      console.log('videoProp changed')
       if (!newVal.play) {
         this.dplayer.destroy()
       } else {
@@ -57,10 +56,10 @@ export default {
             this.message = 'url 类型无法识别'
           }
         } else {
-          console.error(res.msg)
+          this.$message.warning(res.msg)
         }
       }).catch(error => {
-        console.error(error.message)
+        this.$message.error(error.message)
       })
     },
     initMp4Player(coverUrl, videoUrls) {

+ 0 - 7
src/router/background_post.js

@@ -2,7 +2,6 @@ const Background = () => import('views/admin/Background')
 // ********************************************************************************************************************
 // 稿件后台管理
 const VideoPost = () => import('views/post/VideoPost')
-const VideoPostEdit = () => import('views/post/VideoPostEdit')
 const ImagePost = () => import('views/post/ImagePost')
 const ImagePostEdit = () => import('views/post/ImagePostEdit')
 const ArticlePost = () => import('views/post/ArticlePost')
@@ -25,12 +24,6 @@ export default {
       component: VideoPost,
       meta: { needAuth: true, roles: ['tnb_user'] }
     },
-    /* {
-      path: '/bg/post/video/edit/:videoId',
-      name: 'VideoPostEdit',
-      component: VideoPostEdit,
-      meta: { needAuth: true, roles: ['tnb_user'] }
-    },*/
     {
       path: '/bg/post/image',
       name: 'ImagePost',

+ 2 - 12
src/views/my/MyProfile.vue

@@ -269,21 +269,11 @@ export default {
             this.loginUser.avatarUrl = resp.data.avatarUrl
             updateAuthedUser(this.loginUser)
           } else {
-            this.$notify({
-              title: '头像更新失败',
-              message: resp.msg,
-              type: 'warning',
-              duration: 3000
-            })
+            this.$message.warning('头像更新失败')
           }
         })
       } else {
-        this.$notify({
-          title: '提示',
-          message: '头像上传失败,请重试!' + res.msg,
-          type: 'warning',
-          duration: 3000
-        })
+        this.$message.error('头像上传失败,请重试!' + res.msg)
       }
     },
     // ****************************************************************************************************************

+ 95 - 62
src/views/post/VideoPost.vue

@@ -3,7 +3,7 @@
     <el-header height="auto" class="post-header">
       <div class="header-wrapper">
         <h3 class="page-title">
-          <i class="el-icon-collection"></i> 稿件管理
+          <i class="el-icon-collection" /> 稿件管理
         </h3>
         <div class="filter-controls">
           <el-select
@@ -24,7 +24,7 @@
             icon="el-icon-plus"
             size="small"
             class="publish-btn"
-            @click="publishVideoDiaglog = true"
+            @click="handlePost"
           >发布稿件</el-button>
         </div>
       </div>
@@ -33,10 +33,10 @@
     <el-main class="post-main">
       <div class="table-card">
         <el-table
+          v-loading="loading"
           :data="dataList"
           style="width: 100%"
           header-cell-class-name="table-header-cell"
-          v-loading="loading"
         >
           <el-table-column label="No" type="index" width="60" align="center" />
 
@@ -49,7 +49,7 @@
                 :preview-src-list="[scope.row.coverUrl]"
               >
                 <div slot="error" class="image-slot">
-                  <i class="el-icon-picture-outline"></i>
+                  <i class="el-icon-picture-outline" />
                 </div>
               </el-image>
             </template>
@@ -63,7 +63,7 @@
                 </router-link>
                 <div class="video-meta">
                   <span class="vid-tag">ID: {{ scope.row.videoId }}</span>
-                  <span class="duration-tag"><i class="el-icon-time"></i> {{ scope.row.duration }}</span>
+                  <span class="duration-tag"><i class="el-icon-time" /> {{ scope.row.duration }}</span>
                 </div>
               </div>
             </template>
@@ -74,7 +74,7 @@
           <el-table-column label="可见范围" width="130" align="center">
             <template slot-scope="scope">
               <el-tooltip content="点击修改可见范围" placement="top">
-                <div @click="handleScope(scope.$index, scope.row)" class="scope-pointer">
+                <div class="scope-pointer" @click="handleScope(scope.$index, scope.row)">
                   <el-tag v-if="scope.row.scope === 1" size="medium" type="info" effect="plain">本人可见</el-tag>
                   <el-tag v-else-if="scope.row.scope === 2" size="medium" type="success" effect="plain">所有人可见</el-tag>
                   <el-tag v-else-if="scope.row.scope === 3" size="medium" type="warning" effect="plain">VIP 可见</el-tag>
@@ -94,7 +94,7 @@
           <el-table-column label="操作" width="220" fixed="right" align="center">
             <template slot-scope="scope">
               <el-button type="text" size="small" icon="el-icon-view" @click="handlePreview(scope.$index, scope.row)">预览</el-button>
-              <el-button type="text" size="small" icon="el-icon-edit" @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
+              <el-button type="text" size="small" icon="el-icon-edit" @click="handleEdit(scope.row)">编辑</el-button>
               <el-button type="text" size="small" icon="el-icon-delete" class="danger-text" @click="handleDelete(scope.$index, scope.row)">删除</el-button>
             </template>
           </el-table-column>
@@ -114,16 +114,28 @@
       </div>
     </el-main>
 
-    <el-dialog :visible.sync="publishVideoDiaglog" width="70%" custom-class="custom-glass-dialog" append-to-body destroy-on-close>
+    <el-dialog
+      :visible.sync="publishVideoDiaglog"
+      width="70%"
+      custom-class="custom-glass-dialog"
+      append-to-body
+      destroy-on-close
+      @close="editingVideo = null"
+    >
       <div slot="title" class="dialog-header-custom">
-        <i class="el-icon-circle-plus-outline"></i><span>发布新视频稿件</span>
+        <i :class="editingVideo ? 'el-icon-edit' : 'el-icon-circle-plus-outline'" />
+        <span>{{ editingVideo ? '编辑视频稿件' : '发布新视频稿件' }}</span>
       </div>
-      <video-post-publish @video-publish="onVideoPublish" />
+
+      <video-post-publish
+        :video-info="editingVideo"
+        @video-publish="onVideoPublish"
+      />
     </el-dialog>
 
     <el-dialog :visible.sync="showEditScopeDialog" width="400px" custom-class="custom-glass-dialog compact-dialog" append-to-body center>
       <div slot="title" class="dialog-header-custom">
-        <i class="el-icon-lock"></i><span>权限设置</span>
+        <i class="el-icon-lock" /><span>权限设置</span>
       </div>
       <div class="dialog-body-padding">
         <p class="input-label-tip">请选择该视频的可见范围:</p>
@@ -141,7 +153,7 @@
 
     <el-dialog :visible.sync="showPreviewDialog" width="900px" top="8vh" custom-class="custom-glass-dialog preview-dialog" append-to-body @close="handleDialogClose(()=> { showPreviewDialog = false })">
       <div slot="title" class="dialog-header-custom">
-        <i class="el-icon-video-play"></i><span>内容预览</span>
+        <i class="el-icon-video-play" /><span>内容预览</span>
       </div>
       <div class="preview-wrapper">
         <video-preview-player :video-prop.sync="videoProp" />
@@ -153,7 +165,7 @@
 <script>
 import VideoPreviewPlayer from 'components/VideoPreviewPlayer'
 import VideoPostPublish from '@/views/post/VideoPostPublish'
-import { updateVideoScope, videoInfo, deleteVideoPost, getVideoPosts, addVideoPost } from '@/api/video'
+import {updateVideoScope, videoInfo, deleteVideoPost, getVideoPosts, addVideoPost, updateVideoInfo} from '@/api/video'
 
 export default {
   name: 'VideoPost',
@@ -163,7 +175,7 @@ export default {
       loading: false,
       queryInfo: {
         scope: null, // 从 URL 获取
-        pn: 1        // 从 URL 获取
+        pn: 1 // 从 URL 获取
       },
       screenWidth: document.body.clientWidth,
       pageSize: 12,
@@ -173,13 +185,14 @@ export default {
       showEditScopeDialog: false,
       showPreviewDialog: false,
       form: { videoId: null, scope: 1 },
-      publishVideoDiaglog: false
+      publishVideoDiaglog: false,
+      editingVideo: null
     }
   },
   computed: {
     // 映射当前页码给 el-pagination 使用
     currentPage() {
-      return parseInt(this.queryInfo.pn) || 1;
+      return parseInt(this.queryInfo.pn) || 1
     }
   },
   watch: {
@@ -192,10 +205,10 @@ export default {
   methods: {
     // 1. 同步 URL 参数到 data,并执行请求
     syncParamsAndLoad() {
-      const { pn, scope } = this.$route.query;
-      this.queryInfo.pn = pn ? parseInt(pn) : 1;
-      this.queryInfo.scope = scope ? (isNaN(scope) ? scope : parseInt(scope)) : null;
-      this.getData();
+      const { pn, scope } = this.$route.query
+      this.queryInfo.pn = pn ? parseInt(pn) : 1
+      this.queryInfo.scope = scope ? (isNaN(scope) ? scope : parseInt(scope)) : null
+      this.getData()
     },
 
     // 2. 更新 URL 的通用方法
@@ -208,64 +221,69 @@ export default {
         }
       }).catch(err => {
         // 捕获冗余导航错误(点击同一页时)
-        if (err.name !== 'NavigationDuplicated') throw err;
-      });
+        if (err.name !== 'NavigationDuplicated') throw err
+      })
     },
 
     // 3. 交互触发:切换分页
     handlePageChange(pageNumber) {
-      this.queryInfo.pn = pageNumber;
-      this.updateRouter();
-      window.scrollTo({ top: 0, behavior: 'smooth' });
+      this.queryInfo.pn = pageNumber
+      this.updateRouter()
+      window.scrollTo({ top: 0, behavior: 'smooth' })
     },
 
     // 4. 交互触发:切换筛选
     handleFilterChange() {
-      this.queryInfo.pn = 1; // 筛选条件变了,重置回第一页
-      this.updateRouter();
+      this.queryInfo.pn = 1 // 筛选条件变了,重置回第一页
+      this.updateRouter()
     },
 
     getData() {
-      this.loading = true;
+      this.loading = true
       // 注意:这里建议给后端接口也加上 scope 参数,此处假设你原有接口支持
       getVideoPosts(this.queryInfo.pn, this.queryInfo.scope).then(resp => {
         if (resp.code === 0) {
-          this.dataList = resp.data.list;
-          this.totalSize = resp.data.totalSize;
+          this.dataList = resp.data.list
+          this.totalSize = resp.data.totalSize
         }
       }).finally(() => {
-        this.loading = false;
-      });
+        this.loading = false
+      })
     },
 
     /* --- 其余 UI 逻辑保持不变 --- */
     getStatusClass(status) {
-      const map = { 1: 'warning', 2: 'success', 3: 'danger', 4: 'info' };
-      return map[status] || 'info';
+      const map = { 1: 'warning', 2: 'success', 3: 'danger', 4: 'info' }
+      return map[status] || 'info'
     },
     getStatusText(status) {
-      const map = { 1: '审核中', 2: '已发布', 3: '审核失败', 4: '已下架' };
-      return map[status] || `未知(${status})`;
+      const map = { 1: '审核中', 2: '已发布', 3: '审核失败', 4: '已下架' }
+      return map[status] || `未知(${status})`
     },
     handleScope(index, row) {
-      this.form.videoId = row.videoId;
-      this.form.scope = '' + row.scope;
-      this.showEditScopeDialog = true;
+      this.form.videoId = row.videoId
+      this.form.scope = '' + row.scope
+      this.showEditScopeDialog = true
     },
     handleDialogClose(done) {
-      this.videoProp = { videoId: null, play: false };
-      if(typeof done === 'function') done();
+      this.videoProp = { videoId: null, play: false }
+      if (typeof done === 'function') done()
     },
     handlePreview(index, row) {
       videoInfo(row.videoId).then(res => {
         if (res.code === 0) {
-          this.showPreviewDialog = true;
-          this.videoProp = { videoId: res.data.videoId, play: true };
+          this.showPreviewDialog = true
+          this.videoProp = { videoId: res.data.videoId, play: true }
         }
-      });
+      })
     },
-    handleEdit(index, row) {
-      this.$router.push('/bg/post/video/edit/' + row.videoId);
+    handleEdit(row) {
+      this.editingVideo = row
+      this.publishVideoDiaglog = true
+    },
+    handlePost() {
+      this.editingVideo = null
+      this.publishVideoDiaglog = true
     },
     handleDelete(index, row) {
       this.$confirm(`确定要删除《${row.title}》?`, '警告', {
@@ -274,29 +292,44 @@ export default {
       }).then(() => {
         deleteVideoPost(row.videoId).then(res => {
           if (res.code === 0) {
-            this.$message.success('稿件已删除');
-            this.getData();
+            this.$message.info('稿件已删除')
+            this.getData()
           }
-        });
-      }).catch(() => {});
+        })
+      }).catch(() => {})
     },
     onUpdateScope() {
       updateVideoScope(this.form).then(res => {
         if (res.code === 0) {
-          this.showEditScopeDialog = false;
-          this.getData();
-          this.$message.success('可见范围已更新');
+          this.showEditScopeDialog = false
+          this.getData()
+          this.$message.info('可见范围已更新')
         }
-      });
+      })
     },
-    onVideoPublish(videoForm) {
-      addVideoPost(videoForm).then(res => {
-        if (res.code === 0) {
-          this.publishVideoDiaglog = false;
-          this.$message.success('投稿成功,请等待审核');
-          this.getData();
-        }
-      });
+    onVideoPublish({ data, mode }) {
+      const videoForm = data
+      if (mode === 'edit') {
+        updateVideoInfo(videoForm).then(res => {
+          this.$message.info(res.msg)
+        }).catch(error => {
+          this.$message.error(error.message)
+        })
+      } else {
+        addVideoPost(videoForm).then(res => {
+          if (res.code === 0) {
+            this.publishVideoDiaglog = false
+            this.$message.info('投稿成功,请等待审核')
+            this.getData()
+          }
+        })
+      }
+      this.finishPublish()
+    },
+    finishPublish() {
+      this.publishVideoDiaglog = false
+      this.editingVideo = null
+      this.getData()
     }
   }
 }

+ 0 - 445
src/views/post/VideoPostEdit.vue

@@ -1,445 +0,0 @@
-<template>
-  <el-row class="movie-list">
-    <el-row style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px">
-      <el-col :md="24" style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px">
-        <el-card class="box-card">
-          <div slot="header" class="clearfix">
-            <span>编辑视频稿件</span>
-            <el-button style="float: right; padding: 10px" type="text" @click="onReturnVideo">返回视频稿件列表</el-button>
-          </div>
-        </el-card>
-      </el-col>
-    </el-row>
-    <el-row style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px">
-      <el-col :md="12" style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px">
-        <el-row style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px">
-          <el-card class="box-card">
-            <div slot="header" class="clearfix">
-              <span>更新视频封面</span>
-              <el-button style="float: right; padding: 3px 0" type="text" @click="onUpdateVideoCover">更新</el-button>
-            </div>
-            <div class="text item">
-              <el-tooltip class="item" effect="dark" content="点击上传图片" placement="top-end">
-                <el-upload
-                  class="avatar-uploader"
-                  :action="imgOssUrl"
-                  :headers="imgHeaders"
-                  :data="imgData"
-                  :with-credentials="true"
-                  :show-file-list="false"
-                  :before-upload="beforeAvatarUpload"
-                  :on-success="handleAvatarSuccess"
-                  :on-change="handleOnChange"
-                >
-                  <img :src="coverUrl" class="avatar">
-                </el-upload>
-              </el-tooltip>
-            </div>
-          </el-card>
-        </el-row>
-        <el-row style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px">
-          <el-card class="box-card">
-            <div slot="header" class="clearfix">
-              <span>更新视频文件</span>
-              <el-button style="float: right; padding: 3px 0" type="text" @click="onUpdateVideoFile">更新</el-button>
-            </div>
-            <div class="text item">
-              <uploader
-                class="uploader-example"
-                :options="options"
-                :auto-start="true"
-                @file-added="onFileAdded"
-                @file-success="onFileSuccess"
-                @file-progress="onFileProgress"
-                @file-error="onFileError"
-              >
-                <uploader-unsupport />
-                <uploader-drop>
-                  <p>拖动视频文件到此处或</p>
-                  <uploader-btn :attrs="attrs">选择视频文件</uploader-btn>
-                </uploader-drop>
-                <uploader-list />
-              </uploader>
-            </div>
-          </el-card>
-        </el-row>
-      </el-col>
-      <el-col :md="12" style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px">
-        <el-card class="box-card">
-          <div slot="header" class="clearfix">
-            <span>更新视频信息</span>
-            <el-button style="float: right; padding: 3px 0" type="text" @click="onUpdateVideoInfo">更新</el-button>
-          </div>
-          <div class="text item">
-            <el-form ref="form" :model="videoInfoForm" label-width="80px">
-              <el-form-item label="标题">
-                <el-input v-model="videoInfoForm.title" style="padding-right: 1px" placeholder="标题不能超过 50 个字符" />
-              </el-form-item>
-              <el-form-item label="描述">
-                <el-input v-model="videoInfoForm.description" type="textarea" autosize style="padding-right: 1px;" />
-              </el-form-item>
-              <el-form-item label="发布时间">
-                <el-date-picker
-                  v-model="videoInfoForm.pubDate"
-                  type="datetime"
-                  placeholder="选择发布的时间"
-                />
-              </el-form-item>
-            </el-form>
-          </div>
-        </el-card>
-      </el-col>
-    </el-row>
-  </el-row>
-</template>
-
-<script>
-import { getVideoPost, updateVideoInfo, updateVideoCover, updateVideoFile } from '@/api/video'
-import { getVideoChannelInfo, getVideoCoverChannelInfo } from '@/api/file'
-
-export default {
-  name: 'VideoPostEdit',
-  data() {
-    return {
-      // ****************************************************************************************************************
-      options: null,
-      attrs: {
-        accept: 'video/*'
-      },
-      imgOssUrl: null,
-      imgHeaders: {
-        Authorization: ''
-      },
-      imgData: {
-        channelId: process.env.VUE_APP_UPLOAD_COVER_CHANNEL
-      },
-      // ****************************************************************************************************************
-      coverUrl: null,
-      coverUrl1: null,
-      coverFileId: null,
-      videoFileId: null,
-      // 提交给后端的数据
-      videoInfoForm: {
-        videoId: null,
-        title: null,
-        description: null,
-        pubDate: null
-      },
-      ossToken: null
-    }
-  },
-  created() {
-    document.title = '编辑视频稿件'
-
-    const videoId = this.$route.params.videoId
-    getVideoPost(videoId).then(res => {
-      if (res.code === 0) {
-        const userVideoPost = res.data
-        this.coverUrl = userVideoPost.coverUrl
-        this.videoInfoForm.videoId = userVideoPost.videoId
-        this.videoInfoForm.title = userVideoPost.title
-        this.videoInfoForm.description = userVideoPost.description
-        this.videoInfoForm.pubDate = userVideoPost.pubDate
-      } else {
-        this.$notify({
-          title: '提示',
-          message: '获取 OSS 服务器地址失败, 暂时无法上传文件',
-          type: 'error',
-          duration: 3000
-        })
-      }
-    }).catch(error => {
-      this.$notify({
-        title: '提示',
-        message: error.message,
-        type: 'warning',
-        duration: 3000
-      })
-    })
-
-    getVideoChannelInfo().then(res => {
-      if (res.code === 0) {
-        const resData = res.data
-        this.options = {
-          target: resData.ossUrl,
-          chunkSize: resData.maxSize,
-          fileParameterName: 'file',
-          testChunks: false,
-          query: (file, chunk) => {
-            return {
-              channelId: resData.channelCode
-            }
-          },
-          headers: {
-            Authorization: 'Bearer ' + resData.token
-          },
-          withCredentials: false
-        }
-      } else {
-        this.$notify({
-          title: '提示',
-          message: '获取 OSS 服务器地址失败, 暂时无法上传文件',
-          type: 'error',
-          duration: 3000
-        })
-      }
-    }).catch(error => {
-      this.$notify({
-        title: '提示',
-        message: '视频上传配置失败 ' + error.message,
-        type: 'warning',
-        duration: 3000
-      })
-    })
-
-    getVideoCoverChannelInfo().then(res => {
-      if (res.code === 0) {
-        const resData = res.data
-        this.imgOssUrl = resData.ossUrl
-        this.imgHeaders.Authorization = 'Bearer ' + resData.token
-      } else {
-        this.$notify({
-          title: '提示',
-          message: '获取 OSS 服务器地址失败, 暂时无法上传文件',
-          type: 'error',
-          duration: 3000
-        })
-      }
-    }).catch(error => {
-      this.$notify({
-        title: '提示',
-        message: '图片上传配置失败 ' + error.message,
-        type: 'warning',
-        duration: 3000
-      })
-    })
-  },
-  methods: {
-    // ****************************************************************************************************************
-    onFileAdded(file) {
-      if (file.file.size > 1024 * 1024 * 1024 * 10) {
-        file.cancel()
-        this.$notify(
-          {
-            title: '提示',
-            message: '视频文件应小于 10GB',
-            type: 'warning',
-            duration: 3000
-          }
-        )
-        return
-      }
-    },
-    onFileProgress(rootFile, file, chunk) {
-    },
-    onFileSuccess(rootFile, file, response, chunk) {
-      const res = JSON.parse(response)
-      if (res.code === 0) {
-        const resData = res.data
-        this.videoFileId = resData.uploadId
-
-        this.$notify(
-          {
-            title: '提示',
-            message: '视频已上传',
-            type: 'warning',
-            duration: 3000
-          }
-        )
-      }
-    },
-    onFileError(rootFile, file, response, chunk) {
-      this.$notify({
-        title: '提示',
-        message: '文件上传错误',
-        type: 'warning',
-        duration: 3000
-      })
-    },
-    // ****************************************************************************************************************
-    beforeAvatarUpload(file) {
-      const isJPG = file.type === 'image/jpeg'
-      const isLt2M = file.size / 1024 / 1024 < 2
-      if (!isJPG) {
-        this.$message.error('上传头像图片只能是 JPG 格式!')
-      }
-      if (!isLt2M) {
-        this.$message.error('上传头像图片大小不能超过 2MB!')
-      }
-      return isJPG && isLt2M
-    },
-    handleAvatarSuccess(res, file) {
-      const localImageUrl = URL.createObjectURL(file.raw)
-      if (res.code === 0) {
-        const resData = res.data
-        this.coverFileId = resData.uploadId
-        this.coverUrl = localImageUrl
-        this.coverUrl1 = resData.url
-      } else {
-        this.$notify({
-          title: '提示',
-          message: '视频封面上传失败,请重试!' + res.msg,
-          type: 'warning',
-          duration: 3000
-        })
-      }
-    },
-    handleOnChange(file, fileList) {
-    },
-    // ****************************************************************************************************************
-    onReturnVideo() {
-      this.$router.push('/bg/post/video')
-    },
-    onUpdateVideoInfo() {
-      updateVideoInfo(this.videoInfoForm).then(res => {
-        if (res.code === 0) {
-          this.$notify({
-            title: '提示',
-            message: '视频信息已更新',
-            type: 'warning',
-            duration: 3000
-          })
-        }
-      }).catch(error => {
-        this.$notify({
-          title: '提示',
-          message: error.message,
-          type: 'warning',
-          duration: 3000
-        })
-      })
-    },
-    onUpdateVideoCover() {
-      if (this.coverUrl1 === null) {
-        this.$notify({
-          title: '提示',
-          message: '你还没有上传视频封面',
-          type: 'warning',
-          duration: 3000
-        })
-        return
-      }
-
-      const videoCover = {
-        videoId: this.videoInfoForm.videoId,
-        coverUrl: this.coverUrl1,
-        coverFileId: this.coverFileId
-      }
-
-      updateVideoCover(videoCover).then(res => {
-        if (res.code === 0) {
-          this.$notify({
-            title: '提示',
-            message: '视频封面已更新',
-            type: 'warning',
-            duration: 3000
-          })
-        }
-      }).catch(error => {
-        this.$notify({
-          title: '提示',
-          message: error.message,
-          type: 'warning',
-          duration: 3000
-        })
-      })
-    },
-    onUpdateVideoFile() {
-      if (this.videoFileId === null) {
-        this.$notify({
-          title: '提示',
-          message: '你还没有上传视频文件',
-          type: 'warning',
-          duration: 3000
-        })
-        return
-      }
-
-      const videoFile = {
-        videoId: this.videoInfoForm.videoId,
-        videoFileId: this.videoFileId
-      }
-
-      updateVideoFile(videoFile).then(res => {
-        if (res.code === 0) {
-          this.$notify({
-            title: '提示',
-            message: '视频文件已更新',
-            type: 'warning',
-            duration: 3000
-          })
-        }
-      }).catch(error => {
-        this.$notify({
-          title: '提示',
-          message: error.message,
-          type: 'warning',
-          duration: 3000
-        })
-      })
-    }
-  }
-}
-</script>
-
-<style>
-/*处于手机屏幕时*/
-@media screen and (max-width: 768px){
-  .movie-list {
-    padding-top: 8px;
-    padding-left: 0.5%;
-    padding-right: 0.5%;
-  }
-
-  .coverImg {
-    height: 120px !important;
-  }
-}
-
-.movie-list {
-  padding-top: 15px;
-  padding-left: 6%;
-  padding-right: 6%;
-}
-
-.uploader-example {
-  width: 500px;
-  padding: 15px;
-  margin: 40px auto 0;
-  font-size: 12px;
-  box-shadow: 0 0 10px rgba(0, 0, 0, .4);
-}
-.uploader-example .uploader-btn {
-  margin-right: 4px;
-}
-.uploader-example .uploader-list {
-  max-height: 440px;
-  overflow: auto;
-  overflow-x: hidden;
-  overflow-y: auto;
-}
-
-.avatar-uploader .el-upload {
-  border: 1px dashed #d9d9d9;
-  border-radius: 6px;
-  cursor: pointer;
-  position: relative;
-  overflow: hidden;
-}
-.avatar-uploader .el-upload:hover {
-  border-color: #409EFF;
-}
-.avatar-uploader-icon {
-  font-size: 28px;
-  color: #8c939d;
-  width: 320px;
-  height: 240px;
-  line-height: 178px;
-  text-align: center;
-}
-.avatar {
-  width: 320px;
-  height: 240px;
-  display: block;
-}
-</style>

+ 108 - 32
src/views/post/VideoPostPublish.vue

@@ -77,7 +77,7 @@
               />
             </el-form-item>
 
-            <el-form-item label="选择分区" prop="categoryId">
+            <el-form-item v-if="!isEdit" label="选择分区" prop="categoryId">
               <el-row :gutter="10">
                 <el-col :span="12">
                   <el-select v-model="form.categoryPid" placeholder="一级分区" @change="handlePCategoryChange">
@@ -102,7 +102,7 @@
               </el-row>
             </el-form-item>
 
-            <el-form-item label="标签" prop="tags">
+            <el-form-item v-if="!isEdit" label="标签" prop="tags">
               <el-select
                 v-model="form.tags"
                 multiple
@@ -121,7 +121,7 @@
               </el-select>
             </el-form-item>
 
-            <el-form-item label="权限设置">
+            <el-form-item v-if="!isEdit" label="权限设置">
               <el-radio-group v-model="form.scope">
                 <el-radio label="1">私有</el-radio>
                 <el-radio label="2">公开</el-radio>
@@ -137,12 +137,19 @@
 
 <script>
 import UploaderCard from 'components/card/UploaderCard.vue'
-import { addVideoFile, videoRegion } from '@/api/video'
+import {addVideoFile, updateVideoCover, updateVideoFile, videoRegion} from '@/api/video'
 import { getVideoChannelInfo, getVideoCoverChannelInfo } from '@/api/file'
 
 export default {
   name: 'VideoPostPublish',
   components: { UploaderCard },
+  props: {
+    // 接收视频信息,null 为发布,非 null 为编辑
+    videoInfo: {
+      type: Object,
+      default: null
+    }
+  },
   data() {
     return {
       uploadConfig: null,
@@ -179,11 +186,52 @@ export default {
       }
     }
   },
+  computed: {
+    isEdit() {
+      return this.videoInfo !== null
+    }
+  },
+  watch: {
+    // 监听 props 变化,防止父组件异步获取数据后子组件不更新
+    videoInfo: {
+      handler(val) {
+        if (val) {
+          this.initEditData(val)
+        }
+      },
+      immediate: true
+    },
+    // 监听原始分区数据加载完毕后,如果处于编辑模式,初始化二级分区列表
+    categoryRawData(val) {
+      if (val && val.length > 0 && this.form.categoryPid) {
+        this.handlePCategoryChange(this.form.categoryPid, true)
+      }
+    }
+  },
   created() {
     this.initCategoryData()
     this.initCoverUploadConfig()
   },
   methods: {
+    // 初始化编辑数据
+    initEditData(info) {
+      this.form = {
+        videoId: info.videoId,
+        coverFileId: info.coverFileId || null,
+        title: info.title || '',
+        description: info.description || '',
+        categoryPid: info.categoryPid || null,
+        categoryId: info.categoryId || null,
+        tags: Array.isArray(info.tags) ? info.tags : (info.tags ? info.tags.split(',') : []),
+        scope: String(info.scope || '2')
+      }
+      this.coverUrl = info.coverUrl || null
+
+      // 如果数据已准备好,立即触发二级分区列表
+      if (this.categoryRawData.length > 0) {
+        this.handlePCategoryChange(this.form.categoryPid, true)
+      }
+    },
     // 1. 初始化数据
     async initCategoryData() {
       const res = await videoRegion()
@@ -197,7 +245,7 @@ export default {
       getVideoChannelInfo().then(resp => {
         if (resp.code === 0) {
           this.uploadConfig = resp.data
-          this.uploadConfig.title = '上传视频文件'
+          this.uploadConfig.title = this.isEdit ? '重新上传视频文件' : '上传视频文件'
           this.uploadConfig.fileAttrs = { accept: 'video/*' }
         }
       })
@@ -212,28 +260,41 @@ export default {
 
     // 2. 视频上传组件回调
     handleVideoAdded(filename) {
-      if (!this.form.title) {
+      // 仅在发布模式下,上传视频后自动填充标题
+      if (!this.isEdit && !this.form.title) {
         this.form.title = filename.replace(/\.[^/.]+$/, '').substring(0, 50)
       }
     },
 
     handleVideoSuccess(uploadResult) {
-      const { uploadId, file, channelCode } = uploadResult
-      this.generateLocalCover(file.file)
-
-      // 调用业务接口关联视频
-      addVideoFile({
-        videoFileId: uploadId,
-        channelCode: channelCode,
-        filename: file.name
-      }).then(resp => {
-        if (resp.code === 0) {
-          this.form.videoId = resp.data
-          this.$notify.success({ title: '成功', message: '视频处理完毕' })
-        } else {
-          this.$message.warning(resp.msg)
+      if (this.isEdit) {
+        const videoFile = {
+          videoId: this.videoInfo.videoId,
+          videoFileId: uploadResult.uploadId
         }
-      })
+        updateVideoFile(videoFile).then(res => {
+          this.$message.info(res.msg)
+        }).catch(error => {
+          this.$message.error(error.message)
+        })
+      } else {
+        const { uploadId, file, channelCode } = uploadResult
+        // 自动截取视频封面
+        this.generateLocalCover(file.file)
+
+        addVideoFile({
+          videoFileId: uploadId,
+          channelCode: channelCode,
+          filename: file.name
+        }).then(resp => {
+          if (resp.code === 0) {
+            this.form.videoId = resp.data
+            this.$message.info('视频处理完成')
+          } else {
+            this.$message.warning(resp.msg)
+          }
+        })
+      }
     },
     generateLocalCover(rawFile) {
       const video = document.createElement('video')
@@ -290,15 +351,30 @@ export default {
 
     handleManualCoverSuccess(res, file) {
       if (res.code === 0) {
-        this.form.coverFileId = res.data.uploadId
-        this.coverUrl = URL.createObjectURL(file.raw)
-        this.$message.success('封面上传成功')
+        if (this.isEdit) {
+          const videoCover = {
+            videoId: this.videoInfo.videoId,
+            coverUrl: res.data.url,
+            coverFileId: res.data.uploadId
+          }
+          updateVideoCover(videoCover).then(res => {
+            this.$message.info(res.msg)
+          }).catch(error => {
+            this.$message.error(error.message)
+          })
+        } else {
+          this.form.coverFileId = res.data.uploadId
+          this.coverUrl = URL.createObjectURL(file.raw)
+          this.$message.success('封面上传成功')
+        }
       }
     },
 
     // 4. 分区联动
-    handlePCategoryChange(pid) {
-      this.form.categoryId = null // 重置子分区
+    handlePCategoryChange(pid, isInit = false) {
+      if (!isInit) {
+        this.form.categoryId = null
+      }
       const parent = this.categoryRawData.find(i => i.value === pid)
       this.subCategoryList = parent ? parent.children : []
     },
@@ -310,14 +386,14 @@ export default {
         if (!this.form.videoId) {
           return this.$message.warning('请等待视频上传完成')
         }
-        if (!this.form.coverFileId) {
-          return this.$message.warning('请上传视频封面')
-        }
 
         this.submitting = true
-        // 触发父组件或发送 API
-        this.$emit('video-publish', this.form)
-        // 模拟提交完成
+        // 向上抛出事件,带上模式标识
+        this.$emit('video-publish', {
+          data: this.form,
+          mode: this.isEdit ? 'edit' : 'publish'
+        })
+
         setTimeout(() => { this.submitting = false }, 1000)
       })
     }