Sfoglia il codice sorgente

refactor(components): 重构产品和新闻组件结构与样式

- 重命名 PcoreProducts 组件为 CoreProducts 并优化布局响应式设计
- 更新产品列表项样式,增强悬停效果和图片缩放动画
- 移动新闻组件从 pages 目录到 components/news 目录并拆分 NewsList
- 优化新闻列表项组件 NewsItem 使用 Image 组件和悬停变换效果
- 删除冗余的 CSS 文件 NewsPage.css 和 head.css
- 升级 Next.js 版本依赖并调整相关类型引用路径
- 调整新闻页面引用路径以匹配新组件结构
- 移除未使用的 removeHTMLTags 工具函数导入
- 更新特殊新闻区块注释并保留原有功能逻辑
- 修复类型定义和环境变量获取方式提升代码健壮性
nahida 5 mesi fa
parent
commit
341f7cf425

+ 1 - 1
next-env.d.ts

@@ -1,6 +1,6 @@
 /// <reference types="next" />
 /// <reference types="next/image-types/global" />
-import "./.next/dev/types/routes.d.ts";
+/// <reference path="./.next/types/routes.d.ts" />
 
 // NOTE: This file should not be edited
 // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.

+ 1 - 1
package.json

@@ -15,7 +15,7 @@
     "axios": "^1.11.0",
     "framer-motion": "^12.23.19",
     "lucide-react": "^0.542.0",
-    "next": "16.1.0",
+    "next": "^15.5.2",
     "react": "19.1.0",
     "react-dom": "19.1.0",
     "tw-animate-css": "^1.3.8"

+ 0 - 325
src/app/news/NewsPage.css

