This commit is contained in:
lzp
2026-03-04 15:06:24 +08:00
11 changed files with 127 additions and 47 deletions

View File

@@ -221,7 +221,8 @@
}
buffer += decoder.decode(value, { stream: true })
console.log('收到chunk',new Date().getTime());
// 优先按空行拆分事件块SSE标准
let events = buffer.split(/\n\n/)
buffer = events.pop() // 保留不完整块
@@ -261,7 +262,7 @@
try {
const jsonData = JSON.parse(jsonText)
console.log('jsonData', jsonData)
// console.log('jsonData', jsonData)
// 赋值 project_id 和 version_id
// if (jsonData.project_id) params.projectID = jsonData.project_id
@@ -361,6 +362,67 @@
)
}
// 处理对话列表,将连续的 assistant 消息合并为一条
const processDialogue = (dialogue, startIndex, existingImgList) => {
if (!dialogue || dialogue.length === 0) return []
const result = []
let i = startIndex
while (i < dialogue.length) {
const item = dialogue[i]
if (item.image_url) {
existingImgList.push(item.image_url)
}
if (item.role === 'user') {
// user 角色直接添加
result.push({
...item,
text: item.content,
isUser: true,
id: result.length + 1
})
i++
} else if (item.role === 'assistant') {
// assistant 角色,拼接直到下一个 user
let combinedContent = item.content || ''
// 继续往后找连续的 assistant 消息
let j = i + 1
while (j < dialogue.length && dialogue[j].role === 'assistant') {
if (dialogue[j].image_url) {
existingImgList.push(dialogue[j].image_url)
}
combinedContent += dialogue[j].content || ''
j++
}
result.push({
...item,
content: combinedContent,
text: combinedContent,
isUser: false,
id: result.length + 1
})
i = j
} else {
// 其他角色直接添加
result.push({
...item,
text: item.content,
isUser: item.role === 'user',
id: result.length + 1
})
i++
}
}
return result
}
const setChatInfo = (info) => {
const initialData = agentStore.getInitialProjectData
if (isGenerating.value || initialData) return
@@ -389,35 +451,22 @@
const imgList = []
const ancestorsList = []
let ancestorsIdCounter = 1
if (ancestors) {
ancestors.forEach((item) => {
const list =
item.dialogue?.map((el, index) => {
if (el.image_url) {
imgList.push(el.image_url)
}
return {
...el,
text: el.content,
isUser: el.role === 'user',
id: index + 1
}
}) || []
const list = processDialogue(item.dialogue, 0, imgList)
// 重新设置 id
list.forEach((el) => {
el.id = ancestorsIdCounter++
})
ancestorsList.push(...list)
})
}
const currentList =
current?.dialogue?.map((item, index) => {
if (item.image_url) {
imgList.push(item.image_url)
}
return {
...item,
text: item.content,
isUser: item.role === 'user',
id: index + 1 + ancestorsList.length
}
}) || []
const currentList = processDialogue(current?.dialogue, 0, imgList)
// 重新设置 id
currentList.forEach((el, index) => {
el.id = index + 1 + ancestorsList.length
})
// 延迟设置新数据,确保 UI 有时间响应清空操作
nextTick(() => {

View File

@@ -7,10 +7,7 @@
<div class="thumb">
<img :src="content.isUser ? userThumb : agentThumb" class="thumb-icon" />
</div>
<div
class="message-context"
v-show="!content.loading && !content.thinking && !content.streaming"
>
<div class="message-context" v-show="!content.loading">
<div class="img-list flex" v-if="imageList.length > 0">
<img
v-for="(item, index) in imageList"
@@ -22,7 +19,7 @@
<div class="message-txt markdown-body">
<div v-html="formatMessage"></div>
</div>
<div class="operate flex" v-show="isLast" :class="{ 'is-user': content.isUser }">
<div class="operate flex" :class="{ 'is-user': content.isUser }">
<template v-if="content.isUser">
<SvgIcon name="copy" size="16" color="#000" @click.stop="handleCopyText" />
</template>
@@ -30,6 +27,7 @@
<SvgIcon
v-for="operate in operateList"
:key="operate.name"
v-show="isLast"
:name="operate.name"
:size="operate.name === 'refreshTransparent' ? '14' : '16'"
color="#000000A6"
@@ -196,7 +194,7 @@
column-gap: 0.9rem;
// align-items: flex-start;
&.is-user {
text-align: right;
// text-align: right;
flex-direction: row-reverse;
column-gap: 1.3rem;
}
@@ -221,6 +219,7 @@
.message-context {
line-height: 2rem;
font-size: 1.4rem;
max-width: 82%;
}
}
.operate {

View File

@@ -165,12 +165,17 @@
height: 100%;
gap: 1.2rem;
flex-wrap: wrap;
overflow-y: auto;
align-content: flex-start;
.sketch-item {
position: relative;
&,
width: calc((100% - 1.2rem * 3) / 4);
//设置比例
aspect-ratio: 1 / 1;
border-radius: 1.6rem;
img {
width: 21.9rem;
height: 21.9rem;
width: 100%;
height: 100%;
border-radius: 1.6rem;
}
.menu-btn {

View File

@@ -62,7 +62,7 @@ function traverseArray(items, callback) {
const initialize = ()=>{
isLoad.value = false
treeList.value = []
treeList.value.push({versionId: null,name:'index',})
// treeList.value.push({versionId: null,name:'index',})
traverseArray(props.versionsList, (item, index) => {
treeList.value.push(item)
})

View File

@@ -60,7 +60,7 @@ const push = (item)=>{
let className = `custom-node item${item.versionId.replace(/-/g, "_")}`
let id = item.versionId
let source = edges.value.length == 0?'0':item.versionId.slice(0, -2)
nodes.value.push({id,type:'SecondaryNode',class:className,position,data:item})
nodes.value.push({id,type:nodes.value.length == 0?'SecondaryNode':'SecondaryNode',class:className,position,data:item})
edges.value.push({ id, target:id, source, type: 'smoothstep' })
}
}

View File

@@ -18,7 +18,7 @@ const props = defineProps<{
<!-- source输入target输出 -->
<template>
<div class="node" :class="{active:props.selectItem.id == props.data.id}">
<Handle type="target" id="Top" :connectableStart="false" :connectableEnd="false" :position="Position.Top" />
<Handle v-if="props.data?.versionId !== '1'" type="target" id="Top" :connectableStart="false" :connectableEnd="false" :position="Position.Top" />
<Handle type="source" id="Bottom" :connectableStart="false" :connectableEnd="false" :position="Position.Bottom" />
<!-- <Handle type="source" id="Right" :position="Position.Right" />
<Handle type="target" id="Left" :position="Position.Left" /> -->

View File

@@ -517,8 +517,8 @@
const settingPopupVisible = ref(false)
const settingOptions = ref([
{ label: 'Input.settingOptions.creativity', value: 50 },
{ label: 'Input.settingOptions.diversity', value: 75 },
{ label: 'Input.settingOptions.relevance', value: 60 }
{ label: 'Input.settingOptions.diversity', value: 50 },
{ label: 'Input.settingOptions.relevance', value: 50 }
])
const openStylePopup = () => {

View File

@@ -48,7 +48,12 @@
<el-popover
placement="right"
trigger="click"
popper-style="padding: 1rem 0.5rem;"
width="10rem"
popper-style="
padding: .6rem 0.7rem;
border-radius: 1rem;
min-width: 10rem;
"
v-model:visible="item.visible"
>
<template #reference>
@@ -56,10 +61,20 @@
</template>
<div class="history-item-menu">
<div class="rename" @click="onRenameHistoryItem(item)">
{{ $t('Home.rename') }}
<div class="icon">
<svg-icon name="historyEdit" size="13" />
</div>
<span>
{{ $t('Home.rename') }}
</span>
</div>
<div class="delete" @click="onDeleteHistoryItem(item)">
{{ $t('Home.delete') }}
<div class="icon">
<svg-icon name="historyDelete" size="13" />
</div>
<span>
{{ $t('Home.delete') }}
</span>
</div>
</div>
</el-popover>
@@ -338,18 +353,24 @@
}
.history-item-menu {
user-select: none;
> div {
cursor: pointer;
padding: 0.5rem 1rem;
padding: 0.6rem .7rem;
display: flex;
font-size: 1.3rem;
> .icon{
margin-right: .8rem;
}
&:hover {
background-color: rgba(0, 0, 0, 0.06);
}
}
> .rename {
color: #409eff;
color: #000;
}
> .delete {
color: #ff4d4f;
color: #ff4747;
}
}
</style>

View File

@@ -89,7 +89,7 @@
if (isVisible.value) {
isVisible.value = false
} else {
router.back()
router.push({ name: 'index' })
}
}
const onForgotPassword = () => {