feat(api): 集成后端API,实现数据持久化和用户登录
- 新增token管理和用户信息状态,支持基于token的自动登录 - 实现登录或注册接口调用,自动根据URL参数完成身份验证 - 从后端加载交易记录、消息和待办提醒,替代本地缓存读写 - 对交易记录的增删改操作同步到数据库,确保数据一致性 - 对消息和提醒的增删改操作支持服务器同步,提升数据可靠性 - 优化消息发送逻辑,先保存用户消息,再处理AI回复并存库 - 添加待办提醒切换状态API调用,确保状态同步服务器 - 改进界面互动,支持文件与头像上传功能,更新管家形象 - 采用异步处理并增加错误捕获和用户提示,增强稳定性
This commit is contained in:
@@ -11,6 +11,8 @@ const VoiceInput: React.FC<VoiceInputProps> = ({ onTranscript, isProcessing, isF
|
||||
const recognitionRef = useRef<any>(null);
|
||||
const timerRef = useRef<number | null>(null);
|
||||
|
||||
const isStartingRef = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
if ('SpeechRecognition' in window || 'webkitSpeechRecognition' in window) {
|
||||
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
|
||||
@@ -22,30 +24,37 @@ const VoiceInput: React.FC<VoiceInputProps> = ({ onTranscript, isProcessing, isF
|
||||
recognitionRef.current.onresult = (event: any) => {
|
||||
const transcript = event.results[0][0].transcript;
|
||||
if (transcript) onTranscript(transcript);
|
||||
setIsListening(false);
|
||||
};
|
||||
|
||||
recognitionRef.current.onerror = (event: any) => {
|
||||
if (event.error === 'network') {
|
||||
alert("语音识别网络连接失败。请检查网络或尝试切换浏览器(推荐 Chrome/Edge)。");
|
||||
}
|
||||
console.error('Speech recognition error', event.error);
|
||||
setIsListening(false);
|
||||
};
|
||||
|
||||
recognitionRef.current.onend = () => {
|
||||
setIsListening(false);
|
||||
isStartingRef.current = false;
|
||||
};
|
||||
}
|
||||
}, [onTranscript]);
|
||||
|
||||
const startListening = (e: React.MouseEvent | React.TouchEvent) => {
|
||||
e.preventDefault();
|
||||
if (isProcessing || isListening) return;
|
||||
const startListening = (e: React.PointerEvent) => {
|
||||
if (e.cancelable) e.preventDefault();
|
||||
|
||||
if (isProcessing || isListening || isStartingRef.current) return;
|
||||
|
||||
if (recognitionRef.current) {
|
||||
try {
|
||||
isStartingRef.current = true;
|
||||
recognitionRef.current.start();
|
||||
setIsListening(true);
|
||||
} catch (e) {
|
||||
console.error("Failed to start recognition", e);
|
||||
} catch (err) {
|
||||
console.error("Failed to start recognition", err);
|
||||
// 如果 start 失败,立即重置状态,否则会锁死
|
||||
isStartingRef.current = false;
|
||||
setIsListening(false);
|
||||
}
|
||||
} else {
|
||||
alert("您的浏览器不支持语音识别,请使用 Chrome 或 Edge。");
|
||||
@@ -53,20 +62,23 @@ const VoiceInput: React.FC<VoiceInputProps> = ({ onTranscript, isProcessing, isF
|
||||
};
|
||||
|
||||
const stopListening = () => {
|
||||
// 仅在识别引擎正在运行时尝试停止
|
||||
// 不要在这里重置 isListening 和 isStartingRef,等待 onend 回调
|
||||
if (isListening) {
|
||||
recognitionRef.current?.stop();
|
||||
setIsListening(false);
|
||||
try {
|
||||
recognitionRef.current?.stop();
|
||||
} catch (err) {
|
||||
console.error("Failed to stop recognition", err);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`${isFullWidth ? 'w-full' : ''} relative select-none touch-none`}>
|
||||
<button
|
||||
onMouseDown={startListening}
|
||||
onMouseUp={stopListening}
|
||||
onMouseLeave={stopListening}
|
||||
onTouchStart={startListening}
|
||||
onTouchEnd={stopListening}
|
||||
onPointerDown={startListening}
|
||||
onPointerUp={stopListening}
|
||||
onPointerLeave={stopListening}
|
||||
disabled={isProcessing}
|
||||
className={`relative flex items-center justify-center transition-all duration-300 active:scale-95 shadow-lg ${
|
||||
isFullWidth
|
||||
|
||||
Reference in New Issue
Block a user