feat: 重构聊天时间线与附件上传交互
- 管理端和用户中心统一切换到 chatTime 时间线模型,按真实 assistant/tool 时序渲染 - 收紧工具气泡与 JSON 展示样式,保留同气泡内的工具状态更新 - 回形针直接触发文件选择,附件列表上移到输入框上方并补充共享 helper 测试
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import type { ChatTimeTimelineItem } from '@easyflow/types';
|
||||
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import { cloneDeep } from '@easyflow/utils';
|
||||
|
||||
import { ArrowLeft, Minus, Plus } from '@element-plus/icons-vue';
|
||||
import {
|
||||
ElAside,
|
||||
@@ -81,40 +81,12 @@ function removeBotFromRecentlyUsed(botId: any) {
|
||||
}
|
||||
});
|
||||
}
|
||||
const messageList = ref<any>([]);
|
||||
function addMessage(message: any) {
|
||||
messageList.value.push(message);
|
||||
const messageList = ref<ChatTimeTimelineItem[]>([]);
|
||||
function mutateMessages(mutator: (messages: ChatTimeTimelineItem[]) => void) {
|
||||
const next = [...messageList.value];
|
||||
mutator(next);
|
||||
messageList.value = next;
|
||||
}
|
||||
function updateLastMessage(item: any) {
|
||||
const lastIndex = messageList.value.length - 1;
|
||||
let message = item;
|
||||
|
||||
if (typeof item === 'function') {
|
||||
message = item(messageList.value[lastIndex]);
|
||||
}
|
||||
|
||||
if (lastIndex >= 0) {
|
||||
messageList.value[lastIndex] = {
|
||||
...messageList.value[lastIndex],
|
||||
...message,
|
||||
};
|
||||
}
|
||||
}
|
||||
const stopThinking = () => {
|
||||
const lastIndex = messageList.value.length - 1;
|
||||
|
||||
if (lastIndex >= 0 && messageList.value[lastIndex]?.chains) {
|
||||
const chains = cloneDeep(messageList.value[lastIndex].chains);
|
||||
|
||||
for (const chain of chains) {
|
||||
if (!('id' in chain) && chain.thinkingStatus === 'thinking') {
|
||||
chain.thinkingStatus = 'end';
|
||||
}
|
||||
}
|
||||
|
||||
messageList.value[lastIndex].chains = chains;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -145,11 +117,9 @@ const stopThinking = () => {
|
||||
<ChatBubbleList v-else :bot="botInfo" :messages="messageList" />
|
||||
<ChatSender
|
||||
class="absolute bottom-5 left-0 w-full"
|
||||
:add-message="addMessage"
|
||||
:update-last-message="updateLastMessage"
|
||||
:stop-thinking="stopThinking"
|
||||
:bot="botInfo"
|
||||
:conversation-id="conversationId"
|
||||
:mutate-messages="mutateMessages"
|
||||
/>
|
||||
</div>
|
||||
</ElMain>
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import type { ChatTimeTimelineItem } from '@easyflow/types';
|
||||
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
import { IconifyIcon } from '@easyflow/icons';
|
||||
import { cloneDeep, cn } from '@easyflow/utils';
|
||||
import { cn } from '@easyflow/utils';
|
||||
|
||||
import { ElAside, ElContainer, ElMain } from 'element-plus';
|
||||
|
||||
@@ -34,43 +36,15 @@ function getAssistantList() {
|
||||
}
|
||||
});
|
||||
}
|
||||
const messageList = ref<any>([]);
|
||||
function addMessage(message: any) {
|
||||
messageList.value.push(message);
|
||||
}
|
||||
function updateLastMessage(item: any) {
|
||||
const lastIndex = messageList.value.length - 1;
|
||||
let message = item;
|
||||
|
||||
if (typeof item === 'function') {
|
||||
message = item(messageList.value[lastIndex]);
|
||||
}
|
||||
|
||||
if (lastIndex >= 0) {
|
||||
messageList.value[lastIndex] = {
|
||||
...messageList.value[lastIndex],
|
||||
...message,
|
||||
};
|
||||
}
|
||||
}
|
||||
const stopThinking = () => {
|
||||
const lastIndex = messageList.value.length - 1;
|
||||
|
||||
if (lastIndex >= 0 && messageList.value[lastIndex]?.chains) {
|
||||
const chains = cloneDeep(messageList.value[lastIndex].chains);
|
||||
|
||||
for (const chain of chains) {
|
||||
if (!('id' in chain) && chain.thinkingStatus === 'thinking') {
|
||||
chain.thinkingStatus = 'end';
|
||||
}
|
||||
}
|
||||
|
||||
messageList.value[lastIndex].chains = chains;
|
||||
}
|
||||
};
|
||||
function setMessageList(messages: any) {
|
||||
const messageList = ref<ChatTimeTimelineItem[]>([]);
|
||||
function setMessageList(messages: ChatTimeTimelineItem[]) {
|
||||
messageList.value = messages;
|
||||
}
|
||||
function mutateMessages(mutator: (messages: ChatTimeTimelineItem[]) => void) {
|
||||
const next = [...messageList.value];
|
||||
mutator(next);
|
||||
messageList.value = next;
|
||||
}
|
||||
const isFold = ref(false);
|
||||
const toggleFold = () => {
|
||||
isFold.value = !isFold.value;
|
||||
@@ -95,11 +69,9 @@ const toggleFold = () => {
|
||||
<ChatBubbleList :bot="currentBot" :messages="messageList" />
|
||||
<div class="mx-auto w-full max-w-[1000px]">
|
||||
<ChatSender
|
||||
:add-message="addMessage"
|
||||
:update-last-message="updateLastMessage"
|
||||
:stop-thinking="stopThinking"
|
||||
:bot="currentBot"
|
||||
:conversation-id="conversationId"
|
||||
:mutate-messages="mutateMessages"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import type { ChatTimeTimelineItem } from '@easyflow/types';
|
||||
|
||||
import { computed, onMounted, ref, watch } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import { ChatTimeHistoryMapper } from '@easyflow/utils';
|
||||
import { Delete, Edit, Search } from '@element-plus/icons-vue';
|
||||
import {
|
||||
ElButton,
|
||||
@@ -43,7 +46,8 @@ const pageState = ref({
|
||||
const drawerVisible = ref(false);
|
||||
const drawerLoading = ref(false);
|
||||
const currentSession = ref<any>();
|
||||
const messageList = ref<any[]>([]);
|
||||
const messageList = ref<ChatTimeTimelineItem[]>([]);
|
||||
const loadedMessageRecordCount = ref(0);
|
||||
const messagePage = ref({
|
||||
total: 0,
|
||||
pageNumber: 1,
|
||||
@@ -123,6 +127,7 @@ async function openSession(sessionId: string | number) {
|
||||
}
|
||||
currentSession.value = summaryRes.data;
|
||||
messageList.value = [];
|
||||
loadedMessageRecordCount.value = 0;
|
||||
messagePage.value = {
|
||||
total: 0,
|
||||
pageNumber: 1,
|
||||
@@ -162,32 +167,22 @@ async function loadMessages(reset = false) {
|
||||
} else {
|
||||
messageList.value = [...normalized, ...messageList.value];
|
||||
}
|
||||
loadedMessageRecordCount.value = reset
|
||||
? (res.data?.records || []).length
|
||||
: loadedMessageRecordCount.value + (res.data?.records || []).length;
|
||||
messagePage.value.total = res.data?.total || 0;
|
||||
messagePage.value.pageNumber = nextPageNumber;
|
||||
}
|
||||
|
||||
function normalizeMessages(records: any[]) {
|
||||
return [...records]
|
||||
.reverse()
|
||||
.map((item: any) => ({
|
||||
key: String(item.id),
|
||||
role: item.senderRole === 'assistant' ? 'assistant' : 'user',
|
||||
content:
|
||||
item.senderRole === 'assistant'
|
||||
? String(item.contentText || '').replace(/^Final Answer:\s*/i, '')
|
||||
: item.contentText,
|
||||
placement: item.senderRole === 'assistant' ? 'start' : 'end',
|
||||
created: item.created,
|
||||
chains: Array.isArray(item.contentPayload?.chains)
|
||||
? item.contentPayload.chains
|
||||
: undefined,
|
||||
}));
|
||||
return ChatTimeHistoryMapper.fromHistoryRecords([...records].reverse());
|
||||
}
|
||||
|
||||
function closeDrawer() {
|
||||
drawerVisible.value = false;
|
||||
currentSession.value = undefined;
|
||||
messageList.value = [];
|
||||
loadedMessageRecordCount.value = 0;
|
||||
router.replace({ path: '/chatHistory', query: {} });
|
||||
}
|
||||
|
||||
@@ -349,7 +344,7 @@ function formatTime(value?: string) {
|
||||
<div class="flex-1 overflow-hidden">
|
||||
<div class="mb-4 flex justify-center">
|
||||
<ElButton
|
||||
v-if="messageList.length < messagePage.total"
|
||||
v-if="loadedMessageRecordCount < messagePage.total"
|
||||
text
|
||||
type="primary"
|
||||
@click="loadMessages(false)"
|
||||
|
||||
Reference in New Issue
Block a user