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

223 lines
7.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;