✨ Complete multi-document mode
This commit is contained in:
@@ -1,39 +1,30 @@
|
||||
<script setup lang="ts">
|
||||
import {onBeforeUnmount, onMounted, ref} from 'vue';
|
||||
import {useEditorStore} from '@/stores/editorStore';
|
||||
import {useDocumentStore} from '@/stores/documentStore';
|
||||
import {useConfigStore} from '@/stores/configStore';
|
||||
import {createWheelZoomHandler} from './basic/wheelZoomExtension';
|
||||
import Toolbar from '@/components/toolbar/Toolbar.vue';
|
||||
|
||||
const editorStore = useEditorStore();
|
||||
const documentStore = useDocumentStore();
|
||||
const configStore = useConfigStore();
|
||||
|
||||
const props = defineProps({
|
||||
initialDoc: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
|
||||
const editorElement = ref<HTMLElement | null>(null);
|
||||
|
||||
// 创建滚轮缩放处理器
|
||||
const wheelHandler = createWheelZoomHandler(
|
||||
configStore.increaseFontSize,
|
||||
configStore.decreaseFontSize
|
||||
configStore.increaseFontSize,
|
||||
configStore.decreaseFontSize
|
||||
);
|
||||
|
||||
onMounted(async () => {
|
||||
if (!editorElement.value) return;
|
||||
|
||||
await documentStore.initialize();
|
||||
// 设置编辑器容器
|
||||
editorStore.setEditorContainer(editorElement.value);
|
||||
|
||||
// 如果编辑器还没有初始化,创建编辑器
|
||||
if (!editorStore.isEditorInitialized) {
|
||||
await editorStore.createEditor(props.initialDoc);
|
||||
}
|
||||
|
||||
// 添加滚轮事件监听
|
||||
editorElement.value.addEventListener('wheel', wheelHandler, {passive: false});
|
||||
});
|
||||
@@ -49,7 +40,7 @@ onBeforeUnmount(() => {
|
||||
<template>
|
||||
<div class="editor-container">
|
||||
<div ref="editorElement" class="editor"></div>
|
||||
<Toolbar />
|
||||
<Toolbar/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@@ -1,109 +0,0 @@
|
||||
import { EditorView, ViewPlugin, ViewUpdate } from '@codemirror/view';
|
||||
import { useDocumentStore } from '@/stores/documentStore';
|
||||
|
||||
// 自动保存配置选项
|
||||
export interface AutoSaveOptions {
|
||||
// 防抖延迟(毫秒)
|
||||
debounceDelay?: number;
|
||||
// 保存状态回调
|
||||
onSaveStart?: () => void;
|
||||
onSaveSuccess?: () => void;
|
||||
onSaveError?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 简单防抖函数
|
||||
*/
|
||||
function debounce<T extends (...args: any[]) => any>(
|
||||
func: T,
|
||||
delay: number
|
||||
): T & { cancel: () => void } {
|
||||
let timeoutId: number | null = null;
|
||||
|
||||
const debounced = ((...args: Parameters<T>) => {
|
||||
if (timeoutId !== null) {
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
|
||||
timeoutId = window.setTimeout(() => {
|
||||
timeoutId = null;
|
||||
func(...args);
|
||||
}, delay);
|
||||
}) as T & { cancel: () => void };
|
||||
|
||||
debounced.cancel = () => {
|
||||
if (timeoutId !== null) {
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = null;
|
||||
}
|
||||
};
|
||||
|
||||
return debounced;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建自动保存插件
|
||||
*/
|
||||
export function createAutoSavePlugin(options: AutoSaveOptions = {}) {
|
||||
const {
|
||||
debounceDelay = 2000,
|
||||
onSaveStart = () => {},
|
||||
onSaveSuccess = () => {},
|
||||
onSaveError = () => {}
|
||||
} = options;
|
||||
|
||||
return ViewPlugin.fromClass(
|
||||
class AutoSavePlugin {
|
||||
private documentStore = useDocumentStore();
|
||||
private debouncedSave: ((content: string) => void) & { cancel: () => void };
|
||||
private isDestroyed = false;
|
||||
private lastContent = '';
|
||||
|
||||
constructor(private view: EditorView) {
|
||||
this.lastContent = view.state.doc.toString();
|
||||
this.debouncedSave = debounce(
|
||||
(content: string) => this.performSave(content),
|
||||
debounceDelay
|
||||
);
|
||||
}
|
||||
|
||||
private async performSave(content: string): Promise<void> {
|
||||
if (this.isDestroyed) return;
|
||||
|
||||
try {
|
||||
onSaveStart();
|
||||
const success = await this.documentStore.saveDocumentContent(content);
|
||||
|
||||
if (success) {
|
||||
this.lastContent = content;
|
||||
onSaveSuccess();
|
||||
} else {
|
||||
onSaveError();
|
||||
}
|
||||
} catch (error) {
|
||||
onSaveError();
|
||||
}
|
||||
}
|
||||
|
||||
update(update: ViewUpdate) {
|
||||
if (!update.docChanged || this.isDestroyed) return;
|
||||
|
||||
const newContent = this.view.state.doc.toString();
|
||||
if (newContent === this.lastContent) return;
|
||||
|
||||
this.debouncedSave(newContent);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.isDestroyed = true;
|
||||
this.debouncedSave.cancel();
|
||||
|
||||
// 如果内容有变化,立即保存
|
||||
const currentContent = this.view.state.doc.toString();
|
||||
if (currentContent !== this.lastContent) {
|
||||
this.documentStore.saveDocumentContent(currentContent).catch(() => {});
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
39
frontend/src/views/editor/basic/contentChangeExtension.ts
Normal file
39
frontend/src/views/editor/basic/contentChangeExtension.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { EditorView, ViewPlugin, ViewUpdate } from '@codemirror/view';
|
||||
import { useDocumentStore } from '@/stores/documentStore';
|
||||
import { useEditorStore } from '@/stores/editorStore';
|
||||
|
||||
/**
|
||||
* 内容变化监听插件 - 集成文档和编辑器管理
|
||||
*/
|
||||
export function createContentChangePlugin() {
|
||||
return ViewPlugin.fromClass(
|
||||
class ContentChangePlugin {
|
||||
private documentStore = useDocumentStore();
|
||||
private editorStore = useEditorStore();
|
||||
private lastContent = '';
|
||||
|
||||
constructor(private view: EditorView) {
|
||||
this.lastContent = view.state.doc.toString();
|
||||
}
|
||||
|
||||
update(update: ViewUpdate) {
|
||||
if (!update.docChanged) return;
|
||||
|
||||
const newContent = this.view.state.doc.toString();
|
||||
if (newContent === this.lastContent) return;
|
||||
|
||||
this.lastContent = newContent;
|
||||
|
||||
// 通知编辑器管理器内容已变化
|
||||
const currentDocId = this.documentStore.currentDocumentId;
|
||||
if (currentDocId) {
|
||||
this.editorStore.onContentChange(currentDocId);
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
Reference in New Issue
Block a user