Files
urldb/web/pages/hot-dramas.vue
2025-07-21 15:27:58 +08:00

349 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="min-h-screen bg-gray-50 dark:bg-gray-900 text-gray-800 dark:text-gray-100 flex flex-col">
<!-- 主要内容区域 -->
<div class="flex-1 p-3 sm:p-5">
<div class="max-w-7xl mx-auto">
<!-- 头部 -->
<div class="bg-slate-800 dark:bg-gray-800 text-white dark:text-gray-100 rounded-lg shadow-lg p-4 sm:p-8 mb-4 sm:mb-8 text-center relative">
<h1 class="text-2xl sm:text-3xl font-bold mb-4">
<a href="/" class="text-white hover:text-gray-200 dark:hover:text-gray-300 no-underline">
热播剧榜单
</a>
</h1>
<p class="text-gray-300 max-w-2xl mx-auto">实时获取豆瓣热门电影和电视剧榜单</p>
<nav class="mt-4 flex flex-col sm:flex-row justify-center gap-2 sm:gap-2 right-4 top-0 absolute">
<NuxtLink to="/" class="hidden sm:flex">
<n-button size="tiny" type="tertiary" round ghost class="!px-2 !py-1 !text-xs !text-white dark:!text-white !border-white/30 hover:!border-white">
<i class="fas fa-home text-xs"></i> 首页
</n-button>
</NuxtLink>
<NuxtLink to="/monitor" class="hidden sm:flex">
<n-button size="tiny" type="tertiary" round ghost class="!px-2 !py-1 !text-xs !text-white dark:!text-white !border-white/30 hover:!border-white">
<i class="fas fa-chart-line text-xs"></i> 系统监控
</n-button>
</NuxtLink>
<NuxtLink to="/api-docs" class="hidden sm:flex">
<n-button size="tiny" type="tertiary" round ghost class="!px-2 !py-1 !text-xs !text-white dark:!text-white !border-white/30 hover:!border-white">
<i class="fas fa-book text-xs"></i> API文档
</n-button>
</NuxtLink>
</nav>
</div>
<!-- 统计信息 -->
<div class="mb-6 grid grid-cols-1 md:grid-cols-4 gap-4">
<div class="bg-white dark:bg-gray-800 rounded-lg p-4 shadow-sm">
<div class="flex items-center">
<div class="p-2 bg-blue-100 dark:bg-blue-900 rounded-lg">
<i class="fas fa-film text-blue-600 dark:text-blue-400"></i>
</div>
<div class="ml-3">
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">总数量</p>
<p class="text-lg font-semibold text-gray-900 dark:text-white">{{ total }}</p>
</div>
</div>
</div>
<div class="bg-white dark:bg-gray-800 rounded-lg p-4 shadow-sm">
<div class="flex items-center">
<div class="p-2 bg-green-100 dark:bg-green-900 rounded-lg">
<i class="fas fa-video text-green-600 dark:text-green-400"></i>
</div>
<div class="ml-3">
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">电影</p>
<p class="text-lg font-semibold text-gray-900 dark:text-white">{{ movieCount }}</p>
</div>
</div>
</div>
<div class="bg-white dark:bg-gray-800 rounded-lg p-4 shadow-sm">
<div class="flex items-center">
<div class="p-2 bg-purple-100 dark:bg-purple-900 rounded-lg">
<i class="fas fa-tv text-purple-600 dark:text-purple-400"></i>
</div>
<div class="ml-3">
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">电视剧</p>
<p class="text-lg font-semibold text-gray-900 dark:text-white">{{ tvCount }}</p>
</div>
</div>
</div>
<div class="bg-white dark:bg-gray-800 rounded-lg p-4 shadow-sm">
<div class="flex items-center">
<div class="p-2 bg-yellow-100 dark:bg-yellow-900 rounded-lg">
<i class="fas fa-star text-yellow-600 dark:text-yellow-400"></i>
</div>
<div class="ml-3">
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">平均评分</p>
<p class="text-lg font-semibold text-gray-900 dark:text-white">{{ averageRating }}</p>
</div>
</div>
</div>
</div>
<!-- 筛选器 -->
<div class="mb-6 flex flex-wrap gap-4">
<button
v-for="category in categories"
:key="category.value"
@click="selectedCategory = category.value"
:class="[
'px-4 py-2 rounded-lg font-medium transition-colors',
selectedCategory === category.value
? 'bg-blue-600 text-white'
: 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 border border-gray-300 dark:border-gray-600'
]"
>
{{ category.label }}
</button>
</div>
<!-- 加载状态 -->
<div v-if="loading" class="flex justify-center items-center py-12">
<div class="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
</div>
<!-- 热播剧列表 -->
<div v-else-if="filteredDramas.length > 0" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
<div
v-for="drama in filteredDramas"
:key="drama.id"
class="bg-white dark:bg-gray-800 rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow border border-gray-200 dark:border-gray-700"
>
<!-- 剧集信息 -->
<div class="p-6">
<div class="flex items-start justify-between mb-3">
<h3 class="text-lg font-semibold text-gray-900 dark:text-white line-clamp-2 flex-1">
{{ drama.title }}
</h3>
<div class="flex items-center ml-2 flex-shrink-0">
<span class="text-yellow-500 text-sm font-medium">{{ drama.rating }}</span>
<span class="text-gray-400 dark:text-gray-500 text-xs ml-1"></span>
</div>
</div>
<!-- 副标题 -->
<div v-if="drama.card_subtitle" class="mb-3">
<p class="text-sm text-gray-600 dark:text-gray-400 line-clamp-1">{{ drama.card_subtitle }}</p>
</div>
<!-- 年份地区类型 -->
<div class="flex items-center gap-2 mb-3 flex-wrap">
<span v-if="drama.year" class="text-sm text-gray-500 dark:text-gray-400 bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded">
{{ drama.year }}
</span>
<span v-if="drama.region" class="text-sm text-gray-500 dark:text-gray-400 bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded">
{{ drama.region }}
</span>
<span class="text-sm text-blue-600 dark:text-blue-400 bg-blue-100 dark:bg-blue-900 px-2 py-1 rounded">
{{ drama.category }}
</span>
<span v-if="drama.sub_type" class="text-sm text-purple-600 dark:text-purple-400 bg-purple-100 dark:bg-purple-900 px-2 py-1 rounded">
{{ drama.sub_type }}
</span>
</div>
<!-- 类型标签 -->
<div v-if="drama.genres" class="mb-3">
<div class="flex flex-wrap gap-1">
<span
v-for="genre in drama.genres.split(',')"
:key="genre"
class="text-xs text-gray-600 dark:text-gray-400 bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded"
>
{{ genre.trim() }}
</span>
</div>
</div>
<!-- 导演 -->
<div v-if="drama.directors" class="mb-2">
<span class="text-xs text-gray-500 dark:text-gray-400">导演</span>
<span class="text-sm text-gray-700 dark:text-gray-300 line-clamp-1">{{ drama.directors }}</span>
</div>
<!-- 演员 -->
<div v-if="drama.actors" class="mb-3">
<span class="text-xs text-gray-500 dark:text-gray-400">主演</span>
<span class="text-sm text-gray-700 dark:text-gray-300 line-clamp-2">{{ drama.actors }}</span>
</div>
<!-- 集数信息 -->
<div v-if="drama.episodes_info" class="mb-3">
<span class="text-xs text-gray-500 dark:text-gray-400">集数</span>
<span class="text-sm text-gray-700 dark:text-gray-300">{{ drama.episodes_info }}</span>
</div>
<!-- 评分人数 -->
<div v-if="drama.rating_count" class="mb-3">
<span class="text-xs text-gray-500 dark:text-gray-400">评分人数</span>
<span class="text-sm text-gray-700 dark:text-gray-300">{{ formatNumber(drama.rating_count) }}</span>
</div>
<!-- 数据来源和时间 -->
<div class="flex items-center justify-between text-xs text-gray-400 dark:text-gray-500 pt-3 border-t border-gray-200 dark:border-gray-600">
<span>来源{{ drama.source }}</span>
<span>{{ formatDate(drama.created_at) }}</span>
</div>
</div>
</div>
</div>
<!-- 空状态 -->
<div v-else class="text-center py-12">
<div class="text-gray-400 dark:text-gray-500 mb-4">
<i class="fas fa-film text-6xl"></i>
</div>
<h3 class="text-lg font-medium text-gray-900 dark:text-white mb-2">暂无热播剧数据</h3>
<p class="text-gray-500 dark:text-gray-400">请稍后再试或联系管理员</p>
</div>
</div>
</div>
<!-- 页脚 -->
<AppFooter />
</div>
</template>
<script setup>
// 设置页面布局
definePageMeta({
layout: 'default'
})
import { ref, computed, onMounted, watch } from 'vue'
// 响应式数据
const loading = ref(false)
const dramas = ref([])
const total = ref(0)
const selectedCategory = ref('')
// 分类选项
const categories = ref([
{ label: '全部', value: '' },
{ label: '电影', value: '电影' },
{ label: '电视剧', value: '电视剧' }
])
// 计算属性
const filteredDramas = computed(() => {
if (!selectedCategory.value) {
return dramas.value
}
return dramas.value.filter(drama => drama.category === selectedCategory.value)
})
const movieCount = computed(() => {
return dramas.value.filter(drama => drama.category === '电影').length
})
const tvCount = computed(() => {
return dramas.value.filter(drama => drama.category === '电视剧').length
})
const averageRating = computed(() => {
const validRatings = dramas.value.filter(drama => drama.rating > 0)
if (validRatings.length === 0) return '0.0'
const sum = validRatings.reduce((acc, drama) => acc + drama.rating, 0)
return (sum / validRatings.length).toFixed(1)
})
// 获取热播剧列表(获取所有数据)
const fetchDramas = async () => {
loading.value = true
try {
const { useHotDramaApi } = await import('~/composables/useApi')
const hotDramaApi = useHotDramaApi()
const params = {
page: 1,
page_size: 1000 // 获取大量数据,实际会返回所有数据
}
if (selectedCategory.value) {
params.category = selectedCategory.value
}
const response = await hotDramaApi.getHotDramas(params)
console.log('API响应:', response)
// 使用新的统一响应格式
if (response && response.items) {
dramas.value = response.items
total.value = response.total || 0
console.log('设置数据:', dramas.value.length, '条')
console.log('第一条数据:', dramas.value[0])
} else {
// 兼容旧格式
dramas.value = Array.isArray(response) ? response : []
total.value = dramas.value.length
console.log('兼容格式数据:', dramas.value.length, '条')
}
} catch (error) {
console.error('获取热播剧列表失败:', error)
} finally {
loading.value = false
}
}
// 格式化日期
const formatDate = (dateString) => {
if (!dateString) return ''
const date = new Date(dateString)
return date.toLocaleDateString('zh-CN')
}
// 格式化数字
const formatNumber = (num) => {
if (!num) return '0'
if (num >= 10000) {
return (num / 10000).toFixed(1) + '万'
}
return num.toString()
}
// 处理图片加载错误
const handleImageError = (event) => {
console.log('图片加载失败:', event.target.src)
event.target.style.display = 'none'
}
// 处理图片加载成功
const handleImageLoad = (event) => {
console.log('图片加载成功:', event.target.src)
}
// 监听分类变化
watch(selectedCategory, () => {
fetchDramas()
})
// 页面加载时获取数据
onMounted(() => {
console.log('热播剧页面加载')
fetchDramas()
})
// 监听数据变化
watch(dramas, (newDramas) => {
console.log('dramas数据变化:', newDramas?.length)
if (newDramas && newDramas.length > 0) {
console.log('第一条数据:', newDramas[0])
console.log('第一条数据的poster_url:', newDramas[0].poster_url)
}
}, { deep: true })
</script>
<style scoped>
.line-clamp-1 {
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
overflow: hidden;
}
.line-clamp-2 {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
</style>