效果图
安装codemirror依赖
本示例为Vue2项目,安装低版本的依赖
npm i codemirror@5.65.12
npm i vue-codemirror@4.0.6
实现
实现代码如下,里边涉及到的变量和函数自行替换即可,没有其他复杂逻辑。
<template>
<div class="picker">
<div class="code-edit">
<div class="top-title">公式</div>
<codemirror
ref="codeEditor"
v-model="formulaStr"
:options="cmOptions"
@input="codeMirrorChange"
></codemirror>
</div>
<el-button
size="mini"
icon="el-icon-setting"
@click="insertContent('表单4', 'variable')"
>添加变量
</el-button
>
<el-button
size="mini"
icon="el-icon-setting"
@click="insertContent('SUM', 'func')"
>添加函数
</el-button
>
</div>
</template>
<script>
import {codemirror} from "vue-codemirror";
import "codemirror/lib/codemirror.css";
import "codemirror/theme/idea.css";
export default {
components: {codemirror},
data() {
return {
cmOptions: {
// 语言及语法模式
mode: 'text/javascript',
// 主题
theme: "idea",
// 显示函数
line: true,
lineNumbers: false,
// 软换行
lineWrapping: true,
// tab宽度
tabSize: 4,
},
lang: 'javascript',
formulaStr: "表单 表单1 * 表单11*表单12+SUM(1,2) AVG(99,21) IF()",
};
},
computed: {
editor() {
return this.$refs.codeEditor.codemirror;
}
},
mounted() {
this.focus(this.formulaStr)
this.autoMarkText()
},
methods: {
codeMirrorChange() {
//获取 editor 的内容
console.log("content1: " + this.formulaStr);
console.log("content2: " + JSON.stringify(this.editor.getValue()));
},
addFormula(content, type) {
this.insertContent(content, type)
},
/**
* editor 中的对内容进行处理
* @param value
* @param type variable | func,variable为表单变量,需标记,func 为函数,也需要做标记
*/
insertContent(value, type) {
const from = this.editor.getCursor();
if (type === 'variable') {
this.editor.replaceSelection(value);
const to = this.editor.getCursor();
this.markText(from, to, value, 'cm-field');
} else if (type === 'func') {
this.editor.replaceSelection(`${value}()`);
const to = this.editor.getCursor();
this.markText(from, {line: to.line, ch: to.ch - 2}, value, 'cm-func');
this.editor.setCursor({line: to.line, ch: to.ch - 1});
} else if (typeof value === 'string') {
this.editor.replaceSelection(value);
}
this.editor.focus();
},
autoMarkText() {
if (this.formulaStr) {
this.autoMark(this.formulaStr);
this.focus(this.formulaStr);
}
},
focus(value) {
this.editor.setCursor({
line: 0,
ch: value ? value.length : 0
});
this.editor.focus()
},
markText(from, to, label, className) {
if (className === void 0) {
className = "cm-func";
}
let text = document.createElement("span");
text.className = className;
text.innerText = label;
this.editor.markText(from, to, {
atomic: true,
replacedWith: text,
});
},
/**
* 解析 editor 的内容,分别对表单变量和函数进行标记
*/
autoMark() {
const editor = this.editor;
const lines = editor.lineCount();
for (let line = 0; line < lines; line++) {
const content = editor.getLine(line);
// 标记函数调用,匹配一个或多个连续的大写字母,后面可以有任意数量的空白字符,再紧跟一个左括号
content.replace(/([A-Z]+)\s*\(/g, (_, func, pos) => {
this.markText({line: line, ch: pos}, {line: line, ch: pos + func.length}, func, 'cm-func');
return _;
});
// 标记表单变量,这应该是动态获取,自行替换即可
let vars = ["表单", "表单1", "表单11", "表单12"];
vars.forEach(v => {
let from = 0;
let idx = -1;
while (~(idx = content.indexOf(v, from))) {
this.markText({line: line, ch: idx}, {line: line, ch: idx + v.length}, v, 'cm-field');
from = idx + v.length;
}
});
}
},
},
};
</script>
<style lang="less" scoped>
.picker {
height: 525px;
text-align: left;
width: 50%;
margin: 0 auto;
.code-edit {
height: 240px;
border-radius: 6px;
border: 1px solid #e8e9eb;
}
}
.top-title {
background-color: #fafafa;
height: 30px;
vertical-align: center;
line-height: 30px;
padding-left: 10px;
border-radius: 4px 4px 0 0;
border-bottom: none;
}
/deep/ .CodeMirror {
height: 200px !important;
/*表单变量样式*/
.cm-field {
background: #007bff;
padding: 3px 5px;
border-radius: 3px;
color: #fff;
margin: 0 1px;
}
/*函数样式*/
.cm-func {
font-weight: bold;
color: #ae4597;
line-height: 14px;
margin: 0 1px;
padding: 0 2px;
}
.CodeMirror-scroll {
width: 100%;
}
}
</style>