@@ -1,325 +0,0 @@
-
-/* 页面容器样式 */
-.news-page {
-    max-width: 1200px;
-    margin: 0 auto;
-    min-height: 100vh;
-}
-
-/* 页面标题样式 */
-.page-title {
-    text-align: center;
-    color: #333;
-    margin-bottom: 40px;
-    font-size: 2.5rem;
-    font-weight: 600;
-    letter-spacing: 1px;
-}
-
-/* 标签切换容器样式 */
-.news-tabs {
-    display: flex;
-    justify-content: center;
-    margin-bottom: 30px;
-    gap: 10px;
-}
-
-/* 切换按钮基础样式 */
-.tab-btn {
-    padding: 12px 30px;
-    border: none;
-    border-radius: 30px;
-    background-color: #e0e0e0;
-    color: #666;
-    font-size: 1rem;
-    cursor: pointer;
-    transition: all 0.3s ease;
-    outline: none;
-}
-
-/* 激活状态按钮样式 */
-.tab-btn.active {
-    background-color: #2f54eb;
-    color: white;
-    box-shadow: 0 4px 12px rgba(47, 84, 235, 0.3);
-}
-
-/* 按钮hover效果 */
-.tab-btn:hover:not(.active) {
-    background-color: #d0d0d0;
-    color: #333;
-}
-
-/* 加载状态样式 */
-.loading {
-    text-align: center;
-    font-size: 1.2rem;
-    color: #666;
-    padding: 40px 0;
-}
-
-/* 无新闻提示样式 */
-.no-news {
-    text-align: center;
-    font-size: 1.2rem;
-    color: #999;
-    padding: 40px 0;
-}
-
-/* 新闻列表容器样式(列表形式) */
-.news-list {
-    display: flex;
-    flex-direction: column;
-    gap: 20px;
-    margin-top: 20px;
-    margin-bottom: 40px;
-}
-
-/* 新闻列表项样式(横向布局:图片+文字) */
-.news-item {
-    display: flex;
-    align-items: flex-start;
-    background-color: white;
-    border-radius: 12px;
-    padding: 20px;
-    box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
-    transition: transform 0.3s ease, box-shadow 0.3s ease;
-    gap: 20px;
-}
-
-/* 列表项hover效果 */
-.news-item:hover {
-    transform: translateY(-3px);
-    box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
-}
-
-/* 新闻封面图容器 */
-.news-cover {
-    flex-shrink: 0; /* 防止图片被压缩 */
-    width: 150px;
-    height: 100px;
-    border-radius: 8px;
-    overflow: hidden;
-}
-
-/* 封面图片样式 */
-.news-cover img {
-    width: 100%;
-    height: 100%;
-    object-fit: cover; /* 保持图片比例,填充容器 */
-    transition: transform 0.3s ease;
-}
-
-/* 图片hover放大效果 */
-.news-item:hover .news-cover img {
-    transform: scale(1.05);
-}
-
-/* 新闻内容容器 */
-.news-content-wrapper {
-    flex: 1; /* 占满剩余空间 */
-    overflow: hidden;
-}
-
-/* 新闻标题样式 */
-.news-title {
-    color: #333;
-    font-size: 1.2rem;
-    margin-bottom: 10px;
-    line-height: 1.4;
-    font-weight: 600;
-    white-space: nowrap;
-    overflow: hidden;
-    text-overflow: ellipsis; /* 标题过长显示省略号 */
-}
-
-/* 新闻元信息(日期、作者)样式 */
-.news-meta {
-    display: flex;
-    justify-content: space-between;
-    margin-bottom: 8px;
-    font-size: 0.85rem;
-    color: #999;
-}
-
-.news-date{
-    margin: 0 20px;
-}
-
-.news-author{
-    white-space: nowrap;
-}
-
-/* 新闻内容样式 */
-.news-content {
-    color: #666;
-    line-height: 1.6;
-    font-size: 0.95rem;
-    display: -webkit-box;
-    -webkit-line-clamp: 2; /* 内容最多显示2行 */
-    -webkit-box-orient: vertical;
-    overflow: hidden;
-    text-overflow: ellipsis;
-}
-
-/* 分页容器样式 */
-.pagination-container {
-    display: flex;
-    align-items: center;
-    justify-content: space-between;
-    padding: 20px 0;
-    flex-wrap: wrap;
-    gap: 20px;
-}
-
-/* 每页条数选择器 */
-.page-size-selector {
-    display: flex;
-    align-items: center;
-    gap: 10px;
-    color: #666;
-    font-size: 0.95rem;
-}
-
-.page-size-select {
-    padding: 6px 10px;
-    border: 1px solid #ddd;
-    border-radius: 6px;
-    background-color: white;
-    color: #666;
-    cursor: pointer;
-    font-size: 0.95rem;
-}
-
-/* 分页控件 */
-.pagination-controls {
-    display: flex;
-    align-items: center;
-    gap: 10px;
-}
-
-.page-btn {
-    padding: 8px 16px;
-    border: 1px solid #ddd;
-    border-radius: 6px;
-    background-color: white;
-    color: #666;
-    cursor: pointer;
-    transition: all 0.2s ease;
-    font-size: 0.95rem;
-}
-
-.page-btn:hover:not(:disabled) {
-    background-color: #f0f0f0;
-    color: #333;
-}
-
-.page-btn:disabled {
-    opacity: 0.5;
-    cursor: not-allowed;
-}
-
-/* 页码容器 */
-.page-numbers {
-    display: flex;
-    gap: 5px;
-}
-
-.page-number-btn {
-    width: 36px;
-    height: 36px;
-    border: 1px solid #ddd;
-    border-radius: 6px;
-    background-color: white;
-    color: #666;
-    cursor: pointer;
-    transition: all 0.2s ease;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    font-size: 0.95rem;
-}
-
-.page-number-btn.active {
-    background-color: #2f54eb;
-    color: white;
-    border-color: #2f54eb;
-}
-
-.page-number-btn:hover:not(.active):not(.ellipsis) {
-    background-color: #f0f0f0;
-    color: #333;
-}
-
-.page-number-btn.ellipsis {
-    border: none;
-    cursor: default;
-    background: transparent;
-}
-
-.page-number-btn:disabled {
-    opacity: 1;
-    cursor: default;
-}
-
-/* 页码信息 */
-.page-info {
-    color: #666;
-    font-size: 0.95rem;
-}
-
-/* 响应式适配 */
-@media (max-width: 768px) {
-    .page-title {
-        font-size: 2rem;
-    }
-
-    .tab-btn {
-        padding: 10px 20px;
-        font-size: 0.9rem;
-    }
-
-    /* 移动端改为纵向布局:图片在上,文字在下 */
-    .news-item {
-        flex-direction: column;
-        padding: 15px;
-        gap: 15px;
-    }
-
-    .news-cover {
-        width: 100%;
-        height: 180px;
-    }
-
-    .news-title {
-        font-size: 1.1rem;
-        white-space: normal; /* 移动端标题允许换行 */
-        -webkit-line-clamp: 2;
-        display: -webkit-box;
-        white-space: normal;
-    }
-
-    .news-content {
-        font-size: 0.9rem;
-        -webkit-line-clamp: 3; /* 移动端内容显示3行 */
-    }
-
-    /* 移动端分页布局调整 */
-    .pagination-container {
-        flex-direction: column;
-        align-items: center;
-        gap: 15px;
-    }
-
-    .page-size-selector {
-        order: 1;
-    }
-
-    .pagination-controls {
-        order: 2;
-    }
-
-    .page-info {
-        order: 3;
-    }
-}

+ 1 - 1
src/app/news/[id]/page.tsx

@@ -1,7 +1,7 @@
 import React from 'react';
 import {Breadcrumb} from "antd";
 import {serverGet} from "@/utils/request";
-// import './rich.module.scss'
+import './rich.css'
 import MainTitle from "@/components/MainTitle";
 import ContentNotFound from "@/components/ContentNotFound";
 

+ 0 - 0
src/app/news/[id]/rich.module.scss → src/app/news/[id]/rich.css


+ 0 - 156
src/app/news/news.tsx

