feat(purchase): 添加采购单审核通过页面及发货单生成功能

- 新增采购单审核通过页面,展示审核通过后的采购单信息
- 实现自动关联并展示发货单信息
- 添加快捷生成发货单据、采购底单和成本单的功能
- 根据经销商配置控制可生成的单据类型
- 完善审核通过后页面的UI布局和交互逻辑
- 优化订单审核完成后跳转逻辑,携带订单ID参数
- 在工作台常量中增加发货单菜单项
- 重构认证高阶组件,将overlay状态管理改为loading状态
- 优化纸箱包装模块的类型标签显示和分隔线样式
- 修复部分条件过滤逻辑和按钮状态判断代码格式问题
This commit is contained in:
shenyifei 2025-11-08 15:11:08 +08:00
parent 4dbba0fa6b
commit e1d4824ad8
11 changed files with 377 additions and 31 deletions

View File

@ -16,7 +16,7 @@ config = {
},
{
root: "pages/purchase",
pages: ["order/create", "order/list", "order/audit", "order/preview"],
pages: ["order/create", "order/list", "order/audit", "order/preview", "order/approved"],
},
{
root: "pages/ship",

View File

@ -37,7 +37,18 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
// 初始化数据
useEffect(() => {
setSupplierVO(value);
setOrderPackageList(value.orderPackageList || []);
const orderPackageList = value.orderPackageList || []
setOrderPackageList(orderPackageList);
if (orderPackageList.length > 0) {
setPackageTypeEnabled({
USED: orderPackageList.some((item) => item.boxType === "USED") ? 1 : 2,
EXTRA_USED: orderPackageList.some((item) => item.boxType === "EXTRA_USED") ? 1 : 2,
EXTRA: orderPackageList.some((item) => item.boxType === "EXTRA") ? 1 : 2,
REMAIN: orderPackageList.some((item) => item.boxType === "REMAIN") ? 1 : 2,
OWN: orderPackageList.some((item) => item.boxType === "OWN") ? 1 : 2,
});
}
}, []);
const [orderPackageList, setOrderPackageList] = useState<
@ -87,11 +98,10 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
}
// 检查所需选项是否都已做出选择不是1就是2不能是0
console.log("requiredTypes", requiredTypes, packageTypeEnabled)
console.log("requiredTypes", requiredTypes, packageTypeEnabled);
const allRequiredAnswered = requiredTypes.every(
(type) =>
packageTypeEnabled[type] === 1 ||
packageTypeEnabled[type] === 2,
packageTypeEnabled[type] === 1 || packageTypeEnabled[type] === 2,
);
// 检查必须回答的问题
@ -711,18 +721,18 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
// 根据类型设置显示标题
const typeLabels = {
USED: "记录装车用的纸箱",
EXTRA_USED: "记录用的额外运输纸箱纸箱",
EXTRA: "记录额外运输来的所有纸箱",
REMAIN: "记录车上没用完的纸箱",
OWN: "记录装车用的瓜农的纸箱",
USED: "装车用的纸箱",
EXTRA_USED: "用的额外运输纸箱纸箱",
EXTRA: "额外运输来的所有纸箱",
REMAIN: "车上没用完的纸箱",
OWN: "装车用的瓜农的纸箱",
};
return (
<View key={type} className="mb-2.5">
<View className="mb-3 flex items-center justify-between">
<View className="text-base font-bold text-gray-800">
{typeLabels[type]}
{typeLabels[type]}
</View>
</View>
@ -748,7 +758,7 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
setShowBatchModal(true);
}}
>
<View></View>
<View>{typeLabels[type]}</View>
</Button>
</View>
);
@ -1121,10 +1131,15 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
// 如果关闭,也需要清除相关数据
if (packageTypeEnabled.OWN === 2) {
setOrderPackageList((list) =>
list.filter((item) => item.boxType !== "USED" && item.boxType !== "EXTRA_USED" && item.boxType !== "EXTRA"),
list.filter(
(item) =>
item.boxType !== "USED" &&
item.boxType !== "EXTRA_USED" &&
item.boxType !== "EXTRA",
),
);
togglePackageType("EXTRA_USED", 0)
togglePackageType("EXTRA", 0)
togglePackageType("EXTRA_USED", 0);
togglePackageType("EXTRA", 0);
}
}}
>
@ -1158,6 +1173,9 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
<>
{renderPackageByType("OWN")}
{/* 分隔线 */}
<View className="my-4 h-px bg-gray-200" />
{/* 空车带来的纸箱用完了吗? */}
<View className="mb-2.5 flex items-center justify-between">
<View className="text-sm"></View>
@ -1205,6 +1223,9 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
<>
{renderPackageByType("USED")}
{/* 分隔线 */}
<View className="my-4 h-px bg-gray-200" />
{/* 空车带来的纸箱用完了吗? */}
<View className="mb-2.5 flex items-center justify-between">
<View className="text-sm"></View>
@ -1245,6 +1266,9 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
{packageTypeEnabled.REMAIN === 1 &&
renderPackageByType("REMAIN")}
{/* 分隔线 */}
<View className="my-4 h-px bg-gray-200" />
{/* 用额外运输纸箱的了吗? */}
<View className="mb-2.5 flex items-center justify-between">
<View className="text-sm"></View>
@ -1272,7 +1296,9 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
// 如果关闭,也需要清除相关数据
if (packageTypeEnabled.EXTRA_USED === 1) {
setOrderPackageList((list) =>
list.filter((item) => item.boxType !== "EXTRA_USED"),
list.filter(
(item) => item.boxType !== "EXTRA_USED",
),
);
}
}}
@ -1296,6 +1322,8 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
/* 当选择了使用自己的纸箱时 */
<>
{renderPackageByType("OWN")}
{/* 分隔线 */}
<View className="my-4 h-px bg-gray-200" />
</>
)}
@ -1304,6 +1332,9 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
<>
{renderPackageByType("USED")}
{/* 分隔线 */}
<View className="my-4 h-px bg-gray-200" />
{/* 有额外运输纸箱过来吗? */}
<View className="mb-2.5 flex items-center justify-between">
<View className="text-sm"></View>
@ -1311,9 +1342,7 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
<Button
size="small"
type={
packageTypeEnabled.EXTRA === 1
? "primary"
: "default"
packageTypeEnabled.EXTRA === 1 ? "primary" : "default"
}
onClick={() => togglePackageType("EXTRA", 1)}
>
@ -1322,9 +1351,7 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
<Button
size="small"
type={
packageTypeEnabled.EXTRA === 2
? "primary"
: "default"
packageTypeEnabled.EXTRA === 2 ? "primary" : "default"
}
onClick={() => {
togglePackageType("EXTRA", 2);
@ -1355,12 +1382,18 @@ export default forwardRef<OrderPackageRef, IOrderPackageProps>(
/* 当选择了使用自己的纸箱时 */
<>
{renderPackageByType("OWN")}
{/* 分隔线 */}
<View className="my-4 h-px bg-gray-200" />
</>
)}
{packageTypeEnabled.OWN === 2 && (
/* 当没有选择使用自己的纸箱时 */
<>{renderPackageByType("EXTRA")}</>
<>
{renderPackageByType("EXTRA")}
{/* 分隔线 */}
<View className="my-4 h-px bg-gray-200" />
</>
)}
</>
)}

