From 875de022c1e0f7efd8487b497e6b4527af00ec5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=AE=97=E6=B4=8B?= Date: Fri, 28 Oct 2022 17:42:23 +0800 Subject: [PATCH] monaco --- mayfly_go_web/package.json | 2 + mayfly_go_web/src/views/ops/db/DbList.vue | 2 - mayfly_go_web/src/views/ops/db/SqlExec.vue | 296 +++++++++++++++++---- mayfly_go_web/vite.config.ts | 9 +- 4 files changed, 253 insertions(+), 56 deletions(-) diff --git a/mayfly_go_web/package.json b/mayfly_go_web/package.json index 6eb7901b..b8fbc135 100644 --- a/mayfly_go_web/package.json +++ b/mayfly_go_web/package.json @@ -19,6 +19,8 @@ "jsoneditor": "^9.9.2", "lodash": "^4.17.21", "mitt": "^3.0.0", + "monaco-editor": "^0.34.1", + "monaco-sql-languages": "^0.9.5", "nprogress": "^0.2.0", "screenfull": "^6.0.2", "sortablejs": "^1.13.0", diff --git a/mayfly_go_web/src/views/ops/db/DbList.vue b/mayfly_go_web/src/views/ops/db/DbList.vue index 1c3b4842..6297f3f4 100644 --- a/mayfly_go_web/src/views/ops/db/DbList.vue +++ b/mayfly_go_web/src/views/ops/db/DbList.vue @@ -364,8 +364,6 @@ export default defineComponent({ tableCreateDialog: { title:'创建表', visible: false, - columns: [], - indexs: [], activeName: '1', data: { // 修改表时,传递修改数据 edit: false, diff --git a/mayfly_go_web/src/views/ops/db/SqlExec.vue b/mayfly_go_web/src/views/ops/db/SqlExec.vue index 5a197cab..0d21d860 100644 --- a/mayfly_go_web/src/views/ops/db/SqlExec.vue +++ b/mayfly_go_web/src/views/ops/db/SqlExec.vue @@ -21,7 +21,7 @@ {{ `${item.name} [${item.tagPath}]` }} - {{ + {{ `${item.host}:${item.port} ${item.type}` }} @@ -129,7 +129,9 @@
- +
+
+
+ @@ -339,6 +342,13 @@ import SqlExecBox from './component/SqlExecBox'; import { dateStrFormat } from '@/common/utils/date.ts'; import { useStore } from '@/store/index.ts'; import { tagApi } from '../tag/api.ts'; +import * as EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker.js'; +import { language as sqlLanguage } from 'monaco-editor/esm/vs/basic-languages/mysql/mysql.js'; + +import * as monaco from "monaco-editor"; +import { editor, languages, Position} from 'monaco-editor'; +import ITextModel = editor.ITextModel; +import CompletionItem = languages.CompletionItem; export default defineComponent({ name: 'SqlExec', @@ -346,6 +356,7 @@ export default defineComponent({ setup() { const store = useStore(); const codeTextarea: any = ref(null); + const monacoTextarea: any = ref(null); const token = getSession('token'); let codemirror = null as any; const tableMap = new Map(); @@ -376,16 +387,16 @@ export default defineComponent({ // 点击执行按钮执行结果信息 execRes: { data: [], - tableColumn: [], + tableColumn: [] }, loading: false, nowTableName: '', //当前表格数据操作的数据库表名,用于双击编辑表内容使用 - selectionDatas: [], + selectionDatas: [] }, params: { pageNum: 1, pageSize: 100, - tagPath: null, + tagPath: null }, conditionDialog: { title: '', @@ -394,30 +405,19 @@ export default defineComponent({ dataTab: null, visible: false, condition: '=', - value: null, + value: null }, genSqlDialog: { visible: false, - sql: '', - }, - cmOptions: { - tabSize: 4, - mode: 'text/x-sql', - lineNumbers: true, - line: true, - indentWithTabs: true, - smartIndent: true, - matchBrackets: true, - theme: 'base16-light', - autofocus: true, - extraKeys: { Tab: 'autocomplete' }, // 自定义快捷键 - hintOptions: { - completeSingle: false, - // 自定义提示选项 - tables: {}, - }, - // more CodeMirror options... + sql: '' }, + monacoOptions: { + defaultSuggestions: [] as any[], // 默认的mysql提示 + customSuggestions: { + tableCache: [] as any[], // 表名提示 + columns: [] as any[], // 列名提示 + } + } }); const initCodemirror = () => { @@ -436,15 +436,222 @@ export default defineComponent({ }); }; - onMounted(() => { - initCodemirror(); +self.MonacoEnvironment = { + getWorker(_: string, label: string) { + return new EditorWorker(); + } +}; + +const initMonacoEditor = () => { + console.log('初始化编辑器') + // options参数参考 https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.IStandaloneEditorConstructionOptions.html#language + let editor = monaco.editor.create(monacoTextarea.value, { + value: '// some comment', + language: 'sql', + theme: 'vs-dark' + }); + + // 参考 https://microsoft.github.io/monaco-editor/playground.html#extending-language-services-completion-provider-example + monaco.languages.registerCompletionItemProvider('sql', { + provideCompletionItems: (model: ITextModel, position: Position) : languages.ProviderResult => { + // const { lineNumber, column } = position + // // 光标前文本 + // const textBeforePointer = model.getValueInRange({ + // startLineNumber: lineNumber, + // startColumn: 0, + // endLineNumber: lineNumber, + // endColumn: column + // }) + // const textBeforePointerMulti = model.getValueInRange({ + // startLineNumber: 1, + // startColumn: 0, + // endLineNumber: lineNumber, + // endColumn: column + // }) + // // 光标后文本 + // const textAfterPointerMulti = model.getValueInRange({ + // startLineNumber: lineNumber, + // startColumn: column, + // endLineNumber: model.getLineCount(), + // endColumn: model.getLineMaxColumn(model.getLineCount()) + // }) + // // const nextTokens = textAfterPointer.trim().split(/\s+/) + // // const nextToken = nextTokens[0].toLowerCase() + // const tokens = textBeforePointer.trim().split(/\s+/) + // const lastToken = tokens[tokens.length - 1].toLowerCase() + // + // console.log(lastToken) + // console.log("光标前文本"+textBeforePointerMulti) + // console.log("光标后文本"+textAfterPointerMulti) + // + // // 表名联想 + // + // // 字段名联想 + // + let word = model.getWordUntilPosition(position); + let range = { + startLineNumber: position.lineNumber, + endLineNumber: position.lineNumber, + startColumn: word.startColumn, + endColumn: word.endColumn + }; + + let suggestions: CompletionItem[] = [] + sqlLanguage.keywords.forEach((item: any) => { + suggestions.push({ + label: item, + kind: monaco.languages.CompletionItemKind.Keyword, + insertText: item, + range + }); + }) + sqlLanguage.operators.forEach((item: any) => { + suggestions.push({ + label: item, + kind: monaco.languages.CompletionItemKind.Operator, + insertText: item, + range + }); + }) + sqlLanguage.builtinFunctions.forEach((item: any) => { + suggestions.push({ + label: item, + kind: monaco.languages.CompletionItemKind.Function, + insertText: item, + range + }); + }) + sqlLanguage.builtinVariables.forEach((item: any) => { + suggestions.push({ + label: item, + kind: monaco.languages.CompletionItemKind.Variable, + insertText: item, + range + }); + }) + + // 默认提示 + return { + suggestions: suggestions + }; + }, + }); + + // monaco.languages.registerCompletionItemProvider('sql', { + // provideCompletionItems: (model: ITextModel, position: Position) : languages.ProviderResult => provideCompletionItems(model, position) + // }) + +}; + + +const provideCompletionItems= async (model: editor.ITextModel, position: monaco.Position) => { + const { lineNumber, column } = position + // 光标前文本 + const textBeforePointer = model.getValueInRange({ + startLineNumber: lineNumber, + startColumn: 0, + endLineNumber: lineNumber, + endColumn: column + }) + const textBeforePointerMulti = model.getValueInRange({ + startLineNumber: 1, + startColumn: 0, + endLineNumber: lineNumber, + endColumn: column + }) + // 光标后文本 + const textAfterPointerMulti = model.getValueInRange({ + startLineNumber: lineNumber, + startColumn: column, + endLineNumber: model.getLineCount(), + endColumn: model.getLineMaxColumn(model.getLineCount()) + }) + // const nextTokens = textAfterPointer.trim().split(/\s+/) + // const nextToken = nextTokens[0].toLowerCase() + const tokens = textBeforePointer.trim().split(/\s+/) + const lastToken = tokens[tokens.length - 1].toLowerCase() + + console.log(lastToken) + console.log(textBeforePointerMulti) + console.log(textAfterPointerMulti) + + return { + suggestions: [{ + label:'test', + kind: 9, + insertText:'test', + documentation: 'test', + range: { + startLineNumber: 1, + startColumn: 1, + endLineNumber: 10, + endColumn: 10, + } + }] + } + + // if (lastToken.endsWith('.')) { + // // 去掉点后的字符串 + // const tokenNoDot = lastToken.slice(0, lastToken.length - 1) + // // if (this.dbSchema.find(db => db.dbName === tokenNoDot.replace(/^.*,/g, ''))) { + // // // <库名>.<表名>联想 + // // return { + // // suggestions: [...this.getTableSuggestByDbName(tokenNoDot.replace(/^.*,/g, ''))] + // // } + // // } else + // if (this.getTableNameAndTableAlia(textBeforePointerMulti.split(';')[textBeforePointerMulti.split(';').length - 1] + textAfterPointerMulti.split(';')[0])) { + // const tableInfoList = this.getTableNameAndTableAlia(textBeforePointerMulti.split(';')[textBeforePointerMulti.split(';').length - 1] + textAfterPointerMulti.split(';')[0]) + // const currentTable = tableInfoList.find(item => item.tableAlia === tokenNoDot.replace(/^.*,/g, '')) + // // <别名>.<字段>联想 + // if (currentTable && currentTable.tableName) { + // return { + // suggestions: await this.getTableColumnSuggestByTableAlia(currentTable.tableName) + // } + // } else { + // return { + // suggestions: [] + // } + // } + // } else { + // return { + // suggestions: [] + // } + // } + // // 库名联想 + // } else if (lastToken === 'from' || lastToken === 'join' || /(from|join)\s+.*?\s?,\s*$/.test(textBeforePointer.replace(/.*?\(/gm, '').toLowerCase())) { + // // const tables = this.getTableSuggest() + // const databases = this.getDataBaseSuggest() + // return { + // suggestions: databases + // } + // // 字段联想 + // } else if (['select', 'where', 'order by', 'group by', 'by', 'and', 'or', 'having', 'distinct', 'on'].includes(lastToken.replace(/.*?\(/g, '')) || (lastToken.endsWith('.') && !this.dbSchema.find(db => `${db.dbName}.` === lastToken)) || /(select|where|order by|group by|by|and|or|having|distinct|on)\s+.*?\s?,\s*$/.test(textBeforePointer.toLowerCase())) { + // return { + // suggestions: await this.getTableColumnSuggest() + // } + // // 自定义字段联想 + // } else if (this.customKeywords.toString().includes(lastToken)) { + // return { + // suggestions: this.getCustomSuggest(lastToken.startsWith('$')) + // } + // // 默认联想 + // } else { + // return { + // suggestions: [...this.getDataBaseSuggest(), ...this.getTableSuggest(), ...this.getKeywordSuggest()] + // } + // } +} + +onMounted(() => { + initCodemirror(); + initMonacoEditor(); + setHeight(); + // 监听浏览器窗口大小变化,更新对应组件高度 + window.onresize = () => + (() => { setHeight(); - // 监听浏览器窗口大小变化,更新对应组件高度 - window.onresize = () => - (() => { - setHeight(); - })(); - }); + })(); +}); /** * 设置codemirror高度和数据表高度 @@ -458,7 +665,7 @@ export default defineComponent({ /** * 项目及环境更改后的回调事件 */ - const changeTag = (projectId: any, envId: any) => { + const changeTag = () => { state.dbs = []; state.dbId = null; state.db = ''; @@ -727,12 +934,7 @@ export default defineComponent({ * 获取sql,如果有鼠标选中,则返回选中内容,否则返回输入框内所有内容 */ const getSql = () => { - // 没有选中的文本,则为全部文本 - let selectSql = codemirror.getSelection(); - if (!selectSql) { - selectSql = getCodermirrorValue(); - } - return selectSql; + return codemirror.getSelection() || codemirror.getValue(); }; /** @@ -769,6 +971,11 @@ export default defineComponent({ getSqlNames(); }; +const addTableSuggestions = (tables: any[]) => { + + +} + // 选择表事件 const changeTable = async (tableName: string, execSelectSql: boolean = true) => { if (tableName == '') { @@ -983,10 +1190,6 @@ export default defineComponent({ codemirror.setValue(value); }; - const getCodermirrorValue = () => { - codemirror.getValue(); - }; - /** * 获取用户保存的sql模板名称 */ @@ -1154,7 +1357,7 @@ export default defineComponent({ const primaryKeyColumnName = primaryKey.columnName; // 更新字段列信息 const updateColumn = await getColumn(state.nowTableName, column.rawColumnKey); - const sql = `UPDATE ${state.nowTableName} SET ${column.rawColumnKey} = ${wrapColumnValue(updateColumn, input.value)} + const sql = `UPDATE ${state.nowTableName} SET ${column.rawColumnKey} = ${wrapColumnValue(updateColumn, input.value)} WHERE ${primaryKeyColumnName} = ${wrapColumnValue(primaryKey, row[primaryKeyColumnName])}`; promptExeSql(sql, () => { row[property] = text; @@ -1273,6 +1476,7 @@ export default defineComponent({ ...toRefs(state), getTags, codeTextarea, + monacoTextarea, changeTag, changeTable, cellClick, diff --git a/mayfly_go_web/vite.config.ts b/mayfly_go_web/vite.config.ts index 4fa068ce..eaffab96 100644 --- a/mayfly_go_web/vite.config.ts +++ b/mayfly_go_web/vite.config.ts @@ -37,14 +37,7 @@ const viteConfig: UserConfig = { outDir: 'dist', minify: 'esbuild', sourcemap: false, - chunkSizeWarningLimit: 1500, - rollupOptions: { - output: { - entryFileNames: `assets/[name].${new Date().getTime()}.js`, - chunkFileNames: `assets/[name].${new Date().getTime()}.js`, - assetFileNames: `assets/[name].${new Date().getTime()}.[ext]`, - }, - }, + chunkSizeWarningLimit: 1500 }, define: { __VUE_I18N_LEGACY_API__: JSON.stringify(false),