feat(purchase): 优化采购单驳回逻辑与界面交互

- 在 PurchaseOrderRejectApprove 和 PurchaseOrderRejectFinal 组件中增加对 API 返回结果的判断,若驳回失败则展示错误提示
- 调整驳回弹窗内输入框样式,增强视觉效果与用户体验
- 为多个采购单相关 Section 组件传入统一的 calculator 实例,避免重复创建提升性能
- 移除冗余的状态管理逻辑,直接使用 calculator 计算值进行展示
- 更新采购单状态常量定义及文案描述,新增“已驳回”状态及相关路径配置
- 修改工作台菜单标题及跳转路径,如将“我的草稿”改为“待提交草稿”
- 调整审批页面路由结构,重命名 approver/approve.tsx 为 audit/audit.tsx 并更新引用路径
- 新增 purchaser/result.tsx 页面用于展示采购单提交审核后的结果,并支持查看详情或返回首页
- 修复部分条件渲染逻辑,确保仅在满足条件下才渲染特定内容
- 升级 app 版本号从 v0.0.22 到 v0.0.24
This commit is contained in:
shenyifei 2025-11-20 15:58:53 +08:00
parent 19cf99863f
commit 5eefd62d85
39 changed files with 646 additions and 930 deletions

View File

@ -18,23 +18,24 @@ config = {
{
root: "pages/purchase",
pages: [
"history",
// 采购员(市场/产地)
"purchaser/create",
"purchaser/history",
"purchaser/preview",
"purchaser/draft",
"purchaser/result",
// 采购员(草稿)
"purchaser/draft/list",
// 审核员(报价)
"reviewer/list",
"reviewer/audit",
"reviewer/history",
"reviewer/submitted",
"reviewer/audit/audit",
"reviewer/audit/list",
"reviewer/audit/result",
// 审批员(老板)
"approver/list",
"approver/approve",
"approver/approved",
"approver/history",
"approver/audit/audit",
"approver/audit/list",
"approver/audit/result",
],
},
// 发货单

View File

@ -42,11 +42,21 @@ export default function PurchaseOrderRejectApprove(
try {
// 调用驳回API
await business.purchaseOrder.rejectApprovePurchaseOrder({
const {
data: { success, errMessage },
} = await business.purchaseOrder.rejectApprovePurchaseOrder({
orderId: purchaseOrderVO.orderId,
rejectReason: rejectReason,
});
if (!success) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: errMessage || "驳回失败",
});
return;
}
Toast.show("toast", {
icon: "success",
title: "提示",
@ -98,16 +108,21 @@ export default function PurchaseOrderRejectApprove(
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="p-2.5">
<View className="mb-2.5 text-sm font-bold"></View>
<View
className={`flex w-full items-center rounded-md border-4 border-gray-300`}
>
<TextArea
value={rejectReason}
onChange={(value) => setRejectReason(value)}
placeholder="请输入驳回原因"
rows={4}
maxLength={200}
showCount
/>
</View>
<View className="mt-4 flex justify-between gap-2">
<View className="flex-1">
<Button
@ -134,6 +149,7 @@ export default function PurchaseOrderRejectApprove(
</View>
</View>
</View>
<SafeArea position="bottom" />
</Popup>
</>

View File

@ -42,10 +42,21 @@ export default function PurchaseOrderRejectFinal(
try {
// 调用驳回API
await business.purchaseOrder.rejectFinalPurchaseOrder({
const {
data: { success, errMessage },
} = await business.purchaseOrder.rejectFinalPurchaseOrder({
orderId: purchaseOrderVO.orderId,
});
if (!success) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: errMessage || "驳回失败",
});
return;
}
Toast.show("toast", {
icon: "success",
title: "提示",
@ -97,16 +108,21 @@ export default function PurchaseOrderRejectFinal(
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="p-2.5">
<View className="mb-2.5 text-sm font-bold"></View>
<View
className={`flex w-full items-center rounded-md border-4 border-gray-300`}
>
<TextArea
value={rejectReason}
onChange={(value) => setRejectReason(value)}
placeholder="请输入驳回原因"
rows={4}
maxLength={200}
showCount
/>
</View>
<View className="mt-4 flex justify-between gap-2">
<View className="flex-1">
<Button

View File

@ -8,30 +8,27 @@ export default function CostDifferenceSection(props: {
purchaseOrderVO: BusinessAPI.PurchaseOrderVO;
onChange?: (purchaseOrderVO: BusinessAPI.PurchaseOrderVO) => void;
readOnly?: boolean;
calculator: PurchaseOrderCalculator;
}) {
const { purchaseOrderVO, onChange, readOnly } = props;
const { purchaseOrderVO, onChange, readOnly, calculator } = props;
const orderDealer = purchaseOrderVO.orderDealer;
const calculator = new PurchaseOrderCalculator(purchaseOrderVO);
const [visible, setVisible] = useState(false);
// 主状态,用于页面显示
const [costDifference, setCostDifference] = useState<number>(
orderDealer?.costDifference || 0,
);
const costDifference =
orderDealer?.costDifference || calculator.getCostDifference();
// 弹窗内的临时状态
const [tempCostDifference, setTempCostDifference] = useState<number>(
orderDealer?.costDifference || 0,
);
const profitSharing =
calculator.getShareProfit() || orderDealer?.profitSharing || 0;
const profitSharing = calculator.getShareProfit() || 0;
useEffect(() => {
if (!orderDealer?.costDifference) {
const defaultCostDifference = calculator.getCostDifference();
setCostDifference(defaultCostDifference);
setTempCostDifference(defaultCostDifference);
// 更新父组件的状态
onChange?.({
@ -47,9 +44,6 @@ export default function CostDifferenceSection(props: {
// 保存调整成本
const saveCostDifference = () => {
// 将临时状态的值应用到主状态
setCostDifference(tempCostDifference);
onChange?.({
...purchaseOrderVO,
orderDealer: {

View File

@ -7,9 +7,9 @@ export default function CostSummarySection(props: {
purchaseOrderVO: BusinessAPI.PurchaseOrderVO;
onChange?: (purchaseOrderVO: BusinessAPI.PurchaseOrderVO) => void;
readOnly?: boolean;
calculator: PurchaseOrderCalculator;
}) {
const { purchaseOrderVO } = props;
const calculator = new PurchaseOrderCalculator(purchaseOrderVO);
const { calculator } = props;
// 计算总净重
const totalNetWeight = calculator.getTotalGrossWeight();

View File

@ -9,8 +9,9 @@ export default function MarketPriceSection(props: {
purchaseOrderVO: BusinessAPI.PurchaseOrderVO;
onChange?: (purchaseOrderVO: BusinessAPI.PurchaseOrderVO) => void;
readOnly?: boolean;
calculator: PurchaseOrderCalculator;
}) {
const { purchaseOrderVO, onChange, readOnly } = props;
const { purchaseOrderVO, onChange, readOnly, calculator } = props;
const [pricingMethod, setPricingMethod] =
useState<BusinessAPI.PurchaseOrderVO["pricingMethod"]>();
@ -130,7 +131,6 @@ export default function MarketPriceSection(props: {
}
}, [purchaseOrderVO.pricingMethod]);
const calculator = new PurchaseOrderCalculator(purchaseOrderVO);
// 销售金额
const saleAmount = calculator.getSalesAmount();

View File

@ -9,9 +9,9 @@ export default function RebateCalcSection(props: {
purchaseOrderVO: BusinessAPI.PurchaseOrderVO;
onChange?: (purchaseOrderVO: BusinessAPI.PurchaseOrderVO) => void;
readOnly?: boolean;
calculator: PurchaseOrderCalculator;
}) {
const { purchaseOrderVO, onChange, readOnly } = props;
const calculator = new PurchaseOrderCalculator(purchaseOrderVO);
const { purchaseOrderVO, onChange, readOnly, calculator } = props;
const totalNetWeight = calculator.getTotalNetWeight();
const [orderRebate, setOrderRebate] = useState<BusinessAPI.OrderRebate>({});

View File

@ -8,18 +8,15 @@ export default function TaxProvisionSection(props: {
purchaseOrderVO: BusinessAPI.PurchaseOrderVO;
onChange?: (purchaseOrderVO: BusinessAPI.PurchaseOrderVO) => void;
readOnly?: boolean;
calculator: PurchaseOrderCalculator;
}) {
const { purchaseOrderVO, onChange, readOnly } = props;
const { purchaseOrderVO, onChange, readOnly, calculator } = props;
const orderDealer = purchaseOrderVO.orderDealer;
const calculator = new PurchaseOrderCalculator(purchaseOrderVO);
const [visible, setVisible] = useState(false);
// 主状态 - 用于显示在页面上的值
const [displayTaxProvision, setDisplayTaxProvision] = useState<number>(
orderDealer?.taxProvision || 0,
);
const displayTaxProvision =
orderDealer.taxProvision || calculator.getDefaultTaxProvision();
// 临时状态 - 用于弹窗内编辑的值
const [tempTaxProvision, setTempTaxProvision] = useState<number>(
@ -29,7 +26,6 @@ export default function TaxProvisionSection(props: {
useEffect(() => {
if (!orderDealer?.taxProvision) {
const defaultValue = calculator.getDefaultTaxProvision();
setDisplayTaxProvision(defaultValue);
setTempTaxProvision(defaultValue);
// 更新父组件的状态
onChange?.({
@ -50,9 +46,6 @@ export default function TaxProvisionSection(props: {
// 保存计提税金
const saveTaxProvision = () => {
// 更新显示值
setDisplayTaxProvision(tempTaxProvision);
// 更新父组件的状态
onChange?.({
...purchaseOrderVO,

View File

@ -8,17 +8,16 @@ export default function TaxSubsidySection(props: {
purchaseOrderVO: BusinessAPI.PurchaseOrderVO;
onChange?: (purchaseOrderVO: BusinessAPI.PurchaseOrderVO) => void;
readOnly?: boolean;
calculator: PurchaseOrderCalculator;
}) {
const { purchaseOrderVO, onChange, readOnly } = props;
const { purchaseOrderVO, onChange, readOnly, calculator } = props;
const orderDealer = purchaseOrderVO.orderDealer;
const calculator = new PurchaseOrderCalculator(purchaseOrderVO);
const [visible, setVisible] = useState(false);
// 主状态 - 用于显示在页面上的值
const [displayTaxSubsidy, setDisplayTaxSubsidy] = useState<number>(
orderDealer?.taxSubsidy || 0,
);
const displayTaxSubsidy =
orderDealer?.taxSubsidy || calculator.getDefaultTaxSubsidy();
// 临时状态 - 用于弹窗内编辑的值
const [tempTaxSubsidy, setTempTaxSubsidy] = useState<number>(
@ -28,7 +27,6 @@ export default function TaxSubsidySection(props: {
useEffect(() => {
if (!orderDealer?.taxSubsidy) {
const defaultValue = calculator.getDefaultTaxSubsidy();
setDisplayTaxSubsidy(defaultValue);
setTempTaxSubsidy(defaultValue);
// 更新父组件的状态
onChange?.({
@ -49,9 +47,6 @@ export default function TaxSubsidySection(props: {
// 保存税费补贴
const saveTaxSubsidy = () => {
// 更新显示值
setDisplayTaxSubsidy(tempTaxSubsidy);
// 更新父组件的状态
onChange?.({
...purchaseOrderVO,

View File

@ -1,2 +1,2 @@
// App 相关常量
export const APP_VERSION = "v0.0.22";
export const APP_VERSION = "v0.0.24";

View File

@ -1,23 +1,36 @@
/**
* 采购订单状态: 0_草稿1_审核中2_审批中3_待发货4_已发货5_已付款6_已完结7_已驳回8_已关闭
DRAFT(0, "草稿"),
WAITING_AUDIT(1, "审核中"),
WAITING_APPROVE(2, "审批中"),
WAITING_SHIPMENT(3, "待发货"),
SHIPPED(4, "已发货"),
PAID(5, "已付款"),
COMPLETED(6, "已完结"),
REJECTED(7, "已驳回"),
*/
const stateList = [
{
title: "全部",
value: "ALL",
},
{
title: "草稿",
value: "DRAFT",
},
{
title: "待审核",
title: "审核中",
value: "WAITING_AUDIT",
},
{
title: "待老板审批",
value: "WAITING_BOSS_APPROVE",
title: "审批中",
value: "WAITING_APPROVE",
},
{
title: "已发货(待付款)",
value: "SHIPPING",
title: "待发货",
value: "WAITING_SHIPMENT",
},
{
title: "已发货",
value: "SHIPPED",
},
{
title: "已付款",
@ -27,6 +40,10 @@ const stateList = [
title: "已完结",
value: "COMPLETED",
},
{
title: "已驳回",
value: "REJECTED",
},
];
// 定义需求状态映射 - 优化颜色方案
@ -43,13 +60,19 @@ const stateMap = {
bgColor: "#DBEAFE",
borderColor: "#3B82F6",
},
WAITING_BOSS_APPROVE: {
WAITING_APPROVE: {
label: "待老板审批",
color: "#7C2D12",
bgColor: "#FDE68A",
borderColor: "#F59E0B",
},
SHIPPING: {
WAITING_SHIPMENT: {
label: "待发货",
color: "#7C2D12",
bgColor: "#FDE68A",
borderColor: "#F59E0B",
},
SHIPPED: {
label: "已发货(待付款)",
color: "#86198F",
bgColor: "#F3E8FF",
@ -66,6 +89,12 @@ const stateMap = {
color: "#374151",
bgColor: "#E5E7EB",
borderColor: "#6B7280",
},
REJECTED: {
label: "已驳回",
color: "#991B1B",
bgColor: "#FEE2E2",
borderColor: "#EF4444",
}
};

View File

@ -3,7 +3,7 @@ const reviewCardMap = {
"origin-entry": [
{
id: "draftPurchase",
title: "我的草稿",
title: "待提交草稿",
count: 0,
unit: "采购单",
iconColor: "var(--color-blue-600)",
@ -11,12 +11,20 @@ const reviewCardMap = {
},
{
id: "pendingPurchase",
title: "我的采购单",
title: "审核中",
count: 10,
unit: "采购单",
iconColor: "var(--color-green-600)",
icon: "clipboard-list",
},
{
id: "rejectPurchase",
title: "被驳回",
count: 10,
unit: "采购单",
iconColor: "var(--color-red-600)",
icon: "file-signature",
},
{
id: "pendingInvoiceUpload",
title: "待上传发票",
@ -106,23 +114,23 @@ const quickActionMap = {
"origin-entry": [
{
id: "myDraft",
title: "我的草稿",
title: "待提交的采购单",
icon: "folder",
iconColor: "var(--color-blue-600)",
bgColorClass: "bg-blue-100",
path: "/pages/purchase/purchaser/draft",
path: "/pages/purchase/purchaser/draft/list",
},
{
id: "history",
title: "采购记录",
title: "我的采购单",
icon: "clipboard-list",
iconColor: "var(--color-green-600)",
bgColorClass: "bg-green-100",
path: "/pages/purchase/purchaser/history",
path: "/pages/purchase/history",
},
{
id: "invoiceUpload",
title: "上传发票",
title: "上传瓜农发票",
icon: "file-invoice",
iconColor: "var(--color-orange-600)",
bgColorClass: "bg-orange-100",
@ -144,7 +152,7 @@ const quickActionMap = {
icon: "folder",
iconColor: "var(--color-blue-600)",
bgColorClass: "bg-blue-100",
path: "/pages/purchase/purchaser/draft",
path: "/pages/purchase/purchaser/draft/list",
},
{
id: "history",
@ -152,7 +160,7 @@ const quickActionMap = {
icon: "clipboard-list",
iconColor: "var(--color-green-600)",
bgColorClass: "bg-green-100",
path: "/pages/purchase/purchaser/history",
path: "/pages/purchase/history",
},
],
reviewer: [
@ -162,7 +170,7 @@ const quickActionMap = {
icon: "file-signature",
iconColor: "var(--color-primary)",
bgColorClass: "bg-primary/10",
path: "/pages/purchase/reviewer/list",
path: "/pages/purchase/reviewer/audit/list",
},
{
id: "history",
@ -170,7 +178,7 @@ const quickActionMap = {
icon: "clipboard-list",
iconColor: "var(--color-green-600)",
bgColorClass: "bg-green-100",
path: "/pages/purchase/reviewer/history",
path: "/pages/purchase/history",
},
{
id: "shipOrder",
@ -216,7 +224,7 @@ const quickActionMap = {
icon: "file-signature",
iconColor: "var(--color-primary)",
bgColorClass: "bg-primary/10",
path: "/pages/purchase/approver/list",
path: "/pages/purchase/approver/audit/list",
},
{
id: "history",
@ -224,7 +232,7 @@ const quickActionMap = {
icon: "clipboard-list",
iconColor: "var(--color-green-600)",
bgColorClass: "bg-green-100",
path: "/pages/purchase/approver/history",
path: "/pages/purchase/history",
},
{
id: "shipOrder",

View File

@ -121,9 +121,7 @@ export default hocAuth(function Page(props: CommonComponent) {
color={action.iconColor}
></Icon>
</View>
<View
className={`overflow-hidden text-xs text-ellipsis whitespace-nowrap text-gray-700`}
>
<View className={`text-xs text-gray-700`}>
{action.title}
</View>
</View>

View File

@ -106,25 +106,23 @@ export default hocAuth(function Page(props: CommonComponent) {
)}
{purchaseOrderVO.orderCostList.map((item) => {
return (
<View
className="cost-item flex flex-col px-3 py-2"
key={item.itemId}
>
<View className="text-sm text-gray-500">{item.name}</View>
<View className="font-medium">
{item.price * item.count}
</View>
{item.name === "人工费" && (
<View className="text-xs text-gray-500">
:
{
purchaseOrderVO.orderCostList.filter(
(item) => item.costType === "HUMAN_COST",
)[0].principal
}
item.price * item.count > 0 && (
<View
className="cost-item flex flex-col px-3 py-2"
key={item.itemId}
>
<View className="text-sm text-gray-500">{item.name}</View>
<View className="font-medium">
{item.price * item.count}
</View>
)}
</View>
{item.name === "人工费" && (
<View className="text-xs text-gray-500">
:
{purchaseOrderVO.foreman}
</View>
)}
</View>
)
);
})}
{purchaseOrderVO.orderDealer?.taxSubsidy && (
@ -183,7 +181,7 @@ export default hocAuth(function Page(props: CommonComponent) {
{/* 按钮操作 */}
<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_BOSS_APPROVE" && (
{purchaseOrderVO.state === "WAITING_APPROVE" && (
<>
<View className={"flex-1"}>
<PurchaseOrderRejectFinal
@ -191,7 +189,9 @@ export default hocAuth(function Page(props: CommonComponent) {
size={"xlarge"}
onFinish={() => {
// 返回首页
Taro.redirectTo({ url: "/pages/purchase/approver/list" });
Taro.redirectTo({
url: "/pages/purchase/approver/audit/list",
});
}}
/>
</View>
@ -202,7 +202,7 @@ export default hocAuth(function Page(props: CommonComponent) {
onFinish={() => {
// 关闭当前页面并跳转到采购单审核通过页面
Taro.redirectTo({
url: buildUrl(`/pages/purchase/approver/approved`, {
url: buildUrl(`/pages/purchase/approver/audit/result`, {
orderId: purchaseOrderVO?.orderId,
}),
});

View File

@ -168,7 +168,7 @@ export default hocAuth(function Page(props: CommonComponent) {
</View>
<View className={"py-2.5"}>
<View className={"flex flex-row justify-end gap-2"}>
{purchaseOrderVO.state === "WAITING_BOSS_APPROVE" && (
{purchaseOrderVO.state === "WAITING_APPROVE" && (
<View className={"flex flex-row justify-end gap-2"}>
<PurchaseOrderRejectFinal
purchaseOrderVO={purchaseOrderVO}
@ -182,9 +182,12 @@ export default hocAuth(function Page(props: CommonComponent) {
size={"small"}
onClick={(e) => {
Taro.navigateTo({
url: buildUrl("/pages/purchase/approver/approve", {
orderId: purchaseOrderVO.orderId,
}),
url: buildUrl(
"/pages/purchase/approver/audit/audit",
{
orderId: purchaseOrderVO.orderId,
},
),
});
e.stopPropagation();
}}
@ -205,7 +208,7 @@ export default hocAuth(function Page(props: CommonComponent) {
} = await business.purchaseOrder.pagePurchaseOrder({
purchaseOrderPageQry: {
...params,
state: "WAITING_BOSS_APPROVE",
state: "WAITING_APPROVE",
},
});

View File

@ -1,335 +0,0 @@
import {
ActionType,
DealerPicker,
Icon,
PageList,
PurchaseOrderRejectFinal,
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/buildUrl";
import { Button } from "@nutui/nutui-react-taro";
import purchaseOrder from "@/constant/purchaseOrder";
import dayjs from "dayjs";
import { formatCurrency } from "@/utils/format";
import { PurchaseOrderCalculator } from "@/utils/PurchaseOrderCalculator";
export default hocAuth(function Page(props: CommonComponent) {
const { role, shareOptions } = props;
const [state, setState] =
useState<BusinessAPI.PurchaseOrderPageQry["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: "请输入车牌号",
},
{
key: "supplierName",
name: "瓜农姓名",
placeholder: "请输入瓜农姓名",
},
],
},
tabs: {
activeKey: "state",
defaultActiveKey: state || "ALL",
items: purchaseOrder.stateList.filter((item) => {
if (role === "reviewer") {
return item.value !== "DRAFT";
}
return item;
}),
onChange: (item) => {
setState(item as BusinessAPI.PurchaseOrderPageQry["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-2 border-primary flex h-6 items-center rounded-md 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>
),
actions: [
role === "origin-entry" && (
<View className={"flex flex-row gap-2 p-3"} key={"create"}>
<View className={"flex-1"}>
<View
className="bg-primary flex w-full flex-col items-center justify-center space-y-2 rounded-xl py-2.5 text-white"
onClick={() => {
Taro.navigateTo({
url: buildUrl("/pages/purchase/purchaser/create"),
});
}}
>
<View className="text-base font-bold"></View>
</View>
</View>
</View>
),
],
};
useShareAppMessage((res) => {
console.log("useShareAppMessage1", res, shareOptions);
// 如果是按钮触发的转发,使用默认配置
if (res.from === "button") {
return shareOptions;
}
// 页面转发使用设置的配置
return {};
});
return (
<PageList<BusinessAPI.PurchaseOrderVO, BusinessAPI.PurchaseOrderPageQry>
rowId={"purchaseOrderId"}
itemHeight={182}
type={"infinite"}
actionRef={actionRef}
render={(purchaseOrderVO: BusinessAPI.PurchaseOrderVO, index) => {
const calculator = new PurchaseOrderCalculator(purchaseOrderVO);
return (
<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"}
onClick={(event) => {
if (purchaseOrderVO.active === 7) {
Taro.navigateTo({
url: buildUrl("/pages/purchase/purchaser/preview", {
orderId: purchaseOrderVO.orderId,
}),
});
event.stopPropagation();
}
}}
>
<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"}
>
{purchaseOrderVO.orderVehicle?.vehicleNo
? "第" +
purchaseOrderVO.orderVehicle?.vehicleNo +
"车"
: "暂未生成车次"}
</Text>
</View>
<Text className={"text-neutral-dark text-sm"}>
{purchaseOrderVO.orderSn}
</Text>
</View>
<State
state={purchaseOrderVO.state}
stateMap={purchaseOrder.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"}>
{purchaseOrderVO.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(purchaseOrderVO.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"}>
{purchaseOrderVO.orderVehicle?.origin} {"->"}
{purchaseOrderVO.orderVehicle?.destination}
</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"}>
{purchaseOrderVO.orderVehicle.dealerName}
</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"}>
{purchaseOrderVO.orderVehicle?.plate}
</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"}>
{purchaseOrderVO.orderSupplierList.length}
</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"}>
{formatCurrency(calculator.getTotalGrossWeight())}{" "}
/ {" "}
{formatCurrency(calculator.getTotalNetWeight())}
</Text>
</View>
</View>
</View>
</View>
<View className={"py-2.5"}>
{purchaseOrderVO.state === "WAITING_BOSS_APPROVE" && (
<View className={"flex flex-row justify-end gap-2"}>
<PurchaseOrderRejectFinal
purchaseOrderVO={purchaseOrderVO}
size={"small"}
onFinish={() => {
actionRef.current?.reload();
}}
/>
<Button
type={"primary"}
size={"small"}
onClick={(e) => {
Taro.navigateTo({
url: buildUrl("/pages/purchase/approver/approve", {
orderId: purchaseOrderVO.orderId,
}),
});
e.stopPropagation();
}}
>
</Button>
</View>
)}
</View>
</View>
</View>
);
}}
toolbar={toolbar}
request={async (params) => {
const {
data: { data, success, notEmpty },
} = await business.purchaseOrder.pagePurchaseOrder({
purchaseOrderPageQry: {
...params,
//@ts-ignore
state: state !== "ALL" ? state : undefined,
...(dealerVO
? {
dealerId: dealerVO.dealerId,
}
: {}),
},
});
return {
data,
success,
hasMore: notEmpty,
};
}}
pagination={{
pageSize: 10,
}}
/>
);
});

View File

@ -3,6 +3,8 @@ import {
DealerPicker,
Icon,
PageList,
PurchaseOrderRejectApprove,
PurchaseOrderRejectFinal,
PurchaseOrderWithdrawReview,
State,
ToolBar,
@ -273,56 +275,112 @@ export default hocAuth(function Page(props: CommonComponent) {
</View>
</View>
</View>
<View className={"py-2.5"}>
{role === "origin-entry" && (
<View className={"flex flex-row justify-end gap-2"}>
{purchaseOrderVO.state === "DRAFT" && (
<>
{role === "boss" && (
<View className={"py-2.5"}>
{purchaseOrderVO.state === "WAITING_APPROVE" && (
<View className={"flex flex-row justify-end gap-2"}>
<PurchaseOrderRejectFinal
purchaseOrderVO={purchaseOrderVO}
size={"small"}
onFinish={() => {
actionRef.current?.reload();
}}
/>
<Button
type={"primary"}
size={"small"}
onClick={(e) => {
Taro.navigateTo({
url: buildUrl(
"/pages/purchase/approver/audit/audit",
{
orderId: purchaseOrderVO.orderId,
},
),
});
e.stopPropagation();
}}
>
</Button>
</View>
)}
</View>
)}
{role === "reviewer" && (
<View className={"py-2.5"}>
{purchaseOrderVO.state === "WAITING_AUDIT" && (
<View className={"flex flex-row justify-end gap-2"}>
<PurchaseOrderRejectApprove
purchaseOrderVO={purchaseOrderVO}
size={"small"}
onFinish={() => {
actionRef.current?.reload();
}}
/>
<Button
type={"primary"}
size={"small"}
onClick={(e) => {
Taro.navigateTo({
url: buildUrl(
"/pages/purchase/reviewer/audit/audit",
{
orderId: purchaseOrderVO.orderId,
},
),
});
e.stopPropagation();
}}
>
</Button>
</View>
)}
{purchaseOrderVO.state === "WAITING_APPROVE" && (
<View className={"flex flex-row justify-end gap-2"}>
<Button
type={"primary"}
size={"small"}
onClick={(e) => {
Taro.navigateTo({
url: buildUrl(
"/pages/purchase/reviewer/audit/result",
{
orderId: purchaseOrderVO.orderId,
},
),
});
e.stopPropagation();
}}
>
</Button>
</View>
)}
</View>
)}
{role === "origin-entry" && (
<View className={"py-2.5"}>
{purchaseOrderVO.state === "DRAFT" && (
<View className={"flex flex-row justify-end gap-2"}>
<Button
type={"default"}
size={"small"}
onClick={(e) => {
Taro.navigateTo({
url: buildUrl("/pages/purchase/purchaser/create", {
orderId: purchaseOrderVO.orderId,
}),
});
e.stopPropagation();
}}
>
</Button>
{purchaseOrderVO.active === 7 && (
<Button
type={"default"}
size={"small"}
onClick={(e) => {
Taro.navigateTo({
url: buildUrl(
"/pages/purchase/purchaser/create",
{
orderId: purchaseOrderVO.orderId,
},
),
});
e.stopPropagation();
}}
>
</Button>
{purchaseOrderVO.active === 7 && (
<Button
type={"primary"}
size={"small"}
onClick={(e) => {
Taro.navigateTo({
url: buildUrl(
"/pages/purchase/purchaser/preview",
{
orderId: purchaseOrderVO.orderId,
},
),
});
e.stopPropagation();
}}
>
</Button>
)}
<Button type={"danger"} size={"small"}>
</Button>
</>
)}
{purchaseOrderVO.state === "WAITING_AUDIT" && (
<>
<Button
type={"default"}
type={"primary"}
size={"small"}
onClick={(e) => {
Taro.navigateTo({
@ -336,20 +394,41 @@ export default hocAuth(function Page(props: CommonComponent) {
e.stopPropagation();
}}
>
</Button>
<PurchaseOrderWithdrawReview
purchaseOrderVO={purchaseOrderVO}
size={"small"}
onFinish={() => {
actionRef.current?.reload();
}}
/>
</>
)}
</View>
)}
</View>
)}
<Button type={"danger"} size={"small"}>
</Button>
</View>
)}
{purchaseOrderVO.state === "WAITING_AUDIT" && (
<View className={"flex flex-row justify-end gap-2"}>
<Button
type={"default"}
size={"small"}
onClick={(e) => {
Taro.navigateTo({
url: buildUrl("/pages/purchase/purchaser/preview", {
orderId: purchaseOrderVO.orderId,
}),
});
e.stopPropagation();
}}
>
</Button>
<PurchaseOrderWithdrawReview
purchaseOrderVO={purchaseOrderVO}
size={"small"}
onFinish={() => {
actionRef.current?.reload();
}}
/>
</View>
)}
</View>
)}
</View>
</View>
);

View File

@ -1,7 +1,14 @@
import hocAuth from "@/hocs/auth";
import { CommonComponent, CostItem, SupplierVO } from "@/types/typings";
import { View } from "@tarojs/components";
import { Button, Dialog, SafeArea, Toast, Tour, TourList } from "@nutui/nutui-react-taro";
import {
Button,
Dialog,
SafeArea,
Toast,
Tour,
TourList,
} from "@nutui/nutui-react-taro";
import { purchase } from "@/constant";
import { useEffect, useRef, useState } from "react";
import {
@ -16,7 +23,7 @@ import {
SupplierList,
TicketUpload,
Weigh,
WeighRef
WeighRef,
} from "@/components";
import { business } from "@/services";
import { generateShortId } from "@/utils/generateShortId";
@ -506,7 +513,7 @@ export default hocAuth(function Page(props: CommonComponent) {
content: "当前采购订单已暂存成功",
});
Taro.redirectTo({
url: "/pages/purchase/purchaser/history",
url: "/pages/purchase/history",
});
}
},

View File

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

View File

@ -4,9 +4,9 @@ import { View } from "@tarojs/components";
import { Button, SafeArea } from "@nutui/nutui-react-taro";
import { useEffect, useState } from "react";
import {
PurchasePreview,
PurchaseOrderSubmitReview,
PurchaseOrderWithdrawReview,
PurchasePreview,
} from "@/components";
import { business } from "@/services";
import Taro from "@tarojs/taro";
@ -78,7 +78,11 @@ export default hocAuth(function Page(props: CommonComponent) {
purchaseOrderVO={purchaseOrder}
size={"xlarge"}
onFinish={() => {
init(orderId).then();
Taro.switchTab({
url: buildUrl("/pages/purchase/purchaser/result", {
orderId: purchaseOrder.orderId,
}),
});
}}
/>
</View>

View File

@ -0,0 +1,144 @@
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 init = async (orderId: string) => {
setLoading(true);
try {
// 获取采购单信息
const { data: purchaseData } =
await business.purchaseOrder.showPurchaseOrder({
purchaseOrderShowQry: {
orderId: orderId,
},
});
if (purchaseData.success) {
setPurchaseOrder(purchaseData.data);
}
} catch (error) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: "获取采购单信息失败",
});
} finally {
setLoading(false);
}
};
useEffect(() => {
if (orderId) {
init(orderId);
}
}, [orderId]);
// 查看采购单详情
const viewPurchaseOrderDetail = () => {
if (purchaseOrder?.orderId) {
Taro.navigateTo({
url: buildUrl("/pages/purchase/purchaser/preview", {
orderId: purchaseOrder.orderId,
}),
});
}
};
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?.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>
<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">
<Button
type="primary"
size={"xlarge"}
block
onClick={viewPurchaseOrderDetail}
>
</Button>
</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

@ -156,6 +156,8 @@ export default hocAuth(function Page(props: CommonComponent) {
const [purchaseOrderVO, setPurchaseOrderVO] =
useState<BusinessAPI.PurchaseOrderVO>();
console.log("purchaseOrderVO", purchaseOrderVO);
// 驳回原因弹窗相关状态
const [rejectVisible, setRejectVisible] = useState(false);
const [rejectReason, setRejectReason] = useState("");
@ -187,11 +189,22 @@ export default hocAuth(function Page(props: CommonComponent) {
try {
// 调用驳回API
await business.purchaseOrder.rejectApprovePurchaseOrder({
const {
data: { success, errMessage },
} = await business.purchaseOrder.rejectApprovePurchaseOrder({
orderId: purchaseOrderVO?.orderId,
rejectReason: rejectReason,
});
if (!success) {
Toast.show("toast", {
icon: "fail",
title: "提示",
content: errMessage || "驳回失败",
});
return;
}
Toast.show("toast", {
icon: "success",
title: "提示",
@ -203,7 +216,9 @@ export default hocAuth(function Page(props: CommonComponent) {
setRejectReason("");
setTimeout(() => {
Taro.redirectTo({ url: buildUrl("/pages/purchase/reviewer/list") });
Taro.redirectTo({
url: buildUrl("/pages/purchase/reviewer/audit/list"),
});
}, 1000);
// 返回采购单页面
@ -266,7 +281,7 @@ export default hocAuth(function Page(props: CommonComponent) {
content: "暂存成功",
});
// 返回采购单页面
Taro.redirectTo({ url: "/pages/purchase/reviewer/list" });
Taro.redirectTo({ url: "/pages/purchase/reviewer/audit/list" });
}
};
@ -307,7 +322,7 @@ export default hocAuth(function Page(props: CommonComponent) {
});
// 跳转到提交结果页面
Taro.redirectTo({
url: buildUrl("/pages/purchase/reviewer/submitted", {
url: buildUrl("/pages/purchase/reviewer/audit/result", {
orderId: purchaseOrderVO!.orderId,
}),
});
@ -387,7 +402,7 @@ export default hocAuth(function Page(props: CommonComponent) {
return;
}
const calculator = new PurchaseOrderCalculator(purchaseOrderVO);
const calculator = new PurchaseOrderCalculator(purchaseOrderVO, true);
const personalProfit = calculator.getPersonalProfit();
return (
@ -498,6 +513,7 @@ export default hocAuth(function Page(props: CommonComponent) {
purchaseOrderVO={purchaseOrderVO}
onChange={setPurchaseOrderVO}
costItemVOList={costItemVOList}
calculator={calculator}
/>
</View>
</>
@ -560,7 +576,7 @@ export default hocAuth(function Page(props: CommonComponent) {
</View>
</>
)}
{purchaseOrderVO.state === "WAITING_BOSS_APPROVE" && (
{purchaseOrderVO.state === "WAITING_APPROVE" && (
<>
<View className={"flex-1"}>
<Button
@ -643,16 +659,21 @@ export default hocAuth(function Page(props: CommonComponent) {
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="p-2.5">
<View className="mb-2.5 text-sm font-bold"></View>
<View
className={`flex w-full items-center rounded-md border-4 border-gray-300`}
>
<TextArea
value={rejectReason}
onChange={(value) => setRejectReason(value)}
placeholder="请输入驳回原因"
rows={4}
maxLength={200}
showCount
/>
</View>
<View className="mt-4 flex justify-between gap-2">
<View className="flex-1">
<Button
@ -679,6 +700,7 @@ export default hocAuth(function Page(props: CommonComponent) {
</View>
</View>
</View>
<SafeArea position="bottom" />
</Popup>
</>

View File

@ -182,9 +182,12 @@ export default hocAuth(function Page(props: CommonComponent) {
size={"small"}
onClick={(e) => {
Taro.navigateTo({
url: buildUrl("/pages/purchase/reviewer/audit", {
orderId: purchaseOrderVO.orderId,
}),
url: buildUrl(
"/pages/purchase/reviewer/audit/audit",
{
orderId: purchaseOrderVO.orderId,
},
),
});
e.stopPropagation();
}}

View File

@ -1,4 +1,4 @@
export default definePageConfig({
navigationBarTitleText: "采购单",
navigationBarTitleText: "提交审核结果",
navigationBarBackgroundColor: "#fff",
});

View File

@ -49,7 +49,7 @@ export default hocAuth(function Page(props: CommonComponent) {
const viewPurchaseOrderDetail = () => {
if (purchaseOrder?.orderId) {
Taro.navigateTo({
url: buildUrl("/pages/purchase/reviewer/audit", {
url: buildUrl("/pages/purchase/reviewer/audit/audit", {
orderId: purchaseOrder.orderId,
}),
});

View File

@ -1,353 +0,0 @@
import {
ActionType,
DealerPicker,
Icon,
PageList,
PurchaseOrderRejectApprove,
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/buildUrl";
import { Button } from "@nutui/nutui-react-taro";
import purchaseOrder from "@/constant/purchaseOrder";
import dayjs from "dayjs";
import { formatCurrency } from "@/utils/format";
import { PurchaseOrderCalculator } from "@/utils/PurchaseOrderCalculator";
export default hocAuth(function Page(props: CommonComponent) {
const { role, shareOptions } = props;
const [state, setState] =
useState<BusinessAPI.PurchaseOrderPageQry["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: "请输入车牌号",
},
{
key: "supplierName",
name: "瓜农姓名",
placeholder: "请输入瓜农姓名",
},
],
},
tabs: {
activeKey: "state",
defaultActiveKey: state || "ALL",
items: purchaseOrder.stateList.filter((item) => {
if (role === "reviewer") {
return item.value !== "DRAFT";
}
return item;
}),
onChange: (item) => {
setState(item as BusinessAPI.PurchaseOrderPageQry["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-2 border-primary flex h-6 items-center rounded-md 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>
),
actions: [
role === "origin-entry" && (
<View className={"flex flex-row gap-2 p-3"} key={"create"}>
<View className={"flex-1"}>
<View
className="bg-primary flex w-full flex-col items-center justify-center space-y-2 rounded-xl py-2.5 text-white"
onClick={() => {
Taro.navigateTo({
url: buildUrl("/pages/purchase/purchaser/create"),
});
}}
>
<View className="text-base font-bold"></View>
</View>
</View>
</View>
),
],
};
useShareAppMessage((res) => {
console.log("useShareAppMessage1", res, shareOptions);
// 如果是按钮触发的转发,使用默认配置
if (res.from === "button") {
return shareOptions;
}
// 页面转发使用设置的配置
return {};
});
return (
<PageList<BusinessAPI.PurchaseOrderVO, BusinessAPI.PurchaseOrderPageQry>
rowId={"purchaseOrderId"}
itemHeight={182}
type={"infinite"}
actionRef={actionRef}
render={(purchaseOrderVO: BusinessAPI.PurchaseOrderVO, index) => {
const calculator = new PurchaseOrderCalculator(purchaseOrderVO);
return (
<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"}
onClick={(event) => {
if (purchaseOrderVO.active === 7) {
Taro.navigateTo({
url: buildUrl("/pages/purchase/purchaser/preview", {
orderId: purchaseOrderVO.orderId,
}),
});
event.stopPropagation();
}
}}
>
<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"}
>
{purchaseOrderVO.orderVehicle?.vehicleNo
? "第" +
purchaseOrderVO.orderVehicle?.vehicleNo +
"车"
: "暂未生成车次"}
</Text>
</View>
<Text className={"text-neutral-dark text-sm"}>
{purchaseOrderVO.orderSn}
</Text>
</View>
<State
state={purchaseOrderVO.state}
stateMap={purchaseOrder.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"}>
{purchaseOrderVO.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(purchaseOrderVO.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"}>
{purchaseOrderVO.orderVehicle?.origin} {"->"}
{purchaseOrderVO.orderVehicle?.destination}
</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"}>
{purchaseOrderVO.orderVehicle.dealerName}
</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"}>
{purchaseOrderVO.orderVehicle?.plate}
</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"}>
{purchaseOrderVO.orderSupplierList.length}
</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"}>
{formatCurrency(calculator.getTotalGrossWeight())}{" "}
/ {" "}
{formatCurrency(calculator.getTotalNetWeight())}
</Text>
</View>
</View>
</View>
</View>
<View className={"py-2.5"}>
{purchaseOrderVO.state === "WAITING_AUDIT" && (
<View className={"flex flex-row justify-end gap-2"}>
<PurchaseOrderRejectApprove
purchaseOrderVO={purchaseOrderVO}
size={"small"}
onFinish={() => {
actionRef.current?.reload();
}}
/>
<Button
type={"primary"}
size={"small"}
onClick={(e) => {
Taro.navigateTo({
url: buildUrl("/pages/purchase/reviewer/audit", {
orderId: purchaseOrderVO.orderId,
}),
});
e.stopPropagation();
}}
>
</Button>
</View>
)}
{purchaseOrderVO.state === "WAITING_BOSS_APPROVE" && (
<View className={"flex flex-row justify-end gap-2"}>
<Button
type={"primary"}
size={"small"}
onClick={(e) => {
Taro.navigateTo({
url: buildUrl("/pages/purchase/reviewer/submitted", {
orderId: purchaseOrderVO.orderId,
}),
});
e.stopPropagation();
}}
>
</Button>
</View>
)}
</View>
</View>
</View>
);
}}
toolbar={toolbar}
request={async (params) => {
const {
data: { data, success, notEmpty },
} = await business.purchaseOrder.pagePurchaseOrder({
purchaseOrderPageQry: {
...params,
//@ts-ignore
state: state !== "ALL" ? state : undefined,
...(dealerVO
? {
dealerId: dealerVO.dealerId,
}
: {}),
},
});
return {
data,
success,
hasMore: notEmpty,
};
}}
pagination={{
pageSize: 10,
}}
/>
);
});

View File

@ -3448,6 +3448,7 @@ declare namespace BusinessAPI {
orderPackageList: OrderPackage[];
/** 是否是暂存 */
draft: boolean;
/** 报价方式1_按毛重报价2_按净重报价 */
pricingMethod?: "BY_GROSS_WEIGHT" | "BY_NET_WEIGHT";
/** 公司信息 */
orderCompany: OrderCompany;
@ -3458,14 +3459,16 @@ declare namespace BusinessAPI {
type PurchaseOrderCountQry = {
/** 状态1_启用0_禁用 */
status?: boolean;
/** 订单状态: 0-草稿, 1-待审核, 2-待老板审核, 3-已发货(待付款), 4-已付款, 5-已完结 */
/** 采购订单状态: 0_草稿1_审核中2_审批中3_待发货4_已发货5_已驳回6_已撤回7_已关闭8_已完结 */
state?:
| "DRAFT"
| "WAITING_AUDIT"
| "WAITING_BOSS_APPROVE"
| "SHIPPING"
| "WAITING_APPROVE"
| "WAITING_SHIPMENT"
| "SHIPPED"
| "PAID"
| "COMPLETED";
| "COMPLETED"
| "REJECTED";
};
type PurchaseOrderCreateCmd = {
@ -3529,14 +3532,16 @@ declare namespace BusinessAPI {
vehicleNo?: string;
/** 采购订单编号 */
orderSn?: string;
/** 订单状态: 0-草稿, 1-待审核, 2-待老板审核, 3-已发货(待付款), 4-已付款, 5-已完结 */
/** 采购订单状态: 0_草稿1_审核中2_审批中3_待发货4_已发货5_已驳回6_已撤回7_已关闭8_已完结 */
state?:
| "DRAFT"
| "WAITING_AUDIT"
| "WAITING_BOSS_APPROVE"
| "SHIPPING"
| "WAITING_APPROVE"
| "WAITING_SHIPMENT"
| "SHIPPED"
| "PAID"
| "COMPLETED";
| "COMPLETED"
| "REJECTED";
/** 供应商名称 */
supplierName?: string;
/** 经销商ID */
@ -3547,13 +3552,15 @@ declare namespace BusinessAPI {
type PurchaseOrderRejectApproveCmd = {
/** 采购订单ID */
orderId: string;
/** 驳回原因 */
/** 审核原因 */
rejectReason: string;
};
type PurchaseOrderRejectFinalCmd = {
/** 采购订单ID */
orderId: string;
/** 驳回原因 */
rejectReason: string;
};
type PurchaseOrderShowQry = {
@ -3661,14 +3668,16 @@ declare namespace BusinessAPI {
freightCharge?: number;
/** 瓜农数量 */
supplierCount?: number;
/** 订单状态: 0-草稿, 1-待审核, 2-待老板审核, 3-已发货(待付款), 4-已付款, 5-已完结 */
/** 采购订单状态: 0_草稿1_审核中2_审批中3_待发货4_已发货5_已驳回6_已撤回7_已关闭8_已完结 */
state?:
| "DRAFT"
| "WAITING_AUDIT"
| "WAITING_BOSS_APPROVE"
| "SHIPPING"
| "WAITING_APPROVE"
| "WAITING_SHIPMENT"
| "SHIPPED"
| "PAID"
| "COMPLETED";
| "COMPLETED"
| "REJECTED";
/** 备注 */
remark?: string;
/** 创建人ID */

View File

@ -7,9 +7,53 @@ import { Decimal } from "decimal.js";
export class PurchaseOrderCalculator {
private purchaseOrderVO: BusinessAPI.PurchaseOrderVO;
constructor(purchaseOrderVO: BusinessAPI.PurchaseOrderVO) {
constructor(purchaseOrderVO: BusinessAPI.PurchaseOrderVO, debug?: boolean) {
this.purchaseOrderVO = purchaseOrderVO;
this.init();
if (!debug) {
return;
}
console.log("采购订单计算器");
console.table([
{
成本项总和: this.getTotalCostItemAmount(),
辅料费: this.getCostItemAmount("PACKAGING_MATERIALS"),
人工费: this.getCostItemAmount("HUMAN_COST"),
产地垫付: this.getCostItemAmount("PRODUCTION_ADVANCE"),
工头垫付: this.getCostItemAmount("WORKER_ADVANCE"),
:
this.getCostItemAmount("FIXED_COST", "纸箱费") || this.getBoxSale(),
计提费: this.getCostItemAmount("FIXED_COST", "计提费"),
收代办费: this.getCostItemAmount("FIXED_COST", "收代办费"),
王超费用: this.getCostItemAmount("FIXED_COST", "王超费用"),
其他费用: this.getCostItemAmount("OTHER_COST"),
草帘费: this.getStrawCurtainCost(),
},
]);
console.table([
{
西瓜采购成本: this.getSupplierPurchaseCost(),
成本项总和: this.getTotalCostItemAmount(),
税费补贴: this.getTaxSubsidy(),
计提税金: this.getTaxProvision(),
成本差异: this.getCostDifference(),
"采购成本(不包含运费)": this.getTotalPurchaseCost(),
运费: this.getDeliveryFee(),
"采购成本(含运费)": this.getMelonCost1(),
市场报价: this.getSalesAmount(),
平均单价: this.getAverageSalesPrice(),
},
]);
console.table([
{
分成利润: this.getShareProfit(),
西瓜利润: this.getMelonNetProfit(),
诚信志远分成: this.getShareProfitRatio(),
个人利润: this.getPersonalProfit(),
},
]);
}
/**
@ -24,11 +68,29 @@ export class PurchaseOrderCalculator {
});
}
/**
*
*/
getCostItemAmount(
costType: BusinessAPI.CostItemVO["costType"],
name?: BusinessAPI.CostItemVO["name"],
): number {
return this.purchaseOrderVO.orderCostList
?.filter(
(item) => item.costType === costType && (!name || item.name === name),
)
.reduce((sum, cost) => {
return new Decimal(sum)
.plus(new Decimal(cost.price || 0).mul(cost.count || 0))
.toNumber();
}, 0);
}
/**
* = + + + + + + + +
*/
getTotalCostItemAmount(): number {
const costItemsCost = this.purchaseOrderVO.orderCostList.reduce(
const costItemsCost = this.purchaseOrderVO.orderCostList?.reduce(
(sum, cost) => {
// 先过滤一下
if (cost.name === "纸箱费") {
@ -59,6 +121,9 @@ export class PurchaseOrderCalculator {
*/
getSupplierPurchaseCost(): number {
return this.purchaseOrderVO.orderSupplierList.reduce((sum, supplier) => {
// return new Decimal(sum)
// .plus(this.calculateSupplierAmount(supplier))
// .toNumber();
const netWeight = supplier.netWeight || 0;
// 瓜农自己的纸箱重量
@ -166,6 +231,9 @@ export class PurchaseOrderCalculator {
return new Decimal(sum)
.plus(
supplier.orderPackageList?.reduce((sum, pkg) => {
if (pkg.boxType === "OWN") {
return 0;
}
return new Decimal(sum)
.plus(new Decimal(pkg.boxCount || 0).mul(pkg.boxSalePrice || 0))
.toNumber();
@ -206,6 +274,7 @@ export class PurchaseOrderCalculator {
getPersonalProfit(): number {
return new Decimal(this.getMelonNetProfit())
.minus(this.getShareProfitRatio())
.plus(this.getCostDifference())
.toNumber();
}
@ -239,7 +308,7 @@ export class PurchaseOrderCalculator {
*/
getShareProfit(): number {
return new Decimal(this.getMarketPrice())
.minus(this.getMelonCost2())
.minus(this.getMelonCost1())
.toNumber();
}
@ -290,14 +359,26 @@ export class PurchaseOrderCalculator {
*
*/
getTaxProvision(): number {
return this.purchaseOrderVO.orderDealer?.taxProvision || 0;
if (this.purchaseOrderVO.orderDealer?.enableAccrualTax) {
return (
this.purchaseOrderVO.orderDealer?.taxProvision ||
this.getDefaultTaxProvision()
);
}
return 0;
}
/**
*
*/
getTaxSubsidy(): number {
return this.purchaseOrderVO.orderDealer?.taxSubsidy || 0;
if (this.purchaseOrderVO.orderDealer?.enableCompanyRebate) {
return (
this.purchaseOrderVO.orderDealer?.taxSubsidy ||
this.getDefaultTaxSubsidy()
);
}
return 0;
}
/**
@ -370,13 +451,14 @@ export class PurchaseOrderCalculator {
*
*/
getDefaultTaxProvision(): number {
if (this.purchaseOrderVO.orderDealer?.accrualTaxRatio) {
if (this.purchaseOrderVO.orderDealer?.enableAccrualTax) {
const totalAmount = this.getTotalAmount();
const taxSubsidyValue = this.purchaseOrderVO.orderDealer?.taxSubsidy || 0;
const taxSubsidyValue = this.getTaxSubsidy();
return new Decimal(totalAmount)
.sub(taxSubsidyValue)
.mul(this.purchaseOrderVO.orderDealer?.accrualTaxRatio / 100)
.mul(this.purchaseOrderVO.orderDealer?.accrualTaxRatio!)
.div(100)
.toNumber();
}
@ -387,13 +469,14 @@ export class PurchaseOrderCalculator {
*
*/
getDefaultTaxSubsidy(): number {
if (this.purchaseOrderVO.orderDealer?.companyRebateRatio) {
if (this.purchaseOrderVO.orderDealer?.enableCompanyRebate) {
const totalPackagingCost = this.getTotalCostItemAmount();
const salesAmount1 = this.getSalesAmount();
return new Decimal(salesAmount1)
.plus(totalPackagingCost)
.mul(this.purchaseOrderVO.orderDealer?.companyRebateRatio / 100)
.mul(this.purchaseOrderVO.orderDealer?.companyRebateRatio!)
.div(100)
.toNumber();
}
return 0;
@ -422,7 +505,11 @@ export class PurchaseOrderCalculator {
return new Decimal(sum)
.plus(
supplier.orderPackageList?.reduce((sum, pkg) => {
return new Decimal(sum).plus(pkg.boxProductWeight || 0).toNumber();
return new Decimal(sum)
.plus(
new Decimal(pkg.boxProductWeight || 0).mul(pkg.boxCount || 0),
)
.toNumber();
}, 0) || 0,
)
.toNumber();

File diff suppressed because one or more lines are too long