feat: 新增聊天页面,包含消息发送、确认卡片、语音输入及打字机效果组件。
This commit is contained in:
32
src/components/common/Typewriter/Typewriter.tsx
Normal file
32
src/components/common/Typewriter/Typewriter.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
interface TypewriterProps {
|
||||
text: string;
|
||||
speed?: number;
|
||||
onComplete?: () => void;
|
||||
children?: (text: string) => React.ReactNode;
|
||||
}
|
||||
|
||||
export const Typewriter: React.FC<TypewriterProps> = ({ text, speed = 30, onComplete, children }) => {
|
||||
const [displayedText, setDisplayedText] = useState('');
|
||||
const [currentIndex, setCurrentIndex] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
if (currentIndex < text.length) {
|
||||
const timeout = setTimeout(() => {
|
||||
setDisplayedText(prev => prev + text[currentIndex]);
|
||||
setCurrentIndex(prev => prev + 1);
|
||||
}, speed);
|
||||
|
||||
return () => clearTimeout(timeout);
|
||||
} else if (onComplete) {
|
||||
onComplete();
|
||||
}
|
||||
}, [currentIndex, text, speed, onComplete]);
|
||||
|
||||
if (children) {
|
||||
return <>{children(displayedText)}</>;
|
||||
}
|
||||
|
||||
return <>{displayedText}</>;
|
||||
};
|
||||
@@ -6,6 +6,7 @@ import ReactMarkdown from 'react-markdown';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import aiService from '../../services/aiService';
|
||||
import type { AIChatResponse, ConfirmationCard } from '../../types';
|
||||
import { Typewriter } from '../../components/common/Typewriter/Typewriter';
|
||||
import './Chat.css';
|
||||
|
||||
interface Message {
|
||||
@@ -273,16 +274,26 @@ export default function Chat() {
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{messages.map((msg) => (
|
||||
{messages.map((msg, index) => (
|
||||
<div key={msg.id} className={`chat-message ${msg.role}`}>
|
||||
<div className="chat-message-avatar">
|
||||
{msg.role === 'assistant' ? '🪙' : '👤'}
|
||||
</div>
|
||||
<div>
|
||||
<div className="chat-message-content">
|
||||
{msg.role === 'assistant' && index === messages.length - 1 ? (
|
||||
<Typewriter text={msg.content} speed={20}>
|
||||
{(text) => (
|
||||
<ReactMarkdown remarkPlugins={[remarkGfm]}>
|
||||
{text}
|
||||
</ReactMarkdown>
|
||||
)}
|
||||
</Typewriter>
|
||||
) : (
|
||||
<ReactMarkdown remarkPlugins={[remarkGfm]}>
|
||||
{msg.content}
|
||||
</ReactMarkdown>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{msg.confirmationCard && (
|
||||
|
||||
Reference in New Issue
Block a user