|
|
|
@ -7,6 +7,21 @@
|
|
|
|
|
height: chatRoom + 'px'
|
|
|
|
|
}"
|
|
|
|
|
>
|
|
|
|
|
<div
|
|
|
|
|
:class="['chat-item', 'ai-message']"
|
|
|
|
|
>
|
|
|
|
|
<!-- 头像 -->
|
|
|
|
|
<img :src="aiIcon" class="user-icon" alt="" />
|
|
|
|
|
<!-- 内容 -->
|
|
|
|
|
<div class="message-content">
|
|
|
|
|
<div class="message-time">当前数据</div>
|
|
|
|
|
<div class="message-text">
|
|
|
|
|
<span>{{ toString(source)}}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div
|
|
|
|
|
v-for="(item, index) in messagesList"
|
|
|
|
|
:key="index"
|
|
|
|
@ -44,9 +59,13 @@
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
import { toString, isArray } from '@/utils'
|
|
|
|
|
|
|
|
|
|
import { ChartFrameEnum } from '@/packages/index.d'
|
|
|
|
|
import { useMessage } from 'naive-ui'
|
|
|
|
|
import { ref, reactive, watch, nextTick, onMounted } from 'vue'
|
|
|
|
|
import { ref, reactive, watch, nextTick, onMounted ,computed} from 'vue'
|
|
|
|
|
import { DataResultEnum, TimelineTitleEnum } from '../../index.d'
|
|
|
|
|
import { useTargetData } from '../../../hooks/useTargetData.hook'
|
|
|
|
|
import moment from 'moment'
|
|
|
|
|
// @ts-ignore
|
|
|
|
@ -57,14 +76,42 @@ import aiIcon from '@/assets/images/ai/ai-icon.png'
|
|
|
|
|
|
|
|
|
|
//数据定义区
|
|
|
|
|
const { targetData, chartEditStore } = useTargetData()
|
|
|
|
|
const source = ref()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let keyWord = ref('')
|
|
|
|
|
let loading = ref(false)
|
|
|
|
|
const messageDialog = useMessage()
|
|
|
|
|
let messagesList = reactive([])
|
|
|
|
|
const scrollContainer = ref(null)
|
|
|
|
|
const chatBxo = ref(null)
|
|
|
|
|
const chatInput = ref(null)
|
|
|
|
|
let messagesList :any[] = reactive([])
|
|
|
|
|
const scrollContainer = ref<HTMLDivElement | null>(null)
|
|
|
|
|
const chatBxo =ref<HTMLDivElement | null>(null)
|
|
|
|
|
const chatInput = ref<HTMLDivElement | null>(null)
|
|
|
|
|
let chatRoom = ref(0)
|
|
|
|
|
const noData = ref(false)
|
|
|
|
|
const dimensions = ref()
|
|
|
|
|
const dimensionsAndSource = ref()
|
|
|
|
|
// 映射列表, 注意内部的mapping是响应式的,上方需要修改
|
|
|
|
|
const fieldList = ref<
|
|
|
|
|
Array<{
|
|
|
|
|
field: string
|
|
|
|
|
mapping: string[]
|
|
|
|
|
result: DataResultEnum
|
|
|
|
|
}>
|
|
|
|
|
>([])
|
|
|
|
|
|
|
|
|
|
// 处理映射列表状态结果
|
|
|
|
|
const matchingHandle = (mapping: string) => {
|
|
|
|
|
let res = DataResultEnum.SUCCESS
|
|
|
|
|
for (let i = 0; i < source.value.length; i++) {
|
|
|
|
|
if (source.value[i][mapping] === undefined) {
|
|
|
|
|
res = DataResultEnum.FAILURE
|
|
|
|
|
return res
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return DataResultEnum.SUCCESS
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//方法区
|
|
|
|
|
const handleSend = async () => {
|
|
|
|
|
if (!keyWord.value.trim()) {
|
|
|
|
@ -74,38 +121,38 @@ const handleSend = async () => {
|
|
|
|
|
/**
|
|
|
|
|
*保存用户提问信息
|
|
|
|
|
*/
|
|
|
|
|
messagesList.push({
|
|
|
|
|
const messagesItem = {
|
|
|
|
|
from: 'user',
|
|
|
|
|
text: keyWord.value,
|
|
|
|
|
time: moment().format('YYYY/MM/DD HH:mm:ss')
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
messagesList.push(messagesItem)
|
|
|
|
|
|
|
|
|
|
loading.value = true
|
|
|
|
|
try {
|
|
|
|
|
const res = await getAiMsg({ prompt: keyWord.value })
|
|
|
|
|
let result = {}
|
|
|
|
|
if(res.chartType){
|
|
|
|
|
result.dimensions = [
|
|
|
|
|
'product','data1'
|
|
|
|
|
]
|
|
|
|
|
let result:any = {}
|
|
|
|
|
if (res.chartType) {
|
|
|
|
|
result.dimensions = ['product', 'data1']
|
|
|
|
|
result.source = []
|
|
|
|
|
res.xData.map((item,index)=>{
|
|
|
|
|
res.xData.map((item:any, index:any) => {
|
|
|
|
|
result.source.push({
|
|
|
|
|
product:item,
|
|
|
|
|
data1:parseInt(res.yData[index])
|
|
|
|
|
product: item,
|
|
|
|
|
data1: parseInt(res.yData[index])
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
}else{
|
|
|
|
|
targetData.value.option.dataset = result
|
|
|
|
|
// targetData.value.option.messages = messagesItem
|
|
|
|
|
} else {
|
|
|
|
|
result = res
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//保存AI回答
|
|
|
|
|
messagesList.push({
|
|
|
|
|
from: 'ai',
|
|
|
|
|
text: result,
|
|
|
|
|
time: moment().format('YYYY/MM/DD HH:mm:ss')
|
|
|
|
|
})
|
|
|
|
|
targetData.value.option.dataset = result
|
|
|
|
|
// //保存AI回答
|
|
|
|
|
// messagesList.push({
|
|
|
|
|
// from: 'ai',
|
|
|
|
|
// text: result,
|
|
|
|
|
// time: moment().format('YYYY/MM/DD HH:mm:ss')
|
|
|
|
|
// })
|
|
|
|
|
|
|
|
|
|
keyWord.value = '' //清除搜索值
|
|
|
|
|
loading.value = false
|
|
|
|
|
} catch (error) {
|
|
|
|
@ -119,7 +166,56 @@ const handleSend = async () => {
|
|
|
|
|
loading.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理映射列表
|
|
|
|
|
const dimensionsAndSourceHandle = () => {
|
|
|
|
|
try {
|
|
|
|
|
// 去除首项数据轴标识
|
|
|
|
|
return dimensions.value.map((dimensionsItem: string, index: number) => {
|
|
|
|
|
return index === 0
|
|
|
|
|
? {
|
|
|
|
|
// 字段
|
|
|
|
|
field: '通用标识',
|
|
|
|
|
// 映射
|
|
|
|
|
mapping: dimensionsItem,
|
|
|
|
|
// 结果
|
|
|
|
|
result: DataResultEnum.NULL
|
|
|
|
|
}
|
|
|
|
|
: {
|
|
|
|
|
field: `数据项-${index}`,
|
|
|
|
|
mapping: dimensionsItem,
|
|
|
|
|
result: matchingHandle(dimensionsItem)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
} catch (error) {
|
|
|
|
|
return []
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 是支持 dataset 的图表类型
|
|
|
|
|
const isCharts = computed(() => {
|
|
|
|
|
return targetData.value.chartConfig.chartFrame === ChartFrameEnum.ECHARTS
|
|
|
|
|
})
|
|
|
|
|
// 处理 vchart 映射列表
|
|
|
|
|
const initFieldListHandle = () => {
|
|
|
|
|
if (targetData.value?.option) {
|
|
|
|
|
fieldList.value = []
|
|
|
|
|
// 所有名称,找到其中中 Field 结尾 的 key 和值
|
|
|
|
|
for (const key in targetData.value.option) {
|
|
|
|
|
if (key.endsWith('Field')) {
|
|
|
|
|
const value = targetData.value.option[key]
|
|
|
|
|
targetData.value.option[key] = value
|
|
|
|
|
const item = {
|
|
|
|
|
field: key,
|
|
|
|
|
mapping: value,
|
|
|
|
|
result: DataResultEnum.SUCCESS
|
|
|
|
|
}
|
|
|
|
|
if (item.mapping === undefined) {
|
|
|
|
|
item.result = DataResultEnum.FAILURE
|
|
|
|
|
}
|
|
|
|
|
fieldList.value.push(item)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//watch区
|
|
|
|
|
// 监听messages变化,自动滚动到底部
|
|
|
|
|
watch(
|
|
|
|
@ -134,11 +230,59 @@ watch(
|
|
|
|
|
{ deep: true }
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
//监听图表选中变化
|
|
|
|
|
// watch(
|
|
|
|
|
// () => targetData.value.id,
|
|
|
|
|
// (
|
|
|
|
|
|
|
|
|
|
// ) => {
|
|
|
|
|
// messagesList = []
|
|
|
|
|
// },
|
|
|
|
|
|
|
|
|
|
// )
|
|
|
|
|
|
|
|
|
|
//监听选中图表值
|
|
|
|
|
watch(
|
|
|
|
|
() => targetData.value?.option?.dataset,
|
|
|
|
|
(
|
|
|
|
|
newData?: {
|
|
|
|
|
source: any
|
|
|
|
|
dimensions: any
|
|
|
|
|
} | null
|
|
|
|
|
) => {
|
|
|
|
|
// console.log(targetData.value.option.messages,'sss')
|
|
|
|
|
noData.value = false
|
|
|
|
|
if (newData && targetData?.value?.chartConfig?.chartFrame === ChartFrameEnum.ECHARTS) {
|
|
|
|
|
// 只有 DataSet 数据才有对应的格式
|
|
|
|
|
source.value = newData
|
|
|
|
|
if (isCharts.value) {
|
|
|
|
|
dimensions.value = newData.dimensions
|
|
|
|
|
dimensionsAndSource.value = dimensionsAndSourceHandle()
|
|
|
|
|
}
|
|
|
|
|
} else if (newData && targetData?.value?.chartConfig?.chartFrame === ChartFrameEnum.VCHART) {
|
|
|
|
|
source.value = newData
|
|
|
|
|
initFieldListHandle()
|
|
|
|
|
} else if (newData !== undefined && newData !== null) {
|
|
|
|
|
dimensionsAndSource.value = null
|
|
|
|
|
source.value = newData
|
|
|
|
|
fieldList.value = []
|
|
|
|
|
} else {
|
|
|
|
|
noData.value = true
|
|
|
|
|
source.value = '此组件无数据源'
|
|
|
|
|
}
|
|
|
|
|
if (isArray(newData)) {
|
|
|
|
|
dimensionsAndSource.value = null
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
immediate: true
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
if (chatBxo.value) {
|
|
|
|
|
if (chatBxo.value && chatInput.value) {
|
|
|
|
|
// 使用 offsetHeight 获取元素高度
|
|
|
|
|
chatRoom.value = chatBxo.value.offsetHeight - (chatInput.value.offsetHeight+100 )
|
|
|
|
|
console.log(chatBxo.value.offsetHeight, '高度')
|
|
|
|
|
chatRoom.value = chatBxo.value.offsetHeight - (chatInput.value.offsetHeight + 100)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
</script>
|
|
|
|
@ -161,6 +305,7 @@ onMounted(() => {
|
|
|
|
|
gap: 25px;
|
|
|
|
|
width: 100%;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
overflow-x: hidden;
|
|
|
|
|
.chat-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 6px;
|
|
|
|
|