在最近的一个项目中想要做一个sql在线编辑的编辑器,由于项目中vue已经升级的到了vue3,使用codemirror 5总是会发生各种错误.索性就直接使用codemirror 6.codemirror 6使用TypeScript编写,与vue3的 结合相当融洽.接下来看一下具体的实现过程
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';
// 数据库类型, 高度, 重载.(监听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为编辑器内容
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,
});
};
//获取编辑器里的文本内容
const getEditorDoc = (): string | null => {
return (editorView.value as EditorView).state.doc.toString();
// return (editorView.value as EditorView).contentDOM.textContent;
};
//监听对应sql的代码补全信息,如果更新,则重置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>
/* 这个$props没有写错,不要改 */
.cm-editor {
height: v-bind('$props.initHeight');
font-size: 18px;
}
</style>
<style lang="less" scoped></style>
store.vue
const state = {
//mysql自动补全提示内容,key为表名,数组内为列名
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>