674 lines
18 KiB
TypeScript
674 lines
18 KiB
TypeScript
import hocAuth from "@/hocs/auth";
|
|
import { CommonComponent } from "@/types/typings";
|
|
import Taro, { useDidShow } from "@tarojs/taro";
|
|
import { business } from "@/services";
|
|
import { useEffect, useState } from "react";
|
|
import { View } from "@tarojs/components";
|
|
import purchaseOrder from "@/constant/purchaseOrder";
|
|
import { ActionSheet, Button, Dialog, Input, Popup, SafeArea, TextArea, Toast } from "@nutui/nutui-react-taro";
|
|
import {
|
|
BasicInfoSection,
|
|
CompanyInfoSection,
|
|
CostDifferenceSection,
|
|
CostSummarySection,
|
|
DealerInfoSection,
|
|
EmptyBoxInfoSection,
|
|
MarketPriceSection,
|
|
MaterialCostSection,
|
|
PackageInfoSection,
|
|
PackagingCostSection,
|
|
ProductionAdvanceSection,
|
|
RebateCalcSection,
|
|
State,
|
|
TaxProvisionSection,
|
|
TaxSubsidySection,
|
|
WorkerAdvanceSection
|
|
} from "@/components";
|
|
import buildUrl from "@/utils/buildUrl";
|
|
import { PurchaseOrderCalculator } from "@/utils/PurchaseOrderCalculator";
|
|
|
|
const sections = {
|
|
// 市场报价
|
|
marketPrice: {
|
|
component: MarketPriceSection,
|
|
title: "市场报价",
|
|
},
|
|
// 我方入账公司
|
|
supplierInfo: {
|
|
component: CompanyInfoSection,
|
|
title: "我方入账公司",
|
|
},
|
|
// 下游经销商信息
|
|
dealerInfo: {
|
|
component: DealerInfoSection,
|
|
title: "下游经销商",
|
|
},
|
|
// 基础信息
|
|
basicInfo: {
|
|
component: BasicInfoSection,
|
|
title: "基础信息",
|
|
},
|
|
// // 瓜农信息
|
|
// farmerInfo: {
|
|
// component: SupplierInfoSection,
|
|
// title: "瓜农信息",
|
|
// },
|
|
// // 采购成本
|
|
// purchaseCostInfo: {
|
|
// component: PurchaseCostInfoSection,
|
|
// title: "采购成本",
|
|
// },
|
|
// 包装纸箱费
|
|
packageInfo: {
|
|
component: PackageInfoSection,
|
|
title: "包装纸箱费复核",
|
|
},
|
|
// 空箱费用
|
|
emptyBoxInfo: {
|
|
component: EmptyBoxInfoSection,
|
|
title: "空箱费用复核",
|
|
},
|
|
// 工头垫付费用复核
|
|
foremanAdvance: {
|
|
component: WorkerAdvanceSection,
|
|
title: "工头垫付费用复核",
|
|
},
|
|
// 产地垫付费用复核
|
|
originAdvance: {
|
|
component: ProductionAdvanceSection,
|
|
title: "产地垫付费用复核",
|
|
},
|
|
// 辅料费用复核
|
|
materialCost: {
|
|
component: MaterialCostSection,
|
|
title: "辅料费复核",
|
|
},
|
|
// 其他费用复核
|
|
otherCost: {
|
|
component: PackagingCostSection,
|
|
title: "其他费用复核",
|
|
},
|
|
// 公司返点复核
|
|
taxSubsidy: {
|
|
component: TaxSubsidySection,
|
|
title: "公司返点复核",
|
|
},
|
|
// 计提税金复核
|
|
taxProvision: {
|
|
component: TaxProvisionSection,
|
|
title: "计提税金复核",
|
|
},
|
|
// 调诚信志远分红
|
|
costDifference: {
|
|
component: CostDifferenceSection,
|
|
title: "调诚信志远分红",
|
|
},
|
|
// 成本合计
|
|
costSummary: {
|
|
component: CostSummarySection,
|
|
title: "成本合计",
|
|
},
|
|
// 个人返点复核
|
|
rebateCalc: {
|
|
component: RebateCalcSection,
|
|
title: "个人返点复核",
|
|
},
|
|
};
|
|
|
|
export default hocAuth(function Page(props: CommonComponent) {
|
|
const { router, isInitialized, setIsInitialized, role, setLoading } = props;
|
|
|
|
const orderId = router.params
|
|
.orderId as BusinessAPI.PurchaseOrderVO["orderId"];
|
|
|
|
// 费用项目列表
|
|
const [costItemVOList, setCostItemVOList] = useState<
|
|
BusinessAPI.CostItemVO[]
|
|
>([]);
|
|
|
|
// 获取费用项目列表
|
|
useEffect(() => {
|
|
const fetchCostItems = async () => {
|
|
try {
|
|
const { data } = await business.costItem.listCostItem({
|
|
costItemListQry: {
|
|
status: true,
|
|
},
|
|
});
|
|
setCostItemVOList(data.data || []);
|
|
} catch (error) {
|
|
console.error("获取费用项目列表失败:", error);
|
|
}
|
|
};
|
|
|
|
fetchCostItems();
|
|
}, []);
|
|
|
|
const [purchaseOrderVO, setPurchaseOrderVO] =
|
|
useState<BusinessAPI.PurchaseOrderVO>();
|
|
|
|
// 驳回原因弹窗相关状态
|
|
const [rejectVisible, setRejectVisible] = useState(false);
|
|
const [rejectReason, setRejectReason] = useState("");
|
|
|
|
// 驳回操作
|
|
const handleReject = () => {
|
|
setRejectVisible(true);
|
|
};
|
|
|
|
// 确认驳回
|
|
const confirmReject = async () => {
|
|
if (!purchaseOrderVO) {
|
|
Toast.show("toast", {
|
|
icon: "fail",
|
|
title: "提示",
|
|
content: "请稍后再试",
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (!rejectReason.trim()) {
|
|
Toast.show("toast", {
|
|
icon: "fail",
|
|
title: "提示",
|
|
content: "请输入驳回原因",
|
|
});
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// 调用驳回API
|
|
await business.purchaseOrder.rejectApprovePurchaseOrder({
|
|
orderId: purchaseOrderVO?.orderId,
|
|
rejectReason: rejectReason,
|
|
});
|
|
|
|
Toast.show("toast", {
|
|
icon: "success",
|
|
title: "提示",
|
|
content: "驳回成功,正在跳转我的审核...",
|
|
});
|
|
|
|
// 关闭弹窗
|
|
setRejectVisible(false);
|
|
setRejectReason("");
|
|
|
|
setTimeout(() => {
|
|
Taro.redirectTo({ url: buildUrl("/pages/purchase/reviewer/list") });
|
|
}, 1000);
|
|
|
|
// 返回采购单页面
|
|
} catch (error) {
|
|
Toast.show("toast", {
|
|
icon: "fail",
|
|
title: "提示",
|
|
content: error.message || "驳回失败",
|
|
});
|
|
}
|
|
};
|
|
|
|
const [originPrincipal, setOriginPrincipal] = useState("");
|
|
|
|
// 暂存和提交审核的Dialog状态
|
|
const [saveDialogVisible, setSaveDialogVisible] = useState(false);
|
|
const [submitDialogVisible, setSubmitDialogVisible] = useState(false);
|
|
|
|
// 控制更多操作的ActionSheet显示状态
|
|
const [moreActionVisible, setMoreActionVisible] = useState(false);
|
|
|
|
const [dealerRebateCustomerVOList, setDealerRebateCustomerVOList] =
|
|
useState<BusinessAPI.DealerRebateCustomerVO[]>();
|
|
|
|
const initDealer = async (dealerId: BusinessAPI.DealerVO["dealerId"]) => {
|
|
const {
|
|
data: { data: dealerRebateCustomerVOList },
|
|
} = await business.dealerRebateCustomer.listDealerRebateCustomer({
|
|
dealerRebateCustomerListQry: {
|
|
dealerId: dealerId,
|
|
status: true,
|
|
},
|
|
});
|
|
|
|
setDealerRebateCustomerVOList(dealerRebateCustomerVOList);
|
|
};
|
|
|
|
// 暂存操作
|
|
const handleSave = () => {
|
|
setSaveDialogVisible(true);
|
|
};
|
|
|
|
// 确认暂存
|
|
const confirmSave = async () => {
|
|
// 关闭对话框
|
|
setSaveDialogVisible(false);
|
|
|
|
// 这里应该调用暂存API
|
|
console.log("暂存数据:", purchaseOrderVO);
|
|
|
|
const { data } = await business.purchaseOrder.approvePurchaseOrder({
|
|
...purchaseOrderVO!,
|
|
draft: true,
|
|
});
|
|
|
|
if (data.success) {
|
|
Toast.show("toast", {
|
|
icon: "success",
|
|
title: "提示",
|
|
content: "暂存成功",
|
|
});
|
|
// 返回采购单页面
|
|
Taro.redirectTo({ url: "/pages/purchase/reviewer/list" });
|
|
}
|
|
};
|
|
|
|
// 提交老板审核操作
|
|
const handleSubmit = () => {
|
|
setSubmitDialogVisible(true);
|
|
};
|
|
|
|
// 确认提交老板审核
|
|
const confirmSubmit = async () => {
|
|
// 关闭对话框
|
|
setSubmitDialogVisible(false);
|
|
|
|
// 表单校验
|
|
const errorMsg = validateForm();
|
|
if (errorMsg) {
|
|
Toast.show("toast", {
|
|
icon: "fail",
|
|
title: "校验失败",
|
|
content: errorMsg,
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 这里应该调用提交审核API
|
|
console.log("提交老板审核:", purchaseOrderVO);
|
|
|
|
const { data } = await business.purchaseOrder.approvePurchaseOrder({
|
|
...purchaseOrderVO!,
|
|
draft: false,
|
|
});
|
|
|
|
if (data.success) {
|
|
Toast.show("toast", {
|
|
icon: "success",
|
|
title: "提示",
|
|
content: "提交审核成功",
|
|
});
|
|
// 跳转到提交结果页面
|
|
Taro.redirectTo({
|
|
url: buildUrl("/pages/purchase/reviewer/submitted", {
|
|
orderId: purchaseOrderVO!.orderId,
|
|
}),
|
|
});
|
|
}
|
|
};
|
|
|
|
// 表单校验
|
|
const validateForm = () => {
|
|
// 校验销售方
|
|
if (!purchaseOrderVO?.orderCompany?.companyId) {
|
|
return "请选择销售方";
|
|
}
|
|
|
|
// 校验经销商
|
|
if (!purchaseOrderVO?.orderDealer?.dealerId) {
|
|
return "请选择经销商";
|
|
}
|
|
|
|
// 校验本车次号
|
|
if (!purchaseOrderVO?.orderVehicle?.vehicleNo) {
|
|
return "请输入本车次号";
|
|
}
|
|
|
|
// 校验运费类型
|
|
if (!purchaseOrderVO?.orderVehicle?.priceType) {
|
|
return "请选择运费类型";
|
|
}
|
|
|
|
// 校验市场报价的报价方式
|
|
if (!purchaseOrderVO?.pricingMethod) {
|
|
return "请选择市场报价的报价方式";
|
|
}
|
|
|
|
// 校验市场报价的销售单价
|
|
purchaseOrderVO.orderSupplierList.forEach(
|
|
(supplier: BusinessAPI.OrderSupplier) => {
|
|
if (!supplier.salePrice || supplier.salePrice <= 0) {
|
|
return "请填写市场报价的销售单价";
|
|
}
|
|
},
|
|
);
|
|
|
|
return null;
|
|
};
|
|
|
|
const init = async (orderId: BusinessAPI.PurchaseOrderVO["orderId"]) => {
|
|
const { data } = await business.purchaseOrder.showPurchaseOrder({
|
|
purchaseOrderShowQry: {
|
|
orderId,
|
|
},
|
|
});
|
|
|
|
if (data.success) {
|
|
setPurchaseOrderVO(data.data);
|
|
|
|
await initDealer(data.data?.orderDealer?.dealerId!);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (orderId && !isInitialized) {
|
|
setLoading(true);
|
|
init(orderId).then(() => {
|
|
setIsInitialized(true);
|
|
setLoading(false);
|
|
});
|
|
}
|
|
}, []);
|
|
|
|
useDidShow(() => {
|
|
if (orderId && isInitialized) {
|
|
init(orderId).then();
|
|
}
|
|
});
|
|
|
|
if (!purchaseOrderVO || !dealerRebateCustomerVOList) {
|
|
return;
|
|
}
|
|
|
|
const calculator = new PurchaseOrderCalculator(purchaseOrderVO);
|
|
const personalProfit = calculator.getPersonalProfit();
|
|
|
|
return (
|
|
<>
|
|
<View
|
|
className={"overflow-x-hidden overflow-y-auto bg-[#D1D5DB]"}
|
|
id={"purchase-order-audit"}
|
|
>
|
|
<View className={"flex flex-col gap-2.5 p-2.5"}>
|
|
{/* 顶部导航 */}
|
|
<View className="flex flex-col gap-2.5 rounded-md bg-white p-2.5 shadow-sm">
|
|
<View className={"flex flex-row justify-between gap-2.5"}>
|
|
<View className="text-lg font-bold">
|
|
{purchaseOrderVO?.orderDealer?.shortName || "-"} 第
|
|
{purchaseOrderVO?.orderVehicle?.vehicleNo || "-"}车
|
|
</View>
|
|
|
|
<State
|
|
state={purchaseOrderVO.state}
|
|
stateMap={purchaseOrder.stateMap}
|
|
/>
|
|
</View>
|
|
<View className="text-neutral-darker text-sm font-medium">
|
|
{purchaseOrderVO?.orderVehicle.origin || "-"} 至{" "}
|
|
{purchaseOrderVO?.orderVehicle.destination || "-"}
|
|
</View>
|
|
|
|
{/* 展示产地负责人*/}
|
|
<View className="flex flex-row items-center gap-2.5">
|
|
<View className="flex-shrink-0 text-base font-bold text-gray-900">
|
|
产地负责人
|
|
</View>
|
|
|
|
<View
|
|
className={`flex h-10 flex-1 items-center rounded-md border-4 border-gray-300`}
|
|
>
|
|
<Input
|
|
type="text"
|
|
disabled={
|
|
role === "boss" ||
|
|
(role === "reviewer" &&
|
|
purchaseOrderVO.state !== "WAITING_AUDIT")
|
|
}
|
|
placeholder="请输入产地负责人姓名"
|
|
value={originPrincipal || purchaseOrderVO.createdByName}
|
|
onChange={(value) => setOriginPrincipal(value)}
|
|
onBlur={() => {
|
|
// 更新采购订单中的产地负责人
|
|
setPurchaseOrderVO((prev) => ({
|
|
...prev!,
|
|
originPrincipal: originPrincipal,
|
|
}));
|
|
}}
|
|
className="w-full bg-transparent"
|
|
/>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
|
|
{/* 循环渲染各部分内容 */}
|
|
{Object.keys(sections).map((sectionKey) => {
|
|
const section = sections[sectionKey];
|
|
const orderDealer = purchaseOrderVO.orderDealer;
|
|
|
|
if (
|
|
!orderDealer?.enableCompanyRebate &&
|
|
sectionKey === "taxSubsidy"
|
|
) {
|
|
return null;
|
|
}
|
|
|
|
if (
|
|
!orderDealer?.enableAccrualTax &&
|
|
sectionKey === "taxProvision"
|
|
) {
|
|
return null;
|
|
}
|
|
|
|
if (!orderDealer?.enableShare && sectionKey === "costDifference") {
|
|
return null;
|
|
}
|
|
|
|
// 如果没有返点人这个模块,则不渲染
|
|
if (
|
|
(!dealerRebateCustomerVOList ||
|
|
dealerRebateCustomerVOList.length === 0) &&
|
|
sectionKey === "rebateCalc"
|
|
) {
|
|
return null;
|
|
}
|
|
|
|
if (
|
|
(!purchaseOrderVO.orderPackageList ||
|
|
purchaseOrderVO?.orderPackageList.length === 0) &&
|
|
sectionKey === "emptyBoxInfo"
|
|
) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<View className="text-sm font-bold">{section.title}</View>
|
|
<View
|
|
className={`overflow-x-auto rounded-md rounded-b-lg bg-white p-2.5 shadow-sm `}
|
|
>
|
|
<section.component
|
|
readOnly={purchaseOrderVO.state !== "WAITING_AUDIT"}
|
|
purchaseOrderVO={purchaseOrderVO}
|
|
onChange={setPurchaseOrderVO}
|
|
costItemVOList={costItemVOList}
|
|
/>
|
|
</View>
|
|
</>
|
|
);
|
|
})}
|
|
|
|
{/* 个人利润 */}
|
|
<View
|
|
className={
|
|
"flex flex-row items-center rounded-md border border-[#FFBB96] p-4"
|
|
}
|
|
style={{
|
|
background: "linear-gradient(100deg, #FFF2E8 25%, #FFE7D9 75%)",
|
|
}}
|
|
>
|
|
<View className={"flex-1 text-center"}>个人利润</View>
|
|
{personalProfit > 0 ? (
|
|
<View
|
|
className={"text-primary flex-1 text-left text-2xl font-bold"}
|
|
>
|
|
¥ {personalProfit || "-"}
|
|
</View>
|
|
) : (
|
|
<View
|
|
className={"flex-1 text-left text-2xl font-bold text-red-500"}
|
|
>
|
|
¥ {personalProfit || "-"}
|
|
</View>
|
|
)}
|
|
</View>
|
|
</View>
|
|
</View>
|
|
|
|
{/* 按钮操作 */}
|
|
<View className={"sticky bottom-0 z-10 bg-white"} id={"bottomBar"}>
|
|
<View className="flex justify-between gap-2 border-t border-gray-200 p-2.5">
|
|
{purchaseOrderVO.state === "WAITING_AUDIT" && (
|
|
<>
|
|
<View className={"flex-1"}>
|
|
<Button
|
|
block
|
|
type={"default"}
|
|
size={"xlarge"}
|
|
className="bg-gray-200 text-gray-700"
|
|
onClick={() => setMoreActionVisible(true)}
|
|
>
|
|
更多操作
|
|
</Button>
|
|
</View>
|
|
<View className={"flex-1"}>
|
|
<Button
|
|
block
|
|
type={"primary"}
|
|
size={"xlarge"}
|
|
className="bg-primary text-white"
|
|
onClick={handleSubmit}
|
|
>
|
|
提交老板审批
|
|
</Button>
|
|
</View>
|
|
</>
|
|
)}
|
|
{purchaseOrderVO.state === "WAITING_BOSS_APPROVE" && (
|
|
<>
|
|
<View className={"flex-1"}>
|
|
<Button
|
|
block
|
|
type={"default"}
|
|
size={"xlarge"}
|
|
onClick={() => {
|
|
Taro.redirectTo({ url: "/pages/main/index/index" });
|
|
}}
|
|
>
|
|
返回首页
|
|
</Button>
|
|
</View>
|
|
</>
|
|
)}
|
|
</View>
|
|
<SafeArea position={"bottom"} />
|
|
</View>
|
|
|
|
{/* 暂存确认对话框 */}
|
|
<Dialog
|
|
visible={saveDialogVisible}
|
|
title="确认暂存"
|
|
content="确定要暂存当前采购订单吗?"
|
|
onCancel={() => setSaveDialogVisible(false)}
|
|
onConfirm={confirmSave}
|
|
/>
|
|
|
|
{/* 提交审核确认对话框 */}
|
|
<Dialog
|
|
visible={submitDialogVisible}
|
|
title="提交审核"
|
|
content="确定要提交给老板审核吗?"
|
|
onCancel={() => setSubmitDialogVisible(false)}
|
|
onConfirm={confirmSubmit}
|
|
/>
|
|
|
|
{/* 更多操作ActionSheet */}
|
|
<ActionSheet
|
|
title="更多操作"
|
|
visible={moreActionVisible}
|
|
options={[
|
|
{
|
|
name: "暂存",
|
|
},
|
|
{
|
|
name: "驳回",
|
|
description: "此采购单将退回到产地录入员",
|
|
danger: true,
|
|
},
|
|
]}
|
|
onCancel={() => setMoreActionVisible(false)}
|
|
onSelect={(item) => {
|
|
setMoreActionVisible(false);
|
|
if (item.name === "暂存") {
|
|
// 暂存操作
|
|
handleSave();
|
|
} else if (item.name === "驳回") {
|
|
// 驳回操作
|
|
handleReject();
|
|
}
|
|
}}
|
|
cancelText="取消"
|
|
/>
|
|
|
|
{/* 驳回原因弹窗 */}
|
|
<Popup
|
|
visible={rejectVisible}
|
|
position="bottom"
|
|
onClose={() => {
|
|
setRejectVisible(false);
|
|
setRejectReason("");
|
|
}}
|
|
closeable
|
|
destroyOnClose
|
|
title={"驳回"}
|
|
description={"此采购单将退回到产地录入员"}
|
|
>
|
|
<View className="p-4">
|
|
<View className="mb-4 text-lg font-bold">请输入驳回原因</View>
|
|
<TextArea
|
|
value={rejectReason}
|
|
onChange={(value) => setRejectReason(value)}
|
|
placeholder="请输入驳回原因"
|
|
rows={4}
|
|
maxLength={200}
|
|
showCount
|
|
/>
|
|
<View className="mt-4 flex justify-between gap-2">
|
|
<View className="flex-1">
|
|
<Button
|
|
size={"large"}
|
|
block
|
|
type="default"
|
|
onClick={() => {
|
|
setRejectVisible(false);
|
|
setRejectReason("");
|
|
}}
|
|
>
|
|
取消
|
|
</Button>
|
|
</View>
|
|
<View className="flex-1">
|
|
<Button
|
|
size={"large"}
|
|
block
|
|
type="primary"
|
|
onClick={confirmReject}
|
|
>
|
|
确认
|
|
</Button>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
<SafeArea position="bottom" />
|
|
</Popup>
|
|
</>
|
|
);
|
|
});
|