refactor(project): Reorganize frontend and backend directories
This commit is contained in:
@@ -0,0 +1,205 @@
|
||||
<template>
|
||||
<div class="home-page">
|
||||
<!-- 顶部导航 -->
|
||||
<van-nav-bar title="HAOVP" fixed placeholder>
|
||||
<template #right>
|
||||
<van-icon name="search" size="22" @click="router.push('/search')" />
|
||||
</template>
|
||||
</van-nav-bar>
|
||||
|
||||
<!-- 分类导航 -->
|
||||
<div class="category-nav">
|
||||
<van-tabs v-model:active="activeCategory" sticky shrink @change="onCategoryChange">
|
||||
<van-tab title="推荐" name="recommend" />
|
||||
<van-tab title="热门" name="hot" />
|
||||
<van-tab v-for="cat in categories" :key="cat.id" :title="cat.name" :name="cat.id" />
|
||||
</van-tabs>
|
||||
</div>
|
||||
|
||||
<!-- 视频列表 -->
|
||||
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
|
||||
<van-list
|
||||
v-model:loading="loading"
|
||||
:finished="finished"
|
||||
finished-text="没有更多了"
|
||||
@load="loadMore"
|
||||
class="video-list"
|
||||
>
|
||||
<div class="video-grid">
|
||||
<VideoCard
|
||||
v-for="video in videos"
|
||||
:key="video.id"
|
||||
:video="video"
|
||||
@click="goToVideo(video.id)"
|
||||
/>
|
||||
</div>
|
||||
</van-list>
|
||||
</van-pull-refresh>
|
||||
|
||||
<!-- 底部导航 -->
|
||||
<AppTabbar />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { showToast } from 'vant'
|
||||
import VideoCard from '@/components/video/VideoCard.vue'
|
||||
import AppTabbar from '@/components/layout/AppTabbar.vue'
|
||||
import { videoApi } from '@/api/video.ts'
|
||||
import { categoryApi, type Category } from '@/api/category.ts'
|
||||
import { recommendApi } from '@/api/recommend.ts'
|
||||
import type { VideoInfo } from '@/types'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
// 分类
|
||||
const categories = ref<Category[]>([])
|
||||
const activeCategory = ref<string | number>('recommend')
|
||||
|
||||
// 视频列表
|
||||
const videos = ref<VideoInfo[]>([])
|
||||
const loading = ref(false)
|
||||
const finished = ref(false)
|
||||
const refreshing = ref(false)
|
||||
const currentPage = ref(1)
|
||||
const pageSize = 20
|
||||
|
||||
// 加载分类
|
||||
const loadCategories = async () => {
|
||||
try {
|
||||
const res = await categoryApi.getList()
|
||||
categories.value = res
|
||||
} catch (error) {
|
||||
console.error('加载分类失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 加载视频列表
|
||||
const loadVideos = async (isRefresh = false) => {
|
||||
if (isRefresh) {
|
||||
currentPage.value = 1
|
||||
videos.value = []
|
||||
finished.value = false
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
let res
|
||||
const params = {
|
||||
page: currentPage.value,
|
||||
size: pageSize,
|
||||
...(typeof activeCategory.value === 'number' && { categoryId: activeCategory.value })
|
||||
}
|
||||
|
||||
if (activeCategory.value === 'recommend') {
|
||||
res = await recommendApi.getHome(params)
|
||||
} else if (activeCategory.value === 'hot') {
|
||||
res = await recommendApi.getHot(params)
|
||||
} else {
|
||||
res = await videoApi.getList(params)
|
||||
}
|
||||
|
||||
if (isRefresh) {
|
||||
videos.value = res.records
|
||||
} else {
|
||||
videos.value.push(...res.records)
|
||||
}
|
||||
|
||||
// 判断是否还有更多
|
||||
if (videos.value.length >= res.total || res.records.length < pageSize) {
|
||||
finished.value = true
|
||||
}
|
||||
|
||||
currentPage.value++
|
||||
} catch (error: any) {
|
||||
showToast(error.message || '加载失败')
|
||||
finished.value = true
|
||||
} finally {
|
||||
loading.value = false
|
||||
refreshing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 加载更多
|
||||
const loadMore = () => {
|
||||
if (!finished.value) {
|
||||
loadVideos()
|
||||
}
|
||||
}
|
||||
|
||||
// 下拉刷新
|
||||
const onRefresh = () => {
|
||||
loadVideos(true)
|
||||
}
|
||||
|
||||
// 分类切换
|
||||
const onCategoryChange = () => {
|
||||
loadVideos(true)
|
||||
}
|
||||
|
||||
// 跳转视频详情
|
||||
const goToVideo = (id: number) => {
|
||||
router.push(`/video/${id}`)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadCategories()
|
||||
loadVideos()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.home-page {
|
||||
min-height: 100vh;
|
||||
background: #f5f5f5;
|
||||
padding-bottom: 60px;
|
||||
|
||||
@media (min-width: 768px) {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.category-nav {
|
||||
background: #fff;
|
||||
position: sticky;
|
||||
top: 46px;
|
||||
z-index: 100;
|
||||
|
||||
:deep(.van-tabs__nav) {
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
:deep(.van-tab) {
|
||||
padding: 0 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
:deep(.van-tabs__line) {
|
||||
background: #00a1d6;
|
||||
}
|
||||
}
|
||||
|
||||
.video-list {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.video-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 12px;
|
||||
|
||||
@media (min-width: 768px) {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
}
|
||||
|
||||
@media (min-width: 1600px) {
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user