Browse Source

商品详情页修改

main
Mac 1 month ago
parent
commit
24fa0d0646
  1. 12
      app/screens/HomeScreen.tsx
  2. 10
      app/screens/LoginScreen.tsx
  3. 1803
      app/screens/ProductCard.tsx
  4. 17
      app/screens/ProductDetailScreen.tsx
  5. 6
      app/screens/productStatus/OrderDatails.tsx
  6. 4
      app/screens/productStatus/Progress.tsx
  7. 364
      app/store/productCart.ts
  8. BIN
      assets/img/vip1.png
  9. BIN
      assets/img/折扣VIP1 (1).png

12
app/screens/HomeScreen.tsx

@ -535,7 +535,7 @@ const styles = StyleSheet.create({
searchPlaceholder: { searchPlaceholder: {
flex: 1, flex: 1,
marginLeft: 8, marginLeft: 8,
fontSize: 16, fontSize: fontSize(16),
}, },
cameraButton: { cameraButton: {
padding: 5, padding: 5,
@ -808,7 +808,7 @@ const styles = StyleSheet.create({
marginTop: 9, marginTop: 9,
}, },
beautyProductTitle: { beautyProductTitle: {
fontSize: 14, fontSize: fontSize(14),
fontWeight: "600", fontWeight: "600",
color: "black", color: "black",
lineHeight: 18, lineHeight: 18,
@ -823,25 +823,25 @@ const styles = StyleSheet.create({
}, },
highlightedText: { highlightedText: {
fontWeight: "700", fontWeight: "700",
fontSize: 24, fontSize: fontSize(24),
color: "#ff5100", color: "#ff5100",
}, },
highlightedText1: { highlightedText1: {
fontWeight: "700", fontWeight: "700",
fontSize: 14, fontSize: fontSize(14),
color: "#ff5100", color: "#ff5100",
}, },
priceContainer1: { priceContainer1: {
marginLeft: 5.75, marginLeft: 5.75,
}, },
priceLabel1: { priceLabel1: {
fontSize: 12, fontSize: fontSize(12),
fontWeight: "600", fontWeight: "600",
color: "#9a9a9a", color: "#9a9a9a",
}, },
beautySalesInfo: { beautySalesInfo: {
marginTop: 6.75, marginTop: 6.75,
fontSize: 14, fontSize: fontSize(14),
fontWeight: "600", fontWeight: "600",
color: "#7c7c7c", color: "#7c7c7c",
}, },

10
app/screens/LoginScreen.tsx

@ -27,9 +27,9 @@ import AsyncStorage from "@react-native-async-storage/async-storage";
type RootStackParamList = { type RootStackParamList = {
Login: undefined; Login: undefined;
EmailLogin: undefined; EmailLogin: undefined;
MainTabs: undefined; MainTabs: { screen: string };
Google: undefined; Google: undefined;
Home: undefined; Home: { screen: string };
}; };
@ -353,7 +353,7 @@ export const LoginScreen = ({ onClose, isModal }: LoginScreenProps) => {
const res = await userApi.login(params); const res = await userApi.login(params);
const token = res.token_type + " " + res.access_token; const token = res.token_type + " " + res.access_token;
await AsyncStorage.setItem("token", token); await AsyncStorage.setItem("token", token);
navigation.navigate("Home"); navigation.navigate('MainTabs', { screen: 'Home' });
const data = await settingApi.postFirstLogin(221); const data = await settingApi.postFirstLogin(221);
console.log(data); console.log(data);
}; };
@ -366,7 +366,7 @@ export const LoginScreen = ({ onClose, isModal }: LoginScreenProps) => {
const handlePhoneVerificationSuccess = async () => { const handlePhoneVerificationSuccess = async () => {
await login(); await login();
closePhoneLogin(); closePhoneLogin();
navigation.replace('MainTabs'); navigation.replace('MainTabs', { screen: 'Home' });
}; };
const handlePhoneContinue = async () => { const handlePhoneContinue = async () => {
@ -374,7 +374,7 @@ export const LoginScreen = ({ onClose, isModal }: LoginScreenProps) => {
if (password === '123') { if (password === '123') {
await login(); await login();
closePhoneLogin(); closePhoneLogin();
navigation.replace('MainTabs'); navigation.replace('MainTabs', { screen: 'Home' });
} else { } else {
setPasswordError(true); setPasswordError(true);
} }

1803
app/screens/ProductCard.tsx

File diff suppressed because it is too large Load Diff

17
app/screens/ProductDetailScreen.tsx

@ -38,6 +38,7 @@ import { RouteProp } from "@react-navigation/native";
import BackIcon from "../components/BackIcon"; import BackIcon from "../components/BackIcon";
import CameraIcon from "../components/CameraIcon"; import CameraIcon from "../components/CameraIcon";
import ProductCard from "./ProductCard"; import ProductCard from "./ProductCard";
import useProductCartStore from "../store/productCart";
type ProductDetailRouteParams = { type ProductDetailRouteParams = {
offer_id: string; offer_id: string;
searchKeyword?: string; searchKeyword?: string;
@ -45,6 +46,7 @@ type ProductDetailRouteParams = {
}; };
export const ProductDetailScreen = () => { export const ProductDetailScreen = () => {
const {product,setProduct,groupList,setGroupList} = useProductCartStore()
const { width } = useWindowDimensions(); // 移动到组件内部 const { width } = useWindowDimensions(); // 移动到组件内部
const navigation = useNavigation<NativeStackNavigationProp<any>>(); const navigation = useNavigation<NativeStackNavigationProp<any>>();
const route = const route =
@ -56,8 +58,7 @@ export const ProductDetailScreen = () => {
}>({}); }>({});
const [isHeartRed, setIsHeartRed] = useState(false); const [isHeartRed, setIsHeartRed] = useState(false);
const [product, setProduct] = useState<ProductDetailParams>();
const [groupList, setGroupList] = useState<ProductGroupList[]>([]);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [similars, setSimilars] = useState<Similars>(); const [similars, setSimilars] = useState<Similars>();
@ -370,6 +371,12 @@ export const ProductDetailScreen = () => {
[navigation] [navigation]
); );
useEffect(() => {
if (showBottomSheet) {
console.log(123);
}
}, [showBottomSheet])
return ( return (
<View style={{ flex: 1 }}> <View style={{ flex: 1 }}>
{isLoading ? ( {isLoading ? (
@ -380,9 +387,9 @@ export const ProductDetailScreen = () => {
) : ( ) : (
<> <>
<View style={styles.headerBox}> <View style={styles.headerBox}>
<View style={styles.backIcon}> <TouchableOpacity style={styles.backIcon} onPress={() => navigation.goBack()}>
<BackIcon size={fontSize(20)} /> <BackIcon size={fontSize(20)} />
</View> </TouchableOpacity>
<TouchableOpacity style={styles.search} onPress={handleSearchPress}> <TouchableOpacity style={styles.search} onPress={handleSearchPress}>
<Text style={styles.searchText}></Text> <Text style={styles.searchText}></Text>
<CameraIcon size={fontSize(20)} /> <CameraIcon size={fontSize(20)} />
@ -479,7 +486,7 @@ export const ProductDetailScreen = () => {
}} }}
> >
<Text style={styles.activeIndexText}> <Text style={styles.activeIndexText}>
{activeIndex + 1}/{product?.product_image_urls.length} {activeIndex + 1}/{product?.product_image_urls?.length}
</Text> </Text>
</View> </View>
</View> </View>

6
app/screens/productStatus/OrderDatails.tsx

@ -24,10 +24,10 @@ import AddressIcon from "../../components/AddressIcon";
import EditIcon from "../../components/ColorfulEditIcon"; import EditIcon from "../../components/ColorfulEditIcon";
import BrightnessIcon from "../../components/BrightnessIcon"; import BrightnessIcon from "../../components/BrightnessIcon";
import PhoneIcon from "../../components/PhoneIcon"; import PhoneIcon from "../../components/PhoneIcon";
import WatchAppIcon from "../../components/watchAppIcon";
import ShoppingBagIcon from "../../components/ShoppingBagIcon"; import ShoppingBagIcon from "../../components/ShoppingBagIcon";
import PowerIcon from "../../components/PowerIcon"; import PowerIcon from "../../components/PowerIcon";
import CardIcon from "../../components/ShoppingCartIcon"; import CardIcon from "../../components/ShoppingCartIcon";
import WhatsAppIcon from "../../components/WatchAppIcon";
import { useOrderListStore } from "../../store/orderList"; import { useOrderListStore } from "../../store/orderList";
export const OrderDetails = () => { export const OrderDetails = () => {
const navigation = useNavigation<NativeStackNavigationProp<any>>(); const navigation = useNavigation<NativeStackNavigationProp<any>>();
@ -206,7 +206,7 @@ export const OrderDetails = () => {
{/* watchApp */} {/* watchApp */}
<View style={styles.recipientTitle}> <View style={styles.recipientTitle}>
<View style={styles.recipientPhoneContainer}> <View style={styles.recipientPhoneContainer}>
<WatchAppIcon size={16} /> <WhatsAppIcon size={16} />
<Text style={styles.recipientPhone}>WhatsApp:</Text> <Text style={styles.recipientPhone}>WhatsApp:</Text>
<Text style={styles.recipientPhone}> <Text style={styles.recipientPhone}>
{orderDetails.receiver_phone} {orderDetails.receiver_phone}
@ -551,10 +551,12 @@ const styles = StyleSheet.create({
orderIdText: { orderIdText: {
color: "#999", color: "#999",
width: "50%", width: "50%",
fontSize: fontSize(14),
}, },
orderIdText1: { orderIdText1: {
width: "50%", width: "50%",
textAlign: "right", textAlign: "right",
fontSize: fontSize(14),
}, },
TotalText: { TotalText: {
color: "#f77f3a", color: "#f77f3a",

4
app/screens/productStatus/Progress.tsx

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { View, StyleSheet } from 'react-native'; import { View, StyleSheet } from 'react-native';
import { Text } from 'react-native-paper'; import { Text } from 'react-native-paper';
import fontSize from '../../utils/fontsizeUtils';
interface ProgressProps { interface ProgressProps {
statuses: number; statuses: number;
@ -74,7 +74,7 @@ const styles = StyleSheet.create({
paddingHorizontal: 10, paddingHorizontal: 10,
}, },
label: { label: {
fontSize: 12, fontSize: fontSize(12),
color: '#666', color: '#666',
textAlign: 'center', textAlign: 'center',
width: 60, width: 60,

364
app/store/productCart.ts

@ -0,0 +1,364 @@
import { create } from "zustand";
import {
ProductDetailParams,
ProductGroupList,
SkuAttribute,
Sku,
} from "../services/api/productApi";
interface ProductCartState {
product: ProductDetailParams;
groupList: ProductGroupList[];
imgTitle: string;
price: number;
hasImg: ProductGroupList;
totalPrice: number;
sizeList: SkuAttribute[];
selectedSize: number;
setProduct: (product: ProductDetailParams) => void;
setGroupList: (groupList: ProductGroupList[]) => void;
setImgTitle: (imgTitle: string) => void;
setPrice: (price: number) => void;
setHasImg: (hasImg: ProductGroupList) => void;
setSizeList: (sizeList: SkuAttribute[]) => void;
setTotalPrice: (totalPrice: number) => void;
setSelectedSize: (selectedSize: number) => void;
noImgList: Sku[];
setNoImgList: (noImgList: Sku[]) => void;
flag: boolean;
setFlag: (flag: boolean) => void;
sizeTitle: string;
setSizeTitle: (sizeTitle: string) => void;
size: string;
setSize: (size: string) => void;
processProductData: () => void;
handleSizeSelect: (
value: string,
type: string,
index: number,
amount_on_sale: number
) => void;
handleNoImgSizeSelect: (
value: string,
type: string,
index: number,
amount_on_sale: number
) => void;
calculateTotalSize: (hasImgData: ProductGroupList | undefined) => void;
handleColorSelect: (
colorId: string,
index: number,
sku_image_url: string
) => void;
offer_id:number;
setOfferId: (offer_id:number) => void;
}
const useProductCartStore = create<ProductCartState>((set, get) => ({
product: {} as ProductDetailParams,
groupList: [] as ProductGroupList[],
imgTitle: "",
price: 0,
hasImg: {} as ProductGroupList,
sizeList: [] as SkuAttribute[],
totalPrice: 0,
selectedSize: 0,
noImgList: [] as Sku[],
flag: false,
sizeTitle: "",
size: "",
offer_id:0,
setOfferId: (offer_id:number) => set({ offer_id }),
setSizeTitle: (sizeTitle: string) => set({ sizeTitle }),
setSize: (size: string) => set({ size }),
setProduct: (product: ProductDetailParams) => set({ product }),
setGroupList: (groupList: ProductGroupList[]) => set({ groupList }),
setImgTitle: (imgTitle: string) => set({ imgTitle }),
setPrice: (price: number = 0) =>
set((state) => ({
price: price,
})),
setHasImg: (hasImg: ProductGroupList) => set({ hasImg }),
setSizeList: (sizeList: SkuAttribute[]) => set({ sizeList }),
setTotalPrice: (totalPrice: number) => set({ totalPrice }),
setSelectedSize: (selectedSize: number) => set({ selectedSize }),
setNoImgList: (noImgList: Sku[]) => set({ noImgList }),
setFlag: (flag: boolean) => set({ flag }),
processProductData: () => {
const { groupList, product, offer_id, setOfferId } = get();
set({ price: product.price as number });
if (product) {
if (offer_id !== product.offer_id) {
set({ totalPrice: 0, selectedSize: 0 });
}
const imageItem = groupList.filter((item) => item.has_image);
if (imageItem.length > 0) {
set({ sizeTitle: imageItem[imageItem.length - 1].attribute_name });
imageItem.forEach((item) => {
const colorItem = item.attributes.filter(
(attribute) => attribute.has_color
);
if (colorItem.length > 0) {
set({ imgTitle: colorItem[0].sku_image_url });
}
});
set({ flag: true });
} else {
set({ flag: false });
}
if (imageItem.length === 0) {
set({ imgTitle: product?.product_image_urls?.[0] || '' });
}
const sizeItem = groupList.filter((item) => !item.has_image);
if (sizeItem.length > 0) {
set({ sizeTitle: sizeItem[sizeItem.length - 1].attribute_name });
}
const noImg = groupList
.find((item) => !item.has_image)
?.attributes.find((item) => item.has_color);
set({ size: noImg?.value ?? "" });
const shotData = groupList.sort((a, b) => (a.has_image ? -1 : 1));
if (shotData.length > 1) {
const hasImg = shotData[0];
if (hasImg) {
// 创建一个深拷贝,避免修改原始数据
const processedImg = { ...hasImg };
processedImg.attributes = hasImg.attributes.map((attr) => ({
...attr,
list: [],
}));
// 处理每个属性,添加匹配的SKU到list
processedImg.attributes.forEach((attribute) => {
product.skus.forEach((sku) => {
// 检查SKU是否包含当前属性值
const matchedAttr = sku.attributes.find(
(attr) => attr.value === attribute.value
);
if (matchedAttr) {
// 创建SKU的复制,不修改原始SKU
const skuCopy = { ...sku };
// 过滤属性,创建新的属性数组而不是修改原始数组
skuCopy.attributes = sku.attributes
.filter((attr) => attr.value !== attribute.value)
.map((attr) => ({ ...attr })); // 复制每个属性对象
// 将处理后的SKU添加到list
attribute.list.push(skuCopy);
}
});
});
set({ hasImg: processedImg });
} else {
set({ hasImg: groupList[0] });
}
} else {
set({ noImgList: product.skus });
}
const img = groupList
.find((item) => item.has_image)
?.attributes.find((item) => item.has_color);
set({ size: img?.value ?? "" });
setOfferId(product.offer_id);
}
},
handleColorSelect: (
colorId: string,
index: number,
sku_image_url: string
) => {
const { hasImg, product, setImgTitle, setPrice, setHasImg } = get();
if (!hasImg) return;
if (sku_image_url) {
setImgTitle(sku_image_url);
}
// 创建attributes的深拷贝
const newAttributes = hasImg.attributes.map((attr, i) => {
if (i === index) {
// 当前选中项设为true
return { ...attr, has_color: true };
} else {
// 其他项设为false
return { ...attr, has_color: false };
}
});
const newPrice = newAttributes[index].list[0].offer_price;
setPrice(
newPrice ??
product.sale_info.price_range_list[
product.sale_info.price_range_list.length - 1
].price
);
set({ size: newAttributes[index].value });
// 更新hasImg状态
setHasImg({
...hasImg,
attributes: newAttributes,
});
},
handleSizeSelect: (
value: string,
type: string,
index: number,
amount_on_sale: number
) => {
const { hasImg, product } = get();
if (!hasImg) return;
const data = hasImg.attributes.find((item) => item.has_color);
if (data) {
// 创建hasImg的深拷贝
const newHasImg = { ...hasImg };
// 找到有颜色的属性索引
const colorIndex = newHasImg.attributes.findIndex(
(item) => item.has_color
);
if (colorIndex !== -1) {
// 创建属性数组的深拷贝
newHasImg.attributes = [...newHasImg.attributes];
// 创建特定属性的深拷贝
newHasImg.attributes[colorIndex] = {
...newHasImg.attributes[colorIndex],
};
// 创建list数组的深拷贝
newHasImg.attributes[colorIndex].list = [
...(newHasImg.attributes[colorIndex].list || []),
];
// 创建特定list项的深拷贝
if (index < newHasImg.attributes[colorIndex].list.length) {
newHasImg.attributes[colorIndex].list[index] = {
...newHasImg.attributes[colorIndex].list[index],
};
// 修改size值
if (type === "+") {
newHasImg.attributes[colorIndex].size =
(newHasImg.attributes[colorIndex].size ?? 0) + 1;
if (newHasImg.attributes[colorIndex].size > amount_on_sale) {
newHasImg.attributes[colorIndex].size = amount_on_sale;
}
newHasImg.attributes[colorIndex].list[index].size =
(newHasImg.attributes[colorIndex].list[index].size ?? 0) + 1;
if (
newHasImg.attributes[colorIndex].list[index].size > amount_on_sale
) {
newHasImg.attributes[colorIndex].list[index].size =
amount_on_sale;
}
} else if (type === "-") {
newHasImg.attributes[colorIndex].size =
(newHasImg.attributes[colorIndex].size ?? 0) - 1;
newHasImg.attributes[colorIndex].list[index].size =
(newHasImg.attributes[colorIndex].list[index].size ?? 0) - 1;
if (newHasImg.attributes[colorIndex].list[index].size < 0) {
newHasImg.attributes[colorIndex].list[index].size = 0;
}
if (newHasImg.attributes[colorIndex].size < 0) {
newHasImg.attributes[colorIndex].size = 0;
}
} else {
// 处理直接输入数字的情况
const newSize = parseInt(type);
if (!isNaN(newSize)) {
// 确保输入的数字在有效范围内
const validSize = Math.min(Math.max(0, newSize), amount_on_sale);
newHasImg.attributes[colorIndex].list[index].size = validSize;
// 更新总数量
let totalSize = 0;
newHasImg.attributes[colorIndex].list.forEach(item => {
totalSize += item.size ?? 0;
});
newHasImg.attributes[colorIndex].size = totalSize;
}
}
// 更新hasImg状态
set({ hasImg: newHasImg });
get().calculateTotalSize(newHasImg);
}
}
}
},
handleNoImgSizeSelect: (
value: string,
type: string,
index: number,
amount_on_sale: number
) => {
const { noImgList, product } = get();
if (!noImgList || !product || !product.sale_info || !product.sale_info.price_range_list) return;
const newNoImgList = [...noImgList];
if (type === "+") {
newNoImgList[index].size = (newNoImgList[index].size ?? 0) + 1;
if (newNoImgList[index].size > amount_on_sale) {
newNoImgList[index].size = amount_on_sale;
}
} else if (type === "-") {
newNoImgList[index].size = (newNoImgList[index].size ?? 0) - 1;
if (newNoImgList[index].size < 0) {
newNoImgList[index].size = 0;
}
} else {
// Handle direct number input
const newSize = parseInt(type);
if (!isNaN(newSize)) {
// Ensure the input number is within valid range
const validSize = Math.min(Math.max(0, newSize), amount_on_sale);
newNoImgList[index].size = validSize;
}
}
set({ noImgList: newNoImgList });
let total = 0;
let priceSum = 0;
newNoImgList.forEach((item) => {
total += item.size ?? 0;
priceSum +=
(item.offer_price ??
product.sale_info.price_range_list[
product.sale_info.price_range_list.length - 1
].price) * (item.size ?? 0);
});
set({ selectedSize: total, totalPrice: priceSum });
},
calculateTotalSize: (hasImgData: ProductGroupList | undefined) => {
if (!hasImgData) return;
const { product } = get();
let total = 0;
let priceSum = 0;
hasImgData.attributes.forEach((attr) => {
attr.list?.forEach((item) => {
const itemSize = item.size ?? 0;
total += itemSize;
priceSum +=
(item.offer_price ??
product.sale_info.price_range_list[
product.sale_info.price_range_list.length - 1
].price) * itemSize;
});
});
set({ selectedSize: total, totalPrice: priceSum });
},
}));
export default useProductCartStore;

BIN
assets/img/vip1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 B

BIN
assets/img/折扣VIP1 (1).png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Loading…
Cancel
Save