ERPTurbo_Client/packages/app-client/src/components/purchase/section/SupplierInfoSection.tsx
shenyifei 32017a6ce0 fix(purchase): 优化采购订单审批和表单验证逻辑
- 调整采购订单最终审批成功后的提示逻辑,确保只在审批成功时显示
- 更新采购订单提交审核组件,增加调试日志输出
- 修改采购订单撤回提审按钮样式类型为危险类型
- 优化 MelonFarmer 组件中的身份证和银行卡输入框类型
- 重构 OrderPackage 组件中的纸箱类型启用逻辑,根据供应商条件动态设置
- 调整 TicketUpload 组件中发票和合同上传状态管理方式
- 改进 Weigh 组件中空车过磅纸箱选择逻辑,仅对首个瓜农显示选择项
- 更新 BasicInfoSection 组件中开关按钮的显示文案
- 优化 SupplierInfoSection 组件中图片预览逻辑
- 调整工作台常量配置,修改历史记录标题并增加采购记录菜单项
- 优化发货单文档页面中的日期选择范围和表单字段渲染逻辑
2025-11-11 16:45:16 +08:00

383 lines
13 KiB
TypeScript

import {
Button,
Popup,
SafeArea,
Tabs,
Uploader,
UploaderFileItem,
} from "@nutui/nutui-react-taro";
import { View } from "@tarojs/components";
import { useState } from "react";
import Taro from "@tarojs/taro";
import { uploadFile } from "@/utils/uploader";
export default function SupplierInfoSection(props: {
purchaseOrderVO: BusinessAPI.PurchaseOrderVO;
onChange?: (purchaseOrderVO: BusinessAPI.PurchaseOrderVO) => void;
readOnly?: boolean;
}) {
const { purchaseOrderVO, readOnly, onChange } = props;
const [tab, setTab] = useState<string | number>("0");
// 为每个供应商维护未上传状态
const [uploadStates, setUploadStates] = useState<
Record<
string,
{
emptyWeightImg?: UploaderFileItem[];
totalWeightImg?: UploaderFileItem[];
contractImg?: UploaderFileItem[];
invoiceImg?: UploaderFileItem[];
}
>
>({});
// 控制弹窗显示状态
const [popupVisible, setPopupVisible] = useState(false);
const [currentSupplier, setCurrentSupplier] =
useState<BusinessAPI.OrderSupplier | null>(null);
const [currentUploadType, setCurrentUploadType] = useState<string>("");
// 判断供应商是否所有附件都已未上传
const isSupplierFullyUploaded = (supplier: BusinessAPI.OrderSupplier) => {
return (
supplier.emptyWeightImg &&
supplier.totalWeightImg &&
supplier.invoiceUpload &&
supplier.contractUpload
);
};
// 获取显示的标签文本
const getTabTitle = (supplier: BusinessAPI.OrderSupplier) => {
if (isSupplierFullyUploaded(supplier)) {
return `${supplier.name} - 票证已未上传`;
} else {
return `${supplier.name} - 待补充`;
}
};
// 处理未上传按钮点击
const handleUploadClick = (
supplier: BusinessAPI.OrderSupplier,
type: string,
) => {
if (readOnly) return;
setCurrentSupplier(supplier);
setCurrentUploadType(type);
setPopupVisible(true);
};
// 处理查看按钮点击 - 使用 Taro.previewImage 预览图片
const handleViewClick = (
supplier: BusinessAPI.OrderSupplier,
type: string,
) => {
// 构造图片 URL 数组
const imageUrls: string[] = [];
console.log("supplier", supplier, type)
if (type === "emptyWeightImg" && supplier.emptyWeightImg) {
imageUrls.push(supplier.emptyWeightImg);
} else if (type === "totalWeightImg" && supplier.totalWeightImg) {
imageUrls.push(supplier.totalWeightImg);
} else if (type === "contractImg" && supplier.contractUpload) {
supplier.contractImg?.forEach((file) => {
if (file) {
imageUrls.push(file);
}
});
} else if (type === "invoiceImg" && supplier.invoiceUpload) {
supplier.invoiceImg?.forEach((file) => {
if (file) {
imageUrls.push(file);
}
});
}
// 使用 Taro.previewImage 预览图片
if (imageUrls.length > 0) {
Taro.previewImage({
urls: imageUrls,
});
}
};
// 处理上传确认
const handleUploadConfirm = () => {
if (!currentSupplier || !currentUploadType) return;
// 获取当前上传的文件列表
const uploadedFiles = uploadStates[currentSupplier.orderSupplierId]?.[currentUploadType];
if (!uploadedFiles) return;
// 创建新的 purchaseOrderVO 副本
const updatedPurchaseOrderVO = { ...purchaseOrderVO };
if (updatedPurchaseOrderVO.orderSupplierList) {
updatedPurchaseOrderVO.orderSupplierList = [...updatedPurchaseOrderVO.orderSupplierList];
}
// 找到当前供应商在列表中的索引
const supplierIndex = updatedPurchaseOrderVO.orderSupplierList?.findIndex(
(supplier) => supplier.orderSupplierId === currentSupplier.orderSupplierId
);
if (supplierIndex === undefined || supplierIndex === -1) return;
// 更新对应的供应商字段
const updatedSupplier = { ...updatedPurchaseOrderVO.orderSupplierList[supplierIndex] };
switch (currentUploadType) {
case "emptyWeightImg":
// 空磅照片只取第一张图片的 URL
updatedSupplier.emptyWeightImg = uploadedFiles[0]?.url || "";
break;
case "totalWeightImg":
// 总磅照片只取第一张图片的 URL
updatedSupplier.totalWeightImg = uploadedFiles[0]?.url || "";
break;
case "contractImg":
// 合同图片可以有多张,保存所有 URL
updatedSupplier.contractImg = uploadedFiles
.filter(file => file.url)
.map(file => file.url!);
updatedSupplier.contractUpload = uploadedFiles.length > 0;
break;
case "invoiceImg":
// 发票图片可以有多张,保存所有 URL
updatedSupplier.invoiceImg = uploadedFiles
.filter(file => file.url)
.map(file => file.url!);
updatedSupplier.invoiceUpload = uploadedFiles.length > 0;
break;
}
// 更新供应商列表
if (updatedPurchaseOrderVO.orderSupplierList) {
updatedPurchaseOrderVO.orderSupplierList[supplierIndex] = updatedSupplier;
}
// 调用 onChange 回调更新父组件状态
if (onChange) {
onChange(updatedPurchaseOrderVO);
}
// 重置弹窗状态
setPopupVisible(false);
setCurrentSupplier(null);
setCurrentUploadType("");
};
return (
<>
<Tabs
style={{
// @ts-ignore
"--nutui-tabs-tabpane-padding": 0,
}}
value={tab}
onChange={(value) => {
setTab(value);
}}
>
{purchaseOrderVO?.orderSupplierList?.map((item, index) => (
<Tabs.TabPane title={getTabTitle(item)} key={index}>
{/* 瓜农凭证信息 */}
<View className="flex w-full flex-col gap-2.5">
<View className="flex !h-8 flex-row items-center justify-between">
<View className="text-neutral-dark flex-shrink-0 text-sm">
</View>
<View className="text-neutral-darkest text-sm font-medium">
{item.emptyWeightImg ? (
<Button
fill={"none"}
type={"primary"}
className="edit-btn"
onClick={() => handleViewClick(item, "emptyWeightImg")}
>
</Button>
) : readOnly ? (
<Button fill={"none"} type={"default"} disabled>
</Button>
) : (
<Button
fill={"none"}
type={"primary"}
className="edit-btn"
onClick={() => handleUploadClick(item, "emptyWeightImg")}
>
</Button>
)}
</View>
</View>
<View className="flex !h-8 flex-row items-center justify-between">
<View className="text-neutral-dark flex-shrink-0 text-sm">
</View>
<View className="text-neutral-darkest text-sm font-medium">
{item.totalWeightImg ? (
<Button
fill={"none"}
type={"primary"}
className="edit-btn"
onClick={() => handleViewClick(item, "totalWeightImg")}
>
</Button>
) : readOnly ? (
<Button fill={"none"} type={"default"} disabled>
</Button>
) : (
<Button
fill={"none"}
type={"primary"}
className="edit-btn"
onClick={() => handleUploadClick(item, "totalWeightImg")}
>
</Button>
)}
</View>
</View>
<View className="flex !h-8 flex-row items-center justify-between">
<View className="text-neutral-dark flex-shrink-0 text-sm">
</View>
<View className="text-neutral-darkest text-sm font-medium">
{item.contractUpload ? (
<Button
fill={"none"}
type={"primary"}
className="edit-btn"
onClick={() => handleViewClick(item, "contractImg")}
>
</Button>
) : readOnly ? (
<Button fill={"none"} type={"default"} disabled>
</Button>
) : (
<Button
fill={"none"}
type={"primary"}
className="edit-btn"
onClick={() => handleUploadClick(item, "contractImg")}
>
</Button>
)}
</View>
</View>
<View className="flex !h-8 flex-row items-center justify-between">
<View className="text-neutral-dark flex-shrink-0 text-sm">
</View>
<View className="text-neutral-darkest text-sm font-medium">
{item.invoiceUpload ? (
<Button
fill={"none"}
type={"primary"}
className="edit-btn"
onClick={() => handleViewClick(item, "invoiceImg")}
>
</Button>
) : readOnly ? (
<Button fill={"none"} type={"default"} disabled>
</Button>
) : (
<Button
fill={"none"}
type={"primary"}
className="edit-btn"
onClick={() => handleUploadClick(item, "invoiceImg")}
>
</Button>
)}
</View>
</View>
</View>
</Tabs.TabPane>
))}
</Tabs>
{/* 未上传弹窗 */}
<Popup
visible={popupVisible && !readOnly}
position="bottom"
onClose={() => setPopupVisible(false)}
round
lockScroll
>
<View className="flex h-full flex-col p-4">
<View className="mb-4 text-lg font-bold">
{currentUploadType === "emptyWeightImg" && "空磅照片"}
{currentUploadType === "totalWeightImg" && "总磅照片"}
{currentUploadType === "contractImg" && "合同"}
{currentUploadType === "invoiceImg" && "发票"}
</View>
<View className="mb-4 flex-1 overflow-y-auto">
<Uploader
multiple={currentUploadType === "contractImg"}
maxCount={currentUploadType === "contractImg" ? 9 : 1}
//@ts-ignore
upload={uploadFile}
defaultValue={
currentSupplier
? uploadStates[currentSupplier.orderSupplierId]?.[currentUploadType] || []
: []
}
onChange={(fileList) => {
if (currentSupplier) {
setUploadStates((prev) => ({
...prev,
[currentSupplier.orderSupplierId]: {
...prev[currentSupplier.orderSupplierId],
[currentUploadType]: fileList,
},
}));
}
}}
/>
</View>
<View className="flex justify-end gap-2">
<View className={"flex-1"}>
<Button
size="large"
block
type="default"
onClick={() => setPopupVisible(false)}
>
</Button>
</View>
<View className={"flex-1"}>
<Button
size="large"
block
type="primary"
onClick={handleUploadConfirm}
>
</Button>
</View>
</View>
</View>
<SafeArea position={"bottom"} />
</Popup>
</>
);
}