"use client" import { Box } from "@mantine/core" import { RichTextEditor } from "@mantine/tiptap" import "@mantine/tiptap/styles.css" import { IconPhoto } from "@tabler/icons-react" import Image from "@tiptap/extension-image" import Link from "@tiptap/extension-link" import Placeholder from "@tiptap/extension-placeholder" import Underline from "@tiptap/extension-underline" import { useEditor } from "@tiptap/react" import StarterKit from "@tiptap/starter-kit" import { useEffect, useRef, type ChangeEvent } from "react" import { useKeyEventStore } from "../store" import { useTopToolsStore } from "../useTopToolsStore" export const splitWord = "@1024-" const getEditorHtml = (rawValue?: string) => { if (!rawValue) return "" const [html] = rawValue.split(splitWord) return html ?? rawValue } const editorLabels = { boldControlLabel: "加粗", italicControlLabel: "斜体", underlineControlLabel: "下划线", strikeControlLabel: "删除线", clearFormattingControlLabel: "清除格式", codeControlLabel: "行内代码", h1ControlLabel: "一级标题", h2ControlLabel: "二级标题", h3ControlLabel: "三级标题", h4ControlLabel: "四级标题", bulletListControlLabel: "无序列表", orderedListControlLabel: "有序列表", blockquoteControlLabel: "引用", hrControlLabel: "分割线", linkControlLabel: "插入链接", unlinkControlLabel: "移除链接", linkEditorInputLabel: "输入链接地址", linkEditorInputPlaceholder: "请输入链接,例如:https://example.com", linkEditorExternalLink: "在新标签页打开", linkEditorInternalLink: "在当前标签页打开", linkEditorSave: "保存", } interface EditorContainerProps { value: string onChange: (value: string) => void color?: string disabled?: boolean } const EditorContainer = ({ value, onChange, color, disabled, }: EditorContainerProps) => { const { isView } = useTopToolsStore() const fileInputRef = useRef(null) const editor = useEditor({ immediatelyRender: false, extensions: [ StarterKit, Underline, Link, Image, Placeholder.configure({ placeholder: "请输入内容..." }), ], content: getEditorHtml(value), editable: !disabled && !isView, onUpdate: ({ editor }) => { const html = getEditorHtml(editor.getHTML()) onChange(`${html}${splitWord}${editor.getText()}`) }, onFocus: () => { useKeyEventStore.getState().setFocusInput(true) }, onBlur: () => { useKeyEventStore.getState().setFocusInput(false) }, }) useEffect(() => { if (editor) { editor.setEditable(!disabled && !isView) } }, [disabled, isView, editor]) useEffect(() => { if (!editor) return const incomingHtml = getEditorHtml(value) || "

" if (editor.getHTML() !== incomingHtml) { // Keep editor content in sync with external state, including empty value switch. editor.commands.setContent(incomingHtml, { emitUpdate: false }) } }, [value, editor]) const handleImageUpload = (e: ChangeEvent) => { const file = e.target.files?.[0] if (file && editor) { const reader = new FileReader() reader.onload = (e) => { const result = e.target?.result as string editor.chain().focus().setImage({ src: result }).run() } reader.readAsDataURL(file) } } return ( fileInputRef.current?.click()} aria-label="上传图片" title="上传图片"> ) } export default EditorContainer