02272258
This commit is contained in:
parent
96b25aa4cd
commit
1605cea0aa
|
@ -184,32 +184,7 @@ export class PostService extends BaseTreeService<Prisma.PostDelegate> {
|
||||||
where?: Prisma.PostWhereInput;
|
where?: Prisma.PostWhereInput;
|
||||||
orderBy?: OrderByArgs<(typeof db.post)['findMany']>;
|
orderBy?: OrderByArgs<(typeof db.post)['findMany']>;
|
||||||
select?: Prisma.PostSelect<DefaultArgs>;
|
select?: Prisma.PostSelect<DefaultArgs>;
|
||||||
}): Promise<{
|
}) {
|
||||||
items: {
|
|
||||||
id: string;
|
|
||||||
type: string | null;
|
|
||||||
level: string | null;
|
|
||||||
state: string | null;
|
|
||||||
title: string | null;
|
|
||||||
subTitle: string | null;
|
|
||||||
content: string | null;
|
|
||||||
important: boolean | null;
|
|
||||||
domainId: string | null;
|
|
||||||
order: number | null;
|
|
||||||
duration: number | null;
|
|
||||||
rating: number | null;
|
|
||||||
createdAt: Date;
|
|
||||||
views: number;
|
|
||||||
publishedAt: Date | null;
|
|
||||||
updatedAt: Date;
|
|
||||||
deletedAt: Date | null;
|
|
||||||
authorId: string | null;
|
|
||||||
parentId: string | null;
|
|
||||||
hasChildren: boolean | null;
|
|
||||||
meta: Prisma.JsonValue | null;
|
|
||||||
}[];
|
|
||||||
totalPages: number;
|
|
||||||
}> {
|
|
||||||
// super.updateOrder;
|
// super.updateOrder;
|
||||||
return super.findManyWithPagination(args);
|
return super.findManyWithPagination(args);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,5 @@ import { useParams } from "react-router-dom";
|
||||||
|
|
||||||
export function CourseDetailPage() {
|
export function CourseDetailPage() {
|
||||||
const { id, lectureId } = useParams();
|
const { id, lectureId } = useParams();
|
||||||
console.log("Course ID:", id);
|
|
||||||
return <CourseDetail id={id} lectureId={lectureId}></CourseDetail>;
|
return <CourseDetail id={id} lectureId={lectureId}></CourseDetail>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ const HeroSection = () => {
|
||||||
{
|
{
|
||||||
icon: <EyeOutlined />,
|
icon: <EyeOutlined />,
|
||||||
value: statistics.reads,
|
value: statistics.reads,
|
||||||
label: "观看次数",
|
label: "播放次数",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}, [statistics]);
|
}, [statistics]);
|
||||||
|
|
|
@ -30,8 +30,10 @@ export function MainHeader() {
|
||||||
<NavigationMenu />
|
<NavigationMenu />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 中间搜索区域 - 允许适当收缩但保持可用性 */}
|
|
||||||
<div className="mx-4 flex-shrink md:flex-shrink-0 md:w-auto w-auto">
|
{/* 右侧区域 - 可以灵活收缩 */}
|
||||||
|
<div className="flex justify-end gap-2 md:gap-4 flex-shrink">
|
||||||
|
<div className="flex items-center gap-2 md:gap-4">
|
||||||
<Input
|
<Input
|
||||||
size="large"
|
size="large"
|
||||||
prefix={
|
prefix={
|
||||||
|
@ -60,14 +62,12 @@ export function MainHeader() {
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 右侧区域 - 可以灵活收缩 */}
|
|
||||||
<div className="flex justify-end gap-2 md:gap-4 flex-shrink">
|
|
||||||
<div className="flex items-center gap-2 md:gap-4">
|
|
||||||
{isAuthenticated && (
|
{isAuthenticated && (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
|
size="large"
|
||||||
|
shape="round"
|
||||||
|
icon={<PlusOutlined></PlusOutlined>}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const url = id
|
const url = id
|
||||||
? `/course/${id}/editor`
|
? `/course/${id}/editor`
|
||||||
|
@ -82,9 +82,12 @@ export function MainHeader() {
|
||||||
)}
|
)}
|
||||||
{isAuthenticated && (
|
{isAuthenticated && (
|
||||||
<Button
|
<Button
|
||||||
|
size="large"
|
||||||
|
shape="round"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
window.location.href = "/path/editor";
|
window.location.href = "/path/editor";
|
||||||
}}
|
}}
|
||||||
|
ghost type="primary"
|
||||||
icon={<PlusOutlined></PlusOutlined>}>
|
icon={<PlusOutlined></PlusOutlined>}>
|
||||||
创建思维导图
|
创建思维导图
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -93,8 +96,11 @@ export function MainHeader() {
|
||||||
<UserMenu />
|
<UserMenu />
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<Button
|
||||||
|
type="primary"
|
||||||
|
size="large"
|
||||||
|
shape="round"
|
||||||
onClick={() => navigate("/login")}
|
onClick={() => navigate("/login")}
|
||||||
className="flex items-center space-x-1 bg-gradient-to-r from-blue-500 to-blue-600 text-white hover:from-blue-600 hover:to-blue-700 border-none shadow-md hover:shadow-lg transition-all"
|
|
||||||
icon={<UserOutlined />}>
|
icon={<UserOutlined />}>
|
||||||
登录
|
登录
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -11,7 +11,7 @@ export const NavigationMenu = () => {
|
||||||
const menuItems = useMemo(() => {
|
const menuItems = useMemo(() => {
|
||||||
const baseItems = [
|
const baseItems = [
|
||||||
{ key: "home", path: "/", label: "首页" },
|
{ key: "home", path: "/", label: "首页" },
|
||||||
{ key: "path", path: "/path", label: "思维导图" },
|
{ key: "path", path: "/path", label: "全部思维导图" },
|
||||||
{ key: "courses", path: "/courses", label: "所有课程" },
|
{ key: "courses", path: "/courses", label: "所有课程" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -20,9 +20,10 @@ export const NavigationMenu = () => {
|
||||||
} else {
|
} else {
|
||||||
return [
|
return [
|
||||||
...baseItems,
|
...baseItems,
|
||||||
{ key: "my-duty", path: "/my-duty", label: "我的授课" },
|
{ key: "my-duty", path: "/my-duty", label: "我创建的课程" },
|
||||||
{ key: "my-learning", path: "/my-learning", label: "我的课程" },
|
{ key: "my-learning", path: "/my-learning", label: "我学习的课程" },
|
||||||
{ key: "my-path", path: "/my-path", label: "我的路径" },
|
{ key: "my-duty-path", path: "/my-duty-path", label: "我创建的思维导图" },
|
||||||
|
{ key: "my-path", path: "/my-path", label: "我学习的思维导图" },
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}, [isAuthenticated]);
|
}, [isAuthenticated]);
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
import PostList from "@web/src/components/models/course/list/PostList";
|
||||||
|
import { useAuth } from "@web/src/providers/auth-provider";
|
||||||
|
import { useMainContext } from "../../layout/MainProvider";
|
||||||
|
import { PostType } from "@nice/common";
|
||||||
|
import PathCard from "@web/src/components/models/post/SubPost/PathCard";
|
||||||
|
|
||||||
|
export default function MyLearningListContainer() {
|
||||||
|
const { user } = useAuth();
|
||||||
|
const { searchCondition, termsCondition } = useMainContext();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<PostList
|
||||||
|
renderItem={(post) => <PathCard post={post}></PathCard>}
|
||||||
|
params={{
|
||||||
|
pageSize: 12,
|
||||||
|
where: {
|
||||||
|
type: PostType.PATH,
|
||||||
|
students: {
|
||||||
|
some: {
|
||||||
|
id: user?.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...termsCondition,
|
||||||
|
...searchCondition,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
cols={4}></PostList>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import BasePostLayout from "../layout/BasePost/BasePostLayout";
|
||||||
|
import { useMainContext } from "../layout/MainProvider";
|
||||||
|
import { PostType } from "@nice/common";
|
||||||
|
import MyDutyPathContainer from "./components/MyDutyPathContainer";
|
||||||
|
|
||||||
|
export default function MyDutyPathPage() {
|
||||||
|
const { setSearchMode } = useMainContext();
|
||||||
|
useEffect(() => {
|
||||||
|
setSearchMode(PostType.PATH);
|
||||||
|
}, [setSearchMode]);
|
||||||
|
return (
|
||||||
|
<BasePostLayout>
|
||||||
|
<MyDutyPathContainer></MyDutyPathContainer>
|
||||||
|
</BasePostLayout>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
import { Button, Card, Empty, Form, Space, Spin, message, theme } from "antd";
|
import { Button, Card, Empty, Form, Space, Spin, message, theme } from "antd";
|
||||||
import NodeMenu from "./NodeMenu";
|
import NodeMenu from "./NodeMenu";
|
||||||
import { api, usePost } from "@nice/client";
|
import { api, usePost, useVisitor } from "@nice/client";
|
||||||
import {
|
import {
|
||||||
ObjectType,
|
ObjectType,
|
||||||
PathDto,
|
PathDto,
|
||||||
|
@ -8,6 +8,7 @@ import {
|
||||||
PostType,
|
PostType,
|
||||||
Prisma,
|
Prisma,
|
||||||
RolePerms,
|
RolePerms,
|
||||||
|
VisitType,
|
||||||
} from "@nice/common";
|
} from "@nice/common";
|
||||||
import TermSelect from "../../models/term/term-select";
|
import TermSelect from "../../models/term/term-select";
|
||||||
import DepartmentSelect from "../../models/department/department-select";
|
import DepartmentSelect from "../../models/department/department-select";
|
||||||
|
@ -23,6 +24,7 @@ export default function MindEditor({ id }: { id?: string }) {
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
const [instance, setInstance] = useState<MindElixirInstance | null>(null);
|
const [instance, setInstance] = useState<MindElixirInstance | null>(null);
|
||||||
const { isAuthenticated, user, hasSomePermissions } = useAuth();
|
const { isAuthenticated, user, hasSomePermissions } = useAuth();
|
||||||
|
const { read } = useVisitor()
|
||||||
const { data: post, isLoading }: { data: PathDto; isLoading: boolean } =
|
const { data: post, isLoading }: { data: PathDto; isLoading: boolean } =
|
||||||
api.post.findFirst.useQuery({
|
api.post.findFirst.useQuery({
|
||||||
where: {
|
where: {
|
||||||
|
@ -42,9 +44,19 @@ export default function MindEditor({ id }: { id?: string }) {
|
||||||
});
|
});
|
||||||
const { handleFileUpload } = useTusUpload();
|
const { handleFileUpload } = useTusUpload();
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
useEffect(() => {
|
||||||
|
if (post?.id) {
|
||||||
|
read.mutateAsync({
|
||||||
|
data: {
|
||||||
|
visitorId: user?.id || null,
|
||||||
|
postId: post?.id,
|
||||||
|
type: VisitType.READED,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [post]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (post && form && instance && id) {
|
if (post && form && instance && id) {
|
||||||
console.log(post);
|
|
||||||
instance.refresh((post as any).meta);
|
instance.refresh((post as any).meta);
|
||||||
const deptIds = (post?.depts || [])?.map((dept) => dept.id);
|
const deptIds = (post?.depts || [])?.map((dept) => dept.id);
|
||||||
const formData = {
|
const formData = {
|
||||||
|
|
|
@ -8,11 +8,7 @@ export default function CourseDetail({
|
||||||
id?: string;
|
id?: string;
|
||||||
lectureId?: string;
|
lectureId?: string;
|
||||||
}) {
|
}) {
|
||||||
const iframeStyle = {
|
|
||||||
width: "50%",
|
|
||||||
height: "100vh",
|
|
||||||
border: "none",
|
|
||||||
};
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<CourseDetailProvider editId={id}>
|
<CourseDetailProvider editId={id}>
|
||||||
|
|
|
@ -7,25 +7,10 @@ import { Course, LectureType, PostType } from "@nice/common";
|
||||||
import { CourseDetailContext } from "./CourseDetailContext";
|
import { CourseDetailContext } from "./CourseDetailContext";
|
||||||
import CollapsibleContent from "@web/src/components/common/container/CollapsibleContent";
|
import CollapsibleContent from "@web/src/components/common/container/CollapsibleContent";
|
||||||
import { Skeleton } from "antd";
|
import { Skeleton } from "antd";
|
||||||
import { CoursePreview } from "./CoursePreview/CoursePreview";
|
|
||||||
import ResourcesShower from "@web/src/components/common/uploader/ResourceShower";
|
import ResourcesShower from "@web/src/components/common/uploader/ResourceShower";
|
||||||
import {
|
|
||||||
BookOutlined,
|
|
||||||
CalendarOutlined,
|
|
||||||
EditTwoTone,
|
|
||||||
EyeOutlined,
|
|
||||||
ReloadOutlined,
|
|
||||||
} from "@ant-design/icons";
|
|
||||||
import dayjs from "dayjs";
|
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import CourseDetailTitle from "./CourseDetailTitle";
|
import CourseDetailTitle from "./CourseDetailTitle";
|
||||||
|
|
||||||
// interface CourseDetailDisplayAreaProps {
|
|
||||||
// // course: Course;
|
|
||||||
// // videoSrc?: string;
|
|
||||||
// // videoPoster?: string;
|
|
||||||
// // isLoading?: boolean;
|
|
||||||
// }
|
|
||||||
|
|
||||||
export const CourseDetailDisplayArea: React.FC = () => {
|
export const CourseDetailDisplayArea: React.FC = () => {
|
||||||
// 创建滚动动画效果
|
// 创建滚动动画效果
|
||||||
|
|
|
@ -19,11 +19,7 @@ export default function CourseDetailLayout() {
|
||||||
const [isSyllabusOpen, setIsSyllabusOpen] = useState(true);
|
const [isSyllabusOpen, setIsSyllabusOpen] = useState(true);
|
||||||
return (
|
return (
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
{/* <CourseDetailHeader /> */}
|
|
||||||
|
|
||||||
{/* 添加 Header 组件 */}
|
|
||||||
{/* 主内容区域 */}
|
|
||||||
{/* 为了防止 Header 覆盖内容,添加上边距 */}
|
|
||||||
<div className="pt-12 px-32">
|
<div className="pt-12 px-32">
|
||||||
{" "}
|
{" "}
|
||||||
{/* 添加这个包装 div */}
|
{/* 添加这个包装 div */}
|
||||||
|
|
|
@ -44,7 +44,7 @@ export default function CourseDetailTitle() {
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-1">
|
<div className="flex gap-1">
|
||||||
<EyeOutlined></EyeOutlined>
|
<EyeOutlined></EyeOutlined>
|
||||||
<div>{`观看次数${course?.meta?.views || 0}`}</div>
|
<div>{`播放次数${course?.meta?.views || 0}`}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-1">
|
<div className="flex gap-1">
|
||||||
<BookOutlined />
|
<BookOutlined />
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
import { CheckOutlined } from '@ant-design/icons';
|
|
||||||
import React from 'react';
|
|
||||||
interface CourseObjectivesProps {
|
|
||||||
objectives: string[];
|
|
||||||
title?: string;
|
|
||||||
}
|
|
||||||
const CourseObjectives: React.FC<CourseObjectivesProps> = ({
|
|
||||||
objectives,
|
|
||||||
title = "您将会学到"
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<div className="bg-white p-6 rounded-lg shadow-sm border border-gray-200">
|
|
||||||
<h2 className="text-xl font-bold mb-4">{title}</h2>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
||||||
{objectives.map((objective, index) => (
|
|
||||||
<div
|
|
||||||
key={index}
|
|
||||||
className="flex items-start space-x-3"
|
|
||||||
>
|
|
||||||
<CheckOutlined></CheckOutlined>
|
|
||||||
<span className="text-gray-700">{objective}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CourseObjectives;
|
|
|
@ -86,6 +86,14 @@ export const routes: CustomRouteObject[] = [
|
||||||
</WithAuth>
|
</WithAuth>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "my-duty-path",
|
||||||
|
element: (
|
||||||
|
<WithAuth>
|
||||||
|
<MyPathPage></MyPathPage>
|
||||||
|
</WithAuth>
|
||||||
|
),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "my-duty",
|
path: "my-duty",
|
||||||
element: (
|
element: (
|
||||||
|
|
Loading…
Reference in New Issue