1116 lines
30 KiB
Vue
1116 lines
30 KiB
Vue
<template>
|
|
<div class="hot-resource-view">
|
|
<div class="hot-resource-view-head">
|
|
<div class="page-header">
|
|
<div class="header-content">
|
|
<div class="header-left">
|
|
<button class="back-button" @click="goBack">
|
|
<el-icon>
|
|
<ArrowLeft />
|
|
</el-icon>
|
|
<span>返回</span>
|
|
</button>
|
|
<div class="header-info">
|
|
<h1 class="page-title">
|
|
<img src="@/assets/imgs/hot.svg" alt="热门" class="title-icon" />
|
|
热门文章
|
|
</h1>
|
|
<p class="page-desc">根据浏览量为您推荐最受欢迎的文章内容</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 排序和筛选 -->
|
|
<div class="filter-controls">
|
|
<!-- <el-select :model-value="sortType" @change="handleSortChange" placeholder="排序方式" style="width: 150px;">
|
|
<el-option label="浏览量最多" value="viewCount" />
|
|
<el-option label="点赞最多" value="likeCount" />
|
|
<el-option label="收藏最多" value="collectCount" />
|
|
<el-option label="最新发布" value="publishTime" />
|
|
</el-select> -->
|
|
|
|
<el-select :model-value="selectedTagID" @change="handleTagChange" placeholder="文章分类" clearable
|
|
style="width: 150px;">
|
|
<el-option label="全部分类" :value="''" />
|
|
<el-option v-for="tag in articleTags" :key="tag.tagID" :label="tag.name"
|
|
:value="tag.tagID || ''" />
|
|
</el-select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 统计信息 -->
|
|
<div class="stats-bar">
|
|
<div class="stat-item">
|
|
<span class="stat-label">共找到</span>
|
|
<span class="stat-value">{{ total }}</span>
|
|
<span class="stat-label">篇热门文章</span>
|
|
</div>
|
|
<div class="stat-divider"></div>
|
|
<div class="stat-item">
|
|
<span class="stat-label">总浏览量</span>
|
|
<span class="stat-value">{{ formatNumber(totalViews) }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- 页面头部 -->
|
|
|
|
<!-- 文章列表 -->
|
|
<div ref="articlesContainerRef" v-loading="loading" class="articles-container">
|
|
<div class="articles-grid">
|
|
<div v-for="(article, index) in articles" :key="article.resourceID" class="article-card"
|
|
:class="{ 'top-rank': index < 3 }" @click="handleArticleClick(article)">
|
|
<!-- 排名徽章 -->
|
|
<div v-if="index < 3" class="rank-badge" :class="`rank-${index + 1}`">
|
|
<span>{{ index + 1 }}</span>
|
|
</div>
|
|
<div v-else class="rank-number">{{ index + 1 }}</div>
|
|
|
|
<!-- 文章封面 -->
|
|
<div class="article-cover">
|
|
<img v-if="article.coverImage" :src="FILE_DOWNLOAD_URL + article.coverImage"
|
|
:alt="article.title" />
|
|
<div v-else class="cover-placeholder">
|
|
<el-icon>
|
|
<Document />
|
|
</el-icon>
|
|
</div>
|
|
<div v-if="article.coverImage" class="cover-overlay">
|
|
<span class="view-button">查看详情</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 文章信息 -->
|
|
<div class="article-info">
|
|
|
|
<h3 class="article-title" :title="article.title">{{ article.title }}</h3>
|
|
|
|
<!-- 标签 -->
|
|
<div v-if="article.tagID" class="article-tag">
|
|
{{ getTagName(article.tagID) }}
|
|
</div>
|
|
|
|
<!-- 简介 -->
|
|
<p class="article-summary">{{ article.summary || '暂无简介' }}</p>
|
|
|
|
<!-- 底部元信息 -->
|
|
<div class="article-meta">
|
|
<div class="meta-item">
|
|
<img src="@/assets/imgs/hot.svg" alt="浏览" class="meta-icon" />
|
|
<span>{{ formatNumber(article.viewCount) }}</span>
|
|
</div>
|
|
<div class="meta-item">
|
|
<el-icon>
|
|
<Star />
|
|
</el-icon>
|
|
<span>{{ formatNumber(article.likeCount) }}</span>
|
|
</div>
|
|
<div class="meta-item">
|
|
<el-icon>
|
|
<Star />
|
|
</el-icon>
|
|
<span>{{ formatNumber(article.collectCount) }}</span>
|
|
</div>
|
|
<div class="meta-item author">
|
|
<el-icon>
|
|
<User />
|
|
</el-icon>
|
|
<span>{{ article.author || '未知' }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 发布时间 -->
|
|
<div class="article-time">
|
|
{{ formatDate(article.publishTime) }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 空状态 -->
|
|
<div v-if="!loading && articles.length === 0" class="empty-state">
|
|
<el-icon class="empty-icon">
|
|
<Document />
|
|
</el-icon>
|
|
<p>暂无热门文章</p>
|
|
</div>
|
|
|
|
<!-- 移动端加载更多提示 -->
|
|
<div v-if="isMobileDevice" class="mobile-load-more">
|
|
<div v-if="loadingMore" class="loading-tip">
|
|
<el-icon class="is-loading"><Loading /></el-icon>
|
|
<span>加载中...</span>
|
|
</div>
|
|
<div v-else-if="!hasMore && articles.length > 0" class="no-more-tip">
|
|
没有更多了
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 分页 (移动端隐藏) -->
|
|
<div v-if="total > 0 && !isMobileDevice" class="pagination-container">
|
|
<el-pagination :current-page="currentPage" :page-size="pageSize" :total="total"
|
|
:page-sizes="[9, 18, 27, 36]" layout="total, sizes, prev, pager, next, jumper"
|
|
@size-change="handleSizeChange" @current-change="handlePageChange" />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, computed, onMounted, onUnmounted, nextTick, watch } from 'vue';
|
|
import { useRouter } from 'vue-router';
|
|
import { ElMessage, ElIcon, ElSelect, ElOption, ElPagination } from 'element-plus';
|
|
import { ArrowLeft, Document, Star, User, Loading } from '@element-plus/icons-vue';
|
|
import { resourceApi, resourceTagApi } from '@/apis/resource';
|
|
import { FILE_DOWNLOAD_URL } from '@/config';
|
|
import type { Resource, Tag, ResourceSearchParams, PageParam } from '@/types';
|
|
import { useDevice } from '@/utils/deviceUtils';
|
|
|
|
defineOptions({
|
|
name: 'HotResourceView'
|
|
});
|
|
|
|
const router = useRouter();
|
|
|
|
// 数据状态
|
|
const articles = ref<Resource[]>([]);
|
|
const articleTags = ref<Tag[]>([]);
|
|
const loading = ref(false);
|
|
const total = ref(0);
|
|
const currentPage = ref(1);
|
|
const pageSize = ref(9);
|
|
|
|
// 筛选和排序
|
|
const sortType = ref<'viewCount' | 'likeCount' | 'collectCount' | 'publishTime'>('viewCount');
|
|
const selectedTagID = ref<string>('');
|
|
|
|
// 移动端相关
|
|
const { isMobileDevice } = useDevice();
|
|
const loadingMore = ref(false);
|
|
const hasMore = computed(() => articles.value.length < total.value);
|
|
const articlesContainerRef = ref<HTMLElement | null>(null);
|
|
|
|
// 计算总浏览量
|
|
const totalViews = computed(() => {
|
|
return articles.value.reduce((sum, article) => sum + (article.viewCount || 0), 0);
|
|
});
|
|
|
|
// 页面挂载
|
|
onMounted(() => {
|
|
loadTags();
|
|
loadArticles();
|
|
// 移动端添加滚动监听
|
|
nextTick(() => {
|
|
if (isMobileDevice.value && articlesContainerRef.value) {
|
|
setupInfiniteScroll();
|
|
}
|
|
});
|
|
});
|
|
|
|
// 页面卸载
|
|
onUnmounted(() => {
|
|
if (articlesContainerRef.value) {
|
|
articlesContainerRef.value.removeEventListener('scroll', handleScroll);
|
|
}
|
|
});
|
|
|
|
// 监听设备变化
|
|
watch(isMobileDevice, (isMobile) => {
|
|
if (isMobile) {
|
|
nextTick(() => {
|
|
if (articlesContainerRef.value) {
|
|
setupInfiniteScroll();
|
|
}
|
|
});
|
|
} else {
|
|
if (articlesContainerRef.value) {
|
|
articlesContainerRef.value.removeEventListener('scroll', handleScroll);
|
|
}
|
|
}
|
|
});
|
|
|
|
// 设置无限滚动
|
|
function setupInfiniteScroll() {
|
|
if (!articlesContainerRef.value) return;
|
|
articlesContainerRef.value.addEventListener('scroll', handleScroll, { passive: true });
|
|
}
|
|
|
|
// 滚动加载更多(移动端)
|
|
function handleScroll() {
|
|
if (!isMobileDevice.value || loading.value || loadingMore.value || !hasMore.value) return;
|
|
|
|
const container = articlesContainerRef.value;
|
|
if (!container) return;
|
|
|
|
const { scrollTop, scrollHeight, clientHeight } = container;
|
|
const distanceFromBottom = scrollHeight - scrollTop - clientHeight;
|
|
|
|
// 距离底部小于等于 50px 时触发加载
|
|
if (distanceFromBottom <= 50) {
|
|
loadMoreArticles();
|
|
}
|
|
}
|
|
|
|
// 加载更多文章(移动端滚动触发)
|
|
async function loadMoreArticles() {
|
|
if (!hasMore.value || loadingMore.value) return;
|
|
|
|
loadingMore.value = true;
|
|
try {
|
|
const filter: ResourceSearchParams = {
|
|
status: 1,
|
|
tagID: selectedTagID.value || undefined
|
|
};
|
|
|
|
const pageParam: PageParam = {
|
|
pageNumber: currentPage.value + 1,
|
|
pageSize: pageSize.value
|
|
};
|
|
|
|
const result = await resourceApi.getResourcePage(pageParam, filter);
|
|
|
|
if (result.success && result.pageDomain?.dataList) {
|
|
let sortedArticles = [...result.pageDomain.dataList];
|
|
|
|
switch (sortType.value) {
|
|
case 'viewCount':
|
|
sortedArticles.sort((a, b) => (b.viewCount || 0) - (a.viewCount || 0));
|
|
break;
|
|
case 'likeCount':
|
|
sortedArticles.sort((a, b) => (b.likeCount || 0) - (a.likeCount || 0));
|
|
break;
|
|
case 'collectCount':
|
|
sortedArticles.sort((a, b) => (b.collectCount || 0) - (a.collectCount || 0));
|
|
break;
|
|
case 'publishTime':
|
|
sortedArticles.sort((a, b) => {
|
|
const timeA = a.publishTime ? new Date(a.publishTime).getTime() : 0;
|
|
const timeB = b.publishTime ? new Date(b.publishTime).getTime() : 0;
|
|
return timeB - timeA;
|
|
});
|
|
break;
|
|
}
|
|
|
|
// 追加到现有列表
|
|
articles.value = [...articles.value, ...sortedArticles];
|
|
currentPage.value += 1;
|
|
total.value = result.pageDomain.pageParam?.totalElements || 0;
|
|
}
|
|
} catch (error) {
|
|
console.error('加载更多文章失败:', error);
|
|
} finally {
|
|
loadingMore.value = false;
|
|
}
|
|
}
|
|
|
|
// 加载标签列表
|
|
async function loadTags() {
|
|
try {
|
|
const result = await resourceTagApi.getTagList({ tagType: 1 }); // 1-文章分类标签
|
|
if (result.success && result.dataList) {
|
|
articleTags.value = result.dataList;
|
|
}
|
|
} catch (error) {
|
|
console.error('加载标签失败:', error);
|
|
}
|
|
}
|
|
|
|
// 加载热门文章
|
|
async function loadArticles() {
|
|
loading.value = true;
|
|
try {
|
|
const filter: ResourceSearchParams = {
|
|
status: 1, // 只显示已发布的
|
|
tagID: selectedTagID.value || undefined
|
|
};
|
|
|
|
const pageParam: PageParam = {
|
|
pageNumber: currentPage.value,
|
|
pageSize: pageSize.value
|
|
};
|
|
|
|
const result = await resourceApi.getResourcePage(pageParam, filter);
|
|
|
|
if (result.success && result.pageDomain?.dataList) {
|
|
// 根据选择的排序方式进行排序
|
|
let sortedArticles = [...result.pageDomain.dataList];
|
|
|
|
switch (sortType.value) {
|
|
case 'viewCount':
|
|
sortedArticles.sort((a, b) => (b.viewCount || 0) - (a.viewCount || 0));
|
|
break;
|
|
case 'likeCount':
|
|
sortedArticles.sort((a, b) => (b.likeCount || 0) - (a.likeCount || 0));
|
|
break;
|
|
case 'collectCount':
|
|
sortedArticles.sort((a, b) => (b.collectCount || 0) - (a.collectCount || 0));
|
|
break;
|
|
case 'publishTime':
|
|
sortedArticles.sort((a, b) => {
|
|
const timeA = a.publishTime ? new Date(a.publishTime).getTime() : 0;
|
|
const timeB = b.publishTime ? new Date(b.publishTime).getTime() : 0;
|
|
return timeB - timeA;
|
|
});
|
|
break;
|
|
}
|
|
|
|
articles.value = sortedArticles;
|
|
total.value = result.pageDomain.pageParam?.totalElements || 0;
|
|
}
|
|
} catch (error) {
|
|
console.error('加载热门文章失败:', error);
|
|
ElMessage.error('加载热门文章失败');
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
}
|
|
|
|
// 获取标签名称
|
|
function getTagName(tagID: string): string {
|
|
const tag = articleTags.value.find(t => t.tagID === tagID);
|
|
return tag?.name || '未知分类';
|
|
}
|
|
|
|
// 格式化数字
|
|
function formatNumber(num?: number): string {
|
|
if (num === undefined || num === null) return '0';
|
|
if (num < 1000) return num.toString();
|
|
if (num < 10000) return `${(num / 1000).toFixed(1)}k`;
|
|
return `${(num / 10000).toFixed(1)}w`;
|
|
}
|
|
|
|
// 格式化日期
|
|
function formatDate(dateStr?: string): string {
|
|
if (!dateStr) return '未知时间';
|
|
const date = new Date(dateStr);
|
|
const now = new Date();
|
|
const diff = now.getTime() - date.getTime();
|
|
|
|
// 小于1小时
|
|
if (diff < 3600000) {
|
|
const minutes = Math.floor(diff / 60000);
|
|
return `${minutes}分钟前`;
|
|
}
|
|
// 小于1天
|
|
if (diff < 86400000) {
|
|
const hours = Math.floor(diff / 3600000);
|
|
return `${hours}小时前`;
|
|
}
|
|
// 小于7天
|
|
if (diff < 604800000) {
|
|
const days = Math.floor(diff / 86400000);
|
|
return `${days}天前`;
|
|
}
|
|
|
|
// 否则显示完整日期
|
|
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
|
|
}
|
|
|
|
// 处理排序变化
|
|
function handleSortChange(value: string) {
|
|
sortType.value = value as typeof sortType.value;
|
|
currentPage.value = 1;
|
|
loadArticles();
|
|
}
|
|
|
|
// 处理标签筛选
|
|
function handleTagChange(value: string) {
|
|
selectedTagID.value = value;
|
|
currentPage.value = 1;
|
|
articles.value = []; // 清空列表,重新加载
|
|
loadArticles();
|
|
}
|
|
|
|
// 处理分页变化
|
|
function handlePageChange(page: number) {
|
|
currentPage.value = page;
|
|
loadArticles();
|
|
// 滚动到顶部
|
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
}
|
|
|
|
// 处理每页大小变化
|
|
function handleSizeChange(size: number) {
|
|
pageSize.value = size;
|
|
currentPage.value = 1;
|
|
loadArticles();
|
|
}
|
|
|
|
// 点击文章卡片
|
|
function handleArticleClick(article: Resource) {
|
|
if (article.resourceID) {
|
|
// 增加浏览次数
|
|
resourceApi.incrementViewCount(article.resourceID);
|
|
// 跳转到文章详情页
|
|
router.push(`/article/show?articleId=${article.resourceID}`);
|
|
}
|
|
}
|
|
|
|
// 返回上一页
|
|
function goBack() {
|
|
router.back();
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.hot-resource-view {
|
|
height: 100%;
|
|
background: linear-gradient(180deg, #FEF2F2 0%, #F9F9F9 100%);
|
|
padding: 0 0 20px 0;
|
|
|
|
.hot-resource-view-head {
|
|
height: 15%;
|
|
}
|
|
|
|
// 页面头部
|
|
.page-header {
|
|
background: #FFFFFF;
|
|
padding: 5px 0;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
|
// margin-bottom: 24px;
|
|
}
|
|
|
|
.header-content {
|
|
|
|
margin: 0 auto;
|
|
padding: 0 24px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
gap: 24px;
|
|
}
|
|
|
|
.header-left {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 20px;
|
|
flex: 1;
|
|
}
|
|
|
|
.back-button {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
padding: 8px 16px;
|
|
background: #F9FAFB;
|
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
|
border-radius: 8px;
|
|
font-size: 14px;
|
|
color: #4A5565;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
|
|
&:hover {
|
|
background: #F3F4F6;
|
|
color: #E7000B;
|
|
border-color: #E7000B;
|
|
}
|
|
}
|
|
|
|
.header-info {
|
|
flex: 1;
|
|
}
|
|
|
|
.page-title {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
font-size: 28px;
|
|
font-weight: 600;
|
|
color: #101828;
|
|
margin: 0 0 8px 0;
|
|
|
|
.title-icon {
|
|
width: 32px;
|
|
height: 32px;
|
|
}
|
|
}
|
|
|
|
.page-desc {
|
|
font-size: 14px;
|
|
color: #6B7280;
|
|
margin: 0;
|
|
}
|
|
|
|
.filter-controls {
|
|
display: flex;
|
|
gap: 12px;
|
|
}
|
|
|
|
// 统计信息栏
|
|
.stats-bar {
|
|
|
|
margin: 0 auto 24px;
|
|
padding: 0 24px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 24px;
|
|
}
|
|
|
|
.stat-item {
|
|
display: flex;
|
|
align-items: baseline;
|
|
gap: 6px;
|
|
|
|
.stat-label {
|
|
font-size: 14px;
|
|
color: #6B7280;
|
|
}
|
|
|
|
.stat-value {
|
|
font-size: 24px;
|
|
font-weight: 600;
|
|
color: #E7000B;
|
|
}
|
|
}
|
|
|
|
.stat-divider {
|
|
width: 1px;
|
|
height: 20px;
|
|
background: rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
// 文章容器
|
|
.articles-container {
|
|
margin: 0 auto;
|
|
padding: 0 24px;
|
|
height: 80%;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.articles-grid {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 16px;
|
|
height: 100%;
|
|
}
|
|
|
|
// 文章卡片
|
|
.article-card {
|
|
position: relative;
|
|
background: #FFFFFF;
|
|
border-radius: 12px;
|
|
overflow: hidden;
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
|
transition: all 0.3s;
|
|
cursor: pointer;
|
|
display: flex;
|
|
flex-direction: column;
|
|
width: calc((100% - 32px) / 3);
|
|
height: 50%;
|
|
|
|
&:hover {
|
|
transform: translateY(-4px);
|
|
box-shadow: 0 8px 24px rgba(231, 0, 11, 0.15);
|
|
|
|
.cover-overlay {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
&.top-rank {
|
|
border: 2px solid;
|
|
|
|
&.article-card:nth-child(1) {
|
|
border-color: #FFD700;
|
|
}
|
|
|
|
&.article-card:nth-child(2) {
|
|
border-color: #C0C0C0;
|
|
}
|
|
|
|
&.article-card:nth-child(3) {
|
|
border-color: #CD7F32;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 排名徽章
|
|
.rank-badge {
|
|
position: absolute;
|
|
top: 8px;
|
|
left: 8px;
|
|
width: 32px;
|
|
height: 32px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 50%;
|
|
font-size: 16px;
|
|
font-weight: 700;
|
|
color: #FFFFFF;
|
|
z-index: 10;
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
|
|
|
&.rank-1 {
|
|
background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%);
|
|
}
|
|
|
|
&.rank-2 {
|
|
background: linear-gradient(135deg, #C0C0C0 0%, #A8A8A8 100%);
|
|
}
|
|
|
|
&.rank-3 {
|
|
background: linear-gradient(135deg, #CD7F32 0%, #B8722B 100%);
|
|
}
|
|
}
|
|
|
|
.rank-number {
|
|
position: absolute;
|
|
top: 8px;
|
|
left: 8px;
|
|
width: 28px;
|
|
height: 28px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: rgba(0, 0, 0, 0.6);
|
|
border-radius: 50%;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
color: #FFFFFF;
|
|
z-index: 10;
|
|
}
|
|
|
|
// 文章封面
|
|
.article-cover {
|
|
position: relative;
|
|
width: 100%;
|
|
height: 160px;
|
|
flex-shrink: 0;
|
|
overflow: hidden;
|
|
background: #F3F4F6;
|
|
|
|
img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
transition: transform 0.3s;
|
|
}
|
|
|
|
.article-card:hover & img {
|
|
transform: scale(1.05);
|
|
}
|
|
|
|
.cover-placeholder {
|
|
width: 100%;
|
|
height: 100%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 48px;
|
|
color: #9CA3AF;
|
|
}
|
|
|
|
.cover-overlay {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, 0.7) 100%);
|
|
display: flex;
|
|
align-items: flex-end;
|
|
justify-content: center;
|
|
padding: 20px;
|
|
opacity: 0;
|
|
transition: opacity 0.3s;
|
|
|
|
.view-button {
|
|
padding: 8px 20px;
|
|
background: #E7000B;
|
|
color: #FFFFFF;
|
|
border-radius: 20px;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 文章信息
|
|
.article-info {
|
|
padding: 12px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
height: 50%;
|
|
flex: 1;
|
|
}
|
|
|
|
.article-title {
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
color: #101828;
|
|
margin: 0;
|
|
line-height: 1.4;
|
|
display: -webkit-box;
|
|
-webkit-line-clamp: 2;
|
|
line-clamp: 2;
|
|
-webkit-box-orient: vertical;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.article-tag {
|
|
display: inline-flex;
|
|
align-self: flex-start;
|
|
padding: 4px 12px;
|
|
background: #FEF2F2;
|
|
border: 1px solid #FCA5A5;
|
|
border-radius: 12px;
|
|
font-size: 12px;
|
|
color: #E7000B;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.article-summary {
|
|
font-size: 12px;
|
|
color: #6B7280;
|
|
line-height: 1.5;
|
|
margin: 0;
|
|
display: -webkit-box;
|
|
-webkit-line-clamp: 2;
|
|
line-clamp: 2;
|
|
-webkit-box-orient: vertical;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
flex: 1;
|
|
}
|
|
|
|
.article-meta {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
padding-top: 8px;
|
|
border-top: 1px solid #F3F4F6;
|
|
|
|
.meta-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 3px;
|
|
font-size: 12px;
|
|
color: #6B7280;
|
|
|
|
.meta-icon {
|
|
width: 12px;
|
|
height: 12px;
|
|
}
|
|
|
|
&.author {
|
|
margin-left: auto;
|
|
max-width: 80px;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
}
|
|
}
|
|
|
|
.article-time {
|
|
font-size: 11px;
|
|
color: #9CA3AF;
|
|
}
|
|
|
|
// 空状态
|
|
.empty-state {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 80px 20px;
|
|
color: #9CA3AF;
|
|
|
|
.empty-icon {
|
|
font-size: 64px;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
p {
|
|
font-size: 16px;
|
|
margin: 0;
|
|
}
|
|
}
|
|
|
|
// 分页
|
|
.pagination-container {
|
|
height: 5%;
|
|
}
|
|
|
|
// 移动端加载更多提示
|
|
.mobile-load-more {
|
|
padding: 20px 0;
|
|
text-align: center;
|
|
|
|
.loading-tip {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 8px;
|
|
color: #E7000B;
|
|
font-size: 14px;
|
|
|
|
.is-loading {
|
|
animation: rotating 1s linear infinite;
|
|
}
|
|
}
|
|
|
|
.no-more-tip {
|
|
color: #9CA3AF;
|
|
font-size: 13px;
|
|
}
|
|
}
|
|
|
|
@keyframes rotating {
|
|
from {
|
|
transform: rotate(0deg);
|
|
}
|
|
to {
|
|
transform: rotate(360deg);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 手机端样式适配
|
|
@media screen and (max-width: 768px) {
|
|
.hot-resource-view {
|
|
padding: 0 0 16px 0;
|
|
|
|
.hot-resource-view-head {
|
|
height: auto;
|
|
}
|
|
|
|
// 页面头部
|
|
.page-header {
|
|
padding: 12px 0;
|
|
}
|
|
|
|
.header-content {
|
|
padding: 0 16px;
|
|
flex-direction: column;
|
|
align-items: flex-start;
|
|
gap: 12px;
|
|
}
|
|
|
|
.header-left {
|
|
flex-direction: column;
|
|
align-items: flex-start;
|
|
gap: 12px;
|
|
width: 100%;
|
|
}
|
|
|
|
.back-button {
|
|
padding: 6px 12px;
|
|
font-size: 13px;
|
|
}
|
|
|
|
.page-title {
|
|
font-size: 20px;
|
|
gap: 8px;
|
|
margin-bottom: 4px;
|
|
|
|
.title-icon {
|
|
width: 24px;
|
|
height: 24px;
|
|
}
|
|
}
|
|
|
|
.page-desc {
|
|
font-size: 12px;
|
|
}
|
|
|
|
.filter-controls {
|
|
width: 100%;
|
|
|
|
:deep(.el-select) {
|
|
width: 100% !important;
|
|
}
|
|
}
|
|
|
|
// 统计信息栏
|
|
.stats-bar {
|
|
padding: 12px 16px;
|
|
margin-bottom: 16px;
|
|
gap: 16px;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.stat-item {
|
|
.stat-value {
|
|
font-size: 18px;
|
|
}
|
|
|
|
.stat-label {
|
|
font-size: 12px;
|
|
}
|
|
}
|
|
|
|
// 文章容器 - 需要固定高度才能触发滚动
|
|
.articles-container {
|
|
padding: 0 16px;
|
|
max-height: calc(100vh - 200px);
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.articles-grid {
|
|
gap: 12px;
|
|
height: auto;
|
|
}
|
|
|
|
// 文章卡片 - 手机端改为单列或双列
|
|
.article-card {
|
|
width: 100%;
|
|
height: auto;
|
|
min-height: 280px;
|
|
|
|
&:hover {
|
|
transform: none;
|
|
}
|
|
}
|
|
|
|
// 排名徽章
|
|
.rank-badge {
|
|
width: 28px;
|
|
height: 28px;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.rank-number {
|
|
width: 24px;
|
|
height: 24px;
|
|
font-size: 12px;
|
|
}
|
|
|
|
// 文章封面
|
|
.article-cover {
|
|
height: 140px;
|
|
|
|
.cover-placeholder {
|
|
font-size: 36px;
|
|
}
|
|
|
|
.cover-overlay {
|
|
opacity: 0;
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
// 文章信息
|
|
.article-info {
|
|
padding: 10px;
|
|
gap: 6px;
|
|
height: auto;
|
|
}
|
|
|
|
.article-title {
|
|
font-size: 14px;
|
|
}
|
|
|
|
.article-tag {
|
|
padding: 3px 10px;
|
|
font-size: 11px;
|
|
}
|
|
|
|
.article-summary {
|
|
font-size: 12px;
|
|
-webkit-line-clamp: 2;
|
|
line-clamp: 2;
|
|
}
|
|
|
|
.article-meta {
|
|
gap: 8px;
|
|
padding-top: 6px;
|
|
flex-wrap: wrap;
|
|
|
|
.meta-item {
|
|
font-size: 11px;
|
|
gap: 2px;
|
|
|
|
.meta-icon {
|
|
width: 11px;
|
|
height: 11px;
|
|
}
|
|
|
|
&.author {
|
|
max-width: 70px;
|
|
}
|
|
}
|
|
}
|
|
|
|
.article-time {
|
|
font-size: 10px;
|
|
}
|
|
|
|
// 空状态
|
|
.empty-state {
|
|
padding: 60px 16px;
|
|
|
|
.empty-icon {
|
|
font-size: 48px;
|
|
}
|
|
|
|
p {
|
|
font-size: 14px;
|
|
}
|
|
}
|
|
|
|
// 分页
|
|
.pagination-container {
|
|
height: auto;
|
|
padding: 16px;
|
|
|
|
:deep(.el-pagination) {
|
|
flex-wrap: wrap;
|
|
justify-content: center;
|
|
gap: 8px;
|
|
|
|
.el-pagination__total,
|
|
.el-pagination__sizes,
|
|
.el-pagination__jump {
|
|
display: none;
|
|
}
|
|
|
|
.btn-prev,
|
|
.btn-next {
|
|
min-width: 28px;
|
|
height: 28px;
|
|
}
|
|
|
|
.el-pager li {
|
|
min-width: 28px;
|
|
height: 28px;
|
|
line-height: 28px;
|
|
font-size: 12px;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 超小屏幕适配 (< 480px)
|
|
@media screen and (max-width: 480px) {
|
|
.hot-resource-view {
|
|
.page-title {
|
|
font-size: 18px;
|
|
|
|
.title-icon {
|
|
width: 20px;
|
|
height: 20px;
|
|
}
|
|
}
|
|
|
|
.article-card {
|
|
min-height: 260px;
|
|
}
|
|
|
|
.article-cover {
|
|
height: 120px;
|
|
}
|
|
|
|
.article-meta {
|
|
.meta-item.author {
|
|
display: none;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style>
|