Merge "add resource recommend" into main
diff --git "a/src/app/resource/recommend/\133classification\135/page.tsx" "b/src/app/resource/recommend/\133classification\135/page.tsx"
index c48b626..453cf90 100644
--- "a/src/app/resource/recommend/\133classification\135/page.tsx"
+++ "b/src/app/resource/recommend/\133classification\135/page.tsx"
@@ -1,12 +1,142 @@
'use client';
-import React from 'react';
-const EmptyPage: React.FC = () => {
- return (
- <div className="p-d-flex p-jc-center p-ai-center" style={{ height: '100vh' }}>
- {"一个空页面"}
- </div>
- );
+import {useState, useEffect, useRef} from 'react';
+import {InputText} from 'primereact/inputtext';
+import {Card} from 'primereact/card';
+import {Image} from 'primereact/image';
+// 分页
+import {Paginator, type PaginatorPageChangeEvent} from 'primereact/paginator';
+// 页面跳转
+import {useParams} from 'next/navigation'
+import {useRouter} from 'next/navigation';
+// 消息提醒
+import {Toast} from 'primereact/toast';
+// 接口传输
+import axios from 'axios';
+// 防抖函数
+import {debounce} from 'lodash';
+// 样式
+import './resource-recommend.scss';
+import { useLocalStorage } from '@/app/hook/useLocalStorage';
+
+// 单元社区信息
+interface Resource {
+ resourceId: number;
+ resourceName: string;
+ resourcePicture: number;
+ resourceDetail: number;
+ lastUpdateTime: string;
+}
+
+// 推荐资源列表
+interface ResourceList {
+ total: number;
+ records: Resource[];
+}
+
+interface User {
+ Id: number;
};
-export default EmptyPage;
+// 推荐资源界面
+export default function ResourceRecommendPage() {
+ const user = useLocalStorage<User>('user');
+ const userId: number = user?.Id ?? -1;
+ // 获取URL参数
+ const params = useParams<{ classification: string }>()
+ const classify = decodeURIComponent(params.classification); // 防止中文路径乱码
+ const router = useRouter();
+ const imgUrl = process.env.NEXT_PUBLIC_NGINX_URL || "http://localhost:65/";
+
+ // 社区列表数据
+ const [resourceList, setResourceList] = useState<Resource[]>([]);
+ const [totalResources, setTotalResources] = useState<number>(0);
+ // 消息提醒
+ const toast = useRef<Toast>(null);
+ //搜索框
+ const [searchValue, setSearchValue] = useState('');
+ const debouncedSearch = useRef(
+ debounce((value: string) => {
+ setSearchValue(value);
+ }, 600)
+ ).current;
+
+ //分页
+ const [first, setFirst] = useState(0);
+ const [rows, setRows] = useState(5);
+ const onPageChange = (event: PaginatorPageChangeEvent) => {
+ setFirst(event.first);
+ setRows(event.rows);
+ };
+
+
+ // 获取社区列表
+ useEffect(() => {
+ fetchResources();
+ }, [classify, first, rows, searchValue]);
+
+ const fetchResources = async () => {
+ try {
+ const pageNumber = first / rows + 1;
+ console.log("当前页" + pageNumber + "size" + rows + "classify" + classify + "searchValue" + searchValue);
+ const response = await axios.get<ResourceList>(
+ process.env.PUBLIC_URL + `/resource/recommend`, {
+ params: {userId, classify, pageNumber, rows, searchValue}
+ }
+ );
+ console.log('获取推荐资源列表:', response.data.records);
+ setTotalResources(response.data.total);
+ setResourceList(response.data.records);
+ } catch (err) {
+ console.error('获取推荐资源失败', err);
+ toast.current?.show({severity: 'error', summary: 'error', detail: '获取推荐资源失败'});
+ }
+ };
+
+
+ return (
+ <div className="resource-community-list-container">
+ <Toast ref={toast}></Toast>
+ <div className="resource-communities-header">
+ <h1 className="title">
+ {classify === 'all' ? '全部资源' : `推荐${classify}资源`}
+ </h1>
+ </div>
+
+ {/* 社区搜索栏 */}
+ <div className="searchBar">
+ <i className="pi pi-search"/>
+ <InputText type="search" className="search-helper" placeholder="搜索你感兴趣的资源" onChange={(e) => {
+ const target = e.target as HTMLInputElement;
+ debouncedSearch(target.value);
+ }}/>
+ </div>
+
+
+ {/* 社区列表 */}
+ <div className="resource-communities-list">
+ {resourceList.map((resource) => (
+ <Card key={resource.resourceId} className="resource-communities-list-card"
+ onClick={() => router.push(`/resource/resource-detail/${resource.resourceId}`)}>
+ <Image alt="avatar" src={imgUrl + resource.resourcePicture}
+ className="community-avatar" width="250" height="140"/>
+ <div className="community-header">
+ <div className="community-content">
+ <h3>{resource.resourceName}</h3>
+ <p className="community-introduction">{resource.resourceDetail}</p>
+ </div>
+ <div className="community-states">
+ <div className="state-item">
+ <span>最近更新时间: {resource.lastUpdateTime}</span>
+ </div>
+ </div>
+ </div>
+ </Card>
+ ))}
+ {totalResources > 5 &&
+ <Paginator className="Paginator" first={first} rows={rows} totalRecords={totalResources}
+ rowsPerPageOptions={[5, 10]} onPageChange={onPageChange}/>}
+ </div>
+ </div>
+ );
+}
\ No newline at end of file
diff --git "a/src/app/resource/recommend/\133classification\135/resource-recommend.scss" "b/src/app/resource/recommend/\133classification\135/resource-recommend.scss"
new file mode 100644
index 0000000..9b863bf
--- /dev/null
+++ "b/src/app/resource/recommend/\133classification\135/resource-recommend.scss"
@@ -0,0 +1,105 @@
+// 全局容器样式
+.resource-community-list-container {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 0 2rem;
+}
+
+.resource-communities-header {
+ h1 {
+ text-align: center; // 页面居中
+ }
+}
+
+// 全部社区样式
+.resource-communities-list {
+ width: 100%;
+ padding: 1rem;
+
+ &-card {
+ cursor: pointer;
+ height: 140px;
+ padding: 1.5rem;
+ margin-bottom: 1rem;
+ border-radius: 0.5rem;
+ transition: transform 0.3s ease;
+ box-shadow: none !important; // 取消阴影
+
+ //填充卡片
+ &.p-card.p-component {
+ padding: 0;
+ }
+
+ .p-card-body {
+ padding: 0;
+ }
+
+ &:hover {
+ transform: translateY(-3px);
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+ }
+
+ .p-card-content {
+ height: 140px;
+ display: flex;
+ justify-content: space-between;
+ padding: 0;
+ }
+
+ img {
+ border-radius: 0.5rem 0 0 0.5rem;
+ object-fit: cover;
+ }
+
+ .community-header {
+ display: flex;
+ flex: 1;
+ max-width: 800px;
+ padding-left: 20px;
+ padding-right: 20px;
+ margin-bottom: 20px;
+ }
+
+
+ .community-avatar {
+ width: 80px;
+ height: 80px;
+ border-radius: 8px;
+ object-fit: cover;
+ }
+
+ .community-content {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ h3 {
+ font-size: 1.5rem;
+ font-weight: bold;
+ color: #2c3e50;
+ }
+
+ .community-introduction {
+ color: #666;
+ font-size: 1rem;
+ margin-bottom: 0;
+ }
+ }
+
+ .community-states {
+ min-width: 120px;
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-end;
+ align-items: flex-end;
+ gap: 0.5rem;
+
+ .state-item {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ color: #666;
+ font-size: 1rem;
+ }
+ }
+ }
+}
\ No newline at end of file