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