This commit is contained in:
longdayi 2025-02-22 21:07:17 +08:00
commit 1f60884703
6 changed files with 85 additions and 17 deletions

View File

@ -0,0 +1,54 @@
import React, { useRef, useState } from "react";
interface CollapsibleContentProps {
content: string;
maxHeight?: number;
}
const CollapsibleContent: React.FC<CollapsibleContentProps> = ({
content,
maxHeight = 150,
}) => {
const contentWrapperRef = useRef<HTMLDivElement>(null);
const [isExpanded, setIsExpanded] = useState(false);
// Determine if content needs to be collapsed
const shouldCollapse = contentWrapperRef.current
? contentWrapperRef.current.scrollHeight > maxHeight
: false;
return (
<div>
<div
ref={contentWrapperRef}
className={`duration-300 ${
shouldCollapse && !isExpanded
? `max-h-[${maxHeight}px] overflow-hidden relative`
: ""
}`}>
<div
className="ql-editor p-0 space-y-1 leading-relaxed"
dangerouslySetInnerHTML={{
__html: content || "",
}}
/>
{/* Gradient overlay */}
{shouldCollapse && !isExpanded && (
<div className="absolute bottom-0 left-0 right-0 h-20 bg-gradient-to-t from-white to-transparent" />
)}
</div>
{/* Expand/Collapse button */}
{shouldCollapse && (
<button
onClick={() => setIsExpanded(!isExpanded)}
className="mt-2 text-blue-500 hover:text-blue-700">
{isExpanded ? "收起" : "展开"}
</button>
)}
</div>
);
};
export default CollapsibleContent;

View File

