- 新增 PurchaseOrderList、PurchaseOrderAuditList、PurchaseOrderApprovalList 三个列表组件 - 在 purchase/index.ts 中导出新增的三个组件 - 更新 purchaseOrder 常量配置,增加审核与审批相关的状态列表和映射 - 修改 PageList 组件从 globalStore 获取 loading 状态 - 调整部分页面路径配置 - 新增发票上传相关页面及功能实现 - 优化部分组件导入和状态管理逻辑
605 lines
18 KiB
TypeScript
605 lines
18 KiB
TypeScript
import { View } from "@tarojs/components";
|
||
import {
|
||
Button,
|
||
Input,
|
||
Toast,
|
||
Uploader,
|
||
UploaderFileItem,
|
||
} from "@nutui/nutui-react-taro";
|
||
import { Icon } from "@/components";
|
||
import hocAuth from "@/hocs/auth";
|
||
import { CommonComponent } from "@/types/typings";
|
||
import { useEffect, useState } from "react";
|
||
import { business } from "@/services";
|
||
import Taro from "@tarojs/taro";
|
||
import { buildUrl, generateShortId, uploadFile } from "@/utils";
|
||
|
||
export default hocAuth(function Page(props: CommonComponent) {
|
||
const { setLoading, router } = props;
|
||
const supplierId = router.params
|
||
.supplierId as BusinessAPI.SupplierVO["supplierId"];
|
||
|
||
const [supplierVO, setSupplierVO] = useState<BusinessAPI.SupplierCreateCmd>({
|
||
supplierId: generateShortId(),
|
||
name: "",
|
||
idCard: "",
|
||
phone: "",
|
||
bankCard: "",
|
||
status: true,
|
||
});
|
||
const [btnLoading, setBtnLoading] = useState(false);
|
||
const [picList, setPicList] = useState<UploaderFileItem[]>([]);
|
||
|
||
// 错误状态
|
||
const [nameError, setNameError] = useState(false);
|
||
const [idCardError, setIdCardError] = useState(false);
|
||
const [bankCardError, setBankCardError] = useState(false);
|
||
const [phoneError, setPhoneError] = useState(false);
|
||
const [nameDuplicateError, setNameDuplicateError] = useState(false); // 姓名重复错误
|
||
|
||
// 初始化微信二维码图片列表
|
||
useEffect(() => {
|
||
if (supplierId) {
|
||
setLoading(true);
|
||
business.supplier
|
||
.showSupplier({
|
||
supplierShowQry: {
|
||
supplierId: supplierId,
|
||
},
|
||
})
|
||
.then(({ data: { data: supplierVO } }) => {
|
||
if (supplierVO) {
|
||
setSupplierVO({
|
||
...supplierVO,
|
||
});
|
||
if (supplierVO.wechatQr) {
|
||
setPicList([
|
||
{
|
||
url: supplierVO.wechatQr,
|
||
name: "wechat-qrcode",
|
||
status: "success",
|
||
},
|
||
]);
|
||
}
|
||
}
|
||
})
|
||
.finally(() => {
|
||
setLoading(false);
|
||
});
|
||
}
|
||
}, [supplierId]);
|
||
|
||
// 微信二维码变更处理函数
|
||
const handleWechatQrChange = (files: UploaderFileItem[]) => {
|
||
setPicList(files);
|
||
|
||
// 如果有文件且上传成功,保存URL到supplierVO
|
||
if (files.length > 0 && files[0].url) {
|
||
setSupplierVO((prev) => ({
|
||
...prev,
|
||
wechatQr: files[0].url,
|
||
}));
|
||
} else {
|
||
// 如果没有文件,清空URL
|
||
setSupplierVO((prev) => ({
|
||
...prev,
|
||
wechatQr: undefined,
|
||
}));
|
||
}
|
||
};
|
||
|
||
// 校验姓名函数
|
||
const validateName = (name: string) => {
|
||
if (!name) {
|
||
return false;
|
||
}
|
||
// 姓名至少2个字符
|
||
return name.length >= 2;
|
||
};
|
||
|
||
// 校验身份证号函数
|
||
const validateIdCard = (idCard: string) => {
|
||
if (!idCard) {
|
||
return false;
|
||
}
|
||
// 18位身份证号正则表达式
|
||
const idCardRegex =
|
||
/^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/;
|
||
return idCardRegex.test(idCard);
|
||
};
|
||
|
||
// 校验银行卡号函数
|
||
const validateBankCard = (bankCard: string) => {
|
||
if (!bankCard) {
|
||
return false;
|
||
}
|
||
// 银行卡号一般为16-19位数字
|
||
const bankCardRegex = /^\d{16,19}$/;
|
||
return bankCardRegex.test(bankCard);
|
||
};
|
||
|
||
// 校验手机号函数 (使用项目中已有的规则)
|
||
const validatePhone = (phone: string) => {
|
||
if (!phone) {
|
||
return false;
|
||
}
|
||
// 使用项目规范的手机号正则表达式
|
||
const phoneRegex = /^1[3456789]\d{9}$/;
|
||
return phoneRegex.test(phone);
|
||
};
|
||
|
||
// 处理姓名变化
|
||
const handleNameChange = (value: string) => {
|
||
setSupplierVO({
|
||
...supplierVO,
|
||
name: value,
|
||
});
|
||
|
||
// 校验并更新错误状态
|
||
const isValid = validateName(value);
|
||
setNameError(!isValid && value !== "");
|
||
|
||
if (isValid) {
|
||
setNameError(false);
|
||
}
|
||
|
||
// 清除姓名重复错误提示
|
||
setNameDuplicateError(false);
|
||
};
|
||
|
||
// 处理姓名失焦
|
||
const handleNameBlur = async (value: string) => {
|
||
if (value && !validateName(value)) {
|
||
setNameError(true);
|
||
return;
|
||
} else {
|
||
setNameError(false);
|
||
}
|
||
|
||
// 检查瓜农姓名是否在系统中存在
|
||
if (value) {
|
||
setLoading(true);
|
||
try {
|
||
const { data } = await business.supplier.checkSupplier({
|
||
supplierCheckQry: {
|
||
name: value,
|
||
},
|
||
});
|
||
|
||
if (data.success && data.data) {
|
||
// 系统中已存在该瓜农信息,设置姓名重复错误状态
|
||
setNameDuplicateError(true);
|
||
} else {
|
||
setNameDuplicateError(false);
|
||
}
|
||
} catch (error) {
|
||
console.error("检查瓜农信息失败", error);
|
||
setNameDuplicateError(false);
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
}
|
||
};
|
||
|
||
// 处理身份证号变化
|
||
const handleIdCardChange = (value: string) => {
|
||
setSupplierVO({
|
||
...supplierVO,
|
||
idCard: value,
|
||
});
|
||
|
||
// 校验并更新错误状态
|
||
const isValid = validateIdCard(value);
|
||
setIdCardError(!isValid && value !== "");
|
||
|
||
if (isValid) {
|
||
setIdCardError(false);
|
||
}
|
||
};
|
||
|
||
// 处理身份证号失焦
|
||
const handleIdCardBlur = (value: string) => {
|
||
if (value && !validateIdCard(value)) {
|
||
setIdCardError(true);
|
||
} else {
|
||
setIdCardError(false);
|
||
}
|
||
};
|
||
|
||
// 处理银行卡号变化
|
||
const handleBankCardChange = (value: string) => {
|
||
setSupplierVO({
|
||
...supplierVO,
|
||
bankCard: value,
|
||
});
|
||
|
||
// 校验并更新错误状态
|
||
const isValid = validateBankCard(value);
|
||
setBankCardError(!isValid && value !== "");
|
||
|
||
if (isValid) {
|
||
setBankCardError(false);
|
||
}
|
||
};
|
||
|
||
// 处理银行卡号失焦
|
||
const handleBankCardBlur = (value: string) => {
|
||
if (value && !validateBankCard(value)) {
|
||
setBankCardError(true);
|
||
} else {
|
||
setBankCardError(false);
|
||
}
|
||
};
|
||
|
||
// 处理手机号变化
|
||
const handlePhoneChange = (value: string) => {
|
||
setSupplierVO({
|
||
...supplierVO,
|
||
phone: value,
|
||
});
|
||
|
||
// 校验并更新错误状态
|
||
const isValid = validatePhone(value);
|
||
setPhoneError(!isValid && value !== "");
|
||
|
||
if (isValid) {
|
||
setPhoneError(false);
|
||
}
|
||
};
|
||
|
||
// 处理手机号失焦
|
||
const handlePhoneBlur = (value: string) => {
|
||
if (value && !validatePhone(value)) {
|
||
setPhoneError(true);
|
||
} else {
|
||
setPhoneError(false);
|
||
}
|
||
};
|
||
|
||
const handleSave = async () => {
|
||
// 校验表单
|
||
const isNameValid = validateName(supplierVO.name || "");
|
||
const isIdCardValid = validateIdCard(supplierVO.idCard || "");
|
||
const isBankCardValid = validateBankCard(supplierVO.bankCard || "");
|
||
const isPhoneValid = validatePhone(supplierVO.phone || "");
|
||
|
||
setNameError(!isNameValid);
|
||
setIdCardError(!isIdCardValid);
|
||
setBankCardError(!isBankCardValid);
|
||
setPhoneError(!isPhoneValid);
|
||
|
||
// 检查是否已存在同名瓜农
|
||
let isDuplicate = false;
|
||
if (isNameValid) {
|
||
try {
|
||
setBtnLoading(true);
|
||
const { data: checkData } = await business.supplier.checkSupplier({
|
||
supplierCheckQry: {
|
||
name: supplierVO.name,
|
||
},
|
||
});
|
||
|
||
if (checkData.success && checkData.data) {
|
||
isDuplicate = true;
|
||
setNameDuplicateError(true);
|
||
}
|
||
} catch (error) {
|
||
console.error("检查瓜农信息失败", error);
|
||
} finally {
|
||
setBtnLoading(false);
|
||
}
|
||
}
|
||
|
||
const isValid =
|
||
isNameValid &&
|
||
isIdCardValid &&
|
||
isBankCardValid &&
|
||
isPhoneValid &&
|
||
!isDuplicate;
|
||
|
||
if (!isValid) {
|
||
if (isDuplicate) {
|
||
Toast.show("toast", {
|
||
icon: "fail",
|
||
title: "提示",
|
||
content: "该瓜农已存在,请勿重复创建",
|
||
});
|
||
} else {
|
||
Toast.show("toast", {
|
||
icon: "fail",
|
||
title: "提示",
|
||
content: "请完善瓜农信息后再进行保存操作",
|
||
});
|
||
}
|
||
return;
|
||
}
|
||
|
||
// 创建新瓜农
|
||
try {
|
||
setBtnLoading(true);
|
||
const { data } = await business.supplier.createSupplier({
|
||
supplierId: supplierVO.supplierId,
|
||
name: supplierVO.name,
|
||
idCard: supplierVO.idCard,
|
||
phone: supplierVO.phone,
|
||
bankCard: supplierVO.bankCard,
|
||
wechatQr: supplierVO.wechatQr,
|
||
status: supplierVO.status,
|
||
});
|
||
|
||
if (data.success) {
|
||
Toast.show("toast", {
|
||
icon: "success",
|
||
title: "成功",
|
||
content: "瓜农创建成功",
|
||
});
|
||
setTimeout(() => {
|
||
Taro.navigateBack();
|
||
}, 1500);
|
||
} else {
|
||
Toast.show("toast", {
|
||
icon: "fail",
|
||
title: "失败",
|
||
content: data.errMessage || "创建失败",
|
||
});
|
||
}
|
||
} catch (error) {
|
||
Toast.show("toast", {
|
||
icon: "fail",
|
||
title: "错误",
|
||
content: "创建过程中发生错误",
|
||
});
|
||
console.error("创建瓜农失败:", error);
|
||
} finally {
|
||
setBtnLoading(false);
|
||
}
|
||
};
|
||
|
||
return (
|
||
<>
|
||
<View className="flex-1">
|
||
<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>
|
||
|
||
{/* 快捷工具 */}
|
||
<View className="flex gap-2">
|
||
<View className={"flex-1"}>
|
||
<Button
|
||
block
|
||
icon={<Icon name={"id-card"} size={28} color={"white"} />}
|
||
type={"primary"}
|
||
size={"xlarge"}
|
||
className="bg-primary flex flex-1 items-center justify-center text-white"
|
||
onClick={() => {
|
||
Taro.navigateTo({
|
||
url: buildUrl("/pages/public/camera/ocr", {
|
||
type: "idcard",
|
||
}),
|
||
complete: () => {
|
||
Taro.eventCenter.on("ocr", (res) => {
|
||
console.log("识别结果为:", res.result);
|
||
setSupplierVO({
|
||
...supplierVO,
|
||
name: res.result.name,
|
||
idCard: res.result.idCard,
|
||
});
|
||
|
||
Taro.eventCenter.off("ocr");
|
||
});
|
||
},
|
||
});
|
||
}}
|
||
>
|
||
<View>扫描身份证</View>
|
||
</Button>
|
||
</View>
|
||
<View className={"flex-1"}>
|
||
<Button
|
||
block
|
||
icon={<Icon name={"credit-card"} size={28} color={"white"} />}
|
||
type={"primary"}
|
||
size={"xlarge"}
|
||
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="rounded-lg bg-white p-2.5 shadow-sm">
|
||
<View className="mb-2.5">
|
||
<View className="flex items-center justify-between">
|
||
<View className={"text-primary text-sm font-bold"}>
|
||
{supplierVO.name || "瓜农"}信息
|
||
</View>
|
||
</View>
|
||
</View>
|
||
<View className="mb-2.5">
|
||
<View className="mb-1 block text-sm font-normal text-[#000000]">
|
||
姓名
|
||
</View>
|
||
<View
|
||
className={`flex h-10 w-full items-center rounded-md ${nameError || nameDuplicateError ? "border-4 border-red-500" : "border-4 border-gray-300"}`}
|
||
>
|
||
<Icon name="user" size={16} color="#999" className="mx-2" />
|
||
<Input
|
||
type="text"
|
||
placeholder="请输入姓名"
|
||
value={supplierVO.name}
|
||
onChange={(value) => handleNameChange(value)}
|
||
onBlur={() => handleNameBlur(supplierVO.name || "")}
|
||
className="flex-1"
|
||
/>
|
||
</View>
|
||
{nameError && (
|
||
<View className="mt-1 text-xs text-red-500">
|
||
{`姓名"${supplierVO.name}"至少2个字符`}
|
||
</View>
|
||
)}
|
||
{nameDuplicateError && (
|
||
<View className="mt-1 text-xs text-red-500">
|
||
该瓜农已存在,请勿重复创建
|
||
</View>
|
||
)}
|
||
</View>
|
||
<View className="mb-2.5">
|
||
<View className="mb-1 block text-sm font-normal text-[#000000]">
|
||
身份证号
|
||
</View>
|
||
<View
|
||
className={`flex h-10 w-full items-center rounded-md ${idCardError ? "border-4 border-red-500" : "border-4 border-gray-300"}`}
|
||
>
|
||
<Icon name="id-card" size={16} color="#999" className="mx-2" />
|
||
<Input
|
||
type="idcard"
|
||
placeholder="请输入身份证号"
|
||
value={supplierVO.idCard}
|
||
onChange={(value) => handleIdCardChange(value)}
|
||
onBlur={() => handleIdCardBlur(supplierVO.idCard || "")}
|
||
className="flex-1"
|
||
/>
|
||
</View>
|
||
{idCardError && (
|
||
<View className="mt-1 text-xs text-red-500">
|
||
请输入正确的身份证号
|
||
</View>
|
||
)}
|
||
</View>
|
||
<View className="mb-2.5">
|
||
<View className="mb-1 block text-sm font-normal text-[#000000]">
|
||
银行卡号
|
||
</View>
|
||
<View
|
||
className={`flex h-10 w-full items-center rounded-md ${bankCardError ? "border-4 border-red-500" : "border-4 border-gray-300"}`}
|
||
>
|
||
<Icon
|
||
name="credit-card"
|
||
size={16}
|
||
color="#999"
|
||
className="mx-2"
|
||
/>
|
||
<Input
|
||
type="digit"
|
||
placeholder="请输入银行卡号"
|
||
value={supplierVO.bankCard}
|
||
onChange={(value) => handleBankCardChange(value)}
|
||
onBlur={() => handleBankCardBlur(supplierVO.bankCard || "")}
|
||
className="flex-1"
|
||
/>
|
||
</View>
|
||
{bankCardError && (
|
||
<View className="mt-1 text-xs text-red-500">
|
||
请输入正确的银行卡号
|
||
</View>
|
||
)}
|
||
</View>
|
||
<View className="mb-2.5">
|
||
<View className="mb-1 block text-sm font-normal text-[#000000]">
|
||
手机号码
|
||
</View>
|
||
<View
|
||
className={`flex h-10 w-full items-center rounded-md ${phoneError ? "border-4 border-red-500" : "border-4 border-gray-300"}`}
|
||
>
|
||
<Icon name="phone" size={16} color="#999" className="mx-2" />
|
||
<Input
|
||
type="tel"
|
||
placeholder="请输入手机号码"
|
||
value={supplierVO.phone}
|
||
onChange={(value) => handlePhoneChange(value)}
|
||
onBlur={() => handlePhoneBlur(supplierVO.phone || "")}
|
||
className="flex-1"
|
||
/>
|
||
</View>
|
||
{phoneError && (
|
||
<View className="mt-1 text-xs text-red-500">
|
||
请输入正确的手机号
|
||
</View>
|
||
)}
|
||
</View>
|
||
</View>
|
||
|
||
{/* 若瓜农无法开发票,则可打款到微信 */}
|
||
<View className="rounded-lg bg-white p-2.5 shadow-sm">
|
||
<View className={`flex w-full border-gray-300`}>
|
||
<Uploader
|
||
className={"w-full"}
|
||
value={picList}
|
||
onChange={handleWechatQrChange}
|
||
sourceType={["album", "camera"]}
|
||
uploadIcon={<Icon name={"camera"} size={36} />}
|
||
uploadLabel={
|
||
<View className={"flex flex-col items-center"}>
|
||
<View className="text-sm">拍照上传微信收款码</View>
|
||
<View className="mt-1 text-xs text-gray-400">
|
||
若瓜农无法开发票,则可打款到微信
|
||
</View>
|
||
<View className="mt-1 text-xs text-gray-400">
|
||
(可跳过)
|
||
</View>
|
||
</View>
|
||
}
|
||
maxCount={1}
|
||
//@ts-ignore
|
||
upload={uploadFile}
|
||
multiple
|
||
/>
|
||
</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
|
||
block
|
||
type="primary"
|
||
size={"xlarge"}
|
||
onClick={handleSave}
|
||
loading={btnLoading}
|
||
>
|
||
保存瓜农信息
|
||
</Button>
|
||
</View>
|
||
</View>
|
||
</View>
|
||
</>
|
||
);
|
||
});
|