feat(purchase): 优化采购模块费用与箱型信息处理逻辑

- 在 PriceEditor 组件中增加 negative 属性以支持负数显示
- 优化 Step1Form 表单模块渲染结构,提升可读性
- 修正 CostCard 中价格与数量的展示顺序
- 在 CostCreate 组件中过滤掉“空箱费”和“纸箱费”的选项
- 增加 getTitle 方法动态设置费用类型的标题
- 重构 OrderOption 中费用保存的筛选逻辑
- 强化 OrderCostItem 中字段校验及数据初始化处理
- 完善 BasicInfoSection 的发货日期选择器功能
- 调整 CostDifferenceSection 中分红金额相关文案与计算方式
- 简化 DeliveryFormSection 数据初始化流程
- 移除 EmptyBoxInfoSection 和 PackageInfoSection 中冗余的成本单价和箱重编辑功能
- 在 audit 页面中优化费用项目的初始化加载逻辑并确保计提费正确附加到订单中
This commit is contained in:
shenyifei 2025-12-11 17:14:27 +08:00
parent dfe9a89213
commit 881685a653
15 changed files with 320 additions and 485 deletions

View File

@ -11,6 +11,8 @@ interface PriceEditorProps {
unit?: string; unit?: string;
icon?: IconNames; icon?: IconNames;
hint?: string; hint?: string;
// 是否负数
negative?: boolean;
} }
export default function PriceEditor(props: PriceEditorProps) { export default function PriceEditor(props: PriceEditorProps) {
@ -22,6 +24,7 @@ export default function PriceEditor(props: PriceEditorProps) {
unit = "元/斤", unit = "元/斤",
icon = "money-bill", icon = "money-bill",
hint = "点击销售单价可直接编辑", hint = "点击销售单价可直接编辑",
negative = false,
} = props; } = props;
// 控制区域是否处于编辑状态 // 控制区域是否处于编辑状态
@ -34,7 +37,11 @@ export default function PriceEditor(props: PriceEditorProps) {
// 当开始编辑时,设置初始值并聚焦输入框 // 当开始编辑时,设置初始值并聚焦输入框
useEffect(() => { useEffect(() => {
if (isEditing) { if (isEditing) {
setInputValue(value.toFixed(2)); if (value == 0) {
setInputValue("");
} else {
setInputValue(value.toString());
}
// 聚焦到输入框 // 聚焦到输入框
setTimeout(() => { setTimeout(() => {
if (inputRef.current) { if (inputRef.current) {
@ -83,6 +90,7 @@ export default function PriceEditor(props: PriceEditorProps) {
{isEditing ? ( {isEditing ? (
<View className="relative flex flex-1 items-end"> <View className="relative flex flex-1 items-end">
<View className="input-bold flex h-10 w-full items-center border-b-2 border-red-500 !pb-2 text-3xl font-bold text-red-500 focus:outline-none"> <View className="input-bold flex h-10 w-full items-center border-b-2 border-red-500 !pb-2 text-3xl font-bold text-red-500 focus:outline-none">
{negative && <View>-</View>}
<Input <Input
ref={inputRef} ref={inputRef}
type="digit" type="digit"
@ -106,6 +114,7 @@ export default function PriceEditor(props: PriceEditorProps) {
onClick={() => setIsEditing(true)} onClick={() => setIsEditing(true)}
> >
<Text className="h-10 w-full border-b-2 border-red-500 pb-2 text-3xl font-bold text-red-500 focus:outline-none"> <Text className="h-10 w-full border-b-2 border-red-500 pb-2 text-3xl font-bold text-red-500 focus:outline-none">
{negative && "-"}
{formatCurrency(value || 0)} {formatCurrency(value || 0)}
</Text> </Text>
<Icon <Icon
@ -135,7 +144,7 @@ export default function PriceEditor(props: PriceEditorProps) {
</View> </View>
<View className="relative"> <View className="relative">
<Text className="w-full py-2 text-3xl font-bold text-red-500"> <Text className="w-full py-2 text-3xl font-bold text-red-500">
{formatCurrency(value || 0)} {Number.isNaN(value) ? "" : formatCurrency(value)}
</Text> </Text>
</View> </View>
</View> </View>

View File

@ -548,9 +548,7 @@ const Step1Form = forwardRef<Step1FormRef, Step1FormProps>((props, ref) => {
return !hasErrors; return !hasErrors;
}; };
return ( return moduleList.map((module) => {
<View>
{moduleList.map((module) => {
const contentFields = renderContentFields(module); const contentFields = renderContentFields(module);
// 如果没有内容配置字段,则不渲染该模块 // 如果没有内容配置字段,则不渲染该模块
if (!contentFields) return null; if (!contentFields) return null;
@ -563,9 +561,7 @@ const Step1Form = forwardRef<Step1FormRef, Step1FormProps>((props, ref) => {
{contentFields} {contentFields}
</View> </View>
); );
})} });
</View>
);
}); });
export default Step1Form; export default Step1Form;

View File

@ -116,7 +116,8 @@ export default function CostCard(props: CostCardComponentProps) {
{orderCostItem.name} {orderCostItem.name}
</Text> </Text>
<Text className="text-sm font-medium"> <Text className="text-sm font-medium">
{orderCostItem.count} {orderCostItem.unit} {orderCostItem.price} {orderCostItem.count}{" "}
{orderCostItem.unit}
</Text> </Text>
</View> </View>
); );

View File

@ -224,7 +224,11 @@ export default function CostCreate(props: AddCostComponentProps) {
<View className="mb-2 text-sm text-gray-600"></View> <View className="mb-2 text-sm text-gray-600"></View>
<ScrollView className="mb-2.5" scrollX> <ScrollView className="mb-2.5" scrollX>
<View className="flex w-fit flex-row gap-2.5"> <View className="flex w-fit flex-row gap-2.5">
{costList?.map((cost) => ( {costList
.filter((cost) => {
return !(cost.name === "空箱费" || cost.name === "纸箱费");
})
?.map((cost) => (
<View <View
key={cost.costId} key={cost.costId}
className={"flex flex-col items-center justify-center"} className={"flex flex-col items-center justify-center"}
@ -362,6 +366,23 @@ export default function CostCreate(props: AddCostComponentProps) {
); );
}; };
const getTitle = (type: string) => {
if (type === "ARTIFICIAL_TYPE") {
return "新增人工类型费用";
}
if (type === "MATERIAL_TYPE") {
return "新增辅料类型费用";
}
if (type === "PRODUCTION_TYPE") {
return "新增产地类型费用";
}
if (type === "OTHER_TYPE") {
return "新增其他类型费用";
}
return "新增费用";
};
return ( return (
<Popup <Popup
duration={150} duration={150}
@ -370,7 +391,7 @@ export default function CostCreate(props: AddCostComponentProps) {
}} }}
visible={visible} visible={visible}
position="bottom" position="bottom"
title={editMode ? `编辑${orderCost?.name || "费用"}` : "新增其他人工费用"} title={editMode ? `编辑${orderCost?.name || "费用"}` : getTitle(type)}
onClose={onClose} onClose={onClose}
onOverlayClick={onClose} onOverlayClick={onClose}
lockScroll lockScroll
@ -411,10 +432,11 @@ export default function CostCreate(props: AddCostComponentProps) {
block block
type="primary" type="primary"
disabled={ disabled={
!selectedCost || selectedCost?.type !== "OTHER_TYPE" &&
(!selectedCost ||
Array.from(costItemCounts.values()).every( Array.from(costItemCounts.values()).every(
(count) => count === 0, (count) => count === 0,
) ))
} }
onClick={addCostItems} onClick={addCostItems}
> >

View File

@ -27,11 +27,11 @@ export default function CostList(props: {
(cost) => !costIdList.includes(cost.costId) && cost.type === type, (cost) => !costIdList.includes(cost.costId) && cost.type === type,
); );
// 新增人工费弹窗状态 // 新增费弹窗状态
const [showAddCostPopup, setShowAddCostPopup] = useState(false); const [showAddCostPopup, setShowAddCostPopup] = useState(false);
// 人工费类型 // 费类型
const workerAdvanceCosts = const orderCosts =
purchaseOrderVO.orderCostList?.filter((item) => item.type === type) || []; purchaseOrderVO.orderCostList?.filter((item) => item.type === type) || [];
const handleSaveNewCost = ( const handleSaveNewCost = (
@ -155,7 +155,7 @@ export default function CostList(props: {
</View> </View>
)} )}
{workerAdvanceCosts.map((orderCost) => { {orderCosts.map((orderCost) => {
if (type === "MATERIAL_TYPE") { if (type === "MATERIAL_TYPE") {
if (orderCost.name === "空箱费" || orderCost.name === "纸箱费") { if (orderCost.name === "空箱费" || orderCost.name === "纸箱费") {
return <></>; return <></>;

View File

@ -37,7 +37,7 @@ export default forwardRef<OrderCostRef, IOrderCostProps>(
const orderCostMap = new Map<string, BusinessAPI.OrderCost>(); const orderCostMap = new Map<string, BusinessAPI.OrderCost>();
orderCostList?.forEach((item) => { orderCostList?.forEach((item) => {
if (item.costId && costIds.includes(item.costId)) { if (item.costId && costIds?.includes(item.costId)) {
orderCostMap.set(item.costId, item); orderCostMap.set(item.costId, item);
} }
}); });

View File

@ -43,7 +43,10 @@ export default forwardRef<OrderCostItemRef, IOrderCostItemProps>(
const orderCostItemMap = new Map<string, BusinessAPI.OrderCostItem>(); const orderCostItemMap = new Map<string, BusinessAPI.OrderCostItem>();
orderCostItemList?.forEach((item) => { orderCostItemList?.forEach((item) => {
if (item.costItemId) { if (item.costItemId) {
orderCostItemMap.set(item.costItemId, item); orderCostItemMap.set(item.costItemId, {
...item,
selected: true,
});
} }
}); });
@ -132,7 +135,7 @@ export default forwardRef<OrderCostItemRef, IOrderCostItemProps>(
// 校验工头姓名 // 校验工头姓名
const validatePrincipal = (principal: string) => { const validatePrincipal = (principal: string) => {
const isValid = principal.trim().length > 0; const isValid = principal?.trim().length > 0;
setForemanError(!isValid); setForemanError(!isValid);
return isValid; return isValid;
}; };
@ -170,8 +173,8 @@ export default forwardRef<OrderCostItemRef, IOrderCostItemProps>(
}; };
// 失去焦点时校验工头姓名 // 失去焦点时校验工头姓名
const handlePrincipalBlur = (principal: string) => { const handlePrincipalBlur = (foreman: string) => {
validatePrincipal(principal); validatePrincipal(foreman);
}; };
// 对外暴露的校验方法 // 对外暴露的校验方法

View File

@ -171,7 +171,26 @@ export default forwardRef<OrderOptionRef, IOrderOptionProps>(
// 空箱 // 空箱
orderPackageList: value.orderPackageList, orderPackageList: value.orderPackageList,
// 费用 // 费用
orderCostList: value.orderCostList.filter((item) => item.selected), orderCostList: value.orderCostList.filter((item) => {
if (item.type === "PRODUCTION_TYPE") {
return item.selected;
}
if (
item.type === "ARTIFICIAL_TYPE" ||
item.type === "MATERIAL_TYPE"
) {
return (
value.orderCostItemList.filter(
(orderCostItem) =>
item.costItemIds?.includes(orderCostItem.costItemId!) &&
orderCostItem.selected,
).length > 0
);
}
return false;
}),
orderCostItemList: value.orderCostItemList.filter( orderCostItemList: value.orderCostItemList.filter(
(item) => item.selected, (item) => item.selected,
), ),
@ -190,12 +209,6 @@ export default forwardRef<OrderOptionRef, IOrderOptionProps>(
}; };
const onFinish = async () => { const onFinish = async () => {
Dialog.open("dialog", {
title: "预览确认",
content: "即将保存并预览当前采购订单,确定要继续吗?",
confirmText: "确认预览",
cancelText: "取消",
onConfirm: async () => {
// 只保存第六步的人工和辅料信息 // 只保存第六步的人工和辅料信息
const costSuccess = await saveCostInfo(); const costSuccess = await saveCostInfo();
if (!costSuccess) { if (!costSuccess) {
@ -203,24 +216,12 @@ export default forwardRef<OrderOptionRef, IOrderOptionProps>(
return; return;
} }
Toast.show("toast", {
icon: "success",
title: "提示",
content: "保存成功,正在跳转预览...",
});
// 跳转到预览页面 // 跳转到预览页面
Taro.redirectTo({ Taro.redirectTo({
url: buildUrl("/pages/purchase/enter/preview", { url: buildUrl("/pages/purchase/enter/preview", {
orderId: value.orderId, orderId: value.orderId,
}), }),
}); });
Dialog.close("dialog");
},
onCancel: () => {
Dialog.close("dialog");
},
});
}; };
const onAdd = () => { const onAdd = () => {
@ -452,7 +453,7 @@ export default forwardRef<OrderOptionRef, IOrderOptionProps>(
setLoading(true); setLoading(true);
// 第六步(人工辅料费用)时进行校验 // 第六步(人工辅料费用)时进行校验
if ( if (
orderCostRef.current?.validate() || orderCostRef.current?.validate() &&
orderCostItemRef.current?.validate() orderCostItemRef.current?.validate()
) { ) {
await onFinish(); await onFinish();

View File

@ -1,5 +1,13 @@
import { ScrollView, Text, View } from "@tarojs/components"; import { ScrollView, Text, View } from "@tarojs/components";
import { Button, Input, Popup, Radio, SafeArea } from "@nutui/nutui-react-taro"; import {
Button,
DatePicker,
Input,
PickerOption,
Popup,
Radio,
SafeArea,
} from "@nutui/nutui-react-taro";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { formatCurrency } from "@/utils"; import { formatCurrency } from "@/utils";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
@ -15,6 +23,34 @@ export default function BasicInfoSection(props: {
const { orderVehicle } = purchaseOrderVO; const { orderVehicle } = purchaseOrderVO;
// 当天和未来10天
const startDate = new Date();
const endDate = new Date(startDate.getTime() + 86400000 * 10);
const [show, setShow] = useState(false);
const formatter = (type: string, option: PickerOption) => {
switch (type) {
case "year":
option.label += "年";
break;
case "month":
option.label += "月";
break;
case "day":
option.label += "日";
break;
case "hour":
option.label += "时";
break;
case "minute":
option.label += "分";
break;
default:
break;
}
return option;
};
// 弹窗可见状态 // 弹窗可见状态
const [visiblePopup, setVisiblePopup] = useState({ const [visiblePopup, setVisiblePopup] = useState({
basicInfo: false, // 基础信息弹窗 basicInfo: false, // 基础信息弹窗
@ -40,9 +76,11 @@ export default function BasicInfoSection(props: {
const [loadingLastVehicleNo, setLoadingLastVehicleNo] = useState(false); const [loadingLastVehicleNo, setLoadingLastVehicleNo] = useState(false);
// 获取上一车次号 // 获取上一车次号
const fetchLastVehicleNo = async () => { const fetchLastVehicleNo = async (
dealerId: BusinessAPI.DealerVO["dealerId"],
) => {
// 如果已经有车次号,则不需要获取上一车次号 // 如果已经有车次号,则不需要获取上一车次号
if (orderVehicle?.vehicleNo) { if (orderVehicle?.vehicleNo || !dealerId) {
return; return;
} }
@ -55,7 +93,9 @@ export default function BasicInfoSection(props: {
try { try {
const { data: res } = const { data: res } =
await businessServices.purchaseOrder.getLastVehicleNo({ await businessServices.purchaseOrder.getLastVehicleNo({
lastVehicleNoQry: {}, lastVehicleNoQry: {
dealerId: purchaseOrderVO.orderDealer.dealerId,
},
}); });
if (res.success && res.data) { if (res.success && res.data) {
@ -83,8 +123,8 @@ export default function BasicInfoSection(props: {
// 组件加载时获取上一车次号 // 组件加载时获取上一车次号
useEffect(() => { useEffect(() => {
fetchLastVehicleNo(); fetchLastVehicleNo(purchaseOrderVO.orderDealer.dealerId);
}, []); }, [purchaseOrderVO.orderDealer.dealerId]);
// 打开基础信息弹窗 // 打开基础信息弹窗
const openBasicInfoPopup = () => { const openBasicInfoPopup = () => {
@ -246,7 +286,8 @@ export default function BasicInfoSection(props: {
/> />
</View> </View>
<View className={"text-neutral-darkest text-sm font-medium"}> <View>
<View className="mb-1 block text-sm font-normal text-[#000000]">
</View> </View>
<View <View
@ -254,19 +295,40 @@ export default function BasicInfoSection(props: {
"border-neutral-base flex flex-row items-center rounded-md border border-solid" "border-neutral-base flex flex-row items-center rounded-md border border-solid"
} }
> >
<Input <View
className={"placeholder:text-neutral-dark"} className={
placeholder={"请输入发货日期"} "flex flex-1 flex-row items-center justify-between px-5"
type={"text"} }
value={editValues.deliveryTime} style={{
onChange={(value) => { color: "var(--nutui-color-title, #1a1a1a)",
}}
onClick={() => setShow(true)}
>
<View className={"text-sm"}>
{editValues.deliveryTime || "请输入发货日期"}
</View>
<Icon name={"chevron-down"} />
</View>
<DatePicker
title="发货时间选择"
type="date"
startDate={startDate}
endDate={endDate}
visible={show}
defaultValue={new Date()}
formatter={formatter}
onClose={() => setShow(false)}
onConfirm={(_, values) => {
setEditValues((prev) => ({ setEditValues((prev) => ({
...prev, ...prev,
deliveryTime: value, deliveryTime: dayjs(values.join("-")).format(
"YYYY-MM-DD",
),
})); }));
}} }}
/> />
</View> </View>
</View>
<View className={"text-neutral-darkest text-sm font-medium"}> <View className={"text-neutral-darkest text-sm font-medium"}>

View File

@ -10,13 +10,14 @@ export default function CostDifferenceSection(props: {
}) { }) {
const { purchaseOrderVO, onChange, readOnly, calculator } = props; const { purchaseOrderVO, onChange, readOnly, calculator } = props;
const orderDealer = purchaseOrderVO.orderDealer; const orderDealer = purchaseOrderVO.orderDealer;
console.log("calculator.getShareProfit()", calculator.getShareProfit());
return ( return (
<View className={"flex flex-col gap-2.5"}> <View className={"flex flex-col gap-2.5"}>
{/* 卡片形式展示分成信息 */} {/* 卡片形式展示分成信息 */}
<View className="bg-primary/3 rounded-lg border-b border-gray-100 p-2.5"> <View className="bg-primary/3 rounded-lg border-b border-gray-100 p-2.5">
<View className="mb-2"> <View className="mb-2">
<Text className="text-sm font-medium"></Text> <Text className="text-sm font-medium"></Text>
</View> </View>
<View className="flex"> <View className="flex">
@ -37,22 +38,20 @@ export default function CostDifferenceSection(props: {
}, },
}); });
}} }}
readOnly={readOnly || !orderDealer?.shareAdjusted} readOnly={readOnly}
label={orderDealer?.shareAdjusted ? "调分成金额" : "分成金额"} label={"调整的金额"}
unit="元" unit="元"
hint="点击金额可直接编辑" hint="点击金额可直接编辑"
negative
/> />
<View className="flex flex-1 flex-col gap-2 pl-4"> <View className="flex flex-1 flex-col gap-2 pl-4">
<View className="flex items-center justify-between"> <View className="flex items-center justify-between">
<Text className="text-sm text-gray-500"></Text> <Text className="text-sm text-gray-500"></Text>
</View> </View>
<View className="flex items-center justify-between"> <View className="flex items-center justify-between">
<Text className="text-primary text-2xl font-bold text-nowrap"> <Text className="text-primary text-2xl font-bold text-nowrap">
{" "} {formatCurrency(calculator.getShareProfit() || 0)}
{formatCurrency(
orderDealer.profitSharing || calculator.getShareProfit() || 0,
)}
</Text> </Text>
</View> </View>
</View> </View>

View File

@ -1,5 +1,4 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { View } from "@tarojs/components";
import { DeliveryStep1Form } from "@/components"; import { DeliveryStep1Form } from "@/components";
import { import {
convertPurchaseOrderToShipOrder, convertPurchaseOrderToShipOrder,
@ -21,10 +20,10 @@ export default function DeliveryFormSection(props: {
? purchaseOrderVO.shipOrderVOList[0] ? purchaseOrderVO.shipOrderVOList[0]
: convertPurchaseOrderToShipOrder(purchaseOrderVO); : convertPurchaseOrderToShipOrder(purchaseOrderVO);
const init = async (shipOrderVO: BusinessAPI.ShipOrderVO) => { const init = async (purchaseOrderVO: BusinessAPI.PurchaseOrderVO) => {
const { data } = await business.dealer.showDealer({ const { data } = await business.dealer.showDealer({
dealerShowQry: { dealerShowQry: {
dealerId: shipOrderVO.dealerId, dealerId: purchaseOrderVO.orderDealer.dealerId,
}, },
}); });
@ -36,14 +35,15 @@ export default function DeliveryFormSection(props: {
template, template,
convertedData, convertedData,
); );
console.log("updatedTemplate", updatedTemplate);
setModuleList(updatedTemplate); setModuleList(updatedTemplate);
} else {
setModuleList([]);
} }
}; };
useEffect(() => { useEffect(() => {
init(shipOrderVO); init(purchaseOrderVO);
}, []); }, [purchaseOrderVO]);
// 更新模板配置 // 更新模板配置
const updateTemplateConfig = async (template: any[], data: any) => { const updateTemplateConfig = async (template: any[], data: any) => {
@ -64,7 +64,6 @@ export default function DeliveryFormSection(props: {
} }
return ( return (
<View className={"flex flex-col gap-2.5"}>
<DeliveryStep1Form <DeliveryStep1Form
readOnly={readOnly} readOnly={readOnly}
moduleList={moduleList} moduleList={moduleList}
@ -76,6 +75,5 @@ export default function DeliveryFormSection(props: {
}); });
}} }}
/> />
</View>
); );
} }

View File

@ -12,18 +12,10 @@ export default function EmptyBoxInfoSection(props: {
const { purchaseOrderVO, onChange, readOnly } = props; const { purchaseOrderVO, onChange, readOnly } = props;
const defaultColumns = [ const defaultColumns = [
{
title: "品牌",
key: "boxBrandName",
fixed: "left",
},
{
title: "规格",
key: "boxSpecName",
},
{ {
title: "纸箱型号", title: "纸箱型号",
key: "boxProductName", key: "boxProductName",
fixed: "left",
}, },
{ {
title: "个数", title: "个数",
@ -95,87 +87,11 @@ export default function EmptyBoxInfoSection(props: {
orderPackageId?: string; orderPackageId?: string;
isTotalRow?: boolean; isTotalRow?: boolean;
}, },
) => { ) => formatCurrency(value.boxProductWeight),
// 合计行不显示编辑按钮
if (value.isTotalRow) {
return formatCurrency(value.boxProductWeight);
}
return (
<View
className="flex w-full items-center justify-between"
onClick={(e) => {
if (!readOnly) {
e.stopPropagation();
// 设置临时编辑值为当前值
setTempEditValues((prev) => ({
...prev,
[value.orderPackageId || ""]:
editValues[value.orderPackageId || ""],
}));
setVisiblePopup((prev) => ({
...prev,
[value.orderPackageId || ""]: true,
}));
}
}}
>
<View className={!readOnly ? "cursor-pointer underline" : ""}>
{formatCurrency(value.boxProductWeight)}
</View>
{!readOnly && (
<View className="-m-2 ml-2 flex items-center justify-center p-2">
<Icon name={"pen-to-square"} size={16} color={"#1a73e8"} />
</View>
)}
</View>
);
},
}, },
{ {
title: "成本单价(元)", title: "品牌",
key: "boxCostPrice", key: "boxBrandName",
render: (
value: BusinessAPI.OrderPackage & {
orderPackageId?: string;
isTotalRow?: boolean;
},
) => {
// 合计行不显示编辑按钮
if (value.isTotalRow) {
return formatCurrency(value.boxCostPrice as number);
}
return (
<View
className="flex w-full items-center justify-between"
onClick={(e) => {
if (!readOnly) {
e.stopPropagation();
// 设置临时编辑值为当前值
setTempEditValues((prev) => ({
...prev,
[value.orderPackageId || ""]:
editValues[value.orderPackageId || ""],
}));
setVisiblePopup((prev) => ({
...prev,
[value.orderPackageId || ""]: true,
}));
}
}}
>
<View className={!readOnly ? "cursor-pointer underline" : ""}>
{formatCurrency(value.boxCostPrice as number)}
</View>
{!readOnly && (
<View className="-m-2 ml-2 flex items-center justify-center p-2">
<Icon name={"pen-to-square"} size={16} color={"#1a73e8"} />
</View>
)}
</View>
);
},
}, },
]; ];
const [columns, setColumns] = useState<any[]>(defaultColumns); const [columns, setColumns] = useState<any[]>(defaultColumns);
@ -194,55 +110,38 @@ export default function EmptyBoxInfoSection(props: {
// 编辑值的状态 // 编辑值的状态
const [editValues, setEditValues] = useState<{ const [editValues, setEditValues] = useState<{
[key: string]: { [key: string]: {
boxCostPrice?: number;
boxSalePrice?: number; boxSalePrice?: number;
boxProductWeight?: number;
}; };
}>({}); }>({});
// 临时编辑值的状态(用于在保存前暂存编辑的值) // 临时编辑值的状态(用于在保存前暂存编辑的值)
const [tempEditValues, setTempEditValues] = useState<{ const [tempEditValues, setTempEditValues] = useState<{
[key: string]: { [key: string]: {
boxCostPrice?: number;
boxSalePrice?: number; boxSalePrice?: number;
boxProductWeight?: number;
}; };
}>({}); }>({});
// 初始化编辑值 // 初始化编辑值
const initEditValues = ( const initEditValues = (pkgId: string, boxSalePrice?: number) => {
pkgId: string,
boxCostPrice?: number,
boxSalePrice?: number,
boxProductWeight?: number,
) => {
const updates: { const updates: {
editValuesUpdate?: { editValuesUpdate?: {
boxCostPrice?: number;
boxSalePrice?: number; boxSalePrice?: number;
boxProductWeight?: number;
}; };
tempEditValuesUpdate?: { tempEditValuesUpdate?: {
boxCostPrice?: number;
boxSalePrice?: number; boxSalePrice?: number;
boxProductWeight?: number;
}; };
} = {}; } = {};
if (!editValues[pkgId]) { if (!editValues[pkgId]) {
updates.editValuesUpdate = { updates.editValuesUpdate = {
boxCostPrice,
boxSalePrice, boxSalePrice,
boxProductWeight,
}; };
} }
// 同时初始化临时编辑值 // 同时初始化临时编辑值
if (!tempEditValues[pkgId]) { if (!tempEditValues[pkgId]) {
updates.tempEditValuesUpdate = { updates.tempEditValuesUpdate = {
boxCostPrice,
boxSalePrice, boxSalePrice,
boxProductWeight,
}; };
} }
@ -288,25 +187,21 @@ export default function EmptyBoxInfoSection(props: {
// 计算各项合计 // 计算各项合计
let totalBoxProductCount = 0; let totalBoxProductCount = 0;
let totalBoxSalePayment = 0; let totalBoxSalePayment = 0;
let totalBoxCostPayment = 0;
let totalBoxProductWeight = 0; let totalBoxProductWeight = 0;
packageData.forEach((pkg: any) => { packageData.forEach((pkg: any) => {
totalBoxProductCount += pkg.boxProductCount || 0; totalBoxProductCount += pkg.boxProductCount || 0;
totalBoxSalePayment += totalBoxSalePayment +=
Number((pkg?.boxSalePrice || 0) * pkg.boxProductCount) || 0; Number((pkg?.boxSalePrice || 0) * pkg.boxProductCount) || 0;
totalBoxCostPayment +=
Number((pkg?.boxCostPrice || 0) * pkg.boxProductCount) || 0;
totalBoxProductWeight += totalBoxProductWeight +=
Number((pkg?.boxProductWeight || 0) * pkg.boxProductCount) || 0; Number((pkg?.boxProductWeight || 0) * pkg.boxProductCount) || 0;
}); });
return { return {
boxBrandName: "合计", boxProductName: "合计",
boxProductCount: totalBoxProductCount, boxProductCount: totalBoxProductCount,
boxSalePayment: totalBoxSalePayment, boxSalePayment: totalBoxSalePayment,
boxProductWeight: totalBoxProductWeight, boxProductWeight: totalBoxProductWeight,
boxCostPrice: totalBoxCostPayment,
isTotalRow: true, // 标记这是合计行 isTotalRow: true, // 标记这是合计行
}; };
}; };
@ -322,12 +217,7 @@ export default function EmptyBoxInfoSection(props: {
packageData.forEach((pkg: BusinessAPI.OrderPackage) => { packageData.forEach((pkg: BusinessAPI.OrderPackage) => {
const pkgId = pkg.orderPackageId || ""; const pkgId = pkg.orderPackageId || "";
const updates = initEditValues( const updates = initEditValues(pkgId, pkg.boxSalePrice);
pkgId,
pkg.boxCostPrice,
pkg.boxSalePrice,
pkg.boxProductWeight,
);
if (updates.editValuesUpdate) { if (updates.editValuesUpdate) {
newEditValues[pkgId] = updates.editValuesUpdate; newEditValues[pkgId] = updates.editValuesUpdate;
@ -364,18 +254,10 @@ export default function EmptyBoxInfoSection(props: {
if (editValue) { if (editValue) {
return { return {
...pkg, ...pkg,
boxCostPrice:
editValue.boxCostPrice !== undefined
? editValue.boxCostPrice
: pkg.boxCostPrice,
boxSalePrice: boxSalePrice:
editValue.boxSalePrice !== undefined editValue.boxSalePrice !== undefined
? editValue.boxSalePrice ? editValue.boxSalePrice
: pkg.boxSalePrice, : pkg.boxSalePrice,
boxProductWeight:
editValue.boxProductWeight !== undefined
? editValue.boxProductWeight
: pkg.boxProductWeight,
}; };
} }
return pkg; return pkg;
@ -397,7 +279,7 @@ export default function EmptyBoxInfoSection(props: {
// 自定义列配置,对合计行特殊处理 // 自定义列配置,对合计行特殊处理
const columnsWithTotalsRender = columns.map((column) => { const columnsWithTotalsRender = columns.map((column) => {
if (column.key === "boxBrandName") { if (column.key === "boxProductName") {
// 品牌列显示"合计" // 品牌列显示"合计"
return { return {
...column, ...column,
@ -405,11 +287,11 @@ export default function EmptyBoxInfoSection(props: {
if (rowData.isTotalRow) { if (rowData.isTotalRow) {
return ( return (
<span style={{ fontWeight: "bold" }}> <span style={{ fontWeight: "bold" }}>
{rowData.boxBrandName} {rowData.boxProductName}
</span> </span>
); );
} }
return rowData.boxBrandName; return rowData.boxProductName;
}, },
}; };
} else if (column.key === "boxProductCount") { } else if (column.key === "boxProductCount") {
@ -476,21 +358,6 @@ export default function EmptyBoxInfoSection(props: {
return column.render(rowData, rowData); return column.render(rowData, rowData);
}, },
}; };
} else if (column.key === "boxCostPrice") {
// 成本单价列合计行处理
return {
...column,
render: (rowData: any) => {
if (rowData.isTotalRow) {
return (
<span style={{ fontWeight: "bold" }}>
{formatCurrency(rowData.boxCostPrice as number)}
</span>
);
}
return column.render(rowData, rowData);
},
};
} }
// 其他列保持原有render函数或者默认显示 // 其他列保持原有render函数或者默认显示
@ -566,66 +433,6 @@ export default function EmptyBoxInfoSection(props: {
/> />
<View className="mr-2"></View> <View className="mr-2"></View>
</View> </View>
<View className="text-neutral-darkest flex flex-row text-sm font-medium">
<Icon name="money-bill" size={16} className="mr-1" />
</View>
<View className="border-neutral-base flex flex-row items-center rounded-md border border-solid">
<Input
className="placeholder:text-neutral-dark"
placeholder="请输入成本单价"
type="digit"
value={
tempEditValues[
pkg.orderPackageId || ""
]?.boxCostPrice?.toString() || ""
}
onChange={(value) => {
const numValue = validatePrice(value);
if (numValue !== undefined) {
setTempEditValues((prev) => ({
...prev,
[pkg.orderPackageId || ""]: {
...prev[pkg.orderPackageId || ""],
boxCostPrice: numValue as number,
},
}));
}
}}
/>
<View className="mr-2"></View>
</View>
<View className="text-neutral-darkest flex flex-row text-sm font-medium">
<Icon name="weight-scale" size={16} className="mr-1" />
</View>
<View className="border-neutral-base flex flex-row items-center rounded-md border border-solid">
<Input
className="placeholder:text-neutral-dark"
placeholder="请输入箱重"
type="digit"
value={
tempEditValues[
pkg.orderPackageId || ""
]?.boxProductWeight?.toString() || ""
}
onChange={(value) => {
const numValue = validatePrice(value);
if (numValue !== undefined) {
setTempEditValues((prev) => ({
...prev,
[pkg.orderPackageId || ""]: {
...prev[pkg.orderPackageId || ""],
boxProductWeight: numValue as number,
},
}));
}
}}
/>
<View className="mr-2"></View>
</View>
</View> </View>
<View className="flex w-full flex-col bg-white"> <View className="flex w-full flex-col bg-white">
<View className="flex flex-row gap-2 p-3"> <View className="flex flex-row gap-2 p-3">

View File

@ -87,47 +87,11 @@ export default function PackageInfoSection(props: {
orderPackageId?: string; orderPackageId?: string;
isTotalRow?: boolean; isTotalRow?: boolean;
}, },
) => { ) => formatCurrency(value.boxProductWeight),
// 合计行不显示编辑按钮
if (value.isTotalRow) {
return formatCurrency(value.boxProductWeight);
}
return (
<View
className="flex w-full items-center justify-between"
onClick={(e) => {
if (!readOnly) {
e.stopPropagation();
// 设置临时编辑值为当前值
setTempEditValues((prev) => ({
...prev,
[value.orderPackageId || ""]:
editValues[value.orderPackageId || ""],
}));
setVisiblePopup((prev) => ({
...prev,
[value.orderPackageId || ""]: true,
}));
}
}}
>
<View className={!readOnly ? "cursor-pointer underline" : ""}>
{formatCurrency(value.boxProductWeight)}
</View>
{!readOnly && (
<View className="-m-2 ml-2 flex items-center justify-center p-2">
<Icon name={"pen-to-square"} size={16} color={"#1a73e8"} />
</View>
)}
</View>
);
},
}, },
{ {
title: "品牌", title: "品牌",
key: "boxBrandName", key: "boxBrandName",
fixed: "left",
}, },
]; ];
const [columns, setColumns] = useState<any[]>(defaultColumns); const [columns, setColumns] = useState<any[]>(defaultColumns);
@ -146,55 +110,38 @@ export default function PackageInfoSection(props: {
// 编辑值的状态 // 编辑值的状态
const [editValues, setEditValues] = useState<{ const [editValues, setEditValues] = useState<{
[key: string]: { [key: string]: {
boxCostPrice?: number;
boxSalePrice?: number; boxSalePrice?: number;
boxProductWeight?: number;
}; };
}>({}); }>({});
// 临时编辑值的状态(用于在保存前暂存编辑的值) // 临时编辑值的状态(用于在保存前暂存编辑的值)
const [tempEditValues, setTempEditValues] = useState<{ const [tempEditValues, setTempEditValues] = useState<{
[key: string]: { [key: string]: {
boxCostPrice?: number;
boxSalePrice?: number; boxSalePrice?: number;
boxProductWeight?: number;
}; };
}>({}); }>({});
// 初始化编辑值 // 初始化编辑值
const initEditValues = ( const initEditValues = (pkgId: string, boxSalePrice?: number) => {
pkgId: string,
boxCostPrice?: number,
boxSalePrice?: number,
boxProductWeight?: number,
) => {
const updates: { const updates: {
editValuesUpdate?: { editValuesUpdate?: {
boxCostPrice?: number;
boxSalePrice?: number; boxSalePrice?: number;
boxProductWeight?: number;
}; };
tempEditValuesUpdate?: { tempEditValuesUpdate?: {
boxCostPrice?: number;
boxSalePrice?: number; boxSalePrice?: number;
boxProductWeight?: number;
}; };
} = {}; } = {};
if (!editValues[pkgId]) { if (!editValues[pkgId]) {
updates.editValuesUpdate = { updates.editValuesUpdate = {
boxCostPrice,
boxSalePrice, boxSalePrice,
boxProductWeight,
}; };
} }
// 同时初始化临时编辑值 // 同时初始化临时编辑值
if (!tempEditValues[pkgId]) { if (!tempEditValues[pkgId]) {
updates.tempEditValuesUpdate = { updates.tempEditValuesUpdate = {
boxCostPrice,
boxSalePrice, boxSalePrice,
boxProductWeight,
}; };
} }
@ -244,15 +191,12 @@ export default function PackageInfoSection(props: {
// 计算各项合计 // 计算各项合计
let totalBoxProductCount = 0; let totalBoxProductCount = 0;
let totalBoxSalePayment = 0; let totalBoxSalePayment = 0;
let totalBoxCostPayment = 0;
let totalBoxProductWeight = 0; let totalBoxProductWeight = 0;
packageData.forEach((pkg: any) => { packageData.forEach((pkg: any) => {
totalBoxProductCount += pkg.boxProductCount || 0; totalBoxProductCount += pkg.boxProductCount || 0;
totalBoxSalePayment += totalBoxSalePayment +=
Number((pkg?.boxSalePrice || 0) * pkg.boxProductCount) || 0; Number((pkg?.boxSalePrice || 0) * pkg.boxProductCount) || 0;
totalBoxCostPayment +=
Number((pkg?.boxCostPrice || 0) * pkg.boxProductCount) || 0;
totalBoxProductWeight += totalBoxProductWeight +=
Number((pkg?.boxProductWeight || 0) * pkg.boxProductCount) || 0; Number((pkg?.boxProductWeight || 0) * pkg.boxProductCount) || 0;
}); });
@ -262,7 +206,6 @@ export default function PackageInfoSection(props: {
boxProductCount: totalBoxProductCount, boxProductCount: totalBoxProductCount,
boxSalePayment: totalBoxSalePayment, boxSalePayment: totalBoxSalePayment,
boxProductWeight: totalBoxProductWeight, boxProductWeight: totalBoxProductWeight,
boxCostPrice: totalBoxCostPayment,
isTotalRow: true, // 标记这是合计行 isTotalRow: true, // 标记这是合计行
}; };
}; };
@ -278,12 +221,7 @@ export default function PackageInfoSection(props: {
packageData.forEach((pkg: BusinessAPI.OrderPackage) => { packageData.forEach((pkg: BusinessAPI.OrderPackage) => {
const pkgId = pkg.orderPackageId || ""; const pkgId = pkg.orderPackageId || "";
const updates = initEditValues( const updates = initEditValues(pkgId, pkg.boxSalePrice);
pkgId,
pkg.boxCostPrice,
pkg.boxSalePrice,
pkg.boxProductWeight,
);
if (updates.editValuesUpdate) { if (updates.editValuesUpdate) {
newEditValues[pkgId] = updates.editValuesUpdate; newEditValues[pkgId] = updates.editValuesUpdate;
@ -323,18 +261,10 @@ export default function PackageInfoSection(props: {
if (editValue) { if (editValue) {
return { return {
...pkg, ...pkg,
boxCostPrice:
editValue.boxCostPrice !== undefined
? editValue.boxCostPrice
: pkg.boxCostPrice,
boxSalePrice: boxSalePrice:
editValue.boxSalePrice !== undefined editValue.boxSalePrice !== undefined
? editValue.boxSalePrice ? editValue.boxSalePrice
: pkg.boxSalePrice, : pkg.boxSalePrice,
boxProductWeight:
editValue.boxProductWeight !== undefined
? editValue.boxProductWeight
: pkg.boxProductWeight,
}; };
} }
return pkg; return pkg;
@ -518,36 +448,6 @@ export default function PackageInfoSection(props: {
/> />
<View className="mr-2"></View> <View className="mr-2"></View>
</View> </View>
<View className="text-neutral-darkest flex flex-row text-sm font-medium">
<Icon name="weight-scale" size={16} className="mr-1" />
</View>
<View className="border-neutral-base flex flex-row items-center rounded-md border border-solid">
<Input
className="placeholder:text-neutral-dark"
placeholder="请输入箱重"
type="digit"
value={
tempEditValues[
pkg.orderPackageId || ""
]?.boxProductWeight?.toString() || ""
}
onChange={(value) => {
const numValue = validatePrice(value);
if (numValue !== undefined) {
setTempEditValues((prev) => ({
...prev,
[pkg.orderPackageId || ""]: {
...prev[pkg.orderPackageId || ""],
boxProductWeight: numValue as number,
},
}));
}
}}
/>
<View className="mr-2"></View>
</View>
</View> </View>
<View className="flex w-full flex-col bg-white"> <View className="flex w-full flex-col bg-white">
<View className="flex flex-row gap-2 p-3"> <View className="flex flex-row gap-2 p-3">

View File

@ -38,7 +38,12 @@ import {
TaxSubsidySection, TaxSubsidySection,
WorkerAdvanceSection, WorkerAdvanceSection,
} from "@/components"; } from "@/components";
import { buildUrl, formatCurrency, PurchaseOrderCalculator } from "@/utils"; import {
buildUrl,
formatCurrency,
generateShortId,
PurchaseOrderCalculator,
} from "@/utils";
import classNames from "classnames"; import classNames from "classnames";
const defaultSections = [ const defaultSections = [
@ -179,11 +184,11 @@ const fullSections = [
component: TaxProvisionSection, component: TaxProvisionSection,
title: "计提税金复核", title: "计提税金复核",
}, },
// 调诚信志远分红 // 待分红金额复核
{ {
name: "costDifference", name: "costDifference",
component: CostDifferenceSection, component: CostDifferenceSection,
title: "调诚信志远分红", title: "待分红金额复核",
}, },
// 成本合计 // 成本合计
{ {
@ -215,24 +220,6 @@ export default hocAuth(function Page(props: CommonComponent) {
// 费用项目列表 // 费用项目列表
const [costList, setCostList] = useState<BusinessAPI.CostVO[]>([]); const [costList, setCostList] = useState<BusinessAPI.CostVO[]>([]);
// 获取费用项目列表
useEffect(() => {
const fetchCost = async () => {
try {
const { data } = await business.cost.listCost({
costListQry: {
status: true,
},
});
setCostList(data.data || []);
} catch (error) {
console.error("获取费用项目列表失败:", error);
}
};
fetchCost();
}, []);
const [purchaseOrderVO, setPurchaseOrderVO] = const [purchaseOrderVO, setPurchaseOrderVO] =
useState<BusinessAPI.PurchaseOrderVO>(); useState<BusinessAPI.PurchaseOrderVO>();
@ -449,16 +436,63 @@ export default hocAuth(function Page(props: CommonComponent) {
}; };
const init = async (orderId: BusinessAPI.PurchaseOrderVO["orderId"]) => { const init = async (orderId: BusinessAPI.PurchaseOrderVO["orderId"]) => {
const { data } = await business.purchaseOrder.showPurchaseOrder({ const {
data: { data: purchaseOrderVO, success },
} = await business.purchaseOrder.showPurchaseOrder({
purchaseOrderShowQry: { purchaseOrderShowQry: {
orderId, orderId,
}, },
}); });
if (data.success) { if (success && purchaseOrderVO) {
setPurchaseOrderVO(data.data); await initDealer(purchaseOrderVO?.orderDealer?.dealerId!);
await initDealer(data.data?.orderDealer?.dealerId!); const orderCost = purchaseOrderVO?.orderCostList.find(
(item) => item.name === "计提费" && item.type === "OTHER_TYPE",
);
if (orderCost) {
await business.cost
.listCost({
costListQry: {
status: true,
},
})
.then(({ data: { data: costList } }) => {
setCostList(costList || []);
});
} else {
const {
data: { data: costList },
} = await business.cost.listCost({
costListQry: {
status: true,
},
});
setCostList(costList || []);
const cost = costList?.find(
(cost) => cost.name === "计提费" && cost.type === "OTHER_TYPE",
);
if (cost) {
purchaseOrderVO.orderCostList.push({
orderCostId: generateShortId(),
costId: cost.costId || "",
name: cost.name || "",
price: cost.price || 0,
unit: cost.unit || "元",
count: 1,
type: "OTHER_TYPE",
costItemIds: [],
principal: "",
selected: true,
});
}
}
setPurchaseOrderVO(purchaseOrderVO);
} }
}; };
@ -567,7 +601,10 @@ export default hocAuth(function Page(props: CommonComponent) {
return null; return null;
} }
if (!orderDealer?.enableShare && sectionKey === "costDifference") { if (
!orderDealer?.shareAdjusted &&
sectionKey === "costDifference"
) {
return null; return null;
} }
@ -603,7 +640,7 @@ export default hocAuth(function Page(props: CommonComponent) {
} }
return ( return (
<> <View key={sectionKey} className={"flex flex-col gap-2.5"}>
<View className="text-sm font-bold">{section.title}</View> <View className="text-sm font-bold">{section.title}</View>
<View <View
className={`overflow-x-auto rounded-md rounded-b-lg bg-white p-2.5 shadow-sm`} className={`overflow-x-auto rounded-md rounded-b-lg bg-white p-2.5 shadow-sm`}
@ -617,7 +654,7 @@ export default hocAuth(function Page(props: CommonComponent) {
calculator={calculator} calculator={calculator}
/> />
</View> </View>
</> </View>
); );
})} })}

View File

@ -49,7 +49,7 @@ export class PurchaseOrderCalculator {
{ {
分成利润: this.getShareProfit(), 分成利润: this.getShareProfit(),
西瓜利润: this.getMelonNetProfit(), 西瓜利润: this.getMelonNetProfit(),
诚信志远分成: this.getShareProfitRatio(), 分成: this.getShareProfitRatio(),
个人利润: this.getPersonalProfit(), 个人利润: this.getPersonalProfit(),
}, },
]); ]);