@ -1,10 +1,11 @@
// components/CourseDetailDisplayArea.tsx // components/CourseDetailDisplayArea.tsx
import { motion, useScroll, useTransform } from "framer-motion"; import { motion, useScroll, useTransform } from "framer-motion";
import React, { useContext } from "react"; import React, { useContext, useRef, useState } from "react";
import { VideoPlayer } from "@web/src/components/presentation/video-player/VideoPlayer"; import { VideoPlayer } from "@web/src/components/presentation/video-player/VideoPlayer";
import { CourseDetailDescription } from "./CourseDetailDescription/CourseDetailDescription"; import { CourseDetailDescription } from "./CourseDetailDescription/CourseDetailDescription";
import { Course, PostType } from "@nice/common"; import { Course, LectureType, PostType } from "@nice/common";
import { CourseDetailContext } from "./CourseDetailContext"; import { CourseDetailContext } from "./CourseDetailContext";
import CollapsibleContent from "@web/src/components/common/container/CollapsibleContent";
interface CourseDetailDisplayAreaProps { interface CourseDetailDisplayAreaProps {
// course: Course; // course: Course;
@ -21,23 +22,34 @@ export const CourseDetailDisplayArea: React.FC<
const { scrollY } = useScroll(); const { scrollY } = useScroll();
const videoScale = useTransform(scrollY, [0, 200], [1, 0.8]); const videoScale = useTransform(scrollY, [0, 200], [1, 0.8]);
const videoOpacity = useTransform(scrollY, [0, 200], [1, 0.8]); const videoOpacity = useTransform(scrollY, [0, 200], [1, 0.8]);
const contentWrapperRef = useRef(null);
const [isExpanded, setIsExpanded] = useState(false);
const [shouldCollapse, setShouldCollapse] = useState(false);
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-gray-50">
{/* 固定的视频区域 */} {/* 固定的视频区域 */}
{/* 移除 sticky 定位,让视频区域随页面滚动 */} {/* 移除 sticky 定位,让视频区域随页面滚动 */}
<motion.div {lecture?.meta?.type === LectureType.VIDEO && (
style={{ <motion.div
opacity: videoOpacity, style={{
}} opacity: videoOpacity,
className="w-full bg-black"> }}
{lecture.type === PostType.LECTURE && ( className="w-full bg-black">
<div className=" w-full "> <div className=" w-full ">
<VideoPlayer src={videoSrc} poster={videoPoster} /> <VideoPlayer src={videoSrc} poster={videoPoster} />
</div> </div>
)} </motion.div>
</motion.div> )}
{lecture?.meta?.type === LectureType.ARTICLE && (
<div className="flex justify-center w-full my-2">
<div className="w-4/5 bg-white shadow-md rounded-lg border border-gray-200 p-6 ">
<CollapsibleContent
content={lecture?.content || ""}
maxHeight={150} // Optional, defaults to 150
/>
</div>
</div>
)}
{/* 课程内容区域 */} {/* 课程内容区域 */}
<motion.div <motion.div
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}

View File

@ -51,7 +51,6 @@ export const CourseDetailHeader = () => {
123 123
</Button> </Button>
<nav className="flex items-center space-x-4"> <nav className="flex items-center space-x-4">
{/* 添加你的导航项目 */}
<button className="px-4 py-2 rounded-full bg-blue-500 text-white hover:bg-blue-600 transition-colors"> <button className="px-4 py-2 rounded-full bg-blue-500 text-white hover:bg-blue-600 transition-colors">
</button> </button>

View File

@ -83,7 +83,7 @@ export const CourseSyllabus: React.FC<CourseSyllabusProps> = ({
<div className="flex-1 overflow-y-auto p-4"> <div className="flex-1 overflow-y-auto p-4">
<div className="space-y-4"> <div className="space-y-4">
{sections.map((section) => ( {sections.map((section, index) => (
<SectionItem <SectionItem
key={section.id} key={section.id}
ref={(el) => ref={(el) =>
@ -91,6 +91,7 @@ export const CourseSyllabus: React.FC<CourseSyllabusProps> = ({
section.id section.id
] = el) ] = el)
} }
index={index + 1}
section={section} section={section}
isExpanded={expandedSections.includes( isExpanded={expandedSections.includes(
section.id section.id

View File

@ -7,6 +7,7 @@ import { LectureItem } from "./LectureItem";
// components/CourseSyllabus/SectionItem.tsx // components/CourseSyllabus/SectionItem.tsx
interface SectionItemProps { interface SectionItemProps {
section: SectionDto; section: SectionDto;
index?: number;
isExpanded: boolean; isExpanded: boolean;
onToggle: (sectionId: string) => void; onToggle: (sectionId: string) => void;
onLectureClick: (lectureId: string) => void; onLectureClick: (lectureId: string) => void;
@ -14,7 +15,7 @@ interface SectionItemProps {
} }
export const SectionItem = React.forwardRef<HTMLDivElement, SectionItemProps>( export const SectionItem = React.forwardRef<HTMLDivElement, SectionItemProps>(
({ section, isExpanded, onToggle, onLectureClick }, ref) => ( ({ section, index, isExpanded, onToggle, onLectureClick }, ref) => (
<motion.div <motion.div
ref={ref} ref={ref}
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
@ -26,9 +27,9 @@ export const SectionItem = React.forwardRef<HTMLDivElement, SectionItemProps>(
onClick={() => onToggle(section.id)}> onClick={() => onToggle(section.id)}>
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<span className="text-lg font-medium text-gray-700"> <span className="text-lg font-medium text-gray-700">
{Math.floor(section.order)} {index}
</span> </span>
<div> <div className="flex flex-col items-start">
<h3 className="text-left font-medium text-gray-900"> <h3 className="text-left font-medium text-gray-900">
{section.title} {section.title}
</h3> </h3>

View File

@ -43,6 +43,7 @@ export type PostDto = Post & {
}; };
export type LectureMeta = { export type LectureMeta = {
type?: string;
videoUrl?: string; videoUrl?: string;
videoThumbnail?: string; videoThumbnail?: string;
}; };