import { Button, Empty, Form, Spin } from "antd"; import NodeMenu from "./NodeMenu"; import { api, usePost, useVisitor } from "@nice/client"; import { ObjectType, PathDto, postDetailSelect, PostType, Prisma, RolePerms, VisitType, } from "@nice/common"; import TermSelect from "../../models/term/term-select"; import DepartmentSelect from "../../models/department/department-select"; import { useEffect, useMemo, useRef, useState } from "react"; import toast from "react-hot-toast"; import { MindElixirInstance } from "mind-elixir"; import MindElixir from "mind-elixir"; import { useTusUpload } from "@web/src/hooks/useTusUpload"; import { useNavigate } from "react-router-dom"; import { useAuth } from "@web/src/providers/auth-provider"; import { MIND_OPTIONS } from "./constant"; import { SaveOutlined } from "@ant-design/icons"; export default function MindEditor({ id }: { id?: string }) { const containerRef = useRef(null); const [instance, setInstance] = useState(null); const { isAuthenticated, user, hasSomePermissions } = useAuth(); const { read } = useVisitor(); const { data: post, isLoading }: { data: PathDto; isLoading: boolean } = api.post.findFirst.useQuery( { where: { id, }, select: postDetailSelect, }, { enabled: Boolean(id) } ); const canEdit: boolean = useMemo(() => { const isAuth = isAuthenticated && user?.id === post?.author?.id; return !!id || isAuth || hasSomePermissions(RolePerms.MANAGE_ANY_POST); }, [user]); const navigate = useNavigate(); const { create, update } = usePost(); const { data: taxonomies } = api.taxonomy.getAll.useQuery({ type: ObjectType.COURSE, }); const { handleFileUpload } = useTusUpload(); const [form] = Form.useForm(); useEffect(() => { if (post?.id && id) { read.mutateAsync({ data: { visitorId: user?.id || null, postId: post?.id, type: VisitType.READED, }, }); } }, [post]); useEffect(() => { if (post && form && instance && id) { instance.refresh((post as any).meta); const deptIds = (post?.depts || [])?.map((dept) => dept.id); const formData = { title: post.title, deptIds: deptIds, }; post.terms?.forEach((term) => { formData[term.taxonomyId] = term.id; // 假设 taxonomyName是您在 Form.Item 中使用的name }); form.setFieldsValue(formData); } }, [post, form, instance, id]); useEffect(() => { if (!containerRef.current) return; const mind = new MindElixir({ ...MIND_OPTIONS, el: containerRef.current, before: { beginEdit() { return canEdit; }, }, draggable: canEdit, // 禁用拖拽 contextMenu: canEdit, // 禁用右键菜单 toolBar: canEdit, // 禁用工具栏 nodeMenu: canEdit, // 禁用节点右键菜单 keypress: canEdit, // 禁用键盘快捷键 }); mind.init(MindElixir.new("新思维导图")); containerRef.current.hidden = true; //挂载实例 setInstance(mind); }, [canEdit]); useEffect(() => { if ((!id || post) && instance) { containerRef.current.hidden = false; instance.toCenter(); if (post?.meta?.nodeData) { instance.refresh(post?.meta); } } }, [id, post, instance]); //保存 按钮 函数 const handleSave = async () => { if (!instance) return; const values = form.getFieldsValue(); //以图片格式导出思维导图以作为思维导图封面 const imgBlob = await instance?.exportPng(); handleFileUpload( imgBlob, async (result) => { const termIds = taxonomies .map((tax) => values[tax.id]) .filter((id) => id); const deptIds = (values?.deptIds || []) as string[]; const { theme, ...data } = instance.getData(); try { if (post && id) { const params: Prisma.PostUpdateArgs = { where: { id, }, data: { title: data.nodeData.topic, meta: { ...data, thumbnail: result.compressedUrl, }, terms: { set: termIds.map((id) => ({ id })), }, depts: { set: deptIds.map((id) => ({ id })), }, updatedAt: new Date(), }, }; await update.mutateAsync(params); toast.success("更新成功"); } else { const params: Prisma.PostCreateInput = { type: PostType.PATH, title: data.nodeData.topic, meta: { ...data, thumbnail: result.compressedUrl }, terms: { connect: termIds.map((id) => ({ id })), }, depts: { connect: deptIds.map((id) => ({ id })), }, updatedAt: new Date(), }; const res = await create.mutateAsync({ data: params }); navigate(`/path/editor/${res.id}`, { replace: true }); toast.success("创建成功"); } } catch (error) { toast.error("保存失败"); throw error; } console.log(result); }, (error) => {}, `mind-thumb-${new Date().toString()}` ); }; useEffect(() => { containerRef.current.style.height = `${Math.floor(window.innerHeight - 271)}px`; }, []); return (
{canEdit && taxonomies && (
{taxonomies.map((tax, index) => ( ))}
{canEdit && ( )}
)}
e.preventDefault()} /> {canEdit && instance && } {isLoading && (
)} {!post && id && !isLoading && (
)}
); }