View File

@ -199,6 +199,14 @@ const quickActionMap = {
bgColorClass: "bg-primary/10",
path: "/pages/purchase/order/list",
},
{
id: "shipOrder",
title: "发货单",
icon: "print",
iconColor: "var(--color-yellow-600)",
bgColorClass: "bg-yellow-100",
path: "/pages/ship/order/list",
},
{
id: "dailyExpense",
title: "录花销",

View File

@ -20,7 +20,7 @@ const hocAuth = (
const [longitude, setLongitude] = useState<number>();
const [latitude, setLatitude] = useState<number>();
const [showOverlay, setShowOverlay] = useState<boolean>(false);
const [loading, setLoading] = useState<boolean>(false)
// 控制是否已初始化
const [isInitialized, setIsInitialized] = useState(false);
@ -98,7 +98,7 @@ const hocAuth = (
return (
<>
<Overlay visible={showOverlay}>
<Overlay visible={loading}>
<View
className="wrapper"
style={{
@ -120,7 +120,7 @@ const hocAuth = (
setRole={setRole}
isInitialized={isInitialized}
setIsInitialized={setIsInitialized}
setShowOverlay={setShowOverlay}
setLoading={setLoading}
/>
</>
);

View File

@ -95,7 +95,7 @@ const base = (Component: React.FC) => (props: any) => {
}, []);
return (
<SWRConfig value={{ provider: () => map, revalidateOnFocus: false, }}>
<SWRConfig value={{ provider: () => map, revalidateOnFocus: false }}>
<CustomTheme>
<Component
{...props}

View File

@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: "采购单审核通过",
navigationBarBackgroundColor: "#fff",
});

View File

@ -0,0 +1,282 @@
import hocAuth from "@/hocs/auth";
import { CommonComponent } from "@/types/typings";
import { useEffect, useState } from "react";
import { Text, View } from "@tarojs/components";
import { Button, SafeArea, Toast } from "@nutui/nutui-react-taro";
import Taro from "@tarojs/taro";
import { business } from "@/services";
import buildUrl from "@/utils/buildUrl";
export default hocAuth(function Page(props: CommonComponent) {
const { router, setLoading } = props;
const orderId = router.params.orderId as string;
const [purchaseOrder, setPurchaseOrder] =
useState<BusinessAPI.PurchaseOrderVO>();
const [canGenerateDocuments, setCanGenerateDocuments] = useState({
shipDocument: false,
purchaseDocument: false,
costDocument: false,
});
const [shipOrder, setShipOrder] = useState<BusinessAPI.ShipOrderVO>();
const init = async (orderId: string) => {
setLoading(true)
try {
// 获取采购单信息
const { data: purchaseData } =
await business.purchaseOrder.showPurchaseOrder({
purchaseOrderShowQry: {
orderId: orderId,
},
});
if (purchaseData.success) {
setPurchaseOrder(purchaseData.data);
// 获取关联的发货单信息
// 使用 showShipOrder 替代 listShipOrder
const { data: shipData } = await business.shipOrder.showShipOrder({
shipOrderShowQry: {
purchaseOrderId: orderId,
},
});
if (shipData.success && shipData.data) {
setShipOrder(shipData.data);
}
// 检查经销商支持的单据类型
if (purchaseData.data?.orderDealer?.dealerId) {
const { data: dealerData } = await business.dealer.showDealer({
dealerShowQry: {
dealerId: purchaseData.data.orderDealer.dealerId,
},
});
if (dealerData.success && dealerData.data?.documentTypes) {
const documentTypes = dealerData.data.documentTypes;
setCanGenerateDocuments({
shipDocument: documentTypes.includes("delivery"),
purchaseDocument: documentTypes.includes("purchase"),
costDocument: documentTypes.includes("cost"),
});
}
}
}
} catch (error) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: "获取采购单信息失败",
});
} finally {
setLoading(false)
}
};
useEffect(() => {
if (orderId) {
init(orderId);
}
}, [orderId]);
// 生成发货单据
const generateShipDocument = async () => {
if (!canGenerateDocuments.shipDocument) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: "当前经销商不支持生成发货单据",
});
return;
}
if (!shipOrder?.shipOrderId) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: "未找到关联的发货单",
});
return;
}
try {
// 跳转到发货单据生成页面
Taro.navigateTo({
url: buildUrl("/pages/ship/document/create", {
shipOrderId: shipOrder.shipOrderId,
}),
});
} catch (error) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: "跳转发货单据页面失败",
});
}
};
// 生成采购底单
const generatePurchaseDocument = () => {
if (!canGenerateDocuments.purchaseDocument) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: "当前经销商不支持生成采购底单",
});
return;
}
Toast.show("toast", {
icon: "success",
title: "提示",
content: "采购底单生成成功",
});
};
// 生成成本单
const generateCostDocument = () => {
if (!canGenerateDocuments.costDocument) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: "当前经销商不支持生成成本单",
});
return;
}
Toast.show("toast", {
icon: "success",
title: "提示",
content: "成本单生成成功",
});
};
return (
<View className="flex flex-1 flex-col gap-2.5">
<View className="flex flex-1 flex-col items-center justify-start bg-gray-100 p-2.5">
<View className="mb-2.5 flex h-16 w-16 items-center justify-center rounded-full bg-green-100">
<Text className="text-2xl text-green-600"></Text>
</View>
<View className="mb-2.5 text-xl font-bold text-gray-800">
</View>
<View className="mb-2.5 text-sm text-gray-600"></View>
<View className="mb-2.5 text-xs text-gray-400">
</View>
<View className="w-full rounded-lg bg-white p-2.5 shadow-md">
<View className="mb-2.5 border-b border-gray-200 pb-2">
<Text className="text-lg font-semibold"></Text>
</View>
<View className="mb-2.5 flex flex-col gap-2.5">
<View className="flex flex-row justify-between">
<Text className="text-sm text-gray-600">:</Text>
<Text className="text-sm font-medium">
{purchaseOrder?.orderSn || "-"}
</Text>
</View>
<View className="flex flex-row justify-between">
<Text className="text-sm text-gray-600">:</Text>
<Text className="text-sm font-medium">
{purchaseOrder?.orderVehicle?.vehicleNo || "-"}
</Text>
</View>
<View className="flex flex-row justify-between">
<Text className="text-sm text-gray-600">:</Text>
<Text className="text-sm font-medium">
{purchaseOrder?.orderVehicle?.dealerName || "-"}
</Text>
</View>
<View className="flex flex-row justify-between">
<Text className="text-sm text-gray-600">:</Text>
<Text className="text-sm font-medium text-green-600">
</Text>
</View>
</View>
{(canGenerateDocuments.shipDocument ||
canGenerateDocuments.purchaseDocument ||
canGenerateDocuments.costDocument) && (
<View className="border-t border-gray-200 pt-2.5">
<View className="mb-2">
<Text className="text-lg font-semibold"></Text>
</View>
<View className="flex flex-col gap-3">
{canGenerateDocuments.shipDocument && shipOrder && (
<Button
type="primary"
size={"xlarge"}
block
onClick={generateShipDocument}
>
</Button>
)}
{canGenerateDocuments.purchaseDocument && (
<Button
type="default"
size={"xlarge"}
block
onClick={generatePurchaseDocument}
>
</Button>
)}
{canGenerateDocuments.costDocument && (
<Button
type="default"
size={"xlarge"}
block
onClick={generateCostDocument}
>
</Button>
)}
</View>
{!shipOrder && (
<View className="mt-2.5 text-center text-sm text-red-500">
</View>
)}
</View>
)}
</View>
</View>
<View className={"sticky bottom-0 z-10 bg-white"}>
<View className="flex justify-between gap-2 border-t border-gray-200 p-2.5">
<View className="flex-1">
<Button
type="default"
size={"xlarge"}
block
onClick={() =>
Taro.switchTab({
url: buildUrl("/pages/main/index/index"),
})
}
>
</Button>
</View>
</View>
<SafeArea position={"bottom"} />
</View>
</View>
);
});

