diff --git a/src/i18n/lang/en.ts b/src/i18n/lang/en.ts
index 49477552fe6fb71342096cc9912570aa282597e9..6f3db55e1127388dc6b636572e919a1376960139 100644
--- a/src/i18n/lang/en.ts
+++ b/src/i18n/lang/en.ts
@@ -20,6 +20,17 @@ export default {
setting: '设置',
reply: 'Reply',
},
+ conversation: {
+ interrupt_title: 'Interrupt Conversation',
+ interrupt_warning: 'A conversation is currently in progress. Exiting will interrupt the current conversation. Are you sure you want to continue?',
+ loading_history: 'Loading conversation history...',
+ guard: {
+ title: 'Confirm Leave',
+ message: 'A conversation is currently in progress. Leaving the page will interrupt the conversation. Are you sure you want to leave?',
+ confirm: 'Leave',
+ cancel: 'Cancel',
+ },
+ },
settings: {
model: 'Model',
setting: 'Settings',
diff --git a/src/i18n/lang/zh-cn.ts b/src/i18n/lang/zh-cn.ts
index 75b8e30a8f0f2698149c38923313fc2436119495..fcdaa7b9fc811452e4dba334935647910b3eba7e 100644
--- a/src/i18n/lang/zh-cn.ts
+++ b/src/i18n/lang/zh-cn.ts
@@ -19,6 +19,17 @@ export default {
setting: '设置',
reply: '回复',
},
+ conversation: {
+ interrupt_title: '中断对话',
+ interrupt_warning: '当前对话正在进行中,退出将会中断当前对话。确定要继续吗?',
+ loading_history: '正在加载历史对话...',
+ guard: {
+ title: '确认离开',
+ message: '当前有对话正在进行中,离开页面将中断对话。确定要离开吗?',
+ confirm: '确定离开',
+ cancel: '取消',
+ },
+ },
settings: {
model: '模型',
setting: '设置',
diff --git a/src/router/index.ts b/src/router/index.ts
index 1fa0a405354ae3d9e758ec0db27798d40963ffe1..4a6fef1006e0b6dd39648bc8ccc8d902ca502735 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -81,4 +81,7 @@ const router = createRouter({
routes,
});
+// 移除路由拦截逻辑,改为在页面销毁时自动停止对话生成
+// router.beforeEach 已被移除,现在依赖组件的 onBeforeUnmount 生命周期
+
export default router;
diff --git a/src/store/conversation.ts b/src/store/conversation.ts
index a28a27e618329ec8082624b9150a315c2335b818..7cb102722fc068fad03cde252df05ede1f3588da 100644
--- a/src/store/conversation.ts
+++ b/src/store/conversation.ts
@@ -8,8 +8,9 @@
// PURPOSE.
// See the Mulan PSL v2 for more details.
import { defineStore } from 'pinia';
-import { ref } from 'vue';
+import { ref, nextTick } from 'vue';
import { fetchEventSource } from '@microsoft/fetch-event-source';
+// conversationGuard 已移除,不再使用路由拦截逻辑
import { useHistorySessionStore, useLangStore } from 'src/store';
import {
@@ -65,6 +66,9 @@ export const useSessionStore = defineStore('conversation', () => {
const appList = ref
();
// ai回复是否还在生成中
const isAnswerGenerating = ref(false);
+
+ // 历史对话加载状态
+ const isLoadingHistory = ref(false);
const currentTaskId = ref(null);
@@ -205,6 +209,7 @@ export const useSessionStore = defineStore('conversation', () => {
}
conversationItem.isFinish = true;
isAnswerGenerating.value = false;
+ // 对话拦截状态管理已移除
// 如果是工作流的调试功能-调试对话结束时-发送调试对话结束
if (isFlowDebug) {
$bus.emit('debugChatEnd');
@@ -297,8 +302,10 @@ export const useSessionStore = defineStore('conversation', () => {
msgData: Record,
conversationItem: RobotConversationItem,
) => {
- if (isPaused.value) {
- // 手动暂停输出
+ // 🔑 关键修复:检查暂停状态和生成状态,双重保险
+ if (isPaused.value || !isAnswerGenerating.value) {
+ // 手动暂停输出或已停止生成
+ console.log('🛑 [handleMsgDataShow] 检测到暂停状态或已停止生成,忽略消息处理');
isAnswerGenerating.value = false;
return;
}
@@ -578,7 +585,7 @@ export const useSessionStore = defineStore('conversation', () => {
await funcFetch.fetchHistory(streamUrl, params, pp, fetchParams);
} else if (params.user_selected_app) {
// 新的工作流调试记录
- await funcFetch.fetchAppNew(streamUrl, params, pp, fetchParams);
+ await funcFetch.fetchAppNew(streamUrl, params, pp, fetchParams, false);
} else if (waitType) {
if (waitType === 'params') {
await funcFetch.fetchWait(streamUrl, params, pp, fetchParams);
@@ -649,6 +656,16 @@ export const useSessionStore = defineStore('conversation', () => {
waitType?: string,
isDebug?: boolean,
): Promise => {
+ // 如果当前有对话正在生成,先停止它
+ if (isAnswerGenerating.value) {
+ forceStopGeneration();
+ // 等待一小段时间确保停止操作完成
+ await new Promise(resolve => setTimeout(resolve, 100));
+ }
+
+ // 重置暂停状态,确保新对话可以正常发送
+ isPaused.value = false;
+
const { updateSessionTitle, currentSelectedSession } =
useHistorySessionStore();
if (conversationList.value.length === 0) {
@@ -698,6 +715,7 @@ export const useSessionStore = defineStore('conversation', () => {
);
}
isAnswerGenerating.value = true;
+ // 对话拦截状态管理已移除
scrollToBottom(true);
let getStreamParams: Parameters[0] = {
@@ -745,6 +763,7 @@ export const useSessionStore = defineStore('conversation', () => {
const resp = await api.stopGeneration(currentTaskId.value || '');
if (resp?.[1]?.code === 200) {
isAnswerGenerating.value = false;
+ // 对话拦截状态管理已移除
}
};
@@ -830,12 +849,19 @@ export const useSessionStore = defineStore('conversation', () => {
/**
* 获取历史对话数据
* @param conversationId
+ * @param manageLoadingState 是否由此方法管理loading状态,默认为true
*/
- const getConversation = async (conversationId: string): Promise => {
- const [err, res] = await api.getHistoryConversation(conversationId);
- if (!err && res) {
- conversationList.value = [];
- const cList = conversationList.value as RobotConversationItem[];
+ const getConversation = async (conversationId: string, manageLoadingState: boolean = true): Promise => {
+ // 如果需要管理loading状态,则设置为true
+ if (manageLoadingState) {
+ isLoadingHistory.value = true;
+ }
+
+ try {
+ const [err, res] = await api.getHistoryConversation(conversationId);
+ if (!err && res) {
+ conversationList.value = [];
+ const cList = conversationList.value as RobotConversationItem[];
res.result.records.forEach((record) => {
const targetRecord = cList.find((i) => i.groupId === record.groupId);
if (targetRecord) {
@@ -879,7 +905,7 @@ export const useSessionStore = defineStore('conversation', () => {
conversationId: record.conversationId,
groupId: record.groupId,
metadata: record.metadata,
- document: record.document,
+ files: record.document,
flowdata: record?.flow
? (generateFlowData(record.flow) as FlowType)
: undefined,
@@ -887,6 +913,16 @@ export const useSessionStore = defineStore('conversation', () => {
);
scrollToBottom();
});
+
+ // 🔑 重要:确保DOM更新后再滚动到底部
+ await nextTick();
+ scrollToBottom(true);
+ }
+ } catch (error) {
+ console.error('获取历史对话失败:', error);
+ } finally {
+ // 无论成功或失败都重置加载状态
+ isLoadingHistory.value = false;
}
};
@@ -937,6 +973,7 @@ export const useSessionStore = defineStore('conversation', () => {
const resp = await api.stopGeneration(currentTaskId.value || '');
if (resp?.[1]?.code === 200) {
isAnswerGenerating.value = false;
+ // 对话拦截状态管理已移除
}
};
@@ -944,10 +981,80 @@ export const useSessionStore = defineStore('conversation', () => {
controller.abort();
};
+ // 强制停止对话生成 - 用于页面销毁时清理
+ const forceStopGeneration = (): void => {
+ console.log('🛑 [forceStopGeneration] 开始强制停止对话生成,当前状态:', isAnswerGenerating.value);
+
+ if (isAnswerGenerating.value) {
+ // 🔑 立即设置状态,阻止新的流式数据处理
+ isPaused.value = true;
+ isAnswerGenerating.value = false;
+
+ // 立即取消当前请求
+ cancel();
+
+ // 🔑 关键修复:确保最后一个对话项标记为完成,允许后续发送
+ if (conversationList.value.length > 0) {
+ const lastItem = conversationList.value[conversationList.value.length - 1] as RobotConversationItem;
+ if (lastItem && lastItem.belong === 'robot' && !lastItem.isFinish) {
+ lastItem.isFinish = true;
+ // 添加中断标记
+ if (lastItem.message && Array.isArray(lastItem.message)) {
+ lastItem.message[lastItem.currentInd || 0] += ' [已中断]';
+ }
+ console.log('✅ [forceStopGeneration] 已标记最后一个对话项为完成状态');
+ }
+ }
+
+ // 🔑 新增:调用后端停止接口,使用Promise但不等待响应
+ if (currentTaskId.value) {
+ console.log('🔄 [forceStopGeneration] 调用后端停止接口,taskId:', currentTaskId.value);
+
+ // 使用api.stopGeneration但不等待响应
+ api.stopGeneration(currentTaskId.value).then(([error, response]) => {
+ if (!error && response?.code === 200) {
+ console.log('✅ [forceStopGeneration] 后端停止成功');
+ } else {
+ console.warn('⚠️ [forceStopGeneration] 后端停止失败:', error);
+ }
+ }).catch((err) => {
+ console.warn('⚠️ [forceStopGeneration] 后端停止接口调用异常:', err);
+ });
+
+ // 降级方案:使用fetch with keepalive作为备用
+ const stopData = JSON.stringify({ taskId: currentTaskId.value });
+
+ if (navigator.sendBeacon) {
+ // 使用sendBeacon确保在页面卸载时也能发送请求
+ const blob = new Blob([stopData], { type: 'application/json' });
+ navigator.sendBeacon('/api/stop-generation', blob);
+ } else {
+ // 降级方案:使用fetch with keepalive
+ fetch('/api/stop-generation', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: stopData,
+ keepalive: true
+ }).catch(() => {
+ // 静默处理错误
+ });
+ }
+ }
+
+ // 清理当前任务ID
+ currentTaskId.value = null;
+
+ console.log('✅ [forceStopGeneration] 强制停止完成,最终状态:', isAnswerGenerating.value);
+ } else {
+ console.log('ℹ️ [forceStopGeneration] 当前没有对话在生成,无需停止');
+ }
+ };
+
return {
isPaused,
conversationList,
isAnswerGenerating,
+ isLoadingHistory,
dialogueRef,
app,
appList,
@@ -960,5 +1067,6 @@ export const useSessionStore = defineStore('conversation', () => {
reGenerateAnswer,
getConversation,
cancel,
+ forceStopGeneration,
};
});
\ No newline at end of file
diff --git a/src/store/historySession.ts b/src/store/historySession.ts
index 9068c8fb9f0f1670aa79e83882c98e889fe10e24..7af0927b22adaa521c91d02d9e9391cdf6218f4d 100644
--- a/src/store/historySession.ts
+++ b/src/store/historySession.ts
@@ -45,15 +45,47 @@ export const useHistorySessionStore = defineStore(
/**
* 选择历史会话
* @param conversationId 会话id
+ * @param setLoadingState 是否设置loading状态,默认为true
*/
- const changeSession = async (conversationId: string): Promise => {
- const { isAnswerGenerating } = useSessionStore();
- if (
- currentSelectedSession.value === conversationId ||
- isAnswerGenerating
- ) {
+ const changeSession = async (conversationId: string, setLoadingState: boolean = true): Promise => {
+ const sessionStore = useSessionStore();
+ const { isAnswerGenerating, isLoadingHistory, conversationList } = storeToRefs(sessionStore);
+ const { forceStopGeneration } = sessionStore;
+
+ // 如果是相同会话,直接返回
+ if (currentSelectedSession.value === conversationId) {
return;
}
+
+ // 🔑 重要:根据参数决定是否设置loading状态
+ if (setLoadingState) {
+ isLoadingHistory.value = true;
+ }
+
+ try {
+ // 🔑 关键修复:如果当前有对话正在生成,强制停止它
+ if (isAnswerGenerating.value) {
+ console.log('🛑 [changeSession] 检测到对话正在生成,强制停止...');
+
+ // 立即设置暂停状态,阻止新的流式数据处理
+ forceStopGeneration();
+
+ // 🔑 重要:等待足够时间让前端和后端完成清理操作
+ await new Promise(resolve => setTimeout(resolve, 300));
+
+ // 🔑 双重保险:再次检查并确保生成状态已停止
+ if (isAnswerGenerating.value) {
+ console.warn('⚠️ [changeSession] 第一次停止未完成,再次尝试停止...');
+ forceStopGeneration();
+ await new Promise(resolve => setTimeout(resolve, 200));
+ }
+
+ console.log('✅ [changeSession] 对话生成已停止,当前状态:', isAnswerGenerating.value);
+ }
+
+ // 🔑 新增:清空当前对话列表,避免显示上一个会话的内容
+ conversationList.value = [];
+
currentSelectedSession.value = conversationId;
// 🔑 关键修复:根据当前对话的appId更新app选择状态
@@ -105,12 +137,20 @@ export const useHistorySessionStore = defineStore(
});
const { getConversation } = useSessionStore();
- await getConversation(currentSelectedSession.value).then(() => {
- const a = document.getElementsByClassName('draw');
- for (const i of a) {
- (i as HTMLElement).style.display = 'none';
+ await getConversation(currentSelectedSession.value, false).then(() => {
+ const a = document.getElementsByClassName('draw');
+ for (const i of a) {
+ (i as HTMLElement).style.display = 'none';
+ }
+ });
+ } catch (error) {
+ console.error('切换会话失败:', error);
+ } finally {
+ // 确保loading状态被重置(只有当我们设置了loading状态时才重置)
+ if (setLoadingState) {
+ isLoadingHistory.value = false;
}
- });
+ }
};
// #region -----------------------------------------< select conversation >------------------------------------------------
// 被选中的会话id列表
@@ -180,64 +220,93 @@ export const useHistorySessionStore = defineStore(
const initSessionList = (): void => {
selectedSessionIds.value = [];
};
+ // 🔑 新增:防重复调用标志
+ let isLoadingSessionRecord = false;
+
/**
* 获取历史会话列表
* @returns
*/
const getHistorySession = async (): Promise => {
- const conversationId = localStorage.getItem('conversationId');
- const [err, res] = await api.getSessionRecord();
- const { conversationList } = storeToRefs(useSessionStore());
- if (!err && res) {
- // 🔑 关键修复:根据当前选择的app过滤对话记录
- const currentAppId = user_selected_app.value || '';
- console.log('🔍 [getHistorySession] 当前选择的app:', currentAppId);
-
- const allConversations = res.result.conversations
- .reverse()
- .map((item) => ({
- conversationId: item.conversationId,
- createdTime: item.createdTime,
- title: item.title,
- docCount: item.docCount || 0,
- appId: item.appId || '', // 🔑 关键修复:添加appId字段
- debug: item.debug || false, // 🔑 添加debug字段
- kbList: item.kbList || [], // 🔑 添加kbList字段
- llm: item.llm || {},
- }))
- .filter((item) => item.conversationId !== conversationId);
-
- // 🔑 新增:按app分离对话记录
- historySession.value = allConversations.filter((item) => {
- const itemAppId = item.appId || '';
- const matches = itemAppId === currentAppId;
- return matches;
- });
+ // 🔑 防重复调用:如果正在加载中,直接返回
+ if (isLoadingSessionRecord) {
+ console.log('🔄 [getHistorySession] 正在加载中,跳过重复调用');
+ return;
+ }
+
+ isLoadingSessionRecord = true;
+
+ try {
+ const conversationId = localStorage.getItem('conversationId');
+ const [err, res] = await api.getSessionRecord();
+ const { conversationList } = storeToRefs(useSessionStore());
- // 🔑 修复:当当前app没有对话时,自动创建新对话
- if (historySession.value.length === 0) {
- console.log('🔍 [getHistorySession] 当前app无对话记录,自动创建新对话,appId:', currentAppId);
- await generateSession(false);
- // 🔑 关键修复:创建新对话后需要重新加载历史记录
- await getHistorySession();
- return;
- }
- if (!currentSelectedSession.value) {
- currentSelectedSession.value =
- res.result.conversations[0]?.conversationId;
- }
- if (currentSelectedSession.value) {
- const { getConversation, isAnswerGenerating } = useSessionStore();
- if (isAnswerGenerating) {
+ if (!err && res) {
+ // 🔑 关键修复:根据当前选择的app过滤对话记录
+ const currentAppId = user_selected_app.value || '';
+ console.log('🔍 [getHistorySession] 当前选择的app:', currentAppId);
+
+ const allConversations = res.result.conversations
+ .reverse()
+ .map((item: any) => ({
+ conversationId: item.conversationId,
+ createdTime: item.createdTime,
+ title: item.title,
+ docCount: item.docCount || 0,
+ appId: item.appId || '', // 🔑 关键修复:添加appId字段
+ debug: item.debug || false, // 🔑 添加debug字段
+ kbList: item.kbList || [], // 🔑 添加kbList字段
+ llm: item.llm || { icon: '', modelName: '', llmId: '' },
+ }))
+ .filter((item) => item.conversationId !== conversationId);
+
+ // 🔑 新增:按app分离对话记录
+ historySession.value = allConversations.filter((item) => {
+ const itemAppId = item.appId || '';
+ const matches = itemAppId === currentAppId;
+ return matches;
+ });
+
+ // 🔑 修复:当当前app没有对话时,自动创建新对话(但避免递归)
+ if (historySession.value.length === 0) {
+ console.log('🔍 [getHistorySession] 当前app无对话记录,自动创建新对话,appId:', currentAppId);
+ // 🔑 重要修复:先释放锁,再创建新对话,避免死锁
+ isLoadingSessionRecord = false;
+ await generateSession(false);
+ return; // 🔑 generateSession内部会调用getHistorySession,所以这里直接返回
+ }
+
+ if (!currentSelectedSession.value) {
+ currentSelectedSession.value =
+ res.result.conversations[0]?.conversationId;
+ }
+
+ if (currentSelectedSession.value) {
+ const sessionStore = useSessionStore();
+ const { isAnswerGenerating } = storeToRefs(sessionStore);
+ const { getConversation, forceStopGeneration } = sessionStore;
+
+ // 如果当前有对话正在生成,先停止它
+ if (isAnswerGenerating.value) {
+ forceStopGeneration();
+ // 等待一小段时间确保停止操作完成
+ await new Promise(resolve => setTimeout(resolve, 100));
+ }
+
+ await getConversation(currentSelectedSession.value);
return;
}
- await getConversation(currentSelectedSession.value);
- return;
- }
- await createNewSession();
- if (res.result.conversations.length === 0) {
- conversationList.value = [];
+
+ await createNewSession();
+ if (res.result.conversations.length === 0) {
+ conversationList.value = [];
+ }
}
+ } catch (error) {
+ console.error('🔥 [getHistorySession] 获取历史会话失败:', error);
+ } finally {
+ // 🔑 确保无论成功失败都释放锁
+ isLoadingSessionRecord = false;
}
};
/**
@@ -305,6 +374,7 @@ export const useHistorySessionStore = defineStore(
query: currentQuery
});
+ // 🔑 优化:创建新会话后刷新历史记录,但要确保防重复机制生效
await getHistorySession();
}
};
diff --git a/src/views/chat/Sender.vue b/src/views/chat/Sender.vue
index 2735e4345036946b9f1091f57f91c498b2320183..547c35e162cd418503167d005a60397c259a4907 100644
--- a/src/views/chat/Sender.vue
+++ b/src/views/chat/Sender.vue
@@ -1,6 +1,8 @@
@@ -315,10 +333,27 @@ onBeforeUnmount(() => {
+
+
+
+
+ {{ $t('conversation.loading_history') }}
+
+
+
-
+
{
margin-top: 25px;
}
}
+
+/* 历史对话加载状态样式 */
+.loading-container {
+ width: 100%;
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 24px;
+ padding: 40px 20px;
+}
+
+.loading-skeleton {
+ width: 100%;
+ max-width: 800px;
+ display: flex;
+ flex-direction: column;
+ gap: 24px;
+}
+
+.skeleton-item {
+ display: flex;
+ gap: 24px; /* 匹配Bubble组件的margin-right: 24px */
+ align-items: flex-start;
+}
+
+.skeleton-avatar {
+ width: 48px; /* 匹配Bubble组件的头像尺寸 */
+ height: 48px;
+ border-radius: 50%;
+ background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
+ background-size: 200% 100%;
+ animation: skeleton-loading 1.5s infinite;
+ flex-shrink: 0;
+}
+
+.skeleton-content {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.skeleton-line {
+ height: 16px;
+ border-radius: 4px;
+ background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
+ background-size: 200% 100%;
+ animation: skeleton-loading 1.5s infinite;
+}
+
+.skeleton-line-1 {
+ width: 80%;
+}
+
+.skeleton-line-2 {
+ width: 60%;
+}
+
+.skeleton-line-3 {
+ width: 40%;
+}
+
+.loading-text {
+ font-size: 14px;
+ color: var(--o-text-color-secondary);
+ display: flex;
+ align-items: center;
+ width: 100%;
+ max-width: 800px;
+ position: relative;
+}
+
+.loading-text::before {
+ content: '';
+ width: 16px;
+ height: 16px;
+ border: 2px solid var(--o-color-primary);
+ border-top-color: transparent;
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+ position: absolute;
+ left: 16px; /* 图标位置:在skeleton头像中心位置 (48px/2 - 16px/2 = 16px) */
+}
+
+.loading-text span {
+ margin-left: 72px; /* 文字位置:skeleton内容开始位置 (48px头像 + 24px间距) */
+}
+
+@keyframes skeleton-loading {
+ 0% {
+ background-position: -200% 0;
+ }
+ 100% {
+ background-position: 200% 0;
+ }
+}
+
+@keyframes spin {
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+/* 暗色主题适配 */
+[theme="dark"] .skeleton-avatar,
+[theme="dark"] .skeleton-line {
+ background: linear-gradient(90deg, #2a2a2a 25%, #3a3a3a 50%, #2a2a2a 75%);
+ background-size: 200% 100%;
+}
diff --git a/src/views/createapp/components/McpDrawer.vue b/src/views/createapp/components/McpDrawer.vue
index 4420802ac17799f425a4d4f0c4b26cfcd47287d4..b1aaa92c2bb59be35c4e805cecebc2c449458e33 100644
--- a/src/views/createapp/components/McpDrawer.vue
+++ b/src/views/createapp/components/McpDrawer.vue
@@ -284,7 +284,7 @@ async function onActiveService(
display: flex;
flex-direction: row-reverse;
.el-icon:not(:last-child) {
- border-left: 1px solid var(--el-border-color);
+ border-left: 1px solid var(--o-border-color);
padding-left: 8px;
}
}
diff --git a/src/views/createapp/components/workFlow.vue b/src/views/createapp/components/workFlow.vue
index 5c4c9f18b852cc363e00cc4c58c881b608e1b3e9..b9edae3780f2eebe3e7da8dc6e23b0cde5d2625b 100644
--- a/src/views/createapp/components/workFlow.vue
+++ b/src/views/createapp/components/workFlow.vue
@@ -217,17 +217,8 @@ const yamlExportFileName = ref('workflow.yaml');
// 注释相关状态(已简化,注释现在直接在CommentNode中编辑)
-// 额外的内置节点类型
-const extraNodeTypes = ref([
- {
- nodeId: 'FileExtract',
- callId: 'FileExtract',
- name: i18n.global.t('flow.node_names.file_extractor'),
- description: '从文件中提取文本内容,支持多种文件格式',
- type: 'transform',
- serviceId: 'system'
- }
-]);
+// 额外的内置节点类型 - 现在完全依赖后端数据,不再有静态定义
+const extraNodeTypes = ref([]);
const hanleAsideVisible = () => {
if (!copilotAside.value) return;
@@ -1375,16 +1366,14 @@ const nodesChange = (nodes) => {
removeSelectedNodes([getSelectedNodes.value[0]]);
}
if (nodes?.[0]?.type === 'remove') {
- // TODO 为什么0.10.0删除了delNode?
- delNode(nodes[0].id);
- // 节点增加删除时直接将工作流debug状态置为false
+ // 节点删除时直接将工作流debug状态置为false
emits('updateFlowsDebug', false);
nextTick(() => {
debouncedNodeAndLineConnection();
});
}
if (nodes?.[0]?.type === 'add') {
- // 节点增加删除时直接将工作流debug状态置为false
+ // 节点增加时直接将工作流debug状态置为false
emits('updateFlowsDebug', false);
nextTick(() => {
debouncedNodeAndLineConnection();
@@ -2887,11 +2876,6 @@ const saveFlow = async (updateNodeParameter?, debug?) => {
queryFlow('update');
const updatedCurFlow = response[1].result.flow;
isNodeConnect.value = updatedCurFlow.connectivity;
- if (!isNodeConnect.value) {
- ElMessage.error(i18n.global.t('semantic.check_connect'));
- }
-
-
redrageFlow(updatedCurFlow?.nodes, updatedCurFlow?.edges, updatedCurFlow?.notes || []);
}
@@ -3639,9 +3623,10 @@ defineExpose({
.aside-content {
flex: 1;
- overflow: hidden;
+ overflow: visible;
display: flex;
flex-direction: column;
+ min-height: 0;
}
}
diff --git a/src/views/createapp/components/workFlowConfig/ChoiceBranchDrawer.vue b/src/views/createapp/components/workFlowConfig/ChoiceBranchDrawer.vue
index 550d3bac7d461e3e7e158ab5e6c7d5b46650f17d..4c4192e2bbc016e23a8ea10127efcad5e01234e4 100644
--- a/src/views/createapp/components/workFlowConfig/ChoiceBranchDrawer.vue
+++ b/src/views/createapp/components/workFlowConfig/ChoiceBranchDrawer.vue
@@ -736,7 +736,7 @@ const formatValue = (value, type) => {
body[theme='dark'] & {
background-color: #1f2329;
color: #e4e8ee;
- border: 1px solid var(--el-border-color);
+ border: 1px solid var(--o-border-color);
}
svg {
diff --git a/src/views/createapp/components/workFlowConfig/NodeListPanel.vue b/src/views/createapp/components/workFlowConfig/NodeListPanel.vue
index 50d82f0f4e37e5ae16cef2da29a8fcd6dd08a1aa..94b09bcb4c2e0b5cd640e996919abc3944f3bff4 100644
--- a/src/views/createapp/components/workFlowConfig/NodeListPanel.vue
+++ b/src/views/createapp/components/workFlowConfig/NodeListPanel.vue
@@ -95,16 +95,15 @@ const searchKeyword = ref('');
// 计算属性:获取service类型的节点数据
const serviceNodes = computed(() => {
const nodes: any[] = [];
+ // 现在 apiServiceList 已经在父组件中过滤为只包含 system 类型的服务
props.apiServiceList.forEach(service => {
- if (service.type === 'system' || !service.type) { // 兼容没有type字段的情况
- if (service.nodeMetaDatas && Array.isArray(service.nodeMetaDatas)) {
- // 为每个节点添加serviceId信息
- const nodesWithServiceId = service.nodeMetaDatas.map(node => ({
- ...node,
- serviceId: service.serviceId
- }));
- nodes.push(...nodesWithServiceId);
- }
+ if (service.nodeMetaDatas && Array.isArray(service.nodeMetaDatas)) {
+ // 为每个节点添加serviceId信息
+ const nodesWithServiceId = service.nodeMetaDatas.map(node => ({
+ ...node,
+ serviceId: service.serviceId
+ }));
+ nodes.push(...nodesWithServiceId);
}
});
return nodes;
@@ -275,7 +274,8 @@ defineExpose({
.nodes-list {
flex: 1;
- overflow: visible;
+ overflow-y: auto;
+ overflow-x: hidden;
min-height: 0;
.node-group {
diff --git a/src/views/createapp/components/workFlowConfig/NodeSelector.vue b/src/views/createapp/components/workFlowConfig/NodeSelector.vue
index a1922da9cf85783452a654f10975a461944b4ec3..73a82e54bfba6b24f2d0feb2becaf9eff2f1f8e5 100644
--- a/src/views/createapp/components/workFlowConfig/NodeSelector.vue
+++ b/src/views/createapp/components/workFlowConfig/NodeSelector.vue
@@ -22,7 +22,7 @@
@@ -1250,8 +1337,26 @@ const getFormatFileList = (ConversationItem, str) => {
+
+
+
+
+ {{ $t('conversation.loading_history') }}
+
+
+
{
:test="getItem(item, 'test')"
:metadata="getItem(item, 'metadata')"
:flowdata="getItem(item, 'flowdata')"
- :fileList="getItem(item, 'files')"
+ :fileList="getItem(item, 'files') || getFormatFileList(item, 'document')"
:created-at="item.createdAt"
:current-selected="item.currentInd"
:need-regernerate="item.cid === conversationList.slice(-1)[0].cid"
@@ -1286,8 +1391,9 @@ const getFormatFileList = (ConversationItem, str) => {
@openShowFileSource="openShowFileSource"
/>
-
+
+
{
maxlength="2000"
:placeholder="$t('main.ask_me_anything')"
@keydown="handleKeydown"
+ @compositionstart="handleCompositionStart"
+ @compositionend="handleCompositionEnd"
/>
@@ -1523,7 +1631,7 @@ const getFormatFileList = (ConversationItem, str) => {