feat(ChatInput): 集成快捷短语功能
- 添加快捷短语触发按钮和弹出层 - 实现短语内容插入到输入框功能 - 集成快捷短语管理模态框 - 添加点击外部和ESC键关闭弹出层 - 统一按钮圆角样式为 rounded-md
This commit is contained in:
parent
caf19f4c09
commit
16079af79d
@ -5,11 +5,14 @@ import { Paperclip, ArrowUp, Upload } from 'lucide-react';
|
||||
import { ModelSelector } from './ModelSelector';
|
||||
import { ToolsDropdown } from './ToolsDropdown';
|
||||
import { FilePreviewList } from './FilePreviewList';
|
||||
import { QuickPhrasesTrigger, QuickPhrasesPopover } from './QuickPhrasesPopover';
|
||||
import { QuickPhrasesModal } from './QuickPhrasesModal';
|
||||
import { Tooltip } from '@/components/ui/Tooltip';
|
||||
import { useFileUpload } from '@/hooks/useFileUpload';
|
||||
import { usePromptOptimizer } from '@/providers/PromptOptimizerProvider';
|
||||
import { useQuickPhrases } from '@/hooks/useQuickPhrases';
|
||||
import { cn } from '@/lib/utils';
|
||||
import type { Model, Tool } from '@/types';
|
||||
import type { Model, Tool, QuickPhrase } from '@/types';
|
||||
import type { UploadFile } from '@/types/file-upload';
|
||||
|
||||
interface ChatInputProps {
|
||||
@ -36,11 +39,22 @@ export function ChatInput({
|
||||
className,
|
||||
}: ChatInputProps) {
|
||||
const [message, setMessage] = useState('');
|
||||
const [isManageModalOpen, setIsManageModalOpen] = useState(false);
|
||||
const [isPhrasesOpen, setIsPhrasesOpen] = useState(false);
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const phrasesContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// 使用提示词优化 Hook
|
||||
const { consumeOptimizedPrompt } = usePromptOptimizer();
|
||||
|
||||
// 使用快捷短语 Hook
|
||||
const {
|
||||
phrases,
|
||||
addPhrase,
|
||||
updatePhrase,
|
||||
deletePhrase,
|
||||
} = useQuickPhrases();
|
||||
|
||||
// 监听优化后的提示词并填入输入框
|
||||
useEffect(() => {
|
||||
const prompt = consumeOptimizedPrompt();
|
||||
@ -49,6 +63,39 @@ export function ChatInput({
|
||||
}
|
||||
}, [consumeOptimizedPrompt]);
|
||||
|
||||
// 点击外部关闭快捷短语弹出层
|
||||
useEffect(() => {
|
||||
function handleClickOutside(event: MouseEvent) {
|
||||
if (
|
||||
phrasesContainerRef.current &&
|
||||
!phrasesContainerRef.current.contains(event.target as Node)
|
||||
) {
|
||||
setIsPhrasesOpen(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (isPhrasesOpen) {
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
}
|
||||
|
||||
return () => document.removeEventListener('mousedown', handleClickOutside);
|
||||
}, [isPhrasesOpen]);
|
||||
|
||||
// ESC键关闭快捷短语弹出层
|
||||
useEffect(() => {
|
||||
function handleEscKey(event: KeyboardEvent) {
|
||||
if (event.key === 'Escape' && isPhrasesOpen) {
|
||||
setIsPhrasesOpen(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (isPhrasesOpen) {
|
||||
document.addEventListener('keydown', handleEscKey);
|
||||
}
|
||||
|
||||
return () => document.removeEventListener('keydown', handleEscKey);
|
||||
}, [isPhrasesOpen]);
|
||||
|
||||
// 使用文件上传 Hook
|
||||
const {
|
||||
files,
|
||||
@ -97,9 +144,36 @@ export function ChatInput({
|
||||
fileInputRef.current?.click();
|
||||
};
|
||||
|
||||
// 插入快捷短语内容
|
||||
const handleInsertPhrase = (content: string) => {
|
||||
setMessage((prev) => {
|
||||
// 如果已有内容,在末尾添加空格
|
||||
if (prev.trim()) {
|
||||
return prev + ' ' + content;
|
||||
}
|
||||
return content;
|
||||
});
|
||||
};
|
||||
|
||||
// 打开管理模态框
|
||||
const handleOpenManageModal = () => {
|
||||
setIsManageModalOpen(true);
|
||||
};
|
||||
|
||||
// 关闭管理模态框
|
||||
const handleCloseManageModal = () => {
|
||||
setIsManageModalOpen(false);
|
||||
};
|
||||
|
||||
// 处理添加短语(从管理模态框)
|
||||
const handleAddFromModal = () => {
|
||||
handleOpenManageModal();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cn('w-full max-w-[var(--input-max-width)] mx-auto', className)}>
|
||||
<div
|
||||
ref={phrasesContainerRef}
|
||||
className={cn(
|
||||
'relative flex flex-col bg-[var(--color-bg-primary)] border border-[var(--color-border)] rounded-md p-4 shadow-[var(--shadow-input)]',
|
||||
'transition-all duration-150',
|
||||
@ -111,6 +185,19 @@ export function ChatInput({
|
||||
onDragOver={handleDragOver}
|
||||
onDrop={handleDrop}
|
||||
>
|
||||
{/* 快捷短语弹出层 - 横跨在输入框顶部 */}
|
||||
<QuickPhrasesPopover
|
||||
phrases={phrases}
|
||||
isOpen={isPhrasesOpen}
|
||||
onInsert={handleInsertPhrase}
|
||||
onEdit={(phrase) => {
|
||||
setIsManageModalOpen(true);
|
||||
}}
|
||||
onDelete={deletePhrase}
|
||||
onManage={handleOpenManageModal}
|
||||
onAdd={handleAddFromModal}
|
||||
onClose={() => setIsPhrasesOpen(false)}
|
||||
/>
|
||||
{/* 拖拽覆盖层 */}
|
||||
{isDragging && (
|
||||
<div className="absolute inset-0 z-10 flex flex-col items-center justify-center bg-[var(--color-bg-primary)]/90 rounded-md border-2 border-dashed border-[var(--color-primary)]">
|
||||
@ -153,7 +240,7 @@ export function ChatInput({
|
||||
<button
|
||||
onClick={openFileDialog}
|
||||
className={cn(
|
||||
'w-8 h-8 flex items-center justify-center rounded-lg transition-colors cursor-pointer',
|
||||
'w-8 h-8 flex items-center justify-center rounded-md transition-colors cursor-pointer',
|
||||
'text-[var(--color-text-tertiary)] hover:bg-[var(--color-bg-hover)] hover:text-[var(--color-text-secondary)]',
|
||||
files.length > 0 && 'text-[var(--color-primary)]'
|
||||
)}
|
||||
@ -172,6 +259,12 @@ export function ChatInput({
|
||||
className="hidden"
|
||||
/>
|
||||
|
||||
{/* 快捷短语触发按钮 */}
|
||||
<QuickPhrasesTrigger
|
||||
phrasesCount={phrases.length}
|
||||
onClick={() => setIsPhrasesOpen(!isPhrasesOpen)}
|
||||
/>
|
||||
|
||||
{/* 工具下拉 */}
|
||||
<ToolsDropdown
|
||||
tools={tools}
|
||||
@ -195,7 +288,7 @@ export function ChatInput({
|
||||
onClick={handleSend}
|
||||
disabled={!message.trim() && files.length === 0}
|
||||
className={cn(
|
||||
'w-[38px] h-[38px] flex items-center justify-center bg-[var(--color-primary)] text-white rounded-xl transition-all duration-150 cursor-pointer',
|
||||
'w-[38px] h-[38px] flex items-center justify-center bg-[var(--color-primary)] text-white rounded-md transition-all duration-150 cursor-pointer',
|
||||
message.trim() || files.length > 0
|
||||
? 'hover:bg-[var(--color-primary-hover)] hover:-translate-y-0.5'
|
||||
: 'opacity-50 cursor-not-allowed'
|
||||
@ -212,6 +305,16 @@ export function ChatInput({
|
||||
<p className="text-xs text-[var(--color-text-tertiary)] text-center mt-2">
|
||||
可拖拽文件到此处或使用 Ctrl+V 粘贴图片
|
||||
</p>
|
||||
|
||||
{/* 快捷短语管理模态框 */}
|
||||
<QuickPhrasesModal
|
||||
isOpen={isManageModalOpen}
|
||||
onClose={handleCloseManageModal}
|
||||
phrases={phrases}
|
||||
onAdd={addPhrase}
|
||||
onUpdate={updatePhrase}
|
||||
onDelete={deletePhrase}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user