SwiperItems组件改成动态版从后端读取组件数据
This commit is contained in:
parent
5f2217b193
commit
5e748005a1
@ -25,8 +25,6 @@ export default function Contact() {
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
if (loading) return <div className="text-center py-20">Loading...</div>;
|
||||
if (error) return null;
|
||||
if (!data) return null;
|
||||
|
||||
const title = data.title || "";
|
||||
|
||||
@ -14,7 +14,7 @@ export default function Footer15() {
|
||||
|
||||
return (
|
||||
<footer className="bg-[#fefefe] !text-[#333333]">
|
||||
<div className="container pt-50 pb-7">
|
||||
<div className="container pt-20 pb-7">
|
||||
<div className="w-full flex flex-col md:flex-row justify-center items-stretch gap-8 md:gap-10 xl:gap-16">
|
||||
{/* 产品中心 */}
|
||||
<div className="flex-1 min-w-[220px] max-w-xs flex flex-col">
|
||||
|
||||
@ -147,7 +147,7 @@ export default function Hero() {
|
||||
<h1 className="xl:!text-[3.2rem] !text-[calc(1.445rem_+_2.34vw)] font-semibold !leading-[1.15] !mb-5 md:mx-10 lg:mx-0 xl:mx-0">
|
||||
{title}
|
||||
</h1>
|
||||
<h2 className="xl:!text-[2.2rem] !text-[calc(1.445rem_+_2.34vw)] font-semibold !leading-[1.15] !mb-5 md:mx-10 lg:mx-0 xl:mx-0">
|
||||
<h2 className="xl:!text-[2.2rem] !text-[calc(1rem_+_2vw)] font-semibold !leading-[1.15] !mb-5 md:mx-10 lg:mx-0 xl:mx-0">
|
||||
<span className="rotator-fade !text-[#333333]">
|
||||
<AnimatedText
|
||||
strings={[
|
||||
|
||||
@ -1,17 +1,52 @@
|
||||
"use client";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useState, useRef } from "react";
|
||||
import { Swiper, SwiperSlide } from "swiper/react";
|
||||
import { Navigation, Pagination, Grid } from "swiper/modules";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import PropTypes from "prop-types";
|
||||
import axios from "axios";
|
||||
|
||||
export default function SwiperItems({ pagetype = "", category = "", category_slug = "" , count = 8, columns = 4, rows = 1 }) {
|
||||
export default function SwiperItems(props) {
|
||||
const [data, setData] = useState(null);
|
||||
const [posts, setPosts] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
// 统一字段映射,后台优先,props兜底
|
||||
const title = data?.title || props.title;
|
||||
const subtitle = data?.subtitle || props.subtitle;
|
||||
const pagetype = data?.t1 || props.pagetype || "";
|
||||
const category = data?.t2 || props.category || "";
|
||||
const category_slug = data?.t3 || props.category_slug || "";
|
||||
const count = data?.t4 || props.count || 8;
|
||||
const columns = data?.t5 || props.columns || 4;
|
||||
const rows = (data?.p1 && !isNaN(Number(data.p1))) ? Number(data.p1) : (props.rows || 1);
|
||||
const button_text = data?.button_text || props.button_text;
|
||||
|
||||
const prevRef = useRef(null);
|
||||
const nextRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchData() {
|
||||
async function fetchComponentData() {
|
||||
try {
|
||||
setLoading(true);
|
||||
const res = await axios.get("/api/get-component-data", {
|
||||
params: { component_name: "SwiperItems" },
|
||||
});
|
||||
setData(res.data.data);
|
||||
} catch (err) {
|
||||
setError("获取SwiperItems数据失败");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
fetchComponentData();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchPosts() {
|
||||
if (!pagetype) return;
|
||||
setLoading(true);
|
||||
try {
|
||||
const params = new URLSearchParams({ pagetype });
|
||||
@ -29,9 +64,10 @@ export default function SwiperItems({ pagetype = "", category = "", category_slu
|
||||
}
|
||||
setLoading(false);
|
||||
}
|
||||
fetchData();
|
||||
fetchPosts();
|
||||
}, [pagetype, category, count]);
|
||||
|
||||
if (!data) return null;
|
||||
// 渲染卡片内容
|
||||
function renderCardImage(post, idx) {
|
||||
// 多图轮播
|
||||
@ -134,106 +170,108 @@ export default function SwiperItems({ pagetype = "", category = "", category_slu
|
||||
|
||||
return (
|
||||
<div className="xl:w-10/12 lg:w-10/12 w-full flex-[0_0_auto] !px-[15px] max-w-full !mx-auto my-[150px]">
|
||||
{/* 新增模块标题和副标题显示 */}
|
||||
{(title || subtitle) && (
|
||||
<div className="relative z-20 text-center mb-10 select-text">
|
||||
{title && <h2 className="text-3xl font-bold mb-2">{title}</h2>}
|
||||
{subtitle && <p className="text-lg text-gray-500">{subtitle}</p>}
|
||||
</div>
|
||||
)}
|
||||
<div className="swiper-container blog classic-view !mb-[7rem] xl:!mb-[10rem] lg:!mb-[10rem] md:!mb-[10rem]">
|
||||
{loading ? (
|
||||
<div className={`text-center py-12 text-gray-400`}>加载中...</div>
|
||||
) : posts.length === 0 ? (
|
||||
<div className={`text-center py-12 text-gray-400`}>暂无数据</div>
|
||||
) : (
|
||||
<Swiper
|
||||
className="swiper"
|
||||
modules={[Navigation, Pagination, Grid]}
|
||||
spaceBetween={30}
|
||||
slidesPerView={columns}
|
||||
slidesPerGroup={columns * rows}
|
||||
grid={{ rows }}
|
||||
pagination={{ clickable: true, el: ".spdb15" }}
|
||||
navigation={{ prevEl: ".snbpb15", nextEl: ".snbnb15" }}
|
||||
breakpoints={{
|
||||
0: { slidesPerView: 1, grid: { rows: 1 } },
|
||||
575: { slidesPerView: 1, grid: { rows: 1 } },
|
||||
768: { slidesPerView: 2, grid: { rows: rows > 1 ? 2 : 1 } },
|
||||
992: { slidesPerView: columns, grid: { rows } },
|
||||
}}
|
||||
>
|
||||
{posts.map((post, idx) => (
|
||||
<SwiperSlide key={post.slug || post.id || idx} className="swiper-slide">
|
||||
<article className="post !mb-8">
|
||||
<div className="card">
|
||||
{/* 图片/轮播/视频部分 */}
|
||||
<figure className="card-img-top overlay overlay-1 hover-scale group">
|
||||
<Link href={post.slug ? `/${category_slug}/${post.slug}` : "#"} className="block relative">
|
||||
{renderCardImage(post, idx)}
|
||||
<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">
|
||||
<h5 className="from-top !mb-0 absolute w-full translate-y-[-80%] p-[.75rem_1rem] left-0 top-2/4">
|
||||
查看详情
|
||||
</h5>
|
||||
</figcaption>
|
||||
</Link>
|
||||
</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="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-[#3f78e0]">
|
||||
<a href={`/${category_slug}`} className="hover" rel="category">
|
||||
{post.category || ""}
|
||||
</a>
|
||||
<div className="relative">
|
||||
<Swiper
|
||||
className="swiper"
|
||||
modules={[Navigation, Pagination, Grid]}
|
||||
spaceBetween={30}
|
||||
slidesPerView={columns}
|
||||
slidesPerGroup={columns * rows}
|
||||
grid={{ rows }}
|
||||
pagination={{ clickable: true, el: ".spdb15" }}
|
||||
navigation={{ prevEl: prevRef.current, nextEl: nextRef.current }}
|
||||
onInit={swiper => {
|
||||
// Swiper 8+ 需要手动传递 ref.current
|
||||
swiper.params.navigation.prevEl = prevRef.current;
|
||||
swiper.params.navigation.nextEl = nextRef.current;
|
||||
swiper.navigation.init();
|
||||
swiper.navigation.update();
|
||||
}}
|
||||
breakpoints={{
|
||||
0: { slidesPerView: 1, slidesPerGroup: 1, grid: { rows: 1 } },
|
||||
575: { slidesPerView: 1, slidesPerGroup: 1, grid: { rows: 1 } },
|
||||
768: { slidesPerView: 2, slidesPerGroup: 2, grid: { rows: rows > 1 ? 2 : 1 } },
|
||||
992: { slidesPerView: columns, slidesPerGroup: columns * rows, grid: { rows } },
|
||||
}}
|
||||
>
|
||||
{posts.map((post, idx) => (
|
||||
<SwiperSlide key={post.slug || post.id || idx} className="swiper-slide">
|
||||
<article className="post !mb-8">
|
||||
<div className="card">
|
||||
{/* 图片/轮播/视频部分 */}
|
||||
<figure className="card-img-top overlay overlay-1 hover-scale group">
|
||||
<Link href={post.slug ? `/${category_slug}/${post.slug}` : "#"} className="block relative">
|
||||
{renderCardImage(post, idx)}
|
||||
<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">
|
||||
<h5 className="from-top !mb-0 absolute w-full translate-y-[-80%] p-[.75rem_1rem] left-0 top-2/4">
|
||||
查看详情
|
||||
</h5>
|
||||
</figcaption>
|
||||
</Link>
|
||||
</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="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-[#3f78e0]">
|
||||
{post.additional_title || ""}
|
||||
</div>
|
||||
<h2 className="post-title !mt-1 !leading-[1.35] !mb-0">
|
||||
<Link
|
||||
className="!text-[#343f52] hover:!text-[#3f78e0]"
|
||||
href={post.slug ? `/${category_slug}/${post.slug}` : "#"}
|
||||
>
|
||||
{post.title}
|
||||
</Link>
|
||||
</h2>
|
||||
</div>
|
||||
<div className="!relative">
|
||||
<p>{getSummary(post.subtitle)}</p>
|
||||
</div>
|
||||
<h2 className="post-title !mt-1 !leading-[1.35] !mb-0">
|
||||
<Link
|
||||
className="!text-[#343f52] hover:!text-[#3f78e0]"
|
||||
href={post.slug ? `/${category_slug}/${post.slug}` : "#"}
|
||||
>
|
||||
{post.title}
|
||||
</Link>
|
||||
</h2>
|
||||
</div>
|
||||
<div className="!relative">
|
||||
<p>{getSummary(post.subtitle)}</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* 底部信息 */}
|
||||
<div className="card-footer xl:!p-[1.25rem_2.5rem_1.25rem] lg:!p-[1.25rem_2.5rem_1.25rem] md:!p-[1.25rem_2.5rem_1.25rem] p-[18px_40px]">
|
||||
<ul className="!text-[0.7rem] !text-[#aab0bc] m-0 p-0 list-none flex !mb-0">
|
||||
<li className="post-date inline-block">
|
||||
<i className="uil uil-calendar-alt pr-[0.2rem] align-[-.05rem] before:content-['\\e9ba']" />
|
||||
<span>{formatDate(post.creation)}</span>
|
||||
</li>
|
||||
{post.author && (
|
||||
<li className="post-author inline-block before:content-[''] before:inline-block before:w-[0.2rem] before:h-[0.2rem] before:opacity-50 before:m-[0_.6rem_0] before:rounded-[100%] before:align-[.15rem] before:bg-[#aab0bc]">
|
||||
<i className="uil uil-user pr-[0.2rem] align-[-.05rem] before:content-['\\ed6f']" />
|
||||
<span>{post.author}</span>
|
||||
</li>
|
||||
)}
|
||||
{typeof post.comments === 'number' && (
|
||||
<li className="post-comments inline-block before:content-[''] before:inline-block before:w-[0.2rem] before:h-[0.2rem] before:opacity-50 before:m-[0_.6rem_0] before:rounded-[100%] before:align-[.15rem] before:bg-[#aab0bc]">
|
||||
<a className="!text-[#aab0bc] hover:!text-[#3f78e0] hover:!border-[#3f78e0]" href="#">
|
||||
<i className="uil uil-comment pr-[0.2rem] align-[-.05rem] before:content-['\\ea54']" />
|
||||
{post.comments}
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
{typeof post.likes === 'number' && (
|
||||
<li className="post-likes !ml-auto inline-block">
|
||||
<a className="!text-[#aab0bc] hover:!text-[#3f78e0] hover:!border-[#3f78e0]" href="#">
|
||||
<i className="uil uil-heart-alt pr-[0.2rem] align-[-.05rem] before:content-['\\eb60']" />
|
||||
{post.likes}
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</SwiperSlide>
|
||||
))}
|
||||
</Swiper>
|
||||
</article>
|
||||
</SwiperSlide>
|
||||
))}
|
||||
</Swiper>
|
||||
{/* 美化后的导航按钮 */}
|
||||
<button
|
||||
ref={prevRef}
|
||||
className="absolute left-[-32px] top-1/2 z-20 -translate-y-1/2 w-10 h-10 flex items-center justify-center !rounded-full border-2 border-[#e5e7eb] bg-transparent transition-all duration-200 hover:border-[#1fc76f] group"
|
||||
style={{ color: '#b0b7c3' }}
|
||||
aria-label="上一页"
|
||||
>
|
||||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M15.5 19L9.5 12L15.5 5" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" className="transition-all duration-200 group-hover:stroke-[#1fc76f]"/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
ref={nextRef}
|
||||
className="absolute right-[-32px] top-1/2 z-20 -translate-y-1/2 w-10 h-10 flex items-center justify-center !rounded-full border-2 border-[#e5e7eb] bg-transparent transition-all duration-200 hover:border-[#1fc76f] group"
|
||||
style={{ color: '#b0b7c3' }}
|
||||
aria-label="下一页"
|
||||
>
|
||||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M8.5 5L14.5 12L8.5 19" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" className="transition-all duration-200 group-hover:stroke-[#1fc76f]"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
<div className="swiper-controls">
|
||||
<div className="swiper-pagination spdb15"></div>
|
||||
<div className="swiper-button-prev snbpb15"></div>
|
||||
<div className="swiper-button-next snbnb15"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user