549 lines
18 KiB
Vue
549 lines
18 KiB
Vue
<template>
|
|
<!-- @click="getChatInfo()" -->
|
|
<div class="frame">
|
|
<div class="opentiong">
|
|
<div class="isShowBtn" @click="getChatInfo()">
|
|
<img src="@/assets/icon/刷新.svg" alt="" />
|
|
</div>
|
|
<div class="isShowBtn" @click="quanpingModel = !quanpingModel">
|
|
<img src="@/assets/icon/流式输出.svg" alt="" />
|
|
</div>
|
|
<div class="isShowBtn" @click="isShow = !isShow">
|
|
<img src="@/assets/map-icon/expand-logo.svg" alt="" />
|
|
</div>
|
|
</div>
|
|
<div class="huakuai" v-if="isShow" @mousedown.prevent="startSplitting" @mouseup.prevent="endSplitting">
|
|
</div>
|
|
<div class="InfoChatView" ref="chatList"
|
|
:style="isShow ? { width: currentWidth + 'px' } : { width: '0px', border: 'none' }">
|
|
<t-collapse :default-value="[1]" @change="handlePanelChange" default-expand-all>
|
|
<t-collapse-panel header="思考过程"> <t-collapse default-expand-all>
|
|
<template v-for="(item, index) in chatList">
|
|
<t-collapse-panel header="思考过程" v-if="item.type === 'show_text'">
|
|
<CodeData :markdown="item.text"></CodeData>
|
|
</t-collapse-panel>
|
|
<t-collapse-panel header="分析报告" v-if="item.type === 'summary'">
|
|
<CodeData :markdown="item.text"></CodeData>
|
|
</t-collapse-panel>
|
|
<template v-if="item.type === 'google_search' && item.data['q']">
|
|
<div class="search" v-if="index === chatList.length - 1">
|
|
<t-loading text="搜索中..." size="small"></t-loading>{{
|
|
item.data['q'] }}
|
|
</div>
|
|
</template>
|
|
<t-collapse-panel :header="chatList[index - 1].data['q'] || '思考过程'"
|
|
v-if="item.type === 'google_search' && item.data['result']">
|
|
<div class="resultList">
|
|
<div class="item" @click="openDetails(item)"
|
|
v-for="(item, i) in JSON.parse(item.data['result']).organic">
|
|
<div class="eventTitle text-ellipsis ellipsis1">
|
|
<span class="num"> {{ i + 1 }} </span> {{ item.title }}
|
|
</div>
|
|
<div class="eventContent text-ellipsis ellipsis3">
|
|
{{ item.content }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</t-collapse-panel>
|
|
</template>
|
|
</t-collapse></t-collapse-panel>
|
|
</t-collapse>
|
|
|
|
<div class="summary">
|
|
<CodeData :markdown="summary"></CodeData>
|
|
</div>
|
|
|
|
<t-loading v-if="loading" :loading="loading" show-overlay></t-loading>
|
|
</div>
|
|
</div>
|
|
<t-dialog v-model:visible="detailModel" header="详情" :zIndex="2000" width="1000" :footer='false'>
|
|
<t-space direction=" vertical" style="width: 100%">
|
|
<div class="detailModel">
|
|
<div class="title">
|
|
{{ detailData.title }}
|
|
</div>
|
|
<!-- <a class="link" target="_blank"> 原文链接</a> -->
|
|
<t-link hover="color"
|
|
:href="'http://chlib.b.uat.4.cn/knowlibrary/#/knowledge/mid-detail?recUuid=' + detailData.recUuid"
|
|
target="_blank">
|
|
<template #suffix-icon>
|
|
<jump-icon />
|
|
</template>
|
|
跳转链接
|
|
</t-link>
|
|
<div class="content">
|
|
<CodeData :markdown="detailData.content" />
|
|
</div>
|
|
</div>
|
|
|
|
</t-space>
|
|
</t-dialog>
|
|
<t-dialog v-model:visible="quanpingModel" header="详情" width="1600" :footer='false'>
|
|
<t-space direction=" vertical" style="width: 100%">
|
|
<div style="display: flex;flex-direction: column;gap: 10px;">
|
|
<t-collapse :default-value="[1]" @change="handlePanelChange" default-expand-all>
|
|
<t-collapse-panel header="思考过程"> <t-collapse default-expand-all>
|
|
<template v-for="(item, index) in chatList">
|
|
<t-collapse-panel header="思考过程" v-if="item.type === 'show_text'">
|
|
<CodeData :markdown="item.text"></CodeData>
|
|
</t-collapse-panel>
|
|
<t-collapse-panel header="分析报告" v-if="item.type === 'summary'">
|
|
<CodeData :markdown="item.text"></CodeData>
|
|
</t-collapse-panel>
|
|
<template v-if="item.type === 'google_search' && item.data['q']">
|
|
<div class="search" v-if="index === chatList.length - 1">
|
|
<t-loading text="搜索中..." size="small"></t-loading>{{
|
|
item.data['q'] }}
|
|
</div>
|
|
</template>
|
|
<t-collapse-panel :header="chatList[index - 1].data['q'] || '思考过程'"
|
|
v-if="item.type === 'google_search' && item.data['result']">
|
|
<div class="resultList">
|
|
<div class="item" @click="openDetails(item)"
|
|
v-for="(item, i) in JSON.parse(item.data['result']).organic">
|
|
<div class="eventTitle text-ellipsis ellipsis1">
|
|
<span class="num"> {{ i + 1 }} </span> {{ item.title }}
|
|
</div>
|
|
<div class="eventContent text-ellipsis ellipsis3">
|
|
{{ item.content }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</t-collapse-panel>
|
|
</template>
|
|
</t-collapse></t-collapse-panel>
|
|
</t-collapse>
|
|
|
|
<div class="summary">
|
|
<CodeData :markdown="summary"></CodeData>
|
|
</div>
|
|
<t-loading v-if="loading" :loading="loading" show-overlay></t-loading>
|
|
</div>
|
|
</t-space>
|
|
</t-dialog>
|
|
</template>
|
|
|
|
<script>
|
|
import { requestAxiosService } from "@/service/request-axios-service"; // 引入发送异步请求方法
|
|
import CodeData from '../markdown/CodeData.vue'
|
|
import { MessagePlugin } from 'tdesign-vue-next';
|
|
import { LinkIcon, JumpIcon } from 'tdesign-icons-vue-next';
|
|
export default {
|
|
data() {
|
|
return {
|
|
rawData: [],
|
|
chatList: [],
|
|
quanpingModel: false,
|
|
loading: false,
|
|
isShow: true,
|
|
service: requestAxiosService,
|
|
abortController: null,
|
|
MessagePlugin: MessagePlugin,
|
|
|
|
isResizing: false,
|
|
currentWidth: 320,
|
|
startX: 0, //开始的X坐标
|
|
startWidth: 0, //开始的宽度
|
|
detailModel: false,
|
|
detailData: {},
|
|
summary: '', //总结
|
|
};
|
|
},
|
|
props: ['prompt', 'generate', 'taskId', 'taskChatList', 'taskSummary'],
|
|
components: { CodeData, JumpIcon },
|
|
computed: {
|
|
},
|
|
methods: {
|
|
openDetails(d) {
|
|
console.log(d);
|
|
this.detailData = d
|
|
this.detailModel = true
|
|
|
|
|
|
},
|
|
startSplitting(e) {
|
|
this.isResizing = true
|
|
this.startX = e.pageX;
|
|
this.startWidth = this.currentWidth
|
|
console.log(document);
|
|
|
|
document.addEventListener('mousemove', this.handleMouseMove);
|
|
document.addEventListener('mouseup', this.endResizing);
|
|
},
|
|
handleMouseMove(e) {
|
|
|
|
if (!this.isResizing) return;
|
|
|
|
const deltaX = this.startX - e.pageX;
|
|
|
|
const newWidth = this.startWidth + deltaX;
|
|
|
|
// 设置宽度限制
|
|
const minWidth = 320;
|
|
const maxWidth = 800;
|
|
|
|
this.currentWidth = Math.max(minWidth, Math.min(newWidth, maxWidth));
|
|
console.log(this.currentWidth);
|
|
|
|
},
|
|
endResizing() {
|
|
this.isResizing = false;
|
|
document.removeEventListener('mousemove', this.handleMouseMove);
|
|
document.removeEventListener('mouseup', this.endResizing);
|
|
},
|
|
dataHandl() {
|
|
console.log(this.rawData);
|
|
console.log(this.chatList);
|
|
|
|
this.service
|
|
.post(
|
|
`${environment["b1ConsumerUrl"]}/cop/batchEnemyElement`,
|
|
{
|
|
"data": [JSON.stringify(this.chatList), this.summary, ...this.rawData],
|
|
"taskId": this.taskId,
|
|
}
|
|
)
|
|
.then((res) => {
|
|
console.log("热点事件", res);
|
|
this.$emit('upDataInfo')
|
|
});
|
|
|
|
},
|
|
async getChatInfo() {
|
|
if (this.loading) {
|
|
this.MessagePlugin.warning('正在生成中,请稍后!')
|
|
return
|
|
}
|
|
this.loading = true
|
|
this.chatList = []
|
|
this.summary = ''
|
|
this.abortController = new AbortController();
|
|
try {
|
|
const response = await fetch(`https://ch1.b.uat.4.cn/qq_b1_chat/generate_qq`, {
|
|
// const response = await fetch(`http://localhost:8095/generate_qq`, {
|
|
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
prompt: this.prompt
|
|
}),
|
|
// signal: this.abortController.signal
|
|
});
|
|
// console.log(response);
|
|
if (!response.ok) {
|
|
const errorData = await response.json().catch(() => ({ detail: 'Unknown error' }));
|
|
throw new Error(errorData.detail || `HTTP error ${response.status}`);
|
|
}
|
|
if (!response.body) {
|
|
throw new Error('Response body is null');
|
|
}
|
|
const reader = response.body.getReader();
|
|
const decoder = new TextDecoder();
|
|
let buffer = '';
|
|
while (true) {
|
|
const { done, value } = await reader.read();
|
|
console.log(done, value);
|
|
|
|
if (done) {
|
|
this.loading = false
|
|
this.dataHandl()
|
|
break;
|
|
}
|
|
buffer += decoder.decode(value, { stream: true });
|
|
const lines = buffer.split('\n');
|
|
const lastLine = lines.pop();
|
|
buffer = lastLine || '';
|
|
console.log(lines);
|
|
|
|
|
|
for (let line of lines) {
|
|
if (!line.startsWith('data: ')) continue;
|
|
this.rawData.push(line.slice(6))
|
|
try {
|
|
// 解析SSE数据
|
|
// line = line.replace(/'/g, '"').trim();
|
|
const parsedData = JSON.parse(line.slice(6));
|
|
const { event, data } = parsedData;
|
|
|
|
|
|
// 处理不同类型的事件
|
|
switch (event) {
|
|
case 'task_created': {
|
|
// taskIdRef.current = data.task_id;
|
|
break;
|
|
}
|
|
case 'tool_call': {
|
|
this.handleToolCall(data);
|
|
break;
|
|
}
|
|
case 'message': {
|
|
// 处理消息事件
|
|
break;
|
|
}
|
|
case 'done': {
|
|
break;
|
|
}
|
|
case 'references': {
|
|
break;
|
|
}
|
|
default:
|
|
console.log(`未知事件类型: ${event}`, data);
|
|
}
|
|
} catch (error) {
|
|
console.error('JSON解析错误:', error);
|
|
console.log('原始数据:', line.slice(6));
|
|
}
|
|
}
|
|
}
|
|
} catch (err) {
|
|
this.loading = false
|
|
console.error('JSON解析错误:', err);
|
|
console.log('接口中断');
|
|
setTimeout(() => {
|
|
this.MessagePlugin.warning('数据异常中断,开始重试!')
|
|
this.getChatInfo()
|
|
}, 1000)
|
|
|
|
}
|
|
|
|
},
|
|
handleToolCall(d) {
|
|
console.log(d);
|
|
switch (d.tool_name) {
|
|
case 'show_text': {
|
|
const chatText = d.tool_input.text || '';
|
|
console.log(chatText);
|
|
|
|
if (chatText.includes('<think>')) {
|
|
// 添加新的思考过程
|
|
const thinkContent = chatText.replace('<think>', '').replace('</think>', '');
|
|
this.chatList.push({
|
|
text: thinkContent,
|
|
type: d.tool_name,
|
|
});
|
|
} else {
|
|
this.summary += chatText;
|
|
// 更新总结回答
|
|
}
|
|
break;
|
|
}
|
|
case 'google_search': {
|
|
this.chatList.push({
|
|
type: d.tool_name,
|
|
data: d.tool_input
|
|
})
|
|
}
|
|
|
|
}
|
|
this.$nextTick(() => {
|
|
this.scrollToBottom()
|
|
})
|
|
},
|
|
scrollToBottom() {
|
|
const container = this.$refs.chatList
|
|
// 平滑滚动
|
|
if (container && container.scrollTo) {
|
|
container.scrollTo({
|
|
top: container.scrollHeight,
|
|
behavior: 'smooth'
|
|
})
|
|
}
|
|
|
|
}
|
|
},
|
|
watch: {
|
|
generate(v) {
|
|
if (v) {
|
|
this.getChatInfo()
|
|
}
|
|
},
|
|
taskChatList(v) {
|
|
console.log(v);
|
|
|
|
if (v) {
|
|
|
|
this.chatList = v
|
|
}
|
|
},
|
|
taskSummary(d) {
|
|
if (d) {
|
|
this.summary = d
|
|
}
|
|
}
|
|
},
|
|
mounted() {
|
|
// this.getChatInfo()
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style lang="less" scoped>
|
|
.detailModel {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 10px;
|
|
|
|
.title {
|
|
color: #fff;
|
|
font-size: 32px;
|
|
line-height: 40px;
|
|
font-weight: 700;
|
|
text-align: center;
|
|
}
|
|
|
|
.link {
|
|
color: #fff;
|
|
text-align: center;
|
|
}
|
|
|
|
.content {
|
|
color: #fff;
|
|
font-size: 14px;
|
|
}
|
|
}
|
|
|
|
.huakuai {
|
|
width: 8px;
|
|
height: 50px;
|
|
background-color: #075176;
|
|
border: 1px solid #05244f;
|
|
border-radius: 3px;
|
|
position: absolute;
|
|
left: 0;
|
|
top: 50%;
|
|
z-index: 1001;
|
|
cursor: pointer;
|
|
}
|
|
|
|
:deep(.t-collapse-panel__body) {
|
|
background: rgba(3, 38, 80, 0.8);
|
|
}
|
|
|
|
:deep(.t-collapse) {
|
|
background-color: transparent;
|
|
}
|
|
|
|
.hidden {
|
|
height: 0 !important;
|
|
padding: 0 !important;
|
|
border: 0 !important;
|
|
}
|
|
|
|
.frame {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
top: 230px;
|
|
position: absolute;
|
|
align-items: end;
|
|
|
|
.opentiong {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
|
|
.isShowBtn {
|
|
cursor: pointer;
|
|
width: 30px;
|
|
height: 30px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: #032650cc;
|
|
border: 1px solid;
|
|
border-color: #2b8ffe99;
|
|
border-radius: 4px 0 0 4px;
|
|
}
|
|
}
|
|
|
|
.InfoChatView {
|
|
z-index: 1000;
|
|
display: flex;
|
|
gap: 10px;
|
|
flex-direction: column;
|
|
// transition: width 0.3s ease;
|
|
overflow-y: auto;
|
|
width: 319px;
|
|
padding: 20px 0;
|
|
height: calc(100vh - 360px);
|
|
background: rgba(3, 38, 80, 0.8);
|
|
border: 1px solid;
|
|
border-color: rgba(43, 143, 254, 0.6);
|
|
border-radius: 4px;
|
|
backdrop-filter: blur(10px);
|
|
|
|
// 滚动条样式
|
|
&::-webkit-scrollbar {
|
|
width: 8px;
|
|
}
|
|
|
|
::-webkit-scrollbar-track {
|
|
background: #f8f8f8;
|
|
}
|
|
|
|
&::-webkit-scrollbar-thumb {
|
|
background: #ccc;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb:hover {
|
|
background: #aaa;
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
.chatItem {
|
|
.search {
|
|
color: #fff;
|
|
}
|
|
|
|
.think {
|
|
padding: 10px;
|
|
border: 1px solid;
|
|
background: rgba(0, 98, 217, 0.1);
|
|
border: 1px solid;
|
|
border-color: rgba(85, 166, 254, 0.1);
|
|
border-radius: 4px;
|
|
}
|
|
}
|
|
|
|
.resultList {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
|
|
.item {
|
|
cursor: pointer;
|
|
|
|
.eventTitle {
|
|
color: #fff;
|
|
font-size: 20px;
|
|
font-weight: 600;
|
|
|
|
.num {
|
|
width: 16px;
|
|
height: 16px;
|
|
border-radius: 50%;
|
|
font-size: 14px;
|
|
display: inline-block;
|
|
text-align: center;
|
|
background-color: var(--bg-color);
|
|
}
|
|
}
|
|
|
|
.eventContent {
|
|
color: #fff;
|
|
}
|
|
}
|
|
}
|
|
|
|
.summary {
|
|
padding: 10px;
|
|
}
|
|
</style> |