基於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元素標簽時,自動補齊右側閉合標簽、或者輸入完 </ 時,自動補齊閉合標簽
使用場景舉例:輸入完<book>時自動補齊右側</book>
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);
...略
編輯器組件實現
<template>
<codemirror
ref="myCm"
:value="editorValue"
:options="cmOptions"
@changes="onCmCodeChanges"
@blur="onCmBlur"
@keydown.native="onKeyDown"
@mousedown.native="onMouseDown"
@paste.native="OnPaste"
></codemirror>
</template>
<script>
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);
}
}
};
</script>
<style>
.CodeMirror-selected {
background-color: blue !important;
}
.CodeMirror-selectedtext {
color: white !important;
}
.cm-matchhighlight {
background-color: #ae00ae;
}
</style>
引用編輯器組件
<template>
<div class="code-mirror-div">
<div class="tool-bar">
<span>請選擇主題</span>
<el-select v-model="cmTheme" placeholder="請選擇" size="small" style="width:150px">
<el-option v-for="item in cmThemeOptions" :key="item" :label="item" :value="item"></el-option>
</el-select>
<span style="margin-left: 10px">請選擇編輯模式</span>
<el-select
v-model="cmEditorMode"
placeholder="請選擇"
size="small"
style="width:150px"
@change="onEditorModeChange"
>
<el-option
v-for="item in cmEditorModeOptions"
:key="item"
:label="item"
:value="item"
></el-option>
</el-select>
<el-button type="primary" size="small" style="margin-left:10x" @click="setStyle">修改樣式</el-button>
<el-button type="primary" size="small" style="margin-left:10x" @click="getValue">獲取內容</el-button>
<el-button type="primary" size="small" style="margin-left:10x" @click="setValue">修改內容</el-button>
</div>
<code-mirror-editor
ref="cmEditor"
:cmTheme="cmTheme"
:cmMode="cmMode"
:autoFormatJson="autoFormatJson"
:jsonIndentation="jsonIndentation"
></code-mirror-editor>
</div>
</template>
<script>
// 使用時需要根據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";
}
},
// 修改樣式(不推薦,建議參考<style>中的樣式,提前配置好樣式)
setStyle() {
let styleStr =
"position: absolute; top: 80px; left: 50px; right: 200px; bottom: 20px; padding: 2px; height: auto;";
this.$refs.cmEditor.setStyle(styleStr);
},
//獲取內容
getValue() {
let content = this.$refs.cmEditor.getValue();
console.log(content);
},
//修改內容
setValue() {
let jsonValue = {
name: "laiyu",
addr: "廣東省深圳市",
other: "nothing",
tel: "168888888",
intro: [{ item1: "item1" }]
};
this.$refs.cmEditor.setValue(JSON.stringify(jsonValue));
}
}
};
</script>
<style>
.CodeMirror {
position: absolute;
top: 80px;
left: 2px;
right: 5px;
bottom: 0px;
padding: 2px;
height: auto;
overflow-y: auto;
}
</style>
<style lang="scss" scoped>
.code-mirror-div {
position: absolute;
top: 0px;
left: 2px;
right: 5px;
bottom: 0px;
padding: 2px;
.tool-bar {
top: 20px;
margin: 30px 2px 0px 20px;
}
}
</style>
效果展示
- 1. 支持多主題
- 2. 代碼折疊/展開
- 3. 靜態代碼語法檢查
- 4. 查找內容
- 5. 批量替換內容
- 6. 跳轉到指定行
- 7. 自動補全提示
- 8. 自動匹配xml標簽
- 9. 自動匹配括號
- 10. 鼠標點擊高亮匹配單詞