ERPTurbo_Client/packages/app-client/src/components/order/package/PackageCreate.tsx
2025-12-29 23:52:15 +08:00

427 lines
14 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { ScrollView, View } from "@tarojs/components";
import {
Button,
Image,
Input,
Popup,
SafeArea,
Toast,
} from "@nutui/nutui-react-taro";
import { Icon } from "@/components";
import classNames from "classnames";
import { convertBoxBrandToOrderPackages } from "@/utils";
import { useEffect, useState } from "react";
import { BoxBrand } from "@/types/typings";
interface IPackageCreateProps {
visible: boolean;
// 添加编辑模式的支持
editMode?: boolean;
onClose: () => void;
// 选中的纸箱品牌,编辑的时候使用
boxBrand?: BoxBrand;
// 全部纸箱品牌列表
boxBrandList: BoxBrand[];
orderPackageList: BusinessAPI.OrderPackage[];
onSave: (orderPackageList: BusinessAPI.OrderPackage[]) => void;
boxType: BusinessAPI.OrderPackage["boxType"];
}
export default function PackageCreate(props: IPackageCreateProps) {
const {
visible,
editMode = false,
boxBrand,
boxBrandList,
orderPackageList,
onClose,
onSave,
boxType,
} = props;
// 批量添加纸箱相关状态
const [selectedBrand, setSelectedBrand] = useState<BoxBrand | null>(null);
const [productCounts, setProductCounts] = useState<Map<string, number>>(
new Map(),
); // 产品数量映射
// 获取费用列表
useEffect(() => {
if (visible) {
// 如果是编辑模式并且有初始数据,在获取费用列表后设置初始状态
if (editMode && boxBrand) {
console.log("boxBrand", boxBrand);
// 查找对应的费用类型
const brand = boxBrandList?.find(
(c) => c.boxBrandId === boxBrand.boxBrandId,
);
if (brand) {
setSelectedBrand(brand);
// 初始化费用项目数量
const initialCounts = new Map<string, number>();
brand.boxSpecList?.forEach((boxSpec) => {
boxSpec.boxProductList.forEach((product) => {
const count = boxBrand.boxSpecList
.find((pkg) => pkg.boxSpecId === product.boxSpecId)
?.boxProductList.find(
(pkg) => pkg.boxProductId === product.boxProductId,
)?.boxCount;
initialCounts.set(product.boxProductId, count || 0);
});
});
setProductCounts(initialCounts);
}
}
}
}, [visible, editMode, boxBrandList]);
// 批量添加包装信息
const addBatchPackageInfo = () => {
if (!selectedBrand) {
return;
}
// 使用convertBoxBrandToOrderPackages转换数据
const newOrderPackages = convertBoxBrandToOrderPackages(
selectedBrand,
boxType,
);
// 过滤掉数量为0的项目
const filteredOrderPackages = newOrderPackages.filter((pkg) => {
// 从productCounts中获取对应产品的数量
const product = selectedBrand.boxSpecList
?.flatMap((c) => c.boxProductList)
.find((p) => p.id === pkg.orderPackageId);
const count = product ? productCounts.get(product.boxProductId) || 0 : 0;
return count > 0;
});
// 更新数量信息
const updatedOrderPackages = filteredOrderPackages.map((pkg) => {
// 从productCounts中获取对应产品的数量
const product = selectedBrand.boxSpecList
?.flatMap((c) => c.boxProductList)
.find((p) => p.id === pkg.orderPackageId);
const count = product ? productCounts.get(product.boxProductId) || 0 : 0;
const orderPackage = orderPackageList.find((pkg) => {
return (
pkg.boxType === boxType && pkg.boxProductId === product?.boxProductId
);
});
return {
...(orderPackage || pkg)!,
boxCount: count,
};
});
onSave([
...(orderPackageList.filter(
(pkg) =>
pkg.boxType !== boxType ||
(pkg.boxType === boxType &&
pkg.boxBrandId !== selectedBrand?.boxBrandId),
) || []),
...updatedOrderPackages,
]);
// 重置选择并关闭弹窗
setSelectedBrand(null);
setProductCounts(new Map());
onClose();
};
// 处理批量添加时的品牌选择
const handleBatchBrandSelect = (brand: BoxBrand) => {
// 检查当前boxType下是否已存在该品牌
const isBrandAlreadySelected = orderPackageList.some(
(pkg) => pkg.boxType === boxType && pkg.boxBrandId === brand.boxBrandId,
);
if (isBrandAlreadySelected) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: "该纸箱品牌在此类型下已存在,请选择其他品牌或编辑现有信息",
});
return;
}
setSelectedBrand(brand);
// 初始化所有产品的数量为0
const initialCounts = new Map<string, number>();
brand.boxSpecList?.forEach((boxSpec) => {
boxSpec.boxProductList.forEach((product) => {
initialCounts.set(product.id, 0);
});
});
setProductCounts(initialCounts);
};
// 处理产品数量变化
const handleProductCountChange = (productId: string, count: number) => {
setProductCounts((prev) => {
const newCounts = new Map(prev);
newCounts.set(productId, Math.max(0, count)); // 允许为0表示不使用
return newCounts;
});
// 同时更新selectedBrand中的产品数量
if (selectedBrand) {
const updatedBrand = { ...selectedBrand };
updatedBrand.boxSpecList = updatedBrand.boxSpecList?.map((boxSpec) => {
const updatedProducts = boxSpec.boxProductList.map((product) => {
if (product.id === productId) {
return { ...product, boxCount: count };
}
return product;
});
return { ...boxSpec, boxProductList: updatedProducts };
});
setSelectedBrand(updatedBrand);
}
};
const renderBrandSelection = () => {
// 编辑模式下不允许更改费用类型
if (editMode) {
return (
<View className="mb-4">
<View className="mb-2 text-sm text-gray-600"></View>
<View className="flex items-center rounded-lg bg-gray-50 p-3">
{selectedBrand?.boxBrandImage && (
<View className="border-primary mr-3 h-16 w-16 overflow-hidden rounded-xl border-4 object-cover">
<Image
src={selectedBrand.boxBrandImage}
className="h-full w-full"
mode="aspectFill"
/>
</View>
)}
<View className="text-base font-medium text-gray-800">
{selectedBrand?.boxBrandName}
</View>
</View>
</View>
);
}
return (
<View className="mb-4">
<View className="mb-2 text-sm text-gray-600"></View>
<ScrollView className="mb-2.5" scrollX>
<View className="flex w-fit flex-row gap-2.5">
{boxBrandList
?.filter((item) => {
return (
item.boxBrandType === "THIRD_PARTY_BOX" ||
item.boxBrandType === "FARMER_BOX" ||
item.boxBrandType === "OUR_BOX"
);
})
?.map((boxBrand) => (
<View
key={boxBrand.id}
className={"flex flex-col items-center justify-center"}
onClick={() => handleBatchBrandSelect(boxBrand)}
>
<View
className={classNames(
"border-primary box-content !size-16 overflow-hidden rounded-xl border-4 object-cover",
{
"border-primary": selectedBrand?.id === boxBrand.id,
"border-transparent": selectedBrand?.id !== boxBrand.id,
},
)}
>
<Image
src={boxBrand.boxBrandImage}
className="h-full w-full"
mode="aspectFit"
alt={boxBrand.boxBrandImage}
/>
</View>
<View className="text-center text-xs">
{boxBrand.boxBrandName}
</View>
</View>
))}
</View>
</ScrollView>
</View>
);
};
return (
<Popup
duration={150}
style={{
minHeight: "auto",
}}
visible={visible}
position="bottom"
title={editMode ? "批量编辑纸箱" : "批量添加纸箱"}
onClose={() => {
onClose();
setSelectedBrand(null);
setProductCounts(new Map());
}}
onOverlayClick={onClose}
lockScroll
>
<View className="p-2.5">
<ScrollView
scrollY
style={{
height: "65vh",
width: "100%",
}}
>
{renderBrandSelection()}
{/* 未选择品牌时的提示 */}
{!selectedBrand && !editMode && (
<View className="mb-4 rounded-lg bg-yellow-50 p-4 text-center">
<View className="text-yellow-800"></View>
</View>
)}
{/* 产品展示 */}
{selectedBrand && (
<View className="mb-4">
<View className="mb-2 text-sm text-gray-600">
+/- 0使
</View>
{selectedBrand.boxSpecList?.map((boxSpec) => (
<View key={boxSpec.id} className="mb-4">
<View className="mb-2 text-base font-medium">
{boxSpec.boxSpecName}
</View>
<View className="space-y-3">
{boxSpec.boxProductList.map((boxProduct) => {
const currentCount =
productCounts.get(boxProduct.boxProductId) || 0;
// 计算总重量
const totalWeight = boxProduct.boxProductWeight
? (boxProduct.boxProductWeight * currentCount).toFixed(
1,
)
: 0;
return (
<View
key={boxProduct.boxProductId}
className="flex items-center justify-between rounded-lg bg-gray-50 p-3"
>
<View className="flex flex-col">
<View className="text-gray-800">
{boxProduct.boxProductName}
</View>
<View className={"flex flex-row gap-2.5"}>
{boxProduct.boxProductWeight > 0 && (
<View className="text-xs text-gray-500">
{boxProduct.boxProductWeight}
</View>
)}
{currentCount > 0 &&
boxProduct.boxProductWeight > 0 && (
<View className="text-primary text-xs font-medium">
{totalWeight}
</View>
)}
</View>
</View>
<View className="flex items-center">
<View
className="flex h-8 w-8 items-center justify-center rounded-l bg-gray-200"
onClick={() => {
handleProductCountChange(
boxProduct.boxProductId,
Math.max(0, currentCount - 1),
);
}}
>
<Icon name="minus" size={16} />
</View>
<View className="flex h-8 w-12 items-center justify-center border-y border-gray-200">
<Input
type="number"
value={currentCount.toString()}
align={"center"}
className="!h-8 !w-12 !p-0 !text-center"
onChange={(value) => {
const num = Number(value);
if (!Number.isNaN(num) && num >= 0) {
handleProductCountChange(
boxProduct.boxProductId,
num,
);
}
}}
/>
</View>
<View
className="flex h-8 w-8 items-center justify-center rounded-r bg-gray-200"
onClick={() => {
handleProductCountChange(
boxProduct.boxProductId,
currentCount + 1,
);
}}
>
<Icon name="plus" size={16} />
</View>
<View className="ml-2 text-gray-800"></View>
</View>
</View>
);
})}
</View>
</View>
))}
</View>
)}
</ScrollView>
{/* 底部按钮 */}
<View className="flex gap-2 pt-4">
<View className="flex-1">
<Button
type={"default"}
size={"large"}
block
onClick={() => {
onClose();
setSelectedBrand(null);
setProductCounts(new Map());
}}
>
</Button>
</View>
<View className="flex-1">
<Button
size={"large"}
block
type={"primary"}
disabled={
!selectedBrand ||
Array.from(productCounts.values()).every((count) => count === 0)
}
onClick={addBatchPackageInfo}
>
{editMode ? "保存" : "添加"}
</Button>
</View>
</View>
<SafeArea position={"bottom"} />
</View>
</Popup>
);
}