This commit is contained in:
ditiqi 2025-02-26 16:11:10 +08:00
commit 5c943711a4
10 changed files with 83 additions and 36 deletions

View File

@ -24,8 +24,7 @@ export default function CourseCard({ course }: CourseCardProps) {
onClick={() => handleClick(course)} onClick={() => handleClick(course)}
key={course.id} key={course.id}
hoverable hoverable
className="group overflow-hidden rounded-2xl border border-gray-200 bg-white 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"
shadow-xl hover:shadow-2xl transition-all duration-300 transform hover:-translate-y-2"
cover={ cover={
<div className="relative h-56 bg-gradient-to-br from-gray-900 to-gray-800 overflow-hidden"> <div className="relative h-56 bg-gradient-to-br from-gray-900 to-gray-800 overflow-hidden">
<div <div

View File

@ -43,7 +43,7 @@ const CategorySection = () => {
return ( return (
<section className="py-8 relative overflow-hidden"> <section className="py-8 relative overflow-hidden">
<div className="max-w-screen-2xl mx-auto px-4 relative"> <div className="max-w-screen-2xl mx-auto px-4 relative">
<div className="text-center mb-24"> <div className="text-center mb-12">
<Title <Title
level={2} level={2}
className="font-bold text-5xl mb-6 bg-gradient-to-r from-gray-900 via-gray-700 to-gray-800 bg-clip-text text-transparent motion-safe:animate-gradient-x"> className="font-bold text-5xl mb-6 bg-gradient-to-r from-gray-900 via-gray-700 to-gray-800 bg-clip-text text-transparent motion-safe:animate-gradient-x">

View File

@ -16,8 +16,9 @@ function useGetTaxonomy({ type }): GetTaxonomyProps {
taxonomy: { taxonomy: {
slug: type, slug: type,
}, },
parentId : null
}, },
take: 10, // 只取前10个 take: 11, // 只取前10个
}); });
const categories = useMemo(() => { const categories = useMemo(() => {
const allCategories = isLoading const allCategories = isLoading
@ -43,16 +44,16 @@ const CoursesSection: React.FC<CoursesSectionProps> = ({
type: TaxonomySlug.CATEGORY, type: TaxonomySlug.CATEGORY,
}); });
return ( return (
<section className="relative py-20 overflow-hidden bg-gradient-to-b from-gray-50 to-white"> <section className="relative py-16 overflow-hidden bg-gray-200">
<div className="absolute inset-0 bg-white max-w-screen-2xl mx-auto px-6"></div>
<div className="max-w-screen-2xl mx-auto px-6 relative"> <div className="max-w-screen-2xl mx-auto px-6 relative">
<div className="flex justify-between items-end mb-16"> <div className="flex justify-between items-end mb-16 ">
<div> <div>
<Title <Title
level={2} level={2}
className="font-bold text-5xl mb-6 bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent"> className="font-bold text-5xl mb-6 bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent">
{title} {title}
</Title> </Title>
<Text <Text
type="secondary" type="secondary"
className="text-xl font-light text-gray-600"> className="text-xl font-light text-gray-600">

View File

@ -30,7 +30,7 @@ interface PlatformStat {
const HeroSection = () => { const HeroSection = () => {
const carouselRef = useRef<CarouselRef>(null); const carouselRef = useRef<CarouselRef>(null);
const { statistics, slides } = useAppConfig(); const { statistics, slides } = useAppConfig();
const [countStatistics, setCountStatistics] = useState<number>(0) const [countStatistics, setCountStatistics] = useState<number>(4)
const platformStats: PlatformStat[] = useMemo(() => { const platformStats: PlatformStat[] = useMemo(() => {
return [ return [
{ icon: <TeamOutlined />, value: statistics.staffs, label: "注册学员" }, { icon: <TeamOutlined />, value: statistics.staffs, label: "注册学员" },
@ -111,7 +111,7 @@ const HeroSection = () => {
{ {
countStatistics > 1 && ( countStatistics > 1 && (
<div className="absolute -bottom-20 left-1/2 -translate-x-1/2 w-3/5 max-w-6xl px-4"> <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} 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]`}> <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) => { {platformStats.map((stat, index) => {
return stat.value return stat.value
? (<div ? (<div

View File

@ -11,7 +11,7 @@ export function MainLayout() {
<MainProvider> <MainProvider>
<Layout className="min-h-screen bg-gray-100"> <Layout className="min-h-screen bg-gray-100">
<MainHeader /> <MainHeader />
<Content className="mt-16 bg-gray-50 "> <Content className="mt-16 bg-gray-200 ">
<Outlet /> <Outlet />
</Content> </Content>
<MainFooter /> <MainFooter />

View File

@ -50,10 +50,13 @@ export function CourseDetailProvider({
(api.post as any).findFirst.useQuery( (api.post as any).findFirst.useQuery(
{ {
where: { id: editId }, where: { id: editId },
include: { // include: {
// sections: { include: { lectures: true } }, // // sections: { include: { lectures: true } },
enrollments: true, // enrollments: true,
}, // terms:true
// },
select:courseDetailSelect
}, },
{ enabled: Boolean(editId) } { enabled: Boolean(editId) }
); );

View File

@ -1,14 +1,16 @@
import { Course } from "@nice/common"; import { Course, TaxonomySlug } from "@nice/common";
import React, { useContext, useMemo } from "react"; import React, { useContext, useMemo } from "react";
import { Image, Typography, Skeleton } from "antd"; // 引入 antd 组件 import { Image, Typography, Skeleton, Tag } from "antd"; // 引入 antd 组件
import { CourseDetailContext } from "./CourseDetailContext"; import { CourseDetailContext } from "./CourseDetailContext";
import { import {
CalendarOutlined, CalendarOutlined,
EditTwoTone,
EyeOutlined, EyeOutlined,
PlayCircleOutlined, PlayCircleOutlined,
ReloadOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { useNavigate } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
export const CourseDetailDescription: React.FC = () => { export const CourseDetailDescription: React.FC = () => {
const { course, isLoading, selectedLectureId, setSelectedLectureId } = const { course, isLoading, selectedLectureId, setSelectedLectureId } =
@ -18,12 +20,14 @@ export const CourseDetailDescription: React.FC = () => {
return course?.sections?.[0]?.lectures?.[0]?.id; return course?.sections?.[0]?.lectures?.[0]?.id;
}, [course]); }, [course]);
const navigate = useNavigate(); const navigate = useNavigate();
const { canEdit } = useContext(CourseDetailContext);
const { id } = useParams();
return ( return (
<div className="w-full bg-white shadow-md rounded-lg border border-gray-200 p-6"> <div className="w-full bg-white shadow-md rounded-lg border border-gray-200 p-5 my-4">
{isLoading || !course ? ( {isLoading || !course ? (
<Skeleton active paragraph={{ rows: 4 }} /> <Skeleton active paragraph={{ rows: 4 }} />
) : ( ) : (
<div className="space-y-4"> <div className="space-y-2">
{!selectedLectureId && ( {!selectedLectureId && (
<> <>
<div className="relative my-4 overflow-hidden flex justify-center items-center"> <div className="relative my-4 overflow-hidden flex justify-center items-center">
@ -43,16 +47,61 @@ export const CourseDetailDescription: React.FC = () => {
</> </>
)} )}
<div className="text-lg font-bold">{"课程简介:"}</div> <div className="text-lg font-bold">{"课程简介:"}</div>
<div className="text-gray-600 flex justify-start gap-4"> <div className="flex gap-2 flex-wrap items-center">
<div>{course?.subTitle}</div> <div>{course?.subTitle}</div>
<div className="flex gap-1"> {
<EyeOutlined></EyeOutlined> course.terms.map((term) => {
<div>{course?.meta?.views || 0}</div> return (
<Tag
key={term.id}
// color={term.taxonomy.slug===TaxonomySlug.CATEGORY? "blue" : "green"}
color={
term?.taxonomy?.slug ===
TaxonomySlug.CATEGORY
? "blue"
: term?.taxonomy?.slug ===
TaxonomySlug.LEVEL
? "green"
: "orange"
}
className="px-3 py-1 rounded-full bg-blue-100 text-blue-600 border-0">
{term.name}
</Tag>
)
})
}
</div> </div>
<div className="text-gray-800 flex justify-start gap-5">
<div className="flex gap-1"> <div className="flex gap-1">
<CalendarOutlined></CalendarOutlined> <CalendarOutlined></CalendarOutlined>
{dayjs(course?.createdAt).format("YYYY年M月D日")} {dayjs(course?.createdAt).format("YYYY年M月D日")}
</div> </div>
<div className="flex gap-1">
<ReloadOutlined></ReloadOutlined>
{dayjs(course?.updatedAt).format("YYYY年M月D日")}
</div>
<div className="flex gap-1">
<EyeOutlined></EyeOutlined>
<div>{course?.meta?.views || 0}</div>
</div>
{
canEdit && (
<div
className="flex gap-1 text-primary hover:cursor-pointer"
onClick={() => {
const url = id
? `/course/${id}/editor`
: "/course/editor";
navigate(url);
}}
>
<EditTwoTone></EditTwoTone>
{"点击编辑课程"}
</div>
)
}
</div> </div>
<Paragraph <Paragraph
className="text-gray-600" className="text-gray-600"

View File

@ -25,7 +25,7 @@ export function CourseDetailHeader() {
return ( return (
<Header className="select-none flex items-center justify-center bg-white shadow-md border-b border-gray-100 fixed w-full z-30"> <Header className="select-none flex items-center justify-center bg-white shadow-md border-b border-gray-100 fixed w-full z-30">
<div className="w-full flex items-center justify-between h-full"> <div className="w-full flex items-center justify-between h-full">
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-10">
<HomeOutlined <HomeOutlined
onClick={() => { onClick={() => {
navigate("/"); navigate("/");
@ -33,7 +33,7 @@ export function CourseDetailHeader() {
className="text-2xl text-primary-500 hover:scale-105 cursor-pointer" className="text-2xl text-primary-500 hover:scale-105 cursor-pointer"
/> />
<div className="text-2xl font-bold bg-gradient-to-r from-primary-600 via-primary-500 to-primary-400 bg-clip-text text-transparent transition-transform "> <div className="text-2xl tracking-widest font-bold bg-gradient-to-r from-primary-600 via-primary-500 to-primary-400 bg-clip-text text-transparent transition-transform ">
{course?.title} {course?.title}
</div> </div>
{/* <NavigationMenu /> */} {/* <NavigationMenu /> */}

View File

@ -111,7 +111,7 @@ export function CourseFormProvider({
delete formattedValues.sections; delete formattedValues.sections;
delete formattedValues.deptIds; delete formattedValues.deptIds;
console.log(course.meta); console.log(course?.meta);
console.log(formattedValues?.meta); console.log(formattedValues?.meta);
try { try {
if (editId) { if (editId) {

View File

@ -2,7 +2,6 @@ import { api } from "@nice/client/";
import { Checkbox, Form } from "antd"; import { Checkbox, Form } from "antd";
import { TermDto } from "@nice/common"; import { TermDto } from "@nice/common";
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
export default function TermParentSelector({ export default function TermParentSelector({
value, value,
onChange, onChange,
@ -13,11 +12,7 @@ export default function TermParentSelector({
domainId, domainId,
style, style,
}: any) { }: any) {
const utils = api.useUtils();
const [selectedValues, setSelectedValues] = useState<string[]>([]); // 用于存储选中的值 const [selectedValues, setSelectedValues] = useState<string[]>([]); // 用于存储选中的值
const [termsData, setTermsData] = useState<any[]>([]);
const { const {
data, data,
isLoading, isLoading,