2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-06 16:21:03 +08:00

feat: 使用全新的方式加载monaco

This commit is contained in:
2025-12-31 02:49:12 +08:00
parent 9530a06da5
commit b86fc187d7
7 changed files with 426 additions and 51 deletions

View File

@@ -1,6 +1,7 @@
<script setup lang="ts">
import '@/utils/ace'
import { VAceEditor } from 'vue3-ace-editor'
import { useThemeStore } from '@/store'
import { getMonaco } from '@/utils/monaco'
import type * as Monaco from 'monaco-editor'
const value = defineModel<string>('value', { type: String, required: true })
const props = defineProps({
@@ -19,17 +20,105 @@ const props = defineProps({
required: false
}
})
const containerRef = ref<HTMLDivElement>()
const editorRef = shallowRef<Monaco.editor.IStandaloneCodeEditor>()
const monacoRef = shallowRef<typeof Monaco>()
const loading = ref(true)
const themeStore = useThemeStore()
async function initEditor() {
if (!containerRef.value) return
const monaco = await getMonaco(themeStore.locale)
monacoRef.value = monaco
editorRef.value = monaco.editor.create(containerRef.value, {
value: value.value,
language: props.lang,
theme: 'vs-dark',
readOnly: props.readOnly,
automaticLayout: true,
smoothScrolling: true,
formatOnPaste: true,
formatOnType: true
})
editorRef.value.onDidChangeModelContent(() => {
const newValue = editorRef.value?.getValue() ?? ''
if (newValue !== value.value) {
value.value = newValue
}
})
loading.value = false
}
watch(value, (newValue) => {
if (editorRef.value && editorRef.value.getValue() !== newValue) {
editorRef.value.setValue(newValue)
}
})
watch(
() => props.lang,
(newLang) => {
if (editorRef.value && monacoRef.value) {
const model = editorRef.value.getModel()
if (model) {
monacoRef.value.editor.setModelLanguage(model, newLang)
}
}
}
)
watch(
() => props.readOnly,
(newReadOnly) => {
if (editorRef.value) {
editorRef.value.updateOptions({ readOnly: newReadOnly })
}
}
)
onMounted(() => {
initEditor()
})
onBeforeUnmount(() => {
editorRef.value?.dispose()
})
</script>
<template>
<v-ace-editor
v-model:value="value"
:lang="props.lang"
:options="{ useWorker: true }"
:readonly="props.readOnly"
theme="monokai"
:style="{ height: props.height }"
/>
<div class="common-editor" :style="{ height: props.height }">
<div v-if="loading" class="editor-loading">
<n-spin size="medium" />
</div>
<div ref="containerRef" class="editor-container" :style="{ height: props.height }" />
</div>
</template>
<style scoped lang="scss"></style>
<style scoped lang="scss">
.common-editor {
position: relative;
width: 100%;
}
.editor-loading {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
}
.editor-container {
width: 100%;
}
</style>

View File

@@ -1,9 +1,11 @@
<script setup lang="ts">
import file from '@/api/panel/file'
import { useThemeStore } from '@/store'
import { decodeBase64 } from '@/utils'
import '@/utils/ace'
import { languageByPath } from '@/utils/file'
import { VAceEditor } from 'vue3-ace-editor'
import { getMonaco } from '@/utils/monaco'
import type * as Monaco from 'monaco-editor'
import { onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue'
import { useGettext } from 'vue3-gettext'
const { $gettext } = useGettext()
@@ -18,9 +20,59 @@ const props = defineProps({
}
})
const themeStore = useThemeStore()
const containerRef = ref<HTMLDivElement>()
const editorRef = shallowRef<Monaco.editor.IStandaloneCodeEditor>()
const monacoRef = shallowRef<typeof Monaco>()
const loading = ref(true)
const disabled = ref(false) // 在出现错误的情况下禁用保存
const content = ref('')
async function initEditor() {
if (!containerRef.value) return
const monaco = await getMonaco(themeStore.locale)
monacoRef.value = monaco
editorRef.value = monaco.editor.create(containerRef.value, {
value: content.value,
language: languageByPath(props.path),
theme:
(languageByPath(props.path) == 'nginx' ? 'nginx-theme' : 'vs') +
(themeStore.darkMode ? '-dark' : ''),
readOnly: props.readOnly,
automaticLayout: true,
smoothScrolling: true,
formatOnPaste: true,
formatOnType: true
})
editorRef.value.onDidChangeModelContent(() => {
const newValue = editorRef.value?.getValue() ?? ''
if (newValue !== content.value) {
content.value = newValue
}
})
loading.value = false
}
watch(content, (newValue) => {
if (editorRef.value && editorRef.value.getValue() !== newValue) {
editorRef.value.setValue(newValue)
}
})
watch(
() => props.readOnly,
(newReadOnly) => {
if (editorRef.value) {
editorRef.value.updateOptions({ readOnly: newReadOnly })
}
}
)
const get = () => {
useRequest(file.content(encodeURIComponent(props.path)))
.onSuccess(({ data }) => {
@@ -44,6 +96,11 @@ const save = () => {
onMounted(() => {
get()
initEditor()
})
onBeforeUnmount(() => {
editorRef.value?.dispose()
})
defineExpose({
@@ -53,13 +110,33 @@ defineExpose({
</script>
<template>
<v-ace-editor
v-model:value="content"
:lang="languageByPath(props.path)"
:options="{ useWorker: true }"
theme="monokai"
style="height: 60vh"
/>
<div class="file-editor" style="height: 60vh">
<div v-if="loading" class="editor-loading">
<n-spin size="medium" />
</div>
<div ref="containerRef" class="editor-container" style="height: 60vh" />
</div>
</template>
<style scoped lang="scss"></style>
<style scoped lang="scss">
.file-editor {
position: relative;
width: 100%;
}
.editor-loading {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
}
.editor-container {
width: 100%;
}
</style>