Преглед изворни кода

使用 gemini 优化视频分区页面 Region.vue

reghao пре 18 часа
родитељ
комит
0cc51d5bbd
4 измењених фајлова са 212 додато и 206 уклоњено
  1. 2 2
      src/components/layout/NavBar.vue
  2. 4 4
      src/router/index.js
  3. 206 0
      src/views/home/Region.vue
  4. 0 200
      src/views/home/Video.vue

+ 2 - 2
src/components/layout/NavBar.vue

@@ -11,7 +11,7 @@
 
         <el-col :sm="6" :md="7" class="hidden-xs-only">
           <div class="nav-links">
-            <router-link to="/video" class="nav-item-link">分区</router-link>
+            <router-link to="/region" class="nav-item-link">分区</router-link>
             <router-link to="/shortvideo" class="nav-item-link">短视频</router-link>
             <router-link to="/playlist" class="nav-item-link">播放列表</router-link>
           </div>
@@ -82,7 +82,7 @@
         <i class="el-icon-house"></i>
         <span>首页</span>
       </router-link>
-      <router-link to="/video" class="bottom-nav-item" active-class="is-active">
+      <router-link to="/region" class="bottom-nav-item" active-class="is-active">
         <i class="el-icon-menu"></i>
         <span>分区</span>
       </router-link>

+ 4 - 4
src/router/index.js

@@ -21,7 +21,7 @@ const ShareVideo = () => import('views/home/ShareVideo')
 
 const Home = () => import('views/home/Home')
 const TimelineIndex = () => import('views/home/Timeline')
-const VideoIndex = () => import('views/home/Video')
+const RegionIndex = () => import('views/home/Region')
 const VideoTag = () => import('views/home/VideoTag')
 const ShortVideoIndex = () => import('views/home/ShortVideo')
 const VideoPage = () => import('views/home/VideoPage')
