diff --git a/components/ask/aiChat.tsx b/components/ask/aiChat.tsx index b0ff2fd..48cabaf 100644 --- a/components/ask/aiChat.tsx +++ b/components/ask/aiChat.tsx @@ -1,16 +1,11 @@ import ChatSvg from "@/assets/icons/svg/chat.svg"; -import FolderSvg from "@/assets/icons/svg/folder.svg"; import MoreSvg from "@/assets/icons/svg/more.svg"; -import ReturnArrow from "@/assets/icons/svg/returnArrow.svg"; -import YesSvg from "@/assets/icons/svg/yes.svg"; import { Message, Video } from "@/types/ask"; import { MaterialItem } from "@/types/personal-info"; import { TFunction } from "i18next"; import React from 'react'; import { - FlatList, Image, - Modal, Pressable, StyleSheet, Text, @@ -18,9 +13,10 @@ import { View } from 'react-native'; import { ThemedText } from "../ThemedText"; +import SelectModel from "./selectModel"; +import SingleContentModel from "./singleContentModel"; import TypewriterText from "./typewriterText"; import { mergeArrays } from "./utils"; -import VideoPlayer from "./VideoPlayer"; interface RenderMessageProps { insets: { top: number }; @@ -28,8 +24,8 @@ interface RenderMessageProps { sessionId: string; setModalVisible: React.Dispatch>; modalVisible: { visible: boolean, data: Video | MaterialItem }; - setModalDetailsVisible: React.Dispatch>; - modalDetailsVisible: boolean; + setModalDetailsVisible: React.Dispatch>; + modalDetailsVisible: { visible: boolean, content: any }; setSelectedImages: React.Dispatch>; selectedImages: string[]; t: TFunction; @@ -37,9 +33,6 @@ interface RenderMessageProps { const MessageItem = ({ t, insets, item, sessionId, setModalVisible, modalVisible, setModalDetailsVisible, modalDetailsVisible, setSelectedImages, selectedImages }: RenderMessageProps) => { const isUser = item.role === 'User'; - const isVideo = (data: Video | MaterialItem): data is Video => { - return 'video' in data; - }; return ( @@ -92,7 +85,7 @@ const MessageItem = ({ t, insets, item, sessionId, setModalVisible, modalVisible { ((item.content.video_material_infos?.length || 0) + (item.content.image_material_infos?.length || 0)) > 3 && { - setModalDetailsVisible(true); + setModalDetailsVisible({ visible: true, content: item.content }); }}> {((item.content.video_material_infos?.length || 0) + (item.content.image_material_infos?.length || 0))} @@ -116,152 +109,17 @@ const MessageItem = ({ t, insets, item, sessionId, setModalVisible, modalVisible ))} )} */} - { - setModalVisible({ visible: false, data: {} as Video | MaterialItem }); - }}> - - { - setModalVisible({ visible: false, data: {} as Video | MaterialItem }) - }} - /> - setModalVisible({ visible: false, data: {} as Video | MaterialItem })}> - {isVideo(modalVisible.data) ? ( - // 视频播放器 - setModalVisible({ visible: false, data: {} as Video | MaterialItem })} - > - setModalVisible({ visible: false, data: {} as Video | MaterialItem })} - /> - - ) : ( - // 图片预览 - setModalVisible({ visible: false, data: {} as Video | MaterialItem })} - style={styles.imageContainer} - > - - - )} - - - - { - setModalDetailsVisible(false); - }} - > - - - setModalDetailsVisible(false)}> - - - {t('ask.selectPhoto', { ns: 'ask' })} - - - - item.id} - showsVerticalScrollIndicator={false} - contentContainerStyle={detailsStyles.flatListContent} - initialNumToRender={12} - maxToRenderPerBatch={12} - updateCellsBatchingPeriod={50} - windowSize={10} - removeClippedSubviews={true} - renderItem={({ item }) => { - return ( - - - - {selectedImages?.map((image, index) => { - if (image === item.id || image === item.video?.id) { - return index + 1 - } - })} - - console.log('Image load error:', error.nativeEvent.error)} - onLoad={() => console.log('Image loaded successfully')} - loadingIndicatorSource={require('@/assets/images/png/placeholder.png')} - /> - { - setSelectedImages((prev) => { - if (prev.includes(item?.id || item?.video?.id)) { - return prev.filter((id) => id !== (item.id || item?.video?.id)); - } else { - return [...prev, item.id || item.video?.id]; - } - }); - }} - > - {selectedImages.includes(item?.id || item?.video?.id) ? : ""} - - - - ); - }} - /> - - - { - // 如果用户没有选择 则为选择全部 - if (selectedImages?.length < 0) { - setSelectedImages(mergeArrays(item.content.image_material_infos || [], item.content.video_material_infos || [])?.map((item) => { - return item.id || item.video?.id - })) - } - setModalDetailsVisible(false) - }} - activeOpacity={0.8} - > - - {t('ask.continueAsking', { ns: 'ask' })} - - - - - + {/* 单个图片弹窗 */} + + {/* 全部图片详情弹窗 */} + ); @@ -352,123 +210,3 @@ const styles = StyleSheet.create({ }, }); -const detailsStyles = StyleSheet.create({ - gridItemContainer: { - flex: 1, // 使用 flex 布局使项目平均分配空间 - maxWidth: '33.33%', // 每行最多4个项目 - aspectRatio: 1, // 保持1:1的宽高比 - }, - flatListContent: { - paddingBottom: 100, // 为底部按钮留出更多空间 - paddingHorizontal: 8, // 添加水平内边距 - paddingTop: 8, - }, - headerText: { - fontSize: 20, - fontWeight: 'bold', - color: "#4C320C" - }, - container: { - flex: 1, - padding: 0, - margin: 0, - backgroundColor: '#fff', - width: '100%', - height: '100%', - position: 'relative', - }, - imageNumber: { - fontSize: 16, - fontWeight: 'bold', - color: '#fff', - position: 'absolute', - top: 10, - left: 10, - zIndex: 10, // 确保数字显示在图片上方 - }, - imageNumberText: { - fontSize: 16, - fontWeight: 'bold', - color: '#fff', - }, - numberText: { - position: 'absolute', - top: 10, - left: 10, - width: 28, - height: 28, - borderRadius: 14, - backgroundColor: 'rgba(0, 122, 255, 0.9)', // 使用半透明蓝色背景 - justifyContent: 'center', - alignItems: 'center', - shadowColor: '#000', - shadowOffset: { width: 0, height: 2 }, - shadowOpacity: 0.25, - shadowRadius: 3.84, - elevation: 5, - }, - header: { - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - padding: 16, - borderBottomWidth: 1, - borderBottomColor: '#eee', - }, - gridItem: { - flex: 1, // 填充父容器 - overflow: 'hidden', - backgroundColor: '#f5f5f5', - borderWidth: 1, - borderColor: '#eee', - height: '100%', // 确保高度填满容器 - position: 'relative', - }, - image: { - width: '100%', - height: '100%', - resizeMode: 'cover', - }, - circleMarker: { - position: 'absolute', - top: 10, - right: 10, - width: 28, - height: 28, - borderRadius: 14, - justifyContent: 'center', - alignItems: 'center', - borderWidth: 3, - borderColor: '#fff', - }, - circleMarkerSelected: { - backgroundColor: '#FFB645', - }, - markerText: { - fontSize: 16, - fontWeight: 'bold', - color: '#000', - }, - footer: { - position: 'absolute', - bottom: 20, - left: 0, - right: 0, - paddingHorizontal: 16, - zIndex: 10, - paddingVertical: 10, - }, - continueButton: { - backgroundColor: '#E2793F', - borderRadius: 32, - padding: 16, - alignItems: 'center', - width: '100%', - zIndex: 10, - }, - continueButtonText: { - color: '#fff', - fontSize: 18, - fontWeight: 'bold', - } -}); \ No newline at end of file diff --git a/components/ask/chat.tsx b/components/ask/chat.tsx index 2517f1e..8a98679 100644 --- a/components/ask/chat.tsx +++ b/components/ask/chat.tsx @@ -34,7 +34,7 @@ function ChatComponent( paddingTop: 0, }), []); - const [modalDetailsVisible, setModalDetailsVisible] = useState(false); + const [modalDetailsVisible, setModalDetailsVisible] = useState<{ visible: boolean, content: any }>({ visible: false, content: [] }); return ( diff --git a/components/ask/selectModel.tsx b/components/ask/selectModel.tsx new file mode 100644 index 0000000..60bda27 --- /dev/null +++ b/components/ask/selectModel.tsx @@ -0,0 +1,234 @@ +import FolderSvg from "@/assets/icons/svg/folder.svg"; +import ReturnArrow from "@/assets/icons/svg/returnArrow.svg"; +import YesSvg from "@/assets/icons/svg/yes.svg"; +import { TFunction } from "i18next"; +import React from "react"; +import { FlatList, Image, Modal, StyleSheet, TouchableOpacity, View } from "react-native"; +import { ThemedText } from "../ThemedText"; +import { mergeArrays } from "./utils"; + +interface SelectModelProps { + modalDetailsVisible: { visible: boolean, content: any }; + setModalDetailsVisible: React.Dispatch>; + insets: { top: number }; + setSelectedImages: React.Dispatch>; + selectedImages: string[]; + t: TFunction; +} +const SelectModel = ({ modalDetailsVisible, setModalDetailsVisible, insets, setSelectedImages, selectedImages, t }: SelectModelProps) => { + + return ( + { + setModalDetailsVisible({ visible: false, content: [] }); + }} + > + + + setModalDetailsVisible({ visible: false, content: [] })}> + + + {t('ask.selectPhoto', { ns: 'ask' })} + + + + item.id} + showsVerticalScrollIndicator={false} + contentContainerStyle={detailsStyles.flatListContent} + initialNumToRender={12} + maxToRenderPerBatch={12} + updateCellsBatchingPeriod={50} + windowSize={10} + removeClippedSubviews={true} + renderItem={({ item }) => { + return ( + + + + {selectedImages?.map((image, index) => { + if (image === item.id || image === item.video?.id) { + return index + 1 + } + })} + + console.log('Image load error:', error.nativeEvent.error)} + onLoad={() => console.log('Image loaded successfully')} + loadingIndicatorSource={require('@/assets/images/png/placeholder.png')} + /> + { + setSelectedImages((prev) => { + if (prev.includes(item?.id || item?.video?.id)) { + return prev.filter((id) => id !== (item.id || item?.video?.id)); + } else { + return [...prev, item.id || item.video?.id]; + } + }); + }} + > + {selectedImages.includes(item?.id || item?.video?.id) ? : ""} + + + + ); + }} + /> + + + { + // 如果用户没有选择 则为选择全部 + if (selectedImages?.length < 0) { + setSelectedImages(mergeArrays(modalDetailsVisible?.content?.image_material_infos || [], modalDetailsVisible?.content?.video_material_infos || [])?.map((item) => { + return item.id || item.video?.id + })) + } + setModalDetailsVisible({ visible: false, content: [] }) + }} + activeOpacity={0.8} + > + + {t('ask.continueAsking', { ns: 'ask' })} + + + + + + ) +} + + +const detailsStyles = StyleSheet.create({ + gridItemContainer: { + width: '33.33%', + aspectRatio: 1, + padding: 1, + }, + flatListContent: { + paddingBottom: 100, + paddingHorizontal: 8, + paddingTop: 8, + }, + headerText: { + fontSize: 20, + fontWeight: 'bold', + color: "#4C320C" + }, + container: { + flex: 1, + padding: 0, + margin: 0, + backgroundColor: '#fff', + width: '100%', + height: '100%', + position: 'relative', + }, + imageNumber: { + fontSize: 16, + fontWeight: 'bold', + color: '#fff', + position: 'absolute', + top: 10, + left: 10, + zIndex: 10, + }, + imageNumberText: { + fontSize: 16, + fontWeight: 'bold', + color: '#fff', + }, + numberText: { + position: 'absolute', + top: 10, + left: 10, + width: 28, + height: 28, + borderRadius: 14, + backgroundColor: 'rgba(0, 122, 255, 0.9)', + justifyContent: 'center', + alignItems: 'center', + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.25, + shadowRadius: 3.84, + elevation: 5, + }, + header: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + padding: 16, + borderBottomWidth: 1, + borderBottomColor: '#eee', + }, + gridItem: { + flex: 1, + backgroundColor: '#f5f5f5', + borderRadius: 8, + overflow: 'hidden', + position: 'relative', + }, + image: { + width: '100%', + height: '100%', + }, + circleMarker: { + position: 'absolute', + top: 10, + right: 10, + width: 28, + height: 28, + borderRadius: 14, + justifyContent: 'center', + alignItems: 'center', + borderWidth: 2, + borderColor: '#fff', + }, + circleMarkerSelected: { + backgroundColor: '#FFB645', + }, + markerText: { + fontSize: 16, + fontWeight: 'bold', + color: '#000', + }, + footer: { + position: 'absolute', + bottom: 20, + left: 0, + right: 0, + paddingHorizontal: 16, + zIndex: 10, + paddingVertical: 10, + }, + continueButton: { + backgroundColor: '#E2793F', + borderRadius: 32, + padding: 16, + alignItems: 'center', + width: '100%', + zIndex: 10, + }, + continueButtonText: { + color: '#fff', + fontSize: 18, + fontWeight: 'bold', + } +}); + +export default SelectModel \ No newline at end of file diff --git a/components/ask/singleContentModel.tsx b/components/ask/singleContentModel.tsx new file mode 100644 index 0000000..4140ae7 --- /dev/null +++ b/components/ask/singleContentModel.tsx @@ -0,0 +1,159 @@ +import { Video } from "@/types/ask"; +import { MaterialItem } from "@/types/personal-info"; +import { Image, Modal, StyleSheet, TouchableOpacity, View } from "react-native"; +import VideoPlayer from "./VideoPlayer"; + +interface SingleContentModelProps { + modalVisible: { visible: boolean, data: Video | MaterialItem }; + setModalVisible: React.Dispatch>; +} +const SingleContentModel = ({ modalVisible, setModalVisible }: SingleContentModelProps) => { + const isVideo = (data: Video | MaterialItem): data is Video => { + return 'video' in data; + }; + + return ( + { + setModalVisible({ visible: false, data: {} as Video | MaterialItem }); + }}> + + { + setModalVisible({ visible: false, data: {} as Video | MaterialItem }) + }} + /> + setModalVisible({ visible: false, data: {} as Video | MaterialItem })}> + {isVideo(modalVisible.data) ? ( + // 视频播放器 + setModalVisible({ visible: false, data: {} as Video | MaterialItem })} + > + setModalVisible({ visible: false, data: {} as Video | MaterialItem })} + /> + + ) : ( + // 图片预览 + setModalVisible({ visible: false, data: {} as Video | MaterialItem })} + style={styles.imageContainer} + > + + + )} + + + + ) +} + + + +const styles = StyleSheet.create({ + imageGridContainer: { + flexDirection: 'row', + flexWrap: 'nowrap', + width: '100%', + marginTop: 8, + }, + video: { + width: '100%', + height: '100%', + }, + imageContainer: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + width: '100%', + maxHeight: '60%', + }, + fullWidthImage: { + width: '100%', + height: "54%", + marginBottom: 8, + }, + gridImage: { + aspectRatio: 1, + marginBottom: 8, + }, + background: { + ...StyleSheet.absoluteFillObject, + backgroundColor: 'rgba(0, 0, 0, 0.5)', // 添加半透明黑色背景 + }, + centeredView: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + }, + modalView: { + borderRadius: 20, + alignItems: 'center', + height: '100%', + width: "100%", + justifyContent: 'center', + alignSelf: 'center', + }, + container: { + flex: 1, + backgroundColor: '#f5f5f5', + }, + userAvatar: { + width: 30, + height: 30, + borderRadius: 15, + }, + messageList: { + padding: 16, + }, + messageBubble: { + paddingHorizontal: 16, + paddingVertical: 12, + fontWeight: "600" + }, + userBubble: { + alignSelf: 'flex-end', + backgroundColor: '#FFB645', + marginLeft: '20%', + }, + aiBubble: { + alignSelf: 'flex-start', + backgroundColor: '#fff', + marginRight: '20%', + borderWidth: 1, + borderColor: '#e5e5ea', + }, + userText: { + color: '#4C320C', + fontSize: 16, + }, + aiText: { + color: '#000', + fontSize: 16, + }, +}); + +export default SingleContentModel \ No newline at end of file