128 lines
4.2 KiB
JavaScript
128 lines
4.2 KiB
JavaScript
"use client";
|
||
import React, { useEffect, useState } from "react";
|
||
import axios from "axios";
|
||
|
||
/**
|
||
* Product category sidebar component
|
||
* @param {string} componentName - component name
|
||
* @param {string} className - additional class names
|
||
*/
|
||
export default 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 || {};
|
||
// 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({
|
||
title: metaData.title || "Product Categories",
|
||
subtitle: metaData.subtitle || "",
|
||
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);
|
||
}}
|
||
>
|
||
<a
|
||
className="!text-[#60697b] hover:!text-[#22b573]"
|
||
href={`/${currentPath.join('/')}`}
|
||
onClick={e => e.stopPropagation()} // 阻止冒泡,点击a只跳转
|
||
>
|
||
{item.name}
|
||
</a>
|
||
{hasChildren && (
|
||
<span className="ml-2 text-gray-400 hover:text-[#22b573]">{'>'}</span>
|
||
)}
|
||
</div>
|
||
|
||
{hasChildren && isActive && (
|
||
<div className="pl-4 sub-category">
|
||
<ul className="list-none pl-0">
|
||
{item.children.map((child) => (
|
||
<li key={child.slug} className="py-1">
|
||
<a
|
||
className="!text-[#60697b] hover:!text-[#22b573] text-sm"
|
||
href={`/${currentPath.join('/')}/${child.slug}`}
|
||
onClick={e => e.stopPropagation()}
|
||
>
|
||
{child.name}
|
||
</a>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
</div>
|
||
)}
|
||
</li>
|
||
);
|
||
})}
|
||
</ul>
|
||
);
|
||
};
|
||
|
||
return (
|
||
<aside className={`hidden md:block lg:w-3/12 xl:w-3/12 sidebar px-8 py-8 bg-white rounded-xl shadow-md ${className}`}>
|
||
<div className="widget">
|
||
<h4 className="widget-title !mb-3">{data.title}</h4>
|
||
{data.subtitle && <div className="text-xs text-gray-500 mb-2">{data.subtitle}</div>}
|
||
{renderCategories(data.items, [data.slug])}
|
||
</div>
|
||
</aside>
|
||
);
|
||
}
|