yunzer_go/frontend/src/views/officeResources/index.vue

432 lines
11 KiB
Vue

<template>
<Header />
<div class="resources-page">
<div class="container">
<div class="top-content">
<h1 class="page-title">办公资源</h1>
<div class="content flex flex-column align-items-center">
<p>发现优质程序与工具</p>
</div>
</div>
<div class="main-content">
<!-- 左侧分类导航 -->
<div class="left-menu">
<div class="menu-title">分类导航</div>
<ul class="category-list">
<li
v-for="category in categories"
:key="category.id"
class="category-item"
:class="{ active: selectedCategory === category.id }"
@click="selectCategory(category.id)"
>
{{ category.name }}
</li>
</ul>
</div>
<!-- 右侧内容区域 -->
<div class="contents">
<div v-if="loading" class="loading">加载中...</div>
<div v-else-if="resources.length === 0" class="no-data">暂无内容</div>
<div v-else class="resources-grid">
<div
v-for="resource in resources"
:key="resource.id"
class="resource-card"
>
<div class="resource-image">
<img :src="getImageUrl(resource.icon)" :alt="resource.title" />
</div>
<div class="card-header">
<router-link
:to="`/resource/${resource.id}`"
class="resource-title-link"
>
<h3 class="resource-title">{{ resource.title }}</h3>
</router-link>
<div class="resource-meta">
<span class="resource-view">
<i class="fa-solid fa-eye"></i>
{{ resource.view || 0 }}
</span>
<span class="resource-likes">
<i class="fas fa-download"></i>
{{ resource.likes || 0 }}
</span>
<span class="resource-date">{{
formatDate(resource.create_time)
}}</span>
</div>
</div>
</div>
</div>
<!-- 分页组件 -->
<div v-if="total > 0" class="pagination-wrapper">
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 50]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handlePageSizeChange"
@current-change="handlePageChange"
/>
</div>
</div>
</div>
</div>
</div>
<Footer />
</template>
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { ElMessage } from "element-plus";
import Header from "@/views/components/header.vue";
import Footer from "@/views/components/footer.vue";
import { officeResources } from "@/api/officeResources";
// 响应式数据
const categories = ref<any[]>([]); // 分类列表
const resources = ref<any[]>([]); // 文章列表
const selectedCategory = ref<string>(""); // 选中的分类
const loading = ref(false); // 加载状态
const currentPage = ref(1); // 当前页码
const pageSize = ref(10); // 每页条数
const total = ref(0); // 总条数
// 获取图片地址
const getImageUrl = (imagePath: string) => {
if (!imagePath) return "/src/assets/imgs/default.png";
return import.meta.env.VITE_API_DOMAIN + imagePath;
};
// 获取分类列表
const fetchCategories = async () => {
try {
const response: any = await officeResources.getOfficeResourcesCategory();
if (
response.data &&
response.data.data &&
Array.isArray(response.data.data)
) {
categories.value = response.data.data;
// 默认选择第一个分类
if (categories.value.length > 0) {
selectedCategory.value = categories.value[0].id;
fetchResources();
}
} else {
categories.value = [];
ElMessage.warning(response.data?.msg || response.msg || "获取分类失败");
}
} catch (error) {
console.error("获取分类失败:", error);
categories.value = [];
ElMessage.warning("获取分类失败");
}
};
// 获取文章列表
const fetchResources = async (page: number = currentPage.value) => {
loading.value = true;
try {
const response: any = await officeResources.getOfficeResourcesSimpleLists(
selectedCategory.value
);
const data = response.data?.data;
if (data?.resources && Array.isArray(data.resources)) {
resources.value = data.resources;
total.value = data.total || 0;
currentPage.value = data.page || 1;
pageSize.value = data.limit || 10;
} else {
resources.value = [];
total.value = 0;
ElMessage.warning(response.data?.msg || "获取文章失败");
}
} catch (error) {
console.error("获取文章失败:", error);
resources.value = [];
total.value = 0;
ElMessage.warning("获取文章失败");
} finally {
loading.value = false;
}
};
// 选择分类
const selectCategory = (categoryId: string) => {
selectedCategory.value = categoryId;
currentPage.value = 1; // 切换分类时重置到第一页
fetchResources(1);
};
// 分页处理
const handlePageChange = (page: number) => {
currentPage.value = page;
fetchResources(page);
};
// 每页条数变化
const handlePageSizeChange = (size: number) => {
pageSize.value = size;
currentPage.value = 1; // 重置到第一页
fetchResources(1);
};
// 格式化日期
const formatDate = (dateTime: string | number) => {
if (!dateTime) return "";
if (typeof dateTime === "string") {
// 如果包含空格,取日期部分
return dateTime.split(' ')[0];
}
// 如果是数字,当作时间戳处理
const date = new Date(dateTime < 1e10 ? dateTime * 1000 : dateTime);
return date.toLocaleDateString("zh-CN");
};
// 组件挂载时获取数据
onMounted(() => {
fetchCategories();
});
</script>
<style scoped lang="less">
.resources-page {
padding-top: 100px;
min-height: 100vh;
background: #f9fafc;
.top-content {
background: linear-gradient(135deg, #1e9fff 0%, #0d8aff 100%);
padding: 80px 40px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: var(--white);
.page-title {
font-size: 28px;
font-weight: 600;
margin-bottom: 20px;
text-align: center;
}
.content {
line-height: 1.6;
p {
margin-bottom: 16px;
}
}
}
.main-content {
width: 1200px;
margin: 0 auto;
display: flex;
gap: 30px;
padding: 40px 0;
align-items: flex-start;
.left-menu {
background-color: var(--white);
padding: 20px;
border-radius: 8px;
width: 250px;
flex-shrink: 0;
align-self: flex-start;
.menu-title {
font-size: 18px;
font-weight: 600;
color: #333;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 2px solid #1e9fff;
}
.category-list {
list-style: none;
padding: 0;
margin: 0;
.category-item {
padding: 12px 16px;
margin-bottom: 8px;
cursor: pointer;
border-radius: 6px;
transition: all 0.3s ease;
border-left: 3px solid transparent;
&:hover {
background: #f0f8ff;
border-left-color: #1e9fff;
}
&.active {
background: #e6f7ff;
border-left-color: #1e9fff;
color: #1e9fff;
font-weight: 500;
}
}
}
}
.contents {
background-color: var(--white);
// min-height: 400px;
border-radius: 8px;
padding: 20px;
flex: 1;
.pagination-wrapper {
margin-top: 30px;
display: flex;
justify-content: center;
padding: 20px 0;
border-top: 1px solid #e8e8e8;
.el-pagination {
--el-pagination-font-size: 14px;
--el-pagination-button-width: 40px;
--el-pagination-button-height: 40px;
}
}
.loading,
.no-data {
text-align: center;
padding: 60px 20px;
color: #666;
font-size: 16px;
}
.resources-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
.resource-card {
// min-width: 250px;
background: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 3px 15px rgba(0, 0, 0, 0.03);
transition: all 0.3s ease;
display: flex;
flex-direction: column;
&:hover {
transform: translateY(-2px);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
}
.resource-image {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 140px;
overflow: hidden;
img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
}
.card-header {
padding: 16px 16px 12px;
.resource-title-link {
text-decoration: none;
display: block;
&:hover .resource-title {
color: #007bff;
}
}
.resource-title {
font-size: 14px;
font-weight: 600;
height: 40px;
color: #333;
margin: 0 0 8px 0;
line-height: 1.4;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-word;
transition: color 0.3s ease;
cursor: pointer;
}
.resource-meta {
display: flex;
justify-content: space-between;
font-size: 12px;
color: #999;
.resource-view,
.resource-likes,
.resource-date {
display: flex;
align-items: center;
gap: 4px;
}
}
}
.card-body {
padding: 0 16px;
.resource-summary {
font-size: 12px;
color: #666;
line-height: 1.5;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
margin: 0;
}
}
.card-footer {
padding: 12px 16px 16px;
text-align: right;
.read-more {
color: #1e9fff;
text-decoration: none;
font-size: 12px;
font-weight: 500;
transition: color 0.3s ease;
&:hover {
color: #0d8aff;
text-decoration: underline;
}
}
}
}
}
}
}
}
</style>