重构Hero组件Category组件Banner组件

This commit is contained in:
jingrow 2025-06-23 20:01:24 +08:00
parent fb3bb322ac
commit a15a7e0c18
8 changed files with 103 additions and 148 deletions

View File

@ -12,8 +12,7 @@ const LoadingSpinner = () => (
</div>
);
export const revalidate = 10;
export const dynamicParams = true;
export const revalidate = Number(process.env.JSITE_REVALIDATE_SECONDS) || 3600;
export async function generateStaticParams() {
const slugs = await getAllSlugs();

View File

@ -4,7 +4,7 @@ import CategoryItems from "@/components/homes/home-15/CategoryItems";
import React from "react";
import { getSiteSettings } from "@/utils/siteSettings";
export const revalidate = 3600;
export const revalidate = Number(process.env.JSITE_REVALIDATE_SECONDS) || 3600;
export async function generateMetadata() {
const siteUrl = process.env.SITE_URL;
@ -36,7 +36,7 @@ export default function Page() {
<Hero />
</section>
<section className="w-full xl:w-10/12 xl:mx-auto overflow-x-hidden">
<CategoryItems />
<SwiperItems />
</section>
</>
</div>{" "}

View File

@ -1,35 +1,13 @@
"use client";
import React, { useEffect, useState } from "react";
import Image from "next/image";
import axios from "axios";
import { fetchComponentData, downloadToLocal } from "@/utils/data";
export default function Banner({ componentName = "Banner", className = "" }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
export default async function Banner({ componentName = "Banner", className = "" }) {
const res = await fetchComponentData(componentName);
const data = res.data;
useEffect(() => {
async function fetchData() {
try {
setLoading(true);
const res = await axios.get("/api/get-component-data", {
params: { component_name: componentName },
});
setData(res.data.data);
} catch (err) {
setError(`Failed to fetch ${componentName} data`);
} finally {
setLoading(false);
}
}
fetchData();
}, [componentName]);
if (loading) return null;
if (error) return null;
if (!data) return null;
const bannerImg = data.image;
//
const bannerImg = data.image ? await downloadToLocal(data.image) : "";
return (
<section
@ -38,7 +16,7 @@ export default function Banner({ componentName = "Banner", className = "" }) {
>
<div className="container pt-36 xl:pt-[12.5rem] lg:pt-[12.5rem] md:pt-[12.5rem] pb-32 xl:pb-40 lg:pb-40 md:pb-40 !text-center">
<div className="flex flex-wrap mx-[-15px]">
<div className="md:w-10/12 lg:w-8/12 xl:w-7/12 xxl:w-6/12 w-full flex-[0_0_auto] !px-[15px] max-w-full !mx-auto">
<div className="md:w-10/12 lg:w-8/12 xl:w-7/12 w-full flex-[0_0_auto] !px-[15px] max-w-full !mx-auto">
<h1 className="!text-[calc(1.365rem_+_1.38vw)] font-bold !leading-[1.2] xl:!text-[2.4rem] !text-white !mb-3">
{data.title}
</h1>

View File

@ -1,37 +1,12 @@
"use client";
import React, { useEffect, useState } from "react";
import Image from "next/image";
import AnimatedText from "@/components/common/AnimatedText";
import Link from "next/link";
import axios from "axios";
export default function Hero() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchData() {
try {
setLoading(true);
const res = await axios.get("/api/get-component-data", {
params: { component_name: "Hero" },
});
setData(res.data.data);
} catch (err) {
setError("获取Features数据失败");
} finally {
setLoading(false);
}
}
fetchData();
}, []);
if (loading) return <div className="text-center py-20">加载中...</div>;
if (error) return null;
export default function HeroUI({ data }) {
if (!data) return null;
const bgImage = data.bg_image || "/assets/img/photos/blurry.png";
const bgImage = data.image || "/assets/img/photos/blurry.png";
const title = data.title || "";
const subtitle = data.subtitle || "";
const buttonText = data.button_text || "MORE";
@ -39,7 +14,6 @@ export default function Hero() {
const buttonText2 = data.button_2_text || "MORE";
const buttonLink2 = data.button_2_link || "#";
return (
<div className="container pt-36 xl:py-[12.5rem] lg:py-[12.5rem] md:py-[12.5rem] !text-center !relative">
<div

View File

@ -0,0 +1,13 @@
import { fetchComponentData } from "@/utils/data";
import HeroUI from "./HeroUI";
export default async function Hero() {
const result = await fetchComponentData("Hero");
if (result.error || !result.data) {
if(result.error) console.error("Failed to fetch Hero data:", result.error);
return null;
}
return <HeroUI data={result.data} />;
}

View File

@ -1,88 +1,52 @@
"use client";
import React, { useEffect, useState } from "react";
import axios from "axios";
import { fetchComponentData, fetchCategoryData } from "@/utils/data";
/**
* Product category sidebar component
* @param {string} componentName - component name
* @param {string} className - additional class names
*/
export default function Category({
export default async function Category({
componentName = "Category",
className = "",
}) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [activeCategory, setActiveCategory] = useState(null);
useEffect(() => {
async function fetchData() {
try {
setLoading(true);
// 1.
const metaRes = await axios.get("/api/get-component-data", {
params: { component_name: componentName },
});
const metaData = metaRes.data.data || {};
const metaRes = await fetchComponentData(componentName);
const metaData = metaRes.data;
// componentName
if (!metaData || !metaData.p1 || !metaData.p2) {
return null;
}
// 2.
if (metaData.p1 && metaData.p2) {
const catRes = await axios.get("/api/get-category", {
params: { pagetype: metaData.p1, name: metaData.p2 },
});
const categoryData = catRes.data.message?.data || {};
setData({
let data = {
title: metaData.title || "Product Categories",
subtitle: metaData.subtitle || "",
slug: "",
items: [],
};
const catRes = await fetchCategoryData({ pagetype: metaData.p1, name: metaData.p2 });
const categoryData = catRes.data || {};
data = {
...data,
slug: categoryData.slug,
items: categoryData.children || [],
});
}
} catch (err) {
console.error("获取分类数据失败:", err);
setError("Failed to fetch category data");
} finally {
setLoading(false);
}
}
fetchData();
}, [componentName]);
if (loading) return null;
if (error) return null;
if (!data) return null;
const handleCategoryClick = (categorySlug) => {
if (activeCategory === categorySlug) {
setActiveCategory(null);
} else {
setActiveCategory(categorySlug);
}
};
//
const renderCategories = (items, parentSlugs = []) => {
if (!items || !items.length) return null;
return (
<ul className="pl-0 list-none">
{items.map((item) => {
const hasChildren = Array.isArray(item.children) && item.children.length > 0;
const isActive = activeCategory === item.slug;
const currentPath = [...parentSlugs, item.slug];
return (
<li key={item.slug} className="nav-item">
<div
className="flex items-center justify-between cursor-pointer py-2"
onClick={() => {
if (hasChildren) handleCategoryClick(item.slug);
}}
>
<div className="flex items-center justify-between cursor-pointer py-2">
<a
className="!text-[#60697b] hover:!text-[#1fc76f]"
href={`/${currentPath.join('/')}`}
onClick={e => e.stopPropagation()} // a
>
{item.name}
</a>
@ -90,8 +54,7 @@ export default function Category({
<span className="ml-2 text-gray-400 hover:text-[#1fc76f]">{'>'}</span>
)}
</div>
{hasChildren && isActive && (
{hasChildren && (
<div className="pl-4 sub-category">
<ul className="list-none pl-0">
{item.children.map((child) => (
@ -99,7 +62,6 @@ export default function Category({
<a
className="!text-[#60697b] hover:!text-[#1fc76f] text-sm"
href={`/${currentPath.join('/')}/${child.slug}`}
onClick={e => e.stopPropagation()}
>
{child.name}
</a>

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

View File

@ -9,7 +9,7 @@ if (!fs.existsSync(PUBLIC_FILES_DIR)) {
fs.mkdirSync(PUBLIC_FILES_DIR, { recursive: true });
}
async function downloadToLocal(fileUrl) {
export async function downloadToLocal(fileUrl) {
if (!fileUrl) return fileUrl;
try {
let fullUrl = fileUrl;
@ -51,7 +51,7 @@ function extractImageUrlsFromHtml(html) {
return urls;
}
async function processDataItem(item, downloadFiles) {
export async function processDataItem(item, downloadFiles) {
if (!downloadFiles) return item;
if (item.image) {
@ -174,38 +174,67 @@ export async function getAllSlugs() {
export async function fetchComponentData(componentName) {
try {
// We use no-store to ensure data is fresh, suitable for dynamic component settings.
// For more static data, you might adjust the cache policy.
const res = await fetch(`${JINGROW_SERVER_URL}/api/method/jsite.api.v1.get_component_data?component_name=${componentName}`, { cache: 'no-store' });
if (!res.ok) {
const errorText = await res.text();
console.error(`Failed to fetch component data for ${componentName}: ${res.statusText}`, errorText);
const res = await axios.get(
`${JINGROW_SERVER_URL}/api/method/jsite.api.v1.get_component_data`,
{
params: { component_name: componentName },
}
);
return { data: res.data.message?.data || null };
} catch (error) {
if (
error.response &&
(error.response.status === 403 || error.response.status === 404)
) {
return { data: null };
}
const result = await res.json();
return { data: result.message?.data || null };
} catch (error) {
console.error(`Error fetching component data for ${componentName}:`, error);
console.error(
`Error fetching component data for ${componentName}:`,
error.message
);
return { data: null };
}
}
export async function fetchListViewData({ pagetype, category, count }) {
try {
const queryParams = new URLSearchParams({ pagetype });
if (category) queryParams.append("category", category);
if (count !== undefined && count !== null) queryParams.append("count", String(count));
const params = { pagetype };
if (category) params.category = category;
if (count !== undefined && count !== null) params.count = String(count);
const res = await fetch(`${JINGROW_SERVER_URL}/api/method/jsite.api.v1.get_listview_data?${queryParams.toString()}`, { cache: 'no-store' });
if (!res.ok) {
const errorText = await res.text();
console.error(`Failed to fetch list view data: ${res.statusText}`, errorText);
return { data: [] };
}
const result = await res.json();
return { data: result.message?.data || [] };
const res = await axios.get(
`${JINGROW_SERVER_URL}/api/method/jsite.api.v1.get_listview_data`,
{ params }
);
return { data: res.data.message?.data || [] };
} catch (error) {
console.error(`Error fetching list view data:`, error);
if (
error.response &&
(error.response.status === 403 || error.response.status === 404)
) {
return { data: [] };
}
console.error(`Error fetching list view data:`, error.message);
return { data: [] };
}
}
export async function fetchCategoryData({ pagetype, name }) {
try {
const res = await axios.get(
`${JINGROW_SERVER_URL}/api/method/jsite.api.v1.get_category`,
{ params: { pagetype, name } }
);
return { data: res.data.message?.data || null };
} catch (error) {
if (
error.response &&
(error.response.status === 403 || error.response.status === 404)
) {
return { data: null };
}
console.error(`Error fetching category data:`, error.message);
return { data: null };
}
}