Browse Source

清理无用代码, 简化项目中...

reghao 4 tuần trước cách đây
mục cha
commit
0fc0cfef34

+ 0 - 25
src/App.vue

@@ -1,16 +1,8 @@
 <template>
   <div id="app">
-    <!--导航栏-->
-    <!--    <nav-bar v-if="type===1" />-->
-    <!--下面的部分通过路由动态决定渲染与否-->
-    <!--exclude,其值为正则,匹配到的组件的名称会被排除在keep-alive之外-->
     <keep-alive exclude="Collection,History">
       <router-view />
     </keep-alive>
-    <!--页脚-->
-    <!--    <footer-bar />-->
-    <!--回到顶部按钮-->
-    <!--    <el-backtop :visibility-height="100" :bottom="60" />-->
   </div>
 </template>
 
@@ -25,15 +17,9 @@ export default {
     }
   },
   created() {
-    /**
-     * 防止vuex中的state在界面刷新后丢失
-     */
-    // 在页面加载时读取sessionStorage里的状态信息
     if (sessionStorage.getItem('store')) {
       this.$store.replaceState(Object.assign({}, this.$store.state, JSON.parse(sessionStorage.getItem('store'))))
     }
-
-    // 在页面刷新时将vuex里的信息保存到sessionStorage里
     window.addEventListener('beforeunload', () => {
       sessionStorage.setItem('store', JSON.stringify(this.$store.state))
     })
@@ -50,19 +36,8 @@ export default {
 #app {
   overflow-y: scroll;
 }
-.searchd {
-  margin-top: 15px;
-  text-align: center;
-}
 html,body,#app{
   height: 100%;
   width: 100%;
 }
-/*.fade-enter-active,.fade-leave-active {
-  -webkit-transition:opacity 1s;
-  transition:opacity 1s
-}
-.fade-enter,.fade-leave-to {
-  opacity:0
-}*/
 </style>

+ 2 - 2
src/api/admin.js

@@ -50,8 +50,8 @@ export function getUserAvatarList() {
   return get(adminApi.getUserAvatarsApi)
 }
 
