ERPTurbo_Client/packages/app-client/src/components/purchase/section/PurchaseCostInfoSection.tsx
2025-11-03 10:24:10 +08:00

399 lines
13 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 { formatCurrency, validatePrice } 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 PurchaseCostInfoSection(props: {
purchaseOrderVO: BusinessAPI.PurchaseOrderVO;
onChange?: (purchaseOrderVO: BusinessAPI.PurchaseOrderVO) => void;
readOnly?: boolean;
}) {
const { purchaseOrderVO, onChange, readOnly } = props;
// 弹窗可见状态
const [visiblePopup, setVisiblePopup] = useState<{ [key: string]: boolean }>(
{},
);
// 编辑值的状态
const [editValues, setEditValues] = useState<{
[key: string]: {
purchasePrice?: number;
};
}>({});
// 临时编辑值的状态(用于在保存前暂存编辑的值)
const [tempEditValues, setTempEditValues] = useState<{
[key: string]: {
purchasePrice?: number;
};
}>({});
// 初始化编辑值
const initEditValues = (supplierId: string, purchasePrice?: number) => {
const updates: {
editValuesUpdate?: {
purchasePrice?: number;
};
tempEditValuesUpdate?: {
purchasePrice?: number;
};
} = {};
if (!editValues[supplierId]) {
updates.editValuesUpdate = {
purchasePrice,
};
}
// 同时初始化临时编辑值
if (!tempEditValues[supplierId]) {
updates.tempEditValuesUpdate = {
purchasePrice,
};
}
return updates;
};
// 初始化所有供应商的编辑值
useEffect(() => {
const newEditValues = { ...editValues };
const newTempEditValues = { ...tempEditValues };
let hasEditValuesChanged = false;
let hasTempEditValuesChanged = false;
purchaseOrderVO.orderSupplierList?.forEach((supplier) => {
const supplierId = supplier.orderSupplierId || "";
const updates = initEditValues(supplierId, supplier.purchasePrice);
if (updates.editValuesUpdate) {
newEditValues[supplierId] = updates.editValuesUpdate;
hasEditValuesChanged = true;
}
if (updates.tempEditValuesUpdate) {
newTempEditValues[supplierId] = updates.tempEditValuesUpdate;
hasTempEditValuesChanged = true;
}
});
if (hasEditValuesChanged) {
setEditValues(newEditValues);
}
if (hasTempEditValuesChanged) {
setTempEditValues(newTempEditValues);
}
}, [purchaseOrderVO.orderSupplierList]);
// 当editValues发生变化时更新purchaseOrderVO
useEffect(() => {
// 只有当onChange存在时才更新
if (onChange) {
// 创建新的purchaseOrderVO对象
const newPurchaseOrderVO = { ...purchaseOrderVO };
// 更新供应商列表中的采购单价信息
if (newPurchaseOrderVO.orderSupplierList) {
newPurchaseOrderVO.orderSupplierList =
newPurchaseOrderVO.orderSupplierList.map((supplier) => {
const editValue = editValues[supplier.orderSupplierId || ""];
if (editValue) {
return {
...supplier,
purchasePrice:
editValue.purchasePrice !== undefined
? editValue.purchasePrice
: supplier.purchasePrice,
};
}
return supplier;
});
}
// 调用onChange回调
onChange(newPurchaseOrderVO);
}
}, [editValues]);
// 计算箱重
const calculateBoxWeight = (supplier: BusinessAPI.OrderSupplier) => {
return (
supplier.orderPackageList?.reduce(
(sum, orderPackage) =>
sum + orderPackage.boxCount * orderPackage.boxProductWeight || 0,
0,
) || 0
);
};
// 计算净重
const calculateNetWeight = (supplier: BusinessAPI.OrderSupplier) => {
const grossWeight = supplier.grossWeight || 0;
const boxWeight = calculateBoxWeight(supplier);
return grossWeight - boxWeight;
};
// 计算合计金额
const calculateTotalAmount = (supplier: BusinessAPI.OrderSupplier) => {
const netWeight = calculateNetWeight(supplier);
const purchasePrice =
editValues[supplier.orderSupplierId || ""]?.purchasePrice !== undefined
? editValues[supplier.orderSupplierId || ""].purchasePrice
: supplier.purchasePrice;
return netWeight * (purchasePrice || 0);
};
return (
<>
<Table
columns={[
{
title: "项目",
key: "project",
},
...(purchaseOrderVO?.orderSupplierList.length > 0
? purchaseOrderVO?.orderSupplierList.map((item) => {
return {
title: item.name,
key: item.orderSupplierId,
render: (record: any) => {
// 只在单价(元/斤)行显示编辑按钮
if (record.project === "单价(元/斤)") {
return readOnly ? (
formatCurrency(record[item.orderSupplierId])
) : (
<View
className="flex items-center justify-between gap-2.5"
onClick={() => {
setVisiblePopup((prev) => ({
...prev,
[record.project]: true,
}));
}}
>
{formatCurrency(record[item.orderSupplierId])}
<Icon
name={"pen-to-square"}
size={16}
color={"#1a73e8"}
/>
</View>
);
}
return formatCurrency(record[item.orderSupplierId]);
},
};
})
: []),
{
title: "合计",
key: "total",
},
]}
data={[
{
project: "毛重(斤)",
...(purchaseOrderVO?.orderSupplierList.length > 0
? purchaseOrderVO?.orderSupplierList.reduce((acc, item) => {
acc[item.orderSupplierId] = item.grossWeight;
return acc;
}, {})
: {}),
total: formatCurrency(
purchaseOrderVO?.orderSupplierList.reduce(
(sum, supplier) => sum + (supplier.grossWeight || 0),
0,
),
),
},
{
project: "箱重(斤)",
...(purchaseOrderVO?.orderSupplierList.length > 0
? purchaseOrderVO?.orderSupplierList.reduce((acc, item) => {
acc[item.orderSupplierId] = calculateBoxWeight(item);
return acc;
}, {})
: {}),
total: formatCurrency(
purchaseOrderVO?.orderSupplierList.reduce(
(sum, supplier) => sum + calculateBoxWeight(supplier),
0,
),
),
},
{
project: "净重(斤)",
...(purchaseOrderVO?.orderSupplierList.length > 0
? purchaseOrderVO?.orderSupplierList.reduce((acc, item) => {
acc[item.orderSupplierId] = calculateNetWeight(item);
return acc;
}, {})
: {}),
total: formatCurrency(
purchaseOrderVO?.orderSupplierList.reduce(
(sum, supplier) => sum + calculateNetWeight(supplier),
0,
),
),
},
{
project: "单价(元/斤)",
...(purchaseOrderVO?.orderSupplierList.length > 0
? purchaseOrderVO?.orderSupplierList.reduce((acc, item) => {
const purchasePrice =
editValues[item.orderSupplierId || ""]?.purchasePrice !==
undefined
? editValues[item.orderSupplierId || ""].purchasePrice
: item.purchasePrice;
acc[item.orderSupplierId] = formatCurrency(
Number(purchasePrice),
);
return acc;
}, {})
: {}),
total: formatCurrency(
purchaseOrderVO?.orderSupplierList.reduce((sum, supplier) => {
const purchasePrice =
editValues[supplier.orderSupplierId || ""]?.purchasePrice !==
undefined
? editValues[supplier.orderSupplierId || ""].purchasePrice
: supplier.purchasePrice;
return sum + (purchasePrice || 0);
}, 0),
),
},
{
project: "合计(元)",
...(purchaseOrderVO?.orderSupplierList.length > 0
? purchaseOrderVO?.orderSupplierList.reduce((acc, item) => {
acc[item.orderSupplierId] = formatCurrency(
calculateTotalAmount(item),
);
return acc;
}, {})
: {}),
total: formatCurrency(
purchaseOrderVO?.orderSupplierList.reduce(
(sum, supplier) => sum + calculateTotalAmount(supplier),
0,
),
),
},
]}
/>
{/* 采购单价编辑弹窗 */}
<Popup
visible={visiblePopup["单价(元/斤)"] || false}
position="bottom"
title="编辑采购单价"
onClose={() =>
setVisiblePopup((prev) => ({
...prev,
["单价(元/斤)"]: false,
}))
}
onOverlayClick={() =>
setVisiblePopup((prev) => ({
...prev,
["单价(元/斤)"]: false,
}))
}
lockScroll
>
<View className="flex flex-col gap-3 p-2.5">
{purchaseOrderVO.orderSupplierList?.map((supplier) => (
<View key={supplier.orderSupplierId}>
<View className="text-neutral-darkest mb-1 text-sm font-medium">
{supplier.name}
</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[
supplier.orderSupplierId || ""
]?.purchasePrice?.toString() ||
editValues[
supplier.orderSupplierId || ""
]?.purchasePrice?.toString() ||
""
}
onChange={(value) => {
const numValue = validatePrice(value);
if (numValue !== undefined) {
setTempEditValues((prev) => ({
...prev,
[supplier.orderSupplierId || ""]: {
...prev[supplier.orderSupplierId || ""],
purchasePrice: numValue as number,
},
}));
}
}}
/>
<View className="mr-2">/</View>
</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,
["单价(元/斤)"]: false,
}))
}
>
</Button>
</View>
<View className="flex-1">
<Button
size="large"
block
type="primary"
onClick={() => {
// 保存时才更新editValues状态
const newEditValues = { ...editValues };
purchaseOrderVO.orderSupplierList?.forEach((supplier) => {
const tempValue =
tempEditValues[supplier.orderSupplierId || ""];
if (tempValue) {
newEditValues[supplier.orderSupplierId || ""] = {
...newEditValues[supplier.orderSupplierId || ""],
purchasePrice: tempValue.purchasePrice,
};
}
});
setEditValues(newEditValues);
// 关闭弹窗
setVisiblePopup((prev) => ({
...prev,
["单价(元/斤)"]: false,
}));
}}
>
</Button>
</View>
</View>
</View>
<SafeArea position="bottom" />
</Popup>
</>
);
}