[Core] [AI] Support chatgpt for query

This commit is contained in:
qianmoQ 2023-03-14 22:15:11 +08:00
parent 1315fa0da4
commit 5e97bc0ae9
8 changed files with 195 additions and 15 deletions

View File

@ -30,6 +30,7 @@
"vue-clipboard3": "^2.0.0",
"vue-i18n": "^9.2.2",
"vue-router": "^4.0.3",
"vue3-markdown": "^1.1.7",
"watermark-dom": "^2.3.0",
"webpack-bundle-analyzer": "^4.6.1"
},

View File

@ -0,0 +1,6 @@
export default {
optimizeSQL: 'Optimize SQL',
optimizeSQLContent: 'Help me optimize the following SQL, the format is markdown',
analysisSQL: 'Analysis SQL',
analysisSQLContent: 'Help me analyze the following SQL, the format is markdown',
}

View File

@ -8,6 +8,7 @@ import grid from "@/i18n/langs/en/grid";
import signup from "@/i18n/langs/en/signup";
import alert from "@/i18n/langs/en/alert";
import monitor from "@/i18n/langs/en/monitor";
import ai from "@/i18n/langs/en/ai";
export default {
...en,
@ -19,5 +20,6 @@ export default {
grid: grid,
signup: signup,
alert: alert,
monitor: monitor
monitor: monitor,
ai: ai
}

View File

@ -0,0 +1,6 @@
export default {
optimizeSQL: '优化 SQL',
optimizeSQLContent: '帮我优化以下SQL,格式为markdown',
analysisSQL: '解析 SQL',
analysisSQLContent: '帮我解析以下SQL,格式为markdown',
}

View File

@ -8,6 +8,7 @@ import grid from "@/i18n/langs/zhCn/grid";
import signup from "@/i18n/langs/zhCn/signup";
import alert from "@/i18n/langs/zhCn/alert";
import monitor from "@/i18n/langs/zhCn/monitor";
import ai from "@/i18n/langs/zhCn/ai";
export default {
...zh,
@ -19,5 +20,6 @@ export default {
grid: grid,
signup: signup,
alert: alert,
monitor: monitor
monitor: monitor,
ai: ai
}

View File

@ -5,6 +5,7 @@ import router from "./router";
import ViewUIPlus from 'view-ui-plus';
import 'view-ui-plus/dist/styles/viewuiplus.css';
import VMdEditor from '@kangc/v-md-editor';
import VMdPreview from '@kangc/v-md-editor/lib/preview';
import '@kangc/v-md-editor/lib/style/preview.css';
import githubTheme from '@kangc/v-md-editor/lib/theme/github';
@ -18,5 +19,6 @@ const app = createApp(App);
app.use(router);
app.use(ViewUIPlus);
app.use(i18n);
app.use(VMdEditor);
app.use(VMdPreview);
app.mount("#app");

View File

@ -0,0 +1,146 @@
<template>
<div>
<Modal title="AI" width="80%" :closable="false" v-model="visible" :maskClosable="false" :z-index="9">
<div style="height: 350px; max-height: 350px;">
<Tabs v-if="!loading" :animated="false" @on-click="handlerTab($event)">
<TabPane :label="$t('ai.optimizeSQL')" name="OPTIMIZE" :disabled="actionLoading">
<div style="height: 300px; max-height: 350px; overflow: auto;">
<Skeleton v-if="actionLoading" loading :title="false" :animated="true"
:paragraph="{ rows: 5, width: [100, 200, '300px', '50%', '62%'] }"/>
<VMarkdownView v-else :mode="'light'" :content="finalContent"></VMarkdownView>
</div>
</TabPane>
<TabPane :label="$t('ai.analysisSQL')" name="ANALYSIS" :disabled="actionLoading">
<div style="height: 300px; max-height: 350px; overflow: auto;">
<Skeleton v-if="actionLoading" loading :title="false" :animated="true"
:paragraph="{ rows: 5, width: [100, 200, '300px', '50%', '62%'] }"/>
<VMarkdownView v-else :mode="'light'" :content="finalContent"></VMarkdownView>
</div>
</TabPane>
</Tabs>
</div>
<div class="datacap-drawer-footer">
<Button type="error" :disabled="actionLoading" @click="handlerCancel()">{{ $t('common.cancel') }}</Button>
</div>
<Spin size="large" fix :show="loading"></Spin>
</Modal>
</div>
</template>
<script lang="ts">
import {defineComponent} from 'vue';
import UserService from "@/services/UserService";
import {User, UserQuestion} from "@/model/User";
import {VMarkdownView} from 'vue3-markdown'
import 'vue3-markdown/dist/style.css'
import {Skeleton} from "view-ui-plus";
export enum TabType
{
OPTIMIZE = ('OPTIMIZE'),
ANALYSIS = ('ANALYSIS')
}
export default defineComponent({
name: 'QueryAiHelp',
props: {
isVisible: {
type: Boolean,
default: () => false
},
content: {
type: String,
default: () => ''
}
},
components: {Skeleton, VMarkdownView},
data()
{
return {
loading: false,
actionLoading: false,
userInfo: null as User,
finalContent: null
}
},
created()
{
this.handlerInitialize();
},
methods: {
handlerInitialize()
{
this.loading = true;
UserService.getInfo()
.then(response => {
if (response.status) {
this.userInfo = response.data;
this.handlerTab(TabType.OPTIMIZE);
}
})
.finally(() => {
this.loading = false;
});
},
handlerTab(value: TabType)
{
this.finalContent = null;
let message = '';
if (value === TabType.OPTIMIZE) {
message += this.$t('ai.optimizeSQLContent');
message += '\n';
message += this.content;
}
else if (value === TabType.ANALYSIS) {
message += this.$t('ai.analysisSQLContent');
message += '\n';
message += this.content;
}
this.actionLoading = true;
const userQuestion = new UserQuestion();
userQuestion.type = 'ChatGPT';
userQuestion.question = message;
UserService.startChat(userQuestion)
.then(response => {
if (response.status) {
this.finalContent = response.data.toString();
}
else {
this.$Message.error(response.message);
}
})
.finally(() => {
this.actionLoading = false;
});
},
handlerCancel()
{
this.visible = false;
}
},
computed: {
visible: {
get(): boolean
{
return this.isVisible
},
set(value: boolean)
{
this.$emit('close', value)
}
}
},
});
</script>
<style scoped>
.datacap-drawer-footer {
width: 100%;
position: absolute;
bottom: 0;
left: 0;
border-top: 0px solid #e8e8e8;
padding: 10px 16px;
text-align: right;
background: #fff;
}
</style>

View File

@ -14,11 +14,11 @@
<Card style="width:100%">
<template #title>
<Space>
<Button type="primary" size="small" :loading="tableLoading" :disabled="!applySource"
<Button type="primary" size="small" :loading="tableLoading" :disabled="!applySource || !activeEditorValue"
icon="md-arrow-dropright-circle" @click="handlerRun()">
{{ $t('common.run') }}
</Button>
<Button type="dashed" size="small" :disabled="!applySource" icon="md-code" @click="handlerFormat()">
<Button type="dashed" size="small" :disabled="!applySource || !activeEditorValue" icon="md-code" @click="handlerFormat()">
{{ $t('common.format') }}
</Button>
<Button type="error" size="small" :disabled="!applySource || !tableLoading"
@ -54,6 +54,12 @@
</Space>
</template>
</Poptip>
<Badge v-if="applySource && activeEditorValue">
<Button type="primary" size="small" icon="md-ionitron" @click="handlerVisibleHelp(true)"></Button>
<template #count>
<Icon type="md-help-circle" color="#ed4014" size="16"/>
</template>
</Badge>
</Space>
</template>
<div ref="editorContainer">
@ -64,8 +70,8 @@
<TabPane v-for="editor in editors" :key="editor.key" :name="editor.key" :label="editor.title" :closable="editor.closable">
</TabPane>
<MonacoEditor theme="vs" :options="{theme: 'vs-dark', fontSize: 15}" language="sql" :height="300"
:key="activeKey.value" @change="handlerChangeEditorValue" :width="'100%'"
v-model:value="activeEditorValue" @editorDidMount="handlerEditorDidMount($event, 'mysql')">
:key="activeKey.value" @change="handlerChangeEditorValue" :width="'100%'"
v-model:value="activeEditorValue" @editorDidMount="handlerEditorDidMount($event, 'mysql')">
</MonacoEditor>
</Tabs>
</div>
@ -79,6 +85,7 @@
<SnippetDetails v-if="snippetDetails" :isVisible="snippetDetails"
:codeSnippet="activeEditorValue" @close="handlerCloseSnippetDetails($event)">
</SnippetDetails>
<QueryAiHelp v-if="visibleAiHelp" :isVisible="visibleAiHelp" :content="activeEditorValue" @close="handlerVisibleHelp($event)"></QueryAiHelp>
</div>
</template>
@ -100,6 +107,7 @@ import {AuditService} from "@/services/AuditService";
import FunctionsService from "@/services/settings/functions/FunctionsService";
import {useI18n} from "vue-i18n";
import DataLazyTree from "@/components/common/DataLazyTree.vue";
import QueryAiHelp from "@/views/pages/query/QueryAiHelp.vue";
const editors = ref<{ title: string; key: string; closable?: boolean }[]>([
{title: 'Editor', key: '1', closable: false}
@ -110,7 +118,7 @@ const editorValueMap = new Map<string, string>();
export default defineComponent({
name: "QueryHome",
components: {DataLazyTree, BasicTableComponent, SnippetDetails, SourceSelect, MonacoEditor},
components: {QueryAiHelp, DataLazyTree, BasicTableComponent, SnippetDetails, SourceSelect, MonacoEditor},
unmounted()
{
if (this.editorCompletionProvider) {
@ -140,17 +148,19 @@ export default defineComponent({
activeEditorValue: '',
editors,
activeKey,
editorValueMap
editorValueMap,
visibleAiHelp: false
}
},
created()
{
this.handlerInitialize();
},
mounted(){
window.onresize=()=>{
if(editorMap.values().next().value){
editorMap.values().next().value.layout({width: this.$refs.editorContainer.offsetWidth,height:300})
mounted()
{
window.onresize = () => {
if (editorMap.values().next().value) {
editorMap.values().next().value.layout({width: this.$refs.editorContainer.offsetWidth, height: 300})
}
}
},
@ -236,9 +246,9 @@ export default defineComponent({
});
}
});
setTimeout(()=>{
editorMap.values().next().value?.layout({width: this.$refs.editorContainer.offsetWidth,height:300})
},200)
setTimeout(() => {
editorMap.values().next().value?.layout({width: this.$refs.editorContainer.offsetWidth, height: 300})
}, 200)
},
handlerRun()
{
@ -348,6 +358,11 @@ export default defineComponent({
handlerChangeEditorValue(value: string)
{
editorValueMap.set(activeKey.value, value);
},
handlerVisibleHelp(value: boolean)
{
this.visibleAiHelp = value;
console.log(1)
}
},
// Prevents errors from affecting other components