|
|
@ -1,4 +1,10 @@ |
|
|
|
import React, { useState, useEffect, useCallback, useRef, useMemo } from "react"; |
|
|
|
import React, { |
|
|
|
|
|
|
|
useState, |
|
|
|
|
|
|
|
useEffect, |
|
|
|
|
|
|
|
useCallback, |
|
|
|
|
|
|
|
useRef, |
|
|
|
|
|
|
|
useMemo, |
|
|
|
|
|
|
|
} from "react"; |
|
|
|
import { |
|
|
|
import { |
|
|
|
View, |
|
|
|
View, |
|
|
|
Text, |
|
|
|
Text, |
|
|
@ -17,7 +23,11 @@ import Ionicons from "@expo/vector-icons/Ionicons"; |
|
|
|
import { useNavigation, useRoute } from "@react-navigation/native"; |
|
|
|
import { useNavigation, useRoute } from "@react-navigation/native"; |
|
|
|
import { NativeStackNavigationProp } from "@react-navigation/native-stack"; |
|
|
|
import { NativeStackNavigationProp } from "@react-navigation/native-stack"; |
|
|
|
import { RouteProp } from "@react-navigation/native"; |
|
|
|
import { RouteProp } from "@react-navigation/native"; |
|
|
|
import { productApi, type Product, ProductParams } from "../services/api/productApi"; |
|
|
|
import { |
|
|
|
|
|
|
|
productApi, |
|
|
|
|
|
|
|
type Product, |
|
|
|
|
|
|
|
ProductParams, |
|
|
|
|
|
|
|
} from "../services/api/productApi"; |
|
|
|
import { useTranslation } from "react-i18next"; |
|
|
|
import { useTranslation } from "react-i18next"; |
|
|
|
import isSmallScreen from "../utils/isSmallScreen"; |
|
|
|
import isSmallScreen from "../utils/isSmallScreen"; |
|
|
|
import { Svg, Path } from "react-native-svg"; |
|
|
|
import { Svg, Path } from "react-native-svg"; |
|
|
@ -230,113 +240,152 @@ export const ImageSearchResultScreen = ({ |
|
|
|
const [isFilterVisible, setIsFilterVisible] = useState(false); |
|
|
|
const [isFilterVisible, setIsFilterVisible] = useState(false); |
|
|
|
const [sortOrder, setSortOrder] = useState<"asc" | "desc" | null>(null); |
|
|
|
const [sortOrder, setSortOrder] = useState<"asc" | "desc" | null>(null); |
|
|
|
const [sortField, setSortField] = useState<"price" | "time">("price"); |
|
|
|
const [sortField, setSortField] = useState<"price" | "time">("price"); |
|
|
|
const [activeTab, setActiveTab] = useState<"default" | "volume" | "price">("default"); |
|
|
|
const [activeTab, setActiveTab] = useState<"default" | "volume" | "price">( |
|
|
|
|
|
|
|
"default" |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
// 获取初始图片URI
|
|
|
|
// 获取初始图片URI
|
|
|
|
const imageUri = useMemo(() => { |
|
|
|
const imageUri = useMemo(() => { |
|
|
|
console.log('获取图片URI', route.params?.image); |
|
|
|
console.log("获取图片URI", route.params?.image); |
|
|
|
return route.params?.image || null; |
|
|
|
return route.params?.image || null; |
|
|
|
}, [route.params?.image]); |
|
|
|
}, [route.params?.image]); |
|
|
|
|
|
|
|
|
|
|
|
// 将图片URI转换为FormData
|
|
|
|
// 将图片URI转换为base64
|
|
|
|
const uriToFormData = async (uri: string) => { |
|
|
|
const uriToBase64 = async (uri: string): Promise<string> => { |
|
|
|
try { |
|
|
|
try { |
|
|
|
console.log('开始转换图片', uri); |
|
|
|
console.log("开始转换图片为base64", uri); |
|
|
|
const formData = new FormData(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const filename = uri.split("/").pop() || "image.jpg"; |
|
|
|
|
|
|
|
const match = /\.(\w+)$/.exec(filename); |
|
|
|
|
|
|
|
const type = match ? `image/${match[1]}` : "image/jpeg"; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const imageUri = Platform.OS === "ios" ? uri.replace("file://", "") : uri; |
|
|
|
const imageUri = Platform.OS === "ios" ? uri.replace("file://", "") : uri; |
|
|
|
|
|
|
|
|
|
|
|
const imageFetchResponse = await fetch(imageUri); |
|
|
|
const imageFetchResponse = await fetch(imageUri); |
|
|
|
const imageBlob = await imageFetchResponse.blob(); |
|
|
|
const imageBlob = await imageFetchResponse.blob(); |
|
|
|
|
|
|
|
|
|
|
|
formData.append("image", imageBlob, filename); |
|
|
|
// 转换为base64
|
|
|
|
|
|
|
|
const reader = new FileReader(); |
|
|
|
console.log('图片转换完成'); |
|
|
|
const base64Promise = new Promise<string>((resolve, reject) => { |
|
|
|
return formData; |
|
|
|
reader.onload = () => { |
|
|
|
|
|
|
|
const result = reader.result as string; |
|
|
|
|
|
|
|
resolve(result); |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
reader.onerror = reject; |
|
|
|
|
|
|
|
reader.readAsDataURL(imageBlob); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const base64Data = await base64Promise; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 记录图片大小信息
|
|
|
|
|
|
|
|
console.log( |
|
|
|
|
|
|
|
"原始Blob大小:", |
|
|
|
|
|
|
|
imageBlob.size, |
|
|
|
|
|
|
|
"bytes =", |
|
|
|
|
|
|
|
(imageBlob.size / 1024).toFixed(2), |
|
|
|
|
|
|
|
"KB" |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
console.log("Base64数据长度:", base64Data.length, "字符"); |
|
|
|
|
|
|
|
console.log( |
|
|
|
|
|
|
|
"Base64数据大小:", |
|
|
|
|
|
|
|
(base64Data.length * 0.75).toFixed(0), |
|
|
|
|
|
|
|
"bytes ≈", |
|
|
|
|
|
|
|
((base64Data.length * 0.75) / 1024).toFixed(2), |
|
|
|
|
|
|
|
"KB" |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 提取base64数据部分(去掉data:image/jpeg;base64,前缀)
|
|
|
|
|
|
|
|
const base64String = base64Data.split(",")[1]; |
|
|
|
|
|
|
|
console.log("纯Base64字符串长度:", base64String.length); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
console.log("图片转换为base64完成"); |
|
|
|
|
|
|
|
return base64String; |
|
|
|
} catch (error) { |
|
|
|
} catch (error) { |
|
|
|
console.error('图片转换出错:', error); |
|
|
|
console.error("图片转换base64出错:", error); |
|
|
|
throw error; |
|
|
|
throw error; |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// 搜索图片
|
|
|
|
// 搜索图片
|
|
|
|
const searchByImage = async (uri: string) => { |
|
|
|
const searchByImage = async (uri: string) => { |
|
|
|
if (!uri) { |
|
|
|
if (!uri) { |
|
|
|
console.log('没有有效的图片URI'); |
|
|
|
console.log("没有有效的图片URI"); |
|
|
|
setLoading(false); |
|
|
|
setLoading(false); |
|
|
|
setShowSkeleton(false); |
|
|
|
setShowSkeleton(false); |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
try { |
|
|
|
console.log('开始搜索图片:', uri); |
|
|
|
console.log("开始搜索图片:", uri); |
|
|
|
|
|
|
|
console.log("用户信息:", userStore.user); |
|
|
|
setLoading(true); |
|
|
|
setLoading(true); |
|
|
|
setShowSkeleton(true); |
|
|
|
setShowSkeleton(true); |
|
|
|
|
|
|
|
|
|
|
|
const formData = await uriToFormData(uri); |
|
|
|
const base64String = await uriToBase64(uri); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const userId = userStore.user?.user_id || null; |
|
|
|
const data = { |
|
|
|
const data = { |
|
|
|
formData: formData as FormData, |
|
|
|
image_base64: base64String, |
|
|
|
user_id: userStore.user?.user_id, |
|
|
|
user_id: userId, |
|
|
|
type: 'formData', |
|
|
|
|
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
console.log('调用图片搜索API'); |
|
|
|
console.log("调用图片搜索API,用户ID:", userId); |
|
|
|
const response = await productApi.searchByImage(data); |
|
|
|
const response = await productApi.searchByImage(data); |
|
|
|
|
|
|
|
|
|
|
|
console.log('图片搜索完成,返回数据:', JSON.stringify(response)); |
|
|
|
console.log("图片搜索完成,返回数据:", JSON.stringify(response)); |
|
|
|
|
|
|
|
|
|
|
|
// 确保我们有有效的产品数组
|
|
|
|
// 确保我们有有效的产品数组
|
|
|
|
const productList = Array.isArray(response) ? response : []; |
|
|
|
const productList = Array.isArray(response) ? response : []; |
|
|
|
|
|
|
|
|
|
|
|
setProducts(productList); |
|
|
|
setProducts(productList); |
|
|
|
setOriginalProducts(productList); |
|
|
|
setOriginalProducts(productList); |
|
|
|
|
|
|
|
|
|
|
|
// 立即更新状态,无需等待
|
|
|
|
// 立即更新状态,无需等待
|
|
|
|
setLoading(false); |
|
|
|
setLoading(false); |
|
|
|
setShowSkeleton(false); |
|
|
|
setShowSkeleton(false); |
|
|
|
} catch (error) { |
|
|
|
} catch (error: any) { |
|
|
|
console.error('图片搜索出错:', error); |
|
|
|
console.error("图片搜索出错:", error); |
|
|
|
|
|
|
|
// 更详细的错误处理
|
|
|
|
|
|
|
|
if (error.code === "ERR_NETWORK") { |
|
|
|
|
|
|
|
console.error("网络连接错误,请检查:"); |
|
|
|
|
|
|
|
console.error("1. 手机是否连接到同一WiFi网络"); |
|
|
|
|
|
|
|
console.error("2. 开发服务器是否正在运行"); |
|
|
|
|
|
|
|
console.error("3. 后端API服务是否正常"); |
|
|
|
|
|
|
|
} |
|
|
|
setProducts([]); |
|
|
|
setProducts([]); |
|
|
|
setOriginalProducts([]); |
|
|
|
setOriginalProducts([]); |
|
|
|
setLoading(false); |
|
|
|
setLoading(false); |
|
|
|
setShowSkeleton(false); |
|
|
|
setShowSkeleton(false); |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// 只在组件加载时执行一次搜索
|
|
|
|
// 只在组件加载时执行一次搜索
|
|
|
|
useEffect(() => { |
|
|
|
useEffect(() => { |
|
|
|
console.log('imageUri', imageUri); |
|
|
|
console.log("imageUri", imageUri); |
|
|
|
|
|
|
|
console.log(imageUri); |
|
|
|
|
|
|
|
|
|
|
|
// 重置状态,确保每次都是新的开始
|
|
|
|
// 重置状态,确保每次都是新的开始
|
|
|
|
imageProcessed.current = false; |
|
|
|
imageProcessed.current = false; |
|
|
|
setLoading(true); |
|
|
|
setLoading(true); |
|
|
|
setShowSkeleton(true); |
|
|
|
setShowSkeleton(true); |
|
|
|
|
|
|
|
|
|
|
|
// 如果没有图片URI,立即结束加载状态
|
|
|
|
// 如果没有图片URI,立即结束加载状态
|
|
|
|
if (!imageUri) { |
|
|
|
if (!imageUri) { |
|
|
|
console.log('没有图片URI,结束加载'); |
|
|
|
console.log("没有图片URI,结束加载"); |
|
|
|
setLoading(false); |
|
|
|
setLoading(false); |
|
|
|
setShowSkeleton(false); |
|
|
|
setShowSkeleton(false); |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 如果已经处理过图片,则跳过
|
|
|
|
// 如果已经处理过图片,则跳过
|
|
|
|
if (imageProcessed.current) { |
|
|
|
if (imageProcessed.current) { |
|
|
|
console.log('已处理过图片,跳过'); |
|
|
|
console.log("已处理过图片,跳过"); |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
console.log('首次加载,处理图片', imageUri); |
|
|
|
console.log("首次加载,处理图片", imageUri); |
|
|
|
imageProcessed.current = true; |
|
|
|
imageProcessed.current = true; |
|
|
|
|
|
|
|
|
|
|
|
// 执行图片搜索
|
|
|
|
// 执行图片搜索
|
|
|
|
searchByImage(imageUri); |
|
|
|
searchByImage(imageUri); |
|
|
|
}, [imageUri]); |
|
|
|
}, [imageUri]); |
|
|
|
|
|
|
|
|
|
|
|
// 搜索产品的API调用
|
|
|
|
// 搜索产品的API调用
|
|
|
|
const searchProducts = useCallback( |
|
|
|
const searchProducts = useCallback( |
|
|
|
async (keyword: string, isLoadMore = false) => { |
|
|
|
async (keyword: string, isLoadMore = false) => { |
|
|
@ -356,7 +405,7 @@ export const ImageSearchResultScreen = ({ |
|
|
|
language: "en", |
|
|
|
language: "en", |
|
|
|
user_id: userStore.user?.user_id, |
|
|
|
user_id: userStore.user?.user_id, |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const res = await productApi.getSearchProducts(params); |
|
|
|
const res = await productApi.getSearchProducts(params); |
|
|
|
if (isLoadMore) { |
|
|
|
if (isLoadMore) { |
|
|
|
setProducts((prev) => [...prev, ...res.products]); |
|
|
|
setProducts((prev) => [...prev, ...res.products]); |
|
|
@ -384,7 +433,7 @@ export const ImageSearchResultScreen = ({ |
|
|
|
}, |
|
|
|
}, |
|
|
|
[userStore.user] |
|
|
|
[userStore.user] |
|
|
|
); |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
// 处理搜索提交
|
|
|
|
// 处理搜索提交
|
|
|
|
const handleSearch = useCallback(() => { |
|
|
|
const handleSearch = useCallback(() => { |
|
|
|
if (searchText.trim()) { |
|
|
|
if (searchText.trim()) { |
|
|
@ -398,12 +447,12 @@ export const ImageSearchResultScreen = ({ |
|
|
|
searchProducts(searchText.trim()); |
|
|
|
searchProducts(searchText.trim()); |
|
|
|
} |
|
|
|
} |
|
|
|
}, [searchText, searchProducts]); |
|
|
|
}, [searchText, searchProducts]); |
|
|
|
|
|
|
|
|
|
|
|
// 切换筛选器显示状态
|
|
|
|
// 切换筛选器显示状态
|
|
|
|
const toggleFilter = useCallback(() => { |
|
|
|
const toggleFilter = useCallback(() => { |
|
|
|
setIsFilterVisible(!isFilterVisible); |
|
|
|
setIsFilterVisible(!isFilterVisible); |
|
|
|
}, [isFilterVisible]); |
|
|
|
}, [isFilterVisible]); |
|
|
|
|
|
|
|
|
|
|
|
// 处理点击产品
|
|
|
|
// 处理点击产品
|
|
|
|
const handleProductPress = useCallback( |
|
|
|
const handleProductPress = useCallback( |
|
|
|
(product: Product) => { |
|
|
|
(product: Product) => { |
|
|
@ -414,26 +463,24 @@ export const ImageSearchResultScreen = ({ |
|
|
|
}, |
|
|
|
}, |
|
|
|
[navigation] |
|
|
|
[navigation] |
|
|
|
); |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
// 返回上一页
|
|
|
|
// 返回上一页
|
|
|
|
const goBack = useCallback(() => { |
|
|
|
const goBack = useCallback(() => { |
|
|
|
navigation.goBack(); |
|
|
|
navigation.goBack(); |
|
|
|
}, [navigation]); |
|
|
|
}, [navigation]); |
|
|
|
|
|
|
|
|
|
|
|
// 渲染列表为空时的组件
|
|
|
|
// 渲染列表为空时的组件
|
|
|
|
const renderEmptyList = useCallback( |
|
|
|
const renderEmptyList = useCallback( |
|
|
|
() => ( |
|
|
|
() => ( |
|
|
|
<View style={styles.emptyContainer}> |
|
|
|
<View style={styles.emptyContainer}> |
|
|
|
<IconComponent name="image-outline" size={48} color="#ccc" /> |
|
|
|
<IconComponent name="image-outline" size={48} color="#ccc" /> |
|
|
|
<Text style={styles.emptyText}> |
|
|
|
<Text style={styles.emptyText}>{t("noResults")}</Text> |
|
|
|
{t("noResults")} |
|
|
|
|
|
|
|
</Text> |
|
|
|
|
|
|
|
<Text style={styles.emptySubtext}>{t("tryDifferentImage")}</Text> |
|
|
|
<Text style={styles.emptySubtext}>{t("tryDifferentImage")}</Text> |
|
|
|
</View> |
|
|
|
</View> |
|
|
|
), |
|
|
|
), |
|
|
|
[t] |
|
|
|
[t] |
|
|
|
); |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
// 渲染产品项
|
|
|
|
// 渲染产品项
|
|
|
|
const renderProductItem = useCallback( |
|
|
|
const renderProductItem = useCallback( |
|
|
|
({ item }: { item: Product }) => ( |
|
|
|
({ item }: { item: Product }) => ( |
|
|
@ -446,29 +493,29 @@ export const ImageSearchResultScreen = ({ |
|
|
|
), |
|
|
|
), |
|
|
|
[handleProductPress, t, userStore] |
|
|
|
[handleProductPress, t, userStore] |
|
|
|
); |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
// 创建产品列表项的key提取器
|
|
|
|
// 创建产品列表项的key提取器
|
|
|
|
const keyExtractor = useCallback( |
|
|
|
const keyExtractor = useCallback( |
|
|
|
(item: Product, index: number) => `${item.offer_id}-${index}`, |
|
|
|
(item: Product, index: number) => `${item.offer_id}-${index}`, |
|
|
|
[] |
|
|
|
[] |
|
|
|
); |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
// 处理滚动事件
|
|
|
|
// 处理滚动事件
|
|
|
|
const handleScroll = useCallback((event: any) => { |
|
|
|
const handleScroll = useCallback((event: any) => { |
|
|
|
const offsetY = event.nativeEvent.contentOffset.y; |
|
|
|
const offsetY = event.nativeEvent.contentOffset.y; |
|
|
|
// 当滚动超过屏幕高度的一半时显示回到顶部按钮
|
|
|
|
// 当滚动超过屏幕高度的一半时显示回到顶部按钮
|
|
|
|
setShowBackToTop(offsetY > 300); |
|
|
|
setShowBackToTop(offsetY > 300); |
|
|
|
}, []); |
|
|
|
}, []); |
|
|
|
|
|
|
|
|
|
|
|
// 回到顶部
|
|
|
|
// 回到顶部
|
|
|
|
const scrollToTop = useCallback(() => { |
|
|
|
const scrollToTop = useCallback(() => { |
|
|
|
flatListRef.current?.scrollToOffset({ offset: 0, animated: true }); |
|
|
|
flatListRef.current?.scrollToOffset({ offset: 0, animated: true }); |
|
|
|
}, []); |
|
|
|
}, []); |
|
|
|
|
|
|
|
|
|
|
|
// 渲染骨架屏网格
|
|
|
|
// 渲染骨架屏网格
|
|
|
|
const renderSkeletonGrid = useCallback(() => { |
|
|
|
const renderSkeletonGrid = useCallback(() => { |
|
|
|
const skeletonArray = Array(8).fill(null); |
|
|
|
const skeletonArray = Array(8).fill(null); |
|
|
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
return ( |
|
|
|
<View style={styles.productGrid}> |
|
|
|
<View style={styles.productGrid}> |
|
|
|
<FlatList |
|
|
|
<FlatList |
|
|
@ -482,7 +529,7 @@ export const ImageSearchResultScreen = ({ |
|
|
|
</View> |
|
|
|
</View> |
|
|
|
); |
|
|
|
); |
|
|
|
}, []); |
|
|
|
}, []); |
|
|
|
|
|
|
|
|
|
|
|
// 处理排序
|
|
|
|
// 处理排序
|
|
|
|
const handleSort = useCallback( |
|
|
|
const handleSort = useCallback( |
|
|
|
(field: "price" | "time", order: "asc" | "desc") => { |
|
|
|
(field: "price" | "time", order: "asc" | "desc") => { |
|
|
@ -510,7 +557,7 @@ export const ImageSearchResultScreen = ({ |
|
|
|
}, |
|
|
|
}, |
|
|
|
[] |
|
|
|
[] |
|
|
|
); |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
// 处理标签切换
|
|
|
|
// 处理标签切换
|
|
|
|
const handleTabChange = useCallback( |
|
|
|
const handleTabChange = useCallback( |
|
|
|
(tab: "default" | "volume" | "price") => { |
|
|
|
(tab: "default" | "volume" | "price") => { |
|
|
@ -546,7 +593,7 @@ export const ImageSearchResultScreen = ({ |
|
|
|
}, |
|
|
|
}, |
|
|
|
[handleSort, activeTab, sortOrder, originalProducts, scrollToTop] |
|
|
|
[handleSort, activeTab, sortOrder, originalProducts, scrollToTop] |
|
|
|
); |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
// 渲染列表底部加载更多
|
|
|
|
// 渲染列表底部加载更多
|
|
|
|
const renderFooter = useCallback(() => { |
|
|
|
const renderFooter = useCallback(() => { |
|
|
|
if (!hasMore) |
|
|
|
if (!hasMore) |
|
|
@ -564,7 +611,7 @@ export const ImageSearchResultScreen = ({ |
|
|
|
); |
|
|
|
); |
|
|
|
return <View style={styles.footerSpace} />; |
|
|
|
return <View style={styles.footerSpace} />; |
|
|
|
}, [loadingMore, hasMore, t]); |
|
|
|
}, [loadingMore, hasMore, t]); |
|
|
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
return ( |
|
|
|
<SafeAreaView style={styles.safeArea}> |
|
|
|
<SafeAreaView style={styles.safeArea}> |
|
|
|
<StatusBar barStyle="dark-content" backgroundColor="#fff" /> |
|
|
|
<StatusBar barStyle="dark-content" backgroundColor="#fff" /> |
|
|
@ -604,7 +651,7 @@ export const ImageSearchResultScreen = ({ |
|
|
|
)} |
|
|
|
)} |
|
|
|
</View> |
|
|
|
</View> |
|
|
|
</View> |
|
|
|
</View> |
|
|
|
|
|
|
|
|
|
|
|
{/* 标签筛选 */} |
|
|
|
{/* 标签筛选 */} |
|
|
|
<View style={styles.tabContainer}> |
|
|
|
<View style={styles.tabContainer}> |
|
|
|
<TouchableOpacity |
|
|
|
<TouchableOpacity |
|
|
@ -620,7 +667,7 @@ export const ImageSearchResultScreen = ({ |
|
|
|
activeTab === "default" && styles.activeTabText, |
|
|
|
activeTab === "default" && styles.activeTabText, |
|
|
|
]} |
|
|
|
]} |
|
|
|
> |
|
|
|
> |
|
|
|
{t('default')} |
|
|
|
{t("default")} |
|
|
|
</Text> |
|
|
|
</Text> |
|
|
|
</TouchableOpacity> |
|
|
|
</TouchableOpacity> |
|
|
|
<TouchableOpacity |
|
|
|
<TouchableOpacity |
|
|
@ -636,7 +683,7 @@ export const ImageSearchResultScreen = ({ |
|
|
|
activeTab === "volume" && styles.activeTabText, |
|
|
|
activeTab === "volume" && styles.activeTabText, |
|
|
|
]} |
|
|
|
]} |
|
|
|
> |
|
|
|
> |
|
|
|
{t('volume')} |
|
|
|
{t("volume")} |
|
|
|
</Text> |
|
|
|
</Text> |
|
|
|
</TouchableOpacity> |
|
|
|
</TouchableOpacity> |
|
|
|
<TouchableOpacity |
|
|
|
<TouchableOpacity |
|
|
@ -653,12 +700,14 @@ export const ImageSearchResultScreen = ({ |
|
|
|
activeTab === "price" && styles.activeTabText, |
|
|
|
activeTab === "price" && styles.activeTabText, |
|
|
|
]} |
|
|
|
]} |
|
|
|
> |
|
|
|
> |
|
|
|
{t('price')} |
|
|
|
{t("price")} |
|
|
|
</Text> |
|
|
|
</Text> |
|
|
|
{activeTab === "price" && ( |
|
|
|
{activeTab === "price" && ( |
|
|
|
<View style={styles.tabIcon}> |
|
|
|
<View style={styles.tabIcon}> |
|
|
|
<IconComponent |
|
|
|
<IconComponent |
|
|
|
name={sortOrder === "desc" ? "chevron-down" : "chevron-up"} |
|
|
|
name={ |
|
|
|
|
|
|
|
sortOrder === "desc" ? "chevron-down" : "chevron-up" |
|
|
|
|
|
|
|
} |
|
|
|
size={16} |
|
|
|
size={16} |
|
|
|
color="#000" |
|
|
|
color="#000" |
|
|
|
/> |
|
|
|
/> |
|
|
@ -667,7 +716,7 @@ export const ImageSearchResultScreen = ({ |
|
|
|
</View> |
|
|
|
</View> |
|
|
|
</TouchableOpacity> |
|
|
|
</TouchableOpacity> |
|
|
|
</View> |
|
|
|
</View> |
|
|
|
|
|
|
|
|
|
|
|
{/* 搜索结果 */} |
|
|
|
{/* 搜索结果 */} |
|
|
|
<View style={styles.resultsContainer}> |
|
|
|
<View style={styles.resultsContainer}> |
|
|
|
{/* 搜索结果标题栏和排序选项 */} |
|
|
|
{/* 搜索结果标题栏和排序选项 */} |
|
|
@ -739,7 +788,7 @@ export const ImageSearchResultScreen = ({ |
|
|
|
</View> |
|
|
|
</View> |
|
|
|
<View style={styles.sortDivider} /> |
|
|
|
<View style={styles.sortDivider} /> |
|
|
|
<View style={styles.sortGroup}> |
|
|
|
<View style={styles.sortGroup}> |
|
|
|
<Text style={styles.sortLabel}>{t('time')}:</Text> |
|
|
|
<Text style={styles.sortLabel}>{t("time")}:</Text> |
|
|
|
<View style={styles.sortButtons}> |
|
|
|
<View style={styles.sortButtons}> |
|
|
|
<TouchableOpacity |
|
|
|
<TouchableOpacity |
|
|
|
style={[ |
|
|
|
style={[ |
|
|
@ -888,12 +937,12 @@ const styles = StyleSheet.create({ |
|
|
|
clearButton: { |
|
|
|
clearButton: { |
|
|
|
position: "absolute", |
|
|
|
position: "absolute", |
|
|
|
right: 10, |
|
|
|
right: 10, |
|
|
|
top: '50%', |
|
|
|
top: "50%", |
|
|
|
marginTop: -10, |
|
|
|
marginTop: -10, |
|
|
|
width: 20, |
|
|
|
width: 20, |
|
|
|
height: 20, |
|
|
|
height: 20, |
|
|
|
alignItems: 'center', |
|
|
|
alignItems: "center", |
|
|
|
justifyContent: 'center', |
|
|
|
justifyContent: "center", |
|
|
|
zIndex: 20, |
|
|
|
zIndex: 20, |
|
|
|
}, |
|
|
|
}, |
|
|
|
titleContainer: { |
|
|
|
titleContainer: { |
|
|
@ -1142,4 +1191,4 @@ const styles = StyleSheet.create({ |
|
|
|
backgroundColor: "#EAEAEA", |
|
|
|
backgroundColor: "#EAEAEA", |
|
|
|
borderRadius: 4, |
|
|
|
borderRadius: 4, |
|
|
|
}, |
|
|
|
}, |
|
|
|
});
|
|
|
|
}); |
|
|
|