diff --git a/apps/web/src/app/main/courses/components/CourseCard.tsx b/apps/web/src/app/main/courses/components/CourseCard.tsx index 5d7c3cd..84688f3 100755 --- a/apps/web/src/app/main/courses/components/CourseCard.tsx +++ b/apps/web/src/app/main/courses/components/CourseCard.tsx @@ -1,49 +1,90 @@ -import { Card, Rate, Tag } from 'antd'; +import { Card, Rate, Tag ,Typography,Button} from 'antd'; import { Course } from '../mockData'; -import { UserOutlined, ClockCircleOutlined } from '@ant-design/icons'; +import { UserOutlined, ClockCircleOutlined, PlayCircleOutlined, TeamOutlined } from '@ant-design/icons'; import { CourseDto } from '@nice/common'; +import { useNavigate } from 'react-router-dom'; + interface CourseCardProps { course: CourseDto; } - +const { Title, Text } = Typography; export default function CourseCard({ course }: CourseCardProps) { + const navigate = useNavigate(); + const handleClick = (course: CourseDto) => { + navigate(`/course/${course.id}/detail`); + } return ( - handleClick(course)} + key={course.id} + hoverable + className="group overflow-hidden rounded-2xl border border-gray-200 bg-white + shadow-xl hover:shadow-2xl transition-all duration-300 transform hover:-translate-y-2" + cover={ +
+
- } - > -
-

- {course.title} -

-

{course.subTitle}

-
- - {course.rating} -
-
-
- - {course.enrollments?.length} 人在学 -
-
- - {course.duration} -
-
-
- {course.terms[0].name} - {course.terms[1].name} -
+
+
- + } + > +
+
+ + {course.terms[0].name} + + + {course.terms[1].name} + +
+ + <button > {course.title}</button> + + +
+ +
+ + {course?.depts[0]?.name} + +
+ + 观看次数{course?.meta?.views}次 + +
+ + +
+ +
+ +
+ ); } diff --git a/apps/web/src/app/main/courses/page.tsx b/apps/web/src/app/main/courses/page.tsx index 55a6d47..fa2fe9a 100755 --- a/apps/web/src/app/main/courses/page.tsx +++ b/apps/web/src/app/main/courses/page.tsx @@ -11,148 +11,25 @@ import { } from "@nice/common"; import { useSearchParams } from "react-router-dom"; import { set } from "idb-keyval"; +import { useMainContext } from "../layout/MainProvider"; +interface paginationData { + items: CourseDto[]; + totalPages: number; +} export default function CoursesPage() { - const [currentPage, setCurrentPage] = useState(1); - const [selectedCategory, setSelectedCategory] = useState(""); - const [selectedLevel, setSelectedLevel] = useState(""); - const pageSize = 9; - const [isAll, setIsAll] = useState(true); - const [searchParams, setSearchParams] = useSearchParams(); - let coursesData = []; - let isCourseLoading = false; - const searchValue = searchParams.get("searchValue"); - - if (!searchParams.get("searchValue")) { - console.log("no category"); - const { data, isLoading } = api.post.findManyWithPagination.useQuery({ - where: { - type: PostType.COURSE, - }, - select: courseDetailSelect, - }); - coursesData = data?.items; - isCourseLoading = isLoading; - } else { - console.log("searchValue:" + searchParams.get("searchValue")); - const searchValue = searchParams.get("searchValue"); - const { data, isLoading } = api.post.findManyWithPagination.useQuery({ - where: { - type: PostType.COURSE, - terms: isAll - ? {} - : { - some: { - OR: [ - selectedCategory - ? { name: selectedCategory } - : {}, - selectedLevel - ? { name: selectedLevel } - : {}, - ], - }, - }, - OR: [ - { title: { contains: searchValue, mode: "insensitive" } }, - { - subTitle: { - contains: searchValue, - mode: "insensitive", - }, - }, - { content: { contains: searchValue, mode: "insensitive" } }, - { - terms: { - some: { - name: { - contains: searchValue, - mode: "insensitive", - }, - }, - }, - }, - ], - }, - select: courseDetailSelect, - }); - coursesData = data?.items; - isCourseLoading = isLoading; - } - useEffect(() => { - if (searchParams.get("searchValue") == "") { - setSelectedCategory(""); - setSelectedLevel(""); - } - }, [searchParams.get("searchValue")]); - const filteredCourses = useMemo(() => { - return isCourseLoading ? [] : coursesData; - }, [isCourseLoading, coursesData, selectedCategory, selectedLevel]); - - const paginatedCourses: CourseDto[] = useMemo(() => { - const startIndex = (currentPage - 1) * pageSize; - return isCourseLoading - ? [] - : (filteredCourses.slice( - startIndex, - startIndex + pageSize - ) as any as CourseDto[]); - }, [filteredCourses, currentPage]); - - const handlePageChange = (page: number) => { - setCurrentPage(page); - window.scrollTo({ top: 0, behavior: "smooth" }); - }; - useEffect(() => { - setCurrentPage(1); - }, []); - + const { searchValue, setSearchValue } = useMainContext(); + return ( -
-
-
- {/* 左侧筛选区域 */} -
-
- { - console.log(category); - setSelectedCategory(category); - setCurrentPage(1); - setIsAll(!category); - setSearchParams({ searchValue: "" }); - }} - onLevelChange={(level) => { - setSelectedLevel(level); - setCurrentPage(1); - setIsAll(!level); - setSearchParams({ searchValue: "" }); - }} - /> -
-
- - {/* 右侧课程列表区域 */} -
-
-
- - 共找到 {filteredCourses.length} 门课程 - -
- -
-
-
+ <> +
+
{searchValue}
+
-
+ ); } diff --git a/apps/web/src/app/main/home/components/CategorySection.tsx b/apps/web/src/app/main/home/components/CategorySection.tsx index 81b201a..e06a5da 100755 --- a/apps/web/src/app/main/home/components/CategorySection.tsx +++ b/apps/web/src/app/main/home/components/CategorySection.tsx @@ -3,7 +3,7 @@ import { Typography, Button, Spin } from 'antd'; import { stringToColor, TaxonomySlug, TermDto } from '@nice/common'; import { api,} from '@nice/client'; import { ControlOutlined } from '@ant-design/icons'; -import { useNavigate } from 'react-router-dom'; +import { useNavigate, useSearchParams } from 'react-router-dom'; const { Title, Text } = Typography; @@ -124,6 +124,7 @@ const CategorySection = () => { onClick={()=>{ console.log(category.name) navigate(`/courses?category=${category.name}`) + window.scrollTo({ top: 0, behavior: 'smooth' }) }} >
diff --git a/apps/web/src/app/main/home/components/CoursesSection.tsx b/apps/web/src/app/main/home/components/CoursesSection.tsx index b4938b0..ec2e759 100755 --- a/apps/web/src/app/main/home/components/CoursesSection.tsx +++ b/apps/web/src/app/main/home/components/CoursesSection.tsx @@ -1,6 +1,6 @@ import React, { useState, useMemo, useEffect } from 'react'; -import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; -import { Button, Card, Typography, Tag, Progress, Spin, Empty } from 'antd'; +import { useNavigate } from 'react-router-dom'; +import { Button, Card, Typography, Tag, Spin, Empty } from 'antd'; import { PlayCircleOutlined, UserOutlined, @@ -12,7 +12,6 @@ import { } from '@ant-design/icons'; import { CourseDto, TaxonomySlug, TermDto } from '@nice/common'; import { api } from '@nice/client'; -// const {courseId} = useParams(); interface GetTaxonomyProps { categories: string[]; isLoading: boolean; @@ -39,6 +38,7 @@ function useGetTaxonomy({ type }): GetTaxonomyProps { }, [data]); return { categories, isLoading } } + // 不同分类跳转 function useFetchCoursesByCategory(category: string) { const isAll = category === '全部'; @@ -52,7 +52,8 @@ function useFetchCoursesByCategory(category: string) { }, take: 8, include: { - terms: true + terms: true, + depts:true } }); @@ -83,12 +84,9 @@ interface CoursesSectionProps { initialVisibleCoursesCount?: number; } - const CoursesSection: React.FC = ({ title, description, - courses, - isLoading, initialVisibleCoursesCount = 8, }) => { const navigate = useNavigate(); @@ -97,10 +95,11 @@ const CoursesSection: React.FC = ({ const gateGory: GetTaxonomyProps = useGetTaxonomy({ type: TaxonomySlug.CATEGORY, }) + const { data, isLoading: isDataLoading } = useFetchCoursesByCategory(selectedCategory); - useEffect(() => { - console.log('data:', data) - }) + // useEffect(() => { + // console.log('data:', data) + // }) const handleClick = (course: CourseDto) => { navigate(`/course/${course.id}/detail`); } @@ -108,6 +107,20 @@ const CoursesSection: React.FC = ({ useEffect(() => { console.log('data:', data) }) + + // const { data: depts, isLoading: isDeptLoading }: { data: CourseDto[], isLoading: boolean } = api.post.findMany.useQuery({ + // where: {}, + // include: { + // depts: true, + // }, + // orderBy: { + // createdAt: 'desc' // 按创建时间降序排列 + // }, + // take: 8 // 只获取前8个课程 + // }); + + + const filteredCourses = useMemo(() => { return selectedCategory === '全部' ? data @@ -115,6 +128,7 @@ const CoursesSection: React.FC = ({ }, [selectedCategory, data]); const displayedCourses = isDataLoading ? [] : filteredCourses?.slice(0, visibleCourses); + return (
@@ -170,8 +184,8 @@ const CoursesSection: React.FC = ({
- {displayedCourses.length=== 0 ? ( -
+ {displayedCourses.length === 0 ? ( +
) : displayedCourses?.map((course) => ( @@ -223,14 +237,16 @@ const CoursesSection: React.FC = ({
- + + {course?.depts[0]?.name}
- - - 观看次数{course?.meta?.views}次 + + 观看次数{course?.meta?.views}次
+ +
{/* Stats Container */} -
-
+
+
{platformStats.map((stat, index) => (
@@ -37,7 +38,11 @@ export function MainHeader() { onChange={(e) => setSearchValue(e.target.value)} onPressEnter={(e) => { //console.log(e) - setSearchValue(""); + //setSearchValue(""); + setSearchParams((prev)=>{ + if(searchParams.get("category")) prev.delete("category") + return prev + }) navigate( `/courses/?searchValue=${searchValue}` ); diff --git a/packages/common/src/models/post.ts b/packages/common/src/models/post.ts index de740d5..6994d4b 100755 --- a/packages/common/src/models/post.ts +++ b/packages/common/src/models/post.ts @@ -79,4 +79,5 @@ export type CourseDto = Course & { sections?: SectionDto[]; terms: Term[]; lectureCount?: number; + depts:Department[] };