feat: 图片终于吐出来了
This commit is contained in:
parent
301c818a66
commit
56b0cff7ec
@ -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<React.SetStateAction<{ visible: boolean, data: Video | MaterialItem }>>;
|
||||
modalVisible: { visible: boolean, data: Video | MaterialItem };
|
||||
setModalDetailsVisible: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
modalDetailsVisible: boolean;
|
||||
setModalDetailsVisible: React.Dispatch<React.SetStateAction<{ visible: boolean, content: any }>>;
|
||||
modalDetailsVisible: { visible: boolean, content: any };
|
||||
setSelectedImages: React.Dispatch<React.SetStateAction<string[]>>;
|
||||
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 (
|
||||
<View className={`flex-row items-start gap-2 w-full ${isUser ? 'justify-end' : 'justify-start'}`}>
|
||||
@ -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
|
||||
&& <TouchableOpacity className="absolute top-1/2 -translate-y-1/2 -right-4 translate-x-1/2 bg-bgPrimary flex flex-row items-center gap-2 p-1 pl-2 rounded-full" onPress={() => {
|
||||
setModalDetailsVisible(true);
|
||||
setModalDetailsVisible({ visible: true, content: item.content });
|
||||
}}>
|
||||
<ThemedText className="!text-white font-semibold">{((item.content.video_material_infos?.length || 0) + (item.content.image_material_infos?.length || 0))}</ThemedText>
|
||||
<View className="bg-white rounded-full p-2">
|
||||
@ -116,152 +109,17 @@ const MessageItem = ({ t, insets, item, sessionId, setModalVisible, modalVisible
|
||||
))}
|
||||
</View>
|
||||
)} */}
|
||||
<Modal
|
||||
animationType="fade"
|
||||
transparent={true}
|
||||
visible={modalVisible.visible}
|
||||
onRequestClose={() => {
|
||||
setModalVisible({ visible: false, data: {} as Video | MaterialItem });
|
||||
}}>
|
||||
<View style={styles.centeredView}>
|
||||
<TouchableOpacity
|
||||
style={styles.background}
|
||||
onPress={() => {
|
||||
setModalVisible({ visible: false, data: {} as Video | MaterialItem })
|
||||
}}
|
||||
/>
|
||||
<TouchableOpacity style={styles.modalView} onPress={() => setModalVisible({ visible: false, data: {} as Video | MaterialItem })}>
|
||||
{isVideo(modalVisible.data) ? (
|
||||
// 视频播放器
|
||||
<TouchableOpacity
|
||||
activeOpacity={1}
|
||||
style={{
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
maxHeight: "60%",
|
||||
}}
|
||||
onPress={() => setModalVisible({ visible: false, data: {} as Video | MaterialItem })}
|
||||
>
|
||||
<VideoPlayer
|
||||
videoUrl={modalVisible.data.video.file_info.url}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
alignSelf: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
onPress={() => setModalVisible({ visible: false, data: {} as Video | MaterialItem })}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
) : (
|
||||
// 图片预览
|
||||
<TouchableOpacity
|
||||
activeOpacity={1}
|
||||
onPress={() => setModalVisible({ visible: false, data: {} as Video | MaterialItem })}
|
||||
style={styles.imageContainer}
|
||||
>
|
||||
<Image
|
||||
source={{ uri: modalVisible.data.preview_file_info?.url }}
|
||||
style={styles.fullWidthImage}
|
||||
resizeMode="contain"
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</Modal>
|
||||
<Modal
|
||||
animationType="fade"
|
||||
visible={modalDetailsVisible}
|
||||
transparent={false}
|
||||
statusBarTranslucent={true}
|
||||
onRequestClose={() => {
|
||||
setModalDetailsVisible(false);
|
||||
}}
|
||||
>
|
||||
<View style={[detailsStyles.container, { paddingTop: insets?.top }]}>
|
||||
<View style={detailsStyles.header}>
|
||||
<TouchableOpacity onPress={() => setModalDetailsVisible(false)}>
|
||||
<ReturnArrow />
|
||||
</TouchableOpacity>
|
||||
<ThemedText style={detailsStyles.headerText}>{t('ask.selectPhoto', { ns: 'ask' })}</ThemedText>
|
||||
<FolderSvg />
|
||||
</View>
|
||||
<View style={{ overflow: 'scroll', height: "100%" }}>
|
||||
<FlatList
|
||||
data={mergeArrays(item.content.image_material_infos || [], item.content.video_material_infos || [])}
|
||||
numColumns={3}
|
||||
keyExtractor={(item) => item.id}
|
||||
showsVerticalScrollIndicator={false}
|
||||
contentContainerStyle={detailsStyles.flatListContent}
|
||||
initialNumToRender={12}
|
||||
maxToRenderPerBatch={12}
|
||||
updateCellsBatchingPeriod={50}
|
||||
windowSize={10}
|
||||
removeClippedSubviews={true}
|
||||
renderItem={({ item }) => {
|
||||
return (
|
||||
<TouchableOpacity
|
||||
style={detailsStyles.gridItemContainer}
|
||||
key={item.id}
|
||||
>
|
||||
<View style={detailsStyles.gridItem}>
|
||||
<ThemedText style={detailsStyles.imageNumber}>
|
||||
{selectedImages?.map((image, index) => {
|
||||
if (image === item.id || image === item.video?.id) {
|
||||
return index + 1
|
||||
}
|
||||
})}
|
||||
</ThemedText>
|
||||
<Image
|
||||
source={{ uri: item?.preview_file_info?.url || item.video?.preview_file_info?.url }}
|
||||
style={detailsStyles.image}
|
||||
onError={(error) => console.log('Image load error:', error.nativeEvent.error)}
|
||||
onLoad={() => console.log('Image loaded successfully')}
|
||||
loadingIndicatorSource={require('@/assets/images/png/placeholder.png')}
|
||||
/>
|
||||
<TouchableOpacity
|
||||
style={[detailsStyles.circleMarker, selectedImages.includes(item?.id || item?.video?.id) ? detailsStyles.circleMarkerSelected : ""]}
|
||||
onPress={() => {
|
||||
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) ? <YesSvg width={16} height={16} /> : ""}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
<View style={detailsStyles.footer}>
|
||||
<TouchableOpacity
|
||||
style={detailsStyles.continueButton}
|
||||
onPress={async () => {
|
||||
// 如果用户没有选择 则为选择全部
|
||||
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}
|
||||
>
|
||||
<Text style={detailsStyles.continueButtonText}>
|
||||
{t('ask.continueAsking', { ns: 'ask' })}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
{/* 单个图片弹窗 */}
|
||||
<SingleContentModel modalVisible={modalVisible} setModalVisible={setModalVisible} />
|
||||
{/* 全部图片详情弹窗 */}
|
||||
<SelectModel
|
||||
modalDetailsVisible={modalDetailsVisible}
|
||||
setModalDetailsVisible={setModalDetailsVisible}
|
||||
insets={insets}
|
||||
setSelectedImages={setSelectedImages}
|
||||
selectedImages={selectedImages}
|
||||
t={t}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
@ -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',
|
||||
}
|
||||
});
|
||||
@ -34,7 +34,7 @@ function ChatComponent(
|
||||
paddingTop: 0,
|
||||
}), []);
|
||||
|
||||
const [modalDetailsVisible, setModalDetailsVisible] = useState<boolean>(false);
|
||||
const [modalDetailsVisible, setModalDetailsVisible] = useState<{ visible: boolean, content: any }>({ visible: false, content: [] });
|
||||
|
||||
return (
|
||||
<SafeAreaView style={{ flex: 1 }}>
|
||||
|
||||
234
components/ask/selectModel.tsx
Normal file
234
components/ask/selectModel.tsx
Normal file
@ -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<React.SetStateAction<{ visible: boolean, content: any }>>;
|
||||
insets: { top: number };
|
||||
setSelectedImages: React.Dispatch<React.SetStateAction<string[]>>;
|
||||
selectedImages: string[];
|
||||
t: TFunction;
|
||||
}
|
||||
const SelectModel = ({ modalDetailsVisible, setModalDetailsVisible, insets, setSelectedImages, selectedImages, t }: SelectModelProps) => {
|
||||
|
||||
return (
|
||||
<Modal
|
||||
animationType="fade"
|
||||
visible={modalDetailsVisible.visible}
|
||||
transparent={false}
|
||||
statusBarTranslucent={true}
|
||||
onRequestClose={() => {
|
||||
setModalDetailsVisible({ visible: false, content: [] });
|
||||
}}
|
||||
>
|
||||
<View style={[detailsStyles.container, { paddingTop: insets?.top }]}>
|
||||
<View style={detailsStyles.header}>
|
||||
<TouchableOpacity onPress={() => setModalDetailsVisible({ visible: false, content: [] })}>
|
||||
<ReturnArrow />
|
||||
</TouchableOpacity>
|
||||
<ThemedText style={detailsStyles.headerText}>{t('ask.selectPhoto', { ns: 'ask' })}</ThemedText>
|
||||
<FolderSvg />
|
||||
</View>
|
||||
<View style={{ overflow: 'scroll', height: "100%" }}>
|
||||
<FlatList
|
||||
data={mergeArrays(modalDetailsVisible?.content?.image_material_infos || [], modalDetailsVisible?.content?.video_material_infos || [])}
|
||||
numColumns={3}
|
||||
keyExtractor={(item) => item.id}
|
||||
showsVerticalScrollIndicator={false}
|
||||
contentContainerStyle={detailsStyles.flatListContent}
|
||||
initialNumToRender={12}
|
||||
maxToRenderPerBatch={12}
|
||||
updateCellsBatchingPeriod={50}
|
||||
windowSize={10}
|
||||
removeClippedSubviews={true}
|
||||
renderItem={({ item }) => {
|
||||
return (
|
||||
<TouchableOpacity
|
||||
style={detailsStyles.gridItemContainer}
|
||||
key={item.id}
|
||||
>
|
||||
<View style={detailsStyles.gridItem}>
|
||||
<ThemedText style={detailsStyles.imageNumber}>
|
||||
{selectedImages?.map((image, index) => {
|
||||
if (image === item.id || image === item.video?.id) {
|
||||
return index + 1
|
||||
}
|
||||
})}
|
||||
</ThemedText>
|
||||
<Image
|
||||
source={{ uri: item?.preview_file_info?.url || item.video?.preview_file_info?.url }}
|
||||
style={detailsStyles.image}
|
||||
onError={(error) => console.log('Image load error:', error.nativeEvent.error)}
|
||||
onLoad={() => console.log('Image loaded successfully')}
|
||||
loadingIndicatorSource={require('@/assets/images/png/placeholder.png')}
|
||||
/>
|
||||
<TouchableOpacity
|
||||
style={[detailsStyles.circleMarker, selectedImages.includes(item?.id || item?.video?.id) ? detailsStyles.circleMarkerSelected : ""]}
|
||||
onPress={() => {
|
||||
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) ? <YesSvg width={16} height={16} /> : ""}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
<View style={detailsStyles.footer}>
|
||||
<TouchableOpacity
|
||||
style={detailsStyles.continueButton}
|
||||
onPress={async () => {
|
||||
// 如果用户没有选择 则为选择全部
|
||||
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}
|
||||
>
|
||||
<ThemedText style={detailsStyles.continueButtonText}>
|
||||
{t('ask.continueAsking', { ns: 'ask' })}
|
||||
</ThemedText>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
159
components/ask/singleContentModel.tsx
Normal file
159
components/ask/singleContentModel.tsx
Normal file
@ -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<React.SetStateAction<{ visible: boolean, data: Video | MaterialItem }>>;
|
||||
}
|
||||
const SingleContentModel = ({ modalVisible, setModalVisible }: SingleContentModelProps) => {
|
||||
const isVideo = (data: Video | MaterialItem): data is Video => {
|
||||
return 'video' in data;
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
animationType="fade"
|
||||
transparent={true}
|
||||
visible={modalVisible.visible}
|
||||
onRequestClose={() => {
|
||||
setModalVisible({ visible: false, data: {} as Video | MaterialItem });
|
||||
}}>
|
||||
<View style={styles.centeredView}>
|
||||
<TouchableOpacity
|
||||
style={styles.background}
|
||||
onPress={() => {
|
||||
setModalVisible({ visible: false, data: {} as Video | MaterialItem })
|
||||
}}
|
||||
/>
|
||||
<TouchableOpacity style={styles.modalView} onPress={() => setModalVisible({ visible: false, data: {} as Video | MaterialItem })}>
|
||||
{isVideo(modalVisible.data) ? (
|
||||
// 视频播放器
|
||||
<TouchableOpacity
|
||||
activeOpacity={1}
|
||||
style={{
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
maxHeight: "60%",
|
||||
}}
|
||||
onPress={() => setModalVisible({ visible: false, data: {} as Video | MaterialItem })}
|
||||
>
|
||||
<VideoPlayer
|
||||
videoUrl={modalVisible.data.video.file_info.url}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
alignSelf: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
onPress={() => setModalVisible({ visible: false, data: {} as Video | MaterialItem })}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
) : (
|
||||
// 图片预览
|
||||
<TouchableOpacity
|
||||
activeOpacity={1}
|
||||
onPress={() => setModalVisible({ visible: false, data: {} as Video | MaterialItem })}
|
||||
style={styles.imageContainer}
|
||||
>
|
||||
<Image
|
||||
source={{ uri: modalVisible.data.preview_file_info?.url }}
|
||||
style={styles.fullWidthImage}
|
||||
resizeMode="contain"
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
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
|
||||
Loading…
x
Reference in New Issue
Block a user