首页:删除气泡重新生成功能
- 移除失败气泡与 AI/用户消息操作栏的重新生成入口 - 清理 retry/regenerate 事件链、handleRetry 及 retryQuestion 字段 - 保留失败/空回复提示文本、复制与删除操作 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -53,10 +53,6 @@
|
||||
v-html="renderAnswer(msg.content)"
|
||||
@click="handleAnswerClick"
|
||||
></div>
|
||||
<!-- 失败重试(工作流结果不提供文本重试,需重新提交表单卡片) -->
|
||||
<button v-if="isErrorMsg(msg) && msg.recordType !== 'workflow'" type="button" class="retry-btn" @click="emit('retry', msg)">
|
||||
<span class="retry-icon">↻</span> 重新生成
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
<!-- 工作流执行产出文件卡片(挂在结果消息上,预览/下载/删除) -->
|
||||
@@ -70,9 +66,6 @@
|
||||
/>
|
||||
<!-- AI 消息操作栏(hover 显示) -->
|
||||
<div v-if="!msg.isUser && msg.content && !isErrorMsg(msg)" class="msg-actions">
|
||||
<button v-if="msg.recordType !== 'workflow'" class="msg-action" title="重新生成" @click="emit('regenerate', msg)">
|
||||
<el-icon><RefreshRight /></el-icon>
|
||||
</button>
|
||||
<button class="msg-action" title="复制回答" @click="copyText(msg.content)">
|
||||
<el-icon><DocumentCopy /></el-icon>
|
||||
</button>
|
||||
@@ -82,9 +75,6 @@
|
||||
</div>
|
||||
<!-- 用户消息操作栏(hover 显示:重新生成 + 删除) -->
|
||||
<div v-if="msg.isUser" class="msg-actions">
|
||||
<button class="msg-action" title="重新生成" @click="emit('regenerate', msg)">
|
||||
<el-icon><RefreshRight /></el-icon>
|
||||
</button>
|
||||
<button v-if="msg.recordId" class="msg-action is-danger" title="删除对话" @click="emit('delete', msg)">
|
||||
<el-icon><Delete /></el-icon>
|
||||
</button>
|
||||
@@ -99,7 +89,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, onMounted, reactive, ref, watch } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { Delete, DocumentCopy, RefreshRight } from '@element-plus/icons-vue';
|
||||
import { Delete, DocumentCopy } from '@element-plus/icons-vue';
|
||||
import 'highlight.js/styles/github-dark.css';
|
||||
import { renderMarkdown } from '../utils/markdown';
|
||||
import type { WorkflowOutput } from '../utils/flowDsl';
|
||||
@@ -115,7 +105,6 @@ interface ChatMessage {
|
||||
loading?: boolean;
|
||||
thinking?: string;
|
||||
thinkingSeconds?: number;
|
||||
retryQuestion?: string;
|
||||
// 后端记录 id / 类型(来自 session/get 的每条结果),存在时才显示删除按钮
|
||||
recordId?: string;
|
||||
recordType?: string;
|
||||
@@ -131,8 +120,6 @@ interface ChatMessage {
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'retry', msg: ChatMessage): void;
|
||||
(e: 'regenerate', msg: ChatMessage): void;
|
||||
(e: 'delete', msg: ChatMessage): void;
|
||||
(e: 'form-submit', msg: ChatMessage, payload: any): void;
|
||||
(e: 'form-retry', msg: ChatMessage, payload: any): void;
|
||||
@@ -481,33 +468,6 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
|
||||
/* 失败重试 */
|
||||
.retry-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
margin-top: 8px;
|
||||
padding: 7px 14px;
|
||||
border: 1px solid #fecaca;
|
||||
border-radius: 10px;
|
||||
background: #fef2f2;
|
||||
color: #dc2626;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
|
||||
&:hover {
|
||||
background: #fee2e2;
|
||||
border-color: #fca5a5;
|
||||
}
|
||||
|
||||
.retry-icon {
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* 思考区:DeepSeek 风格折叠块 */
|
||||
.thinking-block {
|
||||
margin: 0 0 12px;
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
:loading-more="loadingMore"
|
||||
:execute-request="executeRequest"
|
||||
class="chat-list-scroll"
|
||||
@retry="emit('retry', $event)"
|
||||
@regenerate="emit('regenerate', $event)"
|
||||
@delete="emit('delete', $event)"
|
||||
@form-submit="(msg: any, payload: any) => emit('form-submit', msg, payload)"
|
||||
@form-retry="(msg: any, payload: any) => emit('form-retry', msg, payload)"
|
||||
@@ -70,8 +68,6 @@ interface Props {
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'retry', msg: any): void;
|
||||
(e: 'regenerate', msg: any): void;
|
||||
(e: 'workflow-select', id: string, isTemplate?: boolean): void;
|
||||
(e: 'delete', msg: any): void;
|
||||
(e: 'form-submit', msg: any, payload: any): void;
|
||||
|
||||
@@ -29,8 +29,6 @@
|
||||
:has-more="currentSessionHasMore"
|
||||
:loading-more="currentSessionLoadingMore"
|
||||
:execute-request="executeRequest"
|
||||
@retry="handleRetry"
|
||||
@regenerate="handleRetry"
|
||||
@delete="handleDeleteMessage"
|
||||
@form-submit="handleFormSubmit"
|
||||
@form-retry="handleFormRetry"
|
||||
@@ -129,7 +127,6 @@ interface ChatMessage {
|
||||
loading?: boolean;
|
||||
thinking?: string;
|
||||
thinkingSeconds?: number;
|
||||
retryQuestion?: string;
|
||||
// 后端记录 id / 类型(来自 session/get 的每条结果),用于删除对话记录
|
||||
recordId?: string;
|
||||
recordType?: string;
|
||||
@@ -855,56 +852,6 @@ const handleSend = async (message: string) => {
|
||||
await runChat(sid, sessionId, message);
|
||||
};
|
||||
|
||||
// ===== 失败重试:重新发送同一条用户消息 =====
|
||||
const handleRetry = async (msg: ChatMessage) => {
|
||||
// 工作流结果消息不提供文本重试(重试需重新填写表单卡片提交)
|
||||
if (msg.recordType === 'workflow') return;
|
||||
let sid = '';
|
||||
for (const [k, list] of sessionMessages.value) {
|
||||
if (list.some((m) => m.id === msg.id)) {
|
||||
sid = k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!sid || sendingSessions[sid]) return;
|
||||
const session = historyList.value.find((h) => h.id === sid);
|
||||
if (!session) return;
|
||||
const list = sessionMessages.value.get(sid) || [];
|
||||
const idx = list.findIndex((m) => m.id === msg.id);
|
||||
if (idx < 0) return;
|
||||
// 待重发的用户问题:用户消息直接用自身内容;失败消息优先记录的原文,其次向上找最近一条用户消息
|
||||
let question = msg.isUser ? msg.content : (msg.retryQuestion || '');
|
||||
if (!question) {
|
||||
for (let i = idx - 1; i >= 0; i--) {
|
||||
if (list[i].isUser) {
|
||||
question = list[i].content;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!question) return;
|
||||
// 移除待重生成的消息:用户消息连同紧随其后的 AI 回复一并移除(避免旧回复残留错位);AI 回复只移除自身
|
||||
if (msg.isUser && idx + 1 < list.length && !list[idx + 1].isUser && list[idx + 1].type !== 'form') {
|
||||
list.splice(idx, 2);
|
||||
} else {
|
||||
list.splice(idx, 1);
|
||||
}
|
||||
// 用户消息重新生成:runChat 只追加 AI 气泡、不会补用户消息,这里先把用户问题重新追加进消息流,避免用户气泡消失
|
||||
if (msg.isUser) {
|
||||
list.push({ id: 'msg-' + Date.now() + '-user', content: question, time: formatTime(new Date()), isUser: true });
|
||||
}
|
||||
sendingSessions[sid] = true;
|
||||
session.status = 'executing';
|
||||
const sessionId = resolveSessionIdForConnect(session);
|
||||
try {
|
||||
await runChat(sid, sessionId, question);
|
||||
} catch {
|
||||
session.status = 'failed';
|
||||
} finally {
|
||||
delete sendingSessions[sid];
|
||||
}
|
||||
};
|
||||
|
||||
// ===== 删除会话内对话记录:删除选中的 AI 回复 + 紧邻其上方的用户问题(同一 record 时去重)=====
|
||||
const handleDeleteMessage = async (msg: ChatMessage) => {
|
||||
const sid = activeHistoryId.value;
|
||||
@@ -1382,16 +1329,14 @@ const runChat = async (sid: string, sessionId: string, message: string) => {
|
||||
list[aiIdx].loading = false;
|
||||
list[aiIdx].content = content;
|
||||
list[aiIdx].thinking = '';
|
||||
list[aiIdx].retryQuestion = message;
|
||||
} else {
|
||||
pushToSession(sid, { id: 'msg-' + Date.now() + '-fail', content, time: formatTime(new Date()), isUser: false });
|
||||
}
|
||||
} else if (list && aiIdx >= 0) {
|
||||
// 成功兜底:后端未推任何回复内容时,给出明确提示并允许重新生成(避免空白气泡)
|
||||
// 成功兜底:后端未推任何回复内容时,给出明确提示(避免空白气泡)
|
||||
const ai = list[aiIdx];
|
||||
if (!ai.content) {
|
||||
ai.content = '⚠️ 未收到回复内容,请重试';
|
||||
ai.retryQuestion = message;
|
||||
}
|
||||
}
|
||||
// 更新会话状态
|
||||
|
||||
Reference in New Issue
Block a user