基于vue-codemirror实现的代码编辑器
开发环境
jshint 2.11.1
jsonlint 1.6.3
script-loader 0.7.2
vue 2.6.11
vue-codemirror 4.0.6
element-ui 2.13.1 (使用到element-ui message组件,提示错误消息,如果不想安装该组件,替换编辑器中的this.$message所在行函数代码即可)
功能介绍
1、 支持不同的代码编辑模式
目前仅支持支持json, sql, javascript,css,xml, html,yaml, markdown, python编辑模式,默认为 json
2、 支持使用不同主题
支持62种主题,默认为 blackboard
3、 支持API编程
目前支持修改样式,获取内容,修改编辑框内容值
4、 支持复制,黏贴,剪切,撤销等常见操作
5、 支持文件拖拽导入
支持鼠标拖拽文件到编辑框,编辑框自动展示被拖拽文件的内容(当然,不是所有文件都可以,比如word文件,.exe文件就不行)
6、 支持json格式化
1)json编辑模式下,鼠标失去焦点时自动格式化json字符串,支持定义开关该特性
2)支持自定义格式化化缩进,支持字符或数字,最大不超过10,默认缩进2个空格
3)json编辑模式下,黏贴json字符串到编辑框时,支持自动格式化编辑框内容
4)json编辑模式下,支持按Ctrl+Alt+L快捷键主动格式化当前json格式字符内容
7、 支持显示代码行号
8、 支持编辑时“智能”缩进
9、 支持代码折叠/展开
支持json, sql, javascript,css,xml, html,yaml, markdown, python等
10、 支持静态代码语法检查
目前仅支持支持 json,javascript
11、 支持批量替换
操作方法:
按Ctrl + Shift + r键,弹出框中输入要被替换的内容,回车,然后再次输入用于替换的内容,回车即可。
12、 支持快速搜索
操作方法:
按Ctrl + F,弹出框中输入要查找内容,回车
13、 支持跳转到指定行
操作方法:
按Alt + G 快捷键, 弹出快对话框中输入行号,回车即可
14、 支持鼠标点击高亮匹配单词
使用场景举例:鼠标点击某个单词,高亮其它区域和被点击单词相同的单词
15、 支持自动补全提示
目前仅支持 sql,javascript,html,python
备注:出现自动补全提示时,按tab键可自动补全
16、 支持自动补全括号,单、双引号
支持自动补全括号:(),[],{},单引号,双引号:'' ""
使用场景举例:输入 [ 时,自动显示为[],并且把光标定位在括号中间
17、 支持自动补全xml标签
支持输入完开放xml、html元素标签时,自动补齐右侧闭合标签、或者输入完 时,自动补齐闭合标签
使用场景举例:输入完
18、 支持自动匹配xml标签
xml、html编辑模式下,支持自动匹配标签
使用场景举例:鼠标点击时xml标签时(开放标签或闭合标签),自动高亮另一半标签
19、 支持自动匹配括号
使用场景举例:光标点击紧挨{、]括号左、右侧时,自动突出显示匹配的括号 }、]
20、 支持光标所在当前行背景高亮
21、 支持高亮选中内容
使用场景举例:按下鼠标左键,拖拽选择内容时,高亮被选中内容,文字反白
主要依赖安装
npm install jsonlint
npm install jshint
npm install script-loader
npm install vue-codemirror
npm install element-ui
src/main.js配置
添加以下带背景色的部分的配置
import Vue from "vue"
import ElementUI from "element-ui"
import "element-ui/lib/theme-chalk/index.css"
...略
// 引入jshint用于实现js自动补全提示
import jshint from "jshint";
window.JSHINT = jshint.JSHINT;
// 引入代码编辑器
import { codemirror } from "vue-codemirror";
import "codemirror/lib/codemirror.css";
import App from "./app/App"
...略
Vue.use(ElementUI);
Vue.use(codemirror);
...略
编辑器组件实现
ref="myCm"
:value="editorValue"
:options="cmOptions"
@changes="onCmCodeChanges"
@blur="onCmBlur"
@keydown.native="onKeyDown"
@mousedown.native="onMouseDown"
@paste.native="OnPaste"
>
import { codemirror } from "vue-codemirror";
import "codemirror/theme/blackboard.css";
import "codemirror/mode/javascript/javascript.js";
import "codemirror/mode/xml/xml.js";
import "codemirror/mode/htmlmixed/htmlmixed.js";
import "codemirror/mode/css/css.js";
import "codemirror/mode/yaml/yaml.js";
import "codemirror/mode/sql/sql.js";
import "codemirror/mode/python/python.js";
import "codemirror/mode/markdown/markdown.js";
import "codemirror/addon/hint/show-hint.css";
import "codemirror/addon/hint/show-hint.js";
import "codemirror/addon/hint/javascript-hint.js";
import "codemirror/addon/hint/xml-hint.js";
import "codemirror/addon/hint/css-hint.js";
import "codemirror/addon/hint/html-hint.js";
import "codemirror/addon/hint/sql-hint.js";
import "codemirror/addon/hint/anyword-hint.js";
import "codemirror/addon/lint/lint.css";
import "codemirror/addon/lint/lint.js";
import "codemirror/addon/lint/json-lint";
require("script-loader!jsonlint");
// import "codemirror/addon/lint/html-lint.js";
// import "codemirror/addon/lint/css-lint.js";
import "codemirror/addon/lint/javascript-lint.js";
import "codemirror/addon/fold/foldcode.js";
import "codemirror/addon/fold/foldgutter.js";
import "codemirror/addon/fold/foldgutter.css";
import "codemirror/addon/fold/brace-fold.js";
import "codemirror/addon/fold/xml-fold.js";
import "codemirror/addon/fold/comment-fold.js";
import "codemirror/addon/fold/markdown-fold.js";
import "codemirror/addon/fold/indent-fold.js";
import "codemirror/addon/edit/closebrackets.js";
import "codemirror/addon/edit/closetag.js";
import "codemirror/addon/edit/matchtags.js";
import "codemirror/addon/edit/matchbrackets.js";
import "codemirror/addon/selection/active-line.js";
import "codemirror/addon/search/jump-to-line.js";
import "codemirror/addon/dialog/dialog.js";
import "codemirror/addon/dialog/dialog.css";
import "codemirror/addon/search/searchcursor.js";
import "codemirror/addon/search/search.js";
import "codemirror/addon/display/autorefresh.js";
import "codemirror/addon/selection/mark-selection.js";
import "codemirror/addon/search/match-highlighter.js";
export default {
components: {
codemirror
},
props: ["cmTheme", "cmMode", "autoFormatJson", "jsonIndentation"],
data() {
return {
editorValue: "",
cmOptions: {
theme:
!this.cmTheme || this.cmTheme == "default"
? "blackboard"
: this.cmTheme,
mode:
!this.cmMode || this.cmMode == "default"
? "application/json"
: this.cmMode,
lineWrapping: true,
lineNumbers: true,
autofocus: true,
smartIndent: false,
autocorrect: true,
spellcheck: true,
extraKeys: {
Tab: "autocomplete",
"Ctrl-Alt-L": () => {
try {
if (
this.cmOptions.mode == "application/json" &&
this.editorValue
) {
this.editorValue = this.formatStrInJson(
this.editorValue
);
}
} catch (e) {
this.$message.error(
"格式化代码出错:" + e.toString()
);
}
}
},
lint: true,
gutters: [
"CodeMirror-lint-markers",
"CodeMirror-linenumbers",
"CodeMirror-foldgutter"
],
foldGutter: true,
autoCloseBrackets: true,
autoCloseTags: true,
matchTags: { bothTags: true },
matchBrackets: true,
styleActiveLine: true,
autoRefresh: true,
highlightSelectionMatches: {
minChars: 2,
style: "matchhighlight",
showToken: true
},
styleSelectedText: true,
enableAutoFormatJson:
this.autoFormatJson == null ? true : this.autoFormatJson,
defaultJsonIndentation:
!this.jsonIndentation ||
typeof this.jsonIndentation != typeof 1
? 2
: this.jsonIndentation
},
enableAutoFormatJson:
this.autoFormatJson == null ? true : this.autoFormatJson,
defaultJsonIndentation:
!this.jsonIndentation || typeof this.jsonIndentation != typeof 1
? 2
: this.jsonIndentation
};
},
watch: {
cmTheme: function(newValue, oldValue) {
try {
let theme =
this.cmTheme == "default" ? "blackboard" : this.cmTheme;
require("codemirror/theme/" + theme + ".css");
this.cmOptions.theme = theme;
this.resetLint();
} catch (e) {
this.$message.error("切换编辑器主题出错:" + e.toString());
}
},
cmMode: function(newValue, oldValue) {
this.$set(this.cmOptions, "mode", this.cmMode);
this.resetLint();
this.resetFoldGutter();
}
},
methods: {
resetLint() {
if (!this.$refs.myCm.codemirror.getValue()) {
this.$nextTick(() => {
this.$refs.myCm.codemirror.setOption("lint", false);
});
return;
}
this.$refs.myCm.codemirror.setOption("lint", false);
this.$nextTick(() => {
this.$refs.myCm.codemirror.setOption("lint", true);
});
},
resetFoldGutter() {
this.$refs.myCm.codemirror.setOption("foldGutter", false);
this.$nextTick(() => {
this.$refs.myCm.codemirror.setOption("foldGutter", true);
});
},
// 修改编辑框样式
setStyle(style) {
try {
this.$nextTick(() => {
let cm = this.$refs.myCm.$el.querySelector(".CodeMirror");
if (cm) {
cm.style.cssText = style;
} else {
this.$message.error(
"未找到编辑器元素,修改编辑器样式失败"
);
}
});
} catch (e) {
this.$message.error("修改编辑器样式失败:" + e.toString());
}
},
// 获取值
getValue() {
try {
return this.$refs.myCm.codemirror.getValue();
} catch (e) {
let errorInfo = e.toString();
this.$message.error("获取编辑框内容失败:" + errorInfo);
return errorInfo;
}
},
// 修改值
setValue(value) {
try {
if (typeof value != typeof "") {
this.$message.error(
"修改编辑框内容失败:编辑宽内容只能为字符串"
);
return;
}
if (this.cmOptions.mode == "application/json") {
this.editorValue = this.formatStrInJson(value);
} else {
this.editorValue = value;
}
} catch (e) {
this.$message.error("修改编辑框内容失败:" + e.toString());
}
},
// 黏贴事件处理函数
OnPaste(event) {
if (this.cmOptions.mode == "application/json") {
try {
this.editorValue = this.formatStrInJson(this.editorValue);
} catch (e) {
// 啥都不做
}
}
},
// 失去焦点时处理函数
onCmBlur(cm, event) {
try {
let editorValue = cm.getValue();
if (this.cmOptions.mode == "application/json" && editorValue) {
if (!this.enableAutoFormatJson) {
return;
}
this.editorValue = this.formatStrInJson(editorValue);
}
} catch (e) {
// 啥也不做
}
},
// 按下键盘事件处理函数
onKeyDown(event) {
const keyCode = event.keyCode || event.which || event.charCode;
const keyCombination =
event.ctrlKey || event.altKey || event.metaKey;
if (!keyCombination && keyCode > 64 && keyCode < 123) {
this.$refs.myCm.codemirror.showHint({ completeSingle: false });
}
},
// 按下鼠标时事件处理函数
onMouseDown(event) {
this.$refs.myCm.codemirror.closeHint();
},
onCmCodeChanges(cm, changes) {
this.editorValue = cm.getValue();
this.resetLint();
},
// 格式化字符串为json格式字符串
formatStrInJson(strValue) {
return JSON.stringify(
JSON.parse(strValue),
null,
this.defaultJsonIndentation
);
}
},
created() {
try {
if (!this.editorValue) {
this.cmOptions.lint = false;
return;
}
if (this.cmOptions.mode == "application/json") {
if (!this.enableAutoFormatJson) {
return;
}
this.editorValue = this.formatStrInJson(this.editorValue);
}
} catch (e) {
console.log("初始化codemirror出错:" + e);
// this.$message.error("初始化codemirror出错:" + e);
}
}
};
.CodeMirror-selected {
background-color: blue !important;
}
.CodeMirror-selectedtext {
color: white !important;
}
.cm-matchhighlight {
background-color: #ae00ae;
}
引用编辑器组件
请选择主题 请选择编辑模式 v-model="cmEditorMode" placeholder="请选择" size="small" style="width:150px" @change="onEditorModeChange" > v-for="item in cmEditorModeOptions" :key="item" :label="item" :value="item" > ref="cmEditor" :cmTheme="cmTheme" :cmMode="cmMode" :autoFormatJson="autoFormatJson" :jsonIndentation="jsonIndentation" >
// 使用时需要根据CodeMirrorEditor.vue的实际存放路径,调整from后面的组件路径,以便正确引用
import CodeMirrorEditor from "@/common/components/public/CodeMirrorEditor";
export default {
components: {
CodeMirrorEditor
},
data() {
return {
cmTheme: "default", // codeMirror主题
// codeMirror主题选项
cmThemeOptions: [
"default",
"3024-day",
"3024-night",
"abcdef",
"ambiance",
"ayu-dark",
"ayu-mirage",
"base16-dark",
"base16-light",
"bespin",
"blackboard",
"cobalt",
"colorforth",
"darcula",
"dracula",
"duotone-dark",
"duotone-light",
"eclipse",
"elegant",
"erlang-dark",
"gruvbox-dark",
"hopscotch",
"icecoder",
"idea",
"isotope",
"lesser-dark",
"liquibyte",
"lucario",
"material",
"material-darker",
"material-palenight",
"material-ocean",
"mbo",
"mdn-like",
"midnight",
"monokai",
"moxer",
"neat",
"neo",
"night",
"nord",
"oceanic-next",
"panda-syntax",
"paraiso-dark",
"paraiso-light",
"pastel-on-dark",
"railscasts",
"rubyblue",
"seti",
"shadowfox",
"solarized dark",
"solarized light",
"the-matrix",
"tomorrow-night-bright",
"tomorrow-night-eighties",
"ttcn",
"twilight",
"vibrant-ink",
"xq-dark",
"xq-light",
"yeti",
"yonce",
"zenburn"
],
cmEditorMode: "default", // 编辑模式
// 编辑模式选项
cmEditorModeOptions: [
"default",
"json",
"sql",
"javascript",
"css",
"xml",
"html",
"yaml",
"markdown",
"python"
],
cmMode: "application/json", //codeMirror模式
jsonIndentation: 2, // json编辑模式下,json格式化缩进 支持字符或数字,最大不超过10,默认缩进2个空格
autoFormatJson: true // json编辑模式下,输入框失去焦点时是否自动格式化,true 开启, false 关闭
};
},
methods: {
// 切换编辑模式事件处理函数
onEditorModeChange(value) {
switch (value) {
case "json":
this.cmMode = "application/json";
break;
case "sql":
this.cmMode = "sql";
break;
case "javascript":
this.cmMode = "javascript";
break;
case "xml":
this.cmMode = "xml";
break;
case "css":
this.cmMode = "css";
break;
case "html":
this.cmMode = "htmlmixed";
break;
case "yaml":
this.cmMode = "yaml";
break;
case "markdown":
this.cmMode = "markdown";
break;
case "python":
this.cmMode = "python";
break;
default:
this.cmMode = "application/json";
}
},
// 修改样式(不推荐,建议参考
.code-mirror-div {
position: absolute;
top: 0px;
left: 2px;
right: 5px;
bottom: 0px;
padding: 2px;
.tool-bar {
top: 20px;
margin: 30px 2px 0px 20px;
}
}
效果展示
- 1. 支持多主题
- 2. 代码折叠/展开
- 3. 静态代码语法检查
- 4. 查找内容
- 5. 批量替换内容
- 6. 跳转到指定行
- 7. 自动补全提示
- 8. 自动匹配xml标签
- 9. 自动匹配括号
- 10. 鼠标点击高亮匹配单词