feat(purchase): 添加工头信息并优化费用展示逻辑

- 在 MelonFarmer 组件中新增微信二维码图片处理逻辑
- 重构 OrderCost 组件中的工头姓名状态管理,替换 principal 为 foreman
- 更新 PurchasePreview 组件展示工头信息的方式
- 修改 LaborInfoSection 和 WorkerAdvanceSection 中的工头字段引用
- 调整审批页面价格和成本项的显示格式,增加单位标注
- 完善创建采购单时工头信息的提交逻辑
- 更新业务类型定义文件,补充 foreman 字段声明
This commit is contained in:
shenyifei 2025-11-19 19:47:39 +08:00
parent 8eabf09da5
commit a05015fd19
8 changed files with 87 additions and 70 deletions

View File

@ -696,6 +696,16 @@ export default forwardRef<MelonFarmerRef, IMelonFarmerProps>(
...supplierVO1, ...supplierVO1,
}); });
if (supplierVO1.wechatQr) {
setPicList([
{
url: supplierVO1.wechatQr,
name: "wechat-qrcode",
status: "success",
},
]);
}
// 清除所有错误状态 // 清除所有错误状态
setNameError((prev) => ({ setNameError((prev) => ({
...prev, ...prev,

View File

@ -40,6 +40,8 @@ export interface IOrderCostProps {
onEmptyBoxChange?: (orderPackageList: BusinessAPI.OrderPackage[]) => void; onEmptyBoxChange?: (orderPackageList: BusinessAPI.OrderPackage[]) => void;
onAdd: () => void; onAdd: () => void;
costItemVOList: BusinessAPI.CostItemVO[]; costItemVOList: BusinessAPI.CostItemVO[];
foreman: string;
setForeman: (foreman: string) => void;
} }
export default forwardRef<OrderCostRef, IOrderCostProps>( export default forwardRef<OrderCostRef, IOrderCostProps>(
@ -52,6 +54,8 @@ export default forwardRef<OrderCostRef, IOrderCostProps>(
onEmptyBoxChange, onEmptyBoxChange,
emptyBoxList: defaultEmptyBoxList, emptyBoxList: defaultEmptyBoxList,
costItemVOList, costItemVOList,
foreman,
setForeman,
} = IOrderCostProps; } = IOrderCostProps;
console.log("defaultEmptyBoxList", defaultEmptyBoxList); console.log("defaultEmptyBoxList", defaultEmptyBoxList);
@ -70,7 +74,6 @@ export default forwardRef<OrderCostRef, IOrderCostProps>(
const [boxBrandList, setBoxBrandList] = useState<BoxBrand[]>(); const [boxBrandList, setBoxBrandList] = useState<BoxBrand[]>();
const [emptyBoxCostItem, setEmptyBoxCostItem] = useState<CostItem>(); const [emptyBoxCostItem, setEmptyBoxCostItem] = useState<CostItem>();
console.log("emptyBoxCostItem", emptyBoxCostItem);
// 批量添加空箱相关状态 // 批量添加空箱相关状态
const [selectedBrand, setSelectedBrand] = useState<BoxBrand | null>(null); const [selectedBrand, setSelectedBrand] = useState<BoxBrand | null>(null);
@ -177,10 +180,8 @@ export default forwardRef<OrderCostRef, IOrderCostProps>(
); );
}, [emptyBoxList, packageTypeEnabled.EMPTY]); }, [emptyBoxList, packageTypeEnabled.EMPTY]);
// 总的工头姓名状态
const [principal, setPrincipal] = useState<string>("");
// 工头姓名错误状态 // 工头姓名错误状态
const [principalError, setPrincipalError] = useState<boolean>(false); const [foremanError, setForemanError] = useState<boolean>(false);
// 初始化空箱品牌数据 // 初始化空箱品牌数据
const initBoxBrandList = async () => { const initBoxBrandList = async () => {
@ -329,17 +330,6 @@ export default forwardRef<OrderCostRef, IOrderCostProps>(
}; };
}) || []), }) || []),
]); ]);
// 初始化总的工头姓名(如果有启用且费用承担方为"我方"的项目)
const enabledUsItems = initialList.filter(
(item) =>
item.selected &&
item.payerType === "US" &&
item.costType === "HUMAN_COST",
);
if (enabledUsItems.length > 0 && enabledUsItems[0].principal) {
setPrincipal(enabledUsItems[0].principal || "");
}
} }
}; };
@ -376,14 +366,14 @@ export default forwardRef<OrderCostRef, IOrderCostProps>(
item.selected && item.selected &&
item.payerType === "US" item.payerType === "US"
) { ) {
return { ...item, principal }; return { ...item, principal: foreman };
} }
return item; return item;
}); });
onChange(updatedList); onChange(updatedList);
} }
}, [costItemList, principal]); }, [costItemList, foreman]);
// 错误状态 // 错误状态
const [countError, setCountError] = useState<{ [key: string]: boolean }>( const [countError, setCountError] = useState<{ [key: string]: boolean }>(
@ -439,7 +429,7 @@ export default forwardRef<OrderCostRef, IOrderCostProps>(
// 校验工头姓名 // 校验工头姓名
const validatePrincipal = (value: string) => { const validatePrincipal = (value: string) => {
const isValid = value.trim().length > 0; const isValid = value.trim().length > 0;
setPrincipalError(!isValid); setForemanError(!isValid);
return isValid; return isValid;
}; };
@ -461,8 +451,8 @@ export default forwardRef<OrderCostRef, IOrderCostProps>(
}; };
// 处理工头姓名变化 // 处理工头姓名变化
const handlePrincipalChange = (value: string) => { const handleForemanChange = (value: string) => {
setPrincipal(value); setForeman(value);
// 如果有启用且费用承担方为"我方"的项目,则校验工头姓名 // 如果有启用且费用承担方为"我方"的项目,则校验工头姓名
const enabledUsItems = costItemList?.filter( const enabledUsItems = costItemList?.filter(
@ -509,8 +499,6 @@ export default forwardRef<OrderCostRef, IOrderCostProps>(
} }
}); });
console.log("costItemList", costItemList);
// 校验总的工头姓名(如果有启用且费用承担方为"我方"的项目) // 校验总的工头姓名(如果有启用且费用承担方为"我方"的项目)
const enabledUsItems = costItemList.filter( const enabledUsItems = costItemList.filter(
(item) => (item) =>
@ -518,13 +506,12 @@ export default forwardRef<OrderCostRef, IOrderCostProps>(
item.selected && item.selected &&
item.payerType === "US", item.payerType === "US",
); );
console.log("enabledUsItems", enabledUsItems);
isValid = true;
if (enabledUsItems.length > 0) { if (enabledUsItems.length > 0) {
if (!validatePrincipal(principal)) { if (!validatePrincipal(foreman)) {
isValid = false; isValid = false;
} }
} else {
isValid = true;
} }
if (!isValid) { if (!isValid) {
@ -1422,20 +1409,20 @@ export default forwardRef<OrderCostRef, IOrderCostProps>(
<View> <View>
<View className="mb-1 text-sm font-medium"></View> <View className="mb-1 text-sm font-medium"></View>
<View <View
className={`flex h-12 items-center rounded-md px-3 ${principalError ? "border-2 border-red-500 bg-red-100" : "border-2 border-gray-300 bg-white"}`} className={`flex h-12 items-center rounded-md px-3 ${foremanError ? "border-2 border-red-500 bg-red-100" : "border-2 border-gray-300 bg-white"}`}
> >
<Input <Input
className="text-base" className="text-base"
type="text" type="text"
placeholder={"工头的名字"} placeholder={"工头的名字"}
value={principal} value={foreman}
onChange={(value) => { onChange={(value) => {
handlePrincipalChange(value); handleForemanChange(value);
}} }}
onBlur={() => handlePrincipalBlur(principal)} onBlur={() => handlePrincipalBlur(foreman)}
/> />
</View> </View>
{principalError && ( {foremanError && (
<View className="mt-1 text-xs text-red-500"> <View className="mt-1 text-xs text-red-500">
</View> </View>

View File

@ -334,17 +334,24 @@ export default function PurchasePreview(props: IPurchasePreviewProps) {
? "瓜农承担费用" ? "瓜农承担费用"
: "未指定"} : "未指定"}
</View> </View>
{costItem.payerType === "US" && (
<View className="text-sm font-normal text-gray-400">
{costItem.principal}
</View>
)}
<View className="mr-2 text-sm font-medium"> <View className="mr-2 text-sm font-medium">
{costItem.count} {costItem.unit} {costItem.count} {costItem.unit}
</View> </View>
</View> </View>
</View> </View>
))} ))}
{purchaseOrder.orderCostList?.some(
(costItem: CostItem) =>
costItem.costType === "HUMAN_COST" &&
costItem.payerType === "US",
) && (
<View className="flex items-center justify-between border-t pt-2.5">
<View className="text-sm font-semibold"></View>
<View className="text-primary text-sm font-semibold">
{purchaseOrder.foreman}
</View>
</View>
)}
<View className="flex items-center justify-between border-t pt-2.5"> <View className="flex items-center justify-between border-t pt-2.5">
<View className="text-sm font-semibold"></View> <View className="text-sm font-semibold"></View>
<View className="text-primary text-sm font-semibold"> <View className="text-primary text-sm font-semibold">

View File

@ -37,7 +37,7 @@ export default function LaborInfoSection(props: {
unitPrice: "", // 单价 unitPrice: "", // 单价
amount: "", // 金额 amount: "", // 金额
payerType: "US", // 费用承担方,默认我方 payerType: "US", // 费用承担方,默认我方
principal: "", // 工头姓名 principal: "", // 负责人
requireQuantityAndPrice: false, requireQuantityAndPrice: false,
}); });
@ -175,7 +175,7 @@ export default function LaborInfoSection(props: {
<View className={"flex flex-col gap-2.5 divide-y divide-[#eaeaea]"}> <View className={"flex flex-col gap-2.5 divide-y divide-[#eaeaea]"}>
<View className={"flex flex-col gap-2.5"}> <View className={"flex flex-col gap-2.5"}>
<View className={"text-sm font-bold"}> <View className={"text-sm font-bold"}>
{humanCosts?.[0].principal} {purchaseOrderVO.foreman}
</View> </View>
<View <View
className={ className={
@ -192,7 +192,7 @@ export default function LaborInfoSection(props: {
<View className={"text-sm font-bold"}></View> <View className={"text-sm font-bold"}></View>
{/* 工头垫付 */} {/* 工头垫付 */}
{workerAdvanceCosts.map((item, index) => { {workerAdvanceCosts.map((item, index) => {
// 初始化编辑值,包括费用承担方和工头姓名 // 初始化编辑值,包括费用承担方和负责人
initEditValues( initEditValues(
item.orderCostId, item.orderCostId,
item.count, item.count,
@ -243,7 +243,7 @@ export default function LaborInfoSection(props: {
<View className={"text-sm font-bold"}></View> <View className={"text-sm font-bold"}></View>
{/* 产地垫付 */} {/* 产地垫付 */}
{productionAdvanceCosts.map((item, index) => { {productionAdvanceCosts.map((item, index) => {
// 初始化编辑值,包括费用承担方和工头姓名 // 初始化编辑值,包括费用承担方和负责人
initEditValues( initEditValues(
item.orderCostId, item.orderCostId,
item.count, item.count,
@ -403,7 +403,7 @@ export default function LaborInfoSection(props: {
: "元", // 单位 : "元", // 单位
unitPrice: "", // 单价 unitPrice: "", // 单价
amount: "", // 金额 amount: "", // 金额
principal: "", // 工头姓名 principal: "", // 负责人
requireQuantityAndPrice: requireQuantityAndPrice:
selectedItem.requireQuantityAndPrice, selectedItem.requireQuantityAndPrice,
})); }));
@ -535,7 +535,7 @@ export default function LaborInfoSection(props: {
unitPrice: "", // 单价 unitPrice: "", // 单价
amount: "", // 金额 amount: "", // 金额
payerType: "", // 费用承担方,默认我方 payerType: "", // 费用承担方,默认我方
principal: "", // 工头姓名 principal: "", // 负责人
requireQuantityAndPrice: false, requireQuantityAndPrice: false,
}); });
}} }}
@ -612,7 +612,7 @@ export default function LaborInfoSection(props: {
unitPrice: "", // 单价 unitPrice: "", // 单价
amount: "", // 金额 amount: "", // 金额
payerType: "", // 费用承担方,默认我方 payerType: "", // 费用承担方,默认我方
principal: "", // 工头姓名 principal: "", // 负责人
requireQuantityAndPrice: false, requireQuantityAndPrice: false,
}); });
}} }}
@ -776,7 +776,7 @@ export default function LaborInfoSection(props: {
...prev, ...prev,
[item.orderCostId]: { [item.orderCostId]: {
...tempEditValues[item.orderCostId], ...tempEditValues[item.orderCostId],
// 如果费用承担方是瓜农,清空工头姓名 // 如果费用承担方是瓜农,清空负责人
principal: principal:
tempEditValues[item.orderCostId]?.payerType === tempEditValues[item.orderCostId]?.payerType ===
"OTHER" "OTHER"
@ -989,7 +989,7 @@ export default function LaborInfoSection(props: {
...prev, ...prev,
[item.orderCostId]: { [item.orderCostId]: {
...tempEditValues[item.orderCostId], ...tempEditValues[item.orderCostId],
// 如果费用承担方是瓜农,清空工头姓名 // 如果费用承担方是瓜农,清空负责人
principal: principal:
tempEditValues[item.orderCostId]?.payerType === tempEditValues[item.orderCostId]?.payerType ===
"OTHER" "OTHER"

View File

@ -165,7 +165,7 @@ export default function WorkerAdvanceSection(props: {
{humanCosts && humanCosts.length > 0 && ( {humanCosts && humanCosts.length > 0 && (
<View className={"flex flex-col gap-2.5"}> <View className={"flex flex-col gap-2.5"}>
<View className={"text-sm font-bold"}> <View className={"text-sm font-bold"}>
{humanCosts?.[0].principal} {purchaseOrderVO.foreman}
</View> </View>
<View <View
className={ className={

View File

@ -5,7 +5,10 @@ import { business } from "@/services";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { View } from "@tarojs/components"; import { View } from "@tarojs/components";
import { SafeArea } from "@nutui/nutui-react-taro"; import { SafeArea } from "@nutui/nutui-react-taro";
import { PurchaseOrderFinalApprove, PurchaseOrderRejectFinal } from "@/components"; import {
PurchaseOrderFinalApprove,
PurchaseOrderRejectFinal,
} from "@/components";
import buildUrl from "@/utils/buildUrl"; import buildUrl from "@/utils/buildUrl";
import { formatCurrency, formatUnitPrice } from "@/utils/format"; import { formatCurrency, formatUnitPrice } from "@/utils/format";
import { PurchaseOrderCalculator } from "@/utils/PurchaseOrderCalculator"; import { PurchaseOrderCalculator } from "@/utils/PurchaseOrderCalculator";
@ -62,8 +65,8 @@ export default hocAuth(function Page(props: CommonComponent) {
<View className="mb-1 text-center text-sm text-gray-500"> <View className="mb-1 text-center text-sm text-gray-500">
</View> </View>
<View className="flex flex-row gap-2.5 text-center"> <View className="flex flex-row items-center justify-center gap-2.5 text-center">
<View className="price-highlight text-primary"> <View className="text-primary text-3xl">
{formatUnitPrice(calculator.getAveragePurchasePrice())} {formatUnitPrice(calculator.getAveragePurchasePrice())}
</View> </View>
<View className="text-sm text-gray-500">/</View> <View className="text-sm text-gray-500">/</View>
@ -73,9 +76,9 @@ export default hocAuth(function Page(props: CommonComponent) {
<View className="mb-1 text-center text-sm text-gray-500"> <View className="mb-1 text-center text-sm text-gray-500">
</View> </View>
<View className="text-center"> <View className="flex flex-row items-center justify-center gap-2.5 text-center">
<View className="price-highlight text-green-500"> <View className="text-primary text-2xl">
{formatUnitPrice(calculator.getAverageSalesPrice())} {formatUnitPrice(calculator.getAverageSalesPrice())}
</View> </View>
<View className="text-sm text-gray-500">/</View> <View className="text-sm text-gray-500">/</View>
</View> </View>
@ -90,14 +93,14 @@ export default hocAuth(function Page(props: CommonComponent) {
<View className="cost-item flex flex-col px-3 py-2"> <View className="cost-item flex flex-col px-3 py-2">
<View className="text-sm text-gray-500">西</View> <View className="text-sm text-gray-500">西</View>
<View className="font-medium"> <View className="font-medium">
{formatCurrency(calculator.getSupplierPurchaseCost())} {formatCurrency(calculator.getSupplierPurchaseCost())}
</View> </View>
</View> </View>
{purchaseOrderVO.orderDealer?.freightCostFlag && ( {purchaseOrderVO.orderDealer?.freightCostFlag && (
<View className="cost-item flex flex-col px-3 py-2"> <View className="cost-item flex flex-col px-3 py-2">
<View className="text-sm text-gray-500"></View> <View className="text-sm text-gray-500"></View>
<View className="font-medium"> <View className="font-medium">
{purchaseOrderVO.orderVehicle.price} {purchaseOrderVO.orderVehicle.price}
</View> </View>
</View> </View>
)} )}
@ -109,7 +112,7 @@ export default hocAuth(function Page(props: CommonComponent) {
> >
<View className="text-sm text-gray-500">{item.name}</View> <View className="text-sm text-gray-500">{item.name}</View>
<View className="font-medium"> <View className="font-medium">
{item.price * item.count} {item.price * item.count}
</View> </View>
{item.name === "人工费" && ( {item.name === "人工费" && (
<View className="text-xs text-gray-500"> <View className="text-xs text-gray-500">
@ -128,7 +131,7 @@ export default hocAuth(function Page(props: CommonComponent) {
<View className="cost-item flex flex-col px-3 py-2"> <View className="cost-item flex flex-col px-3 py-2">
<View className="text-sm text-gray-500"></View> <View className="text-sm text-gray-500"></View>
<View className="font-medium"> <View className="font-medium">
{purchaseOrderVO.orderDealer?.taxSubsidy} {purchaseOrderVO.orderDealer?.taxSubsidy}
</View> </View>
</View> </View>
)} )}
@ -137,7 +140,7 @@ export default hocAuth(function Page(props: CommonComponent) {
<View className="cost-item flex flex-col px-3 py-2"> <View className="cost-item flex flex-col px-3 py-2">
<View className="text-sm text-gray-500"></View> <View className="text-sm text-gray-500"></View>
<View className="font-medium"> <View className="font-medium">
{purchaseOrderVO.orderDealer?.taxProvision} {purchaseOrderVO.orderDealer?.taxProvision}
</View> </View>
</View> </View>
)} )}
@ -146,7 +149,7 @@ export default hocAuth(function Page(props: CommonComponent) {
<View className="cost-item flex flex-col px-3 py-2"> <View className="cost-item flex flex-col px-3 py-2">
<View className="text-sm text-gray-500"></View> <View className="text-sm text-gray-500"></View>
<View className="font-medium"> <View className="font-medium">
{purchaseOrderVO.orderDealer?.costDifference} {purchaseOrderVO.orderDealer?.costDifference}
</View> </View>
</View> </View>
)} )}
@ -155,7 +158,7 @@ export default hocAuth(function Page(props: CommonComponent) {
<View className="flex flex-col"> <View className="flex flex-col">
<View className="text-sm text-gray-500"></View> <View className="text-sm text-gray-500"></View>
<View className="font-bold"> <View className="font-bold">
{calculator.getMelonCost1()} {calculator.getMelonCost1()}
</View> </View>
</View> </View>
<View className="flex flex-col"> <View className="flex flex-col">
@ -171,7 +174,7 @@ export default hocAuth(function Page(props: CommonComponent) {
<View className="flex items-center justify-between"> <View className="flex items-center justify-between">
<View className="text-gray-500"></View> <View className="text-gray-500"></View>
<View className="profit-highlight text-primary text-3xl"> <View className="profit-highlight text-primary text-3xl">
{calculator.getShareProfit()} {calculator.getShareProfit()}
</View> </View>
</View> </View>
</View> </View>

View File

@ -1,14 +1,7 @@
import hocAuth from "@/hocs/auth"; import hocAuth from "@/hocs/auth";
import { CommonComponent, CostItem, SupplierVO } from "@/types/typings"; import { CommonComponent, CostItem, SupplierVO } from "@/types/typings";
import { View } from "@tarojs/components"; import { View } from "@tarojs/components";
import { import { Button, Dialog, SafeArea, Toast, Tour, TourList } from "@nutui/nutui-react-taro";
Button,
Dialog,
SafeArea,
Toast,
Tour,
TourList,
} from "@nutui/nutui-react-taro";
import { purchase } from "@/constant"; import { purchase } from "@/constant";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { import {
@ -23,7 +16,7 @@ import {
SupplierList, SupplierList,
TicketUpload, TicketUpload,
Weigh, Weigh,
WeighRef, WeighRef
} from "@/components"; } from "@/components";
import { business } from "@/services"; import { business } from "@/services";
import { generateShortId } from "@/utils/generateShortId"; import { generateShortId } from "@/utils/generateShortId";
@ -190,7 +183,10 @@ export default hocAuth(function Page(props: CommonComponent) {
(item: CostItem) => ({ (item: CostItem) => ({
...item, ...item,
// 设置默认选中, // 设置默认选中,
selected: item.count > 0, selected:
item.selected ||
item.costType !== "PACKAGING_MATERIALS" ||
item.count > 0,
}), }),
) as any; ) as any;
setOrderCostList(orderCostList1); setOrderCostList(orderCostList1);
@ -414,6 +410,7 @@ export default hocAuth(function Page(props: CommonComponent) {
const { data } = await business.purchaseOrder.savePurchaseOrderStep3({ const { data } = await business.purchaseOrder.savePurchaseOrderStep3({
orderId: orderId, orderId: orderId,
active: active + 1, active: active + 1,
foreman: purchaseOrder.foreman,
orderCostList: purchaseOrder.orderCostList.filter( orderCostList: purchaseOrder.orderCostList.filter(
(item: CostItem) => item.selected, (item: CostItem) => item.selected,
), ),
@ -880,6 +877,13 @@ export default hocAuth(function Page(props: CommonComponent) {
setOrderPackageList(emptyBoxList); setOrderPackageList(emptyBoxList);
}} }}
costItemVOList={costItemVOList} costItemVOList={costItemVOList}
foreman={purchaseOrder?.foreman || ""}
setForeman={(foreman) => {
setPurchaseOrder((prev) => ({
...prev!,
foreman,
}));
}}
/> />
)} )}
</View> </View>

View File

@ -3467,6 +3467,8 @@ declare namespace BusinessAPI {
active?: number; active?: number;
/** 产地负责人 */ /** 产地负责人 */
originPrincipal?: string; originPrincipal?: string;
/** 工头 */
foreman?: string;
/** 备注 */ /** 备注 */
remark?: string; remark?: string;
/** 车辆信息 */ /** 车辆信息 */
@ -3582,6 +3584,8 @@ declare namespace BusinessAPI {
orderId: string; orderId: string;
/** 步骤标识 */ /** 步骤标识 */
active?: number; active?: number;
/** 工头 */
foreman?: string;
/** 采购订单费用信息 */ /** 采购订单费用信息 */
orderCostList: OrderCost[]; orderCostList: OrderCost[];
/** 采购订单包装箱信息 */ /** 采购订单包装箱信息 */
@ -3625,6 +3629,8 @@ declare namespace BusinessAPI {
active?: number; active?: number;
/** 产地负责人 */ /** 产地负责人 */
originPrincipal?: string; originPrincipal?: string;
/** 工头 */
foreman?: string;
/** 报价方式1_按毛重报价2_按净重报价 */ /** 报价方式1_按毛重报价2_按净重报价 */
pricingMethod?: "BY_GROSS_WEIGHT" | "BY_NET_WEIGHT"; pricingMethod?: "BY_GROSS_WEIGHT" | "BY_NET_WEIGHT";
/** 销售金额 */ /** 销售金额 */