1237 lines
40 KiB
TypeScript
1237 lines
40 KiB
TypeScript
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<StallInfoRef, IStallInfoProps>(
|
||
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 (
|
||
<View
|
||
className="flex flex-1 flex-col gap-2.5 p-2.5"
|
||
style={{
|
||
//@ts-ignore
|
||
"--nutui-input-padding": 0,
|
||
}}
|
||
>
|
||
{/* 功能提醒 */}
|
||
<View className="flex items-center rounded-lg border border-blue-200 bg-blue-50 p-2.5">
|
||
<Icon
|
||
className={"mr-1"}
|
||
name="circle-info"
|
||
color={"var(--color-blue-700)"}
|
||
size={18}
|
||
/>
|
||
<View className={"text-sm text-blue-700"}>
|
||
收集档口信息的目的是用于打款
|
||
</View>
|
||
</View>
|
||
|
||
{/* 只有最后一个档口才显示是否为最后一个档口的选项和添加按钮 */}
|
||
{isLast && (
|
||
<View className="border-primary rounded-lg border-4 bg-white p-2.5 shadow-sm">
|
||
<View className="flex items-center justify-between">
|
||
{supplierCount > 1 ? (
|
||
<View className="text-sm">这车货还需继续拼车吗?</View>
|
||
) : (
|
||
<View className="text-sm">这车货需拼车吗?</View>
|
||
)}
|
||
|
||
<View className="text-neutral-darkest text-sm font-medium">
|
||
<Radio.Group
|
||
direction="horizontal"
|
||
value={
|
||
supplierVO.isLast === true
|
||
? "true"
|
||
: supplierVO.isLast === false
|
||
? "false"
|
||
: undefined
|
||
}
|
||
onChange={(value) => {
|
||
// 清除错误状态
|
||
setIsLastFarmerError((prev) => ({
|
||
...prev,
|
||
[supplierVO.orderSupplierId]: false,
|
||
}));
|
||
|
||
// 根据用户选择设置是否为最后一个档口
|
||
const isLastValue =
|
||
value === "true"
|
||
? true
|
||
: value === "false"
|
||
? false
|
||
: undefined;
|
||
setSupplierVO({
|
||
...supplierVO,
|
||
// @ts-ignore
|
||
isLast: isLastValue,
|
||
});
|
||
}}
|
||
>
|
||
<Radio value="false">需要</Radio>
|
||
<Radio value="true">不需要</Radio>
|
||
</Radio.Group>
|
||
</View>
|
||
</View>
|
||
{isLastFarmerError[supplierVO.orderSupplierId] && (
|
||
<View className="mt-1 text-xs text-red-500">
|
||
请选择本车货要不要拼车
|
||
</View>
|
||
)}
|
||
</View>
|
||
)}
|
||
|
||
{/* 档口类型选择卡片 */}
|
||
<View className="border-primary rounded-lg border-4 bg-white p-2.5 shadow-sm">
|
||
<View className="text-primary mb-2.5 text-base font-bold">
|
||
选择档口类型
|
||
</View>
|
||
|
||
{/* 自家档口选项 */}
|
||
<View
|
||
className={`mb-2.5 flex cursor-pointer items-center rounded-lg border-2 p-2.5 transition-colors ${
|
||
supplierVO.type === "STALL"
|
||
? "border-blue-500 bg-blue-50"
|
||
: "border-gray-200 hover:border-gray-300 hover:bg-gray-50"
|
||
}`}
|
||
onClick={() => {
|
||
// 自家档口需要选择供应商
|
||
setSupplierVO({
|
||
...supplierVO,
|
||
supplierId: "",
|
||
name: "",
|
||
payeeName: "",
|
||
bankName: "",
|
||
bankCard: "",
|
||
phone: "",
|
||
wechatQr: undefined,
|
||
type: "STALL",
|
||
});
|
||
setShowSupplierPicker(true);
|
||
}}
|
||
>
|
||
<Radio
|
||
value="OWN"
|
||
className="mr-2.5"
|
||
checked={supplierVO.type === "STALL"}
|
||
onChange={() => {
|
||
// 自家档口需要选择供应商
|
||
setSupplierVO({
|
||
...supplierVO,
|
||
supplierId: "",
|
||
name: "",
|
||
payeeName: "",
|
||
bankName: "",
|
||
bankCard: "",
|
||
phone: "",
|
||
wechatQr: undefined,
|
||
type: "STALL",
|
||
});
|
||
setShowSupplierPicker(true);
|
||
}}
|
||
/>
|
||
<View className="flex-1">
|
||
<View className="font-medium text-gray-900">自家档口</View>
|
||
<View className="text-sm text-gray-600">
|
||
无需填写收款信息,款项转为内部回款
|
||
</View>
|
||
</View>
|
||
</View>
|
||
|
||
{/* 其他家档口选项 */}
|
||
<View
|
||
className={`flex cursor-pointer items-center rounded-lg border-2 p-2.5 transition-colors ${
|
||
supplierVO.type === "OTHER_STALL"
|
||
? "border-blue-500 bg-blue-50"
|
||
: "border-gray-200 hover:border-gray-300 hover:bg-gray-50"
|
||
}`}
|
||
onClick={() => {
|
||
// 其他家档口需要清空供应商ID
|
||
setSupplierVO({
|
||
...supplierVO,
|
||
supplierId: "",
|
||
name: "",
|
||
payeeName: "",
|
||
bankName: "",
|
||
bankCard: "",
|
||
phone: "",
|
||
wechatQr: undefined,
|
||
type: "OTHER_STALL",
|
||
});
|
||
}}
|
||
>
|
||
<Radio
|
||
value="EXTERNAL"
|
||
className="mr-2.5"
|
||
checked={supplierVO.type === "OTHER_STALL"}
|
||
onChange={() => {
|
||
// 其他家档口需要清空供应商ID
|
||
setSupplierVO({
|
||
...supplierVO,
|
||
supplierId: "",
|
||
name: "",
|
||
payeeName: "",
|
||
bankName: "",
|
||
bankCard: "",
|
||
phone: "",
|
||
wechatQr: undefined,
|
||
type: "OTHER_STALL",
|
||
});
|
||
}}
|
||
/>
|
||
<View className="flex-1">
|
||
<View className="font-medium text-gray-900">其他家档口</View>
|
||
<View className="text-sm text-gray-600">
|
||
需填写收款人、银行卡号等信息,走外部结算流程
|
||
</View>
|
||
</View>
|
||
</View>
|
||
</View>
|
||
|
||
{/* 快捷工具 */}
|
||
{supplierVO.type === "OTHER_STALL" && (
|
||
<View className="flex gap-2">
|
||
<View className={"flex-1"}>
|
||
<Button
|
||
block
|
||
icon={<Icon name={"credit-card"} size={28} color={"white"} />}
|
||
type={"primary"}
|
||
size={"large"}
|
||
className="bg-primary flex flex-1 items-center justify-center text-white"
|
||
onClick={() => {
|
||
Taro.navigateTo({
|
||
url: buildUrl("/pages/public/camera/ocr", {
|
||
type: "bankcard",
|
||
}),
|
||
complete: () => {
|
||
Taro.eventCenter.on("ocr", (res) => {
|
||
console.log("识别结果为:", res.result);
|
||
setSupplierVO({
|
||
...supplierVO,
|
||
bankCard: res.result.number,
|
||
});
|
||
|
||
Taro.eventCenter.off("ocr");
|
||
});
|
||
},
|
||
});
|
||
}}
|
||
>
|
||
<View>扫描银行卡</View>
|
||
</Button>
|
||
</View>
|
||
</View>
|
||
)}
|
||
|
||
{/* 档口信息 */}
|
||
<View className="border-primary rounded-lg border-4 bg-white p-2.5 shadow-sm">
|
||
<View className="flex flex-col gap-2.5">
|
||
<View className="flex items-center justify-between">
|
||
<View className="text-primary text-base font-bold">
|
||
{supplierVO.name || "档口"}的基本信息
|
||
</View>
|
||
{supplierCount > 1 && isLast && (
|
||
<View
|
||
className="cursor-pointer text-sm text-red-500"
|
||
onClick={() => handleRemoveConfirm(supplierVO)}
|
||
>
|
||
移除档口
|
||
</View>
|
||
)}
|
||
</View>
|
||
|
||
{/* 根据档口类型显示不同的表单内容 */}
|
||
{supplierVO.type === "STALL" && (
|
||
// 自家档口:显示档口选择器
|
||
<>
|
||
<View className="flex flex-row justify-between">
|
||
<View className={"block text-sm font-normal text-[#000000]"}>
|
||
选择档口
|
||
</View>
|
||
<SupplierPicker
|
||
type={"STALL"}
|
||
trigger={
|
||
<View className="flex items-center">
|
||
<Icon
|
||
className={"mr-1 leading-4"}
|
||
name="address-book"
|
||
size={16}
|
||
color="var(--color-primary)"
|
||
/>
|
||
<Text className={"text-primary ml-1 text-sm font-bold"}>
|
||
点我选档口
|
||
</Text>
|
||
</View>
|
||
}
|
||
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,
|
||
}));
|
||
}}
|
||
/>
|
||
</View>
|
||
{supplierVO.name ? (
|
||
<View className="rounded-lg bg-green-50 p-2.5">
|
||
<View className="flex items-center">
|
||
<Icon
|
||
name="circle-check"
|
||
size={16}
|
||
color="var(--color-green-600)"
|
||
className="mr-2"
|
||
/>
|
||
<View>
|
||
<View className="font-medium text-green-700">
|
||
已选择档口:{supplierVO.name}
|
||
</View>
|
||
<View className="text-sm text-green-600">
|
||
自家档口,无需填写收款信息
|
||
</View>
|
||
</View>
|
||
</View>
|
||
</View>
|
||
) : (
|
||
<View className="rounded-lg bg-yellow-50 p-2.5">
|
||
<View className="flex items-center">
|
||
<Icon
|
||
name="exclamation"
|
||
size={16}
|
||
color="var(--color-yellow-600)"
|
||
className="mr-2"
|
||
/>
|
||
<View className="text-sm text-yellow-700">
|
||
请选择一个档口,款项将转为内部回款
|
||
</View>
|
||
</View>
|
||
</View>
|
||
)}
|
||
</>
|
||
)}
|
||
{supplierVO.type === "OTHER_STALL" && (
|
||
// 其他家档口:显示完整的表单
|
||
<>
|
||
<View className="flex flex-row justify-between">
|
||
<View className={"block text-sm font-normal text-[#000000]"}>
|
||
档口名称
|
||
</View>
|
||
|
||
<SupplierPicker
|
||
type={"OTHER_STALL"}
|
||
trigger={
|
||
<View className="flex items-center">
|
||
<Icon
|
||
className={"mr-1 leading-4"}
|
||
name="address-book"
|
||
size={16}
|
||
color="var(--color-primary)"
|
||
/>
|
||
<Text className={"text-primary ml-1 text-sm font-bold"}>
|
||
点我选档口
|
||
</Text>
|
||
</View>
|
||
}
|
||
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,
|
||
}));
|
||
}}
|
||
/>
|
||
</View>
|
||
<View
|
||
className={`flex h-10 w-full items-center rounded-md ${nameError[supplierVO.orderSupplierId] ? "border-4 border-red-500" : "border-4 border-gray-300"}`}
|
||
>
|
||
<Icon name="user" size={16} color="#999" className="mx-2" />
|
||
<Input
|
||
clearable
|
||
type="text"
|
||
placeholder="请输入档口名称"
|
||
value={supplierVO.name}
|
||
onChange={(value) => handleNameChange(value, supplierVO)}
|
||
onBlur={() =>
|
||
handleNameBlur(
|
||
supplierVO?.name || "",
|
||
supplierVO.orderSupplierId,
|
||
)
|
||
}
|
||
className="flex-1"
|
||
/>
|
||
</View>
|
||
{nameError[supplierVO.orderSupplierId] && (
|
||
<View className="mt-1 text-xs text-red-500">
|
||
{`档口名称"${supplierVO.name}"至少2个字符`}
|
||
</View>
|
||
)}
|
||
<View className="block text-sm font-normal text-[#000000]">
|
||
收款人姓名
|
||
</View>
|
||
<View
|
||
className={`flex h-10 w-full items-center rounded-md ${payeeNameError[supplierVO.orderSupplierId] ? "border-4 border-red-500" : "border-4 border-gray-300"}`}
|
||
>
|
||
<Icon name="user" size={16} color="#999" className="mx-2" />
|
||
<Input
|
||
clearable
|
||
type="text"
|
||
placeholder="请输入收款人姓名"
|
||
value={supplierVO.payeeName}
|
||
onChange={(value) =>
|
||
handlePayeeNameChange(value, supplierVO)
|
||
}
|
||
onBlur={() =>
|
||
handlePayeeNameBlur(
|
||
supplierVO?.payeeName || "",
|
||
supplierVO.orderSupplierId,
|
||
)
|
||
}
|
||
className="flex-1"
|
||
/>
|
||
</View>
|
||
{payeeNameError[supplierVO.orderSupplierId] && (
|
||
<View className="mt-1 text-xs text-red-500">
|
||
{`收款人姓名至少2个字符`}
|
||
</View>
|
||
)}
|
||
<View className="block text-sm font-normal text-[#000000]">
|
||
银行名称
|
||
</View>
|
||
<View
|
||
className={`flex h-10 w-full items-center rounded-md ${bankNameError[supplierVO.orderSupplierId] ? "border-4 border-red-500" : "border-4 border-gray-300"}`}
|
||
>
|
||
<Icon
|
||
name="building"
|
||
size={16}
|
||
color="#999"
|
||
className="mx-2"
|
||
/>
|
||
<Input
|
||
clearable
|
||
type="text"
|
||
placeholder="请输入银行名称"
|
||
value={supplierVO.bankName}
|
||
onChange={(value) =>
|
||
handleBankNameChange(value, supplierVO)
|
||
}
|
||
onBlur={() =>
|
||
handleBankNameBlur(
|
||
supplierVO?.bankName || "",
|
||
supplierVO.orderSupplierId,
|
||
)
|
||
}
|
||
className="flex-1"
|
||
/>
|
||
</View>
|
||
{bankNameError[supplierVO.orderSupplierId] && (
|
||
<View className="text-xs text-red-500">
|
||
{`银行名称"${supplierVO.bankName}"至少2个字符`}
|
||
</View>
|
||
)}
|
||
<View className="block text-sm font-normal text-[#000000]">
|
||
银行卡号
|
||
</View>
|
||
<View
|
||
className={`flex h-10 w-full items-center rounded-md ${bankCardError[supplierVO.orderSupplierId] ? "border-4 border-red-500" : "border-4 border-gray-300"}`}
|
||
>
|
||
<Icon
|
||
name="credit-card"
|
||
size={16}
|
||
color="#999"
|
||
className="mx-2"
|
||
/>
|
||
<Input
|
||
clearable
|
||
type="digit"
|
||
placeholder="请输入银行卡号"
|
||
value={supplierVO.bankCard}
|
||
onChange={(value) =>
|
||
handleBankCardChange(value, supplierVO)
|
||
}
|
||
onBlur={() =>
|
||
handleBankCardBlur(
|
||
supplierVO?.bankCard || "",
|
||
supplierVO.orderSupplierId,
|
||
)
|
||
}
|
||
className="flex-1"
|
||
/>
|
||
</View>
|
||
{bankCardError[supplierVO.orderSupplierId] && (
|
||
<View className="text-xs text-red-500">
|
||
请输入正确的银行卡号
|
||
</View>
|
||
)}
|
||
<View className="block text-sm font-normal text-[#000000]">
|
||
手机号码
|
||
</View>
|
||
<View
|
||
className={`flex h-10 w-full items-center rounded-md ${phoneError[supplierVO.orderSupplierId] ? "border-4 border-red-500" : "border-4 border-gray-300"}`}
|
||
>
|
||
<Icon name="phone" size={16} color="#999" className="mx-2" />
|
||
<Input
|
||
clearable
|
||
type="tel"
|
||
placeholder="请输入手机号码"
|
||
value={supplierVO.phone}
|
||
onChange={(value) => handlePhoneChange(value, supplierVO)}
|
||
onBlur={() =>
|
||
handlePhoneBlur(
|
||
supplierVO?.phone || "",
|
||
supplierVO.orderSupplierId,
|
||
)
|
||
}
|
||
className="flex-1"
|
||
/>
|
||
</View>
|
||
{phoneError[supplierVO.orderSupplierId] && (
|
||
<View className="text-xs text-red-500">
|
||
请输入正确的手机号
|
||
</View>
|
||
)}
|
||
</>
|
||
)}
|
||
</View>
|
||
</View>
|
||
|
||
{/* 只有其他家档口才显示微信收款码部分 */}
|
||
{supplierVO.type === "OTHER_STALL" && (
|
||
<View className="border-primary rounded-lg border-4 bg-white p-2.5 shadow-sm">
|
||
<View className="flex flex-col gap-2.5">
|
||
<View className="text-primary text-base font-bold">
|
||
{supplierVO.name || "档口"}的微信收款码(可跳过)
|
||
</View>
|
||
{supplierVO.wechatQr ? (
|
||
<View className="flex flex-col gap-2.5 rounded-lg bg-green-50 p-2.5">
|
||
<View className="flex items-center">
|
||
<View className="relative mr-3 h-20 w-20 overflow-hidden rounded-lg">
|
||
<Image
|
||
className="h-full w-full object-cover"
|
||
src={supplierVO.wechatQr}
|
||
/>
|
||
</View>
|
||
<View className="flex-1">
|
||
<View className="font-medium text-green-700">
|
||
微信收款码
|
||
</View>
|
||
<View className="text-sm text-green-600">已上传完成</View>
|
||
<View className="text-neutral-darker mt-1 text-xs">
|
||
若档口无法开发票,则可打款到微信
|
||
</View>
|
||
</View>
|
||
</View>
|
||
<View className="flex flex-row gap-2.5">
|
||
<View className={"flex-1"}>
|
||
<Button
|
||
type={"primary"}
|
||
size={"large"}
|
||
fill={"outline"}
|
||
block
|
||
onClick={handleWechatQrUpload}
|
||
>
|
||
<View>重新上传</View>
|
||
</Button>
|
||
</View>
|
||
<View className={"flex-1"}>
|
||
<Button
|
||
type={"warning"}
|
||
size={"large"}
|
||
fill={"outline"}
|
||
block
|
||
onClick={(e) => {
|
||
setSupplierVO({
|
||
...supplierVO,
|
||
wechatQr: undefined,
|
||
});
|
||
Toast.show("toast", {
|
||
title: "删除成功",
|
||
icon: "success",
|
||
content: "微信收款码已删除",
|
||
});
|
||
e.stopPropagation();
|
||
}}
|
||
>
|
||
<View>删除</View>
|
||
</Button>
|
||
</View>
|
||
</View>
|
||
</View>
|
||
) : (
|
||
<View
|
||
className={
|
||
"flex h-40 flex-1 flex-col items-center justify-center rounded-md border-2 border-dashed border-green-300 bg-green-50 p-2.5"
|
||
}
|
||
onClick={handleWechatQrUpload}
|
||
>
|
||
<Icon name={"camera"} size={24} />
|
||
<View className="text-sm font-medium text-green-700">
|
||
上传微信收款码
|
||
</View>
|
||
<View className="text-neutral-darker mt-1 text-xs">
|
||
支持从相册选择或拍照
|
||
</View>
|
||
<View className="mt-2 text-center text-xs text-gray-400">
|
||
若档口无法开发票,则可打款到微信
|
||
</View>
|
||
</View>
|
||
)}
|
||
</View>
|
||
</View>
|
||
)}
|
||
|
||
{/* 隐藏的供应商选择器,用于自家档口选择 */}
|
||
{showSupplierPicker && (
|
||
<SupplierPicker
|
||
type={"STALL"}
|
||
trigger={
|
||
<View
|
||
style={{ display: "none" }}
|
||
onClick={() => 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,
|
||
}));
|
||
}}
|
||
/>
|
||
)}
|
||
</View>
|
||
);
|
||
},
|
||
);
|