web-资源中心修改

This commit is contained in:
2025-10-20 18:28:38 +08:00
parent 0d1b0373ad
commit 3b4a639b95
26 changed files with 1213 additions and 1131 deletions

View File

@@ -0,0 +1,286 @@
<template>
<div class="resource-list">
<div class="list-container" ref="listContainerRef">
<div
v-for="resource in resources"
:key="resource.resourceID "
class="resource-item"
@click="handleResourceClick(resource)"
>
<div class="resource-cover">
<img
:src="resource.coverImage ? (FILE_DOWNLOAD_URL + resource.coverImage) : defaultArticleImg"
alt="cover"
/>
</div>
<div class="resource-info">
<h3 class="resource-title">{{ resource.title }}</h3>
<div class="resource-collect">
<img src="@/assets/imgs/star-icon.svg" alt="collect" />
<span>{{ (resource.collectCount || 0) > 0 ? '已收藏' : '收藏' }}</span>
</div>
<p class="resource-summary">{{ resource.summary || '暂无简介' }}</p>
</div>
</div>
<div v-if="loading" class="loading-more">加载中...</div>
<div v-if="resources.length === 0 && !loading" class="empty">暂无数据</div>
</div>
<div v-if="total > 0" class="pagination-wrapper">
<el-pagination
v-model:current-page="currentPage"
:page-size="pageSize"
:total="total"
layout="prev, pager, next, jumper"
@current-change="handlePageChange"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, watch, onMounted, nextTick } from 'vue';
import { resourceApi } from '@/apis/resource';
import { FILE_DOWNLOAD_URL } from '@/config';
import type { Resource, ResourceSearchParams } from '@/types/resource';
import type { PageParam } from '@/types';
import defaultArticleImg from '@/assets/imgs/article-default.png';
interface Props {
categoryId?: string;
searchKeyword?: string;
}
const props = defineProps<Props>();
const emit = defineEmits<{
'resource-click': [resource: Resource];
'list-updated': [resources: Resource[]];
}>();
const resources = ref<Resource[]>([]);
const loading = ref(false);
const total = ref(0);
const currentPage = ref(1);
const pageSize = 10;
const listContainerRef = ref<HTMLElement>();
onMounted(() => {
loadResources();
});
watch(() => [props.categoryId, props.searchKeyword], () => {
currentPage.value = 1;
loadResources();
}, { deep: true });
async function loadResources() {
if (loading.value) return;
loading.value = true;
try {
const filter: ResourceSearchParams = {
categoryID: props.categoryId,
keyword: props.searchKeyword,
// status: 1 // 只加载已发布的
};
const pageParam: PageParam = {
page: currentPage.value,
size: pageSize
};
const res = await resourceApi.getResourcePage(pageParam, filter);
if (res.success && res.dataList) {
resources.value = res.dataList;
total.value = res.pageDomain?.total || 0;
// 通知父组件列表已更新
emit('list-updated', res.dataList);
// 重置滚动位置到顶部
await nextTick();
if (listContainerRef.value) {
listContainerRef.value.scrollTop = 0;
}
}
} catch (error) {
console.error('加载资源列表失败:', error);
} finally {
loading.value = false;
}
}
function handlePageChange(page: number) {
currentPage.value = page;
loadResources();
}
function getResources() {
return resources.value;
}
function getPageInfo() {
return { currentPage: currentPage.value, total: total.value };
}
async function loadNextPage() {
const totalPages = Math.ceil(total.value / pageSize);
if (currentPage.value < totalPages) {
currentPage.value++;
await loadResources();
}
}
async function loadPrevPage() {
if (currentPage.value > 1) {
currentPage.value--;
await loadResources();
}
}
function handleResourceClick(resource: Resource) {
emit('resource-click', resource);
}
defineExpose({
loadResources,
getResources,
getPageInfo,
loadNextPage,
loadPrevPage
});
</script>
<style lang="scss" scoped>
.resource-list {
flex: 1;
align-self: stretch;
background: #FFFFFF;
border-radius: 10px;
overflow: hidden;
display: flex;
flex-direction: column;
}
.list-container {
flex: 1;
padding: 30px;
display: flex;
flex-direction: column;
gap: 30px;
min-height: 400px;
}
.resource-item {
display: flex;
gap: 20px;
padding-bottom: 30px;
border-bottom: 1px solid #EEEEEE;
cursor: pointer;
transition: all 0.3s;
&:last-child {
border-bottom: none;
padding-bottom: 0;
}
&:hover {
.resource-title {
color: #C62828;
}
}
}
.resource-cover {
width: 202px;
height: 123px;
border-radius: 6px;
overflow: hidden;
flex-shrink: 0;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
.default-cover {
width: 100%;
height: 100%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
}
.resource-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 8px;
}
.resource-title {
font-family: 'PingFang SC';
font-weight: 600;
font-size: 28px;
line-height: 28px;
color: #C62828;
margin: 0;
transition: color 0.3s;
}
.resource-collect {
display: flex;
align-items: center;
gap: 4px;
img {
width: 20px;
height: 20px;
}
span {
font-family: 'PingFang SC';
font-weight: 400;
font-size: 14px;
line-height: 24px;
color: #979797;
}
}
.resource-summary {
font-family: 'PingFang SC';
font-weight: 400;
font-size: 16px;
line-height: 24px;
color: #334155;
margin: 8px 0 0 0;
display: -webkit-box;
-webkit-line-clamp: 3;
line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
.loading-more,
.empty {
text-align: center;
padding: 20px;
font-family: 'PingFang SC';
font-size: 14px;
color: #979797;
}
.pagination-wrapper {
display: flex;
justify-content: center;
padding: 30px 0;
background: #FFFFFF;
border-top: 1px solid #EEEEEE;
}
</style>