ERPTurbo_Client/packages/app-client/src/pages/purchase/purchaser/create.tsx
shenyifei 3f8c6d962a feat(purchase): 优化采购模块人工费用和车辆信息处理逻辑
- 调整API域名配置,区分h5和小程序环境
- 重构OrderCost组件,支持多种费用类型筛选和展示
- 优化OrderVehicle组件,简化经销商信息赋值逻辑
- Weigh组件新增西瓜品种选择功能,包含弹窗和校验逻辑
- 重写LaborInfoSection组件,支持新增和编辑人工费用项
- 改进费用承担方和工头信息的处理流程
- 优化UI布局和交互体验
2025-11-16 19:14:15 +08:00

872 lines
27 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, CostItem, SupplierVO } from "@/types/typings";
import { View } from "@tarojs/components";
import { Button, Dialog, SafeArea, Toast } from "@nutui/nutui-react-taro";
import { purchase } from "@/constant";
import { useEffect, useRef, useState } from "react";
import {
MelonFarmer,
MelonFarmerRef,
OrderCost,
OrderCostRef,
OrderPackage,
OrderPackageRef,
OrderVehicle,
OrderVehicleRef,
SupplierList,
TicketUpload,
Weigh,
WeighRef,
} from "@/components";
import { calculateSupplierWeights } from "@/utils/calculateSupplierWeights";
import { business } from "@/services";
import { generateShortId } from "@/utils/generateShortId";
import Taro from "@tarojs/taro";
import buildUrl from "@/utils/buildUrl";
const defaultSupplierList: SupplierVO[] = [
{
orderSupplierId: generateShortId(),
supplierId: "",
name: "瓜农1",
idCard: "",
bankCard: "",
phone: "",
selected: true,
wechatQr: "",
isLast: undefined,
// 空磅是否包含纸箱
isPaper: undefined,
// 空磅重量
emptyWeight: undefined,
// 总磅重量
totalWeight: undefined,
// 采购单价
purchasePrice: undefined,
// 空磅照片
emptyWeightImg: "",
// 总磅照片
totalWeightImg: "",
orderPackageList: [],
},
];
export default hocAuth(function Page(props: CommonComponent) {
const { router, role, setLoading } = props;
const [orderId, setOrderId] = useState(
router.params.orderId as BusinessAPI.PurchaseOrderShowQry["orderId"],
);
const defaultStep = router.params.step as number;
const defaultSupplierId = router.params.supplierId as string;
const vehicleRef = useRef<OrderVehicleRef>(null);
// 创建MelonFarmer组件的ref数组
const melonFarmerRefs = useRef<MelonFarmerRef[]>([]);
// 创建Weigh组件的ref数组
const weighRefs = useRef<WeighRef[]>([]);
// 创建OrderCost组件的ref
const orderCostRef = useRef<OrderCostRef>(null);
// 创建OrderPackage组件的ref
const orderPackageRefs = useRef<OrderPackageRef[]>([]);
const [purchaseOrder, setPurchaseOrder] =
useState<BusinessAPI.PurchaseOrderCreateCmd>();
console.log("purchaseOrder", purchaseOrder);
const [step, setStep] = useState<any>();
const [active, setActive] = useState<number>(1);
const [orderVehicle, setOrderVehicle] =
useState<BusinessAPI.PurchaseOrderCreateCmd["orderVehicle"]>();
const [orderDealer, setOrderDealer] =
useState<BusinessAPI.PurchaseOrderCreateCmd["orderDealer"]>();
const [orderSupplierList, setOrderSupplierList] =
useState<SupplierVO[]>(defaultSupplierList);
const [orderCostList, setOrderCostList] = useState<CostItem[]>([]);
useEffect(() => {
if (orderId) {
setLoading(true);
business.purchaseOrder
.showPurchaseOrder({
purchaseOrderShowQry: {
orderId: orderId,
},
})
.then(({ data: { data: purchaseOrder, success } }) => {
if (success && purchaseOrder) {
// 如果订单状态不是草稿,则跳转到预览页面
if (purchaseOrder.state !== "DRAFT" && role === "origin-entry") {
Taro.redirectTo({
url: buildUrl("/pages/purchase/purchaser/preview", {
orderId: purchaseOrder.orderId,
}),
});
return;
}
setPurchaseOrder(purchaseOrder);
setOrderVehicle(purchaseOrder.orderVehicle);
const orderSupplierList1 = purchaseOrder.orderSupplierList as any;
console.log(
"orderSupplierList1",
orderSupplierList1,
defaultSupplierId,
);
if (defaultSupplierId) {
const index = orderSupplierList1.findIndex(
(item: any) => item.supplierId === defaultSupplierId,
);
orderSupplierList1[index].selected = true;
setOrderSupplierList(orderSupplierList1);
} else {
orderSupplierList1[0].selected = true;
setOrderSupplierList(orderSupplierList1);
}
if (defaultStep) {
setActive(Number(defaultStep));
} else {
const active = purchaseOrder.active || 1;
if (active < 1 || active > 6) {
setActive(1);
} else {
setActive(active);
}
}
const orderCostList1 = purchaseOrder.orderCostList.map(
(item: CostItem) => ({
...item,
// 设置默认选中,
selected: item.count > 0,
}),
) as any;
setOrderCostList(orderCostList1);
}
})
.catch((err) => {
console.error("获取采购订单失败:", err);
})
.finally(() => {
setLoading(false);
});
}
}, []);
useEffect(() => {
if (active) {
setStep(purchase.steps.find((item) => item.value === active));
// 同步更新purchaseOrder中的active值
setPurchaseOrder((prev) => ({ ...prev!, active }));
}
}, [active]);
useEffect(() => {
setPurchaseOrder((prev) => ({
...prev!,
orderSupplierList: calculateSupplierWeights(orderSupplierList),
}));
}, [orderSupplierList]);
useEffect(() => {
setPurchaseOrder((prev) => ({
...prev!,
orderVehicle: orderVehicle as BusinessAPI.OrderVehicle,
}));
}, [orderVehicle]);
useEffect(() => {
setPurchaseOrder((prev) => ({
...prev!,
orderDealer: orderDealer as BusinessAPI.OrderDealer,
}));
}, [orderDealer]);
useEffect(() => {
setPurchaseOrder((prev) => ({
...prev!,
orderCostList: orderCostList || [],
}));
}, [orderCostList]);
if (step === undefined) {
return;
}
const onAdd = () => {
// 获取当前选中的供应商
const selectedIndex = orderSupplierList.findIndex(
(supplier) => supplier.selected,
);
if (active === 2 && !melonFarmerRefs.current[selectedIndex]?.validate()) {
return;
}
// 在第三步(称重信息)时进行校验
else if (active === 3 && !weighRefs.current[selectedIndex]?.validate()) {
return;
} // 在第四步(包装信息)时进行校验
else if (
active === 4 &&
!orderPackageRefs.current[selectedIndex]?.validate()
) {
return;
}
setOrderSupplierList([
...orderSupplierList.map((supplierVO: SupplierVO) => {
supplierVO.selected = false;
return supplierVO;
}),
{
orderSupplierId: generateShortId(),
supplierId: "",
name: "瓜农" + (orderSupplierList.length + 1),
idCard: "",
bankCard: "",
phone: "",
selected: true,
isPaper: orderSupplierList[selectedIndex].isPaper,
orderPackageList: [],
},
]);
};
// 车辆信息和经销商信息的保存
const saveVehicleAndDealerInfo = async () => {
if (!purchaseOrder) {
Toast.show("toast", {
icon: "warn",
title: "提示",
content: "保存失败",
});
return false;
}
try {
let tempOrderId = orderId;
if (tempOrderId) {
const { data } = await business.purchaseOrder.savePurchaseOrderStep1({
orderId: tempOrderId,
active: active,
orderVehicle: purchaseOrder.orderVehicle,
orderDealer: purchaseOrder.orderDealer,
});
if (data.success) {
return true;
} else {
Toast.show("toast", {
icon: "warn",
title: "提示",
content: data?.errMessage || "保存失败",
});
return false;
}
} else {
const { data } = await business.purchaseOrder.savePurchaseOrderStep1({
active: active,
orderVehicle: purchaseOrder.orderVehicle,
orderDealer: purchaseOrder.orderDealer,
});
if (data.success) {
setOrderId(data.data?.orderId);
return true;
} else {
Toast.show("toast", {
icon: "warn",
title: "提示",
content: data?.errMessage || "保存失败",
});
return false;
}
}
} catch (error) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: "保存失败",
});
console.error("保存车辆和经销商信息失败:", error);
return false;
}
};
// 供应商信息的保存(基础信息,称重信息,包材信息)
const saveSupplierInfo = async () => {
if (!purchaseOrder || !orderId) {
Toast.show("toast", {
icon: "warn",
title: "提示",
content: "保存失败",
});
return false;
}
try {
const { data } = await business.purchaseOrder.savePurchaseOrderStep2({
orderId: orderId,
active: active,
orderSupplierList: purchaseOrder.orderSupplierList,
orderCostList: purchaseOrder.orderCostList,
});
if (data.success) {
return true;
} else {
Toast.show("toast", {
icon: "warn",
title: "提示",
content: data?.errMessage || "保存失败",
});
return false;
}
} catch (error) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: "保存失败",
});
console.error("保存供应商信息失败:", error);
return false;
}
};
// 人工和辅料等保存
const saveCostInfo = async () => {
if (!purchaseOrder || !orderId) {
Toast.show("toast", {
icon: "warn",
title: "提示",
content: "保存失败",
});
return false;
}
try {
const { data } = await business.purchaseOrder.savePurchaseOrderStep3({
orderId: orderId,
active: active + 1,
orderCostList: purchaseOrder.orderCostList.filter(
(item: CostItem) => item.selected,
),
});
if (data.success) {
return true;
} else {
Toast.show("toast", {
icon: "warn",
title: "提示",
content: data?.errMessage || "保存失败",
});
return false;
}
} catch (error) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: "保存失败",
});
console.error("保存人工和辅料信息失败:", error);
return false;
}
};
const onFinish = async () => {
Dialog.open("dialog", {
title: "预览确认",
content: "即将保存并预览当前采购订单,确定要继续吗?",
confirmText: "确认预览",
cancelText: "取消",
onConfirm: async () => {
// 只保存第六步的人工和辅料信息
const costSuccess = await saveCostInfo();
if (!costSuccess) {
Dialog.close("dialog");
return;
}
Toast.show("toast", {
icon: "success",
title: "提示",
content: "保存成功,正在跳转预览...",
});
// 跳转到预览页面
Taro.redirectTo({
url: buildUrl("/pages/purchase/purchaser/preview", {
orderId: orderId,
}),
});
Dialog.close("dialog");
},
onCancel: () => {
Dialog.close("dialog");
},
});
};
async function saveDraft() {
Dialog.open("dialog", {
title: "暂存确认",
content: "确定要暂存当前采购订单吗?",
confirmText: "确认暂存",
cancelText: "取消",
onConfirm: async () => {
// 按步骤调用保存接口进行暂存
let success = true;
// 保存第一步车辆和经销商信息
if (active == 1) {
success = await saveVehicleAndDealerInfo();
}
// 保存第二到第五步的供应商信息
if (success && active >= 2 && active <= 5) {
success = await saveSupplierInfo();
}
// 保存第六步的人工和辅料信息
if (success && active === 6) {
success = await saveCostInfo();
}
Dialog.close("dialog");
if (success) {
Toast.show("toast", {
icon: "success",
title: "提示",
content: "当前采购订单已暂存成功",
});
Taro.redirectTo({
url: "/pages/purchase/purchaser/history",
});
}
},
onCancel: () => {
Dialog.close("dialog");
},
});
}
return (
<>
<View className={"sticky top-0 z-11"}>
{step.value !== 7 && (
<View className="h-12 bg-white p-2.5 shadow-sm">
<View className="step-indicator flex h-2 rounded-full bg-gray-200">
<View
className="bg-primary rounded-full"
style={`width: ${(100 / purchase.steps.length) * step.value}%`}
></View>
</View>
<View className="mt-1 text-sm font-bold text-[#000000]">
{step.title}
</View>
</View>
)}
{(step.value === 2 ||
step.value === 3 ||
step.value === 4 ||
step.value === 5) && (
<SupplierList
value={orderSupplierList}
onSelect={(supplierVO: SupplierVO) => {
// 获取当前选中的供应商
const selectedIndex = orderSupplierList.findIndex(
(supplier) => supplier.selected,
);
if (
active === 2 &&
!melonFarmerRefs.current[selectedIndex]?.validate()
) {
return;
}
// 在第三步(称重信息)时进行校验
else if (
active === 3 &&
!weighRefs.current[selectedIndex]?.validate()
) {
return;
} // 在第四步(包装信息)时进行校验
else if (
active === 4 &&
!orderPackageRefs.current[selectedIndex]?.validate()
) {
return;
}
setOrderSupplierList(
orderSupplierList.map((item: any) => {
item.selected =
item.orderSupplierId === supplierVO.orderSupplierId;
return item;
}),
);
}}
/>
)}
</View>
<View className={"flex-1 bg-[#D1D5DB]"}>
{/* 车辆信息 */}
{step.value === 1 && (
<OrderVehicle
ref={vehicleRef}
orderVehicle={orderVehicle!}
setOrderVehicle={setOrderVehicle}
orderDealer={orderDealer!}
setOrderDealer={setOrderDealer}
/>
)}
{/* 瓜农信息 */}
{orderSupplierList.map((item: SupplierVO, index) => {
if (!item.selected) {
return;
}
if (step.value === 2) {
// 确保ref数组足够长
while (melonFarmerRefs.current.length <= index) {
melonFarmerRefs.current.push({
validate: () => true, // 默认验证方法
} as MelonFarmerRef);
}
// 获取已选择的供应商ID列表排除当前项
const selectedSupplierIds = orderSupplierList
.filter((supplier, idx) => idx !== index && supplier.supplierId)
.map((supplier) => supplier.supplierId!);
// 获取已选择的供应商ID列表排除当前项
const selectedSupplierNames = orderSupplierList
.filter((supplier, idx) => idx !== index && supplier.supplierId)
.map((supplier) => supplier.name!);
return (
<MelonFarmer
setLoading={setLoading}
supplierCount={orderSupplierList.length}
key={item.orderSupplierId}
ref={(ref) => {
if (ref) {
melonFarmerRefs.current[index] = ref;
}
}}
value={item}
onRemove={(supplierVO: SupplierVO) => {
if (orderSupplierList.length <= 1) {
setOrderSupplierList([
{
orderSupplierId: generateShortId(),
supplierId: "",
name: "瓜农1",
idCard: "",
bankCard: "",
phone: "",
selected: true,
orderPackageList: [],
},
]);
return;
}
let temp = orderSupplierList.filter(
(item: SupplierVO) =>
item.orderSupplierId !== supplierVO.orderSupplierId,
);
temp[0].selected = true;
setOrderSupplierList(temp);
}}
onChange={(supplierVO: SupplierVO) => {
orderSupplierList[index] = { ...item, ...supplierVO };
setOrderSupplierList([...orderSupplierList]);
}}
isLast={index === orderSupplierList.length - 1}
selectedSupplierIds={selectedSupplierIds}
selectedSupplierNames={selectedSupplierNames}
/>
);
}
// 称重信息
if (step.value === 3) {
// 确保ref数组足够长
while (weighRefs.current.length <= index) {
weighRefs.current.push({
validate: () => true, // 默认验证方法
} as WeighRef);
}
return (
<Weigh
changePaper={(isPaper) => {
setOrderSupplierList([
...orderSupplierList.map((item: SupplierVO) => {
item.isPaper = isPaper;
return item;
}),
]);
}}
changeProduct={(productVO: BusinessAPI.ProductVO) => {
setOrderSupplierList([
...orderSupplierList.map((item: SupplierVO) => {
item.productId = productVO.productId;
item.productName = productVO.name;
return item;
}),
]);
setPurchaseOrder((prev) => {
return {
...prev!,
orderCostList: [
...(prev?.orderCostList?.filter((item) => {
return (
item.costType !== "WORKER_ADVANCE" &&
item.costType !== "PRODUCTION_ADVANCE"
);
}) || []),
...(productVO.costItemVOList?.map((item) => {
return {
orderCostId: generateShortId(),
orderId: purchaseOrder?.orderId,
itemId: item.itemId,
name: item.name,
price: item.price,
unit: item.unit,
count: 0,
payerType: "US" as any,
principal: "",
costType: item.costType,
};
}) || []),
],
};
});
}}
isFirst={index === 0}
key={item.orderSupplierId}
ref={(ref) => {
if (ref) {
weighRefs.current[index] = ref;
}
}}
value={item}
onChange={(supplierVO: SupplierVO) => {
orderSupplierList[index] = { ...item, ...supplierVO };
setOrderSupplierList([...orderSupplierList]);
}}
/>
);
}
// 包装信息
if (step.value === 4) {
// 确保ref数组足够长
while (orderPackageRefs.current.length <= index) {
orderPackageRefs.current.push({
validate: () => true, // 默认验证方法
} as OrderPackageRef);
}
return (
<OrderPackage
key={item.orderSupplierId}
ref={(ref) => {
if (ref) {
orderPackageRefs.current[index] = ref;
}
}}
value={item}
onChange={(supplierVO: SupplierVO) => {
orderSupplierList[index] = { ...item, ...supplierVO };
setOrderSupplierList([...orderSupplierList]);
}}
/>
);
}
// 票证上传
if (step.value === 5) {
return (
<TicketUpload
key={item.orderSupplierId}
value={item}
onChange={(supplierVO: SupplierVO) => {
orderSupplierList[index] = { ...item, ...supplierVO };
setOrderSupplierList([...orderSupplierList]);
}}
/>
);
}
})}
{/* 人工和辅料信息 */}
{step.value === 6 && (
<OrderCost
supplierVO={orderSupplierList[orderSupplierList.length - 1]}
onAdd={() => {
saveSupplierInfo();
setActive(2);
onAdd();
}}
ref={orderCostRef}
value={orderCostList}
onChange={(costItemList: CostItem[]) =>
setOrderCostList(costItemList)
}
/>
)}
</View>
{/* 按钮操作 */}
<View className={"sticky bottom-0 z-10 bg-white"}>
<View className="flex justify-between gap-2 border-t border-gray-200 p-2.5">
{active > 1 && active <= 6 && (
<View className={"flex-1"}>
<Button
block
type={"default"}
size={"xlarge"}
className="flex-1 bg-gray-200 text-gray-700"
onClick={() => {
if (active > 1) {
setActive(active - 1);
}
}}
>
</Button>
</View>
)}
{active >= 1 && active <= 6 && (
<View className={"flex-1"}>
<Button
block
type={"default"}
size={"xlarge"}
className="flex-1 bg-gray-200 text-gray-700"
onClick={async () => {
await saveDraft();
}}
>
</Button>
</View>
)}
{active < 6 && (
<View className={"flex-1"}>
<Button
block
type={"primary"}
size={"xlarge"}
className="bg-primary flex-1 text-white"
onClick={async () => {
setLoading(true);
// 在第一步(车辆信息)时进行校验
if (active === 1) {
if (vehicleRef.current?.validate()) {
const success = await saveVehicleAndDealerInfo();
if (success) {
setActive(active + 1);
}
}
}
// 在第二步(瓜农信息)时进行校验
else if (active === 2) {
// 获取当前选中的供应商
const selectedIndex = orderSupplierList.findIndex(
(supplier) => supplier.selected,
);
if (
selectedIndex !== -1 &&
melonFarmerRefs.current[selectedIndex]?.validate()
) {
const success = await saveSupplierInfo();
if (success) {
setActive(active + 1);
}
}
}
// 在第三步(称重信息)时进行校验
else if (active === 3) {
// 获取当前选中的供应商
const selectedIndex = orderSupplierList.findIndex(
(supplier) => supplier.selected,
);
if (
selectedIndex !== -1 &&
weighRefs.current[selectedIndex]?.validate()
) {
const success = await saveSupplierInfo();
if (success) {
setActive(active + 1);
}
}
} // 在第四步(包装信息)时进行校验
else if (active === 4) {
// 获取当前选中的供应商
const selectedIndex = orderSupplierList.findIndex(
(supplier) => supplier.selected,
);
if (
selectedIndex !== -1 &&
orderPackageRefs.current[selectedIndex]?.validate()
) {
const success = await saveSupplierInfo();
if (success) {
setActive(active + 1);
}
}
} // 在第五步(票证上传)时进行校验
else if (active === 5) {
const success = await saveSupplierInfo();
if (success) {
setActive(active + 1);
}
} else {
setActive(active + 1);
}
setLoading(false);
}}
>
</Button>
</View>
)}
{active === 6 && (
<>
<View className={"flex-1"}>
<Button
block
type={"primary"}
size={"xlarge"}
className="btn-large bg-primary ml-2 flex-1 text-white"
onClick={async () => {
setLoading(true);
// 第六步(人工辅料)时进行校验
if (orderCostRef.current?.validate()) {
await onFinish();
}
setLoading(false);
}}
>
</Button>
</View>
</>
)}
</View>
<SafeArea position={"bottom"} />
</View>
</>
);
});