import { Image, Text, View } from "@tarojs/components"; import { Button, Dialog, Input, Radio, Toast } from "@nutui/nutui-react-taro"; import { Icon, SupplierPicker } from "@/components"; import Taro from "@tarojs/taro"; import { forwardRef, useImperativeHandle, useState } from "react"; import { buildUrl, generateShortId, uploadFile } from "@/utils"; import { globalStore } from "@/store/global-store"; // 定义ref暴露的方法接口 export interface StallInfoRef { validate: () => boolean; } interface IStallInfoProps { index: number; value: BusinessAPI.OrderVO; onChange: (orderVO: BusinessAPI.OrderVO) => void; } export default forwardRef( function StallInfo(props, ref) { const [showSupplierPicker, setShowSupplierPicker] = useState(false); const onRemove = (supplierVO: BusinessAPI.OrderSupplier) => { if (orderSupplierList.length <= 1) { setOrderSupplierList([ { orderSupplierId: generateShortId(), supplierId: "", // 空的supplierId表示其他家档口 name: "", payeeName: "", bankName: "", bankCard: "", phone: "", type: "", selected: true, isPaper: false, orderPackageList: [], packageUsage: [ { boxType: "USED", isUsed: 0, }, ], } as any, ]); return; } else { let temp = orderSupplierList.filter( (item: BusinessAPI.OrderSupplier) => item.orderSupplierId !== supplierVO.orderSupplierId, ) as BusinessAPI.OrderSupplier[]; temp[0].selected = true; setOrderSupplierList(temp); } }; const { value, onChange, index } = props; const { orderSupplierList } = value; const supplierVO = orderSupplierList[index] as BusinessAPI.OrderSupplier; const supplierCount = orderSupplierList.length; const isLast = index === supplierCount - 1; const { setLoading } = globalStore((state: any) => state); // 获取已选择的供应商ID列表(排除当前项) const selectedSupplierIds = orderSupplierList .filter((supplier, idx) => idx !== index && supplier.supplierId) .map((supplier) => supplier.supplierId!); // 获取已选择的供应商ID列表(排除当前项) const selectedSupplierNames = orderSupplierList .filter((supplier, idx) => idx !== index && supplier.supplierId) .map((supplier) => supplier.name!); const setOrderSupplierList = ( newOrderSupplierList: BusinessAPI.OrderSupplier[], ) => { onChange({ ...value, orderSupplierList: newOrderSupplierList, }); }; const setSupplierVO = (newSupplierVO: BusinessAPI.OrderSupplier) => { console.log("setSupplierVO", newSupplierVO); setOrderSupplierList([ ...orderSupplierList.map((item: BusinessAPI.OrderSupplier) => { if (item.orderSupplierId == newSupplierVO.orderSupplierId) { return { ...item, ...newSupplierVO, }; } return item; }), ]); }; // 微信收款码上传处理函数 const handleWechatQrUpload = async () => { await Taro.chooseImage({ count: 1, sourceType: ["album", "camera"], success: (res) => { setLoading(true); const file = res.tempFiles[0]; uploadFile(file.path) .then(({ url }) => { setSupplierVO({ ...supplierVO, wechatQr: url, }); setLoading(false); Toast.show("toast", { title: "上传成功", icon: "success", content: "微信收款码已上传", }); }) .catch((err) => { Toast.show("toast", { title: "上传失败", icon: "fail", content: err.message || "上传过程中发生错误", }); setLoading(false); }); }, fail: (err) => { Toast.show("toast", { title: "选择图片失败", icon: "fail", content: err.errMsg, }); }, }); }; // 校验状态 const [nameError, setNameError] = useState<{ [key: string]: boolean }>({}); const [payeeNameError, setPayeeNameError] = useState<{ [key: string]: boolean; }>({}); const [bankCardError, setBankCardError] = useState<{ [key: string]: boolean; }>({}); const [bankNameError, setBankNameError] = useState<{ [key: string]: boolean; }>({}); const [phoneError, setPhoneError] = useState<{ [key: string]: boolean; }>({}); const [isLastFarmerError, setIsLastFarmerError] = useState<{ [key: string]: boolean; }>({}); // 添加是否要拼车的错误状态 // 校验姓名函数 const validateName = (name: string) => { if (!name) { return false; } // 姓名至少2个字符 return name.length >= 2; }; // 校验身份证号函数 const validatePayeeName = (payeeName: string) => { if (!payeeName) { return false; } // 收款人姓名至少2个字符 return payeeName.length >= 2; }; // 校验银行卡号函数 const validateBankCard = (bankCard: string) => { if (!bankCard) { return false; } // 银行卡号一般为16-19位数字 const bankCardRegex = /^\d{16,19}$/; return bankCardRegex.test(bankCard); }; // 校验银行名称函数 const validateBankName = (bankName: string) => { if (!bankName) { return false; } // 银行名称至少2个字符 return bankName.length >= 2; }; // 校验手机号函数 const validatePhone = (phone: string) => { if (!phone) { return false; } // 使用项目规范的手机号正则表达式 const phoneRegex = /^1[3456789]\d{9}$/; return phoneRegex.test(phone); }; // 校验是否要拼车函数 const validateIsLastFarmer = (isLast?: boolean) => { // 必须选择要不要 return isLast !== undefined; }; // 对外暴露的校验方法 const validate = () => { const id = supplierVO?.orderSupplierId; if (!id) return false; const isNameValid = validateName(supplierVO.name || ""); if ( selectedSupplierNames && selectedSupplierNames.includes(supplierVO.name || "") ) { Toast.show("toast", { icon: "fail", title: "提示", content: "该档口已被选择,请输入其他名称", }); return false; } // 根据档口类型决定是否校验收款信息 const isPayeeNameValid = supplierVO.type === "STALL" ? true // 自家档口不需要校验收款人姓名 : validatePayeeName(supplierVO.payeeName || ""); const isBankNameValid = supplierVO.type === "STALL" ? true // 自家档口不需要校验银行名称 : validateBankName(supplierVO.bankName || ""); const isBankCardValid = supplierVO.type === "STALL" ? true // 自家档口不需要校验银行卡号 : validateBankCard(supplierVO.bankCard || ""); const isPhoneValid = supplierVO.type === "STALL" ? true // 自家档口不需要校验手机号 : validatePhone(supplierVO.phone || ""); const isLastFarmerValid = validateIsLastFarmer(supplierVO?.isLast); // 校验是否需要拼车 // 更新错误状态 setNameError((prev) => ({ ...prev, [id]: !isNameValid, })); setPayeeNameError((prev) => ({ ...prev, [id]: supplierVO.type === "STALL" ? false : !isPayeeNameValid, })); setBankNameError((prev) => ({ ...prev, [id]: supplierVO.type === "STALL" ? false : !isBankNameValid, })); setBankCardError((prev) => ({ ...prev, [id]: supplierVO.type === "STALL" ? false : !isBankCardValid, })); setPhoneError((prev) => ({ ...prev, [id]: supplierVO.type === "STALL" ? false : !isPhoneValid, })); // 更新是否为最后一个档口的错误状态 setIsLastFarmerError((prev) => ({ ...prev, [id]: !isLastFarmerValid, })); const isValid = isNameValid && isPayeeNameValid && isBankNameValid && isBankCardValid && isPhoneValid && isLastFarmerValid; if (!isValid) { const errorMsg = supplierVO.type === "STALL" ? "请选择自家档口" : "请完善其他家档口信息"; Toast.show("toast", { icon: "fail", title: "提示", content: errorMsg, }); } return isValid; }; // 将校验方法暴露给父组件 useImperativeHandle(ref, () => ({ validate, })); // 处理姓名变化 const handleNameChange = ( value: string, supplierVO: BusinessAPI.OrderSupplier, ) => { setSupplierVO({ ...supplierVO!, name: value, }); const id = supplierVO.orderSupplierId; // 校验并更新错误状态 const isValid = validateName(value); setNameError((prev) => ({ ...prev, [id]: !isValid && value !== "", })); if (isValid) { setNameError((prev) => ({ ...prev, [id]: false, })); } if (selectedSupplierNames && selectedSupplierNames.includes(value)) { Toast.show("toast", { icon: "fail", title: "提示", content: "该档口已被选择,请输入其他名称", }); } }; // 处理姓名失焦 const handleNameBlur = async (value: string, id: any) => { if (value && !validateName(value)) { setNameError((prev) => ({ ...prev, [id]: true, })); return; } else { setNameError((prev) => ({ ...prev, [id]: false, })); } }; // 处理收款人姓名变化 const handlePayeeNameChange = ( value: string, supplierVO: BusinessAPI.OrderSupplier, ) => { setSupplierVO({ ...supplierVO, payeeName: value, }); const id = supplierVO.orderSupplierId; // 校验并更新错误状态 const isValid = validatePayeeName(value); setPayeeNameError((prev) => ({ ...prev, [id]: !isValid && value !== "", })); if (isValid) { setPayeeNameError((prev) => ({ ...prev, [id]: false, })); } }; // 处理收款人姓名失焦 const handlePayeeNameBlur = (value: string, id: any) => { if (value && !validatePayeeName(value)) { setPayeeNameError((prev) => ({ ...prev, [id]: true, })); Toast.show("toast", { icon: "fail", title: "提示", content: "请输入正确的收款人姓名", }); } else { setPayeeNameError((prev) => ({ ...prev, [id]: false, })); } }; // 处理银行卡号变化 const handleBankCardChange = ( value: string, supplierVO: BusinessAPI.OrderSupplier, ) => { setSupplierVO({ ...supplierVO, bankCard: value, }); const id = supplierVO.orderSupplierId; // 校验并更新错误状态 const isValid = validateBankCard(value); setBankCardError((prev) => ({ ...prev, [id]: !isValid && value !== "", })); if (isValid) { setBankCardError((prev) => ({ ...prev, [id]: false, })); } }; // 处理银行卡号失焦 const handleBankCardBlur = (value: string, id: any) => { if (value && !validateBankCard(value)) { setBankCardError((prev) => ({ ...prev, [id]: true, })); Toast.show("toast", { icon: "fail", title: "提示", content: "请输入正确的银行卡号", }); } else { setBankCardError((prev) => ({ ...prev, [id]: false, })); } }; // 处理银行名称变化 const handleBankNameChange = ( value: string, supplierVO: BusinessAPI.OrderSupplier, ) => { setSupplierVO({ ...supplierVO, bankName: value, }); const id = supplierVO.orderSupplierId; // 校验并更新错误状态 const isValid = validateBankName(value); setBankNameError((prev) => ({ ...prev, [id]: !isValid && value !== "", })); if (isValid) { setBankNameError((prev) => ({ ...prev, [id]: false, })); } }; // 处理银行名称失焦 const handleBankNameBlur = (value: string, id: any) => { if (value && !validateBankName(value)) { setBankNameError((prev) => ({ ...prev, [id]: true, })); Toast.show("toast", { icon: "fail", title: "提示", content: "请输入正确的银行名称", }); } else { setBankNameError((prev) => ({ ...prev, [id]: false, })); } }; // 处理手机号变化 const handlePhoneChange = ( value: string, supplierVO: BusinessAPI.OrderSupplier, ) => { setSupplierVO({ ...supplierVO, phone: value, }); const id = supplierVO.orderSupplierId; // 校验并更新错误状态 const isValid = validatePhone(value); setPhoneError((prev) => ({ ...prev, [id]: !isValid && value !== "", })); if (isValid) { setPhoneError((prev) => ({ ...prev, [id]: false, })); } }; // 处理手机号失焦 const handlePhoneBlur = (value: string, id: any) => { if (value && !validatePhone(value)) { setPhoneError((prev) => ({ ...prev, [id]: true, })); Toast.show("toast", { icon: "fail", title: "提示", content: "请输入正确的手机号", }); } else { setPhoneError((prev) => ({ ...prev, [id]: false, })); } }; // 处理删除档口确认 const handleRemoveConfirm = (supplierVO: BusinessAPI.OrderSupplier) => { Dialog.open("dialog", { title: "移除档口", content: `确定要移除"${supplierVO.name || "这个档口"}"吗?移除后如果需要可以重新添加。`, onConfirm: () => { onRemove(supplierVO); Dialog.close("dialog"); }, onCancel: () => { Dialog.close("dialog"); }, }); }; console.log("supplierVO", supplierVO); if (!supplierVO) { return; } return ( {/* 功能提醒 */} 收集档口信息的目的是用于打款 {/* 只有最后一个档口才显示是否为最后一个档口的选项和添加按钮 */} {isLast && ( {supplierCount > 1 ? ( 这车货还需继续拼车吗? ) : ( 这车货需拼车吗? )} { // 清除错误状态 setIsLastFarmerError((prev) => ({ ...prev, [supplierVO.orderSupplierId]: false, })); // 根据用户选择设置是否为最后一个档口 const isLastValue = value === "true" ? true : value === "false" ? false : undefined; setSupplierVO({ ...supplierVO, // @ts-ignore isLast: isLastValue, }); }} > 需要 不需要 {isLastFarmerError[supplierVO.orderSupplierId] && ( 请选择本车货要不要拼车 )} )} {/* 档口类型选择卡片 */} 选择档口类型 {/* 自家档口选项 */} { // 自家档口需要选择供应商 setSupplierVO({ ...supplierVO, supplierId: "", name: "", payeeName: "", bankName: "", bankCard: "", phone: "", wechatQr: undefined, type: "STALL", }); setShowSupplierPicker(true); }} > { // 自家档口需要选择供应商 setSupplierVO({ ...supplierVO, supplierId: "", name: "", payeeName: "", bankName: "", bankCard: "", phone: "", wechatQr: undefined, type: "STALL", }); setShowSupplierPicker(true); }} /> 自家档口 无需填写收款信息,款项转为内部回款 {/* 其他家档口选项 */} { // 其他家档口需要清空供应商ID setSupplierVO({ ...supplierVO, supplierId: "", name: "", payeeName: "", bankName: "", bankCard: "", phone: "", wechatQr: undefined, type: "OTHER_STALL", }); }} > { // 其他家档口需要清空供应商ID setSupplierVO({ ...supplierVO, supplierId: "", name: "", payeeName: "", bankName: "", bankCard: "", phone: "", wechatQr: undefined, type: "OTHER_STALL", }); }} /> 其他家档口 需填写收款人、银行卡号等信息,走外部结算流程 {/* 快捷工具 */} {supplierVO.type === "OTHER_STALL" && ( )} {/* 档口信息 */} {supplierVO.name || "档口"}的基本信息 {supplierCount > 1 && isLast && ( handleRemoveConfirm(supplierVO)} > 移除档口 )} {/* 根据档口类型显示不同的表单内容 */} {supplierVO.type === "STALL" && ( // 自家档口:显示档口选择器 <> 选择档口 点我选档口 } onFinish={(supplierVO1) => { // 检查是否已经选择了该档口 if ( selectedSupplierIds && selectedSupplierIds.includes(supplierVO1.supplierId!) ) { Toast.show("toast", { icon: "fail", title: "提示", content: "该档口已被选择,请选择其他档口", }); return; } setSupplierVO({ ...supplierVO, ...supplierVO1, // 自家档口时清空收款信息 payeeName: "", bankName: "", bankCard: "", phone: "", wechatQr: undefined, }); // 清除所有错误状态 setNameError((prev) => ({ ...prev, [supplierVO.orderSupplierId]: false, })); }} /> {supplierVO.name ? ( 已选择档口:{supplierVO.name} 自家档口,无需填写收款信息 ) : ( 请选择一个档口,款项将转为内部回款 )} )} {supplierVO.type === "OTHER_STALL" && ( // 其他家档口:显示完整的表单 <> 档口名称 点我选档口 } onFinish={(supplierVO1) => { // 检查是否已经选择了该档口 if ( selectedSupplierIds && selectedSupplierIds.includes(supplierVO1.supplierId!) ) { Toast.show("toast", { icon: "fail", title: "提示", content: "该档口已被选择,请选择其他档口", }); return; } setSupplierVO({ ...supplierVO, ...supplierVO1, }); // 清除所有错误状态 setNameError((prev) => ({ ...prev, [supplierVO.orderSupplierId]: false, })); }} /> handleNameChange(value, supplierVO)} onBlur={() => handleNameBlur( supplierVO?.name || "", supplierVO.orderSupplierId, ) } className="flex-1" /> {nameError[supplierVO.orderSupplierId] && ( {`档口名称"${supplierVO.name}"至少2个字符`} )} 收款人姓名 handlePayeeNameChange(value, supplierVO) } onBlur={() => handlePayeeNameBlur( supplierVO?.payeeName || "", supplierVO.orderSupplierId, ) } className="flex-1" /> {payeeNameError[supplierVO.orderSupplierId] && ( {`收款人姓名至少2个字符`} )} 银行名称 handleBankNameChange(value, supplierVO) } onBlur={() => handleBankNameBlur( supplierVO?.bankName || "", supplierVO.orderSupplierId, ) } className="flex-1" /> {bankNameError[supplierVO.orderSupplierId] && ( {`银行名称"${supplierVO.bankName}"至少2个字符`} )} 银行卡号 handleBankCardChange(value, supplierVO) } onBlur={() => handleBankCardBlur( supplierVO?.bankCard || "", supplierVO.orderSupplierId, ) } className="flex-1" /> {bankCardError[supplierVO.orderSupplierId] && ( 请输入正确的银行卡号 )} 手机号码 handlePhoneChange(value, supplierVO)} onBlur={() => handlePhoneBlur( supplierVO?.phone || "", supplierVO.orderSupplierId, ) } className="flex-1" /> {phoneError[supplierVO.orderSupplierId] && ( 请输入正确的手机号 )} )} {/* 只有其他家档口才显示微信收款码部分 */} {supplierVO.type === "OTHER_STALL" && ( {supplierVO.name || "档口"}的微信收款码(可跳过) {supplierVO.wechatQr ? ( 微信收款码 已上传完成 若档口无法开发票,则可打款到微信 ) : ( 上传微信收款码 支持从相册选择或拍照 若档口无法开发票,则可打款到微信 )} )} {/* 隐藏的供应商选择器,用于自家档口选择 */} {showSupplierPicker && ( setShowSupplierPicker(true)} /> } onFinish={(supplierVO1) => { setShowSupplierPicker(false); // 检查是否已经选择了该档口 if ( selectedSupplierIds && selectedSupplierIds.includes(supplierVO1.supplierId!) ) { Toast.show("toast", { icon: "fail", title: "提示", content: "该档口已被选择,请选择其他档口", }); return; } setSupplierVO({ ...supplierVO, ...supplierVO1, // 自家档口时清空收款信息 payeeName: "", bankName: "", bankCard: "", phone: "", wechatQr: undefined, }); // 清除所有错误状态 setNameError((prev) => ({ ...prev, [supplierVO.orderSupplierId]: false, })); }} /> )} ); }, );