diff --git a/src/api/temporaryPersonnel/index.js b/src/api/temporaryPersonnel/index.js index d2fc98e..3606a89 100644 --- a/src/api/temporaryPersonnel/index.js +++ b/src/api/temporaryPersonnel/index.js @@ -10,7 +10,7 @@ export const temporaryPersonnelList = declareRequest( ); export const temporaryPersonnelAdd = declareRequest( "temporaryPersonnelLoading", - `Post > @/primeport/vehicleApply/saveTemporaryVisitors`, + `Post > @/primeport/personApply/xgfPersonSave`, ); export const temporaryPersonnelInfo = declareRequest( "temporaryPersonnelLoading", diff --git a/src/api/temporaryVehicle/index.js b/src/api/temporaryVehicle/index.js index 9e71e3c..1e75fa9 100644 --- a/src/api/temporaryVehicle/index.js +++ b/src/api/temporaryVehicle/index.js @@ -2,7 +2,7 @@ import { declareRequest } from "@cqsjjb/jjb-dva-runtime"; export const temporaryVehicleAdd = declareRequest( "temporaryVehicleLoading", - `Post > @/primeport/vehicleApply/saveTemporaryVisitors`, + `Post > @/primeport/vehicleApply/save`, ); export const temporaryVehicleReviewList = declareRequest( "temporaryVehicleLoading", diff --git a/src/pages/Container/Supervision/FirstLevelDoor/ApproverUser/index.js b/src/pages/Container/Supervision/FirstLevelDoor/ApproverUser/index.js index 814c848..059f7b6 100644 --- a/src/pages/Container/Supervision/FirstLevelDoor/ApproverUser/index.js +++ b/src/pages/Container/Supervision/FirstLevelDoor/ApproverUser/index.js @@ -220,6 +220,9 @@ function AddModalComponent(props) { if (data?.available) { return Promise.resolve(); } + if(!data.available){ + form.setFieldValue("userId", undefined); + } throw new Error(data?.availableMessage || "该审批人已录入,不可重复添加"); }, }, diff --git a/src/pages/Container/Supervision/FirstLevelDoor/PortEntryApproval/LongTermVehicleApproval/RelatedVehicles/index.js b/src/pages/Container/Supervision/FirstLevelDoor/PortEntryApproval/LongTermVehicleApproval/RelatedVehicles/index.js index 2ccaf0c..50dc00d 100644 --- a/src/pages/Container/Supervision/FirstLevelDoor/PortEntryApproval/LongTermVehicleApproval/RelatedVehicles/index.js +++ b/src/pages/Container/Supervision/FirstLevelDoor/PortEntryApproval/LongTermVehicleApproval/RelatedVehicles/index.js @@ -111,6 +111,7 @@ function RelatedVehicles(props) { { setReviewModalVisible(false); setCurrentId(""); diff --git a/src/pages/Container/Supervision/FirstLevelDoor/PortEntryApproval/TemporaryVisitor/TemporaryPersonnel/Add/index.js b/src/pages/Container/Supervision/FirstLevelDoor/PortEntryApproval/TemporaryVisitor/TemporaryPersonnel/Add/index.js index 81389a6..b74059c 100644 --- a/src/pages/Container/Supervision/FirstLevelDoor/PortEntryApproval/TemporaryVisitor/TemporaryPersonnel/Add/index.js +++ b/src/pages/Container/Supervision/FirstLevelDoor/PortEntryApproval/TemporaryVisitor/TemporaryPersonnel/Add/index.js @@ -12,6 +12,7 @@ import { ID_NUMBER, PHONE } from "zy-react-library/regular"; import { getLabelName, validatorEndTime, validatorTimeGECurrentDay } from "zy-react-library/utils"; import { NS_APPROVER_USER, NS_FIRST_LEVEL_DOOR_INFO, NS_TEMPORARY_PERSONNEL } from "~/enumerate/namespace"; + function Add(props) { const [form] = FormBuilder.useForm(); const gateLevelAuthArea = FormBuilder.useWatch("gateLevelAuthArea", form); @@ -45,14 +46,15 @@ function Add(props) { files: values.userFace, params: { type: UPLOAD_FILE_TYPE_ENUM[608] }, }); + const { success } = await props["temporaryPersonnelAdd"]({ ...values, personBelongType: 4, personApplyList: [ - { userFaceUrl }, - { employeePersonUserName: values.employeePersonUserName }, - { userPhone: values.userPhone }, - { userCard: btoa(values.userCard) }, + { userFaceUrl, + employeePersonUserName: values.employeePersonUserName , + userPhone: values.userPhone , + userCard: btoa(values.userCard) }, ], gateLevelAuthArea: JSON.stringify({ area: values.area }), }); diff --git a/src/pages/Container/Supervision/FirstLevelDoor/PortEntryApproval/TemporaryVisitor/TemporaryPersonnel/List/index.js b/src/pages/Container/Supervision/FirstLevelDoor/PortEntryApproval/TemporaryVisitor/TemporaryPersonnel/List/index.js index 3a7f0fc..5d92a26 100644 --- a/src/pages/Container/Supervision/FirstLevelDoor/PortEntryApproval/TemporaryVisitor/TemporaryPersonnel/List/index.js +++ b/src/pages/Container/Supervision/FirstLevelDoor/PortEntryApproval/TemporaryVisitor/TemporaryPersonnel/List/index.js @@ -15,7 +15,7 @@ import useGetFile from "zy-react-library/hooks/useGetFile"; import useTable from "zy-react-library/hooks/useTable"; import { getLabelName } from "zy-react-library/utils"; import { NS_FIRST_LEVEL_DOOR_INFO, NS_TEMPORARY_PERSONNEL } from "~/enumerate/namespace"; - +import { UseDecodeIdCard } from "~/utils"; const STATUS_ENUM = [ { bianma: "1", name: "审核中" }, { bianma: "2", name: "通过" }, @@ -99,7 +99,7 @@ function List(props) { { title: "身份证号", dataIndex: "userCard", - render: (_, record) => record.userCard ? atob(record.userCard) : "", + render: (_, record) => UseDecodeIdCard(record.userCard), }, { title: "来访事由", dataIndex: "reasonVisit" }, { title: "访问开始时间", dataIndex: "visitStartTime" }, diff --git a/src/pages/Container/Supervision/FirstLevelDoor/PortEntryApproval/TemporaryVisitor/TemporaryVehicle/Add/index.js b/src/pages/Container/Supervision/FirstLevelDoor/PortEntryApproval/TemporaryVisitor/TemporaryVehicle/Add/index.js index 1b844bb..104363f 100644 --- a/src/pages/Container/Supervision/FirstLevelDoor/PortEntryApproval/TemporaryVisitor/TemporaryVehicle/Add/index.js +++ b/src/pages/Container/Supervision/FirstLevelDoor/PortEntryApproval/TemporaryVisitor/TemporaryVehicle/Add/index.js @@ -16,6 +16,7 @@ import { NS_TEMPORARY_VEHICLE, NS_VEHICLE_APPLY, } from "~/enumerate/namespace"; +import {VEHICLE_TYPE_ENUM} from "~/enumerate/constant"; function Add(props) { const [form] = FormBuilder.useForm(); @@ -60,10 +61,12 @@ function Add(props) { files: values.attachmentFile, params: { type: UPLOAD_FILE_TYPE_ENUM[602], foreignKey: "" }, }); + const { success } = await props["temporaryVehicleAdd"]({ ...values, drivingLicenseId, attachmentId, + lsUserIdcard: btoa(values.lsUserIdcard), gateLevelAuthArea: JSON.stringify({ area: values.area }), }); if (success) { @@ -139,6 +142,13 @@ function Add(props) { render: FORM_ITEM_RENDER_ENUM.DATE, rules: [validatorEndTime({ startTime: visitStartTime })], }, + { + name: "vehicleBelongType", + label: "车辆所属类型", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items:VEHICLE_TYPE_ENUM + }, + { name: "gateLevelAuthArea", label: "访问港区", @@ -174,14 +184,14 @@ function Add(props) { }, }, { name: "mkmjName", label: "访问口门名称", onlyForLabel: true }, - { name: "reasonVisit", label: "来访事由", span: 24, render: FORM_ITEM_RENDER_ENUM.TEXTAREA }, + { name: "remarks", label: "来访事由", span: 24, render: FORM_ITEM_RENDER_ENUM.TEXTAREA }, { name: "drivingLicenseFile", label: "行驶证照片", span: 24, render: ( diff --git a/src/pages/Container/Supervision/FirstLevelDoor/PortEntryApproval/TemporaryVisitor/TemporaryVehicle/List/index.js b/src/pages/Container/Supervision/FirstLevelDoor/PortEntryApproval/TemporaryVisitor/TemporaryVehicle/List/index.js index 39cd37e..122aa08 100644 --- a/src/pages/Container/Supervision/FirstLevelDoor/PortEntryApproval/TemporaryVisitor/TemporaryVehicle/List/index.js +++ b/src/pages/Container/Supervision/FirstLevelDoor/PortEntryApproval/TemporaryVisitor/TemporaryVehicle/List/index.js @@ -16,6 +16,7 @@ import useTable from "zy-react-library/hooks/useTable"; import { getLabelName } from "zy-react-library/utils"; import { VEHICLE_AUDIT_STATUS_ENUM } from "~/enumerate/constant"; import { NS_FIRST_LEVEL_DOOR_INFO, NS_TEMPORARY_VEHICLE, NS_VEHICLE_APPLY, NS_VEHICLE_AUDIT } from "~/enumerate/namespace"; +import {UseDecodeIdCard} from "~/utils"; function List(props) { const [qrCodeModalVisible, setQrCodeModalVisible] = useState(false); @@ -28,7 +29,7 @@ function List(props) { const [form] = Search.useForm(); const { tableProps, getData } = useTable(props["vehicleAuditList"], { form, - params: { vehicleBelongTypeArr: "6", processOrRecord: !props.isRecords ? 1 : 2 }, + params: { processOrRecord: !props.isRecords ? 1 : 2 }, }); const getFirstLevelDoorInfoListAll = async () => { @@ -49,9 +50,9 @@ function List(props) { { name: "licenceNo", label: "车牌号" }, { name: "visitStartTime", label: "访问开始时间", render: FORM_ITEM_RENDER_ENUM.DATE }, { name: "visitEndTime", label: "访问结束时间", render: FORM_ITEM_RENDER_ENUM.DATE }, - { name: "auditStatus", label: "审批状态", render: FORM_ITEM_RENDER_ENUM.SELECT, items: VEHICLE_AUDIT_STATUS_ENUM }, + { name: "processOrRecord", label: "审批状态", render: FORM_ITEM_RENDER_ENUM.SELECT, items: VEHICLE_AUDIT_STATUS_ENUM }, { - name: "todo6", + name: "gateLevelAuthAreaId", label: "访问口门名称", render: FORM_ITEM_RENDER_ENUM.SELECT, items: firstLevelDoorInfoListAll, @@ -90,7 +91,7 @@ function List(props) { columns={[ { title: "访问人姓名", dataIndex: "employeeVehicleUserName" }, { title: "手机号", dataIndex: "lsUserPhone" }, - { title: "身份证号", dataIndex: "lsUserIdcard" }, + { title: "身份证号", dataIndex: "lsUserIdcard",render: (_, record) => UseDecodeIdCard(record.lsUserIdcard), }, { title: "车牌号", dataIndex: "licenceNo" }, { title: "车牌类型", dataIndex: "licenceTypeName" }, { title: "车辆类型", dataIndex: "vehicleTypeName" }, diff --git a/src/pages/Container/Supervision/FirstLevelDoor/PortEntryApproval/TemporaryVisitor/TemporaryVehicle/View/index.js b/src/pages/Container/Supervision/FirstLevelDoor/PortEntryApproval/TemporaryVisitor/TemporaryVehicle/View/index.js index 3b96f28..255b9c7 100644 --- a/src/pages/Container/Supervision/FirstLevelDoor/PortEntryApproval/TemporaryVisitor/TemporaryVehicle/View/index.js +++ b/src/pages/Container/Supervision/FirstLevelDoor/PortEntryApproval/TemporaryVisitor/TemporaryVehicle/View/index.js @@ -22,7 +22,11 @@ function View(props) { eqForeignKey: data.drivingLicenseId, }); const attachmentFile = await getFile({ eqType: UPLOAD_FILE_TYPE_ENUM[602], eqForeignKey: data.attachmentId }); - const informSignFile = await getFile({ eqType: UPLOAD_FILE_TYPE_ENUM[606], eqForeignKey: data.informSignId }); + let informSignFile = [] + if(data.informSignId){ + informSignFile = await getFile({ eqType: UPLOAD_FILE_TYPE_ENUM[606], eqForeignKey: data.informSignId }) + } + console.log(informSignFile); setInfo({ ...data, drivingLicenseFile, @@ -54,10 +58,10 @@ function View(props) { { label: "访问结束时间", children: info.visitEndTime }, { label: "访问港区", children: info.gateLevelAuthArea && JSON.parse(info.gateLevelAuthArea).area.map(item => item.value).join("、") }, { label: "访问口门名称", children: info.mkmjName }, - { label: "来访事由", children: info.reasonVisit, span: 2 }, + { label: "来访事由", children: info.remarks, span: 2 }, { label: "行驶证照片", children: (), span: 2 }, { label: "车辆图片", children: (), span: 2 }, - { label: "申请人安全告知签字", children: (), span: 2 }, + ...( info.informSignFile && info.informSignFile.length ===0 ? [] : [{ label: "申请人安全告知签字", children: (), span: 2 }]), ]} /> 审批信息 @@ -73,7 +77,8 @@ function View(props) { { label: "审批部门", children: item.auditDeptName }, { label: "审批人", children: item.auditUserName }, { label: "审批状态", children: getLabelName({ list: VEHICLE_AUDIT_STATUS_ENUM, status: `${item.auditStatus}` }) }, - ...(item.auditStatus === 0 ? [] : [{ label: "审批时间", children: item.auditTime }, { label: "驳回原因", children: item.remarks }]), + ...(item.auditStatus === 1 ? [] : [{ label: "审批时间", children: item.auditTime }]), + ...(item.auditStatus === 3 ? [ { label: "驳回原因", children: item.remarks }] :[]), ]} /> )) diff --git a/src/utils/index.js b/src/utils/index.js new file mode 100644 index 0000000..348f4e5 --- /dev/null +++ b/src/utils/index.js @@ -0,0 +1,272 @@ +import { useEffect, useRef, useState } from "react"; + +/** + * 校验中国大陆18位身份证号 + * 包含地址码、年份(18/19/20开头)、月份、日期、顺序码和校验位 + * @param {string} idCard + * @returns {boolean} + */ +export function isValidChineseIdCard(idCard) { + if (!idCard || typeof idCard !== "string") { + return false; + } + + const value = idCard.trim(); + if (!isValidChineseIdCardFormat(value)) { + return false; + } + + const weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]; + const checkCodeMap = ["1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"]; + const sum = value + .substring(0, 17) + .split("") + .reduce((total, num, index) => total + Number(num) * weights[index], 0); + + return checkCodeMap[sum % 11] === value[17].toUpperCase(); +} + +export function isValidChineseIdCardFormat(idCard) { + if (!idCard || typeof idCard !== "string") { + return false; + } + + const value = idCard.trim(); + const pattern = /^[1-9][0-9]{5}(18|19|20)[0-9]{2}(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])[0-9]{3}[0-9Xx]$/; + + if (!pattern.test(value)) { + return false; + } + + const year = Number.parseInt(value.substring(6, 10), 10); + const month = Number.parseInt(value.substring(10, 12), 10); + const day = Number.parseInt(value.substring(12, 14), 10); + const date = new Date(year, month - 1, day); + + return ( + date.getFullYear() === year + && date.getMonth() === month - 1 + && date.getDate() === day + ); +} + +export function getChineseIdCardInputErrorMessage(idCard) { + if (!idCard || typeof idCard !== "string") { + return ""; + } + + const value = idCard.trim(); + + if (!/^[0-9Xx]*$/.test(value)) { + return "身份证号只能输入数字或X"; + } + + if (value.length > 18) { + return "身份证号长度不能超过18位"; + } + + if (value.length < 18) { + return ""; + } + + if (!isValidChineseIdCardFormat(value)) { + return "请输入正确的18位身份证号"; + } + + return ""; +} + +export function getChineseIdCardFullErrorMessage(idCard) { + const inputErrorMessage = getChineseIdCardInputErrorMessage(idCard); + if (inputErrorMessage) { + return inputErrorMessage; + } + + if (!idCard || typeof idCard !== "string") { + return ""; + } + + const value = idCard.trim(); + if (value.length < 18) { + return ""; + } + + if (!isValidChineseIdCard(value)) { + return "身份证校验位不正确"; + } + + return ""; +} + +/** + * 根据身份证号计算年龄 + * @param {string} idCard - 18位身份证号码 + * @returns {number | null} 年龄(整数),无效身份证返回 null + */ +export function getAgeByIdCard(idCard) { + // 非空 & 类型校验 + if (!idCard || typeof idCard !== "string") { + return null; + } + + // 只处理18位身份证 + const id = idCard.trim(); + if (id.length !== 18) { + return null; + } + + // 提取出生年月日:第7~14位(索引6~13) + const birthStr = id.substring(6, 14); // 如 "19900101" + const year = Number.parseInt(birthStr.substring(0, 4), 10); + const month = Number.parseInt(birthStr.substring(4, 6), 10); + const day = Number.parseInt(birthStr.substring(6, 8), 10); + + // 简单校验日期合法性(可选增强) + if ( + year < 1900 + || year > new Date().getFullYear() + || month < 1 + || month > 12 + || day < 1 + || day > 31 + ) { + return null; + } + + // 构造出生日期 + const birthDate = new Date(year, month - 1, day); // 月份从0开始 + const today = new Date(); + + // 计算年龄 + let age = today.getFullYear() - birthDate.getFullYear(); + const monthDiff = today.getMonth() - birthDate.getMonth(); + const dayDiff = today.getDate() - birthDate.getDate(); + + // 如果还没过生日,减1岁 + if (monthDiff < 0 || (monthDiff === 0 && dayDiff < 0)) { + age -= 1; + } + return age >= 0 ? age : null; // 年龄不能为负 +} +/** + * 从18位身份证号中提取出生年月日 + * @param {string} idCard - 身份证号码 + * @returns {{ year: number; month: number; day: number } | null} 出生年月日对象,无效则返回 null + */ +export function getBirthDateFromIdCard(idCard) { + if (!idCard || typeof idCard !== "string") { + return null; + } + const id = idCard.trim(); + if (id.length !== 18) { + return null; // 仅支持18位身份证 + } + + // 提取第7~14位:YYYYMMDD + const birthStr = id.substring(6, 14); // 如 "19900307" + + const year = Number.parseInt(birthStr.substring(0, 4), 10); + const month = Number.parseInt(birthStr.substring(4, 6), 10); + const day = Number.parseInt(birthStr.substring(6, 8), 10); + + // 简单合法性校验 + const currentYear = new Date().getFullYear(); + if ( + year < 1900 + || year > currentYear + 1 // 允许未来1年(录入误差) + || month < 1 + || month > 12 + || day < 1 + || day > 31 + ) { + return null; + } + return `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`; +} +export function useDebounce(value, delay = 500) { + const [debouncedValue, setDebouncedValue] = useState(value); + const timerRef = useRef(null); + useEffect(() => { + // 清除上一次的定时器 + if (timerRef.current) { + clearTimeout(timerRef.current); + } + + // 设置新的定时器 + timerRef.current = setTimeout(() => { + setDebouncedValue(value); + }, delay); + + // 组件卸载或 delay/value 变化时清理 + return () => { + if (timerRef.current) { + clearTimeout(timerRef.current); + } + }; + }, [value, delay]); + return debouncedValue; +} +// 手机号脱敏 +export function maskPhone(phone) { + if (!phone) + return ""; + const str = String(phone).replace(/\s+/g, ""); // 去除空格 + if (!/^1[3-9]\d{9}$/.test(str)) { + return phone; // 非标准手机号,原样返回 + } + return `${str.substring(0, 3)}****${str.substring(7)}`; +} +// 身份证号脱敏 +export function maskIdCard(idCard) { + if (!idCard) + return ""; + + // 转为字符串并去除空格 + const str = String(idCard).replace(/\s+/g, ""); + + // 判断是否为 18 位身份证(支持末尾 X/x) + if (!/^\d{17}[\dX]$/i.test(str)) { + return idCard; // 非标准身份证,原样返回 + } + + return `${str.substring(0, 6)}********${str.substring(14)}`; +} +// 属地 +export const getAreaNamePath = (item) => { + const names = [ + item.provinceName, + item.cityName, + item.countryName, + item.streetName, + item.villageName, + + ].filter(name => name != null && name !== ""); + return names.join("/"); +}; +// 行业类型 +export const getCorpTypeNamePath = (item) => { + const names = [ + item.corpTypeName, + item.corpType2Name, + item.corpType3Name, + item.corpType4Name, + ].filter(name => name != null && name !== ""); + return names.join("/"); +}; +// 身份证解码 +export const UseDecodeIdCard = (userIdCard) => { + if (!userIdCard) + return userIdCard; + + try { + const decoded = atob(userIdCard); + if (/^\d{17}[\dX]$/.test(decoded)) { + return decoded; + } + } + catch { + console.warn("Not a valid Base64 string, keep as is:", userIdCard); + } + + return userIdCard; // fallback +};