@@ -1,156 +0,0 @@
-"use client";
-import React, {useState, useEffect, useMemo} from 'react';
-import './NewsPage.css';
-import Link from "next/link";
-
-// 1. 抽离类型定义,提升可维护性
-interface Article {
-    id: string;
-    newsUrl: string;
-    newsName: string;
-    newsDesc: string;
-    newsAuthor?: string; // 可选字段
-    content: string;
-    newsCategory: string; // 补充分类字段(原逻辑依赖)
-}
-
-// 2. 定义Props类型,避免隐式any
-interface NewsItemProps {
-    news: Article,
-    key?: string
-}
-
-interface NewsListProps {
-    newsList: Article[];
-}
-
-interface NewsPageProps {
-    newsList: Article[];
-}
-
-// 3. 抽离常量,便于统一维护
-const CATEGORY_MAP = Object.freeze({
-    company: '公司新闻',
-    industry: '行内新闻'
-} as const); // 常量断言,锁定类型
-
-// 4. 新闻列表项组件(纯组件,优化性能)
-const NewsItem: React.FC<NewsItemProps> = ({news: article, key}) => {
-    // 环境变量默认值兜底,避免undefined
-    const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL || '';
-
-    // 图片地址处理抽离,提升可读性
-    const coverImageUrl = article.newsUrl ? `${BASE_URL}${article.newsUrl}` : '';
-
-    return (
-        <div className="news-item">
-            <div className="news-cover">
-                <img
-                    src={coverImageUrl}
-                    alt={article.newsName} // 补充有意义的alt属性,优化可访问性
-                    loading="lazy"
-                    onError={(e) => {
-                        // 图片加载失败兜底,提升用户体验
-                        (e.target as HTMLImageElement).src = '/default-news-cover.png';
-                    }}
-                />
-            </div>
-            <div className="news-content-wrapper">
-                <h3 className="news-title">{article.newsName}</h3>
-                <div className="news-meta">
-                    <span className="news-date">{article.newsDesc}</span>
-                    {/* 可选字段兜底,避免空值展示 */}
-                    <span className="news-author">作者:{article.newsAuthor || '未知'}</span>
-                </div>
-                <p className="news-content">{article.content}</p>
-            </div>
-            <Link
-                href={`/news/${article.id}`}
-                className="text-blue-500 hover:underline mt-4 self-start text-sm sm:text-base"
-                aria-label={`查看${article.newsName}详情`} // 优化可访问性
-            >
-                了解更多 &gt;
-            </Link>
-        </div>
-    );
-};
-
-// 5. 新闻列表组件(添加空状态优化)
-const NewsList: React.FC<NewsListProps> = ({newsList}) => {
-    // 空数组判断优化,避免length判断出错
-    if (!Array.isArray(newsList) || newsList.length === 0) {
-        return <div className="no-news">暂无相关新闻</div>;
-    }
-
-    return (
-        <div className="news-list">
-            {newsList.map((news) => (
-                <NewsItem
-                    key={news.id}
-                    news={news}
-                />
-            ))}
-        </div>
-    );
-};
-
-// 6. 主新闻页面组件
-const NewsPage: React.FC<NewsPageProps> = ({newsList = []}) => {
-    // 类型守卫,确保newsList是数组类型
-    const safeNewsList = useMemo(() =>
-            Array.isArray(newsList) ? newsList : [],
-        [newsList]);
-
-    const [activeTab, setActiveTab] = useState<keyof typeof CATEGORY_MAP>('company'); // 严格类型约束
-    const [filteredNews, setFilteredNews] = useState<Article[]>([]);
-    const [isLoading, setIsLoading] = useState(true);
-
-    // 7. 优化筛选逻辑:使用useMemo减少重复计算
-    useEffect(() => {
-        setIsLoading(true);
-
-        const timer = setTimeout(() => {
-            const targetCategory = CATEGORY_MAP[activeTab];
-            const filtered = safeNewsList.filter(
-                (news) => news.newsCategory === targetCategory
-            );
-
-            setFilteredNews(filtered);
-            setIsLoading(false);
-        }, 300);
-
-        return () => clearTimeout(timer);
-    }, [safeNewsList, activeTab]);
-
-    // 8. 抽离标签切换逻辑,简化代码
-    const handleTabChange = (tab: keyof typeof CATEGORY_MAP) => {
-        setIsLoading(true);
-        setActiveTab(tab);
-    };
-
-    return (
-        <div className="news-page">
-            <div className="news-tabs">
-                <button
-                    className={`tab-btn ${activeTab === 'company' ? 'active' : ''}`}
-                    onClick={() => handleTabChange('company')}
-                    aria-pressed={activeTab === 'company'} // 优化可访问性
-                >
-                    公司新闻
-                </button>
-                <button
-                    className={`tab-btn ${activeTab === 'industry' ? 'active' : ''}`}
-                    onClick={() => handleTabChange('industry')}
-                    aria-pressed={activeTab === 'industry'}
-                >
-                    行内新闻
-                </button>
-            </div>
-
-            {isLoading && <div className="loading">加载中...</div>}
-            {!isLoading && <NewsList newsList={filteredNews}/>}
-        </div>
-    );
-};
-
-export default NewsPage;

+ 110 - 110
src/app/news/page.tsx

@@ -1,135 +1,135 @@
-import Link from "next/link"
 import "antd/dist/reset.css"
 import MainTitle from "@/components/MainTitle";
 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";
-import News from "./news";
+import News from "@/components/news/NewsList";
+
 // 调整NewsUpdates类型,对齐Article的结构
 interface NewsUpdates {
-    id: string;
-    newsUrl: string;
-    newsName: string;
-    newsDesc: string;
-    newsAuthor?: string; // 可选字段
-    content: string;
-    newsCategory: string; // 补充分类字段(原逻辑依赖)
+  id: string;
+  newsUrl: string;
+  newsName: string;
+  newsDesc: string;
+  newsAuthor?: string; // 可选字段
+  content: string;
+  newsCategory: string; // 补充分类字段(原逻辑依赖)
 }
