mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-26 00:01:11 +00:00
feat: 统一 CodeMirror 组件 (#5716)
This commit is contained in:
committed by
zhengkunwang223
parent
ed2f5c42d0
commit
d91ce3da65
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user