143 lines
3.3 KiB
TypeScript
143 lines
3.3 KiB
TypeScript
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
// app/components/Editor/MonacoEditor.tsx
|
|
'use client'
|
|
|
|
import { useRef, useCallback, useEffect } from 'react'
|
|
import Editor from '@monaco-editor/react'
|
|
import * as Y from 'yjs'
|
|
import { WebsocketProvider } from 'y-websocket'
|
|
import { MonacoBinding } from 'y-monaco'
|
|
import { message } from 'antd'
|
|
import { DocumentService } from '@/app/lib/services/document'
|
|
|
|
interface MonacoEditorProps {
|
|
docId: string
|
|
defaultValue?: string
|
|
onContentChange?: (content: string) => void
|
|
}
|
|
|
|
export default function MonacoEditor({
|
|
docId,
|
|
defaultValue = '',
|
|
onContentChange
|
|
}: MonacoEditorProps) {
|
|
const editorRef = useRef<any>(null)
|
|
const monacoRef = useRef<any>(null)
|
|
|
|
// 自动保存防抖
|
|
const autoSaveDebounced = useCallback(
|
|
debounce(async (content: string) => {
|
|
try {
|
|
await DocumentService.saveDocument({
|
|
id: docId,
|
|
content,
|
|
})
|
|
message.success('自动保存成功')
|
|
} catch (error) {
|
|
message.error('自动保存失败'+error)
|
|
}
|
|
}, 2000),
|
|
[docId]
|
|
)
|
|
|
|
// 手动保存
|
|
const saveContent = async () => {
|
|
if (!editorRef.current) return
|
|
|
|
const content = editorRef.current.getValue()
|
|
try {
|
|
await DocumentService.saveDocument({
|
|
id: docId,
|
|
content,
|
|
})
|
|
message.success('保存成功')
|
|
} catch (error) {
|
|
message.error('保存失败'+error)
|
|
}
|
|
}
|
|
|
|
// 监听内容变化
|
|
const handleEditorChange = (value: string | undefined) => {
|
|
if (value === undefined) return
|
|
|
|
onContentChange?.(value)
|
|
autoSaveDebounced(value)
|
|
}
|
|
|
|
useEffect(() => {
|
|
if (!editorRef.current) return
|
|
|
|
const ydoc = new Y.Doc()
|
|
const provider = new WebsocketProvider(
|
|
'ws://localhost:1234',
|
|
docId,
|
|
ydoc
|
|
)
|
|
const type = ydoc.getText('monaco')
|
|
|
|
const binding = new MonacoBinding(
|
|
type,
|
|
editorRef.current.getModel(),
|
|
new Set([editorRef.current]),
|
|
provider.awareness
|
|
)
|
|
|
|
// 添加快捷键保存
|
|
editorRef.current.addCommand(
|
|
monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.KeyS,
|
|
() => {
|
|
saveContent()
|
|
}
|
|
)
|
|
|
|
return () => {
|
|
ydoc.destroy()
|
|
provider.destroy()
|
|
}
|
|
}, [docId])
|
|
|
|
function handleEditorDidMount(editor: any, monaco: any) {
|
|
editorRef.current = editor
|
|
monacoRef.current = monaco
|
|
|
|
editor.updateOptions({
|
|
fontSize: 14,
|
|
fontFamily: 'JetBrains Mono, Consolas, monospace',
|
|
minimap: { enabled: false },
|
|
scrollBeyondLastLine: false,
|
|
lineNumbers: 'on',
|
|
renderWhitespace: 'boundary',
|
|
wordWrap: 'on',
|
|
})
|
|
}
|
|
|
|
return (
|
|
<div className="h-full relative">
|
|
<Editor
|
|
height="100%"
|
|
defaultLanguage="markdown"
|
|
// defaultValue={defaultValue}
|
|
theme="vs-light"
|
|
// onMount={handleEditorDidMount}
|
|
// onChange={handleEditorChange}
|
|
options={{
|
|
automaticLayout: true,
|
|
}}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
// 防抖函数
|
|
function debounce(func: Function, wait: number) {
|
|
let timeout: NodeJS.Timeout
|
|
return function executedFunction(...args: any[]) {
|
|
const later = () => {
|
|
clearTimeout(timeout)
|
|
func(...args)
|
|
}
|
|
clearTimeout(timeout)
|
|
timeout = setTimeout(later, wait)
|
|
}
|
|
} |