在最近的一個(gè)項(xiàng)目中想要做一個(gè)sql在線編輯的編輯器,由于項(xiàng)目中vue已經(jīng)升級(jí)的到了vue3,使用codemirror 5總是會(huì)發(fā)生各種錯(cuò)誤.索性就直接使用codemirror 6.codemirror 6使用TypeScript編寫,與vue3的 結(jié)合相當(dāng)融洽.接下來看一下具體的實(shí)現(xiàn)過程
codemirror.vue
<template>
<div ref="codemirror" class="codemirror"></div>
</template>
<script lang="ts">
import { EditorView, keymap } from '@codemirror/view';
import { EditorState } from '@codemirror/state';
import { history, historyKeymap } from '@codemirror/history';
import { standardKeymap, insertTab } from '@codemirror/commands';
import { lineNumbers } from '@codemirror/gutter';
import { sql, MySQL } from '@codemirror/lang-sql';
import { oneDarkTheme, oneDarkHighlightStyle } from '@codemirror/theme-one-dark';
import { autocompletion } from '@codemirror/autocomplete';
import { ref, watch } from 'vue';
import { SqlType } from '/@/views/index';
import { useStore } from 'vuex';
// 數(shù)據(jù)庫類型, 高度, 重載.(監(jiān)聽stroe,destory后create)
//獲取props
export default {
props: {
sqlType: {
required: true,
type: String,
},
initHeight: {
default: '300px',
type: String,
},
initDoc: {
default: '',
type: String,
},
editable: {
default: true,
type: Boolean,
},
},
setup(props) {
const { state } = useStore();
let editorView = ref<EditorView>();
const codemirror = ref(null);
const sqlType = {
[SqlType.MYSQL]: MySQL,
};
let startState;
const createEditor = (editorContainer, doc) => {
if (typeof editorView.value !== 'undefined') {
editorView.value.destroy();
}
startState = EditorState.create({
//doc為編輯器內(nèi)容
doc: doc,
extensions: [
history(),
oneDarkTheme,
keymap.of([
...standardKeymap,
...historyKeymap,
// Tab Keymap
{
key: 'Tab',
run: insertTab,
},
]),
sql({
dialect: sqlType[props.sqlType],
schema: state.common[`${props.sqlType}Content`],
}),
lineNumbers(),
oneDarkHighlightStyle,
autocompletion({ activateOnTyping: true }),
EditorView.editable.of(props.editable),
],
});
editorView.value = new EditorView({
state: startState,
parent: editorContainer,
});
};
//獲取編輯器里的文本內(nèi)容
const getEditorDoc = (): string | null => {
return (editorView.value as EditorView).state.doc.toString();
// return (editorView.value as EditorView).contentDOM.textContent;
};
//監(jiān)聽對(duì)應(yīng)sql的代碼補(bǔ)全信息,如果更新,則重置editor
watch(
() => state.common[`${props.sqlType}Content`],
() => {
let doc = (editorView.value as EditorView).state.doc.toString() ?? '';
const editorContainer = codemirror.value;
createEditor(editorContainer, doc);
}
);
return {
createEditor,
getEditorDoc,
editorView,
};
},
mounted() {
let doc = (this as any).$props.initDoc;
const editorContainer = (this as any).$refs.codemirror;
(this as any).createEditor(editorContainer, doc);
},
methods: {
reloadEditor({ doc }) {
//更新編輯器里的文檔
let text = (this as any).editorView.state.doc.toString();
(this as any).editorView.dispatch({
changes: { from: 0, to: text.length, insert: doc },
});
},
formatEditorDoc() {
let text = (this as any).editorView.state.doc.toString();
let to = text.length;
text = text.replaceAll(';', ';\r\n');
let i = 0;
let newText = '';
let needNewLine = 50;
while (i < text.length) {
let modStr = text.substring(i);
let namespace = modStr.indexOf(' ');
let curText = modStr.substring(0, namespace + 1);
if (newText.length - newText.lastIndexOf('\n') > needNewLine) {
newText += curText + '\r\n';
} else {
newText += curText;
}
if (namespace === -1) {
i = text.length + 1;
newText += modStr;
}
i = namespace + i + 1;
}
(this as any).editorView.dispatch({
changes: { from: 0, to: to, insert: newText },
});
},
},
};
</script>
<style>
/* 這個(gè)$props沒有寫錯(cuò),不要改 */
.cm-editor {
height: v-bind('$props.initHeight');
font-size: 18px;
}
</style>
<style lang="less" scoped></style>
store.vue
const state = {
//mysql自動(dòng)補(bǔ)全提示內(nèi)容,key為表名,數(shù)組內(nèi)為列名
MYSQLContent: {
apom: ['user', 'app_user', 'app_user_user'],
},
};
const mutations = {
setMysqlTableContent(state,data) {
state.MYSQLContent = { ...state.MYSQLContent, ...data };
},
};
export default {
state,
mutations,
};
index.vue
<codemirror :sql-type="SqlType.MYSQL" :init-doc="'select * from table;'"></codemirror>
最后看一下效果
image.png