Compare commits

...

3 Commits

Author SHA1 Message Date
shenyifei
9c0c0de0c7 fix(purchase): 修复采购订单相关组件中的空值处理和样式问题
- 统一处理 purchaseOrderVO.orderDealer 可能为空的情况,避免运行时错误
- 修复多个组件中颜色样式结尾多余的分号导致的渲染异常
- 调整 OrderCost 组件中 selected 状态的判断逻辑,确保 count 大于 0 才选中
- 优化 PurchasePreview 组件中空箱使用明细的条件渲染,仅在有数据时显示
- 完善成本校验逻辑,在没有启用的"我方"费用项时标记为有效
- 修正 WorkerAdvanceSection 中费用类型的初始值设置错误
- 更新市场报价计算逻辑,根据是否包含包装费标志位决定计算方式
- 加强 SupplierWeightCalculator 中数值计算的空值防护,防止 NaN问题
- 移除冗余的控制台日志输出,清理调试代码- 调整部分组件结构和类名,提升代码可读性和一致性
2025-11-18 16:48:01 +08:00
shenyifei
a69525bfb5 feat(purchase): 实现空箱费用计算和空箱信息展示功能
- 在生产环境配置中添加海报域名环境变量
- 优化经销商选择器组件样式和交互
- 引入 decimal.js 库用于精确计算空箱费用
- 实现空箱费用自动计算和固定费用项生成功能
- 添加空箱使用明细展示和分组统计功能
- 完善空箱信息编辑功能,支持销售价、成本价、箱重编辑
- 优化订单预览页面空箱信息展示逻辑
- 移除重复的费用项目获取逻辑,统一通过 props 传递
- 修复纸箱品牌过滤条件错误问题
- 优化输入框和选择器组件样式和交互体验
2025-11-18 15:16:30 +08:00
shenyifei
fb71cf003e feat(purchase): 添加空箱管理和订单修改追踪功能
- 在 OrderCost 组件中实现空箱品牌选择、批量添加和编辑功能
- 添加空箱类型启用状态管理及 UI 展示
- 实现空箱数据转换工具函数 convertBoxBrandToOrderPackages 和 convertOrderPackagesToBoxBrands
- 在创建订单页面添加修改标识状态管理,追踪车辆、瓜农、称重、包装、票证和成本信息变更
- 添加 lodash 用于深度比较对象变更
- 移除多个调试用 console.log语句
- 优化保存逻辑,仅在信息变更时调用接口
2025-11-18 10:10:21 +08:00
28 changed files with 2459 additions and 749 deletions

View File

