hookehuyr

refactor(ui): 删除不再使用的UI组件

清理代码库中不再使用的UI组件,包括FrostedGlass、SearchBar、CourseCard、GradientHeader、LiveStreamCard、SummerCampCard和ActivityCard。这些组件在当前项目中已无作用,删除以保持代码简洁。
import React from 'react';
import { Link } from 'react-router-dom';
import FrostedGlass from './FrostedGlass';
/**
* ActivityCard component displays an activity item in the activities list
*
* @param {Object} props - Component props
* @param {Object} props.activity - Activity data
* @returns {JSX.Element} ActivityCard component
*/
const ActivityCard = ({ activity }) => {
// Function to get the appropriate status class
const getStatusClass = (status) => {
switch (status) {
case '活动中':
return 'bg-blue-100 text-blue-600';
case '进行中':
return 'bg-green-100 text-green-600';
case '即将开始':
return 'bg-orange-100 text-orange-600';
case '已结束':
return 'bg-gray-100 text-gray-600';
default:
return 'bg-gray-100 text-gray-600';
}
};
return (
<Link to={`/activities/${activity.id}`}>
<FrostedGlass className="flex overflow-hidden rounded-xl shadow-sm">
{/* Activity Image */}
<div className="w-1/3 h-28 relative">
<img
src={activity.imageUrl}
alt={activity.title}
className="w-full h-full object-cover"
/>
{activity.isHot && (
<div className="absolute top-0 left-0 bg-red-500 text-white text-xs px-2 py-0.5">
热门
</div>
)}
</div>
{/* Activity Info */}
<div className="flex-1 p-3 flex flex-col justify-between">
<div>
<h3 className="font-medium text-base mb-1 line-clamp-1">{activity.title}</h3>
{/* Status Tags */}
<div className="flex items-center space-x-2 mb-1">
<span className={`px-2 py-0.5 rounded-full text-xs ${getStatusClass(activity.status)}`}>
{activity.status}
</span>
{activity.isFree && (
<span className="px-2 py-0.5 rounded-full text-xs bg-green-100 text-green-600">
免费
</span>
)}
</div>
</div>
{/* Location and Time */}
<div className="text-xs text-gray-500 space-y-1">
<div className="flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" className="h-3.5 w-3.5 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
<span>{activity.location}</span>
</div>
<div className="flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" className="h-3.5 w-3.5 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
<span>{activity.period}</span>
</div>
</div>
{/* Bottom Info Section */}
<div className="mt-1 flex items-center justify-between">
{activity.price ? (
<div className="flex items-baseline">
<span className="text-red-500 font-medium">¥{activity.price}</span>
{activity.originalPrice && (
<span className="text-xs text-gray-400 ml-1 line-through">¥{activity.originalPrice}</span>
)}
</div>
) : (
<div></div> // Empty div for spacing when no price
)}
<div className="flex items-center text-xs text-gray-500">
<svg xmlns="http://www.w3.org/2000/svg" className="h-3.5 w-3.5 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" />
</svg>
<span>{activity.participantsCount || '15'}/{activity.maxParticipants || '30'}</span>
</div>
</div>
</div>
</FrostedGlass>
</Link>
);
};
export default ActivityCard;
\ No newline at end of file
import React from 'react';
import { Link } from 'react-router-dom';
/**
* CourseCard component displays a course item in the course list
*
* @param {Object} props - Component props
* @param {Object} props.course - Course data
* @returns {JSX.Element} CourseCard component
*/
const CourseCard = ({ course }) => {
return (
<Link to={`/courses/${course.id}`} className="flex bg-white rounded-lg overflow-hidden shadow-sm">
<div className="w-1/3 h-28">
<img
src={course.imageUrl}
alt={course.title}
className="w-full h-full object-cover"
/>
</div>
<div className="flex-1 p-3 flex flex-col justify-between">
<div>
<h3 className="font-medium text-sm mb-1 line-clamp-2">{course.title}</h3>
<div className="text-gray-500 text-xs">{course.subtitle}</div>
</div>
<div className="flex justify-between items-end mt-1">
<div className="text-orange-500 font-semibold">¥{course.price}</div>
<div className="text-gray-400 text-xs">
{course.subscribers}人订阅
</div>
</div>
<div className="text-gray-400 text-xs">
已更新{course.updatedLessons}期 | {course.subscribers}人订阅
</div>
</div>
</Link>
);
};
export default CourseCard;
\ No newline at end of file
import React from 'react';
/**
* FrostedGlass component creates a container with a frosted glass effect
* using backdrop-filter blur and a semi-transparent white background.
*
* @param {Object} props - Component props
* @param {ReactNode} props.children - Child elements
* @param {string} props.className - Additional CSS classes
* @returns {JSX.Element} FrostedGlass component
*/
const FrostedGlass = ({ children, className = '' }) => {
return (
<div
className={`bg-white/20 backdrop-blur-md rounded-xl border border-white/30
shadow-lg ${className}`}
>
{children}
</div>
);
};
export default FrostedGlass;
\ No newline at end of file
import React from 'react';
/**
* GradientHeader component for page headers with gradient background
* and navigation elements.
*
* @param {Object} props - Component props
* @param {string} props.title - Header title
* @param {boolean} props.showBackButton - Whether to show back button
* @param {Function} props.onBack - Back button click handler
* @param {ReactNode} props.rightContent - Content to display on the right side
* @returns {JSX.Element} GradientHeader component
*/
const GradientHeader = ({ title, showBackButton = false, onBack, rightContent }) => {
return (
<header className="bg-gradient-to-r from-green-50 to-blue-50 p-4 relative">
<div className="flex items-center justify-between">
{showBackButton && (
<button
onClick={onBack}
className="p-2 rounded-full bg-white/30 backdrop-blur-sm"
aria-label="返回"
>
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5 text-gray-700" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
</svg>
</button>
)}
<h1 className={`text-xl font-medium text-center ${showBackButton ? 'flex-1' : ''}`}>
{title}
</h1>
{rightContent && <div>{rightContent}</div>}
</div>
</header>
);
};
export default GradientHeader;
\ No newline at end of file
import React from 'react';
import { Link } from 'react-router-dom';
/**
* LiveStreamCard component displays a live stream in the courses page
*
* @param {Object} props - Component props
* @param {Object} props.stream - Stream data
* @returns {JSX.Element} LiveStreamCard component
*/
const LiveStreamCard = ({ stream }) => {
return (
<div className="relative">
{/* Live indicator */}
<div className="absolute top-2 left-2 bg-red-500/90 text-white text-xs px-2 py-1 rounded flex items-center z-10">
<div className="w-2 h-2 bg-white rounded-full mr-1 animate-pulse"></div>
直播中
</div>
<Link to={`/courses/${stream.id}`} className="block rounded-lg overflow-hidden shadow-sm relative">
<img
src={stream.imageUrl}
alt={stream.title}
className="w-full h-28 object-cover"
/>
{/* Gradient overlay */}
<div className="absolute inset-0 bg-gradient-to-b from-transparent to-black/60"></div>
{/* Stream info */}
<div className="absolute bottom-2 left-2 right-2">
<h3 className="text-white text-sm font-medium">{stream.title}{stream.subtitle}</h3>
<div className="flex items-center mt-1">
<div className="flex -space-x-2">
<div className="w-5 h-5 rounded-full bg-gray-300 border border-white"></div>
<div className="w-5 h-5 rounded-full bg-gray-400 border border-white"></div>
<div className="w-5 h-5 rounded-full bg-gray-500 border border-white"></div>
</div>
<span className="text-white text-xs ml-1">{stream.viewers}人在看</span>
<button className="ml-auto bg-green-500 text-white text-xs px-2 py-1 rounded">
立即观看
</button>
</div>
</div>
</Link>
</div>
);
};
export default LiveStreamCard;
\ No newline at end of file
import React from 'react';
import FrostedGlass from './FrostedGlass';
/**
* SearchBar component with frosted glass effect
*
* @param {Object} props - Component props
* @param {string} props.placeholder - Placeholder text
* @param {Function} props.onSearch - Search callback function
* @returns {JSX.Element} SearchBar component
*/
const SearchBar = ({ placeholder = '搜索', onSearch }) => {
return (
<FrostedGlass className="px-4 py-2 mx-4 my-3 flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5 text-gray-400 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
<input
type="text"
placeholder={placeholder}
className="bg-transparent outline-none flex-1 text-gray-700 placeholder-gray-400"
onChange={(e) => onSearch && onSearch(e.target.value)}
/>
</FrostedGlass>
);
};
export default SearchBar;
\ No newline at end of file
import React from 'react';
import PropTypes from 'prop-types';
/**
* SummerCampCard component - displays summer camp information with image background
* @param {Object} props - Component props
* @returns {JSX.Element} - Rendered component
*/
const SummerCampCard = ({
title = "大国少年-世界正东方",
subtitle = "亲子夏令营",
badge = "亲子夏令营",
price = "¥1280",
discount = "限时优惠",
episodes = 16,
subscribers = 1140
}) => {
return (
<div className="relative overflow-hidden rounded-b-3xl shadow-lg">
{/* Background image with overlay */}
<div
className="absolute inset-0 z-0 bg-cover bg-center"
style={{
backgroundImage: `url('/assets/images/summer-camp.jpg')`,
filter: 'brightness(0.4)'
}}
></div>
{/* Gradient overlay */}
<div className="absolute inset-0 z-1 bg-gradient-to-b from-red-500/70 to-red-600/90"></div>
{/* Content */}
<div className="relative z-10 p-4">
<div className="bg-white/10 backdrop-blur-sm rounded-lg p-3 mb-3 inline-block">
<div className="text-white font-semibold">{badge}</div>
</div>
<h1 className="text-2xl text-white font-bold mb-1">{title}</h1>
<h2 className="text-lg text-white/90">{subtitle}</h2>
<div className="mt-4 flex justify-between items-center">
<div className="text-orange-300 font-bold text-2xl">{price}</div>
<div className="bg-orange-500/30 text-orange-100 text-xs px-3 py-1 rounded-full">{discount}</div>
</div>
<div className="flex justify-between text-xs text-white/80 mt-3">
<div>已更新{episodes}</div>
<div>{subscribers}人订阅</div>
</div>
</div>
</div>
);
};
SummerCampCard.propTypes = {
title: PropTypes.string,
subtitle: PropTypes.string,
badge: PropTypes.string,
price: PropTypes.string,
discount: PropTypes.string,
episodes: PropTypes.number,
subscribers: PropTypes.number
};
export default SummerCampCard;
\ No newline at end of file