ref: 重构codemirror编辑器组件

This commit is contained in:
TsMask
2024-04-01 15:58:29 +08:00
parent 00e8b7acbb
commit fbbd0496de
2 changed files with 184 additions and 122 deletions

View File

@@ -1,116 +1,131 @@
<template>
<a-modal
:title="props.title"
width="80%"
:visible="props.visible"
:body-style="{ padding: '0 24px' }"
:destroy-on-close="true"
@cancel="fnCronModal(false)"
@ok="fnCronModal(true)"
>
<div ref="mergeViewContainer" class="mergeViewContainer"></div>
</a-modal>
</template>
<script lang="ts" setup>
import { javascript } from '@codemirror/lang-javascript';
import { yaml } from '@codemirror/lang-yaml';
import { oneDark } from '@codemirror/theme-one-dark';
import { MergeView } from '@codemirror/merge';
import { EditorView, basicSetup } from 'codemirror';
import { EditorState } from '@codemirror/state';
import { watch, ref } from 'vue';
import { nextTick } from 'vue';
const emit = defineEmits(['cancel', 'ok', 'update:visible']);
import { ref, onMounted, onBeforeUnmount, watch } from 'vue';
const emit = defineEmits(['update:newArea', 'change']);
const props = defineProps({
visible: {
type: Boolean,
required: true,
},
title: {
type: String,
default: '内容比较',
},
newArea: {
type: String,
default: '当前内容',
},
oldArea: {
type: String,
default: '原始内容',
},
/**当前变更内容 */
newArea: {
type: String,
default: '当前内容',
},
/**编辑框高度 */
height: {
type: String,
default: '400px !important',
default: '400px',
},
/**缩进2空格 */
tabSize: {
type: Number,
default: 2,
},
/**是否禁止输入 */
disabled: {
type: Boolean,
default: true,
},
/**高亮语言 */
lang: {
type: String,
default: 'javascript',
},
});
const mergeViewContainer = ref();
/**视图容器 */
const viewContainerDom = ref<HTMLElement | undefined>(undefined);
let viewContainer: MergeView | null = null;
/**监听是否显示初始cron属性 */
/**高亮语言拓展 */
function fnLangExtension() {
if (props.lang === 'yaml') {
return yaml();
}
return javascript();
}
/**初始化渲染视图 */
function handleRanderView(container: HTMLElement | undefined) {
if (!container) return;
viewContainer = new MergeView({
a: {
doc: props.oldArea,
extensions: [
fnLangExtension(),
oneDark,
basicSetup,
EditorView.editable.of(false),
EditorState.readOnly.of(true),
],
},
b: {
doc: props.newArea,
extensions: [
fnLangExtension(),
oneDark,
basicSetup,
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:newArea', docStr);
}
}
}),
],
},
parent: container,
});
}
/**监听是否value改变 */
watch(
() => props.visible,
() => props.newArea,
val => {
if (val) {
// 开启时等待dom完成
nextTick(() => {
// 设置高度
mergeViewContainer.value.style.height = props.height;
// 实例到dom
new MergeView({
a: {
doc: props.oldArea,
extensions: [
javascript(),
oneDark,
basicSetup,
EditorView.editable.of(false),
EditorState.readOnly.of(true),
],
},
b: {
doc: props.newArea,
extensions: [
javascript(),
oneDark,
basicSetup,
EditorView.editable.of(!props.disabled),
EditorState.readOnly.of(props.disabled),
],
},
parent: mergeViewContainer.value,
});
// 禁用时无输入靠外部值变化改变数值
if (props.disabled && viewContainer) {
const docLine = viewContainer.b.state.doc.length;
viewContainer.b.dispatch({
changes: { from: 0, to: docLine, insert: val },
});
}
}
);
/**
* 窗口事件
* @param val modal触发事件
*/
function fnCronModal(val: boolean) {
emit('update:visible', false);
if (val) {
emit('ok', true);
} else {
emit('cancel');
}
}
onMounted(() => {
handleRanderView(viewContainerDom.value);
});
onBeforeUnmount(() => {
viewContainer?.destroy();
});
</script>
<template>
<div
ref="viewContainerDom"
class="container"
:style="{ '--editor-height': height }"
></div>
</template>
<style lang="less" scoped>
.mergeViewContainer {
height: 400px;
overflow-y: scroll;
overflow-x: hidden;
color: #abb2bf;
background-color: #282c34;
.container {
--editor-height: 400px;
}
.container :deep(.cm-editor) {
height: var(--editor-height);
}
</style>