fix: 对话创建防抖

This commit is contained in:
Gsh
2025-07-12 00:36:11 +08:00
parent 57fae7fe4b
commit 5162f9ce3b
4 changed files with 83 additions and 12 deletions

View File

@@ -40,6 +40,7 @@
"element-plus": "^2.9.11",
"fingerprintjs": "^0.5.3",
"hook-fetch": "^2.0.4-beta.1",
"lodash-es": "^4.17.21",
"nprogress": "^0.2.0",
"pinia": "^3.0.3",
"pinia-plugin-persistedstate": "^4.3.0",

View File

@@ -41,6 +41,9 @@ importers:
hook-fetch:
specifier: ^2.0.4-beta.1
version: 2.0.4-beta.1(react@19.1.0)(typescript-api-pro@0.0.7)(vue@3.5.16(typescript@5.8.3))
lodash-es:
specifier: ^4.17.21
version: 4.17.21
nprogress:
specifier: ^0.2.0
version: 0.2.0

View File

@@ -1,10 +1,15 @@
<!-- 默认消息列表页 -->
<script setup lang="ts">
import type { FilesCardProps } from 'vue-element-plus-x/types/FilesCard';
import { Loading } from '@element-plus/icons-vue';
import { useDebounceFn } from '@vueuse/core';
import { ElMessage } from 'element-plus';
import { nextTick, ref, watch } from 'vue';
import ModelSelect from '@/components/ModelSelect/index.vue';
import WelecomeText from '@/components/WelecomeText/index.vue';
import { useUserStore } from '@/stores';
import { useFilesStore } from '@/stores/modules/files';
import { useSessionStore } from '@/stores/modules/session';
const userStore = useUserStore();
@@ -13,15 +18,45 @@ const filesStore = useFilesStore();
const senderValue = ref('');
const senderRef = ref();
const isSending = ref(false); // 发送状态标志
async function handleSend() {
localStorage.setItem('chatContent', senderValue.value);
await sessionStore.createSessionList({
userId: userStore.userInfo?.userId as number,
sessionContent: senderValue.value,
sessionTitle: senderValue.value.slice(0, 10),
remark: senderValue.value.slice(0, 10),
});
// 防抖发送函数
const debouncedSend = useDebounceFn(async () => {
if (!senderValue.value.trim()) {
ElMessage.warning('消息内容不能为空');
return;
}
if (isSending.value) {
ElMessage.warning('请等待上一条消息发送完成');
return;
}
const content = senderValue.value;
isSending.value = true;
try {
localStorage.setItem('chatContent', content);
await sessionStore.createSessionList({
userId: userStore.userInfo?.userId as number,
sessionContent: content,
sessionTitle: content.slice(0, 10),
remark: content.slice(0, 10),
});
senderValue.value = ''; // 清空输入框
}
catch (error) {
console.error('发送消息失败:', error);
ElMessage.error('发送消息失败,请重试');
}
finally {
isSending.value = false;
}
}, 800, { leading: true, trailing: false }); // 800ms防抖
// 处理发送事件
function handleSend() {
debouncedSend();
}
function handleDeleteCard(_item: FilesCardProps, index: number) {
@@ -33,12 +68,12 @@ watch(
(val) => {
if (val > 0) {
nextTick(() => {
senderRef.value.openHeader();
senderRef.value?.openHeader();
});
}
else {
nextTick(() => {
senderRef.value.closeHeader();
senderRef.value?.closeHeader();
});
}
},
@@ -59,6 +94,7 @@ watch(
variant="updown"
clearable
allow-speech
:loading="isSending"
@submit="handleSend"
>
<template #header>
@@ -96,10 +132,14 @@ watch(
</template>
<template #prefix>
<div class="flex-1 flex items-center gap-8px flex-none w-fit overflow-hidden">
<!-- <FilesSelect /> -->
<ModelSelect />
</div>
</template>
<template #suffix>
<el-icon v-if="isSending" class="is-loading">
<Loading />
</el-icon>
</template>
</Sender>
</div>
</template>
@@ -117,4 +157,19 @@ watch(
width: 100%;
}
}
:deep(.el-icon.is-loading) {
margin-left: 8px;
color: var(--el-color-primary);
animation: rotating 2s linear infinite;
}
@keyframes rotating {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>

View File

@@ -6,7 +6,7 @@ import type { BubbleProps } from 'vue-element-plus-x/types/Bubble';
import type { BubbleListInstance } from 'vue-element-plus-x/types/BubbleList';
import type { FilesCardProps } from 'vue-element-plus-x/types/FilesCard';
import type { ThinkingStatus } from 'vue-element-plus-x/types/Thinking';
import { ArrowLeftBold, ArrowRightBold } from '@element-plus/icons-vue';
import { ArrowLeftBold, ArrowRightBold, Loading } from '@element-plus/icons-vue';
import { ElIcon, ElMessage } from 'element-plus';
import { useHookFetch } from 'hook-fetch/vue';
import { computed, nextTick, ref, watch } from 'vue';
@@ -164,8 +164,12 @@ function handleDataChunk(chunk: AnyObject) {
function handleError(err: any) {
console.error('Fetch error:', err);
}
const isSending = ref(false);
async function startSSE(chatContent: string) {
if (isSending.value)
return;
try {
// 清空输入框
inputValue.value = '';
@@ -199,6 +203,8 @@ async function startSSE(chatContent: string) {
}
}
finally {
isSending.value = false;
// 停止打字器状态
if (bubbleItems.value.length) {
bubbleItems.value[bubbleItems.value.length - 1].typing = false;
@@ -348,6 +354,12 @@ function copy(item: any) {
<ModelSelect />
</div>
</template>
<!-- 新增的 #suffix 模板 -->
<template #suffix>
<ElIcon v-if="isSending" class="is-loading">
<Loading />
</ElIcon>
</template>
</Sender>
</div>
</div>