feat(web): 添加发送按钮冷却机制,防止连续发送消息

This commit is contained in:
Wenjie Zhang 2026-03-24 21:42:37 +08:00
parent d9b9980877
commit 8e88830e2d
2 changed files with 35 additions and 3 deletions

View File

@ -150,7 +150,7 @@
v-model="userInput"
:is-loading="isProcessing"
:disabled="!currentAgent"
:send-button-disabled="(!userInput || !currentAgent) && !isProcessing"
:send-button-disabled="isSendButtonDisabled"
placeholder="输入问题..."
:mention="mentionConfig"
:supports-file-upload="supportsFileUpload"
@ -274,6 +274,8 @@ const { organization } = storeToRefs(infoStore)
// ==================== LOCAL CHAT & UI STATE ====================
const userInput = ref('')
const sendCooldownActive = ref(false)
let sendCooldownTimer = null
const sidebarLogo = computed(() => organization.value?.logo || organization.value?.avatar || '')
const useRunsApi =
import.meta.env.VITE_USE_RUNS_API === 'true' &&
@ -498,6 +500,20 @@ const isStreaming = computed(() => {
return threadState ? threadState.isStreaming : false
})
const isProcessing = computed(() => isStreaming.value)
const isSendButtonDisabled = computed(() => {
return sendCooldownActive.value || (((!userInput.value || !currentAgent.value) && !isProcessing.value))
})
const startSendCooldown = () => {
sendCooldownActive.value = true
if (sendCooldownTimer) {
clearTimeout(sendCooldownTimer)
}
sendCooldownTimer = setTimeout(() => {
sendCooldownActive.value = false
sendCooldownTimer = null
}, 2000)
}
// ==================== SCROLL & RESIZE HANDLING ====================
const scrollController = new ScrollController('.chat-main')
@ -546,6 +562,10 @@ onUnmounted(() => {
if (chatMainResizeObserver) {
chatMainResizeObserver.disconnect()
}
if (sendCooldownTimer) {
clearTimeout(sendCooldownTimer)
sendCooldownTimer = null
}
// 线
resetOnGoingConv()
})
@ -1010,7 +1030,11 @@ const togglePinChat = async (chatId) => {
const handleSendMessage = async ({ image } = {}) => {
const text = userInput.value.trim()
if ((!text && !image) || !currentAgent.value || isProcessing.value) return
if ((!text && !image) || !currentAgent.value || isProcessing.value || sendCooldownActive.value)
return
//
startSendCooldown()
let threadId = currentChatId.value
if (!threadId) {
@ -1109,6 +1133,10 @@ const handleSendMessage = async ({ image } = {}) => {
//
const handleSendOrStop = async (payload) => {
if (sendCooldownActive.value) {
return
}
const threadId = currentChatId.value
const threadState = getThreadState(threadId)
if (isProcessing.value && threadState) {

View File

@ -58,7 +58,7 @@ import ImagePreviewComponent from '@/components/ImagePreviewComponent.vue'
import AttachmentOptionsComponent from '@/components/AttachmentOptionsComponent.vue'
import { FolderCode } from 'lucide-vue-next'
defineProps({
const props = defineProps({
modelValue: { type: String, default: '' },
isLoading: { type: Boolean, default: false },
disabled: { type: Boolean, default: false },
@ -112,6 +112,10 @@ const handleSend = () => {
}
const handleKeyDown = (e) => {
if (props.sendButtonDisabled) {
return
}
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault()
handleSend()