ERPTurbo_Client/packages/app-client/src/components/purchase/module/OrderPackage.tsx
shenyifei a69525bfb5 feat(purchase): 实现空箱费用计算和空箱信息展示功能
- 在生产环境配置中添加海报域名环境变量
- 优化经销商选择器组件样式和交互
- 引入 decimal.js 库用于精确计算空箱费用
- 实现空箱费用自动计算和固定费用项生成功能
- 添加空箱使用明细展示和分组统计功能
- 完善空箱信息编辑功能,支持销售价、成本价、箱重编辑
- 优化订单预览页面空箱信息展示逻辑
- 移除重复的费用项目获取逻辑,统一通过 props 传递
- 修复纸箱品牌过滤条件错误问题
- 优化输入框和选择器组件样式和交互体验
2025-11-18 15:16:30 +08:00

1404 lines
50 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 { Icon } from "@/components";
import {
Button,
Dialog,
Image,
Input,
Popup,
SafeArea,
Toast,
} from "@nutui/nutui-react-taro";
import classNames from "classnames";
import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
import { business } from "@/services";
import { BoxBrand, BoxProduct, BoxSpec, SupplierVO } from "@/types/typings";
import { generateShortId } from "@/utils/generateShortId";
import {
convertBoxBrandToOrderPackages,
convertOrderPackagesToBoxBrands,
} from "@/utils/orderPackage";
// 定义ref暴露的方法接口
export interface OrderPackageRef {
validate: () => boolean;
}
interface IOrderPackageProps {
value: SupplierVO;
onChange: (supplierVO: SupplierVO) => void;
}
export default forwardRef<OrderPackageRef, IOrderPackageProps>(
function OrderPackage(props, ref) {
const { value, onChange } = props;
const [supplierVO, setSupplierVO] = useState<SupplierVO>(value);
// 初始化数据
useEffect(() => {
setSupplierVO(value);
const orderPackageList = value.orderPackageList || [];
setOrderPackageList(orderPackageList);
if (orderPackageList.length > 0) {
// 根据当前供应商确定需要检查的纸箱类型
let requiredTypes: string[] = [];
// 确定当前供应商需要检查哪些纸箱类型
if (value.isPaper && value.isLast) {
requiredTypes = ["OWN", "USED", "EXTRA_USED", "REMAIN"];
} else if (value.isPaper && !value.isLast) {
requiredTypes = ["USED", "OWN", "EXTRA"];
} else if (!value.isPaper) {
requiredTypes = ["OWN", "EXTRA_USED"];
}
requiredTypes.map((type) => {
setPackageTypeEnabled((prev) => {
return {
...prev,
[type]: orderPackageList.some((item) => item.boxType === type)
? 1
: 2,
};
});
});
}
}, []);
const [orderPackageList, setOrderPackageList] = useState<
BusinessAPI.OrderPackage[]
>([]);
// 将校验方法暴露给父组件
useImperativeHandle(ref, () => ({
validate,
}));
// 校验函数
const validate = () => {
// 根据当前供应商确定需要检查的纸箱类型
let requiredTypes: string[] = [];
// 确定当前供应商需要检查哪些纸箱类型
if (supplierVO.isPaper && supplierVO.isLast) {
// 空磅包含纸箱 + 最后一个瓜农
if (packageTypeEnabled.OWN === 1) {
requiredTypes = ["OWN", "USED"];
if (packageTypeEnabled.REMAIN === 1) {
requiredTypes.push("REMAIN");
}
} else if (packageTypeEnabled.OWN === 2) {
requiredTypes = ["USED", "OWN", "EXTRA_USED"];
if (packageTypeEnabled.REMAIN === 1) {
requiredTypes.push("REMAIN");
}
}
} else if (supplierVO.isPaper && !supplierVO.isLast) {
// 空磅包含纸箱 + 非最后一个瓜农
if (packageTypeEnabled.OWN === 1) {
requiredTypes = ["OWN", "USED"];
} else if (packageTypeEnabled.OWN === 2) {
requiredTypes = ["USED", "OWN", "EXTRA"];
}
} else if (!supplierVO.isPaper) {
// 空磅不包含纸箱
if (packageTypeEnabled.OWN === 1) {
requiredTypes = ["OWN"];
} else if (packageTypeEnabled.OWN === 2) {
requiredTypes = ["EXTRA_USED"];
}
}
// 检查所需选项是否都已做出选择不是1就是2不能是0
const allRequiredAnswered = requiredTypes.every(
(type) =>
packageTypeEnabled[type] === 1 || packageTypeEnabled[type] === 2,
);
if (!allRequiredAnswered || requiredTypes.length === 0) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: "请回答相关的纸箱使用情况问题",
});
return false;
}
// 检查所有启用的纸箱类型是否都填写了信息
const enabledTypes = Object.entries(packageTypeEnabled)
.filter(
([type, enabled]) => requiredTypes.includes(type) && enabled === 1,
)
.map(([type, _]) => type);
// 检查每个启用的类型是否有对应的纸箱数据
for (const type of enabledTypes) {
const hasPackagesOfType = orderPackageList.some(
(item) => item.boxType === type,
);
if (!hasPackagesOfType) {
const typeLabels = {
USED: "记录装车用的纸箱",
EXTRA_USED: "记录额外运输来的所有纸箱",
EXTRA: "记录额外运输来的所有纸箱",
REMAIN: "记录车上没用完的纸箱",
OWN: "记录装车用的瓜农的纸箱",
};
Toast.show("toast", {
icon: "fail",
title: "提示",
content: `已选择使用${typeLabels[type]},请添加对应的纸箱信息`,
});
return false;
}
}
return true;
};
// 纸箱品牌数据
const [boxBrandList, setBoxBrandList] = useState<BoxBrand[]>();
const initBoxBrandList = async () => {
const { data } = await business.boxBrand.listBoxBrand({
boxBrandListQry: {
status: true,
withProduct: true,
},
});
const boxBrandList =
data.data
?.filter(
(boxBrand) =>
boxBrand.status &&
boxBrand.boxProductVOList &&
boxBrand.boxProductVOList.length > 0,
)
.map((boxBrand) => {
// 将产品按规格分类
const boxSpecList: BoxSpec[] =
boxBrand.boxSpecVOList?.map((item) => {
return {
id: generateShortId(),
boxSpecId: item.specId,
boxSpecName: item.name,
boxProductList: [],
};
}) || [];
const boxProductList = boxBrand.boxProductVOList?.map(
(boxProductVO) => {
const boxProduct: BoxProduct = {
id: generateShortId(),
boxProductId: boxProductVO.productId,
boxProductName: boxProductVO.name,
boxProductWeight: boxProductVO.weight,
boxSpecId: boxProductVO.specId,
boxSpecName: boxProductVO.specName,
boxCostPrice: boxProductVO.costPrice,
boxSalePrice: boxProductVO.salePrice,
boxBrandId: boxBrand.brandId,
boxBrandName: boxBrand.name,
boxCount: 0,
};
return boxProduct;
},
);
return {
id: generateShortId(),
boxBrandId: boxBrand.brandId,
boxBrandName: boxBrand.name,
boxBrandImage: boxBrand.image,
boxBrandType: boxBrand.type,
boxSpecList:
boxSpecList
.map((boxSpec) => {
return {
id: generateShortId(),
boxSpecId: boxSpec.boxSpecId,
boxSpecName: boxSpec.boxSpecName,
boxProductList:
boxProductList?.filter(
(boxProduct) =>
boxProduct.boxSpecId === boxSpec.boxSpecId,
) || [],
};
})
.filter(
(boxSpec) =>
boxSpec.boxProductList &&
boxSpec.boxProductList.length > 0,
) || [],
};
}) || [];
setBoxBrandList(boxBrandList as any);
};
useEffect(() => {
initBoxBrandList().then();
}, []);
// 添加状态来跟踪当前选择的纸箱类型
const [boxType, setBoxType] =
useState<BusinessAPI.OrderPackage["boxType"]>("USED");
// 新增状态:跟踪每种纸箱类型的启用状态
// 0: 未选, 1: 选择是, 2: 选择否
const [packageTypeEnabled, setPackageTypeEnabled] = useState({
USED: 0,
EXTRA_USED: 0,
EXTRA: 0,
REMAIN: 0,
OWN: 0,
});
// 监听供应商信息变化
useEffect(() => {
if (orderPackageList) {
onChange({
...supplierVO,
orderPackageList,
});
}
}, [orderPackageList]);
// 批量添加纸箱相关状态
const [selectedBrand, setSelectedBrand] = useState<BoxBrand | null>(null);
const [productCounts, setProductCounts] = useState<Map<string, number>>(
new Map(),
); // 产品数量映射
// 批量添加纸箱弹窗状态
const [showBatchModal, setShowBatchModal] = useState(false);
// 编辑纸箱弹窗状态
const [showEditModal, setShowEditModal] = useState(false);
const [editingItem, setEditingItem] = useState<any>(null);
const [editingItemIndex, setEditingItemIndex] = useState<number | null>(
null,
);
// 处理批量添加时的品牌选择
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 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.id) || 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.id) || 0 : 0;
return {
...pkg,
boxCount: count,
};
});
// 添加到orderPackageList中
setOrderPackageList((prev) => [...prev, ...updatedOrderPackages]);
// 重置选择并关闭弹窗
setSelectedBrand(null);
setProductCounts(new Map());
setShowBatchModal(false);
};
// 删除包装信息
const removePackageInfo = (boxBrandId: string) => {
Dialog.open("dialog", {
title: "提示",
content: "确定要移除此纸箱品牌吗?",
confirmText: "确定",
cancelText: "取消",
onConfirm: () => {
const newList = [
...orderPackageList.filter(
(item) => item.boxBrandId !== boxBrandId,
),
];
setOrderPackageList(newList);
Dialog.close("dialog");
},
onCancel: () => {
Dialog.close("dialog");
},
});
};
// 处理纸箱类型启用状态切换
const togglePackageType = (
type: BusinessAPI.OrderPackage["boxType"],
value: number, // 0: 未选, 1: 选择是, 2: 选择否
) => {
setPackageTypeEnabled((prev) => ({
...prev,
[type]: value,
}));
};
// 处理编辑按钮点击
const handleEditItem = (item: BoxBrand, index: number) => {
console.log(
"handleEditItem",
item,
index,
orderPackageList,
orderPackageList.filter(
(pkg) =>
pkg.boxBrandId === item.boxBrandId && pkg.boxType === item.boxType,
),
);
// 从packages中找到完整的品牌信息用于编辑
const brandToEdit = convertOrderPackagesToBoxBrands(
orderPackageList.filter(
(pkg) =>
pkg.boxBrandId === item.boxBrandId && pkg.boxType === item.boxType,
),
)[0];
// 获取完整品牌信息(包括所有规格)
const fullBrandInfo = boxBrandList?.find(
(brand) => brand.boxBrandId === item.boxBrandId,
);
// 合并已有的数据和完整品牌信息
const mergedBrandInfo = fullBrandInfo
? {
...fullBrandInfo,
boxType: item.boxType, // 保持boxType
boxSpecList: fullBrandInfo.boxSpecList?.map((boxSpec) => {
// 找到对应的已存在分类数据
const existingSpec = brandToEdit?.boxSpecList?.find(
(spec) => spec.boxSpecId === boxSpec.boxSpecId,
);
return {
...boxSpec,
boxProductList: boxSpec.boxProductList.map((product) => {
// 找到对应的产品数据
const existingProduct = existingSpec?.boxProductList.find(
(prod) => prod.boxProductId === product.boxProductId,
);
return {
...product,
boxCount: existingProduct?.boxCount || 0,
};
}),
};
}),
}
: brandToEdit;
if (mergedBrandInfo) {
setEditingItem(mergedBrandInfo);
setEditingItemIndex(index);
setShowEditModal(true);
}
};
// 更新编辑项中的数量
const updateEditingItemCount = (
specIndex: number,
detailIndex: number,
count: number,
) => {
if (!editingItem) return;
// 获取完整品牌信息
const fullBrandInfo = boxBrandList?.find(
(brand) => brand.boxBrandId === editingItem.boxBrandId,
);
if (!fullBrandInfo) return;
const updatedItem = { ...editingItem };
// 确保boxSpecList存在
if (!updatedItem.boxSpecList) {
updatedItem.boxSpecList = fullBrandInfo.boxSpecList.map((boxSpec) => ({
...boxSpec,
boxProductList: boxSpec.boxProductList.map((product) => ({
...product,
boxCount: 0,
})),
}));
}
// 确保spec存在
if (!updatedItem.boxSpecList[specIndex]) {
updatedItem.boxSpecList[specIndex] = {
...fullBrandInfo.boxSpecList[specIndex],
boxProductList: fullBrandInfo.boxSpecList[
specIndex
].boxProductList.map((product) => ({
...product,
boxCount: 0,
})),
};
}
// 更新对应的产品数量
const boxSpec = { ...updatedItem.boxSpecList[specIndex] };
// 确保boxProductList存在
if (!boxSpec.boxProductList) {
boxSpec.boxProductList = fullBrandInfo.boxSpecList[
specIndex
].boxProductList.map((product) => ({
...product,
boxCount: 0,
}));
}
const boxProductList = [...boxSpec.boxProductList];
// 确保产品存在
if (!boxProductList[detailIndex]) {
boxProductList[detailIndex] = {
...fullBrandInfo.boxSpecList[specIndex].boxProductList[detailIndex],
boxCount: 0,
};
}
boxProductList[detailIndex] = {
...boxProductList[detailIndex],
boxCount: count,
};
boxSpec.boxProductList = boxProductList;
const updatedSpecList = [...updatedItem.boxSpecList];
updatedSpecList[specIndex] = boxSpec;
updatedItem.boxSpecList = updatedSpecList;
setEditingItem(updatedItem);
};
// 保存编辑的纸箱信息
const saveEditedItem = () => {
if (editingItem && editingItemIndex !== null) {
// 检查是否尝试更改为已存在的品牌(排除自身)
const isBrandAlreadySelected = orderPackageList.some(
(pkg) =>
pkg.boxType === editingItem.boxType &&
pkg.boxBrandId === editingItem.boxBrandId &&
!(
pkg.boxBrandId === editingItem.boxBrandId &&
pkg.boxType === editingItem.boxType
),
);
if (isBrandAlreadySelected) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: "该纸箱品牌在此类型下已存在,请选择其他品牌",
});
return;
}
// 使用convertBoxBrandToOrderPackages转换编辑后的数据
const updatedOrderPackages = convertBoxBrandToOrderPackages(
editingItem,
editingItem.boxType,
).filter((pkg) => pkg.boxCount > 0);
// 更新orderPackageList
const newList = [...orderPackageList];
// 找到对应品牌和类型的现有项目并替换
const startIndex = newList.findIndex(
(pkg) =>
pkg.boxBrandId === editingItem.boxBrandId &&
pkg.boxType === editingItem.boxType,
);
if (startIndex !== -1) {
// 移除旧的项目
const filteredList = newList.filter(
(pkg) =>
!(
pkg.boxBrandId === editingItem.boxBrandId &&
pkg.boxType === editingItem.boxType
),
);
// 添加更新后的项目
setOrderPackageList([...filteredList, ...updatedOrderPackages]);
} else {
// 如果没有找到,直接添加
setOrderPackageList((prev) => [...prev, ...updatedOrderPackages]);
}
}
setShowEditModal(false);
setEditingItem(null);
setEditingItemIndex(null);
};
// 渲染单个包装信息模块
const renderOrderPackageItem = (item: BoxBrand, index: number) => {
// 获取品牌信息用于展示品牌图片
const brandInfo = boxBrandList?.find(
(brand) => brand.boxBrandId === item.boxBrandId,
);
return (
<View
className="mb-2.5 overflow-hidden rounded-xl border border-gray-200 bg-white p-2.5 shadow-sm"
key={item.id}
>
{/* 品牌背景水印 */}
{brandInfo?.boxBrandImage && (
<View className="absolute top-2 right-2 opacity-10">
<Image
src={brandInfo.boxBrandImage}
className="h-16 w-16"
mode="aspectFit"
/>
</View>
)}
{/* 品牌信息和操作按钮放在同一行 */}
<View className="mb-3 flex items-start justify-between">
<View className="flex items-center">
{brandInfo?.boxBrandImage && (
<View className="mr-3 h-10 w-10 overflow-hidden rounded-lg border border-gray-200">
<Image
src={brandInfo.boxBrandImage}
className="h-full w-full"
mode="aspectFit"
/>
</View>
)}
<View>
<View className="text-lg font-bold text-gray-800">
{item.boxBrandName || "未选择品牌"}
</View>
</View>
</View>
{/* 操作按钮与品牌信息放在同一行 */}
<View className="flex gap-2">
<View
className="cursor-pointer rounded-lg bg-blue-100 px-3 py-1 text-xs font-medium text-blue-600 transition-colors hover:bg-blue-200"
onClick={() => handleEditItem(item, index)}
>
</View>
<View
className="cursor-pointer rounded-lg bg-red-100 px-3 py-1 text-xs font-medium text-red-600 transition-colors hover:bg-red-200"
onClick={() => removePackageInfo(item.boxBrandId)}
>
</View>
</View>
</View>
{/* 详细信息展示,按分类显示 */}
<View className="space-y-2">
{item.boxSpecList &&
item.boxSpecList.map((boxSpec, boxIndex) => (
<View
key={boxIndex}
className="rounded-lg bg-gray-50 p-3 text-sm text-gray-600"
>
<View className="mb-1 font-medium text-gray-700">
{boxSpec.boxSpecName}
</View>
<View>
{boxSpec.boxProductList
.map(
(detail) =>
`${detail.boxProductName}${detail.boxCount}`,
)
.join("")}
</View>
</View>
))}
</View>
</View>
);
};
// 根据启用的纸箱类型渲染对应的内容
const renderPackageByType = (type: keyof typeof packageTypeEnabled) => {
// 获取该类型下所有的纸箱
const packagesOfType = convertOrderPackagesToBoxBrands(
orderPackageList.filter((pkg) => pkg.boxType === type),
);
// 根据类型设置显示标题
const typeLabels = {
USED: "装车用的纸箱",
EXTRA_USED: "用的额外运输纸箱",
EXTRA: "额外运输来的所有纸箱",
REMAIN: "车上没用完的纸箱",
OWN: "装车用的瓜农的纸箱",
};
return (
<View key={type} className="mb-2.5">
<View className="mb-3 flex items-center justify-between">
<View className="text-base font-bold text-gray-800">
{typeLabels[type]}
</View>
</View>
{packagesOfType.length > 0 ? (
packagesOfType.map((item, index) =>
renderOrderPackageItem(item, index),
)
) : (
<View className="mb-4 rounded-lg bg-gray-50 p-4 text-center text-gray-500">
</View>
)}
<Button
icon={<Icon name={"plus"} size={20} />}
type={"primary"}
size={"large"}
fill={"outline"}
block
className="border-primary text-primary flex w-full items-center justify-center !border-2 !bg-white"
onClick={() => {
setBoxType(type);
setShowBatchModal(true);
}}
>
<View>{typeLabels[type]}</View>
</Button>
</View>
);
};
// 渲染批量添加纸箱弹窗
const renderBatchAddModal = () => {
// 检查是否至少有一个产品的数量大于0
const hasAnyProductWithCount = selectedBrand
? Array.from(productCounts.values()).some((count) => count > 0)
: false;
return (
<Popup
visible={showBatchModal}
position="bottom"
onClose={() => {
setShowBatchModal(false);
setSelectedBrand(null);
setProductCounts(new Map());
}}
title={"批量添加纸箱"}
round
>
<View className="p-2.5">
<ScrollView
scrollY
style={{
height: "65vh",
width: "100%",
}}
>
{/* 品牌选择 */}
<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) => {
if (boxType === "OWN") {
return item.boxBrandType === "FARMER_BOX";
} else {
return (
item.boxBrandType === "THIRD_PARTY_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>
{/* 未选择品牌时的提示 */}
{!selectedBrand && (
<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.id) || 0;
return (
<View
key={boxProduct.id}
className="flex items-center justify-between rounded-lg bg-gray-50 p-3"
>
<View className="text-gray-800">
{boxProduct.boxProductName}
</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.id,
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.id,
num,
);
}
}}
/>
</View>
<View
className="flex h-8 w-8 items-center justify-center rounded-r bg-gray-200"
onClick={() => {
handleProductCountChange(
boxProduct.id,
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={() => {
setShowBatchModal(false);
setSelectedBrand(null);
setProductCounts(new Map());
}}
>
</Button>
</View>
<View className="flex-1">
<Button
type={"primary"}
size={"large"}
block
disabled={!selectedBrand || !hasAnyProductWithCount}
onClick={addBatchPackageInfo}
>
</Button>
</View>
</View>
<SafeArea position={"bottom"} />
</View>
</Popup>
);
};
// 渲染编辑纸箱弹窗
const renderEditModal = () => {
if (!editingItem) return null;
// 获取品牌信息
const brandInfo = boxBrandList?.find(
(brand) => brand.boxBrandId === editingItem.boxBrandId,
);
return (
<Popup
visible={showEditModal}
position="bottom"
onClose={() => setShowEditModal(false)}
title={"编辑纸箱信息"}
round
>
<View className="p-2.5">
<ScrollView
scrollY
style={{
height: "65vh",
width: "100%",
}}
>
{/* 品牌信息 */}
<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">
{brandInfo?.boxBrandImage && (
<View className="border-primary mr-3 h-16 w-16 overflow-hidden rounded-xl border-4 object-cover">
<Image
src={brandInfo.boxBrandImage}
className="h-full w-full"
mode="aspectFill"
/>
</View>
)}
<View className="text-base font-medium text-gray-800">
{editingItem.boxBrandName}
</View>
</View>
</View>
{/* 详细信息 */}
<View className="mb-4">
<View className="mb-2 text-sm text-gray-600"></View>
<View className="space-y-3">
{editingItem?.boxSpecList &&
editingItem.boxSpecList.map((boxSpec, specId) => (
<View key={specId} className="rounded-lg bg-gray-50 p-3">
<View className="mb-2 text-base font-medium text-gray-800">
{boxSpec.boxSpecName}
</View>
<View className="space-y-2">
{boxSpec.boxProductList.map((detail, detailIndex) => {
const currentCount = detail?.boxCount || 0;
return (
<View
key={detailIndex}
className="flex items-center justify-between py-1"
>
<View className="text-gray-600">
{detail.boxProductName}
</View>
<View className="flex items-center">
<View
className="flex h-8 w-8 items-center justify-center rounded-l bg-gray-200"
onClick={() => {
const newCount = Math.max(
0,
currentCount - 1,
);
updateEditingItemCount(
specId,
detailIndex,
newCount,
);
}}
>
<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) {
updateEditingItemCount(
specId,
detailIndex,
num,
);
}
}}
/>
</View>
<View
className="flex h-8 w-8 items-center justify-center rounded-r bg-gray-200"
onClick={() => {
updateEditingItemCount(
specId,
detailIndex,
currentCount + 1,
);
}}
>
<Icon name="plus" size={16} />
</View>
<View className="ml-2 font-medium text-gray-800">
</View>
</View>
</View>
);
})}
</View>
</View>
))}
</View>
</View>
</ScrollView>
{/* 底部按钮 */}
<View className="flex gap-2 pt-4">
<View className="flex-1">
<Button
size={"large"}
type={"default"}
block
onClick={() => setShowEditModal(false)}
>
</Button>
</View>
<View className="flex-1">
<Button
type="primary"
size={"large"}
block
onClick={saveEditedItem}
>
</Button>
</View>
</View>
<SafeArea position={"bottom"} />
</View>
</Popup>
);
};
return (
<View className="flex flex-1 flex-col gap-2.5 p-2.5">
{/* 替换原来的Tab导航为问答式选择 */}
<View className="rounded-lg bg-white p-2.5 shadow-sm">
<View className="mb-3 text-base font-bold">
{supplierVO.name}使
</View>
{/* 是否使用瓜农自己的纸箱 */}
<View className="mb-2.5 flex items-center justify-between">
<View className="text-sm"></View>
<View className="flex flex-shrink-0 gap-2">
<Button
size="small"
type={packageTypeEnabled.OWN === 1 ? "primary" : "default"}
onClick={() => {
togglePackageType("USED", 2);
togglePackageType("OWN", 1);
// 如果关闭,也需要清除相关数据
if (packageTypeEnabled.OWN === 2) {
setOrderPackageList((list) =>
list.filter(
(item) =>
item.boxType !== "USED" &&
item.boxType !== "EXTRA_USED" &&
item.boxType !== "EXTRA",
),
);
togglePackageType("EXTRA_USED", 0);
togglePackageType("EXTRA", 0);
}
}}
>
</Button>
<Button
size="small"
type={packageTypeEnabled.OWN === 2 ? "primary" : "default"}
onClick={() => {
togglePackageType("USED", 1);
togglePackageType("OWN", 2);
// 如果关闭,也需要清除相关数据
if (packageTypeEnabled.OWN === 1) {
setOrderPackageList((list) =>
list.filter((item) => item.boxType !== "OWN"),
);
}
if (!supplierVO.isPaper) {
togglePackageType("EXTRA_USED", 1);
}
}}
>
</Button>
</View>
</View>
{/* 根据供应商属性显示不同的问题 */}
{supplierVO.isPaper && supplierVO.isLast && (
/* 当前为最后一个瓜农 */
<>
{packageTypeEnabled.OWN === 1 && (
/* 当选择了使用自己的纸箱时 */
<>
{renderPackageByType("OWN")}
{/* 分隔线 */}
<View className="my-4 h-px bg-gray-200" />
{/* 空车带来的纸箱用完了吗? */}
<View className="mb-2.5 flex items-center justify-between">
<View className="text-sm"></View>
<View className="flex flex-shrink-0 gap-2">
<Button
size="small"
type={
packageTypeEnabled.REMAIN === 2
? "primary"
: "default"
}
onClick={() => {
togglePackageType("REMAIN", 2);
// 如果关闭,也需要清除相关数据
if (packageTypeEnabled.REMAIN === 1) {
setOrderPackageList((list) =>
list.filter((item) => item.boxType !== "REMAIN"),
);
}
}}
>
</Button>
<Button
size="small"
type={
packageTypeEnabled.REMAIN === 1
? "primary"
: "default"
}
onClick={() => togglePackageType("REMAIN", 1)}
>
</Button>
</View>
</View>
{packageTypeEnabled.REMAIN === 1 &&
renderPackageByType("REMAIN")}
</>
)}
{packageTypeEnabled.OWN === 2 && (
/* 当没有选择使用自己的纸箱时 */
<>
{renderPackageByType("USED")}
{/* 分隔线 */}
<View className="my-4 h-px bg-gray-200" />
{/* 空车带来的纸箱用完了吗? */}
<View className="mb-2.5 flex items-center justify-between">
<View className="text-sm"></View>
<View className="flex flex-shrink-0 gap-2">
<Button
size="small"
type={
packageTypeEnabled.REMAIN === 2
? "primary"
: "default"
}
onClick={() => {
togglePackageType("REMAIN", 2);
// 如果关闭,也需要清除相关数据
if (packageTypeEnabled.REMAIN === 1) {
setOrderPackageList((list) =>
list.filter((item) => item.boxType !== "REMAIN"),
);
}
}}
>
</Button>
<Button
size="small"
type={
packageTypeEnabled.REMAIN === 1
? "primary"
: "default"
}
onClick={() => togglePackageType("REMAIN", 1)}
>
</Button>
</View>
</View>
{packageTypeEnabled.REMAIN === 1 &&
renderPackageByType("REMAIN")}
{/* 分隔线 */}
<View className="my-4 h-px bg-gray-200" />
{/* 用额外运输纸箱的了吗? */}
<View className="mb-2.5 flex items-center justify-between">
<View className="text-sm"></View>
<View className="flex flex-shrink-0 gap-2">
<Button
size="small"
type={
packageTypeEnabled.EXTRA_USED === 1
? "primary"
: "default"
}
onClick={() => togglePackageType("EXTRA_USED", 1)}
>
</Button>
<Button
size="small"
type={
packageTypeEnabled.EXTRA_USED === 2
? "primary"
: "default"
}
onClick={() => {
togglePackageType("EXTRA_USED", 2);
// 如果关闭,也需要清除相关数据
if (packageTypeEnabled.EXTRA_USED === 1) {
setOrderPackageList((list) =>
list.filter(
(item) => item.boxType !== "EXTRA_USED",
),
);
}
}}
>
</Button>
</View>
</View>
{packageTypeEnabled.EXTRA_USED === 1 &&
renderPackageByType("EXTRA_USED")}
</>
)}
</>
)}
{supplierVO.isPaper && !supplierVO.isLast && (
/* 非最后一个瓜农 */
<>
{packageTypeEnabled.OWN === 1 && (
/* 当选择了使用自己的纸箱时 */
<>
{renderPackageByType("OWN")}
{/* 分隔线 */}
<View className="my-4 h-px bg-gray-200" />
</>
)}
{packageTypeEnabled.OWN === 2 && (
/* 当没有选择使用自己的纸箱时 */
<>
{renderPackageByType("USED")}
{/* 分隔线 */}
<View className="my-4 h-px bg-gray-200" />
{/* 有额外运输纸箱过来吗? */}
<View className="mb-2.5 flex items-center justify-between">
<View className="text-sm"></View>
<View className="flex flex-shrink-0 gap-2">
<Button
size="small"
type={
packageTypeEnabled.EXTRA === 1 ? "primary" : "default"
}
onClick={() => togglePackageType("EXTRA", 1)}
>
</Button>
<Button
size="small"
type={
packageTypeEnabled.EXTRA === 2 ? "primary" : "default"
}
onClick={() => {
togglePackageType("EXTRA", 2);
// 如果关闭,也需要清除相关数据
if (packageTypeEnabled.EXTRA === 1) {
setOrderPackageList((list) =>
list.filter((item) => item.boxType !== "EXTRA"),
);
}
}}
>
</Button>
</View>
</View>
{packageTypeEnabled.EXTRA === 1 &&
renderPackageByType("EXTRA")}
</>
)}
</>
)}
{!supplierVO.isPaper && (
/* 空磅不包含纸箱 */
<>
{packageTypeEnabled.OWN === 1 && (
/* 当选择了使用自己的纸箱时 */
<>
{renderPackageByType("OWN")}
{/* 分隔线 */}
<View className="my-4 h-px bg-gray-200" />
</>
)}
{packageTypeEnabled.OWN === 2 && (
/* 当没有选择使用自己的纸箱时 */
<>
{renderPackageByType("EXTRA_USED")}
{/* 分隔线 */}
<View className="my-4 h-px bg-gray-200" />
</>
)}
</>
)}
</View>
{/* 批量添加弹窗 */}
{renderBatchAddModal()}
{/* 编辑弹窗 */}
{renderEditModal()}
</View>
);
},
);