新增:连接管理、数据查询等功能
This commit is contained in:
139
web/src/views/db-cli/composables/useTabEditor.ts
Normal file
139
web/src/views/db-cli/composables/useTabEditor.ts
Normal file
@@ -0,0 +1,139 @@
|
||||
import { ref, nextTick } from 'vue'
|
||||
import { EditorView } from '@codemirror/view'
|
||||
import { EditorState } from '@codemirror/state'
|
||||
|
||||
export interface TabEditorTab {
|
||||
id?: number
|
||||
key: string
|
||||
title: string
|
||||
content: string
|
||||
connectionId?: number
|
||||
}
|
||||
|
||||
export interface TabEditorOptions {
|
||||
findContainer: (tabKey: string, retryCount?: number) => Promise<{ container: HTMLElement; pane?: HTMLElement } | null>
|
||||
checkContainerSize: (container: HTMLElement) => Promise<void>
|
||||
createExtensions: (tab: TabEditorTab) => any[]
|
||||
getInitialContent: (tab: TabEditorTab) => string
|
||||
onContentChange?: (tabKey: string, content: string) => void
|
||||
onEditorReady?: (tabKey: string, editor: EditorView) => void
|
||||
}
|
||||
|
||||
const INIT_DELAY = 200
|
||||
|
||||
export function useTabEditor(options: TabEditorOptions) {
|
||||
const { findContainer, checkContainerSize, createExtensions, getInitialContent, onContentChange, onEditorReady } = options
|
||||
|
||||
const editorViews = ref<Map<string, EditorView>>(new Map())
|
||||
|
||||
const getEditor = (tabKey: string): EditorView | null => {
|
||||
return editorViews.value.get(tabKey) as EditorView || null
|
||||
}
|
||||
|
||||
const destroyEditor = (tabKey: string): void => {
|
||||
const editor = editorViews.value.get(tabKey)
|
||||
if (!editor) return
|
||||
|
||||
if (onContentChange) {
|
||||
onContentChange(tabKey, editor.state.doc.toString())
|
||||
}
|
||||
editor.destroy()
|
||||
editorViews.value.delete(tabKey)
|
||||
}
|
||||
|
||||
const focusEditor = (editor: EditorView, delay = 0): void => {
|
||||
if (!editor) return
|
||||
|
||||
const focus = () => {
|
||||
editor.requestMeasure?.()
|
||||
editor.dispatch({ effects: [] })
|
||||
requestAnimationFrame(() => editor.focus())
|
||||
}
|
||||
|
||||
delay > 0 ? setTimeout(focus, delay) : requestAnimationFrame(focus)
|
||||
}
|
||||
|
||||
const initEditor = async (tabKey: string, tab: any, isActive: boolean, forceInit = false): Promise<boolean> => {
|
||||
if (!isActive && !forceInit) return false
|
||||
|
||||
const existingEditor = editorViews.value.get(tabKey)
|
||||
if (existingEditor instanceof EditorView) {
|
||||
if (isActive) focusEditor(existingEditor, 100)
|
||||
return true
|
||||
}
|
||||
|
||||
destroyEditor(tabKey)
|
||||
await nextTick()
|
||||
|
||||
const containerResult = await findContainer(tabKey)
|
||||
if (!containerResult) return false
|
||||
|
||||
const { container } = containerResult
|
||||
await checkContainerSize(container)
|
||||
|
||||
const rect = container.getBoundingClientRect()
|
||||
if (rect.width === 0 || rect.height === 0) {
|
||||
if (isActive) {
|
||||
setTimeout(() => initEditor(tabKey, tab, isActive, forceInit), 100)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const state = EditorState.create({
|
||||
doc: getInitialContent(tab),
|
||||
extensions: createExtensions(tab)
|
||||
})
|
||||
|
||||
container.innerHTML = ''
|
||||
const editorView = new EditorView({ state, parent: container })
|
||||
editorViews.value.set(tabKey, editorView)
|
||||
|
||||
if (onEditorReady) onEditorReady(tabKey, editorView)
|
||||
if (isActive) focusEditor(editorView, INIT_DELAY)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
const destroyAll = (): void => {
|
||||
editorViews.value.forEach((_, tabKey) => destroyEditor(tabKey))
|
||||
editorViews.value.clear()
|
||||
}
|
||||
|
||||
const updateEditorContent = (tabKey: string, content: string): boolean => {
|
||||
const editor = editorViews.value.get(tabKey)
|
||||
if (!editor) return false
|
||||
|
||||
const update = () => {
|
||||
const state = editor.state
|
||||
if (!state?.doc) return false
|
||||
if (state.doc.toString() === content) return true
|
||||
|
||||
try {
|
||||
editor.dispatch(state.update({ changes: { from: 0, to: state.doc.length, insert: content } }))
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return update() || update()
|
||||
} catch (error: unknown) {
|
||||
const errorMessage = error instanceof Error ? error.message : ''
|
||||
if (errorMessage?.includes('doesn\'t start from the previous state')) {
|
||||
try { return update() } catch {}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
editorViews,
|
||||
getEditor,
|
||||
destroyEditor,
|
||||
initEditor,
|
||||
focusEditor,
|
||||
destroyAll,
|
||||
updateEditorContent
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user