This commit is contained in:
ditiqi 2025-02-06 19:23:40 +08:00
parent 74b618e0c1
commit 5b4953665c
1 changed files with 196 additions and 0 deletions

View File

@ -0,0 +1,196 @@
import {
DragOutlined,
CaretRightOutlined,
SaveOutlined,
} from "@ant-design/icons";
import { Form, Button, Input, Select, Space } from "antd";
import React, { useState } from "react";
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import QuillEditor from "@web/src/components/common/editor/quill/QuillEditor";
import { TusUploader } from "@web/src/components/common/uploader/TusUploader";
import { LectureType, LessonTypeLabel, PostType } from "@nice/common";
import { usePost } from "@nice/client";
import toast from "react-hot-toast";
import { LectureData } from "./interface";
interface SortableLectureProps {
field: LectureData;
remove: () => void;
sectionFieldKey: string;
}
export const SortableLecture: React.FC<SortableLectureProps> = ({
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;
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,
},
});
}
toast.success("课时已更新");
field.id = result.id;
setEditing(false);
} catch (err) {
toast.success("更新失败");
console.log(err);
} finally {
setLoading(false);
}
};
const style = {
transform: CSS.Transform.toString(transform),
transition,
borderBottom: "1px solid #f0f0f0",
backgroundColor: isDragging ? "#f5f5f5" : undefined,
};
return (
<div ref={setNodeRef} style={style} className="p-4">
{editing ? (
<Form form={form} initialValues={field}>
<div className="flex gap-4">
<Form.Item
name="title"
initialValue={field?.title}
className="mb-0 flex-1"
rules={[{ required: true }]}>
<Input placeholder="课时标题" />
</Form.Item>
<Form.Item
name={["meta", "type"]}
className="mb-0 w-32"
rules={[{ required: true }]}>
<Select
placeholder="选择类型"
options={[
{ label: "视频", value: LectureType.VIDEO },
{
label: "文章",
value: LectureType.ARTICLE,
},
]}
/>
</Form.Item>
</div>
<div className="mt-4 flex flex-1 ">
{lectureType === LectureType.VIDEO ? (
<Form.Item
name={["meta", "fileIds"]}
className="mb-0 flex-1"
rules={[{ required: true }]}>
<TusUploader multiple={false} />
</Form.Item>
) : (
<Form.Item
name="content"
className="mb-0 flex-1"
rules={[{ required: true }]}>
<QuillEditor />
</Form.Item>
)}
</div>
<div className="mt-4 flex justify-end gap-2">
<Button
onClick={handleSave}
loading={loading}
type="primary"
icon={<SaveOutlined />}>
</Button>
<Button
onClick={() => {
setEditing(false);
if (!field?.id) {
remove();
}
}}>
</Button>
</div>
</Form>
) : (
<div className="flex items-center justify-between">
<Space>
<DragOutlined
{...attributes}
{...listeners}
className="cursor-move"
/>
<CaretRightOutlined />
<span>{LessonTypeLabel[field?.meta?.type]}</span>
<span>{field?.title || "未命名课时"}</span>
</Space>
<Space>
<Button type="link" onClick={() => setEditing(true)}>
</Button>
<Button type="link" danger onClick={remove}>
</Button>
</Space>
</div>
)}
</div>
);
};