add
This commit is contained in:
parent
ee9df61320
commit
49d3f613fc
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 8.4 KiB |
|
@ -20,7 +20,7 @@ export function BasePostLayout({
|
||||||
<div className="w-1/6">
|
<div className="w-1/6">
|
||||||
<FilterSection></FilterSection>
|
<FilterSection></FilterSection>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-5/6 p-4">{children}</div>
|
<div className="w-5/6 p-4 py-8">{children}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -19,26 +19,27 @@ export function MainHeader() {
|
||||||
const { searchValue, setSearchValue } = useMainContext();
|
const { searchValue, setSearchValue } = useMainContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="select-none w-full flex items-center justify-start bg-white shadow-md border-b border-gray-100 fixed z-30 py-2">
|
<div className="select-none w-full flex items-center justify-between bg-white shadow-md border-b border-gray-100 fixed z-30 py-2 px-4 md:px-6">
|
||||||
<div className="flex-1 px-4 md:px-6 mx-auto flex items-center justify-start h-full">
|
{/* 左侧区域 - 设置为不收缩 */}
|
||||||
<div className="flex items-center justify-start space-x-4">
|
<div className="flex items-center justify-start space-x-4 flex-shrink-0">
|
||||||
<img src="/logo.svg" className="h-12 w-12 " />
|
<img src="/logo.svg" className="h-12 w-12" />
|
||||||
<div
|
<div
|
||||||
onClick={() => navigate("/")}
|
onClick={() => navigate("/")}
|
||||||
className="text-2xl font-bold bg-gradient-to-r from-primary-600 via-primary-500 to-primary-400 bg-clip-text text-transparent hover:scale-105 transition-transform cursor-pointer">
|
className="text-2xl font-bold bg-gradient-to-r from-primary-600 via-primary-500 to-primary-400 bg-clip-text text-transparent hover:scale-105 transition-transform cursor-pointer whitespace-nowrap">
|
||||||
烽火慕课
|
烽火慕课
|
||||||
</div>
|
|
||||||
<NavigationMenu />
|
|
||||||
</div>
|
</div>
|
||||||
|
<NavigationMenu />
|
||||||
</div>
|
</div>
|
||||||
<div className=" flex ">
|
|
||||||
|
{/* 中间搜索区域 - 允许适当收缩但保持可用性 */}
|
||||||
|
<div className="mx-4 flex-shrink md:flex-shrink-0 md:w-auto w-auto">
|
||||||
<Input
|
<Input
|
||||||
size="large"
|
size="large"
|
||||||
prefix={
|
prefix={
|
||||||
<SearchOutlined className="text-gray-400 group-hover:text-blue-500 transition-colors" />
|
<SearchOutlined className="text-gray-400 group-hover:text-blue-500 transition-colors" />
|
||||||
}
|
}
|
||||||
placeholder="搜索课程"
|
placeholder="搜索课程"
|
||||||
className="w-96 rounded-full"
|
className="w-full md:w-96 rounded-full"
|
||||||
value={searchValue}
|
value={searchValue}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
if (!window.location.pathname.startsWith("/search")) {
|
if (!window.location.pathname.startsWith("/search")) {
|
||||||
|
@ -61,8 +62,10 @@ export function MainHeader() {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 flex justify-end gap-4 mr-2">
|
|
||||||
<div className="flex items-center gap-4">
|
{/* 右侧区域 - 可以灵活收缩 */}
|
||||||
|
<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
|
||||||
|
|
|
@ -11,8 +11,8 @@ export const NavigationMenu = () => {
|
||||||
const menuItems = useMemo(() => {
|
const menuItems = useMemo(() => {
|
||||||
const baseItems = [
|
const baseItems = [
|
||||||
{ key: "home", path: "/", label: "首页" },
|
{ key: "home", path: "/", label: "首页" },
|
||||||
{ key: "courses", path: "/courses", label: "全部课程" },
|
|
||||||
{ key: "path", path: "/path", label: "学习路径" },
|
{ key: "path", path: "/path", label: "学习路径" },
|
||||||
|
{ key: "courses", path: "/courses", label: "全部课程" },
|
||||||
];
|
];
|
||||||
|
|
||||||
if (!isAuthenticated) {
|
if (!isAuthenticated) {
|
||||||
|
|
|
@ -2,10 +2,8 @@ import { Tag } from "antd";
|
||||||
import { PostDto, TaxonomySlug } from "@nice/common";
|
import { PostDto, TaxonomySlug } from "@nice/common";
|
||||||
|
|
||||||
const TermInfo = ({ post }: { post: PostDto }) => {
|
const TermInfo = ({ post }: { post: PostDto }) => {
|
||||||
console.log("xx", post?.terms);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div>
|
||||||
{post?.terms && post?.terms?.length > 0 ? (
|
{post?.terms && post?.terms?.length > 0 ? (
|
||||||
<div className="flex gap-2 mb-4">
|
<div className="flex gap-2 mb-4">
|
||||||
{post?.terms?.map((term: any) => {
|
{post?.terms?.map((term: any) => {
|
||||||
|
@ -15,10 +13,10 @@ const TermInfo = ({ post }: { post: PostDto }) => {
|
||||||
color={
|
color={
|
||||||
term?.taxonomy?.slug ===
|
term?.taxonomy?.slug ===
|
||||||
TaxonomySlug.CATEGORY
|
TaxonomySlug.CATEGORY
|
||||||
? "blue"
|
? "green"
|
||||||
: term?.taxonomy?.slug ===
|
: term?.taxonomy?.slug ===
|
||||||
TaxonomySlug.LEVEL
|
TaxonomySlug.LEVEL
|
||||||
? "green"
|
? "blue"
|
||||||
: "orange"
|
: "orange"
|
||||||
}
|
}
|
||||||
className="px-3 py-1 rounded-full border-0">
|
className="px-3 py-1 rounded-full border-0">
|
||||||
|
@ -36,7 +34,7 @@ const TermInfo = ({ post }: { post: PostDto }) => {
|
||||||
</Tag>
|
</Tag>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ const CollapsibleContent: React.FC<CollapsibleContentProps> = ({ content }) => {
|
||||||
const contentWrapperRef = useRef(null);
|
const contentWrapperRef = useRef(null);
|
||||||
return (
|
return (
|
||||||
<div className=" text-base ">
|
<div className=" text-base ">
|
||||||
<div className=" flex flex-col gap-4 border border-white hover:ring-1 ring-white transition-all duration-300 ease-in-out rounded-xl p-6 ">
|
<div className=" flex flex-col gap-4 transition-all duration-300 ease-in-out rounded-xl p-6 ">
|
||||||
{/* 包装整个内容区域的容器 */}
|
{/* 包装整个内容区域的容器 */}
|
||||||
<div ref={contentWrapperRef}>
|
<div ref={contentWrapperRef}>
|
||||||
{/* 内容区域 */}
|
{/* 内容区域 */}
|
||||||
|
|
|
@ -35,7 +35,7 @@ export default function ResourcesShower({
|
||||||
const imageResources = dealedResources.filter((res) => res.isImage);
|
const imageResources = dealedResources.filter((res) => res.isImage);
|
||||||
const fileResources = dealedResources.filter((res) => !res.isImage);
|
const fileResources = dealedResources.filter((res) => !res.isImage);
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-3">
|
||||||
{imageResources.length > 0 && (
|
{imageResources.length > 0 && (
|
||||||
<Row gutter={[16, 16]} className="mb-6">
|
<Row gutter={[16, 16]} className="mb-6">
|
||||||
<Image.PreviewGroup>
|
<Image.PreviewGroup>
|
||||||
|
@ -82,6 +82,7 @@ export default function ResourcesShower({
|
||||||
</Image.PreviewGroup>
|
</Image.PreviewGroup>
|
||||||
</Row>
|
</Row>
|
||||||
)}
|
)}
|
||||||
|
<div className=" text-sm px-2">附件:</div>
|
||||||
{fileResources.length > 0 && (
|
{fileResources.length > 0 && (
|
||||||
<div className="rounded-xl p-1 border border-gray-100 bg-white">
|
<div className="rounded-xl p-1 border border-gray-100 bg-white">
|
||||||
<div className="flex flex-nowrap overflow-x-auto scrollbar-hide gap-1.5">
|
<div className="flex flex-nowrap overflow-x-auto scrollbar-hide gap-1.5">
|
||||||
|
|
|
@ -36,8 +36,12 @@ interface CourseFormProviderProps {
|
||||||
editId?: string; // 添加 editId 参数
|
editId?: string; // 添加 editId 参数
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CourseDetailContext =createContext<CourseDetailContextType | null>(null);
|
export const CourseDetailContext =
|
||||||
export function CourseDetailProvider({children,editId}: CourseFormProviderProps) {
|
createContext<CourseDetailContextType | null>(null);
|
||||||
|
export function CourseDetailProvider({
|
||||||
|
children,
|
||||||
|
editId,
|
||||||
|
}: CourseFormProviderProps) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { read } = useVisitor();
|
const { read } = useVisitor();
|
||||||
const { user, hasSomePermissions, isAuthenticated } = useAuth();
|
const { user, hasSomePermissions, isAuthenticated } = useAuth();
|
||||||
|
@ -86,7 +90,9 @@ export function CourseDetailProvider({children,editId}: CourseFormProviderProps)
|
||||||
}
|
}
|
||||||
}, [course]);
|
}, [course]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
navigate(`/course/${editId}/detail/${selectedLectureId}`);
|
if (lectureId !== selectedLectureId) {
|
||||||
|
navigate(`/course/${editId}/detail/${selectedLectureId}`);
|
||||||
|
}
|
||||||
}, [selectedLectureId, editId]);
|
}, [selectedLectureId, editId]);
|
||||||
const [isHeaderVisible, setIsHeaderVisible] = useState(true); // 新增
|
const [isHeaderVisible, setIsHeaderVisible] = useState(true); // 新增
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -2,22 +2,25 @@ import { Course, TaxonomySlug } from "@nice/common";
|
||||||
import React, { useContext, useMemo } from "react";
|
import React, { useContext, useMemo } from "react";
|
||||||
import { Image, Typography, Skeleton, Tag } from "antd"; // 引入 antd 组件
|
import { Image, Typography, Skeleton, Tag } from "antd"; // 引入 antd 组件
|
||||||
import { CourseDetailContext } from "./CourseDetailContext";
|
import { CourseDetailContext } from "./CourseDetailContext";
|
||||||
import {
|
|
||||||
BookOutlined,
|
|
||||||
CalendarOutlined,
|
|
||||||
EditTwoTone,
|
|
||||||
EyeOutlined,
|
|
||||||
PlayCircleOutlined,
|
|
||||||
ReloadOutlined,
|
|
||||||
TeamOutlined,
|
|
||||||
} from "@ant-design/icons";
|
|
||||||
import dayjs from "dayjs";
|
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
|
import { useStaff } from "@nice/client";
|
||||||
|
import { useAuth } from "@web/src/providers/auth-provider";
|
||||||
|
import TermInfo from "@web/src/app/main/path/components/TermInfo";
|
||||||
|
import { PictureOutlined } from "@ant-design/icons";
|
||||||
|
|
||||||
export const CourseDetailDescription: React.FC = () => {
|
export const CourseDetailDescription: React.FC = () => {
|
||||||
const { course,canEdit, isLoading, selectedLectureId, setSelectedLectureId } =
|
const {
|
||||||
useContext(CourseDetailContext);
|
course,
|
||||||
const { Paragraph, Title } = Typography;
|
canEdit,
|
||||||
|
isLoading,
|
||||||
|
selectedLectureId,
|
||||||
|
setSelectedLectureId,
|
||||||
|
userIsLearning,
|
||||||
|
lecture = null,
|
||||||
|
} = useContext(CourseDetailContext);
|
||||||
|
const { Paragraph } = Typography;
|
||||||
|
const { user } = useAuth();
|
||||||
|
const { update } = useStaff();
|
||||||
const firstLectureId = useMemo(() => {
|
const firstLectureId = useMemo(() => {
|
||||||
return course?.sections?.[0]?.lectures?.[0]?.id;
|
return course?.sections?.[0]?.lectures?.[0]?.id;
|
||||||
}, [course]);
|
}, [course]);
|
||||||
|
@ -30,49 +33,44 @@ export const CourseDetailDescription: React.FC = () => {
|
||||||
<Skeleton active paragraph={{ rows: 4 }} />
|
<Skeleton active paragraph={{ rows: 4 }} />
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{!selectedLectureId && course?.meta?.thumbnail && (
|
{!selectedLectureId && (
|
||||||
<>
|
<div className="relative mb-4 overflow-hidden flex justify-center items-center">
|
||||||
<div className="relative mb-4 overflow-hidden flex justify-center items-center">
|
{
|
||||||
<Image
|
<Image
|
||||||
src={course?.meta?.thumbnail}
|
src={course.meta.thumbnail}
|
||||||
preview={false}
|
preview={false}
|
||||||
className="w-full h-full object-cover z-0"
|
className="w-full h-full object-cover z-0"
|
||||||
|
fallback="/placeholder.webp"
|
||||||
/>
|
/>
|
||||||
<div
|
}
|
||||||
onClick={() => {
|
<div
|
||||||
setSelectedLectureId(firstLectureId);
|
onClick={async () => {
|
||||||
}}
|
setSelectedLectureId(firstLectureId);
|
||||||
className="w-full h-full absolute top-0 z-10 bg-[rgba(0,0,0,0.3)] transition-all duration-300 ease-in-out hover:bg-[rgba(0,0,0,0.7)] cursor-pointer">
|
if (!userIsLearning) {
|
||||||
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-white text-4xl z-10">
|
await update.mutateAsync({
|
||||||
点击进入学习
|
where: { id: user?.id },
|
||||||
</div>
|
data: {
|
||||||
|
learningPosts: {
|
||||||
|
connect: {
|
||||||
|
id: course.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className="w-full h-full absolute top-0 z-10 bg-[rgba(0,0,0,0.3)] transition-all duration-300 ease-in-out hover:bg-[rgba(0,0,0,0.7)] cursor-pointer group">
|
||||||
|
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-white text-4xl z-10 opacity-0 group-hover:opacity-100 transition-opacity duration-300">
|
||||||
|
点击进入学习
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="text-lg font-bold">{"课程简介:"}</div>
|
<div className="text-lg font-bold">{"课程简介:"}</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="flex gap-2 flex-wrap items-center float-start">
|
<div className="flex gap-2 flex-wrap items-center float-start">
|
||||||
{course?.subTitle && <div>{course?.subTitle}</div>}
|
{course?.subTitle && <div>{course?.subTitle}</div>}
|
||||||
{course.terms.map((term) => {
|
<TermInfo post={course}></TermInfo>
|
||||||
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>
|
</div>
|
||||||
<Paragraph
|
<Paragraph
|
||||||
|
|
|
@ -65,8 +65,8 @@ export const CourseDetailDisplayArea: React.FC = () => {
|
||||||
{!lectureIsLoading &&
|
{!lectureIsLoading &&
|
||||||
selectedLectureId &&
|
selectedLectureId &&
|
||||||
lecture?.meta?.type === LectureType.ARTICLE && (
|
lecture?.meta?.type === LectureType.ARTICLE && (
|
||||||
<div className="flex justify-center flex-col items-center gap-2 w-full my-2 px-4">
|
<div className="flex justify-center flex-col items-center gap-2 w-full my-2 ">
|
||||||
<div className="w-full bg-white shadow-md rounded-lg border border-gray-200 p-6 ">
|
<div className="w-full rounded-lg ">
|
||||||
<CollapsibleContent
|
<CollapsibleContent
|
||||||
content={lecture?.content || ""}
|
content={lecture?.content || ""}
|
||||||
maxHeight={500} // Optional, defaults to 150
|
maxHeight={500} // Optional, defaults to 150
|
||||||
|
|
|
@ -12,29 +12,34 @@ import dayjs from "dayjs";
|
||||||
import CourseOperationBtns from "./JoinLearingButton";
|
import CourseOperationBtns from "./JoinLearingButton";
|
||||||
|
|
||||||
export default function CourseDetailTitle() {
|
export default function CourseDetailTitle() {
|
||||||
const {
|
const { course } = useContext(CourseDetailContext);
|
||||||
course,
|
|
||||||
isLoading,
|
|
||||||
canEdit,
|
|
||||||
lecture,
|
|
||||||
lectureIsLoading,
|
|
||||||
selectedLectureId,
|
|
||||||
} = useContext(CourseDetailContext);
|
|
||||||
const navigate = useNavigate();
|
|
||||||
return (
|
return (
|
||||||
<div className="flex justify-center flex-col items-center gap-2 w-full my-2 px-6">
|
<div className="flex justify-center flex-col items-center gap-2 w-full my-2 px-6">
|
||||||
<div className="flex justify-start w-full text-2xl font-bold">
|
<div className="flex justify-start w-full text-2xl font-bold">
|
||||||
{course?.title}
|
{course?.title}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="text-gray-600 flex w-full justify-start items-center gap-5">
|
||||||
|
{course?.author?.showname && (
|
||||||
|
<div>
|
||||||
|
发布者:
|
||||||
|
{course?.author?.showname}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{course?.depts && course?.depts?.length > 0 && (
|
||||||
|
<div>
|
||||||
|
发布单位:
|
||||||
|
{course?.depts?.map((dept) => dept.name)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<div className="text-gray-600 flex w-full justify-start items-center gap-5">
|
<div className="text-gray-600 flex w-full justify-start items-center 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">
|
<div className="flex gap-1">
|
||||||
<ReloadOutlined spin></ReloadOutlined>
|
{"最后更新:"}
|
||||||
{"更新于:"}
|
|
||||||
{dayjs(course?.updatedAt).format("YYYY年M月D日")}
|
{dayjs(course?.updatedAt).format("YYYY年M月D日")}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-1">
|
<div className="flex gap-1">
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import { Card, Typography, Button } from "antd";
|
import { Card, Typography, Button, Empty } from "antd";
|
||||||
|
|
||||||
import { PostDto } from "@nice/common";
|
import { PostDto } from "@nice/common";
|
||||||
import DeptInfo from "@web/src/app/main/path/components/DeptInfo";
|
import DeptInfo from "@web/src/app/main/path/components/DeptInfo";
|
||||||
import TermInfo from "@web/src/app/main/path/components/TermInfo";
|
import TermInfo from "@web/src/app/main/path/components/TermInfo";
|
||||||
|
import { PictureOutlined } from "@ant-design/icons";
|
||||||
|
|
||||||
interface PostCardProps {
|
interface PostCardProps {
|
||||||
post?: PostDto;
|
post?: PostDto;
|
||||||
|
@ -20,18 +21,24 @@ export default function PostCard({ post, onClick }: PostCardProps) {
|
||||||
hoverable
|
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"
|
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={
|
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 group">
|
||||||
<div
|
{post?.meta?.thumbnail ? (
|
||||||
className="absolute inset-0 bg-cover bg-center transform transition-all duration-700 ease-out group-hover:scale-110"
|
<div
|
||||||
style={{
|
className="absolute inset-0 bg-cover bg-center transform transition-all duration-700 ease-out group-hover:scale-110"
|
||||||
backgroundImage: `url(${post?.meta?.thumbnail})`,
|
style={{
|
||||||
}}
|
backgroundImage: `url(${post?.meta?.thumbnail})`,
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="absolute inset-0 flex items-center justify-center bg-gradient-to-br from-primary-500 to-primary-700">
|
||||||
|
<PictureOutlined className="text-white text-6xl" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
}>
|
}>
|
||||||
<div className="px-4 ">
|
<div className="px-4 ">
|
||||||
<div className="overflow-hidden hover:overflow-auto">
|
<div className="overflow-hidden hover:overflow-auto">
|
||||||
<div className="flex gap-2 h-7 mb-4 whiteSpace-nowrap">
|
<div className="flex gap-2 h-7 whiteSpace-nowrap">
|
||||||
<TermInfo post={post}></TermInfo>
|
<TermInfo post={post}></TermInfo>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -40,20 +40,23 @@ export type PostDto = Post & {
|
||||||
delete: boolean;
|
delete: boolean;
|
||||||
// edit: boolean;
|
// edit: boolean;
|
||||||
};
|
};
|
||||||
|
meta?: PostMeta;
|
||||||
watchableDepts: Department[];
|
watchableDepts: Department[];
|
||||||
watchableStaffs: Staff[];
|
watchableStaffs: Staff[];
|
||||||
terms: TermDto[];
|
terms: TermDto[];
|
||||||
depts: DepartmentDto[];
|
depts: DepartmentDto[];
|
||||||
meta?: {
|
|
||||||
thumbnail?: string;
|
|
||||||
views?: number;
|
|
||||||
};
|
|
||||||
studentIds?: string[];
|
studentIds?: string[];
|
||||||
};
|
};
|
||||||
|
export type PostMeta = {
|
||||||
export type LectureMeta = {
|
thumbnail?: string;
|
||||||
type?: string;
|
|
||||||
views?: number;
|
views?: number;
|
||||||
|
likes?: number;
|
||||||
|
hates?: number;
|
||||||
|
};
|
||||||
|
export type LectureMeta = PostMeta & {
|
||||||
|
type?: string;
|
||||||
|
|
||||||
videoUrl?: string;
|
videoUrl?: string;
|
||||||
videoThumbnail?: string;
|
videoThumbnail?: string;
|
||||||
videoIds?: string[];
|
videoIds?: string[];
|
||||||
|
@ -65,7 +68,7 @@ export type Lecture = Post & {
|
||||||
meta?: LectureMeta;
|
meta?: LectureMeta;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SectionMeta = {
|
export type SectionMeta = PostMeta & {
|
||||||
objectives?: string[];
|
objectives?: string[];
|
||||||
};
|
};
|
||||||
export type Section = Post & {
|
export type Section = Post & {
|
||||||
|
@ -74,13 +77,8 @@ export type Section = Post & {
|
||||||
export type SectionDto = Section & {
|
export type SectionDto = Section & {
|
||||||
lectures: Lecture[];
|
lectures: Lecture[];
|
||||||
};
|
};
|
||||||
export type CourseMeta = {
|
export type CourseMeta = PostMeta & {
|
||||||
thumbnail?: string;
|
|
||||||
|
|
||||||
objectives?: string[];
|
objectives?: string[];
|
||||||
views?: number;
|
|
||||||
likes?: number;
|
|
||||||
hates?: number;
|
|
||||||
};
|
};
|
||||||
export type Course = PostDto & {
|
export type Course = PostDto & {
|
||||||
meta?: CourseMeta;
|
meta?: CourseMeta;
|
||||||
|
@ -93,3 +91,45 @@ export type CourseDto = Course & {
|
||||||
depts: Department[];
|
depts: Department[];
|
||||||
studentIds: string[];
|
studentIds: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type Summary = {
|
||||||
|
id: string;
|
||||||
|
text: string;
|
||||||
|
parent: string;
|
||||||
|
start: number;
|
||||||
|
end: number;
|
||||||
|
};
|
||||||
|
export type NodeObj = {
|
||||||
|
topic: string;
|
||||||
|
id: string;
|
||||||
|
style?: {
|
||||||
|
fontSize?: string;
|
||||||
|
color?: string;
|
||||||
|
background?: string;
|
||||||
|
fontWeight?: string;
|
||||||
|
};
|
||||||
|
children?: NodeObj[];
|
||||||
|
};
|
||||||
|
export type Arrow = {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
from: string;
|
||||||
|
to: string;
|
||||||
|
delta1: {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
};
|
||||||
|
delta2: {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
export type PathMeta = PostMeta & {
|
||||||
|
nodeData: NodeObj;
|
||||||
|
arrows?: Arrow[];
|
||||||
|
summaries?: Summary[];
|
||||||
|
direction?: number;
|
||||||
|
};
|
||||||
|
export type PathDto = PostDto & {
|
||||||
|
meta: PathMeta;
|
||||||
|
};
|
||||||
|
|
|
@ -6,6 +6,8 @@ export const postDetailSelect: Prisma.PostSelect = {
|
||||||
title: true,
|
title: true,
|
||||||
content: true,
|
content: true,
|
||||||
resources: true,
|
resources: true,
|
||||||
|
parent: true,
|
||||||
|
parentId: true,
|
||||||
// watchableDepts: true,
|
// watchableDepts: true,
|
||||||
// watchableStaffs: true,
|
// watchableStaffs: true,
|
||||||
updatedAt: true,
|
updatedAt: true,
|
||||||
|
@ -18,9 +20,9 @@ export const postDetailSelect: Prisma.PostSelect = {
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
slug: true,
|
slug: true,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
depts: true,
|
depts: true,
|
||||||
author: {
|
author: {
|
||||||
|
@ -42,12 +44,14 @@ export const postDetailSelect: Prisma.PostSelect = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
meta: true
|
meta: true,
|
||||||
};
|
};
|
||||||
export const postUnDetailSelect: Prisma.PostSelect = {
|
export const postUnDetailSelect: Prisma.PostSelect = {
|
||||||
id: true,
|
id: true,
|
||||||
type: true,
|
type: true,
|
||||||
title: true,
|
title: true,
|
||||||
|
parent: true,
|
||||||
|
parentId: true,
|
||||||
content: true,
|
content: true,
|
||||||
resources: true,
|
resources: true,
|
||||||
updatedAt: true,
|
updatedAt: true,
|
||||||
|
@ -85,6 +89,8 @@ export const courseDetailSelect: Prisma.PostSelect = {
|
||||||
title: true,
|
title: true,
|
||||||
subTitle: true,
|
subTitle: true,
|
||||||
type: true,
|
type: true,
|
||||||
|
author: true,
|
||||||
|
authorId: true,
|
||||||
content: true,
|
content: true,
|
||||||
depts: true,
|
depts: true,
|
||||||
// isFeatured: true,
|
// isFeatured: true,
|
||||||
|
|
Loading…
Reference in New Issue