重构CategoryItems组件

This commit is contained in:
jingrow 2025-06-23 16:39:56 +08:00
parent b3cabf7293
commit fb3bb322ac
2 changed files with 72 additions and 168 deletions

View File

@ -1,5 +1,6 @@
import Hero from "@/components/homes/home-15/Hero"; import Hero from "@/components/homes/home-15/Hero";
import SwiperItems from "@/components/homes/home-15/SwiperItems"; import SwiperItems from "@/components/homes/home-15/SwiperItems";
import CategoryItems from "@/components/homes/home-15/CategoryItems";
import React from "react"; import React from "react";
import { getSiteSettings } from "@/utils/siteSettings"; import { getSiteSettings } from "@/utils/siteSettings";
@ -35,7 +36,7 @@ export default function Page() {
<Hero /> <Hero />
</section> </section>
<section className="w-full xl:w-10/12 xl:mx-auto overflow-x-hidden"> <section className="w-full xl:w-10/12 xl:mx-auto overflow-x-hidden">
<SwiperItems /> <CategoryItems />
</section> </section>
</> </>
</div>{" "} </div>{" "}

View File

@ -1,94 +1,9 @@
"use client";
import { useEffect, useState } from "react";
import { Swiper, SwiperSlide } from "swiper/react";
import { Navigation, Pagination } from "swiper/modules";
import Link from "next/link"; import Link from "next/link";
import Image from "next/image"; import Image from "next/image";
import axios from "axios"; import { fetchComponentData, fetchListViewData } from "@/utils/data";
export default function CategoryItems() {
const [data, setData] = useState(null);
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
// 便
const title = data?.title;
const subtitle = data?.subtitle;
const pagetype = data?.t1;
const category = data?.t2;
const category_slug = data?.t3;
const count = data?.t4;
const columns = data?.t5;
const button_text = data?.button_text;
useEffect(() => {
async function fetchComponentData() {
try {
setLoading(true);
const res = await axios.get("/api/get-component-data", {
params: { component_name: "CategoryItems" },
});
setData(res.data.data);
} catch (err) {
setError("获取CategoryItems数据失败");
} finally {
setLoading(false);
}
}
fetchComponentData();
}, []);
useEffect(() => {
async function fetchPosts() {
if (!data) return;
setLoading(true);
try {
// 使
const params = new URLSearchParams({ pagetype: pagetype || "" });
if (category) params.append("category", category);
if (count !== undefined && count !== null) params.append("count", count);
const res = await fetch(`/api/get-listview-data?${params.toString()}`);
const json = await res.json();
if (Array.isArray(json.data)) {
setPosts(json.data);
} else {
setPosts([]);
}
} catch (e) {
setPosts([]);
}
setLoading(false);
}
fetchPosts();
}, [data, pagetype, category, count]);
if (!data) return null;
// //
function renderCardImage(post, idx) { function renderCardImage(post) {
//
if (Array.isArray(post.images) && post.images.length > 1) {
return (
<Swiper
className="swiper"
modules={[Navigation, Pagination]}
pagination={{ clickable: true, el: `.spdb${idx}` }}
navigation={{ prevEl: `.snbpb${idx}`, nextEl: `.snbnb${idx}` }}
>
{post.images.map((img, i) => (
<SwiperSlide key={i} className="swiper-slide">
<Image
className="!transition-all !duration-[0.35s] !ease-in-out group-hover:scale-105 w-full h-auto"
alt={post.title || "image"}
src={img}
width={960}
height={600}
/>
</SwiperSlide>
))}
</Swiper>
);
}
// //
if (post.video_src || post.videoId) { if (post.video_src || post.videoId) {
if (post.video_src && (post.video_src.endsWith('.mp4') || post.video_src.startsWith('/files/'))) { if (post.video_src && (post.video_src.endsWith('.mp4') || post.video_src.startsWith('/files/'))) {
@ -130,14 +45,11 @@ export default function CategoryItems() {
function getSummary(text, maxLen = 58) { function getSummary(text, maxLen = 58) {
if (!text) return ""; if (!text) return "";
//
if (/[\u4e00-\u9fa5]/.test(text)) { if (/[\u4e00-\u9fa5]/.test(text)) {
return text.length > maxLen ? text.slice(0, maxLen) + "..." : text; return text.length > maxLen ? text.slice(0, maxLen) + "..." : text;
} }
//
if (text.length <= maxLen) return text; if (text.length <= maxLen) return text;
let cut = text.slice(0, maxLen); let cut = text.slice(0, maxLen);
//
if (!/\s/.test(text[maxLen])) { if (!/\s/.test(text[maxLen])) {
const lastSpace = cut.lastIndexOf(" "); const lastSpace = cut.lastIndexOf(" ");
if (lastSpace > 0) cut = cut.slice(0, lastSpace); if (lastSpace > 0) cut = cut.slice(0, lastSpace);
@ -145,28 +57,27 @@ export default function CategoryItems() {
return cut + "..."; return cut + "...";
} }
// export default async function CategoryItems() {
function formatDate(dateStr, options = {}) { //
if (!dateStr) return ""; const componentDataResult = await fetchComponentData("CategoryItems");
const d = new Date(dateStr); const data = componentDataResult.data;
if (isNaN(d.getTime())) return dateStr;
const { showTime = false } = options; let posts = [];
const yyyy = d.getFullYear(); if (data) {
const mm = String(d.getMonth() + 1).padStart(2, '0'); const { t1: pagetype, t2: category, t4: count } = data;
const dd = String(d.getDate()).padStart(2, '0'); const postsResult = await fetchListViewData({ pagetype, category, count });
let result = `${yyyy}-${mm}-${dd}`; posts = postsResult.data || [];
if (showTime) {
const hh = String(d.getHours()).padStart(2, '0');
const min = String(d.getMinutes()).padStart(2, '0');
const ss = String(d.getSeconds()).padStart(2, '0');
result += ` ${hh}:${min}:${ss}`;
} }
return result;
if (!data) {
return null;
} }
//
const { title, subtitle, t3: category_slug, t5: columns, button_text } = data;
return ( return (
<div className="xl:w-10/12 lg:w-10/12 w-full flex-[0_0_auto] !px-[15px] max-w-full !mx-auto"> <div className="xl:w-10/12 lg:w-10/12 w-full flex-[0_0_auto] !px-[15px] max-w-full !mx-auto">
{/* 新增模块标题和副标题显示 */}
{(title || subtitle) && ( {(title || subtitle) && (
<div className="relative z-20 text-center mb-10 select-text"> <div className="relative z-20 text-center mb-10 select-text">
{title && <h2 className="text-3xl font-bold mb-2">{title}</h2>} {title && <h2 className="text-3xl font-bold mb-2">{title}</h2>}
@ -174,18 +85,15 @@ export default function CategoryItems() {
</div> </div>
)} )}
<div className={`blog classic-view grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-${columns || 4} gap-6`}> <div className={`blog classic-view grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-${columns || 4} gap-6`}>
{loading ? ( {posts.length === 0 ? (
<div className={`col-span-${columns || 4} text-center py-12 text-gray-400`}>加载中...</div>
) : posts.length === 0 ? (
<div className={`col-span-${columns || 4} text-center py-12 text-gray-400`}>暂无数据</div> <div className={`col-span-${columns || 4} text-center py-12 text-gray-400`}>暂无数据</div>
) : ( ) : (
posts.map((post, idx) => ( posts.map((post, idx) => (
<article key={post.slug || post.id || idx} className="post !mb-8"> <article key={post.slug || post.id || idx} className="post !mb-8">
<div className="card"> <div className="card">
{/* 图片/轮播/视频部分 */}
<figure className="card-img-top overlay overlay-1 hover-scale group"> <figure className="card-img-top overlay overlay-1 hover-scale group">
<Link href={post.slug ? `/${category_slug}/${post.slug}` : "#"} className="block relative"> <Link href={post.slug ? `/${category_slug}/${post.slug}` : "#"} className="block relative">
{renderCardImage(post, idx)} {renderCardImage(post)}
<span className="bg"></span> <span className="bg"></span>
<figcaption className="group-hover:opacity-100 absolute w-full h-full opacity-0 text-center px-4 py-3 inset-0 z-[5] pointer-events-none p-2"> <figcaption className="group-hover:opacity-100 absolute w-full h-full opacity-0 text-center px-4 py-3 inset-0 z-[5] pointer-events-none p-2">
<h5 className="from-top !mb-0 absolute w-full translate-y-[-80%] p-[.75rem_1rem] left-0 top-2/4"> <h5 className="from-top !mb-0 absolute w-full translate-y-[-80%] p-[.75rem_1rem] left-0 top-2/4">
@ -194,17 +102,13 @@ export default function CategoryItems() {
</figcaption> </figcaption>
</Link> </Link>
</figure> </figure>
{/* 文字内容 */}
<div className="card-body flex-[1_1_auto] p-[40px] xl:!p-[2rem_2.5rem_1.25rem] lg:!p-[2rem_2.5rem_1.25rem] md:!p-[2rem_2.5rem_1.25rem] max-md:pb-4"> <div className="card-body flex-[1_1_auto] p-[40px] xl:!p-[2rem_2.5rem_1.25rem] lg:!p-[2rem_2.5rem_1.25rem] md:!p-[2rem_2.5rem_1.25rem] max-md:pb-4">
<div className="post-header !mb-[.9rem]"> <div className="post-header !mb-[.9rem]">
<div className="inline-flex !mb-[.4rem] uppercase !tracking-[0.02rem] text-[0.7rem] font-bold !text-[#aab0bc] relative align-top !pl-[1.4rem] before:content-[''] before:absolute before:inline-block before:translate-y-[-60%] before:w-3 before:h-[0.05rem] before:left-0 before:top-2/4 before:bg-[#1fc76f]"> <div className="inline-flex !mb-[.4rem] uppercase !tracking-[0.02rem] text-[0.7rem] font-bold !text-[#aab0bc] relative align-top !pl-[1.4rem] before:content-[''] before:absolute before:inline-block before:translate-y-[-60%] before:w-3 before:h-[0.05rem] before:left-0 before:top-2/4 before:bg-[#1fc76f]">
{post.additional_title} {post.additional_title}
</div> </div>
<h2 className="post-title !mt-1 !leading-[1.35] !mb-0 text-base md:!text-[0.85rem]"> <h2 className="post-title !mt-1 !leading-[1.35] !mb-0 text-base md:!text-[0.85rem]">
<Link <Link className="!text-[#333333] hover:!text-[#1fc76f]" href={post.slug ? `/${category_slug}/${post.slug}` : "#"}>
className="!text-[#333333] hover:!text-[#1fc76f]"
href={post.slug ? `/${category_slug}/${post.slug}` : "#"}
>
{post.title} {post.title}
</Link> </Link>
</h2> </h2>
@ -218,7 +122,6 @@ export default function CategoryItems() {
)) ))
)} )}
</div> </div>
{/* 底部更多按钮 */}
{category_slug && ( {category_slug && (
<div className="text-center mt-8"> <div className="text-center mt-8">
<a <a