|
|
@@ -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>
|