+
 const getNewsList = async (pageNum: number, pageSize: number) => {
-    return serverGet<Page<NewsUpdates>>("/webSite/getNewsUpdatesListWithoutSpecialNews", {
-        pageNum,
-        pageSize
-    }, {
-        next: {
-            revalidate: 180
-        },
-        cache: "force-cache"
-    })
+  return serverGet<Page<NewsUpdates>>("/webSite/getNewsUpdatesListWithoutSpecialNews", {
+    pageNum,
+    pageSize
+  }, {
+    next: {
+      revalidate: 180
+    },
+    cache: "force-cache"
+  })
 }
 
 const getSpecialNews = async () => {
-    return serverGet<Page<NewsUpdates>>("/webSite/getSpecialNewsUpdatesList", {
-        pageNum: 1,
-        pageSize: 1
-    }, {
-        next: {
-            revalidate: 180
-        },
-        cache: "force-cache"
-    })
+  return serverGet<Page<NewsUpdates>>("/webSite/getSpecialNewsUpdatesList", {
+    pageNum: 1,
+    pageSize: 1
+  }, {
+    next: {
+      revalidate: 180
+    },
+    cache: "force-cache"
+  })
 }
 
 export default async function NewsPage({
-                                           searchParams,
+                                         searchParams,
                                        }: {
-    searchParams: Promise<{ page?: string }>
+  searchParams: Promise<{ page?: string }>
 }) {
 
-    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
+  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 (
-        <>
-            <AnimatedSection effect="slide" direction="left">
-                <div className="w-full h-full flex items-center justify-center text-white text-4xl font-bold">
-                    <Image src={"/assets/news/1.png"} alt={"banner"} width={1920} height={1080}/>
-                </div>
-            </AnimatedSection>
-            <div className="py-6 sm:py-10">
-                <MainTitle title={"新闻动态"} titleLetter={"NEWS_DYNAMIC"}/>
-            </div>
-            <News newsList={newsList}></News>
+  return (
+    <>
+      <AnimatedSection effect="slide" direction="left">
+        <div className="w-full h-full flex items-center justify-center text-white text-4xl font-bold">
+          <Image src={"/assets/news/1.png"} alt={"banner"} width={1920} height={1080}/>
+        </div>
+      </AnimatedSection>
+      <div className="py-6 sm:py-10">
+        <MainTitle title={"新闻动态"} titleLetter={"NEWS_DYNAMIC"}/>
+      </div>
+      <News newsList={newsList}></News>
 
 
-            {/*<AnimatedSection effect="slide" direction="right">*/}
-            {/*    /!*<div className="container sm:max-w-7/12 mx-auto p-4 space-y-6">*!/*/}
-            {/*    <div className="sm:max-w-7/12 mx-auto p-4 space-y-6">*/}
-            {/*        /!* 顶部大新闻 *!/*/}
-            {/*        <div className="bg-white rounded-2xl p-6">*/}
-            {/*            <div className="flex flex-col sm:flex-row gap-6">*/}
-            {/*                /!* 模拟图片色块 *!/*/}
-            {/*                <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">{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={`/news/${specialNews?.id}`}*/}
-            {/*                          className="text-blue-500 hover:underline mt-4 self-start text-sm sm:text-base">*/}
-            {/*                        了解更多 &gt;*/}
-            {/*                    </Link>*/}
-            {/*                </div>*/}
-            {/*            </div>*/}
-            {/*        </div>*/}
+      {/*<AnimatedSection effect="slide" direction="right">*/}
+      {/*    /!*<div className="container sm:max-w-7/12 mx-auto p-4 space-y-6">*!/*/}
+      {/*    <div className="sm:max-w-7/12 mx-auto p-4 space-y-6">*/}
+      {/*        /!* 顶部大新闻 *!/*/}
+      {/*        <div className="bg-white rounded-2xl p-6">*/}
+      {/*            <div className="flex flex-col sm:flex-row gap-6">*/}
+      {/*                /!* 模拟图片色块 *!/*/}
+      {/*                <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">{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={`/news/${specialNews?.id}`}*/}
+      {/*                          className="text-blue-500 hover:underline mt-4 self-start text-sm sm:text-base">*/}
+      {/*                        了解更多 &gt;*/}
+      {/*                    </Link>*/}
+      {/*                </div>*/}
+      {/*            </div>*/}
+      {/*        </div>*/}
 
-            {/*        /!* 下方小新闻卡片 *!/*/}
-            {/*        <div className="grid sm:grid-cols-2 gap-4">*/}
-            {/*            {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.newsName}</h3>*/}
-            {/*                        <p className="text-gray-500 text-xs sm:text-sm">{item.releaseTime}</p>*/}
-            {/*                    </div>*/}
-            {/*                    <Link href={`/news/${item.id}`}*/}
-            {/*                          className="text-blue-500 hover:underline mt-2 self-end text-xs sm:text-sm">*/}
-            {/*                        了解更多 &gt;*/}
-            {/*                    </Link>*/}
-            {/*                </div>*/}
-            {/*            ))}*/}
-            {/*        </div>*/}
+      {/*        /!* 下方小新闻卡片 *!/*/}
+      {/*        <div className="grid sm:grid-cols-2 gap-4">*/}
+      {/*            {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.newsName}</h3>*/}
+      {/*                        <p className="text-gray-500 text-xs sm:text-sm">{item.releaseTime}</p>*/}
+      {/*                    </div>*/}
+      {/*                    <Link href={`/news/${item.id}`}*/}
+      {/*                          className="text-blue-500 hover:underline mt-2 self-end text-xs sm:text-sm">*/}
+      {/*                        了解更多 &gt;*/}
+      {/*                    </Link>*/}
+      {/*                </div>*/}
+      {/*            ))}*/}
+      {/*        </div>*/}
 
-            {/*        /!* 分页器 *!/*/}
-                    <div className="flex justify-center mt-6">
-                        <PaginationClient
-                            current={pageNum}
-                            total={totalNews}
-                            pageSize={pageSize}
-                        />
-                    </div>
-            {/*    </div>*/}
-            {/*</AnimatedSection>*/}
-        </>
-    )
+      {/*        /!* 分页器 *!/*/}
+      <div className="flex justify-center mt-6">
+        <PaginationClient
+          current={pageNum}
+          total={totalNews}
+          pageSize={pageSize}
+        />
+      </div>
+      {/*    </div>*/}
+      {/*</AnimatedSection>*/}
+    </>
+  )
 }

