refactor(delivery): 重构发货单相关功能实现

- 优化 State 组件渲染逻辑,增加状态判断避免无效渲染
- 在 Step1Form 组件中添加调试日志便于排查问题
- 调整 DeliveryFormSection 中 convertShipOrderVOToExamplesFormat 方法参数
- 重命名常量文件 shipOrder.ts 为 orderShip.ts 并扩展状态枚举值
- 新增草稿(DRAFT)和待发货(WAIT_SHIPMENT)两种订单状态配置
- 更新发货单页面导入模块路径并调整数据处理逻辑
- 修改发货单文档页面初始化方法参数并增强类型安全
- 重构发货单列表页删除冗余弹窗及生成单据逻辑
- 调整发票上传页面筛选条件限制仅允许特定状态下操作
- 优化审批结果页面发货单据下载流程简化交互步骤
- 补充业务接口定义完善 OrderShip 和 OrderSupplier 类型声明
- 更新工具函数中 purchaseOrderConverter 和 shipOrderConverter 实现细节
- 调整应用路由配置同步页面文件名变更影响范围
This commit is contained in:
shenyifei 2025-12-16 14:48:21 +08:00
parent 8bca8c2476
commit 72a0e06da6
18 changed files with 390 additions and 549 deletions

View File

@ -35,7 +35,7 @@
#### 命名规范 #### 命名规范
- **组件**: PascalCase (如: CustomTabBar, PurchaseSection) - **组件**: PascalCase (如: CustomTabBar, PurchaseSection)
- **文件**: kebab-case (如: purchase-order-list.tsx) - **文件**: kebab-case (如: purchase-order-all.tsx)
- **变量/函数**: camelCase (如: getUserInfo, handleSubmit) - **变量/函数**: camelCase (如: getUserInfo, handleSubmit)
- **常量**: SCREAMING_SNAKE_CASE (如: API_BASE_URL) - **常量**: SCREAMING_SNAKE_CASE (如: API_BASE_URL)
- **CSS类**: TailwindCSS原子类 + 自定义kebab-case类 - **CSS类**: TailwindCSS原子类 + 自定义kebab-case类

View File

