|
@@ -35,7 +35,7 @@
|
|
|
`${talk.type === 'question' ? 'justify-end ' + user.nameColor + '--text' : system.nameColor + '--text'}`
|
|
|
"
|
|
|
>
|
|
|
- {{ talk.type === 'question' ? $store.getters.userInfo.username : '' }}
|
|
|
+ {{ talk.type === 'question' ? formatName(baseInfo?.name ?? baseInfo?.phone) : '' }}
|
|
|
<template v-if="talk.type === 'question'">
|
|
|
<v-chip v-for="tag in talk.tags" :key="tag" x-small class="ml-2">{{ tag }}</v-chip>
|
|
|
</template>
|
|
@@ -75,7 +75,6 @@
|
|
|
</template>
|
|
|
<template v-else>
|
|
|
<div v-for="(content, index) in talk.content" :key="index + content">{{ content }}</div>
|
|
|
- <!-- {{ talk.content }} -->
|
|
|
</template>
|
|
|
</div>
|
|
|
</v-list-item-subtitle>
|
|
@@ -125,222 +124,194 @@
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
-<script>
|
|
|
+<script setup>
|
|
|
+import { ref, watch, nextTick } from 'vue'
|
|
|
import { api } from '@/api/dataGovernance'
|
|
|
-export default {
|
|
|
- name: 'knowledge-talk',
|
|
|
- props: {
|
|
|
- // 1 手册探索 2 图表实验室 3 产品知识库 (RAG + Graph) 4 产品知识库 (RAG)
|
|
|
- type: {
|
|
|
- type: Number,
|
|
|
- default: 1
|
|
|
- },
|
|
|
- title: {
|
|
|
- type: String,
|
|
|
- default: '数据探索'
|
|
|
- },
|
|
|
- welcome: {
|
|
|
- type: String,
|
|
|
- default: '您好,我是您的智能助手,有什么问题可以问我哦!'
|
|
|
- }
|
|
|
+import { formatName } from '@/utils/getText'
|
|
|
+import Snackbar from '@/plugins/snackbar'
|
|
|
+
|
|
|
+const props = defineProps({
|
|
|
+ // 1 手册探索 2 图表实验室 3 产品知识库 (RAG + Graph) 4 产品知识库 (RAG)
|
|
|
+ type: {
|
|
|
+ type: Number,
|
|
|
+ default: 1
|
|
|
},
|
|
|
- data () {
|
|
|
- return {
|
|
|
- // 系统端信息配置
|
|
|
- system: {
|
|
|
- name: '智能助手',
|
|
|
- nameColor: 'indigo',
|
|
|
- icon: 'mdi-face-agent',
|
|
|
- iconColor: 'indigo',
|
|
|
- bgColor: 'indigo',
|
|
|
- welcome: this.welcome
|
|
|
- },
|
|
|
- // 用户端信息配置
|
|
|
- user: {
|
|
|
- name: this.$store.getters.userInfo.username,
|
|
|
- nameColor: 'blue',
|
|
|
- icon: 'mdi-account-circle',
|
|
|
- iconColor: 'blue',
|
|
|
- bgColor: 'blue'
|
|
|
- },
|
|
|
- loading: false,
|
|
|
- talks: [],
|
|
|
- question: '',
|
|
|
- list: [],
|
|
|
- id: 0
|
|
|
- }
|
|
|
+ title: {
|
|
|
+ type: String,
|
|
|
+ default: '数据探索'
|
|
|
},
|
|
|
- watch: {
|
|
|
- // 长度变化自动滑动至底部
|
|
|
- talks: {
|
|
|
- handler () {
|
|
|
- this.$nextTick(() => {
|
|
|
- this.$refs.box.scrollTo({
|
|
|
- top: this.$refs.box.scrollHeight,
|
|
|
- behavior: 'smooth'
|
|
|
- })
|
|
|
- })
|
|
|
- },
|
|
|
- deep: true,
|
|
|
- immediate: true
|
|
|
+ welcome: {
|
|
|
+ type: String,
|
|
|
+ default: '您好,我是您的智能助手,有什么问题可以问我哦!'
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+// 企业基本信息
|
|
|
+const baseInfo = ref(JSON.parse(localStorage.getItem('entBaseInfo')) || {})
|
|
|
+
|
|
|
+const box = ref(null)
|
|
|
+const loading = ref(false)
|
|
|
+const talks = ref([])
|
|
|
+const question = ref('')
|
|
|
+const list = ref([])
|
|
|
+const id = ref(0)
|
|
|
+
|
|
|
+// 系统端信息配置
|
|
|
+const system = ref({
|
|
|
+ name: '智能助手',
|
|
|
+ nameColor: 'indigo',
|
|
|
+ icon: 'mdi-face-agent',
|
|
|
+ iconColor: 'indigo',
|
|
|
+ bgColor: 'indigo',
|
|
|
+ welcome: props.welcome
|
|
|
+})
|
|
|
+
|
|
|
+// 用户端信息配置
|
|
|
+const user = ref({
|
|
|
+ name: formatName(baseInfo.value?.name ?? baseInfo.value?.phone),
|
|
|
+ nameColor: 'blue',
|
|
|
+ icon: 'mdi-account-circle',
|
|
|
+ iconColor: 'blue',
|
|
|
+ bgColor: 'blue'
|
|
|
+})
|
|
|
+
|
|
|
+// 监听talks变化,自动滚动到底部
|
|
|
+watch(talks, () => {
|
|
|
+ nextTick(() => {
|
|
|
+ box.value.scrollTo({
|
|
|
+ top: box.value.scrollHeight,
|
|
|
+ behavior: 'smooth'
|
|
|
+ })
|
|
|
+ })
|
|
|
+}, { deep: true, immediate: true })
|
|
|
+
|
|
|
+const handleSendMsg = () => {
|
|
|
+ if (!question.value) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ switch (props.type) {
|
|
|
+ case 2:
|
|
|
+ getSql()
|
|
|
+ break
|
|
|
+ case 4:
|
|
|
+ talkTo(3, true)
|
|
|
+ break
|
|
|
+ default:
|
|
|
+ talkTo(props.type)
|
|
|
+ break
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const handleKeyCode = (event) => {
|
|
|
+ if (event.keyCode === 13) {
|
|
|
+ if (!event.ctrlKey) {
|
|
|
+ event.preventDefault()
|
|
|
+ handleSendMsg()
|
|
|
+ } else {
|
|
|
+ question.value += '\n'
|
|
|
}
|
|
|
- },
|
|
|
- methods: {
|
|
|
- handleSendMsg () {
|
|
|
- if (!this.question) {
|
|
|
- return
|
|
|
- }
|
|
|
- switch (this.type) {
|
|
|
- case 2:
|
|
|
- this.getSql()
|
|
|
- break
|
|
|
- case 4:
|
|
|
- this.talkTo(3, true)
|
|
|
- break
|
|
|
- default:
|
|
|
- this.talkTo(this.type)
|
|
|
- break
|
|
|
- }
|
|
|
- },
|
|
|
- handleKeyCode (event) {
|
|
|
- if (event.keyCode === 13) {
|
|
|
- if (!event.ctrlKey) {
|
|
|
- event.preventDefault()
|
|
|
- this.handleSendMsg()
|
|
|
- } else {
|
|
|
- this.question += '\n'
|
|
|
- }
|
|
|
- }
|
|
|
- },
|
|
|
- async getSql () {
|
|
|
- this.loading = true
|
|
|
- this.talks.push({
|
|
|
- id: this.id++,
|
|
|
- type: 'question',
|
|
|
- tags: [],
|
|
|
- content: this.question.split('\n')
|
|
|
- })
|
|
|
- const params = {
|
|
|
- question: this.question
|
|
|
- }
|
|
|
- this.question = ''
|
|
|
- try {
|
|
|
- const { data } = await api.getSql(params)
|
|
|
- this.talks.push({
|
|
|
- id: this.id++,
|
|
|
- type: 'response',
|
|
|
- tags: [],
|
|
|
- content: data.text.split('\n')
|
|
|
- })
|
|
|
- this.getTable(data.id)
|
|
|
- } catch (error) {
|
|
|
- this.$snackbar.error(error)
|
|
|
- } finally {
|
|
|
- this.loading = false
|
|
|
- }
|
|
|
- },
|
|
|
- async getTable (id) {
|
|
|
- this.loading = true
|
|
|
- const params = { id }
|
|
|
- try {
|
|
|
- const { data } = await api.getToTable(params)
|
|
|
- if (data.type === 'error') {
|
|
|
- return
|
|
|
- }
|
|
|
- const _table = JSON.parse(data.df)
|
|
|
- if (!_table.length) {
|
|
|
- return
|
|
|
- }
|
|
|
- // console.log(_table)
|
|
|
- const _headers = Object.keys(_table[0])
|
|
|
- this.talks.push({
|
|
|
- id: this.id++,
|
|
|
- type: 'response',
|
|
|
- tags: [],
|
|
|
- content: {
|
|
|
- headers: _headers,
|
|
|
- body: _table
|
|
|
- },
|
|
|
- view: 'table'
|
|
|
- })
|
|
|
- this.$emit('change', _headers, _table)
|
|
|
- } catch (error) {
|
|
|
- this.$snackbar.error(error)
|
|
|
- } finally {
|
|
|
- this.loading = false
|
|
|
- }
|
|
|
- },
|
|
|
- async talkTo (useType, rag) {
|
|
|
- this.talks.push({
|
|
|
- id: this.id++,
|
|
|
- type: 'question',
|
|
|
- tags: [],
|
|
|
- content: this.question.split('\n')
|
|
|
- })
|
|
|
- const param = {
|
|
|
- question: this.question,
|
|
|
- chat_history: this.list
|
|
|
- }
|
|
|
- this.question = ''
|
|
|
- this.loading = true
|
|
|
- const askApi = useType === 3 ? api.askToUnstructured : api.ask
|
|
|
- try {
|
|
|
- const { data } = await askApi(param)
|
|
|
- this.talks.push({
|
|
|
- id: this.id++,
|
|
|
- type: 'response',
|
|
|
- tags: useType === 3 ? ['RAG + Graph'] : [],
|
|
|
- content: data.response.split('\n')
|
|
|
- })
|
|
|
- // 多获取一个图谱+RAG
|
|
|
- if (rag) {
|
|
|
- const { data: _data } = await api.askToProduct(param)
|
|
|
- this.talks.push({
|
|
|
- id: this.id++,
|
|
|
- type: 'response',
|
|
|
- tags: ['RAG'],
|
|
|
- content: _data.response.split('\n')
|
|
|
- })
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- this.$snackbar.error(error)
|
|
|
- } finally {
|
|
|
- this.loading = false
|
|
|
- }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const getSql = async () => {
|
|
|
+ loading.value = true
|
|
|
+ talks.value.push({
|
|
|
+ id: id.value++,
|
|
|
+ type: 'question',
|
|
|
+ tags: [],
|
|
|
+ content: question.value.split('\n')
|
|
|
+ })
|
|
|
+ const params = {
|
|
|
+ question: question.value
|
|
|
+ }
|
|
|
+ question.value = ''
|
|
|
+ try {
|
|
|
+ const { data } = await api.getSql(params)
|
|
|
+ talks.value.push({
|
|
|
+ id: id.value++,
|
|
|
+ type: 'response',
|
|
|
+ tags: [],
|
|
|
+ content: data.text.split('\n')
|
|
|
+ })
|
|
|
+ getTable(data.id)
|
|
|
+ } catch (error) {
|
|
|
+ Snackbar.error(error)
|
|
|
+ } finally {
|
|
|
+ loading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const getTable = async (id) => {
|
|
|
+ loading.value = true
|
|
|
+ const params = { id }
|
|
|
+ try {
|
|
|
+ const { data } = await api.getToTable(params)
|
|
|
+ if (data.type === 'error') {
|
|
|
+ return
|
|
|
}
|
|
|
+ const _table = JSON.parse(data.df)
|
|
|
+ talks.value.push({
|
|
|
+ id: id.value++,
|
|
|
+ type: 'response',
|
|
|
+ tags: [],
|
|
|
+ view: 'table',
|
|
|
+ content: _table
|
|
|
+ })
|
|
|
+ } catch (error) {
|
|
|
+ Snackbar.error(error)
|
|
|
+ } finally {
|
|
|
+ loading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const talkTo = async (type, isRag = false) => {
|
|
|
+ loading.value = true
|
|
|
+ talks.value.push({
|
|
|
+ id: id.value++,
|
|
|
+ type: 'question',
|
|
|
+ tags: [],
|
|
|
+ content: question.value.split('\n')
|
|
|
+ })
|
|
|
+ const params = {
|
|
|
+ question: question.value,
|
|
|
+ type,
|
|
|
+ isRag
|
|
|
+ }
|
|
|
+ question.value = ''
|
|
|
+ try {
|
|
|
+ const { data } = await api.talkTo(params)
|
|
|
+ talks.value.push({
|
|
|
+ id: id.value++,
|
|
|
+ type: 'response',
|
|
|
+ tags: data.tags || [],
|
|
|
+ content: data.text.split('\n')
|
|
|
+ })
|
|
|
+ } catch (error) {
|
|
|
+ Snackbar.error(error)
|
|
|
+ } finally {
|
|
|
+ loading.value = false
|
|
|
}
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
-.round {
|
|
|
- border-radius: 5px;
|
|
|
- max-width: 80%;
|
|
|
-}
|
|
|
.send {
|
|
|
- // height: 130px;
|
|
|
- // margin: 20px 0;
|
|
|
- padding: 20px;
|
|
|
- &-box {
|
|
|
+ padding: 10px;
|
|
|
+ border-top: 1px solid #e0e0e0;
|
|
|
+ .send-box {
|
|
|
width: 100%;
|
|
|
- // max-width: 800px;
|
|
|
- position: relative;
|
|
|
- .btn {
|
|
|
- position: absolute;
|
|
|
- right: 20px;
|
|
|
- bottom: 12px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ .send-box-area {
|
|
|
+ flex: 1;
|
|
|
+ margin-right: 10px;
|
|
|
}
|
|
|
- &-area {
|
|
|
- position: relative;
|
|
|
- bottom: 0;
|
|
|
- ::v-deep textarea {
|
|
|
- padding: 15px 70px 15px 0 !important;
|
|
|
- max-height: 300px;
|
|
|
- min-height: 60px;
|
|
|
- overflow: auto;
|
|
|
- margin: 0 !important;
|
|
|
- }
|
|
|
+ .btn {
|
|
|
+ flex-shrink: 0;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+.round {
|
|
|
+ border-radius: 8px;
|
|
|
+}
|
|
|
</style>
|