- 添加了PC端预览页面,支持多种设备模拟预览 - 在采购审核页面集成了发货单预览按钮 - 重构了发货表单组件,移除ref暴露机制 - 更新订单转换器以支持发货单数据转换 - 在发票上传页面添加供应商选择器组件 - 添加初始车次号相关字段到API类型定义 - 将发货表单中的Input组件替换为TextArea组件 - 修复了订单项ID匹配逻辑错误
441 lines
12 KiB
TypeScript
441 lines
12 KiB
TypeScript
import hocAuth from "@/hocs/auth";
|
|
import { CommonComponent } from "@/types/typings";
|
|
import { useEffect, useRef, useState } from "react";
|
|
import { View } from "@tarojs/components";
|
|
import { Button, Dialog, SafeArea, Step, Steps } from "@nutui/nutui-react-taro";
|
|
import orderShip from "@/constant/orderShip";
|
|
import Taro from "@tarojs/taro";
|
|
import classNames from "classnames";
|
|
import { business, poster } from "@/services";
|
|
import {
|
|
buildUrl,
|
|
convertOrderShipVOToExamplesFormat,
|
|
PdfTemplate,
|
|
} from "@/utils";
|
|
import {
|
|
DeliveryStep1Form,
|
|
DeliveryStep1FormRef,
|
|
DeliveryStep2Preview,
|
|
DeliveryStep3Success,
|
|
} from "@/components";
|
|
import dayjs from "dayjs";
|
|
|
|
// 特殊处理:其他费用要实时获取费用项目
|
|
const updateOtherFeesModule = async (
|
|
module: any,
|
|
orderVO: BusinessAPI.OrderVO,
|
|
) => {
|
|
const {
|
|
data: { data },
|
|
} = await business.costItem.listCostItem({
|
|
costItemListQry: {
|
|
status: true,
|
|
},
|
|
});
|
|
const costItems =
|
|
data?.filter((item) => item.type !== "ARTIFICIAL_TYPE") || [];
|
|
|
|
console.log("module", module);
|
|
|
|
return {
|
|
...module,
|
|
config: {
|
|
...module.config,
|
|
feeItems: orderVO.orderCostList?.filter(
|
|
(item) => item.type !== "ARTIFICIAL_TYPE",
|
|
),
|
|
feeLabels: costItems.reduce((acc: any, item: any) => {
|
|
acc[item.itemId] = item.name;
|
|
return acc;
|
|
}, {}),
|
|
},
|
|
schemas: [
|
|
{
|
|
title: "显示配置",
|
|
valueType: "group",
|
|
columns: [
|
|
{
|
|
dataIndex: "feeItems",
|
|
title: "显示费用项目",
|
|
valueType: "checkbox",
|
|
valueEnum: costItems.reduce((acc: any, item: any) => {
|
|
acc[item.itemId] = item.name;
|
|
return acc;
|
|
}, {}),
|
|
},
|
|
],
|
|
},
|
|
],
|
|
};
|
|
};
|
|
|
|
export default hocAuth(function Page(props: CommonComponent) {
|
|
const { router, setLoading } = props;
|
|
const orderShipId = router.params
|
|
.orderShipId as BusinessAPI.OrderShipVO["orderShipId"];
|
|
const orderId = router.params.orderId as BusinessAPI.OrderShipVO["orderId"];
|
|
|
|
const [step, setStep] = useState(1);
|
|
const [moduleList, setModuleList] = useState<any[]>([]);
|
|
const [orderVO, setOrderVO] = useState<BusinessAPI.OrderVO>();
|
|
|
|
const [orderShipVO, setOrderShipVO] = useState<BusinessAPI.OrderShip>();
|
|
const [deliveryTemplate, setDeliveryTemplate] = useState<string>();
|
|
const [pdfUrl, setPdfUrl] = useState<string>();
|
|
const [picUrl, setPicUrl] = useState<string>();
|
|
|
|
const step1FormRef = useRef<DeliveryStep1FormRef>(null);
|
|
|
|
const init = async (
|
|
orderId: BusinessAPI.OrderShipVO["orderId"],
|
|
orderShipId: BusinessAPI.OrderShipVO["orderShipId"],
|
|
) => {
|
|
setLoading(true);
|
|
const { data } = await business.order.showOrder({
|
|
orderShowQry: {
|
|
orderId,
|
|
orderShipId,
|
|
},
|
|
});
|
|
const orderVO = data.data;
|
|
if (orderVO) {
|
|
setOrderVO(orderVO);
|
|
const orderShip = orderVO.orderShipList[0];
|
|
setOrderShipVO(orderShip);
|
|
if (orderShip.pdfUrl || orderShip.picUrl) {
|
|
setStep(3);
|
|
setPdfUrl(orderShip.pdfUrl);
|
|
setPicUrl(orderShip.picUrl);
|
|
}
|
|
|
|
const { data } = await business.dealer.showDealer({
|
|
dealerShowQry: {
|
|
dealerId: orderShip?.dealerId,
|
|
},
|
|
});
|
|
|
|
const deliveryTemplate = data.data?.deliveryTemplate!;
|
|
setDeliveryTemplate(deliveryTemplate);
|
|
}
|
|
setLoading(false);
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (orderId && orderShipId) {
|
|
init(orderId, orderShipId).then();
|
|
}
|
|
}, [orderId, orderShipId]);
|
|
|
|
const refresh = async (
|
|
deliveryTemplate: string,
|
|
orderVO: BusinessAPI.OrderVO,
|
|
) => {
|
|
const template = JSON.parse(deliveryTemplate);
|
|
// 将 orderShipVO 转换为 examples 的数据格式,然后再替换 moduleList 里面的 config 数据
|
|
const convertedData = convertOrderShipVOToExamplesFormat(orderVO);
|
|
const updatedTemplate = await updateTemplateConfig(
|
|
template,
|
|
convertedData,
|
|
orderVO,
|
|
);
|
|
setModuleList(updatedTemplate);
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (deliveryTemplate && orderVO) {
|
|
refresh(deliveryTemplate, orderVO).then();
|
|
}
|
|
}, [orderVO, deliveryTemplate]);
|
|
|
|
// 更新模板配置
|
|
const updateTemplateConfig = async (
|
|
template: any[],
|
|
data: any,
|
|
orderVO: BusinessAPI.OrderVO,
|
|
) => {
|
|
let templateList: any[] = [];
|
|
template.map(async (module: any) => {
|
|
let newModule = { ...module };
|
|
if (data[module.type]) {
|
|
newModule.config = { ...module.config, ...data[module.type] };
|
|
}
|
|
// 特殊处理: otherFees 要更新为最新的数据
|
|
if (module.type === "otherFees") {
|
|
newModule = await updateOtherFeesModule(module, orderVO);
|
|
}
|
|
templateList.push(newModule);
|
|
});
|
|
|
|
return templateList;
|
|
};
|
|
|
|
// 预览确认
|
|
const previewAndConfirm = () => {
|
|
if (step === 2) {
|
|
// 显示确认对话框
|
|
Dialog.open("dialog", {
|
|
title: "生成发货单据",
|
|
content: "即将生成发货单据,请确认发货单据是否正确。",
|
|
confirmText: "确认生成",
|
|
cancelText: "取消",
|
|
onConfirm: async () => {
|
|
// 截图预览内容
|
|
await capturePreview();
|
|
Dialog.close("dialog");
|
|
// 进入第三步
|
|
setStep(3);
|
|
},
|
|
onCancel: () => {
|
|
Dialog.close("dialog");
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (step === 1) {
|
|
// 调用 Step1Form 的表单校验方法
|
|
if (step1FormRef.current && step1FormRef.current.validateForm()) {
|
|
setStep(2);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (step < 3) {
|
|
setStep(step + 1);
|
|
}
|
|
};
|
|
|
|
// 生成发货单据
|
|
const generateShippingDocument = async () => {
|
|
// 显示确认对话框
|
|
Dialog.open("dialog", {
|
|
title: "生成发货单据",
|
|
content: "即将生成发货单据,请确认发货单据是否正确。",
|
|
confirmText: "确认生成",
|
|
cancelText: "取消",
|
|
onConfirm: async () => {
|
|
// 截图预览内容
|
|
await capturePreview();
|
|
Dialog.close("dialog");
|
|
// 进入第三步
|
|
setStep(3);
|
|
},
|
|
onCancel: () => {
|
|
Dialog.close("dialog");
|
|
},
|
|
});
|
|
};
|
|
|
|
// 截图预览内容
|
|
const capturePreview = async () => {
|
|
const template = new PdfTemplate(moduleList);
|
|
const {
|
|
data: { data: pdfData },
|
|
} = await poster.pdf.postApiV1Pdf({
|
|
html: template.generateHtmlString(),
|
|
});
|
|
|
|
const {
|
|
data: { data: picData },
|
|
} = await poster.poster.postApiV1Poster({
|
|
html: template.generateHtmlString(),
|
|
//@ts-ignore
|
|
format: "a4",
|
|
});
|
|
|
|
setPdfUrl(pdfData?.path);
|
|
setPicUrl(picData?.path);
|
|
|
|
const orderShip = orderVO?.orderShipList[0];
|
|
// 存储 至 orderShip previewUrl
|
|
if (orderShip) {
|
|
let formData: BusinessAPI.OrderShipGenerateDocumentCmd = {
|
|
orderShipId: orderShip?.orderShipId,
|
|
pdfUrl: pdfData?.path as string,
|
|
picUrl: picData?.path as string,
|
|
};
|
|
// 检查各模块中的必填字段是否已填写
|
|
for (const module of moduleList) {
|
|
const contentSchema = module.schemas.find(
|
|
(schema) => schema.title === "内容配置",
|
|
);
|
|
|
|
if (
|
|
!contentSchema ||
|
|
!contentSchema.columns ||
|
|
contentSchema.columns.length === 0
|
|
) {
|
|
continue;
|
|
}
|
|
|
|
for (const column of contentSchema.columns) {
|
|
// 检查西瓜品级字段是否开启且已填写
|
|
if (column.dataIndex === "requiredWatermelonGrade") {
|
|
formData.watermelonGrade = orderShip?.watermelonGrade;
|
|
}
|
|
// 检查发货地字段是否开启且已填写
|
|
else if (column.dataIndex === "requiredShippingFrom") {
|
|
formData.shippingAddress = orderShip?.shippingAddress;
|
|
}
|
|
// 检查预计到仓时间字段是否开启且已填写
|
|
else if (column.dataIndex === "requiredEstimatedArrivalTime") {
|
|
formData.estimatedArrivalDate = dayjs(
|
|
orderShip?.estimatedArrivalDate,
|
|
).format("YYYY-MM-DD");
|
|
}
|
|
// 检查备注字段是否开启且已填写
|
|
else if (column.dataIndex === "requiredRemarks") {
|
|
formData.remark = orderShip?.remark;
|
|
}
|
|
// 检查品级字段是否开启且已填写
|
|
else if (column.dataIndex === "requiredGrade") {
|
|
formData.orderShipItemList = orderShip?.orderShipItemList;
|
|
}
|
|
}
|
|
}
|
|
business.orderShip.generateDocumentOrderShip(formData).then();
|
|
}
|
|
};
|
|
|
|
// 返回上一步
|
|
const goToPrevStep = () => {
|
|
if (step > 0) {
|
|
setStep(step - 1);
|
|
}
|
|
};
|
|
|
|
// 重置表单
|
|
const resetForm = () => {
|
|
setStep(1);
|
|
// 重新加载初始数据
|
|
if (orderId && orderShipId) {
|
|
init(orderId, orderShipId).then();
|
|
}
|
|
};
|
|
|
|
// 重新生成单据
|
|
const regenerateDocument = () => {
|
|
setStep(1);
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<View
|
|
className={
|
|
"flex flex-1 flex-col overflow-x-hidden overflow-y-auto bg-[#D1D5DB]"
|
|
}
|
|
>
|
|
<View className={"p-2.5"}>
|
|
<View className={"rounded-lg bg-white p-4 shadow-md"}>
|
|
<Steps value={step} status="dynamic" layout="single">
|
|
{orderShip.steps.map((item, index) => (
|
|
<Step key={index} {...item} />
|
|
))}
|
|
</Steps>
|
|
</View>
|
|
</View>
|
|
|
|
<View
|
|
className={classNames("px-2.5", {
|
|
"overflow-hidden": step === 2,
|
|
})}
|
|
>
|
|
{step === 1 && (
|
|
<View className="flex flex-col rounded-lg bg-white p-2.5 shadow-md">
|
|
<DeliveryStep1Form
|
|
ref={step1FormRef}
|
|
moduleList={moduleList}
|
|
orderShip={orderShipVO}
|
|
setOrderShip={setOrderShipVO}
|
|
/>
|
|
</View>
|
|
)}
|
|
|
|
{step === 2 && <DeliveryStep2Preview moduleList={moduleList} />}
|
|
|
|
{step === 3 && pdfUrl && (
|
|
<DeliveryStep3Success pdfUrl={pdfUrl} picUrl={picUrl} />
|
|
)}
|
|
</View>
|
|
</View>
|
|
|
|
<View className={"sticky bottom-0 left-0 z-10 w-full bg-white"}>
|
|
<View className={"flex flex-row gap-2.5 p-2.5"}>
|
|
{step == 1 && (
|
|
<View className={"flex-1"}>
|
|
<Button type="default" block size={"large"} onClick={resetForm}>
|
|
重置
|
|
</Button>
|
|
</View>
|
|
)}
|
|
{step == 1 && (
|
|
<View className={"flex-1"}>
|
|
<Button
|
|
type="primary"
|
|
block
|
|
size={"large"}
|
|
onClick={previewAndConfirm}
|
|
>
|
|
下一步
|
|
</Button>
|
|
</View>
|
|
)}
|
|
{step == 2 && (
|
|
<View className={"flex-1"}>
|
|
<Button
|
|
type="default"
|
|
block
|
|
size={"large"}
|
|
onClick={goToPrevStep}
|
|
>
|
|
返回修改
|
|
</Button>
|
|
</View>
|
|
)}
|
|
{step == 2 && (
|
|
<View className={"flex-1"}>
|
|
<Button
|
|
type="primary"
|
|
block
|
|
size={"large"}
|
|
onClick={generateShippingDocument}
|
|
>
|
|
生成发货单据
|
|
</Button>
|
|
</View>
|
|
)}
|
|
{step == 3 && (
|
|
<View className={"flex-1"}>
|
|
<Button
|
|
type="default"
|
|
block
|
|
size={"large"}
|
|
onClick={() =>
|
|
Taro.switchTab({
|
|
url: buildUrl("/pages/main/index/index"),
|
|
})
|
|
}
|
|
>
|
|
返回首页
|
|
</Button>
|
|
</View>
|
|
)}
|
|
{step == 3 && (
|
|
<View className={"flex-1"}>
|
|
<Button
|
|
type="primary"
|
|
block
|
|
size={"large"}
|
|
onClick={regenerateDocument}
|
|
>
|
|
重新生成
|
|
</Button>
|
|
</View>
|
|
)}
|
|
</View>
|
|
<SafeArea position={"bottom"} />
|
|
</View>
|
|
</>
|
|
);
|
|
});
|