ERPTurbo_Client/packages/app-client/src/components/purchase/section/PurchaseCostInfoSection.tsx
shenyifei d37626d5bf refactor(utils): 重构工具函数导入路径
- 统一从 utils 目录导入工具函数,而非具体的子文件
- 更新了 uploadFile, buildUrl, formatCurrency 等函数的导入路径
- 修改 CustomTabBar 组件接收 userRoleVO 对象而非 role 字符串
- 调整金额格式化相关工具函数的引用方式
- 更新文档中项目结构和费用管理相关说明
- 优化用户角色权限相关的数据传递逻辑
2025-11-21 19:59:49 +08:00

418 lines
14 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

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

import { formatCurrency, validatePrice } from "@/utils";
import { useEffect, useState } from "react";
import { Button, Input, Popup, SafeArea, Table } from "@nutui/nutui-react-taro";
import { Icon } from "@/components";
import { Text, 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",
render: (record: any) => {
return (
<View className="flex flex-row items-center">
{record.icon && (
<Icon name={record.icon} size={16} className="mr-1" />
)}
<Text>{record.project}</Text>
</View>
);
},
},
...(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: "毛重(斤)",
icon: "weight-scale",
...(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: "箱重(斤)",
icon: "weight-scale",
...(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: "净重(斤)",
icon: "weight-scale",
...(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: "单价(元/斤)",
icon: "money-bill",
...(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: "合计(元)",
icon: "money-bill",
...(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
duration={150}
style={{
minHeight: "auto",
}}
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>
</>
);
}