mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-03 16:00:25 +08:00
fix: 数据库多库切换关键字提示错误修复&sql编辑器组件统一
This commit is contained in:
@@ -9,7 +9,6 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, watch, toRefs, reactive, onMounted, onBeforeUnmount } from 'vue';
|
import { ref, watch, toRefs, reactive, onMounted, onBeforeUnmount } from 'vue';
|
||||||
// import * as monaco from 'monaco-editor';
|
|
||||||
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
|
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
|
||||||
// 相关语言
|
// 相关语言
|
||||||
import 'monaco-editor/esm/vs/basic-languages/shell/shell.contribution.js';
|
import 'monaco-editor/esm/vs/basic-languages/shell/shell.contribution.js';
|
||||||
@@ -155,7 +154,22 @@ const options = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const monacoTextarea: any = ref();
|
||||||
|
|
||||||
|
let monacoEditorIns: editor.IStandaloneCodeEditor = null as any;
|
||||||
|
let completionItemProvider: any = null;
|
||||||
|
|
||||||
|
self.MonacoEnvironment = {
|
||||||
|
getWorker(_: any, label: string) {
|
||||||
|
if (label === 'json') {
|
||||||
|
return new JsonWorker();
|
||||||
|
}
|
||||||
|
return new EditorWorker();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
|
editorHeight: '500px',
|
||||||
languageMode: 'shell',
|
languageMode: 'shell',
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -173,6 +187,7 @@ onBeforeUnmount(() => {
|
|||||||
monacoEditorIns.dispose();
|
monacoEditorIns.dispose();
|
||||||
}
|
}
|
||||||
if (completionItemProvider) {
|
if (completionItemProvider) {
|
||||||
|
console.log('unmount=> dispose completion item provider');
|
||||||
completionItemProvider.dispose();
|
completionItemProvider.dispose();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -203,20 +218,6 @@ watch(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const monacoTextarea: any = ref(null);
|
|
||||||
|
|
||||||
let monacoEditorIns: editor.IStandaloneCodeEditor = null as any;
|
|
||||||
let completionItemProvider: any = null;
|
|
||||||
|
|
||||||
self.MonacoEnvironment = {
|
|
||||||
getWorker(_: any, label: string) {
|
|
||||||
if (label === 'json') {
|
|
||||||
return new JsonWorker();
|
|
||||||
}
|
|
||||||
return new EditorWorker();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const initMonacoEditorIns = () => {
|
const initMonacoEditorIns = () => {
|
||||||
console.log('初始化monaco编辑器');
|
console.log('初始化monaco编辑器');
|
||||||
// options参数参考 https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.IStandaloneEditorConstructionOptions.html#language
|
// options参数参考 https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.IStandaloneEditorConstructionOptions.html#language
|
||||||
@@ -260,6 +261,7 @@ const setEditorValue = (value: any) => {
|
|||||||
*/
|
*/
|
||||||
const registerCompletionItemProvider = () => {
|
const registerCompletionItemProvider = () => {
|
||||||
if (completionItemProvider) {
|
if (completionItemProvider) {
|
||||||
|
console.log('exist competion item provider, dispose now');
|
||||||
completionItemProvider.dispose();
|
completionItemProvider.dispose();
|
||||||
}
|
}
|
||||||
if (state.languageMode == 'shell') {
|
if (state.languageMode == 'shell') {
|
||||||
@@ -299,7 +301,11 @@ const format = () => {
|
|||||||
monacoEditorIns.trigger('', 'editor.action.formatDocument', '');
|
monacoEditorIns.trigger('', 'editor.action.formatDocument', '');
|
||||||
};
|
};
|
||||||
|
|
||||||
defineExpose({ format });
|
const getEditor = () => {
|
||||||
|
return monacoEditorIns;
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({ getEditor, format });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* key: language, value: CompletionItemProvider
|
||||||
|
*/
|
||||||
|
const completionItemProviders: Map<string, any> = new Map();
|
||||||
|
|
||||||
|
export function registerCompletionItemProvider(language: string, completionItemProvider: any, replace: boolean = true) {
|
||||||
|
const exist = completionItemProviders.get(language);
|
||||||
|
if (exist) {
|
||||||
|
if (!replace) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
exist.dispose();
|
||||||
|
}
|
||||||
|
completionItemProviders.set(language, monaco.languages.registerCompletionItemProvider(language, completionItemProvider));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function dispposeCompletionItemProvider(language: string) {
|
||||||
|
const exist = completionItemProviders.get(language);
|
||||||
|
if (exist) {
|
||||||
|
exist.dispose();
|
||||||
|
completionItemProviders.delete(language);
|
||||||
|
}
|
||||||
|
}
|
||||||
9
mayfly_go_web/src/types/shim.d.ts
vendored
9
mayfly_go_web/src/types/shim.d.ts
vendored
@@ -1,14 +1,5 @@
|
|||||||
/* eslint-disable */
|
|
||||||
import {IDisposable} from 'monaco-editor';
|
|
||||||
declare global {
|
|
||||||
interface Window {
|
|
||||||
completionItemProvider?: IDisposable | undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 申明外部 npm 插件模块
|
// 申明外部 npm 插件模块
|
||||||
declare module 'sql-formatter';
|
declare module 'sql-formatter';
|
||||||
declare module 'jsoneditor';
|
declare module 'jsoneditor';
|
||||||
declare module 'asciinema-player';
|
declare module 'asciinema-player';
|
||||||
declare module 'monaco-editor';
|
|
||||||
declare module 'vue-grid-layout';
|
declare module 'vue-grid-layout';
|
||||||
@@ -95,13 +95,14 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineAsyncComponent, onMounted, reactive, ref, toRefs } from 'vue';
|
import { defineAsyncComponent, onMounted, reactive, ref, toRefs, onBeforeUnmount } from 'vue';
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
|
|
||||||
import { DbInst, TabInfo, TabType } from './db';
|
import { DbInst, TabInfo, TabType, registerDbCompletionItemProvider } from './db';
|
||||||
import { TagTreeNode } from '../component/tag';
|
import { TagTreeNode } from '../component/tag';
|
||||||
import TagTree from '../component/TagTree.vue';
|
import TagTree from '../component/TagTree.vue';
|
||||||
import { dbApi } from './api';
|
import { dbApi } from './api';
|
||||||
|
import { dispposeCompletionItemProvider } from '../../../components/monaco/completionItemProvider';
|
||||||
|
|
||||||
const Query = defineAsyncComponent(() => import('./component/tab/Query.vue'));
|
const Query = defineAsyncComponent(() => import('./component/tab/Query.vue'));
|
||||||
const TableData = defineAsyncComponent(() => import('./component/tab/TableData.vue'));
|
const TableData = defineAsyncComponent(() => import('./component/tab/TableData.vue'));
|
||||||
@@ -144,12 +145,15 @@ const state = reactive({
|
|||||||
const { nowDbInst } = toRefs(state);
|
const { nowDbInst } = toRefs(state);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
self.completionItemProvider?.dispose();
|
|
||||||
setHeight();
|
setHeight();
|
||||||
// 监听浏览器窗口大小变化,更新对应组件高度
|
// 监听浏览器窗口大小变化,更新对应组件高度
|
||||||
window.onresize = () => setHeight();
|
window.onresize = () => setHeight();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
dispposeCompletionItemProvider('sql');
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置editor高度和数据表高度
|
* 设置editor高度和数据表高度
|
||||||
*/
|
*/
|
||||||
@@ -207,7 +211,6 @@ const loadNode = async (node: any) => {
|
|||||||
// 点击数据库实例 -> 加载库列表
|
// 点击数据库实例 -> 加载库列表
|
||||||
if (nodeType === NodeType.DbInst) {
|
if (nodeType === NodeType.DbInst) {
|
||||||
const dbs = params.database.split(' ')?.sort();
|
const dbs = params.database.split(' ')?.sort();
|
||||||
console.log(dbs);
|
|
||||||
return dbs.map((x: any) => {
|
return dbs.map((x: any) => {
|
||||||
return new TagTreeNode(`${data.key}.${x}`, x, NodeType.Db).withParams({
|
return new TagTreeNode(`${data.key}.${x}`, x, NodeType.Db).withParams({
|
||||||
tagPath: params.tagPath,
|
tagPath: params.tagPath,
|
||||||
@@ -406,9 +409,15 @@ const onTabChange = () => {
|
|||||||
state.db = '';
|
state.db = '';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nowTab = state.tabs.get(state.activeName);
|
const nowTab = state.tabs.get(state.activeName);
|
||||||
state.nowDbInst = DbInst.getInst(nowTab?.dbId);
|
state.nowDbInst = DbInst.getInst(nowTab?.dbId);
|
||||||
state.db = nowTab?.db as string;
|
state.db = nowTab?.db as string;
|
||||||
|
|
||||||
|
if (nowTab?.type == TabType.Query) {
|
||||||
|
// 注册sql提示
|
||||||
|
registerDbCompletionItemProvider('sql', nowTab.dbId, nowTab.db, nowTab.params.dbs);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onGenerateInsertSql = async (sql: string) => {
|
const onGenerateInsertSql = async (sql: string) => {
|
||||||
|
|||||||
@@ -44,9 +44,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt5 sqlEditor">
|
<MonacoEditor ref="monacoEditorRef" class="mt5" v-model="state.sql" language="sql" :height="editorHeight" :id="'MonacoTextarea-' + ti.key" />
|
||||||
<div :id="'MonacoTextarea-' + ti.key" :style="{ height: editorHeight }"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="editor-move-resize" @mousedown="onDragSetHeight">
|
<div class="editor-move-resize" @mousedown="onDragSetHeight">
|
||||||
<el-icon>
|
<el-icon>
|
||||||
@@ -90,44 +88,22 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { nextTick, watch, onMounted, reactive, toRefs, ref, Ref } from 'vue';
|
import { nextTick, watch, onMounted, reactive, toRefs, ref, Ref } from 'vue';
|
||||||
import { storeToRefs } from 'pinia';
|
|
||||||
import { useThemeConfig } from '@/store/themeConfig';
|
|
||||||
import { getToken } from '@/common/utils/storage';
|
import { getToken } from '@/common/utils/storage';
|
||||||
import { isTrue, notBlank } from '@/common/assert';
|
import { isTrue, notBlank } from '@/common/assert';
|
||||||
import { format as sqlFormatter } from 'sql-formatter';
|
import { format as sqlFormatter } from 'sql-formatter';
|
||||||
import config from '@/common/config';
|
import config from '@/common/config';
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||||
|
|
||||||
import { language as sqlLanguage } from 'monaco-editor/esm/vs/basic-languages/mysql/mysql.js';
|
|
||||||
import { language as addSqlLanguage } from '../../lang/mysql.js';
|
|
||||||
import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker.js?worker';
|
|
||||||
// import * as monaco from 'monaco-editor';
|
|
||||||
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
|
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
|
||||||
import { editor, languages, Position } from 'monaco-editor';
|
import { editor } from 'monaco-editor';
|
||||||
// 相关语言
|
|
||||||
import 'monaco-editor/esm/vs/basic-languages/sql/sql.contribution.js';
|
|
||||||
import 'monaco-editor/esm/vs/editor/contrib/suggest/browser/suggestController.js';
|
|
||||||
import 'monaco-editor/esm/vs/editor/contrib/suggest/browser/suggestInlineCompletions.js';
|
|
||||||
// 右键菜单
|
|
||||||
import 'monaco-editor/esm/vs/editor/contrib/contextmenu/browser/contextmenu.js';
|
|
||||||
import 'monaco-editor/esm/vs/editor/contrib/caretOperations/browser/caretOperations.js';
|
|
||||||
import 'monaco-editor/esm/vs/editor/contrib/clipboard//browser/clipboard.js';
|
|
||||||
import 'monaco-editor/esm/vs/editor/contrib/find/browser/findController.js';
|
|
||||||
import 'monaco-editor/esm/vs/editor/contrib/format//browser/formatActions.js';
|
|
||||||
|
|
||||||
// 主题仓库 https://github.com/brijeshb42/monaco-themes
|
|
||||||
// 主题例子 https://editor.bitwiser.in/
|
|
||||||
import SolarizedLight from 'monaco-themes/themes/Solarized-light.json';
|
|
||||||
import DbTable from '../DbTable.vue';
|
import DbTable from '../DbTable.vue';
|
||||||
import { DbInst, TabInfo } from '../../db';
|
import { TabInfo } from '../../db';
|
||||||
import { exportCsv } from '@/common/utils/export';
|
import { exportCsv } from '@/common/utils/export';
|
||||||
import { dateStrFormat } from '@/common/utils/date';
|
import { dateStrFormat } from '@/common/utils/date';
|
||||||
import { dbApi } from '../../api';
|
import { dbApi } from '../../api';
|
||||||
|
|
||||||
const sqlCompletionKeywords = [...sqlLanguage.keywords, ...addSqlLanguage.keywords];
|
import MonacoEditor from '@/components/monaco/MonacoEditor.vue';
|
||||||
const sqlCompletionOperators = [...sqlLanguage.operators, ...addSqlLanguage.operators];
|
|
||||||
const sqlCompletionBuiltinFunctions = [...sqlLanguage.builtinFunctions, ...addSqlLanguage.builtinFunctions];
|
|
||||||
const sqlCompletionBuiltinVariables = [...sqlLanguage.builtinVariables, ...addSqlLanguage.builtinVariables];
|
|
||||||
|
|
||||||
const emits = defineEmits(['saveSqlSuccess', 'deleteSqlSuccess']);
|
const emits = defineEmits(['saveSqlSuccess', 'deleteSqlSuccess']);
|
||||||
|
|
||||||
@@ -147,11 +123,12 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { themeConfig } = storeToRefs(useThemeConfig());
|
|
||||||
const token = getToken();
|
const token = getToken();
|
||||||
let monacoEditor = {} as editor.IStandaloneCodeEditor;
|
const monacoEditorRef: any = ref(null);
|
||||||
const dbTableRef = ref(null) as Ref;
|
const dbTableRef = ref(null) as Ref;
|
||||||
|
|
||||||
|
let monacoEditor: editor.IStandaloneCodeEditor;
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
token,
|
token,
|
||||||
ti: {} as TabInfo,
|
ti: {} as TabInfo,
|
||||||
@@ -180,15 +157,6 @@ watch(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// 监听 themeConfig editorTheme配置文件的变化
|
|
||||||
watch(
|
|
||||||
() => themeConfig.value.editorTheme,
|
|
||||||
(val) => {
|
|
||||||
console.log('monaco editor theme change: ', val);
|
|
||||||
monaco?.editor?.setTheme(val);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
console.log('in query mounted');
|
console.log('in query mounted');
|
||||||
state.ti = props.data;
|
state.ti = props.data;
|
||||||
@@ -207,39 +175,8 @@ onMounted(async () => {
|
|||||||
await state.ti.getNowDbInst().loadDbHints(state.ti.db);
|
await state.ti.getNowDbInst().loadDbHints(state.ti.db);
|
||||||
});
|
});
|
||||||
|
|
||||||
self.MonacoEnvironment = {
|
|
||||||
getWorker() {
|
|
||||||
return new EditorWorker();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const initMonacoEditor = () => {
|
const initMonacoEditor = () => {
|
||||||
registerSqlCompletionItemProvider();
|
monacoEditor = monacoEditorRef.value.getEditor();
|
||||||
|
|
||||||
let monacoTextarea = document.getElementById('MonacoTextarea-' + state.ti.key) as HTMLElement;
|
|
||||||
// options参数参考 https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.IStandaloneEditorConstructionOptions.html#language
|
|
||||||
// 初始化一些主题
|
|
||||||
monaco.editor.defineTheme('SolarizedLight', SolarizedLight);
|
|
||||||
monacoEditor = monaco.editor.create(monacoTextarea, {
|
|
||||||
language: 'sql',
|
|
||||||
theme: themeConfig.value.editorTheme,
|
|
||||||
automaticLayout: true, //自适应宽高布局
|
|
||||||
folding: false,
|
|
||||||
roundedSelection: false, // 禁用选择文本背景的圆角
|
|
||||||
matchBrackets: 'near',
|
|
||||||
linkedEditing: true,
|
|
||||||
cursorBlinking: 'smooth', // 光标闪烁样式
|
|
||||||
mouseWheelZoom: true, // 在按住Ctrl键的同时使用鼠标滚轮时,在编辑器中缩放字体
|
|
||||||
overviewRulerBorder: false, // 不要滚动条的边框
|
|
||||||
tabSize: 2, // tab 缩进长度
|
|
||||||
// fontFamily: 'JetBrainsMono', // 字体 暂时不要设置,否则光标容易错位
|
|
||||||
fontWeight: 'bold',
|
|
||||||
// letterSpacing: 1, 字符间距
|
|
||||||
// quickSuggestions:false, // 禁用代码提示
|
|
||||||
minimap: {
|
|
||||||
enabled: false, // 不要小地图
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// 注册快捷键:ctrl + R 运行选中的sql
|
// 注册快捷键:ctrl + R 运行选中的sql
|
||||||
monacoEditor.addAction({
|
monacoEditor.addAction({
|
||||||
@@ -294,11 +231,6 @@ const initMonacoEditor = () => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// 如果sql有值,则默认赋值
|
|
||||||
if (state.sql) {
|
|
||||||
monacoEditor.getModel()?.setValue(state.sql);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -308,7 +240,7 @@ const onDragSetHeight = () => {
|
|||||||
document.onmousemove = (e) => {
|
document.onmousemove = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
//得到鼠标拖动的宽高距离:取绝对值
|
//得到鼠标拖动的宽高距离:取绝对值
|
||||||
state.editorHeight = `${document.getElementById('MonacoTextarea-' + state.ti.key)!.offsetHeight + e.movementY}px`;
|
state.editorHeight = `${document.getElementById('MonacoTextarea-' + state.ti.key)!.clientHeight + e.movementY}px`;
|
||||||
state.tableDataHeight -= e.movementY;
|
state.tableDataHeight -= e.movementY;
|
||||||
};
|
};
|
||||||
document.onmouseup = () => {
|
document.onmouseup = () => {
|
||||||
@@ -590,308 +522,6 @@ const submitUpdateFields = () => {
|
|||||||
const cancelUpdateFields = () => {
|
const cancelUpdateFields = () => {
|
||||||
dbTableRef.value.cancelUpdateFields();
|
dbTableRef.value.cancelUpdateFields();
|
||||||
};
|
};
|
||||||
|
|
||||||
const registerSqlCompletionItemProvider = () => {
|
|
||||||
// 参考 https://microsoft.github.io/monaco-editor/playground.html#extending-language-services-completion-provider-example
|
|
||||||
self.completionItemProvider =
|
|
||||||
self.completionItemProvider ||
|
|
||||||
monaco.languages.registerCompletionItemProvider('sql', {
|
|
||||||
triggerCharacters: ['.', ' '],
|
|
||||||
provideCompletionItems: async (model: editor.ITextModel, position: Position): Promise<languages.CompletionList | null | undefined> => {
|
|
||||||
let word = model.getWordUntilPosition(position);
|
|
||||||
const nowTab = props.data;
|
|
||||||
if (!nowTab) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const { db, dbId } = nowTab;
|
|
||||||
const dbInst = DbInst.getInst(dbId);
|
|
||||||
const { lineNumber, column } = position;
|
|
||||||
const { startColumn, endColumn } = word;
|
|
||||||
|
|
||||||
// 当前行文本
|
|
||||||
let lineContent = model.getLineContent(lineNumber);
|
|
||||||
// 注释行不需要代码提示
|
|
||||||
if (lineContent.startsWith('--')) {
|
|
||||||
return { suggestions: [] };
|
|
||||||
}
|
|
||||||
|
|
||||||
let range = {
|
|
||||||
startLineNumber: lineNumber,
|
|
||||||
endLineNumber: lineNumber,
|
|
||||||
startColumn,
|
|
||||||
endColumn,
|
|
||||||
};
|
|
||||||
|
|
||||||
// 光标前文本
|
|
||||||
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+/);
|
|
||||||
let lastToken = tokens[tokens.length - 1].toLowerCase();
|
|
||||||
const secondToken = (tokens.length > 2 && tokens[tokens.length - 2].toLowerCase()) || '';
|
|
||||||
|
|
||||||
const dbs = (nowTab.params && nowTab.params.dbs && nowTab.params.dbs) || [];
|
|
||||||
// console.log("光标前文本:=>" + textBeforePointerMulti)
|
|
||||||
// console.log("最后输入的:=>" + lastToken)
|
|
||||||
|
|
||||||
let suggestions: languages.CompletionItem[] = [];
|
|
||||||
const tables = await dbInst.loadTables(db);
|
|
||||||
|
|
||||||
async function hintTableColumns(tableName: any, db: any) {
|
|
||||||
let dbHits = await dbInst.loadDbHints(db);
|
|
||||||
let columns = dbHits[tableName];
|
|
||||||
let suggestions: languages.CompletionItem[] = [];
|
|
||||||
columns?.forEach((a: string, index: any) => {
|
|
||||||
// 字段数据格式 字段名 字段注释, 如: create_time [datetime][创建时间]
|
|
||||||
const nameAndComment = a.split(' ');
|
|
||||||
const fieldName = nameAndComment[0];
|
|
||||||
suggestions.push({
|
|
||||||
label: {
|
|
||||||
label: a,
|
|
||||||
description: 'column',
|
|
||||||
},
|
|
||||||
kind: monaco.languages.CompletionItemKind.Property,
|
|
||||||
detail: '', // 不显示detail, 否则选中时备注等会被遮挡
|
|
||||||
insertText: fieldName, // create_time
|
|
||||||
range,
|
|
||||||
sortText: 100 + index + '', // 使用表字段声明顺序排序,排序需为字符串类型
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return suggestions;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastToken.indexOf('.') > -1 || secondToken.indexOf('.') > -1) {
|
|
||||||
// 如果是.触发代码提示,则进行【 库.表名联想 】 或 【 表别名.表字段联想 】
|
|
||||||
let str = lastToken.substring(0, lastToken.lastIndexOf('.'));
|
|
||||||
if (lastToken.trim().startsWith('.')) {
|
|
||||||
str = secondToken;
|
|
||||||
}
|
|
||||||
// 如果字符串粘连起了如:'a.creator,a.',需要重新取出别名
|
|
||||||
let aliasArr = lastToken.split(',');
|
|
||||||
if (aliasArr.length > 1) {
|
|
||||||
lastToken = aliasArr[aliasArr.length - 1];
|
|
||||||
str = lastToken.substring(0, lastToken.lastIndexOf('.'));
|
|
||||||
if (lastToken.trim().startsWith('.')) {
|
|
||||||
str = secondToken;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 库.表名联想
|
|
||||||
if (dbs && dbs.filter((a: any) => a === str)?.length > 0) {
|
|
||||||
let tables = await dbInst.loadTables(str);
|
|
||||||
let suggestions: languages.CompletionItem[] = [];
|
|
||||||
for (let item of tables) {
|
|
||||||
const { tableName, tableComment } = item;
|
|
||||||
suggestions.push({
|
|
||||||
label: {
|
|
||||||
label: tableName + (tableComment ? ' - ' + tableComment : ''),
|
|
||||||
description: 'table',
|
|
||||||
},
|
|
||||||
kind: monaco.languages.CompletionItemKind.File,
|
|
||||||
insertText: tableName,
|
|
||||||
range,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return { suggestions };
|
|
||||||
}
|
|
||||||
|
|
||||||
let sql = textBeforePointerMulti.split(';')[textBeforePointerMulti.split(';').length - 1] + textAfterPointerMulti.split(';')[0];
|
|
||||||
// 表别名.表字段联想
|
|
||||||
let tableInfo = getTableByAlias(sql, db, str);
|
|
||||||
if (tableInfo.tableName) {
|
|
||||||
let tableName = tableInfo.tableName;
|
|
||||||
let db = tableInfo.dbName;
|
|
||||||
// 取出表名并提示
|
|
||||||
let suggestions = await hintTableColumns(tableName, db);
|
|
||||||
if (suggestions.length > 0) {
|
|
||||||
return { suggestions };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { suggestions: [] };
|
|
||||||
} else {
|
|
||||||
// 如果sql里含有表名,则提示表字段
|
|
||||||
let mat = textBeforePointerMulti.match(/[from|update]\n*\s+\n*(\w+)\n*\s+\n*/i);
|
|
||||||
if (mat && mat.length > 1) {
|
|
||||||
let tableName = mat[1];
|
|
||||||
// 取出表名并提示
|
|
||||||
let addSuggestions = await hintTableColumns(tableName, db);
|
|
||||||
if (addSuggestions.length > 0) {
|
|
||||||
suggestions = suggestions.concat(addSuggestions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 表名联想
|
|
||||||
tables.forEach((tableMeta: any) => {
|
|
||||||
const { tableName, tableComment } = tableMeta;
|
|
||||||
suggestions.push({
|
|
||||||
label: {
|
|
||||||
label: tableName + ' - ' + tableComment,
|
|
||||||
description: 'table',
|
|
||||||
},
|
|
||||||
kind: monaco.languages.CompletionItemKind.File,
|
|
||||||
detail: tableComment,
|
|
||||||
insertText: tableName + ' ',
|
|
||||||
range,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// mysql关键字
|
|
||||||
sqlCompletionKeywords.forEach((item: any) => {
|
|
||||||
suggestions.push({
|
|
||||||
label: {
|
|
||||||
label: item,
|
|
||||||
description: 'keyword',
|
|
||||||
},
|
|
||||||
kind: monaco.languages.CompletionItemKind.Keyword,
|
|
||||||
insertText: item,
|
|
||||||
range,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// 操作符
|
|
||||||
sqlCompletionOperators.forEach((item: any) => {
|
|
||||||
suggestions.push({
|
|
||||||
label: {
|
|
||||||
label: item,
|
|
||||||
description: 'opt',
|
|
||||||
},
|
|
||||||
kind: monaco.languages.CompletionItemKind.Operator,
|
|
||||||
insertText: item,
|
|
||||||
range,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
let replacedFunctions = [] as string[];
|
|
||||||
|
|
||||||
// 添加的函数
|
|
||||||
addSqlLanguage.replaceFunctions.forEach((item: any) => {
|
|
||||||
replacedFunctions.push(item.label);
|
|
||||||
suggestions.push({
|
|
||||||
label: {
|
|
||||||
label: item.label,
|
|
||||||
description: item.description,
|
|
||||||
},
|
|
||||||
kind: monaco.languages.CompletionItemKind.Function,
|
|
||||||
insertText: item.insertText,
|
|
||||||
range,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// 内置函数
|
|
||||||
sqlCompletionBuiltinFunctions.forEach((item: any) => {
|
|
||||||
replacedFunctions.indexOf(item) < 0 &&
|
|
||||||
suggestions.push({
|
|
||||||
label: {
|
|
||||||
label: item,
|
|
||||||
description: 'func',
|
|
||||||
},
|
|
||||||
kind: monaco.languages.CompletionItemKind.Function,
|
|
||||||
insertText: item,
|
|
||||||
range,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
// 内置变量
|
|
||||||
sqlCompletionBuiltinVariables.forEach((item: string) => {
|
|
||||||
suggestions.push({
|
|
||||||
label: {
|
|
||||||
label: item,
|
|
||||||
description: 'var',
|
|
||||||
},
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: item,
|
|
||||||
range,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// 库名提示
|
|
||||||
if (dbs && dbs.length > 0) {
|
|
||||||
dbs.forEach((a: any) => {
|
|
||||||
suggestions.push({
|
|
||||||
label: {
|
|
||||||
label: a,
|
|
||||||
description: 'schema',
|
|
||||||
},
|
|
||||||
kind: monaco.languages.CompletionItemKind.Folder,
|
|
||||||
insertText: a,
|
|
||||||
range,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 默认提示
|
|
||||||
return {
|
|
||||||
suggestions: suggestions,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据别名获取sql里的表名
|
|
||||||
* @param sql sql
|
|
||||||
* @param db 默认数据库
|
|
||||||
* @param alias 别名
|
|
||||||
*/
|
|
||||||
const getTableByAlias = (sql: string, db: string, alias: string): { dbName: string; tableName: string } => {
|
|
||||||
// 表别名:表名
|
|
||||||
let result = {};
|
|
||||||
let defName = '';
|
|
||||||
let defResult = {};
|
|
||||||
// 正则匹配取出表名和表别名
|
|
||||||
// 测试sql
|
|
||||||
/*
|
|
||||||
|
|
||||||
`select * from database.Outvisit l
|
|
||||||
left join patient p on l.patid=p.patientid
|
|
||||||
join patstatic c on l.patid=c.patid inner join patphone ph on l.patid=ph.patid
|
|
||||||
where l.name='kevin' and exsits(select 1 from pharmacywestpas pw where p.outvisitid=l.outvisitid)
|
|
||||||
unit all
|
|
||||||
select * from invisit v where`.match(/(join|from)\s+(\w*-?\w*\.?\w+)\s*(as)?\s*(\w*)/gi)
|
|
||||||
*/
|
|
||||||
let match = sql.match(/(join|from)\n*\s+\n*(\w*-?\w*\.?\w+)\s*(as)?\s*(\w*)\n*/gi);
|
|
||||||
if (match && match.length > 0) {
|
|
||||||
match.forEach((a) => {
|
|
||||||
// 去掉前缀,取出
|
|
||||||
let t = a
|
|
||||||
.substring(5, a.length)
|
|
||||||
.replaceAll(/\s+/g, ' ')
|
|
||||||
.replaceAll(/\s+as\s+/gi, ' ')
|
|
||||||
.replaceAll(/\r\n/g, ' ')
|
|
||||||
.trim()
|
|
||||||
.split(/\s+/);
|
|
||||||
let withDb = t[0].split('.');
|
|
||||||
// 表名是 db名.表名
|
|
||||||
let tName = withDb.length > 1 ? withDb[1] : withDb[0];
|
|
||||||
let dbName = withDb.length > 1 ? withDb[0] : db || '';
|
|
||||||
if (t.length == 2) {
|
|
||||||
// 表别名:表名
|
|
||||||
result[t[1]] = { tableName: tName, dbName };
|
|
||||||
} else {
|
|
||||||
// 只有表名无别名 取第一个无别名的表为默认表
|
|
||||||
!defName && (defResult = { tableName: tName, dbName: db });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return result[alias] || defResult;
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@@ -905,12 +535,6 @@ select * from invisit v where`.match(/(join|from)\s+(\w*-?\w*\.?\w+)\s*(as)?\s*(
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sqlEditor {
|
|
||||||
font-size: 8pt;
|
|
||||||
font-weight: 600;
|
|
||||||
border: 1px solid var(--el-border-color-light, #ebeef5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.update_field_active {
|
.update_field_active {
|
||||||
background-color: var(--el-color-success);
|
background-color: var(--el-color-success);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,18 @@ import { dbApi } from './api';
|
|||||||
import { getTextWidth } from '@/common/utils/string';
|
import { getTextWidth } from '@/common/utils/string';
|
||||||
import SqlExecBox from './component/SqlExecBox';
|
import SqlExecBox from './component/SqlExecBox';
|
||||||
|
|
||||||
|
import { language as sqlLanguage } from 'monaco-editor/esm/vs/basic-languages/mysql/mysql.js';
|
||||||
|
import { language as addSqlLanguage } from './lang/mysql.js';
|
||||||
|
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
|
||||||
|
import { editor, languages, Position } from 'monaco-editor';
|
||||||
|
|
||||||
|
import { registerCompletionItemProvider } from '@/components/monaco/completionItemProvider';
|
||||||
|
|
||||||
|
const sqlCompletionKeywords = [...sqlLanguage.keywords, ...addSqlLanguage.keywords];
|
||||||
|
const sqlCompletionOperators = [...sqlLanguage.operators, ...addSqlLanguage.operators];
|
||||||
|
const sqlCompletionBuiltinFunctions = [...sqlLanguage.builtinFunctions, ...addSqlLanguage.builtinFunctions];
|
||||||
|
const sqlCompletionBuiltinVariables = [...sqlLanguage.builtinVariables, ...addSqlLanguage.builtinVariables];
|
||||||
|
|
||||||
const dbInstCache: Map<number, DbInst> = new Map();
|
const dbInstCache: Map<number, DbInst> = new Map();
|
||||||
|
|
||||||
export class DbInst {
|
export class DbInst {
|
||||||
@@ -463,3 +475,305 @@ export type FieldsMeta = {
|
|||||||
// 新值
|
// 新值
|
||||||
newValue: string;
|
newValue: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册数据库表、字段等信息提示
|
||||||
|
*
|
||||||
|
* @param language 语言
|
||||||
|
* @param dbId 数据库id
|
||||||
|
* @param db 库名
|
||||||
|
* @param dbs 该库所有库名
|
||||||
|
*/
|
||||||
|
export function registerDbCompletionItemProvider(language: string, dbId: number, db: string, dbs: [] = []) {
|
||||||
|
registerCompletionItemProvider(language, {
|
||||||
|
triggerCharacters: ['.', ' '],
|
||||||
|
provideCompletionItems: async (model: editor.ITextModel, position: Position): Promise<languages.CompletionList | null | undefined> => {
|
||||||
|
let word = model.getWordUntilPosition(position);
|
||||||
|
const dbInst = DbInst.getInst(dbId);
|
||||||
|
const { lineNumber, column } = position;
|
||||||
|
const { startColumn, endColumn } = word;
|
||||||
|
|
||||||
|
// 当前行文本
|
||||||
|
let lineContent = model.getLineContent(lineNumber);
|
||||||
|
// 注释行不需要代码提示
|
||||||
|
if (lineContent.startsWith('--')) {
|
||||||
|
return { suggestions: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
let range = {
|
||||||
|
startLineNumber: lineNumber,
|
||||||
|
endLineNumber: lineNumber,
|
||||||
|
startColumn,
|
||||||
|
endColumn,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 光标前文本
|
||||||
|
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+/);
|
||||||
|
let lastToken = tokens[tokens.length - 1].toLowerCase();
|
||||||
|
const secondToken = (tokens.length > 2 && tokens[tokens.length - 2].toLowerCase()) || '';
|
||||||
|
|
||||||
|
// console.log("光标前文本:=>" + textBeforePointerMulti)
|
||||||
|
// console.log("最后输入的:=>" + lastToken)
|
||||||
|
|
||||||
|
let suggestions: languages.CompletionItem[] = [];
|
||||||
|
const tables = await dbInst.loadTables(db);
|
||||||
|
|
||||||
|
async function hintTableColumns(tableName: any, db: any) {
|
||||||
|
let dbHits = await dbInst.loadDbHints(db);
|
||||||
|
let columns = dbHits[tableName];
|
||||||
|
let suggestions: languages.CompletionItem[] = [];
|
||||||
|
columns?.forEach((a: string, index: any) => {
|
||||||
|
// 字段数据格式 字段名 字段注释, 如: create_time [datetime][创建时间]
|
||||||
|
const nameAndComment = a.split(' ');
|
||||||
|
const fieldName = nameAndComment[0];
|
||||||
|
suggestions.push({
|
||||||
|
label: {
|
||||||
|
label: a,
|
||||||
|
description: 'column',
|
||||||
|
},
|
||||||
|
kind: monaco.languages.CompletionItemKind.Property,
|
||||||
|
detail: '', // 不显示detail, 否则选中时备注等会被遮挡
|
||||||
|
insertText: fieldName, // create_time
|
||||||
|
range,
|
||||||
|
sortText: 100 + index + '', // 使用表字段声明顺序排序,排序需为字符串类型
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return suggestions;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastToken.indexOf('.') > -1 || secondToken.indexOf('.') > -1) {
|
||||||
|
// 如果是.触发代码提示,则进行【 库.表名联想 】 或 【 表别名.表字段联想 】
|
||||||
|
let str = lastToken.substring(0, lastToken.lastIndexOf('.'));
|
||||||
|
if (lastToken.trim().startsWith('.')) {
|
||||||
|
str = secondToken;
|
||||||
|
}
|
||||||
|
// 如果字符串粘连起了如:'a.creator,a.',需要重新取出别名
|
||||||
|
let aliasArr = lastToken.split(',');
|
||||||
|
if (aliasArr.length > 1) {
|
||||||
|
lastToken = aliasArr[aliasArr.length - 1];
|
||||||
|
str = lastToken.substring(0, lastToken.lastIndexOf('.'));
|
||||||
|
if (lastToken.trim().startsWith('.')) {
|
||||||
|
str = secondToken;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 库.表名联想
|
||||||
|
if (dbs && dbs.filter((a: any) => a === str)?.length > 0) {
|
||||||
|
let tables = await dbInst.loadTables(str);
|
||||||
|
let suggestions: languages.CompletionItem[] = [];
|
||||||
|
for (let item of tables) {
|
||||||
|
const { tableName, tableComment } = item;
|
||||||
|
suggestions.push({
|
||||||
|
label: {
|
||||||
|
label: tableName + (tableComment ? ' - ' + tableComment : ''),
|
||||||
|
description: 'table',
|
||||||
|
},
|
||||||
|
kind: monaco.languages.CompletionItemKind.File,
|
||||||
|
insertText: tableName,
|
||||||
|
range,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return { suggestions };
|
||||||
|
}
|
||||||
|
|
||||||
|
let sql = textBeforePointerMulti.split(';')[textBeforePointerMulti.split(';').length - 1] + textAfterPointerMulti.split(';')[0];
|
||||||
|
// 表别名.表字段联想
|
||||||
|
let tableInfo = getTableByAlias(sql, db, str);
|
||||||
|
if (tableInfo.tableName) {
|
||||||
|
let tableName = tableInfo.tableName;
|
||||||
|
let db = tableInfo.dbName;
|
||||||
|
// 取出表名并提示
|
||||||
|
let suggestions = await hintTableColumns(tableName, db);
|
||||||
|
if (suggestions.length > 0) {
|
||||||
|
return { suggestions };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { suggestions: [] };
|
||||||
|
} else {
|
||||||
|
// 如果sql里含有表名,则提示表字段
|
||||||
|
let mat = textBeforePointerMulti.match(/[from|update]\n*\s+\n*(\w+)\n*\s+\n*/i);
|
||||||
|
if (mat && mat.length > 1) {
|
||||||
|
let tableName = mat[1];
|
||||||
|
// 取出表名并提示
|
||||||
|
let addSuggestions = await hintTableColumns(tableName, db);
|
||||||
|
if (addSuggestions.length > 0) {
|
||||||
|
suggestions = suggestions.concat(addSuggestions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表名联想
|
||||||
|
tables.forEach((tableMeta: any, index: any) => {
|
||||||
|
const { tableName, tableComment } = tableMeta;
|
||||||
|
suggestions.push({
|
||||||
|
label: {
|
||||||
|
label: tableName + ' - ' + tableComment,
|
||||||
|
description: 'table',
|
||||||
|
},
|
||||||
|
kind: monaco.languages.CompletionItemKind.File,
|
||||||
|
detail: tableComment,
|
||||||
|
insertText: tableName + ' ',
|
||||||
|
range,
|
||||||
|
sortText: 300 + index + '',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// mysql关键字
|
||||||
|
sqlCompletionKeywords.forEach((item: any) => {
|
||||||
|
suggestions.push({
|
||||||
|
label: {
|
||||||
|
label: item,
|
||||||
|
description: 'keyword',
|
||||||
|
},
|
||||||
|
kind: monaco.languages.CompletionItemKind.Keyword,
|
||||||
|
insertText: item,
|
||||||
|
range,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 操作符
|
||||||
|
sqlCompletionOperators.forEach((item: any) => {
|
||||||
|
suggestions.push({
|
||||||
|
label: {
|
||||||
|
label: item,
|
||||||
|
description: 'opt',
|
||||||
|
},
|
||||||
|
kind: monaco.languages.CompletionItemKind.Operator,
|
||||||
|
insertText: item,
|
||||||
|
range,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let replacedFunctions = [] as string[];
|
||||||
|
|
||||||
|
// 添加的函数
|
||||||
|
addSqlLanguage.replaceFunctions.forEach((item: any) => {
|
||||||
|
replacedFunctions.push(item.label);
|
||||||
|
suggestions.push({
|
||||||
|
label: {
|
||||||
|
label: item.label,
|
||||||
|
description: item.description,
|
||||||
|
},
|
||||||
|
kind: monaco.languages.CompletionItemKind.Function,
|
||||||
|
insertText: item.insertText,
|
||||||
|
range,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 内置函数
|
||||||
|
sqlCompletionBuiltinFunctions.forEach((item: any) => {
|
||||||
|
replacedFunctions.indexOf(item) < 0 &&
|
||||||
|
suggestions.push({
|
||||||
|
label: {
|
||||||
|
label: item,
|
||||||
|
description: 'func',
|
||||||
|
},
|
||||||
|
kind: monaco.languages.CompletionItemKind.Function,
|
||||||
|
insertText: item,
|
||||||
|
range,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// 内置变量
|
||||||
|
sqlCompletionBuiltinVariables.forEach((item: string) => {
|
||||||
|
suggestions.push({
|
||||||
|
label: {
|
||||||
|
label: item,
|
||||||
|
description: 'var',
|
||||||
|
},
|
||||||
|
kind: monaco.languages.CompletionItemKind.Variable,
|
||||||
|
insertText: item,
|
||||||
|
range,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 库名提示
|
||||||
|
if (dbs && dbs.length > 0) {
|
||||||
|
dbs.forEach((a: any) => {
|
||||||
|
suggestions.push({
|
||||||
|
label: {
|
||||||
|
label: a,
|
||||||
|
description: 'schema',
|
||||||
|
},
|
||||||
|
kind: monaco.languages.CompletionItemKind.Folder,
|
||||||
|
insertText: a,
|
||||||
|
range,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认提示
|
||||||
|
return {
|
||||||
|
suggestions: suggestions,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据别名获取sql里的表名
|
||||||
|
* @param sql sql
|
||||||
|
* @param db 默认数据库
|
||||||
|
* @param alias 别名
|
||||||
|
*/
|
||||||
|
const getTableByAlias = (sql: string, db: string, alias: string): { dbName: string; tableName: string } => {
|
||||||
|
// 表别名:表名
|
||||||
|
let result = {};
|
||||||
|
let defName = '';
|
||||||
|
let defResult = {};
|
||||||
|
// 正则匹配取出表名和表别名
|
||||||
|
// 测试sql
|
||||||
|
/*
|
||||||
|
|
||||||
|
`select * from database.Outvisit l
|
||||||
|
left join patient p on l.patid=p.patientid
|
||||||
|
join patstatic c on l.patid=c.patid inner join patphone ph on l.patid=ph.patid
|
||||||
|
where l.name='kevin' and exsits(select 1 from pharmacywestpas pw where p.outvisitid=l.outvisitid)
|
||||||
|
unit all
|
||||||
|
select * from invisit v where`.match(/(join|from)\s+(\w*-?\w*\.?\w+)\s*(as)?\s*(\w*)/gi)
|
||||||
|
*/
|
||||||
|
let match = sql.match(/(join|from)\n*\s+\n*(\w*-?\w*\.?\w+)\s*(as)?\s*(\w*)\n*/gi);
|
||||||
|
if (match && match.length > 0) {
|
||||||
|
match.forEach((a) => {
|
||||||
|
// 去掉前缀,取出
|
||||||
|
let t = a
|
||||||
|
.substring(5, a.length)
|
||||||
|
.replaceAll(/\s+/g, ' ')
|
||||||
|
.replaceAll(/\s+as\s+/gi, ' ')
|
||||||
|
.replaceAll(/\r\n/g, ' ')
|
||||||
|
.trim()
|
||||||
|
.split(/\s+/);
|
||||||
|
let withDb = t[0].split('.');
|
||||||
|
// 表名是 db名.表名
|
||||||
|
let tName = withDb.length > 1 ? withDb[1] : withDb[0];
|
||||||
|
let dbName = withDb.length > 1 ? withDb[0] : db || '';
|
||||||
|
if (t.length == 2) {
|
||||||
|
// 表别名:表名
|
||||||
|
result[t[1]] = { tableName: tName, dbName };
|
||||||
|
} else {
|
||||||
|
// 只有表名无别名 取第一个无别名的表为默认表
|
||||||
|
!defName && (defResult = { tableName: tName, dbName: db });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result[alias] || defResult;
|
||||||
|
};
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ func (m *Mysql) Default() {
|
|||||||
if m.MaxIdleConns == 0 {
|
if m.MaxIdleConns == 0 {
|
||||||
m.MaxIdleConns = 5
|
m.MaxIdleConns = 5
|
||||||
}
|
}
|
||||||
|
if m.MaxOpenConns == 0 {
|
||||||
|
m.MaxOpenConns = m.MaxIdleConns
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Mysql) Dsn() string {
|
func (m *Mysql) Dsn() string {
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ func NewClientManager() (clientManager *ClientManager) {
|
|||||||
// 管道处理程序
|
// 管道处理程序
|
||||||
func (manager *ClientManager) Start() {
|
func (manager *ClientManager) Start() {
|
||||||
manager.HeartbeatTimer()
|
manager.HeartbeatTimer()
|
||||||
go manager.WriteMessage()
|
manager.WriteMessage()
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case client := <-manager.ConnectChan:
|
case client := <-manager.ConnectChan:
|
||||||
@@ -118,7 +118,7 @@ func (manager *ClientManager) HeartbeatTimer() {
|
|||||||
}
|
}
|
||||||
if err := cli.Ping(); err != nil {
|
if err := cli.Ping(); err != nil {
|
||||||
manager.CloseClient(cli)
|
manager.CloseClient(cli)
|
||||||
logx.Errorf("WS发送心跳失败: %v 总连接数:%d", userId, Manager.Count())
|
logx.Debugf("WS发送心跳失败: %v 总连接数:%d", userId, Manager.Count())
|
||||||
} else {
|
} else {
|
||||||
logx.Debugf("WS发送心跳成功: uid=%v", userId)
|
logx.Debugf("WS发送心跳成功: uid=%v", userId)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user