Browse Source

添加首页图搜

main
Your Name 3 weeks ago
parent
commit
36a355a817
  1. 3
      .npmrc
  2. 4
      app/screens/CartScreen.tsx
  3. 307
      app/screens/HomeScreen.tsx
  4. 49
      app/screens/MemberScreen/MemberIntroduction.tsx
  5. 8
      app/screens/ProductCard.tsx
  6. 23408
      package-lock.json
  7. 4
      package.json
  8. 4029
      yarn.lock

3
.npmrc

@ -0,0 +1,3 @@
legacy-peer-deps=true
strict-peer-dependencies=false
engine-strict=false

4
app/screens/CartScreen.tsx

@ -35,7 +35,7 @@ import useUserStore from "../store/user";
export const CartScreen = () => {
const [cartList, setCartList] = useState<GetCartList[]>([]);
const {
user: { user_id },
user: { user_id,currency },
} = useUserStore();
const [selectedItems, setSelectedItems] = useState<{
[key: string]: boolean;
@ -763,7 +763,7 @@ export const CartScreen = () => {
<View style={styles.productInfoContainer}>
<View style={styles.productInfoContainer}>
<Text style={styles.highlightedText1}>{totalAmount}</Text>
<Text style={styles.priceLabel}>FCFA</Text>
<Text style={styles.priceLabel}>{currency}</Text>
</View>
<TouchableOpacity
style={styles.submitButtonStyle}

307
app/screens/HomeScreen.tsx

@ -9,13 +9,17 @@ import {
Image,
ScrollView,
Modal,
ImageBackground,
RefreshControl,
Dimensions,
Animated,
Platform,
StatusBar,
SafeAreaView,
ViewStyle,
TextStyle,
ImageStyle,
Linking,
Alert,
} from "react-native";
import {
productApi,
@ -35,6 +39,8 @@ import CloseIcon from "../components/CloseIcon";
import CheckmarkIcon from "../components/CheckmarkIcon";
import { getSubjectTransLanguage } from "../utils/languageUtils";
import useUserStore from "../store/user";
import * as ImagePicker from 'expo-image-picker';
import * as FileSystem from 'expo-file-system';
// 为图标定义类型
type IconProps = {
name: string;
@ -200,12 +206,102 @@ const ProductSkeleton = React.memo(() => {
);
});
// Define the styles type to fix TypeScript errors
type StylesType = {
safeArea: ViewStyle;
safeAreaContent: ViewStyle;
container: ViewStyle;
swpImg: ImageStyle;
searchOverlay: ViewStyle;
searchBar: ViewStyle;
searchPlaceholder: TextStyle;
cameraButton: ViewStyle;
bannerContainer: ViewStyle;
leftContainer: ViewStyle;
leftTopItem: ViewStyle;
leftBottomItem: ViewStyle;
rightContainer: ViewStyle;
bannerIcon: ImageStyle;
bigbannerIcon: ImageStyle;
category: ViewStyle;
categoryScrollContainer: ViewStyle;
categoryScroll: ViewStyle;
categoryItem: ViewStyle;
categoryItemActive: ViewStyle;
categoryText: TextStyle;
categoryTextActive: TextStyle;
swiperContainer: ViewStyle;
swiper: ViewStyle;
dot: ViewStyle;
activeDot: ViewStyle;
slide: ViewStyle;
slideImage: ImageStyle;
fadeGradient: ViewStyle;
categoryArrowContainer: ViewStyle;
modalOverlay: ViewStyle;
modalContent: ViewStyle;
modalHeader: ViewStyle;
modalTitleContainer: ViewStyle;
modalTitle: TextStyle;
closeButton: ViewStyle;
closeButtonText: TextStyle;
modalScrollView: ViewStyle;
categoryModalItem: ViewStyle;
categoryModalText: TextStyle;
selectedCategoryText: TextStyle;
subcategoryContainer: ViewStyle;
subcategoryScroll: ViewStyle;
subcategoryContent: ViewStyle;
subcategoryItem: ViewStyle;
subcategoryImagePlaceholder: ViewStyle;
subcategoryText: TextStyle;
productContainer: ViewStyle;
productCardList: ViewStyle;
productCardGroup: ViewStyle;
beautyProductCard1: ViewStyle;
beautyCardContainer1: ViewStyle;
vipButtonContainer: ViewStyle;
vipButton: ViewStyle;
vipButtonText: TextStyle;
vipLabelBold: TextStyle;
beautyProductCard: ViewStyle;
beautyProductTitle: TextStyle;
beautyProductInfoRow: ViewStyle;
flexRowCentered: ViewStyle;
priceContainer: ViewStyle;
highlightedText: TextStyle;
highlightedText1: TextStyle;
priceContainer1: ViewStyle;
priceLabel1: TextStyle;
beautySalesInfo: TextStyle;
indicatorContainer: ViewStyle;
indicator: ViewStyle;
activeIndicator: ViewStyle;
inactiveIndicator: ViewStyle;
skeletonContainer: ViewStyle;
skeletonImage: ViewStyle;
skeletonTitle: ViewStyle;
skeletonPrice: ViewStyle;
skeletonSales: ViewStyle;
shimmer: ViewStyle;
imagePlaceholder: ViewStyle;
productImage: ImageStyle;
imagePickerOverlay: ViewStyle;
imagePickerContent: ViewStyle;
imagePickerOption: ViewStyle;
imagePickerText: TextStyle;
imagePickerDivider: ViewStyle;
imagePickerCancelButton: ViewStyle;
imagePickerCancelText: TextStyle;
};
export const HomeScreen = () => {
const [activeIndex, setActiveIndex] = useState(0);
const screenWidth = Dimensions.get("window").width;
const navigation = useNavigation<NativeStackNavigationProp<any>>();
const { t } = useTranslation();
const [showCategoryModal, setShowCategoryModal] = useState(false);
const [showImagePickerModal, setShowImagePickerModal] = useState(false);
const [selectedCategory, setSelectedCategory] = useState("Bijoux");
const [selectedHorizontalCategory, setSelectedHorizontalCategory] =
useState("Tous");
@ -237,6 +333,7 @@ export const HomeScreen = () => {
add: "CompanyScreen",
},
];
const [galleryUsed, setGalleryUsed] = useState(false); // 标记是否使用过相册
const getProductData = async () => {
setLoading(true); // 开始加载,显示骨架屏
try {
@ -369,7 +466,7 @@ export const HomeScreen = () => {
resizeMode="cover"
/>
) : (
<View style={[styles.productImage, styles.imagePlaceholder]}>
<View style={[styles.productImage as any, styles.imagePlaceholder]}>
<IconComponent name="image-outline" size={24} color="#999" />
</View>
)}
@ -438,6 +535,109 @@ export const HomeScreen = () => {
);
}, []);
// 清理expo-image-picker临时文件
const cleanupImagePickerCache = async () => {
try {
// 相册选择后清理临时缓存
const cacheDir = `${FileSystem.cacheDirectory}ImagePicker`;
await FileSystem.deleteAsync(cacheDir, { idempotent: true });
console.log('已清理ImagePicker缓存');
// 立即重置状态,无需用户干预
setGalleryUsed(false);
} catch (error) {
console.log('清理缓存错误', error);
}
};
// 处理从相册选择
const handleChooseFromGallery = useCallback(async () => {
console.log('handleChooseFromGallery');
setShowImagePickerModal(false);
// 等待模态窗关闭后再执行
setTimeout(async () => {
try {
// 请求相册权限
const permissionResult = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (permissionResult.status !== 'granted') {
console.log('相册权限被拒绝');
return;
}
// 打开相册
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
if (!result.canceled && result.assets && result.assets.length > 0) {
console.log('相册选择成功:', result.assets[0].uri);
// 这里可以添加后续处理代码,如图片上传等
// 相册选择完成后,立即清理缓存并重置状态
await cleanupImagePickerCache();
}
} catch (error: any) {
console.error('相册错误:', error);
// 出错时也清理缓存
await cleanupImagePickerCache();
}
}, 500);
}, []);
// 处理相机拍照 - 简化版本,不再需要处理galleryUsed
const handleTakePhoto = useCallback(async () => {
console.log('handleTakePhoto');
setShowImagePickerModal(false);
// 等待模态窗关闭后再执行
setTimeout(async () => {
try {
const permissionResult = await ImagePicker.requestCameraPermissionsAsync();
if (permissionResult.status !== 'granted') {
console.log('相机权限被拒绝');
return;
}
const result = await ImagePicker.launchCameraAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
console.log(result);
if (!result.canceled && result.assets && result.assets.length > 0) {
console.log('拍照成功:', result.assets[0].uri);
// 这里可以添加后续处理代码,如图片上传等
}
// 使用后清理缓存
await cleanupImagePickerCache();
} catch (error: any) {
console.error('相机错误:', error);
// 出错时也清理缓存
await cleanupImagePickerCache();
}
}, 500);
}, []);
// 重置应用状态函数
const resetAppState = useCallback(() => {
// 重置标记
setGalleryUsed(false);
// 清理缓存
cleanupImagePickerCache();
// 提示用户
Alert.alert('已重置', '现在您可以使用相机功能了');
}, []);
// 渲染列表头部内容
const renderHeader = () => (
<>
@ -495,7 +695,10 @@ export const HomeScreen = () => {
>
<IconComponent name="search-outline" size={20} color="#999" />
<Text style={styles.searchPlaceholder}></Text>
<TouchableOpacity style={styles.cameraButton}>
<TouchableOpacity
style={styles.cameraButton}
onPress={() => setShowImagePickerModal(true)}
>
<IconComponent name="camera-outline" size={24} color="#333" />
</TouchableOpacity>
</TouchableOpacity>
@ -710,12 +913,65 @@ export const HomeScreen = () => {
</View>
</View>
</Modal>
{/* Image Picker Modal */}
<Modal
visible={showImagePickerModal}
animationType="slide"
transparent={true}
onRequestClose={() => setShowImagePickerModal(false)}
>
<TouchableOpacity
style={styles.imagePickerOverlay}
activeOpacity={1}
onPress={() => setShowImagePickerModal(false)}
>
<View style={styles.imagePickerContent}>
{!galleryUsed ? (
// 正常状态,显示相机选项
<TouchableOpacity
style={styles.imagePickerOption}
onPress={handleTakePhoto}
>
<IconComponent name="camera-outline" size={24} color="#333" />
<Text style={styles.imagePickerText}></Text>
</TouchableOpacity>
) : (
// 已使用相册状态,显示重置选项
<TouchableOpacity
style={styles.imagePickerOption}
onPress={resetAppState}
>
<IconComponent name="refresh-outline" size={24} color="#333" />
<Text style={styles.imagePickerText}></Text>
</TouchableOpacity>
)}
<View style={styles.imagePickerDivider} />
<TouchableOpacity
style={styles.imagePickerOption}
onPress={handleChooseFromGallery}
>
<IconComponent name="images-outline" size={24} color="#333" />
<Text style={styles.imagePickerText}></Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.imagePickerCancelButton}
onPress={() => setShowImagePickerModal(false)}
>
<Text style={styles.imagePickerCancelText}></Text>
</TouchableOpacity>
</View>
</TouchableOpacity>
</Modal>
</View>
</View>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
const styles = StyleSheet.create<StylesType>({
safeArea: {
flex: 1,
backgroundColor: '#fff',
@ -1013,8 +1269,8 @@ const styles = StyleSheet.create({
zIndex: 2,
},
vipButton: {
width: widthUtils(30, 66).width,
height: widthUtils(30, 66).height,
width: widthUtils(30, 60).width,
height: widthUtils(30, 60).height,
justifyContent: "center",
alignItems: "center",
backgroundColor: "#3b3b3b",
@ -1157,4 +1413,43 @@ const styles = StyleSheet.create({
height: "100%",
borderRadius: 10,
},
// Image Picker Modal Styles
imagePickerOverlay: {
flex: 1,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
justifyContent: 'flex-end',
},
imagePickerContent: {
backgroundColor: '#fff',
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
paddingTop: 20,
},
imagePickerOption: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 16,
paddingHorizontal: 20,
},
imagePickerText: {
fontSize: fontSize(16),
marginLeft: 12,
color: '#333',
},
imagePickerDivider: {
height: 1,
backgroundColor: '#f0f0f0',
marginHorizontal: 20,
},
imagePickerCancelButton: {
alignItems: 'center',
paddingVertical: 16,
marginTop: 8,
borderTopWidth: 1,
borderTopColor: '#f0f0f0',
},
imagePickerCancelText: {
fontSize: fontSize(16),
color: '#999',
},
});

49
app/screens/MemberScreen/MemberIntroduction.tsx

@ -16,6 +16,7 @@ import BookLabIcon from "../../components/BookLabIcon";
import { useNavigation } from "@react-navigation/native";
import { NativeStackNavigationProp } from "@react-navigation/native-stack";
import useUserStore from "../../store/user";
import BackIcon from "../../components/BackIcon";
type RootStackParamList = {
Balance: undefined;
};
@ -26,7 +27,8 @@ export const MemberIntroduction = () => {
return (
<View style={styles.container}>
<ScrollView
bounces={false} overScrollMode="never"
bounces={false}
overScrollMode="never"
style={styles.scrollView}
contentContainerStyle={{ flexGrow: 1 }}
showsVerticalScrollIndicator={false}
@ -36,12 +38,24 @@ export const MemberIntroduction = () => {
style={styles.timecardWidget}
source={require("../../../assets/img/制作背景图 (1) (2).png")}
resizeMode="stretch"
></ImageBackground>
>
<View style={styles.titleContainer}>
<View style={styles.backIconContainer}>
<TouchableOpacity onPress={() => navigation.goBack()}>
<BackIcon size={20} color="#fff"/>
</TouchableOpacity>
</View>
<Text style={styles.titleHeading}></Text>
</View>
</ImageBackground>
<View style={styles.VipContainer}>
<View style={styles.VipContainerTop}>
<View style={styles.VipContainerBox}>
<View style={styles.Vip}>
<Text style={styles.VipText}>VIP {userStore.user?.vip_level || 0}</Text>
<Text style={styles.VipText}>
VIP {userStore.user?.vip_level || 0}
</Text>
</View>
<View style={styles.VipLine}>
<View style={styles.lineText}>
@ -509,6 +523,35 @@ const styles = StyleSheet.create({
width: "100%",
height: "100%",
},
titleContainer: {
width: "100%",
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
position: "absolute",
left: 0,
right: 0,
top: 10,
height: 48,
zIndex: 10,
paddingLeft: 19,
paddingRight: 19,
paddingTop: 10,
paddingBottom: 10,
},
backIconContainer: {
position: "absolute",
left: 19,
top: 10,
zIndex: 11,
},
titleHeading: {
fontWeight: "600",
fontSize: 20,
lineHeight: 16,
fontFamily: "PingFang SC",
color: "#fff",
},
VipContainer: {
width: widthUtils(400, 400).width,
height: widthUtils(200, 200).height,

8
app/screens/ProductCard.tsx

@ -213,6 +213,7 @@ const ProductCard: React.FC<ProductCardProps> = ({
return (
<View style={styles.wrapper}>
<ScrollView style={{flex: 1}}>
<View style={styles.container}>
<View style={styles.productInfo}>
<View style={styles.productBigImgBox}>
@ -231,7 +232,7 @@ const ProductCard: React.FC<ProductCardProps> = ({
<View style={styles.priceInfoBox}>
<View style={styles.price}>
<Text style={styles.priceInfoText}>{price}</Text>
<Text style={styles.priceInfoTextCon}>FCFA</Text>
<Text style={styles.priceInfoTextCon}>{currency}</Text>
</View>
{vip_level > 0 && (
<>
@ -281,7 +282,7 @@ const ProductCard: React.FC<ProductCardProps> = ({
<Text style={styles.priceListBoxText}>
{item.price}
</Text>
<Text style={styles.priceListBoxTextCon}>FCFA</Text>
<Text style={styles.priceListBoxTextCon}>{currency}</Text>
</View>
<View style={styles.priceListBoxPie}>
<Text style={styles.priceListBoxListText}>
@ -726,6 +727,7 @@ const ProductCard: React.FC<ProductCardProps> = ({
</View>
)}
</View>
</ScrollView>
<View style={styles.fixedBottomView}>
<View style={styles.fixedBottomViewBox}>
@ -738,7 +740,7 @@ const ProductCard: React.FC<ProductCardProps> = ({
<View style={styles.fixedBottomViewBoxRight}>
<Text style={styles.fixedBottomViewBoxRightText}>:</Text>
<Text style={styles.fixedBottomViewBoxPriceText}>
{totalPrice.toFixed(2)} FCFA
{totalPrice.toFixed(2)} {currency}
</Text>
</View>
</View>

23408
package-lock.json generated

File diff suppressed because it is too large Load Diff

4
package.json

@ -26,6 +26,7 @@
"expo-auth-session": "~6.0.3",
"expo-build-properties": "~0.13.3",
"expo-image": "~2.0.7",
"expo-image-picker": "^16.1.4",
"expo-linear-gradient": "~14.0.2",
"expo-localization": "^16.0.1",
"expo-random": "^14.0.1",
@ -72,5 +73,8 @@
"react-native-svg-transformer": "^1.5.0",
"typescript": "^5.3.3"
},
"resolutions": {
"expo": "~52.0.41"
},
"private": true
}

4029
yarn.lock

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save