feat: 当月热门笑话 + 统计面板,删除首页清除筛选按钮

- 后端新增 GET /api/jokes/hot-monthly(当月浏览量 top 10)
- 后端新增 GET /api/jokes/stats(公开统计:总数/审核/待审/浏览等)
- 右侧栏热门改为当月热门笑话
- 右侧栏新增网站统计区(笑话总数/已审核/待审核/总浏览)
- 首页删除清除筛选按钮
This commit is contained in:
bwstudio
2026-06-12 05:48:32 +08:00
parent 93b1d725e3
commit 076812f1aa
5 changed files with 139 additions and 34 deletions
+13 -5
View File
@@ -58,6 +58,7 @@
:hot-jokes="hotJokes"
:types="allTypes"
:crowds="allCrowds"
:stats="stats"
/>
</div>
@@ -75,7 +76,7 @@ import AppSubNav from '@/components/AppSubNav.vue'
import AppRightbar from '@/components/AppRightbar.vue'
import Footer from '@/components/Footer.vue'
import { getTypes, getCrowds } from '@/api/category'
import { getJokes } from '@/api/joke'
import { getJokes, getHotMonthly, getStats } from '@/api/joke'
import { useThemeStore } from '@/stores/theme'
const themeStore = useThemeStore()
@@ -98,6 +99,8 @@ const filterCrowdIds = ref([])
// 热门笑话(右侧栏)
const hotJokes = ref([])
// 网站统计
const stats = ref(null)
// 右侧栏:仅首页显示
@@ -180,14 +183,19 @@ onMounted(async () => {
}
try {
const res = await getJokes({ page: 1, page_size: 30 })
console.log('[APP] jokes loaded:', res.items?.length, 'total:', res.total)
const sorted = ((res.items || []).sort((a, b) => (b.view_count || 0) - (a.view_count || 0)))
hotJokes.value = sorted.slice(0, 5)
hotJokes.value = await getHotMonthly()
console.log('[APP] monthly hot jokes loaded:', hotJokes.value.length)
} catch (e) {
console.warn('[APP] Failed to load hot jokes:', e)
hotJokes.value = []
}
try {
stats.value = await getStats()
console.log('[APP] stats loaded:', stats.value)
} catch (e) {
console.warn('[APP] Failed to load stats:', e)
}
})
</script>
+2
View File
@@ -6,3 +6,5 @@ export const getRandomJoke = () => request.get('/jokes/random')
export const likeJoke = id => request.post(`/jokes/${id}/like`)
export const dislikeJoke = id => request.post(`/jokes/${id}/dislike`)
export const searchJokes = params => request.get('/jokes/', { params })
export const getHotMonthly = () => request.get('/jokes/hot-monthly')
export const getStats = () => request.get('/jokes/stats')
+63 -3
View File
@@ -1,10 +1,10 @@
<template>
<aside class="rightbar">
<!-- 热门笑话 -->
<!-- 当月热门笑话 -->
<div class="rightbar-section">
<div class="section-title">
<span class="title-icon">&#x1F525;</span>
热门笑话
当月热门笑话
</div>
<div class="hot-list" v-if="hotJokes.length">
<div
@@ -23,6 +23,32 @@
<div v-else class="empty-hint">暂无数据</div>
</div>
<!-- 网站统计 -->
<div class="rightbar-section stats-section" v-if="stats">
<div class="section-title">
<span class="title-icon">&#x1F4CA;</span>
网站统计
</div>
<div class="stats-grid">
<div class="stat-item">
<span class="stat-num">{{ stats.total_jokes || 0 }}</span>
<span class="stat-label">笑话总数</span>
</div>
<div class="stat-item">
<span class="stat-num">{{ stats.approved_jokes || 0 }}</span>
<span class="stat-label">已审核</span>
</div>
<div class="stat-item">
<span class="stat-num">{{ stats.pending_jokes || 0 }}</span>
<span class="stat-label">待审核</span>
</div>
<div class="stat-item">
<span class="stat-num">{{ stats.total_views || 0 }}</span>
<span class="stat-label">总浏览</span>
</div>
</div>
</div>
<!-- 类型快捷 -->
<div class="rightbar-section">
<div class="section-title">
@@ -74,7 +100,8 @@
defineProps({
hotJokes: { type: Array, default: () => [] },
types: { type: Array, default: () => [] },
crowds: { type: Array, default: () => [] }
crowds: { type: Array, default: () => [] },
stats: { type: Object, default: null }
})
</script>
@@ -157,6 +184,32 @@ defineProps({
}
.quick-tag.crowd:hover { background: #2196F3; color: white; }
/* 统计区域 */
.stats-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.stat-item {
text-align: center;
padding: 10px 6px;
background: var(--bg-page);
border-radius: 8px;
transition: background 0.3s;
}
.stat-num {
display: block;
font-size: 20px;
font-weight: 800;
color: var(--primary);
line-height: 1.2;
}
.stat-label {
font-size: 11px;
color: var(--text-muted);
margin-top: 2px;
}
/* 每日一笑 */
.daily-section { background: var(--tag-type-bg); }
.daily-text {
@@ -166,4 +219,11 @@ defineProps({
font-style: italic;
transition: color 0.3s;
}
/* 移动端隐藏右侧栏 */
@media (max-width: 1199px) {
.rightbar {
display: none;
}
}
</style>
-6
View File
@@ -4,7 +4,6 @@
<div class="filter-bar" v-if="filterText">
<span class="filter-text">{{ filterText }}</span>
<span class="filter-count"> {{ jokeStore.total }} </span>
<el-button link type="primary" @click="clearFilter" v-if="jokeStore.total > 0">清除筛选</el-button>
</div>
<!-- 笑话网格 -->
@@ -84,11 +83,6 @@ const loadJokes = async (p = 1) => {
window.scrollTo({ top: 0, behavior: 'smooth' })
}
const clearFilter = () => {
window.history.pushState({}, '', '/')
loadJokes(1)
}
onMounted(async () => {
types.value = allTypes.value?.length ? allTypes.value : await getTypes()
crowds.value = allCrowds.value?.length ? allCrowds.value : await getCrowds()