add
This commit is contained in:
parent
fb026e1fde
commit
32516187b2
|
@ -1,11 +1,12 @@
|
||||||
import { api } from "@nice/client";
|
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 React, { createContext, ReactNode, useEffect, useState } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
interface CourseDetailContextType {
|
interface CourseDetailContextType {
|
||||||
editId?: string; // 添加 editId
|
editId?: string; // 添加 editId
|
||||||
course?: CourseDto;
|
course?: CourseDto;
|
||||||
|
lecture?: Lecture;
|
||||||
selectedLectureId?: string | undefined;
|
selectedLectureId?: string | undefined;
|
||||||
setSelectedLectureId?: React.Dispatch<React.SetStateAction<string>>;
|
setSelectedLectureId?: React.Dispatch<React.SetStateAction<string>>;
|
||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
|
@ -37,9 +38,16 @@ export function CourseDetailProvider({
|
||||||
const [selectedLectureId, setSelectedLectureId] = useState<
|
const [selectedLectureId, setSelectedLectureId] = useState<
|
||||||
string | undefined
|
string | undefined
|
||||||
>(undefined);
|
>(undefined);
|
||||||
|
const { data: lecture, isLoading: lectureIsLoading } = (
|
||||||
|
api.post as any
|
||||||
|
).findFirst.useQuery(
|
||||||
|
{
|
||||||
|
where: { id: selectedLectureId },
|
||||||
|
},
|
||||||
|
{ enabled: Boolean(editId) }
|
||||||
|
);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
navigate(``)
|
navigate(`/course/${editId}/detail/${selectedLectureId}`);
|
||||||
|
|
||||||
}, [selectedLectureId, editId]);
|
}, [selectedLectureId, editId]);
|
||||||
const [isHeaderVisible, setIsHeaderVisible] = useState(true); // 新增
|
const [isHeaderVisible, setIsHeaderVisible] = useState(true); // 新增
|
||||||
return (
|
return (
|
||||||
|
@ -47,6 +55,7 @@ export function CourseDetailProvider({
|
||||||
value={{
|
value={{
|
||||||
editId,
|
editId,
|
||||||
course,
|
course,
|
||||||
|
lecture,
|
||||||
selectedLectureId,
|
selectedLectureId,
|
||||||
setSelectedLectureId,
|
setSelectedLectureId,
|
||||||
isLoading,
|
isLoading,
|
||||||
|
|
|
@ -1,21 +1,23 @@
|
||||||
// components/CourseDetailDisplayArea.tsx
|
// components/CourseDetailDisplayArea.tsx
|
||||||
import { motion, useScroll, useTransform } from "framer-motion";
|
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 { VideoPlayer } from "@web/src/components/presentation/video-player/VideoPlayer";
|
||||||
import { CourseDetailDescription } from "./CourseDetailDescription/CourseDetailDescription";
|
import { CourseDetailDescription } from "./CourseDetailDescription/CourseDetailDescription";
|
||||||
import { Course } from "@nice/common";
|
import { Course, PostType } from "@nice/common";
|
||||||
|
import { CourseDetailContext } from "./CourseDetailContext";
|
||||||
|
|
||||||
interface CourseDetailDisplayAreaProps {
|
interface CourseDetailDisplayAreaProps {
|
||||||
course: Course;
|
// course: Course;
|
||||||
videoSrc?: string;
|
videoSrc?: string;
|
||||||
videoPoster?: string;
|
videoPoster?: string;
|
||||||
isLoading?: boolean;
|
// isLoading?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CourseDetailDisplayArea: React.FC<
|
export const CourseDetailDisplayArea: React.FC<
|
||||||
CourseDetailDisplayAreaProps
|
CourseDetailDisplayAreaProps
|
||||||
> = ({ course, videoSrc, videoPoster, isLoading = false }) => {
|
> = ({ videoSrc, videoPoster }) => {
|
||||||
// 创建滚动动画效果
|
// 创建滚动动画效果
|
||||||
|
const { course, isLoading, lecture } = useContext(CourseDetailContext);
|
||||||
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]);
|
||||||
|
@ -25,16 +27,15 @@ export const CourseDetailDisplayArea: React.FC<
|
||||||
{/* 固定的视频区域 */}
|
{/* 固定的视频区域 */}
|
||||||
{/* 移除 sticky 定位,让视频区域随页面滚动 */}
|
{/* 移除 sticky 定位,让视频区域随页面滚动 */}
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, y: 20 }}
|
|
||||||
animate={{ opacity: 1, y: 0 }}
|
|
||||||
transition={{ duration: 0.5 }}
|
|
||||||
style={{
|
style={{
|
||||||
opacity: videoOpacity,
|
opacity: videoOpacity,
|
||||||
}}
|
}}
|
||||||
className="w-full bg-black">
|
className="w-full bg-black">
|
||||||
<div className=" w-full ">
|
{lecture.type === PostType.LECTURE && (
|
||||||
<VideoPlayer src={videoSrc} poster={videoPoster} />
|
<div className=" w-full ">
|
||||||
</div>
|
<VideoPlayer src={videoSrc} poster={videoPoster} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
{/* 课程内容区域 */}
|
{/* 课程内容区域 */}
|
||||||
|
|
|
@ -2,12 +2,13 @@
|
||||||
import { motion, useScroll, useTransform } from "framer-motion";
|
import { motion, useScroll, useTransform } from "framer-motion";
|
||||||
import { useContext, useEffect, useState } from "react";
|
import { useContext, useEffect, useState } from "react";
|
||||||
import { CourseDetailContext } from "../CourseDetailContext";
|
import { CourseDetailContext } from "../CourseDetailContext";
|
||||||
|
import { Button } from "antd";
|
||||||
|
|
||||||
export const CourseDetailHeader = () => {
|
export const CourseDetailHeader = () => {
|
||||||
const { scrollY } = useScroll();
|
const { scrollY } = useScroll();
|
||||||
|
|
||||||
const [lastScrollY, setLastScrollY] = useState(0);
|
const [lastScrollY, setLastScrollY] = useState(0);
|
||||||
const { course, isHeaderVisible, setIsHeaderVisible } =
|
const { course, isHeaderVisible, setIsHeaderVisible, lecture } =
|
||||||
useContext(CourseDetailContext);
|
useContext(CourseDetailContext);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const updateHeader = () => {
|
const updateHeader = () => {
|
||||||
|
@ -43,6 +44,12 @@ export const CourseDetailHeader = () => {
|
||||||
<div className="flex items-center space-x-4">
|
<div className="flex items-center space-x-4">
|
||||||
<h1 className="text-white text-xl ">{course?.title}</h1>
|
<h1 className="text-white text-xl ">{course?.title}</h1>
|
||||||
</div>
|
</div>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
console.log(lecture);
|
||||||
|
}}>
|
||||||
|
123
|
||||||
|
</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">
|
||||||
|
|
|
@ -5,10 +5,16 @@ import { CourseDetailContext } from "./CourseDetailContext";
|
||||||
import CourseDetailDisplayArea from "./CourseDetailDisplayArea";
|
import CourseDetailDisplayArea from "./CourseDetailDisplayArea";
|
||||||
import { CourseSyllabus } from "./CourseSyllabus/CourseSyllabus";
|
import { CourseSyllabus } from "./CourseSyllabus/CourseSyllabus";
|
||||||
import CourseDetailHeader from "./CourseDetailHeader/CourseDetailHeader";
|
import CourseDetailHeader from "./CourseDetailHeader/CourseDetailHeader";
|
||||||
|
import { Button } from "antd";
|
||||||
|
|
||||||
export default function CourseDetailLayout() {
|
export default function CourseDetailLayout() {
|
||||||
const { course, selectedLectureId, isLoading, setSelectedLectureId } =
|
const {
|
||||||
useContext(CourseDetailContext);
|
course,
|
||||||
|
selectedLectureId,
|
||||||
|
lecture,
|
||||||
|
isLoading,
|
||||||
|
setSelectedLectureId,
|
||||||
|
} = useContext(CourseDetailContext);
|
||||||
|
|
||||||
const handleLectureClick = (lectureId: string) => {
|
const handleLectureClick = (lectureId: string) => {
|
||||||
setSelectedLectureId(lectureId);
|
setSelectedLectureId(lectureId);
|
||||||
|
@ -16,7 +22,8 @@ export default function CourseDetailLayout() {
|
||||||
const [isSyllabusOpen, setIsSyllabusOpen] = useState(false);
|
const [isSyllabusOpen, setIsSyllabusOpen] = useState(false);
|
||||||
return (
|
return (
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<CourseDetailHeader />
|
<CourseDetailHeader />
|
||||||
|
|
||||||
{/* 添加 Header 组件 */}
|
{/* 添加 Header 组件 */}
|
||||||
{/* 主内容区域 */}
|
{/* 主内容区域 */}
|
||||||
{/* 为了防止 Header 覆盖内容,添加上边距 */}
|
{/* 为了防止 Header 覆盖内容,添加上边距 */}
|
||||||
|
@ -30,8 +37,8 @@ export default function CourseDetailLayout() {
|
||||||
transition={{ type: "spring", stiffness: 300, damping: 30 }}
|
transition={{ type: "spring", stiffness: 300, damping: 30 }}
|
||||||
className="relative">
|
className="relative">
|
||||||
<CourseDetailDisplayArea
|
<CourseDetailDisplayArea
|
||||||
course={course}
|
// course={course}
|
||||||
isLoading={isLoading}
|
// isLoading={isLoading}
|
||||||
videoSrc="https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8"
|
videoSrc="https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8"
|
||||||
videoPoster="https://picsum.photos/800/450"
|
videoPoster="https://picsum.photos/800/450"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -35,7 +35,6 @@ export function useTusUpload() {
|
||||||
if (uploadIndex === -1 || uploadIndex + 4 >= parts.length) {
|
if (uploadIndex === -1 || uploadIndex + 4 >= parts.length) {
|
||||||
throw new Error("Invalid upload URL format");
|
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("/")}`;
|
const resUrl = `http://${env.SERVER_IP}:${env.UPLOAD_PORT}/uploads/${parts.slice(uploadIndex + 1, uploadIndex + 6).join("/")}`;
|
||||||
|
|
||||||
return resUrl;
|
return resUrl;
|
||||||
|
|
Loading…
Reference in New Issue