feat: 统一 CodeMirror 组件 (#5716)

This commit is contained in:
zhengkunwang
2024-07-17 17:16:11 +08:00
committed by zhengkunwang223
parent ed2f5c42d0
commit d91ce3da65
31 changed files with 356 additions and 626 deletions
@@ -1,47 +1,16 @@
<template>
<el-drawer
v-model="codeVisible"
:destroy-on-close="true"
:close-on-click-modal="false"
:close-on-press-escape="false"
size="50%"
>
<template #header>
<DrawerHeader :header="header" :back="handleClose" />
</template>
<codemirror
ref="mymirror"
:autofocus="true"
:placeholder="$t('commons.msg.noneData')"
:indent-with-tab="true"
:tabSize="4"
style="width: 100%; height: calc(100vh - 160px)"
:lineWrapping="true"
:matchBrackets="true"
theme="cobalt"
:styleActiveLine="true"
:extensions="extensions"
v-model="detailInfo"
:disabled="true"
/>
<DrawerPro v-model="codeVisible" :header="header" size="large" @close="handleClose">
<CodemirrorPro v-model="detailInfo" :height="800" :disabled="true" mode="json"></CodemirrorPro>
<template #footer>
<span class="dialog-footer">
<el-button @click="codeVisible = false">{{ $t('commons.button.cancel') }}</el-button>
</span>
</template>
</el-drawer>
</DrawerPro>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { Codemirror } from 'vue-codemirror';
import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from '@codemirror/theme-one-dark';
import DrawerHeader from '@/components/drawer-header/index.vue';
const mymirror = ref();
const extensions = [javascript(), oneDark];
const header = ref();
const detailInfo = ref();
const codeVisible = ref(false);
@@ -0,0 +1,136 @@
<template>
<div :style="customStyle">
<div ref="editorRef" class="editor-container"></div>
</div>
</template>
<script lang="ts" setup>
import { CSSProperties } from 'vue';
import { basicSetup, EditorView } from 'codemirror';
import { EditorState } from '@codemirror/state';
import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from '@codemirror/theme-one-dark';
import { StreamLanguage } from '@codemirror/language';
import { nginx } from '@codemirror/legacy-modes/mode/nginx';
import { yaml } from '@codemirror/legacy-modes/mode/yaml';
import { dockerFile } from '@codemirror/legacy-modes/mode/dockerfile';
import { placeholder } from '@codemirror/view';
import { json } from '@codemirror/lang-json';
defineOptions({ name: 'CodemirrorPro' });
const props = defineProps({
disabled: {
type: Boolean,
default: false,
},
modelValue: {
type: String,
default: '',
},
mode: {
type: String,
default: 'javascript',
},
placeholder: {
type: String,
default: '',
},
heightDiff: {
type: Number,
default: 200,
},
minHeight: {
type: Number,
default: 400,
},
});
const emit = defineEmits(['update:modelValue']);
const editorRef = ref();
const editorView = ref();
const content = computed(() => {
return props.modelValue;
});
const customStyle = computed<CSSProperties>(() => ({
width: '100%',
}));
const initCodeMirror = () => {
const defaultTheme = EditorView.theme({
'&.cm-editor': {
minHeight: props.minHeight + 'px',
height: 'calc(100vh - ' + props.heightDiff + 'px)',
},
});
const extensions = [
defaultTheme,
oneDark,
basicSetup,
EditorView.updateListener.of((v: any) => {
if (v.docChanged) {
emit('update:modelValue', v.state.doc.toString());
}
}),
placeholder(props.placeholder),
EditorView.editable.of(!props.disabled),
];
switch (props.mode) {
case 'dockerfile':
extensions.push(StreamLanguage.define(dockerFile));
break;
case 'javascript':
extensions.push(javascript());
break;
case 'nginx':
extensions.push(StreamLanguage.define(nginx));
break;
case 'yaml':
extensions.push(StreamLanguage.define(yaml));
break;
case 'json':
extensions.push(json());
break;
}
let startState = EditorState.create({
doc: content.value,
extensions: extensions,
});
editorView.value = new EditorView({
state: startState,
parent: editorRef.value,
});
};
watch(
() => content.value,
(newValue) => {
if (editorView.value) {
if (newValue === editorView.value.state.doc.toString()) {
return;
}
editorView.value.dispatch({
changes: {
from: 0,
to: editorView.value.state.doc.length,
insert: newValue,
},
scrollIntoView: false,
});
} else {
initCodeMirror();
}
},
{ immediate: true },
);
onMounted(() => {
initCodeMirror();
});
onUnmounted(() => {
editorView.value?.destroy();
});
</script>
+34 -29
View File
@@ -38,48 +38,37 @@
</el-button>
</div>
<codemirror
:autofocus="true"
:placeholder="$t('commons.msg.noneData')"
:indent-with-tab="true"
:tabSize="4"
style="margin-top: 10px; height: calc(100vh - 200px)"
:lineWrapping="true"
:matchBrackets="true"
theme="cobalt"
:styleActiveLine="true"
:extensions="extensions"
@ready="handleReady"
v-model="logInfo"
:disabled="true"
/>
<div class="mt-2.5">
<highlightjs
v-if="showLog"
ref="editorRef"
class="editor-main"
language="JavaScript"
:autodetect="false"
:code="logInfo"
></highlightjs>
</div>
</el-drawer>
</template>
<script lang="ts" setup>
import i18n from '@/lang';
import { dateFormatForName } from '@/utils/util';
import { computed, onBeforeUnmount, reactive, ref, shallowRef, watch } from 'vue';
import { Codemirror } from 'vue-codemirror';
import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from '@codemirror/theme-one-dark';
import { computed, onBeforeUnmount, reactive, ref, watch } from 'vue';
import { MsgError } from '@/utils/message';
import { GlobalStore } from '@/store';
import screenfull from 'screenfull';
import { DownloadFile } from '@/api/modules/container';
const extensions = [javascript(), oneDark];
const logInfo = ref();
const view = shallowRef();
const handleReady = (payload) => {
view.value = payload.view;
};
const terminalSocket = ref<WebSocket>();
const open = ref(false);
const resource = ref('');
const globalStore = GlobalStore();
const logVisible = ref(false);
const showLog = ref(false);
const editorRef = ref();
const scrollerElement = ref<HTMLElement | null>(null);
const mobile = computed(() => {
return globalStore.isMobile();
@@ -146,10 +135,8 @@ const searchLogs = async () => {
);
terminalSocket.value.onmessage = (event) => {
logInfo.value += event.data;
const state = view.value.state;
view.value.dispatch({
selection: { anchor: state.doc.length, head: state.doc.length },
scrollIntoView: true,
nextTick(() => {
scrollerElement.value.scrollTop = scrollerElement.value.scrollHeight;
});
};
};
@@ -208,6 +195,17 @@ onBeforeUnmount(() => {
handleClose();
});
onMounted(async () => {
await nextTick(() => {
showLog.value = true;
});
if (editorRef.value) {
scrollerElement.value = editorRef.value.$el as HTMLElement;
let hljsDom = scrollerElement.value.querySelector('.hljs') as HTMLElement;
hljsDom.style['min-height'] = '500px';
}
});
defineExpose({
acceptParams,
});
@@ -217,4 +215,11 @@ defineExpose({
.selectWidth {
width: 200px;
}
.editor-main {
height: calc(100vh - 100px);
width: 100%;
min-height: 600px;
overflow: auto;
}
</style>
+33 -29
View File
@@ -26,21 +26,16 @@
</el-button>
</div>
<codemirror
:autofocus="true"
:placeholder="$t('commons.msg.noneData')"
:indent-with-tab="true"
:tabSize="4"
:style="{ height: `calc(100vh - ${loadHeight()})`, 'margin-top': '10px' }"
:lineWrapping="true"
:matchBrackets="true"
theme="cobalt"
:styleActiveLine="true"
:extensions="extensions"
@ready="handleReady"
v-model="logInfo"
:disabled="true"
/>
<div :style="{ height: `calc(100vh - ${loadHeight()})`, 'margin-top': '10px', 'min-height': '400px' }">
<highlightjs
v-if="showLog"
ref="editorRef"
class="editor-main"
language="JavaScript"
:autodetect="false"
:code="logInfo"
></highlightjs>
</div>
</div>
</template>
@@ -48,22 +43,16 @@
import { cleanContainerLog } from '@/api/modules/container';
import i18n from '@/lang';
import { dateFormatForName, downloadWithContent } from '@/utils/util';
import { onBeforeUnmount, reactive, ref, shallowRef } from 'vue';
import { Codemirror } from 'vue-codemirror';
import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from '@codemirror/theme-one-dark';
import { onBeforeUnmount, reactive, ref } from 'vue';
import { MsgError, MsgSuccess } from '@/utils/message';
import { GlobalStore } from '@/store';
const globalStore = GlobalStore();
const extensions = [javascript(), oneDark];
const logInfo = ref();
const view = shallowRef();
const handleReady = (payload) => {
view.value = payload.view;
};
const terminalSocket = ref<WebSocket>();
const editorRef = ref();
const scrollerElement = ref<HTMLElement | null>(null);
const showLog = ref(false);
const loadHeight = () => {
return globalStore.openMenuTabs ? '405px' : '375px';
@@ -113,10 +102,8 @@ const searchLogs = async () => {
);
terminalSocket.value.onmessage = (event) => {
logInfo.value += event.data;
const state = view.value.state;
view.value.dispatch({
selection: { anchor: state.doc.length, head: state.doc.length },
scrollIntoView: true,
nextTick(() => {
scrollerElement.value.scrollTop = scrollerElement.value.scrollHeight;
});
};
};
@@ -165,6 +152,17 @@ onBeforeUnmount(() => {
terminalSocket.value?.send('close conn');
});
onMounted(async () => {
await nextTick(() => {
showLog.value = true;
});
if (editorRef.value) {
scrollerElement.value = editorRef.value.$el as HTMLElement;
let hljsDom = scrollerElement.value.querySelector('.hljs') as HTMLElement;
hljsDom.style['min-height'] = '500px';
}
});
defineExpose({
acceptParams,
});
@@ -177,4 +175,10 @@ defineExpose({
.selectWidth {
width: 150px;
}
.editor-main {
height: calc(100vh - 480px);
width: 100%;
min-height: 600px;
overflow: auto;
}
</style>