399 lines
13 KiB
TypeScript
399 lines
13 KiB
TypeScript
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>
|
||
</>
|
||
);
|
||
}
|