-
+ {icon}
- 点击或拖拽文件到此区域进行上传
+ {description}
{multiple ? "支持单个或批量上传文件" : "仅支持上传单个文件"}
- {allowTypes && (
+ {/* {allowTypes && (
允许类型: {allowTypes.join(", ")}
- )}
+ )} */}
diff --git a/apps/web/src/components/models/course/editor/form/CourseContentForm/SortableLecture.tsx b/apps/web/src/components/models/course/editor/form/CourseContentForm/SortableLecture.tsx
index 2d9bfd2..368f1e7 100755
--- a/apps/web/src/components/models/course/editor/form/CourseContentForm/SortableLecture.tsx
+++ b/apps/web/src/components/models/course/editor/form/CourseContentForm/SortableLecture.tsx
@@ -3,6 +3,8 @@ import {
CaretRightOutlined,
SaveOutlined,
CaretDownOutlined,
+ PaperClipOutlined,
+ PlaySquareOutlined,
} from "@ant-design/icons";
import { Form, Button, Input, Select, Space } from "antd";
import React, { useState } from "react";
@@ -86,12 +88,12 @@ export const SortableLecture: React.FC = ({
resources:
[videoUrlId, ...fileIds].filter(Boolean)?.length > 0
? {
- connect: [videoUrlId, ...fileIds]
- .filter(Boolean)
- .map((fileId) => ({
- fileId,
- })),
- }
+ connect: [videoUrlId, ...fileIds]
+ .filter(Boolean)
+ .map((fileId) => ({
+ fileId,
+ })),
+ }
: undefined,
content: values?.content,
},
@@ -115,12 +117,12 @@ export const SortableLecture: React.FC = ({
resources:
[videoUrlId, ...fileIds].filter(Boolean)?.length > 0
? {
- set: [videoUrlId, ...fileIds]
- .filter(Boolean)
- .map((fileId) => ({
- fileId,
- })),
- }
+ set: [videoUrlId, ...fileIds]
+ .filter(Boolean)
+ .map((fileId) => ({
+ fileId,
+ })),
+ }
: undefined,
content: values?.content,
},
@@ -175,30 +177,42 @@ export const SortableLecture: React.FC = ({
/>
-
+
{lectureType === LectureType.VIDEO ? (
<>
-
-
+ {/* 添加视频 */}
+
+ }
+ description="点击或拖拽视频到此区域进行上传"
+ />
+
+
+
+ {/* 添加附件 */}
+
+ }
+ description="点击或拖拽附件到此区域进行上传"
/>
-
-
-
-
+
+
>
-
+
) : (
= ({
]}>
From 6ff7105fd774ca98b439723c2c47b5253f94717c Mon Sep 17 00:00:00 2001
From: linfeng <2819853134@qq.com>
Date: Fri, 7 Mar 2025 17:44:41 +0800
Subject: [PATCH 3/7] lin
---
apps/web/src/components/common/editor/NodeMenu.tsx | 9 ++++++---
.../models/course/detail/CourseDetailTitle.tsx | 8 +-------
.../src/components/models/post/PostSelect/PostSelect.tsx | 2 ++
apps/web/src/routes/index.tsx | 2 +-
4 files changed, 10 insertions(+), 11 deletions(-)
diff --git a/apps/web/src/components/common/editor/NodeMenu.tsx b/apps/web/src/components/common/editor/NodeMenu.tsx
index e833a79..2519c7e 100755
--- a/apps/web/src/components/common/editor/NodeMenu.tsx
+++ b/apps/web/src/components/common/editor/NodeMenu.tsx
@@ -12,7 +12,7 @@ import PostSelect from "../../models/post/PostSelect/PostSelect";
import { Lecture, PostType } from "@nice/common";
import { xmindColorPresets } from "./constant";
import { api } from "@nice/client";
-import { env } from "@web/src/env";
+import { useAuth } from "@web/src/providers/auth-provider";
interface NodeMenuProps {
mind: MindElixirInstance;
@@ -20,12 +20,13 @@ interface NodeMenuProps {
//管理节点样式状态
const NodeMenu: React.FC
= ({ mind }) => {
+
const [isOpen, setIsOpen] = useState(false);
const [selectedFontColor, setSelectedFontColor] = useState("");
const [selectedBgColor, setSelectedBgColor] = useState("");
const [selectedSize, setSelectedSize] = useState("");
const [isBold, setIsBold] = useState(false);
-
+ const { user} = useAuth();
const [urlMode, setUrlMode] = useState<"URL" | "POSTURL">("POSTURL");
const [url, setUrl] = useState("");
const [postId, setPostId] = useState("");
@@ -238,13 +239,15 @@ const NodeMenu: React.FC = ({ mind }) => {
{urlMode === "POSTURL" ? (
{
- if (typeof value === "string") {
+ if (typeof value === "string" ) {
setPostId(value);
}
}}
params={{
where: {
type: PostType.LECTURE,
+ deletedAt: null,
+ authorId: user?.id,
},
}}
/>
diff --git a/apps/web/src/components/models/course/detail/CourseDetailTitle.tsx b/apps/web/src/components/models/course/detail/CourseDetailTitle.tsx
index 3d23341..ff1a30e 100755
--- a/apps/web/src/components/models/course/detail/CourseDetailTitle.tsx
+++ b/apps/web/src/components/models/course/detail/CourseDetailTitle.tsx
@@ -23,15 +23,9 @@ export default function CourseDetailTitle() {
{!selectedLectureId ? course?.title : lecture?.title}
- {course?.author?.showname && (
-
- 发布者:
- {course?.author?.showname}
-
- )}
{course?.depts && course?.depts?.length > 0 && (
- 发布单位:
+ 发布单位:
{course?.depts?.map((dept) => dept.name)}
)}
diff --git a/apps/web/src/components/models/post/PostSelect/PostSelect.tsx b/apps/web/src/components/models/post/PostSelect/PostSelect.tsx
index f97d148..63b01ee 100644
--- a/apps/web/src/components/models/post/PostSelect/PostSelect.tsx
+++ b/apps/web/src/components/models/post/PostSelect/PostSelect.tsx
@@ -13,6 +13,7 @@ export default function PostSelect({
placeholder = "请选择课时",
params = { where: {}, select: {} },
className,
+ createdById,
}: {
value?: string | string[];
onChange?: (value: string | string[]) => void;
@@ -22,6 +23,7 @@ export default function PostSelect({
select?: Prisma.PostSelect
;
};
className?: string;
+ createdById?: string;
}) {
const [searchValue, setSearch] = useState("");
const searchCondition: Prisma.PostWhereInput = useMemo(() => {
diff --git a/apps/web/src/routes/index.tsx b/apps/web/src/routes/index.tsx
index 97552d0..e48165d 100755
--- a/apps/web/src/routes/index.tsx
+++ b/apps/web/src/routes/index.tsx
@@ -128,7 +128,7 @@ export const routes: CustomRouteObject[] = [
path: "course",
children: [
{
- path: ":id?/editor",
+ path: ":id?/editor",
element: (
From b8e82aeaaf2f8085cf321010eefbfd137f5aafaf Mon Sep 17 00:00:00 2001
From: Rao <1227431568@qq.com>
Date: Fri, 7 Mar 2025 18:01:07 +0800
Subject: [PATCH 4/7] rht
---
.../components/common/editor/MindEditor.tsx | 24 +++++++++++--------
1 file changed, 14 insertions(+), 10 deletions(-)
diff --git a/apps/web/src/components/common/editor/MindEditor.tsx b/apps/web/src/components/common/editor/MindEditor.tsx
index 065917e..0b1dff6 100755
--- a/apps/web/src/components/common/editor/MindEditor.tsx
+++ b/apps/web/src/components/common/editor/MindEditor.tsx
@@ -62,16 +62,16 @@ export default function MindEditor({ id }: { id?: string }) {
});
const { handleFileUpload } = useTusUpload();
const [form] = Form.useForm();
- const CustomLinkIconPlugin = (mind) => {
- mind.bus.addListener("operation", async () => {
- const hyperLinkElement =
- await document.querySelectorAll(".hyper-link");
- console.log("hyperLinkElement", hyperLinkElement);
- hyperLinkElement.forEach((item) => {
- const hyperLinkDom = createRoot(item);
- hyperLinkDom.render();
- });
+ const handleIcon = () => {
+ const hyperLinkElement =document.querySelectorAll(".hyper-link");
+ console.log("hyperLinkElement", hyperLinkElement);
+ hyperLinkElement.forEach((item) => {
+ const hyperLinkDom = createRoot(item);
+ hyperLinkDom.render();
});
+ }
+ const CustomLinkIconPlugin = (mind) => {
+ mind.bus.addListener("operation", handleIcon)
};
useEffect(() => {
if (post?.id && id) {
@@ -133,7 +133,11 @@ export default function MindEditor({ id }: { id?: string }) {
containerRef.current.hidden = true;
//挂载实例
setInstance(mind);
+
}, [canEdit]);
+ useEffect(() => {
+ handleIcon()
+ });
useEffect(() => {
if ((!id || post) && instance) {
containerRef.current.hidden = false;
@@ -204,7 +208,7 @@ export default function MindEditor({ id }: { id?: string }) {
}
console.log(result);
},
- (error) => {},
+ (error) => { },
`mind-thumb-${new Date().toString()}`
);
};
From b9cd68b82aac4812700ad542d31212927d7ae266 Mon Sep 17 00:00:00 2001
From: linfeng <2819853134@qq.com>
Date: Fri, 7 Mar 2025 18:44:39 +0800
Subject: [PATCH 5/7] lin
---
apps/web/src/app/main/path/editor/page.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/web/src/app/main/path/editor/page.tsx b/apps/web/src/app/main/path/editor/page.tsx
index 2c0b8b0..16c106e 100755
--- a/apps/web/src/app/main/path/editor/page.tsx
+++ b/apps/web/src/app/main/path/editor/page.tsx
@@ -6,7 +6,7 @@ export default function PathEditorPage() {
const { id } = useParams();
return (
- ;
+
);
}
From 632bfe26aabec72c576bf93e32da68bb535dc30a Mon Sep 17 00:00:00 2001
From: Rao <1227431568@qq.com>
Date: Tue, 11 Mar 2025 18:55:59 +0800
Subject: [PATCH 6/7] rht
---
apps/web/package.json | 1 +
.../course/detail/CourseDetailDisplayArea.tsx | 13 ++++++--
pnpm-lock.yaml | 32 +++++++++++++++++++
3 files changed, 44 insertions(+), 2 deletions(-)
diff --git a/apps/web/package.json b/apps/web/package.json
index b484901..af89247 100755
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -64,6 +64,7 @@
"react-dom": "18.2.0",
"react-hook-form": "^7.54.2",
"react-hot-toast": "^2.4.1",
+ "react-player": "^2.16.0",
"react-resizable": "^3.0.5",
"react-router-dom": "^6.24.1",
"superjson": "^2.2.1",
diff --git a/apps/web/src/components/models/course/detail/CourseDetailDisplayArea.tsx b/apps/web/src/components/models/course/detail/CourseDetailDisplayArea.tsx
index f46e22f..fc9ed18 100755
--- a/apps/web/src/components/models/course/detail/CourseDetailDisplayArea.tsx
+++ b/apps/web/src/components/models/course/detail/CourseDetailDisplayArea.tsx
@@ -10,7 +10,7 @@ import { Skeleton } from "antd";
import ResourcesShower from "@web/src/components/common/uploader/ResourceShower";
import { useNavigate } from "react-router-dom";
import CourseDetailTitle from "./CourseDetailTitle";
-
+import ReactPlayer from "react-player";
export const CourseDetailDisplayArea: React.FC = () => {
// 创建滚动动画效果
const {
@@ -41,7 +41,16 @@ export const CourseDetailDisplayArea: React.FC = () => {
}}
className="w-full bg-black rounded-lg ">
-
+ {
+ console.log(error);
+ }}
+ />
+ {/* */}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0efc82c..c92a770 100755
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -386,6 +386,9 @@ importers:
react-hot-toast:
specifier: ^2.4.1
version: 2.5.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
+ react-player:
+ specifier: ^2.16.0
+ version: 2.16.0(react@18.2.0)
react-resizable:
specifier: ^3.0.5
version: 3.0.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
@@ -5603,6 +5606,9 @@ packages:
enquirer:
optional: true
+ load-script@1.0.0:
+ resolution: {integrity: sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA==}
+
load-tsconfig@0.2.5:
resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
@@ -5818,6 +5824,9 @@ packages:
resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==}
engines: {node: '>= 4.0.0'}
+ memoize-one@5.2.1:
+ resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==}
+
meow@8.1.2:
resolution: {integrity: sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==}
engines: {node: '>=10'}
@@ -6645,6 +6654,9 @@ packages:
react: '>= 16.3.0'
react-dom: '>= 16.3.0'
+ react-fast-compare@3.2.2:
+ resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==}
+
react-hook-form@7.54.2:
resolution: {integrity: sha512-eHpAUgUjWbZocoQYUHposymRb4ZP6d0uwUnooL2uOybA9/3tPUvoAKqEWK1WaSiTxxOfTpffNZP7QwlnM3/gEg==}
engines: {node: '>=18.0.0'}
@@ -6664,6 +6676,11 @@ packages:
react-is@18.3.1:
resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
+ react-player@2.16.0:
+ resolution: {integrity: sha512-mAIPHfioD7yxO0GNYVFD1303QFtI3lyyQZLY229UEAp/a10cSW+hPcakg0Keq8uWJxT2OiT/4Gt+Lc9bD6bJmQ==}
+ peerDependencies:
+ react: '>=16.6.0'
+
react-refresh@0.14.2:
resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==}
engines: {node: '>=0.10.0'}
@@ -13723,6 +13740,8 @@ snapshots:
rfdc: 1.4.1
wrap-ansi: 8.1.0
+ load-script@1.0.0: {}
+
load-tsconfig@0.2.5: {}
loader-runner@4.3.0: {}
@@ -13899,6 +13918,8 @@ snapshots:
dependencies:
fs-monkey: 1.0.6
+ memoize-one@5.2.1: {}
+
meow@8.1.2:
dependencies:
'@types/minimist': 1.2.5
@@ -14774,6 +14795,8 @@ snapshots:
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
+ react-fast-compare@3.2.2: {}
+
react-hook-form@7.54.2(react@18.2.0):
dependencies:
react: 18.2.0
@@ -14789,6 +14812,15 @@ snapshots:
react-is@18.3.1: {}
+ react-player@2.16.0(react@18.2.0):
+ dependencies:
+ deepmerge: 4.3.1
+ load-script: 1.0.0
+ memoize-one: 5.2.1
+ prop-types: 15.8.1
+ react: 18.2.0
+ react-fast-compare: 3.2.2
+
react-refresh@0.14.2: {}
react-resizable@3.0.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
From 1fc1aa368c1a7f2823bf8cff3fbd8e7187d9692b Mon Sep 17 00:00:00 2001
From: Rao <1227431568@qq.com>
Date: Thu, 27 Mar 2025 13:26:13 +0800
Subject: [PATCH 7/7] rht
---
Dockerfile | 2 +-
apps/server/src/models/post/post.router.ts | 9 ++
apps/server/src/models/post/post.service.ts | 56 +++++-----
.../components/common/editor/MindEditor.tsx | 49 +++++++--
.../course/detail/CourseDetailDisplayArea.tsx | 6 +-
.../CourseOperationBtns.tsx | 103 ++++--------------
.../editor/context/CourseEditorContext.tsx | 20 +++-
.../CourseContentForm/CourseContentForm.tsx | 13 ++-
.../editor/layout/CourseEditorHeader.tsx | 55 ++++++++--
.../models/course/list/PostList.tsx | 3 +-
apps/web/web-dist.zip | Bin 0 -> 1941116 bytes
config/nginx/entrypoint.sh | 2 +-
packages/common/src/models/select.ts | 1 +
13 files changed, 175 insertions(+), 144 deletions(-)
create mode 100644 apps/web/web-dist.zip
diff --git a/Dockerfile b/Dockerfile
index 3d15a41..ff297ad 100755
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,5 @@
# 基础镜像
-FROM node:18.17-alpine as base
+FROM node:18-alpine as base
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
echo "https://mirrors.aliyun.com/alpine/v3.18/community" >> /etc/apk/repositories
diff --git a/apps/server/src/models/post/post.router.ts b/apps/server/src/models/post/post.router.ts
index bcde094..9ee4f9b 100755
--- a/apps/server/src/models/post/post.router.ts
+++ b/apps/server/src/models/post/post.router.ts
@@ -125,5 +125,14 @@ export class PostRouter {
const { staff } = ctx;
return await this.postService.updateOrderByIds(input.ids);
}),
+ softDeletePostDescendant:this.trpc.protectProcedure
+ .input(
+ z.object({
+ ancestorId:z.string()
+ })
+ )
+ .mutation(async ({ input })=>{
+ return await this.postService.softDeletePostDescendant(input)
+ })
});
}
diff --git a/apps/server/src/models/post/post.service.ts b/apps/server/src/models/post/post.service.ts
index 0f3e8cd..5d4db4c 100755
--- a/apps/server/src/models/post/post.service.ts
+++ b/apps/server/src/models/post/post.service.ts
@@ -101,6 +101,7 @@ export class PostService extends BaseTreeService
{
},
params: { staff?: UserProfile; tx?: Prisma.TransactionClient },
) {
+
const { courseDetail } = args;
// If no transaction is provided, create a new one
if (!params.tx) {
@@ -295,40 +296,33 @@ export class PostService extends BaseTreeService {
staff?.id && {
authorId: staff.id,
},
- // staff?.id && {
- // watchableStaffs: {
- // some: {
- // id: staff.id,
- // },
- // },
- // },
- // deptId && {
- // watchableDepts: {
- // some: {
- // id: {
- // in: parentDeptIds,
- // },
- // },
- // },
- // },
-
- // {
- // AND: [
- // {
- // watchableStaffs: {
- // none: {}, // 匹配 watchableStaffs 为空
- // },
- // },
- // {
- // watchableDepts: {
- // none: {}, // 匹配 watchableDepts 为空
- // },
- // },
- // ],
- // },
].filter(Boolean);
if (orCondition?.length > 0) return orCondition;
return undefined;
}
+ async softDeletePostDescendant(args:{ancestorId?:string}){
+ const { ancestorId } = args
+ const descendantIds = []
+ await db.postAncestry.findMany({
+ where:{
+ ancestorId,
+ },
+ select:{
+ descendantId:true
+ }
+ }).then(res=>{
+ res.forEach(item=>{
+ descendantIds.push(item.descendantId)
+ })
+ })
+ console.log(descendantIds)
+ const result = super.softDeleteByIds([...descendantIds,ancestorId])
+ EventBus.emit('dataChanged', {
+ type: ObjectType.POST,
+ operation: CrudOperation.DELETED,
+ data: result,
+ });
+ return result
+ }
}
diff --git a/apps/web/src/components/common/editor/MindEditor.tsx b/apps/web/src/components/common/editor/MindEditor.tsx
index 0b1dff6..0aba7cf 100755
--- a/apps/web/src/components/common/editor/MindEditor.tsx
+++ b/apps/web/src/components/common/editor/MindEditor.tsx
@@ -25,6 +25,8 @@ import JoinButton from "../../models/course/detail/CourseOperationBtns/JoinButto
import { CourseDetailContext } from "../../models/course/detail/PostDetailContext";
import ReactDOM from "react-dom";
import { createRoot } from "react-dom/client";
+import { useQueryClient } from "@tanstack/react-query";
+import { getQueryKey } from "@trpc/react-query";
export default function MindEditor({ id }: { id?: string }) {
const containerRef = useRef(null);
const {
@@ -36,6 +38,7 @@ export default function MindEditor({ id }: { id?: string }) {
const [instance, setInstance] = useState(null);
const { isAuthenticated, user, hasSomePermissions } = useAuth();
const { read } = useVisitor();
+ const queryClient = useQueryClient();
// const { data: post, isLoading }: { data: PathDto; isLoading: boolean } =
// api.post.findFirst.useQuery(
// {
@@ -46,7 +49,11 @@ export default function MindEditor({ id }: { id?: string }) {
// },
// { enabled: Boolean(id) }
// );
-
+ const softDeletePostDescendant = api.post.softDeletePostDescendant.useMutation({
+ onSuccess:()=>{
+ queryClient.invalidateQueries({ queryKey: getQueryKey(api.post) });
+ }
+ })
const canEdit: boolean = useMemo(() => {
const isAuth = isAuthenticated && user?.id === post?.author?.id;
return (
@@ -63,7 +70,7 @@ export default function MindEditor({ id }: { id?: string }) {
const { handleFileUpload } = useTusUpload();
const [form] = Form.useForm();
const handleIcon = () => {
- const hyperLinkElement =document.querySelectorAll(".hyper-link");
+ const hyperLinkElement = document.querySelectorAll(".hyper-link");
console.log("hyperLinkElement", hyperLinkElement);
hyperLinkElement.forEach((item) => {
const hyperLinkDom = createRoot(item);
@@ -133,7 +140,7 @@ export default function MindEditor({ id }: { id?: string }) {
containerRef.current.hidden = true;
//挂载实例
setInstance(mind);
-
+
}, [canEdit]);
useEffect(() => {
handleIcon()
@@ -212,6 +219,12 @@ export default function MindEditor({ id }: { id?: string }) {
`mind-thumb-${new Date().toString()}`
);
};
+ const handleDelete = async () => {
+ await softDeletePostDescendant.mutateAsync({
+ ancestorId: id,
+ });
+ navigate("/path");
+ }
useEffect(() => {
containerRef.current.style.height = `${Math.floor(window.innerHeight - 271)}px`;
}, []);
@@ -252,14 +265,28 @@ export default function MindEditor({ id }: { id?: string }) {
{canEdit && (
-
+ <>
+ {
+ id && (
+
+ )
+ }
+
+ >
)}