diff --git a/apps/server/src/models/post/post.service.ts b/apps/server/src/models/post/post.service.ts index 8f90a85..947ecfc 100755 --- a/apps/server/src/models/post/post.service.ts +++ b/apps/server/src/models/post/post.service.ts @@ -166,19 +166,7 @@ export class PostService extends BaseTreeService { ); return transDto; } - // async findMany(args: Prisma.PostFindManyArgs, staff?: UserProfile) { - // if (!args.where) args.where = {}; - // args.where.OR = await this.preFilter(args.where.OR, staff); - // return this.wrapResult(super.findMany(args), async (result) => { - // await Promise.all( - // result.map(async (item) => { - // await setPostRelation({ data: item, staff }); - // await this.setPerms(item, staff); - // }), - // ); - // return { ...result }; - // }); - // } + async findManyWithCursor(args: Prisma.PostFindManyArgs, staff?: UserProfile) { if (!args.where) args.where = {}; args.where.OR = await this.preFilter(args.where.OR, staff); @@ -255,6 +243,7 @@ export class PostService extends BaseTreeService { // 批量执行更新 return updates.length > 0 ? await db.$transaction(updates) : []; } + protected async setPerms(data: Post, staff?: UserProfile) { if (!staff) return; const perms: ResPerm = { diff --git a/apps/server/src/models/post/utils.ts b/apps/server/src/models/post/utils.ts index 0e1f835..57b0fd0 100755 --- a/apps/server/src/models/post/utils.ts +++ b/apps/server/src/models/post/utils.ts @@ -168,6 +168,21 @@ export async function setCourseInfo({ data }: { data: Post }) { (lecture) => lecture.parentId === section.id, ) as any as Lecture[]; }); - Object.assign(data, { sections, lectureCount }); + + const students = await db.staff.findMany({ + where: { + learningPosts: { + some: { + id: data.id, + }, + }, + }, + select: { + id: true, + }, + }); + + const studentIds = (students || []).map((student) => student?.id); + Object.assign(data, { sections, lectureCount, studentIds }); } } diff --git a/apps/web/src/app/main/courses/components/CourseCard.tsx b/apps/web/src/app/main/courses/components/CourseCard.tsx index 9f64cbe..597caf0 100755 --- a/apps/web/src/app/main/courses/components/CourseCard.tsx +++ b/apps/web/src/app/main/courses/components/CourseCard.tsx @@ -1,5 +1,6 @@ import { Card, Tag, Typography, Button } from "antd"; import { + BookOutlined, EyeOutlined, PlayCircleOutlined, TeamOutlined, @@ -73,10 +74,10 @@ export default function CourseCard({ course, edit = false }: CourseCardProps) { -
+
- + {course?.depts?.length > 1 ? `${course.depts[0].name}等` : course?.depts?.[0]?.name} @@ -84,10 +85,16 @@ export default function CourseCard({ course, edit = false }: CourseCardProps) { {/* {course?.depts?.map((dept)=>{return dept.name})} */}
+
+
- + {`观看次数 ${course?.meta?.views || 0}`} + + + {`学习人数 ${course?.studentIds?.length || 0}`} +
+ )} {canEdit && ( - <> - - + )} {isAuthenticated ? ( diff --git a/apps/web/src/components/models/course/detail/CourseDetailHeader/CourseDetailHeader_BACKUP.tsx b/apps/web/src/components/models/course/detail/CourseDetailHeader/CourseDetailHeader_BACKUP.tsx deleted file mode 100755 index 0fc9815..0000000 --- a/apps/web/src/components/models/course/detail/CourseDetailHeader/CourseDetailHeader_BACKUP.tsx +++ /dev/null @@ -1,77 +0,0 @@ -// // components/Header.tsx -// import { motion, useScroll, useTransform } from "framer-motion"; -// import { useContext, useEffect, useState } from "react"; -// import { CourseDetailContext } from "../CourseDetailContext"; -// import { Avatar, Button, Dropdown } from "antd"; -// import { UserOutlined } from "@ant-design/icons"; -// import { UserMenu } from "@web/src/app/main/layout/UserMenu"; -// import { useAuth } from "@web/src/providers/auth-provider"; - -// export const CourseDetailHeader = () => { -// const { scrollY } = useScroll(); -// const { user, isAuthenticated } = useAuth(); -// const [lastScrollY, setLastScrollY] = useState(0); -// const { course, isHeaderVisible, setIsHeaderVisible, lecture } = -// useContext(CourseDetailContext); -// useEffect(() => { -// const updateHeader = () => { -// const current = scrollY.get(); -// const direction = current > lastScrollY ? "down" : "up"; - -// if (direction === "down" && current > 100) { -// setIsHeaderVisible(false); -// } else if (direction === "up") { -// setIsHeaderVisible(true); -// } - -// setLastScrollY(current); -// }; - -// // 使用 requestAnimationFrame 来优化性能 -// const unsubscribe = scrollY.on("change", () => { -// requestAnimationFrame(updateHeader); -// }); - -// return () => { -// unsubscribe(); -// }; -// }, [lastScrollY, scrollY, setIsHeaderVisible]); - -// return ( -// -//
-//
-//

{course?.title}

-//
- -// {isAuthenticated ? ( -// } -// trigger={["click"]} -// placement="bottomRight"> -// -// {(user?.showname || -// user?.username || -// "")[0]?.toUpperCase()} -// -// -// ) : ( -// -// )} -//
-//
-// ); -// }; - -// export default CourseDetailHeader; diff --git a/apps/web/src/components/models/course/editor/context/CourseEditorContext.tsx b/apps/web/src/components/models/course/editor/context/CourseEditorContext.tsx index 0fd8040..88571a3 100755 --- a/apps/web/src/components/models/course/editor/context/CourseEditorContext.tsx +++ b/apps/web/src/components/models/course/editor/context/CourseEditorContext.tsx @@ -99,10 +99,10 @@ export function CourseFormProvider({ }), }, terms: { - connect: termIds.map((id) => ({ id })), // 转换成 connect 格式 + set: termIds.map((id) => ({ id })), // 转换成 connect 格式 }, depts: { - connect: deptIds.map((id) => ({ id })), + set: deptIds.map((id) => ({ id })), }, }; // 删除原始的 taxonomy 字段 diff --git a/apps/web/src/components/models/course/editor/form/CourseBasicForm.tsx b/apps/web/src/components/models/course/editor/form/CourseBasicForm.tsx index cf683f1..4e41477 100755 --- a/apps/web/src/components/models/course/editor/form/CourseBasicForm.tsx +++ b/apps/web/src/components/models/course/editor/form/CourseBasicForm.tsx @@ -32,7 +32,7 @@ export function CourseBasicForm() { + rules={[{ max: 20, message: "副标题最多20个字符" }]}> diff --git a/packages/client/src/api/hooks/useStaff.ts b/packages/client/src/api/hooks/useStaff.ts index fbdf571..aed3aa7 100755 --- a/packages/client/src/api/hooks/useStaff.ts +++ b/packages/client/src/api/hooks/useStaff.ts @@ -5,39 +5,48 @@ import { ObjectType, Staff } from "@nice/common"; import { findQueryData } from "../utils"; import { CrudOperation, emitDataChange } from "../../event"; export function useStaff() { - const queryClient = useQueryClient(); - const queryKey = getQueryKey(api.staff); + const queryClient = useQueryClient(); + const queryKey = getQueryKey(api.staff); - const create = api.staff.create.useMutation({ - onSuccess: (result) => { - queryClient.invalidateQueries({ queryKey }); - emitDataChange(ObjectType.STAFF, result as any, CrudOperation.CREATED) - }, - }); - const updateUserDomain = api.staff.updateUserDomain.useMutation({ - onSuccess: async (result) => { - queryClient.invalidateQueries({ queryKey }); - }, - }); - const update = api.staff.update.useMutation({ - onSuccess: (result) => { - queryClient.invalidateQueries({ queryKey }); - emitDataChange(ObjectType.STAFF, result as any, CrudOperation.UPDATED) - }, - }); + const create = api.staff.create.useMutation({ + onSuccess: (result) => { + queryClient.invalidateQueries({ queryKey }); + emitDataChange( + ObjectType.STAFF, + result as any, + CrudOperation.CREATED + ); + }, + }); + const updateUserDomain = api.staff.updateUserDomain.useMutation({ + onSuccess: async (result) => { + queryClient.invalidateQueries({ queryKey }); + }, + }); + const update = api.staff.update.useMutation({ + onSuccess: (result) => { + queryClient.invalidateQueries({ queryKey }); + queryClient.invalidateQueries({ queryKey: getQueryKey(api.post) }); + emitDataChange( + ObjectType.STAFF, + result as any, + CrudOperation.UPDATED + ); + }, + }); const softDeleteByIds = api.staff.softDeleteByIds.useMutation({ onSuccess: (result, variables) => { queryClient.invalidateQueries({ queryKey }); }, }); - const getStaff = (key: string) => { - return findQueryData(queryClient, api.staff, key); - }; - return { - create, - update, - softDeleteByIds, - getStaff, - updateUserDomain - }; + const getStaff = (key: string) => { + return findQueryData(queryClient, api.staff, key); + }; + return { + create, + update, + softDeleteByIds, + getStaff, + updateUserDomain, + }; } diff --git a/packages/common/prisma/schema.prisma b/packages/common/prisma/schema.prisma index 75e8be6..db764d2 100755 --- a/packages/common/prisma/schema.prisma +++ b/packages/common/prisma/schema.prisma @@ -93,7 +93,7 @@ model Staff { posts Post[] - learningPost Post[] @relation("post_student") + learningPosts Post[] @relation("post_student") sentMsgs Message[] @relation("message_sender") receivedMsgs Message[] @relation("message_receiver") registerToken String? diff --git a/packages/common/src/models/post.ts b/packages/common/src/models/post.ts index a10fcf5..63a38b0 100755 --- a/packages/common/src/models/post.ts +++ b/packages/common/src/models/post.ts @@ -83,4 +83,5 @@ export type CourseDto = Course & { terms: TermDto[]; lectureCount?: number; depts: Department[]; + studentIds: string[]; };