|
|
@@ -6,7 +6,7 @@
|
|
|
<!-- 中间历史记录区域 -->
|
|
|
<div class="history-sidebar">
|
|
|
<div class="new-chat-container">
|
|
|
- <button class="new-chat-btn-full" @click="createNewChat" :disabled="isSending">
|
|
|
+ <button class="new-chat-btn-full" @click="createNewChat" :disabled="isSending || hasTypingMessage">
|
|
|
<span class="plus-icon">+</span>
|
|
|
新建任务
|
|
|
</button>
|
|
|
@@ -29,9 +29,9 @@
|
|
|
v-for="(item, index) in historyData"
|
|
|
:key="index"
|
|
|
:class="['history-item', { active: item.isActive }]"
|
|
|
- @click="item.isActive ? null : (isSending ? null : handleHistoryItem(item))"
|
|
|
+ @click="item.isActive ? null : ((isSending || hasTypingMessage) ? null : handleHistoryItem(item))"
|
|
|
:style="{
|
|
|
- cursor: item.isActive ? 'default' : (isSending ? 'not-allowed' : 'pointer')
|
|
|
+ cursor: item.isActive ? 'default' : ((isSending || hasTypingMessage) ? 'not-allowed' : 'pointer')
|
|
|
}"
|
|
|
>
|
|
|
<div class="history-content">
|
|
|
@@ -327,7 +327,7 @@
|
|
|
<img :src="copyIcon" alt="复制" class="action-icon">
|
|
|
复制
|
|
|
</button>
|
|
|
- <button class="action-btn regenerate-btn" @click="regenerateResponse(index)" :disabled="hasTypingMessage">
|
|
|
+ <button class="action-btn regenerate-btn" @click="regenerateResponse(index)" :disabled="isSending || hasTypingMessage">
|
|
|
<img :src="regenerateIcon" alt="重新生成" class="action-icon">
|
|
|
重新生成
|
|
|
</button>
|
|
|
@@ -488,7 +488,7 @@
|
|
|
v-model="messageText"
|
|
|
@keyup.enter.exact="handleSendMessage"
|
|
|
@input="handleInput"
|
|
|
- :disabled="hasTypingMessage"
|
|
|
+ :disabled="isSending || hasTypingMessage"
|
|
|
maxlength="2000"
|
|
|
rows="3"
|
|
|
></textarea>
|
|
|
@@ -497,7 +497,7 @@
|
|
|
<div class="input-toolbar">
|
|
|
<div class="toolbar-left">
|
|
|
<!-- 语音输入 -->
|
|
|
- <button class="voice-btn" @click="handleVoiceClick" :disabled="hasTypingMessage" :class="{ 'recording': isListening }">
|
|
|
+ <button class="voice-btn" @click="handleVoiceClick" :disabled="isSending || hasTypingMessage" :class="{ 'recording': isListening }">
|
|
|
<div class="icon-container">
|
|
|
<img :src="voiceInputIcon" alt="语音" class="action-icon">
|
|
|
<div v-if="isListening" class="recording-indicator"></div>
|
|
|
@@ -544,7 +544,7 @@
|
|
|
class="model-link-btn"
|
|
|
:class="{ 'active': isOnlineModel }"
|
|
|
@click="toggleModelType"
|
|
|
- :disabled="hasTypingMessage"
|
|
|
+ :disabled="isSending || hasTypingMessage"
|
|
|
:title="isOnlineModel ? '当前:在线大模型 (glm-4-plus)' : '当前:本地模型'"
|
|
|
>
|
|
|
<div class="icon-container">
|
|
|
@@ -553,7 +553,7 @@
|
|
|
</button>
|
|
|
|
|
|
<!-- 附件图标 (右侧) -->
|
|
|
- <button class="attach-btn" @click="triggerFileUpload" :disabled="hasTypingMessage">
|
|
|
+ <button class="attach-btn" @click="triggerFileUpload" :disabled="isSending || hasTypingMessage">
|
|
|
<div class="icon-container">
|
|
|
<img :src="attachmentIcon" alt="附件" class="action-icon">
|
|
|
</div>
|
|
|
@@ -561,11 +561,11 @@
|
|
|
|
|
|
<button
|
|
|
class="send-btn"
|
|
|
- @click="isSending ? handleStopGeneration() : handleSendMessage()"
|
|
|
- :disabled="!isSending && (hasTypingMessage || !messageText.trim())"
|
|
|
+ @click="(isSending || hasTypingMessage) ? handleStopGeneration() : handleSendMessage()"
|
|
|
+ :disabled="!(isSending || hasTypingMessage) && !messageText.trim()"
|
|
|
>
|
|
|
- <span v-if="isSending" class="stop-text">停止</span>
|
|
|
- <div v-else class="send-icon-circle" :class="{ 'active': messageText.trim() && !hasTypingMessage }">
|
|
|
+ <span v-if="isSending || hasTypingMessage" class="stop-text">停止</span>
|
|
|
+ <div v-else class="send-icon-circle" :class="{ 'active': messageText.trim() }">
|
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
<path d="M12 19V5M12 5L5 12M12 5L19 12" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
|
</svg>
|
|
|
@@ -1716,6 +1716,12 @@ const handleNonStreamingSubmit = async (data) => {
|
|
|
|
|
|
if (response.statusCode === 200) {
|
|
|
const aiMessage = chatMessages.value[aiMessageIndex]
|
|
|
+
|
|
|
+ // 如果用户已经点击了停止,则不再继续输出
|
|
|
+ if (aiMessage._stopped) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
aiMessage.isTyping = false
|
|
|
aiMessage.content = response.data
|
|
|
|
|
|
@@ -1752,6 +1758,11 @@ const handleNonStreamingSubmit = async (data) => {
|
|
|
} catch (error) {
|
|
|
console.error('发送请求失败:', error)
|
|
|
const aiMessage = chatMessages.value[aiMessageIndex]
|
|
|
+
|
|
|
+ if (aiMessage._stopped) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
aiMessage.isTyping = false
|
|
|
aiMessage.displayContent = '抱歉,生成内容时发生错误,请重试。'
|
|
|
ElMessage.error(`请求失败: ${error.message}`)
|
|
|
@@ -2608,7 +2619,7 @@ const handleCancel = () => {
|
|
|
|
|
|
// 停止报告生成
|
|
|
const handleStopGeneration = async () => {
|
|
|
- if (!sseConnection || ai_conversation_id.value === undefined || ai_conversation_id.value === null) {
|
|
|
+ if (ai_conversation_id.value === undefined || ai_conversation_id.value === null) {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
@@ -2619,10 +2630,12 @@ const handleStopGeneration = async () => {
|
|
|
|
|
|
isSending.value = false
|
|
|
streamingReports.value.clear()
|
|
|
+ clearAllTypeIntervals()
|
|
|
|
|
|
chatMessages.value.forEach(message => {
|
|
|
if (message.type === 'ai' && message.isTyping) {
|
|
|
message.isTyping = false
|
|
|
+ message._stopped = true
|
|
|
|
|
|
// 停止时设置为完成状态
|
|
|
updateMessageStatus(message, 'completed')
|
|
|
@@ -2715,7 +2728,7 @@ const handleReportGeneratorSubmit = async (data) => {
|
|
|
})
|
|
|
|
|
|
try {
|
|
|
- const apiPrefix = getApiPrefix()
|
|
|
+ const apiPrefix = getReportApiPrefix() // 改为使用 getReportApiPrefix
|
|
|
const url = `${apiPrefix}/report/complete-flow`
|
|
|
|
|
|
// 构建 POST 请求体
|