Merge branch 'main' of http://18.167.251.121:10003/aidlab/FiDA_Front
This commit is contained in:
11
src/assets/icons/threeLogo.svg
Normal file
11
src/assets/icons/threeLogo.svg
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg id="_图层_1" data-name="图层 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 317.48 276">
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.cls-1 {
|
||||||
|
fill: #3d3d3d;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<path class="cls-1" d="M163.55,24.01v45c1.72.15,3.22.97,4.68,1.82,25.29,14.72,55.93,30.25,79.63,46.38,3.63,2.47,7.09,3.95,6.74,9.26l.44,87.56,42.03,21.97,1.47-.48c-1.67-3.38-6.49-11.59-6.02-14.95.43-3.07,4.76-6.02,7.72-4.83,6.02,2.42,13.49,26.46,17.23,32.78-.48,3.25-2.61,5.32-5.68,6.25-4.74,1.44-23.8,4.07-28.91,4.18-3.4.07-6.4-1-7.16-4.66-2.05-9.87,12.33-6.96,17.82-9.76l-44.27-22.35-90.3,53.84-5.1-1.82-86.04-52.02-44.27,22.35c5.79,2.51,19.81.16,17.82,9.76-.76,3.66-3.75,4.73-7.16,4.66-5.09-.11-24.21-2.74-28.91-4.18C2.36,253.86,0,251.79,0,248.51c0-1.92,12.92-30.45,14.27-31.54,3.42-2.77,8.94-1.29,10.12,2.77,1.07,3.71-4.02,12.15-5.82,15.78l1.47.48,42.03-21.97.94-91.06,1.52-2.48,89.03-51.47V24.01c-4.23,2.5-6.74,10.94-11.74,11.85-4.41.8-7.9-2.86-7.11-7.11.29-1.59,16.86-24.35,18.85-26.23,3.6-3.39,6.45-3.32,9.99,0,1.99,1.87,18.56,24.64,18.85,26.23.79,4.25-2.7,7.91-7.11,7.11-5-.91-7.51-9.35-11.74-11.85ZM235.55,126l-1.66-2.82-74.9-43.16-77.44,45.49,76.58,45.51,77.43-45.01ZM152.55,258.01v-75.5l-78-46.5v75.5l74.45,45.55,3.55.95ZM242.55,136.01l-78,46.5v75.5l3.55-.95,74.45-45.55v-75.5Z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
BIN
src/assets/images/threeLoading.png
Normal file
BIN
src/assets/images/threeLoading.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 698 B |
@@ -166,9 +166,15 @@ defineExpose({open})
|
|||||||
<div class="modelBox">
|
<div class="modelBox">
|
||||||
<div class="model" ref="threeDom">
|
<div class="model" ref="threeDom">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="icon" v-show="!load.state">
|
||||||
|
<SvgIcon name="threeLogo" size="40" size-unit="px" />
|
||||||
|
</div>
|
||||||
<div class="load" v-show="load.state">
|
<div class="load" v-show="load.state">
|
||||||
<i class="fi fi-rr-cubes"></i>
|
<i class="fi fi-rr-cubes"></i>
|
||||||
<div class="text">Load...</div>
|
<div class="text">
|
||||||
|
<img src="@/assets/images/threeLoading.png" alt="">
|
||||||
|
{{ $t('threeModel.loading') }}
|
||||||
|
</div>
|
||||||
<div class="loadBox">
|
<div class="loadBox">
|
||||||
<div class="schedule" :style="{width:load.progress+'%'}"></div>
|
<div class="schedule" :style="{width:load.progress+'%'}"></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -190,30 +196,49 @@ defineExpose({open})
|
|||||||
border: 1px solid #D9D9D9;
|
border: 1px solid #D9D9D9;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
> .icon{
|
||||||
|
position: absolute;
|
||||||
|
bottom: 2.4rem;
|
||||||
|
right: 2.4rem;
|
||||||
|
}
|
||||||
> .load{
|
> .load{
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
background: rgba(0, 0, 0, .2);
|
background: #e6e6e6;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
> .text{
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 1.8rem;
|
||||||
|
line-height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #747474;
|
||||||
|
> img{
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
> i{
|
> i{
|
||||||
font-size: 3rem;
|
font-size: 3rem;
|
||||||
}
|
}
|
||||||
> .loadBox{
|
> .loadBox{
|
||||||
width: 15rem;
|
width: 26rem;
|
||||||
height: 1rem;
|
height: 1rem;
|
||||||
border-radius: 1rem;
|
border-radius: 3.3rem;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
margin-top: 1.2rem;
|
||||||
> .schedule{
|
> .schedule{
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: greenyellow;
|
border-radius: 3.3rem;
|
||||||
|
background: #848484;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,7 +80,7 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
font-size: 6px;
|
font-size: 9px;
|
||||||
color: #000;
|
color: #000;
|
||||||
border: 1px solid #d9d9d9;
|
border: 1px solid #d9d9d9;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|||||||
@@ -187,5 +187,9 @@ export default {
|
|||||||
},
|
},
|
||||||
assistant: {
|
assistant: {
|
||||||
inputPlaceholder: 'Ask anything',
|
inputPlaceholder: 'Ask anything',
|
||||||
|
},
|
||||||
|
//3d面板
|
||||||
|
threeModel: {
|
||||||
|
loading: 'Loading',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -182,5 +182,9 @@ export default {
|
|||||||
},
|
},
|
||||||
assistant: {
|
assistant: {
|
||||||
inputPlaceholder: '请输入'
|
inputPlaceholder: '请输入'
|
||||||
|
},
|
||||||
|
//3d面板
|
||||||
|
threeModel: {
|
||||||
|
loading: '加载中',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,10 @@
|
|||||||
const agentStore = useAgentStore()
|
const agentStore = useAgentStore()
|
||||||
const projectStore = useProjectStore()
|
const projectStore = useProjectStore()
|
||||||
|
|
||||||
const emits = defineEmits(['update:sketchList'])
|
const reportsContent = ref('')
|
||||||
|
const isGeneratingReport = ref(false)
|
||||||
|
|
||||||
|
const emits = defineEmits(['update:sketchList', 'setTitle'])
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
title: string
|
title: string
|
||||||
@@ -143,6 +146,7 @@
|
|||||||
id: messageList.value.length + 1,
|
id: messageList.value.length + 1,
|
||||||
text: '',
|
text: '',
|
||||||
isUser: false,
|
isUser: false,
|
||||||
|
sessionId: 'projectStore.state.id',
|
||||||
loading: true,
|
loading: true,
|
||||||
thinking: false,
|
thinking: false,
|
||||||
thinkingText: '',
|
thinkingText: '',
|
||||||
@@ -206,11 +210,13 @@
|
|||||||
// 流式响应处理
|
// 流式响应处理
|
||||||
let contentBody = ''
|
let contentBody = ''
|
||||||
let buffer = ''
|
let buffer = ''
|
||||||
|
const webAddressList = []
|
||||||
const reader = response.body?.getReader()
|
const reader = response.body?.getReader()
|
||||||
if (!reader) throw new Error('无法获取流读取器')
|
if (!reader) throw new Error('无法获取流读取器')
|
||||||
|
|
||||||
const decoder = new TextDecoder()
|
const decoder = new TextDecoder()
|
||||||
|
let previousEventName = '' // 记录上一个事件名称
|
||||||
|
let hasReportStarted = false // 标记 report 是否已经开始
|
||||||
try {
|
try {
|
||||||
let flag = true
|
let flag = true
|
||||||
while (flag) {
|
while (flag) {
|
||||||
@@ -232,16 +238,41 @@
|
|||||||
|
|
||||||
for (let event of events) {
|
for (let event of events) {
|
||||||
if (!event.trim()) continue
|
if (!event.trim()) continue
|
||||||
// debugger
|
|
||||||
// 过滤掉 id: 等字段,只取 data:
|
|
||||||
|
|
||||||
let isNodeIdEvent = false
|
// 解析事件名称(从 event:xxx 行)
|
||||||
if (event.includes('nodeId')) {
|
const eventName =
|
||||||
isNodeIdEvent = true
|
event
|
||||||
// continue
|
.split(/\n/)
|
||||||
|
.find((line) => line.startsWith('event:'))
|
||||||
|
?.replace(/^event:\s*/, '')
|
||||||
|
?.trim() || ''
|
||||||
|
|
||||||
|
if (!hasReportStarted && eventName === 'report') {
|
||||||
|
console.log('开始生成报告--------')
|
||||||
|
|
||||||
|
isGeneratingReport.value = true
|
||||||
|
contentBody += `<slot slot-name="card" title="123">123</slot>`
|
||||||
|
hasReportStarted = true
|
||||||
}
|
}
|
||||||
console.log('event', event)
|
|
||||||
if (event.includes('error')) {
|
if (
|
||||||
|
previousEventName === 'report' &&
|
||||||
|
eventName !== 'report' &&
|
||||||
|
reportsContent.value
|
||||||
|
) {
|
||||||
|
isGeneratingReport.value = false
|
||||||
|
localStorage.setItem(
|
||||||
|
'reportsContent_' + projectStore.state.id,
|
||||||
|
reportsContent.value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
previousEventName = eventName
|
||||||
|
|
||||||
|
// console.log('eventName:', eventName, 'event:', event)
|
||||||
|
|
||||||
|
// 根据事件名称精确判断,而不是用 includes
|
||||||
|
if (eventName === 'error') {
|
||||||
aiMessage.text = '出现错误,请重试'
|
aiMessage.text = '出现错误,请重试'
|
||||||
aiMessage.streaming = false
|
aiMessage.streaming = false
|
||||||
aiMessage.loading = false
|
aiMessage.loading = false
|
||||||
@@ -249,27 +280,29 @@
|
|||||||
flag = false
|
flag = false
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// TODO: 暂时不处理webAddress
|
if (eventName === 'todo') {
|
||||||
if (event.includes('todo')) {
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
let hasSketch = false
|
|
||||||
if (event.includes('sketchIDAndUrl')) {
|
let isNodeIdEvent = eventName === 'nodeId'
|
||||||
hasSketch = true
|
|
||||||
}
|
let hasSketch = eventName === 'sketch'
|
||||||
|
|
||||||
const dataLines = event
|
const dataLines = event
|
||||||
.split(/\n/)
|
.split(/\n/)
|
||||||
.filter((line) => line.startsWith('data:'))
|
.filter((line) => line.startsWith('data:'))
|
||||||
.map((line) => line.replace(/^data:\s*/, '').trim())
|
.map((line) => line.replace(/^data:\s*/, ''))
|
||||||
.filter((content) => content.startsWith('{') || content.startsWith('['))
|
.filter((content) => content.startsWith('{') || content.startsWith('['))
|
||||||
console.log('dataLInes', dataLines)
|
// console.log('dataLInes', dataLines)
|
||||||
if (isNodeIdEvent) {
|
if (isNodeIdEvent) {
|
||||||
params.versionID = dataLines[0]
|
params.versionID = dataLines[0]
|
||||||
projectStore.setProject({ nodeId: dataLines[0] })
|
projectStore.setProject({ nodeId: dataLines[0] })
|
||||||
}
|
}
|
||||||
|
if (eventName === 'webAddress') {
|
||||||
|
console.log('webAddress-----', eventName, dataLines)
|
||||||
|
}
|
||||||
|
|
||||||
if (event.includes('tool')) {
|
if (eventName === 'tool') {
|
||||||
MyEvent.emit('loading-sketch')
|
MyEvent.emit('loading-sketch')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,21 +311,38 @@
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const jsonData = JSON.parse(jsonText)
|
const jsonData = JSON.parse(jsonText)
|
||||||
console.log('jsonData', jsonData)
|
// console.log('jsonData', jsonData)
|
||||||
|
if (jsonData.webAddress) {
|
||||||
|
console.log('webAddress-----', jsonData)
|
||||||
|
}
|
||||||
|
if (jsonData.title) {
|
||||||
|
emits('setTitle', jsonData.title)
|
||||||
|
}
|
||||||
|
|
||||||
if (hasSketch) {
|
if (hasSketch) {
|
||||||
sketchList.value.push({
|
sketchList.value.push({
|
||||||
[Object.keys(jsonData)[0]]: jsonData[Object.keys(jsonData)[0]]
|
[Object.keys(jsonData)[0]]: jsonData[Object.keys(jsonData)[0]]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (
|
if (eventName === 'report') {
|
||||||
jsonData.content &&
|
reportsContent.value += jsonData.report
|
||||||
jsonData.content.length > 0 &&
|
} else {
|
||||||
jsonData.type !== 'end'
|
if (jsonData.reasoning) {
|
||||||
) {
|
aiMessage.thinking = true
|
||||||
contentBody += jsonData.content
|
aiMessage.loading = false
|
||||||
aiMessage.text = contentBody
|
aiMessage.thinkingText += jsonData.reasoning
|
||||||
aiMessage.loading = false
|
} else {
|
||||||
|
aiMessage.thinking = false
|
||||||
|
if (
|
||||||
|
jsonData.content &&
|
||||||
|
jsonData.content.length > 0 &&
|
||||||
|
jsonData.type !== 'end'
|
||||||
|
) {
|
||||||
|
contentBody += jsonData.content
|
||||||
|
aiMessage.text = contentBody
|
||||||
|
aiMessage.loading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (jsonData.type === 'end') {
|
if (jsonData.type === 'end') {
|
||||||
aiMessage.streaming = false
|
aiMessage.streaming = false
|
||||||
@@ -319,6 +369,9 @@
|
|||||||
console.warn('⚠️ JSON 格式错误,跳过:', jsonText)
|
console.warn('⚠️ JSON 格式错误,跳过:', jsonText)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新上一个事件名称
|
||||||
|
previousEventName = eventName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -378,7 +431,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 处理对话列表,将连续的 assistant 消息合并为一条
|
// 处理对话列表,将连续的 assistant 消息合并为一条
|
||||||
const processDialogue = (dialogue, startIndex, existingImgList) => {
|
const processDialogue = (dialogue, startIndex, existingImgList, sessionId) => {
|
||||||
if (!dialogue || dialogue.length === 0) return []
|
if (!dialogue || dialogue.length === 0) return []
|
||||||
|
|
||||||
const result = []
|
const result = []
|
||||||
@@ -393,26 +446,30 @@
|
|||||||
...item,
|
...item,
|
||||||
text: item.content,
|
text: item.content,
|
||||||
isUser: true,
|
isUser: true,
|
||||||
id: result.length + 1
|
id: result.length + 1,
|
||||||
|
sessionId: sessionId
|
||||||
})
|
})
|
||||||
i++
|
i++
|
||||||
} else if (item.role === 'assistant') {
|
} else if (item.role === 'assistant') {
|
||||||
// assistant 角色,拼接直到下一个 user
|
// assistant 角色,拼接直到下一个 user
|
||||||
let combinedContent = item.content || ''
|
let combinedContent = item.content || ''
|
||||||
|
let combinedThinkingText = item.reasoning || ''
|
||||||
// 继续往后找连续的 assistant 消息
|
// 继续往后找连续的 assistant 消息
|
||||||
let j = i + 1
|
let j = i + 1
|
||||||
while (j < dialogue.length && dialogue[j].role === 'assistant') {
|
while (j < dialogue.length && dialogue[j].role === 'assistant') {
|
||||||
combinedContent += dialogue[j].content || ''
|
combinedContent += dialogue[j].content || ''
|
||||||
|
combinedThinkingText += dialogue[j].reasoning || ''
|
||||||
j++
|
j++
|
||||||
}
|
}
|
||||||
|
|
||||||
result.push({
|
result.push({
|
||||||
...item,
|
...item,
|
||||||
content: combinedContent,
|
content: combinedContent,
|
||||||
|
thinkingText: combinedThinkingText,
|
||||||
text: combinedContent,
|
text: combinedContent,
|
||||||
isUser: false,
|
isUser: false,
|
||||||
id: result.length + 1
|
id: result.length + 1,
|
||||||
|
sessionId: sessionId
|
||||||
})
|
})
|
||||||
|
|
||||||
i = j
|
i = j
|
||||||
@@ -422,7 +479,8 @@
|
|||||||
...item,
|
...item,
|
||||||
text: item.content,
|
text: item.content,
|
||||||
isUser: item.role === 'user',
|
isUser: item.role === 'user',
|
||||||
id: result.length + 1
|
id: result.length + 1,
|
||||||
|
sessionId: sessionId
|
||||||
})
|
})
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
@@ -430,6 +488,44 @@
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
const processSession = (session, imgList, ancestorsList, idCounterRef) => {
|
||||||
|
if (!session) return
|
||||||
|
|
||||||
|
// 1. 在 dialogue 第一个 report 有值的项的 content 中插入报告卡片
|
||||||
|
if (session.dialogue) {
|
||||||
|
for (let i = 0; i < session.dialogue.length; i++) {
|
||||||
|
if (session.dialogue[i].report) {
|
||||||
|
session.dialogue[i].content =
|
||||||
|
`<slot slot-name="card" title="123">123</slot>` +
|
||||||
|
(session.dialogue[i].content || '')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 收集 report 内容并保存到 localStorage
|
||||||
|
let reportStr = ''
|
||||||
|
session.dialogue?.forEach((item) => {
|
||||||
|
if (item.report) {
|
||||||
|
reportStr += item.report
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (reportStr && session.id) {
|
||||||
|
localStorage.setItem(`reportsContent_${session.id}`, reportStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 收集 sketchIDAndUrl 到 imgList
|
||||||
|
if (session.sketchIDAndUrl) {
|
||||||
|
imgList.push(...session.sketchIDAndUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 处理 dialogue
|
||||||
|
const list = processDialogue(session.dialogue, 0, imgList, session.id)
|
||||||
|
list.forEach((el) => {
|
||||||
|
el.id = idCounterRef.value++
|
||||||
|
})
|
||||||
|
ancestorsList.push(...list)
|
||||||
|
}
|
||||||
|
|
||||||
const setChatInfo = (info) => {
|
const setChatInfo = (info) => {
|
||||||
const initialData = agentStore.getInitialProjectData
|
const initialData = agentStore.getInitialProjectData
|
||||||
@@ -454,37 +550,25 @@
|
|||||||
messageList.value = []
|
messageList.value = []
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const { ancestors, current } = data
|
const { ancestors, current } = data
|
||||||
|
|
||||||
let imgList = []
|
// 处理单个会话(ancestor 或 current)
|
||||||
const ancestorsList = []
|
|
||||||
let ancestorsIdCounter = 1
|
|
||||||
if (ancestors) {
|
|
||||||
ancestors.forEach((item) => {
|
|
||||||
if (item.sketchIDAndUrl) {
|
|
||||||
imgList = imgList.concat(item.sketchIDAndUrl)
|
|
||||||
}
|
|
||||||
const list = processDialogue(item.dialogue, 0, imgList)
|
|
||||||
// 重新设置 id
|
|
||||||
list.forEach((el) => {
|
|
||||||
el.id = ancestorsIdCounter++
|
|
||||||
})
|
|
||||||
ancestorsList.push(...list)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const currentList = processDialogue(current?.dialogue, 0, imgList)
|
|
||||||
if (current.sketchIDAndUrl) {
|
|
||||||
imgList = imgList.concat(current.sketchIDAndUrl)
|
|
||||||
}
|
|
||||||
|
|
||||||
currentList.forEach((el, index) => {
|
const imgList = []
|
||||||
el.id = index + 1 + ancestorsList.length
|
const ancestorsList = []
|
||||||
|
const idCounterRef = { value: 1 }
|
||||||
|
|
||||||
|
// 处理所有 ancestors
|
||||||
|
ancestors?.forEach((item) => {
|
||||||
|
processSession(item, imgList, ancestorsList, idCounterRef)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 处理 current
|
||||||
|
processSession(current, imgList, ancestorsList, idCounterRef)
|
||||||
|
|
||||||
// 延迟设置新数据,确保 UI 有时间响应清空操作
|
// 延迟设置新数据,确保 UI 有时间响应清空操作
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
messageList.value = [...ancestorsList, ...currentList]
|
messageList.value = [...ancestorsList]
|
||||||
params.versionID = current?.id
|
params.versionID = current?.id
|
||||||
sketchList.value = imgList
|
sketchList.value = imgList
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
class="img-item"
|
class="img-item"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="message-context" v-show="content.thinking">
|
<div class="message-context" v-show="content.thinkingText?.length > 0">
|
||||||
<div class="thinking">
|
<div class="thinking">
|
||||||
<div
|
<div
|
||||||
class="thinking-header flex align-center"
|
class="thinking-header flex align-center"
|
||||||
@@ -37,15 +37,16 @@
|
|||||||
:markdown="content.text"
|
:markdown="content.text"
|
||||||
:rehype-plugins="[rehypeRaw]"
|
:rehype-plugins="[rehypeRaw]"
|
||||||
>
|
>
|
||||||
<template v-slot:s-ReportCard="" {children:children,...attrs}>
|
<template v-slot:s-card="{ children: children, ...attrs }">
|
||||||
<ReportCard
|
<Card :title="attrs.title" @click.native="handleClickReport" />
|
||||||
:report="{ title: attrs.title, content: attrs.content }"
|
|
||||||
@click="handleClickReport(content)"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
</VueMarkdown>
|
</VueMarkdown>
|
||||||
</div>
|
</div>
|
||||||
<div class="operate flex" :class="{ 'is-user': content.isUser }">
|
<div
|
||||||
|
v-show="!content.thinking"
|
||||||
|
class="operate flex"
|
||||||
|
:class="{ 'is-user': content.isUser }"
|
||||||
|
>
|
||||||
<template v-if="content.isUser">
|
<template v-if="content.isUser">
|
||||||
<SvgIcon name="copy" size="16" color="#000" @click.stop="handleCopyText" />
|
<SvgIcon name="copy" size="16" color="#000" @click.stop="handleCopyText" />
|
||||||
</template>
|
</template>
|
||||||
@@ -81,11 +82,12 @@
|
|||||||
import gsap from 'gsap'
|
import gsap from 'gsap'
|
||||||
import userThumb from '@/assets/images/user-thumb.jpg'
|
import userThumb from '@/assets/images/user-thumb.jpg'
|
||||||
import agentThumb from '@/assets/images/agent-thumb.png'
|
import agentThumb from '@/assets/images/agent-thumb.png'
|
||||||
import ReportCard from './ReportCard.vue'
|
import Card from './ReportCard.vue'
|
||||||
import UrlCard from './UrlCard.vue'
|
import Url from './UrlCard.vue'
|
||||||
import { VueMarkdown } from '@crazydos/vue-markdown'
|
import { VueMarkdown } from '@crazydos/vue-markdown'
|
||||||
import type { CustomAttrs } from '@crazydos/vue-markdown'
|
import type { CustomAttrs } from '@crazydos/vue-markdown'
|
||||||
import rehypeRaw from 'rehype-raw'
|
import rehypeRaw from 'rehype-raw'
|
||||||
|
import MyEvent from '@/utils/myEvent'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
@@ -94,6 +96,14 @@
|
|||||||
isLast: Boolean
|
isLast: Boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.content,
|
||||||
|
(newVal) => {
|
||||||
|
console.log('newVal-----', newVal)
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
const emit = defineEmits(['regenerate'])
|
const emit = defineEmits(['regenerate'])
|
||||||
|
|
||||||
const imageList = computed(() => {
|
const imageList = computed(() => {
|
||||||
@@ -200,7 +210,8 @@
|
|||||||
props.content.thinkingCollapsed = !props.content.thinkingCollapsed
|
props.content.thinkingCollapsed = !props.content.thinkingCollapsed
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleClickReport = (data) => {
|
const handleClickReport = () => {
|
||||||
|
MyEvent.emit('openReport', props.content.sessionId)
|
||||||
// 点击显示报告
|
// 点击显示报告
|
||||||
}
|
}
|
||||||
const handleClickUrls = (data) => {
|
const handleClickUrls = (data) => {
|
||||||
|
|||||||
@@ -57,14 +57,21 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-else class="report-content">
|
<div v-else class="report-content">
|
||||||
<div class="downBtnBox">
|
<div class="downBtnBox">
|
||||||
<div class="downBtn">
|
<div class="downBtn" @click="handleDownloadMd">
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<SvgIcon name="reportDown" size="16"></SvgIcon>
|
<SvgIcon name="reportDown" size="16"></SvgIcon>
|
||||||
</div>
|
</div>
|
||||||
<span>{{ $t('agent.Download') }}</span>
|
<span>{{ $t('agent.Download') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content"></div>
|
<div class="content">
|
||||||
|
<VueMarkdown
|
||||||
|
:custom-attrs="customAttrs"
|
||||||
|
:markdown="markdownContent"
|
||||||
|
:rehype-plugins="[rehypeRaw]"
|
||||||
|
>
|
||||||
|
</VueMarkdown>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -82,6 +89,10 @@
|
|||||||
const projectStore = useProjectStore()
|
const projectStore = useProjectStore()
|
||||||
import MyEvent from '@/utils/myEvent'
|
import MyEvent from '@/utils/myEvent'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { VueMarkdown } from '@crazydos/vue-markdown'
|
||||||
|
import type { CustomAttrs } from '@crazydos/vue-markdown'
|
||||||
|
import rehypeRaw from 'rehype-raw'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const emits = defineEmits(['deleteSketch'])
|
const emits = defineEmits(['deleteSketch'])
|
||||||
|
|
||||||
@@ -99,6 +110,36 @@
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const customAttrs: CustomAttrs = {
|
||||||
|
img: {
|
||||||
|
style: 'max-width: 100%;display:block;'
|
||||||
|
},
|
||||||
|
a: (node, combinedAttrs) => {
|
||||||
|
if (typeof node.properties.href === 'string') {
|
||||||
|
return { target: '_blank', rel: 'noopener noreferrer' }
|
||||||
|
} else {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const sessionId = ref('')
|
||||||
|
const markdownContent = ref('')
|
||||||
|
const setSessionId = (id: string) => {
|
||||||
|
|
||||||
|
sessionId.value = id
|
||||||
|
}
|
||||||
|
watch(
|
||||||
|
() => sessionId.value,
|
||||||
|
(newVal) => {
|
||||||
|
if (newVal) {
|
||||||
|
markdownContent.value = localStorage.getItem(`reportsContent_${newVal}`)
|
||||||
|
console.log('markdownContent-----', markdownContent.value);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// 图片加载完成时触发
|
// 图片加载完成时触发
|
||||||
const handleImageLoad = (index: number) => {
|
const handleImageLoad = (index: number) => {
|
||||||
loadedStatus[index] = true
|
loadedStatus[index] = true
|
||||||
@@ -147,6 +188,20 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleDownloadMd = () => {
|
||||||
|
if (!markdownContent.value) return
|
||||||
|
|
||||||
|
const blob = new Blob([markdownContent.value], { type: 'text/markdown' })
|
||||||
|
const url = URL.createObjectURL(blob)
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = url
|
||||||
|
link.download = `report-${Date.now()}.md`
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
document.body.removeChild(link)
|
||||||
|
URL.revokeObjectURL(url)
|
||||||
|
}
|
||||||
|
|
||||||
const showLoading = ref(false)
|
const showLoading = ref(false)
|
||||||
const handleLoadingSketch = () => {
|
const handleLoadingSketch = () => {
|
||||||
showLoading.value = true
|
showLoading.value = true
|
||||||
@@ -166,6 +221,10 @@
|
|||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
MyEvent.remove('loading-sketch', handleLoadingSketch)
|
MyEvent.remove('loading-sketch', handleLoadingSketch)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
setSessionId
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|||||||
@@ -1,22 +1,23 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="report-card">
|
<div class="report-card">
|
||||||
<div class="report-card-header">
|
<div class="report-card-header">
|
||||||
<span>{{ report.title }}</span>
|
<span>{{ title }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="report-card-content">
|
<div class="report-card-content">
|
||||||
<span>{{ report.content }}</span>
|
<span>{{ title }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
report: any
|
title: string
|
||||||
}>()
|
}>()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.report-card {
|
.report-card {
|
||||||
|
cursor: pointer;
|
||||||
width:100%;
|
width:100%;
|
||||||
margin: 2.4rem 0;
|
margin: 2.4rem 0;
|
||||||
min-height: 11.2rem;
|
min-height: 11.2rem;
|
||||||
@@ -24,9 +25,9 @@
|
|||||||
background-size: 100% 100%;
|
background-size: 100% 100%;
|
||||||
padding: 2.9rem;
|
padding: 2.9rem;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
&:first-of-type{
|
// &:first-of-type{
|
||||||
margin-top: 0;
|
// margin-top: 0;
|
||||||
}
|
// }
|
||||||
&-header {
|
&-header {
|
||||||
font-family: 'Medium';
|
font-family: 'Medium';
|
||||||
font-size: 1.6rem;
|
font-size: 1.6rem;
|
||||||
|
|||||||
@@ -4,9 +4,19 @@
|
|||||||
<div class="btn" @click="versionTreeData.drawer = true">Version Tree</div>
|
<div class="btn" @click="versionTreeData.drawer = true">Version Tree</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content-wrapper">
|
<div class="content-wrapper">
|
||||||
<Agent ref="agentRef" :title="agentTitle" @update:sketchList="updateSketchList" />
|
<Agent
|
||||||
|
ref="agentRef"
|
||||||
|
:title="agentTitle"
|
||||||
|
@update:sketchList="updateSketchList"
|
||||||
|
@setTitle="handleSetTitle"
|
||||||
|
/>
|
||||||
<div class="preview-wrapper">
|
<div class="preview-wrapper">
|
||||||
<Preview :type="previewType" :sketchList="sketchList" @deleteSketch="handleDeleteSketch" />
|
<Preview
|
||||||
|
ref="previewRef"
|
||||||
|
:type="previewType"
|
||||||
|
:sketchList="sketchList"
|
||||||
|
@deleteSketch="handleDeleteSketch"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<VersionTreeIndex
|
<VersionTreeIndex
|
||||||
@@ -19,7 +29,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch, onMounted, computed } from 'vue'
|
import { ref, watch, onMounted, computed, onUnmounted } from 'vue'
|
||||||
import Agent from './components/Agent.vue'
|
import Agent from './components/Agent.vue'
|
||||||
import Preview from './components/Preview.vue'
|
import Preview from './components/Preview.vue'
|
||||||
import VersionTreeIndex from './components/versionTree/index.vue'
|
import VersionTreeIndex from './components/versionTree/index.vue'
|
||||||
@@ -27,10 +37,13 @@
|
|||||||
import { getProjectInfo } from '@/api/agent'
|
import { getProjectInfo } from '@/api/agent'
|
||||||
import { clearNodeChat, getNodeAncestors } from '@/api/versitonTree'
|
import { clearNodeChat, getNodeAncestors } from '@/api/versitonTree'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
|
import MyEvent from '@/utils/myEvent'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const projectStore = useProjectStore()
|
const projectStore = useProjectStore()
|
||||||
|
|
||||||
|
const previewRef = ref(null)
|
||||||
|
|
||||||
const agentTitle = ref('Conversation')
|
const agentTitle = ref('Conversation')
|
||||||
const previewType = ref<'sketch' | 'report'>('sketch')
|
const previewType = ref<'sketch' | 'report'>('sketch')
|
||||||
const VersionTreeIndexRef = ref()
|
const VersionTreeIndexRef = ref()
|
||||||
@@ -50,6 +63,10 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleSetTitle = (title: string) => {
|
||||||
|
agentTitle.value = title
|
||||||
|
}
|
||||||
|
|
||||||
const versionTreeData = ref({
|
const versionTreeData = ref({
|
||||||
drawer: false
|
drawer: false
|
||||||
})
|
})
|
||||||
@@ -86,6 +103,11 @@
|
|||||||
|
|
||||||
const proJectId = computed(() => route.params.id)
|
const proJectId = computed(() => route.params.id)
|
||||||
|
|
||||||
|
const handleOpenReport = (data) => {
|
||||||
|
previewRef.value.setSessionId(data)
|
||||||
|
previewType.value = 'report'
|
||||||
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => proJectId.value,
|
() => proJectId.value,
|
||||||
(newVal, oldVal) => {
|
(newVal, oldVal) => {
|
||||||
@@ -97,11 +119,15 @@
|
|||||||
)
|
)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
MyEvent.add('openReport', handleOpenReport)
|
||||||
projectStore.clearProject()
|
projectStore.clearProject()
|
||||||
if (proJectId.value) {
|
if (proJectId.value) {
|
||||||
handleGetProjectInfoAndHistory()
|
handleGetProjectInfoAndHistory()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
onUnmounted(() => {
|
||||||
|
MyEvent.remove('openReport', handleOpenReport)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|||||||
Reference in New Issue
Block a user