移动端样式修正

This commit is contained in:
2025-12-17 11:48:22 +08:00
parent ad10c5025d
commit e54456f434

View File

@@ -55,7 +55,7 @@
<!-- 页面头部 -->
<!-- 文章列表 -->
<div v-loading="loading" class="articles-container">
<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)">
@@ -63,7 +63,7 @@
<div v-if="index < 3" class="rank-badge" :class="`rank-${index + 1}`">
<span>{{ index + 1 }}</span>
</div>
<div v-else class="rank-number">{{ (currentPage - 1) * pageSize + index + 1 }}</div>
<div v-else class="rank-number">{{ index + 1 }}</div>
<!-- 文章封面 -->
<div class="article-cover">
@@ -133,10 +133,21 @@
</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" class="pagination-container">
<!-- 分页 (移动端隐藏) -->
<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" />
@@ -145,13 +156,14 @@
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue';
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 } from '@element-plus/icons-vue';
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'
@@ -171,6 +183,12 @@ 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);
@@ -180,7 +198,109 @@ const totalViews = computed(() => {
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() {
@@ -296,6 +416,7 @@ function handleSortChange(value: string) {
function handleTagChange(value: string) {
selectedTagID.value = value;
currentPage.value = 1;
articles.value = []; // 清空列表,重新加载
loadArticles();
}
@@ -713,5 +834,282 @@ function goBack() {
.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>