From 11b0c051d11df58ff2da5207d60dc28ebfa86019 Mon Sep 17 00:00:00 2001 From: jinyaqiu Date: Mon, 7 Jul 2025 13:44:44 +0800 Subject: [PATCH] =?UTF-8?q?feat:=200.4=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(tabs)/ask.tsx | 20 +----- components/ask/aiChat.tsx | 6 +- components/ask/chat.tsx | 9 ++- components/ask/send.tsx | 135 ++++++++++++++++++++++++++++++++++++++ components/ask/voice.tsx | 59 +++++++++++------ 5 files changed, 184 insertions(+), 45 deletions(-) create mode 100644 components/ask/send.tsx diff --git a/app/(tabs)/ask.tsx b/app/(tabs)/ask.tsx index aaf4aec..bd0a8e9 100644 --- a/app/(tabs)/ask.tsx +++ b/app/(tabs)/ask.tsx @@ -1,7 +1,7 @@ import ReturnArrow from "@/assets/icons/svg/returnArrow.svg"; import Chat from "@/components/ask/chat"; import AskHello from "@/components/ask/hello"; -import AudioRecordPlay from "@/components/ask/voice"; +import SendMessage from "@/components/ask/send"; import { ThemedText } from "@/components/ThemedText"; import { fetchApi } from "@/lib/server-api-util"; import { Message } from "@/types/ask"; @@ -105,23 +105,7 @@ export default function AskScreen() { {/* 功能区 - 放在 KeyboardAvoidingView 内但在 ScrollView 外 */} - - - - - - - + diff --git a/components/ask/aiChat.tsx b/components/ask/aiChat.tsx index f4f5589..cddff82 100644 --- a/components/ask/aiChat.tsx +++ b/components/ask/aiChat.tsx @@ -95,7 +95,7 @@ const renderMessage = ({ item, sessionId, setModalVisible, modalVisible }: Rende {mergeArrays(item.content.image_material_infos || [], item.content.video_material_infos || [])?.slice(0, 3)?.map((image, index, array) => ( { setModalVisible({ visible: true, data: image }); }} @@ -127,7 +127,7 @@ const renderMessage = ({ item, sessionId, setModalVisible, modalVisible }: Rende )} - {item.askAgain && item.askAgain.length > 0 && ( + {/* {item.askAgain && item.askAgain.length > 0 && ( {item.askAgain.map((suggestion, index, array) => ( ))} - )} + )} */} item.timestamp, []); + const keyExtractor = useCallback((item: Message) => `${item.role}-${item.timestamp}`, []); // 使用 useMemo 缓存样式对象 const contentContainerStyle = useMemo(() => ({ padding: 16 }), []); @@ -49,4 +49,7 @@ function ChatComponent({ userMessages, sessionId }: ChatProps) { /> ); -} \ No newline at end of file +} + +// 使用 React.memo 包装组件,避免不必要的重渲染 +export default memo(ChatComponent); \ No newline at end of file diff --git a/components/ask/send.tsx b/components/ask/send.tsx new file mode 100644 index 0000000..4de8c84 --- /dev/null +++ b/components/ask/send.tsx @@ -0,0 +1,135 @@ +'use client'; +import VoiceSvg from '@/assets/icons/svg/vioce.svg'; +import React, { Dispatch, SetStateAction, useCallback, useState } from 'react'; +import { + StyleSheet, + TextInput, + TouchableOpacity, + View +} from 'react-native'; + +import { fetchApi } from '@/lib/server-api-util'; +import { Message } from '@/types/ask'; + +interface Props { + setIsHello: (isHello: boolean) => void, + conversationId: string | null, + setUserMessages: Dispatch>; + setConversationId: (conversationId: string) => void, +} +export default function SendMessage(props: Props) { + const { setIsHello, conversationId, setUserMessages, setConversationId } = props; + + // 用户询问 + const [inputValue, setInputValue] = useState(''); + + // 创建新对话并获取消息 + const createNewConversation = useCallback(async (user_text: string) => { + const data = await fetchApi("/chat/new", { + method: "POST", + }); + setConversationId(data); + await getConversation({ session_id: data, user_text }); + }, []); + + // 获取对话信息 + const getConversation = useCallback(async ({ session_id, user_text }: { session_id: string, user_text: string }) => { + // 获取对话信息必须要有对话id + if (!session_id) return; + const response = await fetchApi(`/chat`, { + method: "POST", + body: JSON.stringify({ + session_id, + user_text + }) + }); + setUserMessages((prev: Message[]) => [...prev, response]?.filter((item: Message) => item.content.text !== '正在寻找,请稍等...')); + }, []); + + // 发送询问 + const handleSubmit = () => { + const text = inputValue; + // 用户输入信息之后进行后续操作 + if (text) { + // 将用户输入信息添加到消息列表中 + setUserMessages(pre => ([...pre, { + content: { + text: text + }, + role: 'User', + timestamp: new Date().toISOString() + }, + { + content: { + text: "正在寻找,请稍等..." + }, + role: 'Assistant', + timestamp: new Date().toISOString() + } + ])); + // 如果没有对话ID,创建新对话并获取消息,否则直接获取消息 + if (!conversationId) { + createNewConversation(text); + setIsHello(false); + } else { + getConversation({ + session_id: conversationId, + user_text: text + }); + } + // 将输入框清空 + setInputValue(''); + } + } + + return ( + + + { + setInputValue(text); + }} + onSubmitEditing={handleSubmit} + // 调起的键盘类型 + returnKeyType="send" + /> + + + + + + ); +} + +const styles = StyleSheet.create({ + container: { + justifyContent: 'center', + backgroundColor: '#transparent', + }, + input: { + borderColor: '#FF9500', + borderWidth: 1, + borderRadius: 25, + paddingHorizontal: 20, + paddingVertical: 12, + fontSize: 16, + width: '100%', // 确保输入框宽度撑满 + paddingRight: 50 + }, + voiceButton: { + width: 40, + height: 40, + borderRadius: 20, + backgroundColor: '#FF9500', + justifyContent: 'center', + alignItems: 'center', + marginRight: 8, // 添加一点右边距 + }, +}); \ No newline at end of file diff --git a/components/ask/voice.tsx b/components/ask/voice.tsx index 52e16fd..c272dc9 100644 --- a/components/ask/voice.tsx +++ b/components/ask/voice.tsx @@ -1,28 +1,29 @@ 'use client'; -import React, { useCallback, useState } from 'react'; +import React, { Dispatch, SetStateAction, useCallback, useState } from 'react'; import { - Platform, StyleSheet, TextInput, View } from 'react-native'; +import { fetchApi } from '@/lib/server-api-util'; +import { Message } from '@/types/ask'; import { RecordingPresets, useAudioRecorder } from 'expo-audio'; interface Props { setIsHello: (isHello: boolean) => void, - setInputValue: (inputValue: string) => void, - inputValue: string, - createNewConversation: (user_text: string) => void, conversationId: string | null, - getConversation: ({ user_text, session_id }: { user_text: string, session_id: string }) => void + setUserMessages: Dispatch>; + setConversationId: (conversationId: string) => void, } export default function AudioRecordPlay(props: Props) { - const { setIsHello, setInputValue, inputValue, createNewConversation, conversationId, getConversation } = props; + const { setIsHello, conversationId, setUserMessages, setConversationId } = props; const audioRecorder = useAudioRecorder(RecordingPresets.HIGH_QUALITY); const [isRecording, setIsRecording] = useState(false); const [isVoiceStart, setIsVoiceStart] = useState(false); const [elapsedTime, setElapsedTime] = useState(0); + // 用户询问 + const [inputValue, setInputValue] = useState(''); const [timerInterval, setTimerInterval] = useState(0); const formatTime = (ms: number): string => { @@ -68,15 +69,39 @@ export default function AudioRecordPlay(props: Props) { // } // })(); // }, []); - // 使用 useCallback 缓存回调函数 - const handleChangeText = useCallback((text: string) => { - setInputValue(text); + // 获取对话信息 + const createNewConversation = useCallback(async (user_text: string) => { + const data = await fetchApi("/chat/new", { + method: "POST", + }); + setConversationId(data); + await getConversation({ session_id: data, user_text }); + }, []); + + const getConversation = useCallback(async ({ session_id, user_text }: { session_id: string, user_text: string }) => { + if (!session_id) return; + const response = await fetchApi(`/chat`, { + method: "POST", + body: JSON.stringify({ + session_id, + user_text + }) + }); + setUserMessages((prev: Message[]) => [...prev, response]); }, []); // 使用 useCallback 缓存 handleSubmit - const handleSubmit = useCallback(() => { - const text = inputValue.trim(); + const handleSubmit = () => { + const text = inputValue; if (text) { + setUserMessages(pre => ([...pre, { + content: { + text: text + }, + role: 'User', + timestamp: new Date().toISOString() + } + ])); if (!conversationId) { createNewConversation(text); setIsHello(false); @@ -88,15 +113,7 @@ export default function AudioRecordPlay(props: Props) { } setInputValue(''); } - }, [conversationId]); - - // 使用 useCallback 缓存 handleKeyPress - const handleKeyPress = useCallback((e: any) => { - if (Platform.OS === 'web' && e.nativeEvent.key !== 'Enter') { - return; - } - handleSubmit(); - }, [handleSubmit]); + } return (