9 changed files with 1185 additions and 1031 deletions
@ -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; |
After Width: | Height: | Size: 351 B |
After Width: | Height: | Size: 3.7 KiB |
Loading…
Reference in new issue