- 在Step3Success组件中按车次正序排列订单 - 为MadeOption和MarketOption组件添加滚动到顶部功能 - 更新供应商数据结构,添加收款人姓名、银行名称等字段 - 修改TicketUpload组件中的毛重量显示和发票ID初始化 - 优化审批页面中的条件渲染逻辑,避免显示零值 - 更新销售价格计算逻辑,使用总重量替代供应商数量 - 移除废弃的StallWeightCalculator类,整合到SupplierWeightCalculator中 - 修复小数计算精度问题,统一使用两位小数精度 - 添加草帘费成本的开关控制逻辑
385 lines
14 KiB
TypeScript
385 lines
14 KiB
TypeScript
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>
|
||
</>
|
||
);
|
||
});
|