@ -22,6 +22,10 @@ export default {
process.env.TARO_ENV === "h5" process.env.TARO_ENV === "h5"
? '"/api"' ? '"/api"'
: '"https://api.erp.qilincloud168.com"', : '"https://api.erp.qilincloud168.com"',
"process.env.TARO_POSTER_DOMAIN":
process.env.TARO_ENV === "h5"
? '""'
: '"https://poster.qilincloud168.com"',
}, },
mini: { mini: {
miniCssExtractPluginOption: { miniCssExtractPluginOption: {

View File

@ -1,9 +1,9 @@
import { import {
Dialog,
Input,
Popup, Popup,
SafeArea, SafeArea,
SearchBar, SearchBar,
Dialog,
Input,
} from "@nutui/nutui-react-taro"; } from "@nutui/nutui-react-taro";
import { Icon } from "@/components"; import { Icon } from "@/components";
import { ScrollView, View } from "@tarojs/components"; import { ScrollView, View } from "@tarojs/components";
@ -64,6 +64,10 @@ export default function DealerPicker(props: IDealerPickerProps) {
return ( return (
<> <>
<View <View
className={"flex flex-1 flex-row items-center justify-between px-5"}
style={{
color: "var(--nutui-color-title, #1a1a1a)",
}}
onClick={(event) => { onClick={(event) => {
setVisible(true); setVisible(true);
event.stopPropagation(); event.stopPropagation();

View File

@ -1,12 +1,12 @@
import { Text, View } from "@tarojs/components"; import { Text, View } from "@tarojs/components";
import { import {
Button, Button,
Dialog,
Input, Input,
Radio, Radio,
Toast, Toast,
Uploader, Uploader,
UploaderFileItem, UploaderFileItem,
Dialog,
} from "@nutui/nutui-react-taro"; } from "@nutui/nutui-react-taro";
import { Icon, SupplierPicker } from "@/components"; import { Icon, SupplierPicker } from "@/components";
import { forwardRef, useEffect, useImperativeHandle, useState } from "react"; import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
@ -47,7 +47,6 @@ export default forwardRef<MelonFarmerRef, IMelonFarmerProps>(
} = props; } = props;
const [supplierVO, setSupplierVO] = useState<SupplierVO>(); const [supplierVO, setSupplierVO] = useState<SupplierVO>();
console.log("supplierVO", supplierVO);
// 初始化数据 // 初始化数据
useEffect(() => { useEffect(() => {

View File

@ -623,7 +623,7 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
return ( return (
<View <View
className="relative mb-2.5 overflow-hidden rounded-xl border border-gray-200 bg-white p-2.5 shadow-sm" className="mb-2.5 overflow-hidden rounded-xl border border-gray-200 bg-white p-2.5 shadow-sm"
key={item.id} key={item.id}
> >
{/* 品牌背景水印 */} {/* 品牌背景水印 */}
@ -785,8 +785,6 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
<View className="flex w-fit flex-row gap-2.5"> <View className="flex w-fit flex-row gap-2.5">
{boxBrandList {boxBrandList
?.filter((item) => { ?.filter((item) => {
console.log("item: ", item);
console.log("boxType: ", boxType);
if (boxType === "OWN") { if (boxType === "OWN") {
return item.boxBrandType === "FARMER_BOX"; return item.boxBrandType === "FARMER_BOX";
} else { } else {
@ -817,7 +815,8 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
> >
<Image <Image
src={boxBrand.boxBrandImage} src={boxBrand.boxBrandImage}
mode={"aspectFill"} className="h-full w-full"
mode="aspectFit"
alt={boxBrand.boxBrandImage} alt={boxBrand.boxBrandImage}
/> />
</View> </View>

View File

@ -25,6 +25,7 @@ interface IOrderVehicleProps {
setOrderDealer: (orderDealer: BusinessAPI.OrderDealer) => void; setOrderDealer: (orderDealer: BusinessAPI.OrderDealer) => void;
orderCostList: CostItem[]; orderCostList: CostItem[];
setOrderCostList: (orderCostList: CostItem[]) => void; setOrderCostList: (orderCostList: CostItem[]) => void;
costItemVOList: BusinessAPI.CostItemVO[];
} }
export interface OrderVehicleRef { export interface OrderVehicleRef {
@ -40,6 +41,7 @@ export default forwardRef<OrderVehicleRef, IOrderVehicleProps>(
orderDealer, orderDealer,
orderCostList, orderCostList,
setOrderCostList, setOrderCostList,
costItemVOList,
} = props; } = props;
const [dealerVO, setDealerVO] = useState<BusinessAPI.DealerVO>(); const [dealerVO, setDealerVO] = useState<BusinessAPI.DealerVO>();
@ -66,28 +68,6 @@ export default forwardRef<OrderVehicleRef, IOrderVehicleProps>(
const startDate = new Date(currentYear, 0, 1); // 当年第一天 const startDate = new Date(currentYear, 0, 1); // 当年第一天
const endDate = new Date(); // 今天 const endDate = new Date(); // 今天
// 费用项目列表
const [costItems, setCostItems] = useState<BusinessAPI.CostItemVO[]>([]);
// 获取费用项目列表
useEffect(() => {
const fetchCostItems = async () => {
try {
const { data } = await business.costItem.listCostItem({
costItemListQry: {
status: true,
showInEntry: true,
},
});
setCostItems(data.data || []);
} catch (error) {
console.error("获取费用项目列表失败:", error);
}
};
fetchCostItems();
}, []);
// 当desc改变时更新车辆信息中的时间 // 当desc改变时更新车辆信息中的时间
useEffect(() => { useEffect(() => {
if (desc) { if (desc) {
@ -427,7 +407,7 @@ export default forwardRef<OrderVehicleRef, IOrderVehicleProps>(
}; };
const getCostItemByName = (name: string): BusinessAPI.CostItemVO => { const getCostItemByName = (name: string): BusinessAPI.CostItemVO => {
return costItems?.find( return costItemVOList?.find(
(item) => item.name === name && item.costType === "PRODUCTION_ADVANCE", (item) => item.name === name && item.costType === "PRODUCTION_ADVANCE",
)!; )!;
}; };
@ -743,27 +723,23 @@ export default forwardRef<OrderVehicleRef, IOrderVehicleProps>(
</View> </View>
<View className={"flex flex-row gap-2.5"}> <View className={"flex flex-row gap-2.5"}>
<View> <View className={"flex-1"}>
<View <View
id={"target2"} id={"target2"}
className={`relative flex h-10 w-full items-center rounded-md ${dealerNameError ? "border-4 border-red-500" : "border-4 border-gray-300"}`} className={`flex h-10 w-full flex-1 items-center rounded-md ${dealerNameError ? "border-4 border-red-500" : "border-4 border-gray-300"}`}
> >
<DealerPicker <DealerPicker
onFinish={setDealerVO} onFinish={setDealerVO}
enableManualInput enableManualInput
trigger={ trigger={
<Input <>
type="text" <View className={"text-sm"}>
placeholder="选择经销商" {orderVehicle?.dealerName || "选择经销商"}
value={orderVehicle?.dealerName} </View>
disabled <Icon name={"chevron-down"} />
/> </>
} }
/> />
<Icon
name={"chevron-down"}
className={"absolute -right-1 mr-4"}
/>
</View> </View>
{dealerNameError && ( {dealerNameError && (
<View className="mt-1 text-xs text-red-500"> <View className="mt-1 text-xs text-red-500">
@ -771,7 +747,7 @@ export default forwardRef<OrderVehicleRef, IOrderVehicleProps>(
</View> </View>
)} )}
</View> </View>
<View> <View className={"flex-1"}>
<View <View
className={`flex h-10 w-full items-center rounded-md ${destinationError ? "border-4 border-red-500" : "border-4 border-gray-300"}`} className={`flex h-10 w-full items-center rounded-md ${destinationError ? "border-4 border-red-500" : "border-4 border-gray-300"}`}
> >
@ -902,17 +878,22 @@ export default forwardRef<OrderVehicleRef, IOrderVehicleProps>(
<View <View
className={`flex h-10 w-full items-center rounded-md ${deliveryTimeError ? "border-4 border-red-500" : "border-4 border-gray-300"}`} className={`flex h-10 w-full items-center rounded-md ${deliveryTimeError ? "border-4 border-red-500" : "border-4 border-gray-300"}`}
> >
<Input <View
type="text" className={
placeholder="请选择发货时间" "flex flex-1 flex-row items-center justify-between px-5"
value={
orderVehicle?.deliveryTime
? dayjs(orderVehicle?.deliveryTime).format("YYYY年MM月DD日")
: "请选择发货时间"
} }
disabled style={{
color: "var(--nutui-color-title, #1a1a1a)",
}}
onClick={() => setShow(true)} onClick={() => setShow(true)}
/> >
<View className={"text-sm"}>
{orderVehicle?.deliveryTime
? dayjs(orderVehicle?.deliveryTime).format("YYYY年MM月DD日")
: "请选择发货时间"}
</View>
<Icon name={"chevron-down"} />
</View>
</View> </View>
{deliveryTimeError && ( {deliveryTimeError && (
<View className="mt-1 text-xs text-red-500"></View> <View className="mt-1 text-xs text-red-500"></View>

View File

@ -265,6 +265,60 @@ export default function PurchasePreview(props: IPurchasePreviewProps) {
<View>{formatCurrency(totalBoxWeight)} </View> <View>{formatCurrency(totalBoxWeight)} </View>
</View> </View>
</View> </View>
</View>
{purchaseOrder.orderPackageList.length > 0 && (
<>
<View className="text-sm font-bold">使</View>
<View className="flex flex-col gap-2.5">
<View className="rounded-lg bg-white p-2.5 shadow-sm">
{/* 根据boxBrandName对packageInfoList进行分组显示 */}
{(() => {
const groupedPackageInfo = groupBy(
purchaseOrder.orderPackageList || [],
(item) => item.boxBrandName,
);
return Object.entries(groupedPackageInfo).map(
([brandName, packageInfos]) => (
<View key={brandName} className="mb-2.5">
<View className="text-primary mb-2.5 text-base font-bold">
{brandName}
</View>
<View className={"overflow-x-auto"}>
<Table columns={columns} data={packageInfos} />
</View>
</View>
),
);
})()}
</View>
<View className="rounded-lg bg-white p-2.5 shadow-md">
<View className="flex items-center justify-between text-sm font-bold">
<View></View>
<View>
{purchaseOrder.orderPackageList.reduce(
(sum, item) => sum + item.boxCount,
0,
)}
</View>
</View>
<View className="mt-2 flex items-center justify-between text-sm font-bold">
<View></View>
<View>
{purchaseOrder.orderPackageList.reduce(
(sum, item) => sum + item.boxProductWeight * item.boxCount,
0,
)}
</View>
</View>
</View>
</View>
</>
)}
{purchaseOrder.orderCostList?.filter( {purchaseOrder.orderCostList?.filter(
(costItem: CostItem) => costItem.costType === "PACKAGING_MATERIALS", (costItem: CostItem) => costItem.costType === "PACKAGING_MATERIALS",
).length > 0 && ( ).length > 0 && (
@ -352,6 +406,5 @@ export default function PurchasePreview(props: IPurchasePreviewProps) {
</> </>
)} )}
</View> </View>
</View>
); );
} }

View File

@ -17,22 +17,22 @@ export default function CostDifferenceSection(props: {
// 主状态,用于页面显示 // 主状态,用于页面显示
const [costDifference, setCostDifference] = useState<number>( const [costDifference, setCostDifference] = useState<number>(
purchaseOrderVO.orderDealer.costDifference || 0, purchaseOrderVO.orderDealer?.costDifference || 0,
); );
// 弹窗内的临时状态 // 弹窗内的临时状态
const [tempCostDifference, setTempCostDifference] = useState<number>( const [tempCostDifference, setTempCostDifference] = useState<number>(
purchaseOrderVO.orderDealer.costDifference || 0, purchaseOrderVO.orderDealer?.costDifference || 0,
); );
const profitSharing = const profitSharing =
calculator.getDefaultNetProfit() || calculator.getDefaultNetProfit() ||
purchaseOrderVO.orderDealer.profitSharing || purchaseOrderVO.orderDealer?.profitSharing ||
0; 0;
// 当dealerVO变化时自动计算分成 // 当dealerVO变化时自动计算分成
useEffect(() => { useEffect(() => {
if (!purchaseOrderVO.orderDealer.costDifference) { if (!purchaseOrderVO.orderDealer?.costDifference) {
const defaultCostDifference = calculator.getCostDifference(); const defaultCostDifference = calculator.getCostDifference();
setCostDifference(defaultCostDifference); setCostDifference(defaultCostDifference);
setTempCostDifference(defaultCostDifference); setTempCostDifference(defaultCostDifference);
@ -113,7 +113,7 @@ export default function CostDifferenceSection(props: {
<Text className="text-sm text-gray-500"></Text> <Text className="text-sm text-gray-500"></Text>
</View> </View>
<View className="flex items-center justify-between"> <View className="flex items-center justify-between">
<Text className="text-primary text-2xl font-bold"> <Text className="text-primary text-2xl font-bold text-nowrap">
{profitSharing} {profitSharing}
</Text> </Text>
</View> </View>

View File

@ -29,9 +29,7 @@ export default function (props: {
}, [purchaseOrderVO]); }, [purchaseOrderVO]);
// 处理经销商选择完成事件 // 处理经销商选择完成事件
const handleDealerSelect = ( const handleDealerSelect = (dealerVO: BusinessAPI.DealerVO) => {
dealerVO: BusinessAPI.DealerVO,
) => {
if (readOnly) return; if (readOnly) return;
const newOrderDealer: BusinessAPI.OrderDealer = { const newOrderDealer: BusinessAPI.OrderDealer = {
@ -99,25 +97,25 @@ export default function (props: {
</View> </View>
{!readOnly && ( {!readOnly && (
<View className="flex justify-center">
<DealerPicker <DealerPicker
onFinish={(dealer) => { onFinish={(dealer) => {
handleDealerSelect(dealer); handleDealerSelect(dealer);
}} }}
trigger={ trigger={
<View
className={"flex flex-1 flex-row items-center justify-center"}
>
<Button size="small" type="primary" fill="outline"> <Button size="small" type="primary" fill="outline">
</Button> </Button>
</View>
} }
/> />
</View>
)} )}
</View> </View>
) : readOnly ? ( ) : readOnly ? (
<View className="flex flex-col items-center justify-center rounded-lg border border-dashed border-gray-300 p-2.5"> <View className="flex flex-col items-center justify-center rounded-lg border border-dashed border-gray-300 p-2.5">
<View className="text-neutral-darker text-sm"> <View className="text-neutral-darker text-sm"></View>
</View>
</View> </View>
) : ( ) : (
<View className="flex flex-col items-center justify-center rounded-lg border border-dashed border-gray-300 p-2.5"> <View className="flex flex-col items-center justify-center rounded-lg border border-dashed border-gray-300 p-2.5">
@ -147,4 +145,4 @@ export default function (props: {
)} )}
</View> </View>
); );
}; }

View File

@ -1,57 +1,17 @@
import { Table } from "@nutui/nutui-react-taro"; import { formatCurrency, validatePrice } from "@/utils/format";
import { formatCurrency } from "@/utils/format"; import { useEffect, useState } from "react";
import { Button, Input, Popup, SafeArea, Table } from "@nutui/nutui-react-taro";
import { Icon } from "@/components";
import { View } from "@tarojs/components";
export default function EmptyBoxInfoSection(_props: { export default function EmptyBoxInfoSection(props: {
purchaseOrderVO: BusinessAPI.PurchaseOrderVO; purchaseOrderVO: BusinessAPI.PurchaseOrderVO;
onChange?: (purchaseOrderVO: BusinessAPI.PurchaseOrderVO) => void;
readOnly?: boolean; readOnly?: boolean;
}) { }) {
// 将所有空箱信息合并并按品牌、型号、规格分组 const { purchaseOrderVO, onChange, readOnly } = props;
// const groupedEmptyBoxData = purchaseOrderVO.orderSupplierList?.reduce(
// (acc, supplier) => {
// supplier.orderPackageList?.forEach((pkg) => {
// // 生成分组键
// const key = `${pkg.boxBrandName}-${pkg.boxProductName}-${pkg.boxCategoryId}`;
//
// // 转换规格字段
// const boxCategoryName =
// pkg.boxCategoryId === "FOUR_GRADE"
// ? "4粒"
// : pkg.boxCategoryId === "TWO_GRADE"
// ? "2粒"
// : "其他";
//
// if (!acc[key]) {
// acc[key] = {
// boxBrandName: pkg.boxBrandName,
// boxProductName: pkg.boxProductName,
// boxCategoryName,
// boxProductCount: 0,
// boxProductPrice: pkg.boxSalePrice,
// boxProductWeight: pkg.boxProductWeight,
// boxProductCostPrice: pkg.boxCostPrice,
// };
// }
//
// // 累加数量
// acc[key].boxProductCount += pkg.boxCount || 0;
// });
//
// return acc;
// },
// {} as Record<string, any>,
// );
//
// // 转换为数组格式并计算销售金额
// const emptyBoxData = Object.values(groupedEmptyBoxData || {}).map(item => ({
// ...item,
// boxProductPayment: (item.boxProductCount || 0) * (item.boxProductPrice || 0)
// }));
const emptyBoxData = []; const defaultColumns = [
return (
<Table
columns={[
{ {
title: "品牌", title: "品牌",
key: "boxBrandName", key: "boxBrandName",
@ -59,7 +19,7 @@ export default function EmptyBoxInfoSection(_props: {
}, },
{ {
title: "规格", title: "规格",
key: "boxCategoryName", key: "boxSpecName",
}, },
{ {
title: "纸箱型号", title: "纸箱型号",
@ -71,25 +31,641 @@ export default function EmptyBoxInfoSection(_props: {
}, },
{ {
title: "销售单价(元/斤)", title: "销售单价(元/斤)",
key: "boxProductPrice", key: "boxSalePrice",
render: (value: any) => formatCurrency(value.boxProductPrice), render: (
value: BusinessAPI.OrderPackage & {
orderPackageId?: string;
isTotalRow?: boolean;
},
) => {
// 合计行不显示编辑按钮
if (value.isTotalRow) {
return formatCurrency(value.boxSalePrice as number);
}
return (
<View
className="flex w-full items-center justify-between"
onClick={(e) => {
if (!readOnly) {
e.stopPropagation();
// 设置临时编辑值为当前值
setTempEditValues((prev) => ({
...prev,
[value.orderPackageId || ""]:
editValues[value.orderPackageId || ""],
}));
setVisiblePopup((prev) => ({
...prev,
[value.orderPackageId || ""]: true,
}));
}
}}
>
<View className={!readOnly ? "cursor-pointer underline" : ""}>
{formatCurrency(value.boxSalePrice as number)}
</View>
{!readOnly && (
<View className="-m-2 ml-2 flex items-center justify-center p-2">
<Icon name={"pen-to-square"} size={16} color={"#1a73e8"} />
</View>
)}
</View>
);
},
}, },
{ {
title: "销售金额(元)", title: "销售金额(元)",
key: "boxProductPayment", key: "boxSalePayment",
render: (value: any) => formatCurrency(value.boxProductPayment), render: (
value: BusinessAPI.OrderPackage & {
boxProductCount: number;
isTotalRow?: boolean;
},
) =>
formatCurrency(
Number((value?.boxSalePrice || 0) * value.boxProductCount) as number,
),
}, },
{ {
title: "箱重(斤)", title: "箱重(斤)",
key: "boxProductWeight", key: "boxProductWeight",
render: (
value: BusinessAPI.OrderPackage & {
orderPackageId?: string;
isTotalRow?: boolean;
},
) => {
// 合计行不显示编辑按钮
if (value.isTotalRow) {
return formatCurrency(value.boxProductWeight);
}
return (
<View
className="flex w-full items-center justify-between"
onClick={(e) => {
if (!readOnly) {
e.stopPropagation();
// 设置临时编辑值为当前值
setTempEditValues((prev) => ({
...prev,
[value.orderPackageId || ""]:
editValues[value.orderPackageId || ""],
}));
setVisiblePopup((prev) => ({
...prev,
[value.orderPackageId || ""]: true,
}));
}
}}
>
<View className={!readOnly ? "cursor-pointer underline" : ""}>
{formatCurrency(value.boxProductWeight)}
</View>
{!readOnly && (
<View className="-m-2 ml-2 flex items-center justify-center p-2">
<Icon name={"pen-to-square"} size={16} color={"#1a73e8"} />
</View>
)}
</View>
);
},
}, },
{ {
title: "成本单价(元/斤)", title: "成本单价(元)",
key: "boxProductCostPrice", key: "boxCostPrice",
render: (value: any) => formatCurrency(value.boxProductCostPrice), render: (
value: BusinessAPI.OrderPackage & {
orderPackageId?: string;
isTotalRow?: boolean;
}, },
]} ) => {
data={emptyBoxData || []} // 合计行不显示编辑按钮
if (value.isTotalRow) {
return formatCurrency(value.boxCostPrice as number);
}
return (
<View
className="flex w-full items-center justify-between"
onClick={(e) => {
if (!readOnly) {
e.stopPropagation();
// 设置临时编辑值为当前值
setTempEditValues((prev) => ({
...prev,
[value.orderPackageId || ""]:
editValues[value.orderPackageId || ""],
}));
setVisiblePopup((prev) => ({
...prev,
[value.orderPackageId || ""]: true,
}));
}
}}
>
<View className={!readOnly ? "cursor-pointer underline" : ""}>
{formatCurrency(value.boxCostPrice as number)}
</View>
{!readOnly && (
<View className="-m-2 ml-2 flex items-center justify-center p-2">
<Icon name={"pen-to-square"} size={16} color={"#1a73e8"} />
</View>
)}
</View>
);
},
},
];
const [columns, setColumns] = useState<any[]>(defaultColumns);
useEffect(() => {
if (!readOnly) {
setColumns([...defaultColumns]);
}
}, [readOnly]);
// 弹窗可见状态
const [visiblePopup, setVisiblePopup] = useState<{ [key: string]: boolean }>(
{},
);
// 编辑值的状态
const [editValues, setEditValues] = useState<{
[key: string]: {
boxCostPrice?: number;
boxSalePrice?: number;
boxProductWeight?: number;
};
}>({});
// 临时编辑值的状态(用于在保存前暂存编辑的值)
const [tempEditValues, setTempEditValues] = useState<{
[key: string]: {
boxCostPrice?: number;
boxSalePrice?: number;
boxProductWeight?: number;
};
}>({});
// 初始化编辑值
const initEditValues = (
pkgId: string,
boxCostPrice?: number,
boxSalePrice?: number,
boxProductWeight?: number,
) => {
const updates: {
editValuesUpdate?: {
boxCostPrice?: number;
boxSalePrice?: number;
boxProductWeight?: number;
};
tempEditValuesUpdate?: {
boxCostPrice?: number;
boxSalePrice?: number;
boxProductWeight?: number;
};
} = {};
if (!editValues[pkgId]) {
updates.editValuesUpdate = {
boxCostPrice,
boxSalePrice,
boxProductWeight,
};
}
// 同时初始化临时编辑值
if (!tempEditValues[pkgId]) {
updates.tempEditValuesUpdate = {
boxCostPrice,
boxSalePrice,
boxProductWeight,
};
}
return updates;
};
// 将所有包装信息合并并按品牌、型号、规格分组
const groupedPackageData = purchaseOrderVO.orderPackageList?.reduce(
(acc, orderPackage) => {
// 生成分组键
const key = `${orderPackage.boxBrandName}-${orderPackage.boxProductName}-${orderPackage.boxSpecId}`;
// 转换规格字段
const boxSpecId = orderPackage.boxSpecId;
const boxSpecName = orderPackage.boxSpecName;
if (!acc[key]) {
acc[key] = {
...orderPackage,
boxSpecId,
boxSpecName,
boxProductCount: 0,
};
}
// 累加数量
acc[key].boxProductCount += orderPackage.boxCount || 0;
return acc;
},
{} as Record<string, any>,
);
// 转换为数组格式
const packageData = Object.values(groupedPackageData || {});
// 计算合计数据
const calculateTotals = () => {
if (!packageData || packageData.length === 0) {
return {};
}
// 计算各项合计
let totalBoxProductCount = 0;
let totalBoxSalePayment = 0;
let totalBoxCostPayment = 0;
let totalBoxProductWeight = 0;
packageData.forEach((pkg: any) => {
totalBoxProductCount += pkg.boxProductCount || 0;
totalBoxSalePayment +=
Number((pkg?.boxSalePrice || 0) * pkg.boxProductCount) || 0;
totalBoxCostPayment +=
Number((pkg?.boxCostPrice || 0) * pkg.boxProductCount) || 0;
totalBoxProductWeight +=
Number((pkg?.boxProductWeight || 0) * pkg.boxProductCount) || 0;
});
return {
boxBrandName: "合计",
boxProductCount: totalBoxProductCount,
boxSalePayment: totalBoxSalePayment,
boxProductWeight: totalBoxProductWeight,
boxCostPrice: totalBoxCostPayment,
isTotalRow: true, // 标记这是合计行
};
};
const totalsData = calculateTotals();
// 初始化所有包装项的编辑值
useEffect(() => {
const newEditValues = { ...editValues };
const newTempEditValues = { ...tempEditValues };
let hasEditValuesChanged = false;
let hasTempEditValuesChanged = false;
packageData.forEach((pkg: BusinessAPI.OrderPackage) => {
const pkgId = pkg.orderPackageId || "";
const updates = initEditValues(
pkgId,
pkg.boxCostPrice,
pkg.boxSalePrice,
pkg.boxProductWeight,
);
if (updates.editValuesUpdate) {
newEditValues[pkgId] = updates.editValuesUpdate;
hasEditValuesChanged = true;
}
if (updates.tempEditValuesUpdate) {
newTempEditValues[pkgId] = updates.tempEditValuesUpdate;
hasTempEditValuesChanged = true;
}
});
if (hasEditValuesChanged) {
setEditValues(newEditValues);
}
if (hasTempEditValuesChanged) {
setTempEditValues(newTempEditValues);
}
}, [packageData]);
// 当editValues发生变化时更新purchaseOrderVO
useEffect(() => {
// 只有当onChange存在时才更新
if (onChange) {
// 创建新的purchaseOrderVO对象
const newPurchaseOrderVO = { ...purchaseOrderVO };
// 更新供应商列表中的包装信息
if (newPurchaseOrderVO.orderPackageList) {
newPurchaseOrderVO.orderPackageList =
newPurchaseOrderVO.orderPackageList.map((pkg) => {
const editValue = editValues[pkg.orderPackageId || ""];
if (editValue) {
return {
...pkg,
boxCostPrice:
editValue.boxCostPrice !== undefined
? editValue.boxCostPrice
: pkg.boxCostPrice,
boxSalePrice:
editValue.boxSalePrice !== undefined
? editValue.boxSalePrice
: pkg.boxSalePrice,
boxProductWeight:
editValue.boxProductWeight !== undefined
? editValue.boxProductWeight
: pkg.boxProductWeight,
};
}
return pkg;
});
}
// 调用onChange回调
onChange(newPurchaseOrderVO);
}
}, [editValues]);
// 自定义合计行渲染
const renderTableWithTotals = () => {
// 创建包含合计行的数据
const dataWithTotals = [...packageData];
if (Object.keys(totalsData).length > 0) {
dataWithTotals.push(totalsData);
}
// 自定义列配置,对合计行特殊处理
const columnsWithTotalsRender = columns.map((column) => {
if (column.key === "boxBrandName") {
// 品牌列显示"合计"
return {
...column,
render: (rowData: any) => {
if (rowData.isTotalRow) {
return (
<span style={{ fontWeight: "bold" }}>
{rowData.boxBrandName}
</span>
);
}
return rowData.boxBrandName;
},
};
} else if (column.key === "boxProductCount") {
// 个数列显示合计
return {
...column,
render: (rowData: any) => {
if (rowData.isTotalRow) {
return (
<span style={{ fontWeight: "bold" }}>
{rowData.boxProductCount}
</span>
);
}
return rowData.boxProductCount;
},
};
} else if (column.key === "boxSalePrice") {
// 销售单价列合计行处理
return {
...column,
render: (rowData: any) => {
if (rowData.isTotalRow) {
return (
<span style={{ fontWeight: "bold" }}>
{formatCurrency(rowData.boxSalePrice as number)}
</span>
);
}
return column.render(rowData, rowData);
},
};
} else if (column.key === "boxSalePayment") {
// 销售金额列显示合计
return {
...column,
render: (rowData: any) => {
if (rowData.isTotalRow) {
return (
<span style={{ fontWeight: "bold" }}>
{formatCurrency(rowData.boxSalePayment)}
</span>
);
}
return formatCurrency(
Number(
(rowData?.boxSalePrice || 0) * rowData.boxProductCount,
) as number,
);
},
};
} else if (column.key === "boxProductWeight") {
// 重量列合计行处理
return {
...column,
render: (rowData: any) => {
if (rowData.isTotalRow) {
return (
<span style={{ fontWeight: "bold" }}>
{formatCurrency(rowData.boxProductWeight)}
</span>
);
}
return column.render(rowData, rowData);
},
};
} else if (column.key === "boxCostPrice") {
// 成本单价列合计行处理
return {
...column,
render: (rowData: any) => {
if (rowData.isTotalRow) {
return (
<span style={{ fontWeight: "bold" }}>
{formatCurrency(rowData.boxCostPrice as number)}
</span>
);
}
return column.render(rowData, rowData);
},
};
}
// 其他列保持原有render函数或者默认显示
return column;
});
return (
<Table
className={"table-sum"}
columns={columnsWithTotalsRender}
data={dataWithTotals}
striped
/> />
); );
};
return (
<>
{renderTableWithTotals()}
{/* 纸箱价格编辑弹窗 */}
{packageData.map((pkg: BusinessAPI.OrderPackage) => (
<Popup
key={pkg.orderPackageId}
visible={visiblePopup[pkg.orderPackageId || ""]}
position="bottom"
title="编辑纸箱信息"
onClose={() =>
setVisiblePopup((prev) => ({
...prev,
[pkg.orderPackageId || ""]: false,
}))
}
onOverlayClick={() =>
setVisiblePopup((prev) => ({
...prev,
[pkg.orderPackageId || ""]: false,
}))
}
lockScroll
>
<View className="flex flex-col gap-3 p-2.5">
<View className="text-neutral-darkest text-sm font-medium">
</View>
<View className="border-neutral-base flex flex-row items-center rounded-md border border-solid">
<Input
className="placeholder:text-neutral-dark"
placeholder="请输入销售单价"
type="digit"
value={
tempEditValues[
pkg.orderPackageId || ""
]?.boxSalePrice?.toString() || ""
}
onChange={(value) => {
const numValue = validatePrice(value);
if (numValue !== undefined) {
setTempEditValues((prev) => ({
...prev,
[pkg.orderPackageId || ""]: {
...prev[pkg.orderPackageId || ""],
boxSalePrice: numValue as number,
},
}));
}
}}
/>
<View className="mr-2"></View>
</View>
<View className="text-neutral-darkest text-sm font-medium">
</View>
<View className="border-neutral-base flex flex-row items-center rounded-md border border-solid">
<Input
className="placeholder:text-neutral-dark"
placeholder="请输入成本单价"
type="digit"
value={
tempEditValues[
pkg.orderPackageId || ""
]?.boxCostPrice?.toString() || ""
}
onChange={(value) => {
const numValue = validatePrice(value);
if (numValue !== undefined) {
setTempEditValues((prev) => ({
...prev,
[pkg.orderPackageId || ""]: {
...prev[pkg.orderPackageId || ""],
boxCostPrice: numValue as number,
},
}));
}
}}
/>
<View className="mr-2"></View>
</View>
<View className="text-neutral-darkest text-sm font-medium">
</View>
<View className="border-neutral-base flex flex-row items-center rounded-md border border-solid">
<Input
className="placeholder:text-neutral-dark"
placeholder="请输入箱重"
type="digit"
value={
tempEditValues[
pkg.orderPackageId || ""
]?.boxProductWeight?.toString() || ""
}
onChange={(value) => {
const numValue = validatePrice(value);
if (numValue !== undefined) {
setTempEditValues((prev) => ({
...prev,
[pkg.orderPackageId || ""]: {
...prev[pkg.orderPackageId || ""],
boxProductWeight: numValue as number,
},
}));
}
}}
/>
<View className="mr-2"></View>
</View>
</View>
<View className="flex w-full flex-col bg-white">
<View className="flex flex-row gap-2 p-3">
<View className="flex-1">
<Button
size="large"
block
type="default"
onClick={() =>
setVisiblePopup((prev) => ({
...prev,
[pkg.orderPackageId || ""]: false,
}))
}
>
</Button>
</View>
<View className="flex-1">
<Button
size="large"
block
type="primary"
onClick={() => {
// 保存时才更新editValues状态
setEditValues((prev) => ({
...prev,
[pkg.orderPackageId || ""]: {
...tempEditValues[pkg.orderPackageId || ""],
},
}));
// 关闭弹窗
setVisiblePopup((prev) => ({
...prev,
[pkg.orderPackageId || ""]: false,
}));
}}
>
</Button>
</View>
</View>
</View>
<SafeArea position="bottom" />
</Popup>
))}
</>
);
} }

View File

@ -8,16 +8,16 @@ import {
} from "@nutui/nutui-react-taro"; } from "@nutui/nutui-react-taro";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Icon } from "@/components"; import { Icon } from "@/components";
import { business } from "@/services";
import { formatCurrency, validatePrice } from "@/utils/format"; import { formatCurrency, validatePrice } from "@/utils/format";
import { generateShortId } from "@/utils/generateShortId"; import { generateShortId } from "@/utils/generateShortId";
export default function LaborInfoSection(props: { export default function LaborInfoSection(props: {
purchaseOrderVO: BusinessAPI.PurchaseOrderVO; purchaseOrderVO: BusinessAPI.PurchaseOrderVO;
costItemVOList: BusinessAPI.CostItemVO[];
onChange?: (purchaseOrderVO: BusinessAPI.PurchaseOrderVO) => void; onChange?: (purchaseOrderVO: BusinessAPI.PurchaseOrderVO) => void;
readOnly?: boolean; readOnly?: boolean;
}) { }) {
const { purchaseOrderVO, onChange, readOnly } = props; const { purchaseOrderVO, onChange, readOnly, costItemVOList } = props;
// 弹窗相关状态 // 弹窗相关状态
const [visiblePopup, setVisiblePopup] = useState<{ [key: string]: boolean }>( const [visiblePopup, setVisiblePopup] = useState<{ [key: string]: boolean }>(
@ -41,17 +41,12 @@ export default function LaborInfoSection(props: {
requireQuantityAndPrice: false, requireQuantityAndPrice: false,
}); });
console.log("newCostData", newCostData);
// Picker可见状态 // Picker可见状态
const [pickerVisible, setPickerVisible] = useState({ const [pickerVisible, setPickerVisible] = useState({
costType: false, // 费用类型Picker costType: false, // 费用类型Picker
costItem: false, // 费用项目Picker costItem: false, // 费用项目Picker
}); });
// 费用项目列表
const [costItems, setCostItems] = useState<BusinessAPI.CostItemVO[]>([]);
// 编辑值的状态 // 编辑值的状态
const [editValues, setEditValues] = useState<{ const [editValues, setEditValues] = useState<{
[key: string]: { [key: string]: {
@ -108,25 +103,6 @@ export default function LaborInfoSection(props: {
}, },
]; ];
// 获取费用项目列表
useEffect(() => {
const fetchCostItems = async () => {
try {
const { data } = await business.costItem.listCostItem({
costItemListQry: {
status: true,
showInEntry: true,
},
});
setCostItems(data.data || []);
} catch (error) {
console.error("获取费用项目列表失败:", error);
}
};
fetchCostItems();
}, []);
// 人工费 // 人工费
const humanCosts = const humanCosts =
purchaseOrderVO.orderCostList?.filter( purchaseOrderVO.orderCostList?.filter(
@ -368,28 +344,30 @@ export default function LaborInfoSection(props: {
<View className="text-neutral-darkest text-sm font-medium"> <View className="text-neutral-darkest text-sm font-medium">
</View> </View>
<View className="border-neutral-base relative flex h-10 w-full items-center rounded-md border border-solid">
<View <View
className="border-neutral-base relative flex h-10 w-full items-center rounded-md border border-solid" className={
onClick={() => "flex flex-1 flex-row items-center justify-between px-5"
setPickerVisible((prev) => ({ ...prev, costItem: true }))
} }
style={{
color: "var(--nutui-color-title, #1a1a1a)",
}}
onClick={(event) => {
setPickerVisible((prev) => ({ ...prev, costItem: true }));
event.stopPropagation();
}}
> >
<Input <View className={"text-sm"}>
type="text" {newCostData.name || "请选择垫付项目"}
placeholder="请选择垫付项目" </View>
value={newCostData.name || ""} <Icon name={"chevron-down"} />
disabled </View>
/>
<Icon
name="chevron-down"
className="absolute -right-1 mr-4"
/>
</View> </View>
<Picker <Picker
title="请选择垫付项目" title="请选择垫付项目"
visible={pickerVisible.costItem} visible={pickerVisible.costItem}
options={[ options={[
costItems costItemVOList
.filter((item) => item.costType === newCostData.costType) .filter((item) => item.costType === newCostData.costType)
.filter((item) => { .filter((item) => {
if (newCostData.costType === "PRODUCTION_ADVANCE") { if (newCostData.costType === "PRODUCTION_ADVANCE") {
@ -410,7 +388,7 @@ export default function LaborInfoSection(props: {
]} ]}
onConfirm={(_, values) => { onConfirm={(_, values) => {
const selectedValue = values[0] as string; const selectedValue = values[0] as string;
const selectedItem = costItems.find( const selectedItem = costItemVOList.find(
(item) => item.itemId === selectedValue, (item) => item.itemId === selectedValue,
); );
if (selectedItem) { if (selectedItem) {
@ -651,7 +629,9 @@ export default function LaborInfoSection(props: {
{/* 产地垫付 */} {/* 产地垫付 */}
{productionAdvanceCosts.map((item) => { {productionAdvanceCosts.map((item) => {
// 获取费用项目的 requireQuantityAndPrice 属性 // 获取费用项目的 requireQuantityAndPrice 属性
const costItem = costItems.find((cost) => cost.itemId === item.itemId); const costItem = costItemVOList.find(
(cost) => cost.itemId === item.itemId,
);
const requireQuantityAndPrice = const requireQuantityAndPrice =
costItem?.requireQuantityAndPrice ?? false; costItem?.requireQuantityAndPrice ?? false;
@ -862,7 +842,9 @@ export default function LaborInfoSection(props: {
{/* 工头垫付 */} {/* 工头垫付 */}
{workerAdvanceCosts.map((item) => { {workerAdvanceCosts.map((item) => {
// 获取费用项目的 requireQuantityAndPrice 属性 // 获取费用项目的 requireQuantityAndPrice 属性
const costItem = costItems.find((cost) => cost.itemId === item.itemId); const costItem = costItemVOList.find(
(cost) => cost.itemId === item.itemId,
);
const requireQuantityAndPrice = const requireQuantityAndPrice =
costItem?.requireQuantityAndPrice ?? false; costItem?.requireQuantityAndPrice ?? false;

View File

@ -1,7 +1,7 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Text, View } from "@tarojs/components"; import { Text, View } from "@tarojs/components";
import { Button, Input, Popup, Radio, SafeArea } from "@nutui/nutui-react-taro"; import { Button, Input, Popup, Radio, SafeArea } from "@nutui/nutui-react-taro";
import { formatCurrency, validatePrice } from "@/utils/format"; import { validatePrice } from "@/utils/format";
import { PurchaseOrderCalculator } from "@/utils/PurchaseOrderCalculator"; import { PurchaseOrderCalculator } from "@/utils/PurchaseOrderCalculator";
export default function MarketPriceSection(props: { export default function MarketPriceSection(props: {
@ -134,7 +134,6 @@ export default function MarketPriceSection(props: {
const saleAmount = calculator.getSalesAmount(); const saleAmount = calculator.getSalesAmount();
const totalAmount = calculator.getTotalAmount(); const totalAmount = calculator.getTotalAmount();
console.log("totalAmount", totalAmount)
// 计算平均单价(所有供应商单价的平均值) // 计算平均单价(所有供应商单价的平均值)
const averagePurchasePrice = calculator.getAveragePurchasePrice(); const averagePurchasePrice = calculator.getAveragePurchasePrice();
@ -272,7 +271,7 @@ export default function MarketPriceSection(props: {
</View> </View>
<View className="text-neutral-darkest text-sm font-medium"> <View className="text-neutral-darkest text-sm font-medium">
{pricingMethod ? `${formatCurrency(saleAmount)}` : "-"} {pricingMethod ? `${saleAmount}` : "-"}
</View> </View>
</View> </View>
<View className="flex !h-8 flex-row items-center justify-between"> <View className="flex !h-8 flex-row items-center justify-between">
@ -280,17 +279,19 @@ export default function MarketPriceSection(props: {
</View> </View>
<View className="text-neutral-darkest text-sm font-medium"> <View className="text-neutral-darkest text-sm font-medium">
{pricingMethod {pricingMethod ? `${averagePurchasePrice} 元/斤` : "-"}
? `${formatCurrency(averagePurchasePrice)} 元/斤`
: "-"}
</View> </View>
</View> </View>
<View className="flex !h-8 flex-row items-center justify-between bg-yellow-200"> <View className="flex !h-8 flex-row items-center justify-between bg-yellow-200">
<View className="text-neutral-dark flex-shrink-0 text-sm"> <View className="text-neutral-dark flex-shrink-0 text-sm">
+
{purchaseOrderVO.orderDealer?.includePackingFlag
? "销售金额 + 包装费"
: "销售金额"}
</View> </View>
<View className="text-neutral-darkest text-sm font-medium"> <View className="text-neutral-darkest text-sm font-medium">
{pricingMethod ? `${formatCurrency(totalAmount)}` : "-"} {pricingMethod ? `${totalAmount}` : "-"}
</View> </View>
</View> </View>
@ -327,14 +328,11 @@ export default function MarketPriceSection(props: {
value={ value={
tempEditValues[ tempEditValues[
supplier.orderSupplierId || "" supplier.orderSupplierId || ""
]?.salePrice?.toString() || ]?.salePrice?.toString() || ""
editValues[
supplier.orderSupplierId || ""
]?.salePrice?.toString() ||
""
} }
onChange={(value) => { onChange={(value) => {
const numValue = validatePrice(value); const numValue = validatePrice(value);
console.log("num", numValue);
if (numValue !== undefined) { if (numValue !== undefined) {
setTempEditValues((prev) => ({ setTempEditValues((prev) => ({
...prev, ...prev,

View File

@ -9,15 +9,15 @@ import {
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { validatePrice } from "@/utils/format"; import { validatePrice } from "@/utils/format";
import { generateShortId } from "@/utils/generateShortId"; import { generateShortId } from "@/utils/generateShortId";
import { business } from "@/services";
import { Icon } from "@/components"; import { Icon } from "@/components";
export default function MaterialCostSection(props: { export default function MaterialCostSection(props: {
purchaseOrderVO: BusinessAPI.PurchaseOrderVO; purchaseOrderVO: BusinessAPI.PurchaseOrderVO;
costItemVOList: BusinessAPI.CostItemVO[];
onChange?: (purchaseOrderVO: BusinessAPI.PurchaseOrderVO) => void; onChange?: (purchaseOrderVO: BusinessAPI.PurchaseOrderVO) => void;
readOnly?: boolean; readOnly?: boolean;
}) { }) {
const { purchaseOrderVO, onChange, readOnly } = props; const { purchaseOrderVO, onChange, readOnly, costItemVOList } = props;
// 弹窗相关状态 // 弹窗相关状态
const [visiblePopup, setVisiblePopup] = useState<{ [key: string]: boolean }>( const [visiblePopup, setVisiblePopup] = useState<{ [key: string]: boolean }>(
@ -43,9 +43,6 @@ export default function MaterialCostSection(props: {
costItem: false, // 费用项目Picker costItem: false, // 费用项目Picker
}); });
// 费用项目列表
const [costItems, setCostItems] = useState<BusinessAPI.CostItemVO[]>([]);
// 编辑值的状态 // 编辑值的状态
const [editValues, setEditValues] = useState<{ const [editValues, setEditValues] = useState<{
[key: string]: { [key: string]: {
@ -112,26 +109,6 @@ export default function MaterialCostSection(props: {
return updates; return updates;
}; };
// 获取费用项目列表
useEffect(() => {
const fetchCostItems = async () => {
try {
const { data } = await business.costItem.listCostItem({
costItemListQry: {
status: true,
showInEntry: true,
costType: "PACKAGING_MATERIALS", // 只获取辅料费用项目
},
});
setCostItems(data.data || []);
} catch (error) {
console.error("获取费用项目列表失败:", error);
}
};
fetchCostItems();
}, []);
// 辅料费(商标费和网套费) // 辅料费(商标费和网套费)
const packagingMaterials = const packagingMaterials =
purchaseOrderVO.orderCostList?.filter( purchaseOrderVO.orderCostList?.filter(
@ -346,25 +323,31 @@ export default function MaterialCostSection(props: {
<View className="text-neutral-darkest text-sm font-medium"> <View className="text-neutral-darkest text-sm font-medium">
</View> </View>
<View className="border-neutral-base relative flex h-10 w-full items-center rounded-md border border-solid">
<View <View
className="border-neutral-base relative flex h-10 w-full items-center rounded-md border border-solid" className={
onClick={() => "flex flex-1 flex-row items-center justify-between px-5"
setPickerVisible((prev) => ({ ...prev, costItem: true }))
} }
style={{
color: "var(--nutui-color-title, #1a1a1a)",
}}
onClick={(event) => {
setPickerVisible((prev) => ({ ...prev, costItem: true }));
event.stopPropagation();
}}
> >
<Input <View className={"text-sm"}>
type="text" {newCostData.name || "请选择辅料项目"}
placeholder="请选择辅料项目" </View>
value={newCostData.name || ""} <Icon name={"chevron-down"} />
disabled </View>
/>
<Icon name="chevron-down" className="absolute -right-1 mr-4" />
</View> </View>
<Picker <Picker
title="请选择辅料项目" title="请选择辅料项目"
visible={pickerVisible.costItem} visible={pickerVisible.costItem}
options={[ options={[
costItems costItemVOList
.filter((item) => item.costType === "PACKAGING_MATERIALS")
.filter((item) => { .filter((item) => {
// 检查该项目是否已经被选择 // 检查该项目是否已经被选择
return !packagingMaterials.some( return !packagingMaterials.some(
@ -378,7 +361,7 @@ export default function MaterialCostSection(props: {
]} ]}
onConfirm={(_, values) => { onConfirm={(_, values) => {
const selectedValue = values[0] as string; const selectedValue = values[0] as string;
const selectedItem = costItems.find( const selectedItem = costItemVOList.find(
(item) => item.itemId === selectedValue, (item) => item.itemId === selectedValue,
); );
if (selectedItem) { if (selectedItem) {
@ -608,7 +591,9 @@ export default function MaterialCostSection(props: {
{/* 辅料费编辑弹窗 */} {/* 辅料费编辑弹窗 */}
{packagingMaterials.map((item) => { {packagingMaterials.map((item) => {
// 获取费用项目的 requireQuantityAndPrice 属性 // 获取费用项目的 requireQuantityAndPrice 属性
const costItem = costItems.find((cost) => cost.itemId === item.itemId); const costItem = costItemVOList.find(
(cost) => cost.itemId === item.itemId,
);
const requireQuantityAndPrice = const requireQuantityAndPrice =
costItem?.requireQuantityAndPrice ?? false; costItem?.requireQuantityAndPrice ?? false;

View File

@ -13,10 +13,11 @@ import { generateShortId } from "@/utils/generateShortId";
export default function PackagingCostSection(props: { export default function PackagingCostSection(props: {
purchaseOrderVO: BusinessAPI.PurchaseOrderVO; purchaseOrderVO: BusinessAPI.PurchaseOrderVO;
costItemVOList: BusinessAPI.CostItemVO[];
onChange?: (purchaseOrderVO: BusinessAPI.PurchaseOrderVO) => void; onChange?: (purchaseOrderVO: BusinessAPI.PurchaseOrderVO) => void;
readOnly?: boolean; readOnly?: boolean;
}) { }) {
const { purchaseOrderVO, onChange, readOnly } = props; const { purchaseOrderVO, onChange, readOnly, costItemVOList } = props;
// 弹窗相关状态 // 弹窗相关状态
const [visiblePopup, setVisiblePopup] = useState<{ [key: string]: boolean }>( const [visiblePopup, setVisiblePopup] = useState<{ [key: string]: boolean }>(
@ -28,15 +29,16 @@ export default function PackagingCostSection(props: {
// 新增包装费表单数据 // 新增包装费表单数据
const [newCostData, setNewCostData] = useState({ const [newCostData, setNewCostData] = useState({
costType: "", // 费用类型 costType: "FIXED_COST", // 费用类型
itemId: "", // 费用项目ID itemId: "", // 费用项目ID
name: "", // 费用名称 name: "", // 费用名称
payee: "", // 收款人 quantity: 0, // 数量(人数)
quantity: "", // 数量(包数或人数) unit: "", // 单位
unitPrice: "", // 单价 unitPrice: "", // 单价
amount: "", // 金额(固定费用和其他费用使用) amount: "", // 金额
payerType: "", // 费用承担方,默认我方 payerType: "US", // 费用承担方,默认我方
principal: "", // 工头姓名 principal: "", // 工头姓名
requireQuantityAndPrice: false,
}); });
// Picker可见状态 // Picker可见状态
@ -99,13 +101,6 @@ export default function PackagingCostSection(props: {
{ label: "其他费用", value: "OTHER_COST" }, { label: "其他费用", value: "OTHER_COST" },
]; ];
// 固定费用选项
const fixedCostOptions = [
{ label: "计提费", value: "accrual" },
{ label: "王超费用", value: "wangchao" },
{ label: "收代办费", value: "agency" },
];
// 固定费用 // 固定费用
const fixedCosts = const fixedCosts =
purchaseOrderVO.orderCostList?.filter( purchaseOrderVO.orderCostList?.filter(
@ -122,12 +117,16 @@ export default function PackagingCostSection(props: {
useEffect(() => { useEffect(() => {
if (onChange) { if (onChange) {
// 检查是否已存在计提费 // 检查是否已存在计提费
const accrualFeeItem = costItemVOList.find(
(item) => item.name === "计提费" && item.costType === "FIXED_COST",
);
const hasAccrualFee = fixedCosts.some((item) => item.name === "计提费"); const hasAccrualFee = fixedCosts.some((item) => item.name === "计提费");
if (!hasAccrualFee) { if (!hasAccrualFee && accrualFeeItem) {
// 创建默认计提费 // 创建默认计提费
const defaultAccrualFee: BusinessAPI.OrderCost = { const defaultAccrualFee: BusinessAPI.OrderCost = {
itemId: generateShortId(), itemId: accrualFeeItem.itemId,
orderCostId: generateShortId(), orderCostId: generateShortId(),
name: "计提费", name: "计提费",
costType: "FIXED_COST", costType: "FIXED_COST",
@ -207,7 +206,9 @@ export default function PackagingCostSection(props: {
<> <>
<View className="flex flex-col gap-2.5"> <View className="flex flex-col gap-2.5">
{/* 固定费用 */} {/* 固定费用 */}
{fixedCosts.map((item, index) => { {fixedCosts
.filter((item) => item.name !== "纸箱费" && item.name !== "空箱费")
.map((item, index) => {
// 初始化编辑值 // 初始化编辑值
initEditValues(item.orderCostId, item.count, item.price); initEditValues(item.orderCostId, item.count, item.price);
@ -234,7 +235,11 @@ export default function PackagingCostSection(props: {
)} )}
{!readOnly && ( {!readOnly && (
<View className="ml-1 text-gray-500"> <View className="ml-1 text-gray-500">
<Icon name={"pen-to-square"} size={16} color={"#1a73e8"} /> <Icon
name={"pen-to-square"}
size={16}
color={"#1a73e8"}
/>
</View> </View>
)} )}
</View> </View>
@ -326,11 +331,12 @@ export default function PackagingCostSection(props: {
...prev, ...prev,
costType: option.value, costType: option.value,
itemId: "", itemId: "",
name: "", name: "", // 费用名称
payee: "", quantity: 0, // 数量
quantity: "", unit: "", // 单位
unitPrice: "", unitPrice: "", // 单价
amount: "", amount: "", // 金额
requireQuantityAndPrice: false,
})); }));
}} }}
> >
@ -351,37 +357,62 @@ export default function PackagingCostSection(props: {
setPickerVisible((prev) => ({ ...prev, fixedCost: true })) setPickerVisible((prev) => ({ ...prev, fixedCost: true }))
} }
> >
<Input <View
type="text" className={
placeholder="请选择固定费用类型" "flex flex-1 flex-row items-center justify-between px-5"
value={newCostData.name || ""} }
disabled style={{
/> color: "var(--nutui-color-title, #1a1a1a)",
<Icon }}
name="chevron-down" onClick={(event) => {
className="absolute -right-1 mr-4" setPickerVisible((prev) => ({ ...prev, costItem: true }));
/> event.stopPropagation();
}}
>
<View className={"text-sm"}>
{newCostData.name || "请选择固定费用类型"}
</View> </View>
<Icon name={"chevron-down"} />
</View>
</View>
<Picker <Picker
title="请选择固定费用类型" title="请选择垫付项目"
visible={pickerVisible.fixedCost} visible={pickerVisible.fixedCost}
options={[ options={[
fixedCostOptions.filter((option) => { costItemVOList
// 检查该固定费用类型是否已存在于fixedCosts中 .filter((item) => item.costType === newCostData.costType)
.filter((item) => {
// 检查该项目是否已经被选择
return !fixedCosts.some( return !fixedCosts.some(
(cost) => cost.name === option.label, (hc) => hc.itemId === item.itemId,
); );
}), })
.map((item) => ({
label: item.name,
value: item.itemId,
})),
]} ]}
onConfirm={(_, values) => { onConfirm={(_, values) => {
const selectedValue = values[0] as string; const selectedValue = values[0] as string;
const selectedItem = fixedCostOptions.find( const selectedItem = costItemVOList.find(
(option) => option.value === selectedValue, (item) => item.itemId === selectedValue,
); );
if (selectedItem) { if (selectedItem) {
setNewCostData((prev) => ({ setNewCostData((prev) => ({
...prev, ...prev,
name: selectedItem.label, itemId: selectedValue,
name: selectedItem.name,
payerType: "US",
quantity: selectedItem.requireQuantityAndPrice ? 0 : 1,
unit: selectedItem.requireQuantityAndPrice
? selectedItem.unit
: "元", // 单位
unitPrice: "", // 单价
amount: "", // 金额
principal: "", // 工头姓名
requireQuantityAndPrice:
selectedItem.requireQuantityAndPrice,
})); }));
} }
setPickerVisible((prev) => ({ ...prev, fixedCost: false })); setPickerVisible((prev) => ({ ...prev, fixedCost: false }));
@ -423,7 +454,7 @@ export default function PackagingCostSection(props: {
<Input <Input
className="placeholder:text-neutral-dark flex-1" className="placeholder:text-neutral-dark flex-1"
placeholder="请输入收款人姓名" placeholder="请输入收款人姓名"
value={newCostData.payee} value={newCostData.principal}
onChange={(value) => { onChange={(value) => {
setNewCostData((prev) => ({ setNewCostData((prev) => ({
...prev, ...prev,
@ -474,15 +505,16 @@ export default function PackagingCostSection(props: {
setShowAddCostPopup(false); setShowAddCostPopup(false);
// 重置表单 // 重置表单
setNewCostData({ setNewCostData({
itemId: generateShortId(),
costType: "",
payerType: "", payerType: "",
principal: "", principal: "",
costType: "", name: "", // 费用名称
itemId: generateShortId(), quantity: 0, // 数量
name: "", unit: "", // 单位
payee: "", unitPrice: "", // 单价
quantity: "", amount: "", // 金额
unitPrice: "", requireQuantityAndPrice: false,
amount: "",
}); });
}} }}
> >
@ -503,7 +535,7 @@ export default function PackagingCostSection(props: {
if ( if (
newCostData.costType === "OTHER_COST" && newCostData.costType === "OTHER_COST" &&
(!newCostData.name || !newCostData.payee) (!newCostData.name || !newCostData.principal)
) { ) {
console.log("请填写费用名称和收款人姓名"); console.log("请填写费用名称和收款人姓名");
return; return;
@ -569,15 +601,16 @@ export default function PackagingCostSection(props: {
setShowAddCostPopup(false); setShowAddCostPopup(false);
// 重置表单 // 重置表单
setNewCostData({ setNewCostData({
itemId: generateShortId(),
costType: "",
payerType: "", payerType: "",
principal: "", principal: "",
costType: "", name: "", // 费用名称
itemId: generateShortId(), quantity: 0, // 数量
name: "", unit: "", // 单位
payee: "", unitPrice: "", // 单价
quantity: "", amount: "", // 金额
unitPrice: "", requireQuantityAndPrice: false,
amount: "",
}); });
}} }}
> >

View File

@ -8,16 +8,16 @@ import {
} from "@nutui/nutui-react-taro"; } from "@nutui/nutui-react-taro";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Icon } from "@/components"; import { Icon } from "@/components";
import { business } from "@/services";
import { formatCurrency, validatePrice } from "@/utils/format"; import { formatCurrency, validatePrice } from "@/utils/format";
import { generateShortId } from "@/utils/generateShortId"; import { generateShortId } from "@/utils/generateShortId";
export default function ProductionAdvanceSection(props: { export default function ProductionAdvanceSection(props: {
purchaseOrderVO: BusinessAPI.PurchaseOrderVO; purchaseOrderVO: BusinessAPI.PurchaseOrderVO;
costItemVOList: BusinessAPI.CostItemVO[];
onChange?: (purchaseOrderVO: BusinessAPI.PurchaseOrderVO) => void; onChange?: (purchaseOrderVO: BusinessAPI.PurchaseOrderVO) => void;
readOnly?: boolean; readOnly?: boolean;
}) { }) {
const { purchaseOrderVO, onChange, readOnly } = props; const { purchaseOrderVO, onChange, readOnly, costItemVOList } = props;
// 弹窗相关状态 // 弹窗相关状态
const [visiblePopup, setVisiblePopup] = useState<{ [key: string]: boolean }>( const [visiblePopup, setVisiblePopup] = useState<{ [key: string]: boolean }>(
@ -41,17 +41,12 @@ export default function ProductionAdvanceSection(props: {
requireQuantityAndPrice: false, requireQuantityAndPrice: false,
}); });
console.log("newCostData", newCostData);
// Picker可见状态 // Picker可见状态
const [pickerVisible, setPickerVisible] = useState({ const [pickerVisible, setPickerVisible] = useState({
costType: false, // 费用类型Picker costType: false, // 费用类型Picker
costItem: false, // 费用项目Picker costItem: false, // 费用项目Picker
}); });
// 费用项目列表
const [costItems, setCostItems] = useState<BusinessAPI.CostItemVO[]>([]);
// 编辑值的状态 // 编辑值的状态
const [editValues, setEditValues] = useState<{ const [editValues, setEditValues] = useState<{
[key: string]: { [key: string]: {
@ -99,25 +94,6 @@ export default function ProductionAdvanceSection(props: {
} }
}; };
// 获取费用项目列表
useEffect(() => {
const fetchCostItems = async () => {
try {
const { data } = await business.costItem.listCostItem({
costItemListQry: {
status: true,
showInEntry: true,
},
});
setCostItems(data.data || []);
} catch (error) {
console.error("获取费用项目列表失败:", error);
}
};
fetchCostItems();
}, []);
// 产地垫付 // 产地垫付
const productionAdvanceCosts = const productionAdvanceCosts =
purchaseOrderVO.orderCostList?.filter( purchaseOrderVO.orderCostList?.filter(
@ -176,6 +152,7 @@ export default function ProductionAdvanceSection(props: {
return ( return (
<> <>
<View className={"flex flex-col gap-2.5 divide-y divide-[#eaeaea]"}> <View className={"flex flex-col gap-2.5 divide-y divide-[#eaeaea]"}>
{productionAdvanceCosts && productionAdvanceCosts.length > 0 && (
<View className="flex flex-col gap-2.5"> <View className="flex flex-col gap-2.5">
<View className={"text-sm font-bold"}> <View className={"text-sm font-bold"}>
{purchaseOrderVO.originPrincipal} {purchaseOrderVO.originPrincipal}
@ -228,6 +205,7 @@ export default function ProductionAdvanceSection(props: {
); );
})} })}
</View> </View>
)}
{!readOnly && ( {!readOnly && (
<Button <Button
@ -258,28 +236,30 @@ export default function ProductionAdvanceSection(props: {
<View className="text-neutral-darkest text-sm font-medium"> <View className="text-neutral-darkest text-sm font-medium">
</View> </View>
<View className="border-neutral-base relative flex h-10 w-full items-center rounded-md border border-solid">
<View <View
className="border-neutral-base relative flex h-10 w-full items-center rounded-md border border-solid" className={
onClick={() => "flex flex-1 flex-row items-center justify-between px-5"
setPickerVisible((prev) => ({ ...prev, costItem: true }))
} }
style={{
color: "var(--nutui-color-title, #1a1a1a)",
}}
onClick={(event) => {
setPickerVisible((prev) => ({ ...prev, costItem: true }));
event.stopPropagation();
}}
> >
<Input <View className={"text-sm"}>
type="text" {newCostData.name || "请选择垫付项目"}
placeholder="请选择垫付项目" </View>
value={newCostData.name || ""} <Icon name={"chevron-down"} />
disabled </View>
/>
<Icon
name="chevron-down"
className="absolute -right-1 mr-4"
/>
</View> </View>
<Picker <Picker
title="请选择垫付项目" title="请选择垫付项目"
visible={pickerVisible.costItem} visible={pickerVisible.costItem}
options={[ options={[
costItems costItemVOList
.filter((item) => item.costType === newCostData.costType) .filter((item) => item.costType === newCostData.costType)
.filter((item) => { .filter((item) => {
// 检查该项目是否已经被选择 // 检查该项目是否已经被选择
@ -294,7 +274,7 @@ export default function ProductionAdvanceSection(props: {
]} ]}
onConfirm={(_, values) => { onConfirm={(_, values) => {
const selectedValue = values[0] as string; const selectedValue = values[0] as string;
const selectedItem = costItems.find( const selectedItem = costItemVOList.find(
(item) => item.itemId === selectedValue, (item) => item.itemId === selectedValue,
); );
if (selectedItem) { if (selectedItem) {

View File

@ -25,7 +25,7 @@ export default function RebateCalcSection(props: {
const { data } = const { data } =
await business.dealerRebateCustomer.showDealerRebateCustomer({ await business.dealerRebateCustomer.showDealerRebateCustomer({
dealerRebateCustomerShowQry: { dealerRebateCustomerShowQry: {
dealerId: purchaseOrderVO.orderDealer.dealerId, dealerId: purchaseOrderVO.orderDealer?.dealerId,
status: true, status: true,
}, },
}); });

View File

@ -16,12 +16,12 @@ export default function TaxProvisionSection(props: {
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const [taxProvision, setTaxProvision] = useState<number>( const [taxProvision, setTaxProvision] = useState<number>(
purchaseOrderVO.orderDealer.taxProvision || 0, purchaseOrderVO.orderDealer?.taxProvision || 0,
); );
// 当dealerVO变化时自动计算计提税金 // 当dealerVO变化时自动计算计提税金
useEffect(() => { useEffect(() => {
if (!purchaseOrderVO.orderDealer.taxProvision) { if (!purchaseOrderVO.orderDealer?.taxProvision) {
const defaultValue = calculator.getDefaultTaxProvision(); const defaultValue = calculator.getDefaultTaxProvision();
setTaxProvision(defaultValue); setTaxProvision(defaultValue);
// 更新父组件的状态 // 更新父组件的状态

View File

@ -16,12 +16,12 @@ export default function TaxSubsidySection(props: {
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const [taxSubsidy, setTaxSubsidy] = useState<number>( const [taxSubsidy, setTaxSubsidy] = useState<number>(
purchaseOrderVO.orderDealer.taxSubsidy || 0, purchaseOrderVO.orderDealer?.taxSubsidy || 0,
); );
// 当dealerVO变化时自动计算税费补贴 // 当dealerVO变化时自动计算税费补贴
useEffect(() => { useEffect(() => {
if (!purchaseOrderVO.orderDealer.taxSubsidy) { if (!purchaseOrderVO.orderDealer?.taxSubsidy) {
const defaultValue = calculator.getDefaultTaxSubsidy(); const defaultValue = calculator.getDefaultTaxSubsidy();
setTaxSubsidy(defaultValue); setTaxSubsidy(defaultValue);
// 更新父组件的状态 // 更新父组件的状态

View File

@ -8,16 +8,16 @@ import {
} from "@nutui/nutui-react-taro"; } from "@nutui/nutui-react-taro";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Icon } from "@/components"; import { Icon } from "@/components";
import { business } from "@/services";
import { formatCurrency, validatePrice } from "@/utils/format"; import { formatCurrency, validatePrice } from "@/utils/format";
import { generateShortId } from "@/utils/generateShortId"; import { generateShortId } from "@/utils/generateShortId";
export default function WorkerAdvanceSection(props: { export default function WorkerAdvanceSection(props: {
purchaseOrderVO: BusinessAPI.PurchaseOrderVO; purchaseOrderVO: BusinessAPI.PurchaseOrderVO;
costItemVOList: BusinessAPI.CostItemVO[];
onChange?: (purchaseOrderVO: BusinessAPI.PurchaseOrderVO) => void; onChange?: (purchaseOrderVO: BusinessAPI.PurchaseOrderVO) => void;
readOnly?: boolean; readOnly?: boolean;
}) { }) {
const { purchaseOrderVO, onChange, readOnly } = props; const { purchaseOrderVO, onChange, readOnly, costItemVOList } = props;
// 弹窗相关状态 // 弹窗相关状态
const [visiblePopup, setVisiblePopup] = useState<{ [key: string]: boolean }>( const [visiblePopup, setVisiblePopup] = useState<{ [key: string]: boolean }>(
@ -41,17 +41,12 @@ export default function WorkerAdvanceSection(props: {
requireQuantityAndPrice: false, requireQuantityAndPrice: false,
}); });
console.log("newCostData", newCostData);
// Picker可见状态 // Picker可见状态
const [pickerVisible, setPickerVisible] = useState({ const [pickerVisible, setPickerVisible] = useState({
costType: false, // 费用类型Picker costType: false, // 费用类型Picker
costItem: false, // 费用项目Picker costItem: false, // 费用项目Picker
}); });
// 费用项目列表
const [costItems, setCostItems] = useState<BusinessAPI.CostItemVO[]>([]);
// 编辑值的状态 // 编辑值的状态
const [editValues, setEditValues] = useState<{ const [editValues, setEditValues] = useState<{
[key: string]: { [key: string]: {
@ -99,25 +94,6 @@ export default function WorkerAdvanceSection(props: {
} }
}; };
// 获取费用项目列表
useEffect(() => {
const fetchCostItems = async () => {
try {
const { data } = await business.costItem.listCostItem({
costItemListQry: {
status: true,
showInEntry: true,
},
});
setCostItems(data.data || []);
} catch (error) {
console.error("获取费用项目列表失败:", error);
}
};
fetchCostItems();
}, []);
// 人工费 // 人工费
const humanCosts = const humanCosts =
purchaseOrderVO.orderCostList?.filter( purchaseOrderVO.orderCostList?.filter(
@ -186,6 +162,7 @@ export default function WorkerAdvanceSection(props: {
<> <>
<View className={"flex flex-col gap-2.5 divide-y divide-[#eaeaea]"}> <View className={"flex flex-col gap-2.5 divide-y divide-[#eaeaea]"}>
<View className="flex flex-col gap-2.5"> <View className="flex flex-col gap-2.5">
{humanCosts && humanCosts.length > 0 && (
<View className={"flex flex-col gap-2.5"}> <View className={"flex flex-col gap-2.5"}>
<View className={"text-sm font-bold"}> <View className={"text-sm font-bold"}>
{humanCosts?.[0].principal} {humanCosts?.[0].principal}
@ -202,6 +179,8 @@ export default function WorkerAdvanceSection(props: {
})} })}
</View> </View>
</View> </View>
)}
{/* 工头垫付 */} {/* 工头垫付 */}
{workerAdvanceCosts.map((item, index) => { {workerAdvanceCosts.map((item, index) => {
// 初始化编辑值,包括费用承担方和工头姓名 // 初始化编辑值,包括费用承担方和工头姓名
@ -250,7 +229,6 @@ export default function WorkerAdvanceSection(props: {
); );
})} })}
</View> </View>
{!readOnly && ( {!readOnly && (
<Button <Button
type={"primary"} type={"primary"}
@ -280,28 +258,30 @@ export default function WorkerAdvanceSection(props: {
<View className="text-neutral-darkest text-sm font-medium"> <View className="text-neutral-darkest text-sm font-medium">
</View> </View>
<View className="border-neutral-base relative flex h-10 w-full items-center rounded-md border border-solid">
<View <View
className="border-neutral-base relative flex h-10 w-full items-center rounded-md border border-solid" className={
onClick={() => "flex flex-1 flex-row items-center justify-between px-5"
setPickerVisible((prev) => ({ ...prev, costItem: true }))
} }
style={{
color: "var(--nutui-color-title, #1a1a1a)",
}}
onClick={(event) => {
setPickerVisible((prev) => ({ ...prev, costItem: true }));
event.stopPropagation();
}}
> >
<Input <View className={"text-sm"}>
type="text" {newCostData.name || "请选择垫付项目"}
placeholder="请选择垫付项目" </View>
value={newCostData.name || ""} <Icon name={"chevron-down"} />
disabled </View>
/>
<Icon
name="chevron-down"
className="absolute -right-1 mr-4"
/>
</View> </View>
<Picker <Picker
title="请选择垫付项目" title="请选择垫付项目"
visible={pickerVisible.costItem} visible={pickerVisible.costItem}
options={[ options={[
costItems costItemVOList
.filter((item) => item.costType === newCostData.costType) .filter((item) => item.costType === newCostData.costType)
.filter((item) => { .filter((item) => {
return !workerAdvanceCosts.some( return !workerAdvanceCosts.some(
@ -315,7 +295,7 @@ export default function WorkerAdvanceSection(props: {
]} ]}
onConfirm={(_, values) => { onConfirm={(_, values) => {
const selectedValue = values[0] as string; const selectedValue = values[0] as string;
const selectedItem = costItems.find( const selectedItem = costItemVOList.find(
(item) => item.itemId === selectedValue, (item) => item.itemId === selectedValue,
); );
if (selectedItem) { if (selectedItem) {
@ -530,7 +510,7 @@ export default function WorkerAdvanceSection(props: {
setShowAddCostPopup(false); setShowAddCostPopup(false);
// 重置表单 // 重置表单
setNewCostData({ setNewCostData({
costType: "PRODUCTION_ADVANCE", // 费用类型 costType: "WORKER_ADVANCE", // 费用类型
itemId: "", itemId: "",
name: "", // 费用名称 name: "", // 费用名称
quantity: 0, // 数量(人数) quantity: 0, // 数量(人数)

View File

@ -19,6 +19,7 @@ import classNames from "classnames";
import { business, poster } from "@/services"; import { business, poster } from "@/services";
import dayjs from "dayjs"; import dayjs from "dayjs";
import buildUrl from "@/utils/buildUrl"; import buildUrl from "@/utils/buildUrl";
import { Icon } from "@/components";
// 特殊处理:其他费用要实时获取费用项目 // 特殊处理:其他费用要实时获取费用项目
const updateOtherFeesModule = async ( const updateOtherFeesModule = async (
@ -432,37 +433,24 @@ export default hocAuth(function Page(props: CommonComponent) {
<View <View
className={`flex h-10 w-full items-center rounded-md ${formErrors.estimatedArrivalDate ? "border-4 border-red-500" : "border-4 border-gray-300"}`} className={`flex h-10 w-full items-center rounded-md ${formErrors.estimatedArrivalDate ? "border-4 border-red-500" : "border-4 border-gray-300"}`}
> >
<Input <View
value={ className={
shipOrderVO?.estimatedArrivalDate "flex flex-1 flex-row items-center justify-between px-5"
}
style={{
color: "var(--nutui-color-title, #1a1a1a)",
}}
onClick={() => setShow(true)}
>
<View className={"text-sm"}>
{shipOrderVO?.estimatedArrivalDate
? dayjs(shipOrderVO?.estimatedArrivalDate).format( ? dayjs(shipOrderVO?.estimatedArrivalDate).format(
"YYYY年MM月DD日", "YYYY年MM月DD日",
) )
: "" : column.fieldProps?.placeholder || `${column.title}`}
} </View>
placeholder={ <Icon name={"chevron-down"} />
column.fieldProps?.placeholder || `${column.title}` </View>
}
type="text"
className="flex-1"
disabled
onClick={() => setShow(true)}
onBlur={() => {
// 失焦时校验
if (!shipOrderVO?.estimatedArrivalDate) {
setFormErrors((prev) => ({
...prev,
estimatedArrivalDate: true,
}));
Toast.show("toast", {
icon: "fail",
title:
column.fieldProps?.placeholder ||
`请选择${column.title}`,
});
}
}}
/>
<DatePicker <DatePicker
title="发货时间选择" title="发货时间选择"
type="date" type="date"

View File

@ -19,6 +19,7 @@ import classNames from "classnames";
import { business } from "@/services"; import { business } from "@/services";
import dayjs from "dayjs"; import dayjs from "dayjs";
import buildUrl from "@/utils/buildUrl"; import buildUrl from "@/utils/buildUrl";
import { Icon } from "@/components";
export default hocAuth(function Page(props: CommonComponent) { export default hocAuth(function Page(props: CommonComponent) {
const { router, setLoading } = props; const { router, setLoading } = props;
@ -360,37 +361,24 @@ export default hocAuth(function Page(props: CommonComponent) {
<View <View
className={`flex h-10 w-full items-center rounded-md ${formErrors.estimatedArrivalDate ? "border-4 border-red-500" : "border-4 border-gray-300"}`} className={`flex h-10 w-full items-center rounded-md ${formErrors.estimatedArrivalDate ? "border-4 border-red-500" : "border-4 border-gray-300"}`}
> >
<Input <View
value={ className={
shipOrderVO?.estimatedArrivalDate "flex flex-1 flex-row items-center justify-between px-5"
}
style={{
color: "var(--nutui-color-title, #1a1a1a)",
}}
onClick={() => setShow(true)}
>
<View className={"text-sm"}>
{shipOrderVO?.estimatedArrivalDate
? dayjs(shipOrderVO?.estimatedArrivalDate).format( ? dayjs(shipOrderVO?.estimatedArrivalDate).format(
"YYYY年MM月DD日", "YYYY年MM月DD日",
) )
: "" : column.fieldProps?.placeholder || `${column.title}`}
} </View>
placeholder={ <Icon name={"chevron-down"} />
column.fieldProps?.placeholder || `${column.title}` </View>
}
type="text"
className="flex-1"
disabled
onClick={() => setShow(true)}
onBlur={() => {
// 失焦时校验
if (!shipOrderVO?.estimatedArrivalDate) {
setFormErrors((prev) => ({
...prev,
estimatedArrivalDate: true,
}));
Toast.show("toast", {
icon: "fail",
title:
column.fieldProps?.placeholder ||
`请选择${column.title}`,
});
}
}}
/>
<DatePicker <DatePicker
title="发货时间选择" title="发货时间选择"
type="date" type="date"

View File

@ -97,7 +97,7 @@ export default hocAuth(function Page(props: CommonComponent) {
{formatCurrency(calculator.getSupplierPurchaseCost())} {formatCurrency(calculator.getSupplierPurchaseCost())}
</View> </View>
</View> </View>
{purchaseOrderVO.orderDealer.freightCostFlag && ( {purchaseOrderVO.orderDealer?.freightCostFlag && (
<View className="cost-item flex flex-col px-3 py-2"> <View className="cost-item flex flex-col px-3 py-2">
<View className="text-sm text-gray-500"></View> <View className="text-sm text-gray-500"></View>
<View className="font-medium"> <View className="font-medium">
@ -143,13 +143,13 @@ export default hocAuth(function Page(props: CommonComponent) {
<View className="cost-item flex flex-col px-3 py-2"> <View className="cost-item flex flex-col px-3 py-2">
<View className="text-sm text-gray-500"></View> <View className="text-sm text-gray-500"></View>
<View className="font-medium"> <View className="font-medium">
{purchaseOrderVO.orderDealer.taxSubsidy} {purchaseOrderVO.orderDealer?.taxSubsidy}
</View> </View>
</View> </View>
<View className="cost-item flex flex-col px-3 py-2"> <View className="cost-item flex flex-col px-3 py-2">
<View className="text-sm text-gray-500"></View> <View className="text-sm text-gray-500"></View>
<View className="font-medium"> <View className="font-medium">
{purchaseOrderVO.orderDealer.taxSubsidy} {purchaseOrderVO.orderDealer?.taxSubsidy}
</View> </View>
</View> </View>
<View className="cost-total col-span-2 grid grid-cols-2 bg-yellow-50 px-3 py-2"> <View className="cost-total col-span-2 grid grid-cols-2 bg-yellow-50 px-3 py-2">

View File

@ -30,6 +30,7 @@ import { generateShortId } from "@/utils/generateShortId";
import Taro from "@tarojs/taro"; import Taro from "@tarojs/taro";
import buildUrl from "@/utils/buildUrl"; import buildUrl from "@/utils/buildUrl";
import { SupplierWeightCalculator } from "@/utils/SupplierWeightCalculator"; import { SupplierWeightCalculator } from "@/utils/SupplierWeightCalculator";
import { PurchaseOrderCalculator } from "@/utils/PurchaseOrderCalculator";
const defaultSupplierList: SupplierVO[] = [ const defaultSupplierList: SupplierVO[] = [
{ {
@ -113,6 +114,33 @@ export default hocAuth(function Page(props: CommonComponent) {
const [orderSupplierList, setOrderSupplierList] = const [orderSupplierList, setOrderSupplierList] =
useState<SupplierVO[]>(defaultSupplierList); useState<SupplierVO[]>(defaultSupplierList);
const [orderCostList, setOrderCostList] = useState<CostItem[]>([]); const [orderCostList, setOrderCostList] = useState<CostItem[]>([]);
const [orderPackageList, setOrderPackageList] = useState<
BusinessAPI.OrderPackage[]
>([]);
// 费用项目列表
const [costItemVOList, setCostItemVOList] = useState<
BusinessAPI.CostItemVO[]
>([]);
// 获取费用项目列表
useEffect(() => {
const fetchCostItems = async () => {
try {
const { data } = await business.costItem.listCostItem({
costItemListQry: {
status: true,
showInEntry: true,
},
});
setCostItemVOList(data.data || []);
} catch (error) {
console.error("获取费用项目列表失败:", error);
}
};
fetchCostItems();
}, []);
useEffect(() => { useEffect(() => {
if (orderId) { if (orderId) {
@ -138,11 +166,6 @@ export default hocAuth(function Page(props: CommonComponent) {
setPurchaseOrder(purchaseOrder); setPurchaseOrder(purchaseOrder);
setOrderVehicle(purchaseOrder.orderVehicle); setOrderVehicle(purchaseOrder.orderVehicle);
const orderSupplierList1 = purchaseOrder.orderSupplierList as any; const orderSupplierList1 = purchaseOrder.orderSupplierList as any;
console.log(
"orderSupplierList1",
orderSupplierList1,
defaultSupplierId,
);
if (defaultSupplierId) { if (defaultSupplierId) {
const index = orderSupplierList1.findIndex( const index = orderSupplierList1.findIndex(
(item: any) => item.supplierId === defaultSupplierId, (item: any) => item.supplierId === defaultSupplierId,
@ -171,6 +194,8 @@ export default hocAuth(function Page(props: CommonComponent) {
}), }),
) as any; ) as any;
setOrderCostList(orderCostList1); setOrderCostList(orderCostList1);
setOrderPackageList(purchaseOrder.orderPackageList);
} }
}) })
.catch((err) => { .catch((err) => {
@ -223,6 +248,13 @@ export default hocAuth(function Page(props: CommonComponent) {
})); }));
}, [orderCostList]); }, [orderCostList]);
useEffect(() => {
setPurchaseOrder((prev) => ({
...prev!,
orderPackageList: orderPackageList || [],
}));
}, [orderPackageList]);
if (step === undefined) { if (step === undefined) {
return; return;
} }
@ -385,6 +417,7 @@ export default hocAuth(function Page(props: CommonComponent) {
orderCostList: purchaseOrder.orderCostList.filter( orderCostList: purchaseOrder.orderCostList.filter(
(item: CostItem) => item.selected, (item: CostItem) => item.selected,
), ),
orderPackageList: purchaseOrder.orderPackageList,
}); });
if (data.success) { if (data.success) {
@ -551,13 +584,18 @@ export default hocAuth(function Page(props: CommonComponent) {
<OrderVehicle <OrderVehicle
ref={vehicleRef} ref={vehicleRef}
orderVehicle={orderVehicle!} orderVehicle={orderVehicle!}
setOrderVehicle={setOrderVehicle} setOrderVehicle={(orderVehicle) => {
setOrderVehicle(orderVehicle);
}}
orderDealer={orderDealer!} orderDealer={orderDealer!}
setOrderDealer={setOrderDealer} setOrderDealer={(orderDealer) => {
setOrderDealer(orderDealer);
}}
orderCostList={orderCostList} orderCostList={orderCostList}
setOrderCostList={(costItemList: CostItem[]) => { setOrderCostList={(costItemList: CostItem[]) => {
setOrderCostList(costItemList); setOrderCostList(costItemList);
}} }}
costItemVOList={costItemVOList}
/> />
)} )}
@ -624,6 +662,7 @@ export default hocAuth(function Page(props: CommonComponent) {
onChange={(supplierVO: SupplierVO) => { onChange={(supplierVO: SupplierVO) => {
orderSupplierList[index] = { ...item, ...supplierVO }; orderSupplierList[index] = { ...item, ...supplierVO };
setOrderSupplierList([...orderSupplierList]); setOrderSupplierList([...orderSupplierList]);
console.log("supplierVO", supplierVO);
}} }}
isLast={index === orderSupplierList.length - 1} isLast={index === orderSupplierList.length - 1}
selectedSupplierIds={selectedSupplierIds} selectedSupplierIds={selectedSupplierIds}
@ -659,11 +698,12 @@ export default hocAuth(function Page(props: CommonComponent) {
return item; return item;
}), }),
]); ]);
setPurchaseOrder((prev) => { setOrderCostList((prev) => {
const costItemVOList = productVO.costItemVOList; const costItemVOList = productVO.costItemVOList;
console.log("prev", prev);
// 将 orderCostList 中 不存在与 costItemVOList 的项 删除,剩余项保留 // 将 orderCostList 中 不存在与 costItemVOList 的项 删除,剩余项保留
const orderCostList = prev?.orderCostList.filter((item) => { const orderCostList = prev?.filter((item) => {
return ( return (
(item.costType === "WORKER_ADVANCE" || (item.costType === "WORKER_ADVANCE" ||
item.costType === "PRODUCTION_ADVANCE" || item.costType === "PRODUCTION_ADVANCE" ||
@ -693,14 +733,13 @@ export default hocAuth(function Page(props: CommonComponent) {
principal: "", principal: "",
costType: item.costType, costType: item.costType,
requireQuantityAndPrice: item.requireQuantityAndPrice, requireQuantityAndPrice: item.requireQuantityAndPrice,
selected: true,
}); });
} }
}); });
return { return [
...prev!, ...(prev?.filter((item) => {
orderCostList: [
...(prev?.orderCostList?.filter((item) => {
return ( return (
item.costType !== "WORKER_ADVANCE" && item.costType !== "WORKER_ADVANCE" &&
item.costType !== "PRODUCTION_ADVANCE" && item.costType !== "PRODUCTION_ADVANCE" &&
@ -708,8 +747,7 @@ export default hocAuth(function Page(props: CommonComponent) {
); );
}) || []), }) || []),
...(orderCostList || []), ...(orderCostList || []),
], ];
};
}); });
}} }}
isFirst={index === 0} isFirst={index === 0}
@ -749,6 +787,59 @@ export default hocAuth(function Page(props: CommonComponent) {
onChange={(supplierVO: SupplierVO) => { onChange={(supplierVO: SupplierVO) => {
orderSupplierList[index] = { ...item, ...supplierVO }; orderSupplierList[index] = { ...item, ...supplierVO };
setOrderSupplierList([...orderSupplierList]); setOrderSupplierList([...orderSupplierList]);
setOrderCostList((prev) => {
console.log("prev", prev);
// 检查是否已存在纸箱费项目
const hasBoxFee = prev?.some(
(item) =>
item.name === "纸箱费" &&
item.costType === "FIXED_COST",
);
const boxCostItem = costItemVOList.find(
(item) =>
item.name === "纸箱费" &&
item.costType === "FIXED_COST",
);
const calculator = new PurchaseOrderCalculator(
purchaseOrder as any,
);
// 如果不存在空箱费项目,则添加
if (!hasBoxFee) {
const boxFeeItem: CostItem = {
orderCostId: generateShortId(),
itemId: boxCostItem?.itemId || "",
name: "纸箱费",
price: calculator.getBoxSale(),
unit: "项",
selected: true,
count: 1,
payerType: "US",
principal: "",
costType: "FIXED_COST",
requireQuantityAndPrice: false,
};
return [...prev, boxFeeItem];
} else {
// 如果已存在空箱费项目,更新其数量
return prev.map((item) => {
if (
item.name === "纸箱费" &&
item.costType === "FIXED_COST"
) {
return {
...item,
price: calculator.getBoxSale(),
count: 1,
};
}
return item;
});
}
});
}} }}
/> />
); );
@ -780,9 +871,15 @@ export default hocAuth(function Page(props: CommonComponent) {
}} }}
ref={orderCostRef} ref={orderCostRef}
value={orderCostList} value={orderCostList}
onChange={(costItemList: CostItem[]) => onChange={(costItemList: CostItem[]) => {
setOrderCostList(costItemList) // 修改标识将在useEffect中自动更新
} setOrderCostList(costItemList);
}}
emptyBoxList={orderPackageList}
onEmptyBoxChange={(emptyBoxList: BusinessAPI.OrderPackage[]) => {
setOrderPackageList(emptyBoxList);
}}
costItemVOList={costItemVOList}
/> />
)} )}
</View> </View>

View File

@ -121,6 +121,29 @@ export default hocAuth(function Page(props: CommonComponent) {
const orderId = router.params const orderId = router.params
.orderId as BusinessAPI.PurchaseOrderVO["orderId"]; .orderId as BusinessAPI.PurchaseOrderVO["orderId"];
// 费用项目列表
const [costItemVOList, setCostItemVOList] = useState<
BusinessAPI.CostItemVO[]
>([]);
// 获取费用项目列表
useEffect(() => {
const fetchCostItems = async () => {
try {
const { data } = await business.costItem.listCostItem({
costItemListQry: {
status: true,
},
});
setCostItemVOList(data.data || []);
} catch (error) {
console.error("获取费用项目列表失败:", error);
}
};
fetchCostItems();
}, []);
const [purchaseOrderVO, setPurchaseOrderVO] = const [purchaseOrderVO, setPurchaseOrderVO] =
useState<BusinessAPI.PurchaseOrderVO>(); useState<BusinessAPI.PurchaseOrderVO>();
@ -436,6 +459,7 @@ export default hocAuth(function Page(props: CommonComponent) {
dealerVO={dealerVO} dealerVO={dealerVO}
purchaseOrderVO={purchaseOrderVO} purchaseOrderVO={purchaseOrderVO}
onChange={setPurchaseOrderVO} onChange={setPurchaseOrderVO}
costItemVOList={costItemVOList}
/> />
</View> </View>
</> </>

View File

@ -2576,7 +2576,7 @@ declare namespace BusinessAPI {
/** 销售单价(元/个) */ /** 销售单价(元/个) */
boxSalePrice?: number; boxSalePrice?: number;
/** 箱子类型:1_本次使用2_额外运输3_已使用额外运输4_车上剩余 */ /** 箱子类型:1_本次使用2_额外运输3_已使用额外运输4_车上剩余 */
boxType: "USED" | "EXTRA" | "EXTRA_USED" | "REMAIN" | "OWN"; boxType: "USED" | "EXTRA" | "EXTRA_USED" | "REMAIN" | "OWN" | "EMPTY";
}; };
type OrderRebate = { type OrderRebate = {
@ -3362,6 +3362,8 @@ declare namespace BusinessAPI {
orderSupplierList: OrderSupplier[]; orderSupplierList: OrderSupplier[];
/** 采购订单费用信息 */ /** 采购订单费用信息 */
orderCostList: OrderCost[]; orderCostList: OrderCost[];
/** 采购订单空箱费用 */
orderPackageList: OrderPackage[];
}; };
type PurchaseOrderDestroyCmd = { type PurchaseOrderDestroyCmd = {
@ -3467,6 +3469,8 @@ declare namespace BusinessAPI {
active?: number; active?: number;
/** 采购订单费用信息 */ /** 采购订单费用信息 */
orderCostList: OrderCost[]; orderCostList: OrderCost[];
/** 采购订单空箱费用 */
orderPackageList: OrderPackage[];
}; };
type PurchaseOrderSubmitReviewCmd = { type PurchaseOrderSubmitReviewCmd = {
@ -3552,6 +3556,8 @@ declare namespace BusinessAPI {
orderSupplierList: OrderSupplier[]; orderSupplierList: OrderSupplier[];
/** 采购订单费用信息 */ /** 采购订单费用信息 */
orderCostList: OrderCost[]; orderCostList: OrderCost[];
/** 采购订单空箱费用 */
orderPackageList: OrderPackage[];
}; };
type PurchaseOrderWithdrawReviewCmd = { type PurchaseOrderWithdrawReviewCmd = {

View File

@ -36,7 +36,7 @@ export interface BoxBrand {
boxBrandName: string; boxBrandName: string;
boxBrandImage: string; boxBrandImage: string;
boxBrandType: "OUR_BOX" | "FARMER_BOX" | "THIRD_PARTY_BOX" | "GIFT_BOX"; boxBrandType: "OUR_BOX" | "FARMER_BOX" | "THIRD_PARTY_BOX" | "GIFT_BOX";
boxType: "USED" | "EXTRA" | "EXTRA_USED" | "REMAIN" | "OWN" | "DEFAULT"; boxType: "USED" | "EXTRA" | "EXTRA_USED" | "REMAIN" | "OWN" | "EMPTY";
boxSpecList: BoxSpec[]; boxSpecList: BoxSpec[];
} }

View File

@ -258,24 +258,23 @@ export class PurchaseOrderCalculator {
* (CXZY) * (CXZY)
*/ */
getNetProfit(): number { getNetProfit(): number {
if (this.purchaseOrderVO.orderDealer.profitSharing) { if (this.purchaseOrderVO.orderDealer?.profitSharing) {
return this.purchaseOrderVO.orderDealer.profitSharing; return this.purchaseOrderVO.orderDealer?.profitSharing;
} }
return this.getDefaultNetProfit(); return this.getDefaultNetProfit();
} }
/** /**
* (CXZY) * (CXZY)
*/ */
getDefaultNetProfit(): number { getDefaultNetProfit(): number {
if (!this.purchaseOrderVO.orderDealer.enableShare) { if (!this.purchaseOrderVO.orderDealer?.enableShare) {
return 0; return 0;
} }
return new Decimal(this.getMelonNetProfit()) return new Decimal(this.getMelonNetProfit())
.mul(new Decimal(this.purchaseOrderVO.orderDealer.shareRatio || 0)) .mul(new Decimal(this.purchaseOrderVO.orderDealer?.shareRatio || 0))
.div(100) .div(100)
.toNumber(); .toNumber();
} }
@ -322,7 +321,7 @@ export class PurchaseOrderCalculator {
* ( * (
*/ */
getCostDifference(): number { getCostDifference(): number {
return this.purchaseOrderVO.orderDealer.costDifference || 0; return this.purchaseOrderVO.orderDealer?.costDifference || 0;
} }
/** /**
@ -336,14 +335,14 @@ export class PurchaseOrderCalculator {
* *
*/ */
getTaxProvision(): number { getTaxProvision(): number {
return this.purchaseOrderVO.orderDealer.taxProvision || 0; return this.purchaseOrderVO.orderDealer?.taxProvision || 0;
} }
/** /**
* *
*/ */
getTaxSubsidy(): number { getTaxSubsidy(): number {
return this.purchaseOrderVO.orderDealer.taxSubsidy || 0; return this.purchaseOrderVO.orderDealer?.taxSubsidy || 0;
} }
/** /**
@ -385,11 +384,19 @@ export class PurchaseOrderCalculator {
* + * +
*/ */
getTotalAmount(): number { getTotalAmount(): number {
return new Decimal(this.getSalesAmount()) const decimal = new Decimal(this.getSalesAmount());
.add(this.getTotalPackagingCost())
const includePackingFlag =
this.purchaseOrderVO.orderDealer?.includePackingFlag;
if (includePackingFlag) {
return decimal
.plus(this.getTotalPackagingCost())
.toNumber(); .toNumber();
} }
return decimal.toNumber();
}
/** /**
* *
*/ */

View File

@ -34,7 +34,7 @@ export class SupplierWeightCalculator {
} }
// 使用第一个瓜农的空磅重量作为初始空磅重量 // 使用第一个瓜农的空磅重量作为初始空磅重量
const initialEmptyWeight = this.suppliers[0].emptyWeight; const initialEmptyWeight = this.suppliers[0].emptyWeight || 0;
let previousTotalWeight = initialEmptyWeight; // 上一个农户的总磅重量(kg) let previousTotalWeight = initialEmptyWeight; // 上一个农户的总磅重量(kg)
for (let i = 0; i < this.suppliers.length; i++) { for (let i = 0; i < this.suppliers.length; i++) {
@ -46,7 +46,7 @@ export class SupplierWeightCalculator {
if (isFirstSupplier) { if (isFirstSupplier) {
// 第一个农户的空磅重量已经是正确的 // 第一个农户的空磅重量已经是正确的
} else { } else {
supplier.emptyWeight = this.suppliers[i - 1].totalWeight; supplier.emptyWeight = this.suppliers[i - 1].totalWeight || 0;
} }
// 计算本次使用纸箱的总重量(斤) // 计算本次使用纸箱的总重量(斤)
@ -69,19 +69,19 @@ export class SupplierWeightCalculator {
if (!supplier.isPaper) { if (!supplier.isPaper) {
// 如果不是纸箱包装直接使用原始重量kg转斤 // 如果不是纸箱包装直接使用原始重量kg转斤
supplier.grossWeight = new Decimal(supplier.totalWeight) supplier.grossWeight = new Decimal(supplier.totalWeight || 0)
.sub(supplier.emptyWeight) .sub(supplier.emptyWeight || 0)
.mul(2) .mul(2)
.toNumber(); .toNumber();
supplier.netWeight = new Decimal(supplier.grossWeight) supplier.netWeight = new Decimal(supplier.grossWeight || 0)
.sub(usedBoxesWeight) .sub(usedBoxesWeight)
.sub(extraUsedBoxesWeight) .sub(extraUsedBoxesWeight)
.toNumber(); .toNumber();
previousTotalWeight = supplier.totalWeight; previousTotalWeight = supplier.totalWeight;
supplier.invoiceAmount = new Decimal(supplier.netWeight) supplier.invoiceAmount = new Decimal(supplier.netWeight || 0)
.mul(supplier.purchasePrice) .mul(supplier.purchasePrice || 0)
.toNumber(); .toNumber();
continue; continue;
} }
@ -101,58 +101,58 @@ export class SupplierWeightCalculator {
if (isFirstSupplier && isLastSupplier) { if (isFirstSupplier && isLastSupplier) {
// 既是第一个也是最后一个瓜农(单个瓜农情况)- 优先使用最后一个瓜农算法 // 既是第一个也是最后一个瓜农(单个瓜农情况)- 优先使用最后一个瓜农算法
// 净重 = (总磅 - 空磅) * 2 + 剩余空箱子重量 - 已使用额外纸箱重量 // 净重 = (总磅 - 空磅) * 2 + 剩余空箱子重量 - 已使用额外纸箱重量
supplier.netWeight = new Decimal(supplier.totalWeight) supplier.netWeight = new Decimal(supplier.totalWeight || 0)
.sub(initialEmptyWeight) .sub(initialEmptyWeight)
.mul(2) .mul(2)
.add(remainingBoxesWeight) .add(remainingBoxesWeight)
.sub(extraUsedBoxesWeight) .sub(extraUsedBoxesWeight)
.toNumber(); .toNumber();
// 毛重 = 净重 + 本次使用纸箱重量 // 毛重 = 净重 + 本次使用纸箱重量
supplier.grossWeight = new Decimal(supplier.netWeight) supplier.grossWeight = new Decimal(supplier.netWeight || 0)
.add(usedBoxesWeight) .add(usedBoxesWeight)
.toNumber(); .toNumber();
} else if (isLastSupplier) { } else if (isLastSupplier) {
// 最后一个农户根据isLast标识判断 // 最后一个农户根据isLast标识判断
// 净重 = (总磅 - 前一个总磅) * 2 + 剩余空箱子重量 - 额外纸箱重量 // 净重 = (总磅 - 前一个总磅) * 2 + 剩余空箱子重量 - 额外纸箱重量
supplier.netWeight = new Decimal(supplier.totalWeight) supplier.netWeight = new Decimal(supplier.totalWeight || 0)
.sub(previousTotalWeight) .sub(previousTotalWeight)
.mul(2) .mul(2)
.add(remainingBoxesWeight) .add(remainingBoxesWeight)
.sub(extraBoxesWeight) .sub(extraBoxesWeight)
.toNumber(); .toNumber();
// 毛重 = 净重 + 本次使用纸箱重量 // 毛重 = 净重 + 本次使用纸箱重量
supplier.grossWeight = new Decimal(supplier.netWeight) supplier.grossWeight = new Decimal(supplier.netWeight || 0)
.add(usedBoxesWeight) .add(usedBoxesWeight)
.toNumber(); .toNumber();
} else if (isFirstSupplier) { } else if (isFirstSupplier) {
// 第一个农户(但不是最后一个) // 第一个农户(但不是最后一个)
// 净重 = (总磅 - 空磅) * 2 - 额外纸箱重量 // 净重 = (总磅 - 空磅) * 2 - 额外纸箱重量
supplier.netWeight = new Decimal(supplier.totalWeight) supplier.netWeight = new Decimal(supplier.totalWeight || 0)
.sub(initialEmptyWeight) .sub(initialEmptyWeight)
.mul(2) .mul(2)
.sub(extraBoxesWeight) .sub(extraBoxesWeight)
.toNumber(); .toNumber();
// 毛重 = 净重 + 本次使用纸箱重量 // 毛重 = 净重 + 本次使用纸箱重量
supplier.grossWeight = new Decimal(supplier.netWeight) supplier.grossWeight = new Decimal(supplier.netWeight || 0)
.add(usedBoxesWeight) .add(usedBoxesWeight)
.toNumber(); .toNumber();
} else { } else {
// 中间农户 // 中间农户
// 净重 = (总磅 - 前一个总磅) * 2 - 额外纸箱重量 // 净重 = (总磅 - 前一个总磅) * 2 - 额外纸箱重量
supplier.netWeight = new Decimal(supplier.totalWeight) supplier.netWeight = new Decimal(supplier.totalWeight || 0)
.sub(previousTotalWeight) .sub(previousTotalWeight)
.mul(2) .mul(2)
.sub(extraBoxesWeight) .sub(extraBoxesWeight)
.toNumber(); .toNumber();
// 毛重 = 净重 + 本次使用纸箱重量 // 毛重 = 净重 + 本次使用纸箱重量
supplier.grossWeight = new Decimal(supplier.netWeight) supplier.grossWeight = new Decimal(supplier.netWeight || 0)
.add(usedBoxesWeight) .add(usedBoxesWeight)
.toNumber(); .toNumber();
} }
previousTotalWeight = supplier.totalWeight; previousTotalWeight = supplier.totalWeight || 0;
supplier.invoiceAmount = new Decimal(supplier.netWeight) supplier.invoiceAmount = new Decimal(supplier.netWeight || 0)
.mul(supplier.purchasePrice) .mul(supplier.purchasePrice || 0)
.toNumber(); .toNumber();
} }