refactor(order): 为订单表单组件添加表单验证功能
- 为 BasicInfoSection 组件添加 validateForm 方法,校验本车次号和运费类型 - 为 CompanyInfoSection 组件添加 validateForm 方法,校验销售方选择 - 为 DealerInfoSection 组件添加 validateForm 方法,校验经销商选择 - 为 DeliveryFormSection 组件添加 validateForm 方法,调用内部表单验证 - 为 MarketPriceSection 组件添加 validateForm 方法,校验报价方式和销售单价 - 在审核页面中使用各组件的验证方法替代原有验证逻辑 - 为各组件导出对应的 Ref 类型定义 - 更新应用版本号从 v0.0.64 到 v0.0.65
This commit is contained in:
parent
5060811144
commit
2aa7df132a
@ -34,8 +34,11 @@ export { default as OrderWithdrawReview } from "./button/OrderWithdrawReview";
|
||||
export { default as OrderFinalApprove } from "./button/OrderFinalApprove";
|
||||
|
||||
export { default as CompanyInfoSection } from "./section/CompanyInfoSection";
|
||||
export type { CompanyInfoSectionRef } from "./section/CompanyInfoSection";
|
||||
export { default as DealerInfoSection } from "./section/DealerInfoSection";
|
||||
export type { DealerInfoSectionRef } from "./section/DealerInfoSection";
|
||||
export { default as BasicInfoSection } from "./section/BasicInfoSection";
|
||||
export type { BasicInfoSectionRef } from "./section/BasicInfoSection";
|
||||
export { default as SupplierInfoSection } from "./section/SupplierInfoSection";
|
||||
export { default as PurchaseCostInfoSection } from "./section/PurchaseCostInfoSection";
|
||||
export { default as PackageInfoSection } from "./section/PackageInfoSection";
|
||||
@ -44,6 +47,7 @@ export { default as PackagingCostSection } from "./section/PackagingCostSection"
|
||||
export { default as ProductionLossSection } from "./section/ProductionLossSection";
|
||||
export { default as CostSummarySection } from "./section/CostSummarySection";
|
||||
export { default as MarketPriceSection } from "./section/MarketPriceSection";
|
||||
export type { MarketPriceSectionRef } from "./section/MarketPriceSection";
|
||||
export { default as RebateCalcSection } from "./section/RebateCalcSection";
|
||||
export { default as TaxSubsidySection } from "./section/TaxSubsidySection";
|
||||
export { default as TaxProvisionSection } from "./section/TaxProvisionSection";
|
||||
@ -52,6 +56,7 @@ export { default as MaterialCostSection } from "./section/MaterialCostSection";
|
||||
export { default as ProductionAdvanceSection } from "./section/ProductionAdvanceSection";
|
||||
export { default as WorkerAdvanceSection } from "./section/WorkerAdvanceSection";
|
||||
export { default as DeliveryFormSection } from "./section/DeliveryFormSection";
|
||||
export type { DeliveryFormSectionRef } from "./section/DeliveryFormSection";
|
||||
export { default as PurchaseFormSection } from "./section/PurchaseFormSection";
|
||||
|
||||
export { default as PurchaseStep1Form } from "./document/Step1Form";
|
||||
|
||||
@ -9,21 +9,45 @@ import {
|
||||
SafeArea,
|
||||
} from "@nutui/nutui-react-taro";
|
||||
import dayjs from "dayjs";
|
||||
import { useEffect, useState } from "react";
|
||||
import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
|
||||
import businessServices from "@/services/business";
|
||||
import { Icon } from "@/components";
|
||||
import { generateShortId } from "@/utils";
|
||||
|
||||
export default function BasicInfoSection(props: {
|
||||
orderVO: BusinessAPI.OrderVO;
|
||||
onChange?: (orderVO: BusinessAPI.OrderVO) => void;
|
||||
costList: BusinessAPI.CostVO[];
|
||||
readOnly?: boolean;
|
||||
}) {
|
||||
export interface BasicInfoSectionRef {
|
||||
validateForm: () => string | null;
|
||||
}
|
||||
|
||||
export default forwardRef<
|
||||
BasicInfoSectionRef,
|
||||
{
|
||||
orderVO: BusinessAPI.OrderVO;
|
||||
onChange?: (orderVO: BusinessAPI.OrderVO) => void;
|
||||
costList: BusinessAPI.CostVO[];
|
||||
readOnly?: boolean;
|
||||
}
|
||||
>((props, ref) => {
|
||||
const { orderVO, onChange, readOnly, costList } = props;
|
||||
|
||||
const { orderVehicle } = orderVO;
|
||||
|
||||
// 暴露 validateForm 方法
|
||||
useImperativeHandle(ref, () => ({
|
||||
validateForm: () => {
|
||||
// 校验本车次号
|
||||
if (!orderVehicle?.vehicleNo) {
|
||||
return "请输入本车次号";
|
||||
}
|
||||
|
||||
// 校验运费类型
|
||||
if (!orderVehicle?.priceType) {
|
||||
return "请选择运费类型";
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
}));
|
||||
|
||||
// 当天和未来10天
|
||||
const startDate = new Date();
|
||||
const endDate = new Date(startDate.getTime() + 86400000 * 10);
|
||||
@ -809,4 +833,4 @@ export default function BasicInfoSection(props: {
|
||||
</View>
|
||||
</>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,15 +1,33 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
|
||||
import { View } from "@tarojs/components";
|
||||
import { CompanyPicker, Icon } from "@/components";
|
||||
import { Button } from "@nutui/nutui-react-taro";
|
||||
|
||||
export default function CompanyInfoSection(props: {
|
||||
orderVO: BusinessAPI.OrderVO;
|
||||
onChange?: (orderVO: BusinessAPI.OrderVO) => void;
|
||||
readOnly?: boolean;
|
||||
}) {
|
||||
export interface CompanyInfoSectionRef {
|
||||
validateForm: () => string | null;
|
||||
}
|
||||
|
||||
export default forwardRef<
|
||||
CompanyInfoSectionRef,
|
||||
{
|
||||
orderVO: BusinessAPI.OrderVO;
|
||||
onChange?: (orderVO: BusinessAPI.OrderVO) => void;
|
||||
readOnly?: boolean;
|
||||
}
|
||||
>((props, ref) => {
|
||||
const { orderVO, onChange, readOnly } = props;
|
||||
|
||||
// 暴露 validateForm 方法
|
||||
useImperativeHandle(ref, () => ({
|
||||
validateForm: () => {
|
||||
// 校验销售方
|
||||
if (!orderVO?.orderCompany?.companyId) {
|
||||
return "请选择销售方";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
}));
|
||||
|
||||
const [orderCompany, setOrderCompany] = useState<BusinessAPI.OrderCompany>();
|
||||
|
||||
// 当 orderVO 变化时,更新默认显示的公司信息
|
||||
@ -84,4 +102,4 @@ export default function CompanyInfoSection(props: {
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,15 +1,33 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
|
||||
import { View } from "@tarojs/components";
|
||||
import { DealerPicker, Icon } from "@/components";
|
||||
import { Button } from "@nutui/nutui-react-taro";
|
||||
|
||||
export default function DealerInfoSection(props: {
|
||||
orderVO: BusinessAPI.OrderVO;
|
||||
onChange?: (orderDealer: BusinessAPI.OrderVO) => void;
|
||||
readOnly?: boolean;
|
||||
}) {
|
||||
export interface DealerInfoSectionRef {
|
||||
validateForm: () => string | null;
|
||||
}
|
||||
|
||||
export default forwardRef<
|
||||
DealerInfoSectionRef,
|
||||
{
|
||||
orderVO: BusinessAPI.OrderVO;
|
||||
onChange?: (orderDealer: BusinessAPI.OrderVO) => void;
|
||||
readOnly?: boolean;
|
||||
}
|
||||
>((props, ref) => {
|
||||
const { orderVO, onChange, readOnly } = props;
|
||||
|
||||
// 暴露 validateForm 方法
|
||||
useImperativeHandle(ref, () => ({
|
||||
validateForm: () => {
|
||||
// 校验经销商
|
||||
if (!orderVO?.orderDealer?.dealerId) {
|
||||
return "请选择经销商";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
}));
|
||||
|
||||
const [orderDealer, setOrderDealer] = useState<BusinessAPI.OrderDealer>();
|
||||
|
||||
// 添加经销商名称状态(用于只有名称没有ID的情况)
|
||||
@ -87,4 +105,4 @@ export default function DealerInfoSection(props: {
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,16 +1,34 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { DeliveryStep1Form, DeliveryStep2Preview, Icon } from "@/components";
|
||||
import {
|
||||
forwardRef,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import {
|
||||
DeliveryStep1Form,
|
||||
DeliveryStep1FormRef,
|
||||
DeliveryStep2Preview,
|
||||
Icon,
|
||||
} from "@/components";
|
||||
import { convertOrderShipVOToExamplesFormat } from "@/utils";
|
||||
import { business } from "@/services";
|
||||
import { Button, Popup } from "@nutui/nutui-react-taro";
|
||||
import { View } from "@tarojs/components";
|
||||
|
||||
export default function DeliveryFormSection(props: {
|
||||
orderVO: BusinessAPI.OrderVO;
|
||||
onChange?: (orderDealer: BusinessAPI.OrderVO) => void;
|
||||
readOnly?: boolean;
|
||||
costList: BusinessAPI.CostVO[];
|
||||
}) {
|
||||
export interface DeliveryFormSectionRef {
|
||||
validateForm: () => string | null;
|
||||
}
|
||||
|
||||
export default forwardRef<
|
||||
DeliveryFormSectionRef,
|
||||
{
|
||||
orderVO: BusinessAPI.OrderVO;
|
||||
onChange?: (orderDealer: BusinessAPI.OrderVO) => void;
|
||||
readOnly?: boolean;
|
||||
costList: BusinessAPI.CostVO[];
|
||||
}
|
||||
>((props, ref) => {
|
||||
const { orderVO, onChange, readOnly, costList } = props;
|
||||
const [template, setTemplate] = useState<any[]>([]);
|
||||
|
||||
@ -90,6 +108,21 @@ export default function DeliveryFormSection(props: {
|
||||
setPreviewVisible(true);
|
||||
};
|
||||
|
||||
const step1FormRef = useRef<DeliveryStep1FormRef>(null);
|
||||
|
||||
// 暴露 validateForm 方法
|
||||
useImperativeHandle(ref, () => ({
|
||||
validateForm: () => {
|
||||
// 调用 step1FormRef 的 validateForm 方法
|
||||
if (step1FormRef.current && step1FormRef.current.validateForm) {
|
||||
if (!step1FormRef.current.validateForm()) {
|
||||
return "发货单复核填写错误";
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
}));
|
||||
|
||||
if (!template) {
|
||||
return;
|
||||
}
|
||||
@ -99,6 +132,7 @@ export default function DeliveryFormSection(props: {
|
||||
return (
|
||||
<>
|
||||
<DeliveryStep1Form
|
||||
ref={step1FormRef}
|
||||
readOnly={readOnly}
|
||||
moduleList={moduleList}
|
||||
orderShip={orderShip}
|
||||
@ -146,4 +180,4 @@ export default function DeliveryFormSection(props: {
|
||||
</Popup>
|
||||
</>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@ -3,15 +3,42 @@ import { Radio } from "@nutui/nutui-react-taro";
|
||||
import { OrderCalculator } from "@/utils";
|
||||
import { Icon, PriceEditor } from "@/components";
|
||||
import classNames from "classnames";
|
||||
import { forwardRef, useImperativeHandle } from "react";
|
||||
|
||||
export default function MarketPriceSection(props: {
|
||||
orderVO: BusinessAPI.OrderVO;
|
||||
onChange?: (orderVO: BusinessAPI.OrderVO) => void;
|
||||
readOnly?: boolean;
|
||||
calculator: OrderCalculator;
|
||||
}) {
|
||||
export interface MarketPriceSectionRef {
|
||||
validateForm: () => string | null;
|
||||
}
|
||||
|
||||
export default forwardRef<
|
||||
MarketPriceSectionRef,
|
||||
{
|
||||
orderVO: BusinessAPI.OrderVO;
|
||||
onChange?: (orderVO: BusinessAPI.OrderVO) => void;
|
||||
readOnly?: boolean;
|
||||
calculator: OrderCalculator;
|
||||
}
|
||||
>((props, ref) => {
|
||||
const { orderVO, onChange, readOnly, calculator } = props;
|
||||
|
||||
// 暴露 validateForm 方法
|
||||
useImperativeHandle(ref, () => ({
|
||||
validateForm: () => {
|
||||
// 校验报价方式
|
||||
if (!orderVO?.pricingMethod) {
|
||||
return "请选择市场报价的报价方式";
|
||||
}
|
||||
|
||||
// 校验销售单价
|
||||
for (const supplier of orderVO.orderSupplierList || []) {
|
||||
if (!supplier.salePrice || supplier.salePrice <= 0) {
|
||||
return `请填写${supplier.name}的销售单价`;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
}));
|
||||
|
||||
// 销售金额
|
||||
const saleAmount = calculator.getSalesAmount();
|
||||
|
||||
@ -242,4 +269,4 @@ export default function MarketPriceSection(props: {
|
||||
</View>
|
||||
</>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
// App 相关常量
|
||||
export const APP_VERSION = "v0.0.64";
|
||||
export const APP_VERSION = "v0.0.65";
|
||||
|
||||
@ -260,25 +260,27 @@ export default hocAuth(function Page(props: CommonComponent) {
|
||||
</View>
|
||||
)}
|
||||
|
||||
{orderVO.orderDealer?.taxProvision && (
|
||||
<View className="cost-item flex flex-col px-3 py-2">
|
||||
<View className="text-sm text-gray-500">计提税金</View>
|
||||
<View className="font-medium">
|
||||
¥{orderVO.orderDealer?.taxProvision} 元
|
||||
{orderVO.orderDealer?.taxProvision &&
|
||||
orderVO.orderDealer?.taxProvision > 0 && (
|
||||
<View className="cost-item flex flex-col px-3 py-2">
|
||||
<View className="text-sm text-gray-500">计提税金</View>
|
||||
<View className="font-medium">
|
||||
¥{orderVO.orderDealer?.taxProvision} 元
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
)}
|
||||
|
||||
{orderVO.orderDealer?.taxSubsidy && (
|
||||
<View className="cost-item flex flex-col px-3 py-2">
|
||||
<View className="text-sm text-gray-500">公司返点</View>
|
||||
<View className="font-medium">
|
||||
¥{orderVO.orderDealer?.taxSubsidy} 元
|
||||
{orderVO.orderDealer?.taxSubsidy &&
|
||||
orderVO.orderDealer?.taxSubsidy > 0 && (
|
||||
<View className="cost-item flex flex-col px-3 py-2">
|
||||
<View className="text-sm text-gray-500">公司返点</View>
|
||||
<View className="font-medium">
|
||||
¥{orderVO.orderDealer?.taxSubsidy} 元
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
)}
|
||||
|
||||
{orderVO.orderRebate?.amount && (
|
||||
{orderVO.orderRebate?.amount && orderVO.orderRebate?.amount > 0 && (
|
||||
<View className="cost-item flex flex-col px-3 py-2">
|
||||
<View className="text-sm text-gray-500">个人返点</View>
|
||||
<View className="font-medium">
|
||||
@ -287,14 +289,15 @@ export default hocAuth(function Page(props: CommonComponent) {
|
||||
</View>
|
||||
)}
|
||||
|
||||
{orderVO.orderDealer?.costDifference && (
|
||||
<View className="cost-item flex flex-col px-3 py-2">
|
||||
<View className="text-sm text-gray-500">调诚信志远分成</View>
|
||||
<View className="font-medium">
|
||||
¥{orderVO.orderDealer?.costDifference} 元
|
||||
{orderVO.orderDealer?.costDifference &&
|
||||
orderVO.orderDealer?.costDifference > 0 && (
|
||||
<View className="cost-item flex flex-col px-3 py-2">
|
||||
<View className="text-sm text-gray-500">调诚信志远分成</View>
|
||||
<View className="font-medium">
|
||||
¥{orderVO.orderDealer?.costDifference} 元
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
)}
|
||||
|
||||
<View className="cost-total col-span-2 grid grid-cols-2 bg-yellow-50 px-3 py-2">
|
||||
<View className="flex flex-col">
|
||||
|
||||
@ -2,7 +2,7 @@ import hocAuth from "@/hocs/auth";
|
||||
import { CommonComponent } from "@/types/typings";
|
||||
import Taro from "@tarojs/taro";
|
||||
import { business } from "@/services";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { View } from "@tarojs/components";
|
||||
import {
|
||||
ActionSheet,
|
||||
@ -14,14 +14,19 @@ import {
|
||||
} from "@nutui/nutui-react-taro";
|
||||
import {
|
||||
BasicInfoSection,
|
||||
BasicInfoSectionRef,
|
||||
CompanyInfoSection,
|
||||
CompanyInfoSectionRef,
|
||||
CostDifferenceSection,
|
||||
CostSummarySection,
|
||||
DealerInfoSection,
|
||||
DealerInfoSectionRef,
|
||||
DeliveryFormSection,
|
||||
DeliveryFormSectionRef,
|
||||
EmptyBoxInfoSection,
|
||||
Icon,
|
||||
MarketPriceSection,
|
||||
MarketPriceSectionRef,
|
||||
MaterialCostSection,
|
||||
OrderRejectApprove,
|
||||
PackageInfoSection,
|
||||
@ -47,6 +52,15 @@ import classNames from "classnames";
|
||||
import { DecimalUtils } from "@/utils/classes/calculators/core/DecimalUtils";
|
||||
import order from "@/constant/order";
|
||||
|
||||
// Section ref 类型
|
||||
type SectionRef =
|
||||
| BasicInfoSectionRef
|
||||
| CompanyInfoSectionRef
|
||||
| DealerInfoSectionRef
|
||||
| MarketPriceSectionRef
|
||||
| DeliveryFormSectionRef
|
||||
| null;
|
||||
|
||||
const defaultSections = [
|
||||
"marketPrice",
|
||||
"supplierInfo",
|
||||
@ -234,6 +248,9 @@ export default hocAuth(function Page(props: CommonComponent) {
|
||||
const orderId = router.params.orderId as BusinessAPI.OrderVO["orderId"];
|
||||
const auditId = router.params.auditId as BusinessAPI.AuditVO["auditId"];
|
||||
|
||||
// Section refs 映射
|
||||
const sectionRefs = useRef<Record<string, SectionRef>>({});
|
||||
|
||||
// 费用项目列表
|
||||
const [costList, setCostList] = useState<BusinessAPI.CostVO[]>([]);
|
||||
|
||||
@ -313,13 +330,24 @@ export default hocAuth(function Page(props: CommonComponent) {
|
||||
// 关闭对话框
|
||||
setSubmitDialogVisible(false);
|
||||
|
||||
// 表单校验
|
||||
const errorMsg = validateForm();
|
||||
if (errorMsg) {
|
||||
// 先调用所有 section 的 validateForm 方法进行校验
|
||||
const sectionErrors: string[] = [];
|
||||
Object.entries(sectionRefs.current).forEach(
|
||||
([_sectionName, sectionRef]) => {
|
||||
if (sectionRef && sectionRef.validateForm) {
|
||||
const error = sectionRef.validateForm();
|
||||
if (error) {
|
||||
sectionErrors.push(error);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (sectionErrors.length > 0) {
|
||||
Toast.show("toast", {
|
||||
icon: "fail",
|
||||
title: "校验失败",
|
||||
content: errorMsg,
|
||||
content: sectionErrors[0],
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -435,43 +463,6 @@ export default hocAuth(function Page(props: CommonComponent) {
|
||||
}
|
||||
};
|
||||
|
||||
// 表单校验
|
||||
const validateForm = () => {
|
||||
// 校验销售方
|
||||
if (!orderVO?.orderCompany?.companyId) {
|
||||
return "请选择销售方";
|
||||
}
|
||||
|
||||
// 校验经销商
|
||||
if (!orderVO?.orderDealer?.dealerId) {
|
||||
return "请选择经销商";
|
||||
}
|
||||
|
||||
// 校验本车次号
|
||||
if (!orderVO?.orderVehicle?.vehicleNo) {
|
||||
return "请输入本车次号";
|
||||
}
|
||||
|
||||
// 校验运费类型
|
||||
if (!orderVO?.orderVehicle?.priceType) {
|
||||
return "请选择运费类型";
|
||||
}
|
||||
|
||||
// 校验市场报价的报价方式
|
||||
if (!orderVO?.pricingMethod) {
|
||||
return "请选择市场报价的报价方式";
|
||||
}
|
||||
|
||||
// 校验市场报价的销售单价
|
||||
orderVO.orderSupplierList.forEach((supplier: BusinessAPI.OrderSupplier) => {
|
||||
if (!supplier.salePrice || supplier.salePrice <= 0) {
|
||||
return "请填写市场报价的销售单价";
|
||||
}
|
||||
});
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const init = async (
|
||||
orderId: BusinessAPI.OrderVO["orderId"],
|
||||
auditId: BusinessAPI.AuditVO["auditId"],
|
||||
@ -824,6 +815,11 @@ export default hocAuth(function Page(props: CommonComponent) {
|
||||
className={`overflow-x-auto rounded-md rounded-b-lg bg-white p-2.5 shadow-sm`}
|
||||
>
|
||||
<section.component
|
||||
ref={(ref: SectionRef) => {
|
||||
if (ref) {
|
||||
sectionRefs.current[section.name] = ref;
|
||||
}
|
||||
}}
|
||||
readOnly={
|
||||
!(
|
||||
orderVO.state === "AUDITING" &&
|
||||
|
||||
Loading…
Reference in New Issue
Block a user