This commit is contained in:
ditiqi 2025-02-25 19:30:59 +08:00
parent dc8bc72687
commit 9319f112b4
1 changed files with 168 additions and 190 deletions

View File

@ -1,157 +1,141 @@
import React, { useState, useCallback, useEffect, useMemo } from 'react'; import React, { useState, useCallback, useEffect, useMemo } from "react";
import { Typography, Button, Spin } from 'antd'; import { Typography, Button, Spin } from "antd";
import { stringToColor, TaxonomySlug, TermDto } from '@nice/common'; import { stringToColor, TaxonomySlug, TermDto } from "@nice/common";
import { api,} from '@nice/client'; import { api } from "@nice/client";
import { ControlOutlined } from '@ant-design/icons'; import { ControlOutlined } from "@ant-design/icons";
import { useNavigate, useSearchParams } from 'react-router-dom'; import { useNavigate, useSearchParams } from "react-router-dom";
const { Title, Text } = Typography; const { Title, Text } = Typography;
interface CourseCategory { interface CourseCategory {
name: string; name: string;
count: number; count: number;
description: string; description: string;
} }
// const courseCategories: CourseCategory[] = [
// {
// name: '计算机基础',
// count: 120,
// description: '计算机组成原理、操作系统、网络等基础知识'
// },
// {
// name: '编程语言',
// count: 85,
// description: 'Python、Java、JavaScript等主流编程语言'
// },
// {
// name: '人工智能',
// count: 65,
// description: '机器学习、深度学习、自然语言处理等前沿技术'
// },
// {
// name: '数据科学',
// count: 45,
// description: '数据分析、数据可视化、商业智能等'
// },
// {
// name: '云计算',
// count: 38,
// description: '云服务、容器化、微服务架构等'
// },
// {
// name: '网络安全',
// count: 42,
// description: '网络安全基础、渗透测试、安全防护等'
// }
// ];
const CategorySection = () => { const CategorySection = () => {
const [hoveredIndex, setHoveredIndex] = useState<number | null>(null); const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
const [showAll, setShowAll] = useState(false); const [showAll, setShowAll] = useState(false);
//获得分类 //获得分类
const {data:courseCategoriesData,isLoading} :{data:TermDto[],isLoading:boolean}= api.term.findMany.useQuery({ const {
where:{ data: courseCategoriesData,
taxonomy: { isLoading,
slug:TaxonomySlug.CATEGORY }: { data: TermDto[]; isLoading: boolean } = api.term.findMany.useQuery({
} where: {
}, taxonomy: {
include:{ slug: TaxonomySlug.CATEGORY,
children :true },
}, },
orderBy: { include: {
createdAt: 'desc', // 按创建时间降序排列 children: true,
}, },
take:8 orderBy: {
}) createdAt: "desc", // 按创建时间降序排列
// 分类展示 },
const [displayedCategories,setDisplayedCategories] = useState<TermDto[]>([]) take: 8,
useEffect(() => { });
console.log(courseCategoriesData); // 分类展示
// 如果 showAll 为 true则显示所有分类数据 const [displayedCategories, setDisplayedCategories] = useState<TermDto[]>(
// 如果 showAll 为 false则只显示前 8 个分类数据, []
if(!isLoading){ );
if(showAll){ useEffect(() => {
setDisplayedCategories(courseCategoriesData) console.log(courseCategoriesData);
}else{ // 如果 showAll 为 true则显示所有分类数据
setDisplayedCategories(courseCategoriesData.slice(0,8)) // 如果 showAll 为 false则只显示前 8 个分类数据,
} if (!isLoading) {
} if (showAll) {
}, [courseCategoriesData,showAll]); setDisplayedCategories(courseCategoriesData);
// const courseCategories: CourseCategory[] = useMemo(() => { } else {
// return data?.map((term) => ({ setDisplayedCategories(courseCategoriesData.slice(0, 8));
// name: term.name, }
// count: term.hasChildren ? term.children.length : 0, }
// description: term.description }, [courseCategoriesData, showAll]);
// })) || []; // const courseCategories: CourseCategory[] = useMemo(() => {
// },[data]) // return data?.map((term) => ({
const handleMouseEnter = useCallback((index: number) => { // name: term.name,
setHoveredIndex(index); // count: term.hasChildren ? term.children.length : 0,
}, []); // description: term.description
// })) || [];
// },[data])
const handleMouseEnter = useCallback((index: number) => {
setHoveredIndex(index);
}, []);
const handleMouseLeave = useCallback(() => { const handleMouseLeave = useCallback(() => {
setHoveredIndex(null); setHoveredIndex(null);
}, []); }, []);
const navigate = useNavigate() const navigate = useNavigate();
return ( return (
<section className="py-32 relative overflow-hidden"> <section className="py-32 relative overflow-hidden">
<div className="max-w-screen-2xl mx-auto px-4 relative"> <div className="max-w-screen-2xl mx-auto px-4 relative">
<div className="text-center mb-24"> <div className="text-center mb-24">
<Title level={2} className="font-bold text-5xl mb-6 bg-gradient-to-r from-gray-900 via-gray-700 to-gray-800 bg-clip-text text-transparent motion-safe:animate-gradient-x"> <Title
level={2}
</Title> className="font-bold text-5xl mb-6 bg-gradient-to-r from-gray-900 via-gray-700 to-gray-800 bg-clip-text text-transparent motion-safe:animate-gradient-x">
<Text type="secondary" className="text-xl font-light">
</Title>
</Text> <Text type="secondary" className="text-xl font-light">
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6"> </Text>
{ </div>
isLoading ? <Spin></Spin> : <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
(displayedCategories.map((category, index) => { {isLoading ? (
const categoryColor = stringToColor(category.name); <Spin></Spin>
const isHovered = hoveredIndex === index; ) : (
displayedCategories.map((category, index) => {
return ( const categoryColor = stringToColor(category.name);
<div const isHovered = hoveredIndex === index;
key={index}
className="group relative min-h-[130px] rounded-2xl transition-all duration-700 ease-out cursor-pointer will-change-transform hover:-translate-y-2" return (
onMouseEnter={() => handleMouseEnter(index)} <div
onMouseLeave={handleMouseLeave} key={index}
role="button" className="group relative min-h-[130px] rounded-2xl transition-all duration-700 ease-out cursor-pointer will-change-transform hover:-translate-y-2"
tabIndex={0} onMouseEnter={() => handleMouseEnter(index)}
aria-label={`查看${category.name}课程类别`} onMouseLeave={handleMouseLeave}
onClick={()=>{ role="button"
console.log(category.name) tabIndex={0}
navigate(`/courses?category=${category.name}`) aria-label={`查看${category.name}课程类别`}
window.scrollTo({ top: 0, behavior: 'smooth' }) onClick={() => {
}} console.log(category.name);
> navigate(
<div className="absolute -inset-0.5 bg-gradient-to-r from-gray-200 to-gray-300 opacity-50 rounded-2xl transition-all duration-700 group-hover:opacity-75" /> `/courses?category=${category.name}`
<div );
className={`absolute inset-0 rounded-2xl bg-gradient-to-br from-white to-gray-50 shadow-lg transition-all duration-700 ease-out ${ window.scrollTo({
isHovered ? 'scale-[1.02] bg-opacity-95' : 'scale-100 bg-opacity-90' top: 0,
}`} behavior: "smooth",
/> });
<div }}>
className={`absolute inset-0 rounded-2xl transition-all duration-700 ease-out ${ <div className="absolute -inset-0.5 bg-gradient-to-r from-gray-200 to-gray-300 opacity-50 rounded-2xl transition-all duration-700 group-hover:opacity-75" />
isHovered ? 'shadow-[0_8px_30px_rgb(0,0,0,0.12)]' : 'shadow-none opacity-0' <div
}`} className={`absolute inset-0 rounded-2xl bg-gradient-to-br from-white to-gray-50 shadow-lg transition-all duration-700 ease-out ${
/> isHovered
<div ? "scale-[1.02] bg-opacity-95"
className={`absolute top-0 left-1/2 -translate-x-1/2 h-1 rounded-full transition-all duration-500 ease-out ${ : "scale-100 bg-opacity-90"
false ? 'w-36 opacity-90' : 'w-24 opacity-60' }`}
}`} />
style={{ backgroundColor: categoryColor }} <div
/> className={`absolute inset-0 rounded-2xl transition-all duration-700 ease-out ${
<div className="relative w-full h-full p-6"> isHovered
<div className="flex w-2/3 absolute left-1/2 -translate-x-1/2 top-1/2 -translate-y-1/2 flex-col space-y-4 mb-4"> ? "shadow-[0_8px_30px_rgb(0,0,0,0.12)]"
<Text strong className="text-xl font-medium tracking-wide"> : "shadow-none opacity-0"
{category.name} }`}
</Text> />
{/* <span <div
className={`absolute top-0 left-1/2 -translate-x-1/2 h-1 rounded-full transition-all duration-500 ease-out `}
style={{
backgroundColor: categoryColor,
}}
/>
<div className="relative w-full h-full p-6">
<div className="flex w-2/3 absolute left-1/2 -translate-x-1/2 top-1/2 -translate-y-1/2 flex-col space-y-4 mb-4">
<Text
strong
className="text-xl font-medium tracking-wide">
{category.name}
</Text>
{/* <span
className={`px-3 py-1 rounded-full text-sm w-fit font-medium transition-all duration-500 ease-out ${ className={`px-3 py-1 rounded-full text-sm w-fit font-medium transition-all duration-500 ease-out ${
isHovered ? 'shadow-md scale-105' : '' isHovered ? 'shadow-md scale-105' : ''
}`} }`}
@ -162,51 +146,45 @@ const CategorySection = () => {
> >
{category.children.length} {category.children.length}
</span> */} </span> */}
</div> </div>
<Text type="secondary" className="block text-sm leading-relaxed opacity-90"> <Text
{category.description} type="secondary"
</Text> className="block text-sm leading-relaxed opacity-90">
<div {category.description}
className={` mt-6 absolute bottom-4 right-6 text-sm font-medium flex items-center space-x-2 transition-all duration-500 ease-out ${ </Text>
false ? 'translate-x-2' : '' <div
}`} className={` mt-6 absolute bottom-4 right-6 text-sm font-medium flex items-center space-x-2 transition-all duration-500 ease-out `}
style={{ color: categoryColor }} style={{ color: categoryColor }}>
> <span></span>
<span></span> <span
<span className={`transform transition-all duration-500 ease-out `}>
className={`transform transition-all duration-500 ease-out ${
false ? 'translate-x-2' : '' </span>
}`} </div>
> </div>
</div>
</span> );
</div> })
</div> )}
</div> </div>
); {!isLoading && (
})) <div className="flex justify-center mt-12">
} <Button
type="default"
</div> size="large"
{!isLoading && ( className="px-8 h-12 text-base font-medium hover:shadow-md transition-all duration-300"
<div className="flex justify-center mt-12"> onClick={() => {
<Button //setShowAll(!showAll)
type="default" navigate("/courses");
size="large" window.scrollTo({ top: 0, behavior: "smooth" });
className="px-8 h-12 text-base font-medium hover:shadow-md transition-all duration-300" }}>
onClick={() => { {showAll ? "收起" : "查看更多分类"}
//setShowAll(!showAll) </Button>
navigate("/courses") </div>
window.scrollTo({ top: 0, behavior: 'smooth' }) )}
}} </div>
> </section>
{showAll ? '收起' : '查看更多分类'} );
</Button>
</div>
)}
</div>
</section>
);
}; };
export default CategorySection; export default CategorySection;