CodeMirror编辑器使用方式ts+vue3

1.安装插件

"@codemirror/view": "^6.9.0",
"vue-codemirror6": "^1.1.11",
"codemirror": "^6.0.1",

2.vue页面代码

<div>
    <code-mirror 
    :extensions="extensions" 
    :wrap="true" 
    ref="mirrorRef" 
    placeholder="例如:SUM(数量)" 
    class="code-content"
    @keydown="handleCursorActivity" 
    @ready="onReady">
      <pre></pre>
    </code-mirror>
  </div>

2.js代码

<script setup lang="ts">
import { ref, type Ref } from "vue";
import CodeMirror from "vue-codemirror6"; 
import { Decoration, type DecorationSet, EditorView, 
ViewPlugin,  ViewUpdate, MatchDecorator,  WidgetType } from  "@codemirror/view"; 
// showLabelWidget父级传过来的方法并带有返回值,用于回显成文字
// regexp 用于正则替换的
const props = defineProps({
  showLabelWidget:{
    type: Function,
    required: true
  },
  regexp:{
    type: RegExp,
    default:/\{([\w.]+)\}/g
  }
})
const editView = ref<any>();
const mirrorRef = ref(); 
// 删除自定义的WidgetType
const handleCursorActivity = (cm:any)=>{
  if(cm.key == 'Backspace'){  
    let to = mirrorRef.value.getCursor()
    let newContent = mirrorRef.value.getRange()
    let from = 0
    if(newContent.charAt(to-1) == '}'){
      from = newContent.lastIndexOf('{',to-1)
      mirrorRef.value.replaceRange('',from+1,to)
    }
  }
}

// 加载
const onReady = ({ view }: { view: Ref<EditorView> })=>{
  editView.value = view;
}

// 设置内容
const setResult = (value:String)=>{
  mirrorRef.value.replaceSelection(value)
}

// 获取结果
const getResult = () => {
  return mirrorRef.value.getRange(); 
}

// 插入数据  
// focusIndex代表插入数据后光标所在的位置,例如(|)光标在括号中间则需要传1
const insertInfo = (info: any, focusIndex = 0) => {
  mirrorRef.value.replaceSelection(info);
  if(focusIndex>0){
    let cursor = mirrorRef.value.getCursor()
    mirrorRef.value.setSelection(cursor - 1);
  }
  editView.value?.focus(); 
}

defineExpose({ getResult, setResult, insertInfo })

// 格式化代码{}字符串转中文
class PlaceholderWidget extends WidgetType {
	name: string;
	constructor(name: string) {
  	super();
	  this.name = name;
	}
	eq(other: PlaceholderWidget) {
	  return this.name == other.name;
	}
	toDOM() {
	  let elt = document.createElement("span");
	  elt.style.cssText = ` 
	    margin:0 4px 8px 4px;
	    display: inline-block;
	    padding: 0 6px;
	    font-size: 12px;
	    line-height: 28px; 
	    border: 1px solid #E5E6EB;
	    border-radius: 4px;
	    color: #1D2129;
	    background: #F2F3F5;`;
	  elt.textContent = this.name; 
	  return elt;
	}
	ignoreEvent() {  
	  // editView.value.removeWidget(e);
	  // 点击事件
	  return true
	}
}

const placeholderMatcher = new MatchDecorator({
regexp: props.regexp,
decoration: (match) => {
  let label = props.showLabelWidget(match[1])
  if(!label){
    label = ''
  }
  return Decoration.replace({
    widget: new PlaceholderWidget(label),
  });
},
});

const placeholders = ViewPlugin.fromClass(
class {
  placeholders: DecorationSet;
  constructor(view: EditorView) {
    this.placeholders = placeholderMatcher.createDeco(view);
  }
  update(update: ViewUpdate) {
    this.placeholders = placeholderMatcher.updateDeco(
      update,
      this.placeholders
    );
  }
},
{
  decorations: (instance:any) => instance.placeholders,
  provide: (plugin:any) =>
    EditorView.atomicRanges.of((view:any) => {
      return view.plugin(plugin)?.placeholders || Decoration.none;
    }),
});

const extensions = [ 
  placeholders,
];
</script>

3.css样式

<style lang="scss" scoped> 
.code-content{
  :deep(.cm-editor){
      border: 1px solid #E5E6EB;
      height: 200px;
      background-color: #FBFBFB;
      &.cm-focused{
          outline: none;
      } 
  }
}
</style>

使用以上方式封装一个组件,然后直接调用使用即可

你可能感兴趣的:(编辑器,前端)