View File

@ -37,6 +37,7 @@ import {
getTotalGrossWeight,
getTotalNetWeight,
} from "@/utils/calcutePurchaseOrder";
import buildUrl from "@/utils/buildUrl";
const sectionList = {
// 市场报价
@ -508,8 +509,12 @@ export default hocAuth(function Page(props: CommonComponent) {
purchaseOrderVO={purchaseOrderVO}
size={"xlarge"}
onFinish={() => {
// 返回首页
Taro.redirectTo({ url: "/pages/purchase/order/list" });
// 关闭当前页面并跳转到采购单审核通过页面
Taro.redirectTo({
url: buildUrl(`/pages/purchase/order/approved`, {
orderId: purchaseOrderVO?.orderId,
}),
});
}}
/>
</View>

View File

@ -20,7 +20,7 @@ import { business } from "@/services";
import dayjs from "dayjs";
export default hocAuth(function Page(props: CommonComponent) {
const { router } = props;
const { router, setLoading } = props;
const shipOrderId = router.params
.shipOrderId as BusinessAPI.ShipOrderVO["shipOrderId"];
@ -1223,6 +1223,9 @@ export default hocAuth(function Page(props: CommonComponent) {
if (downloadRes.tempFilePath) {
Taro.saveImageToPhotosAlbum({
filePath: downloadRes.tempFilePath,
complete: (res) => {
console.log("saveImageToPhotosAlbum", res);
},
})
.then(() => {
Taro.showToast({
@ -1254,6 +1257,15 @@ export default hocAuth(function Page(props: CommonComponent) {
// 重置表单
const resetForm = () => {
setStep(1);
// 重置表单错误状态
setFormErrors({});
// 重新加载初始数据
if (shipOrderId) {
setLoading(true);
init(shipOrderId).then(() => {
setLoading(false);
});
}
};
// 重新生成单据

View File

@ -3443,6 +3443,8 @@ declare namespace BusinessAPI {
status?: boolean;
/** 发货单ID */
shipOrderId?: string;
/** 采购单ID */
purchaseOrderId?: string;
};
type ShipOrderUpdateCmd = {

View File

@ -20,7 +20,7 @@ export interface CommonComponent {
setIsInitialized: (isInitialized: boolean) => void;
longitude?: number;
latitude?: number;
setShowOverlay?: (showOverlay: boolean) => void;
setLoading: (loading: boolean) => void;
}
export const ComponentDefaults = {