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 index e5c5dbc..8ab2ad2 100644 --- a/apps/web/src/components/models/course/editor/form/CourseContentForm copy.tsx +++ b/apps/web/src/components/models/course/editor/form/CourseContentForm copy.tsx @@ -5,8 +5,17 @@ import { CaretRightOutlined, SaveOutlined, } from "@ant-design/icons"; -import { Form, Alert, Button, Input, Select, Space, Collapse, message } from "antd"; -import React, { useState } from "react"; +import { + Form, + Alert, + Button, + Input, + Select, + Space, + Collapse, + message, +} from "antd"; +import React, { useCallback, useEffect, useState } from "react"; import { DndContext, closestCenter, @@ -14,8 +23,9 @@ import { PointerSensor, useSensor, useSensors, + DragEndEvent, } from "@dnd-kit/core"; -import { usePost } from "@nice/client"; +import { api, emitDataChange } from "@nice/client"; import { arrayMove, SortableContext, @@ -26,9 +36,27 @@ import { import { CSS } from "@dnd-kit/utilities"; import QuillEditor from "../../../../common/editor/quill/QuillEditor"; import { TusUploader } from "../../../../common/uploader/TusUploader"; -import { LectureType } from "@nice/common"; +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 = () => ( ( ); -const SortableSection = ({ id, field, remove, children }: any) => { - const { attributes, listeners, setNodeRef, transform, transition } = - useSortable({ id }); - const [form] = Form.useForm(); - const { update, create } = usePost(); - const [loading, setLoading] = useState(false); +interface SortableSectionProps { + courseId?: string; + field: SectionData; + remove: () => void; + children: React.ReactNode; +} - const style = { - transform: CSS.Transform.toString(transform), +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 { - const values = await form.validateFields(); setLoading(true); - - if (values.id) { - // await update({ ...values, type: 'section' }); - message.success('章节更新成功'); - } else { - // await create({ ...values, type: 'section' }); - message.success('章节创建成功'); + 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); } - } catch (error: any) { - message.error(error.message || '保存失败'); + + field.id = result.id; + setEditing(false); + message.success("保存成功"); + } catch (error) { + console.log(error); + message.error("保存失败"); } finally { setLoading(false); } }; - const items = [ - { - key: field.name, - label: ( -
-
- - - -
- - - - - remove(field.name)} - className="text-red-500 cursor-pointer" - /> -
-
-
- ), - children:
{children}
, - }, - ]; - - return ( -
- ( - - )} - className="bg-gray-50" - /> -
- ); -}; - -const SortableLecture = ({ id, field, remove, sectionId }: any) => { - const { attributes, listeners, setNodeRef, transform, transition } = - useSortable({ id }); - const style = { transform: CSS.Transform.toString(transform), transition, + backgroundColor: isDragging ? "#f5f5f5" : undefined, }; - const { form, taxonomies } = useCourseEditor(); - const lectureType = - Form.useWatch( - ["sections", sectionId, "lectures", field.name, "type"], - form - ) || LectureType.VIDEO; - - const renderLabel = () => ( -
- - - - - + + + + + + + ) : ( +
+ + + {field.title || "未命名章节"} + + + + + +
+ ) + } + key={field.id || "new"}> + {children}
); }; -const LectureList = ({ field }: any) => { - const sensors = useSensors( - useSensor(PointerSensor), - useSensor(KeyboardSensor, { - coordinateGetter: sortableKeyboardCoordinates, - }) - ); +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 ( - - {(fields, { add, remove, move }) => ( - <> - { - if (over && active.id !== over.id) { - const oldIndex = fields.findIndex( - (field) => field.key === active.id - ); - const newIndex = fields.findIndex( - (field) => field.key === over.id - ); - if (oldIndex !== -1 && newIndex !== -1) { - move(oldIndex, newIndex); +
+ {editing ? ( +
+
+ + + + +