@@ -71,9 +71,9 @@ export const constantRoutes = [
         meta: { needAuth: false }
       },
       {
-        path: '/video',
-        name: 'VideoIndex',
-        component: VideoIndex,
+        path: '/region',
+        name: 'RegionIndex',
+        component: RegionIndex,
         meta: { needAuth: false }
       },
       {

+ 206 - 0
src/views/home/Region.vue

@@ -0,0 +1,206 @@
+<template>
+  <div class="video-page-container">
+    <el-row :gutter="20" class="main-layout">
+      <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>
+            <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"
+            >
+              <span slot-scope="{ node, data }" class="tree-node-item">
+                <i :class="data.icon || 'el-icon-folder'"></i>
+                <span class="node-label">{{ node.label }}</span>
+              </span>
+            </el-tree>
+          </div>
+        </el-card>
+      </el-col>
+
+      <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-col
+                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>
+
+          <div class="pagination-container" v-if="totalSize > pageSize">
+            <el-pagination
+                background
+                :small="screenWidth <= 768"
+                layout="prev, pager, next, total"
+                :page-size="pageSize"
+                :current-page="currentPage"
+                :total="totalSize"
+                @current-change="handleCurrentChange"
+            />
+          </div>
+        </div>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import VideoCard from 'components/card/VideoCard'
+import { categoryVideos, videoCategories } from '@/api/video'
+
+export default {
+  name: 'Region',
+  components: { VideoCard },
+  data() {
+    return {
+      screenWidth: document.body.clientWidth,
+      currentPage: 1,
+      pageSize: 12,
+      totalSize: 0,
+      dataList: [],
+      categoryId: 1, // 默认分区 ID
+      treeNode: [],
+      defaultProps: {
+        children: 'children',
+        label: 'label'
+      },
+      loading: false
+    }
+  },
+  watch: {
+    // 核心:监听路由变化,当 URL 参数改变时重新加载数据
+    '$route': {
+      handler: 'loadDataFromRoute',
+      immediate: false
+    }
+  },
+  created() {
+    document.title = '视频分区 - 探索精彩'
+    this.fetchCategories()
+    // 初始化:从 URL 中解析参数并加载数据
+    this.loadDataFromRoute()
+  },
+  mounted() {
+    window.addEventListener('resize', this.handleResize)
+  },
+  beforeDestroy() {
+    window.removeEventListener('resize', this.handleResize)
+  },
+  methods: {
+    handleResize() {
+      this.screenWidth = document.body.clientWidth
+    },
+    // 1. 从路由 Query 参数中提取状态
+    loadDataFromRoute() {
+      const query = this.$route.query
+      // 如果 url 有参数则取参数,否则用 data 里的默认值
+      this.categoryId = query.category ? parseInt(query.category) : 1
+      this.currentPage = query.page ? parseInt(query.page) : 1
+
+      // 同步 Tree 组件的高亮状态
+      this.$nextTick(() => {
+        if (this.$refs.categoryTree) {
+          this.$refs.categoryTree.setCurrentKey(this.categoryId)
+        }
+      })
+
+      this.videoPageWrapper(this.categoryId, this.currentPage)
+    },
+    // 2. 更新 URL 的方法(不直接请求接口,交由 watch $route 处理)
+    updateRoute() {
+      this.$router.push({
+        path: this.$route.path,
+        query: {
+          category: this.categoryId,
+          page: this.currentPage
+        }
+      }).catch(err => {
+        // 规避掉重复点击同一路由产生的 NavigationDuplicated 报错
+        if (err.name !== 'NavigationDuplicated') throw err
+      })
+    },
+    fetchCategories() {
+      videoCategories().then(resp => {
+        if (resp.code === 0) {
+          this.treeNode = resp.data
+          // 获取分类后再次确认 Tree 的选中状态
+          this.$nextTick(() => {
+            this.$refs.categoryTree.setCurrentKey(this.categoryId)
+          })
+        }
+      })
+    },
+    handleNodeClick(data) {
+      if (this.categoryId === data.value) return
+      this.categoryId = data.value
+      this.currentPage = 1 // 切换分类时重置为第一页
+      this.updateRoute()
+    },
+    handleCurrentChange(page) {
+      this.currentPage = page
+      this.updateRoute()
+      window.scrollTo({ top: 0, behavior: 'smooth' })
+    },
+    videoPageWrapper(categoryId, currentPage) {
+      this.loading = true
+      categoryVideos(categoryId, currentPage).then(resp => {
+        if (resp.code === 0) {
+          this.dataList = resp.data.list
+          this.totalSize = resp.data.totalSize
+        }
+      }).finally(() => {
+        this.loading = false
+      })
+    }
+  }
+}
+</script>
+
+<style scoped>
+/* 样式部分保持之前的优化版本不变 */
+.video-page-container {
+  padding: 25px 5%;
+  background-color: #f9fafb;
+  min-height: calc(100vh - 60px);
+}
+.shadow-sm {
+  box-shadow: 0 1px 3px rgba(0,0,0,0.1) !important;
+  border: none !important;
+  border-radius: 12px;
+}
+.category-header {
+  font-weight: bold;
+  font-size: 16px;
+  color: #303133;
+  display: flex;
+  align-items: center;
+}
+.category-header i { margin-right: 8px; color: #409EFF; }
+.tree-node-item { display: flex; align-items: center; font-size: 14px; }
+.tree-node-item i { margin-right: 10px; font-size: 16px; color: #909399; }
+::v-deep .el-tree-node__content { height: 40px !important; margin: 4px 12px; border-radius: 8px; }
+::v-deep .el-tree-node.is-current > .el-tree-node__content { background-color: #ecf5ff !important; color: #409EFF; font-weight: 600; }
+.video-item { margin-bottom: 20px; }
+.hover-up { transition: transform 0.3s; }
+.hover-up:hover { transform: translateY(-5px); }
+.pagination-container { margin-top: 40px; display: flex; justify-content: center; }
+</style>

+ 0 - 200
src/views/home/Video.vue

@@ -1,200 +0,0 @@
-<template>
-  <div>
-    <!--电影列表,与推荐列表-->
-    <el-row id="movie-list">
-      <el-col :md="4">
-        <el-card class="box-card" :body-style="{ paddingTop: '0px' }">
-          <div slot="header" class="clearfix">
-            <span>视频分区</span>
-          </div>
-          <div class="item">
-            <el-tree
-              :accordion="true"
-              :data="treeNode"
-              :props="defaultProps"
-              highlight-current
-              @node-click="handleNodeClick"
-            >
-              <span slot-scope="{ node, data }">
-                <span :class="data.icon">{{ node.label }}</span>
-              </span>
-            </el-tree>
-          </div>
-        </el-card>
-      </el-col>
-      <!--电影列表-->
-      <el-col v-loading="loading" :md="20">
-        <el-col v-for="(video, index) in dataList" :key="index" :md="6" :sm="12" :xs="12">
-          <video-card :video="video" />
-        </el-col>
-        <!--        <el-col v-if="totalSize === 0" class="not-result" :md="6" :sm="12" :xs="12">
-          <img src="@/assets/img/not-result.png">
-          <div>没有视频数据</div>
-        </el-col>-->
-        <!--
-          分页按钮:
-          page-size:每页显示条数
-          total:总条数
-          hide-on-single-page: 页数为一时隐藏
-        -->
-        <el-col :span="24" class="pagination">
-          <el-pagination
-            :small="screenWidth <= 768"
-            layout="prev, pager, next"
-            :page-size="pageSize"
-            :current-page="currentPage"
-            :total="totalSize"
-            @current-change="handleCurrentChange"
-          />
-        </el-col>
-      </el-col>
-    </el-row>
-  </div>
-</template>
-
-<script>
-import VideoCard from 'components/card/VideoCard'
-import { categoryVideos, videoCategories } from '@/api/video'
-
-export default {
-  name: 'Video',
-  components: { VideoCard },
-  data() {
-    return {
-      // 屏幕宽度, 为了控制分页条的大小
-      screenWidth: document.body.clientWidth,
-      currentPage: 1,
-      pageSize: 12,
-      totalSize: 0,
-      dataList: [],
-      categoryId: 1,
-      treeNode: [],
-      defaultProps: {
-        children: 'children',
-        label: 'label',
-        value: 'value'
-      },
-      loading: false
-    }
-  },
-  created() {
-    document.title = '视频主页'
-
-    videoCategories().then(resp => {
-      if (resp.code === 0) {
-        this.treeNode = resp.data
-      } else {
-        this.$notify({
-          title: '提示',
-          message: resp.msg,
-          type: 'error',
-          duration: 3000
-        })
-      }
-    }).catch(error => {
-      this.$notify({
-        title: '提示',
-        message: error.message,
-        type: 'warning',
-        duration: 3000
-      })
-    })
-    this.videoPageWrapper(this.categoryId, this.currentPage)
-  },
-  mounted() {
-    // 当窗口宽度改变时获取屏幕宽度
-    window.onresize = () => {
-      return () => {
-        window.screenWidth = document.body.clientWidth
-        this.screenWidth = window.screenWidth
-      }
-    }
-  },
-  methods: {
-    handleNodeClick(data) {
-      this.currentPage = 1
-      this.dataList = []
-
-      this.categoryId = data.value
-      this.videoPageWrapper(this.categoryId, this.currentPage)
-    },
-    handleCurrentChange(currentPage) {
-      this.currentPage = currentPage
-      this.videoPageWrapper(this.categoryId, this.currentPage)
-      // 回到顶部
-      scrollTo(0, 0)
-    },
-    videoPageWrapper(categoryId, currentPage) {
-      /* const loading = this.$loading({
-        lock: true,
-        text: 'Loading',
-        spinner: 'el-icon-loading',
-        background: 'rgba(0, 0, 0, 0.7)'
-      })*/
-
-      this.loading = true
-      categoryVideos(categoryId, currentPage).then(resp => {
-        // loading.close()
-        this.loading = false
-        if (resp.code === 0) {
-          const respData = resp.data
-          this.dataList = respData.list
-          this.totalSize = respData.totalSize
-        } else {
-          this.$notify({
-            title: '提示',
-            message: resp.msg,
-            type: 'error',
-            duration: 3000
-          })
-        }
-      }).catch(error => {
-        // loading.close()
-        this.$notify({
-          title: '提示',
-          message: error.message,
-          type: 'warning',
-          duration: 3000
-        })
-      }).finally(() => {
-        this.loading = false
-      })
-    }
-  }
-}
-</script>
-
-<style scoped>
-/*处于手机屏幕时*/
-@media screen and (max-width: 768px){
-  #movie-list {
-    padding-top: 8px;
-    padding-left: 0.5%;
-    padding-right: 0.5%;
-  }
-
-  .category-btn {
-    padding-left: 0.5%;
-    padding-right: 0.5%;
-    padding-top: 3%;
-    text-align: center;
-  }
-}
-
-#movie-list {
-  padding-top: 15px;
-  padding-left: 6%;
-  padding-right: 6%;
-}
-
-.not-result {
-  padding-top: 100px;
-  padding-bottom: 100px;
-  text-align: center;
-}
-
-.pagination {
-  text-align: center;
-  padding: 10px;
-}
-</style>