From 5a814cb35832911023639421ebc93c30c04389b4 Mon Sep 17 00:00:00 2001 From: shenyifei Date: Wed, 5 Nov 2025 10:21:11 +0800 Subject: [PATCH] =?UTF-8?q?feat(purchase):=20=E9=87=8D=E6=9E=84=E9=87=87?= =?UTF-8?q?=E8=B4=AD=E6=A8=A1=E5=9D=97=E7=BB=84=E4=BB=B6=E7=BB=93=E6=9E=84?= =?UTF-8?q?=E4=B8=8E=E4=BA=A4=E4=BA=92=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将采购相关组件移至 module 目录统一管理 - 优化 OrderCost 组件的人工费用处理逻辑,改为统一管理工头姓名 - 改进 OrderPackage 组件的纸箱类型渲染逻辑,根据供应商属性动态显示 - 更新 Weigh 组件,支持多供应商场景下的纸箱选择展示 - 调整采购模块导出路径,适配新的目录结构 - 优化表单交互文案,提升用户体验一致性 --- .../src/components/purchase/index.ts | 19 +- .../purchase/{ => module}/MelonFarmer.tsx | 0 .../purchase/{ => module}/OrderCost.tsx | 412 +++++++++--------- .../purchase/{ => module}/OrderPackage.tsx | 65 ++- .../purchase/{ => module}/OrderVehicle.tsx | 0 .../purchase/{ => module}/PurchasePreview.tsx | 0 .../purchase/{ => module}/SupplierList.tsx | 0 .../purchase/{ => module}/TicketUpload.tsx | 0 .../purchase/{ => module}/Weigh.tsx | 71 +-- .../src/pages/purchase/order/create.tsx | 1 + 10 files changed, 292 insertions(+), 276 deletions(-) rename packages/app-client/src/components/purchase/{ => module}/MelonFarmer.tsx (100%) rename packages/app-client/src/components/purchase/{ => module}/OrderCost.tsx (50%) rename packages/app-client/src/components/purchase/{ => module}/OrderPackage.tsx (96%) rename packages/app-client/src/components/purchase/{ => module}/OrderVehicle.tsx (100%) rename packages/app-client/src/components/purchase/{ => module}/PurchasePreview.tsx (100%) rename packages/app-client/src/components/purchase/{ => module}/SupplierList.tsx (100%) rename packages/app-client/src/components/purchase/{ => module}/TicketUpload.tsx (100%) rename packages/app-client/src/components/purchase/{ => module}/Weigh.tsx (91%) diff --git a/packages/app-client/src/components/purchase/index.ts b/packages/app-client/src/components/purchase/index.ts index e71c03a..98a809c 100644 --- a/packages/app-client/src/components/purchase/index.ts +++ b/packages/app-client/src/components/purchase/index.ts @@ -1,11 +1,12 @@ -export { default as OrderVehicle } from "./OrderVehicle"; -export { default as Weigh } from "./Weigh"; -export { default as MelonFarmer } from "./MelonFarmer"; -export { default as OrderPackage } from "./OrderPackage"; -export { default as TicketUpload } from "./TicketUpload"; -export { default as OrderCost } from "./OrderCost"; -export { default as SupplierList } from "./SupplierList"; -export { default as PurchasePreview } from "./PurchasePreview"; +export { default as OrderVehicle } from "./module/OrderVehicle"; +export { default as Weigh } from "./module/Weigh"; +export { default as MelonFarmer } from "./module/MelonFarmer"; +export { default as OrderPackage } from "./module/OrderPackage"; +export { default as TicketUpload } from "./module/TicketUpload"; +export { default as OrderCost } from "./module/OrderCost"; +export { default as SupplierList } from "./module/SupplierList"; +export { default as PurchasePreview } from "./module/PurchasePreview"; + export { default as PurchaseOrderRejectApprove } from "./button/PurchaseOrderRejectApprove"; export { default as PurchaseOrderRejectFinal } from "./button/PurchaseOrderRejectFinal"; export { default as PurchaseOrderSubmitReview } from "./button/PurchaseOrderSubmitReview"; @@ -24,4 +25,4 @@ export { default as PackagingCostSection } from "./section/PackagingCostSection" export { default as CostSummarySection } from "./section/CostSummarySection"; export { default as MarketPriceSection } from "./section/MarketPriceSection"; export { default as RebateCalcSection } from "./section/RebateCalcSection"; -export { default as ProfitCalcSection } from "./section/ProfitCalcSection"; \ No newline at end of file +export { default as ProfitCalcSection } from "./section/ProfitCalcSection"; diff --git a/packages/app-client/src/components/purchase/MelonFarmer.tsx b/packages/app-client/src/components/purchase/module/MelonFarmer.tsx similarity index 100% rename from packages/app-client/src/components/purchase/MelonFarmer.tsx rename to packages/app-client/src/components/purchase/module/MelonFarmer.tsx diff --git a/packages/app-client/src/components/purchase/OrderCost.tsx b/packages/app-client/src/components/purchase/module/OrderCost.tsx similarity index 50% rename from packages/app-client/src/components/purchase/OrderCost.tsx rename to packages/app-client/src/components/purchase/module/OrderCost.tsx index 2382c70..20c1cf4 100644 --- a/packages/app-client/src/components/purchase/OrderCost.tsx +++ b/packages/app-client/src/components/purchase/module/OrderCost.tsx @@ -2,7 +2,7 @@ import { View } from "@tarojs/components"; import { Icon } from "@/components"; import { forwardRef, useEffect, useImperativeHandle, useState } from "react"; import { business } from "@/services"; -import { Checkbox, Input, Picker, Toast } from "@nutui/nutui-react-taro"; +import { Button, Checkbox, Input, Toast } from "@nutui/nutui-react-taro"; import { CostItem } from "@/types/typings"; import { generateShortId } from "@/utils/generateShortId"; @@ -16,19 +16,17 @@ export interface IOrderCostProps { onChange?: (costItemList: CostItem[]) => void; } -const options = [ - [ - { value: "US", label: "我方" }, - { value: "OTHER", label: "瓜农" }, - ], -]; - export default forwardRef( function OrderCost(IOrderCostProps, ref) { const { value, onChange } = IOrderCostProps; const [costItemVOList, setCostItemVOList] = useState(); + // 总的工头姓名状态 + const [principal, setPrincipal] = useState(""); + // 工头姓名错误状态 + const [principalError, setPrincipalError] = useState(false); + const init = async () => { const { data } = await business.costItem.listCostItem({ costItemListQry: { @@ -58,6 +56,17 @@ export default forwardRef( }) || []; setCostItemVOList(initialList as CostItem[]); + + // 初始化总的工头姓名(如果有启用且费用承担方为"我方"的项目) + 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 || ""); + } }; // 当传入的value发生变化时,重新初始化列表 @@ -68,9 +77,20 @@ export default forwardRef( // 当内部状态发生变化时,通知父组件更新 useEffect(() => { if (costItemVOList && onChange) { - onChange(costItemVOList); + // 更新所有启用且费用承担方为"我方"的人工费用项的工头姓名 + const updatedList = costItemVOList.map((item) => { + if ( + item.costType === "HUMAN_COST" && + item.selected && + item.payerType === "US" + ) { + return { ...item, principal }; + } + return item; + }); + onChange(updatedList); } - }, [costItemVOList]); + }, [costItemVOList, principal]); // 错误状态 const [countError, setCountError] = useState<{ [key: string]: boolean }>( @@ -79,14 +99,6 @@ export default forwardRef( const [payerTypeError, setPayerTypeError] = useState<{ [key: string]: boolean; }>({}); - const [principalError, setPrincipalError] = useState<{ - [key: string]: boolean; - }>({}); - - // 为每个项目维护独立的Picker可见状态 - const [pickerVisibleMap, setPickerVisibleMap] = useState<{ - [key: string]: boolean; - }>({}); // 设置人工项目选择状态 const setArtificialSelect = (id: string, selected: boolean) => { @@ -132,12 +144,9 @@ export default forwardRef( }; // 校验工头姓名 - const validatePrincipal = (id: string, value: string) => { + const validatePrincipal = (value: string) => { const isValid = value.trim().length > 0; - setPrincipalError((prev) => ({ - ...prev, - [id]: !isValid, - })); + setPrincipalError(!isValid); return isValid; }; @@ -159,17 +168,18 @@ export default forwardRef( }; // 处理工头姓名变化 - const handlePrincipalChange = (id: string, value: string) => { - if (!costItemVOList) return; - const newList = costItemVOList.map((item) => - item.orderCostId === id ? { ...item, principal: value } : item, - ); - setCostItemVOList(newList); + const handlePrincipalChange = (value: string) => { + setPrincipal(value); - // 如果是瓜农承担且已启用,则校验工头姓名 - const item = costItemVOList.find((item) => item.orderCostId === id); - if (item?.selected && item.payerType === "OTHER") { - validatePrincipal(id, value); + // 如果有启用且费用承担方为"我方"的项目,则校验工头姓名 + const enabledUsItems = costItemVOList?.filter( + (item) => + item.costType === "HUMAN_COST" && + item.selected && + item.payerType === "US", + ); + if (enabledUsItems && enabledUsItems.length > 0) { + validatePrincipal(value); } }; @@ -178,14 +188,9 @@ export default forwardRef( validateCount(id, value); }; - // 失去焦点时校验费用承担方 - const handlePayerTypeBlur = (id: string, value: CostItem["payerType"]) => { - validatePayerType(id, value); - }; - // 失去焦点时校验工头姓名 - const handlePrincipalBlur = (id: string, value: string) => { - validatePrincipal(id, value); + const handlePrincipalBlur = (value: string) => { + validatePrincipal(value); }; // 对外暴露的校验方法 @@ -206,18 +211,23 @@ export default forwardRef( if (!validatePayerType(item.orderCostId, item.payerType)) { isValid = false; } - - // 如果是瓜农承担,校验工头姓名 - if ( - item.payerType === "US" && - !validatePrincipal(item.orderCostId, item.principal || "") - ) { - isValid = false; - } } } }); + // 校验总的工头姓名(如果有启用且费用承担方为"我方"的项目) + const enabledUsItems = costItemVOList.filter( + (item) => + item.costType === "HUMAN_COST" && + item.selected && + item.payerType === "US", + ); + if (enabledUsItems.length > 0) { + if (!validatePrincipal(principal)) { + isValid = false; + } + } + if (!isValid) { Toast.show("toast", { icon: "fail", @@ -255,183 +265,126 @@ export default forwardRef( }} > - 启用 + 用了 + + {item.selected && type === "HUMAN_COST" && ( + + {/* 费用承担方改为按钮形式,参考OrderPackage中的样式 */} + + 谁出钱: + + + + + + + {payerTypeError[item.orderCostId] && item.selected && ( + + 请选择谁出这笔钱 + + )} + + )} + {item.selected && ( - - { - const count = item.count || 1; - handleCountChange( - item.orderCostId, - Math.max(1, count - 1), - ); - }} - > - - - - { - handleCountChange(item.orderCostId, Number(value)); + + 用多少: + + { + const count = item.count || 1; + handleCountChange( + item.orderCostId, + Math.max(1, count - 1), + ); }} - formatter={(value) => - Math.max(1, Number(value)).toString() - } - onBlur={() => - handleCountBlur(item.orderCostId, item.count) - } - /> - - { - const count = item.count || 1; - handleCountChange(item.orderCostId, count + 1); - }} - > - - + > + + + + { + handleCountChange(item.orderCostId, Number(value)); + }} + formatter={(value) => + Math.max(1, Number(value)).toString() + } + onBlur={() => + handleCountBlur(item.orderCostId, item.count) + } + /> + + { + const count = item.count || 1; + handleCountChange(item.orderCostId, count + 1); + }} + > + + - - {item.unit} + + {item.unit} + {countError[item.orderCostId] && item.selected && ( - 请输入正确的数量 + 请填写正确的数量 )} )} - - {item.selected && type === "HUMAN_COST" && ( - - - option.value === item.payerType, - )?.label || "" - } - placeholder={"请选择费用承担方"} - onChange={(value) => { - handlePayerTypeChange( - item.orderCostId, - value as CostItem["payerType"], - ); - }} - onBlur={() => - handlePayerTypeBlur(item.orderCostId, item.payerType) - } - disabled - onClick={() => { - setPickerVisibleMap((prev) => ({ - ...prev, - [item.orderCostId]: true, - })); - }} - /> - { - setPickerVisibleMap((prev) => ({ - ...prev, - [item.orderCostId]: true, - })); - }} - /> - - - { - // 设置费用承担方 - if (values.length > 0) { - handlePayerTypeChange( - item.orderCostId, - values[0] as CostItem["payerType"], - ); - } - - setPickerVisibleMap((prev) => ({ - ...prev, - [item.orderCostId]: false, - })); - }} - onClose={() => { - setPickerVisibleMap((prev) => ({ - ...prev, - [item.orderCostId]: false, - })); - }} - /> - - {payerTypeError[item.orderCostId] && item.selected && ( - - 请选择费用承担方 - - )} - - )} - - {item.selected && - type === "HUMAN_COST" && - item.payerType === "US" && ( - - - { - handlePrincipalChange(item.orderCostId, value); - }} - onBlur={() => - handlePrincipalBlur( - item.orderCostId, - item.principal || "", - ) - } - /> - - {principalError[item.orderCostId] && - item.selected && - item.payerType === "US" && ( - - 请填写工头姓名 - - )} - - )} )); }; + // 检查是否有人工费用项被启用且费用承担方为"我方" + const shouldShowPrincipalInput = () => { + return ( + costItemVOList?.some( + (item) => + item.costType === "HUMAN_COST" && + item.selected && + item.payerType === "US", + ) || false + ); + }; + return ( - 人工信息 + 人工费用 ( color={"var(--color-blue-700)"} size={18} /> - - 瓜农承担费用的人工可以不启用 - + 对方出钱可以不勾选 {renderItemList(getItemsByCostType("HUMAN_COST"), "HUMAN_COST")} + + {/* 总的工头姓名输入框 */} + {shouldShowPrincipalInput() && ( + + 工头叫什么名字 + + { + handlePrincipalChange(value); + }} + onBlur={() => handlePrincipalBlur(principal)} + /> + + {principalError && ( + + 工头叫啥子填一下 + + )} + + )} - 辅料信息 + 辅料费用 + + + 用了几包就填几包 + {renderItemList( getItemsByCostType("PACKAGING_MATERIALS"), "PACKAGING_MATERIALS", )} - - 空纸箱信息 - {renderItemList(getItemsByCostType("OTHER_COST"), "OTHER_COST")} - - - 空礼盒信息 - {renderItemList(getItemsByCostType("OTHER_COST"), "OTHER_COST")} - + {/**/} + {/* 空纸箱费用*/} + {/* {renderItemList(getItemsByCostType("OTHER_COST"), "OTHER_COST")}*/} + {/**/} + {/**/} + {/* 空礼盒费用*/} + {/* {renderItemList(getItemsByCostType("OTHER_COST"), "OTHER_COST")}*/} + {/**/} ); }, diff --git a/packages/app-client/src/components/purchase/OrderPackage.tsx b/packages/app-client/src/components/purchase/module/OrderPackage.tsx similarity index 96% rename from packages/app-client/src/components/purchase/OrderPackage.tsx rename to packages/app-client/src/components/purchase/module/OrderPackage.tsx index 5d6e701..00abc1e 100644 --- a/packages/app-client/src/components/purchase/OrderPackage.tsx +++ b/packages/app-client/src/components/purchase/module/OrderPackage.tsx @@ -40,27 +40,38 @@ export default forwardRef( const orderPackageList = value.orderPackageList || []; setOrderPackageList(value.orderPackageList || []); - // 根据orderPackageList 初始化 packageTypeEnabled - setPackageTypeEnabled({ + // 根据orderPackageList和supplierVO属性初始化 packageTypeEnabled + const initialPackageTypeEnabled = { + USED: undefined, + EXTRA_USED: undefined, + EXTRA: undefined, + REMAIN: undefined, + OWN: undefined, + }; + + // 根据orderPackageList设置已有的纸箱类型 + if (orderPackageList.some((item) => item.boxType === "USED")) { // @ts-ignore - USED: - orderPackageList.some((item) => item.boxType === "USED") || undefined, + initialPackageTypeEnabled.USED = true; + } + if (orderPackageList.some((item) => item.boxType === "EXTRA_USED")) { // @ts-ignore - EXTRA_USED: - orderPackageList.some((item) => item.boxType === "EXTRA_USED") || - undefined, + initialPackageTypeEnabled.EXTRA_USED = true; + } + if (orderPackageList.some((item) => item.boxType === "EXTRA")) { // @ts-ignore - EXTRA: - orderPackageList.some((item) => item.boxType === "EXTRA") || - undefined, + initialPackageTypeEnabled.EXTRA = true; + } + if (orderPackageList.some((item) => item.boxType === "REMAIN")) { // @ts-ignore - REMAIN: - orderPackageList.some((item) => item.boxType === "REMAIN") || - undefined, + initialPackageTypeEnabled.REMAIN = true; + } + if (orderPackageList.some((item) => item.boxType === "OWN")) { // @ts-ignore - OWN: - orderPackageList.some((item) => item.boxType === "OWN") || undefined, - }); + initialPackageTypeEnabled.OWN = true; + } + + setPackageTypeEnabled(initialPackageTypeEnabled); }, []); const [orderPackageList, setOrderPackageList] = useState< @@ -1286,7 +1297,7 @@ export default forwardRef( {!supplierVO.isPaper && ( <> - 有没有用额外拿来的纸箱? + 有没有用本次拉来的纸箱?