training_data/apps/web/src/app/main/home/components/FeaturedTeachersSection.tsx

223 lines
7.4 KiB
TypeScript
Raw Normal View History

2025-02-06 16:32:31 +08:00
import React, { useRef } from 'react';
import { Typography, Tag, Carousel } from 'antd';
import { StarFilled, UserOutlined, ReadOutlined, LeftOutlined, RightOutlined } from '@ant-design/icons';
const { Title, Text } = Typography;
interface Teacher {
name: string;
title: string;
avatar: string;
courses: number;
students: number;
rating: number;
description: string;
}
const featuredTeachers: Teacher[] = [
{
name: '张教授',
title: '资深前端开发专家',
avatar: '/images/teacher1.jpg',
courses: 12,
students: 25000,
rating: 4.9,
description: '前 BAT 高级工程师10年+开发经验'
},
{
name: '李教授',
title: '算法与数据结构专家',
avatar: '/images/teacher2.jpg',
courses: 8,
students: 18000,
rating: 4.8,
description: '计算机博士专注算法教育8年'
},
{
name: '王博士',
title: '人工智能研究员',
avatar: '/images/teacher3.jpg',
courses: 15,
students: 30000,
rating: 4.95,
description: '人工智能领域专家曾主导多个大型AI项目'
},
{
name: '陈教授',
title: '云计算架构师',
avatar: '/images/teacher4.jpg',
courses: 10,
students: 22000,
rating: 4.85,
description: '知名云服务提供商技术总监,丰富的实战经验'
},
{
name: '郑老师',
title: '移动开发专家',
avatar: '/images/teacher5.jpg',
courses: 14,
students: 28000,
rating: 4.88,
description: '资深移动端开发者,著名互联网公司技术专家'
}
];
const generateGradientColors = (name: string) => {
// 优化的哈希函数
const hash = name.split('').reduce((acc, char, index) => {
return char.charCodeAt(0) + ((acc << 5) - acc) + index;
}, 0);
// 定义蓝色色相范围210-240
const blueHueStart = 210;
const blueHueRange = 30;
// 基础蓝色色相 - 将哈希值映射到蓝色范围内
const baseHue = blueHueStart + Math.abs(hash % blueHueRange);
// 生成第二个蓝色色相,保持在蓝色范围内
let secondHue = baseHue + 15; // 在基础色相的基础上略微偏移
if (secondHue > blueHueStart + blueHueRange) {
secondHue -= blueHueRange;
}
// 基于输入字符串的特征调整饱和度和亮度
const nameLength = name.length;
const saturation = Math.max(65, Math.min(85, 75 + (nameLength % 10))); // 65-85%范围
const lightness = Math.max(45, Math.min(65, 55 + (hash % 10))); // 45-65%范围
// 为第二个颜色稍微调整饱和度和亮度,创造层次感
const saturation2 = Math.max(60, saturation - 5);
const lightness2 = Math.min(70, lightness + 5);
return {
from: `hsl(${Math.round(baseHue)}, ${Math.round(saturation)}%, ${Math.round(lightness)}%)`,
to: `hsl(${Math.round(secondHue)}, ${Math.round(saturation2)}%, ${Math.round(lightness2)}%)`
};
};
const FeaturedTeachersSection: React.FC = () => {
const carouselRef = useRef<any>(null);
const settings = {
dots: true,
infinite: true,
speed: 500,
slidesToShow: 4,
slidesToScroll: 1,
autoplay: true,
autoplaySpeed: 5000,
responsive: [
{
breakpoint: 1024,
settings: {
slidesToShow: 2,
slidesToScroll: 1
}
},
{
breakpoint: 640,
settings: {
slidesToShow: 1,
slidesToScroll: 1
}
}
]
};
const TeacherCard = ({ teacher }: { teacher: Teacher }) => {
const gradientColors = generateGradientColors(teacher.name);
return (
<div className="p-8">
<div className="bg-white rounded-2xl shadow-[0_4px_20px_-2px_rgba(0,0,0,0.1)] overflow-hidden transform transition-all duration-300 hover:shadow-[0_8px_30px_-4px_rgba(0,0,0,0.15)] hover:-translate-y-1">
<div className="relative h-48" style={{
background: `linear-gradient(to right, ${gradientColors.from}, ${gradientColors.to})`
}}>
<img
src={teacher.avatar}
alt={teacher.name}
className="absolute left-1/2 bottom-0 transform -translate-x-1/2 translate-y-1/2 w-24 h-24 rounded-full border-4 border-white object-cover shadow-lg"
/>
</div>
<div className="pt-16 p-6 min-h-[280px] flex flex-col">
<div className="text-center mb-4">
<Title level={4} className="mb-1">
{teacher.name}
</Title>
<Tag color="blue" className="text-sm">
{teacher.title}
</Tag>
</div>
<Text type="secondary" className="block text-center mb-6 line-clamp-2 min-h-[3rem]">
{teacher.description}
</Text>
<div className="grid grid-cols-3 gap-4 border-t pt-4">
<div className="text-center">
<div className="flex items-center justify-center gap-1 text-blue-600">
<ReadOutlined className="text-lg" />
<span className="font-bold">{teacher.courses}</span>
</div>
<Text type="secondary" className="text-sm"></Text>
</div>
<div className="text-center border-x">
<div className="flex items-center justify-center gap-1 text-blue-600">
<UserOutlined className="text-lg" />
<span className="font-bold">{(teacher.students / 1000).toFixed(1)}k</span>
</div>
<Text type="secondary" className="text-sm"></Text>
</div>
<div className="text-center">
<div className="flex items-center justify-center gap-1 text-blue-600">
<StarFilled className="text-lg" />
<span className="font-bold">{teacher.rating}</span>
</div>
<Text type="secondary" className="text-sm"></Text>
</div>
</div>
</div>
</div>
</div>
);
};
return (
<section className="py-20 px-4 bg-gradient-to-b from-gray-50/50 to-transparent">
<div className="max-w-screen-2xl mx-auto">
<div className="relative z-10 text-center mb-16">
<Title level={2} className="font-bold text-4xl mb-4">
</Title>
<Text type="secondary" className="text-lg">
</Text>
</div>
<div className="relative group">
<Carousel ref={carouselRef} {...settings}>
{featuredTeachers.map((teacher, index) => (
<TeacherCard key={index} teacher={teacher} />
))}
</Carousel>
<button
onClick={() => carouselRef.current?.prev()}
className="absolute left-0 top-1/2 -translate-y-1/2 -ml-4 w-10 h-10 bg-white rounded-full shadow-lg flex items-center justify-center hover:bg-gray-50 transition-colors opacity-0 group-hover:opacity-100"
>
<LeftOutlined />
</button>
<button
onClick={() => carouselRef.current?.next()}
className="absolute right-0 top-1/2 -translate-y-1/2 -mr-4 w-10 h-10 bg-white rounded-full shadow-lg flex items-center justify-center hover:bg-gray-50 transition-colors opacity-0 group-hover:opacity-100"
>
<RightOutlined />
</button>
</div>
</div>
</section>
);
};
export default FeaturedTeachersSection;