ERPTurbo_Client/packages/app-client/src/pages/approval/audit.tsx
shenyifei 9be4076df8 feat(order): 优化订单处理流程和数据计算逻辑
- 在Step3Success组件中按车次正序排列订单
- 为MadeOption和MarketOption组件添加滚动到顶部功能
- 更新供应商数据结构,添加收款人姓名、银行名称等字段
- 修改TicketUpload组件中的毛重量显示和发票ID初始化
- 优化审批页面中的条件渲染逻辑,避免显示零值
- 更新销售价格计算逻辑,使用总重量替代供应商数量
- 移除废弃的StallWeightCalculator类,整合到SupplierWeightCalculator中
- 修复小数计算精度问题,统一使用两位小数精度
- 添加草帘费成本的开关控制逻辑
2026-01-09 11:18:18 +08:00

385 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 hocAuth from "@/hocs/auth";
import { CommonComponent } from "@/types/typings";
import Taro from "@tarojs/taro";
import { business } from "@/services";
import { useEffect, useState } from "react";
import { View } from "@tarojs/components";
import { Button, SafeArea } from "@nutui/nutui-react-taro";
import { Icon, OrderFinalApprove, OrderRejectFinal } from "@/components";
import { buildUrl, OrderCalculator } from "@/utils";
import { DecimalUtils } from "@/utils/classes/calculators/core/DecimalUtils";
import dayjs from "dayjs";
import classNames from "classnames";
export default hocAuth(function Page(props: CommonComponent) {
const { router, isInitialized, setIsInitialized, setLoading } = props;
const orderId = router.params.orderId as BusinessAPI.OrderVO["orderId"];
const auditId = router.params.auditId as BusinessAPI.AuditVO["auditId"];
const [auditVO, setAuditVO] = useState<BusinessAPI.AuditVO>();
const [orderVO, setOrderVO] = useState<BusinessAPI.OrderVO>();
// 控制供应商采购单价列表展开状态
const [showAllSuppliers, setShowAllSuppliers] = useState(false);
const init = async (
orderId: BusinessAPI.OrderVO["orderId"],
auditId: BusinessAPI.AuditVO["auditId"],
) => {
const {
data: { data: auditVO, success: auditSuccess },
} = await business.audit.showAudit({
auditShowQry: {
auditId,
},
});
if (auditSuccess && auditVO) {
setAuditVO(auditVO);
}
const {
data: { data: orderVO, success: orderSuccess },
} = await business.order.showOrder({
orderShowQry: {
orderId,
},
});
if (orderSuccess) {
setOrderVO(orderVO);
}
};
useEffect(() => {
if (orderId && auditId && !isInitialized) {
setLoading(true);
init(orderId, auditId).then(() => {
setIsInitialized(true);
setLoading(false);
});
}
}, []);
if (!orderVO) {
return;
}
const calculator = new OrderCalculator(orderVO);
let totalCost: number;
const totalWeight = calculator.getTotalWeight();
const shouldIncludeFreightCost = calculator
.getRules()
.shouldIncludeFreightCost();
// 判断采购成本是否包含运费,包含运费说明已经加过一次了
if (calculator.getRules().shouldIncludeFreightCost()) {
totalCost = calculator.getCostCalculator().calculateTotalPurchaseCost(true);
} else {
totalCost = calculator.getCostCalculator().calculateTotalPurchaseCost();
}
const unitCost =
totalWeight > 0
? DecimalUtils.toDecimalPlaces(
DecimalUtils.divide(totalCost, totalWeight),
2,
)
: 0;
const estimatedArrivalDate = orderVO.orderShipList[0].estimatedArrivalDate;
const personalProfit = calculator.getPersonalProfit();
return (
<>
<View
className={"flex flex-1 flex-col gap-2.5 p-2.5"}
id={"purchase-order-approve"}
>
<View className="flex justify-between space-x-2.5">
{auditVO?.state === "AUDIT_REJECTED" && (
<View className="flex flex-col gap-2 rounded-lg border-2 border-red-200 bg-red-50 p-3 shadow-sm">
<View className="flex items-center gap-2">
<Icon name="exclamation" color="red" size={16} />
<View className="text-base font-bold text-red-700">
</View>
</View>
<View className="pl-6 text-sm text-red-600">
{auditVO?.auditReason || "暂无驳回原因"}
</View>
</View>
)}
<View className="price-card flex-1 rounded-lg bg-white p-3">
<View className="mb-1 text-center text-sm text-gray-500">
</View>
{/*<View className="flex flex-row items-center justify-center gap-2.5 text-center">*/}
{/* <View className="text-primary text-3xl">*/}
{/* {calculator.getAveragePurchasePrice()}*/}
{/* </View>*/}
{/* <View className="text-sm text-gray-500">元/斤</View>*/}
{/*</View>*/}
{/* 供应商采购单价列表 */}
{orderVO.orderSupplierList &&
orderVO.orderSupplierList.length > 0 && (
<View className="flex flex-col">
{orderVO.orderSupplierList
.slice(0, showAllSuppliers ? undefined : 3)
.map((supplier) => (
<View
key={supplier.orderSupplierId}
className="flex items-center justify-between py-1"
>
<View className="text-sm text-gray-600">
{supplier.name}
</View>
<View className="flex flex-row items-center gap-0.5">
<View className="text-primary text-xl font-bold">
{supplier.purchasePrice || 0}
</View>
<View className="text-sm text-gray-500">/</View>
</View>
</View>
))}
{/* 查看更多按钮 */}
{orderVO.orderSupplierList.length > 3 && (
<View
className="bg-primary/5 mt-2 flex flex-row items-center justify-center gap-0.5 rounded-md p-1 text-center"
onClick={() => setShowAllSuppliers(!showAllSuppliers)}
>
<View className="text-primary cursor-pointer text-sm">
{showAllSuppliers ? "收起" : "查看更多"}
</View>
<Icon
name={showAllSuppliers ? "chevron-up" : "chevron-down"}
size={14}
/>
</View>
)}
</View>
)}
</View>
<View className="price-card flex-1 rounded-lg bg-white p-3">
<View className="mb-1 text-center text-sm text-gray-500">
</View>
<View className="flex flex-row items-center justify-center gap-2.5 text-center">
<View className="text-primary text-2xl font-bold">
{calculator.getAverageSalesPrice()}
</View>
<View className="text-sm text-gray-500">/</View>
</View>
</View>
</View>
<View className="hidden overflow-hidden rounded-lg bg-white shadow-sm">
<View className="border-b border-gray-100 px-4 py-3">
<View className="text-sm font-bold"></View>
</View>
{/* 采购单号,采购类型 */}
<View className="grid grid-cols-2 divide-x divide-y divide-gray-100">
<View className="flex flex-col px-3 py-2">
<View className="text-sm text-gray-500"></View>
<View className="font-medium">
{orderVO.orderVehicle.dealerName}{" "}
{orderVO?.orderVehicle?.vehicleNo
? "第" + orderVO.orderVehicle.vehicleNo + "车"
: "暂未生成车次"}
</View>
</View>
<View className="flex flex-col px-3 py-2">
<View className="text-sm text-gray-500"></View>
<View className="font-medium">
{orderVO.type === "PRODUCTION_PURCHASE"
? "产地采购单"
: "市场采购单"}
</View>
</View>
</View>
</View>
<View className="overflow-hidden rounded-lg bg-white shadow-sm">
<View className="border-b border-gray-100 px-4 py-3">
<View className={"flex flex-row justify-between"}>
<View className="text-sm font-bold"></View>
<View className="text-sm text-gray-500">
{estimatedArrivalDate
? dayjs(estimatedArrivalDate).format("YYYY-MM-DD")
: "暂无"}
</View>
</View>
</View>
<View className="grid grid-cols-2 divide-x divide-y divide-gray-100">
<View className="cost-item flex flex-col px-3 py-2">
<View className="text-sm text-gray-500"></View>
<View className="font-medium">
{calculator.getMarketPrice()}
</View>
</View>
<View className="cost-item flex flex-col px-3 py-2">
<View className="text-sm text-gray-500">西</View>
<View className="font-medium">
{calculator.getSupplierPurchaseCost()}
</View>
</View>
{orderVO.orderCostList
.filter((item) => {
return (
item.price * item.count > 0 && item.name !== "付瓜农定金"
);
})
.map((item) => {
return (
<View
className="cost-item flex flex-col px-3 py-2"
key={item.costId}
>
<View className="text-sm text-gray-500">{item.name}</View>
<View className="font-medium">
{item.price * item.count}
</View>
{item.name === "人工费" && (
<View className="text-neutral-darker text-xs">
:
{orderVO.foreman}
</View>
)}
</View>
);
})}
{orderVO.orderDealer?.enableLoss &&
Number(orderVO.orderDealer?.lossAmount) > 0 && (
<View className="cost-item flex flex-col px-3 py-2">
<View className="text-sm text-gray-500"></View>
<View className="font-medium">
{orderVO.orderDealer?.lossAmount}
</View>
</View>
)}
{Number(orderVO.orderDealer?.taxProvision || 0) > 0 && (
<View className="cost-item flex flex-col px-3 py-2">
<View className="text-sm text-gray-500"></View>
<View className="font-medium">
{orderVO.orderDealer?.taxProvision}
</View>
</View>
)}
{Number(orderVO.orderDealer?.taxSubsidy || 0) > 0 && (
<View className="cost-item flex flex-col px-3 py-2">
<View className="text-sm text-gray-500"></View>
<View className="font-medium">
{orderVO.orderDealer?.taxSubsidy}
</View>
</View>
)}
{orderVO.orderRebate &&
Number(orderVO.orderRebate.amount || 0) > 0 && (
<View className="cost-item flex flex-col px-3 py-2">
<View className="text-sm text-gray-500"></View>
<View className="font-medium">
{orderVO.orderRebate?.amount}
</View>
</View>
)}
{Number(orderVO.orderDealer?.costDifference || 0) > 0 && (
<View className="cost-item flex flex-col px-3 py-2">
<View className="text-sm text-gray-500"></View>
<View className="font-medium">
{orderVO.orderDealer?.costDifference}
</View>
</View>
)}
<View className="cost-total col-span-2 grid grid-cols-2 bg-yellow-50 px-3 py-2">
<View className="flex flex-col">
<View className="text-sm text-gray-500">
{shouldIncludeFreightCost ? "包含运费" : "不包含运费"}
</View>
<View className="font-bold">{totalCost} </View>
</View>
<View className="flex flex-col">
<View className="text-sm text-gray-500"></View>
<View className="font-bold">{unitCost} /</View>
</View>
</View>
</View>
</View>
<View className="rounded-lg bg-white p-2.5 shadow-sm">
<View className="flex items-center justify-between">
<View className="text-gray-500"></View>
<View
className={classNames("text-2xl font-bold", {
"text-primary": personalProfit > 0,
"text-red-500": personalProfit < 0,
})}
>
{personalProfit}
</View>
</View>
</View>
</View>
{/* 按钮操作 */}
<View className={"sticky bottom-0 z-10 bg-white"} id={"bottomBar"}>
<View className="flex justify-between gap-2 border-t border-gray-200 p-2.5">
<View className="flex-1">
<Button
type="default"
size={"large"}
block
onClick={() =>
Taro.switchTab({
url: buildUrl("/pages/main/index/index"),
})
}
>
</Button>
</View>
{auditVO?.type === "BOSS_AUDIT" &&
auditVO.state === "WAITING_AUDIT" && (
<>
<View className={"flex-1"}>
<OrderRejectFinal
auditVO={auditVO}
size={"large"}
onFinish={() => {
// 返回首页
Taro.redirectTo({
url: "/pages/approval/pending",
});
}}
/>
</View>
<View className={"flex-1"}>
<OrderFinalApprove
auditVO={auditVO}
size={"large"}
onFinish={() => {
// 关闭当前页面并跳转到采购单审核通过页面
Taro.redirectTo({
url: buildUrl(`/pages/approval/result`, {
orderId: orderVO?.orderId,
}),
});
}}
/>
</View>
</>
)}
</View>
<SafeArea position={"bottom"} />
</View>
</>
);
});