diff --git a/app/components/PostBodyMarkdownEditor.vue b/app/components/PostBodyMarkdownEditor.vue index 500b0c5..aa74cf0 100644 --- a/app/components/PostBodyMarkdownEditor.vue +++ b/app/components/PostBodyMarkdownEditor.vue @@ -17,6 +17,43 @@ const toast = useToast() const mountEl = ref(null) const isMobileViewport = ref(false) const isEditorReady = ref(false) +let editorReadyFallbackTimer: ReturnType | null = null +let readinessCheckToken = 0 + +function clearEditorReadyFallbackTimer() { + if (editorReadyFallbackTimer) { + clearTimeout(editorReadyFallbackTimer) + editorReadyFallbackTimer = null + } +} + +function startEditorReadyFallbackTimer() { + clearEditorReadyFallbackTimer() + editorReadyFallbackTimer = setTimeout(() => { + isEditorReady.value = true + editorReadyFallbackTimer = null + }, 3000) +} + +async function markEditorReadyWhenVisible() { + const currentToken = ++readinessCheckToken + await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(() => resolve()))) + const deadline = Date.now() + 2000 + while (Date.now() < deadline) { + if (currentToken !== readinessCheckToken) { + return + } + const host = mountEl.value + const vditorRoot = host?.querySelector('.vditor') as HTMLElement | null + const hasVisibleEditor = Boolean(vditorRoot && vditorRoot.offsetHeight > 0 && vditorRoot.offsetWidth > 0) + if (hasVisibleEditor) { + isEditorReady.value = true + clearEditorReadyFallbackTimer() + return + } + await new Promise(resolve => setTimeout(resolve, 50)) + } +} const editorContainerStyle = computed(() => { if (isMobileViewport.value) { return { @@ -44,6 +81,9 @@ const bridge = createPostBodyMarkdownEditorBridge({ value, isMobile: isMobileViewport.value, onInput, + onEditorReady: () => { + void markEditorReadyWhenVisible() + }, onUploadError: () => { toast.add({ title: '图片上传失败', color: 'warning' }) }, @@ -74,6 +114,8 @@ onMounted(async () => { if (!mountEl.value) { return } + isEditorReady.value = false + startEditorReadyFallbackTimer() bridge.unmount() bridge.mount(mountEl.value) }, @@ -89,8 +131,9 @@ onMounted(async () => { onReady: (ctor) => { vditorCtor.value = ctor if (mountEl.value) { + isEditorReady.value = false + startEditorReadyFallbackTimer() bridge.mount(mountEl.value) - isEditorReady.value = true } }, onError: (error) => { @@ -105,7 +148,9 @@ watch(() => props.modelValue, () => { onBeforeUnmount(() => { unmounted = true + readinessCheckToken += 1 isEditorReady.value = false + clearEditorReadyFallbackTimer() if (viewportMql && onViewportChange) { viewportMql.removeEventListener('change', onViewportChange) } diff --git a/app/components/post-body-markdown-editor-vditor-config.ts b/app/components/post-body-markdown-editor-vditor-config.ts index e500575..a7eecff 100644 --- a/app/components/post-body-markdown-editor-vditor-config.ts +++ b/app/components/post-body-markdown-editor-vditor-config.ts @@ -3,6 +3,7 @@ interface BuildVditorOptionsInput { isMobile: boolean onInput: (value: string) => void onUploadError: () => void + onEditorReady?: () => void } const DESKTOP_TOOLBAR: ReadonlyArray = [ @@ -114,6 +115,9 @@ export function buildPostBodyMarkdownEditorVditorOptions(input: BuildVditorOptio input(value: string) { input.onInput(value) }, + after() { + input.onEditorReady?.() + }, } } diff --git a/packages/drizzle-pkg/db.sqlite b/packages/drizzle-pkg/db.sqlite index b1eb4fa..180e03b 100644 Binary files a/packages/drizzle-pkg/db.sqlite and b/packages/drizzle-pkg/db.sqlite differ