feat(editor): 代码编辑器支持配置自动高度

This commit is contained in:
roymondchen 2025-12-03 17:50:48 +08:00
parent 8d7c8fa725
commit 8d55d0cd8d
8 changed files with 92 additions and 12 deletions

View File

@ -201,7 +201,7 @@ const functionConfig = computed<FormConfig>(() => [
name: 'content', name: 'content',
type: 'vs-code', type: 'vs-code',
options: inject('codeOptions', {}), options: inject('codeOptions', {}),
height: '500px', autosize: { minRows: 10, maxRows: 30 },
onChange: (formState: FormState | undefined, code: string) => { onChange: (formState: FormState | undefined, code: string) => {
try { try {
// js // js

View File

@ -7,6 +7,7 @@
...config.options, ...config.options,
readOnly: disabled, readOnly: disabled,
}" }"
:autosize="config.autosize"
:parse="config.parse" :parse="config.parse"
@save="save" @save="save"
></MagicCodeEditor> ></MagicCodeEditor>

View File

@ -233,7 +233,6 @@ const dataSourceFieldsConfig: FormConfig = [
{ {
name: 'defaultValue', name: 'defaultValue',
text: '默认值', text: '默认值',
height: '200px',
parse: true, parse: true,
type: (mForm: FormState | undefined, { model }: any) => { type: (mForm: FormState | undefined, { model }: any) => {
if (model.type === 'number') return 'number'; if (model.type === 'number') return 'number';
@ -242,6 +241,7 @@ const dataSourceFieldsConfig: FormConfig = [
return 'vs-code'; return 'vs-code';
}, },
autosize: { minRows: 1, maxRows: 30 },
options: [ options: [
{ text: 'true', value: true }, { text: 'true', value: true },
{ text: 'false', value: false }, { text: 'false', value: false },
@ -267,7 +267,7 @@ const jsonFromConfig: FormConfig = [
type: 'vs-code', type: 'vs-code',
labelWidth: '0', labelWidth: '0',
language: 'json', language: 'json',
height: '600px', autosize: { minRows: 30, maxRows: 50 },
options: inject('codeOptions', {}), options: inject('codeOptions', {}),
}, },
]; ];

View File

@ -96,7 +96,7 @@ const formConfig: FormConfig = [
language: 'json', language: 'json',
options: inject('codeOptions', {}), options: inject('codeOptions', {}),
defaultValue: '{}', defaultValue: '{}',
height: '400px', autosize: { minRows: 30, maxRows: 50 },
onChange: (formState: FormState | undefined, v: string | any) => { onChange: (formState: FormState | undefined, v: string | any) => {
if (typeof v !== 'string') return v; if (typeof v !== 'string') return v;
return JSON.parse(v); return JSON.parse(v);

View File

@ -37,12 +37,15 @@
<MagicCodeEditor <MagicCodeEditor
v-if="config.advanced && showCode" v-if="config.advanced && showCode"
height="200px"
:init-values="model[name]" :init-values="model[name]"
language="javascript" language="javascript"
:options="{ :options="{
readOnly: disabled, readOnly: disabled,
}" }"
:autosize="{
minRows: 1,
maxRows: 20,
}"
:parse="true" :parse="true"
@save="save" @save="save"
></MagicCodeEditor> ></MagicCodeEditor>

View File

@ -1,10 +1,7 @@
<template> <template>
<div :class="`magic-code-editor`"> <div :class="`magic-code-editor`">
<Teleport to="body" :disabled="!fullScreen"> <Teleport to="body" :disabled="!fullScreen">
<div <div :class="{ 'magic-code-editor-wrapper': true, 'full-screen': fullScreen }" :style="{ height: computeHeight }">
:class="`magic-code-editor-wrapper${fullScreen ? ' full-screen' : ''}`"
:style="!fullScreen && height ? `height: ${height}` : '100%'"
>
<TMagicButton <TMagicButton
v-if="!disabledFullScreen" v-if="!disabledFullScreen"
class="magic-code-editor-full-screen-icon" class="magic-code-editor-full-screen-icon"
@ -20,7 +17,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onBeforeUnmount, onMounted, onUnmounted, ref, useTemplateRef, watch } from 'vue'; import { computed, nextTick, onBeforeUnmount, onMounted, onUnmounted, ref, useTemplateRef, watch } from 'vue';
import { FullScreen } from '@element-plus/icons-vue'; import { FullScreen } from '@element-plus/icons-vue';
import { throttle } from 'lodash-es'; import { throttle } from 'lodash-es';
import serialize from 'serialize-javascript'; import serialize from 'serialize-javascript';
@ -46,6 +43,10 @@ const props = withDefaults(
autoSave?: boolean; autoSave?: boolean;
parse?: boolean; parse?: boolean;
disabledFullScreen?: boolean; disabledFullScreen?: boolean;
autosize?: {
minRows?: number;
maxRows?: number;
};
}>(), }>(),
{ {
initValues: '', initValues: '',
@ -61,6 +62,58 @@ const props = withDefaults(
const emit = defineEmits(['initd', 'save']); const emit = defineEmits(['initd', 'save']);
const autoHeight = ref<string>('');
const computeHeight = computed(() => {
if (fullScreen.value) {
return '100%';
}
if (props.height) {
return props.height;
}
if (props.autosize) {
return autoHeight.value;
}
return '100%';
});
const setAutoHeight = (v = '') => {
let lines = Math.max(v.split('\n').length, props.autosize?.minRows || 1);
if (v) {
if (props.autosize?.maxRows) {
lines = Math.min(lines, props.autosize.maxRows);
}
}
// 使
let lineHeight = 20;
if (vsEditor) {
const editorOptions = vsEditor.getOptions();
lineHeight = editorOptions.get(monaco.editor.EditorOption.lineHeight) || 20;
}
const newHeight = `${lines * lineHeight + 10}px`;
//
if (autoHeight.value !== newHeight) {
autoHeight.value = newHeight;
//
nextTick(() => {
vsEditor?.layout();
//
if (vsEditor) {
vsEditor.setScrollTop(0);
vsEditor.revealLine(1);
}
});
}
};
const toString = (v: string | any, language: string): string => { const toString = (v: string | any, language: string): string => {
let value: string; let value: string;
if (typeof v !== 'string') { if (typeof v !== 'string') {
@ -110,6 +163,8 @@ const resizeObserver = new globalThis.ResizeObserver(
const setEditorValue = (v: string | any, m: string | any) => { const setEditorValue = (v: string | any, m: string | any) => {
values.value = toString(v, props.language.toLocaleLowerCase()); values.value = toString(v, props.language.toLocaleLowerCase());
setAutoHeight(values.value);
if (props.type === 'diff') { if (props.type === 'diff') {
const originalModel = monaco.editor.createModel(values.value, 'text/javascript'); const originalModel = monaco.editor.createModel(values.value, 'text/javascript');
const modifiedModel = monaco.editor.createModel(toString(m, props.language), 'text/javascript'); const modifiedModel = monaco.editor.createModel(toString(m, props.language), 'text/javascript');
@ -147,6 +202,7 @@ const handleKeyDown = (e: KeyboardEvent) => {
emit('save', props.parse ? parseCode(newValue, props.language) : newValue); emit('save', props.parse ? parseCode(newValue, props.language) : newValue);
} }
}; };
const init = async () => { const init = async () => {
if (!codeEditorEl.value) return; if (!codeEditorEl.value) return;
@ -163,8 +219,24 @@ const init = async () => {
if (props.type === 'diff') { if (props.type === 'diff') {
vsDiffEditor = getEditorConfig('customCreateMonacoDiffEditor')(monaco, codeEditorEl.value, options); vsDiffEditor = getEditorConfig('customCreateMonacoDiffEditor')(monaco, codeEditorEl.value, options);
// diff
vsDiffEditor.getModifiedEditor().onDidChangeModelContent(() => {
// 使 autosize
if (props.autosize) {
setAutoHeight(getEditorValue());
}
});
} else { } else {
vsEditor = getEditorConfig('customCreateMonacoEditor')(monaco, codeEditorEl.value, options); vsEditor = getEditorConfig('customCreateMonacoEditor')(monaco, codeEditorEl.value, options);
//
vsEditor.onDidChangeModelContent(() => {
// 使 autosize
if (props.autosize) {
setAutoHeight(getEditorValue());
}
});
} }
setEditorValue(props.initValues, props.modifiedValues); setEditorValue(props.initValues, props.modifiedValues);

View File

@ -64,7 +64,7 @@ const fillConfig = (config: FormConfig): FormConfig => [
name: 'beforeRequest', name: 'beforeRequest',
type: 'vs-code', type: 'vs-code',
parse: true, parse: true,
height: '600px', autosize: { minRows: 10, maxRows: 30 },
}, },
], ],
}, },
@ -76,7 +76,7 @@ const fillConfig = (config: FormConfig): FormConfig => [
name: 'afterResponse', name: 'afterResponse',
type: 'vs-code', type: 'vs-code',
parse: true, parse: true,
height: '600px', autosize: { minRows: 10, maxRows: 30 },
}, },
], ],
}, },

View File

@ -39,6 +39,10 @@ export interface CodeConfig extends FormItem {
}; };
height?: string; height?: string;
parse?: boolean; parse?: boolean;
autosize?: {
minRows?: number;
maxRows?: number;
};
} }
export interface CodeLinkConfig extends FormItem { export interface CodeLinkConfig extends FormItem {