diff --git a/apps/web/src/app/main/home/components/CategorySection.tsx b/apps/web/src/app/main/home/components/CategorySection.tsx index a64b551..02fa591 100755 --- a/apps/web/src/app/main/home/components/CategorySection.tsx +++ b/apps/web/src/app/main/home/components/CategorySection.tsx @@ -1,7 +1,6 @@ import React, { useState, useCallback, useEffect, useMemo } from 'react'; import { Typography, Button } from 'antd'; import { stringToColor, TaxonomySlug, TermDto } from '@nice/common'; -import { api } from '@nice/client'; const { Title, Text } = Typography; diff --git a/apps/web/src/app/main/home/components/CoursesSection.tsx b/apps/web/src/app/main/home/components/CoursesSection.tsx index a1f0cfe..ce0d5e0 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 } from 'react-router-dom'; -import { Button, Card, Typography, Tag, Progress,Spin } from 'antd'; +import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; +import { Button, Card, Typography, Tag, Progress, Spin } from 'antd'; import { PlayCircleOutlined, UserOutlined, @@ -8,36 +8,37 @@ import { TeamOutlined, StarOutlined, ArrowRightOutlined, + EyeOutlined, } from '@ant-design/icons'; import { TaxonomySlug, TermDto } from '@nice/common'; import { api } from '@nice/client'; - +// const {courseId} = useParams(); interface GetTaxonomyProps { categories: string[]; isLoading: boolean; } -function useGetTaxonomy({type}) : GetTaxonomyProps { - const {data,isLoading} :{data:TermDto[],isLoading:boolean}= api.term.findMany.useQuery({ - where:{ - taxonomy: { - //TaxonomySlug.CATEGORY - slug:type - } - }, - include:{ - children :true - }, - take:10, // 只取前10个 - orderBy: { - createdAt: 'desc', // 按创建时间降序排列 - }, +function useGetTaxonomy({ type }): GetTaxonomyProps { + const { data, isLoading }: { data: TermDto[], isLoading: boolean } = api.term.findMany.useQuery({ + where: { + taxonomy: { + //TaxonomySlug.CATEGORY + slug: type + } + }, + include: { + children: true + }, + take: 10, // 只取前10个 + orderBy: { + createdAt: 'desc', // 按创建时间降序排列 + }, }) const categories = useMemo(() => { - const allCategories = isLoading ? [] : data?.map((course) => course.name); - return [...Array.from(new Set(allCategories))]; + const allCategories = isLoading ? [] : data?.map((course) => course.name); + return [...Array.from(new Set(allCategories))]; }, [data]); - return {categories,isLoading} + return { categories, isLoading } } @@ -63,7 +64,6 @@ interface CoursesSectionProps { initialVisibleCoursesCount?: number; } - const CoursesSection: React.FC = ({ title, description, @@ -73,12 +73,20 @@ const CoursesSection: React.FC = ({ const navigate = useNavigate(); const [selectedCategory, setSelectedCategory] = useState('全部'); const [visibleCourses, setVisibleCourses] = useState(initialVisibleCoursesCount); - const gateGory : GetTaxonomyProps = useGetTaxonomy({ + const gateGory: GetTaxonomyProps = useGetTaxonomy({ type: TaxonomySlug.CATEGORY, }) + const { data } = api.post.findMany.useQuery({ + take: 10, + } + ) useEffect(() => { - - }) + console.log(data) + }, [data]) + const handleClick = (course: Course) => { + navigate(`/courses?courseId=${course.id}/detail`); + } + const filteredCourses = useMemo(() => { return selectedCategory === '全部' ? courses @@ -86,76 +94,74 @@ const CoursesSection: React.FC = ({ }, [selectedCategory, courses]); const displayedCourses = filteredCourses.slice(0, visibleCourses); - return ( -
-
-
+
+
+
{title} - + {description}
-
- {gateGory.isLoading ? : +
+ {gateGory.isLoading ? : ( <> - setSelectedCategory("全部")} - className={`px-4 py-2 text-base cursor-pointer hover:scale-105 transform transition-all duration-300 ${selectedCategory === "全部" - ? 'shadow-[0_2px_8px_-4px_rgba(59,130,246,0.5)]' - : 'hover:shadow-md' - }`} - >全部 - { - gateGory.categories.map((category) => ( - setSelectedCategory(category)} - className={`px-4 py-2 text-base cursor-pointer hover:scale-105 transform transition-all duration-300 ${selectedCategory === category - ? 'shadow-[0_2px_8px_-4px_rgba(59,130,246,0.5)]' - : 'hover:shadow-md' - }`} - > - {category} - - )) - } + setSelectedCategory("全部")} + className={`px-6 py-2 text-base cursor-pointer rounded-full transition-all duration-300 ${selectedCategory === "全部" + ? 'bg-blue-600 text-white shadow-lg' + : 'bg-white text-gray-600 hover:bg-gray-100' + }`} + >全部 + { + gateGory.categories.map((category) => ( + setSelectedCategory(category)} + className={`px-6 py-2 text-base cursor-pointer rounded-full transition-all duration-300 ${selectedCategory === category + ? 'bg-blue-600 text-white shadow-lg' + : 'bg-white text-gray-600 hover:bg-gray-100' + }`} + > + {category} + + )) + } - ) - } + }
-
+
{displayedCourses.map((course) => ( handleClick(course)} key={course.id} hoverable - className="group overflow-hidden rounded-2xl border-0 bg-white/70 backdrop-blur-sm - shadow-[0_10px_40px_-15px_rgba(0,0,0,0.1)] hover:shadow-[0_20px_50px_-15px_rgba(0,0,0,0.15)] - transition-all duration-700 ease-out transform hover:-translate-y-1 will-change-transform" + 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.progress > 0 && ( -
- + {/* = ({ to: '#60a5fa', }} className="m-0" - /> + /> */}
)}
} > -
+
{course.category} @@ -185,35 +191,28 @@ const CoursesSection: React.FC = ({ ? 'blue' : 'purple' } - className="px-3 py-1 rounded-full border-0 shadow-sm transition-all duration-300 hover:shadow-md" + className="px-3 py-1 rounded-full border-0" > {course.level}
- {course.title} + <button > {course.title}</button> -
- - - {course.instructor} - -
-
- - - {course.duration} - - - - {course.students.toLocaleString()} - - - - {course.rating} + +
+ +
+ + {course.instructor} + +
+ + + 观看次数{course.progress}%
@@ -226,25 +225,25 @@ const CoursesSection: React.FC = ({ 立即学习
+
))}
{filteredCourses.length >= visibleCourses && ( -
-
-
-
+
+
+
- + +
-
)}
diff --git a/apps/web/src/app/main/home/page.tsx b/apps/web/src/app/main/home/page.tsx index 4d4be9e..453fa17 100755 --- a/apps/web/src/app/main/home/page.tsx +++ b/apps/web/src/app/main/home/page.tsx @@ -2,6 +2,8 @@ import HeroSection from './components/HeroSection'; import CategorySection from './components/CategorySection'; import CoursesSection from './components/CoursesSection'; import FeaturedTeachersSection from './components/FeaturedTeachersSection'; +import { useEffect } from 'react'; +import { api } from '@nice/client' const HomePage = () => { const mockCourses = [ { @@ -13,7 +15,7 @@ const HomePage = () => { level: '入门', duration: '36小时', category: '编程语言', - progress: 0, + progress: 16, thumbnail: '/images/course1.jpg', }, { @@ -49,7 +51,7 @@ const HomePage = () => { level: '高级', duration: '56小时', category: '编程语言', - progress: 0, + progress: 15, thumbnail: '/images/course4.jpg', }, { @@ -97,15 +99,13 @@ const HomePage = () => { level: '中级', duration: '40小时', category: '移动开发', - progress: 0, + progress: 70, thumbnail: '/images/course8.jpg', }, ]; - return (
- getBaseUrl("http", parseInt(env.SERVER_PORT)), [getBaseUrl] ); - const websocketUrl = useMemo(() => getBaseUrl("ws", 3000), [getBaseUrl]); + const websocketUrl = useMemo(() => parseInt(env.SERVER_PORT), [getBaseUrl]); const checkIsTusUrl = useCallback( (url: string) => { return url.startsWith(tusUrl); diff --git a/apps/web/src/routes/index.tsx b/apps/web/src/routes/index.tsx index 09aeaba..70f60a8 100755 --- a/apps/web/src/routes/index.tsx +++ b/apps/web/src/routes/index.tsx @@ -3,6 +3,7 @@ import { IndexRouteObject, Link, NonIndexRouteObject, + useParams, } from "react-router-dom"; import ErrorPage from "../app/error"; import WithAuth from "../components/utils/with-auth"; @@ -40,6 +41,7 @@ export type CustomRouteObject = | CustomIndexRouteObject | CustomNonIndexRouteObject; export const routes: CustomRouteObject[] = [ + { path: "/", errorElement: , @@ -144,4 +146,5 @@ export const routes: CustomRouteObject[] = [ }, ]; + export const router = createBrowserRouter(routes); diff --git a/apps/web/src/utils/axios-client.ts b/apps/web/src/utils/axios-client.ts index 23877df..f75302f 100755 --- a/apps/web/src/utils/axios-client.ts +++ b/apps/web/src/utils/axios-client.ts @@ -1,6 +1,6 @@ import axios from 'axios'; import { env } from '../env'; -const BASE_URL = `http://${env.SERVER_IP}:3000` +const BASE_URL = `http://${env.SERVER_IP}:${env.SERVER_PORT}` const apiClient = axios.create({ baseURL: BASE_URL, // withCredentials: true, diff --git a/config/nginx/conf.d/web.conf b/config/nginx/conf.d/web.conf index bf90259..67302b8 100755 --- a/config/nginx/conf.d/web.conf +++ b/config/nginx/conf.d/web.conf @@ -101,6 +101,7 @@ server { internal; # 代理到认证服务 proxy_pass http://host.docker.internal:3000/auth/file; + # 请求优化:不传递请求体 proxy_pass_request_body off; proxy_set_header Content-Length ""; diff --git a/packages/common/prisma/schema.prisma b/packages/common/prisma/schema.prisma index 01bb99e..89768ea 100755 --- a/packages/common/prisma/schema.prisma +++ b/packages/common/prisma/schema.prisma @@ -268,7 +268,6 @@ model Message { visits Visit[] createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime? @updatedAt @map("updated_at") - @@index([type, createdAt]) @@map("message") }