浏览代码

feat(news): 实现新闻页面服务端渲染和分页功能

- 移除客户端组件声明和useState状态管理
- 新增服务端获取新闻列表和特别新闻数据
- 集成PaginationClient组件替换原有分页器- 添加AnimatedSection动画效果提升用户体验
- 更新新闻数据展示逻辑适配服务端返回结构
- 实现新闻详情页静态参数生成和数据获取
- 添加HTML标签过滤工具函数优化内容显示
- 配置数据缓存策略提高页面加载性能
nahida 7 月之前
父节点
当前提交
409f2e6412
共有 2 个文件被更改,包括 133 次插入72 次删除
  1. 64 72
      src/app/news/page.tsx
  2. 69 0
      src/app/products/[id]/page.tsx

+ 64 - 72
src/app/news/page.tsx

@@ -1,90 +1,83 @@
-// app/news/page.tsx
-"use client"
-
-import React, { useState } from "react"
 import Link from "next/link"
-import { Pagination } from "antd"  // 引入 Ant Design 分页器
 import "antd/dist/reset.css"
 import MainTitle from "@/components/MainTitle";
-import Image from "next/image";       // 确保全局引入 antd 样式(一般在 layout.tsx 中引入一次即可)
+import Image from "next/image";
+import PaginationClient from "@/components/PaginationClient";
+import {serverGet} from "@/utils/request";
+import {removeHTMLTags} from "@/utils/removeHTMLTags";
+import AnimatedSection from "@/components/AnimatedSection";
 
