From b474fa5faf5c38419b5cbd6f31f64411e2967528 Mon Sep 17 00:00:00 2001 From: Li1304553726 <1304553726@qq.com> Date: Wed, 26 Feb 2025 16:38:41 +0800 Subject: [PATCH 1/4] add Li --- .../main/courses/components/CourseCard.tsx | 59 ++++++++++--------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/apps/web/src/app/main/courses/components/CourseCard.tsx b/apps/web/src/app/main/courses/components/CourseCard.tsx index cbdb80c..f81c3a3 100755 --- a/apps/web/src/app/main/courses/components/CourseCard.tsx +++ b/apps/web/src/app/main/courses/components/CourseCard.tsx @@ -1,7 +1,6 @@ -import { Card, Rate, Tag, Typography, Button } from "antd"; +import { Card, Tag, Typography, Button } from "antd"; import { - UserOutlined, - ClockCircleOutlined, + EyeOutlined, PlayCircleOutlined, TeamOutlined, } from "@ant-design/icons"; @@ -16,9 +15,8 @@ export default function CourseCard({ course }: CourseCardProps) { const navigate = useNavigate(); const handleClick = (course: CourseDto) => { navigate(`/course/${course.id}/detail`); - window.scrollTo({top: 0,behavior: "smooth",}) + window.scrollTo({ top: 0, behavior: "smooth", }) }; - return ( handleClick(course)} @@ -38,32 +36,35 @@ export default function CourseCard({ course }: CourseCardProps) { }> -
-
- {course?.terms?.map((term) => { - return ( - - {term.name} - - ); - })} +
+
+
+ {course?.terms?.map((term) => { + return ( + <> + + {term.name} + + + ); + })} +
+ className="mb-4 mt-4 line-clamp-2 font-bold leading-snug text-gray-800 hover:text-blue-600 transition-colors duration-300 group-hover:scale-[1.02] transform origin-left"> <button> {course.title}</button> @@ -78,8 +79,8 @@ export default function CourseCard({ course }: CourseCardProps) { {/* {course?.depts?.map((dept)=>{return dept.name})} */}
- - {course?.meta?.views + + {course?.meta?.views ? `观看次数 ${course?.meta?.views}` : null} From 754aa9e4963511be586325bd36b01febf6974edb Mon Sep 17 00:00:00 2001 From: ditiqi Date: Wed, 26 Feb 2025 17:04:20 +0800 Subject: [PATCH 2/4] add --- apps/server/src/queue/models/post/utils.ts | 90 ++++++++++++---------- 1 file changed, 51 insertions(+), 39 deletions(-) diff --git a/apps/server/src/queue/models/post/utils.ts b/apps/server/src/queue/models/post/utils.ts index 690a3b0..51d2b04 100644 --- a/apps/server/src/queue/models/post/utils.ts +++ b/apps/server/src/queue/models/post/utils.ts @@ -64,9 +64,50 @@ export async function updateTotalCourseViewCount(type: VisitType) { export async function updatePostViewCount(id: string, type: VisitType) { const post = await db.post.findFirst({ where: { id }, - select: { id: true, meta: true }, + select: { id: true, meta: true, type: true }, }); - + const metaFieldMap = { + [VisitType.READED]: 'views', + [VisitType.LIKE]: 'likes', + [VisitType.HATE]: 'hates', + }; + if (post?.type === PostType.LECTURE) { + const course = await db.postAncestry.findFirst({ + where: { + descendantId: post?.id, + ancestor: { + type: PostType.COURSE, + }, + }, + select: { id: true }, + }); + const lectures = await db.postAncestry.findMany({ + where: { ancestorId: course.id, descendant: { type: PostType.LECTURE } }, + select: { + id: true, + }, + }); + const courseViews = await db.visit.aggregate({ + _sum: { + views: true, + }, + where: { + postId: { + in: [course.id, ...lectures.map((lecture) => lecture.id)], + }, + type: type, + }, + }); + await db.post.update({ + where: { id: course.id }, + data: { + meta: { + ...((post?.meta as any) || {}), + [metaFieldMap[type]]: courseViews._sum.views || 0, + }, + }, + }); + } const totalViews = await db.visit.aggregate({ _sum: { views: true, @@ -76,42 +117,13 @@ export async function updatePostViewCount(id: string, type: VisitType) { type: type, }, }); - if (type === VisitType.READED) { - await db.post.update({ - where: { - id: id, + await db.post.update({ + where: { id }, + data: { + meta: { + ...((post?.meta as any) || {}), + [metaFieldMap[type]]: totalViews._sum.views || 0, }, - data: { - meta: { - ...((post?.meta as any) || {}), - views: totalViews._sum.views || 0, - }, // Use 0 if no visits exist - }, - }); - console.log('readed'); - } else if (type === VisitType.LIKE) { - await db.post.update({ - where: { - id: id, - }, - data: { - meta: { - ...((post?.meta as any) || {}), - likes: totalViews._sum.views || 0, // Use 0 if no visits exist - }, - }, - }); - } else if (type === VisitType.HATE) { - await db.post.update({ - where: { - id: id, - }, - data: { - meta: { - ...((post?.meta as any) || {}), - hates: totalViews._sum.views || 0, // Use 0 if no visits exist - }, - }, - }); - } + }, + }); } From dda7bb6b996d77d073937bee30d11a397a6b2fdf Mon Sep 17 00:00:00 2001 From: Li1304553726 <1304553726@qq.com> Date: Wed, 26 Feb 2025 19:42:48 +0800 Subject: [PATCH 3/4] main Li --- apps/web/src/app/main/courses/components/CourseCard.tsx | 2 +- apps/web/src/app/main/home/components/CoursesSection.tsx | 7 +++---- apps/web/src/app/main/layout/MainLayout.tsx | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/apps/web/src/app/main/courses/components/CourseCard.tsx b/apps/web/src/app/main/courses/components/CourseCard.tsx index f81c3a3..5de3a9d 100755 --- a/apps/web/src/app/main/courses/components/CourseCard.tsx +++ b/apps/web/src/app/main/courses/components/CourseCard.tsx @@ -38,7 +38,7 @@ export default function CourseCard({ course }: CourseCardProps) { }>
-
+
{course?.terms?.map((term) => { return ( <> diff --git a/apps/web/src/app/main/home/components/CoursesSection.tsx b/apps/web/src/app/main/home/components/CoursesSection.tsx index 23cd420..999e931 100755 --- a/apps/web/src/app/main/home/components/CoursesSection.tsx +++ b/apps/web/src/app/main/home/components/CoursesSection.tsx @@ -44,10 +44,9 @@ const CoursesSection: React.FC = ({ type: TaxonomySlug.CATEGORY, }); return ( -
-
-
-
+
+
+
- <Layout className="min-h-screen bg-gray-100"> + <Layout className="min-h-screen"> <MainHeader /> - <Content className="mt-16 bg-gray-200 "> + <Content className="mt-16 bg-gray-50"> <Outlet /> </Content> <MainFooter /> From 57f486ca6eee676e68d6c94352c203591cbfd7fb Mon Sep 17 00:00:00 2001 From: ditiqi <ditiqi@163.com> Date: Wed, 26 Feb 2025 19:49:50 +0800 Subject: [PATCH 4/4] add --- .../app/main/home/components/HeroSection.tsx | 91 ++++++++++++------- .../src/app/main/layout/UserMenu/UserMenu.tsx | 28 ++++-- apps/web/src/app/main/my-duty/page.tsx | 7 ++ apps/web/src/app/main/my-learning/page.tsx | 3 + packages/common/prisma/schema.prisma | 8 -- 5 files changed, 88 insertions(+), 49 deletions(-) create mode 100644 apps/web/src/app/main/my-duty/page.tsx create mode 100644 apps/web/src/app/main/my-learning/page.tsx diff --git a/apps/web/src/app/main/home/components/HeroSection.tsx b/apps/web/src/app/main/home/components/HeroSection.tsx index eca460d..4073109 100755 --- a/apps/web/src/app/main/home/components/HeroSection.tsx +++ b/apps/web/src/app/main/home/components/HeroSection.tsx @@ -1,4 +1,10 @@ -import React, { useRef, useCallback, useEffect, useMemo, useState } from "react"; +import React, { + useRef, + useCallback, + useEffect, + useMemo, + useState, +} from "react"; import { Carousel, Typography } from "antd"; import { TeamOutlined, @@ -30,13 +36,29 @@ interface PlatformStat { const HeroSection = () => { const carouselRef = useRef<CarouselRef>(null); const { statistics, slides } = useAppConfig(); - const [countStatistics, setCountStatistics] = useState<number>(4) + const [countStatistics, setCountStatistics] = useState<number>(4); const platformStats: PlatformStat[] = useMemo(() => { return [ - { icon: <TeamOutlined />, value: statistics.staffs, label: "注册学员" }, - { icon: <StarOutlined />, value: statistics.courses, label: "精品课程" }, - { icon: <BookOutlined />, value: statistics.lectures, label: '课程章节' }, - { icon: <EyeOutlined />, value: statistics.reads, label: "观看次数" }, + { + icon: <TeamOutlined />, + value: statistics.staffs, + label: "注册学员", + }, + { + icon: <StarOutlined />, + value: statistics.courses, + label: "精品课程", + }, + { + icon: <BookOutlined />, + value: statistics.lectures, + label: "课程章节", + }, + { + icon: <EyeOutlined />, + value: statistics.reads, + label: "观看次数", + }, ]; }, [statistics]); const handlePrev = useCallback(() => { @@ -48,7 +70,7 @@ const HeroSection = () => { }, []); const countNonZeroValues = (statistics: Record<string, number>): number => { - return Object.values(statistics).filter(value => value !== 0).length; + return Object.values(statistics).filter((value) => value !== 0).length; }; useEffect(() => { @@ -67,8 +89,8 @@ const HeroSection = () => { dots={{ className: "carousel-dots !bottom-32 !z-20", }}> - {Array.isArray(slides) ? - (slides.map((item, index) => ( + {Array.isArray(slides) ? ( + slides.map((item, index) => ( <div key={index} className="relative h-[600px]"> <div className="absolute inset-0 bg-cover bg-center transform transition-[transform,filter] duration-[2000ms] group-hover:scale-105 group-hover:brightness-110 will-change-[transform,filter]" @@ -87,9 +109,9 @@ const HeroSection = () => { <div className="relative h-full max-w-7xl mx-auto px-6 lg:px-8"></div> </div> )) - ) : ( - <div></div> - )} + ) : ( + <div></div> + )} </Carousel> {/* Navigation Buttons */} @@ -108,31 +130,30 @@ const HeroSection = () => { </div> {/* Stats Container */} - { - countStatistics > 1 && ( - <div className="absolute -bottom-20 left-1/2 -translate-x-1/2 w-3/5 max-w-6xl px-4"> - <div className={`rounded-2xl grid grid-cols-${countStatistics} lg:grid-cols-${countStatistics} md:grid-cols-${countStatistics} gap-4 md:gap-8 p-6 md:p-8 bg-white border shadow-xl hover:shadow-2xl transition-shadow duration-500 will-change-[transform,box-shadow]`}> - {platformStats.map((stat, index) => { - return stat.value - ? (<div - key={index} - className="text-center transform hover:-translate-y-1 hover:scale-105 transition-transform duration-300 ease-out"> - <div className="inline-flex items-center justify-center w-16 h-16 mb-4 rounded-full bg-primary-50 text-primary-600 text-3xl transition-colors duration-300 group-hover:text-primary-700"> - {stat.icon} - </div> - <div className="text-2xl font-bold bg-gradient-to-r from-gray-800 to-gray-600 bg-clip-text text-transparent mb-1.5"> - {stat.value} - </div> - <div className="text-gray-600 font-medium"> - {stat.label} - </div> + {countStatistics > 1 && ( + <div className="absolute -bottom-20 left-1/2 -translate-x-1/2 w-3/5 max-w-6xl px-4"> + <div + className={`rounded-2xl grid grid-cols-${countStatistics} lg:grid-cols-${countStatistics} md:grid-cols-${countStatistics} gap-4 md:gap-8 p-6 md:p-8 bg-white border shadow-xl hover:shadow-2xl transition-shadow duration-500 will-change-[transform,box-shadow]`}> + {platformStats.map((stat, index) => { + return stat.value ? ( + <div + key={index} + className="text-center transform hover:-translate-y-1 hover:scale-105 transition-transform duration-300 ease-out"> + <div className="inline-flex items-center justify-center w-16 h-16 mb-4 rounded-full bg-primary-50 text-primary-600 text-3xl transition-colors duration-300 group-hover:text-primary-700"> + {stat.icon} </div> - ) : null - })} - </div> + <div className="text-2xl font-bold bg-gradient-to-r from-gray-800 to-gray-600 bg-clip-text text-transparent mb-1.5"> + {stat.value} + </div> + <div className="text-gray-600 font-medium"> + {stat.label} + </div> + </div> + ) : null; + })} </div> - ) - } + </div> + )} </section> ); }; diff --git a/apps/web/src/app/main/layout/UserMenu/UserMenu.tsx b/apps/web/src/app/main/layout/UserMenu/UserMenu.tsx index ea23902..41da474 100755 --- a/apps/web/src/app/main/layout/UserMenu/UserMenu.tsx +++ b/apps/web/src/app/main/layout/UserMenu/UserMenu.tsx @@ -86,6 +86,20 @@ export function UserMenu() { setModalOpen(true); }, }, + { + icon: <UserOutlined className="text-lg" />, + label: "我创建的课程", + action: () => { + setModalOpen(true); + }, + }, + { + icon: <UserOutlined className="text-lg" />, + label: "我学习的课程", + action: () => { + setModalOpen(true); + }, + }, canManageAnyStaff && { icon: <SettingOutlined className="text-lg" />, label: "设置", @@ -222,18 +236,20 @@ export function UserMenu() { focus:ring-2 focus:ring-[#00538E]/20 group relative overflow-hidden active:scale-[0.99] - ${item.label === "注销" + ${ + item.label === "注销" ? "text-[#B22234] hover:bg-red-50/80 hover:text-red-700" : "text-[#00538E] hover:bg-[#E6EEF5] hover:text-[#003F6A]" - }`}> + }`}> <span className={`w-5 h-5 flex items-center justify-center transition-all duration-200 ease-in-out group-hover:scale-110 group-hover:rotate-6 - group-hover:translate-x-0.5 ${item.label === "注销" - ? "group-hover:text-red-600" - : "group-hover:text-[#003F6A]" - }`}> + group-hover:translate-x-0.5 ${ + item.label === "注销" + ? "group-hover:text-red-600" + : "group-hover:text-[#003F6A]" + }`}> {item.icon} </span> <span>{item.label}</span> diff --git a/apps/web/src/app/main/my-duty/page.tsx b/apps/web/src/app/main/my-duty/page.tsx new file mode 100644 index 0000000..7871969 --- /dev/null +++ b/apps/web/src/app/main/my-duty/page.tsx @@ -0,0 +1,7 @@ +export default function MyDutyPage() { + + + + return <> + </> +} diff --git a/apps/web/src/app/main/my-learning/page.tsx b/apps/web/src/app/main/my-learning/page.tsx new file mode 100644 index 0000000..503600c --- /dev/null +++ b/apps/web/src/app/main/my-learning/page.tsx @@ -0,0 +1,3 @@ +export default function MyLearningPage() { + return <></>; +} diff --git a/packages/common/prisma/schema.prisma b/packages/common/prisma/schema.prisma index 1c245e2..236c4ab 100755 --- a/packages/common/prisma/schema.prisma +++ b/packages/common/prisma/schema.prisma @@ -288,14 +288,6 @@ model Visit { message Message? @relation(fields: [messageId], references: [id]) messageId String? @map("message_id") lectureId String? @map("lecture_id") // 课时ID - - // 学习数据 - // progress Float? @default(0) @map("progress") // 完成进度(0-100%) - // isCompleted Boolean? @default(false) @map("is_completed") // 是否完成 - // lastPosition Int? @default(0) @map("last_position") // 视频播放位置(秒) - // totalWatchTime Int? @default(0) @map("total_watch_time") // 总观看时长(秒) - // // 时间记录 - // lastWatchedAt DateTime? @map("last_watched_at") // 最后观看时间 createdAt DateTime @default(now()) @map("created_at") // 创建时间 updatedAt DateTime @updatedAt @map("updated_at") // 更新时间 deletedAt DateTime? @map("deleted_at") // 删除时间,可为空