This commit is contained in:
ditiqi 2025-02-21 16:11:02 +08:00
parent fb026e1fde
commit 32516187b2
5 changed files with 44 additions and 21 deletions

View File

@ -1,11 +1,12 @@
import { api } from "@nice/client";
import { courseDetailSelect, CourseDto } from "@nice/common";
import { courseDetailSelect, CourseDto, Lecture } from "@nice/common";
import React, { createContext, ReactNode, useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
interface CourseDetailContextType {
editId?: string; // 添加 editId
course?: CourseDto;
lecture?: Lecture;
selectedLectureId?: string | undefined;
setSelectedLectureId?: React.Dispatch<React.SetStateAction<string>>;
isLoading?: boolean;
@ -37,9 +38,16 @@ export function CourseDetailProvider({
const [selectedLectureId, setSelectedLectureId] = useState<
string | undefined
>(undefined);
const { data: lecture, isLoading: lectureIsLoading } = (
api.post as any
).findFirst.useQuery(
{
where: { id: selectedLectureId },
},
{ enabled: Boolean(editId) }
);
useEffect(() => {
navigate(``)
navigate(`/course/${editId}/detail/${selectedLectureId}`);
}, [selectedLectureId, editId]);
const [isHeaderVisible, setIsHeaderVisible] = useState(true); // 新增
return (
@ -47,6 +55,7 @@ export function CourseDetailProvider({
value={{
editId,
course,
lecture,
selectedLectureId,
setSelectedLectureId,
isLoading,

View File

@ -1,21 +1,23 @@
// components/CourseDetailDisplayArea.tsx
import { motion, useScroll, useTransform } from "framer-motion";
import React from "react";
import React, { useContext } from "react";
import { VideoPlayer } from "@web/src/components/presentation/video-player/VideoPlayer";
import { CourseDetailDescription } from "./CourseDetailDescription/CourseDetailDescription";
import { Course } from "@nice/common";
import { Course, PostType } from "@nice/common";
import { CourseDetailContext } from "./CourseDetailContext";
interface CourseDetailDisplayAreaProps {
course: Course;
// course: Course;
videoSrc?: string;
videoPoster?: string;
isLoading?: boolean;
// isLoading?: boolean;
}
export const CourseDetailDisplayArea: React.FC<
CourseDetailDisplayAreaProps
> = ({ course, videoSrc, videoPoster, isLoading = false }) => {
> = ({ videoSrc, videoPoster }) => {
// 创建滚动动画效果
const { course, isLoading, lecture } = useContext(CourseDetailContext);
const { scrollY } = useScroll();
const videoScale = useTransform(scrollY, [0, 200], [1, 0.8]);
const videoOpacity = useTransform(scrollY, [0, 200], [1, 0.8]);
@ -25,16 +27,15 @@ export const CourseDetailDisplayArea: React.FC<
{/* 固定的视频区域 */}
{/* 移除 sticky 定位,让视频区域随页面滚动 */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
style={{
opacity: videoOpacity,
}}
className="w-full bg-black">
<div className=" w-full ">
<VideoPlayer src={videoSrc} poster={videoPoster} />
</div>
{lecture.type === PostType.LECTURE && (
<div className=" w-full ">
<VideoPlayer src={videoSrc} poster={videoPoster} />
</div>
)}
</motion.div>
{/* 课程内容区域 */}

View File

@ -2,12 +2,13 @@
import { motion, useScroll, useTransform } from "framer-motion";
import { useContext, useEffect, useState } from "react";
import { CourseDetailContext } from "../CourseDetailContext";
import { Button } from "antd";
export const CourseDetailHeader = () => {
const { scrollY } = useScroll();
const [lastScrollY, setLastScrollY] = useState(0);
const { course, isHeaderVisible, setIsHeaderVisible } =
const { course, isHeaderVisible, setIsHeaderVisible, lecture } =
useContext(CourseDetailContext);
useEffect(() => {
const updateHeader = () => {
@ -43,6 +44,12 @@ export const CourseDetailHeader = () => {
<div className="flex items-center space-x-4">
<h1 className="text-white text-xl ">{course?.title}</h1>
</div>
<Button
onClick={() => {
console.log(lecture);
}}>
123
</Button>
<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">

View File

@ -5,10 +5,16 @@ import { CourseDetailContext } from "./CourseDetailContext";
import CourseDetailDisplayArea from "./CourseDetailDisplayArea";
import { CourseSyllabus } from "./CourseSyllabus/CourseSyllabus";
import CourseDetailHeader from "./CourseDetailHeader/CourseDetailHeader";
import { Button } from "antd";
export default function CourseDetailLayout() {
const { course, selectedLectureId, isLoading, setSelectedLectureId } =
useContext(CourseDetailContext);
const {
course,
selectedLectureId,
lecture,
isLoading,
setSelectedLectureId,
} = useContext(CourseDetailContext);
const handleLectureClick = (lectureId: string) => {
setSelectedLectureId(lectureId);
@ -16,7 +22,8 @@ export default function CourseDetailLayout() {
const [isSyllabusOpen, setIsSyllabusOpen] = useState(false);
return (
<div className="relative">
<CourseDetailHeader />
<CourseDetailHeader />
{/* 添加 Header 组件 */}
{/* 主内容区域 */}
{/* 为了防止 Header 覆盖内容,添加上边距 */}
@ -30,8 +37,8 @@ export default function CourseDetailLayout() {
transition={{ type: "spring", stiffness: 300, damping: 30 }}
className="relative">
<CourseDetailDisplayArea
course={course}
isLoading={isLoading}
// course={course}
// isLoading={isLoading}
videoSrc="https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8"
videoPoster="https://picsum.photos/800/450"
/>

View File

@ -35,7 +35,6 @@ export function useTusUpload() {
if (uploadIndex === -1 || uploadIndex + 4 >= parts.length) {
throw new Error("Invalid upload URL format");
}
console.log(env.UPLOAD_PORT);
const resUrl = `http://${env.SERVER_IP}:${env.UPLOAD_PORT}/uploads/${parts.slice(uploadIndex + 1, uploadIndex + 6).join("/")}`;
return resUrl;