|
@@ -46,7 +46,7 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
- <template v-else-if="Object.keys(item.content).length === 0">
|
|
|
+ <template v-else-if="item.progress && item.progress.length === 0">
|
|
|
<span>
|
|
|
正在思考中
|
|
|
<v-progress-circular
|
|
@@ -58,14 +58,19 @@
|
|
|
></v-progress-circular>
|
|
|
</span>
|
|
|
</template>
|
|
|
- <template v-else>
|
|
|
+ <template v-if="item.progress && item.progress.length > 0">
|
|
|
+ <div class="blue-grey lighten-5 text-caption pa-3">
|
|
|
+ <p v-for="_p in item.progress" :key="_p" class="mb-1">{{ _p }}</p>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <template v-if="typeof item.content === 'object' && item.content?.response">
|
|
|
<div>
|
|
|
{{ item.content.response }}
|
|
|
</div>
|
|
|
<div v-if="item.showSnackbar" class="pa-3 blue-grey lighten-3 mt-3">
|
|
|
{{ item.content.sql }}
|
|
|
</div>
|
|
|
- <div class="mt-3" v-if="item.content.records && item.content.records.columns.length">
|
|
|
+ <div class="mt-3" v-if="item.content.query_result && item.content.query_result.columns.length">
|
|
|
<div>
|
|
|
<v-menu
|
|
|
v-model="item.showMenu"
|
|
@@ -87,7 +92,7 @@
|
|
|
<div class="pa-3">
|
|
|
<v-autocomplete
|
|
|
v-model="item.model.typeAxis"
|
|
|
- :items="item.content.records.columns"
|
|
|
+ :items="item.content.query_result.columns"
|
|
|
class="mb-3"
|
|
|
outlined
|
|
|
dense
|
|
@@ -97,7 +102,7 @@
|
|
|
|
|
|
<v-autocomplete
|
|
|
v-model="item.model.dataAxis"
|
|
|
- :items="item.content.records.columns"
|
|
|
+ :items="item.content.query_result.columns"
|
|
|
class="mb-3"
|
|
|
outlined
|
|
|
dense
|
|
@@ -126,7 +131,7 @@
|
|
|
<thead>
|
|
|
<tr>
|
|
|
<th
|
|
|
- v-for="header in item.content.records.columns"
|
|
|
+ v-for="header in item.content.query_result.columns"
|
|
|
:key="header"
|
|
|
class="text-left"
|
|
|
>{{ header }}</th>
|
|
@@ -134,11 +139,11 @@
|
|
|
</thead>
|
|
|
<tbody>
|
|
|
<tr
|
|
|
- v-for="(row, index) in item.content.records.rows"
|
|
|
+ v-for="(row, index) in item.content.query_result.rows"
|
|
|
:key="index"
|
|
|
>
|
|
|
<td
|
|
|
- v-for="header in item.content.records.columns"
|
|
|
+ v-for="header in item.content.query_result.columns"
|
|
|
:key="header"
|
|
|
class="text-left"
|
|
|
>{{ row[header] }}</td>
|
|
@@ -205,7 +210,7 @@
|
|
|
<v-icon>mdi-send</v-icon>
|
|
|
</v-btn>
|
|
|
</div>
|
|
|
- <div>
|
|
|
+ <div v-if="!react">
|
|
|
<v-chip-group
|
|
|
active-class="primary--text"
|
|
|
column
|
|
@@ -228,8 +233,8 @@
|
|
|
|
|
|
<script>
|
|
|
import {
|
|
|
- getAsk,
|
|
|
- getAskThroughReact,
|
|
|
+ // getAsk,
|
|
|
+ // getAskThroughReact,
|
|
|
addFeedback
|
|
|
} from '@/api/dataChart'
|
|
|
import { mapGetters } from 'vuex'
|
|
@@ -263,13 +268,17 @@ export default {
|
|
|
}
|
|
|
],
|
|
|
abortController: null,
|
|
|
- conversationId: undefined
|
|
|
+ conversationId: undefined,
|
|
|
// trueData: false
|
|
|
+ eventSource: null
|
|
|
}
|
|
|
},
|
|
|
computed: {
|
|
|
...mapGetters(['userInfo'])
|
|
|
},
|
|
|
+ beforeDestroy () {
|
|
|
+ this.stopSSE()
|
|
|
+ },
|
|
|
methods: {
|
|
|
onNew () {
|
|
|
if (this.abortController) {
|
|
@@ -290,6 +299,63 @@ export default {
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
+ startSSE (ask) {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ // 先关闭现有连接
|
|
|
+ this.stopSSE()
|
|
|
+ const query = this.react
|
|
|
+ ? {
|
|
|
+ question: encodeURIComponent(ask.question),
|
|
|
+ user_id: this.userInfo.id,
|
|
|
+ conversation_id: this.conversationId
|
|
|
+ }
|
|
|
+ : {
|
|
|
+ question: encodeURIComponent(ask.question),
|
|
|
+ user_id: this.userInfo.id,
|
|
|
+ routing_mode: this.routingMode,
|
|
|
+ conversation_id: this.conversationId
|
|
|
+ }
|
|
|
+ const queryStr = Object.keys(query).reduce((acc, key) => {
|
|
|
+ if (query[key] === undefined) {
|
|
|
+ return acc
|
|
|
+ }
|
|
|
+ acc.push(`${key}=${query[key]}`)
|
|
|
+ return acc
|
|
|
+ }, []).join('&')
|
|
|
+ // 构建带参数的URL
|
|
|
+ const url = `/api/vanna/v0/${this.react ? 'ask_react_agent_stream' : 'ask_agent_stream'}?${queryStr}`
|
|
|
+ // 创建EventSource连接
|
|
|
+ // console.log('开始连接:', url)
|
|
|
+ this.eventSource = new EventSource(url)
|
|
|
+ // 消息处理
|
|
|
+ this.eventSource.onmessage = (event) => {
|
|
|
+ const obj = JSON.parse(event.data)
|
|
|
+ // console.log(obj)
|
|
|
+ ask.progress.push(obj.message)
|
|
|
+ // this.streamData += event.data + '\n'
|
|
|
+ if (obj.data.type === 'completed') {
|
|
|
+ ask.content = obj.data
|
|
|
+ this.conversationId = obj.data.conversation_id
|
|
|
+ this.stopSSE()
|
|
|
+ resolve(obj.data)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ this.eventSource.onerror = (e) => {
|
|
|
+ console.log('SSE错误:', e)
|
|
|
+ reject(e)
|
|
|
+ // 自动重连逻辑
|
|
|
+ // if (e.eventPhase === EventSource.CLOSED) {
|
|
|
+ // setTimeout(() => this.startSSE(), 5000)
|
|
|
+ // }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ stopSSE () {
|
|
|
+ if (this.eventSource) {
|
|
|
+ this.eventSource.close()
|
|
|
+ this.eventSource = null
|
|
|
+ }
|
|
|
+ },
|
|
|
onSend (str) {
|
|
|
if (this.disabled) {
|
|
|
return
|
|
@@ -313,6 +379,7 @@ export default {
|
|
|
const ask = {
|
|
|
type: 1,
|
|
|
content: {},
|
|
|
+ progress: [],
|
|
|
showSnackbar: false,
|
|
|
dataValidation: false,
|
|
|
question, // 记录当前问题
|
|
@@ -325,25 +392,27 @@ export default {
|
|
|
this.items.push(ask)
|
|
|
this.question = ''
|
|
|
try {
|
|
|
- this.abortController = new AbortController()
|
|
|
- const getApi = this.react ? getAskThroughReact : getAsk
|
|
|
- const query = this.react
|
|
|
- ? {
|
|
|
- question,
|
|
|
- user_id: this.userInfo.id,
|
|
|
- conversation_id: this.conversationId
|
|
|
- }
|
|
|
- : {
|
|
|
- question,
|
|
|
- user_id: this.userInfo.id,
|
|
|
- routing_mode: this.routingMode,
|
|
|
- conversation_id: this.conversationId
|
|
|
- }
|
|
|
- const { data } = await getApi(query, {
|
|
|
- signal: this.abortController.signal
|
|
|
- })
|
|
|
- ask.content = data
|
|
|
- this.conversationId = data.conversation_id
|
|
|
+ // this.abortController = new AbortController()
|
|
|
+ // const getApi = this.react ? getAskThroughReact : getAskToSSE
|
|
|
+ // const query = this.react
|
|
|
+ // ? {
|
|
|
+ // question,
|
|
|
+ // user_id: this.userInfo.id,
|
|
|
+ // conversation_id: this.conversationId
|
|
|
+ // }
|
|
|
+ // : {
|
|
|
+ // question,
|
|
|
+ // user_id: this.userInfo.id,
|
|
|
+ // routing_mode: this.routingMode,
|
|
|
+ // conversation_id: this.conversationId
|
|
|
+ // }
|
|
|
+ // const { data } = await getApi(query, {
|
|
|
+ // signal: this.abortController.signal
|
|
|
+ // })
|
|
|
+ // console.log(data)
|
|
|
+ // ask.content = data
|
|
|
+ // this.conversationId = data.conversation_id
|
|
|
+ await this.startSSE(ask)
|
|
|
this.scrollToBottom()
|
|
|
} catch (error) {
|
|
|
ask.content = error.message ?? error
|
|
@@ -384,8 +453,8 @@ export default {
|
|
|
}
|
|
|
const { typeAxis, dataAxis } = model
|
|
|
const data = {
|
|
|
- type: typeAxis ? content.records.rows.map(e => e[typeAxis]) : [],
|
|
|
- data: dataAxis ? dataAxis.map(e => content.records.rows.map(r => r[e])) : []
|
|
|
+ type: typeAxis ? content.query_result.rows.map(e => e[typeAxis]) : [],
|
|
|
+ data: dataAxis ? dataAxis.map(e => content.query_result.rows.map(r => r[e])) : []
|
|
|
}
|
|
|
this.$emit('render', data)
|
|
|
},
|