@@ -50,7 +51,7 @@ export function CourseBasicForm() {
},
]}
label={tax.name}
- name={tax.name}
+ name={tax.id}
key={index}>
diff --git a/apps/web/src/components/models/course/editor/form/CourseContentForm copy.tsx b/apps/web/src/components/models/course/editor/form/CourseContentForm copy.tsx
deleted file mode 100644
index 8ab2ad2..0000000
--- a/apps/web/src/components/models/course/editor/form/CourseContentForm copy.tsx
+++ /dev/null
@@ -1,604 +0,0 @@
-import {
- PlusOutlined,
- DragOutlined,
- DeleteOutlined,
- CaretRightOutlined,
- SaveOutlined,
-} from "@ant-design/icons";
-import {
- Form,
- Alert,
- Button,
- Input,
- Select,
- Space,
- Collapse,
- message,
-} from "antd";
-import React, { useCallback, useEffect, useState } from "react";
-import {
- DndContext,
- closestCenter,
- KeyboardSensor,
- PointerSensor,
- useSensor,
- useSensors,
- DragEndEvent,
-} from "@dnd-kit/core";
-import { api, emitDataChange } from "@nice/client";
-import {
- arrayMove,
- SortableContext,
- sortableKeyboardCoordinates,
- useSortable,
- verticalListSortingStrategy,
-} from "@dnd-kit/sortable";
-import { CSS } from "@dnd-kit/utilities";
-import QuillEditor from "../../../../common/editor/quill/QuillEditor";
-import { TusUploader } from "../../../../common/uploader/TusUploader";
-import { Lecture, LectureType, PostType } from "@nice/common";
-import { useCourseEditor } from "../context/CourseEditorContext";
-import { usePost } from "@nice/client";
-import toast from "react-hot-toast";
-interface SectionData {
- id: string;
- title: string;
- content?: string;
- courseId?: string;
-}
-
-interface LectureData {
- id: string;
- title: string;
- meta?: {
- type?: LectureType;
- fieldIds?: [];
- };
- content?: string;
- sectionId?: string;
-}
-const CourseContentFormHeader = () => (
-
- 通过组织清晰的章节和课时,帮助学员更好地学习。建议:
-
- - 将相关内容组织到章节中
- - 每个章节建议包含 3-7 个课时
- - 课时可以是视频、文章或测验
-
- >
- }
- className="mb-8"
- />
-);
-
-const CourseSectionEmpty = () => (
-
-
-
-
开始创建您的课程内容
-
点击下方按钮添加第一个章节
-
-
-);
-
-interface SortableSectionProps {
- courseId?: string;
- field: SectionData;
- remove: () => void;
- children: React.ReactNode;
-}
-
-const SortableSection: React.FC = ({
- field,
- remove,
- courseId,
- children,
-}) => {
- const {
- attributes,
- listeners,
- setNodeRef,
- transform,
- transition,
- isDragging,
- } = useSortable({ id: field?.id });
-
- const [form] = Form.useForm();
- const [editing, setEditing] = useState(field.id ? false : true);
- const [loading, setLoading] = useState(false);
- const { create, update } = usePost();
-
- const handleSave = async () => {
- if (!courseId) {
- toast.error("课程未创建,请先填写课程基本信息完成创建");
- return;
- }
- try {
- setLoading(true);
- const values = await form.validateFields();
- let result;
- try {
- if (!field?.id) {
- result = await create.mutateAsync({
- data: {
- title: values?.title,
- type: PostType.SECTION,
- parentId: courseId,
- },
- });
- } else {
- result = await update.mutateAsync({
- data: {
- title: values?.title,
- },
- });
- }
- } catch (err) {
- console.log(err);
- }
-
- field.id = result.id;
- setEditing(false);
- message.success("保存成功");
- } catch (error) {
- console.log(error);
- message.error("保存失败");
- } finally {
- setLoading(false);
- }
- };
-
- const style = {
- transform: CSS.Transform.toString(transform),
- transition,
- backgroundColor: isDragging ? "#f5f5f5" : undefined,
- };
-
- return (
-
-
-
-
-
-
-
- }
- type="primary">
- 保存
-
-
-
-
- ) : (
-
-
-
- {field.title || "未命名章节"}
-
-
-
-
-
-
- )
- }
- key={field.id || "new"}>
- {children}
-
-
-
- );
-};
-
-interface SortableLectureProps {
- field: LectureData;
- remove: () => void;
- sectionFieldKey: string;
-}
-
-const SortableLecture: React.FC = ({
- field,
- remove,
- sectionFieldKey,
-}) => {
- const {
- attributes,
- listeners,
- setNodeRef,
- transform,
- transition,
- isDragging,
- } = useSortable({ id: field?.id });
- const { create, update } = usePost();
- const [form] = Form.useForm();
- const [editing, setEditing] = useState(field?.id ? false : true);
- const [loading, setLoading] = useState(false);
- const lectureType =
- Form.useWatch(["meta", "type"], form) || LectureType.ARTICLE;
- const handleSave = async () => {
- try {
- setLoading(true);
- const values = await form.validateFields();
- let result;
- try {
- if (!field.id) {
- result = await create.mutateAsync({
- data: {
- parentId: sectionFieldKey,
- type: PostType.LECTURE,
- title: values?.title,
- meta: {
- type: values?.meta?.type,
- fileIds: values?.meta?.fileIds,
- },
- resources: {
- connect: (values?.meta?.fileIds || []).map(
- (fileId) => ({
- fileId,
- })
- ),
- },
- content: values?.content,
- },
- });
- } else {
- result = await update.mutateAsync({
- where: {
- id: field?.id,
- },
- data: {
- title: values?.title,
- meta: {
- type: values?.meta?.type,
- fieldIds: values?.meta?.fileIds,
- },
- resources: {
- connect: (values?.meta?.fileIds || []).map(
- (fileId) => ({
- fileId,
- })
- ),
- },
- content: values?.content,
- },
- });
- }
- } catch (err) {
- console.log(err);
- }
-
- field.id = result.id;
- setEditing(false);
- message.success("保存成功");
- } catch (error) {
- message.error("保存失败");
- } finally {
- setLoading(false);
- }
- };
-
- const style = {
- transform: CSS.Transform.toString(transform),
- transition,
- borderBottom: "1px solid #f0f0f0",
- backgroundColor: isDragging ? "#f5f5f5" : undefined,
- };
-
- return (
-
- {editing ? (
-
- ) : (
-
-
-
-
- {field?.title || "未命名课时"}
-
-
-
-
-
-
- )}
-
- );
-};
-
-interface LectureListProps {
- field: SectionData;
- sectionId: string;
-}
-
-const LectureList: React.FC = ({ field, sectionId }) => {
- const { softDeleteByIds } = usePost();
- const { data: lectures = [], isLoading } = (
- api.post.findMany as any
- ).useQuery(
- {
- where: {
- parentId: sectionId,
- type: PostType.LECTURE,
- deletedAt: null,
- },
- },
- {
- enabled: !!sectionId,
- }
- );
- useEffect(() => {
- if (lectures && !isLoading) {
- setItems(lectures);
- }
- }, [lectures, isLoading]);
- const [items, setItems] = useState(lectures);
-
- const sensors = useSensors(
- useSensor(PointerSensor),
- useSensor(KeyboardSensor, {
- coordinateGetter: sortableKeyboardCoordinates,
- })
- );
-
- const handleDragEnd = (event: DragEndEvent) => {
- const { active, over } = event;
- if (!over || active.id === over.id) return;
-
- setItems((items) => {
- const oldIndex = items.findIndex((item) => item.id === active.id);
- const newIndex = items.findIndex((item) => item.id === over.id);
- return arrayMove(items, oldIndex, newIndex);
- });
- };
-
- return (
-
-
{
- console.log(lectures);
- }}>
- 123
-
-
-
- {items.map((lecture) => (
- {
- if (lecture?.id) {
- await softDeleteByIds.mutateAsync({
- ids: [lecture.id],
- });
- }
- setItems(lectures);
- }}
- sectionFieldKey={sectionId}
- />
- ))}
-
-
-
}
- className="mt-4"
- onClick={() => {
- setItems([
- ...items.filter((item) => !!item.id),
- {
- id: null,
- title: "",
- meta: {
- type: LectureType.ARTICLE,
- },
- },
- ]);
- }}>
- 添加课时
-
-
- );
-};
-
-const CourseContentForm: React.FC = () => {
- const { editId } = useCourseEditor();
- const sensors = useSensors(
- useSensor(PointerSensor),
- useSensor(KeyboardSensor, {
- coordinateGetter: sortableKeyboardCoordinates,
- })
- );
- const { softDeleteByIds } = usePost();
- const { data: sections = [], isLoading } = api.post.findMany.useQuery(
- {
- where: {
- parentId: editId,
- type: PostType.SECTION,
- deletedAt: null,
- },
- },
- {
- enabled: !!editId,
- }
- );
-
- const [items, setItems] = useState(sections);
- useEffect(() => {
- if (sections && !isLoading) {
- setItems(sections);
- }
- }, [sections]);
- const handleDragEnd = (event: DragEndEvent) => {
- const { active, over } = event;
- if (!over || active.id === over.id) return;
-
- setItems((items) => {
- const oldIndex = items.findIndex((item) => item.id === active.id);
- const newIndex = items.findIndex((item) => item.id === over.id);
- return arrayMove(items, oldIndex, newIndex);
- });
- };
-
- return (
-
-
-
- {items.length === 0 ? (
-
- ) : (
-
-
- {items?.map((section, index) => (
- {
- if (section?.id) {
- await softDeleteByIds.mutateAsync({
- ids: [section.id],
- });
- }
- setItems(sections);
- }}>
-
-
- ))}
-
-
- )}
-
- }
- className="mt-4"
- onClick={() => {
- setItems([
- ...items.filter((item) => !!item.id),
- { id: null, title: "" },
- ]);
- }}>
- 添加章节
-
-
- );
-};
-
-export default CourseContentForm;
diff --git a/apps/web/src/components/models/course/editor/form/CourseContentForm/LectureList.tsx b/apps/web/src/components/models/course/editor/form/CourseContentForm/LectureList.tsx
index 7ca5171..0ef99e7 100644
--- a/apps/web/src/components/models/course/editor/form/CourseContentForm/LectureList.tsx
+++ b/apps/web/src/components/models/course/editor/form/CourseContentForm/LectureList.tsx
@@ -15,7 +15,7 @@ import {
Collapse,
message,
} from "antd";
-import React, { useCallback, useEffect, useState } from "react";
+import React, { useEffect, useState } from "react";
import {
DndContext,
closestCenter,
@@ -69,12 +69,16 @@ export const LectureList: React.FC = ({
enabled: !!sectionId,
}
);
+
+ // 用 lectures 初始化 items 状态
+ const [items, setItems] = useState(lectures);
+
+ // 当 lectures 变化时更新 items
useEffect(() => {
- if (lectures && !isLoading) {
+ if (!isLoading) {
setItems(lectures);
}
}, [lectures, isLoading]);
- const [items, setItems] = useState(lectures);
const sensors = useSensors(
useSensor(PointerSensor),
@@ -96,12 +100,12 @@ export const LectureList: React.FC = ({
return (
-
{
console.log(lectures);
}}>
123
-
+ */}
= ({
icon={}
className="mt-4"
onClick={() => {
- setItems([
- ...items.filter((item) => !!item.id),
+ setItems((prevItems) => [
+ ...prevItems.filter((item) => !!item.id),
{
id: null,
title: "",
diff --git a/apps/web/src/components/models/course/editor/form/CourseContentForm/SortableSection.tsx b/apps/web/src/components/models/course/editor/form/CourseContentForm/SortableSection.tsx
index 020d944..0f0b7d5 100644
--- a/apps/web/src/components/models/course/editor/form/CourseContentForm/SortableSection.tsx
+++ b/apps/web/src/components/models/course/editor/form/CourseContentForm/SortableSection.tsx
@@ -120,6 +120,7 @@ export const SortableSection: React.FC = ({
{speed}x
diff --git a/packages/common/src/constants.ts b/packages/common/src/constants.ts
index 675ad6d..0a41f6e 100755
--- a/packages/common/src/constants.ts
+++ b/packages/common/src/constants.ts
@@ -1,5 +1,5 @@
import { Prisma } from "@prisma/client";
-import { AppConfigSlug, RolePerms, TaxonomySlug } from "./enum";
+import { AppConfigSlug, ObjectType, RolePerms, TaxonomySlug } from "./enum";
export const InitRoles: {
name: string;
@@ -50,7 +50,11 @@ export const InitRoles: {
permissions: Object.keys(RolePerms) as RolePerms[],
},
];
-export const InitTaxonomies: { name: string; slug: string }[] = [
+export const InitTaxonomies: {
+ name: string;
+ slug: string;
+ objectType?: string[];
+}[] = [
{
name: "分类",
slug: TaxonomySlug.CATEGORY,
@@ -58,6 +62,7 @@ export const InitTaxonomies: { name: string; slug: string }[] = [
{
name: "难度等级",
slug: TaxonomySlug.LEVEL,
+ objectType: [ObjectType.COURSE],
},
// {
// name: "研判单元",
diff --git a/packages/common/src/models/post.ts b/packages/common/src/models/post.ts
index b78313f..6c0bc7a 100644
--- a/packages/common/src/models/post.ts
+++ b/packages/common/src/models/post.ts
@@ -1,5 +1,13 @@
-import { Post, Department, Staff, Enrollment } from "@prisma/client";
+import {
+ Post,
+ Department,
+ Staff,
+ Enrollment,
+ Taxonomy,
+ Term,
+} from "@prisma/client";
import { StaffDto } from "./staff";
+import { TermDto } from "./term";
export type PostComment = {
id: string;
@@ -63,4 +71,5 @@ export type Course = Post & {
export type CourseDto = Course & {
enrollments?: Enrollment[];
sections?: SectionDto[];
+ terms: Term[];
};