+ 2 - 3
src/app/page.tsx

@@ -3,8 +3,7 @@ import BannerCarousel from "@/components/bannerCarousel"
 import MainTitle from "@/components/MainTitle";
 import FeatureCard from "@/components/FeatureCard";
 import ProductionSoft from "@/components/ProductionSoft";
-import ProductionHard from "@/components/ProductionHard";
-import PcoreProducts from "@/components/PcoreProducts";
+import CoreProducts from "@/components/CoreProducts";
 import {IntellectualPropertyStats} from "@/components/IntellectualPropertyStats";
 import ServerClient from "@/components/serverClient";
 import {serverGet} from "@/utils/request";
@@ -149,7 +148,7 @@ export default async function Home() {
 
             {/*核心产品的产品介绍图*/}
             <AnimatedSection effect="slide" direction="right">
-                <PcoreProducts products={coreHardwareProducts}/>
+                <CoreProducts products={coreHardwareProducts}/>
             </AnimatedSection>
 
 

+ 0 - 129
src/app/products/[id]/head.css

@@ -1,129 +0,0 @@
-/* 容器整体样式 */
-.container {
-    display: flex;
-    align-items: center;
-    padding: 20px;
-    /*border: 1px solid #eee;*/
-    border-radius: 8px;
-    max-width: 1200px;
-    margin: 0 auto;
-    /*box-shadow: 0 2px 8px rgba(0,0,0,0.05);*/
-    /*justify-content: center;*/
-}
-
-/* 左侧区域 */
-.leftSection {
-    text-align: center;
-    padding-right: 20px;
-    font-size: 30px;
-    font-weight: bold;
-    width: 40%;
-}
-
-.title {
-    font-size: 22px;
-    font-weight: 700;
-    margin-bottom: 8px;
-    color: #333;
-}
-
-.subTitle {
-    font-size: 14px;
-    color: #666;
-    margin-bottom: 15px;
-}
-
-.highlight {
-    color: #e03131;
-    font-weight: 500;
-}
-
-.divider {
-    margin: 0 5px;
-    color: #999;
-}
-
-.deviceImg {
-    width: 100%;
-    height: 300px;
-    object-fit: contain;
-}
-
-/* 右侧区域 */
-.rightSection {
-    padding-left: 100px;
-    border-left: 1px solid #eee;
-}
-
-.productName {
-    font-size: 28px;
-    font-weight: 600;
-    color: #333;
-    margin-bottom: 4px;
-}
-
-.model {
-    font-size: 16px;
-    color: #666;
-    margin-bottom: 12px;
-}
-
-.featureList {
-    list-style: none;
-    padding: 0;
-    margin-bottom: 15px;
-    font-size: 15px;
-    color: #777777;
-    line-height: 1.8;
-}
-
-.specs {
-    font-size: 18px;
-    color: #555;
-    margin-bottom: 20px;
-    line-height: 1.6;
-}
-
-.label {
-    font-weight: 500;
-    color: #333;
-}
-
-/* 按钮组 */
-.buttonGroup {
-    display: flex;
-    gap: 15px;
-}
-
-.contactBtn {
-    display: inline-flex;
-    align-items: center;
-    gap: 5px;
-    padding: 8px 16px;
-    border: 1px solid blue;
-    border-radius: 4px;
-    color: blue;
-    text-decoration: none;
-    font-size: 14px;
-    transition: background-color 0.2s;
-}
-
-.contactBtn:hover {
-    background-color: blue;
-    color: white;
-}
-
-.quoteBtn {
-    padding: 8px 20px;
-    background-color: #e03131;
-    color: #fff;
-    border: none;
-    border-radius: 4px;
-    font-size: 14px;
-    cursor: pointer;
-    transition: background-color 0.2s;
-}
-
-.quoteBtn:hover {
-    background-color: #c92a2a;
-}

+ 29 - 20
src/app/products/[id]/page.tsx

@@ -4,8 +4,8 @@ import {serverGet} from "@/utils/request";
 import './rich.css'
 import MainTitle from "@/components/MainTitle";
 import ContentNotFound from "@/components/ContentNotFound";
-import './head.css';
-import Image from "next/image"; // 引入样式文件
+import Image from "next/image";
+
 export const dynamicParams = true
 const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL as string
 export const generateStaticParams = async () => {
@@ -41,7 +41,7 @@ async function Page({
     if (!res.data) {
         return <ContentNotFound/>
     }
-    console.log(111,res.data)
+
     return (
         <>
             <div className="w-4/5 mx-auto">
@@ -60,48 +60,57 @@ async function Page({
                 <MainTitle title={"产品详情"} titleLetter={"PRODUCT_DETAIL"}/>
             </div>
             {/*头部区域*/}
-            <div className='container'>
+            <div
+                className="flex flex-col sm:flex-row sm:items-center gap-6 p-5 sm:p-6 rounded-lg max-w-[1200px] mx-auto"
+            >
                 {/* 左侧图片区域 */}
-                <div className='leftSection'>
+                <div className="w-full sm:w-2/5 flex justify-center sm:pr-6">
                     {/*<img*/}
                     {/*    src={res.data.productUrl ? BASE_URL + res.data.productUrl : "/assets/home/20.png"} // 替换为实际设备图片链接*/}
                     {/*    alt="雷达水位监测仪EN200-D"*/}
                     {/*    className='deviceImg'*/}
                     {/*/>*/}
                     <Image
-                        className={"h-50 object-contain"}
+                        className="w-full h-64 sm:h-[300px] object-contain"
                         src={res.data.productUrl ? BASE_URL + res.data.productUrl : "/assets/home/20.png"}
-                        alt={res.data.productName}
+                        alt={res.data.productName ? res.data.productName : "产品详情"}
                         width={1000}
                         height={1000}
                     />
                 </div>
 
                 {/* 右侧信息区域 */}
-                <div className='rightSection'>
-                    <h3 className='productName'>{res.data.productName}</h3>
-                    <p className='model'>{res.data.productModel}</p>
-                    <ul className='featureList'>
+                <div className="w-full sm:flex-1 sm:pl-12 lg:pl-24 sm:border-l sm:border-gray-200">
+                    <h3 className="text-2xl sm:text-3xl font-semibold text-gray-900 mb-1">
+                        {res.data.productName}
+                    </h3>
+                    <p className="text-sm sm:text-base text-gray-500 mb-3">{res.data.productModel}</p>
+                    <ul className="list-none p-0 mb-4 text-sm sm:text-[15px] text-[#777777] leading-[1.8] space-y-1">
                         {res.data.productIntroduction?.split('·').map((item, index) => (
                             <li key={index}>{item.trim()}</li>
                         ))}
                     </ul>
 
-                    <div className='specs'>
-                        <p><span className='label'>型号:</span>{res.data.productModel}</p>
-                        <p><span className='label'>品牌:</span>中科盛阳</p>
-                        <p><span className='label'>场景:</span>{res.data.productScene}</p>
+                    <div className="text-base sm:text-lg text-gray-600 mb-5 leading-relaxed space-y-1">
+                        <p><span className="font-medium text-gray-800">型号:</span>{res.data.productModel}</p>
+                        <p><span className="font-medium text-gray-800">品牌:</span>中科盛阳</p>
+                        <p><span className="font-medium text-gray-800">场景:</span>{res.data.productScene}</p>
                     </div>
 
                     {/* 按钮区域 */}
-                    <div className='buttonGroup'>
+                    <div className="flex flex-col sm:flex-row gap-3 sm:gap-4">
                         <a
                             href="tel:0731-8531-5153"
-                            className='contactBtn'
+                            className="inline-flex items-center justify-center gap-2 px-4 py-2 border border-blue-600 rounded text-blue-600 no-underline text-sm font-medium transition-colors hover:bg-blue-600 hover:text-white"
                         >
-                            <span className='phoneIcon'>📞</span> 0731-8531-5153
+                            <span>📞</span>
+                            0731-8531-5153
                         </a>
-                        <button className='quoteBtn'>获取报价</button>
+                        <button
+                            className="px-5 py-2 bg-red-600 hover:bg-red-700 text-white rounded text-sm font-medium transition-colors"
+                        >
+                            获取报价
+                        </button>
                     </div>
                 </div>
             </div>
@@ -118,4 +127,4 @@ async function Page({
 }
 
 
-export default Page;
+export default Page;

+ 60 - 0
src/components/CoreProducts.tsx

@@ -0,0 +1,60 @@
+'use client'
+import React from 'react';
+import Link from 'next/link';
+import Image from 'next/image';
+
+interface ProductMenuProps {
+  products: ProductItem[]
+}
+
+export default function CoreProducts({products}: ProductMenuProps) {
+  const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL as string
+  return (
+    <section className="w-full max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
+      <div className="flex justify-end mb-5 sm:mb-8">
+        <Link
+          href="/products"
+          className="sm:text-2xl text-xl shuheiti text-blue-700 hover:text-blue-800 inline-flex items-center gap-1 transition-colors"
+        >
+          更多
+          <span className="sm:text-2xl text-xl leading-none" aria-hidden="true">→</span>
+        </Link>
+      </div>
+
+      {/* 产品列表 */}
+      <div className="grid grid-cols-2 sm:grid-cols-2 lg:grid-cols-4 gap-3 sm:gap-6">
+        {products.map((product) => (
+          <div
+            key={product.productId}
+            className="group rounded-xl border border-gray-200 bg-white p-4 sm:p-6 flex flex-col items-center text-center shadow-sm hover:shadow-md transition-shadow duration-300"
+          >
+            <h3 className="shuheiti text-sm sm:text-xl text-gray-900 line-clamp-1">
+              {product.productName}
+            </h3>
+            <p className="mt-1 text-xs sm:text-sm text-gray-500 w-full">
+              <span className="text-blue-800 font-bold line-clamp-2">{product.productIntroduction}</span>
+            </p>
+            {/* 产品图片 */}
+            <div className="mt-3 sm:mt-5 w-24 h-24 sm:w-40 sm:h-40 flex items-center justify-center">
+              <Image
+                src={product.productUrl ? BASE_URL + product.productUrl : "/assets/productions/2.png"}
+                alt={product.productType}
+                width={160}
+                height={160}
+                className="object-contain transition-transform duration-300 group-hover:scale-[1.03]"
+              />
+            </div>
+            <div className="mt-3 sm:mt-5 w-full flex flex-col items-center gap-1">
+                            <span className="text-xs sm:text-sm text-gray-700 line-clamp-1">
+                                {product.productType}
+                            </span>
+              <span className="text-[11px] sm:text-xs text-gray-400 line-clamp-1">
+                                {product.productModel}
+                            </span>
+            </div>
+          </div>
+        ))}
+      </div>
+    </section>
+  );
+};

+ 0 - 50
src/components/PcoreProducts.tsx

@@ -1,50 +0,0 @@
-'use client'
-import React from 'react';
-import Link from 'next/link';
-import Image from 'next/image';
-
-interface ProductMenuProps {
-    products: ProductItem[]
-}
-export default function CoreProducts({products}: ProductMenuProps) {
-    const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL as string
-    return (
-        <section className="w-full max-w-7xl mx-auto">
-            {/* 标题区域 */}
-            <div className="flex flex-col items-center mb-8 relative">
-                <Link
-                    href="/products"
-                    className="absolute right-0 top-0 text-sm text-blue-600 hover:underline flex items-center gap-1"
-                >更多
-                </Link>
-            </div>
-
-            {/* 产品列表 */}
-            <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
-                {products.map((product) => (
-                    <div
-                        key={product.productId}
-                        className="border border-gray-200 rounded-sm p-6 flex flex-col items-center text-center hover:shadow-md transition-shadow duration-300"
-                    >
-                        <h3 className="font-bold text-lg mb-1 text-gray-800">{product.productName}</h3>
-                        <p className="text-sm mb-4 text-gray-500">
-                            <span className="text-red-600 line-clamp-1">{product.productIntroduction}</span>
-                        </p>
-                        {/* 产品图片 */}
-                        <div className="w-40 h-40 flex items-center justify-center mb-6">
-                            <Image
-                                src={product.productUrl ? BASE_URL + product.productUrl : "/assets/productions/2.png"}
-                                alt={product.productType}
-                                width={160}
-                                height={160}
-                                className="object-contain"
-                            />
-                        </div>
-                        <p className="text-sm text-gray-600">{product.productType}</p>
-                        <p className="text-xs text-gray-400 mt-1">{product.productModel}</p>
-                    </div>
-                ))}
-            </div>
-        </section>
-    );
-};

+ 173 - 0
src/components/news/NewsList.tsx

@@ -0,0 +1,173 @@
+"use client";
+import React, {useEffect, useMemo, useState} from 'react';
+import Link from "next/link";
+import Image from 'next/image';
+
+// 1. 抽离类型定义,提升可维护性
+interface Article {
+  id: string;
+  newsUrl: string;
+  newsName: string;
+  newsDesc: string;
+  newsAuthor?: string; // 可选字段
+  content: string;
+  newsCategory: string; // 补充分类字段(原逻辑依赖)
+}
+
+// 2. 定义Props类型,避免隐式any
+interface NewsItemProps {
+  news: Article,
+}
+
+interface NewsListProps {
+  newsList: Article[];
+}
+
+interface NewsPageProps {
+  newsList: Article[];
+}
+
+// 3. 抽离常量,便于统一维护
+const CATEGORY_MAP = Object.freeze({
+  company: '公司新闻',
+  industry: '行内新闻'
+} as const); // 常量断言,锁定类型
+
+// 4. 新闻列表项组件(纯组件,优化性能)
+const NewsItem: React.FC<NewsItemProps> = ({news: article}) => {
+  // 环境变量默认值兜底,避免undefined
+  const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL || '';
+
+  // 图片地址处理抽离,提升可读性
+  const coverImageUrl = article.newsUrl ? `${BASE_URL}${article.newsUrl}` : '';
+
+  return (
+    <div
+      className="group flex flex-col md:flex-row md:items-start bg-white rounded-xl p-[15px] md:p-5 shadow-[0_4px_15px_rgba(0,0,0,0.05)] hover:shadow-[0_8px_25px_rgba(0,0,0,0.1)] transition-[transform,box-shadow] duration-300 ease-in-out gap-[15px] md:gap-5 hover:-translate-y-[3px]"
+    >
+      <div className="shrink-0 w-full h-[180px] md:w-[150px] md:h-[100px] rounded-lg overflow-hidden">
+        <Image
+          width={1000}
+          height={1000}
+          src={coverImageUrl}
+          alt={article.newsName} // 补充有意义的alt属性,优化可访问性
+          loading="lazy"
+          className="w-full h-full object-cover transition-transform duration-300 ease-in-out group-hover:scale-[1.05]"
+          onError={(e) => {
+            // 图片加载失败兜底,提升用户体验
+            (e.target as HTMLImageElement).src = '/default-news-cover.png';
+          }}
+        />
+      </div>
+      <div className="flex-1 overflow-hidden">
+        <h3
+          className="text-[1.1rem] md:text-[1.2rem] text-gray-800 font-semibold mb-[10px] leading-snug line-clamp-2 md:line-clamp-1">
+          {article.newsName}
+        </h3>
+        <div className="flex items-center justify-between mb-2 text-[0.85rem] text-gray-400">
+          <span className="md:mx-5">{article.newsDesc}</span>
+          {/* 可选字段兜底,避免空值展示 */}
+          <span className="whitespace-nowrap">作者:{article.newsAuthor || '未知'}</span>
+        </div>
+        <p className="text-[0.9rem] md:text-[0.95rem] text-gray-600 leading-relaxed line-clamp-3 md:line-clamp-2">
+          {article.content}
+        </p>
+      </div>
+      <Link
+        href={`/news/${article.id}`}
+        className="text-blue-500 hover:underline mt-4 self-start text-sm sm:text-base"
+        aria-label={`查看${article.newsName}详情`} // 优化可访问性
+      >
+        了解更多 &gt;
+      </Link>
+    </div>
+  );
+};
+
+// 5. 新闻列表组件(添加空状态优化)
+const NewsList: React.FC<NewsListProps> = ({newsList}) => {
+  // 空数组判断优化,避免length判断出错
+  if (!Array.isArray(newsList) || newsList.length === 0) {
+    return <div className="text-center text-[1.2rem] text-gray-400 py-10">暂无相关新闻</div>;
+  }
+
+  return (
+    <div className="flex flex-col gap-5 mt-5 mb-10">
+      {newsList.map((news) => (
+        <NewsItem
+          key={news.id}
+          news={news}
+        />
+      ))}
+    </div>
+  );
+};
+
+// 6. 主新闻页面组件
+const NewsPage: React.FC<NewsPageProps> = ({newsList = []}) => {
+  // 类型守卫,确保newsList是数组类型
+  const safeNewsList = useMemo(() =>
+      Array.isArray(newsList) ? newsList : [],
+    [newsList]);
+
+  const [activeTab, setActiveTab] = useState<keyof typeof CATEGORY_MAP>('company'); // 严格类型约束
+  const [filteredNews, setFilteredNews] = useState<Article[]>([]);
+  const [isLoading, setIsLoading] = useState(true);
+
+  // 7. 优化筛选逻辑:使用useMemo减少重复计算
+  useEffect(() => {
+    setIsLoading(true);
+
+    const timer = setTimeout(() => {
+      const targetCategory = CATEGORY_MAP[activeTab];
+      const filtered = safeNewsList.filter(
+        (news) => news.newsCategory === targetCategory
+      );
+
+      setFilteredNews(filtered);
+      setIsLoading(false);
+    }, 300);
+
+    return () => clearTimeout(timer);
+  }, [safeNewsList, activeTab]);
+
+  // 8. 抽离标签切换逻辑,简化代码
+  const handleTabChange = (tab: keyof typeof CATEGORY_MAP) => {
+    setIsLoading(true);
+    setActiveTab(tab);
+  };
+
+  return (
+    <div className="max-w-[1200px] mx-auto min-h-screen px-4">
+      <div className="flex justify-center mb-[30px] gap-[10px]">
+        <button
+          className={`px-[30px] py-3 rounded-full text-base transition-all duration-300 ease-in-out outline-none ${
+            activeTab === 'company'
+              ? "bg-[#2f54eb] text-white shadow-[0_4px_12px_rgba(47,84,235,0.3)]"
+              : "bg-gray-200 text-gray-500 hover:bg-gray-300 hover:text-gray-800"
+          }`}
+          onClick={() => handleTabChange('company')}
+          aria-pressed={activeTab === 'company'} // 优化可访问性
+        >
+          公司新闻
+        </button>
+        <button
+          className={`px-[30px] py-3 rounded-full text-base transition-all duration-300 ease-in-out outline-none ${
+            activeTab === 'industry'
+              ? "bg-[#2f54eb] text-white shadow-[0_4px_12px_rgba(47,84,235,0.3)]"
+              : "bg-gray-200 text-gray-500 hover:bg-gray-300 hover:text-gray-800"
+          }`}
+          onClick={() => handleTabChange('industry')}
+          aria-pressed={activeTab === 'industry'}
+        >
+          行内新闻
+        </button>
+      </div>
+
+      {isLoading && <div className="text-center text-[1.2rem] text-gray-500 py-10">加载中...</div>}
+      {!isLoading && <NewsList newsList={filteredNews}/>}
+    </div>
+  );
+};
+
+export default NewsPage;

+ 1 - 1
tsconfig.json

@@ -15,7 +15,7 @@
     "moduleResolution": "bundler",
     "resolveJsonModule": true,
     "isolatedModules": true,
-    "jsx": "react-jsx",
+    "jsx": "preserve",
     "incremental": true,
     "plugins": [
       {