Compare commits

..

2 Commits

Author SHA1 Message Date
shenyifei
d467e25a76 feat(purchase): 优化采购表单与成本编辑功能
- 新增 DeliveryFormSection 组件用于发货表单展示
- 移除多个弹窗组件,改用 PriceEditor 直接编辑金额
- 优化 PriceEditor 组件支持清空与格式化显示
- 调整采购成本相关字段命名与计算逻辑
- 增强返点计算 section 的交互与数据校验
- 更新表格样式并新增定金字段展示
- 修复 delivery 页面成本类型过滤条件
- 统一使用 formatCurrency 处理金额显示格式
- 重构多个 section 组件移除冗余状态管理
- 优化只读模式下表单元素的禁用与样式表现
2025-12-05 00:14:54 +08:00
shenyifei
1676290798 feat(purchase): 优化采购模块UI与逻辑处理
- 在CostCard组件中添加了对空成本项列表的条件渲染
- 更新EmptyBoxModule和OrderPackage组件中的数组长度判断为可选链形式
- 在MelonFarmer组件中增加supplierVO的日志输出并修正isLast属性比较逻辑
- 重构OrderCost组件的成本初始化逻辑,提升性能和代码可读性
- 移除OrderCostItem组件中不必要的控制台日志
- 优化OrderCostItem组件中selected状态的赋值逻辑
- 升级Weigh组件中关于供应商字段的布尔值判断方式
- 将PurchasePreview组件中空箱使用明细的显示条件改为可选链判断
- 提升create页面中添加瓜农按钮的视觉样式与布局结构
- 更新应用版本号从v0.0.26至v0.0.28
2025-12-04 17:57:10 +08:00
28 changed files with 623 additions and 705 deletions

View File