-export default function NewsPage() {
-  const newsList = [
-    {
-      id: 1,
-      title: "喜讯|中科盛阳荣获长沙市人工智能应用示范 资金项目支持",
-      date: "2025-07-15",
-      desc: "近日,长沙市工业和信息化局正式公布2024年人工智能应用示范奖励资金项目名单,中科盛阳信息技术有限公司凭借“园区多源信息融合智慧管理平台研发及应用示范”项目成功入选,成为长沙市推动智慧园区建设的标杆企业。",
-      hasImage: true,
-    },
-    {
-      id: 2,
-      title: "中科盛阳信息技术有限公司博士后创新创业实践...",
-      date: "2024-09-03",
-    },
-    {
-      id: 3,
-      title: "人大代表聚力赋能,携手中科盛阳共绘发展新蓝图",
-      date: "2024-11-08",
-    },
-    {
-      id: 4,
-      title: "中科盛阳自主研发传感设备取得重大突破,荣获...",
-      date: "2023-11-18",
+const getNewsList =async (pageNum: number, pageSize: number) =>{
+  return serverGet<Page<NewsUpdates>>("/webSite/getNewsUpdatesListWithoutSpecialNews",{
+    pageNum,
+    pageSize
+  },{
+    next: {
+      revalidate: 180
     },
-    {
-      id: 5,
-      title: "创新引领安全新篇章——中科盛阳荣获“一种基...",
-      date: "2024-03-15",
-    },
-    {
-      id: 6,
-      title: "中科盛阳信息技术有限公司荣获“双软认定”,彰...",
-      date: "2024-03-20",
-    },
-    {
-      id: 7,
-      title: "标题:中科盛阳荣膺2024年湖南省“专精特新”...",
-      date: "2024-04-26",
+    cache: "force-cache"
+  })
+}
+
+const getSpecialNews = async () =>{
+  return serverGet<Page<NewsUpdates>>("/webSite/getSpecialNewsUpdatesList",{
+    pageNum: 1,
+    pageSize: 1
+  },{
+    next: {
+      revalidate: 180
     },
-  ]
+    cache: "force-cache"
+  })
+}
 
-  const [currentPage, setCurrentPage] = useState(1)
-  const pageSize = 6
-  const totalNews = newsList.length - 1
+export default async function NewsPage({
+                                   searchParams,
+                                 }: {
+  searchParams: Promise<{ page?: string }>
+}) {
 
-  const pagedNews = newsList
-    .slice(1)
-    .slice((currentPage - 1) * pageSize, currentPage * pageSize)
+  const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL;
+  // 等待 searchParams 解析
+  const params = await searchParams
+  const pageNum = Number.parseInt(params.page || "1", 10)
+  const pageSize = 6
+  const newsListRes = await getNewsList(pageNum, pageSize)
+  const specialNewsRes = await getSpecialNews()
+  const specialNews = specialNewsRes.data.records[0]
+  const newsList = newsListRes.data.records
+  const totalNews = newsListRes.data.total
 
   return (
     <>
-      <div>
-        <Image src={"/assets/news/1.png"} alt={"banner"} width={1920} height={1080}/>
-      </div>
+      <AnimatedSection effect="slide" direction="left">
+        <div>
+          <Image src={"/assets/news/1.png"} alt={"banner"} width={1920} height={1080}/>
+        </div>
+      </AnimatedSection>
 
       <div className="py-6 sm:py-10">
-        <MainTitle title={"新闻动态"}/>
+        <MainTitle title={"新闻动态"} titleLetter={"NEWS_DYNAMIC"}/>
       </div>
-
-      <div className="container sm:max-w-9/12 mx-auto p-4 space-y-6">
+      <AnimatedSection effect="slide" direction="right">
+      <div className="container sm:max-w-7/12 mx-auto p-4 space-y-6">
         {/* 顶部大新闻 */}
         <div className="bg-white rounded-2xl shadow p-6">
           <div className="flex flex-col sm:flex-row gap-6">
             {/* 模拟图片色块 */}
-            <div className="w-full sm:w-1/2 bg-gray-300 rounded-lg h-48 sm:h-auto flex items-center justify-center">
-              <span className="text-gray-600">图片占位</span>
+            <div className="w-full sm:w-1/3 bg-gray-300 rounded-lg sm:h-auto flex items-center justify-center">
+              <Image src={specialNews?.newsUrl ? BASE_URL + specialNews?.newsUrl : ""} alt={"特别新闻图片"} width={300} height={300} />
             </div>
             {/* 右侧文字 */}
             <div className="flex-1 flex flex-col justify-between">
               <div>
-                <h2 className="text-lg sm:text-xl font-bold mb-2">{newsList[0].title}</h2>
-                <p className="text-gray-500 text-xs sm:text-sm mb-4">{newsList[0].date}</p>
-                <p className="text-gray-700 text-sm sm:text-base leading-relaxed line-clamp-4">
-                  {newsList[0].desc}
+                <h2 className="text-lg sm:text-xl font-bold mb-2">{specialNews?.newsName}</h2>
+                <p className="text-gray-500 text-xs sm:text-sm mb-4">{specialNews?.releaseTime}</p>
+                <p className="text-gray-700 text-sm sm:text-base leading-relaxed line-clamp-7">
+                  {removeHTMLTags(specialNews?.newsDetails || "")}
                 </p>
               </div>
-              <Link href="#" className="text-blue-500 hover:underline mt-4 self-start text-sm sm:text-base">
+              <Link href={`/news/${specialNews?.id}`} className="text-blue-500 hover:underline mt-4 self-start text-sm sm:text-base">
                 了解更多 &gt;
               </Link>
             </div>
@@ -93,16 +86,16 @@ export default function NewsPage() {
 
         {/* 下方小新闻卡片 */}
         <div className="grid sm:grid-cols-2 gap-4">
-          {pagedNews.map((item) => (
+          {newsList.map((item) => (
             <div
               key={item.id}
               className="bg-white rounded-2xl shadow p-4 flex flex-col justify-between"
             >
               <div>
-                <h3 className="text-sm sm:text-base font-semibold mb-2">{item.title}</h3>
-                <p className="text-gray-500 text-xs sm:text-sm">{item.date}</p>
+                <h3 className="text-sm sm:text-base font-semibold mb-2">{item.newsName}</h3>
+                <p className="text-gray-500 text-xs sm:text-sm">{item.releaseTime}</p>
               </div>
-              <Link href="#" className="text-blue-500 hover:underline mt-2 self-end text-xs sm:text-sm">
+              <Link href={`/news/${item.id}`} className="text-blue-500 hover:underline mt-2 self-end text-xs sm:text-sm">
                 了解更多 &gt;
               </Link>
             </div>
@@ -111,15 +104,14 @@ export default function NewsPage() {
 
         {/* 分页器 */}
         <div className="flex justify-center mt-6">
-          <Pagination
-            current={currentPage}
-            pageSize={pageSize}
+          <PaginationClient
+            current={pageNum}
             total={totalNews}
-            onChange={(page) => setCurrentPage(page)}
-            showSizeChanger={false}
+            pageSize={pageSize}
           />
         </div>
       </div>
+        </AnimatedSection>
     </>
   )
 }

+ 69 - 0
src/app/products/[id]/page.tsx

@@ -0,0 +1,69 @@
+import React from 'react';
+import {Breadcrumb} from "antd";
+import {serverGet} from "@/utils/request";
+import './rich.css'
+import MainTitle from "@/components/MainTitle";
+import ContentNotFound from "@/components/ContentNotFound";
+
+export const dynamicParams = true
+
+export const generateStaticParams = async () => {
+  const res = await serverGet<Page<ProductCenter>, { pageNum: number; pageSize: number }>(
+    "/webSite/getProductByPage",
+    {pageNum: 1, pageSize: 20}, {
+      next: {
+        revalidate: 1800
+      },
+      cache: "force-cache"
+    }
+  )
+  return res.data.records.map((item) => ({
+    id: item.id,
+  }))
+}
+
+async function Page({
+                      params,
+                    }: {
+  params: Promise<{ id: string }>
+}) {
+  const {id} = await params;
+  const res = await serverGet<ProductCenter, { id: string }>(
+    "/webSite/getProductById",
+    {id}, {
+      next: {
+        revalidate: 1800
+      },
+      cache: "force-cache"
+    }
+  )
+  if (!res.data) {
+    return <ContentNotFound />
+  }
+  return (
+    <>
+      <div className="w-4/5 mx-auto">
+        <div className="pt-5 sm:pt-10 sm:ml-20 flex gap-2">
+          <span className="text-sm">您当前的所在位置:</span>
+          <Breadcrumb
+            separator=">"
+            items={[
+              {title: "产品列表", href: "/products"},
+              {title: res.data.productName || "产品详情"},
+            ]}
+          />
+        </div>
+      </div>
+      <div className="py-6 sm:py-10">
+        <MainTitle title={"产品详情"} titleLetter={"PRODUCT_DETAIL"}/>
+      </div>
+      <div
+        dangerouslySetInnerHTML={{__html: res.data.productDetails as string}}
+        className="ql-editor w-9/10 sm:w-7/10 mx-auto sm:px-20 sm:py-10"
+      />
+    </>
+  );
+}
+
+
+export default Page;