feat(purchase): 优化瓜农信息添加逻辑与包装信息删除确认

- 移除了 MelonFarmer 组件中冗余的 isLastFarmer 状态和相关逻辑
- 调整了添加瓜农按钮的显示条件,仅在当前为最后一个瓜农且不为最后时显示
- 在 OrderPackage 中添加删除纸箱品牌时的确认对话框
- 修改了包装信息编辑按钮文本为“修改数量”,删除按钮文本为“不使用了”
- 优化了 OrderCost 组件中添加瓜农按钮的显示逻辑
- 调整了称重信息中提示文本的表述
- 统一了页面中添加瓜农的逻辑处理,避免重复代码
- 修复了保存草稿时的异步调用问题,确保流程正确执行
This commit is contained in:
shenyifei 2025-11-10 17:59:48 +08:00
parent f77acab4cb
commit 6c3b60f7b8
7 changed files with 114 additions and 134 deletions

View File

@ -25,7 +25,6 @@ interface IMelonFarmerProps {
value: SupplierVO; value: SupplierVO;
onChange: (supplierVO: SupplierVO) => void; onChange: (supplierVO: SupplierVO) => void;
onRemove: (supplierVO: SupplierVO) => void; onRemove: (supplierVO: SupplierVO) => void;
onAdd: () => void;
isLast: boolean; // 添加一个属性来标识是否是最后一个瓜农 isLast: boolean; // 添加一个属性来标识是否是最后一个瓜农
supplierCount: number; supplierCount: number;
// 添加已选择的供应商ID列表属性 // 添加已选择的供应商ID列表属性
@ -40,7 +39,6 @@ export default forwardRef<MelonFarmerRef, IMelonFarmerProps>(
value, value,
onChange, onChange,
onRemove, onRemove,
onAdd,
isLast, isLast,
supplierCount, supplierCount,
selectedSupplierIds, selectedSupplierIds,
@ -54,9 +52,6 @@ export default forwardRef<MelonFarmerRef, IMelonFarmerProps>(
// 初始化数据 // 初始化数据
useEffect(() => { useEffect(() => {
setSupplierVO(value); setSupplierVO(value);
if (value.isLast !== undefined) {
setIsLastFarmer(value.isLast);
}
// 初始化微信二维码图片列表 // 初始化微信二维码图片列表
if (value.wechatQr) { if (value.wechatQr) {
setPicList([ setPicList([
@ -77,7 +72,6 @@ export default forwardRef<MelonFarmerRef, IMelonFarmerProps>(
}, [supplierVO]); }, [supplierVO]);
const [picList, setPicList] = useState<UploaderFileItem[]>([]); const [picList, setPicList] = useState<UploaderFileItem[]>([]);
const [isLastFarmer, setIsLastFarmer] = useState<boolean | null>(null); // 修改状态类型null表示未选择
// 微信二维码变更处理函数 // 微信二维码变更处理函数
const handleWechatQrChange = (files: UploaderFileItem[]) => { const handleWechatQrChange = (files: UploaderFileItem[]) => {
@ -167,7 +161,10 @@ export default forwardRef<MelonFarmerRef, IMelonFarmerProps>(
const isNameValid = validateName(supplierVO.name || ""); const isNameValid = validateName(supplierVO.name || "");
if (selectedSupplierNames && selectedSupplierNames.includes(supplierVO.name || "")) { if (
selectedSupplierNames &&
selectedSupplierNames.includes(supplierVO.name || "")
) {
Toast.show("toast", { Toast.show("toast", {
icon: "fail", icon: "fail",
title: "提示", title: "提示",
@ -568,8 +565,6 @@ export default forwardRef<MelonFarmerRef, IMelonFarmerProps>(
...supplierVO, ...supplierVO,
isLast: isLastValue, isLast: isLastValue,
}); });
setIsLastFarmer(isLastValue!);
}} }}
> >
<Radio value="false"></Radio> <Radio value="false"></Radio>
@ -655,7 +650,7 @@ export default forwardRef<MelonFarmerRef, IMelonFarmerProps>(
<View className={"text-primary text-sm font-bold"}> <View className={"text-primary text-sm font-bold"}>
{supplierVO.name || "瓜农"} {supplierVO.name || "瓜农"}
</View> </View>
{supplierCount > 1 && ( {supplierCount > 1 && isLast && (
<View <View
className="cursor-pointer text-sm text-red-500" className="cursor-pointer text-sm text-red-500"
onClick={() => handleRemoveConfirm(supplierVO)} onClick={() => handleRemoveConfirm(supplierVO)}
@ -862,23 +857,6 @@ export default forwardRef<MelonFarmerRef, IMelonFarmerProps>(
/> />
</View> </View>
</View> </View>
{/* 只有当用户选择"否"时才显示添加按钮 */}
{isLast && !isLastFarmer && isLastFarmer !== null && (
<Button
icon={<Icon name={"plus"} size={20} />}
type={"primary"}
size={"xlarge"}
fill={"outline"}
block
className="border-primary text-primary flex w-full items-center justify-center !border-4 !bg-white"
onClick={() => {
onAdd();
}}
>
<View></View>
</Button>
)}
</View> </View>
); );
}, },

View File

@ -3,7 +3,7 @@ import { Icon } from "@/components";
import { forwardRef, useEffect, useImperativeHandle, useState } from "react"; import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
import { business } from "@/services"; import { business } from "@/services";
import { Button, Checkbox, Input, Toast } from "@nutui/nutui-react-taro"; import { Button, Checkbox, Input, Toast } from "@nutui/nutui-react-taro";
import { CostItem } from "@/types/typings"; import { CostItem, SupplierVO } from "@/types/typings";
import { generateShortId } from "@/utils/generateShortId"; import { generateShortId } from "@/utils/generateShortId";
// 定义ref暴露的方法接口 // 定义ref暴露的方法接口
@ -13,12 +13,14 @@ export interface OrderCostRef {
export interface IOrderCostProps { export interface IOrderCostProps {
value: CostItem[]; value: CostItem[];
supplierVO: SupplierVO;
onChange?: (costItemList: CostItem[]) => void; onChange?: (costItemList: CostItem[]) => void;
onAdd: () => void;
} }
export default forwardRef<OrderCostRef, IOrderCostProps>( export default forwardRef<OrderCostRef, IOrderCostProps>(
function OrderCost(IOrderCostProps, ref) { function OrderCost(IOrderCostProps, ref) {
const { value, onChange } = IOrderCostProps; const { value, supplierVO, onChange, onAdd } = IOrderCostProps;
const [costItemVOList, setCostItemVOList] = useState<CostItem[]>(); const [costItemVOList, setCostItemVOList] = useState<CostItem[]>();
@ -274,7 +276,7 @@ export default forwardRef<OrderCostRef, IOrderCostProps>(
<View> <View>
{/* 费用承担方改为按钮形式参考OrderPackage中的样式 */} {/* 费用承担方改为按钮形式参考OrderPackage中的样式 */}
<View className="flex items-center justify-between"> <View className="flex items-center justify-between">
<View className="text-sm flex-shrink-0">:</View> <View className="flex-shrink-0 text-sm">:</View>
<View className="flex gap-2"> <View className="flex gap-2">
<Button <Button
size="small" size="small"
@ -308,7 +310,7 @@ export default forwardRef<OrderCostRef, IOrderCostProps>(
{item.selected && ( {item.selected && (
<View> <View>
<View className="flex items-center justify-between gap-2"> <View className="flex items-center justify-between gap-2">
<View className="text-sm flex-shrink-0">:</View> <View className="flex-shrink-0 text-sm">:</View>
<View className="flex items-center"> <View className="flex items-center">
<View <View
className="rounded-l-button flex !size-12 flex-none items-center justify-center bg-gray-200 text-sm" className="rounded-l-button flex !size-12 flex-none items-center justify-center bg-gray-200 text-sm"
@ -446,6 +448,23 @@ export default forwardRef<OrderCostRef, IOrderCostProps>(
{/* <View className="mb-2.5 text-sm font-bold">空礼盒费用</View>*/} {/* <View className="mb-2.5 text-sm font-bold">空礼盒费用</View>*/}
{/* {renderItemList(getItemsByCostType("OTHER_COST"), "OTHER_COST")}*/} {/* {renderItemList(getItemsByCostType("OTHER_COST"), "OTHER_COST")}*/}
{/*</View>*/} {/*</View>*/}
{/* 只有当用户选择"否"时才显示添加按钮 */}
{!supplierVO.isLast && (
<Button
icon={<Icon name={"plus"} size={20} />}
type={"primary"}
size={"xlarge"}
fill={"outline"}
block
className="border-primary text-primary flex w-full items-center justify-center !border-4 !bg-white"
onClick={() => {
onAdd();
}}
>
<View></View>
</Button>
)}
</View> </View>
); );
}, },

View File

@ -2,6 +2,7 @@ import { ScrollView, View } from "@tarojs/components";
import { Icon } from "@/components"; import { Icon } from "@/components";
import { import {
Button, Button,
Dialog,
Image, Image,
Input, Input,
Popup, Popup,
@ -98,33 +99,12 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
} }
// 检查所需选项是否都已做出选择不是1就是2不能是0 // 检查所需选项是否都已做出选择不是1就是2不能是0
console.log("requiredTypes", requiredTypes, packageTypeEnabled);
const allRequiredAnswered = requiredTypes.every( const allRequiredAnswered = requiredTypes.every(
(type) => (type) =>
packageTypeEnabled[type] === 1 || packageTypeEnabled[type] === 2, packageTypeEnabled[type] === 1 || packageTypeEnabled[type] === 2,
); );
// 检查必须回答的问题 if (!allRequiredAnswered || requiredTypes.length === 0) {
let requiredQuestionsAnswered = true;
if (supplierVO.isPaper && supplierVO.isLast) {
// 最后一个瓜农需要回答"空车带来的纸箱用完了吗?"
requiredQuestionsAnswered = packageTypeEnabled.REMAIN !== 0;
} else if (supplierVO.isPaper && !supplierVO.isLast) {
// 非最后一个瓜农不需要回答额外问题
} else if (!supplierVO.isPaper) {
// 空磅不包含纸箱的情况
}
if (!requiredQuestionsAnswered) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: "请回答所有必答问题",
});
return false;
}
if (!allRequiredAnswered) {
Toast.show("toast", { Toast.show("toast", {
icon: "fail", icon: "fail",
title: "提示", title: "提示",
@ -140,18 +120,6 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
) )
.map(([type, _]) => type); .map(([type, _]) => type);
console.log("enabledTypes", enabledTypes);
// 如果没有任何纸箱类型被启用,提示用户至少选择一种
if (requiredTypes.length > 0 && enabledTypes.length === 0) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: "请至少选择一种纸箱使用情况",
});
return false;
}
// 检查每个启用的类型是否有对应的纸箱数据 // 检查每个启用的类型是否有对应的纸箱数据
for (const type of enabledTypes) { for (const type of enabledTypes) {
const hasPackagesOfType = orderPackageList.some( const hasPackagesOfType = orderPackageList.some(
@ -408,10 +376,22 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
// 删除包装信息 // 删除包装信息
const removePackageInfo = (boxBrandId: string) => { const removePackageInfo = (boxBrandId: string) => {
Dialog.open('dialog', {
title: "提示",
content: "确定要移除此纸箱品牌吗?",
confirmText: "确定",
cancelText: "取消",
onConfirm: () => {
const newList = [ const newList = [
...orderPackageList.filter((item) => item.boxBrandId !== boxBrandId), ...orderPackageList.filter((item) => item.boxBrandId !== boxBrandId),
]; ];
setOrderPackageList(newList); setOrderPackageList(newList);
Dialog.close('dialog');
},
onCancel: () => {
Dialog.close('dialog');
}
})
}; };
// 处理纸箱类型启用状态切换 // 处理纸箱类型启用状态切换
@ -675,13 +655,13 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
className="cursor-pointer rounded-lg bg-blue-100 px-3 py-1 text-xs font-medium text-blue-600 transition-colors hover:bg-blue-200" className="cursor-pointer rounded-lg bg-blue-100 px-3 py-1 text-xs font-medium text-blue-600 transition-colors hover:bg-blue-200"
onClick={() => handleEditItem(item, index)} onClick={() => handleEditItem(item, index)}
> >
</View> </View>
<View <View
className="cursor-pointer rounded-lg bg-red-100 px-3 py-1 text-xs font-medium text-red-600 transition-colors hover:bg-red-200" className="cursor-pointer rounded-lg bg-red-100 px-3 py-1 text-xs font-medium text-red-600 transition-colors hover:bg-red-200"
onClick={() => removePackageInfo(item.boxBrandId)} onClick={() => removePackageInfo(item.boxBrandId)}
> >
使
</View> </View>
</View> </View>
</View> </View>
@ -1390,7 +1370,7 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
{packageTypeEnabled.OWN === 2 && ( {packageTypeEnabled.OWN === 2 && (
/* 当没有选择使用自己的纸箱时 */ /* 当没有选择使用自己的纸箱时 */
<> <>
{renderPackageByType("EXTRA")} {renderPackageByType("EXTRA_USED")}
{/* 分隔线 */} {/* 分隔线 */}
<View className="my-4 h-px bg-gray-200" /> <View className="my-4 h-px bg-gray-200" />
</> </>

View File

@ -267,7 +267,7 @@ export default forwardRef<WeighRef, IWeightProps>(function Weigh(props, ref) {
<View className="flex flex-1 flex-col gap-2.5 p-2.5"> <View className="flex flex-1 flex-col gap-2.5 p-2.5">
<View className="border-primary rounded-lg border-4 bg-white p-2.5 shadow-sm"> <View className="border-primary rounded-lg border-4 bg-white p-2.5 shadow-sm">
<View className={"flex items-center justify-between gap-2.5"}> <View className={"flex items-center justify-between gap-2.5"}>
<View className="text-sm"></View> <View className="text-sm"></View>
<View className="text-neutral-darkest text-sm font-medium"> <View className="text-neutral-darkest text-sm font-medium">
{supplierCount == 1 ? ( {supplierCount == 1 ? (
<Radio.Group <Radio.Group

View File

@ -1,7 +1,6 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { View, Text } from "@tarojs/components"; import { View, Text } from "@tarojs/components";
import { Button, Input, Popup, Radio, SafeArea } from "@nutui/nutui-react-taro"; import { Button, Input, Popup, Radio, SafeArea } from "@nutui/nutui-react-taro";
import { Icon } from "@/components";
import { formatCurrency, validatePrice } from "@/utils/format"; import { formatCurrency, validatePrice } from "@/utils/format";
import { import {
getSalesAmount, getSalesAmount,

View File

@ -189,6 +189,42 @@ export default hocAuth(function Page(props: CommonComponent) {
return; 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,
orderPackageList: [],
},
]);
};
const onFinish = async (preview?: boolean) => { const onFinish = async (preview?: boolean) => {
// 预览操作和暂存操作都显示确认对话框 // 预览操作和暂存操作都显示确认对话框
if (preview) { if (preview) {
@ -224,14 +260,14 @@ export default hocAuth(function Page(props: CommonComponent) {
}; };
// 保存草稿的函数(用于下一步) // 保存草稿的函数(用于下一步)
const saveDraftNextStep = async () => { const saveDraftNextStep = () => {
if (!purchaseOrder) { if (!purchaseOrder) {
return; return;
} }
let tempOrderId = orderId; let tempOrderId = orderId;
try { try {
if (tempOrderId) { if (tempOrderId) {
await business.purchaseOrder.updatePurchaseOrder({ business.purchaseOrder.updatePurchaseOrder({
active: active, active: active,
orderId: tempOrderId, orderId: tempOrderId,
orderVehicle: purchaseOrder.orderVehicle, orderVehicle: purchaseOrder.orderVehicle,
@ -242,7 +278,8 @@ export default hocAuth(function Page(props: CommonComponent) {
orderDealer: purchaseOrder.orderDealer, orderDealer: purchaseOrder.orderDealer,
}); });
} else { } else {
const { data } = await business.purchaseOrder.createPurchaseOrder({ business.purchaseOrder
.createPurchaseOrder({
active: active, active: active,
orderVehicle: purchaseOrder.orderVehicle, orderVehicle: purchaseOrder.orderVehicle,
orderSupplierList: purchaseOrder.orderSupplierList, orderSupplierList: purchaseOrder.orderSupplierList,
@ -250,9 +287,10 @@ export default hocAuth(function Page(props: CommonComponent) {
(item: CostItem) => item.selected, (item: CostItem) => item.selected,
), ),
orderDealer: purchaseOrder.orderDealer, orderDealer: purchaseOrder.orderDealer,
}); })
.then(({ data }) => {
setOrderId(data.data?.orderId); setOrderId(data.data?.orderId);
});
} }
} catch (error) { } catch (error) {
Toast.show("toast", { Toast.show("toast", {
@ -461,47 +499,6 @@ export default hocAuth(function Page(props: CommonComponent) {
} }
}} }}
value={item} value={item}
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,
orderPackageList: [],
},
]);
}}
onRemove={(supplierVO: SupplierVO) => { onRemove={(supplierVO: SupplierVO) => {
if (orderSupplierList.length <= 1) { if (orderSupplierList.length <= 1) {
setOrderSupplierList([ setOrderSupplierList([
@ -609,6 +606,12 @@ export default hocAuth(function Page(props: CommonComponent) {
{/* 人工和辅料信息 */} {/* 人工和辅料信息 */}
{step.value === 6 && ( {step.value === 6 && (
<OrderCost <OrderCost
supplierVO={orderSupplierList[orderSupplierList.length - 1]}
onAdd={() => {
saveDraftNextStep();
setActive(2);
onAdd();
}}
ref={orderCostRef} ref={orderCostRef}
value={orderCostList} value={orderCostList}
onChange={(costItemList: CostItem[]) => onChange={(costItemList: CostItem[]) =>
@ -662,7 +665,7 @@ export default hocAuth(function Page(props: CommonComponent) {
// 在第一步(车辆信息)时进行校验 // 在第一步(车辆信息)时进行校验
if (active === 1) { if (active === 1) {
if (vehicleRef.current?.validate()) { if (vehicleRef.current?.validate()) {
await saveDraftNextStep(); saveDraftNextStep();
setActive(active + 1); setActive(active + 1);
} }
} }
@ -676,7 +679,7 @@ export default hocAuth(function Page(props: CommonComponent) {
selectedIndex !== -1 && selectedIndex !== -1 &&
melonFarmerRefs.current[selectedIndex]?.validate() melonFarmerRefs.current[selectedIndex]?.validate()
) { ) {
await saveDraftNextStep(); saveDraftNextStep();
setActive(active + 1); setActive(active + 1);
} }
} }
@ -690,7 +693,7 @@ export default hocAuth(function Page(props: CommonComponent) {
selectedIndex !== -1 && selectedIndex !== -1 &&
weighRefs.current[selectedIndex]?.validate() weighRefs.current[selectedIndex]?.validate()
) { ) {
await saveDraftNextStep(); saveDraftNextStep();
setActive(active + 1); setActive(active + 1);
} }
} // 在第四步(包装信息)时进行校验 } // 在第四步(包装信息)时进行校验
@ -699,15 +702,16 @@ export default hocAuth(function Page(props: CommonComponent) {
const selectedIndex = orderSupplierList.findIndex( const selectedIndex = orderSupplierList.findIndex(
(supplier) => supplier.selected, (supplier) => supplier.selected,
); );
console.log("selectedIndex", selectedIndex)
if ( if (
selectedIndex !== -1 && selectedIndex !== -1 &&
orderPackageRefs.current[selectedIndex]?.validate() orderPackageRefs.current[selectedIndex]?.validate()
) { ) {
await saveDraftNextStep(); saveDraftNextStep();
setActive(active + 1); setActive(active + 1);
} }
} else { } else {
await saveDraftNextStep(); saveDraftNextStep();
setActive(active + 1); setActive(active + 1);
} }
}} }}

View File

@ -2385,7 +2385,7 @@ declare namespace BusinessAPI {
/** 销售单价(元/个) */ /** 销售单价(元/个) */
boxSalePrice?: number; boxSalePrice?: number;
/** 箱子类型:1_本次使用2_额外运输3_已使用额外运输4_车上剩余 */ /** 箱子类型:1_本次使用2_额外运输3_已使用额外运输4_车上剩余 */
boxType: "USED" | "EXTRA" | "EXTRA_USED" | "REMAIN"; boxType: "USED" | "EXTRA" | "EXTRA_USED" | "REMAIN" | "OWN";
}; };
type OrderRebate = { type OrderRebate = {