refactor(order): 优化订单处理逻辑和界面显示
- 修复经销商信息显示逻辑,正确处理名称和瓜品等级的显示 - 移除PDF自动保存功能,仅保留下载成功提示 - 更新按钮样式为外框样式,改善视觉效果 - 优化箱重计算精度,添加小数位处理 - 移除货币格式化,直接显示数值 - 添加供应商银行名称字段并优化验证逻辑 - 改进供应商信息表单的空值处理 - 优化车次号获取逻辑,支持强制重新获取 - 实现包装信息表格的可编辑功能,支持销售单价修改 - 修复表格渲染逻辑,正确处理合计行显示 - 更新应用版本号至v0.0.61 - 优化PDF模板中经销商和目的地信息的显示逻辑 - 修复订单转换器中的价格计算逻辑 - 调整发货单转换器中的包装分组逻辑
This commit is contained in:
parent
438e17f093
commit
4b9d0002d7
@ -65,25 +65,11 @@ export default function Step3Success(props: Step3SuccessProps) {
|
||||
|
||||
if (downloadRes.tempFilePath) {
|
||||
setTempFilePath(downloadRes.tempFilePath);
|
||||
|
||||
// 保存PDF到手机
|
||||
if (Taro.saveFile) {
|
||||
await Taro.saveFile({
|
||||
tempFilePath: downloadRes.tempFilePath,
|
||||
});
|
||||
|
||||
Taro.showToast({
|
||||
title: "PDF下载成功",
|
||||
icon: "success",
|
||||
duration: 2000,
|
||||
});
|
||||
} else {
|
||||
// 如果不支持saveFile,直接提示下载完成
|
||||
Taro.openDocument({
|
||||
filePath: downloadRes.tempFilePath,
|
||||
showMenu: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("下载PDF失败:", error);
|
||||
@ -173,8 +159,9 @@ export default function Step3Success(props: Step3SuccessProps) {
|
||||
{tempFilePath && (
|
||||
<View className="flex-1">
|
||||
<Button
|
||||
icon={<Icon name="eye" size={16} color={"white"} />}
|
||||
type="default"
|
||||
icon={<Icon name="eye" size={16} />}
|
||||
type="primary"
|
||||
fill={"outline"}
|
||||
size={"large"}
|
||||
block
|
||||
onClick={handleViewPDF}
|
||||
|
||||
@ -11,17 +11,17 @@ export default function DealerInfo(props: { module: any }) {
|
||||
<View className="col-span-1"></View>
|
||||
{config.showDealerName || config.showWatermelonGrade ? (
|
||||
<View className="col-span-3 flex items-end justify-center border-b border-black">
|
||||
{config.showWatermelonGrade
|
||||
? `${config.dealerName}-${config.watermelonGrade}`
|
||||
: config.dealerName}
|
||||
{config.showDealerName ? config.dealerName : ""}
|
||||
{config.showDealerName && config.showWatermelonGrade ? "- " : ""}
|
||||
{config.showWatermelonGrade ? config.watermelonGrade : ""}
|
||||
</View>
|
||||
) : (
|
||||
<View className="col-span-3"></View>
|
||||
)}
|
||||
{config.showDestination || config.showVehicleNumber ? (
|
||||
<View className="col-span-3 flex items-end justify-center border-b border-black">
|
||||
{config.destination}
|
||||
{config.vehicleNumber}
|
||||
{config.showDestination ? config.destination : ""}
|
||||
{config.showVehicleNumber ? config.vehicleNumber : ""}
|
||||
</View>
|
||||
) : (
|
||||
<View className="col-span-3"></View>
|
||||
|
||||
@ -544,6 +544,7 @@ export default function MadePreview(props: IPurchasePreviewProps) {
|
||||
sum +
|
||||
new Decimal(item.boxProductWeight || 0)
|
||||
.mul(new Decimal(item.boxCount || 0))
|
||||
.toDecimalPlaces(0)
|
||||
.toNumber(),
|
||||
0,
|
||||
);
|
||||
@ -594,7 +595,7 @@ export default function MadePreview(props: IPurchasePreviewProps) {
|
||||
<View className="text-gray-600">
|
||||
<View className="text-xs">箱重</View>
|
||||
<View className="font-semibold text-gray-900">
|
||||
{formatCurrency(brandBoxWeight)} 斤
|
||||
{brandBoxWeight} 斤
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
@ -647,6 +648,7 @@ export default function MadePreview(props: IPurchasePreviewProps) {
|
||||
sum +
|
||||
new Decimal(item.boxProductWeight || 0)
|
||||
.mul(new Decimal(item.boxCount || 0))
|
||||
.toDecimalPlaces(0)
|
||||
.toNumber(),
|
||||
0,
|
||||
);
|
||||
@ -697,7 +699,7 @@ export default function MadePreview(props: IPurchasePreviewProps) {
|
||||
<View className="text-gray-600">
|
||||
<View className="text-xs">箱重</View>
|
||||
<View className="font-semibold text-gray-900">
|
||||
{formatCurrency(brandBoxWeight)} 斤
|
||||
{brandBoxWeight} 斤
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@ -471,6 +471,7 @@ export default function MarketPreview(props: IMarketPreviewProps) {
|
||||
sum +
|
||||
new Decimal(item.boxProductWeight || 0)
|
||||
.mul(new Decimal(item.boxCount || 0))
|
||||
.toDecimalPlaces(0)
|
||||
.toNumber(),
|
||||
0,
|
||||
);
|
||||
@ -521,7 +522,7 @@ export default function MarketPreview(props: IMarketPreviewProps) {
|
||||
<View className="text-gray-600">
|
||||
<View className="text-xs">箱重</View>
|
||||
<View className="font-semibold text-gray-900">
|
||||
{formatCurrency(brandBoxWeight)} 斤
|
||||
{brandBoxWeight} 斤
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
@ -574,6 +575,7 @@ export default function MarketPreview(props: IMarketPreviewProps) {
|
||||
sum +
|
||||
new Decimal(item.boxProductWeight || 0)
|
||||
.mul(new Decimal(item.boxCount || 0))
|
||||
.toDecimalPlaces(0)
|
||||
.toNumber(),
|
||||
0,
|
||||
);
|
||||
@ -624,7 +626,7 @@ export default function MarketPreview(props: IMarketPreviewProps) {
|
||||
<View className="text-gray-600">
|
||||
<View className="text-xs">箱重</View>
|
||||
<View className="font-semibold text-gray-900">
|
||||
{formatCurrency(brandBoxWeight)} 斤
|
||||
{brandBoxWeight} 斤
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@ -29,6 +29,7 @@ export default forwardRef<SupplierInfoRef, ISupplierInfoProps>(
|
||||
name: "瓜农1",
|
||||
payeeName: "",
|
||||
idCard: "",
|
||||
bankName: "",
|
||||
bankCard: "",
|
||||
phone: "",
|
||||
selected: true,
|
||||
@ -201,7 +202,7 @@ export default forwardRef<SupplierInfoRef, ISupplierInfoProps>(
|
||||
return false;
|
||||
}
|
||||
// 银行名称至少2个字符
|
||||
return bankName.length >= 2;
|
||||
return bankName?.length >= 2;
|
||||
};
|
||||
|
||||
// 校验手机号函数 (使用项目中已有的规则)
|
||||
@ -895,7 +896,7 @@ export default forwardRef<SupplierInfoRef, ISupplierInfoProps>(
|
||||
clearable
|
||||
type="idcard"
|
||||
placeholder="请输入身份证号"
|
||||
value={supplierVO.idCard}
|
||||
value={supplierVO.idCard || ""}
|
||||
onChange={(value) => handleIdCardChange(value, supplierVO)}
|
||||
onBlur={() =>
|
||||
handleIdCardBlur(
|
||||
@ -920,7 +921,7 @@ export default forwardRef<SupplierInfoRef, ISupplierInfoProps>(
|
||||
clearable
|
||||
type="text"
|
||||
placeholder="请输入银行名称"
|
||||
value={supplierVO.bankName}
|
||||
value={supplierVO.bankName || ""}
|
||||
onChange={(value) => handleBankNameChange(value, supplierVO)}
|
||||
onBlur={() =>
|
||||
handleBankNameBlur(
|
||||
@ -933,7 +934,7 @@ export default forwardRef<SupplierInfoRef, ISupplierInfoProps>(
|
||||
</View>
|
||||
{bankNameError[supplierVO.orderSupplierId] && (
|
||||
<View className="text-xs text-red-500">
|
||||
{`银行名称"${supplierVO.bankName}"至少2个字符`}
|
||||
{`银行名称"${supplierVO.bankName || ""}"至少2个字符`}
|
||||
</View>
|
||||
)}
|
||||
<View className="block text-sm font-normal text-[#000000]">
|
||||
@ -952,7 +953,7 @@ export default forwardRef<SupplierInfoRef, ISupplierInfoProps>(
|
||||
clearable
|
||||
type="digit"
|
||||
placeholder="请输入银行卡号"
|
||||
value={supplierVO.bankCard}
|
||||
value={supplierVO.bankCard || ""}
|
||||
onChange={(value) => handleBankCardChange(value, supplierVO)}
|
||||
onBlur={() =>
|
||||
handleBankCardBlur(
|
||||
|
||||
@ -89,7 +89,10 @@ export default function BasicInfoSection(props: {
|
||||
// 获取上一车次号
|
||||
const fetchLastVehicleNo = async (
|
||||
dealerId: BusinessAPI.DealerVO["dealerId"],
|
||||
// 强制重新获取
|
||||
force: boolean = false,
|
||||
) => {
|
||||
if (!force) {
|
||||
// 如果已经有车次号,则不需要获取上一车次号
|
||||
if (orderVehicle?.vehicleNo || !dealerId) {
|
||||
return;
|
||||
@ -99,6 +102,11 @@ export default function BasicInfoSection(props: {
|
||||
if (loadingLastVehicleNo || lastVehicleNo) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (loadingLastVehicleNo || !dealerId) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setLoadingLastVehicleNo(true);
|
||||
try {
|
||||
@ -133,7 +141,7 @@ export default function BasicInfoSection(props: {
|
||||
|
||||
// 组件加载时获取上一车次号
|
||||
useEffect(() => {
|
||||
fetchLastVehicleNo(orderVO.orderDealer.dealerId);
|
||||
fetchLastVehicleNo(orderVO.orderDealer.dealerId, true);
|
||||
}, [orderVO.orderDealer.dealerId]);
|
||||
|
||||
// 打开基础信息弹窗
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import { formatCurrency } from "@/utils";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useMemo, useRef, useState } from "react";
|
||||
import { Table } from "@nutui/nutui-react-taro";
|
||||
import { Icon } from "@/components";
|
||||
import { View } from "@tarojs/components";
|
||||
import { Input, View } from "@tarojs/components";
|
||||
import { Decimal } from "decimal.js";
|
||||
|
||||
export default function PackageInfoSection(props: {
|
||||
@ -10,9 +9,85 @@ export default function PackageInfoSection(props: {
|
||||
onChange?: (orderVO: BusinessAPI.OrderVO) => void;
|
||||
readOnly?: boolean;
|
||||
}) {
|
||||
const { orderVO, readOnly } = props;
|
||||
const { orderVO, readOnly, onChange } = props;
|
||||
|
||||
const defaultColumns = [
|
||||
const inputRefs = useRef<Record<string, any>>({});
|
||||
|
||||
// 管理编辑状态: key 为分组键,值为输入值
|
||||
const [editingStates, setEditingStates] = useState<
|
||||
Record<string, { isEditing: boolean; inputValue: string }>
|
||||
>({});
|
||||
// 开始编辑
|
||||
const startEdit = (key: string, currentValue: number) => {
|
||||
const inputValue = currentValue === 0 ? "" : currentValue.toString();
|
||||
setEditingStates((prev) => ({
|
||||
...prev,
|
||||
[key]: { isEditing: true, inputValue },
|
||||
}));
|
||||
|
||||
// 聚焦输入框
|
||||
setTimeout(() => {
|
||||
if (inputRefs.current[key]) {
|
||||
inputRefs.current[key].focus();
|
||||
}
|
||||
}, 0);
|
||||
};
|
||||
|
||||
// 处理输入变化
|
||||
const handleInputChange = (key: string, e: any) => {
|
||||
const val = e.detail?.value || e.target?.value || "";
|
||||
// 只允许数字和小数点
|
||||
if (/^\d*\.?\d*$/.test(val) || val === "") {
|
||||
setEditingStates((prev) => ({
|
||||
...prev,
|
||||
[key]: { ...prev[key], inputValue: val },
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
// 保存编辑
|
||||
const saveEdit = (key: string) => {
|
||||
const state = editingStates[key];
|
||||
if (!state) return;
|
||||
|
||||
let newValue = 0;
|
||||
if (state.inputValue !== "" && !Number.isNaN(Number(state.inputValue))) {
|
||||
newValue = Number(state.inputValue);
|
||||
}
|
||||
|
||||
// 更新 orderVO 中的数据
|
||||
const updatedOrderVO = { ...orderVO };
|
||||
updatedOrderVO.orderSupplierList?.forEach((supplier) => {
|
||||
supplier.orderPackageList?.forEach((pkg) => {
|
||||
const pkgKey = `${pkg.boxBrandId}-${pkg.boxProductId}-${pkg.boxSpecId}`;
|
||||
if (pkgKey === key) {
|
||||
pkg.boxSalePrice = newValue;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 触发 onChange 回调
|
||||
onChange?.(updatedOrderVO);
|
||||
|
||||
// 退出编辑状态
|
||||
setEditingStates((prev) => {
|
||||
const newState = { ...prev };
|
||||
delete newState[key];
|
||||
return newState;
|
||||
});
|
||||
};
|
||||
|
||||
// 取消编辑
|
||||
const cancelEdit = (key: string) => {
|
||||
setEditingStates((prev) => {
|
||||
const newState = { ...prev };
|
||||
delete newState[key];
|
||||
return newState;
|
||||
});
|
||||
};
|
||||
|
||||
const columns = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
title: "纸箱型号",
|
||||
key: "boxProductName",
|
||||
@ -35,20 +110,56 @@ export default function PackageInfoSection(props: {
|
||||
) => {
|
||||
// 合计行不显示编辑按钮
|
||||
if (value.isTotalRow) {
|
||||
return formatCurrency(value.boxSalePrice as number);
|
||||
return value.boxSalePrice as number;
|
||||
}
|
||||
|
||||
// 生成分组键
|
||||
const key = `${value.boxBrandId}-${value.boxProductId}-${value.boxSpecId}`;
|
||||
const editingState = editingStates[key];
|
||||
const isEditing = editingState?.isEditing || false;
|
||||
|
||||
if (isEditing) {
|
||||
return (
|
||||
<View className="flex items-center">
|
||||
<Input
|
||||
ref={(el: any) => {
|
||||
inputRefs.current[key] = el;
|
||||
}}
|
||||
type="digit"
|
||||
value={editingState.inputValue}
|
||||
onInput={(e) => handleInputChange(key, e)}
|
||||
onBlur={() => saveEdit(key)}
|
||||
className="flex-1 border-b border-blue-500 px-1"
|
||||
style={{ minWidth: "80px" }}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
/>
|
||||
<View
|
||||
className="ml-1 cursor-pointer"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
cancelEdit(key);
|
||||
}}
|
||||
>
|
||||
<Icon name="circle-xmark" size={16} color={"#ef4444"} />
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View
|
||||
className="flex w-full items-center justify-between"
|
||||
className="flex items-center justify-between"
|
||||
onClick={(e) => {
|
||||
if (!readOnly) {
|
||||
e.stopPropagation();
|
||||
startEdit(key, value.boxSalePrice as number);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<View className={!readOnly ? "cursor-pointer underline" : ""}>
|
||||
{formatCurrency(value.boxSalePrice as number)}
|
||||
<View
|
||||
className={!readOnly ? "cursor-pointer underline" : ""}
|
||||
>
|
||||
{value.boxSalePrice as number}
|
||||
</View>
|
||||
{!readOnly && (
|
||||
<View className="-m-2 ml-2 flex items-center justify-center p-2">
|
||||
@ -77,20 +188,14 @@ export default function PackageInfoSection(props: {
|
||||
orderPackageId?: string;
|
||||
isTotalRow?: boolean;
|
||||
},
|
||||
) => formatCurrency(value.boxProductWeight),
|
||||
) => value.boxProductWeight,
|
||||
},
|
||||
{
|
||||
title: "品牌",
|
||||
key: "boxBrandName",
|
||||
},
|
||||
];
|
||||
const [columns, setColumns] = useState<any[]>(defaultColumns);
|
||||
|
||||
useEffect(() => {
|
||||
if (!readOnly) {
|
||||
setColumns([...defaultColumns]);
|
||||
}
|
||||
}, [readOnly]);
|
||||
}, [readOnly, editingStates]);
|
||||
|
||||
// 将所有包装信息合并并按品牌、型号、规格分组
|
||||
const groupedPackageData = orderVO.orderSupplierList?.reduce(
|
||||
@ -99,7 +204,7 @@ export default function PackageInfoSection(props: {
|
||||
?.filter((pkg) => pkg.boxType === "USED")
|
||||
?.forEach((pkg) => {
|
||||
// 生成分组键
|
||||
const key = `${pkg.boxBrandName}-${pkg.boxProductName}-${pkg.boxSpecId}`;
|
||||
const key = `${pkg.boxBrandId}-${pkg.boxProductId}-${pkg.boxSpecId}`;
|
||||
|
||||
// 转换规格字段
|
||||
const boxSpecId = pkg.boxSpecId;
|
||||
@ -208,11 +313,11 @@ export default function PackageInfoSection(props: {
|
||||
if (rowData.isTotalRow) {
|
||||
return (
|
||||
<span style={{ fontWeight: "bold" }}>
|
||||
{formatCurrency(rowData.boxSalePrice as number)}
|
||||
{rowData.boxSalePrice as number}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return column.render(rowData, rowData);
|
||||
return column?.render?.(rowData);
|
||||
},
|
||||
};
|
||||
} else if (column.key === "boxSalePayment") {
|
||||
@ -223,7 +328,7 @@ export default function PackageInfoSection(props: {
|
||||
if (rowData.isTotalRow) {
|
||||
return (
|
||||
<span style={{ fontWeight: "bold" }}>
|
||||
{formatCurrency(rowData.boxSalePayment)}
|
||||
{rowData.boxSalePayment}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@ -241,11 +346,11 @@ export default function PackageInfoSection(props: {
|
||||
if (rowData.isTotalRow) {
|
||||
return (
|
||||
<span style={{ fontWeight: "bold" }}>
|
||||
{formatCurrency(rowData.boxProductWeight)}
|
||||
{rowData.boxProductWeight}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return column.render(rowData, rowData);
|
||||
return column?.render?.(rowData);
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -257,7 +362,7 @@ export default function PackageInfoSection(props: {
|
||||
return (
|
||||
<Table
|
||||
className={"table-sum"}
|
||||
columns={columnsWithTotalsRender}
|
||||
columns={columnsWithTotalsRender as any}
|
||||
data={dataWithTotals}
|
||||
striped
|
||||
/>
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
// App 相关常量
|
||||
export const APP_VERSION = "v0.0.59";
|
||||
export const APP_VERSION = "v0.0.61";
|
||||
|
||||
@ -35,11 +35,9 @@ export class PdfTemplate {
|
||||
if (config.showDealerName || config.showWatermelonGrade) {
|
||||
htmlString += `
|
||||
<div class="col-span-3 flex items-end justify-center border-b border-black">
|
||||
${
|
||||
config.showWatermelonGrade
|
||||
? `${config.dealerName || ""}-${config.watermelonGrade || ""}`
|
||||
: config.dealerName || ""
|
||||
}
|
||||
${config.showDealerName ? config.dealerName : ""}
|
||||
${config.showDealerName && config.showWatermelonGrade ? "- " : ""}
|
||||
${config.showWatermelonGrade ? config.watermelonGrade : ""}
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
@ -49,7 +47,8 @@ export class PdfTemplate {
|
||||
if (config.showDestination || config.showVehicleNumber) {
|
||||
htmlString += `
|
||||
<div class="col-span-3 flex items-end justify-center border-b border-black">
|
||||
${config.destination || ""} ${config.vehicleNumber || ""}
|
||||
${config.showDestination ? config.destination : ""}
|
||||
${config.showVehicleNumber ? config.vehicleNumber : ""}
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
|
||||
@ -33,7 +33,7 @@ export const convertOrderToOrderShip = (
|
||||
// 转换供应商列表为发货单项列表,根据 purchasePrice 分组
|
||||
const suppliersByPrice = groupBy(
|
||||
orderVO.orderSupplierList || [],
|
||||
(supplier) => String(supplier.purchasePrice),
|
||||
(supplier) => String(supplier.salePrice || "0"),
|
||||
);
|
||||
|
||||
const orderShipId = oldOrderShip?.orderShipId || generateShortId();
|
||||
@ -57,10 +57,15 @@ export const convertOrderToOrderShip = (
|
||||
);
|
||||
|
||||
const totalAmount = DecimalUtils.toDecimalPlaces(
|
||||
suppliers.reduce(
|
||||
(sum, supplier) => DecimalUtils.add(sum, supplier.invoiceAmount || 0),
|
||||
0,
|
||||
),
|
||||
suppliers.reduce((sum, supplier) => {
|
||||
const amount = DecimalUtils.multiply(
|
||||
orderVO.pricingMethod === "BY_GROSS_WEIGHT"
|
||||
? supplier.grossWeight
|
||||
: supplier.netWeight,
|
||||
supplier.salePrice || 0,
|
||||
);
|
||||
return DecimalUtils.add(sum, amount || 0);
|
||||
}, 0),
|
||||
);
|
||||
|
||||
const totalBoxCount = DecimalUtils.toDecimalPlaces(
|
||||
@ -89,7 +94,7 @@ export const convertOrderToOrderShip = (
|
||||
grossWeight: totalGrossWeight,
|
||||
boxWeight: totalGrossWeight - totalNetWeight,
|
||||
netWeight: totalNetWeight,
|
||||
unitPrice: parseFloat(price),
|
||||
unitPrice: price ? parseFloat(price) : 0,
|
||||
totalAmount: totalAmount,
|
||||
watermelonGrade: oldOrderShipItem?.watermelonGrade || "", // 需要手动填写
|
||||
};
|
||||
|
||||
@ -42,7 +42,10 @@ export const convertOrderShipVOToExamplesFormat = (
|
||||
}
|
||||
});
|
||||
|
||||
const packagesBySpec = groupBy(allPackages, (pkg) => pkg.boxSpecId);
|
||||
const packagesBySpec = groupBy(
|
||||
allPackages,
|
||||
(pkg) => pkg.boxSpecName + pkg.boxProductName,
|
||||
);
|
||||
|
||||
const orderShipPackageList = Object.entries(packagesBySpec).map(
|
||||
([specId, packages]) => {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user