-export function getVideoList(nextId) {
-  return get(adminApi.getVideosApi + '?nextId=' + nextId)
+export function getVideoList(queryParams) {
+  return get(adminApi.getVideosApi, queryParams)
 }
 
 export function getAlbumList(nextId) {

+ 1 - 1
src/api/user.js

@@ -57,7 +57,7 @@ export function searchUser(queryForm) {
 }
 
 export function getUnreadCount() {
-  return get(userApi.unreadMessageApi + '/unread')
+  return get(userApi.userMessageApi + '/unread')
 }
 
 export function getUnreadMessages(queryParams) {

+ 11 - 35
src/api/video.js → src/api/vod.js

@@ -2,16 +2,11 @@ import { get, post, delete0 } from '@/utils/request'
 
 const videoApi = {
   videoPostApi: '/api/content/post/video',
-  videoPostApi1: '/api/content/post/video/publish/post',
   videoFileApi: '/api/content/post/video/publish/file',
   updateVideoScopeApi: '/api/content/post/video/update/scope',
-  updateVideoStatusApi: '/api/content/post/video/update/status',
   updateVideoInfoApi: '/api/content/post/video/update/info',
   updateVideoCoverApi: '/api/content/post/video/update/cover',
   updateVideoFileApi: '/api/content/post/video/update/file',
-  videoResourceApi: '/api/content/post/video/resource',
-  convertVideoApi: '/api/content/post/video/convert',
-
   videoCategoryApi: '/api/content/video/categories',
   categoryVideoApi: '/api/content/video/category',
   categoryShortVideoApi: '/api/content/video/short',
@@ -20,16 +15,14 @@ const videoApi = {
   videoInfoApi: '/api/content/video/detail',
   videoUrlApi: '/api/content/video/url',
   videoApi: '/api/content/video',
-
   videoDownloadApi: '/api/content/video/download',
-  cacheBiliApi: '/api/content/video/cache/bili',
   shortUrlApi: '/api/content/video/share',
-
   videoRecommendApi: '/api/content/video/recommend',
   similarVideoApi: '/api/content/video/similar',
   bannerVideoApi: '/api/content/video/hot',
   userContentDataApi: '/api/content/userdata',
-  videoTimelineApi: '/api/content/video/timeline'
+  videoTimelineApi: '/api/content/video/timeline',
+  vodAdminApi: '/api/admin/content/vod'
 }
 
 // *********************************************************************************************************************
@@ -40,7 +33,7 @@ export function addVideoFile(payload) {
 
 // 添加视频贴
 export function addVideoPost(video) {
-  return post(videoApi.videoPostApi1, video)
+  return post(videoApi.videoPostApi + '/publish/post', video)
 }
 
 // 更新视频可见范围
@@ -48,11 +41,6 @@ export function updateVideoScope(data) {
   return post(videoApi.updateVideoScopeApi, data)
 }
 
-// 更新视频状态
-export function updateVideoStatus(data) {
-  return post(videoApi.updateVideoStatusApi, data)
-}
-
 // 更新视频信息
 export function updateVideoInfo(data) {
   return post(videoApi.updateVideoInfoApi, data)
@@ -78,21 +66,6 @@ export function getVideoPosts(page) {
   return get(videoApi.videoPostApi + '?page=' + page)
 }
 
-// 获取视频贴详情
-export function getVideoPost(videoId) {
-  return get(videoApi.videoPostApi + '/' + videoId)
-}
-
-// 获取视频资源
-export function getVideoResource(videoId) {
-  return get(videoApi.videoResourceApi + '/' + videoId)
-}
-
-// 视频转码
-export function convertVideo(videoId) {
-  return post(videoApi.convertVideoApi + '/' + videoId)
-}
-
 // *********************************************************************************************************************
 // 获取视频分类
 export function videoRegion() {
@@ -138,11 +111,6 @@ export function downloadVideo(videoId) {
   return get(videoApi.videoDownloadApi + '/' + videoId)
 }
 
-// 缓存 bili 视频
-export function cacheBiliVideo(bvId) {
-  return post(videoApi.cacheBiliApi + '/' + bvId)
-}
-
 // 获取分享视频的短链接
 export function getShortUrl(videoId) {
   return post(videoApi.shortUrlApi + '/' + videoId)
@@ -178,3 +146,11 @@ export function videoTimeline(nextId) {
 export function videoErrorReport(data) {
   return post(videoApi.videoApi + '/error_report', data)
 }
+
+export function getAuditVideo(queryParams) {
+  return get(videoApi.vodAdminApi + '/audit', queryParams)
+}
+
+export function submitAuditVideo(payload) {
+  return post(videoApi.vodAdminApi + '/audit', payload)
+}

+ 1 - 1
src/components/VideoPlayer.vue

@@ -3,7 +3,7 @@
 </template>
 
 <script>
-import { videoUrl } from '@/api/video'
+import { videoUrl } from '@/api/vod'
 import SocketInstance from '@/utils/ws/socket-instance'
 
 import flvjs from 'flv.js'

+ 1 - 1
src/components/VideoPreviewPlayer.vue

@@ -3,7 +3,7 @@
 </template>
 
 <script>
-import { videoUrl } from '@/api/video'
+import { videoUrl } from '@/api/vod'
 
 import flvjs from 'flv.js'
 import DPlayer from 'dplayer'

+ 1 - 1
src/components/card/VideoPlayerCard.vue

@@ -7,7 +7,7 @@
 <script>
 import flvjs from 'flv.js'
 import DPlayer from 'dplayer'
-import { videoUrl } from '@/api/video'
+import { videoUrl } from '@/api/vod'
 
 export default {
   name: 'VideoPlayerCard',

+ 5 - 5
src/router/ai.js

@@ -15,35 +15,35 @@ export default {
       path: '',
       name: 'ai',
       component: GpuDashboard,
-      meta: { title: 'GPU 信息', needAuth: true}
+      meta: { title: 'GPU 信息', needAuth: true }
     },
     {
       path: '/ai/audio',
       hidden: true,
       name: 'AudioAudit',
       component: AudioAudit,
-      meta: { title: '音频识别', needAuth: true}
+      meta: { title: '音频识别', needAuth: true }
     },
     {
       path: '/ai/image',
       hidden: true,
       name: 'ImageAudit',
       component: ImageAudit,
-      meta: { title: '图像理解', needAuth: true}
+      meta: { title: '图像理解', needAuth: true }
     },
     {
       path: '/ai/video',
       hidden: true,
       name: 'VideoAudit',
       component: VideoAudit,
-      meta: { title: '视频审核', needAuth: true}
+      meta: { title: '视频审核', needAuth: true }
     },
     {
       path: '/ai/text',
       hidden: true,
       name: 'TextAudit',
       component: TextAudit,
-      meta: { title: '文本分析', needAuth: true}
+      meta: { title: '文本分析', needAuth: true }
     }
   ]
 }

+ 8 - 0
src/router/index.js

@@ -35,6 +35,8 @@ const ShortVideoIndex = () => import('views/home/ShortVideo')
 const AMap = () => import('views/map/AMap')
 const OpenLayersMap = () => import('views/map/OpenLayersMap')
 
+const VodAudit = () => import('views/admin/aaa/VideoAudit')
+
 const Index = () => import('views/Index')
 const Login = () => import('views/Login')
 const Register = () => import('views/Register')
@@ -132,6 +134,12 @@ export const constantRoutes = [
       }
     ]
   },
+  {
+    path: '/vod_audit/:id',
+    name: 'VodAudit',
+    component: VodAudit,
+    meta: { needAuth: true }
+  },
   {
     path: '/short',
     name: 'ShortVideoIndex',

+ 412 - 225
src/views/admin/aaa/AdminVideoList.vue

@@ -1,348 +1,535 @@
 <template>
-  <el-container>
-    <el-header height="220">
-      <el-row style="margin-top: 10px">
-        <el-select
-          v-model="queryInfo.scope"
-          clearable
-          placeholder="查询条件"
-          style="margin-left: 5px"
-          @change="onSelectChange"
-        />
-        <el-button type="plain" icon="el-icon-refresh" style="margin-left: 5px" @click="onRefresh">刷新</el-button>
-      </el-row>
+  <el-container class="admin-video-container">
+    <el-header height="auto" class="search-header">
+      <el-form :inline="true" :model="queryParams" size="small" class="search-form" @submit.native.prevent>
+        <el-form-item label="视频标题">
+          <el-input
+            v-model="queryParams.title"
+            clearable
+            placeholder="请输入视频标题模糊搜索"
+            prefix-icon="el-icon-video-camera"
+            class="search-input-long"
+            @keyup.enter.native="handleSearch"
+          />
+        </el-form-item>
+
+        <el-form-item label="审核状态">
+          <el-select
+            v-model="queryParams.status"
+            clearable
+            placeholder="全部状态"
+            class="select-input"
+            @change="handleSearch"
+          >
+            <el-option label="审核中" :value="1" />
+            <el-option label="审核通过" :value="2" />
+            <el-option label="审核未通过" :value="3" />
+            <el-option label="已下架" :value="4" />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="可见范围">
+          <el-select
+            v-model="queryParams.scope"
+            clearable
+            placeholder="全部范围"
+            class="select-input"
+            @change="handleSearch"
+          >
+            <el-option label="本人可见" :value="1" />
+            <el-option label="所有人可见" :value="2" />
+            <el-option label="VIP 可见" :value="3" />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item class="search-actions">
+          <el-button type="primary" icon="el-icon-search" @click="handleSearch">查询</el-button>
+          <el-button icon="el-icon-refresh" class="btn-reset" @click="onRefresh">重置</el-button>
+        </el-form-item>
+      </el-form>
     </el-header>
-    <el-main>
+
+    <el-main class="table-main">
       <el-table
+        v-loading="loading"
         :data="dataList"
         border
-        height="480"
+        stripe
+        height="500"
         style="width: 100%"
+        class="custom-table"
       >
         <el-table-column
           fixed="left"
           label="No"
           type="index"
+          width="60"
+          align="center"
         />
         <el-table-column
           prop="pubDate"
           label="发布时间"
-          width="150"
+          width="160"
+          align="center"
         />
         <el-table-column
           prop="coverUrl"
-          label="封面"
-          width="90"
+          label="视频封面"
+          width="110"
+          align="center"
         >
           <template slot-scope="scope">
-            <el-image :src="scope.row.coverUrl" min-width="30" height="20" />
+            <el-image
+              :src="scope.row.coverUrl"
+              fit="cover"
+              class="table-cover"
+              :preview-src-list="[scope.row.coverUrl]"
+            >
+              <div slot="error" class="image-error-slot">
+                <i class="el-icon-picture-outline" />
+              </div>
+            </el-image>
           </template>
         </el-table-column>
+
         <el-table-column
           prop="videoId"
           label="视频 ID"
           width="120"
+          align="center"
         >
           <template slot-scope="scope">
-            <router-link target="_blank" :to="`/video/${scope.row.videoId}`">
+            <router-link :to="`/video/${scope.row.videoId}`" target="_blank" class="video-id-link">
               <span>{{ scope.row.videoId }}</span>
+              <i class="el-icon-link" />
             </router-link>
           </template>
         </el-table-column>
+
         <el-table-column
           prop="title"
-          label="标题"
-          :show-overflow-tooltip="true"
+          label="视频标题"
+          min-width="160"
         >
           <template slot-scope="scope">
-            <el-tooltip
-              v-if="scope.row.title"
-              :content="scope.row.title"
-              raw-content
-              placement="top-start"
-            >
-              <span v-if="scope.row.title.length <= 15">
-                {{ scope.row.title }}
-              </span>
-              <span v-else>
-                {{ scope.row.title.substr(0, 15) + "..." }}
-              </span>
+            <el-tooltip v-if="scope.row.title" :content="scope.row.title" placement="top" :open-delay="400">
+              <span class="ellipsis-text text-main-title">{{ scope.row.title }}</span>
             </el-tooltip>
+            <span v-else class="text-muted">-</span>
           </template>
         </el-table-column>
+
         <el-table-column
-          prop="description"
-          label="描述"
-          :show-overflow-tooltip="true"
+          prop="duration"
+          label="时长"
+          width="90"
+          align="center"
         >
           <template slot-scope="scope">
-            <el-tooltip
-              v-if="scope.row.description"
-              :content="scope.row.description"
-              raw-content
-              placement="top-start"
-            >
-              <span v-if="scope.row.description && scope.row.description.length <= 15">
-                {{ scope.row.description }}
-              </span>
-              <span v-if="scope.row.description && scope.row.description.length > 15">
-                {{ scope.row.description.substr(0, 15) + "..." }}
-              </span>
-            </el-tooltip>
-            <span v-else-if="scope.row.description === null">-</span>
+            <span class="duration-badge">{{ scope.row.duration || '00:00' }}</span>
           </template>
         </el-table-column>
-        <el-table-column
-          prop="duration"
-          label="时长"
-        />
+
         <el-table-column
           prop="horizontal"
-          label="方向"
+          label="画幅方向"
+          width="95"
+          align="center"
         >
           <template slot-scope="scope">
-            <el-tag v-if="scope.row.horizontal" :type="'warning'" disable-transitions>
-              <span icon="el-icon-monitor">横屏</span>
+            <el-tag v-if="scope.row.horizontal" type="info" size="mini" effect="plain" class="tag-screen">
+              <i class="el-icon-monitor" /> 横屏
             </el-tag>
-            <el-tag v-else :type="'success'" disable-transitions>
-              <span icon="el-icon-mobile-phone">竖屏</span>
+            <el-tag v-else type="primary" size="mini" effect="plain" class="tag-screen">
+              <i class="el-icon-mobile-phone" /> 竖屏
             </el-tag>
           </template>
         </el-table-column>
+
         <el-table-column
           prop="scope"
           label="可见范围"
-          width="120"
+          width="110"
+          align="center"
         >
           <template slot-scope="scope">
-            <el-tag v-if="scope.row.scope === 1" :type="'warning'" disable-transitions>
-              本人可见
-            </el-tag>
-            <el-tag v-if="scope.row.scope === 2" :type="'success'" disable-transitions>
-              所有人可见
-            </el-tag>
-            <el-tag v-if="scope.row.scope === 3" :type="'danger'" disable-transitions>
-              VIP 可见
-            </el-tag>
+            <el-tag v-if="scope.row.scope === 1" type="info" size="mini" effect="light">本人可见</el-tag>
+            <el-tag v-else-if="scope.row.scope === 2" type="success" size="mini" effect="light">所有人可见</el-tag>
+            <el-tag v-else-if="scope.row.scope === 3" v-slot-scope="scope" type="warning" size="mini" effect="light">VIP 可见</el-tag>
+            <el-tag v-else type="info" size="mini">未知: {{ scope.row.scope }}</el-tag>
           </template>
         </el-table-column>
+
         <el-table-column
-          prop="scope"
+          prop="status"
           label="审核状态"
-          width="120"
+          width="110"
+          align="center"
         >
           <template slot-scope="scope">
-            <el-tag v-if="scope.row.status === 1" :type="'warning'" disable-transitions>
-              审核中
-            </el-tag>
-            <el-tag v-else-if="scope.row.status === 2" :type="'success'" disable-transitions>
-              审核通过
-            </el-tag>
-            <el-tag v-else-if="scope.row.status === 3" :type="'danger'" disable-transitions>
-              审核未通过
-            </el-tag>
-            <el-tag v-else-if="scope.row.status === 4" :type="'danger'" disable-transitions>
-              下架
-            </el-tag>
-            <el-tag v-else :type="'danger'" disable-transitions>
-              状态值: {{ scope.row.status }}
-            </el-tag>
+            <el-badge v-if="scope.row.status === 1" is-dot type="warning" class="status-dot-badge">
+              <el-tag type="warning" size="mini">审核中</el-tag>
+            </el-badge>
+            <el-tag v-else-if="scope.row.status === 2" type="success" size="mini">审核通过</el-tag>
+            <el-tag v-else-if="scope.row.status === 3" type="danger" size="mini">审核未通过</el-tag>
+            <el-tag v-else-if="scope.row.status === 4" type="info" size="mini">已下架</el-tag>
+            <el-tag v-else type="info" size="mini">未知: {{ scope.row.status }}</el-tag>
           </template>
         </el-table-column>
+
         <el-table-column
-          prop="videoId"
+          prop="userId"
           label="创作者"
           width="120"
+          align="center"
         >
           <template slot-scope="scope">
-            <router-link target="_blank" :to="`/video/${scope.row.videoId}`">
-              <span>{{ scope.row.videoId }}</span>
-            </router-link>
+            <span v-if="!scope.row.userId" class="text-muted">系统</span>
+
+            <div
+              v-else
+              class="user-creator-click-link"
+              title="点击快捷筛选该创作者的视频"
+              @click="handleCreatorClick(scope.row.userId)"
+            >
+              <i class="el-icon-user" />
+              <span>{{ scope.row.userId }}</span>
+            </div>
           </template>
         </el-table-column>
+
         <el-table-column
           fixed="right"
-          label="操作"
-          width="280"
+          label="操作快捷键"
+          width="180"
+          align="center"
         >
           <template slot-scope="scope">
-            <el-button
-              size="mini"
-              @click="handlePreview(scope.$index, scope.row)"
-            >预览</el-button>
-            <el-button
-              size="mini"
-              @click="handleEdit(scope.$index, scope.row)"
-            >编辑</el-button>
+            <router-link
+              :to="`/vod_audit/${scope.row.videoId}`"
+              target="_blank"
+              style="text-decoration: none; margin-right: 10px;"
+            >
+              <el-button
+                size="mini"
+                type="text"
+                icon="el-icon-finished"
+                class="btn-table-action btn-audit"
+              >
+                去审核
+              </el-button>
+            </router-link>
           </template>
         </el-table-column>
       </el-table>
-      <el-pagination
-        background
-        :small="screenWidth <= 768"
-        layout="prev, pager, next"
-        :page-size="pageSize"
-        :current-page="currentPage"
-        :total="totalSize"
-        @current-change="handleCurrentChange"
-        @prev-click="handleCurrentChange"
-        @next-click="handleCurrentChange"
-      />
-    </el-main>
 
-    <!-- 修改视频可见范围对话框 -->
-    <el-dialog
-      append-to-body
-      :visible.sync="showEditScopeDialog"
-      width="30%"
-      center
-    >
-      <el-card class="box-card">
-        <div slot="header" class="clearfix">
-          <span>修改视频可见范围</span>
-          <el-button style="float: right; padding: 3px 0" type="text" @click="onUpdateScope">更新</el-button>
-        </div>
-        <div class="text item">
-          <el-select v-model="form.scope" placeholder="选择可见范围">
-            <el-option label="本人可见" value="1" />
-            <el-option label="所有人可见" value="2" />
-            <el-option label="VIP 可见" value="3" />
-          </el-select>
-        </div>
-      </el-card>
-    </el-dialog>
-    <!-- 视频预览对话框 -->
-    <el-dialog
-      title="预览视频"
-      append-to-body
-      :visible.sync="showPreviewDialog"
-      :before-close="handleDialogClose"
-      width="70%"
-      center
-    >
-      <template>
-        <video-preview-player :video-prop.sync="videoProp" />
-      </template>
-    </el-dialog>
+      <div class="pagination-container">
+        <el-pagination
+          background
+          :small="screenWidth <= 768"
+          layout="total, sizes, prev, pager, next, jumper"
+          :page-sizes="[10, 12, 25, 50]"
+          :page-size="pageSize"
+          :current-page="currentPage"
+          :total="totalSize"
+          @current-change="handleCurrentChange"
+          @size-change="handleSizeChange"
+        />
+      </div>
+    </el-main>
   </el-container>
 </template>
 
 <script>
-import VideoPreviewPlayer from 'components/VideoPreviewPlayer'
-import { updateVideoScope, videoInfo } from '@/api/video'
 import { getVideoList } from '@/api/admin'
 
 export default {
   name: 'VideoPost',
-  components: { VideoPreviewPlayer },
   data() {
     return {
-      queryInfo: {
-        scope: null,
+      queryParams: {
+        title: '',
+        userId: '',
+        status: '', // 初始化空串,唤醒 clearable 清除特性
+        scope: '',
         pn: 1
       },
-      // 屏幕宽度, 为了控制分页条的大小
       screenWidth: document.body.clientWidth,
       currentPage: 1,
       pageSize: 12,
       totalSize: 0,
       dataList: [],
-      nextId: 0,
-      // **********************************************************************
-      videoProp: null,
-      showVideoResourceDialog: false,
-      showEditScopeDialog: false,
-      showPreviewDialog: false,
-      form: {
-        videoId: null,
-        scope: 1
-      },
-      videoResources: [],
-      publishVideoDiaglog: false
+      loading: false
     }
   },
   created() {
-    document.title = 'AdminVideoList'
     this.getData()
+    window.addEventListener('resize', this.handleWindowResize)
+  },
+  destroyed() {
+    window.removeEventListener('resize', this.handleWindowResize)
   },
   methods: {
-    handleCurrentChange(pageNumber) {
-      this.currentPage = pageNumber
-      this.getData()
-      // 回到顶部
-      scrollTo(0, 0)
-    },
     getData() {
-      this.dataList = []
-      getVideoList(0).then(resp => {
+      this.loading = true
+      // 分页器参数规整同步到 queryParams
+      this.queryParams.pn = this.currentPage
+
+      // 构建干净的请求载荷负载对象
+      const requestPayload = { ...this.queryParams }
+      if (!requestPayload.title.trim()) delete requestPayload.title
+      if (!requestPayload.userId.trim()) delete requestPayload.userId
+      if (requestPayload.status === '') delete requestPayload.status
+      if (requestPayload.scope === '') delete requestPayload.scope
+
+      // 这里传入过滤干净的多条件负载对象(原本写死的写为了动态对象)
+      getVideoList(requestPayload).then(resp => {
         if (resp.code === 0) {
-          const respData = resp.data
-          this.dataList = respData.list
-          this.totalSize = respData.totalSize
+          this.dataList = resp.data.list || []
+          this.totalSize = resp.data.totalSize || 0
         } else {
           this.$message.error(resp.msg)
         }
+      }).catch(err => {
+        this.$message.error(err.message || '获取视频库清单故障')
+      }).finally(() => {
+        this.loading = false
       })
     },
-    onRefresh() {
+    handleSearch() {
+      this.currentPage = 1 // 每次新搜索将分页强制回溯到第一页
       this.getData()
     },
-    handleScope(index, row) {
-      this.form.videoId = row.videoId
-      this.form.scope = '' + row.scope
-      this.showEditScopeDialog = true
+    onRefresh() {
+      // 全域初始化重置
+      this.queryParams.title = ''
+      this.queryParams.userId = ''
+      this.queryParams.status = ''
+      this.queryParams.scope = ''
+      this.currentPage = 1
+      this.getData()
+      this.$message.success('条件过滤器已重置')
     },
-    handleDialogClose(done) {
-      this.showPreviewDialog = false
-      this.videoProp = {
-        videoId: null,
-        play: false
-      }
-      done()
+    handleCurrentChange(pageNumber) {
+      this.currentPage = pageNumber
+      this.getData()
+      // 行内优雅置顶表格
+      const tableWrapper = this.$el.querySelector('.el-table__body-wrapper')
+      if (tableWrapper) tableWrapper.scrollTop = 0
     },
-    handlePreview(index, row) {
-      videoInfo(row.videoId).then(res => {
-        if (res.code === 0) {
-          this.showPreviewDialog = true
-          this.videoProp = {
-            videoId: res.data.videoId,
-            play: true
-          }
-        }
-      })
+    handleSizeChange(newSize) {
+      this.pageSize = newSize
+      this.currentPage = 1
+      this.getData()
     },
-    handleEdit(index, row) {
-      const path = '/post/video/edit/' + row.videoId
-      this.$router.push(path)
+    handleWindowResize() {
+      this.screenWidth = document.body.clientWidth
     },
-    onUpdateScope() {
-      this.showEditScopeDialog = false
-      updateVideoScope(this.form).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
-        })
+    handleCreatorClick(userId) {
+      if (!userId) return
+
+      // 1. 将点击的 ID 赋给顶部的搜索框绑定变量
+      this.queryParams.userId = String(userId)
+
+      // 2. 将页码切回第一页(防止原本在第5页,过滤后总页数不够导致报错)
+      this.currentPage = 1
+
+      // 3. 触发核心请求方法,向后台请求该用户发布的视频
+      this.getData()
+
+      // 4. 可选:加一个优雅的提示
+      this.$message({
+        message: `已为您筛选出创作者 [${userId}] 的视频`,
+        type: 'success',
+        duration: 2000
       })
-    },
-    onSelectChange() {
-      this.$message.info(this.queryInfo)
-    },
-    handleClose() {
     }
   }
 }
 </script>
 
-<style>
+<style scoped>
+/* 核心容器 */
+.admin-video-container {
+  background: #ffffff;
+  border-radius: 8px;
+  display: flex;
+  flex-direction: column;
+}
+
+/* 顶部紧凑型过滤组件 */
+.search-header {
+  padding: 16px 4px 4px 4px;
+  border-bottom: 1px solid #f1f5f9;
+}
+.search-form {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 4px 12px;
+}
+::v-deep .el-form-item {
+  margin-bottom: 12px;
+}
+::v-deep .el-form-item__label {
+  color: #64748b;
+  font-weight: 500;
+  padding-right: 6px;
+}
+.search-input-long { width: 220px; }
+.search-input { width: 150px; }
+.select-input { width: 130px; }
+.btn-reset { background: #f8fafc; border-color: #cbd5e1; color: #64748b; }
+.btn-reset:hover { color: #1890ff; border-color: #1890ff; background: #fff; }
+
+/* 主表格区 */
+.table-main {
+  padding: 16px 0 0 0;
+}
+.custom-table {
+  border-radius: 6px;
+  overflow: hidden;
+}
+
+/* 视频封面图优化 */
+.table-cover {
+  width: 80px;
+  height: 48px;
+  border-radius: 4px;
+  border: 1px solid #e2e8f0;
+  box-shadow: 0 2px 6px rgba(0,0,0,0.04);
+  cursor: zoom-in;
+}
+.image-error-slot {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  width: 100%;
+  height: 100%;
+  background: #f1f5f9;
+  color: #94a3b8;
+  font-size: 16px;
+}
+
+/* 超链接文本美化 */
+.video-id-link, .user-creator-link {
+  color: #1890ff;
+  text-decoration: none;
+  display: inline-flex;
+  align-items: center;
+  gap: 4px;
+  font-weight: 500;
+}
+.video-id-link:hover, .user-creator-link:hover {
+  color: #40a9ff;
+  text-decoration: underline;
+}
+.user-creator-link {
+  color: #475569;
+}
+.user-creator-link i {
+  color: #94a3b8;
+}
+
+/* 文本单行溢出优雅裁切 */
+.ellipsis-text {
+  display: block;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+.text-main-title {
+  color: #1e293b;
+  font-weight: 500;
+}
+.text-muted {
+  color: #64748b;
+  font-size: 13px;
+}
+
+/* 视频时长胶囊 */
+.duration-badge {
+  background-color: #0f172a;
+  color: #ffffff;
+  padding: 2px 6px;
+  border-radius: 4px;
+  font-family: monospace;
+  font-size: 12px;
+}
+
+/* 屏幕类型 Tag 特效调整 */
+.tag-screen {
+  border-radius: 4px;
+  font-weight: 500;
+}
+
+/* 审核中黄色小微标呼吸动态衬托 */
+.status-dot-badge ::v-deep .el-badge__content.is-fixed.is-dot {
+  right: 4px;
+  top: 2px;
+}
+
+/* 操作项去按钮化 */
+.btn-table-action {
+  font-weight: 500;
+  padding: 0 4px;
+}
+.btn-edit {
+  color: #e6a23c;
+}
+.btn-edit:hover {
+  color: #ebb563;
+}
+
+/* 分页器对齐 */
+.pagination-container {
+  margin-top: 20px;
+  display: flex;
+  justify-content: flex-end;
+}
+
+/* 统一高级 Dialog 弧度 */
+::v-deep .custom-dialog {
+  border-radius: 12px;
+  overflow: hidden;
+}
+.player-wrapper {
+  background-color: #000;
+  border-radius: 6px;
+  overflow: hidden;
+  line-height: 0;
+}
+
+/* 创作者快捷点击样式 */
+.user-creator-click-link {
+  color: #1890ff;
+  cursor: pointer;
+  display: inline-flex;
+  align-items: center;
+  gap: 4px;
+  font-weight: 500;
+  padding: 2px 6px;
+  border-radius: 4px;
+  transition: all 0.2s ease;
+}
+
+/* 悬浮时的现代 SaaS 视觉反馈:背景微显、颜色加深 */
+.user-creator-click-link:hover {
+  color: #40a9ff;
+  background-color: rgba(24, 144, 255, 0.1);
+}
+
+.user-creator-click-link i {
+  color: #94a3b8;
+  transition: color 0.2s;
+}
+
+.user-creator-click-link:hover i {
+  color: #40a9ff;
+}
+
+/* 系统文本样式 */
+.text-muted {
+  color: #94a3b8;
+  font-size: 13px;
+}
 </style>

+ 527 - 0
src/views/admin/aaa/VideoAudit.vue

@@ -0,0 +1,527 @@
+<template>
+  <el-container v-loading="loading" class="audit-container">
+    <el-main class="audit-main">
+      <div class="video-card">
+        <div class="video-header">
+          <el-tag size="small" type="info" effect="plain">ID: {{ currentVideo.videoId }}</el-tag>
+          <h1 class="video-title">{{ currentVideo.title }}</h1>
+        </div>
+
+        <div class="player-wrapper">
+          <video
+            ref="videoPlayer"
+            :src="currentVideo.videoUrl"
+            controls
+            autoplay
+            class="main-player"
+            @timeupdate="onTimeUpdate"
+          />
+        </div>
+
+        <div class="timeline-fast-tracks">
+          <span class="track-label"><i class="el-icon-timer" /> 进度抽审:</span>
+          <el-button-group>
+            <el-button size="mini" @click="jumpTo(0.1)">10%</el-button>
+            <el-button size="mini" @click="jumpTo(0.3)">30%</el-button>
+            <el-button size="mini" @click="jumpTo(0.5)">50%</el-button>
+            <el-button size="mini" @click="jumpTo(0.8)">80%</el-button>
+          </el-button-group>
+          <span class="current-time-tips">当前: {{ formatTime(currentTime) }} / {{ formatTime(duration) }}</span>
+        </div>
+      </div>
+
+      <div class="meta-card">
+        <h3 class="section-title">视频简介描述</h3>
+        <p class="description-text">{{ currentVideo.description || '该视频无文字简介' }}</p>
+
+        <div class="meta-grid">
+          <div class="meta-item">
+            <span class="label">画幅方向:</span>
+            <span class="value">{{ currentVideo.horizontal ? '横屏 (16:9)' : '竖屏 (9:16)' }}</span>
+          </div>
+          <div class="meta-item">
+            <span class="label">发布时间:</span>
+            <span class="value">{{ currentVideo.pubDate }}</span>
+          </div>
+          <div class="meta-item">
+            <span class="label">可见范围:</span>
+            <el-tag size="mini" type="info">{{ scopeMap[currentVideo.scope] }}</el-tag>
+          </div>
+        </div>
+      </div>
+    </el-main>
+
+    <el-aside width="360px" class="audit-aside">
+      <div class="aside-card author-card">
+        <h3 class="section-title">创作者信息</h3>
+        <div class="author-info">
+          <el-avatar :size="46" :src="currentVideo.userCard.avatarUrl" icon="el-icon-user-solid" />
+          <div class="author-detail">
+            <div class="author-name">{{ currentVideo.userCard.screenName || '未知用户' }}</div>
+            <div class="author-id">UID: {{ currentVideo.userCard.userIdStr }}</div>
+          </div>
+        </div>
+        <div class="author-badge-group">
+          <el-tag size="mini" type="success">历史通过率: 98%</el-tag>
+          <el-tag size="mini" type="warning">违规记录: 0次</el-tag>
+        </div>
+      </div>
+
+      <div class="aside-card action-card">
+        <h3 class="section-title">审核决策面板</h3>
+
+        <el-form ref="auditForm" :model="auditForm" label-position="top" size="small">
+          <el-form-item label="违规标签(不通过时必选)" prop="reasonTags">
+            <el-checkbox-group v-model="auditForm.reasonTags" :disabled="auditForm.decision === 2">
+              <el-checkbox label="涉政违规" />
+              <el-checkbox label="色情低俗" />
+              <el-checkbox label="血腥暴力" />
+              <el-checkbox label="侵权盗版" />
+              <el-checkbox label="垃圾广告" />
+              <el-checkbox label="画质低劣/封面党" />
+            </el-checkbox-group>
+          </el-form-item>
+
+          <el-form-item label="审核批注与意见" prop="remark">
+            <el-input
+              v-model="auditForm.remark"
+              type="textarea"
+              :rows="4"
+              placeholder="请输入具体的原因说明,将同步给创作者..."
+              maxlength="200"
+              show-word-limit
+            />
+          </el-form-item>
+
+          <div class="submit-buttons">
+            <el-button
+              type="danger"
+              icon="el-icon-close"
+              class="btn-reject"
+              :loading="submitting"
+              @click="submitAudit(3)"
+            >
+              拒绝 (驳回)
+            </el-button>
+            <el-button
+              type="success"
+              icon="el-icon-check"
+              class="btn-approve"
+              :loading="submitting"
+              @click="submitAudit(2)"
+            >
+              通过放行
+            </el-button>
+          </div>
+        </el-form>
+      </div>
+
+      <div class="aside-card shortcut-card">
+        <h4 class="shortcut-title"><i class="el-icon-info" /> 键盘盲审快捷键指南</h4>
+        <div class="shortcut-grid">
+          <div class="shortcut-item"><kbd>Space</kbd> <span>播放 / 暂停</span></div>
+          <div class="shortcut-item"><kbd>→</kbd> <span>快进 5 秒</span></div>
+          <div class="shortcut-item"><kbd>Shift + A</kbd> <span class="text-success">直接通过</span></div>
+          <div class="shortcut-item"><kbd>Shift + R</kbd> <span class="text-danger">直接拒绝</span></div>
+        </div>
+      </div>
+    </el-aside>
+  </el-container>
+</template>
+
+<script>
+import { getAuditVideo, submitAuditVideo } from '@/api/vod'
+
+export default {
+  name: 'VideoAudit',
+  // 组件激活前:摘除全局 #app 的强制滚动行为
+  beforeRouteEnter(to, from, next) {
+    next(vm => {
+      const appEl = document.getElementById('app')
+      if (appEl) appEl.style.overflowY = 'hidden'
+    })
+  },
+  // 组件离开前:还原还原全局 #app 的原貌,不破坏其他页面的行为
+  beforeRouteLeave(to, from, next) {
+    const appEl = document.getElementById('app')
+    if (appEl) appEl.style.overflowY = 'scroll'
+    next()
+  },
+  data() {
+    return {
+      loading: false,
+      submitting: false,
+      currentTime: 0,
+      duration: 0,
+      scopeMap: {
+        1: '本人可见',
+        2: '所有人可见',
+        3: 'VIP 可见'
+      },
+      // 当前待审核的视频数据模型
+      currentVideo: {
+        videoId: '',
+        title: '',
+        description: '',
+        videoUrl: '',
+        horizontal: null,
+        pubDate: '',
+        scope: 0,
+        userCard: {
+          userId: '',
+          screenName: '',
+          avatarUrl: ''
+        }
+      },
+      // 审核提交表单
+      auditForm: {
+        videoId: null,
+        decision: null, // 2:通过, 3:不通过
+        reasonTags: [],
+        remark: ''
+      }
+    }
+  },
+  watch: {
+    $route() {
+      this.$router.go()
+    }
+  },
+  created() {
+    const videoId = this.$route.params.id
+    this.fetchNextVideo(videoId)
+  },
+  mounted() {
+    // 全局监听键盘事件,开启极限盲审模式
+    window.addEventListener('keydown', this.handleKeyboardShortcuts)
+  },
+  beforeDestroy() {
+    window.removeEventListener('keydown', this.handleKeyboardShortcuts)
+  },
+  methods: {
+    // 获取下一条待审核视频
+    fetchNextVideo(videoId) {
+      this.loading = true
+      // 重置表单状态
+      this.auditForm = { decision: null, reasonTags: [], remark: '' }
+      this.currentTime = 0
+      this.duration = 0
+
+      const queryParams = {}
+      queryParams.videoId = videoId
+      getAuditVideo(queryParams).then(resp => {
+        if (resp.code === 0) {
+          this.currentVideo = resp.data
+          this.auditForm.videoId = this.currentVideo.videoId
+        } else {
+          this.$message.warning(resp.msg)
+        }
+      }).finally(() => {
+        this.loading = false
+      })
+    },
+
+    // 提交审核决策
+    submitAudit(status) {
+      this.auditForm.decision = status
+
+      // 校验逻辑:如果不通过,必须选择至少一个违规标签或填写备注
+      if (status === 3 && this.auditForm.reasonTags.length === 0 && !this.auditForm.remark.trim()) {
+        this.$message.error('请选择违规标签或填写不通过的具体审核意见!')
+        return
+      }
+
+      this.submitting = true
+      submitAuditVideo(this.auditForm).then(resp => {
+        if (resp.code === 0) {
+          var nextVideoId = resp.data
+          this.submitting = false
+          this.$notify({
+            title: status === 2 ? '审核通过' : '已驳回',
+            message: `视频 [${this.currentVideo.videoId}] 处理完毕,已自动加载下一条。`,
+            type: status === 2 ? 'success' : 'warning',
+            duration: 2500
+          })
+          this.$router.push('/vod_audit/' + nextVideoId)
+        }
+      })
+    },
+
+    // 播放器时间更新
+    onTimeUpdate(e) {
+      this.currentTime = e.target.currentTime
+      this.duration = e.target.duration || 0
+    },
+
+    // 时间轴快捷百分比跳跃
+    jumpTo(percentage) {
+      const player = this.$refs.videoPlayer
+      if (player && this.duration) {
+        player.currentTime = this.duration * percentage
+      }
+    },
+
+    // 工具函数:格式化时间 (00:00)
+    formatTime(seconds) {
+      if (isNaN(seconds)) return '00:00'
+      const min = Math.floor(seconds / 60).toString().padStart(2, '0')
+      const sec = Math.floor(seconds % 60).toString().padStart(2, '0')
+      return `${min}:${sec}`
+    },
+
+    // 键盘快捷键矩阵处理器
+    handleKeyboardShortcuts(e) {
+      // 如果光标正停留在文本输入框内,不触发快捷键,防止冲突
+      if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return
+
+      const player = this.$refs.videoPlayer
+
+      switch (e.key) {
+        case ' ': // 空格键:暂停/播放
+          e.preventDefault()
+          if (player) {
+            player.paused ? player.play() : player.pause()
+          }
+          break
+        case 'ArrowRight': // 方向右键:快进5秒
+          if (player) player.currentTime += 5
+          break
+        case 'ArrowLeft': // 方向左键:快退5秒
+          if (player) player.currentTime -= 5
+          break
+        case 'A': // Shift + A: 快捷通过
+          if (e.shiftKey) this.submitAudit(2)
+          break
+        case 'R': // Shift + R: 快捷拒绝
+          if (e.shiftKey) this.submitAudit(3)
+          break
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+/* 全局页面沙箱 - 修正为直接吃满 100vh 浏览器视口 */
+.audit-container {
+  height: 100vh;
+  width: 100vw;
+  background-color: #f4f7f9;
+  gap: 20px;
+  padding: 20px; /* 给四周留出高级的视窗边距 */
+  box-sizing: border-box; /* 锁定边距不撑开大盘 */
+  overflow: hidden; /* 严禁外层容器出现滚动条 */
+}
+
+/* ==================== 🎬 左侧内容区 ==================== */
+.audit-main {
+  padding: 0;
+  display: flex;
+  flex-direction: column;
+  gap: 20px;
+  height: 100%; /* 锁定跟随父级高度 */
+  overflow-y: auto; /* 仅允许内容区独立轴向滚动 */
+}
+
+.video-card {
+  background: #ffffff;
+  border-radius: 8px;
+  padding: 20px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
+}
+
+.video-header {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  margin-bottom: 16px;
+}
+
+.video-title {
+  font-size: 18px;
+  font-weight: 600;
+  color: #1e293b;
+  margin: 0;
+}
+
+/* 核心播放器容器 */
+.player-wrapper {
+  background-color: #000000;
+  border-radius: 6px;
+  overflow: hidden;
+  position: relative;
+  width: 100%;
+  aspect-ratio: 16 / 9; /* 保持16:9黄金比例,自适应各屏幕 */
+  max-height: 500px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
+.main-player {
+  width: 100%;
+  height: 100%;
+  object-fit: contain; /* 防止视频拉伸变形 */
+}
+
+/* 抽审进度控制条 */
+.timeline-fast-tracks {
+  margin-top: 14px;
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  background: #f8fafc;
+  padding: 8px 12px;
+  border-radius: 6px;
+  border: 1px dashed #e2e8f0;
+}
+
+.track-label {
+  font-size: 13px;
+  color: #64748b;
+  font-weight: 500;
+}
+
+.current-time-tips {
+  margin-left: auto;
+  font-family: monospace;
+  font-size: 13px;
+  color: #334155;
+  background: #cbd5e1;
+  padding: 2px 8px;
+  border-radius: 4px;
+}
+
+/* 视频元数据描述卡片 */
+.meta-card {
+  background: #ffffff;
+  border-radius: 8px;
+  padding: 20px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
+}
+
+.section-title {
+  font-size: 14px;
+  font-weight: 600;
+  color: #475569;
+  margin-top: 0;
+  margin-bottom: 12px;
+  border-left: 3px solid #1890ff;
+  padding-left: 8px;
+}
+
+.description-text {
+  font-size: 14px;
+  color: #334155;
+  line-height: 1.6;
+  background: #f8fafc;
+  padding: 12px;
+  border-radius: 6px;
+  margin-bottom: 16px;
+}
+
+.meta-grid {
+  display: grid;
+  grid-template-columns: repeat(3, 1fr);
+  gap: 16px;
+}
+
+.meta-item {
+  font-size: 13px;
+}
+.meta-item .label { color: #64748b; }
+.meta-item .value { color: #1e293b; font-weight: 500; }
+
+/* ==================== 🛠️ 右侧风控工作台 ==================== */
+.audit-aside {
+  display: flex;
+  flex-direction: column;
+  gap: 20px;
+  height: 100%; /* 锁定跟随父级高度 */
+  overflow-y: auto; /* 防止右侧内容过多时溢出屏幕 */
+}
+
+.aside-card {
+  background: #ffffff;
+  border-radius: 8px;
+  padding: 20px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
+}
+
+/* 创作者信息 */
+.author-info {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  margin-bottom: 12px;
+}
+.author-name {
+  font-size: 15px;
+  font-weight: 600;
+  color: #1e293b;
+}
+.author-id {
+  font-size: 12px;
+  color: #94a3b8;
+  margin-top: 2px;
+}
+.author-badge-group {
+  display: flex;
+  gap: 6px;
+}
+
+/* 审核决策控制流 */
+::v-deep .el-checkbox {
+  margin-bottom: 8px;
+  margin-right: 16px;
+  width: 120px; /* 两列规整排列 */
+}
+
+.submit-buttons {
+  display: flex;
+  gap: 12px;
+  margin-top: 20px;
+}
+
+.btn-reject, .btn-approve {
+  flex: 1;
+  height: 40px;
+  font-weight: 600;
+  border-radius: 6px;
+}
+
+/* 盲审快捷键指南卡片 */
+.shortcut-card {
+  background: #0f172a; /* 深色极客风,与快捷键功能相呼应 */
+  color: #94a3b8;
+}
+.shortcut-title {
+  color: #f1f5f9;
+  margin: 0 0 12px 0;
+  font-size: 13px;
+  font-weight: 500;
+}
+.shortcut-grid {
+  display: grid;
+  grid-template-columns: 1fr;
+  gap: 8px;
+}
+.shortcut-item {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  font-size: 12px;
+}
+kbd {
+  background-color: #334155;
+  color: #ffffff;
+  border-radius: 3px;
+  border: 1px solid #475569;
+  padding: 2px 6px;
+  font-family: monospace;
+  font-weight: bold;
+  box-shadow: 0 1px 0 rgba(0,0,0,0.2);
+}
+.text-success { color: #10b981; }
+.text-danger { color: #ef4444; }
+</style>

+ 2 - 2
src/views/admin/site/AdminCategory.vue

@@ -137,8 +137,8 @@
 
 <script>
 import { userMixin } from 'assets/js/mixin'
-import {getSiteNotice, getVipPlan, updateSiteNotice} from '@/api/admin'
-import { videoCategories } from '@/api/video'
+import { getSiteNotice, getVipPlan, updateSiteNotice } from '@/api/admin'
+import { videoCategories } from '@/api/vod'
 
 export default {
   name: 'AdminCategory',

+ 0 - 1
src/views/blog/Blog.vue

@@ -73,7 +73,6 @@ export default {
     }
   },
   watch: {
-    // 地址栏 url 发生变化时重新加载本页面
     $route() {
       this.$router.go()
     }

+ 0 - 1
src/views/chart/ChartIndex.vue

@@ -58,7 +58,6 @@ export default {
     }
   },
   watch: {
-    // 地址栏 url 发生变化时重新加载本页面
     $route() {
       this.$router.go()
     }

+ 0 - 1
src/views/chat/Chat.vue

@@ -65,7 +65,6 @@ export default {
     }
   },
   watch: {
-    // 地址栏 url 发生变化时重新加载本页面
     $route() {
       this.$router.go()
     }

+ 1 - 1
src/views/devops/file/FileList.vue

@@ -126,7 +126,7 @@
 
 <script>
 import VideoPreviewPlayer from 'components/VideoPreviewPlayer'
-import { updateVideoScope, videoInfo } from '@/api/video'
+import { updateVideoScope, videoInfo } from '@/api/vod'
 import { getVideoList } from '@/api/admin'
 
 export default {

+ 1 - 1
src/views/devops/file/ImageFile.vue

@@ -45,7 +45,7 @@
 
 <script>
 import VideoPreviewPlayer from 'components/VideoPreviewPlayer'
-import { updateVideoScope, videoInfo } from '@/api/video'
+import { updateVideoScope, videoInfo } from '@/api/vod'
 import { getVideoList } from '@/api/admin'
 
 export default {

+ 1 - 1
src/views/devops/sys/AccessLog.vue

@@ -45,7 +45,7 @@
 
 <script>
 import VideoPreviewPlayer from 'components/VideoPreviewPlayer'
-import { updateVideoScope, videoInfo } from '@/api/video'
+import { updateVideoScope, videoInfo } from '@/api/vod'
 import { getVideoList } from '@/api/admin'
 
 export default {

+ 1 - 1
src/views/devops/sys/RealtimeLog.vue

@@ -45,7 +45,7 @@
 
 <script>
 import VideoPreviewPlayer from 'components/VideoPreviewPlayer'
-import { updateVideoScope, videoInfo } from '@/api/video'
+import { updateVideoScope, videoInfo } from '@/api/vod'
 import { getVideoList } from '@/api/admin'
 
 export default {

+ 1 - 1
src/views/devops/sys/RuntimeLog.vue

@@ -45,7 +45,7 @@
 
 <script>
 import VideoPreviewPlayer from 'components/VideoPreviewPlayer'
-import { updateVideoScope, videoInfo } from '@/api/video'
+import { updateVideoScope, videoInfo } from '@/api/vod'
 import { getVideoList } from '@/api/admin'
 
 export default {

+ 46 - 43
src/views/home/Home.vue

@@ -4,17 +4,17 @@
       <el-col :xs="24" :sm="24" :md="15" :lg="17" class="carousel-col">
         <div class="carousel-full-height-container">
           <el-carousel
-              v-if="carouselList.length > 0"
-              :interval="5000"
-              arrow="hover"
-              class="custom-carousel shadow-hover"
-              :height="carouselHeight"
+            v-if="carouselList.length > 0"
+            :interval="5000"
+            arrow="hover"
+            class="custom-carousel shadow-hover"
+            :height="carouselHeight"
           >
             <el-carousel-item v-for="(item, index) in carouselList" :key="'banner-'+index">
               <router-link :to="`/video/${item.videoId}`">
                 <div class="carousel-item-wrapper">
                   <img class="carousel-img" :src="item.coverUrl" alt="banner">
-                  <div class="carousel-mask"></div>
+                  <div class="carousel-mask" />
                   <div class="carousel-info">
                     <h3 class="carousel-title">{{ item.title || '精彩内容' }}</h3>
                   </div>
@@ -23,7 +23,7 @@
             </el-carousel-item>
           </el-carousel>
           <div v-else class="carousel-placeholder" :style="{height: carouselHeight}">
-            <i class="el-icon-loading"></i>
+            <i class="el-icon-loading" />
           </div>
         </div>
       </el-col>
@@ -34,18 +34,21 @@
     </el-row>
 
     <div
-        class="video-grid-container"
-        v-infinite-scroll="load"
-        :infinite-scroll-disabled="disabled"
-        :infinite-scroll-distance="200"
-        :infinite-scroll-immediate="false"
+      v-infinite-scroll="load"
+      class="video-grid-container"
+      :infinite-scroll-disabled="disabled"
+      :infinite-scroll-distance="200"
+      :infinite-scroll-immediate="false"
     >
       <el-row class="video-row">
         <el-col
-            v-for="(item, index) in dataList"
-            :key="'video-'+index"
-            :xs="12" :sm="8" :md="6" :lg="4"
-            class="video-card-col"
+          v-for="(item, index) in dataList"
+          :key="'video-'+index"
+          :xs="12"
+          :sm="8"
+          :md="6"
+          :lg="4"
+          class="video-card-col"
         >
           <video-card :video="item" />
         </el-col>
@@ -53,10 +56,10 @@
 
       <div class="load-status">
         <div v-if="loading" class="loading-bar">
-          <i class="el-icon-loading"></i> <span>加载中...</span>
+          <i class="el-icon-loading" /> <span>加载中...</span>
         </div>
         <div v-if="noMore" class="no-more-bar">
-          <span class="dot"></span> 到底啦 <span class="dot"></span>
+          <span class="dot" /> 到底啦 <span class="dot" />
         </div>
       </div>
     </div>
@@ -66,7 +69,7 @@
 <script>
 import VideoCard from '@/components/card/VideoCard'
 import SiteNotice from '@/components/card/SiteNotice'
-import { getBannerVideo, videoRecommend } from '@/api/video'
+import { getBannerVideo, videoRecommend } from '@/api/vod'
 
 export default {
   name: 'Home',
@@ -85,30 +88,30 @@ export default {
   },
   computed: {
     disabled() {
-      return this.loading || this.noMore || this.isFirstLoading;
+      return this.loading || this.noMore || this.isFirstLoading
     }
   },
   created() {
-    this.handleResize();
-    this.initData();
+    this.handleResize()
+    this.initData()
   },
   mounted() {
-    window.addEventListener('resize', this.debounceResize);
+    window.addEventListener('resize', this.debounceResize)
   },
   beforeDestroy() {
-    window.removeEventListener('resize', this.debounceResize);
+    window.removeEventListener('resize', this.debounceResize)
   },
   methods: {
     debounceResize() {
-      if (this.timer) clearTimeout(this.timer);
-      this.timer = setTimeout(() => this.handleResize(), 200);
+      if (this.timer) clearTimeout(this.timer)
+      this.timer = setTimeout(() => this.handleResize(), 200)
     },
     handleResize() {
-      const width = window.innerWidth || document.documentElement.clientWidth;
+      const width = window.innerWidth || document.documentElement.clientWidth
       if (width < 992) {
-        this.carouselHeight = width < 768 ? '190px' : '230px';
+        this.carouselHeight = width < 768 ? '190px' : '230px'
       } else {
-        this.carouselHeight = '340px';
+        this.carouselHeight = '340px'
       }
     },
     async initData() {
@@ -116,37 +119,37 @@ export default {
         await Promise.all([
           this.getHotVideoWrapper(),
           this.videoRecommendWrapper(this.nextId)
-        ]);
+        ])
       } finally {
-        this.isFirstLoading = false;
+        this.isFirstLoading = false
       }
     },
     async videoRecommendWrapper(nextId) {
-      if (this.noMore) return;
-      this.loading = true;
+      if (this.noMore) return
+      this.loading = true
       try {
-        const resp = await videoRecommend(nextId);
+        const resp = await videoRecommend(nextId)
         if (resp.code === 0) {
-          const respData = resp.data;
+          const respData = resp.data
           if (!respData || respData.length === 0) {
-            this.noMore = true;
+            this.noMore = true
           } else {
-            this.dataList = [...this.dataList, ...respData];
-            this.nextId++;
+            this.dataList = [...this.dataList, ...respData]
+            this.nextId++
           }
         }
       } catch (e) {
-        console.error(e);
+        console.error(e)
       } finally {
-        setTimeout(() => { this.loading = false; }, 400);
+        setTimeout(() => { this.loading = false }, 400)
       }
     },
     load() {
-      this.videoRecommendWrapper(this.nextId);
+      this.videoRecommendWrapper(this.nextId)
     },
     async getHotVideoWrapper() {
-      const resp = await getBannerVideo();
-      if (resp.code === 0) this.carouselList = resp.data;
+      const resp = await getBannerVideo()
+      if (resp.code === 0) this.carouselList = resp.data
     }
   }
 }

+ 1 - 1
src/views/home/Index.vue

@@ -54,7 +54,7 @@
 <script>
 import VideoCard from '@/components/card/VideoCard'
 import SiteNotice from '@/components/card/SiteNotice'
-import { getHotVideo, videoRecommend } from '@/api/video'
+import { getHotVideo, videoRecommend } from '@/api/vod'
 
 export default {
   name: 'Index',

+ 1 - 1
src/views/home/PlaylistView.vue

@@ -119,7 +119,7 @@
 <script>
 import PermissionDeniedCard from '@/components/card/PermissionDeniedCard'
 import DPlayer from 'dplayer'
-import { videoUrl, videoInfo } from '@/api/video'
+import { videoUrl, videoInfo } from '@/api/vod'
 import { getPlaylistItems } from '@/api/collect'
 
 export default {

+ 31 - 28
src/views/home/Region.vue

@@ -4,22 +4,22 @@
       <el-col :md="5" :lg="4" class="sidebar-col">
         <el-card class="category-card shadow-sm" :body-style="{ padding: '10px 0' }">
           <div slot="header" class="category-header">
-            <i class="el-icon-menu"></i>
+            <i class="el-icon-menu" />
             <span>视频分区</span>
           </div>
           <div class="category-menu-wrapper">
             <el-tree
-                :accordion="true"
-                :data="treeNode"
-                :props="defaultProps"
-                highlight-current
-                node-key="value"
-                ref="categoryTree"
-                @node-click="handleNodeClick"
-                class="custom-tree"
+              ref="categoryTree"
+              :accordion="true"
+              :data="treeNode"
+              :props="defaultProps"
+              highlight-current
+              node-key="value"
+              class="custom-tree"
+              @node-click="handleNodeClick"
             >
               <span slot-scope="{ node, data }" class="tree-node-item">
-                <i :class="data.icon || 'el-icon-folder'"></i>
+                <i :class="data.icon || 'el-icon-folder'" />
                 <span class="node-label">{{ node.label }}</span>
               </span>
             </el-tree>
@@ -29,32 +29,35 @@
 
       <el-col :md="19" :lg="20" class="content-col">
         <div v-loading="loading" class="video-grid-wrapper">
-          <el-row :gutter="16" v-if="dataList && dataList.length > 0">
+          <el-row v-if="dataList && dataList.length > 0" :gutter="16">
             <el-col
-                v-for="(video, index) in dataList"
-                :key="index"
-                :xs="12" :sm="8" :md="8" :lg="6"
-                class="video-item"
+              v-for="(video, index) in dataList"
+              :key="index"
+              :xs="12"
+              :sm="8"
+              :md="8"
+              :lg="6"
+              class="video-item"
             >
               <video-card :video="video" class="hover-up" />
             </el-col>
           </el-row>
 
           <el-empty
-              v-else-if="!loading"
-              description="该分区下暂无视频内容"
-              :image-size="200"
-          ></el-empty>
+            v-else-if="!loading"
+            description="该分区下暂无视频内容"
+            :image-size="200"
+          />
 
-          <div class="pagination-container" v-if="totalSize > pageSize">
+          <div v-if="totalSize > pageSize" class="pagination-container">
             <el-pagination
-                background
-                :small="screenWidth <= 768"
-                layout="prev, pager, next, total"
-                :page-size="pageSize"
-                :current-page="currentPage"
-                :total="totalSize"
-                @current-change="handleCurrentChange"
+              background
+              :small="screenWidth <= 768"
+              layout="prev, pager, next, total"
+              :page-size="pageSize"
+              :current-page="currentPage"
+              :total="totalSize"
+              @current-change="handleCurrentChange"
             />
           </div>
         </div>
@@ -65,7 +68,7 @@
 
 <script>
 import VideoCard from 'components/card/VideoCard'
-import { categoryVideos, videoCategories } from '@/api/video'
+import { categoryVideos, videoCategories } from '@/api/vod'
 
 export default {
   name: 'Region',

+ 2 - 5
src/views/home/ShareVideo.vue

@@ -58,11 +58,9 @@
 </template>
 
 <script>
-import UserAvatarCard from '@/components/card/UserAvatarCard'
-
 import DPlayer from 'dplayer'
-import { videoInfo, videoUrl } from '@/api/video'
-import { getShortVideo } from '@/api/video'
+import { videoInfo, videoUrl } from '@/api/vod'
+import { getShortVideo } from '@/api/vod'
 import { getUserInfo } from '@/api/user'
 import { getAccessToken } from '@/utils/auth'
 
@@ -82,7 +80,6 @@ export default {
     }
   },
   watch: {
-    // 地址栏 url 发生变化时重新加载本页面
     $route() {
       this.$router.go()
     }

+ 67 - 67
src/views/home/ShortVideo.vue

@@ -1,65 +1,65 @@
 <template>
   <div
-      class="short-video-wrapper"
-      v-if="video !== null"
-      @wheel.prevent="handleWheel"
-      @touchstart="touchStart"
-      @touchmove.prevent
-      @touchend="touchEnd"
+    v-if="video !== null"
+    class="short-video-wrapper"
+    @wheel.prevent="handleWheel"
+    @touchstart="touchStart"
+    @touchmove.prevent
+    @touchend="touchEnd"
   >
     <transition name="el-fade-in">
-      <div :key="video.videoId" class="video-bg" :style="{ backgroundImage: `url(${video.coverUrl})` }"></div>
+      <div :key="video.videoId" class="video-bg" :style="{ backgroundImage: `url(${video.coverUrl})` }" />
     </transition>
 
     <div class="video-container" @click="handleSingleClick" @dblclick="handleDoubleLike">
       <video
-          v-if="videoActive && currentVideoUrl"
-          ref="videoPlayer"
-          class="native-video"
-          :src="currentVideoUrl"
-          :poster="video.coverUrl"
-          loop
-          preload="auto"
-          playsinline
-          webkit-playsinline
-          x5-video-player-type="h5-page"
-          x5-video-player-fullscreen="true"
-          @loadedmetadata="onVideoLoad"
-          @waiting="onWaiting"
-          @playing="onPlaying"
-          @error="onVideoError"
-      ></video>
+        v-if="videoActive && currentVideoUrl"
+        ref="videoPlayer"
+        class="native-video"
+        :src="currentVideoUrl"
+        :poster="video.coverUrl"
+        loop
+        preload="auto"
+        playsinline
+        webkit-playsinline
+        x5-video-player-type="h5-page"
+        x5-video-player-fullscreen="true"
+        @loadedmetadata="onVideoLoad"
+        @waiting="onWaiting"
+        @playing="onPlaying"
+        @error="onVideoError"
+      />
 
       <div class="video-status-overlay">
         <transition name="el-zoom-in-center">
-          <i v-if="isLoading" class="el-icon-loading status-icon"></i>
-          <i v-else-if="isPaused" class="el-icon-video-play status-icon"></i>
+          <i v-if="isLoading" class="el-icon-loading status-icon" />
+          <i v-else-if="isPaused" class="el-icon-video-play status-icon" />
         </transition>
       </div>
 
-      <div class="like-anim-container" ref="likeAnimBox"></div>
+      <div ref="likeAnimBox" class="like-anim-container" />
     </div>
 
     <div class="side-bar">
-      <div class="side-item author-box" v-if="user" @click.stop="handleAvatarClick">
+      <div v-if="user" class="side-item author-box" @click.stop="handleAvatarClick">
         <div class="avatar-wrapper">
-          <el-avatar :size="50" :src="user.avatarUrl"></el-avatar>
-          <div class="follow-plus"><i class="el-icon-plus"></i></div>
+          <el-avatar :size="50" :src="user.avatarUrl" />
+          <div class="follow-plus"><i class="el-icon-plus" /></div>
         </div>
       </div>
 
       <transition name="slide-fade">
-        <div class="user-peek-card" v-if="showUserCard">
-          <div class="card-mask" @click="showUserCard = false"></div>
+        <div v-if="showUserCard" class="user-peek-card">
+          <div class="card-mask" @click="showUserCard = false" />
 
           <div class="card-content">
             <div class="profile-main">
               <el-avatar
-                  :size="80"
-                  :src="user.avatarUrl"
-                  class="card-avatar clickable-avatar"
-                  @click.native="goToFullProfile"
-              ></el-avatar>
+                :size="80"
+                :src="user.avatarUrl"
+                class="card-avatar clickable-avatar"
+                @click.native="goToFullProfile"
+              />
               <h2 class="name">@{{ user.screenName }}</h2>
               <p class="bio">{{ user.signature || '暂无个性签名' }}</p>
 
@@ -75,7 +75,7 @@
               <p class="label">近期作品</p>
               <div class="thumb-list">
                 <div v-for="{item, index} in userWorks" :key="index" class="work-thumb">
-                  <i class="el-icon-video-play"></i>
+                  <i class="el-icon-video-play" />
                 </div>
               </div>
             </div>
@@ -84,25 +84,25 @@
       </transition>
 
       <div class="side-item" @click.stop="handleLike">
-        <div class="icon-circle" :class="{ 'is-active': isLiked }"><i class="el-icon-star-on"></i></div>
+        <div class="icon-circle" :class="{ 'is-active': isLiked }"><i class="el-icon-star-on" /></div>
         <span class="count-text">{{ formatCount(video.view) }}</span>
       </div>
       <div class="side-item" @click.stop="toggleComments">
-        <div class="icon-circle"><i class="el-icon-s-comment"></i></div>
+        <div class="icon-circle"><i class="el-icon-s-comment" /></div>
         <span class="count-text">{{ formatCount(video.comment) }}</span>
       </div>
       <div class="side-item" @click.stop="handleShare">
-        <div class="icon-circle"><i class="el-icon-share"></i></div>
+        <div class="icon-circle"><i class="el-icon-share" /></div>
         <span class="count-text">{{ formatCount(video.comment) }}</span>
       </div>
     </div>
 
     <div class="video-info-bottom">
       <div class="info-content">
-        <h3 class="author-name" v-if="user">@{{ user.screenName }}</h3>
+        <h3 v-if="user" class="author-name">@{{ user.screenName }}</h3>
         <p class="video-desc">{{ video.title }}</p>
         <div class="music-tag">
-          <div class="music-icon-box"><i class="el-icon-headset"></i></div>
+          <div class="music-icon-box"><i class="el-icon-headset" /></div>
           <div class="marquee-box">
             <span class="marquee-text">{{ video.title }} - 精彩原声</span>
           </div>
@@ -111,13 +111,13 @@
     </div>
 
     <div class="top-controls">
-      <el-button type="info" icon="el-icon-refresh" circle size="small" :loading="isSwitching" @click="nextVideo"></el-button>
+      <el-button type="info" icon="el-icon-refresh" circle size="small" :loading="isSwitching" @click="nextVideo" />
     </div>
   </div>
 </template>
 
 <script>
-import { videoUrl, getShortVideo } from '@/api/video'
+import { videoUrl, getShortVideo } from '@/api/vod'
 import { getUserInfo } from '@/api/user'
 
 export default {
@@ -145,9 +145,9 @@ export default {
     // 监听卡片状态,触发音量变化
     showUserCard(val) {
       if (val) {
-        this.fadeVolume(0.2); // 弹出时:音量降至 20%
+        this.fadeVolume(0.2) // 弹出时:音量降至 20%
       } else {
-        this.fadeVolume(this.originalVolume); // 关闭时:恢复原音量
+        this.fadeVolume(this.originalVolume) // 关闭时:恢复原音量
       }
     }
   },
@@ -156,69 +156,69 @@ export default {
   },
   methods: {
     fadeVolume(targetVolume) {
-      const v = this.$refs.videoPlayer;
-      if (!v) return;
+      const v = this.$refs.videoPlayer
+      if (!v) return
 
-      clearInterval(this.volumeTimer);
+      clearInterval(this.volumeTimer)
 
       // 每次调节的步长
-      const step = v.volume < targetVolume ? 0.05 : -0.05;
+      const step = v.volume < targetVolume ? 0.05 : -0.05
 
       this.volumeTimer = setInterval(() => {
-        const newVolume = v.volume + step;
+        const newVolume = v.volume + step
 
         // 判断是否到达目标值
         if ((step > 0 && newVolume >= targetVolume) || (step < 0 && newVolume <= targetVolume)) {
-          v.volume = targetVolume;
-          clearInterval(this.volumeTimer);
+          v.volume = targetVolume
+          clearInterval(this.volumeTimer)
         } else {
-          v.volume = Math.max(0, Math.min(1, newVolume));
+          v.volume = Math.max(0, Math.min(1, newVolume))
         }
-      }, 30); // 30ms 调节一次,体感非常平滑
+      }, 30) // 30ms 调节一次,体感非常平滑
     },
     async handleAvatarClick() {
-      this.showUserCard = true;
+      this.showUserCard = true
       // 2. 如果已经加载过该用户的作品,且 ID 没变,可以不重复加载
       // 这里假设 user 对象里有 userId
       if (this.userWorks.length > 0 && this.userWorks[0].authorId === this.user.userId) {
-        return;
+        return
       }
 
       try {
         // 这里的 getWorksByUserId 是你定义的 API 方法
         // const res = await getWorksByUserId(this.user.userId);
         // --- 模拟 API 请求延迟 ---
-        await new Promise(resolve => setTimeout(resolve, 800));
+        await new Promise(resolve => setTimeout(resolve, 800))
         this.userWorks = [
           { id: 1, coverUrl: 'https://via.placeholder.com/150x200', playNum: 12500, authorId: this.user.userId },
           { id: 2, coverUrl: 'https://via.placeholder.com/150x200', playNum: 8800, authorId: this.user.userId },
           { id: 3, coverUrl: 'https://via.placeholder.com/150x200', playNum: 21000, authorId: this.user.userId }
-        ];
-        console.log("获取作品");
+        ]
+        console.log('获取作品')
       } catch (error) {
-        this.$message.error("作品加载失败");
+        this.$message.error('作品加载失败')
       }
     },
     // 点击作品小图的处理
     playThisWork(work) {
-      this.showUserCard = false;
-      this.$message.info(`正在切换至视频: ${work.id}`);
+      this.showUserCard = false
+      this.$message.info(`正在切换至视频: ${work.id}`)
       // 这里可以触发你之前定义的切换视频 API
     },
     // 模拟跳转完整主页
     goToFullProfile() {
       // 1. 立即关闭卡片显示
-      this.showUserCard = false;
+      this.showUserCard = false
 
       // 2. 触发恢复音量的逻辑(watch 会自动监听到 showUserCard 变为 false)
       // 但为了确保跳转瞬间声音已经开始恢复,可以手动调用一次
-      this.fadeVolume(this.originalVolume || 1.0);
+      this.fadeVolume(this.originalVolume || 1.0)
 
       // 3. 执行路由跳转
       // 使用 $nextTick 确保卡片关闭的动画开始后再跳转,或者直接跳转
       this.$router.push(`/user/${this.user.userId}`).catch(err => {
-        if (err.name !== 'NavigationDuplicated') console.error(err);
-      });
+        if (err.name !== 'NavigationDuplicated') console.error(err)
+      })
     },
     handleLike() { this.isLiked = !this.isLiked },
     handleShare() { this.$message.success('链接已复制') },

+ 11 - 11
src/views/home/Timeline.vue

@@ -1,16 +1,16 @@
 <template>
   <div class="timeline-container">
-    <el-row :gutter="24" v-if="userInfo" class="timeline-layout">
+    <el-row v-if="userInfo" :gutter="24" class="timeline-layout">
 
       <el-col :md="6" :lg="5" class="hidden-sm-and-down">
         <div class="sticky-sidebar">
           <el-card class="user-profile-card shadow-soft" :body-style="{ padding: '0' }">
-            <div class="card-banner"></div>
+            <div class="card-banner" />
             <div class="profile-content">
               <div class="avatar-container">
                 <router-link :to="`/user/${userInfo.userId}`">
                   <el-avatar :size="80" :src="userInfo.avatarUrl" class="main-avatar" />
-                  <i v-if="userInfo.isVip" class="el-icon-star-on vip-icon" title="大会员"></i>
+                  <i v-if="userInfo.isVip" class="el-icon-star-on vip-icon" title="大会员" />
                 </router-link>
               </div>
               <div class="user-info">
@@ -46,11 +46,11 @@
         </div>
 
         <div
-            class="scroll-viewport shadow-soft"
-            v-infinite-scroll="load"
-            :infinite-scroll-disabled="loading || noMore"
-            :infinite-scroll-distance="50"
-            :infinite-scroll-immediate="false"
+          v-infinite-scroll="load"
+          class="scroll-viewport shadow-soft"
+          :infinite-scroll-disabled="loading || noMore"
+          :infinite-scroll-distance="50"
+          :infinite-scroll-immediate="false"
         >
           <div class="timeline-feed">
             <div v-for="(video, index) in dataList" :key="index" class="feed-card-item fade-in">
@@ -58,7 +58,7 @@
             </div>
 
             <div v-if="loading" class="loading-box">
-              <i class="el-icon-loading"></i>
+              <i class="el-icon-loading" />
               <span>内容正在赶来...</span>
             </div>
 
@@ -75,7 +75,7 @@
         <div class="sticky-sidebar">
           <el-card class="hot-card shadow-soft" :body-style="{ padding: '15px' }">
             <div slot="header" class="card-header">
-              <span class="header-title"><i class="el-icon-medal"></i> 社区热议</span>
+              <span class="header-title"><i class="el-icon-medal" /> 社区热议</span>
               <el-button type="text" icon="el-icon-refresh">换一换</el-button>
             </div>
             <div class="topic-list">
@@ -98,7 +98,7 @@
 <script>
 import TextCard from '@/components/card/TextCard'
 import SideVideoCard from '@/components/card/SideVideoCard'
-import { videoTimeline } from '@/api/video'
+import { videoTimeline } from '@/api/vod'
 import { getAuthedUser } from '@/utils/auth'
 
 export default {

+ 1 - 1
src/views/home/VideoPage.vue

@@ -128,7 +128,7 @@
 </template>
 
 <script>
-import { similarVideo, videoInfo, downloadVideo, getShortUrl, videoErrorReport } from '@/api/video'
+import { similarVideo, videoInfo, downloadVideo, getShortUrl, videoErrorReport } from '@/api/vod'
 import { collectItem, createAlbum, getUserAlbumList } from '@/api/collect'
 import { getUserInfo } from '@/api/user'
 import { getAccessToken, getAuthedUser } from '@/utils/auth'

+ 1 - 1
src/views/home/VideoTag.vue

@@ -42,7 +42,7 @@
 
 <script>
 import VideoCard from 'components/card/VideoCard'
-import { getTagVideos } from '@/api/video'
+import { getTagVideos } from '@/api/vod'
 
 export default {
   name: 'VideoTag',

+ 1 - 1
src/views/mall/MyProduct.vue

@@ -163,7 +163,7 @@
 <script>
 import VideoPreviewPlayer from 'components/VideoPreviewPlayer'
 import MyProductPublish from '@/views/mall/MyProductPublish'
-import { updateVideoScope, videoInfo, deleteVideoPost, getVideoPosts } from '@/api/video'
+import { updateVideoScope, videoInfo, deleteVideoPost, getVideoPosts } from '@/api/vod'
 import { getProducts } from '@/api/mall'
 
 export default {

+ 1 - 1
src/views/post/VideoPost.vue

@@ -165,7 +165,7 @@
 <script>
 import VideoPreviewPlayer from 'components/VideoPreviewPlayer'
 import VideoPostPublish from '@/views/post/VideoPostPublish'
-import {updateVideoScope, videoInfo, deleteVideoPost, getVideoPosts, addVideoPost, updateVideoInfo} from '@/api/video'
+import { updateVideoScope, videoInfo, deleteVideoPost, getVideoPosts, addVideoPost, updateVideoInfo } from '@/api/vod'
 
 export default {
   name: 'VideoPost',

+ 1 - 1
src/views/post/VideoPostPublish.vue

@@ -138,7 +138,7 @@
 
 <script>
 import UploaderCard from 'components/card/UploaderCard.vue'
-import { addVideoFile, updateVideoCover, updateVideoFile, videoRegion } from '@/api/video'
+import { addVideoFile, updateVideoCover, updateVideoFile, videoRegion } from '@/api/vod'
 import { getVideoChannelInfo, getVideoCoverChannelInfo } from '@/api/file'
 
 export default {

+ 86 - 86
src/views/user/UserHome.vue

@@ -6,11 +6,11 @@
           <div class="avatar-section">
             <el-avatar :size="isMobile ? 70 : 90" :src="user.avatarUrl" class="user-avatar-main" />
             <el-tag
-                v-if="user.vip"
-                size="mini"
-                effect="dark"
-                type="warning"
-                class="vip-tag-overlay"
+              v-if="user.vip"
+              size="mini"
+              effect="dark"
+              type="warning"
+              class="vip-tag-overlay"
             >
               VIP
             </el-tag>
@@ -18,25 +18,25 @@
 
           <div class="action-section">
             <el-button
-                :type="followButton.text === '已关注' ? 'info' : 'danger'"
-                round
-                :size="isMobile ? 'small' : 'medium'"
-                :icon="followButton.icon"
-                @click="followUser(user.userId)"
-                class="action-btn-item"
+              :type="followButton.text === '已关注' ? 'info' : 'danger'"
+              round
+              :size="isMobile ? 'small' : 'medium'"
+              :icon="followButton.icon"
+              class="action-btn-item"
+              @click="followUser(user.userId)"
             >
               {{ followButton.text }}
             </el-button>
             <el-button
-                icon="el-icon-message"
-                round
-                :size="isMobile ? 'small' : 'medium'"
-                @click="sendMessage(user.userId)"
-                class="action-btn-item"
+              icon="el-icon-message"
+              round
+              :size="isMobile ? 'small' : 'medium'"
+              class="action-btn-item"
+              @click="sendMessage(user.userId)"
             >发消息</el-button>
 
-            <el-dropdown trigger="click" class="more-btn" v-if="!isMobile">
-              <el-button icon="el-icon-more" circle plain size="medium"></el-button>
+            <el-dropdown v-if="!isMobile" trigger="click" class="more-btn">
+              <el-button icon="el-icon-more" circle plain size="medium" />
               <el-dropdown-menu slot="dropdown">
                 <el-dropdown-item v-if="user.biliUserId">
                   <a :href="'https://space.bilibili.com/' + user.biliUserId" target="_blank" class="no-link-style">访问B站空间</a>
@@ -60,7 +60,7 @@
               <span class="stat-value">{{ user.following }}</span>
               <span class="stat-label">关注</span>
             </router-link>
-            <div class="stat-divider"></div>
+            <div class="stat-divider" />
             <router-link :to="`/user/${user.userId}/follower`" class="stat-item">
               <span class="stat-value">{{ user.follower }}</span>
               <span class="stat-label">粉丝</span>
@@ -71,10 +71,10 @@
     </div>
 
     <div class="user-content-section">
-      <el-tabs v-if="userContentData" v-model="activeName" class="custom-tabs" @tab-click="tabClick" :stretch="isMobile">
+      <el-tabs v-if="userContentData" v-model="activeName" class="custom-tabs" :stretch="isMobile" @tab-click="tabClick">
         <el-tab-pane name="video">
           <span slot="label">视频 <span class="tab-count">{{ userContentData.videoCount }}</span></span>
-          <el-row :gutter="isMobile ? 10 : 15" class="grid-container" v-loading="loading">
+          <el-row v-loading="loading" :gutter="isMobile ? 10 : 15" class="grid-container">
             <el-col v-for="(video, index) in dataList" :key="index" :xs="12" :sm="8" :md="6">
               <video-card :video="video" class="content-card-hover" />
             </el-col>
@@ -83,7 +83,7 @@
 
         <el-tab-pane name="image">
           <span slot="label">图片 <span class="tab-count">{{ userContentData.imageCount }}</span></span>
-          <el-row :gutter="isMobile ? 10 : 15" class="grid-container" v-loading="loading">
+          <el-row v-loading="loading" :gutter="isMobile ? 10 : 15" class="grid-container">
             <el-col v-for="(album, index) in dataList" :key="index" :xs="12" :sm="8" :md="6" :lg="4">
               <image-album-card :image-album="album" class="content-card-hover" />
             </el-col>
@@ -92,14 +92,14 @@
 
         <el-tab-pane name="album">
           <span slot="label">收藏夹 <span class="tab-count">{{ userContentData.albumCount }}</span></span>
-          <el-row :gutter="isMobile ? 10 : 15" class="grid-container" v-loading="loading">
+          <el-row v-loading="loading" :gutter="isMobile ? 10 : 15" class="grid-container">
             <el-col v-for="(item, index) in dataList" :key="index" :xs="12" :sm="8" :md="6">
               <el-card :body-style="{ padding: '0px' }" class="playlist-card content-card-hover">
                 <router-link :to="`/playlist/${item.albumId}`" class="no-link-style">
                   <div class="playlist-cover-wrapper">
                     <el-image lazy fit="cover" :src="item.coverUrl" class="playlist-img" />
                     <div class="playlist-mask">
-                      <i class="el-icon-collection"></i> <span>{{ item.total }}</span>
+                      <i class="el-icon-collection" /> <span>{{ item.total }}</span>
                     </div>
                   </div>
                   <div class="playlist-info">
@@ -114,15 +114,15 @@
 
       <el-empty v-if="showEmpty && !loading" :image-size="isMobile ? 120 : 200" description="该分类下空空如也" />
 
-      <div class="pagination-wrapper" v-if="totalSize > pageSize">
+      <div v-if="totalSize > pageSize" class="pagination-wrapper">
         <el-pagination
-            background
-            :layout="isMobile ? 'prev, pager, next' : 'prev, pager, next'"
-            :current-page="currentPage"
-            :page-size="pageSize"
-            :total="totalSize"
-            :pager-count="isMobile ? 5 : 7"
-            @current-change="handleCurrentChange"
+          background
+          :layout="isMobile ? 'prev, pager, next' : 'prev, pager, next'"
+          :current-page="currentPage"
+          :page-size="pageSize"
+          :total="totalSize"
+          :pager-count="isMobile ? 5 : 7"
+          @current-change="handleCurrentChange"
         />
       </div>
     </div>
@@ -133,7 +133,7 @@
 import VideoCard from '@/components/card/VideoCard'
 import ImageAlbumCard from '@/components/card/ImageAlbumCard'
 import { getUserInfo, checkRelation, followUser, unfollowUser } from '@/api/user'
-import { getUserContentData, getUserVideos } from '@/api/video'
+import { getUserContentData, getUserVideos } from '@/api/vod'
 import { getAlbumImage1 } from '@/api/image'
 import { getUserPlaylist } from '@/api/collect'
 
@@ -160,41 +160,41 @@ export default {
     '$route.params.id': {
       handler(newId) {
         if (newId) {
-          this.userId = newId;
-          this.resetPageState();
-          this.initUserContext();
+          this.userId = newId
+          this.resetPageState()
+          this.initUserContext()
         }
       },
       immediate: true
     },
     '$route.query': {
       handler() {
-        this.loadDataFromRoute();
+        this.loadDataFromRoute()
       },
       deep: true
     }
   },
   created() {
-    this.userId = this.$route.params.id;
-    this.initUserContext();
-    this.checkMobile();
+    this.userId = this.$route.params.id
+    this.initUserContext()
+    this.checkMobile()
   },
   mounted() {
-    window.addEventListener('resize', this.checkMobile);
+    window.addEventListener('resize', this.checkMobile)
   },
   destroyed() {
-    window.removeEventListener('resize', this.checkMobile);
+    window.removeEventListener('resize', this.checkMobile)
   },
   methods: {
     checkMobile() {
-      this.isMobile = window.innerWidth <= 768;
+      this.isMobile = window.innerWidth <= 768
     },
     resetPageState() {
-      this.dataList = [];
-      this.currentPage = 1;
-      this.activeName = 'video';
-      this.user = null;
-      this.userContentData = null;
+      this.dataList = []
+      this.currentPage = 1
+      this.activeName = 'video'
+      this.user = null
+      this.userContentData = null
     },
     initUserContext() {
       Promise.all([
@@ -203,78 +203,78 @@ export default {
         getUserContentData(this.userId)
       ]).then(([info, relation, content]) => {
         if (info.code === 0) {
-          this.user = info.data;
-          this.loadDataFromRoute();
+          this.user = info.data
+          this.loadDataFromRoute()
         }
         if (relation.code === 0 && relation.data) {
-          this.followButton = { icon: 'el-icon-check', text: '已关注' };
+          this.followButton = { icon: 'el-icon-check', text: '已关注' }
         }
         if (content.code === 0) {
-          this.userContentData = content.data;
+          this.userContentData = content.data
         }
-      });
+      })
     },
     loadDataFromRoute() {
-      const query = this.$route.query;
-      this.activeName = query.tab || 'video';
-      this.currentPage = query.page ? parseInt(query.page) : 1;
-      this.updatePageTitle();
-      this.fetchContentList();
+      const query = this.$route.query
+      this.activeName = query.tab || 'video'
+      this.currentPage = query.page ? parseInt(query.page) : 1
+      this.updatePageTitle()
+      this.fetchContentList()
     },
     syncStateToURL() {
       this.$router.push({
         path: this.$route.path,
         query: { tab: this.activeName, page: this.currentPage }
       }).catch(err => {
-        if (err.name !== 'NavigationDuplicated') throw err;
-      });
+        if (err.name !== 'NavigationDuplicated') throw err
+      })
     },
     fetchContentList() {
-      this.loading = true;
-      this.dataList = [];
-      this.showEmpty = false;
-      let apiCall;
+      this.loading = true
+      this.dataList = []
+      this.showEmpty = false
+      let apiCall
       if (this.activeName === 'video') {
-        apiCall = getUserVideos(this.userId, this.currentPage);
+        apiCall = getUserVideos(this.userId, this.currentPage)
       } else if (this.activeName === 'image') {
-        apiCall = getAlbumImage1(this.currentPage, this.userId);
+        apiCall = getAlbumImage1(this.currentPage, this.userId)
       } else if (this.activeName === 'album') {
-        apiCall = getUserPlaylist({ userId: this.userId, pn: this.currentPage });
+        apiCall = getUserPlaylist({ userId: this.userId, pn: this.currentPage })
       }
       apiCall.then(resp => {
         if (resp.code === 0) {
-          this.dataList = resp.data.list;
-          this.totalSize = resp.data.totalSize;
-          this.showEmpty = this.dataList.length === 0;
+          this.dataList = resp.data.list
+          this.totalSize = resp.data.totalSize
+          this.showEmpty = this.dataList.length === 0
         }
-      }).finally(() => { this.loading = false; });
+      }).finally(() => { this.loading = false })
     },
     tabClick(tab) {
-      this.activeName = tab.name;
-      this.currentPage = 1;
-      this.syncStateToURL();
+      this.activeName = tab.name
+      this.currentPage = 1
+      this.syncStateToURL()
     },
     handleCurrentChange(page) {
-      this.currentPage = page;
-      this.syncStateToURL();
-      window.scrollTo({ top: 0, behavior: 'smooth' });
+      this.currentPage = page
+      this.syncStateToURL()
+      window.scrollTo({ top: 0, behavior: 'smooth' })
     },
     updatePageTitle() {
-      if (!this.user) return;
-      const suffix = { video: '的视频', image: '的图片', album: '的收藏夹' };
-      document.title = `${this.user.screenName}${suffix[this.activeName] || '的主页'}`;
+      if (!this.user) return
+      const suffix = { video: '的视频', image: '的图片', album: '的收藏夹' }
+      document.title = `${this.user.screenName}${suffix[this.activeName] || '的主页'}`
     },
     followUser(userId) {
-      const isFollowing = this.followButton.text === '已关注';
-      const api = isFollowing ? unfollowUser : followUser;
+      const isFollowing = this.followButton.text === '已关注'
+      const api = isFollowing ? unfollowUser : followUser
       api(userId).then(resp => {
         if (resp.code === 0) {
-          this.followButton = isFollowing ? { icon: 'el-icon-plus', text: '关注' } : { icon: 'el-icon-check', text: '已关注' };
-          this.$message.success(isFollowing ? '已取消关注' : '关注成功');
+          this.followButton = isFollowing ? { icon: 'el-icon-plus', text: '关注' } : { icon: 'el-icon-check', text: '已关注' }
+          this.$message.success(isFollowing ? '已取消关注' : '关注成功')
         }
-      });
+      })
     },
-    sendMessage() { this.$message.info('私信功能暂未开放'); }
+    sendMessage() { this.$message.info('私信功能暂未开放') }
   }
 }
 </script>