mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-03 16:00:25 +08:00
feat: monaco
This commit is contained in:
@@ -21,6 +21,7 @@
|
|||||||
"mitt": "^3.0.0",
|
"mitt": "^3.0.0",
|
||||||
"monaco-editor": "^0.34.1",
|
"monaco-editor": "^0.34.1",
|
||||||
"monaco-sql-languages": "^0.9.5",
|
"monaco-sql-languages": "^0.9.5",
|
||||||
|
"monaco-themes": "^0.4.2",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"screenfull": "^6.0.2",
|
"screenfull": "^6.0.2",
|
||||||
"sortablejs": "^1.13.0",
|
"sortablejs": "^1.13.0",
|
||||||
|
|||||||
@@ -87,6 +87,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="float: right" class="fl">
|
<div style="float: right" class="fl">
|
||||||
|
<el-select v-model="monacoOptions.theme" placeholder="切换编辑器主题" @change="changeEditorTheme"
|
||||||
|
filterable allow-create default-first-option size="small" class="mr10">
|
||||||
|
<el-option v-for="item in monacoOptions.defaultThemes as string" :key="item" :label="item"
|
||||||
|
:value="item">
|
||||||
|
{{ item }}
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
<el-select v-model="sqlName" placeholder="选择or输入SQL模板名" @change="changeSqlTemplate"
|
<el-select v-model="sqlName" placeholder="选择or输入SQL模板名" @change="changeSqlTemplate"
|
||||||
filterable allow-create default-first-option size="small" class="mr10">
|
filterable allow-create default-first-option size="small" class="mr10">
|
||||||
<el-option v-for="item in sqlNames as any" :key="item" :label="item.database"
|
<el-option v-for="item in sqlNames as any" :key="item" :label="item.database"
|
||||||
@@ -104,10 +111,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt5 sqlEditor">
|
<div class="mt5 sqlEditor">
|
||||||
<textarea ref="codeTextarea"></textarea>
|
<div ref="monacoTextarea" :style="{height: monacoOptions.height}"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt5 sqlEditor">
|
<div class="editor-move-resize" @mousedown="onDragSetHeight">
|
||||||
<div ref="monacoTextarea" style="height: 200px;"></div>
|
<el-icon><Minus /></el-icon>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt5">
|
<div class="mt5">
|
||||||
<el-row>
|
<el-row>
|
||||||
@@ -122,7 +129,7 @@
|
|||||||
</el-row>
|
</el-row>
|
||||||
<el-table @cell-dblclick="cellClick" @selection-change="onDataSelectionChange"
|
<el-table @cell-dblclick="cellClick" @selection-change="onDataSelectionChange"
|
||||||
:data="queryTab.execRes.data" v-loading="queryTab.loading" element-loading-text="查询中..."
|
:data="queryTab.execRes.data" v-loading="queryTab.loading" element-loading-text="查询中..."
|
||||||
size="small" max-height="250" empty-text="tips: select *开头的单表查询或点击表名默认查询的数据,可双击数据在线修改"
|
size="small" :max-height="monacoOptions.tableMaxHeight" empty-text="tips: select *开头的单表查询或点击表名默认查询的数据,可双击数据在线修改"
|
||||||
stripe border class="mt5">
|
stripe border class="mt5">
|
||||||
<el-table-column v-if="queryTab.execRes.tableColumn.length > 0 && queryTab.nowTableName"
|
<el-table-column v-if="queryTab.execRes.tableColumn.length > 0 && queryTab.nowTableName"
|
||||||
type="selection" width="35" />
|
type="selection" width="35" />
|
||||||
@@ -244,42 +251,38 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, toRefs, reactive, ref, watch } from 'vue';
|
import {onMounted, reactive, ref, toRefs, watch} from 'vue';
|
||||||
import { dbApi } from './api';
|
import {dbApi} from './api';
|
||||||
|
|
||||||
import 'codemirror/addon/hint/show-hint.css';
|
import {format as sqlFormatter} from 'sql-formatter';
|
||||||
// import base style
|
import {isTrue, notBlank, notEmpty} from '@/common/assert';
|
||||||
import 'codemirror/lib/codemirror.css';
|
import {ElMessage, ElMessageBox} from 'element-plus';
|
||||||
// 引入主题后还需要在 options 中指定主题才会生效
|
|
||||||
import 'codemirror/theme/base16-light.css';
|
|
||||||
|
|
||||||
import 'codemirror/addon/selection/active-line';
|
|
||||||
import _CodeMirror from 'codemirror';
|
|
||||||
import 'codemirror/addon/hint/show-hint.js';
|
|
||||||
import 'codemirror/addon/hint/sql-hint.js';
|
|
||||||
|
|
||||||
import { format as sqlFormatter } from 'sql-formatter';
|
|
||||||
import { notBlank, notEmpty, isTrue } from '@/common/assert';
|
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
|
||||||
import config from '@/common/config';
|
import config from '@/common/config';
|
||||||
import { getSession } from '@/common/utils/storage';
|
import {getSession} from '@/common/utils/storage';
|
||||||
import SqlExecBox from './component/SqlExecBox';
|
import SqlExecBox from './component/SqlExecBox';
|
||||||
import { dateStrFormat } from '@/common/utils/date.ts';
|
import {dateStrFormat} from '@/common/utils/date.ts';
|
||||||
import { useStore } from '@/store/index.ts';
|
import {useStore} from '@/store/index.ts';
|
||||||
import { tagApi } from '../tag/api.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 EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker.js?worker';
|
||||||
import { editor, languages, Position} from 'monaco-editor';
|
import {language as sqlLanguage} from 'monaco-editor/esm/vs/basic-languages/mysql/mysql.js';
|
||||||
import ITextModel = editor.ITextModel;
|
import * as monaco from 'monaco-editor';
|
||||||
import CompletionItem = languages.CompletionItem;
|
import {editor, languages, Position} from 'monaco-editor';
|
||||||
|
|
||||||
|
// 主题仓库 https://github.com/brijeshb42/monaco-themes
|
||||||
|
// 主题例子 https://editor.bitwiser.in/
|
||||||
|
import Monokai from 'monaco-themes/themes/Monokai.json'
|
||||||
|
import Active4D from 'monaco-themes/themes/Active4D.json'
|
||||||
|
import ahe from 'monaco-themes/themes/All Hallows Eve.json'
|
||||||
|
import bop from 'monaco-themes/themes/Birds of Paradise.json'
|
||||||
|
import krTheme from 'monaco-themes/themes/krTheme.json'
|
||||||
|
import Dracula from 'monaco-themes/themes/Dracula.json'
|
||||||
|
import TextmateMac from 'monaco-themes/themes/Textmate (Mac Classic).json'
|
||||||
|
import { Minus } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
const codeTextarea: any = ref(null);
|
const monacoTextarea: any = ref(null);
|
||||||
const monacoTextarea: any = ref(null);
|
|
||||||
const token = getSession('token');
|
const token = getSession('token');
|
||||||
let codemirror = null as any;
|
|
||||||
const tableMap = new Map();
|
const tableMap = new Map();
|
||||||
|
|
||||||
const defalutLimit = 20
|
const defalutLimit = 20
|
||||||
@@ -300,20 +303,26 @@ const cmOptions = {
|
|||||||
// 自定义提示选项
|
// 自定义提示选项
|
||||||
tables: {},
|
tables: {},
|
||||||
},
|
},
|
||||||
// more CodeMirror options...
|
}
|
||||||
|
|
||||||
|
export type TableMeta = {
|
||||||
|
// 表名
|
||||||
|
tableName:string,
|
||||||
|
// 表注释
|
||||||
|
tableComment:string
|
||||||
}
|
}
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
token: token,
|
token: token,
|
||||||
tags: [],
|
tags: [],
|
||||||
dbs: [] as any, // 数据库实例列表
|
dbs: [] as any, // 数据库实例列表
|
||||||
databaseList: [], // 数据库实例拥有的数据库列表,1数据库实例 -> 多数据库
|
databaseList: [] as string[], // 数据库实例拥有的数据库列表,1数据库实例 -> 多数据库
|
||||||
db: '', // 当前操作的数据库
|
db: '', // 当前操作的数据库
|
||||||
dbType: '',
|
dbType: '',
|
||||||
tables: [] as any,
|
tables: [] as any,
|
||||||
dbId: null, // 当前选中操作的数据库实例
|
dbId: null, // 当前选中操作的数据库实例
|
||||||
tableName: '',
|
tableName: '',
|
||||||
tableMetadata: [],
|
tableMetadata: [] as TableMeta[],
|
||||||
sqlName: '', // 当前sql模板名
|
sqlName: '', // 当前sql模板名
|
||||||
sqlNames: [], // 所有sql模板名
|
sqlNames: [], // 所有sql模板名
|
||||||
activeName: 'Query',
|
activeName: 'Query',
|
||||||
@@ -351,15 +360,15 @@ const state = reactive({
|
|||||||
visible: false,
|
visible: false,
|
||||||
sql: ''
|
sql: ''
|
||||||
},
|
},
|
||||||
monacoOptions: {
|
monacoOptions: {
|
||||||
defaultSuggestions: [] as any[], // 默认的mysql提示
|
editor: {} as editor.IStandaloneCodeEditor,
|
||||||
customSuggestions: {
|
height: '',
|
||||||
tableCache: [] as any[], // 表名提示
|
tableMaxHeight:250,
|
||||||
columns: [] as any[], // 列名提示
|
dbTables:{},
|
||||||
}
|
theme:'',
|
||||||
}
|
defaultThemes:[ 'vs' ,'vs-dark', 'hc-black', 'hc-light', 'Monokai', 'Active4D', 'ahe', 'bop', 'krTheme', 'Dracula', 'TextmateMac'],
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
tags,
|
tags,
|
||||||
dbs,
|
dbs,
|
||||||
@@ -378,26 +387,13 @@ const {
|
|||||||
params,
|
params,
|
||||||
conditionDialog,
|
conditionDialog,
|
||||||
genSqlDialog,
|
genSqlDialog,
|
||||||
|
monacoOptions
|
||||||
} = toRefs(state)
|
} = toRefs(state)
|
||||||
|
|
||||||
const initCodemirror = () => {
|
let monacoEditor = {} as editor.IStandaloneCodeEditor;
|
||||||
// 初始化编辑器实例,传入需要被实例化的文本域对象和默认配置
|
|
||||||
codemirror = _CodeMirror.fromTextArea(codeTextarea.value, cmOptions);
|
|
||||||
codemirror.on('inputRead', (instance: any, changeObj: any) => {
|
|
||||||
if (/^[a-zA-Z]/.test(changeObj.text[0])) {
|
|
||||||
instance.showHint();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
codemirror.on('beforeChange', (instance: any, changeObj: any) => {
|
|
||||||
var text = changeObj.text[0];
|
|
||||||
// 将sql提示去除
|
|
||||||
changeObj.text[0] = text.split(' ')[0];
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
self.MonacoEnvironment = {
|
self.MonacoEnvironment = {
|
||||||
getWorker(_: string, label: string) {
|
getWorker() {
|
||||||
return new EditorWorker();
|
return new EditorWorker();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -405,58 +401,157 @@ self.MonacoEnvironment = {
|
|||||||
const initMonacoEditor = () => {
|
const initMonacoEditor = () => {
|
||||||
console.log('初始化编辑器')
|
console.log('初始化编辑器')
|
||||||
// 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
|
||||||
let editor = monaco.editor.create(monacoTextarea.value, {
|
|
||||||
value: '// some comment',
|
let defVal = `-- monaco editor
|
||||||
|
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`;
|
||||||
|
|
||||||
|
monacoEditor = monaco.editor.create(monacoTextarea.value, {
|
||||||
|
value: defVal,
|
||||||
language: 'sql',
|
language: 'sql',
|
||||||
theme: 'vs-dark'
|
theme: 'vs',
|
||||||
|
automaticLayout: true, //自适应宽高布局
|
||||||
|
foldingStrategy: 'indentation',//代码可分小段折叠
|
||||||
|
roundedSelection: false, // 禁用选择文本背景的圆角
|
||||||
|
matchBrackets: 'near',
|
||||||
|
linkedEditing:true,
|
||||||
|
cursorBlinking: 'smooth',// 光标闪烁样式
|
||||||
|
mouseWheelZoom: true, // 在按住Ctrl键的同时使用鼠标滚轮时,在编辑器中缩放字体
|
||||||
|
overviewRulerBorder: false, // 不要滚动条的边框
|
||||||
|
tabSize: 2, // tab 缩进长度
|
||||||
|
// fontFamily:'consolas', // 字体 暂时不要设置,否则光标容易错位
|
||||||
|
// letterSpacing: 1, 字符间距
|
||||||
|
// quickSuggestions:false, // 禁用代码提示
|
||||||
|
minimap: {
|
||||||
|
enabled: false, // 不要小地图
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 初始化一些主题
|
||||||
|
monaco.editor.defineTheme('Monokai', Monokai);
|
||||||
|
monaco.editor.defineTheme('Active4D', Active4D);
|
||||||
|
monaco.editor.defineTheme('ahe', ahe);
|
||||||
|
monaco.editor.defineTheme('bop', bop);
|
||||||
|
monaco.editor.defineTheme('krTheme', krTheme);
|
||||||
|
monaco.editor.defineTheme('Dracula', Dracula);
|
||||||
|
monaco.editor.defineTheme('TextmateMac', TextmateMac);
|
||||||
|
|
||||||
|
// 动态设置主题
|
||||||
|
// monaco.editor.setTheme('hc-black');
|
||||||
|
|
||||||
// 参考 https://microsoft.github.io/monaco-editor/playground.html#extending-language-services-completion-provider-example
|
// 参考 https://microsoft.github.io/monaco-editor/playground.html#extending-language-services-completion-provider-example
|
||||||
monaco.languages.registerCompletionItemProvider('sql', {
|
monaco.languages.registerCompletionItemProvider('sql', {
|
||||||
provideCompletionItems: (model: ITextModel, position: Position) : languages.ProviderResult<languages.CompletionList> => {
|
triggerCharacters:['.'],
|
||||||
// const { lineNumber, column } = position
|
provideCompletionItems: async (model: editor.ITextModel, position: Position): Promise<languages.CompletionList | null | undefined> => {
|
||||||
// // 光标前文本
|
|
||||||
// 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 word = model.getWordUntilPosition(position);
|
||||||
|
const {lineNumber, column} = position
|
||||||
|
const {startColumn, endColumn} = word
|
||||||
|
|
||||||
|
// 当前行文本
|
||||||
|
let lineContent = model.getLineContent(lineNumber);
|
||||||
|
// 注释行不需要代码提示
|
||||||
|
if(lineContent.startsWith('--')){
|
||||||
|
return {suggestions: []}
|
||||||
|
}
|
||||||
|
|
||||||
let range = {
|
let range = {
|
||||||
startLineNumber: position.lineNumber,
|
startLineNumber: lineNumber,
|
||||||
endLineNumber: position.lineNumber,
|
endLineNumber: lineNumber,
|
||||||
startColumn: word.startColumn,
|
startColumn,
|
||||||
endColumn: word.endColumn
|
endColumn,
|
||||||
};
|
};
|
||||||
|
|
||||||
let suggestions: CompletionItem[] = []
|
// 光标前文本
|
||||||
|
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("光标前文本:=>" + textBeforePointerMulti)
|
||||||
|
|
||||||
|
console.log("最后输入的:=>" + lastToken)
|
||||||
|
if (lastToken.endsWith('.')) {
|
||||||
|
// 如果是.触发代码提示,则进行【 库.表名联想 】 或 【 表别名.表字段联想 】
|
||||||
|
let str = lastToken.substring(0, lastToken.lastIndexOf('.'))
|
||||||
|
// 库.表名联想
|
||||||
|
if (state.databaseList.indexOf(str) > -1) {
|
||||||
|
let tables = await loadTableMetadata(str)
|
||||||
|
let suggestions: languages.CompletionItem[] = []
|
||||||
|
for(let item of tables){
|
||||||
|
const {tableName, tableComment} = item
|
||||||
|
suggestions.push({
|
||||||
|
label: tableName + ( tableComment?' - ' + tableComment :'' ),
|
||||||
|
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,state.db, str)
|
||||||
|
if(tableInfo.tableName){
|
||||||
|
let table = tableInfo.tableName
|
||||||
|
let db = tableInfo.dbName
|
||||||
|
// 取出表名并提示
|
||||||
|
let dbs = state.monacoOptions.dbTables[db]
|
||||||
|
let tables = dbs ? (dbs[table] || []) : [];
|
||||||
|
if((!tables || tables.length === 0) && db){
|
||||||
|
state.monacoOptions.dbTables[db] = await loadHintTables(db)
|
||||||
|
dbs = state.monacoOptions.dbTables[db]
|
||||||
|
tables = dbs ? (dbs[table] || []) : [];
|
||||||
|
}
|
||||||
|
tables.sort()
|
||||||
|
let suggestions: languages.CompletionItem[] = []
|
||||||
|
tables.forEach((a:string)=>{
|
||||||
|
// 字段数据格式 字段名 字段注释, 如: create_time [datetime][创建时间]
|
||||||
|
let fieldName = a.substring(0,a.indexOf(" "))
|
||||||
|
let comment = a.replace(eval(`/${fieldName}\\s+/`), '')
|
||||||
|
let detail = fieldName + ( comment?' - ' + comment :'' )
|
||||||
|
suggestions.push({
|
||||||
|
label: detail,
|
||||||
|
kind: monaco.languages.CompletionItemKind.Property,
|
||||||
|
detail: detail,
|
||||||
|
insertText: fieldName,
|
||||||
|
range
|
||||||
|
});
|
||||||
|
})
|
||||||
|
return { suggestions }
|
||||||
|
//
|
||||||
|
}
|
||||||
|
return { suggestions:[] }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 库名联想
|
||||||
|
|
||||||
|
let suggestions: languages.CompletionItem[] = []
|
||||||
|
// mysql关键字
|
||||||
sqlLanguage.keywords.forEach((item: any) => {
|
sqlLanguage.keywords.forEach((item: any) => {
|
||||||
suggestions.push({
|
suggestions.push({
|
||||||
label: item,
|
label: item,
|
||||||
@@ -465,6 +560,7 @@ const initMonacoEditor = () => {
|
|||||||
range
|
range
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
// 操作符
|
||||||
sqlLanguage.operators.forEach((item: any) => {
|
sqlLanguage.operators.forEach((item: any) => {
|
||||||
suggestions.push({
|
suggestions.push({
|
||||||
label: item,
|
label: item,
|
||||||
@@ -473,6 +569,7 @@ const initMonacoEditor = () => {
|
|||||||
range
|
range
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
// 内置函数
|
||||||
sqlLanguage.builtinFunctions.forEach((item: any) => {
|
sqlLanguage.builtinFunctions.forEach((item: any) => {
|
||||||
suggestions.push({
|
suggestions.push({
|
||||||
label: item,
|
label: item,
|
||||||
@@ -481,7 +578,8 @@ const initMonacoEditor = () => {
|
|||||||
range
|
range
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
sqlLanguage.builtinVariables.forEach((item: any) => {
|
// 内置变量
|
||||||
|
sqlLanguage.builtinVariables.forEach((item: string) => {
|
||||||
suggestions.push({
|
suggestions.push({
|
||||||
label: item,
|
label: item,
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
kind: monaco.languages.CompletionItemKind.Variable,
|
||||||
@@ -490,6 +588,28 @@ const initMonacoEditor = () => {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 库名提示
|
||||||
|
state.databaseList.forEach(a => {
|
||||||
|
suggestions.push({
|
||||||
|
label: a + ' - schema',
|
||||||
|
kind: monaco.languages.CompletionItemKind.Folder,
|
||||||
|
insertText: a,
|
||||||
|
range
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
// 表名联想
|
||||||
|
state.tableMetadata.forEach((tableMeta: TableMeta) => {
|
||||||
|
const {tableName, tableComment} = tableMeta
|
||||||
|
suggestions.push({
|
||||||
|
label: tableName + ' - ' + tableComment,
|
||||||
|
kind: monaco.languages.CompletionItemKind.File,
|
||||||
|
detail: tableComment,
|
||||||
|
insertText: tableName,
|
||||||
|
range
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
// 默认提示
|
// 默认提示
|
||||||
return {
|
return {
|
||||||
suggestions: suggestions
|
suggestions: suggestions
|
||||||
@@ -497,115 +617,58 @@ const initMonacoEditor = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// monaco.languages.registerCompletionItemProvider('sql', {
|
|
||||||
// provideCompletionItems: (model: ITextModel, position: Position) : languages.ProviderResult<languages.CompletionList> => provideCompletionItems(model, position)
|
|
||||||
// })
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据别名获取sql里的表名
|
||||||
|
* @param sql sql
|
||||||
|
* @param db 默认数据库
|
||||||
|
* @param alias 别名
|
||||||
|
*/
|
||||||
|
const getTableByAlias = (sql: string, db: string, alias: string):{dbName: string, tableName: string} => {
|
||||||
|
|
||||||
const provideCompletionItems= async (model: editor.ITextModel, position: monaco.Position) => {
|
// 表别名:表名
|
||||||
const { lineNumber, column } = position
|
let result = {};
|
||||||
// 光标前文本
|
let defName = '';
|
||||||
const textBeforePointer = model.getValueInRange({
|
let defResult = {};
|
||||||
startLineNumber: lineNumber,
|
// 正则匹配取出表名和表别名
|
||||||
startColumn: 0,
|
// 测试sql
|
||||||
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)
|
`select * from database.Outvisit l
|
||||||
console.log(textBeforePointerMulti)
|
left join patient p on l.patid=p.patientid
|
||||||
console.log(textAfterPointerMulti)
|
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)
|
||||||
|
*/
|
||||||
|
|
||||||
return {
|
let match = sql.match(/(join|from)\s+(\w*-?\w*\.?\w+)\s*(as)?\s*(\w*)/gi)
|
||||||
suggestions: [{
|
if(match && match.length>0){
|
||||||
label:'test',
|
match.forEach(a=>{
|
||||||
kind: 9,
|
// 去掉前缀,取出
|
||||||
insertText:'test',
|
let t = a.substring(5, a.length)
|
||||||
documentation: 'test',
|
.replaceAll(/\s+as\s+/g, ' ')
|
||||||
range: {
|
.split(/\s+/);
|
||||||
startLineNumber: 1,
|
let withDb = t[0].split('.');
|
||||||
startColumn: 1,
|
// 表名是 db名.表名
|
||||||
endLineNumber: 10,
|
let tName = withDb.length > 1 ? withDb[1] : withDb[0]
|
||||||
endColumn: 10,
|
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
|
||||||
// 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(() => {
|
onMounted(() => {
|
||||||
initCodemirror();
|
|
||||||
initMonacoEditor();
|
|
||||||
setHeight();
|
setHeight();
|
||||||
|
initMonacoEditor();
|
||||||
// 监听浏览器窗口大小变化,更新对应组件高度
|
// 监听浏览器窗口大小变化,更新对应组件高度
|
||||||
window.onresize = () =>
|
window.onresize = () =>
|
||||||
(() => {
|
(() => {
|
||||||
@@ -618,10 +681,25 @@ onMounted(() => {
|
|||||||
*/
|
*/
|
||||||
const setHeight = () => {
|
const setHeight = () => {
|
||||||
// 默认300px
|
// 默认300px
|
||||||
codemirror.setSize('auto', `${window.innerHeight - 538}px`);
|
state.monacoOptions.height = window.innerHeight - 550 + 'px'
|
||||||
state.dataTabsTableHeight = window.innerHeight - 274;
|
state.dataTabsTableHeight = window.innerHeight - 274 ;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拖拽改变sql编辑区和查询结果区高度
|
||||||
|
*/
|
||||||
|
const onDragSetHeight = (e: any) => {
|
||||||
|
document.onmousemove = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
//得到鼠标拖动的宽高距离:取绝对值
|
||||||
|
state.monacoOptions.height = `${monacoTextarea.value.offsetHeight + e.movementY}px`
|
||||||
|
state.monacoOptions.tableMaxHeight -= e.movementY
|
||||||
|
}
|
||||||
|
document.onmouseup = () => {
|
||||||
|
document.onmousemove = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 标签更改后的回调事件
|
* 标签更改后的回调事件
|
||||||
*/
|
*/
|
||||||
@@ -650,9 +728,8 @@ const getTags = async () => {
|
|||||||
const onRunSql = async () => {
|
const onRunSql = async () => {
|
||||||
notBlank(state.dbId, '请先选择数据库');
|
notBlank(state.dbId, '请先选择数据库');
|
||||||
// 没有选中的文本,则为全部文本
|
// 没有选中的文本,则为全部文本
|
||||||
let sql = getSql();
|
let sql = getSql() as string;
|
||||||
isTrue(sql && sql.trim(), '请选中需要执行的sql');
|
notBlank(sql && sql.trim(), '请选中需要执行的sql');
|
||||||
|
|
||||||
// 去除字符串前的空格、换行等
|
// 去除字符串前的空格、换行等
|
||||||
sql = sql.replace(/(^\s*)/g, '');
|
sql = sql.replace(/(^\s*)/g, '');
|
||||||
let execRemark = '';
|
let execRemark = '';
|
||||||
@@ -894,7 +971,21 @@ const getColumnTip = (tableName: string, columnName: string) => {
|
|||||||
* 获取sql,如果有鼠标选中,则返回选中内容,否则返回输入框内所有内容
|
* 获取sql,如果有鼠标选中,则返回选中内容,否则返回输入框内所有内容
|
||||||
*/
|
*/
|
||||||
const getSql = () => {
|
const getSql = () => {
|
||||||
return codemirror.getSelection() || codemirror.getValue();
|
let res = '' as string | undefined;
|
||||||
|
// 编辑器还没初始化
|
||||||
|
if(!monacoEditor.getModel){
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
// 选择选中的sql
|
||||||
|
let selection = monacoEditor.getSelection()
|
||||||
|
if (selection){
|
||||||
|
res = monacoEditor.getModel()?.getValueInRange(selection)
|
||||||
|
}
|
||||||
|
// 整个编辑器的sql
|
||||||
|
if(!res){
|
||||||
|
return monacoEditor.getModel()?.getValue()
|
||||||
|
}
|
||||||
|
return res
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -911,26 +1002,29 @@ const changeDbInstance = (dbId: any) => {
|
|||||||
/**
|
/**
|
||||||
* 更改数据库事件
|
* 更改数据库事件
|
||||||
*/
|
*/
|
||||||
const changeDb = (db: string) => {
|
const changeDb = async (db: string) => {
|
||||||
if (!db) {
|
if (!db) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
clearDb();
|
clearDb();
|
||||||
dbApi.tableMetadata.request({ id: state.dbId, db }).then((res) => {
|
|
||||||
state.tableMetadata = res;
|
|
||||||
});
|
|
||||||
|
|
||||||
dbApi.hintTables
|
// 加载数据库下所有表
|
||||||
.request({
|
state.tableMetadata = await loadTableMetadata(db)
|
||||||
id: state.dbId,
|
|
||||||
db,
|
// 加载数据库下所有表字段信息
|
||||||
})
|
state.monacoOptions.dbTables[db] = cmOptions.hintOptions.tables = await loadHintTables(db)
|
||||||
.then((res) => {
|
|
||||||
cmOptions.hintOptions.tables = res;
|
|
||||||
});
|
|
||||||
getSqlNames();
|
getSqlNames();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const loadTableMetadata = async (db: string) =>{
|
||||||
|
return await dbApi.tableMetadata.request({id: state.dbId, db})
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadHintTables = async (db: string) =>{
|
||||||
|
return await dbApi.hintTables.request({id: state.dbId, db,})
|
||||||
|
}
|
||||||
|
|
||||||
// 选择表事件
|
// 选择表事件
|
||||||
const changeTable = async (tableName: string, execSelectSql: boolean = true) => {
|
const changeTable = async (tableName: string, execSelectSql: boolean = true) => {
|
||||||
if (tableName == '') {
|
if (tableName == '') {
|
||||||
@@ -1126,6 +1220,9 @@ const onTableSortChange = async (sort: any) => {
|
|||||||
const changeSqlTemplate = () => {
|
const changeSqlTemplate = () => {
|
||||||
getUserSql();
|
getUserSql();
|
||||||
};
|
};
|
||||||
|
const changeEditorTheme = () => {
|
||||||
|
monaco.editor.setTheme(state.monacoOptions.theme);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取用户保存的sql模板内容
|
* 获取用户保存的sql模板内容
|
||||||
@@ -1134,15 +1231,15 @@ const getUserSql = () => {
|
|||||||
notBlank(state.dbId, '请先选择数据库');
|
notBlank(state.dbId, '请先选择数据库');
|
||||||
dbApi.getSql.request({ id: state.dbId, type: 1, name: state.sqlName, db: state.db }).then((res) => {
|
dbApi.getSql.request({ id: state.dbId, type: 1, name: state.sqlName, db: state.db }).then((res) => {
|
||||||
if (res) {
|
if (res) {
|
||||||
setCodermirrorValue(res.sql);
|
setSqlEditorValue(res.sql);
|
||||||
} else {
|
} else {
|
||||||
setCodermirrorValue('');
|
setSqlEditorValue('');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const setCodermirrorValue = (value: string) => {
|
const setSqlEditorValue = (value: string) => {
|
||||||
codemirror.setValue(value);
|
monacoEditor.getModel()?.setValue(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -1169,8 +1266,8 @@ const getSqlNames = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const saveSql = async () => {
|
const saveSql = async () => {
|
||||||
const sql = codemirror.getValue();
|
const sql = monacoEditor.getModel()?.getValue();
|
||||||
notEmpty(sql, 'sql内容不能为空');
|
notBlank(sql, 'sql内容不能为空');
|
||||||
notBlank(state.dbId, '请先选择数据库实例');
|
notBlank(state.dbId, '请先选择数据库实例');
|
||||||
await dbApi.saveSql.request({ id: state.dbId, db: state.db, sql: sql, type: 1, name: state.sqlName });
|
await dbApi.saveSql.request({ id: state.dbId, db: state.db, sql: sql, type: 1, name: state.sqlName });
|
||||||
ElMessage.success('保存成功');
|
ElMessage.success('保存成功');
|
||||||
@@ -1207,7 +1304,7 @@ const clearDb = () => {
|
|||||||
state.nowTableName = '';
|
state.nowTableName = '';
|
||||||
state.tableMetadata = [];
|
state.tableMetadata = [];
|
||||||
state.dataTabs = {};
|
state.dataTabs = {};
|
||||||
setCodermirrorValue('');
|
setSqlEditorValue('');
|
||||||
state.sqlNames = [];
|
state.sqlNames = [];
|
||||||
state.sqlName = '';
|
state.sqlName = '';
|
||||||
state.activeName = state.queryTab.name;
|
state.activeName = state.queryTab.name;
|
||||||
@@ -1296,19 +1393,21 @@ const cellClick = (row: any, column: any, cell: any) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 转为字符串比较,可能存在数字等
|
// 转为字符串比较,可能存在数字等
|
||||||
let text = (row[property] ? row[property] : '') + '';
|
let text = (row[property] || row[property]==0 ? row[property] : '') + '';
|
||||||
let div = cell.children[0];
|
let div = cell.children[0];
|
||||||
if (div) {
|
if (div) {
|
||||||
let input = document.createElement('input');
|
let input = document.createElement('input');
|
||||||
input.setAttribute('value', text);
|
input.setAttribute('value', text);
|
||||||
// 将表格width也赋值于输入框,避免输入框长度超过表格长度
|
// 将表格width也赋值于输入框,避免输入框长度超过表格长度
|
||||||
input.setAttribute('style', 'height:30px;' + div.getAttribute('style'));
|
input.setAttribute('style', 'height:23px;text-align:center;border:none;' + div.getAttribute('style'));
|
||||||
cell.replaceChildren(input);
|
cell.replaceChildren(input);
|
||||||
input.focus();
|
input.focus();
|
||||||
input.addEventListener('blur', async () => {
|
input.addEventListener('blur', async () => {
|
||||||
row[property] = input.value;
|
row[property] = input.value;
|
||||||
cell.replaceChildren(div);
|
cell.replaceChildren(div);
|
||||||
if (input.value !== text) {
|
if (input.value !== text) {
|
||||||
|
// 设置修改了的字段 背景色
|
||||||
|
// div.setAttribute('style', (div.getAttribute('style')||'')+';background-color:var(--el-color-success)')
|
||||||
const primaryKey = await getColumn(state.nowTableName);
|
const primaryKey = await getColumn(state.nowTableName);
|
||||||
const primaryKeyColumnName = primaryKey.columnName;
|
const primaryKeyColumnName = primaryKey.columnName;
|
||||||
// 更新字段列信息
|
// 更新字段列信息
|
||||||
@@ -1316,7 +1415,10 @@ const cellClick = (row: any, column: any, cell: any) => {
|
|||||||
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])}`;
|
WHERE ${primaryKeyColumnName} = ${wrapColumnValue(primaryKey, row[primaryKeyColumnName])}`;
|
||||||
promptExeSql(sql, () => {
|
promptExeSql(sql, () => {
|
||||||
|
// 还原值
|
||||||
row[property] = text;
|
row[property] = text;
|
||||||
|
// 还原背景色
|
||||||
|
// div.setAttribute('style', (div.getAttribute('style')||'')+';background-color:inherit')
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1387,9 +1489,11 @@ const addRow = async () => {
|
|||||||
* 格式化sql
|
* 格式化sql
|
||||||
*/
|
*/
|
||||||
const formatSql = () => {
|
const formatSql = () => {
|
||||||
let selectSql = codemirror.getSelection();
|
let selectSql = getSql();
|
||||||
isTrue(selectSql, '请选中需要格式化的sql');
|
if(selectSql){
|
||||||
codemirror.replaceSelection(sqlFormatter(selectSql));
|
monacoEditor.getModel()?.setValue(sqlFormatter(selectSql))
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const search = async () => {
|
const search = async () => {
|
||||||
@@ -1400,7 +1504,7 @@ const search = async () => {
|
|||||||
// 加载选中的db
|
// 加载选中的db
|
||||||
const setSelects = async (sqlExecInfo: any) => {
|
const setSelects = async (sqlExecInfo: any) => {
|
||||||
// 保存sql
|
// 保存sql
|
||||||
let sql = codemirror?.getValue();
|
let sql = getSql();
|
||||||
if (sql && sql.length > 0 && state.dbId) {
|
if (sql && sql.length > 0 && state.dbId) {
|
||||||
await saveSql();
|
await saveSql();
|
||||||
}
|
}
|
||||||
@@ -1414,7 +1518,7 @@ const setSelects = async (sqlExecInfo: any) => {
|
|||||||
state.dbId = dbId;
|
state.dbId = dbId;
|
||||||
state.db = db;
|
state.db = db;
|
||||||
// 加载schema下所有表
|
// 加载schema下所有表
|
||||||
changeDb(db);
|
await changeDb(db);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 判断如果有数据则加载下拉选项
|
// 判断如果有数据则加载下拉选项
|
||||||
@@ -1447,6 +1551,12 @@ watch(store.state.sqlExecInfo, async (newValue) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.editor-move-resize {
|
||||||
|
cursor: n-resize;
|
||||||
|
height: 3px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.el-tabs__header {
|
.el-tabs__header {
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ const SqlExecBox = (props: SqlExecProps): void => {
|
|||||||
if (boxInstance) {
|
if (boxInstance) {
|
||||||
const boxVue = boxInstance.component
|
const boxVue = boxInstance.component
|
||||||
// 调用open方法显示弹框,注意不能使用boxVue.ctx来调用组件函数(build打包后ctx会获取不到)
|
// 调用open方法显示弹框,注意不能使用boxVue.ctx来调用组件函数(build打包后ctx会获取不到)
|
||||||
boxVue.proxy.open(props);
|
boxVue.exposed.open(props);
|
||||||
} else {
|
} else {
|
||||||
boxInstance = renderBox()
|
boxInstance = renderBox()
|
||||||
SqlExecBox(props)
|
SqlExecBox(props)
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<el-dialog title="待执行SQL" v-model="dialogVisible" :show-close="false" width="600px">
|
<el-dialog title="待执行SQL" v-model="dialogVisible" :show-close="false" width="600px" @close="cancel">
|
||||||
如需执行多条sql,需要在【数据库管理】配置连接参数:multiStatements=true
|
|
||||||
<codemirror height="350px" class="codesql" ref="cmEditor" language="sql" v-model="sqlValue"
|
<codemirror height="350px" class="codesql" ref="cmEditor" language="sql" v-model="sqlValue"
|
||||||
:options="cmOptions" />
|
:options="cmOptions" />
|
||||||
<el-input ref="remarkInputRef" v-model="remark" placeholder="请输入执行备注" class="mt5" />
|
<el-input ref="remarkInputRef" v-model="remark" placeholder="请输入执行备注" class="mt5" />
|
||||||
@@ -90,14 +89,22 @@ const runSql = async () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
state.btnLoading = true;
|
state.btnLoading = true;
|
||||||
await dbApi.sqlExec.request({
|
const res = await dbApi.sqlExec.request({
|
||||||
id: state.dbId,
|
id: state.dbId,
|
||||||
db: state.db,
|
db: state.db,
|
||||||
remark: state.remark,
|
remark: state.remark,
|
||||||
sql: state.sqlValue.trim(),
|
sql: state.sqlValue.trim(),
|
||||||
});
|
});
|
||||||
ElMessage.success('执行成功');
|
|
||||||
|
for (let re of res.res) {
|
||||||
|
if (re.result !== 'success') {
|
||||||
|
ElMessage.error(`${re.sql} \n执行失败: ${re.result}`);
|
||||||
|
throw new Error(re.result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
runSuccess = true;
|
runSuccess = true;
|
||||||
|
ElMessage.success('执行成功');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
runSuccess = false;
|
runSuccess = false;
|
||||||
}
|
}
|
||||||
@@ -139,6 +146,8 @@ const open = (props: SqlExecProps) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
defineExpose({ open })
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.codesql {
|
.codesql {
|
||||||
|
|||||||
Reference in New Issue
Block a user