feat(purchase): 添加空箱管理和订单修改追踪功能

- 在 OrderCost 组件中实现空箱品牌选择、批量添加和编辑功能
- 添加空箱类型启用状态管理及 UI 展示
- 实现空箱数据转换工具函数 convertBoxBrandToOrderPackages 和 convertOrderPackagesToBoxBrands
- 在创建订单页面添加修改标识状态管理,追踪车辆、瓜农、称重、包装、票证和成本信息变更
- 添加 lodash 用于深度比较对象变更
- 移除多个调试用 console.log语句
- 优化保存逻辑,仅在信息变更时调用接口
This commit is contained in:
shenyifei 2025-11-18 10:10:21 +08:00
parent 5eb4e0f4bb
commit fb71cf003e
7 changed files with 1210 additions and 33 deletions

View File

@ -1,12 +1,12 @@
import { Text, View } from "@tarojs/components";
import {
Button,
Dialog,
Input,
Radio,
Toast,
Uploader,
UploaderFileItem,
Dialog,
} from "@nutui/nutui-react-taro";
import { Icon, SupplierPicker } from "@/components";
import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
@ -47,7 +47,6 @@ export default forwardRef<MelonFarmerRef, IMelonFarmerProps>(
} = props;
const [supplierVO, setSupplierVO] = useState<SupplierVO>();
console.log("supplierVO", supplierVO);
// 初始化数据
useEffect(() => {

View File

@ -785,8 +785,6 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
<View className="flex w-fit flex-row gap-2.5">
{boxBrandList
?.filter((item) => {
console.log("item: ", item);
console.log("boxType: ", boxType);
if (boxType === "OWN") {
return item.boxBrandType === "FARMER_BOX";
} else {

View File

@ -30,6 +30,7 @@ import { generateShortId } from "@/utils/generateShortId";
import Taro from "@tarojs/taro";
import buildUrl from "@/utils/buildUrl";
import { SupplierWeightCalculator } from "@/utils/SupplierWeightCalculator";
import _ from "lodash";
const defaultSupplierList: SupplierVO[] = [
{
@ -59,6 +60,16 @@ const defaultSupplierList: SupplierVO[] = [
},
];
// 添加修改标识的类型定义
type ModifiedFlags = {
vehicle?: boolean; // 车辆信息是否修改
farmer?: boolean; // 瓜农信息是否修改
weigh?: boolean; // 称重信息是否修改
package?: boolean; // 包装信息是否修改
ticket?: boolean; // 票证信息是否修改
cost?: boolean; // 人工辅料信息是否修改
};
export default hocAuth(function Page(props: CommonComponent) {
const { router, role, setLoading } = props;
@ -103,6 +114,20 @@ export default hocAuth(function Page(props: CommonComponent) {
console.log("purchaseOrder", purchaseOrder);
// 存储初始状态用于比较
const [initialState, setInitialState] = useState<{
orderVehicle?: BusinessAPI.PurchaseOrderCreateCmd["orderVehicle"];
orderDealer?: BusinessAPI.PurchaseOrderCreateCmd["orderDealer"];
orderSupplierList?: BusinessAPI.PurchaseOrderCreateCmd["orderSupplierList"];
orderCostList?: BusinessAPI.PurchaseOrderCreateCmd["orderCostList"];
orderPackageList?: BusinessAPI.PurchaseOrderCreateCmd["orderPackageList"];
}>({});
// 添加修改标识状态
const [modifiedFlags, setModifiedFlags] = useState<ModifiedFlags>({});
console.log("modifiedFlags", modifiedFlags);
const [step, setStep] = useState<any>();
const [active, setActive] = useState<number>(1);
@ -113,6 +138,9 @@ export default hocAuth(function Page(props: CommonComponent) {
const [orderSupplierList, setOrderSupplierList] =
useState<SupplierVO[]>(defaultSupplierList);
const [orderCostList, setOrderCostList] = useState<CostItem[]>([]);
const [orderPackageList, setOrderPackageList] = useState<
BusinessAPI.OrderPackage[]
>([]);
useEffect(() => {
if (orderId) {
@ -137,12 +165,14 @@ export default hocAuth(function Page(props: CommonComponent) {
setPurchaseOrder(purchaseOrder);
setOrderVehicle(purchaseOrder.orderVehicle);
setInitialState((prev) => ({
...prev,
orderVehicle: _.cloneDeep(purchaseOrder.orderVehicle),
orderDealer: _.cloneDeep(purchaseOrder.orderDealer),
orderSupplierList: _.cloneDeep(purchaseOrder.orderSupplierList),
orderCostList: _.cloneDeep(purchaseOrder.orderCostList),
}));
const orderSupplierList1 = purchaseOrder.orderSupplierList as any;
console.log(
"orderSupplierList1",
orderSupplierList1,
defaultSupplierId,
);
if (defaultSupplierId) {
const index = orderSupplierList1.findIndex(
(item: any) => item.supplierId === defaultSupplierId,
@ -179,6 +209,9 @@ export default hocAuth(function Page(props: CommonComponent) {
.finally(() => {
setLoading(false);
});
} else {
// 对于新订单,设置初始状态为空对象
setInitialState({});
}
}, []);
@ -207,6 +240,12 @@ export default hocAuth(function Page(props: CommonComponent) {
...prev!,
orderVehicle: orderVehicle as BusinessAPI.OrderVehicle,
}));
// 检查车辆信息是否发生变化
if (initialState.orderVehicle || orderVehicle) {
const isModified = !_.isEqual(initialState.orderVehicle, orderVehicle);
setModifiedFlags((prev) => ({ ...prev, vehicle: isModified }));
}
}, [orderVehicle]);
useEffect(() => {
@ -214,6 +253,12 @@ export default hocAuth(function Page(props: CommonComponent) {
...prev!,
orderDealer: orderDealer as BusinessAPI.OrderDealer,
}));
// 检查经销商信息是否发生变化
if (initialState.orderDealer || orderDealer) {
const isModified = !_.isEqual(initialState.orderDealer, orderDealer);
setModifiedFlags((prev) => ({ ...prev, vehicle: isModified })); // 注意这里vehicle是正确的因为dealer和vehicle属于同一组
}
}, [orderDealer]);
useEffect(() => {
@ -221,8 +266,141 @@ export default hocAuth(function Page(props: CommonComponent) {
...prev!,
orderCostList: orderCostList || [],
}));
// 检查成本列表是否发生变化
if (initialState.orderCostList || orderCostList) {
const isModified = !_.isEqual(initialState.orderCostList, orderCostList);
setModifiedFlags((prev) => ({ ...prev, cost: isModified }));
}
}, [orderCostList]);
useEffect(() => {
setPurchaseOrder((prev) => ({
...prev!,
orderPackageList: orderPackageList || [],
}));
// 检查成本列表是否发生变化
if (initialState.orderPackageList || orderPackageList) {
const isModified = !_.isEqual(
initialState.orderPackageList,
orderPackageList,
);
setModifiedFlags((prev) => ({ ...prev, cost: isModified }));
}
}, [orderPackageList]);
// 检查供应商信息是否发生变化的辅助函数
const checkSupplierModified = (
currentList: SupplierVO[],
initialList: SupplierVO[] | undefined,
) => {
if (!initialList) {
// 如果没有初始状态,则检查当前列表是否有实质内容
return currentList.some(
(supplier) =>
supplier.supplierId ||
supplier.name ||
supplier.idCard ||
supplier.bankCard ||
supplier.phone ||
supplier.emptyWeight ||
supplier.totalWeight ||
supplier.purchasePrice ||
(supplier.orderPackageList && supplier.orderPackageList.length > 0),
);
}
return !_.isEqual(currentList, initialList);
};
// 当供应商列表改变时检查修改状态
useEffect(() => {
const isModified = checkSupplierModified(
orderSupplierList,
initialState.orderSupplierList as any,
);
// 检查具体哪个部分被修改了
if (isModified && initialState.orderSupplierList) {
const hasFarmerChanges = !_.isEqual(
orderSupplierList.map((s) => ({
supplierId: s.supplierId,
name: s.name,
idCard: s.idCard,
bankCard: s.bankCard,
phone: s.phone,
})),
initialState.orderSupplierList.map((s) => ({
supplierId: s.supplierId,
name: s.name,
idCard: s.idCard,
bankCard: s.bankCard,
phone: s.phone,
})),
);
const hasWeighChanges = !_.isEqual(
orderSupplierList.map((s) => ({
emptyWeight: s.emptyWeight,
totalWeight: s.totalWeight,
purchasePrice: s.purchasePrice,
isPaper: s.isPaper,
})),
initialState.orderSupplierList.map((s) => ({
emptyWeight: s.emptyWeight,
totalWeight: s.totalWeight,
purchasePrice: s.purchasePrice,
isPaper: s.isPaper,
})),
);
const hasPackageChanges = !_.isEqual(
orderSupplierList.map((s) => s.orderPackageList),
initialState.orderSupplierList.map((s) => s.orderPackageList),
);
const hasTicketChanges = !_.isEqual(
orderSupplierList.map((s) => ({
emptyWeightImg: s.emptyWeightImg,
totalWeightImg: s.totalWeightImg,
wechatQr: s.wechatQr,
})),
initialState.orderSupplierList.map((s) => ({
emptyWeightImg: s.emptyWeightImg,
totalWeightImg: s.totalWeightImg,
wechatQr: s.wechatQr,
})),
);
setModifiedFlags((prev) => ({
...prev,
farmer: hasFarmerChanges,
weigh: hasWeighChanges,
package: hasPackageChanges,
ticket: hasTicketChanges,
}));
} else if (isModified) {
// 如果没有初始状态但有修改,标记所有为已修改
setModifiedFlags((prev) => ({
...prev,
farmer: true,
weigh: true,
package: true,
ticket: true,
}));
} else {
// 没有修改
setModifiedFlags((prev) => ({
...prev,
farmer: false,
weigh: false,
package: false,
ticket: false,
}));
}
}, [orderSupplierList]);
if (step === undefined) {
return;
}
@ -266,6 +444,11 @@ export default hocAuth(function Page(props: CommonComponent) {
// 车辆信息和经销商信息的保存
const saveVehicleAndDealerInfo = async () => {
// 如果没有修改过车辆信息,则不调用接口
if (!modifiedFlags.vehicle) {
return true;
}
if (!purchaseOrder) {
Toast.show("toast", {
icon: "warn",
@ -287,6 +470,14 @@ export default hocAuth(function Page(props: CommonComponent) {
});
if (data.success) {
// 保存成功后更新初始状态
setInitialState((prev) => ({
...prev,
orderVehicle: _.cloneDeep(purchaseOrder.orderVehicle),
orderDealer: _.cloneDeep(purchaseOrder.orderDealer),
orderCostList: _.cloneDeep(purchaseOrder.orderCostList),
}));
setModifiedFlags((prev) => ({ ...prev, vehicle: false }));
return true;
} else {
Toast.show("toast", {
@ -306,6 +497,14 @@ export default hocAuth(function Page(props: CommonComponent) {
if (data.success) {
setOrderId(data.data?.orderId);
// 保存成功后更新初始状态
setInitialState((prev) => ({
...prev,
orderVehicle: _.cloneDeep(purchaseOrder.orderVehicle),
orderDealer: _.cloneDeep(purchaseOrder.orderDealer),
orderCostList: _.cloneDeep(purchaseOrder.orderCostList),
}));
setModifiedFlags((prev) => ({ ...prev, vehicle: false }));
return true;
} else {
Toast.show("toast", {
@ -329,6 +528,18 @@ export default hocAuth(function Page(props: CommonComponent) {
// 供应商信息的保存(基础信息,称重信息,包材信息)
const saveSupplierInfo = async () => {
// 检查是否有任何供应商相关信息被修改
const hasSupplierModification =
modifiedFlags.farmer ||
modifiedFlags.weigh ||
modifiedFlags.package ||
modifiedFlags.ticket;
// 如果没有任何供应商相关信息被修改,则不调用接口
if (!hasSupplierModification) {
return true;
}
if (!purchaseOrder || !orderId) {
Toast.show("toast", {
icon: "warn",
@ -347,6 +558,19 @@ export default hocAuth(function Page(props: CommonComponent) {
});
if (data.success) {
// 保存成功后更新初始状态
setInitialState((prev) => ({
...prev,
orderSupplierList: _.cloneDeep(purchaseOrder.orderSupplierList),
}));
// 保存成功后重置所有供应商相关的修改标识
setModifiedFlags((prev) => ({
...prev,
farmer: false,
weigh: false,
package: false,
ticket: false,
}));
return true;
} else {
Toast.show("toast", {
@ -369,6 +593,11 @@ export default hocAuth(function Page(props: CommonComponent) {
// 人工和辅料等保存
const saveCostInfo = async () => {
// 如果没有修改过人工辅料信息,则不调用接口
if (!modifiedFlags.cost) {
return true;
}
if (!purchaseOrder || !orderId) {
Toast.show("toast", {
icon: "warn",
@ -388,6 +617,13 @@ export default hocAuth(function Page(props: CommonComponent) {
});
if (data.success) {
// 保存成功后更新初始状态
setInitialState((prev) => ({
...prev,
orderCostList: _.cloneDeep(purchaseOrder.orderCostList),
}));
// 保存成功后重置修改标识
setModifiedFlags((prev) => ({ ...prev, cost: false }));
return true;
} else {
Toast.show("toast", {
@ -551,11 +787,18 @@ export default hocAuth(function Page(props: CommonComponent) {
<OrderVehicle
ref={vehicleRef}
orderVehicle={orderVehicle!}
setOrderVehicle={setOrderVehicle}
setOrderVehicle={(orderVehicle) => {
setOrderVehicle(orderVehicle);
// 修改标识将在useEffect中自动更新
}}
orderDealer={orderDealer!}
setOrderDealer={setOrderDealer}
setOrderDealer={(orderDealer) => {
setOrderDealer(orderDealer);
// 修改标识将在useEffect中自动更新
}}
orderCostList={orderCostList}
setOrderCostList={(costItemList: CostItem[]) => {
// 修改标识将在useEffect中自动更新
setOrderCostList(costItemList);
}}
/>
@ -620,10 +863,13 @@ export default hocAuth(function Page(props: CommonComponent) {
temp[0].selected = true;
setOrderSupplierList(temp);
// 修改标识将在useEffect中自动更新
}}
onChange={(supplierVO: SupplierVO) => {
orderSupplierList[index] = { ...item, ...supplierVO };
setOrderSupplierList([...orderSupplierList]);
console.log("supplierVO", supplierVO);
// 修改标识将在useEffect中自动更新
}}
isLast={index === orderSupplierList.length - 1}
selectedSupplierIds={selectedSupplierIds}
@ -650,6 +896,7 @@ export default hocAuth(function Page(props: CommonComponent) {
return item;
}),
]);
// 修改标识将在useEffect中自动更新
}}
changeProduct={(productVO: BusinessAPI.ProductVO) => {
setOrderSupplierList([
@ -711,6 +958,7 @@ export default hocAuth(function Page(props: CommonComponent) {
],
};
});
// 修改标识将在useEffect中自动更新
}}
isFirst={index === 0}
key={item.orderSupplierId}
@ -723,6 +971,7 @@ export default hocAuth(function Page(props: CommonComponent) {
onChange={(supplierVO: SupplierVO) => {
orderSupplierList[index] = { ...item, ...supplierVO };
setOrderSupplierList([...orderSupplierList]);
// 修改标识将在useEffect中自动更新
}}
/>
);
@ -749,6 +998,7 @@ export default hocAuth(function Page(props: CommonComponent) {
onChange={(supplierVO: SupplierVO) => {
orderSupplierList[index] = { ...item, ...supplierVO };
setOrderSupplierList([...orderSupplierList]);
// 修改标识将在useEffect中自动更新
}}
/>
);
@ -763,6 +1013,7 @@ export default hocAuth(function Page(props: CommonComponent) {
onChange={(supplierVO: SupplierVO) => {
orderSupplierList[index] = { ...item, ...supplierVO };
setOrderSupplierList([...orderSupplierList]);
// 修改标识将在useEffect中自动更新
}}
/>
);
@ -780,9 +1031,14 @@ export default hocAuth(function Page(props: CommonComponent) {
}}
ref={orderCostRef}
value={orderCostList}
onChange={(costItemList: CostItem[]) =>
setOrderCostList(costItemList)
}
onChange={(costItemList: CostItem[]) => {
// 修改标识将在useEffect中自动更新
setOrderCostList(costItemList);
}}
emptyBoxList={orderPackageList}
onEmptyBoxChange={(emptyBoxList: BusinessAPI.OrderPackage[]) => {
setOrderPackageList(emptyBoxList);
}}
/>
)}
</View>

View File

@ -2576,7 +2576,7 @@ declare namespace BusinessAPI {
/** 销售单价(元/个) */
boxSalePrice?: number;
/** 箱子类型:1_本次使用2_额外运输3_已使用额外运输4_车上剩余 */
boxType: "USED" | "EXTRA" | "EXTRA_USED" | "REMAIN" | "OWN";
boxType: "USED" | "EXTRA" | "EXTRA_USED" | "REMAIN" | "OWN" | "EMPTY";
};
type OrderRebate = {
@ -3362,6 +3362,8 @@ declare namespace BusinessAPI {
orderSupplierList: OrderSupplier[];
/** 采购订单费用信息 */
orderCostList: OrderCost[];
/** 采购订单空箱费用 */
orderPackageList: OrderPackage[];
};
type PurchaseOrderDestroyCmd = {
@ -3552,6 +3554,8 @@ declare namespace BusinessAPI {
orderSupplierList: OrderSupplier[];
/** 采购订单费用信息 */
orderCostList: OrderCost[];
/** 采购订单空箱费用 */
orderPackageList: OrderPackage[];
};
type PurchaseOrderWithdrawReviewCmd = {

View File

@ -36,7 +36,7 @@ export interface BoxBrand {
boxBrandName: string;
boxBrandImage: string;
boxBrandType: "OUR_BOX" | "FARMER_BOX" | "THIRD_PARTY_BOX" | "GIFT_BOX";
boxType: "USED" | "EXTRA" | "EXTRA_USED" | "REMAIN" | "OWN" | "DEFAULT";
boxType: "USED" | "EXTRA" | "EXTRA_USED" | "REMAIN" | "OWN" | "EMPTY";
boxSpecList: BoxSpec[];
}

View File

@ -69,19 +69,19 @@ export class SupplierWeightCalculator {
if (!supplier.isPaper) {
// 如果不是纸箱包装直接使用原始重量kg转斤
supplier.grossWeight = new Decimal(supplier.totalWeight)
.sub(supplier.emptyWeight)
supplier.grossWeight = new Decimal(supplier.totalWeight || 0)
.sub(supplier.emptyWeight || 0)
.mul(2)
.toNumber();
supplier.netWeight = new Decimal(supplier.grossWeight)
supplier.netWeight = new Decimal(supplier.grossWeight || 0)
.sub(usedBoxesWeight)
.sub(extraUsedBoxesWeight)
.toNumber();
previousTotalWeight = supplier.totalWeight;
supplier.invoiceAmount = new Decimal(supplier.netWeight)
.mul(supplier.purchasePrice)
supplier.invoiceAmount = new Decimal(supplier.netWeight || 0)
.mul(supplier.purchasePrice || 0)
.toNumber();
continue;
}