一、简介
vue-codemirror是我最近项目中使用的一款代码在线编辑器,支持语言范围广、简单、易配置。
"vue-codemirror": "^4.0.6",
二、使用场景
第一种作为标签使用
第二种写sql也是没问题的,还支持配置关键字提示
三、安装方式(根据自己的使用习惯来,)
npm install -S vue-codemirror@4.0.6
或
yarn add vue-codemirror@4.0.6
四、项目中使用
首先引入以下两项
import { codemirror } from "vue-codemirror";
import "codemirror/lib/codemirror.css";
将codemirror在components中进行注册,然后在代码中使用
<codemirror
ref="codeEditor"
v-model="nameStr"
:options="cmOptions"
@input="codeMirrorChange"
></codemirror>
一部分配置项
cmOptions: {
// 语言及语法模式
mode: "text/x-sparksql",
// 主题
theme: "idea",
// 显示函数
line: true,
lineNumbers: false,
// 软换行
lineWrapping: true,
// tab宽度
tabSize: 4,
cursorHeight: 1, //光标高度,默认是1
},
五、整体代码
<template>
<div class="codeEditor">
<div class="picker">
<div class="ul-box">
<el-input
placeholder="请输入指标名称"
v-model="dataName"
clearable
@input="filterIndex"
@clear="filterIndex"
>
<i slot="prefix" class="el-input__icon el-icon-search"></i>
</el-input>
<ul>
<li
v-for="item in dataOpts"
:key="item.id"
@click="insertContent(item, 'variable')"
>
{{ item.name }}
</li>
</ul>
</div>
<div class="code-edit">
<div class="top-title">公式</div>
<codemirror
ref="codeEditor"
v-model="nameStr"
:options="cmOptions"
@input="codeMirrorChange"
></codemirror>
<div class="code-btn">
<el-button size="mini" @click="clearExp">清除 </el-button>
<el-button size="mini" @click="checkExp"
>公式检验<i v-if="checkStaus == 1" class="el-icon-loading"></i>
<i
v-if="checkStaus == 2"
:class="
checkFlag ? 'el-icon-circle-check' : 'el-icon-circle-close'
"
:title="checkFlag ? '检验成功' : '检验失败'"
></i>
</el-button>
</div>
</div>
</div>
<div class="editor-btn">
<el-button size="small" type="info" plain @click="cancelExp"
>取消
</el-button>
<el-button
size="small"
type="primary"
:disabled="!checkFlag"
@click="sureExp"
>确定
</el-button>
</div>
</div>
</template>
<script>
// import { listByPage, checkExpression } from "@/api/indexSet";
import { codemirror } from "vue-codemirror";
import "codemirror/lib/codemirror.css";
// import "codemirror/theme/idea.css";
export default {
name: "",
props: {
detailInfo: {
type: Object,
default: () => {
return {
// nameStr,
// codeStr,
};
},
},
},
components: {
codemirror,
},
data() {
return {
dataName: "",
cmOptions: {
// 语言及语法模式
mode: "text/x-sparksql",
// 主题
theme: "idea",
// 显示函数
line: true,
lineNumbers: false,
// 软换行
lineWrapping: true,
// tab宽度
tabSize: 4,
cursorHeight: 1, //光标高度,默认是1
},
lang: "x-sparksql",
nameStr: "",
codeStr: "",
codeList: [],
nameList: [],
dataOpts: [],
initDataOpts: [],
checkFlag: false,
checkStaus: "",
timer: null,
};
},
computed: {
editor() {
return this.$refs.codeEditor.codemirror;
},
},
watch: {
nameStr: {
handler(val) {
if (!val) {
this.codeStr = '';
this.codeList = [];
this.nameList = [];
return;
}
console.time("换算时间");
let str = val.match(/\[([\s\S]*?)\]/g);
let res = str.toString().replace(/\[/g, "").replace(/\]/g, "");
let arr = res.split(",");
let codeStr = val;
let codeList = [];
let nameList = [];
arr.forEach((e) => {
let obj = this.initDataOpts.find((o) => o.name === e);
codeList.push(obj.id);
nameList.push(obj.name);
codeStr = codeStr.replace(
new RegExp(`\\[${e}\\]`, "g"),
`[${obj.id}]`
);
});
this.codeStr = codeStr;
this.codeList = codeList;
this.nameList = nameList;
console.timeEnd("换算时间");
},
deep: true,
},
},
created() {},
async mounted() {
await this.getDataOpts();
this.focus(this.nameStr);
this.autoMarkText();
},
methods: {
filterIndex(val) {
if (this.timer) {
clearTimeout(this.timer);
}
this.timer = setTimeout(() => {
if(val) {
this.dataOpts = this.initDataOpts.filter((e) => e.name.includes(val));
} else {
this.dataOpts = JSON.parse(JSON.stringify(this.initDataOpts))
}
}, 500);
},
async getDataOpts() {
let res = await listByPage({ type: 1 }, 1, 1000);
this.dataOpts = res.data.listObject;
this.initDataOpts = res.data.listObject;
return true;
},
clearExp() {
this.nameStr = "";
},
async checkExp() {
this.checkStaus = 1;
if (!this.nameStr) {
setTimeout(() => {
this.checkStaus = 2;
this.checkFlag = false
}, 500);
return;
}
let data = {
expressionVO: {
nameStr: this.nameStr,
codeStr: this.codeStr,
codeList: this.codeList,
nameList: this.nameList,
},
ids: this.codeList.join(","),
};
try {
await checkExpression(data);
this.checkStaus = 2;
this.checkFlag = true;
} catch (error) {
this.checkStaus = 2;
this.checkFlag = false
}
},
cancelExp() {
this.checkStaus = ''
this.$emit("cancelExp");
},
sureExp() {
this.$emit(
"sureExp",
this.nameStr,
this.codeStr,
this.codeList,
this.nameList
);
this.checkStaus = ''
},
codeMirrorChange() {
//获取 editor 的内容
this.$nextTick(() => {
this.autoMark()
})
},
addFormula(content, type) {
this.insertContent(content, type);
},
/**
* editor 中的对内容进行处理
* @param item
* @param type variable | func,variable为表单变量,需标记,func 为函数,也需要做标记
*/
insertContent(item, type) {
const from = this.editor.getCursor();
if (type === "variable") {
this.editor.replaceSelection(`[${item.name}]`);
const to = this.editor.getCursor();
this.markText(from, to, `[${item.name}]`, "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() {
this.$nextTick(() => {
if (this.nameStr) {
this.autoMark(this.nameStr);
this.focus(this.nameStr);
}
});
},
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 _;
});
// 标记表单变量,这应该是动态获取,自行替换即可
this.nameList.forEach((v) => {
let from = 0;
let idx = -1;
while (~(idx = content.indexOf(v, from))) {
this.markText(
{ line: line, ch: idx - 1 },
{ line: line, ch: idx + v.length + 1 },
`[${v}]`,
"cm-field"
);
from = idx + v.length;
}
});
}
},
},
};
</script>
<style lang="less" scoped>
.codeEditor {
}
.picker {
// height: 525px;
text-align: left;
// width: 50%;
// margin: 0 auto;
display: flex;
.ul-box {
width: 300px;
height: 420px;
margin-right: 20px;
.el-input {
width: 100%;
margin-bottom: 15px;
// margin-top: 10px;
::v-deep .el-input__inner {
border-color: #dcdfe6 !important;
}
}
ul {
padding: 10px 0px;
border-radius: 5px;
border: 1px solid #efefef;
overflow: auto;
height: 369px;
}
li {
line-height: 28px;
padding: 0 20px;
cursor: pointer;
}
}
.code-edit {
flex: 1;
height: 420px;
border-radius: 6px;
border: 1px solid #e8e9eb;
::v-deep .CodeMirror {
height: 350px !important;
}
.code-btn {
margin-top: 5px;
text-align: right;
padding-right: 10px;
// border-top: 1px solid #efefef;
::v-deep .el-icon-circle-close{
color: rgb(241, 67, 67);
margin-left: 5px;
}
}
}
}
.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 {
/*表单变量样式*/
.cm-field {
// background: #007bff;
// padding: 3px 5px;
// border-radius: 3px;
color: #179e34;
margin: 0 1px;
}
/*函数样式*/
.cm-func {
font-weight: bold;
color: #ae4597;
line-height: 14px;
margin: 0 1px;
padding: 0 2px;
}
.CodeMirror-scroll {
width: 100%;
}
}
.editor-btn {
margin-top: 15px;
text-align: right;
}
</style>
版权归原作者 一个不太专业的码农 所有, 如有侵权,请联系我们删除。