@ -1,6 +1,7 @@
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { Input, Text, View } from "@tarojs/components"; import { Input, Text, View } from "@tarojs/components";
import { Icon, IconNames } from "@/components"; import { Icon, IconNames } from "@/components";
import { formatCurrency } from "@/utils";
interface PriceEditorProps { interface PriceEditorProps {
value: number; value: number;
@ -61,6 +62,13 @@ export default function PriceEditor(props: PriceEditorProps) {
setIsEditing(false); setIsEditing(false);
}; };
// 清空输入
const clearValue = (e: any) => {
e.stopPropagation();
setInputValue("");
onSave(0);
};
return ( return (
<> <>
{!readOnly ? ( {!readOnly ? (
@ -73,7 +81,7 @@ export default function PriceEditor(props: PriceEditorProps) {
<Text className="text-sm text-gray-500">{unit}</Text> <Text className="text-sm text-gray-500">{unit}</Text>
</View> </View>
{isEditing ? ( {isEditing ? (
<View className="relative flex"> <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">
<Input <Input
ref={inputRef} ref={inputRef}
@ -82,13 +90,36 @@ export default function PriceEditor(props: PriceEditorProps) {
onInput={handleInputChange} onInput={handleInputChange}
onBlur={saveEdit} onBlur={saveEdit}
/> />
{inputValue !== "" && (
<Icon
name="circle-xmark"
size={20}
className="ml-2 cursor-pointer"
onClick={clearValue}
/>
)}
</View> </View>
</View> </View>
) : ( ) : (
<View className="relative flex" onClick={() => setIsEditing(true)}> <View
className="relative flex flex-1 items-end"
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">
{value?.toFixed(2) || "0"} {formatCurrency(value || 0)}
</Text> </Text>
<Icon
name={value !== 0 ? "circle-xmark" : "pen-to-square"}
size={20}
className="absolute right-0 bottom-2 ml-2 cursor-pointer"
onClick={(event) => {
if (value !== 0) {
clearValue(event);
} else {
setIsEditing(true);
}
}}
/>
</View> </View>
)} )}
<View className="mt-1 text-xs text-gray-400">{hint}</View> <View className="mt-1 text-xs text-gray-400">{hint}</View>
@ -104,7 +135,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">
{value?.toFixed(2) || "0"} {formatCurrency(value || 0)}
</Text> </Text>
</View> </View>
</View> </View>

View File

@ -10,6 +10,7 @@ import dayjs from "dayjs";
import { Icon } from "@/components"; import { Icon } from "@/components";
interface Step1FormProps { interface Step1FormProps {
readOnly?: boolean;
moduleList: any[]; moduleList: any[];
shipOrderVO: any; shipOrderVO: any;
setShipOrderVO: (value: any) => void; setShipOrderVO: (value: any) => void;
@ -20,7 +21,7 @@ export interface Step1FormRef {
} }
const Step1Form = forwardRef<Step1FormRef, Step1FormProps>((props, ref) => { const Step1Form = forwardRef<Step1FormRef, Step1FormProps>((props, ref) => {
const { moduleList, shipOrderVO, setShipOrderVO } = props; const { moduleList, shipOrderVO, setShipOrderVO, readOnly = false } = props;
// 当天和未来10天 // 当天和未来10天
const startDate = new Date(); const startDate = new Date();
@ -94,6 +95,7 @@ const Step1Form = forwardRef<Step1FormRef, Step1FormProps>((props, ref) => {
className={`flex h-10 w-full items-center rounded-md ${formErrors.watermelonGrade ? "border-4 border-red-500" : "border-4 border-gray-300"}`} className={`flex h-10 w-full items-center rounded-md ${formErrors.watermelonGrade ? "border-4 border-red-500" : "border-4 border-gray-300"}`}
> >
<Input <Input
disabled={readOnly}
value={shipOrderVO?.watermelonGrade || ""} value={shipOrderVO?.watermelonGrade || ""}
onChange={(value) => { onChange={(value) => {
// 清除错误状态 // 清除错误状态
@ -104,11 +106,9 @@ const Step1Form = forwardRef<Step1FormRef, Step1FormProps>((props, ref) => {
})); }));
} }
setShipOrderVO((prev: any) => { setShipOrderVO({
return { ...shipOrderVO!,
...prev!, watermelonGrade: value,
watermelonGrade: value,
};
}); });
}} }}
onBlur={() => { onBlur={() => {
@ -158,6 +158,7 @@ const Step1Form = forwardRef<Step1FormRef, Step1FormProps>((props, ref) => {
className={`flex h-10 w-full items-center rounded-md ${formErrors.shippingAddress ? "border-4 border-red-500" : "border-4 border-gray-300"}`} className={`flex h-10 w-full items-center rounded-md ${formErrors.shippingAddress ? "border-4 border-red-500" : "border-4 border-gray-300"}`}
> >
<Input <Input
disabled={readOnly}
value={shipOrderVO?.shippingAddress || ""} value={shipOrderVO?.shippingAddress || ""}
onChange={(value) => { onChange={(value) => {
// 清除错误状态 // 清除错误状态
@ -168,11 +169,9 @@ const Step1Form = forwardRef<Step1FormRef, Step1FormProps>((props, ref) => {
})); }));
} }
setShipOrderVO((prev: any) => { setShipOrderVO({
return { ...shipOrderVO!,
...prev!, shippingAddress: value,
shippingAddress: value,
};
}); });
}} }}
onBlur={() => { onBlur={() => {
@ -223,12 +222,13 @@ const Step1Form = forwardRef<Step1FormRef, Step1FormProps>((props, ref) => {
> >
<View <View
className={ className={
"flex flex-1 flex-row items-center justify-between px-5" "flex flex-1 flex-row items-center justify-between px-5" +
(readOnly ? " opacity-50" : "")
} }
style={{ style={{
color: "var(--nutui-color-title, #1a1a1a)", color: "var(--nutui-color-title, #1a1a1a)",
}} }}
onClick={() => setShow(true)} onClick={() => readOnly || setShow(true)}
> >
<View className={"text-sm"}> <View className={"text-sm"}>
{shipOrderVO?.estimatedArrivalDate {shipOrderVO?.estimatedArrivalDate
@ -257,13 +257,11 @@ const Step1Form = forwardRef<Step1FormRef, Step1FormProps>((props, ref) => {
})); }));
} }
setShipOrderVO((prev: any) => { setShipOrderVO({
return { ...shipOrderVO!,
...prev!, estimatedArrivalDate: dayjs(values.join("-")).format(
estimatedArrivalDate: dayjs(values.join("-")).format( "YYYY-MM-DD",
"YYYY-MM-DD", ),
),
};
}); });
}} }}
/> />
@ -290,6 +288,7 @@ const Step1Form = forwardRef<Step1FormRef, Step1FormProps>((props, ref) => {
className={`flex h-10 w-full items-center rounded-md ${formErrors.remark ? "border-4 border-red-500" : "border-4 border-gray-300"}`} className={`flex h-10 w-full items-center rounded-md ${formErrors.remark ? "border-4 border-red-500" : "border-4 border-gray-300"}`}
> >
<Input <Input
disabled={readOnly}
value={shipOrderVO?.remark || ""} value={shipOrderVO?.remark || ""}
onChange={(value) => { onChange={(value) => {
// 清除错误状态 // 清除错误状态
@ -300,11 +299,9 @@ const Step1Form = forwardRef<Step1FormRef, Step1FormProps>((props, ref) => {
})); }));
} }
setShipOrderVO((prev: any) => { setShipOrderVO({
return { ...shipOrderVO!,
...prev!, remark: value,
remark: value,
};
}); });
}} }}
onBlur={() => { onBlur={() => {
@ -362,6 +359,7 @@ const Step1Form = forwardRef<Step1FormRef, Step1FormProps>((props, ref) => {
className={`flex h-10 items-center rounded-md ${formErrors.itemGrades?.[shipOrderItem.itemId] ? "border-4 border-red-500" : "border-4 border-gray-300"}`} className={`flex h-10 items-center rounded-md ${formErrors.itemGrades?.[shipOrderItem.itemId] ? "border-4 border-red-500" : "border-4 border-gray-300"}`}
> >
<Input <Input
disabled={readOnly}
value={shipOrderItem.watermelonGrade || ""} value={shipOrderItem.watermelonGrade || ""}
onChange={(value) => { onChange={(value) => {
// 清除该项的错误状态 // 清除该项的错误状态
@ -378,10 +376,10 @@ const Step1Form = forwardRef<Step1FormRef, Step1FormProps>((props, ref) => {
})); }));
} }
setShipOrderVO((prev: any) => { setShipOrderVO({
return { ...shipOrderVO!,
...prev!, shipOrderItemList:
shipOrderItemList: prev?.shipOrderItemList?.map( shipOrderVO?.shipOrderItemList?.map(
(item: any) => { (item: any) => {
if (item.itemId === shipOrderItem.itemId) { if (item.itemId === shipOrderItem.itemId) {
return { return {
@ -392,7 +390,6 @@ const Step1Form = forwardRef<Step1FormRef, Step1FormProps>((props, ref) => {
return item; return item;
}, },
), ),
};
}); });
}} }}
onBlur={() => { onBlur={() => {
@ -552,7 +549,7 @@ const Step1Form = forwardRef<Step1FormRef, Step1FormProps>((props, ref) => {
}; };
return ( return (
<View className="flex flex-col rounded-lg bg-white p-2.5 shadow-md"> <View>
{moduleList.map((module) => { {moduleList.map((module) => {
const contentFields = renderContentFields(module); const contentFields = renderContentFields(module);
// 如果没有内容配置字段,则不渲染该模块 // 如果没有内容配置字段,则不渲染该模块

View File

@ -1,7 +1,7 @@
import { Text, View } from "@tarojs/components"; import { Text, View } from "@tarojs/components";
import { useState } from "react"; import { useState } from "react";
import { Dialog } from "@nutui/nutui-react-taro"; import { Dialog } from "@nutui/nutui-react-taro";
import { Icon, PriceEditor } from "@/components"; import { PriceEditor } from "@/components";
import CostCreate from "./CostCreate"; import CostCreate from "./CostCreate";
interface CostCardComponentProps { interface CostCardComponentProps {
@ -58,18 +58,21 @@ export default function CostCard(props: CostCardComponentProps) {
<View className="mb-2 flex items-center justify-between"> <View className="mb-2 flex items-center justify-between">
<Text className="text-sm font-medium">{orderCost.name}</Text> <Text className="text-sm font-medium">{orderCost.name}</Text>
{!readOnly && ( {!readOnly && (
<View className="flex flex-row gap-2.5"> <View className="flex gap-2">
<Icon {orderCostItemList.length > 0 && (
name="pen-to-square" <View
size={16} 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={() => setShowEditModal(true)} onClick={() => setShowEditModal(true)}
/> >
<Icon
name="circle-xmark" </View>
size={16} )}
color={"var(--color-red-500)"} <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"
onClick={handleDelete} onClick={handleDelete}
/> >
</View>
</View> </View>
)} )}
</View> </View>
@ -94,23 +97,25 @@ export default function CostCard(props: CostCardComponentProps) {
hint="点击金额可直接编辑" hint="点击金额可直接编辑"
/> />
<View className="flex flex-1 flex-col gap-2 pl-4"> {orderCostItemList.length > 0 && (
{orderCostItemList.map((orderCostItem) => { <View className="flex flex-1 flex-col gap-2 pl-4">
return ( {orderCostItemList.map((orderCostItem) => {
<View return (
key={orderCostItem.orderCostItemId} <View
className="flex items-center justify-between" key={orderCostItem.orderCostItemId}
> className="flex items-center justify-between"
<Text className="text-sm text-gray-500"> >
{orderCostItem.name} <Text className="text-sm text-gray-500">
</Text> {orderCostItem.name}
<Text className="text-sm font-medium"> </Text>
{orderCostItem.count} {orderCostItem.unit} <Text className="text-sm font-medium">
</Text> {orderCostItem.count} {orderCostItem.unit}
</View> </Text>
); </View>
})} );
</View> })}
</View>
)}
</View> </View>
</View> </View>

View File

@ -41,6 +41,7 @@ export { default as CostDifferenceSection } from "./section/CostDifferenceSectio
export { default as MaterialCostSection } from "./section/MaterialCostSection"; export { default as MaterialCostSection } from "./section/MaterialCostSection";
export { default as ProductionAdvanceSection } from "./section/ProductionAdvanceSection"; export { default as ProductionAdvanceSection } from "./section/ProductionAdvanceSection";
export { default as WorkerAdvanceSection } from "./section/WorkerAdvanceSection"; export { default as WorkerAdvanceSection } from "./section/WorkerAdvanceSection";
export { default as DeliveryFormSection } from "./section/DeliveryFormSection";
export { default as PurchaseStep1Form } from "./document/Step1Form"; export { default as PurchaseStep1Form } from "./document/Step1Form";
export type { Step1FormRef as PurchaseStep1FormRef } from "./document/Step1Form"; export type { Step1FormRef as PurchaseStep1FormRef } from "./document/Step1Form";

View File

@ -128,7 +128,7 @@ export default function EmptyBoxModule(props: IEmptyBoxModuleProps) {
initBoxBrandList().then(); initBoxBrandList().then();
setEmptyBoxList(orderPackageList); setEmptyBoxList(orderPackageList);
if (orderPackageList.length > 0) { if (orderPackageList?.length > 0) {
// 根据当前供应商确定需要检查的纸箱类型 // 根据当前供应商确定需要检查的纸箱类型
let requiredTypes: string[] = ["EMPTY"]; let requiredTypes: string[] = ["EMPTY"];

View File

@ -549,6 +549,7 @@ export default forwardRef<MelonFarmerRef, IMelonFarmerProps>(
}); });
}; };
console.log("supplierVO", supplierVO);
if (!supplierVO) { if (!supplierVO) {
return; return;
} }
@ -588,9 +589,9 @@ export default forwardRef<MelonFarmerRef, IMelonFarmerProps>(
<Radio.Group <Radio.Group
direction="horizontal" direction="horizontal"
value={ value={
supplierVO.isLast supplierVO.isLast === true
? "true" ? "true"
: !supplierVO.isLast : supplierVO.isLast === false
? "false" ? "false"
: undefined : undefined
} }

View File

@ -3,7 +3,6 @@ import { Icon } from "@/components";
import { forwardRef, useEffect, useImperativeHandle, useState } from "react"; import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
import { Checkbox, Input, Toast } from "@nutui/nutui-react-taro"; import { Checkbox, Input, Toast } from "@nutui/nutui-react-taro";
import { generateShortId } from "@/utils"; import { generateShortId } from "@/utils";
import { business } from "@/services";
// 定义ref暴露的方法接口 // 定义ref暴露的方法接口
export interface OrderCostRef { export interface OrderCostRef {
@ -30,54 +29,41 @@ export default forwardRef<OrderCostRef, IOrderCostProps>(
// 初始化函数 // 初始化函数
const init = async (costIds: string[]) => { const init = async (costIds: string[]) => {
let newOrderCostList: BusinessAPI.OrderCost[] = []; // 创建一个映射用于快速查找orderCostItemList中的项目
const orderCostMap = new Map<string, BusinessAPI.OrderCost>();
// 获取成本列表 orderCostList?.forEach((item) => {
const { if (item.costId && costIds.includes(item.costId)) {
data: { data: costList, success }, orderCostMap.set(item.costId, item);
} = await business.cost.listCost({ }
costListQry: {
status: true,
},
}); });
if (success) { // 构建初始列表基于costTemplate中的项目
const newCostList = costList?.filter((item) => { const newOrderCostList: IOrderCost[] = [];
return costIds.includes(item.costId);
});
// 遍历costList将costList中的项目添加到orderCostList中 // 遍历costList将costList中的项目添加到orderCostList中
newCostList?.forEach((item) => { parsedCostTemplate.productionTypeList?.forEach((item) => {
const orderCost = orderCostList.find( const existingItem = orderCostMap.get(item.costItemId);
(cost) => cost.costId === item.costId,
);
const selected = parsedCostTemplate.productionTypeList?.some( if (existingItem) {
(cost) => { newOrderCostList.push({
return cost.costId === item.costId; ...existingItem,
}, selected: !!existingItem,
); });
} else {
if (orderCost) { newOrderCostList.push({
newOrderCostList.push({ orderCostId: generateShortId(),
...orderCost, costItemIds: item.costItemIds,
selected: selected, costId: item.costId,
}); name: item.name,
} else { type: item.type,
newOrderCostList.push({ selected: false,
orderCostId: generateShortId(), count: 1,
costItemIds: item.costItemIds, price: item.price || 0,
costId: item.costId, unit: item.unit || "元",
name: item.name, });
type: item.type, }
selected: selected, });
count: 1,
price: item.price || 0,
unit: item.unit || "元",
});
}
});
}
onChange?.({ onChange?.({
...value, ...value,

View File

@ -47,8 +47,6 @@ export default forwardRef<OrderCostItemRef, IOrderCostItemProps>(
} }
}); });
console.log("orderCostItemList,", orderCostItemList);
// 构建初始列表基于costTemplate中的项目 // 构建初始列表基于costTemplate中的项目
const newOrderCostItemList: IOrderCostItem[] = []; const newOrderCostItemList: IOrderCostItem[] = [];
@ -62,7 +60,7 @@ export default forwardRef<OrderCostItemRef, IOrderCostItemProps>(
name: item.name, name: item.name,
price: item.price, price: item.price,
unit: item.unit, unit: item.unit,
selected: true, selected: !!existingItem,
count: existingItem?.count || 1, count: existingItem?.count || 1,
payerType: existingItem?.payerType, payerType: existingItem?.payerType,
type: item.type, type: item.type,
@ -82,7 +80,7 @@ export default forwardRef<OrderCostItemRef, IOrderCostItemProps>(
name: item.name, name: item.name,
price: item.price, price: item.price,
unit: item.unit, unit: item.unit,
selected: true, selected: !!existingItem,
count: existingItem?.count || 1, count: existingItem?.count || 1,
payerType: existingItem?.payerType, payerType: existingItem?.payerType,
type: item.type, type: item.type,
@ -90,7 +88,6 @@ export default forwardRef<OrderCostItemRef, IOrderCostItemProps>(
}); });
}); });
console.log("newOrderCostItemList,", newOrderCostItemList);
onChange?.({ onChange?.({
...value, ...value,
orderCostItemList: newOrderCostItemList, orderCostItemList: newOrderCostItemList,

View File

@ -41,7 +41,7 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
useEffect(() => { useEffect(() => {
// 初始化数据 // 初始化数据
if (orderPackageList.length > 0) { if (orderPackageList?.length > 0) {
// 根据当前供应商确定需要检查的纸箱类型 // 根据当前供应商确定需要检查的纸箱类型
let requiredTypes: string[] = []; let requiredTypes: string[] = [];

View File

@ -245,7 +245,7 @@ export default function PurchasePreview(props: IPurchasePreviewProps) {
</View> </View>
</View> </View>
{purchaseOrder.orderPackageList.length > 0 && ( {purchaseOrder.orderPackageList?.length > 0 && (
<> <>
<View className="text-sm font-bold">使</View> <View className="text-sm font-bold">使</View>
<View className="flex flex-col gap-2.5"> <View className="flex flex-col gap-2.5">

View File

@ -535,9 +535,9 @@ export default forwardRef<WeighRef, IWeightProps>(function Weigh(props, ref) {
<Radio.Group <Radio.Group
direction="horizontal" direction="horizontal"
value={ value={
supplierVO.isPaper supplierVO.isPaper === true
? "true" ? "true"
: !supplierVO.isPaper : supplierVO.isPaper === false
? "false" ? "false"
: undefined : undefined
} }
@ -639,7 +639,7 @@ export default forwardRef<WeighRef, IWeightProps>(function Weigh(props, ref) {
<Radio.Group <Radio.Group
direction="horizontal" direction="horizontal"
value={ value={
supplierVO.isDepositPaid supplierVO.isDepositPaid === true
? "true" ? "true"
: supplierVO.isDepositPaid === false : supplierVO.isDepositPaid === false
? "false" ? "false"

View File

@ -1,7 +1,5 @@
import { useEffect, useState } from "react";
import { Text, View } from "@tarojs/components"; import { Text, View } from "@tarojs/components";
import { Button, Popup, SafeArea } from "@nutui/nutui-react-taro"; import { formatCurrency, PurchaseOrderCalculator } from "@/utils";
import { PurchaseOrderCalculator } from "@/utils";
import { PriceEditor } from "@/components"; import { PriceEditor } from "@/components";
export default function CostDifferenceSection(props: { export default function CostDifferenceSection(props: {
@ -13,49 +11,6 @@ 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;
const [visible, setVisible] = useState(false);
// 主状态,用于页面显示
const costDifference =
orderDealer?.costDifference || calculator.getCostDifference();
// 弹窗内的临时状态
const [tempCostDifference, setTempCostDifference] = useState<number>(
orderDealer?.costDifference || 0,
);
const profitSharing = calculator.getShareProfit() || 0;
useEffect(() => {
if (!orderDealer?.costDifference) {
const defaultCostDifference = calculator.getCostDifference();
setTempCostDifference(defaultCostDifference);
// 更新父组件的状态
onChange?.({
...purchaseOrderVO,
orderDealer: {
...orderDealer,
costDifference: defaultCostDifference,
profitSharing: profitSharing,
},
});
}
}, []);
// 保存调整成本
const saveCostDifference = () => {
onChange?.({
...purchaseOrderVO,
orderDealer: {
...orderDealer,
costDifference: tempCostDifference,
profitSharing: profitSharing,
},
});
setVisible(false);
};
return ( return (
<View className={"flex flex-col gap-2.5"}> <View className={"flex flex-col gap-2.5"}>
{/* 卡片形式展示分成信息 */} {/* 卡片形式展示分成信息 */}
@ -66,21 +21,26 @@ export default function CostDifferenceSection(props: {
<View className="flex"> <View className="flex">
<PriceEditor <PriceEditor
value={costDifference} value={
orderDealer.costDifference === null ||
orderDealer.costDifference === undefined
? calculator.getCostDifference()
: orderDealer.costDifference
}
onSave={(newValue) => { onSave={(newValue) => {
onChange?.({ onChange?.({
...purchaseOrderVO, ...purchaseOrderVO,
orderDealer: { orderDealer: {
...orderDealer, ...orderDealer,
costDifference: newValue, costDifference: newValue,
profitSharing: profitSharing, profitSharing: calculator.getShareProfit(),
}, },
}); });
}} }}
readOnly={readOnly || !orderDealer?.shareAdjusted} readOnly={readOnly || !orderDealer?.shareAdjusted}
label={orderDealer?.shareAdjusted ? "调分成金额" : "分成金额"} label={orderDealer?.shareAdjusted ? "调分成金额" : "分成金额"}
unit="元" unit="元"
hint="" hint="点击金额可直接编辑"
/> />
<View className="flex flex-1 flex-col gap-2 pl-4"> <View className="flex flex-1 flex-col gap-2 pl-4">
@ -89,66 +49,15 @@ export default function CostDifferenceSection(props: {
</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">
{profitSharing} {" "}
{formatCurrency(
orderDealer.profitSharing || calculator.getShareProfit() || 0,
)}
</Text> </Text>
</View> </View>
</View> </View>
</View> </View>
</View> </View>
{/* 分成弹窗 */}
<Popup
duration={150}
style={{
minHeight: "auto",
}}
visible={visible}
className={"flex flex-col"}
position="bottom"
title={"调分成金额"}
onClose={() => setVisible(false)}
onOverlayClick={() => {
setVisible(false);
}}
lockScroll
>
<View className={"flex flex-col gap-3 p-2.5"}>
<View className={"text-neutral-darkest text-sm font-medium"}>
</View>
<View
className={
"border-neutral-base flex flex-row items-center rounded-md border border-solid"
}
>
<Button
size={"large"}
block
type="primary"
onClick={saveCostDifference}
>
</Button>
</View>
</View>
<View className={"flex w-full flex-col bg-white"}>
<View className={"flex flex-row gap-2 p-3"}>
<View className={"flex-1"}>
<Button
size={"large"}
block
type="default"
onClick={() => {
setVisible(false);
}}
>
</Button>
</View>
</View>
<SafeArea position={"bottom"} />
</View>
</Popup>
</View> </View>
); );
} }

View File

@ -3,7 +3,7 @@ import { View } from "@tarojs/components";
import { DealerPicker, Icon } from "@/components"; import { DealerPicker, Icon } from "@/components";
import { Button } from "@nutui/nutui-react-taro"; import { Button } from "@nutui/nutui-react-taro";
export default function (props: { export default function DealerInfoSection(props: {
purchaseOrderVO: BusinessAPI.PurchaseOrderVO; purchaseOrderVO: BusinessAPI.PurchaseOrderVO;
onChange?: (orderDealer: BusinessAPI.PurchaseOrderVO) => void; onChange?: (orderDealer: BusinessAPI.PurchaseOrderVO) => void;
readOnly?: boolean; readOnly?: boolean;
@ -82,7 +82,7 @@ export default function (props: {
fill="outline" fill="outline"
block block
> >
{orderDealer ? "修改经销商" : "选择经销商"} {orderDealer ? "修改下游经销商" : "选择下游经销商"}
</Button> </Button>
</View> </View>
} }

View File

@ -0,0 +1,81 @@
import { useEffect, useState } from "react";
import { View } from "@tarojs/components";
import { DeliveryStep1Form } from "@/components";
import {
convertPurchaseOrderToShipOrder,
convertShipOrderVOToExamplesFormat,
} from "@/utils";
import { business } from "@/services";
export default function DeliveryFormSection(props: {
purchaseOrderVO: BusinessAPI.PurchaseOrderVO;
onChange?: (orderDealer: BusinessAPI.PurchaseOrderVO) => void;
readOnly?: boolean;
}) {
const { purchaseOrderVO, onChange, readOnly } = props;
const [moduleList, setModuleList] = useState<any[]>([]);
const shipOrderVO =
purchaseOrderVO.shipOrderVOList &&
purchaseOrderVO.shipOrderVOList.length > 0
? purchaseOrderVO.shipOrderVOList[0]
: convertPurchaseOrderToShipOrder(purchaseOrderVO);
const init = async (shipOrderVO: BusinessAPI.ShipOrderVO) => {
const { data } = await business.dealer.showDealer({
dealerShowQry: {
dealerId: shipOrderVO.dealerId,
},
});
if (data.data?.deliveryTemplate!) {
const template = JSON.parse(data.data?.deliveryTemplate!);
// 将 shipOrderVO 转换为 examples 的数据格式,然后再替换 moduleList 里面的 config 数据
const convertedData = convertShipOrderVOToExamplesFormat(shipOrderVO);
const updatedTemplate = await updateTemplateConfig(
template,
convertedData,
);
console.log("updatedTemplate", updatedTemplate);
setModuleList(updatedTemplate);
}
};
useEffect(() => {
init(shipOrderVO);
}, []);
// 更新模板配置
const updateTemplateConfig = async (template: any[], data: any) => {
let templateList: any[] = [];
template.map(async (module: any) => {
let newModule = { ...module };
if (data[module.type]) {
newModule.config = { ...module.config, ...data[module.type] };
}
templateList.push(newModule);
});
return templateList;
};
if (!shipOrderVO) {
return;
}
return (
<View className={"flex flex-col gap-2.5"}>
<DeliveryStep1Form
readOnly={readOnly}
moduleList={moduleList}
shipOrderVO={shipOrderVO}
setShipOrderVO={(shipOrderVO) => {
onChange?.({
...purchaseOrderVO,
shipOrderVOList: [shipOrderVO],
});
}}
/>
</View>
);
}

View File

@ -1,6 +1,5 @@
import { useEffect, useState } from "react"; import { useEffect } from "react";
import { Text, View } from "@tarojs/components"; import { Text, View } from "@tarojs/components";
import { Button, Popup, SafeArea } from "@nutui/nutui-react-taro";
import { PurchaseOrderCalculator } from "@/utils"; import { PurchaseOrderCalculator } from "@/utils";
import { PriceEditor } from "@/components"; import { PriceEditor } from "@/components";
@ -13,21 +12,13 @@ export default function ProductionLossSection(props: {
const { purchaseOrderVO, onChange, readOnly, calculator } = props; const { purchaseOrderVO, onChange, readOnly, calculator } = props;
const orderDealer = purchaseOrderVO.orderDealer; const orderDealer = purchaseOrderVO.orderDealer;
const [visible, setVisible] = useState(false);
// 主状态 - 用于显示在页面上的值 // 主状态 - 用于显示在页面上的值
const displayLossAmount = const displayLossAmount =
orderDealer?.lossAmount || calculator.getDefaultLossAmount(); orderDealer?.lossAmount || calculator.getDefaultLossAmount();
// 临时状态 - 用于弹窗内编辑的值
const [tempLossAmount, setTempLossAmount] = useState<number>(
orderDealer?.lossAmount || 0,
);
useEffect(() => { useEffect(() => {
if (!orderDealer?.lossAmount) { if (!orderDealer?.lossAmount) {
const defaultValue = calculator.getDefaultLossAmount(); const defaultValue = calculator.getDefaultLossAmount();
setTempLossAmount(defaultValue);
// 更新父组件的状态 // 更新父组件的状态
onChange?.({ onChange?.({
...purchaseOrderVO, ...purchaseOrderVO,
@ -39,31 +30,6 @@ export default function ProductionLossSection(props: {
} }
}, []); }, []);
// 设置税费补贴默认值
const setDefaultLossAmount = () => {
const defaultValue = calculator.getDefaultLossAmount();
setTempLossAmount(defaultValue);
};
// 保存税费补贴
const saveLossAmount = () => {
// 更新父组件的状态
onChange?.({
...purchaseOrderVO,
orderDealer: {
...orderDealer,
lossAmount: tempLossAmount,
},
});
setVisible(false);
};
// 关闭弹窗时恢复临时值为显示值
const closePopup = () => {
setVisible(false);
};
return ( return (
<View className={"flex flex-col gap-2.5"}> <View className={"flex flex-col gap-2.5"}>
{/* 卡片形式展示返点信息 */} {/* 卡片形式展示返点信息 */}
@ -88,74 +54,10 @@ export default function ProductionLossSection(props: {
readOnly={readOnly} readOnly={readOnly}
label="损耗金额" label="损耗金额"
unit="元" unit="元"
hint="" hint="点击金额可直接编辑"
/> />
<View className="flex flex-1 flex-col gap-2 pl-4">
<View className="flex items-center justify-between">
<Text className="text-sm text-gray-500"></Text>
</View>
<View className="flex items-center justify-between">
<Text className="text-primary text-2xl font-bold text-nowrap">
</Text>
</View>
</View>
</View> </View>
</View> </View>
{/* 产地损耗弹窗 */}
<Popup
duration={150}
style={{
minHeight: "auto",
}}
visible={visible}
className={"flex flex-col"}
position="bottom"
title={"编辑产地损耗金额"}
onClose={closePopup}
onOverlayClick={closePopup}
lockScroll
>
<View className={"flex flex-col gap-3 p-2.5"}>
<View className={"text-neutral-darkest text-sm font-medium"}>
</View>
<View
className={
"border-neutral-base flex flex-row items-center rounded-md border border-solid"
}
>
<Button size="small" onClick={setDefaultLossAmount}>
</Button>
<View className="ml-2 self-center text-xs text-gray-500">
: {orderDealer.lossAmount}
</View>
</View>
</View>
<View className={"flex w-full flex-col bg-white"}>
<View className={"flex flex-row gap-2 p-3"}>
<View className={"flex-1"}>
<Button size={"large"} block type="default" onClick={closePopup}>
</Button>
</View>
<View className={"flex-1"}>
<Button
size={"large"}
block
type="primary"
onClick={saveLossAmount}
>
</Button>
</View>
</View>
<SafeArea position={"bottom"} />
</View>
</Popup>
</View> </View>
); );
} }

View File

@ -149,6 +149,7 @@ export default function PurchaseCostInfoSection(props: {
return ( return (
<> <>
<Table <Table
className={"table-sum"}
columns={[ columns={[
{ {
title: "项目", title: "项目",
@ -279,6 +280,23 @@ export default function PurchaseCostInfoSection(props: {
}, 0), }, 0),
), ),
}, },
{
project: "定金(元)",
icon: "money-bill",
...(purchaseOrderVO?.orderSupplierList.length > 0
? purchaseOrderVO?.orderSupplierList.reduce((acc, item) => {
acc[item.orderSupplierId] = formatCurrency(
Number(item.depositAmount),
);
return acc;
}, {})
: {}),
total: formatCurrency(
purchaseOrderVO?.orderSupplierList.reduce((sum, supplier) => {
return sum + (supplier.depositAmount || 0);
}, 0),
),
},
{ {
project: "合计(元)", project: "合计(元)",
icon: "money-bill", icon: "money-bill",

View File

@ -1,9 +1,8 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { ScrollView, Text, View } from "@tarojs/components"; import { ScrollView, Text, View } from "@tarojs/components";
import { Button, Popup, SafeArea } from "@nutui/nutui-react-taro"; import { Button, Input, Popup, SafeArea } from "@nutui/nutui-react-taro";
import { PurchaseOrderCalculator } from "@/utils"; import { PurchaseOrderCalculator, validatePrice } from "@/utils";
import { business } from "@/services"; import { business } from "@/services";
import { PriceEditor } from "@/components";
export default function RebateCalcSection(props: { export default function RebateCalcSection(props: {
purchaseOrderVO: BusinessAPI.PurchaseOrderVO; purchaseOrderVO: BusinessAPI.PurchaseOrderVO;
@ -112,33 +111,34 @@ export default function RebateCalcSection(props: {
</View> </View>
<View className="flex"> <View className="flex">
<PriceEditor {!readOnly ? (
value={calculateRebateAmount(orderRebate)} <View
onSave={(newValue) => { className="flex flex-1 flex-col"
if (orderRebate.calcMethod === "FIXED_AMOUNT") { onClick={() => setVisible(true)}
const newOrderRebate = { >
...orderRebate, <View className="mb-2 flex items-center justify-between">
amount: newValue, <Text className="text-sm text-gray-500"></Text>
}; <Text className="text-sm text-gray-500"></Text>
</View>
const newPurchaseOrderVO = { <View className="relative flex flex-1 items-end">
...purchaseOrderVO, <Text className="w-full border-b-2 border-red-500 pb-2 text-3xl font-bold text-red-500 focus:outline-none">
rebate: true, {calculateRebateAmount(orderRebate).toFixed(2) || "0"}
orderRebate: newOrderRebate, </Text>
}; </View>
</View>
setOrderRebate(newOrderRebate); ) : (
onChange?.(newPurchaseOrderVO); <View className="flex flex-1 flex-col">
} else { <View className="mb-2 flex items-center justify-between">
// 对于按净重计算的情况,不能直接修改金额,需要通过单价或净重调整 <Text className="text-sm text-gray-500"></Text>
setVisible(true); <Text className="text-sm text-gray-500"></Text>
} </View>
}} <View className="relative flex flex-1 items-end">
readOnly={readOnly} <Text className="w-full py-2 text-3xl font-bold text-red-500">
label="返点金额" {calculateRebateAmount(orderRebate).toFixed(2) || "0"}
unit="元" </Text>
hint="" </View>
/> </View>
)}
<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">
@ -239,14 +239,33 @@ export default function RebateCalcSection(props: {
/ /
</View> </View>
<View className="border-neutral-base flex flex-row items-center rounded-md border border-solid"> <View className="border-neutral-base flex flex-row items-center rounded-md border border-solid">
<Button <Input
size="large" className="placeholder:text-neutral-dark flex-1"
block placeholder="请输入单价"
type="primary" type="digit"
onClick={handleSave} value={tempOrderRebate.unitPrice?.toString() || ""}
> onChange={(value) => {
const numValue = validatePrice(value);
</Button> if (numValue !== undefined) {
handleRebateChange("unitPrice", numValue);
}
}}
/>
<View className="mr-2">/</View>
</View>
<View className="flex items-center justify-between rounded-md bg-gray-100 p-3">
<View className="text-neutral-darkest text-sm font-medium">
</View>
<View className="text-neutral-darkest text-sm font-medium">
{tempOrderRebate.netWeight && tempOrderRebate.unitPrice
? (
tempOrderRebate.netWeight * tempOrderRebate.unitPrice
).toFixed(2)
: "0.00"}{" "}
</View>
</View> </View>
</> </>
)} )}
@ -255,15 +274,21 @@ export default function RebateCalcSection(props: {
<View className="text-neutral-darkest text-sm font-medium"> <View className="text-neutral-darkest text-sm font-medium">
</View> </View>
<View className="border-neutral-base flex flex-row items-center rounded-md border border-solid"> <View className="border-neutral-base flex flex-row items-center rounded-md border border-solid">
<Button <Input
size="large" className="placeholder:text-neutral-dark flex-1"
block placeholder="请输入返点金额"
type="primary" type="digit"
onClick={handleSave} value={tempOrderRebate.amount?.toString() || ""}
> onChange={(value) => {
const numValue = validatePrice(value);
</Button> if (numValue !== undefined) {
handleRebateChange("amount", numValue);
}
}}
/>
<View className="mr-2"></View>
</View> </View>
</> </>
)} )}
@ -281,6 +306,11 @@ export default function RebateCalcSection(props: {
</Button> </Button>
</View> </View>
<View className="flex-1">
<Button size="large" block type="primary" onClick={handleSave}>
</Button>
</View>
</View> </View>
</View> </View>
</ScrollView> </ScrollView>

View File

@ -1,6 +1,4 @@
import { useEffect, useState } from "react";
import { Text, View } from "@tarojs/components"; import { Text, View } from "@tarojs/components";
import { Button, Popup, SafeArea } from "@nutui/nutui-react-taro";
import { PurchaseOrderCalculator } from "@/utils"; import { PurchaseOrderCalculator } from "@/utils";
import { PriceEditor } from "@/components"; import { PriceEditor } from "@/components";
@ -13,56 +11,6 @@ export default function TaxProvisionSection(props: {
const { purchaseOrderVO, onChange, readOnly, calculator } = props; const { purchaseOrderVO, onChange, readOnly, calculator } = props;
const orderDealer = purchaseOrderVO.orderDealer; const orderDealer = purchaseOrderVO.orderDealer;
const [visible, setVisible] = useState(false);
const displayTaxProvision =
orderDealer.taxProvision || calculator.getDefaultTaxProvision();
// 临时状态 - 用于弹窗内编辑的值
const [tempTaxProvision, setTempTaxProvision] = useState<number>(
orderDealer?.taxProvision || 0,
);
useEffect(() => {
if (!orderDealer?.taxProvision) {
const defaultValue = calculator.getDefaultTaxProvision();
setTempTaxProvision(defaultValue);
// 更新父组件的状态
onChange?.({
...purchaseOrderVO,
orderDealer: {
...orderDealer,
taxProvision: defaultValue,
},
});
}
}, []);
// 设置计提税金默认值
const setDefaultTaxProvision = () => {
const defaultValue = calculator.getDefaultTaxProvision();
setTempTaxProvision(defaultValue);
};
// 保存计提税金
const saveTaxProvision = () => {
// 更新父组件的状态
onChange?.({
...purchaseOrderVO,
orderDealer: {
...orderDealer,
taxProvision: tempTaxProvision,
},
});
setVisible(false);
};
// 关闭弹窗时恢复临时值为显示值
const closePopup = () => {
setVisible(false);
};
return ( return (
<View className={"flex flex-col gap-2.5"}> <View className={"flex flex-col gap-2.5"}>
{/* 卡片形式展示计提税金信息 */} {/* 卡片形式展示计提税金信息 */}
@ -73,7 +21,12 @@ export default function TaxProvisionSection(props: {
<View className="flex"> <View className="flex">
<PriceEditor <PriceEditor
value={displayTaxProvision} value={
orderDealer.taxProvision === null ||
orderDealer.taxProvision === undefined
? calculator.getDefaultTaxProvision()
: orderDealer.taxProvision
}
onSave={(newValue) => { onSave={(newValue) => {
// 更新父组件的状态 // 更新父组件的状态
onChange?.({ onChange?.({
@ -87,7 +40,7 @@ export default function TaxProvisionSection(props: {
readOnly={readOnly} readOnly={readOnly}
label="计提金额" label="计提金额"
unit="元" unit="元"
hint="" hint="点击金额可直接编辑"
/> />
<View className="flex flex-1 flex-col gap-2 pl-4"> <View className="flex flex-1 flex-col gap-2 pl-4">
@ -102,60 +55,6 @@ export default function TaxProvisionSection(props: {
</View> </View>
</View> </View>
</View> </View>
{/* 计提税金弹窗 */}
<Popup
duration={150}
style={{
minHeight: "auto",
}}
visible={visible}
className={"flex flex-col"}
position="bottom"
title={"编辑计提税金"}
onClose={closePopup}
onOverlayClick={closePopup}
lockScroll
>
<View className={"flex flex-col gap-3 p-2.5"}>
<View className={"text-neutral-darkest text-sm font-medium"}>
</View>
<View
className={
"border-neutral-base flex flex-row items-center rounded-md border border-solid"
}
>
<Button size="small" onClick={setDefaultTaxProvision}>
</Button>
<View className="ml-2 self-center text-xs text-gray-500">
: ( - ) ×{" "}
{orderDealer.accrualTaxRatio || "未设置"}%
</View>
</View>
</View>
<View className={"flex w-full flex-col bg-white"}>
<View className={"flex flex-row gap-2 p-3"}>
<View className={"flex-1"}>
<Button size={"large"} block type="default" onClick={closePopup}>
</Button>
</View>
<View className={"flex-1"}>
<Button
size={"large"}
block
type="primary"
onClick={saveTaxProvision}
>
</Button>
</View>
</View>
<SafeArea position={"bottom"} />
</View>
</Popup>
</View> </View>
); );
} }

View File

@ -1,6 +1,4 @@
import { useEffect, useState } from "react";
import { Text, View } from "@tarojs/components"; import { Text, View } from "@tarojs/components";
import { Button, Popup, SafeArea } from "@nutui/nutui-react-taro";
import { PurchaseOrderCalculator } from "@/utils"; import { PurchaseOrderCalculator } from "@/utils";
import { PriceEditor } from "@/components"; import { PriceEditor } from "@/components";
@ -13,57 +11,6 @@ export default function TaxSubsidySection(props: {
const { purchaseOrderVO, onChange, readOnly, calculator } = props; const { purchaseOrderVO, onChange, readOnly, calculator } = props;
const orderDealer = purchaseOrderVO.orderDealer; const orderDealer = purchaseOrderVO.orderDealer;
const [visible, setVisible] = useState(false);
// 主状态 - 用于显示在页面上的值
const displayTaxSubsidy =
orderDealer?.taxSubsidy || calculator.getDefaultTaxSubsidy();
// 临时状态 - 用于弹窗内编辑的值
const [tempTaxSubsidy, setTempTaxSubsidy] = useState<number>(
orderDealer?.taxSubsidy || 0,
);
useEffect(() => {
if (!orderDealer?.taxSubsidy) {
const defaultValue = calculator.getDefaultTaxSubsidy();
setTempTaxSubsidy(defaultValue);
// 更新父组件的状态
onChange?.({
...purchaseOrderVO,
orderDealer: {
...orderDealer,
taxSubsidy: defaultValue,
},
});
}
}, []);
// 设置税费补贴默认值
const setDefaultTaxSubsidy = () => {
const defaultValue = calculator.getDefaultTaxSubsidy();
setTempTaxSubsidy(defaultValue);
};
// 保存税费补贴
const saveTaxSubsidy = () => {
// 更新父组件的状态
onChange?.({
...purchaseOrderVO,
orderDealer: {
...orderDealer,
taxSubsidy: tempTaxSubsidy,
},
});
setVisible(false);
};
// 关闭弹窗时恢复临时值为显示值
const closePopup = () => {
setVisible(false);
};
return ( return (
<View className={"flex flex-col gap-2.5"}> <View className={"flex flex-col gap-2.5"}>
{/* 卡片形式展示返点信息 */} {/* 卡片形式展示返点信息 */}
@ -74,7 +21,12 @@ export default function TaxSubsidySection(props: {
<View className="flex"> <View className="flex">
<PriceEditor <PriceEditor
value={displayTaxSubsidy} value={
orderDealer.taxSubsidy === null ||
orderDealer.taxSubsidy === undefined
? calculator.getDefaultTaxSubsidy()
: orderDealer.taxSubsidy
}
onSave={(newValue) => { onSave={(newValue) => {
// 更新父组件的状态 // 更新父组件的状态
onChange?.({ onChange?.({
@ -88,7 +40,7 @@ export default function TaxSubsidySection(props: {
readOnly={readOnly} readOnly={readOnly}
label="返点金额" label="返点金额"
unit="元" unit="元"
hint="" hint="点击金额可直接编辑"
/> />
<View className="flex flex-1 flex-col gap-2 pl-4"> <View className="flex flex-1 flex-col gap-2 pl-4">
@ -103,59 +55,6 @@ export default function TaxSubsidySection(props: {
</View> </View>
</View> </View>
</View> </View>
{/* 税费补贴弹窗 */}
<Popup
duration={150}
style={{
minHeight: "auto",
}}
visible={visible}
className={"flex flex-col"}
position="bottom"
title={"编辑税费补贴"}
onClose={closePopup}
onOverlayClick={closePopup}
lockScroll
>
<View className={"flex flex-col gap-3 p-2.5"}>
<View className={"text-neutral-darkest text-sm font-medium"}>
</View>
<View
className={
"border-neutral-base flex flex-row items-center rounded-md border border-solid"
}
>
<Button size="small" onClick={setDefaultTaxSubsidy}>
</Button>
<View className="ml-2 self-center text-xs text-gray-500">
默认值: 市场报价 × {orderDealer.companyRebateRatio || "未设置"}%
</View>
</View>
</View>
<View className={"flex w-full flex-col bg-white"}>
<View className={"flex flex-row gap-2 p-3"}>
<View className={"flex-1"}>
<Button size={"large"} block type="default" onClick={closePopup}>
</Button>
</View>
<View className={"flex-1"}>
<Button
size={"large"}
block
type="primary"
onClick={saveTaxSubsidy}
>
</Button>
</View>
</View>
<SafeArea position={"bottom"} />
</View>
</Popup>
</View> </View>
); );
} }

View File

@ -1,2 +1,2 @@
// App 相关常量 // App 相关常量
export const APP_VERSION = "v0.0.26"; export const APP_VERSION = "v0.0.28";

View File

@ -7,8 +7,11 @@ import shipOrder from "@/constant/shipOrder";
import Taro from "@tarojs/taro"; import Taro from "@tarojs/taro";
import classNames from "classnames"; import classNames from "classnames";
import { business, poster } from "@/services"; import { business, poster } from "@/services";
import dayjs from "dayjs"; import {
import { buildUrl, PdfTemplate } from "@/utils"; buildUrl,
convertShipOrderVOToExamplesFormat,
PdfTemplate,
} from "@/utils";
import { import {
DeliveryStep1Form, DeliveryStep1Form,
DeliveryStep1FormRef, DeliveryStep1FormRef,
@ -29,7 +32,7 @@ const updateOtherFeesModule = async (
}, },
}); });
const costItems = const costItems =
data?.filter((item) => item.costType !== "HUMAN_COST") || []; data?.filter((item) => item.type !== "ARTIFICIAL_TYPE") || [];
console.log("module", module); console.log("module", module);
@ -38,7 +41,7 @@ const updateOtherFeesModule = async (
config: { config: {
...module.config, ...module.config,
feeItems: shipOrderVO.orderCostList?.filter( feeItems: shipOrderVO.orderCostList?.filter(
(item) => item.costType !== "HUMAN_COST", (item) => item.type !== "ARTIFICIAL_TYPE",
), ),
feeLabels: costItems.reduce((acc: any, item: any) => { feeLabels: costItems.reduce((acc: any, item: any) => {
acc[item.itemId] = item.name; acc[item.itemId] = item.name;
@ -134,76 +137,6 @@ export default hocAuth(function Page(props: CommonComponent) {
} }
}, [shipOrderVO, deliveryTemplate]); }, [shipOrderVO, deliveryTemplate]);
// 将 shipOrderVO 转换为 examples 的数据格式
const convertShipOrderVOToExamplesFormat = (
shipOrderVO: BusinessAPI.ShipOrderVO,
) => {
return {
title: {
title: "西瓜发货清单",
},
dealerInfo: {
dealerName: shipOrderVO.dealerName || "",
vehicleNumber: `${shipOrderVO.vehicleNo || ""}`,
destination: shipOrderVO.receivingAddress || "",
watermelonGrade: shipOrderVO.watermelonGrade || "",
},
shippingInfo: {
shippingFrom: shipOrderVO.shippingAddress || "",
date: shipOrderVO.shippingDate || "",
},
weightInfo: {
data: shipOrderVO.shipOrderItemList,
accountCompany: shipOrderVO.companyName || "",
sumAmount:
shipOrderVO.shipOrderItemList?.reduce(
(acc, item) => acc + item?.totalAmount!,
0,
) || "",
},
packingSpec: {
data:
shipOrderVO.shipOrderPackageList?.map((item) => ({
boxSpecId: item.boxSpecId || "",
boxSpecName: item.boxSpecName || "",
boxType: item.boxProduct || "",
quantity: item.quantity?.toString() || "",
unitPrice: item.unitPrice?.toString() || "",
amount: item.itemAmount?.toString() || "",
unitWeight: item.singleWeight?.toString() || "",
weight: item.totalWeight?.toString() || "",
})) || [],
},
vehicleInfo: {
driverPhone: shipOrderVO?.driverPhone || "",
licensePlate: shipOrderVO.licensePlate || "",
estimatedArrivalTime: shipOrderVO.estimatedArrivalDate
? dayjs(shipOrderVO?.estimatedArrivalDate).format("YYYY年MM月DD日")
: "",
freightDebt: shipOrderVO.freightDebt?.toString() || "",
strawMatDebt: shipOrderVO.strawMatDebt?.toString() || "",
remarks: shipOrderVO.remark || "",
},
otherFees: {},
totalAmount: {
amount: shipOrderVO.totalAmount?.toString() || "",
farmer: shipOrderVO.farmerInfo || "",
},
otherInfo: {
data: shipOrderVO.shipOrderItemList,
driverPhone: shipOrderVO?.driverPhone || "",
licensePlate: shipOrderVO.licensePlate || "",
accountCompany: shipOrderVO.companyName || "",
vehicleNumber: `${shipOrderVO.vehicleNo || ""}`,
destination: shipOrderVO.receivingAddress || "",
shippingFrom: shipOrderVO.shippingAddress || "",
estimatedArrivalTime: shipOrderVO.estimatedArrivalDate
? dayjs(shipOrderVO?.estimatedArrivalDate).format("YYYY年MM月DD日")
: "1",
},
};
};
// 更新模板配置 // 更新模板配置
const updateTemplateConfig = async ( const updateTemplateConfig = async (
template: any[], template: any[],
@ -424,12 +357,14 @@ export default hocAuth(function Page(props: CommonComponent) {
}} }}
> >
{step === 1 && ( {step === 1 && (
<DeliveryStep1Form <View className="flex flex-col rounded-lg bg-white p-2.5 shadow-md">
ref={step1FormRef} <DeliveryStep1Form
moduleList={moduleList} ref={step1FormRef}
shipOrderVO={shipOrderVO} moduleList={moduleList}
setShipOrderVO={setShipOrderVO} shipOrderVO={shipOrderVO}
/> setShipOrderVO={setShipOrderVO}
/>
</View>
)} )}
{step === 2 && <DeliveryStep2Preview moduleList={moduleList} />} {step === 2 && <DeliveryStep2Preview moduleList={moduleList} />}

View File

@ -27,14 +27,17 @@ import {
PackageInfoSection, PackageInfoSection,
PackagingCostSection, PackagingCostSection,
ProductionAdvanceSection, ProductionAdvanceSection,
PurchaseCostInfoSection,
RebateCalcSection, RebateCalcSection,
State, State,
TaxProvisionSection, TaxProvisionSection,
TaxSubsidySection, TaxSubsidySection,
WorkerAdvanceSection, WorkerAdvanceSection,
} from "@/components"; } from "@/components";
import { buildUrl, PurchaseOrderCalculator } from "@/utils"; import { buildUrl, formatCurrency, PurchaseOrderCalculator } from "@/utils";
import ProductionLossSection from "../../../components/purchase/section/ProductionLossSection"; import ProductionLossSection from "../../../components/purchase/section/ProductionLossSection";
import DeliveryFormSection from "../../../components/purchase/section/DeliveryFormSection";
import classNames from "classnames";
const sections = { const sections = {
// 市场报价 // 市场报价
@ -62,11 +65,11 @@ const sections = {
// component: SupplierInfoSection, // component: SupplierInfoSection,
// title: "瓜农信息", // title: "瓜农信息",
// }, // },
// // 采购成本 // 采购成本
// purchaseCostInfo: { purchaseCostInfo: {
// component: PurchaseCostInfoSection, component: PurchaseCostInfoSection,
// title: "采购成本", title: "采购成本复核",
// }, },
// 包装纸箱费 // 包装纸箱费
packageInfo: { packageInfo: {
component: PackageInfoSection, component: PackageInfoSection,
@ -127,6 +130,11 @@ const sections = {
component: RebateCalcSection, component: RebateCalcSection,
title: "个人返点复核", title: "个人返点复核",
}, },
// 发货单复核
deliveryForm: {
component: DeliveryFormSection,
title: "发货单复核",
},
}; };
export default hocAuth(function Page(props: CommonComponent) { export default hocAuth(function Page(props: CommonComponent) {
@ -528,19 +536,14 @@ export default hocAuth(function Page(props: CommonComponent) {
}} }}
> >
<View className={"flex-1 text-center"}></View> <View className={"flex-1 text-center"}></View>
{personalProfit > 0 ? ( <View
<View className={classNames("flex-1 text-left text-2xl font-bold", {
className={"text-primary flex-1 text-left text-2xl font-bold"} "text-primary": personalProfit > 0,
> "text-red-500": personalProfit < 0,
{personalProfit || "-"} })}
</View> >
) : ( {formatCurrency(personalProfit) || "-"}
<View </View>
className={"flex-1 text-left text-2xl font-bold text-red-500"}
>
{personalProfit || "-"}
</View>
)}
</View> </View>
</View> </View>
</View> </View>

View File

@ -422,19 +422,24 @@ export default hocAuth(function Page(props: CommonComponent) {
purchaseOrder?.orderSupplierList[ purchaseOrder?.orderSupplierList[
purchaseOrder?.orderSupplierList.length - 1 purchaseOrder?.orderSupplierList.length - 1
]?.isLast && ( ]?.isLast && (
<Button <View className="flex flex-1 flex-col gap-2.5 bg-[#D1D5DB] p-2.5 pt-2.5">
icon={<Icon name={"plus"} size={20} />} <View className={"flex flex-1 flex-col gap-2.5"}>
type={"primary"} <View className="text-sm font-bold"></View>
size={"xlarge"} <Button
fill={"outline"} icon={<Icon name={"plus"} size={20} />}
block type={"primary"}
className="border-primary text-primary flex w-full items-center justify-center !border-4 !bg-white" size={"xlarge"}
onClick={() => { fill={"outline"}
orderOptionRef.current?.onAdd(); block
}} className="border-primary text-primary flex w-full items-center justify-center !border-4 !bg-white"
> onClick={() => {
<View></View> orderOptionRef.current?.onAdd();
</Button> }}
>
<View></View>
</Button>
</View>
</View>
)} )}
</View> </View>

View File

@ -3924,6 +3924,8 @@ declare namespace BusinessAPI {
orderPackageList: OrderPackage[]; orderPackageList: OrderPackage[];
/** 采购订单项目信息 */ /** 采购订单项目信息 */
orderCostItemList: OrderCostItem[]; orderCostItemList: OrderCostItem[];
/** 发货单信息 */
shipOrderVOList: ShipOrderVO[];
}; };
type PurchaseOrderWithdrawReviewCmd = { type PurchaseOrderWithdrawReviewCmd = {
@ -4264,7 +4266,7 @@ declare namespace BusinessAPI {
/** 发货单ID */ /** 发货单ID */
shipOrderId: string; shipOrderId: string;
/** 箱型ID */ /** 箱型ID */
boxSpecId?: number; boxSpecId?: string;
/** 箱型 */ /** 箱型 */
boxSpecName?: string; boxSpecName?: string;
/** 箱号 */ /** 箱号 */

View File

@ -9,3 +9,6 @@ export {
convertBoxBrandToOrderPackages, convertBoxBrandToOrderPackages,
convertOrderPackagesToBoxBrands convertOrderPackagesToBoxBrands
} from './boxBrandConverter' } from './boxBrandConverter'
export { convertPurchaseOrderToShipOrder } from './purchaseOrderConverter'
export { convertShipOrderVOToExamplesFormat } from './shipOrderConverter'

View File

@ -0,0 +1,139 @@
// 将PurchaseOrderVO转换为ShipOrderVO
import { generateShortId } from "@/utils";
/**
* PurchaseOrderVO转换为ShipOrderVO
* @param purchaseOrderVO
* @returns
*/
export const convertPurchaseOrderToShipOrder = (
purchaseOrderVO: BusinessAPI.PurchaseOrderVO
): BusinessAPI.ShipOrderVO => {
// 添加一个辅助函数用于分组
const groupBy = <T>(
array: T[],
keyFn: (item: T) => string,
): Record<string, T[]> => {
return array.reduce(
(groups, item) => {
const key = keyFn(item);
if (!groups[key]) {
groups[key] = [];
}
groups[key].push(item);
return groups;
},
{} as Record<string, T[]>,
);
};
// 转换供应商列表为发货单项列表,根据 purchasePrice 分组
const suppliersByPrice = groupBy(
purchaseOrderVO.orderSupplierList || [],
(supplier) => String(supplier.purchasePrice)
);
const shipOrderItemList: BusinessAPI.ShipOrderItem[] = Object.entries(suppliersByPrice).map(
([price, suppliers]) => {
// 计算该价格下的总毛重、总净重等
const totalGrossWeight = suppliers.reduce(
(sum, supplier) => sum + (supplier.grossWeight || 0), 0
);
const totalNetWeight = suppliers.reduce(
(sum, supplier) => sum + (supplier.netWeight || 0), 0
);
const totalBoxWeight = suppliers.reduce(
(sum, supplier) => {
if (supplier.totalWeight && supplier.netWeight) {
return sum + (supplier.totalWeight - supplier.netWeight);
}
return sum;
}, 0
);
const totalAmount = suppliers.reduce(
(sum, supplier) => sum + (supplier.invoiceAmount || 0), 0
);
return {
itemId: generateShortId(),
shipOrderId: "", // 将在创建发货单时填充
grossWeight: totalGrossWeight,
boxWeight: totalBoxWeight,
netWeight: totalNetWeight,
unitPrice: parseFloat(price),
totalAmount: totalAmount,
watermelonGrade: "", // 需要手动填写
};
}
);
// 转换包装信息,根据 boxSpecId 分组
const allPackages: BusinessAPI.OrderPackage[] = [];
purchaseOrderVO.orderSupplierList?.forEach(supplier => {
if (supplier.orderPackageList) {
allPackages.push(...supplier.orderPackageList);
}
});
const packagesBySpec = groupBy(
allPackages,
(pkg) => pkg.boxSpecId
);
const shipOrderPackageList: BusinessAPI.ShipOrderPackage[] = Object.entries(packagesBySpec).map(
([specId, packages]) => {
// 计算该规格下的总数量、总重量等
const totalQuantity = packages.reduce(
(sum, pkg) => sum + (pkg.boxCount || 0), 0
);
const totalWeight = packages.reduce(
(sum, pkg) => sum + ((pkg.boxCount || 0) * (pkg.boxProductWeight || 0)), 0
);
// 获取第一条记录的价格信息(假设同一规格价格一致)
const firstPackage = packages[0];
const totalPrice = totalQuantity * (firstPackage.boxSalePrice || 0);
return {
orderPackageId: generateShortId(),
shipOrderId: "", // 将在创建发货单时填充
boxSpecId: specId,
boxSpecName: firstPackage.boxSpecName,
boxProduct: firstPackage.boxProductName,
quantity: totalQuantity,
unitPrice: firstPackage.boxSalePrice,
itemAmount: totalPrice,
singleWeight: firstPackage.boxProductWeight,
totalWeight: totalWeight,
boxBrandImage: firstPackage.boxBrandImage,
};
}
);
// 构建ShipOrderVO对象不转换费用信息
return {
shipOrderId: generateShortId(),
purchaseOrderId: purchaseOrderVO.orderId,
orderSn: purchaseOrderVO.orderSn,
dealerId: purchaseOrderVO.orderDealer?.dealerId,
dealerName: purchaseOrderVO.orderDealer?.shortName,
companyId: purchaseOrderVO.orderCompany?.companyId,
companyName: purchaseOrderVO.orderCompany?.fullName,
vehicleNo: purchaseOrderVO.orderVehicle?.vehicleNo,
shippingAddress: purchaseOrderVO.orderVehicle?.origin,
receivingAddress: purchaseOrderVO.orderVehicle?.destination,
shippingDate: purchaseOrderVO.orderVehicle?.deliveryTime,
driverName: purchaseOrderVO.orderVehicle?.driver,
driverPhone: purchaseOrderVO.orderVehicle?.phone,
licensePlate: purchaseOrderVO.orderVehicle?.plate,
shipOrderItemList,
shipOrderPackageList,
createdBy: purchaseOrderVO.createdBy,
createdByName: purchaseOrderVO.createdByName,
};
};

View File

@ -0,0 +1,75 @@
import dayjs from "dayjs";
/**
* ShipOrderVO转换为示例数据格式
* @param shipOrderVO
* @returns
*/
export const convertShipOrderVOToExamplesFormat = (
shipOrderVO: BusinessAPI.ShipOrderVO,
) => {
return {
title: {
title: "西瓜发货清单",
},
dealerInfo: {
dealerName: shipOrderVO.dealerName || "",
vehicleNumber: `${shipOrderVO.vehicleNo || ""}`,
destination: shipOrderVO.receivingAddress || "",
watermelonGrade: shipOrderVO.watermelonGrade || "",
},
shippingInfo: {
shippingFrom: shipOrderVO.shippingAddress || "",
date: shipOrderVO.shippingDate || "",
},
weightInfo: {
data: shipOrderVO.shipOrderItemList,
accountCompany: shipOrderVO.companyName || "",
sumAmount:
shipOrderVO.shipOrderItemList?.reduce(
(acc, item) => acc + item?.totalAmount!,
0,
) || "",
},
packingSpec: {
data:
shipOrderVO.shipOrderPackageList?.map((item) => ({
boxSpecId: item.boxSpecId || "",
boxSpecName: item.boxSpecName || "",
boxType: item.boxProduct || "",
quantity: item.quantity?.toString() || "",
unitPrice: item.unitPrice?.toString() || "",
amount: item.itemAmount?.toString() || "",
unitWeight: item.singleWeight?.toString() || "",
weight: item.totalWeight?.toString() || "",
})) || [],
},
vehicleInfo: {
driverPhone: shipOrderVO?.driverPhone || "",
licensePlate: shipOrderVO.licensePlate || "",
estimatedArrivalTime: shipOrderVO.estimatedArrivalDate
? dayjs(shipOrderVO?.estimatedArrivalDate).format("YYYY年MM月DD日")
: "",
freightDebt: shipOrderVO.freightDebt?.toString() || "",
strawMatDebt: shipOrderVO.strawMatDebt?.toString() || "",
remarks: shipOrderVO.remark || "",
},
otherFees: {},
totalAmount: {
amount: shipOrderVO.totalAmount?.toString() || "",
farmer: shipOrderVO.farmerInfo || "",
},
otherInfo: {
data: shipOrderVO.shipOrderItemList,
driverPhone: shipOrderVO?.driverPhone || "",
licensePlate: shipOrderVO.licensePlate || "",
accountCompany: shipOrderVO.companyName || "",
vehicleNumber: `${shipOrderVO.vehicleNo || ""}`,
destination: shipOrderVO.receivingAddress || "",
shippingFrom: shipOrderVO.shippingAddress || "",
estimatedArrivalTime: shipOrderVO.estimatedArrivalDate
? dayjs(shipOrderVO?.estimatedArrivalDate).format("YYYY年MM月DD日")
: "1",
},
};
};

View File

@ -21,7 +21,7 @@ export function formatAmount(value: number): string {
} }
// 固定保留2位小数 // 固定保留2位小数
const fixedStr = value.toFixed(2); const fixedStr = Number(value).toFixed(2);
// 去除小数点后多余的零 // 去除小数点后多余的零
// 具体规则: // 具体规则: