feat: 聊天室历史记录对接

main
许宏杰 1 month ago
parent b01f61b773
commit f2ddf37d69

@ -113,6 +113,7 @@ export const translateStr = (target: string | Record<any, any>) => {
* @param globalParams * @param globalParams
*/ */
export const customizeHttp = (targetParams: RequestConfigType, globalParams: RequestGlobalConfigType) => { export const customizeHttp = (targetParams: RequestConfigType, globalParams: RequestGlobalConfigType) => {
if (!targetParams || !globalParams) { if (!targetParams || !globalParams) {
return return
} }

@ -26,6 +26,15 @@ export const historyMessageRoom = async(data: object)=>{
} }
} }
export const historyMessageRoomUpdata = async(data: object)=>{
try{
const res = await http(RequestHttpEnum.PUT)(`${ModuleTypeEnum.MESSAGE}`, data)
return res
}catch{
httpErrorHandle()
}
}
export const historyMessageRoomList = async(data?: object)=>{ export const historyMessageRoomList = async(data?: object)=>{
try{ try{
const res = await http(RequestHttpEnum.GET)(`${ModuleTypeEnum.MESSAGE}`, data) const res = await http(RequestHttpEnum.GET)(`${ModuleTypeEnum.MESSAGE}`, data)

@ -73,6 +73,7 @@ export const useChartDataFetch = (
clearInterval(fetchInterval) clearInterval(fetchInterval)
const fetchFn = async () => { const fetchFn = async () => {
const res = await customizeHttp(toRaw(targetComponent.request), toRaw(chartEditStore.getRequestGlobalConfig)) const res = await customizeHttp(toRaw(targetComponent.request), toRaw(chartEditStore.getRequestGlobalConfig))
if (res) { if (res) {
try { try {

@ -20,9 +20,9 @@ import cloneDeep from 'lodash/cloneDeep'
// 请求基础属性 // 请求基础属性
export const requestConfig: RequestConfigType = { export const requestConfig: RequestConfigType = {
requestDataType: RequestDataTypeEnum.STATIC, requestDataType: RequestDataTypeEnum.AJAX,
requestHttpType: RequestHttpEnum.GET, requestHttpType: RequestHttpEnum.POST,
requestUrl: '', requestUrl: 'http://baijiahu.mynatapp.cc/',
requestInterval: undefined, requestInterval: undefined,
requestIntervalUnit: RequestHttpIntervalEnum.SECOND, requestIntervalUnit: RequestHttpIntervalEnum.SECOND,
requestContentType: RequestContentTypeEnum.DEFAULT, requestContentType: RequestContentTypeEnum.DEFAULT,

@ -2,15 +2,15 @@ import { defineStore } from 'pinia'
const useMessageRoomStore = defineStore('useMessageRoomStore', { const useMessageRoomStore = defineStore('useMessageRoomStore', {
state: () => ({ state: () => ({
messageRoom: { messageRoom: {
fristIssue:undefined, id:undefined,
list:[] createUserId:undefined,
content:[]
} }
}), }),
actions: { actions: {
setMessage(fristIssue,item) { setMessage(item) {
if (this.messageRoom.list.length >= 35) return if (this.messageRoom.content.length >= 35) return
if(!this.messageRoom.fristIssue) this.messageRoom.fristIssue = fristIssue this.messageRoom.content.push(item)
this.messageRoom.list.push(item)
}, },
resetList(obj){ resetList(obj){
this.messageRoom = obj this.messageRoom = obj

@ -69,9 +69,21 @@
align-items: flex-start; align-items: flex-start;
} }
.ai-hint { .ai-hint {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 6px; margin-top: 6px;
font-size: 12px; font-size: 12px;
color: #c3cad9; color: #c3cad9;
gap: 10px;
.upload-text{
cursor: pointer;
display: flex;
align-items: center;
}
.upload-text:hover{
color: #3F7BF8;
}
} }
} }
.message-content { .message-content {

@ -181,5 +181,6 @@ export class BarChart {
this.chart.dispose() this.chart.dispose()
this.chart = null this.chart = null
} }
console.log('清除',this.chart)
} }
} }

@ -25,7 +25,7 @@
<!-- 内容 --> <!-- 内容 -->
<div class="message-content"> <div class="message-content">
<div class="message-time">{{ item.time }}</div> <div class="message-time">{{ item.time }}</div>
<div class="message-text"> <div class="message-text" :id="`box${index}`">
<!-- 纯文本 --> <!-- 纯文本 -->
<div class="text-ros"> <div class="text-ros">
<span>{{ item.text }}</span> <span>{{ item.text }}</span>
@ -76,7 +76,24 @@
</div> </div>
<!-- 免责 --> <!-- 免责 -->
<div class="ai-hint" v-show="item.from === 'ai' && (item.text || item.chartType)"> <div class="ai-hint" v-show="item.from === 'ai' && (item.text || item.chartType)">
本回答由AI生成内容仅供参考 <span>本回答由AI生成内容仅供参考</span>
<div class="upload-text" @click="uploadImage(index)">
<n-icon size="14">
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 16 16"
>
<g fill="none">
<path
d="M3.5 13h9a.75.75 0 0 1 .102 1.493l-.102.007h-9a.75.75 0 0 1-.102-1.493L3.5 13h9h-9zM7.898 1.007L8 1a.75.75 0 0 1 .743.648l.007.102v7.688l2.255-2.254a.75.75 0 0 1 .977-.072l.084.072a.75.75 0 0 1 .072.977l-.072.084L8.53 11.78a.75.75 0 0 1-.976.073l-.084-.073l-3.536-3.535a.75.75 0 0 1 .977-1.133l.084.072L7.25 9.44V1.75a.75.75 0 0 1 .648-.743L8 1l-.102.007z"
fill="currentColor"
></path>
</g>
</svg>
</n-icon>
<span>下载图片</span>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -110,6 +127,15 @@ import { LineChart, BarChart, PieChart } from './class/index'
import { AiHint } from '../components' import { AiHint } from '../components'
import userIcon from '@/assets/images/ai/user-icon.png' import userIcon from '@/assets/images/ai/user-icon.png'
import aiIcon from '@/assets/images/ai/ai-icon.png' import aiIcon from '@/assets/images/ai/ai-icon.png'
import { useMessage } from 'naive-ui'
import useMessageRoomStore from '@/store/modules/messageRoom'
import { canvasCut } from '@/utils'
import { historyMessageRoomById } from '@/api/path'
import { useRoute } from 'vue-router'
const route = useRoute()
const useMessageRoom = useMessageRoomStore()
const messageDialog = useMessage()
// //
let loading = ref(false) let loading = ref(false)
// //
@ -128,25 +154,55 @@ let stopWatch = null
* 页面初始完成 * 页面初始完成
*/ */
onMounted(() => { onMounted(() => {
// bar = new PieChart('barEcharts', { startWatching()
// title:'',
// unit:'', getMessageInfo()
// data: [
// { value: 1048, name: 'Search Engine' },
// { value: 735, name: 'Direct' },
// { value: 580, name: 'Email' },
// { value: 484, name: 'Union Ads' },
// { value: 300, name: 'Video Ads' },
// { value: 200, name: 'qq Ads' },
// { value: 100, name: 'vx Ads' }
// ]
// })
}) })
/**
* 获取聊天详情
*/
const getMessageInfo = async () => {
const id = checkLastPartIsTimestamp(route.path)
try {
const res = await historyMessageRoomById(id)
if (res.code == 200 && res.data) {
res.data.content = JSON.parse(res.data.content)
useMessageRoom.resetList(res.data)
res.data.content.map((item, index) => {
messageList.push(item)
initEcharts(item, index)
})
console.log(res.data)
}
} catch {
messageDialog.error('获取聊天室记录失败!')
}
}
const checkLastPartIsTimestamp = str => {
// /
const lastSlashIndex = str.lastIndexOf('/')
if (lastSlashIndex === -1) {
return false
}
// /
const lastPart = str.slice(lastSlashIndex + 1)
return lastPart
}
/** /**
* 问答 * 问答
*/ */
const handleSend = async () => { const handleSend = async () => {
if (!keyWord.value.trim()) {
messageDialog.warning('请先输入需要统计的数据!')
return
}
if (useMessageRoom.messageRoom.content.length >= 35) {
messageDialog.warning('每个聊天室上限问题为15条')
return
}
loading.value = true loading.value = true
// //
setMessageItem({ setMessageItem({
@ -156,7 +212,8 @@ const handleSend = async () => {
add: true add: true
}) })
//AI //AI
setMessageItem({ setMessageItem(
{
from: 'ai', from: 'ai',
text: '', text: '',
time: moment().format('YYYY/MM/DD HH:mm:ss'), time: moment().format('YYYY/MM/DD HH:mm:ss'),
@ -166,7 +223,9 @@ const handleSend = async () => {
chartType: null, chartType: null,
xData: [], xData: [],
yData: [] yData: []
}) },
false
)
try { try {
const res = await getAiMsg({ prompt: keyWord.value }) const res = await getAiMsg({ prompt: keyWord.value })
if (res.type) { if (res.type) {
@ -184,6 +243,7 @@ const handleSend = async () => {
title: res.title, title: res.title,
unit: res.unit unit: res.unit
}) })
nextTick(() => { nextTick(() => {
const itemData = messageList[lastIndex.value] const itemData = messageList[lastIndex.value]
// //
@ -228,14 +288,53 @@ const handleSend = async () => {
}) })
} }
} }
/**
* 同意渲染图表
*/
const initEcharts = (item, index) => {
nextTick(() => {
//
if (item.chartType === 'pie') {
item.chartInstanceItem = new PieChart(`${item.chartType}${index}`, {
title: item.title,
unit: item.unit,
xData: item.xData,
data: item.yData
})
} else if (item.chartType === 'bar') {
item.chartInstanceItem = new BarChart(`${item.chartType}${index}`, {
title: item.title,
unit: item.unit,
xData: item.xData,
data: item.yData
})
} else if (item.chartType === 'line') {
item.chartInstanceItem = new LineChart(`${item.chartType}${index}`, {
title: item.title,
unit: item.unit,
xData: item.xData,
data: item.yData
})
}
})
}
/** /**
* 存储聊天室数据 * 存储聊天室数据
* @param obj * @param obj
*/ */
const setMessageItem = obj => { const setMessageItem = (obj, set = true) => {
messageList.push(obj) messageList.push(obj)
lastIndex.value = messageList.length - 1 lastIndex.value = messageList.length - 1
if (set) {
useMessageRoom.setMessage({
from: obj.from,
text: obj.text,
time: obj.time,
add: true
})
}
} }
/** /**
@ -246,6 +345,19 @@ const updataMessageItem = data => {
messageList[lastIndex.value] = data messageList[lastIndex.value] = data
keyWord.value = '' keyWord.value = ''
loading.value = false loading.value = false
useMessageRoom.setMessage({
from: data.from,
text: data.text,
chartType: data.chartType,
loading: false, //ai
time: data.time,
chartInstanceItem: data.chartInstanceItem,
chartInstanceActiveIndex: 0,
xData: data.xData,
yData: data.yData,
title: data.title,
unit: data.unit
})
} }
/** /**
@ -256,10 +368,25 @@ const handlerTheme = (item, index) => {
pauseWatching() pauseWatching()
item.chartInstanceActiveIndex = index item.chartInstanceActiveIndex = index
item.chartInstanceItem.changeColorStyle(index) item.chartInstanceItem.changeColorStyle(index)
setTimeout(()=>{ setTimeout(() => {
startWatching() startWatching()
},1000) }, 1000)
}
/**
* 下载AI回答为图片
*/
const uploadImage = index => {
//
const range = document.getElementById(`box${index}`)
// 线
if (!range) {
window['$message'].error('下载失败!')
return
}
canvasCut(range)
} }
// //
@ -280,8 +407,14 @@ const pauseWatching = () => {
stopWatch = null stopWatch = null
} }
} }
onMounted(() => { onUnmounted(() => {
startWatching() //
messageList.forEach(instance => {
if (instance.chartType) {
instance.chartInstanceItem.dispose()
}
})
}) })
</script> </script>

@ -1,8 +0,0 @@
export interface AiChatroom {
from:string,//来自谁
time:string,//时间
test?:string,//纯文本
}
export type AiChatroomType = AiChatroom;

@ -1,5 +1,6 @@
<template> <template>
<div class="history-box"> <div class="history-box">
<section v-show="list.length > 0">
<div class="history-title">聊天历史记录</div> <div class="history-title">聊天历史记录</div>
<div class="history-list"> <div class="history-list">
<div class="history-item" v-for="item in list" :key="item.id" @click="getMessageInfo(item.id)"> <div class="history-item" v-for="item in list" :key="item.id" @click="getMessageInfo(item.id)">
@ -16,20 +17,23 @@
</div> </div>
</div> </div>
</div> </div>
</section>
<section v-show="boardLsit.length > 0">
<div class="history-title">看板历史记录</div> <div class="history-title">看板历史记录</div>
<div class="history-list"> <div class="history-list">
<div class="history-item"></div> <div class="history-item"></div>
</div> </div>
</section>
</div> </div>
</template> </template>
<script setup> <script setup>
import { onMounted, ref } from 'vue' import { onMounted, ref } from 'vue'
import { projectListApi, deleteProjectApi, historyMessageRoomList,historyMessageRoomDel } from '@/api/path' import { projectListApi, deleteProjectApi, historyMessageRoomList, historyMessageRoomDel } from '@/api/path'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
let list = ref([]) let list = ref([])
let boardLsit = ref([])
const router = useRouter() const router = useRouter()
//List //List
const fetchList = async () => { const fetchList = async () => {
@ -43,7 +47,7 @@ const delProjectItem = async id => {
const formatFristIssue = value => { const formatFristIssue = value => {
const data = JSON.parse(value.content) const data = JSON.parse(value.content)
return data.fristIssue return data[0].text
} }
//list //list
const messageList = async () => { const messageList = async () => {
@ -51,14 +55,11 @@ const messageList = async () => {
list.value = res.data.records list.value = res.data.records
} }
// //
const delMessageItem = async (id) => { const delMessageItem = async id => {
const res = await historyMessageRoomDel(id) const res = await historyMessageRoomDel(id)
if(res){ if (res) {
messageList() messageList()
} }
} }
const getMessageInfo = id => { const getMessageInfo = id => {
@ -67,7 +68,7 @@ const getMessageInfo = id => {
onMounted(() => { onMounted(() => {
// fetchList() // fetchList()
// messageList() messageList()
}) })
defineExpose({ defineExpose({

@ -1,3 +1,3 @@
export { default as AiHint } from './aiHint/index.vue' export { default as AiHint } from './aiHint/index.vue'
// export { default as history } from './history' export { default as AiHistory } from './history/index.vue'
// export { default as logo } from './logo' export { default as AiLogo } from './logo/index.vue'

@ -30,11 +30,11 @@
<script setup> <script setup>
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import { createProjectApi, historyMessageRoom } from '@/api/path' import { createProjectApi, historyMessageRoom ,historyMessageRoomUpdata} from '@/api/path'
import { getUUID } from '@/utils' import { getUUID } from '@/utils'
import { ResultEnum } from '@/enums/httpEnum' import { ResultEnum } from '@/enums/httpEnum'
import AiLogo from '../AI/components/logo/index.vue' import { AiLogo, AiHistory } from './components'
import AiHistory from '../AI/components/history/index.vue'
import { ref } from 'vue' import { ref } from 'vue'
import useMessageRoomStore from '@/store/modules/messageRoom' import useMessageRoomStore from '@/store/modules/messageRoom'
import { useSystemStore } from '@/store/modules/systemStore/systemStore' import { useSystemStore } from '@/store/modules/systemStore/systemStore'
@ -53,8 +53,43 @@ const menuList = [
const getCurrentRoute = path => { const getCurrentRoute = path => {
return route.path.includes(path) return route.path.includes(path)
} }
const handlerPath = async (path, index) => {
const saveMessage = async path => {
//
if (useMessageRoom.messageRoom.content.length > 0) {
useMessageRoom.messageRoom.content.forEach(instance => {
if (instance.chartType && instance.chartInstanceItem) {
instance.chartInstanceItem.dispose()
}
})
const jsonData = JSON.stringify(useMessageRoom.messageRoom.content)
let res
try {
if (useMessageRoom.messageRoom.id) {
res = await historyMessageRoomUpdata({ content: jsonData, createUserId: useMessageRoom.messageRoom.id })
} else {
res = await historyMessageRoom({ content: jsonData, createUserId: systemStore.userInfo.userId || 1 })
}
if (res && res.code == 200) {
useMessageRoom.resetList({ id: undefined, createUserId: undefined, content: [] }) //
history.value.messageList()
router.push(path + '/' + Date.now())
}
} catch {
console.log('保存出错')
router.push(path + '/' + Date.now())
}
} else {
router.push(path + '/' + Date.now()) router.push(path + '/' + Date.now())
}
}
const handlerPath = (path, index) => {
if (index === 0) {
saveMessage(path)
}
// if (index == 1) { // if (index == 1) {
// if (history.value) { // if (history.value) {
// // history.value.fetchList() // // history.value.fetchList()

@ -61,6 +61,7 @@
</setting-item> </setting-item>
</setting-item-box> </setting-item-box>
<div v-if="requestContentType === RequestContentTypeEnum.DEFAULT"> <div v-if="requestContentType === RequestContentTypeEnum.DEFAULT">
<n-tabs type="line" animated v-model:value="tabValue"> <n-tabs type="line" animated v-model:value="tabValue">
<n-tab v-for="item in RequestParamsTypeEnum" :key="item" :name="item" :tab="item"> {{ item }} </n-tab> <n-tab v-for="item in RequestParamsTypeEnum" :key="item" :name="item" :tab="item"> {{ item }} </n-tab>
</n-tabs> </n-tabs>

@ -12,6 +12,7 @@
</n-tabs> </n-tabs>
<!-- 各个页面 --> <!-- 各个页面 -->
<div class="go-mt-3"> <div class="go-mt-3">
<div v-if="tabValue !== RequestParamsTypeEnum.BODY"> <div v-if="tabValue !== RequestParamsTypeEnum.BODY">
<request-header-table :target="requestParams[tabValue]" @update="updateRequestParams"></request-header-table> <request-header-table :target="requestParams[tabValue]" @update="updateRequestParams"></request-header-table>

@ -1,8 +1,8 @@
<template> <template>
<div class="go-chart-configurations-data" v-if="targetData"> <div class="go-chart-configurations-data" v-if="targetData">
<!-- <setting-item-box name="请求方式" :alone="true"> <setting-item-box name="请求方式" :alone="true">
<n-select v-model:value="targetData.request.requestDataType" :disabled="isNotData" :options="selectOptions" /> <n-select v-model:value="targetData.request.requestDataType" :disabled="isNotData" :options="selectOptions" />
</setting-item-box> --> </setting-item-box>
<!-- 静态 --> <!-- 静态 -->
<chart-data-static v-if="targetData.request.requestDataType === RequestDataTypeEnum.STATIC"></chart-data-static> <chart-data-static v-if="targetData.request.requestDataType === RequestDataTypeEnum.STATIC"></chart-data-static>
<!-- 动态 --> <!-- 动态 -->

@ -134,28 +134,28 @@ const chartsDefaultTabList = [
icon: ConstructIcon, icon: ConstructIcon,
render: ChartSetting render: ChartSetting
}, },
// { {
// key: TabsEnum.CHART_ANIMATION, key: TabsEnum.CHART_ANIMATION,
// title: '', title: '动画',
// icon: LeafIcon, icon: LeafIcon,
// render: ChartAnimation render: ChartAnimation
// } }
] ]
const chartsTabList = [ const chartsTabList = [
...chartsDefaultTabList, ...chartsDefaultTabList,
{ {
key: TabsEnum.CHART_DATA, key: TabsEnum.CHART_DATA,
title: '统计数据', title: '数据',
icon: FlashIcon, icon: FlashIcon,
render: ChartData render: ChartData
}, },
// { {
// key: TabsEnum.CHART_EVENT, key: TabsEnum.CHART_EVENT,
// title: '', title: '事件',
// icon: RocketIcon, icon: RocketIcon,
// render: ChartEvent render: ChartEvent
// } }
] ]
</script> </script>

@ -321,6 +321,7 @@ export const useMouseHandle = () => {
// * 进入事件 // * 进入事件
const mouseenterHandle = (e: MouseEvent, item: CreateComponentType | CreateComponentGroupType) => { const mouseenterHandle = (e: MouseEvent, item: CreateComponentType | CreateComponentGroupType) => {
e.preventDefault() e.preventDefault()
e.stopPropagation() e.stopPropagation()
if (!chartEditStore.getEditCanvas.isSelect) { if (!chartEditStore.getEditCanvas.isSelect) {

Loading…
Cancel
Save