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

View File

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

View File

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

View File

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

View File

@ -36,7 +36,7 @@ export interface BoxBrand {
boxBrandName: string; boxBrandName: string;
boxBrandImage: string; boxBrandImage: string;
boxBrandType: "OUR_BOX" | "FARMER_BOX" | "THIRD_PARTY_BOX" | "GIFT_BOX"; 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[]; boxSpecList: BoxSpec[];
} }

View File

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