ref: 重构codemirror编辑器组件
This commit is contained in:
@@ -1,70 +1,117 @@
|
||||
<template>
|
||||
<Codemirror
|
||||
:model-value="modelValue"
|
||||
:placeholder="props.placeholder"
|
||||
:style="props.editorStyle"
|
||||
:disabled="props.disabled"
|
||||
:autofocus="false"
|
||||
:indent-with-tab="true"
|
||||
:tab-size="props.tabSize"
|
||||
:extensions="[javascript(), oneDark]"
|
||||
@ready="fnReady"
|
||||
@change="fnChange"
|
||||
/>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { Codemirror } from 'vue-codemirror';
|
||||
import { javascript } from '@codemirror/lang-javascript';
|
||||
import { yaml } from '@codemirror/lang-yaml';
|
||||
import { oneDark } from '@codemirror/theme-one-dark';
|
||||
import { ref, watch } from 'vue';
|
||||
|
||||
const emit = defineEmits(['update:value']);
|
||||
import { basicSetup, EditorView } from 'codemirror';
|
||||
import { indentWithTab } from '@codemirror/commands';
|
||||
import { keymap } from '@codemirror/view';
|
||||
import { EditorState } from '@codemirror/state';
|
||||
import { ref, watch, onMounted, onBeforeUnmount } from 'vue';
|
||||
const emit = defineEmits(['update:value', 'change']);
|
||||
const props = defineProps({
|
||||
/**禁用输入使用v-model:value时不生效 */
|
||||
value: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
placeholder: {
|
||||
/**编辑框高度 */
|
||||
height: {
|
||||
type: String,
|
||||
default: 'input context here...',
|
||||
},
|
||||
/**是否禁止输入 */
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
editorStyle: {
|
||||
type: Object,
|
||||
default: () => ({ height: '400px !important' }),
|
||||
default: '400px',
|
||||
},
|
||||
/**缩进2空格 */
|
||||
tabSize: {
|
||||
type: Number,
|
||||
default: 2,
|
||||
},
|
||||
/**是否禁止输入 */
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
/**高亮语言 支持javascript、yaml */
|
||||
lang: {
|
||||
type: String,
|
||||
default: 'javascript',
|
||||
},
|
||||
});
|
||||
|
||||
// 绑定值
|
||||
const modelValue = ref<string>('');
|
||||
/**视图容器 */
|
||||
const viewContainerDom = ref<HTMLElement | undefined>(undefined);
|
||||
let viewContainer: EditorView | null = null;
|
||||
|
||||
/**变更时更新绑定值 */
|
||||
function fnChange(value: string, viewUpdate: any) {
|
||||
if (props.disabled) return;
|
||||
emit('update:value', value);
|
||||
/**高亮语言拓展 */
|
||||
function fnLangExtension() {
|
||||
if (props.lang === 'yaml') {
|
||||
return yaml();
|
||||
}
|
||||
return javascript();
|
||||
}
|
||||
|
||||
/**组件渲染后 */
|
||||
function fnReady(payload: any) {
|
||||
modelValue.value = props.value;
|
||||
/**初始化渲染视图 */
|
||||
function handleRanderView(container: HTMLElement | undefined) {
|
||||
if (!container) return;
|
||||
viewContainer = new EditorView({
|
||||
doc: props.value,
|
||||
extensions: [
|
||||
oneDark,
|
||||
basicSetup,
|
||||
keymap.of([indentWithTab]),
|
||||
fnLangExtension(),
|
||||
EditorView.editable.of(!props.disabled),
|
||||
EditorState.readOnly.of(props.disabled),
|
||||
EditorState.tabSize.of(props.tabSize),
|
||||
EditorView.updateListener.of(v => {
|
||||
if (v.docChanged) {
|
||||
const docStr = v.state.doc.toString();
|
||||
emit('change', docStr, v.state.doc);
|
||||
// 禁用时不双向绑定,防止监听重复变化数值
|
||||
if (!props.disabled) {
|
||||
emit('update:value', docStr);
|
||||
}
|
||||
}
|
||||
}),
|
||||
],
|
||||
parent: container,
|
||||
});
|
||||
}
|
||||
|
||||
/**监听是否value改变 */
|
||||
watch(
|
||||
() => props.value,
|
||||
val => {
|
||||
modelValue.value = val;
|
||||
// 禁用时无输入靠外部值变化改变数值
|
||||
if (props.disabled && viewContainer) {
|
||||
const docLine = viewContainer.state.doc.length;
|
||||
viewContainer.dispatch({
|
||||
changes: { from: 0, to: docLine, insert: val },
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
handleRanderView(viewContainerDom.value);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
viewContainer?.destroy();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
<template>
|
||||
<div
|
||||
ref="viewContainerDom"
|
||||
class="container"
|
||||
:style="{ '--editor-height': height }"
|
||||
></div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.container {
|
||||
--editor-height: 400px;
|
||||
}
|
||||
.container :deep(.cm-editor) {
|
||||
height: var(--editor-height);
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user