This commit is contained in:
parent
46419e68bc
commit
9bc552cfb8
|
@ -11,33 +11,57 @@
|
||||||
|
|
||||||
<!-- 顶部搜索栏 -->
|
<!-- 顶部搜索栏 -->
|
||||||
<div class="search-bar">
|
<div class="search-bar">
|
||||||
<el-input
|
<div class="search-wrapper">
|
||||||
v-model="searchQuery"
|
<div class="search-box">
|
||||||
placeholder="搜索视频..."
|
<el-input
|
||||||
class="search-input"
|
v-model="searchQuery"
|
||||||
clearable
|
placeholder="搜索视频..."
|
||||||
|
class="search-input"
|
||||||
|
clearable
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<el-icon class="search-icon"><Search /></el-icon>
|
||||||
|
</template>
|
||||||
|
<template #append>
|
||||||
|
<el-button type="primary" :icon="Search" @click="handleSearch">
|
||||||
|
<span class="button-text">搜索</span>
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="search-tags">
|
||||||
|
<div class="hot-searches">
|
||||||
|
<div class="hot-label">
|
||||||
|
<el-icon><Histogram /></el-icon>
|
||||||
|
<span>热门搜索</span>
|
||||||
|
</div>
|
||||||
|
<el-tag
|
||||||
|
v-for="tag in hotSearches"
|
||||||
|
:key="tag"
|
||||||
|
size="small"
|
||||||
|
effect="plain"
|
||||||
|
class="hot-tag"
|
||||||
|
@click="searchQuery = tag"
|
||||||
|
>
|
||||||
|
<el-icon><Search /></el-icon>
|
||||||
|
{{ tag }}
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 分类导航 -->
|
||||||
|
<div class="category-nav">
|
||||||
|
<div class="nav-item"
|
||||||
|
v-for="item in videoCategories"
|
||||||
|
:key="item.value"
|
||||||
|
:class="{ active: currentCategory === item.value }"
|
||||||
|
@click="currentCategory = item.value"
|
||||||
>
|
>
|
||||||
<template #prefix>
|
<el-icon><component :is="item.icon" /></el-icon>
|
||||||
<el-icon class="search-icon"><Search /></el-icon>
|
<span>{{ item.label }}</span>
|
||||||
</template>
|
|
||||||
<template #append>
|
|
||||||
<el-button type="primary" :icon="Search" @click="handleSearch">
|
|
||||||
<span class="button-text">搜索</span>
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-input>
|
|
||||||
<div class="hot-searches">
|
|
||||||
<span class="label">热门搜索:</span>
|
|
||||||
<el-tag
|
|
||||||
v-for="tag in hotSearches"
|
|
||||||
:key="tag"
|
|
||||||
size="small"
|
|
||||||
effect="plain"
|
|
||||||
class="hot-tag"
|
|
||||||
@click="searchQuery = tag"
|
|
||||||
>
|
|
||||||
{{ tag }}
|
|
||||||
</el-tag>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -123,13 +147,24 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="video-grid">
|
<div class="video-grid">
|
||||||
<video-card
|
<template v-if="isLoading">
|
||||||
v-for="video in displayVideos"
|
<div v-for="n in 8" :key="n" class="video-card loading-skeleton">
|
||||||
:key="video.id"
|
<div class="thumbnail-wrapper"></div>
|
||||||
:video="video"
|
<div class="video-info">
|
||||||
@click="playVideo(video)"
|
<div class="title-skeleton"></div>
|
||||||
@favorite="toggleFavorite"
|
<div class="meta-skeleton"></div>
|
||||||
/>
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<video-card
|
||||||
|
v-for="video in displayVideos"
|
||||||
|
:key="video.id"
|
||||||
|
:video="video"
|
||||||
|
@click="playVideo(video)"
|
||||||
|
@favorite="toggleFavorite"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 分页器 -->
|
<!-- 分页器 -->
|
||||||
|
@ -147,7 +182,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
import { Search, Star, VideoPlay, Calendar, Histogram as Fire, Collection, Timer, VideoCamera, Trophy } from '@element-plus/icons-vue'
|
import { Search, Star, VideoPlay, Calendar, Histogram as Fire, Collection, Timer, VideoCamera, Trophy } from '@element-plus/icons-vue'
|
||||||
import VideoCard from '@/components/video/VideoCard.vue'
|
import VideoCard from '@/components/video/VideoCard.vue'
|
||||||
import VideoGrid from '@/components/video/VideoGrid.vue'
|
import VideoGrid from '@/components/video/VideoGrid.vue'
|
||||||
|
@ -159,6 +194,7 @@ const activeCategory = ref('recommended')
|
||||||
const sortBy = ref('latest')
|
const sortBy = ref('latest')
|
||||||
const currentPage = ref(1)
|
const currentPage = ref(1)
|
||||||
const pageSize = ref(12)
|
const pageSize = ref(12)
|
||||||
|
const isLoading = ref(false)
|
||||||
|
|
||||||
// 模拟数据
|
// 模拟数据
|
||||||
const featuredVideos = ref([
|
const featuredVideos = ref([
|
||||||
|
@ -389,10 +425,16 @@ const handleSearch = () => {
|
||||||
// TODO: 实现搜索逻辑
|
// TODO: 实现搜索逻辑
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleCategoryChange = (category: string) => {
|
const handleCategoryChange = async (category: string) => {
|
||||||
activeCategory.value = category
|
activeCategory.value = category
|
||||||
currentPage.value = 1
|
currentPage.value = 1
|
||||||
// TODO: 加载对应分类的视频
|
isLoading.value = true
|
||||||
|
try {
|
||||||
|
// TODO: 加载对应分类的视频
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSort = () => {
|
const handleSort = () => {
|
||||||
|
@ -446,6 +488,40 @@ const hotSearches = [
|
||||||
'心理健康',
|
'心理健康',
|
||||||
'智能设备'
|
'智能设备'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// 视频分类
|
||||||
|
const videoCategories = [
|
||||||
|
{ label: '全部', value: 'all', icon: 'Grid' },
|
||||||
|
{ label: '健康养生', value: 'health', icon: 'FirstAid' },
|
||||||
|
{ label: '运动健身', value: 'fitness', icon: 'Position' },
|
||||||
|
{ label: '营养饮食', value: 'diet', icon: 'Bowl' },
|
||||||
|
{ label: '心理健康', value: 'mental', icon: 'SwitchButton' },
|
||||||
|
{ label: '医疗保健', value: 'medical', icon: 'Stethoscope' },
|
||||||
|
{ label: '智能设备', value: 'device', icon: 'Monitor' },
|
||||||
|
{ label: '生活技巧', value: 'life', icon: 'House' },
|
||||||
|
{ label: '文化娱乐', value: 'entertainment', icon: 'Film' },
|
||||||
|
{ label: '旅游出行', value: 'travel', icon: 'Van' }
|
||||||
|
]
|
||||||
|
|
||||||
|
const currentCategory = ref('all')
|
||||||
|
|
||||||
|
// 检测是否需要显示滚动阴影
|
||||||
|
const hasScroll = ref(false)
|
||||||
|
|
||||||
|
// 监听滚动容器宽度变化
|
||||||
|
onMounted(() => {
|
||||||
|
const observer = new ResizeObserver(entries => {
|
||||||
|
for (const entry of entries) {
|
||||||
|
const { scrollWidth, clientWidth } = entry.target as HTMLElement
|
||||||
|
hasScroll.value = scrollWidth > clientWidth
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const scrollContainer = document.querySelector('.tags-container')
|
||||||
|
if (scrollContainer) {
|
||||||
|
observer.observe(scrollContainer)
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
@ -487,60 +563,214 @@ const hotSearches = [
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-bar {
|
.search-bar {
|
||||||
margin-bottom: 30px;
|
margin-bottom: 24px;
|
||||||
display: flex;
|
background: #fff;
|
||||||
flex-direction: column;
|
border-radius: 12px;
|
||||||
align-items: center;
|
padding: 24px;
|
||||||
gap: 12px;
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.hot-searches {
|
.search-wrapper {
|
||||||
display: flex;
|
max-width: 800px;
|
||||||
align-items: center;
|
margin: 0 auto;
|
||||||
gap: 8px;
|
}
|
||||||
|
|
||||||
.label {
|
.search-box {
|
||||||
color: #909399;
|
margin-bottom: 20px;
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hot-tag {
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #409EFF;
|
|
||||||
border-color: #409EFF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-input {
|
.search-input {
|
||||||
width: 100%;
|
|
||||||
max-width: 600px;
|
|
||||||
|
|
||||||
:deep(.el-input__wrapper) {
|
:deep(.el-input__wrapper) {
|
||||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
|
||||||
border-radius: 8px;
|
|
||||||
padding-left: 16px;
|
padding-left: 16px;
|
||||||
|
background: #f5f7fa;
|
||||||
|
box-shadow: none !important;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #ecf5ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-focus {
|
||||||
|
background: #fff;
|
||||||
|
border-color: #409EFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-input__inner) {
|
||||||
height: 44px;
|
height: 44px;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.search-icon) {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #909399;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.el-input-group__append) {
|
:deep(.el-input-group__append) {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
.el-button {
|
.el-button {
|
||||||
border-radius: 0 8px 8px 0;
|
|
||||||
padding: 0 24px;
|
|
||||||
height: 44px;
|
height: 44px;
|
||||||
|
padding: 0 24px;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
|
border-radius: 0 4px 4px 0;
|
||||||
|
|
||||||
|
.el-icon {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
.button-text {
|
.button-text {
|
||||||
margin-left: 4px;
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.search-tags {
|
||||||
|
position: relative;
|
||||||
|
padding-left: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hot-searches {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
.hot-label {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
color: #606266;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
.el-icon {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #f56c6c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hot-tag {
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0 12px;
|
||||||
|
height: 28px;
|
||||||
|
border-radius: 14px;
|
||||||
|
border-color: #e4e7ed;
|
||||||
|
transition: all 0.3s;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
.el-icon {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #409EFF;
|
||||||
|
border-color: #409EFF;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.1);
|
||||||
|
|
||||||
|
.el-icon {
|
||||||
|
color: #409EFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.search-tags {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hot-searches {
|
||||||
|
.hot-label {
|
||||||
|
position: static;
|
||||||
|
transform: none;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-nav {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
||||||
|
margin-bottom: 24px;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
.el-icon {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #606266;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #f5f7fa;
|
||||||
|
|
||||||
|
.el-icon {
|
||||||
|
color: #409EFF;
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: #409EFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: #ecf5ff;
|
||||||
|
|
||||||
|
.el-icon {
|
||||||
|
color: #409EFF;
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: #409EFF;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1200px) {
|
||||||
|
.category-nav {
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||||||
|
gap: 8px;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item {
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.category-tags {
|
.category-tags {
|
||||||
.custom-tabs {
|
.custom-tabs {
|
||||||
:deep(.el-tabs__item) {
|
:deep(.el-tabs__item) {
|
||||||
|
@ -677,4 +907,20 @@ const hotSearches = [
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 添加加载动画 */
|
||||||
|
.loading-skeleton {
|
||||||
|
background: linear-gradient(90deg, #f2f2f2 25%, #e6e6e6 50%, #f2f2f2 75%);
|
||||||
|
background-size: 200% 100%;
|
||||||
|
animation: loading 1.5s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes loading {
|
||||||
|
0% {
|
||||||
|
background-position: 200% 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position: -200% 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
Loading…
Reference in New Issue