@ -57,7 +57,7 @@ config = {
// 发货单 // 发货单
{ {
root: "pages/delivery", root: "pages/delivery",
pages: ["list", "document/delivery", "document/purchase"], pages: ["all", "document/delivery", "document/purchase"],
}, },
// 瓜农 // 瓜农
{ {

View File

@ -14,22 +14,27 @@ export default function State({
const statusInfo = stateMap[state]; const statusInfo = stateMap[state];
return ( return (
<View statusInfo && (
className={classNames( <View
"flex items-center justify-center rounded-full px-3 py-1 shadow-sm", className={classNames(
{ "flex items-center justify-center rounded-full px-3 py-1 shadow-sm",
"absolute top-4 right-4": position === "absolute", {
relative: position === "relative", "absolute top-4 right-4": position === "absolute",
}, relative: position === "relative",
)} },
style={{ )}
backgroundColor: statusInfo.bgColor, style={{
boxShadow: `0 0 0 1px ${statusInfo.color}20, 0 1px 3px 0 ${statusInfo.color}30`, backgroundColor: statusInfo.bgColor,
}} boxShadow: `0 0 0 1px ${statusInfo.color}20, 0 1px 3px 0 ${statusInfo.color}30`,
> }}
<Text className="text-xs font-medium" style={{ color: statusInfo.color }}> >
{statusInfo.label} <Text
</Text> className="text-xs font-medium"
</View> style={{ color: statusInfo.color }}
>
{statusInfo.label}
</Text>
</View>
)
); );
} }

View File

@ -22,6 +22,7 @@ export interface Step1FormRef {
const Step1Form = forwardRef<Step1FormRef, Step1FormProps>((props, ref) => { const Step1Form = forwardRef<Step1FormRef, Step1FormProps>((props, ref) => {
const { moduleList, orderShip, setOrderShip, readOnly = false } = props; const { moduleList, orderShip, setOrderShip, readOnly = false } = props;
console.log("moduleList", moduleList, orderShip);
// 当天和未来10天 // 当天和未来10天
const startDate = new Date(); const startDate = new Date();

View File

@ -34,10 +34,7 @@ export default forwardRef<
if (data.data?.deliveryTemplate!) { if (data.data?.deliveryTemplate!) {
const template = JSON.parse(data.data?.deliveryTemplate!); const template = JSON.parse(data.data?.deliveryTemplate!);
// 将 shipOrderVO 转换为 examples 的数据格式,然后再替换 moduleList 里面的 config 数据 // 将 shipOrderVO 转换为 examples 的数据格式,然后再替换 moduleList 里面的 config 数据
const convertedData = convertShipOrderVOToExamplesFormat( const convertedData = convertShipOrderVOToExamplesFormat(purchaseOrderVO);
purchaseOrderVO,
orderShip!,
);
const updatedTemplate = await updateTemplateConfig( const updatedTemplate = await updateTemplateConfig(
template, template,
convertedData, convertedData,

View File

@ -3,6 +3,14 @@ const stateList = [
title: "全部", title: "全部",
value: "ALL", value: "ALL",
}, },
{
title: "草稿",
value: "DRAFT",
},
{
title: "待发货",
value: "WAIT_SHIPMENT",
},
{ {
title: "待回款", title: "待回款",
value: "WAIT_PAYMENT", value: "WAIT_PAYMENT",
@ -27,6 +35,18 @@ const stateList = [
// 定义需求状态映射 - 优化颜色方案 // 定义需求状态映射 - 优化颜色方案
const stateMap = { const stateMap = {
DRAFT: {
label: "草稿",
color: "#6B7280",
bgColor: "#F3F4F6",
borderColor: "#D1D5DB",
},
WAIT_SHIPMENT: {
label: "待发货",
color: "#EA580C",
bgColor: "#FEF3C7",
borderColor: "#D97706",
},
WAIT_PAYMENT: { WAIT_PAYMENT: {
label: "待回款", label: "待回款",
color: "#1E40AF", color: "#1E40AF",

View File

@ -0,0 +1,228 @@
import {
ActionType,
DealerPicker,
Icon,
PageList,
State,
ToolBar,
} from "@/components";
import Taro, { useShareAppMessage } from "@tarojs/taro";
import { useRef, useState } from "react";
import { business } from "@/services";
import hocAuth from "@/hocs/auth";
import { CommonComponent } from "@/types/typings";
import { Label, Text, View } from "@tarojs/components";
import { buildUrl } from "@/utils";
import dayjs from "dayjs";
import shipOrder from "@/constant/orderShip";
import { Button } from "@nutui/nutui-react-taro";
export default hocAuth(function Page(props: CommonComponent) {
const { shareOptions } = props;
const [state, setState] = useState<BusinessAPI.OrderShipPageQry["state"]>();
const [dealerVO, setDealerVO] = useState<BusinessAPI.DealerVO>();
const actionRef = useRef<ActionType>();
const toolbar: ToolBar = {
search: {
activeKey: "vehicleNo",
defaultActiveKey: "vehicleNo",
items: [
{
key: "vehicleNo",
name: "车次号",
placeholder: "请输入车次号",
},
{
key: "plate",
name: "车牌号",
placeholder: "请输入车牌号",
},
],
},
tabs: {
activeKey: "state",
defaultActiveKey: state || "ALL",
items: shipOrder.stateList,
onChange: (item) => {
setState(item as BusinessAPI.OrderShipPageQry["state"]);
actionRef.current?.reload();
},
},
render: () => (
<>
<View className={"flex flex-row gap-2.5"}>
<DealerPicker
onFinish={(dealerVO) => {
setDealerVO(dealerVO);
actionRef.current?.reload();
}}
trigger={
<View
className={`border-primary flex h-6 items-center rounded-md border-2 px-2.5`}
>
<View className={"text-primary text-xs"}>
{dealerVO?.shortName || "经销商"}
</View>
{dealerVO?.shortName ? (
<Icon
name={"circle-xmark"}
size={16}
onClick={(event) => {
setDealerVO(undefined);
actionRef.current?.reload();
event.stopPropagation();
}}
/>
) : (
<Icon name={"chevron-down"} size={16} />
)}
</View>
}
/>
</View>
</>
),
};
useShareAppMessage((res) => {
console.log("useShareAppMessage1", res, shareOptions);
// 如果是按钮触发的转发,使用默认配置
if (res.from === "button") {
return shareOptions;
}
// 页面转发使用设置的配置
return {};
});
return (
<PageList<BusinessAPI.OrderShipVO, BusinessAPI.OrderShipPageQry>
rowId={"shipOrderId"}
itemHeight={182}
type={"infinite"}
actionRef={actionRef}
render={(shipOrderVO: BusinessAPI.OrderShipVO, index) => (
<View className={"mb-2.5"} key={index}>
<View
className={
"relative flex flex-col divide-y-2 divide-neutral-100 rounded-lg bg-white px-2.5"
}
>
<View className={"flex flex-col divide-y-2 divide-neutral-100"}>
<View className={"py-2.5"}>
<View className={"flex flex-row items-center"}>
<View className={"flex flex-1 flex-col gap-2"}>
<View className={"flex flex-row gap-1"}>
{/* 复制 */}
<Text
className={"text-neutral-darkest text-xl font-bold"}
>
{shipOrderVO?.orderVehicle?.vehicleNo
? "第" + shipOrderVO.orderVehicle.vehicleNo + "车"
: "暂未生成车次"}
</Text>
</View>
<Text className={"text-neutral-dark text-sm"}>
{shipOrderVO.orderSn}
</Text>
</View>
<State
state={shipOrderVO.state}
stateMap={shipOrder.stateMap}
/>
</View>
</View>
<View className={"py-2.5"}>
<View className={"flex flex-col gap-2"}>
<View
className={
"flex flex-row items-center justify-between gap-2.5"
}
>
<Label className={"text-neutral-dark text-sm"}>
</Label>
<Text className={"text-neutral-darkest text-sm"}>
{shipOrderVO.createdByName}
</Text>
</View>
<View
className={
"flex flex-row items-center justify-between gap-2.5"
}
>
<Label className={"text-neutral-dark text-sm"}>
</Label>
<Text className={"text-neutral-darkest text-sm"}>
{dayjs(shipOrderVO.createdAt).format("MM-DD HH:mm")}
</Text>
</View>
<View
className={
"flex flex-row items-center justify-between gap-2.5"
}
>
<Label className={"text-neutral-dark text-sm"}>
</Label>
<Text className={"text-neutral-darkest text-sm"}>
{shipOrderVO.dealerName}
</Text>
</View>
</View>
</View>
</View>
<View className={"py-2.5"}>
<View className={"flex flex-row justify-end gap-2"}>
<Button
type={"primary"}
size={"small"}
onClick={async (e) => {
// 跳转到发货单据生成页面
await Taro.navigateTo({
url: buildUrl("/pages/delivery/document/delivery", {
orderShipId: shipOrderVO.orderShipId,
orderId: shipOrderVO.orderId,
}),
});
e.stopPropagation();
}}
>
</Button>
</View>
</View>
</View>
</View>
)}
toolbar={toolbar}
request={async (params) => {
const {
data: { data, success, notEmpty },
} = await business.orderShip.pageOrderShip({
orderShipPageQry: {
...params,
//@ts-ignore
state: state !== "ALL" ? state : undefined,
...(dealerVO
? {
dealerId: dealerVO.dealerId,
}
: {}),
},
});
return {
data,
success,
hasMore: notEmpty,
};
}}
pagination={{
pageSize: 10,
}}
/>
);
});

View File

@ -3,7 +3,7 @@ import { CommonComponent } from "@/types/typings";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { View } from "@tarojs/components"; import { View } from "@tarojs/components";
import { Button, Dialog, SafeArea, Step, Steps } from "@nutui/nutui-react-taro"; import { Button, Dialog, SafeArea, Step, Steps } from "@nutui/nutui-react-taro";
import shipOrder from "@/constant/shipOrder"; import orderShip from "@/constant/orderShip";
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";
@ -22,7 +22,7 @@ import {
// 特殊处理:其他费用要实时获取费用项目 // 特殊处理:其他费用要实时获取费用项目
const updateOtherFeesModule = async ( const updateOtherFeesModule = async (
module: any, module: any,
orderShipVO: BusinessAPI.OrderShipVO, purchaseOrderVO: BusinessAPI.PurchaseOrderVO,
) => { ) => {
const { const {
data: { data }, data: { data },
@ -40,7 +40,7 @@ const updateOtherFeesModule = async (
...module, ...module,
config: { config: {
...module.config, ...module.config,
feeItems: orderShipVO.orderCostList?.filter( feeItems: purchaseOrderVO.orderCostList?.filter(
(item) => item.type !== "ARTIFICIAL_TYPE", (item) => item.type !== "ARTIFICIAL_TYPE",
), ),
feeLabels: costItems.reduce((acc: any, item: any) => { feeLabels: costItems.reduce((acc: any, item: any) => {
@ -70,28 +70,38 @@ const updateOtherFeesModule = async (
export default hocAuth(function Page(props: CommonComponent) { export default hocAuth(function Page(props: CommonComponent) {
const { router, setLoading } = props; const { router, setLoading } = props;
const shipOrderId = router.params const orderShipId = router.params
.shipOrderId as BusinessAPI.OrderShipVO["orderShipId"]; .orderShipId as BusinessAPI.OrderShipVO["orderShipId"];
const orderId = router.params.orderId as BusinessAPI.OrderShipVO["orderId"];
const [step, setStep] = useState(1); const [step, setStep] = useState(1);
const [moduleList, setModuleList] = useState<any[]>([]); const [moduleList, setModuleList] = useState<any[]>([]);
const [height, setHeight] = useState<number>(); const [height, setHeight] = useState<number>();
const [shipOrderVO, setShipOrderVO] = useState<BusinessAPI.OrderShipVO>(); const [purchaseOrderVO, setPurchaseOrderVO] =
useState<BusinessAPI.PurchaseOrderVO>();
const [orderShipVO, setOrderShipVO] = useState<BusinessAPI.OrderShip>();
const [deliveryTemplate, setDeliveryTemplate] = useState<string>(); const [deliveryTemplate, setDeliveryTemplate] = useState<string>();
const [document, setDocument] = useState<string>(); const [document, setDocument] = useState<string>();
const step1FormRef = useRef<DeliveryStep1FormRef>(null); const step1FormRef = useRef<DeliveryStep1FormRef>(null);
const init = async (orderShipId: BusinessAPI.OrderShipVO["orderShipId"]) => { const init = async (
orderId: BusinessAPI.OrderShipVO["orderId"],
orderShipId: BusinessAPI.OrderShipVO["orderShipId"],
) => {
setLoading(true); setLoading(true);
const { data } = await business.orderShip.showOrderShip({ const { data } = await business.purchaseOrder.showPurchaseOrder({
orderShipShowQry: { purchaseOrderShowQry: {
orderId,
orderShipId, orderShipId,
}, },
}); });
const shipOrderVO = data.data; const purchaseOrderVO = data.data;
if (shipOrderVO) { if (purchaseOrderVO) {
setShipOrderVO(shipOrderVO); setPurchaseOrderVO(purchaseOrderVO);
const shipOrderVO = purchaseOrderVO.orderShipList[0];
setOrderShipVO(shipOrderVO);
if (shipOrderVO.document) { if (shipOrderVO.document) {
setStep(3); setStep(3);
setDocument(shipOrderVO.document); setDocument(shipOrderVO.document);
@ -99,7 +109,7 @@ export default hocAuth(function Page(props: CommonComponent) {
const { data } = await business.dealer.showDealer({ const { data } = await business.dealer.showDealer({
dealerShowQry: { dealerShowQry: {
dealerId: shipOrderVO.dealerId, dealerId: shipOrderVO?.dealerId,
}, },
}); });
@ -110,38 +120,39 @@ export default hocAuth(function Page(props: CommonComponent) {
}; };
useEffect(() => { useEffect(() => {
if (shipOrderId) { if (orderId && orderShipId) {
init(shipOrderId).then(); init(orderId, orderShipId).then();
} }
}, [shipOrderId]); }, [orderId, orderShipId]);
const refresh = async ( const refresh = async (
deliveryTemplate: string, deliveryTemplate: string,
shipOrderVO: BusinessAPI.OrderShipVO, purchaseOrderVO: BusinessAPI.PurchaseOrderVO,
) => { ) => {
const template = JSON.parse(deliveryTemplate); const template = JSON.parse(deliveryTemplate);
// 将 shipOrderVO 转换为 examples 的数据格式,然后再替换 moduleList 里面的 config 数据 // 将 shipOrderVO 转换为 examples 的数据格式,然后再替换 moduleList 里面的 config 数据
const convertedData = convertShipOrderVOToExamplesFormat(shipOrderVO); const convertedData = convertShipOrderVOToExamplesFormat(purchaseOrderVO);
console.log("convertedData", convertedData);
const updatedTemplate = await updateTemplateConfig( const updatedTemplate = await updateTemplateConfig(
template, template,
convertedData, convertedData,
shipOrderVO, purchaseOrderVO,
); );
console.log("updatedTemplate", updatedTemplate); console.log("updatedTemplate", updatedTemplate);
setModuleList(updatedTemplate); setModuleList(updatedTemplate);
}; };
useEffect(() => { useEffect(() => {
if (deliveryTemplate && shipOrderVO) { if (deliveryTemplate && purchaseOrderVO) {
refresh(deliveryTemplate, shipOrderVO).then(); refresh(deliveryTemplate, purchaseOrderVO).then();
} }
}, [shipOrderVO, deliveryTemplate]); }, [purchaseOrderVO, deliveryTemplate]);
// 更新模板配置 // 更新模板配置
const updateTemplateConfig = async ( const updateTemplateConfig = async (
template: any[], template: any[],
data: any, data: any,
shipOrderVO: BusinessAPI.OrderShipVO, purchaseOrderVO: BusinessAPI.PurchaseOrderVO,
) => { ) => {
let templateList: any[] = []; let templateList: any[] = [];
template.map(async (module: any) => { template.map(async (module: any) => {
@ -151,7 +162,7 @@ export default hocAuth(function Page(props: CommonComponent) {
} }
// 特殊处理: otherFees 要更新为最新的数据 // 特殊处理: otherFees 要更新为最新的数据
if (module.type === "otherFees") { if (module.type === "otherFees") {
newModule = await updateOtherFeesModule(module, shipOrderVO); newModule = await updateOtherFeesModule(module, purchaseOrderVO);
} }
templateList.push(newModule); templateList.push(newModule);
}); });
@ -224,10 +235,11 @@ export default hocAuth(function Page(props: CommonComponent) {
html: template.generateHtmlString(), html: template.generateHtmlString(),
}); });
const orderShip = purchaseOrderVO?.orderShipList[0];
// 存储 至 shipOrder previewUrl // 存储 至 shipOrder previewUrl
if (shipOrderVO) { if (orderShip) {
let formData: BusinessAPI.OrderShipGenerateDocumentCmd = { let formData: BusinessAPI.OrderShipGenerateDocumentCmd = {
orderShipId: shipOrderVO?.orderShipId, orderShipId: orderShip?.orderShipId,
document: data?.data?.path, document: data?.data?.path,
}; };
// 检查各模块中的必填字段是否已填写 // 检查各模块中的必填字段是否已填写
@ -247,23 +259,23 @@ export default hocAuth(function Page(props: CommonComponent) {
for (const column of contentSchema.columns) { for (const column of contentSchema.columns) {
// 检查西瓜品级字段是否开启且已填写 // 检查西瓜品级字段是否开启且已填写
if (column.dataIndex === "requiredWatermelonGrade") { if (column.dataIndex === "requiredWatermelonGrade") {
formData.watermelonGrade = shipOrderVO?.watermelonGrade; formData.watermelonGrade = orderShip?.watermelonGrade;
} }
// 检查发货地字段是否开启且已填写 // 检查发货地字段是否开启且已填写
else if (column.dataIndex === "requiredShippingFrom") { else if (column.dataIndex === "requiredShippingFrom") {
formData.shippingAddress = shipOrderVO?.shippingAddress; formData.shippingAddress = orderShip?.shippingAddress;
} }
// 检查预计到仓时间字段是否开启且已填写 // 检查预计到仓时间字段是否开启且已填写
else if (column.dataIndex === "requiredEstimatedArrivalTime") { else if (column.dataIndex === "requiredEstimatedArrivalTime") {
formData.estimatedArrivalDate = shipOrderVO?.estimatedArrivalDate; formData.estimatedArrivalDate = orderShip?.estimatedArrivalDate;
} }
// 检查备注字段是否开启且已填写 // 检查备注字段是否开启且已填写
else if (column.dataIndex === "requiredRemarks") { else if (column.dataIndex === "requiredRemarks") {
formData.remark = shipOrderVO?.remark; formData.remark = orderShip?.remark;
} }
// 检查品级字段是否开启且已填写 // 检查品级字段是否开启且已填写
else if (column.dataIndex === "requiredGrade") { else if (column.dataIndex === "requiredGrade") {
formData.orderShipItemList = shipOrderVO?.orderShipItemList; formData.orderShipItemList = orderShip?.orderShipItemList;
} }
} }
} }
@ -286,8 +298,8 @@ export default hocAuth(function Page(props: CommonComponent) {
const resetForm = () => { const resetForm = () => {
setStep(1); setStep(1);
// 重新加载初始数据 // 重新加载初始数据
if (shipOrderId) { if (orderId && orderShipId) {
init(shipOrderId).then(); init(orderId, orderShipId).then();
} }
}; };
@ -340,7 +352,7 @@ export default hocAuth(function Page(props: CommonComponent) {
<View className={"p-2.5"}> <View className={"p-2.5"}>
<View className={"rounded-lg bg-white p-4 shadow-md"}> <View className={"rounded-lg bg-white p-4 shadow-md"}>
<Steps value={step} status="dynamic" layout="single"> <Steps value={step} status="dynamic" layout="single">
{shipOrder.steps.map((item, index) => ( {orderShip.steps.map((item, index) => (
<Step key={index} {...item} /> <Step key={index} {...item} />
))} ))}
</Steps> </Steps>
@ -361,8 +373,8 @@ export default hocAuth(function Page(props: CommonComponent) {
<DeliveryStep1Form <DeliveryStep1Form
ref={step1FormRef} ref={step1FormRef}
moduleList={moduleList} moduleList={moduleList}
orderShip={shipOrderVO} orderShip={orderShipVO}
setOrderShip={setShipOrderVO} setOrderShip={setOrderShipVO}
/> />
</View> </View>
)} )}

View File

@ -3,11 +3,11 @@ import { CommonComponent } from "@/types/typings";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { View } from "@tarojs/components"; import { View } from "@tarojs/components";
import { Button, Dialog, SafeArea, Step, Steps } from "@nutui/nutui-react-taro"; import { Button, Dialog, SafeArea, Step, Steps } from "@nutui/nutui-react-taro";
import shipOrder from "@/constant/shipOrder"; import shipOrder from "@/constant/orderShip";
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 { buildUrl, PdfTemplate, PurchaseOrderCalculator } from "@/utils"; import { buildUrl, PdfTemplate } from "@/utils";
import { import {
Icon, Icon,
PurchaseStep1Form, PurchaseStep1Form,
@ -226,8 +226,6 @@ export default hocAuth(function Page(props: CommonComponent) {
return; return;
} }
const calculator = new PurchaseOrderCalculator(purchaseOrderVO);
return ( return (
<View <View
className={ className={

View File

@ -1,392 +0,0 @@
import {
ActionType,
DealerPicker,
Icon,
PageList,
State,
ToolBar,
} from "@/components";
import Taro, { useShareAppMessage } from "@tarojs/taro";
import { useRef, useState } from "react";
import { business } from "@/services";
import hocAuth from "@/hocs/auth";
import { CommonComponent } from "@/types/typings";
import { Label, Text, View } from "@tarojs/components";
import { buildUrl } from "@/utils";
import dayjs from "dayjs";
import shipOrder from "@/constant/shipOrder";
import { Button, Popup, SafeArea, Toast } from "@nutui/nutui-react-taro";
export default hocAuth(function Page(props: CommonComponent) {
const { shareOptions } = props;
const [state, setState] = useState<BusinessAPI.ShipOrderPageQry["state"]>();
const [dealerVO, setDealerVO] = useState<BusinessAPI.DealerVO>();
const [deliveryTemplate, setDeliveryTemplate] =
useState<BusinessAPI.DealerVO["deliveryTemplate"]>();
// 控制弹窗显示状态
const [popupVisible, setPopupVisible] = useState(false);
// 当前选中的发货单
const [currentShipOrder, setCurrentShipOrder] =
useState<BusinessAPI.ShipOrderVO | null>(null);
// 生成发货单据
const generateDocument = async () => {
if (!currentShipOrder?.shipOrderId) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: "未找到关联的发货单",
});
return;
}
try {
// 跳转到发货单据生成页面
Taro.navigateTo({
url: buildUrl("/pages/delivery/document/delivery", {
shipOrderId: currentShipOrder.shipOrderId,
}),
success: () => {
setPopupVisible(false);
},
});
} catch (error) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: "跳转发货单据页面失败",
});
}
};
const actionRef = useRef<ActionType>();
const toolbar: ToolBar = {
search: {
activeKey: "vehicleNo",
defaultActiveKey: "vehicleNo",
items: [
{
key: "vehicleNo",
name: "车次号",
placeholder: "请输入车次号",
},
{
key: "plate",
name: "车牌号",
placeholder: "请输入车牌号",
},
],
},
tabs: {
activeKey: "state",
defaultActiveKey: state || "ALL",
items: shipOrder.stateList,
onChange: (item) => {
setState(item as BusinessAPI.ShipOrderPageQry["state"]);
actionRef.current?.reload();
},
},
render: () => (
<>
{/* 生成单据弹窗 */}
<Popup
duration={150}
style={{
minHeight: "auto",
}}
visible={popupVisible}
className={"flex flex-col"}
position="bottom"
title={"生成单据"}
onClose={() => setPopupVisible(false)}
onOverlayClick={() => setPopupVisible(false)}
lockScroll
>
<View className={"flex flex-col gap-3 p-2.5"}>
<View className="mb-2.5 border-b border-gray-200 pb-2">
<Text className="text-lg font-semibold"></Text>
</View>
{currentShipOrder && (
<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">
{currentShipOrder.orderSn || "-"}
</Text>
</View>
<View className="flex flex-row justify-between">
<Text className="text-sm text-gray-600">:</Text>
<Text className="text-sm font-medium">
{currentShipOrder.vehicleNo || "-"}
</Text>
</View>
<View className="flex flex-row justify-between">
<Text className="text-sm text-gray-600">:</Text>
<Text className="text-sm font-medium">
{currentShipOrder.dealerName || "-"}
</Text>
</View>
</View>
)}
{currentShipOrder && (
<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">
{!deliveryTemplate ? (
<View className="border-t border-gray-200 pt-2.5">
<View className="flex flex-col gap-3">
<View className="mt-2.5 text-center text-sm text-red-500">
</View>
</View>
</View>
) : (
<View className="border-t border-gray-200 pt-2.5">
<View className="flex flex-col gap-3">
<View className="text-primary mt-2.5 text-center text-sm">
</View>
</View>
</View>
)}
</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
type="default"
size={"xlarge"}
block
onClick={() => setPopupVisible(false)}
>
</Button>
</View>
<View className={"flex-1"}>
<Button
type="primary"
size={"xlarge"}
disabled={!deliveryTemplate}
block
onClick={generateDocument}
>
</Button>
</View>
</View>
<SafeArea position={"bottom"} />
</View>
</Popup>
<View className={"flex flex-row gap-2.5"}>
<DealerPicker
onFinish={(dealerVO) => {
setDealerVO(dealerVO);
actionRef.current?.reload();
}}
trigger={
<View
className={`border-primary flex h-6 items-center rounded-md border-2 px-2.5`}
>
<View className={"text-primary text-xs"}>
{dealerVO?.shortName || "经销商"}
</View>
{dealerVO?.shortName ? (
<Icon
name={"circle-xmark"}
size={16}
onClick={(event) => {
setDealerVO(undefined);
actionRef.current?.reload();
event.stopPropagation();
}}
/>
) : (
<Icon name={"chevron-down"} size={16} />
)}
</View>
}
/>
</View>
</>
),
};
useShareAppMessage((res) => {
console.log("useShareAppMessage1", res, shareOptions);
// 如果是按钮触发的转发,使用默认配置
if (res.from === "button") {
return shareOptions;
}
// 页面转发使用设置的配置
return {};
});
return (
<PageList<BusinessAPI.OrderShipVO, BusinessAPI.OrderShipPageQry>
rowId={"shipOrderId"}
itemHeight={182}
type={"infinite"}
actionRef={actionRef}
render={(shipOrderVO: BusinessAPI.OrderShipVO, index) => (
<View className={"mb-2.5"} key={index}>
<View
className={
"relative flex flex-col divide-y-2 divide-neutral-100 rounded-lg bg-white px-2.5"
}
>
<View className={"flex flex-col divide-y-2 divide-neutral-100"}>
<View className={"py-2.5"}>
<View className={"flex flex-row items-center"}>
<View className={"flex flex-1 flex-col gap-2"}>
<View className={"flex flex-row gap-1"}>
{/* 复制 */}
<Text
className={"text-neutral-darkest text-xl font-bold"}
>
{shipOrderVO?.vehicleNo
? "第" + shipOrderVO.vehicleNo + "车"
: "暂未生成车次"}
</Text>
</View>
<Text className={"text-neutral-dark text-sm"}>
{shipOrderVO.orderSn}
</Text>
</View>
<State
state={shipOrderVO.state}
stateMap={shipOrder.stateMap}
/>
</View>
</View>
<View className={"py-2.5"}>
<View className={"flex flex-col gap-2"}>
<View
className={
"flex flex-row items-center justify-between gap-2.5"
}
>
<Label className={"text-neutral-dark text-sm"}>
</Label>
<Text className={"text-neutral-darkest text-sm"}>
{shipOrderVO.createdByName}
</Text>
</View>
<View
className={
"flex flex-row items-center justify-between gap-2.5"
}
>
<Label className={"text-neutral-dark text-sm"}>
</Label>
<Text className={"text-neutral-darkest text-sm"}>
{dayjs(shipOrderVO.createdAt).format("MM-DD HH:mm")}
</Text>
</View>
<View
className={
"flex flex-row items-center justify-between gap-2.5"
}
>
<Label className={"text-neutral-dark text-sm"}>
</Label>
<Text className={"text-neutral-darkest text-sm"}>
{shipOrderVO.dealerName}
</Text>
</View>
</View>
</View>
</View>
<View className={"py-2.5"}>
<View className={"flex flex-row justify-end gap-2"}>
{shipOrderVO.document ? (
<Button
type={"primary"}
size={"small"}
onClick={() => {
Taro.navigateTo({
url: buildUrl("/pages/delivery/document/delivery", {
shipOrderId: shipOrderVO.shipOrderId,
}),
});
}}
>
</Button>
) : (
<Button
type={"primary"}
size={"small"}
onClick={async (e) => {
setCurrentShipOrder(shipOrderVO);
const { data: dealerData } =
await business.dealer.showDealer({
dealerShowQry: {
dealerId: shipOrderVO.dealerId,
},
});
if (dealerData.success) {
const deliveryTemplate =
dealerData.data?.deliveryTemplate;
setDeliveryTemplate(deliveryTemplate);
}
setPopupVisible(true);
e.stopPropagation();
}}
>
</Button>
)}
</View>
</View>
</View>
</View>
)}
toolbar={toolbar}
request={async (params) => {
const {
data: { data, success, notEmpty },
} = await business.orderShip.pageOrderShip({
orderShipPageQry: {
...params,
//@ts-ignore
state: state !== "ALL" ? state : undefined,
...(dealerVO
? {
dealerId: dealerVO.dealerId,
}
: {}),
},
});
return {
data,
success,
hasMore: notEmpty,
};
}}
pagination={{
pageSize: 10,
}}
/>
);
});

View File

@ -411,18 +411,20 @@ export default hocAuth(function Page(props: CommonComponent) {
</View> </View>
<View className={"py-2.5"}> <View className={"py-2.5"}>
<View className={"flex flex-row justify-end gap-2"}> <View className={"flex flex-row justify-end gap-2"}>
{!orderSupplierVO.invoiceUpload && ( {!orderSupplierVO.invoiceUpload &&
<Button (orderSupplierVO.poState === "WAITING_AUDIT" ||
size={"small"} orderSupplierVO.poState === "COMPLETED") && (
type={"primary"} <Button
onClick={() => { size={"small"}
setSelectedOrders([orderSupplierVO]); type={"primary"}
setPopupVisible(true); onClick={() => {
}} setSelectedOrders([orderSupplierVO]);
> setPopupVisible(true);
}}
</Button> >
)}
</Button>
)}
</View> </View>
</View> </View>
</View> </View>
@ -436,7 +438,7 @@ export default hocAuth(function Page(props: CommonComponent) {
orderSupplierPageQry: { orderSupplierPageQry: {
...params, ...params,
invoiceUpload: false, invoiceUpload: false,
poStates: ["WAITING_AUDIT", "COMPLETED", "REJECTED"], poStates: ["WAITING_AUDIT", "COMPLETED"],
...(supplierVO ...(supplierVO
? { ? {
supplierId: supplierVO.supplierId, supplierId: supplierVO.supplierId,

View File

@ -424,7 +424,7 @@ export default hocAuth(function Page(props: CommonComponent) {
orderSupplierPageQry: { orderSupplierPageQry: {
...params, ...params,
invoiceUpload: false, invoiceUpload: false,
poStates: ["WAITING_AUDIT", "COMPLETED", "REJECTED"], poStates: ["WAITING_AUDIT", "COMPLETED"],
...(supplierVO ...(supplierVO
? { ? {
supplierId: supplierVO.supplierId, supplierId: supplierVO.supplierId,

View File

@ -14,46 +14,25 @@ export default hocAuth(function Page(props: CommonComponent) {
const [purchaseOrder, setPurchaseOrder] = const [purchaseOrder, setPurchaseOrder] =
useState<BusinessAPI.PurchaseOrderVO>(); useState<BusinessAPI.PurchaseOrderVO>();
const [shipOrder, setShipOrder] = useState<BusinessAPI.ShipOrderVO>(); const [orderShip, setOrderShip] = useState<BusinessAPI.OrderShip>();
const [deliveryTemplate, setDeliveryTemplate] = console.log("orderShip", orderShip);
useState<BusinessAPI.DealerVO["deliveryTemplate"]>();
const init = async (orderId: string) => { const init = async (orderId: string) => {
setLoading(true); setLoading(true);
try { try {
// 获取采购单信息 // 获取采购单信息
const { data: purchaseData } = const {
await business.purchaseOrder.showPurchaseOrder({ data: { data: purchaseOrder, success },
purchaseOrderShowQry: { } = await business.purchaseOrder.showPurchaseOrder({
orderId: orderId, purchaseOrderShowQry: {
}, orderId: orderId,
}); },
});
if (purchaseData.success) { if (success) {
setPurchaseOrder(purchaseData.data); setPurchaseOrder(purchaseOrder);
const orderShip = purchaseOrder?.orderShipList[0];
// 获取关联的发货单信息 setOrderShip(orderShip);
// 使用 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,
},
});
setDeliveryTemplate(dealerData.data?.deliveryTemplate);
}
} }
} catch (error) { } catch (error) {
Toast.show("toast", { Toast.show("toast", {
@ -72,33 +51,6 @@ export default hocAuth(function Page(props: CommonComponent) {
} }
}, [orderId]); }, [orderId]);
// 生成发货单据
const generateDocument = async () => {
if (!shipOrder?.shipOrderId) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: "未找到关联的发货单",
});
return;
}
try {
// 跳转到发货单据生成页面
Taro.navigateTo({
url: buildUrl("/pages/delivery/document/delivery", {
shipOrderId: shipOrder.shipOrderId,
}),
});
} catch (error) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: "跳转发货单据页面失败",
});
}
};
return ( return (
<View className="flex flex-1 flex-col gap-2.5"> <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="flex flex-1 flex-col items-center justify-start bg-gray-100 p-2.5">
@ -155,27 +107,29 @@ export default hocAuth(function Page(props: CommonComponent) {
</View> </View>
</View> </View>
{shipOrder && ( {orderShip && (
<View className="border-t border-gray-200 pt-2.5"> <View className="border-t border-gray-200 pt-2.5">
<View className="mb-2"> <View className="mb-2">
<Text className="text-lg font-semibold"></Text> <Text className="text-lg font-semibold"></Text>
</View> </View>
<View className="flex flex-col gap-3"> <View className="flex flex-col gap-3">
{deliveryTemplate ? ( <Button
<Button type="primary"
type="primary" size={"xlarge"}
size={"xlarge"} block
block onClick={async () => {
onClick={generateDocument} // 跳转到发货单据生成页面
> await Taro.navigateTo({
url: buildUrl("/pages/delivery/document/delivery", {
</Button> orderShipId: orderShip.orderShipId,
) : ( orderId: orderShip.orderId,
<View className="mt-2.5 text-center text-sm text-red-500"> }),
});
</View> }}
)} >
</Button>
</View> </View>
</View> </View>
)} )}

View File

@ -2788,6 +2788,10 @@ declare namespace BusinessAPI {
orderId: string; orderId: string;
/** 发货单编号 */ /** 发货单编号 */
orderSn: string; orderSn: string;
/** 经销商ID */
dealerId?: string;
/** 经销商名称 */
dealerName?: string;
/** 仓库ID */ /** 仓库ID */
warehouseId?: string; warehouseId?: string;
/** 仓库名称 */ /** 仓库名称 */
@ -2986,6 +2990,10 @@ declare namespace BusinessAPI {
orderShipId: string; orderShipId: string;
/** 采购单ID */ /** 采购单ID */
orderId: string; orderId: string;
/** 经销商ID */
dealerId?: string;
/** 经销商名称 */
dealerName?: string;
/** 发货单编号 */ /** 发货单编号 */
orderSn?: string; orderSn?: string;
/** 仓库ID */ /** 仓库ID */
@ -3032,6 +3040,8 @@ declare namespace BusinessAPI {
orderShipItemList?: OrderShipItem[]; orderShipItemList?: OrderShipItem[];
/** 发货单成本项目信息 */ /** 发货单成本项目信息 */
orderCostList?: OrderCost[]; orderCostList?: OrderCost[];
/** 采购订单车辆运输信息 */
orderVehicle?: OrderVehicle;
}; };
type OrderSupplier = { type OrderSupplier = {
@ -3105,11 +3115,13 @@ declare namespace BusinessAPI {
orderVehicle?: OrderVehicle; orderVehicle?: OrderVehicle;
/** 是否选中 */ /** 是否选中 */
selected: boolean; selected: boolean;
/** 采购订单状态: 0_草稿1_审核中2_已完成3_已驳回4_已关闭 */
poState?: "DRAFT" | "WAITING_AUDIT" | "COMPLETED" | "REJECTED" | "CLOSED";
}; };
type OrderSupplierBatchInvoiceUploadCmd = { type OrderSupplierBatchInvoiceUploadCmd = {
/** 供应商ID列表 */ /** 供应商ID列表 */
orderSupplierIdList: string[]; orderSupplierIdList: number[];
/** 是否上传票证 */ /** 是否上传票证 */
invoiceUpload?: boolean; invoiceUpload?: boolean;
/** 发票照片 */ /** 发票照片 */
@ -4078,6 +4090,8 @@ declare namespace BusinessAPI {
status?: boolean; status?: boolean;
/** 采购订单ID */ /** 采购订单ID */
orderId?: string; orderId?: string;
/** 发货订单ID */
orderShipId?: string;
}; };
type PurchaseOrderStep1Cmd = { type PurchaseOrderStep1Cmd = {

View File

@ -69,6 +69,8 @@ export const convertPurchaseOrderToOrderShip = (
orderId: purchaseOrderVO.orderId, orderId: purchaseOrderVO.orderId,
orderSn: "", orderSn: "",
watermelonGrade: "", watermelonGrade: "",
dealerId: purchaseOrderVO.orderVehicle?.dealerId!,
dealerName: purchaseOrderVO.orderVehicle?.dealerName!,
companyId: purchaseOrderVO.orderCompany?.companyId, companyId: purchaseOrderVO.orderCompany?.companyId,
companyName: purchaseOrderVO.orderCompany?.fullName, companyName: purchaseOrderVO.orderCompany?.fullName,
shippingAddress: purchaseOrderVO.orderVehicle?.origin, shippingAddress: purchaseOrderVO.orderVehicle?.origin,

View File

@ -23,15 +23,15 @@ const groupBy = <T>(
/** /**
* ShipOrder转换为示例数据格式 * ShipOrder转换为示例数据格式
* @param purchaseOrderVO * @param purchaseOrderVO
* @param shipOrder
* @returns * @returns
*/ */
export const convertShipOrderVOToExamplesFormat = ( export const convertShipOrderVOToExamplesFormat = (
purchaseOrderVO: BusinessAPI.PurchaseOrderVO, purchaseOrderVO: BusinessAPI.PurchaseOrderVO,
shipOrder: BusinessAPI.OrderShip,
) => { ) => {
const calculator = new PurchaseOrderCalculator(purchaseOrderVO); const calculator = new PurchaseOrderCalculator(purchaseOrderVO);
const shipOrder = purchaseOrderVO.orderShipList[0]!;
// 转换包装信息,根据 boxSpecId 分组 // 转换包装信息,根据 boxSpecId 分组
const allPackages: BusinessAPI.OrderPackage[] = []; const allPackages: BusinessAPI.OrderPackage[] = [];
purchaseOrderVO.orderSupplierList?.forEach((supplier) => { purchaseOrderVO.orderSupplierList?.forEach((supplier) => {

File diff suppressed because one or more lines are too long