diff --git a/components/SmartChatBox/InputBar.tsx b/components/SmartChatBox/InputBar.tsx index c015596..cf8b1b5 100644 --- a/components/SmartChatBox/InputBar.tsx +++ b/components/SmartChatBox/InputBar.tsx @@ -1,7 +1,22 @@ -import React, { useRef, useState, useEffect } from "react"; +import React, { useRef, useState, useEffect, useCallback } from "react"; import { Image as ImageIcon, Send, Trash2, ArrowUp } from "lucide-react"; import { MessageBlock } from "./types"; import { useUploadFile } from "@/app/service/domain/service"; +import { motion, AnimatePresence } from "framer-motion"; + +// 防抖函数 +function debounce void>(func: T, wait: number) { + let timeout: NodeJS.Timeout | null = null; + return function executedFunction(...args: Parameters) { + if (timeout) { + clearTimeout(timeout); + } + timeout = setTimeout(() => { + func(...args); + timeout = null; + }, wait); + }; +}; interface InputBarProps { onSend: (blocks: MessageBlock[], videoId?: string) => void; @@ -21,18 +36,47 @@ export function InputBar({ onSend, setVideoPreview, initialVideoUrl, initialVide const textareaRef = useRef(null); const { uploadFile } = useUploadFile(); + const [isComposing, setIsComposing] = useState(false); - const adjustHeight = () => { + const adjustHeight = useCallback(() => { const textarea = textareaRef.current; - if (textarea) { - textarea.style.height = 'auto'; - const newHeight = Math.min(Math.max(textarea.scrollHeight, 48), 120); - textarea.style.height = `${newHeight}px`; - - // 检查是否超过一行(48px 是单行高度) - setIsMultiline(newHeight > 48); + if (!textarea || isComposing) return; + + // 保存当前滚动位置 + const scrollPos = window.scrollY; + + // 重置高度以获取实际内容高度 + textarea.style.height = '0px'; + + // 强制浏览器重排,获取准确的 scrollHeight + const scrollHeight = textarea.scrollHeight; + const newHeight = Math.min(Math.max(scrollHeight, 48), 120); + + // 设置新高度 + textarea.style.height = `${newHeight}px`; + + // 恢复滚动位置,避免页面跳动 + window.scrollTo(0, scrollPos); + + // 更新布局状态 + if (!text.trim()) { + setIsMultiline(false); + } else if (!isMultiline && newHeight > 48) { + setIsMultiline(true); } - }; + }, [text, isMultiline, isComposing]); + + // 使用防抖包装 adjustHeight,但确保在输入完成时立即调整 + const debouncedAdjustHeight = useCallback( + debounce(() => { + requestAnimationFrame(() => { + if (!isComposing) { + adjustHeight(); + } + }); + }, 16), // 使用一帧的时间作为防抖时间 + [adjustHeight, isComposing] + ); // 监听初始视频 URL 和 ID 的变化 useEffect(() => { @@ -44,8 +88,8 @@ export function InputBar({ onSend, setVideoPreview, initialVideoUrl, initialVide // 监听文本变化和组件挂载时调整高度 useEffect(() => { - adjustHeight(); - }, [text]); + debouncedAdjustHeight(); + }, [text, debouncedAdjustHeight]); const handleSend = () => { const blocks: MessageBlock[] = []; @@ -172,81 +216,117 @@ export function InputBar({ onSend, setVideoPreview, initialVideoUrl, initialVide )} -
- {/* 图片上传按钮 - 单行时显示在左侧,多行时显示在底部 */} - {!isMultiline && ( - - )} + + + + {/* 图片上传按钮 - 单行时显示在左侧 */} + {!isMultiline && ( + + + + + )} - {/* 文本输入 */} -