feat(web): 添加发送按钮冷却机制,防止连续发送消息
This commit is contained in:
parent
d9b9980877
commit
8e88830e2d
@ -150,7 +150,7 @@
|
|||||||
v-model="userInput"
|
v-model="userInput"
|
||||||
:is-loading="isProcessing"
|
:is-loading="isProcessing"
|
||||||
:disabled="!currentAgent"
|
:disabled="!currentAgent"
|
||||||
:send-button-disabled="(!userInput || !currentAgent) && !isProcessing"
|
:send-button-disabled="isSendButtonDisabled"
|
||||||
placeholder="输入问题..."
|
placeholder="输入问题..."
|
||||||
:mention="mentionConfig"
|
:mention="mentionConfig"
|
||||||
:supports-file-upload="supportsFileUpload"
|
:supports-file-upload="supportsFileUpload"
|
||||||
@ -274,6 +274,8 @@ const { organization } = storeToRefs(infoStore)
|
|||||||
|
|
||||||
// ==================== LOCAL CHAT & UI STATE ====================
|
// ==================== LOCAL CHAT & UI STATE ====================
|
||||||
const userInput = ref('')
|
const userInput = ref('')
|
||||||
|
const sendCooldownActive = ref(false)
|
||||||
|
let sendCooldownTimer = null
|
||||||
const sidebarLogo = computed(() => organization.value?.logo || organization.value?.avatar || '')
|
const sidebarLogo = computed(() => organization.value?.logo || organization.value?.avatar || '')
|
||||||
const useRunsApi =
|
const useRunsApi =
|
||||||
import.meta.env.VITE_USE_RUNS_API === 'true' &&
|
import.meta.env.VITE_USE_RUNS_API === 'true' &&
|
||||||
@ -498,6 +500,20 @@ const isStreaming = computed(() => {
|
|||||||
return threadState ? threadState.isStreaming : false
|
return threadState ? threadState.isStreaming : false
|
||||||
})
|
})
|
||||||
const isProcessing = computed(() => isStreaming.value)
|
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 ====================
|
// ==================== SCROLL & RESIZE HANDLING ====================
|
||||||
const scrollController = new ScrollController('.chat-main')
|
const scrollController = new ScrollController('.chat-main')
|
||||||
@ -546,6 +562,10 @@ onUnmounted(() => {
|
|||||||
if (chatMainResizeObserver) {
|
if (chatMainResizeObserver) {
|
||||||
chatMainResizeObserver.disconnect()
|
chatMainResizeObserver.disconnect()
|
||||||
}
|
}
|
||||||
|
if (sendCooldownTimer) {
|
||||||
|
clearTimeout(sendCooldownTimer)
|
||||||
|
sendCooldownTimer = null
|
||||||
|
}
|
||||||
// 清理所有线程状态
|
// 清理所有线程状态
|
||||||
resetOnGoingConv()
|
resetOnGoingConv()
|
||||||
})
|
})
|
||||||
@ -1010,7 +1030,11 @@ const togglePinChat = async (chatId) => {
|
|||||||
|
|
||||||
const handleSendMessage = async ({ image } = {}) => {
|
const handleSendMessage = async ({ image } = {}) => {
|
||||||
const text = userInput.value.trim()
|
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
|
let threadId = currentChatId.value
|
||||||
if (!threadId) {
|
if (!threadId) {
|
||||||
@ -1109,6 +1133,10 @@ const handleSendMessage = async ({ image } = {}) => {
|
|||||||
|
|
||||||
// 发送或中断
|
// 发送或中断
|
||||||
const handleSendOrStop = async (payload) => {
|
const handleSendOrStop = async (payload) => {
|
||||||
|
if (sendCooldownActive.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const threadId = currentChatId.value
|
const threadId = currentChatId.value
|
||||||
const threadState = getThreadState(threadId)
|
const threadState = getThreadState(threadId)
|
||||||
if (isProcessing.value && threadState) {
|
if (isProcessing.value && threadState) {
|
||||||
|
|||||||
@ -58,7 +58,7 @@ import ImagePreviewComponent from '@/components/ImagePreviewComponent.vue'
|
|||||||
import AttachmentOptionsComponent from '@/components/AttachmentOptionsComponent.vue'
|
import AttachmentOptionsComponent from '@/components/AttachmentOptionsComponent.vue'
|
||||||
import { FolderCode } from 'lucide-vue-next'
|
import { FolderCode } from 'lucide-vue-next'
|
||||||
|
|
||||||
defineProps({
|
const props = defineProps({
|
||||||
modelValue: { type: String, default: '' },
|
modelValue: { type: String, default: '' },
|
||||||
isLoading: { type: Boolean, default: false },
|
isLoading: { type: Boolean, default: false },
|
||||||
disabled: { type: Boolean, default: false },
|
disabled: { type: Boolean, default: false },
|
||||||
@ -112,6 +112,10 @@ const handleSend = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleKeyDown = (e) => {
|
const handleKeyDown = (e) => {
|
||||||
|
if (props.sendButtonDisabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (e.key === 'Enter' && !e.shiftKey) {
|
if (e.key === 'Enter' && !e.shiftKey) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
handleSend()
|
handleSend()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user