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