封闭区域申请:H5

master
LiuJiaNan 2026-03-24 17:52:10 +08:00
parent 928edd66c4
commit 648f3b759d
8 changed files with 561 additions and 225 deletions

View File

@ -34,7 +34,7 @@
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-signature-canvas": "^1.1.0-alpha.2", "react-signature-canvas": "^1.1.0-alpha.2",
"zy-react-library": "^1.2.8" "zy-react-library": "^1.2.9"
}, },
"devDependencies": { "devDependencies": {
"@antfu/eslint-config": "^5.4.1", "@antfu/eslint-config": "^5.4.1",

View File

@ -163,7 +163,7 @@
### H5 端 ### H5 端
- 人员申请/申请 - 人员申请/申请 ##
`/primeport/container/mobile/firstLevelDoor/personnelApplication/apply` `/primeport/container/mobile/firstLevelDoor/personnelApplication/apply`
- 人员申请/申请记录 - 人员申请/申请记录 ##
`/primeport/container/mobile/firstLevelDoor/personnelApplication/applyList` `/primeport/container/mobile/firstLevelDoor/personnelApplication/applyList`

View File

@ -1,7 +1 @@
import { declareRequest } from "@cqsjjb/jjb-dva-runtime";
export {}; export {};
export const getDepartmentListTree = declareRequest(
"Post > @/basicInfo/department/listAllTreeByCorpType",
);

13
src/api/mobile/index.js Normal file
View File

@ -0,0 +1,13 @@
import { declareRequest } from "@cqsjjb/jjb-dva-runtime";
export const getDepartmentListTree = declareRequest(
"Post > @/basicInfo/department/listAllTreeByCorpType",
);
export const temporaryVisitorApplySave = declareRequest(
"temporaryVisitorLoading",
"Post > @/primeport/closedTmpApply/save",
);
export const temporaryVisitorApplyInfo = declareRequest(
"temporaryVisitorLoading",
"Get > /primeport/closedTmpApply/getInfoById",
);

View File

@ -29,3 +29,4 @@ export const NS_ENCLOSED_PERSONNEL_AND_VEHICLE_STATISTICS = defineNamespace("enc
export const NS_ENCLOSED_ENTERPRISE_PERSONNEL_PERMISSIONS = defineNamespace("enclosedEnterprisePersonnelPermissions"); export const NS_ENCLOSED_ENTERPRISE_PERSONNEL_PERMISSIONS = defineNamespace("enclosedEnterprisePersonnelPermissions");
export const NS_ENCLOSED_TEMPORARY_PERSONNEL_REVIEW = defineNamespace("enclosedTemporaryPersonnelReview"); export const NS_ENCLOSED_TEMPORARY_PERSONNEL_REVIEW = defineNamespace("enclosedTemporaryPersonnelReview");
export const NS_ENCLOSED_TEMPORARY_VEHICLE_REVIEW = defineNamespace("enclosedTemporaryVehicleReview"); export const NS_ENCLOSED_TEMPORARY_VEHICLE_REVIEW = defineNamespace("enclosedTemporaryVehicleReview");
export const NS_MOBILE = defineNamespace("mobile");

View File

@ -4,28 +4,55 @@ import {
Cascader, Cascader,
Checkbox, Checkbox,
DatePicker, DatePicker,
DotLoading,
Form, Form,
Image, Image,
ImageUploader, ImageUploader,
Input, Input,
Mask,
Picker, Picker,
Popup, Popup,
TextArea,
Toast, Toast,
} from "antd-mobile"; } from "antd-mobile";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { UPLOAD_FILE_TYPE_ENUM } from "zy-react-library/enum/uploadFile/gwj";
import useDictionary from "zy-react-library/hooks/useDictionary"; import useDictionary from "zy-react-library/hooks/useDictionary";
import useUploadFile from "zy-react-library/hooks/useUploadFile";
import { ID_NUMBER, LICENSE_PLATE_NUMBER, PHONE } from "zy-react-library/regular"; import { ID_NUMBER, LICENSE_PLATE_NUMBER, PHONE } from "zy-react-library/regular";
import { validatorEndTime } from "zy-react-library/utils"; import { getLabelName, validatorEndTime } from "zy-react-library/utils";
import SignatureH5 from "~/components/SignatureH5"; import SignatureH5 from "~/components/SignatureH5";
import { NS_GLOBAL } from "~/enumerate/namespace"; import {
NS_APPROVER_USER,
NS_ENCLOSED_AREA,
NS_FIRST_LEVEL_DOOR_INFO,
NS_MOBILE,
} from "~/enumerate/namespace";
import "../../../index.less"; import "../../../index.less";
export const ACCESS_TYPE_ENUM = [
{ label: "车辆", value: "CAR_APPLY" },
{ label: "人员", value: "PERSON_APPLY" },
];
export const APPLICATION_GATE_PERSON_TYPE_ENUM = [
{ label: "一级口门", value: "ONE_LEVEL_PERSON_APPLY" },
{ label: "封闭区域口门", value: "TWO_LEVEL_PERSON_APPLY" },
];
export const APPLICATION_GATE_CAR_TYPE_ENUM = [
{ label: "一级口门", value: "ONE_LEVEL_CAR_APPLY" },
{ label: "封闭区域口门", value: "TWO_LEVEL_CAR_APPLY" },
];
function Apply(props) { function Apply(props) {
const [primeportAreaList, setPrimeportAreaList] = useState([]); const [primeportAreaList, setPrimeportAreaList] = useState([]);
const [vehicleTypeList, setVehicleTypeList] = useState([]); const [vehicleTypeList, setVehicleTypeList] = useState([]);
const [licensePlateTypeList, setLicensePlateTypeList] = useState([]); const [licensePlateTypeList, setLicensePlateTypeList] = useState([]);
const [firstLevelDoorInfoListAll, setFirstLevelDoorInfoListAll] = useState([]);
const [twoLevelDoorInfoListAll, setTwoLevelDoorInfoListAll] = useState([]);
const [approvalUserListAll, setApprovalUserListAll] = useState([]);
const [departmentList, setDepartmentList] = useState([]); const [departmentList, setDepartmentList] = useState([]);
const [enclosedAreaList, setEnclosedAreaList] = useState([]);
const [noticePopupVisible, setNoticePopupVisible] = useState(false); const [noticePopupVisible, setNoticePopupVisible] = useState(false);
const [signatureUrl, setSignatureUrl] = useState(""); const [signatureUrl, setSignatureUrl] = useState("");
@ -33,15 +60,16 @@ function Apply(props) {
const checkboxRef = useRef(null); const checkboxRef = useRef(null);
const [form] = Form.useForm(); const [form] = Form.useForm();
const accessType = Form.useWatch("accessType", form); const tmpApplyType = Form.useWatch("tmpApplyType", form);
const doorType = Form.useWatch("doorType", form); const tmpMkmjType = Form.useWatch("tmpMkmjType", form);
const { getDictionary } = useDictionary(); const { getDictionary } = useDictionary();
const { loading: uploadFileLoading, uploadFile } = useUploadFile();
const transformDepartmentList = (list) => { const transformTreeList = (list, nameKey, idKey) => {
return list.map(item => ({ return list.map(item => ({
label: item.name, label: item[nameKey],
value: item.id, value: item[idKey],
children: item.childrenList ? transformDepartmentList(item.childrenList) : undefined, children: item.childrenList ? transformTreeList(item.childrenList) : undefined,
})); }));
}; };
@ -52,16 +80,61 @@ function Apply(props) {
setVehicleTypeList(vehicleTypeData); setVehicleTypeList(vehicleTypeData);
const licensePlateTypeData = await getDictionary({ dictValue: "LICENSE_PLATE_TYPE" }); const licensePlateTypeData = await getDictionary({ dictValue: "LICENSE_PLATE_TYPE" });
setLicensePlateTypeList(licensePlateTypeData); setLicensePlateTypeList(licensePlateTypeData);
const { data: departmentList } = await props["getDepartmentListTree"]({ enterpriseType: [1, 2] }); const { data: approvalUserListAll } = await props["approvalUserListAll"]({ temporaryPermissionFlag: 1 });
const transformedDepartmentList = transformDepartmentList(departmentList); setApprovalUserListAll(approvalUserListAll);
const { data: departmentList } = await props["getDepartmentListTree"]({ enterpriseType: [2] });
const transformedDepartmentList = transformTreeList(departmentList, "name", "id");
setDepartmentList(transformedDepartmentList); setDepartmentList(transformedDepartmentList);
}; };
const getFirstLevelDoorInfoListAll = async (gateLevelAuthArea) => {
const { data } = await props["firstLevelDoorInfoListAll"]({ mkmjLevel: 1, hgAuthArea: gateLevelAuthArea });
setFirstLevelDoorInfoListAll(data);
};
const getTwoLevelDoorInfoListAll = async (gateLevelAuthArea) => {
const { data } = await props["firstLevelDoorInfoListAll"]({ mkmjLevel: 2, hgAuthArea: gateLevelAuthArea });
setTwoLevelDoorInfoListAll(data);
};
const getEnclosedAreaList = async (jurisdictionalCorpId) => {
const { data } = await props["enclosedAreaDetailListTree"]({ jurisdictionalCorpId });
const transformedEnclosedAreaList = transformTreeList(data, "closedAreaName", "id");
setEnclosedAreaList(transformedEnclosedAreaList);
};
useEffect(() => { useEffect(() => {
getData(); getData();
}, []); }, []);
const onFinish = (values) => { const getTreeLabelName = (treeList, value, idKey = "value", nameKey = "label") => {
if (!treeList || !Array.isArray(treeList) || !value) {
return "";
}
let result = "";
const findNodeInTree = (tree, targetValue) => {
for (const node of tree) {
if (node[idKey] === targetValue) {
result = node[nameKey];
return true;
}
if (node.children && node.children.length > 0) {
const found = findNodeInTree(node.children, targetValue);
if (found) {
return true;
}
}
}
return false;
};
findNodeInTree(treeList, value);
return result;
};
const onFinish = async (values) => {
if (!values.safetyNoticeAgreed || !values.signature) { if (!values.safetyNoticeAgreed || !values.signature) {
Toast.show({ Toast.show({
icon: "fail", icon: "fail",
@ -69,19 +142,66 @@ function Apply(props) {
}); });
return; return;
} }
if (values.accessType === "1" && values.vehicleLicensePhoto.length !== 2) { if (values.tmpApplyType === "CAR_APPLY" && values.vehicleLicensePhoto.length !== 2) {
Toast.show({ Toast.show({
icon: "fail", icon: "fail",
content: "请上传两张行驶证照片", content: "请上传两张行驶证照片",
}); });
return; return;
} }
console.log(values); const tmpMkmjType = values.tmpMkmjType;
props.history.push(`./success?accessType=${values.accessType}&text=${values.accessType === "1" ? values.licensePlateNumber : values.phoneNumber}`); const informSignType = {
ONE_LEVEL_PERSON_APPLY: 611,
TWO_LEVEL_PERSON_APPLY: 609,
ONE_LEVEL_CAR_APPLY: 606,
TWO_LEVEL_CAR_APPLY: 610,
}[tmpMkmjType];
const { id: drivingLicenseId } = await uploadFile({
single: false,
files: (values.vehicleLicensePhoto || []).map(item => ({ ...item, originFileObj: item.file })),
params: { type: UPLOAD_FILE_TYPE_ENUM[601], foreignKey: "" },
});
const { id: attachmentId } = await uploadFile({
single: false,
files: (values.vehiclePhoto || []).map(item => ({ ...item, originFileObj: item.file })),
params: { type: UPLOAD_FILE_TYPE_ENUM[602], foreignKey: "" },
});
const { filePath: userFaceUrl } = await uploadFile({
files: (values.facePhoto || []).map(item => ({ ...item, originFileObj: item.file })),
params: { type: UPLOAD_FILE_TYPE_ENUM[608], foreignKey: "" },
});
const { id: informSignId } = await uploadFile({
single: false,
files: [{ originFileObj: values.signature }],
params: { type: informSignType, foreignKey: "" },
});
const { success, data } = await props["temporaryVisitorApplySave"]({
...values,
drivingLicenseId,
attachmentId,
userFaceUrl,
informSignId,
});
if (success) {
props.history.push(`./success?id=${data.id}&tmpApplyType=${values.tmpApplyType}&tmpMkmjType=${values.tmpMkmjType}`);
}
}; };
return ( return (
<div style={{ padding: "16px" }}> <div style={{ padding: "16px" }}>
<Mask visible={props.mobile.temporaryVisitorLoading || uploadFileLoading}>
<div style={{
height: "100vh",
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
}}
>
<DotLoading color="white" />
<div style={{ color: "#fff" }}>加载中...</div>
</div>
</Mask>
<Form <Form
form={form} form={form}
preserve={false} preserve={false}
@ -94,7 +214,7 @@ function Apply(props) {
)} )}
> >
<Form.Item <Form.Item
name="accessType" name="tmpApplyType"
label="访问类型" label="访问类型"
rules={[{ required: true, message: "请选择访问类型" }]} rules={[{ required: true, message: "请选择访问类型" }]}
trigger="onConfirm" trigger="onConfirm"
@ -105,17 +225,35 @@ function Apply(props) {
getValueProps={value => [value]} getValueProps={value => [value]}
> >
<Picker <Picker
columns={[[ columns={[ACCESS_TYPE_ENUM]}
{ label: "车辆", value: "1" }, onConfirm={() => {
{ label: "人员", value: "2" }, form.setFieldValue("tmpMkmjType", "");
]]} form.setFieldValue("vehicleTypeName", "");
form.setFieldValue("licenceTypeName", "");
form.setFieldValue("hgAuthAreaName", "");
form.setFieldValue("levelOneMkmjName", "");
form.setFieldValue("auditPersonUserName", "");
form.setFieldValue("auditPersonCorpId", "");
form.setFieldValue("auditPersonCorpName", "");
form.setFieldValue("auditPersonDepartmentId", "");
form.setFieldValue("auditPersonDepartmentName", "");
form.setFieldValue("jurisdictionalCorpName", "");
form.setFieldValue("closedAreaId", "");
form.setFieldValue("closedAreaName", "");
form.setFieldValue("levelTwoMkmjId", "");
form.setFieldValue("levelTwoMkmjName", "");
setFirstLevelDoorInfoListAll([]);
setEnclosedAreaList([]);
setTwoLevelDoorInfoListAll([]);
}}
> >
{value => value?.[0]?.label || "请选择访问类型"} {value => value?.[0]?.label || "请选择访问类型"}
</Picker> </Picker>
</Form.Item> </Form.Item>
{tmpApplyType && (
<Form.Item <Form.Item
name="doorType" name="tmpMkmjType"
label="选择申请口门" label="申请口门"
rules={[{ required: true, message: "请选择选择申请口门" }]} rules={[{ required: true, message: "请选择选择申请口门" }]}
trigger="onConfirm" trigger="onConfirm"
onClick={(_, pickerRef) => { onClick={(_, pickerRef) => {
@ -125,19 +263,36 @@ function Apply(props) {
getValueProps={value => [value]} getValueProps={value => [value]}
> >
<Picker <Picker
columns={[[ columns={[tmpApplyType === "PERSON_APPLY" ? APPLICATION_GATE_PERSON_TYPE_ENUM : APPLICATION_GATE_CAR_TYPE_ENUM]}
{ label: "一级口门", value: "1" }, onConfirm={() => {
{ label: "封闭区域口门", value: "2" }, form.setFieldValue("vehicleTypeName", "");
]]} form.setFieldValue("licenceTypeName", "");
form.setFieldValue("hgAuthAreaName", "");
form.setFieldValue("levelOneMkmjName", "");
form.setFieldValue("auditPersonUserName", "");
form.setFieldValue("auditPersonCorpId", "");
form.setFieldValue("auditPersonCorpName", "");
form.setFieldValue("auditPersonDepartmentId", "");
form.setFieldValue("auditPersonDepartmentName", "");
form.setFieldValue("jurisdictionalCorpName", "");
form.setFieldValue("closedAreaId", "");
form.setFieldValue("closedAreaName", "");
form.setFieldValue("levelTwoMkmjId", "");
form.setFieldValue("levelTwoMkmjName", "");
setFirstLevelDoorInfoListAll([]);
setEnclosedAreaList([]);
setTwoLevelDoorInfoListAll([]);
}}
> >
{value => value?.[0]?.label || "请选择选择申请口门"} {value => value?.[0]?.label || "请选择选择申请口门"}
</Picker> </Picker>
</Form.Item> </Form.Item>
)}
{ {
accessType === "1" tmpApplyType === "CAR_APPLY"
&& ( && (
<> <>
<Form.Item name="driverName" label="驾驶人姓名" rules={[{ required: true }]}> <Form.Item name="drivingUserName" label="驾驶人姓名" rules={[{ required: true }]}>
<Input placeholder="请输入驾驶人姓名" /> <Input placeholder="请输入驾驶人姓名" />
</Form.Item> </Form.Item>
<BasicInfoFields /> <BasicInfoFields />
@ -154,12 +309,23 @@ function Apply(props) {
> >
<Picker <Picker
columns={[vehicleTypeList.map(item => ({ label: item.dictLabel, value: item.dictValue }))]} columns={[vehicleTypeList.map(item => ({ label: item.dictLabel, value: item.dictValue }))]}
onConfirm={(value) => {
form.setFieldValue("vehicleTypeName", getLabelName({
list: vehicleTypeList,
status: value[0],
idKey: "dictValue",
nameKey: "dictLabel",
}));
}}
> >
{value => value?.[0]?.label || "请选择车辆类型"} {value => value?.[0]?.label || "请选择车辆类型"}
</Picker> </Picker>
</Form.Item> </Form.Item>
<Form.Item name="vehicleTypeName" label="车辆类型名称" noStyle>
<input type="hidden" />
</Form.Item>
<Form.Item <Form.Item
name="licensePlateType" name="licenceType"
label="车牌类型" label="车牌类型"
rules={[{ required: true, message: "请选择车牌类型" }]} rules={[{ required: true, message: "请选择车牌类型" }]}
trigger="onConfirm" trigger="onConfirm"
@ -171,16 +337,47 @@ function Apply(props) {
> >
<Picker <Picker
columns={[licensePlateTypeList.map(item => ({ label: item.dictLabel, value: item.dictValue }))]} columns={[licensePlateTypeList.map(item => ({ label: item.dictLabel, value: item.dictValue }))]}
onConfirm={(value) => {
form.setFieldValue("licenceTypeName", getLabelName({
list: licensePlateTypeList,
status: value[0],
idKey: "dictValue",
nameKey: "dictLabel",
}));
}}
> >
{value => value?.[0]?.label || "请选择车牌类型"} {value => value?.[0]?.label || "请选择车牌类型"}
</Picker> </Picker>
</Form.Item> </Form.Item>
<Form.Item name="licensePlateNumber" label="车牌号" rules={[{ required: true }, { pattern: LICENSE_PLATE_NUMBER, message: "请输入正确的车牌号" }]}> <Form.Item name="licenceTypeName" label="车牌类型名称" noStyle>
<input type="hidden" />
</Form.Item>
<Form.Item
name="licenceNo"
label="车牌号"
rules={[{ required: true }, { pattern: LICENSE_PLATE_NUMBER, message: "请输入正确的车牌号" }]}
>
<Input placeholder="请输入车牌号" /> <Input placeholder="请输入车牌号" />
</Form.Item> </Form.Item>
<VisitTime form={form} /> <VisitTime form={form} />
<LocationFields primeportAreaList={primeportAreaList} doorType={doorType} /> <LocationFields
<Form.Item name="vehicleLicensePhoto" label="行驶证照片" rules={[{ required: true, message: "请上传行驶证照片" }]}> primeportAreaList={primeportAreaList}
tmpMkmjType={tmpMkmjType}
firstLevelDoorInfoListAll={firstLevelDoorInfoListAll}
twoLevelDoorInfoListAll={twoLevelDoorInfoListAll}
getFirstLevelDoorInfoListAll={getFirstLevelDoorInfoListAll}
getTwoLevelDoorInfoListAll={getTwoLevelDoorInfoListAll}
departmentList={departmentList}
getTreeLabelName={getTreeLabelName}
getEnclosedAreaList={getEnclosedAreaList}
enclosedAreaList={enclosedAreaList}
form={form}
/>
<Form.Item
name="vehicleLicensePhoto"
label="行驶证照片"
rules={[{ required: true, message: "请上传行驶证照片" }]}
>
<ImageUploader <ImageUploader
upload={file => ({ url: URL.createObjectURL(file), file })} upload={file => ({ url: URL.createObjectURL(file), file })}
maxCount={2} maxCount={2}
@ -192,7 +389,10 @@ function Apply(props) {
maxCount={4} maxCount={4}
/> />
</Form.Item> </Form.Item>
<Approval departmentList={departmentList} /> <Form.Item name="applyReason" label="申请理由" rules={[{ required: true, message: "请上传申请理由" }]}>
<TextArea autoSize maxLength={500} />
</Form.Item>
<Approval form={form} approvalUserListAll={approvalUserListAll} />
<SafetyNoticeAndSignature <SafetyNoticeAndSignature
checkboxRef={checkboxRef} checkboxRef={checkboxRef}
signatureUrl={signatureUrl} signatureUrl={signatureUrl}
@ -202,22 +402,37 @@ function Apply(props) {
) )
} }
{ {
accessType === "2" tmpApplyType === "PERSON_APPLY"
&& ( && (
<> <>
<Form.Item name="visitorName" label="访问人姓名" rules={[{ required: true }]}> <Form.Item name="applyPersonUserName" label="访问人姓名" rules={[{ required: true }]}>
<Input placeholder="请输入访问人姓名" /> <Input placeholder="请输入访问人姓名" />
</Form.Item> </Form.Item>
<BasicInfoFields /> <BasicInfoFields />
<VisitTime form={form} /> <VisitTime form={form} />
<LocationFields primeportAreaList={primeportAreaList} doorType={doorType} /> <LocationFields
primeportAreaList={primeportAreaList}
tmpMkmjType={tmpMkmjType}
firstLevelDoorInfoListAll={firstLevelDoorInfoListAll}
twoLevelDoorInfoListAll={twoLevelDoorInfoListAll}
getFirstLevelDoorInfoListAll={getFirstLevelDoorInfoListAll}
getTwoLevelDoorInfoListAll={getTwoLevelDoorInfoListAll}
departmentList={departmentList}
getTreeLabelName={getTreeLabelName}
getEnclosedAreaList={getEnclosedAreaList}
enclosedAreaList={enclosedAreaList}
form={form}
/>
<Form.Item name="facePhoto" label="人脸照片" rules={[{ required: true, message: "请上传人脸照片" }]}> <Form.Item name="facePhoto" label="人脸照片" rules={[{ required: true, message: "请上传人脸照片" }]}>
<ImageUploader <ImageUploader
upload={file => ({ url: URL.createObjectURL(file), file })} upload={file => ({ url: URL.createObjectURL(file), file })}
maxCount={1} maxCount={1}
/> />
</Form.Item> </Form.Item>
<Approval departmentList={departmentList} /> <Form.Item name="applyReason" label="申请理由" rules={[{ required: true, message: "请上传申请理由" }]}>
<TextArea autoSize maxLength={500} />
</Form.Item>
<Approval form={form} approvalUserListAll={approvalUserListAll} />
<SafetyNoticeAndSignature <SafetyNoticeAndSignature
checkboxRef={checkboxRef} checkboxRef={checkboxRef}
signatureUrl={signatureUrl} signatureUrl={signatureUrl}
@ -236,13 +451,37 @@ function Apply(props) {
}} }}
> >
<div style={{ padding: "40px 20px", overflowY: "auto", height: "100vh" }}> <div style={{ padding: "40px 20px", overflowY: "auto", height: "100vh" }}>
<p style={{ textAlign: "justify", textIndent: "2rem" }}>欢迎您到访秦皇岛港为保障您的人身安全及港口生产作业秩序请注意港口属于重点安全监管区域存在大型机械作业货物装卸车辆往来等生产场景可能面临机械伤害物体打击车辆碰撞等安全风险请您认真阅读以下须知内容确认遵守后签字</p> <p style={{
<p style={{ textAlign: "justify", textIndent: "2rem" }}>1. 入港时请主动出示有效身份证件配合安保人员进行身份核验与信息登记凭港口核发的临时访客证入港自觉接受出港查验不转借冒用访客凭证不将无关人员带入港口</p> textAlign: "justify",
<p style={{ textAlign: "justify", textIndent: "2rem" }}>2. 入港后请严格在指定区域活动未经陪同人员及港口负责人许可绝不擅自进入标有"禁止入内""危险区域"等标识的场所不靠近起重机械输送设备危险品存储点等高危部位不跨越安全护栏警戒线不在作业区域逗留围观</p> textIndent: "2rem",
<p style={{ textAlign: "justify", textIndent: "2rem" }}>3. 遵守港口生产秩序不干扰装卸运输检修等正常工作不随意触摸操作生产设备仪器仪表及安全设施不移动遮挡安全警示标识如需拍摄港口场景须提前征得港口方同意不拍摄涉及安全商业秘密的内容</p> }}
<p style={{ textAlign: "justify", textIndent: "2rem" }}>4. 严格遵守消防安全规定不在港口内吸烟不携带火种易燃易爆物品管制器具等违禁物品入港发现火灾设备故障等隐患或突发情况第一时间告知陪同人员或港口工作人员配合应急处置不擅自行动引发次生风险</p> >
<p style={{ textAlign: "justify", textIndent: "2rem" }}>5. 注意自身安全防护行走时主动避让作业车辆与机械不擅自横穿作业通道雨天雾天等恶劣天气下听从陪同人员安排加强安全防范</p> 欢迎您到访秦皇岛港为保障您的人身安全及港口生产作业秩序请注意港口属于重点安全监管区域存在大型机械作业货物装卸车辆往来等生产场景可能面临机械伤害物体打击车辆碰撞等安全风险请您认真阅读以下须知内容确认遵守后签字
<p style={{ textAlign: "justify", textIndent: "2rem" }}>6. 已知晓港口所去区域应急逃生路线遇紧急情况按港口指引有序疏散</p> </p>
<p style={{ textAlign: "justify", textIndent: "2rem" }}>
1.
入港时请主动出示有效身份证件配合安保人员进行身份核验与信息登记凭港口核发的临时访客证入港自觉接受出港查验不转借冒用访客凭证不将无关人员带入港口
</p>
<p style={{ textAlign: "justify", textIndent: "2rem" }}>
2.
入港后请严格在指定区域活动未经陪同人员及港口负责人许可绝不擅自进入标有"禁止入内""危险区域"等标识的场所不靠近起重机械输送设备危险品存储点等高危部位不跨越安全护栏警戒线不在作业区域逗留围观
</p>
<p style={{ textAlign: "justify", textIndent: "2rem" }}>
3.
遵守港口生产秩序不干扰装卸运输检修等正常工作不随意触摸操作生产设备仪器仪表及安全设施不移动遮挡安全警示标识如需拍摄港口场景须提前征得港口方同意不拍摄涉及安全商业秘密的内容
</p>
<p style={{ textAlign: "justify", textIndent: "2rem" }}>
4.
严格遵守消防安全规定不在港口内吸烟不携带火种易燃易爆物品管制器具等违禁物品入港发现火灾设备故障等隐患或突发情况第一时间告知陪同人员或港口工作人员配合应急处置不擅自行动引发次生风险
</p>
<p style={{ textAlign: "justify", textIndent: "2rem" }}>
5.
注意自身安全防护行走时主动避让作业车辆与机械不擅自横穿作业通道雨天雾天等恶劣天气下听从陪同人员安排加强安全防范
</p>
<p style={{ textAlign: "justify", textIndent: "2rem" }}>
6.
已知晓港口所去区域应急逃生路线遇紧急情况按港口指引有序疏散
</p>
<p>本人确认已完整阅读并理解以上须知承诺严格遵守如因违反本须知及港口安全规定导致自身人身伤害或港口他人财产损失自愿承担全部责任</p> <p>本人确认已完整阅读并理解以上须知承诺严格遵守如因违反本须知及港口安全规定导致自身人身伤害或港口他人财产损失自愿承担全部责任</p>
<SignatureH5 <SignatureH5
onConfirm={(value) => { onConfirm={(value) => {
@ -280,10 +519,18 @@ function Apply(props) {
// 公共基础信息字段组件 // 公共基础信息字段组件
const BasicInfoFields = () => ( const BasicInfoFields = () => (
<> <>
<Form.Item name="phoneNumber" label="手机号" rules={[{ required: true }, { pattern: PHONE, message: "请输入正确的手机号" }]}> <Form.Item
name="userPhone"
label="手机号"
rules={[{ required: true }, { pattern: PHONE, message: "请输入正确的手机号" }]}
>
<Input placeholder="请输入手机号" /> <Input placeholder="请输入手机号" />
</Form.Item> </Form.Item>
<Form.Item name="idNumber" label="身份证号" rules={[{ required: true }, { pattern: ID_NUMBER, message: "请输入正确的身份证号" }]}> <Form.Item
name="userCard"
label="身份证号"
rules={[{ required: true }, { pattern: ID_NUMBER, message: "请输入正确的身份证号" }]}
>
<Input placeholder="请输入身份证号" /> <Input placeholder="请输入身份证号" />
</Form.Item> </Form.Item>
</> </>
@ -310,7 +557,10 @@ const VisitTime = ({ form }) => (
<Form.Item <Form.Item
name="visitEndTime" name="visitEndTime"
label="访问结束时间" label="访问结束时间"
rules={[{ required: true, message: "请选择访问结束时间" }, (() => validatorEndTime(form.getFieldValue("visitStartTime")))()]} rules={[{
required: true,
message: "请选择访问结束时间",
}, (() => validatorEndTime(form.getFieldValue("visitStartTime")))()]}
trigger="onConfirm" trigger="onConfirm"
onClick={(_, pickerRef) => { onClick={(_, pickerRef) => {
pickerRef.current?.open(); pickerRef.current?.open();
@ -326,10 +576,22 @@ const VisitTime = ({ form }) => (
); );
// 公共地点字段组件 // 公共地点字段组件
const LocationFields = ({ primeportAreaList, doorType }) => ( const LocationFields = ({
primeportAreaList,
tmpMkmjType,
firstLevelDoorInfoListAll,
twoLevelDoorInfoListAll,
getFirstLevelDoorInfoListAll,
getTwoLevelDoorInfoListAll,
departmentList,
getTreeLabelName,
getEnclosedAreaList,
enclosedAreaList,
form,
}) => (
<> <>
<Form.Item <Form.Item
name="portArea" name="hgAuthArea"
label="港区" label="港区"
rules={[{ required: true, message: "请选择港区" }]} rules={[{ required: true, message: "请选择港区" }]}
trigger="onConfirm" trigger="onConfirm"
@ -341,12 +603,27 @@ const LocationFields = ({ primeportAreaList, doorType }) => (
> >
<Picker <Picker
columns={[primeportAreaList.map(item => ({ label: item.dictLabel, value: item.dictValue }))]} columns={[primeportAreaList.map(item => ({ label: item.dictLabel, value: item.dictValue }))]}
onConfirm={(value) => {
form.setFieldValue("hgAuthAreaName", getLabelName({
list: primeportAreaList,
status: value[0],
idKey: "dictValue",
nameKey: "dictLabel",
}));
form.setFieldValue("levelOneMkmjId", "");
form.setFieldValue("levelOneMkmjName", "");
getFirstLevelDoorInfoListAll(value[0]);
getTwoLevelDoorInfoListAll(value[0]);
}}
> >
{value => value?.[0]?.label || "请选择港区"} {value => value?.[0]?.label || "请选择港区"}
</Picker> </Picker>
</Form.Item> </Form.Item>
<Form.Item name="hgAuthAreaName" label="港区名称" noStyle>
<input type="hidden" />
</Form.Item>
<Form.Item <Form.Item
name="firstLevelDoor" name="levelOneMkmjId"
label="一级口门" label="一级口门"
rules={[{ required: true, message: "请选择一级口门" }]} rules={[{ required: true, message: "请选择一级口门" }]}
trigger="onConfirm" trigger="onConfirm"
@ -357,43 +634,76 @@ const LocationFields = ({ primeportAreaList, doorType }) => (
getValueProps={value => [value]} getValueProps={value => [value]}
> >
<Picker <Picker
columns={[[ columns={[firstLevelDoorInfoListAll.map(item => ({ label: item.mkmjName, value: item.id }))]}
{ label: "一级口门1", value: "1" }, onConfirm={(value) => {
{ label: "一级口门2", value: "2" }, form.setFieldValue("levelOneMkmjName", getLabelName({
]]} list: firstLevelDoorInfoListAll,
status: value[0],
idKey: "id",
nameKey: "mkmjName",
}));
}}
> >
{value => value?.[0]?.label || "请选择一级口门"} {value => value?.[0]?.label || "请选择一级口门"}
</Picker> </Picker>
</Form.Item> </Form.Item>
{doorType === "2" && <EnclosedAreaFields />} <Form.Item name="levelOneMkmjName" label="一级口门名称" noStyle>
<input type="hidden" />
</Form.Item>
{(tmpMkmjType === "TWO_LEVEL_CAR_APPLY" || tmpMkmjType === "TWO_LEVEL_PERSON_APPLY")
&& (
<EnclosedAreaFields
form={form}
departmentList={departmentList}
getTreeLabelName={getTreeLabelName}
getEnclosedAreaList={getEnclosedAreaList}
enclosedAreaList={enclosedAreaList}
twoLevelDoorInfoListAll={twoLevelDoorInfoListAll}
/>
)}
</> </>
); );
// 封闭区域相关字段组件 // 封闭区域相关字段组件
const EnclosedAreaFields = () => ( const EnclosedAreaFields = ({
form,
departmentList,
getTreeLabelName,
getEnclosedAreaList,
enclosedAreaList,
twoLevelDoorInfoListAll,
}) => (
<> <>
<Form.Item <Form.Item
name="areaCompany" name="jurisdictionalCorpId"
label="区域所属公司" label="区域所属公司"
rules={[{ required: true, message: "请选择区域所属公司" }]} rules={[{ required: true, message: "请选择区域所属公司" }]}
trigger="onConfirm" trigger="onConfirm"
onClick={(_, pickerRef) => { onClick={(_, pickerRef) => {
pickerRef.current?.open(); pickerRef.current?.open();
}} }}
getValueFromEvent={value => value[0]}
getValueProps={value => [value]}
> >
<Picker <Cascader
columns={[[ options={departmentList}
{ label: "区域所属公司1", value: "1" }, onConfirm={(value) => {
{ label: "区域所属公司2", value: "2" }, form.setFieldValue("jurisdictionalCorpName", getTreeLabelName(departmentList, value.at(-1)));
]]} if (value.length > 0) {
getEnclosedAreaList(value.at(-1));
}
form.setFieldValue("closedAreaId", "");
form.setFieldValue("closedAreaName", "");
form.setFieldValue("levelTwoMkmjId", "");
form.setFieldValue("levelTwoMkmjName", "");
}}
> >
{value => value?.[0]?.label || "请选择区域所属公司"} {value => value.length > 0 ? value.map(item => item?.label).filter(Boolean).join("-") : "请选择区域所属公司"}
</Picker> </Cascader>
</Form.Item>
<Form.Item name="jurisdictionalCorpName" label="区域所属公司名称" noStyle>
<input type="hidden" />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name="enclosedArea" name="closedAreaId"
label="封闭区域" label="封闭区域"
rules={[{ required: true, message: "请选择封闭区域" }]} rules={[{ required: true, message: "请选择封闭区域" }]}
trigger="onConfirm" trigger="onConfirm"
@ -403,17 +713,22 @@ const EnclosedAreaFields = () => (
getValueFromEvent={value => value[0]} getValueFromEvent={value => value[0]}
getValueProps={value => [value]} getValueProps={value => [value]}
> >
<Picker <Cascader
columns={[[ options={enclosedAreaList}
{ label: "封闭区域1", value: "1" }, onConfirm={(value) => {
{ label: "封闭区域2", value: "2" }, form.setFieldValue("closedAreaName", getTreeLabelName(enclosedAreaList, value.at(-1)));
]]} form.setFieldValue("levelTwoMkmjId", "");
form.setFieldValue("levelTwoMkmjName", "");
}}
> >
{value => value?.[0]?.label || "请选择封闭区域"} {value => value.length > 0 ? value.map(item => item?.label).filter(Boolean).join("-") : "请选择封闭区域"}
</Picker> </Cascader>
</Form.Item>
<Form.Item name="closedAreaName" label="封闭区域名称" noStyle>
<input type="hidden" />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name="enclosedAreaDoor" name="levelTwoMkmjId"
label="口门" label="口门"
rules={[{ required: true, message: "请选择口门" }]} rules={[{ required: true, message: "请选择口门" }]}
trigger="onConfirm" trigger="onConfirm"
@ -424,35 +739,30 @@ const EnclosedAreaFields = () => (
getValueProps={value => [value]} getValueProps={value => [value]}
> >
<Picker <Picker
columns={[[ columns={[twoLevelDoorInfoListAll.map(item => ({ label: item.mkmjName, value: item.id }))]}
{ label: "口门1", value: "1" }, onConfirm={(value) => {
{ label: "口门2", value: "2" }, form.setFieldValue("levelTwoMkmjName", getLabelName({
]]} list: twoLevelDoorInfoListAll,
status: value[0],
idKey: "id",
nameKey: "mkmjName",
}));
}}
> >
{value => value?.[0]?.label || "请选择口门"} {value => value?.[0]?.label || "请选择口门"}
</Picker> </Picker>
</Form.Item> </Form.Item>
<Form.Item name="levelTwoMkmjName" label="口门名称" noStyle>
<input type="hidden" />
</Form.Item>
</> </>
); );
// 公共审批字段组件 // 公共审批字段组件
const Approval = ({ departmentList }) => ( const Approval = ({ form, approvalUserListAll }) => (
<> <>
<Form.Item <Form.Item
name="approvalEnterprise" name="auditPersonUserId"
label="审批企业"
rules={[{ required: true, message: "请选择审批企业" }]}
trigger="onConfirm"
onClick={(_, pickerRef) => {
pickerRef.current?.open();
}}
>
<Cascader options={departmentList}>
{value => value.length > 0 ? value.map(item => item?.label).filter(Boolean).join("-") : "请选择审批企业"}
</Cascader>
</Form.Item>
<Form.Item
name="approvalPerson"
label="审批人" label="审批人"
rules={[{ required: true, message: "请选择审批人" }]} rules={[{ required: true, message: "请选择审批人" }]}
trigger="onConfirm" trigger="onConfirm"
@ -463,14 +773,37 @@ const Approval = ({ departmentList }) => (
getValueProps={value => [value]} getValueProps={value => [value]}
> >
<Picker <Picker
columns={[[ columns={[approvalUserListAll.map(item => ({
{ label: "审批人1", value: "1" }, label: `${item.userName}${item.corpName}${item.deptName}`,
{ label: "审批人2", value: "2" }, value: item.userId,
]]} }))]}
onConfirm={(value) => {
const item = approvalUserListAll.find(item => item.userId === value[0]);
form.setFieldValue("auditPersonUserName", item.userName);
form.setFieldValue("auditPersonCorpId", item.corpId);
form.setFieldValue("auditPersonCorpName", item.corpName);
form.setFieldValue("auditPersonDepartmentId", item.deptId);
form.setFieldValue("auditPersonDepartmentName", item.deptName);
}}
> >
{value => value?.[0]?.label || "请选择审批人"} {value => value?.[0]?.label || "请选择审批人"}
</Picker> </Picker>
</Form.Item> </Form.Item>
<Form.Item name="auditPersonCorpId" label="审批人员所属企业ID" noStyle>
<input type="hidden" />
</Form.Item>
<Form.Item name="auditPersonCorpName" label="审批人员所属企业名称" noStyle>
<input type="hidden" />
</Form.Item>
<Form.Item name="auditPersonDepartmentId" label="审批人员所属部门id" noStyle>
<input type="hidden" />
</Form.Item>
<Form.Item name="auditPersonDepartmentName" label="审批人员部门名称" noStyle>
<input type="hidden" />
</Form.Item>
<Form.Item name="auditPersonUserName" label="审批人员姓名" noStyle>
<input type="hidden" />
</Form.Item>
</> </>
); );
@ -518,4 +851,4 @@ const SafetyNoticeAndSignature = ({ checkboxRef, signatureUrl, setNoticePopupVis
</> </>
); );
export default Connect([NS_GLOBAL], true)(Apply); export default Connect([NS_MOBILE, NS_FIRST_LEVEL_DOOR_INFO, NS_APPROVER_USER, NS_ENCLOSED_AREA], true)(Apply);

View File

@ -1,127 +1,122 @@
import { DotLoading, InfiniteScroll } from "antd-mobile"; import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { useState } from "react"; import { useEffect, useState } from "react";
import useGetUrlQuery from "zy-react-library/hooks/useGetUrlQuery"; import useGetUrlQuery from "zy-react-library/hooks/useGetUrlQuery";
import { getLabelName } from "zy-react-library/utils";
import { ENCLOSED_AREA_AUDIT_STATUS_ENUM } from "~/enumerate/constant";
import { NS_MOBILE } from "~/enumerate/namespace";
function ApplyList() { function ApplyList(props) {
const query = useGetUrlQuery(); const query = useGetUrlQuery();
const [data, setData] = useState([]); const [info, setInfo] = useState({});
const [hasMore, setHasMore] = useState(true);
const loadMore = async () => { const getData = async () => {
const data = [ const { data } = await props["temporaryVisitorApplyInfo"]({
{ name: "张三", phone: "12345678901", area: "A区", doorTypeName: "1号门", startTime: "2021-01-01 00:00:00", endTime: "2021-01-01 01:00:00", status: "通过", reason: "" }, id: query.id,
]; tmpApplyType: query.tmpApplyType,
setData(val => [...val, ...data]); tmpMkmjType: query.tmpMkmjType,
setHasMore(data.length > 0); });
setInfo(data);
}; };
useEffect(() => {
getData();
}, []);
return ( return (
<div> <div>
<div style={{ backgroundColor: "#f9f9f9", padding: "10px" }}> <div style={{ backgroundColor: "#f9f9f9", padding: "10px" }}>
{data.map((item, index) => ( <div style={{ backgroundColor: "#fff", padding: "10px", marginBottom: "10px", borderRadius: "5px" }}>
<div key={index} style={{ backgroundColor: "#fff", padding: "10px", marginBottom: "10px", borderRadius: "5px" }}>
{ {
query.accessType === "2" && ( query.tmpApplyType === "PERSON_APPLY" && (
<div> <div>
<div style={{ fontSize: "16px", fontWeight: "bold" }}>临时访客申请</div> <div style={{ fontSize: "16px", fontWeight: "bold" }}>临时访客申请</div>
<div> <div>
申请人 申请人
{item.name} {info.applyPersonUserName}
</div> </div>
<div> <div>
手机号 手机号
{item.phone} {info.userPhone}
</div> </div>
<div> <div>
申请区域 申请区域
{item.area} {info.closedAreaName}
</div> </div>
<div> <div>
申请口门 申请口门
{item.doorTypeName} {info.levelOneMkmjName}
</div> </div>
<div> <div>
时间范围 时间范围
{item.startTime} {info.visitStartTime}
{item.endTime} {info.visitEndTime}
</div> </div>
<div> <div>
审核状态 审核状态
{item.status} {getLabelName({ list: ENCLOSED_AREA_AUDIT_STATUS_ENUM, status: info.auditFlag })}
</div> </div>
{info.auditFlag === 3 && (
<div> <div>
驳回原因 驳回原因
{item.reason} {info.refusalReason}
</div> </div>
)}
</div> </div>
) )
} }
{ {
query.accessType === "1" && ( query.tmpApplyType === "CAR_APPLY" && (
<div> <div>
<div style={{ fontSize: "16px", fontWeight: "bold" }}>临时访客车辆申请</div> <div style={{ fontSize: "16px", fontWeight: "bold" }}>临时访客车辆申请</div>
<div> <div>
申请人 申请人
{item.name} {info.drivingUserName}
</div> </div>
<div> <div>
手机号 手机号
{item.phone} {info.userPhone}
</div> </div>
<div> <div>
申请区域 申请区域
{item.area} {info.closedAreaName}
</div> </div>
<div> <div>
申请口门 申请口门
{item.doorTypeName} {info.levelOneMkmjName}
</div> </div>
<div> <div>
时间范围 时间范围
{item.startTime} {info.visitStartTime}
{item.endTime} {info.visitEndTime}
</div> </div>
<div> <div>
车辆类型 车辆类型
{item.carType} {info.vehicleTypeName}
</div> </div>
<div> <div>
车牌号 车牌号
{item.carNo} {info.licenceNo}
</div> </div>
<div> <div>
审核状态 审核状态
{item.status} {getLabelName({ list: ENCLOSED_AREA_AUDIT_STATUS_ENUM, status: info.auditFlag })}
</div> </div>
{info.auditFlag === 3 && (
<div> <div>
驳回原因 驳回原因
{item.reason} {info.refusalReason}
</div> </div>
)}
</div> </div>
) )
} }
</div> </div>
))}
</div> </div>
<InfiniteScroll loadMore={loadMore} hasMore={hasMore}>
{
hasMore
? (
<>
<span>Loading</span>
<DotLoading />
</>
)
: (
<span>--- 我是有底线的 ---</span>
)
}
</InfiniteScroll>
</div> </div>
); );
} }
export default ApplyList; export default Connect([NS_MOBILE], true)(ApplyList);

View File

@ -12,7 +12,7 @@ function Success() {
<div style={{ paddingTop: "10px" }}>信息提交成功</div> <div style={{ paddingTop: "10px" }}>信息提交成功</div>
</div> </div>
<div style={{ textAlign: "center", marginTop: "50px" }}> <div style={{ textAlign: "center", marginTop: "50px" }}>
<QRCode value={`${window.location.origin}/primeport/container/mobile/firstLevelDoor/personnelApplication/applyList?accessType=${query.accessType}&text=${query.text}`} style={{ margin: "0 auto", marginBottom: 10 }} /> <QRCode value={`${window.location.origin}/primeport/container/mobile/firstLevelDoor/personnelApplication/applyList?id=${query.id}&tmpApplyType=${query.tmpApplyType}&tmpMkmjType=${query.tmpMkmjType}`} style={{ margin: "0 auto", marginBottom: 10 }} />
<div style={{ paddingTop: "20px" }}>扫描识别二维码获取审批进度</div> <div style={{ paddingTop: "20px" }}>扫描识别二维码获取审批进度</div>
<div style={{ color: "red", fontSize: 12, paddingTop: "10px" }}>请长按或截屏保存二维码关闭页面后将无法再获取二维码</div> <div style={{ color: "red", fontSize: 12, paddingTop: "10px" }}>请长按或截屏保存二维码关闭页面后将无法再获取二维码</div>
</div> </div>