Merge branch 'dev_1.0.1' of http://47.92.113.182:3000/cq_anquan/safety-eval-service-frontend into dev_1.0.1
commit
ed9cef3c35
|
|
@ -67,7 +67,7 @@ export const modifyFilingMaterial = declareRequest(
|
|||
|
||||
export const queryQualMonitorPage = declareRequest(
|
||||
"qualReviewLoading",
|
||||
"Get > /safetyEval/qual-monitor/page",
|
||||
"Get > /safetyEval/qual-filing/qualificationMonitoringPage",
|
||||
'qualMonitorList: [] | res.data || [] & qualMonitorTotal: 0 | res.total || 0',
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import {
|
|||
fetchStaffCertListByPersonnelId,
|
||||
} from "~/api/staffCertificate";
|
||||
import { fetchOrgPersonnelDetail } from "~/utils/qualFiling/personnelHelper";
|
||||
import { TITLE_LEVEL_MAP } from "~/enumerate/enterpriseOptions";
|
||||
|
||||
const GENDER_MAP = { 1: "男", 2: "女" };
|
||||
const REGISTER_ENGINEER_MAP = { 1: "是", 2: "否" };
|
||||
|
|
@ -120,7 +121,7 @@ export default function StaffViewModal({
|
|||
onCancel={onCancel}
|
||||
>
|
||||
<Descriptions bordered column={2} labelStyle={{ width: 120 }}>
|
||||
<Descriptions.Item label="姓名">{info.titleName}</Descriptions.Item>
|
||||
<Descriptions.Item label="姓名">{info.userName}</Descriptions.Item>
|
||||
<Descriptions.Item label="性别">
|
||||
{GENDER_MAP[info.genderCode]}
|
||||
</Descriptions.Item>
|
||||
|
|
@ -152,7 +153,7 @@ export default function StaffViewModal({
|
|||
{info.educationLevelName || "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="职称">
|
||||
{info.titleName || "-"}
|
||||
{TITLE_LEVEL_MAP[info.titleCode] ?? (info.titleCode || "-")}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="是否注册安全工程师">
|
||||
{REGISTER_ENGINEER_MAP[info.registerEngineerFlag] ?? "-"}
|
||||
|
|
|
|||
|
|
@ -63,10 +63,8 @@ export const CHANGE_STATUS_MAP = {
|
|||
|
||||
/** 资质保持监控状态 */
|
||||
export const QUAL_MONITOR_STATUS_MAP = {
|
||||
normal: { label: "保持正常", color: "success" },
|
||||
person_warning: { label: "人员比例预警", color: "warning" },
|
||||
focus_warning: { label: "重点预警", color: "error" },
|
||||
area_pending: { label: "属地待核验", color: "warning" },
|
||||
normal: { label: "正常", color: "success" },
|
||||
person_rate: { label: "人员比例预警", color: "warning" },
|
||||
};
|
||||
|
||||
/** 性别 */
|
||||
|
|
@ -98,4 +96,25 @@ export const LEGAL_STATUS_STYLE = {
|
|||
PASS: { icon: "✅", borderColor: "#059669", bg: "#f0fdf4" },
|
||||
WARN: { icon: "⚠️", borderColor: "#d97706", bg: "#fffbeb" },
|
||||
NOT_PASS: { icon: "❌", borderColor: "#dc2626", bg: "#fef2f2" },
|
||||
};
|
||||
};
|
||||
|
||||
/** 资质保持监控-条件检查项模板 */
|
||||
export const QUAL_MONITOR_CONDITION_ITEMS = [
|
||||
{ key: "legalPerson", label: "独立法人资格" },
|
||||
{ key: "fixedAsset", label: "固定资产" },
|
||||
{ key: "workplace", label: "工作场所" },
|
||||
{ key: "equipment", label: "设施设备软件" },
|
||||
{ key: "staffCount", label: "专职技术人员数量" },
|
||||
{ key: "staffRatio", label: "人员比例" },
|
||||
{ key: "leader", label: "负责人配备" },
|
||||
{ key: "system", label: "制度与过程控制体系" },
|
||||
{ key: "website", label: "承诺书/网站/报告查询" },
|
||||
{ key: "credit", label: "三年重大违法失信记录" },
|
||||
{ key: "area", label: "属地满足情况" },
|
||||
];
|
||||
|
||||
/** 资质保持监控-预警状态筛选选项 */
|
||||
export const QUAL_MONITOR_STATUS_OPTIONS = [
|
||||
{ value: "normal", label: "正常" },
|
||||
{ value: "person_rate", label: "人员比例预警" },
|
||||
];
|
||||
|
|
@ -173,3 +173,15 @@ export const REGISTER_ENGINEER_OPTIONS = [
|
|||
{ label: "是", value: 1 },
|
||||
{ label: "否", value: 2 },
|
||||
];
|
||||
|
||||
/** 职称等级 */
|
||||
export const TITLE_LEVEL_OPTIONS = [
|
||||
{ value: "SENIOR", label: "高级" },
|
||||
{ value: "MIDDLE", label: "中级" },
|
||||
{ value: "JUNIOR", label: "初级" },
|
||||
];
|
||||
|
||||
export const TITLE_LEVEL_MAP = TITLE_LEVEL_OPTIONS.reduce(
|
||||
(acc, cur) => ({ ...acc, [cur.value]: cur.label }),
|
||||
{},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import {
|
|||
PROFESSIONAL_LEVEL_OPTIONS,
|
||||
QUALIFICATION_INDUSTRY_OPTIONS,
|
||||
REGISTER_ENGINEER_OPTIONS,
|
||||
TITLE_LEVEL_OPTIONS,
|
||||
} from "~/enumerate/enterpriseOptions";
|
||||
import { getBirthDateFromIdCard } from "~/utils";
|
||||
import { idCardRule, mobileRule } from "~/utils/validators";
|
||||
|
|
@ -689,11 +690,11 @@ function StaffFormModal({
|
|||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="titleName"
|
||||
name="titleCode"
|
||||
label="职称"
|
||||
rules={[{ max: 50, message: "职称不能超过50个字符" }]}
|
||||
rules={[{ required: false, message: "请选择职称" }]}
|
||||
>
|
||||
<Input placeholder="请输入职称" maxLength={50} showCount />
|
||||
<Select placeholder="请选择职称" allowClear options={TITLE_LEVEL_OPTIONS} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ function FilingApplicationListPage(props) {
|
|||
okText: "是",
|
||||
cancelText: "否",
|
||||
onOk: async () => {
|
||||
const res = await props.qualFilingDelete({ id: record.id });
|
||||
const res = await props.qualFilingDelete({ data: record.id });
|
||||
if (res?.success !== false) {
|
||||
message.success("删除成功");
|
||||
getData();
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
|||
import { Button, Form, Input, Modal, Table } from "antd";
|
||||
import { useEffect, useState } from "react";
|
||||
import { NS_STAFF_INFO } from "~/enumerate/namespace";
|
||||
import { TITLE_LEVEL_MAP } from "~/enumerate/enterpriseOptions";
|
||||
import StaffViewModal from "~/components/StaffViewModal";
|
||||
|
||||
function OrgPersonnelSelectModalInner(props) {
|
||||
|
|
@ -97,7 +98,7 @@ function OrgPersonnelSelectModalInner(props) {
|
|||
{ title: "人员姓名", dataIndex: "userName", ellipsis: true },
|
||||
{ title: "类型", dataIndex: "personTypeName" },
|
||||
|
||||
{ title: "职称", dataIndex: "titleName", ellipsis: true },
|
||||
{ title: "职称", dataIndex: "titleName", ellipsis: true, render: (_, record) => TITLE_LEVEL_MAP[record.titleCode] ?? (record.titleCode || "-") },
|
||||
{
|
||||
title: "操作",
|
||||
width: 80,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { Button, Modal, Space, Table } from "antd";
|
||||
import { useState } from "react";
|
||||
import { TITLE_LEVEL_MAP } from "~/enumerate/enterpriseOptions";
|
||||
import StaffViewModal from "~/components/StaffViewModal";
|
||||
import OrgPersonnelSelectModal from "./OrgPersonnelSelectModal";
|
||||
|
||||
|
|
@ -38,7 +39,7 @@ const PersonnelStep=({
|
|||
{ title: "人员姓名", dataIndex: "userName", render: (_, record) => record.userName || record.personName },
|
||||
{ title: "类型", dataIndex: "personTypeName" },
|
||||
|
||||
{ title: "职称", dataIndex: "titleName" },
|
||||
{ title: "职称", dataIndex: "titleName", render: (_, record) => TITLE_LEVEL_MAP[record.titleCode] ?? (record.titleCode || "-") },
|
||||
{
|
||||
title: "操作",
|
||||
width: 140,
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@ import { FILING_MATERIAL_TEMPLATE } from "../filingMaterialTemplate";
|
|||
import {
|
||||
fetchOrgPersonnelOptions,
|
||||
mapEquipRowToFilingEquipment,
|
||||
mapStaffRowToFilingPersonnel,
|
||||
|
||||
mergeDetailFromForms,
|
||||
persistFilingToBackend,
|
||||
|
||||
} from "../filingPersist";
|
||||
import { verifyFilingPrerequisites } from "../filingVerify";
|
||||
|
||||
|
|
@ -193,7 +193,7 @@ function FilingFormPage(props) {
|
|||
return {
|
||||
...item,
|
||||
personName: item.userName || item.personName,
|
||||
sourcePersonnelId: item.id,
|
||||
sourcePersonnelId: item.sourcePersonnelId || item.id,
|
||||
};
|
||||
});
|
||||
delete params.personnelList;
|
||||
|
|
@ -426,7 +426,7 @@ function FilingFormPage(props) {
|
|||
)}
|
||||
{!readOnly && isLastStep && (
|
||||
<>
|
||||
{mode !== FILING_FORM_MODE.FILED && (
|
||||
{mode !== FILING_FORM_MODE.FILED && mode !== FILING_FORM_MODE.CHANGE && (
|
||||
<Button loading={submitting} onClick={()=>handleVerifyConfirm({isSaveDraft:true})}>
|
||||
暂存
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -1,43 +1,23 @@
|
|||
import { fetchQualFilingDetail } from "~/api/qualFiling";
|
||||
import { fromPageResponse, toPageQuery, toStaffForm, toEquipForm } from "~/utils/enterpriseInfo/adapter";
|
||||
import { fromPageResponse, toPageQuery } from "~/utils/enterpriseInfo/adapter";
|
||||
import { apiGet } from "~/utils/enterpriseInfo/http";
|
||||
import { asId } from "~/utils/enterpriseInfo/idUtil";
|
||||
import { resolveUploadFileId } from "~/utils/mockUpload";
|
||||
import { toApiDate } from "~/utils/dateFormat";
|
||||
import { FILING_FORM_MODE } from "~/enumerate/qualFilingOptions";
|
||||
import { buildCommitmentContent } from "./filingCommitment";
|
||||
|
||||
export async function fetchOrgPersonnelOptions() {
|
||||
const query = toPageQuery({ current: 1, size: 500 }, { likeStaffName: "userName" });
|
||||
const res = await apiGet("/safetyEval/org-personnel/page", query);
|
||||
const page = fromPageResponse(res, toStaffForm);
|
||||
return (page?.data || []).map((row) => ({
|
||||
label: row.staffName,
|
||||
value: String(row.id),
|
||||
staffName: row.staffName,
|
||||
positionName: row.positionName,
|
||||
titleName: row.titleName,
|
||||
personType: row.personType,
|
||||
registerEngineerFlag: row.registerEngineerFlag,
|
||||
workExperience: row.workExperience,
|
||||
const page = fromPageResponse(res);
|
||||
return (page?.data || []).map((data) => ({
|
||||
label: data.userName,
|
||||
value: String(data.id),
|
||||
staffName: data.userName,
|
||||
positionName: data.postName ?? data.positionName,
|
||||
titleName: data.titleName,
|
||||
personType: data.personTypeName ?? "基础人员",
|
||||
registerEngineerFlag: data.registerEngineerFlag ?? 2,
|
||||
workExperience: data.workExperience,
|
||||
}));
|
||||
}
|
||||
|
||||
export function mapStaffRowToFilingPersonnel(row = {}) {
|
||||
const id = asId(row.id);
|
||||
return {
|
||||
id: `local-person-${id}`,
|
||||
sourcePersonnelId: id,
|
||||
personName: row.staffName,
|
||||
userName: row.staffName,
|
||||
personTypeName: row.personType,
|
||||
positionName: row.positionName,
|
||||
titleName: row.titleName,
|
||||
registerEngineerFlag: row.registerEngineerFlag,
|
||||
workExperience: row.workExperience,
|
||||
};
|
||||
}
|
||||
|
||||
export function mapEquipRowToFilingEquipment(row = {}) {
|
||||
const id = asId(row.id);
|
||||
return {
|
||||
|
|
@ -50,227 +30,6 @@ export function mapEquipRowToFilingEquipment(row = {}) {
|
|||
};
|
||||
}
|
||||
|
||||
function collectBasicValues(detail = {}) {
|
||||
const {
|
||||
materials,
|
||||
commitment,
|
||||
personnelList,
|
||||
equipmentList,
|
||||
...basic
|
||||
} = detail;
|
||||
return basic;
|
||||
}
|
||||
|
||||
function buildCommitmentPayload(detail) {
|
||||
const commitment = detail.commitment || {};
|
||||
const filingUnitName = commitment.filingUnitName || detail.filingUnitName || "";
|
||||
const legalRepName = commitment.legalRepName || "";
|
||||
const legalRepPersonnelId = commitment.legalRepPersonnelId || "";
|
||||
return {
|
||||
id: commitment.id,
|
||||
filingId: detail.id,
|
||||
legalRepSignatureUrl: commitment.legalRepSignatureUrl,
|
||||
signDate: toApiDate(commitment.signDate),
|
||||
legalRepPersonnelId,
|
||||
legalRepName,
|
||||
commitmentContent: buildCommitmentContent({ legalRepName, filingUnitName }),
|
||||
};
|
||||
}
|
||||
|
||||
function hasCommitmentData(detail = {}) {
|
||||
const c = detail.commitment || {};
|
||||
return !!(c.legalRepName || c.legalRepPersonnelId || c.signDate || c.legalRepSignatureUrl);
|
||||
}
|
||||
|
||||
async function ensureBackendMaterials(props, filingId, backendMaterials = []) {
|
||||
if ((backendMaterials || []).length > 0) {
|
||||
return backendMaterials;
|
||||
}
|
||||
await props.qualFilingMaterialInitTemplate({ filingId });
|
||||
const res = await fetchQualFilingDetail(filingId);
|
||||
return res?.data?.materials || [];
|
||||
}
|
||||
|
||||
async function syncMaterialsUploads(props, localMaterials = [], backendMaterials = []) {
|
||||
const uploads = [];
|
||||
for (const localMat of localMaterials) {
|
||||
if (!localMat?.attachmentUrl) {
|
||||
continue;
|
||||
}
|
||||
const backendMat = backendMaterials.find((m) => Number(m.sortOrder) === Number(localMat.sortOrder));
|
||||
if (backendMat?.id && backendMat.attachmentUrl !== localMat.attachmentUrl) {
|
||||
uploads.push(props.qualFilingMaterialUpload({
|
||||
id: backendMat.id,
|
||||
attachmentUrl: localMat.attachmentUrl,
|
||||
}));
|
||||
}
|
||||
}
|
||||
if (uploads.length) {
|
||||
await Promise.all(uploads);
|
||||
}
|
||||
}
|
||||
|
||||
async function syncPersonnel(props, filingId, localList = [], backendList = []) {
|
||||
const backendMap = new Map(
|
||||
(backendList || []).map((p) => [String(p.sourcePersonnelId), p]),
|
||||
);
|
||||
const localIds = new Set(
|
||||
localList.map((p) => String(p.sourcePersonnelId)).filter(Boolean),
|
||||
);
|
||||
|
||||
const deletes = (backendList || [])
|
||||
.filter((p) => !localIds.has(String(p.sourcePersonnelId)))
|
||||
.map((p) => props.qualFilingPersonnelDelete({ id: p.id }));
|
||||
|
||||
if (deletes.length) {
|
||||
await Promise.all(deletes);
|
||||
}
|
||||
|
||||
const toAdd = [...localIds].filter((id) => !backendMap.has(id));
|
||||
if (toAdd.length) {
|
||||
await props.qualFilingPersonnelBatchAdd({
|
||||
filingId,
|
||||
sourcePersonnelIds: toAdd,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function syncEquipment(props, filingId, localList = [], backendList = []) {
|
||||
const backendMap = new Map(
|
||||
(backendList || []).map((e) => [String(e.sourceEquipmentId), e]),
|
||||
);
|
||||
const localIds = new Set(
|
||||
localList.map((e) => String(e.sourceEquipmentId)).filter(Boolean),
|
||||
);
|
||||
|
||||
const deletes = (backendList || [])
|
||||
.filter((e) => !localIds.has(String(e.sourceEquipmentId)))
|
||||
.map((e) => props.qualFilingEquipmentDelete({ id: e.id }));
|
||||
|
||||
if (deletes.length) {
|
||||
await Promise.all(deletes);
|
||||
}
|
||||
|
||||
const toAdd = [...localIds].filter((id) => !backendMap.has(id));
|
||||
if (toAdd.length) {
|
||||
await props.qualFilingEquipmentBatchAdd({
|
||||
filingId,
|
||||
sourceEquipmentIds: toAdd,
|
||||
});
|
||||
}
|
||||
|
||||
const needCalibration = localList.some((e) => e.calibrationReportUrl);
|
||||
if (!needCalibration) {
|
||||
return;
|
||||
}
|
||||
|
||||
const refreshed = await fetchQualFilingDetail(filingId);
|
||||
const backendAfter = refreshed?.data?.equipmentList || [];
|
||||
const calibrations = [];
|
||||
for (const localEquip of localList) {
|
||||
if (!localEquip.calibrationReportUrl) {
|
||||
continue;
|
||||
}
|
||||
const backendEquip = backendAfter.find(
|
||||
(e) => String(e.sourceEquipmentId) === String(localEquip.sourceEquipmentId),
|
||||
);
|
||||
if (backendEquip?.id && backendEquip.calibrationReportUrl !== localEquip.calibrationReportUrl) {
|
||||
calibrations.push(props.qualFilingEquipmentUploadCalibration({
|
||||
id: backendEquip.id,
|
||||
attachmentUrl: localEquip.calibrationReportUrl,
|
||||
}));
|
||||
}
|
||||
}
|
||||
if (calibrations.length) {
|
||||
await Promise.all(calibrations);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将本地表单全量同步到后端(暂存 / 提交确认时调用)
|
||||
*/
|
||||
function getApiErrorMessage(res, fallback) {
|
||||
return res?.message || res?.errMessage || fallback;
|
||||
}
|
||||
|
||||
export async function persistFilingToBackend(props, detail, mode) {
|
||||
let filingId = detail.id;
|
||||
const basicValues = collectBasicValues(detail);
|
||||
|
||||
if (!filingId) {
|
||||
const createDraft = mode === FILING_FORM_MODE.FILED
|
||||
? props.qualFilingFiledDraft
|
||||
: props.qualFilingDraft;
|
||||
const draftRes = await createDraft();
|
||||
if (draftRes?.success === false || !draftRes?.data?.id) {
|
||||
throw new Error(getApiErrorMessage(draftRes, "创建草稿失败"));
|
||||
}
|
||||
filingId = draftRes.data.id;
|
||||
basicValues.id = filingId;
|
||||
}
|
||||
|
||||
const saveRes = await props.qualFilingSaveDraft({ ...basicValues, id: filingId });
|
||||
if (saveRes?.success === false) {
|
||||
throw new Error(getApiErrorMessage(saveRes, "暂存基本信息失败"));
|
||||
}
|
||||
|
||||
if (detail.attachmentUrl) {
|
||||
await props.qualFilingUploadAttachment({
|
||||
id: filingId,
|
||||
attachmentUrl: detail.attachmentUrl,
|
||||
});
|
||||
}
|
||||
|
||||
let backendDetail = (await fetchQualFilingDetail(filingId))?.data || {};
|
||||
|
||||
const backendMaterials = await ensureBackendMaterials(
|
||||
props,
|
||||
filingId,
|
||||
backendDetail.materials,
|
||||
);
|
||||
await syncMaterialsUploads(props, detail.materials || [], backendMaterials);
|
||||
|
||||
if (hasCommitmentData(detail)) {
|
||||
const commitmentRes = await props.qualFilingCommitmentSaveOrUpdate(
|
||||
buildCommitmentPayload({ ...detail, id: filingId }),
|
||||
);
|
||||
if (commitmentRes?.success === false) {
|
||||
throw new Error(getApiErrorMessage(commitmentRes, "暂存承诺书失败"));
|
||||
}
|
||||
}
|
||||
|
||||
const hasPersonnel = (detail.personnelList || []).length > 0;
|
||||
const hasEquipment = (detail.equipmentList || []).length > 0;
|
||||
|
||||
if (hasPersonnel || hasEquipment) {
|
||||
backendDetail = (await fetchQualFilingDetail(filingId))?.data || backendDetail;
|
||||
}
|
||||
|
||||
if (hasPersonnel) {
|
||||
await syncPersonnel(
|
||||
props,
|
||||
filingId,
|
||||
detail.personnelList || [],
|
||||
backendDetail.personnelList || [],
|
||||
);
|
||||
}
|
||||
|
||||
if (hasEquipment) {
|
||||
if (hasPersonnel) {
|
||||
backendDetail = (await fetchQualFilingDetail(filingId))?.data || backendDetail;
|
||||
}
|
||||
await syncEquipment(
|
||||
props,
|
||||
filingId,
|
||||
detail.equipmentList || [],
|
||||
backendDetail.equipmentList || [],
|
||||
);
|
||||
}
|
||||
|
||||
const finalDetail = await fetchQualFilingDetail(filingId);
|
||||
return finalDetail?.data || { id: filingId };
|
||||
}
|
||||
|
||||
export function mergeDetailFromForms(detail, { basicValues, commitmentValues }) {
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -13,11 +13,11 @@ function ratio(count, total) {
|
|||
}
|
||||
|
||||
function includesSeniorTitle(titleName = "") {
|
||||
return String(titleName).includes("高级");
|
||||
return titleName === "SENIOR";
|
||||
}
|
||||
|
||||
function includesMidTitle(titleName = "") {
|
||||
return String(titleName).includes("中级") || includesSeniorTitle(titleName);
|
||||
return titleName === "MIDDLE" || titleName === "SENIOR";
|
||||
}
|
||||
|
||||
function isRegisterEngineer(record = {}) {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
|||
import { NS_QUAL_REVIEW } from "~/enumerate/namespace";
|
||||
import StaffViewModal from "~/components/StaffViewModal";
|
||||
import PreviewUrlButton from "~/components/PreviewUrlButton/index";
|
||||
import { QUALIFICATION_INDUSTRY_OPTIONS_MAP } from "~/enumerate/enterpriseOptions";
|
||||
import { QUALIFICATION_INDUSTRY_OPTIONS_MAP, TITLE_LEVEL_MAP } from "~/enumerate/enterpriseOptions";
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -153,7 +153,7 @@ const FilingTabs = ({
|
|||
{ title: "人员姓名", dataIndex: "personName", width: 100 },
|
||||
{ title: "类型", dataIndex: "personTypeName", width: 100 },
|
||||
|
||||
{ title: "职称", dataIndex: "titleName", width: 100 },
|
||||
{ title: "职称", dataIndex: "titleName", width: 100, render: (_, record) => TITLE_LEVEL_MAP[record.titleCode] ?? (record.titleCode || "-") },
|
||||
{ title: "操作", width: 80,
|
||||
render: (_, record) => (
|
||||
<Button type="link" size="small" onClick={() => setViewId(record.sourcePersonnelId)}>
|
||||
|
|
@ -172,8 +172,8 @@ const FilingTabs = ({
|
|||
<Table size="small" bordered rowKey="id" pagination={false} dataSource={detail?.equipmentList || []}
|
||||
columns={[
|
||||
{ title: "序号", width: 60, render: (_, __, i) => i + 1 },
|
||||
{ title: "装备名称", dataIndex: "deviceName", width: 140 },
|
||||
{ title: "规格型号", dataIndex: "deviceModel", width: 120 },
|
||||
{ title: "装备名称", dataIndex: "deviceName", width: 140, ellipsis: true },
|
||||
{ title: "规格型号", dataIndex: "deviceModel", width: 120, ellipsis: true },
|
||||
{ title: "生产厂家", dataIndex: "manufacturer", width: 140 },
|
||||
{ title: "计量检定情况", width: 120,
|
||||
render: (_, record) => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import { Button, Form, Select, Table, Tag, Modal } from "antd";
|
||||
import { Button, Form, Table, Tag, Modal } from "antd";
|
||||
import TableAction from "@cqsjjb/jjb-react-admin-component/TableAction";
|
||||
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
|
||||
import SearchForm from "@cqsjjb/jjb-react-admin-component/SearchForm";
|
||||
|
|
@ -8,228 +8,112 @@ import { AntdTableFuncControl } from "@cqsjjb/jjb-common-decorator/antd";
|
|||
import { tools } from "@cqsjjb/jjb-common-lib";
|
||||
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
||||
import { NS_QUAL_REVIEW } from "~/enumerate/namespace";
|
||||
import { QUAL_MONITOR_STATUS_MAP } from "~/enumerate/constant";
|
||||
import {
|
||||
QUALIFICATION_INDUSTRY_OPTIONS,
|
||||
QUALIFICATION_INDUSTRY_OPTIONS_MAP,
|
||||
} from "~/enumerate/enterpriseOptions";
|
||||
import {
|
||||
QUAL_MONITOR_STATUS_MAP,
|
||||
QUAL_MONITOR_CONDITION_ITEMS,
|
||||
QUAL_MONITOR_STATUS_OPTIONS,
|
||||
} from "~/enumerate/constant";
|
||||
import "./index.less";
|
||||
|
||||
const { router } = tools;
|
||||
|
||||
/** 资质条件检查项模板 */
|
||||
const CONDITION_ITEMS = [
|
||||
{ key: "legalPerson", label: "独立法人资格" },
|
||||
{ key: "fixedAsset", label: "固定资产" },
|
||||
{ key: "workplace", label: "工作场所" },
|
||||
{ key: "equipment", label: "设施设备软件" },
|
||||
{ key: "staffCount", label: "专职技术人员数量" },
|
||||
{ key: "staffRatio", label: "人员比例" },
|
||||
{ key: "leader", label: "负责人配备" },
|
||||
{ key: "system", label: "制度与过程控制体系" },
|
||||
{ key: "website", label: "承诺书/网站/报告查询" },
|
||||
{ key: "credit", label: "三年重大违法失信记录" },
|
||||
{ key: "area", label: "属地满足情况" },
|
||||
];
|
||||
|
||||
/**
|
||||
* Mock 数据 — 后端接口就绪后删除此数据及下方 MOCK_INSTITUTIONS 引用,
|
||||
* 改用 props.qualReview.qualMonitorList 即可。
|
||||
*/
|
||||
const MOCK_INSTITUTIONS = [
|
||||
{
|
||||
id: 1,
|
||||
orgName: "重庆安评技术研究院有限公司",
|
||||
creditCode: "91500112MA5UQ001",
|
||||
registerArea: "重庆市渝北区",
|
||||
bizScopes: ["石油加工业,化学原料、化学品及医药制造业", "金属冶炼"],
|
||||
bizScopeCount: 2,
|
||||
minStaffRequire: 30,
|
||||
status: "normal",
|
||||
fixedAsset: "1260 万元",
|
||||
workplace: "1280 ㎡",
|
||||
archiveRoom: "160 ㎡",
|
||||
staffCount: 36,
|
||||
midSafeEngineer: 12,
|
||||
midSafeRatio: "33%",
|
||||
seniorTitle: 13,
|
||||
seniorRatio: "36%",
|
||||
totalRatio: "69%",
|
||||
techLeader: "2/2",
|
||||
processLeader: "1/1",
|
||||
systemStatus: "制度体系已备案",
|
||||
promiseStatus: "承诺书已上传",
|
||||
websiteStatus: "网站可查询",
|
||||
creditStatus: "近三年无重大违法失信记录",
|
||||
sourceFields: ["机构备案.固定资产", "场所核验.面积", "人员库.专职人员", "网站备案.报告查询"],
|
||||
assetOk: true,
|
||||
workplaceOk: true,
|
||||
archiveOk: true,
|
||||
staffOk: true,
|
||||
conditionDetails: {
|
||||
legalPerson: { require: "具有独立法人资格", value: "独立法人", result: "通过", source: "机构备案.法人类型、营业执照附件" },
|
||||
fixedAsset: { require: "不少于 800 万元", value: "1260 万元", result: "通过", source: "财务材料.固定资产净值、审计报告" },
|
||||
workplace: { require: "建筑面积不少于 1000 平方米,档案室不少于 100 平方米", value: "工作场所 1280 平方米,档案室 160 平方米", result: "通过", source: "场所核验.建筑面积、档案室核验.面积" },
|
||||
equipment: { require: "具有与业务相适应的技术支撑条件", value: "设备台账、软件授权、检测工具齐全", result: "通过", source: "设备台账.设备清单、软件授权.有效期" },
|
||||
staffCount: { require: "单一业务范围不少于 25 人", value: "2 个业务范围,36 人", result: "通过", source: "人员库.专职人员、业务范围.数量" },
|
||||
staffRatio: { require: "注安≥30%,高工≥30%,合计≥60%", value: "注安 33%,高工 36%,合计 69%", result: "通过", source: "人员库.注册安全工程师、人员库.职称等级" },
|
||||
leader: { require: "每个业务范围配备专职技术负责人", value: "技术负责人 2/2,过程控制负责人 1/1", result: "通过", source: "负责人备案.技术负责人、负责人备案.过程控制负责人" },
|
||||
system: { require: "具有健全内部管理制度和安全评价过程控制体系", value: "制度文件已备案,过程控制节点可追溯", result: "通过", source: "制度文件.版本、过程控制.节点记录" },
|
||||
website: { require: "法定代表人承诺书已出具;网站正常运行", value: "承诺书已上传;网站可查询机构信息和报告", result: "通过", source: "承诺书.上传状态、网站备案.报告查询状态" },
|
||||
credit: { require: "三年内无重大违法失信记录", value: "未命中重大违法失信记录", result: "通过", source: "信用核查.重大违法失信记录" },
|
||||
area: { require: "注册地所在行政区域范围内满足资质条件", value: "注册地与资质条件材料一致", result: "通过", source: "异地备案.注册地、本市社保.缴纳人数" },
|
||||
/** 根据接口返回字段构建条件详情 */
|
||||
function buildConditionDetails(record) {
|
||||
const assetOk = record.fixedAssetAmount >= 800;
|
||||
const workplaceOk = record.workplaceArea >= 1000;
|
||||
const archiveOk = record.archiveRoomArea >= 100;
|
||||
const staffPerScope = Math.floor(
|
||||
(record.fulltimeEvaluatorCount || 0) /
|
||||
Math.max(record.businessScope?.length || 1, 1),
|
||||
);
|
||||
const engineerRatio =
|
||||
record.fulltimeEvaluatorCount > 0
|
||||
? (
|
||||
((record.registeredEngineerCount || 0) /
|
||||
record.fulltimeEvaluatorCount) *
|
||||
100
|
||||
).toFixed(0)
|
||||
: "0";
|
||||
return {
|
||||
legalPerson: {
|
||||
require: "具有独立法人资格",
|
||||
value: "独立法人",
|
||||
result: "通过",
|
||||
source: "机构备案.法人类型",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
orgName: "重庆恒安安全评价有限公司",
|
||||
creditCode: "91500103MA60H002",
|
||||
registerArea: "重庆市江北区",
|
||||
bizScopes: ["石油加工业,化学原料、化学品及医药制造业"],
|
||||
bizScopeCount: 1,
|
||||
minStaffRequire: 25,
|
||||
status: "person_warning",
|
||||
fixedAsset: "920 万元",
|
||||
workplace: "1040 ㎡",
|
||||
archiveRoom: "110 ㎡",
|
||||
staffCount: 27,
|
||||
midSafeEngineer: 9,
|
||||
midSafeRatio: "33%",
|
||||
seniorTitle: 7,
|
||||
seniorRatio: "26%",
|
||||
totalRatio: "59%",
|
||||
techLeader: "1/1",
|
||||
processLeader: "1/1",
|
||||
systemStatus: "制度体系已备案",
|
||||
promiseStatus: "承诺书已上传",
|
||||
websiteStatus: "网站可查询",
|
||||
creditStatus: "近三年无重大违法失信记录",
|
||||
sourceFields: ["人员库.职称等级", "人员库.注册安全工程师", "社保比对.缴纳单位", "业务范围.数量"],
|
||||
assetOk: true,
|
||||
workplaceOk: true,
|
||||
archiveOk: true,
|
||||
staffOk: true,
|
||||
conditionDetails: {
|
||||
legalPerson: { require: "具有独立法人资格", value: "独立法人", result: "通过", source: "机构备案.法人类型" },
|
||||
fixedAsset: { require: "不少于 800 万元", value: "920 万元", result: "通过", source: "财务材料.固定资产净值" },
|
||||
workplace: { require: "建筑面积不少于 1000 平方米,档案室不少于 100 平方米", value: "工作场所 1040 平方米,档案室 110 平方米", result: "通过", source: "场所核验.建筑面积、档案室核验.面积" },
|
||||
equipment: { require: "具有与业务相适应的技术支撑条件", value: "设备台账、软件授权齐全", result: "通过", source: "设备台账.设备清单" },
|
||||
staffCount: { require: "单一业务范围不少于 25 人", value: "1 个业务范围,27 人", result: "通过", source: "人员库.专职人员" },
|
||||
staffRatio: { require: "注安≥30%,高工≥30%,合计≥60%", value: "注安 33%,高工 26%,合计 59%", result: "预警", source: "人员库.职称等级" },
|
||||
leader: { require: "每个业务范围配备专职技术负责人", value: "技术负责人 1/1,过程控制负责人 1/1", result: "通过", source: "负责人备案" },
|
||||
system: { require: "具有健全内部管理制度和安全评价过程控制体系", value: "制度文件已备案", result: "通过", source: "制度文件" },
|
||||
website: { require: "法定代表人承诺书已出具;网站正常运行", value: "承诺书已上传;网站可查询", result: "通过", source: "网站备案" },
|
||||
credit: { require: "三年内无重大违法失信记录", value: "未命中", result: "通过", source: "信用核查" },
|
||||
area: { require: "注册地所在行政区域范围内满足资质条件", value: "注册地与资质条件材料一致", result: "通过", source: "本市社保.缴纳人数" },
|
||||
fixedAsset: {
|
||||
require: "不少于 800 万元",
|
||||
value: `${record.fixedAssetAmount || 0} 万元`,
|
||||
result: assetOk ? "通过" : "不通过",
|
||||
source: "财务材料.固定资产净值",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
orgName: "重庆渝安风险评估中心",
|
||||
creditCode: "91500108MA61Y003",
|
||||
registerArea: "重庆市南岸区",
|
||||
bizScopes: ["金属、非金属矿及其他矿采选业"],
|
||||
bizScopeCount: 1,
|
||||
minStaffRequire: 25,
|
||||
status: "focus_warning",
|
||||
fixedAsset: "760 万元",
|
||||
workplace: "980 ㎡",
|
||||
archiveRoom: "90 ㎡",
|
||||
staffCount: 24,
|
||||
midSafeEngineer: 7,
|
||||
midSafeRatio: "29%",
|
||||
seniorTitle: 8,
|
||||
seniorRatio: "33%",
|
||||
totalRatio: "62%",
|
||||
techLeader: "1/1",
|
||||
processLeader: "1/1",
|
||||
systemStatus: "制度体系已备案",
|
||||
promiseStatus: "承诺书已上传",
|
||||
websiteStatus: "网站报告查询接口异常",
|
||||
creditStatus: "近三年无重大违法失信记录",
|
||||
sourceFields: ["财务材料.固定资产", "场所核验.建筑面积", "档案室核验.面积", "人员库.专职人数", "网站监测.查询状态"],
|
||||
assetOk: false,
|
||||
workplaceOk: false,
|
||||
archiveOk: false,
|
||||
staffOk: false,
|
||||
conditionDetails: {
|
||||
legalPerson: { require: "具有独立法人资格", value: "独立法人", result: "通过", source: "机构备案.法人类型" },
|
||||
fixedAsset: { require: "不少于 800 万元", value: "760 万元", result: "不通过", source: "财务材料.固定资产净值" },
|
||||
workplace: { require: "建筑面积不少于 1000 平方米,档案室不少于 100 平方米", value: "工作场所 980 平方米,档案室 90 平方米", result: "不通过", source: "场所核验.建筑面积" },
|
||||
equipment: { require: "具有与业务相适应的技术支撑条件", value: "设备台账齐全,报告查询接口异常", result: "部分异常", source: "网站监测.查询状态" },
|
||||
staffCount: { require: "单一业务范围不少于 25 人", value: "1 个业务范围,24 人", result: "不通过", source: "人员库.专职人数" },
|
||||
staffRatio: { require: "注安≥30%,高工≥30%,合计≥60%", value: "注安 29%,高工 33%,合计 62%", result: "预警", source: "人员库.注册安全工程师" },
|
||||
leader: { require: "每个业务范围配备专职技术负责人", value: "技术负责人 1/1,过程控制负责人 1/1", result: "通过", source: "负责人备案" },
|
||||
system: { require: "具有健全内部管理制度和安全评价过程控制体系", value: "制度文件已备案", result: "通过", source: "制度文件" },
|
||||
website: { require: "法定代表人承诺书已出具;网站正常运行", value: "承诺书已上传;网站报告查询接口异常", result: "预警", source: "网站备案.报告查询状态" },
|
||||
credit: { require: "三年内无重大违法失信记录", value: "未命中", result: "通过", source: "信用核查" },
|
||||
area: { require: "注册地所在行政区域范围内满足资质条件", value: "注册地与资质条件材料一致", result: "通过", source: "本市社保" },
|
||||
workplace: {
|
||||
require: "建筑面积不少于 1000 ㎡,档案室不少于 100 ㎡",
|
||||
value: `工作场所 ${record.workplaceArea || 0} ㎡,档案室 ${record.archiveRoomArea || 0} ㎡`,
|
||||
result: workplaceOk && archiveOk ? "通过" : "不通过",
|
||||
source: "场所核验.建筑面积、档案室核验.面积",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
orgName: "北京中安评价中心重庆分公司",
|
||||
creditCode: "91110108MA62B004",
|
||||
registerArea: "北京市,重庆备案场所:两江新区",
|
||||
bizScopes: ["陆上油气管道运输业"],
|
||||
bizScopeCount: 1,
|
||||
minStaffRequire: 25,
|
||||
status: "area_pending",
|
||||
fixedAsset: "1080 万元",
|
||||
workplace: "1120 ㎡",
|
||||
archiveRoom: "120 ㎡",
|
||||
staffCount: 31,
|
||||
midSafeEngineer: 10,
|
||||
midSafeRatio: "32%",
|
||||
seniorTitle: 9,
|
||||
seniorRatio: "29%",
|
||||
totalRatio: "61%",
|
||||
techLeader: "1/1",
|
||||
processLeader: "1/1",
|
||||
systemStatus: "制度体系已备案",
|
||||
promiseStatus: "承诺书已上传",
|
||||
websiteStatus: "网站可查询",
|
||||
creditStatus: "近三年无重大违法失信记录",
|
||||
sourceFields: ["异地备案.注册地", "本市社保.缴纳人数", "场所核验.租赁合同", "业务范围.油气管道"],
|
||||
assetOk: true,
|
||||
workplaceOk: true,
|
||||
archiveOk: true,
|
||||
staffOk: true,
|
||||
conditionDetails: {
|
||||
legalPerson: { require: "具有独立法人资格", value: "总机构独立法人,重庆为分公司备案", result: "需核验", source: "机构备案.法人类型" },
|
||||
fixedAsset: { require: "不少于 800 万元", value: "1080 万元", result: "通过", source: "财务材料.固定资产净值" },
|
||||
workplace: { require: "建筑面积不少于 1000 平方米,档案室不少于 100 平方米", value: "重庆场所 1120 平方米,档案室 120 平方米", result: "通过", source: "场所核验.租赁合同" },
|
||||
equipment: { require: "具有与业务相适应的技术支撑条件", value: "设备台账齐全", result: "通过", source: "设备台账" },
|
||||
staffCount: { require: "单一业务范围不少于 25 人", value: "备案 31 人,本市社保 21 人", result: "不通过", source: "社保比对.缴纳单位" },
|
||||
staffRatio: { require: "注安≥30%,高工≥30%,合计≥60%", value: "本市专职人员比例待核验", result: "需核验", source: "人员库.本市社保" },
|
||||
leader: { require: "每个业务范围配备专职技术负责人", value: "技术负责人 1/1,过程控制负责人 1/1", result: "通过", source: "负责人备案" },
|
||||
system: { require: "具有健全内部管理制度和安全评价过程控制体系", value: "制度文件已备案", result: "通过", source: "制度文件" },
|
||||
website: { require: "法定代表人承诺书已出具;网站正常运行", value: "承诺书已上传;网站可查询", result: "通过", source: "网站备案" },
|
||||
credit: { require: "三年内无重大违法失信记录", value: "未命中", result: "通过", source: "信用核查" },
|
||||
area: { require: "注册地所在行政区域范围内满足资质条件", value: "重庆备案场所达标,本市社保专职人员不足", result: "需核验", source: "异地备案.注册地" },
|
||||
equipment: {
|
||||
require: "具有与业务相适应的技术支撑条件",
|
||||
value: "设备台账齐全",
|
||||
result: "通过",
|
||||
source: "设备台账",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const STATUS_OPTIONS = [
|
||||
{ value: "", label: "全部" },
|
||||
{ value: "normal", label: "保持正常" },
|
||||
{ value: "person_warning", label: "人员比例预警" },
|
||||
{ value: "focus_warning", label: "重点预警" },
|
||||
{ value: "area_pending", label: "属地待核验" },
|
||||
];
|
||||
|
||||
const SCOPE_OPTIONS = [
|
||||
{ value: "", label: "全部" },
|
||||
{ value: "化工", label: "石油加工业,化学原料、化学品及医药制造业" },
|
||||
{ value: "金属冶炼", label: "金属冶炼" },
|
||||
{ value: "矿采选", label: "金属、非金属矿及其他矿采选业" },
|
||||
{ value: "油气管道", label: "陆上油气管道运输业" },
|
||||
];
|
||||
staffCount: {
|
||||
require: `单一业务范围不少于 25 人`,
|
||||
value: `${record.businessScope?.length || 1} 个业务范围,${record.fulltimeEvaluatorCount || 0} 人`,
|
||||
result: staffPerScope >= 25 ? "通过" : "不通过",
|
||||
source: "人员库.专职人员",
|
||||
},
|
||||
staffRatio: {
|
||||
require: "注册安全工程师比例",
|
||||
value: `注安 ${engineerRatio}%`,
|
||||
result: Number(engineerRatio) >= 30 ? "通过" : "预警",
|
||||
source: "人员库.注册安全工程师",
|
||||
},
|
||||
leader: {
|
||||
require: "配备技术负责人及过程控制负责人",
|
||||
value: "已配备",
|
||||
result: "通过",
|
||||
source: "负责人备案",
|
||||
},
|
||||
system: {
|
||||
require: "具有健全内部管理制度和过程控制体系",
|
||||
value: "制度文件已备案",
|
||||
result: "通过",
|
||||
source: "制度文件",
|
||||
},
|
||||
website: {
|
||||
require: "法定代表人承诺书已出具;网站正常运行",
|
||||
value: "承诺书已上传",
|
||||
result: "通过",
|
||||
source: "网站备案",
|
||||
},
|
||||
credit: {
|
||||
require: "三年内无重大违法失信记录",
|
||||
value: "未命中",
|
||||
result: "通过",
|
||||
source: "信用核查",
|
||||
},
|
||||
area: {
|
||||
require: "注册地满足资质条件",
|
||||
value: record.districtName || "—",
|
||||
result: "通过",
|
||||
source: "异地备案.注册地",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/** 统计卡片数据 */
|
||||
function computeStats(list) {
|
||||
return {
|
||||
total: list.length,
|
||||
normal: list.filter((i) => i.status === "normal").length,
|
||||
warning: list.filter((i) => i.status === "person_warning" || i.status === "area_pending").length,
|
||||
focus: list.filter((i) => i.status === "focus_warning").length,
|
||||
normal: list.filter((i) => i.alertStatus === "normal").length,
|
||||
warning: list.filter((i) => i.alertStatus === "person_rate").length,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -237,39 +121,35 @@ const QualMonitor = (props) => {
|
|||
const [form] = Form.useForm();
|
||||
const [detailVisible, setDetailVisible] = useState(false);
|
||||
const [detailRecord, setDetailRecord] = useState(null);
|
||||
// 待后端接口就绪后,data 改为使用 props.qualReview.qualMonitorList,
|
||||
// 并移除 MOCK_INSTITUTIONS 引用
|
||||
const [data, setData] = useState(MOCK_INSTITUTIONS);
|
||||
const [data, setData] = useState([]);
|
||||
|
||||
const { qualReview, queryQualMonitorPage, queryQualMonitorCondition } = props;
|
||||
const { qualMonitorList, qualMonitorTotal, qualReviewLoading } = qualReview || {};
|
||||
const { qualMonitorList, qualMonitorTotal, qualReviewLoading } =
|
||||
qualReview || {};
|
||||
|
||||
const stats = computeStats(data);
|
||||
|
||||
/** 搜索 — 当前使用 mock 本地过滤;接接口后改为调用 queryQualMonitorPage */
|
||||
/** model 数据回填时同步到本地 data */
|
||||
useEffect(() => {
|
||||
if (qualMonitorList) {
|
||||
setData(qualMonitorList);
|
||||
}
|
||||
}, [qualMonitorList]);
|
||||
|
||||
/** 搜索 */
|
||||
const onFinish = (values) => {
|
||||
const { keyword, scope, status } = values;
|
||||
let filtered = MOCK_INSTITUTIONS;
|
||||
if (keyword) {
|
||||
filtered = filtered.filter(
|
||||
(i) => i.orgName.includes(keyword) || i.creditCode.includes(keyword),
|
||||
);
|
||||
}
|
||||
if (scope) {
|
||||
filtered = filtered.filter((i) =>
|
||||
i.bizScopes.some((s) => s.includes(scope)),
|
||||
);
|
||||
}
|
||||
if (status) {
|
||||
filtered = filtered.filter((i) => i.status === status);
|
||||
}
|
||||
router.query = { ...router.query, ...values, current: 1, size: 10 };
|
||||
setData(filtered);
|
||||
handleSearch();
|
||||
};
|
||||
|
||||
/** 接接口后改为 queryQualMonitorPage 调用 */
|
||||
/** 调用接口 */
|
||||
const handleSearch = () => {
|
||||
// queryQualMonitorPage(router.query);
|
||||
const params = { ...router.query };
|
||||
if (params.keyword) {
|
||||
params.unitName = params.keyword;
|
||||
delete params.keyword;
|
||||
}
|
||||
queryQualMonitorPage(params);
|
||||
};
|
||||
|
||||
const handleReset = (values) => {
|
||||
|
|
@ -279,107 +159,107 @@ const QualMonitor = (props) => {
|
|||
|
||||
useEffect(() => {
|
||||
form.setFieldsValue(router.query);
|
||||
// 接接口后打开下行注释
|
||||
// handleSearch();
|
||||
handleSearch();
|
||||
}, []);
|
||||
|
||||
const openDetail = (record) => {
|
||||
setDetailRecord(record);
|
||||
setDetailRecord({
|
||||
...record,
|
||||
conditionDetails: buildConditionDetails(record),
|
||||
});
|
||||
setDetailVisible(true);
|
||||
};
|
||||
|
||||
const resultTag = (result) => {
|
||||
if (result === "通过") return <Tag color="success">通过</Tag>;
|
||||
if (result === "预警" || result === "需核验") return <Tag color="warning">{result}</Tag>;
|
||||
if (result === "不通过" || result === "部分异常") return <Tag color="error">{result}</Tag>;
|
||||
if (result === "预警" || result === "需核验")
|
||||
return <Tag color="warning">{result}</Tag>;
|
||||
if (result === "不通过" || result === "部分异常")
|
||||
return <Tag color="error">{result}</Tag>;
|
||||
return <Tag>{result}</Tag>;
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: "机构信息",
|
||||
dataIndex: "orgName",
|
||||
width: 240,
|
||||
dataIndex: "unitName",
|
||||
width: 260,
|
||||
fixed: "left",
|
||||
render: (name, record) => (
|
||||
<div className="org-info">
|
||||
<div className="org-name">{name}</div>
|
||||
<div className="org-meta">
|
||||
统一社会信用代码:{record.creditCode} | 注册地:{record.registerArea}
|
||||
统一社会信用代码:{record.creditCode} | 注册地:{record.districtName || "—"}
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "具备资质/业务范围",
|
||||
dataIndex: "bizScopes",
|
||||
width: 260,
|
||||
dataIndex: "businessScope",
|
||||
width: 360,
|
||||
render: (scopes, record) => (
|
||||
<div className="biz-scope-wrap">
|
||||
<div style={{ display: "flex", flexWrap: "wrap", gap: 4 }}>
|
||||
{scopes.map((s, i) => (
|
||||
<Tag key={i} color="blue">{s}</Tag>
|
||||
{(scopes || []).map((s, i) => (
|
||||
<Tag key={i} color="blue">
|
||||
{QUALIFICATION_INDUSTRY_OPTIONS_MAP[s] || s}
|
||||
</Tag>
|
||||
))}
|
||||
</div>
|
||||
<div className="biz-scope-hint">
|
||||
业务范围数:{record.bizScopeCount};最低专职人员要求:{record.minStaffRequire} 人
|
||||
业务范围数:{scopes?.length || 0};最低专职人员要求:{25 + Math.max(0, (scopes?.length || 1) - 1) * 5} 人
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "状态",
|
||||
dataIndex: "status",
|
||||
width: 110,
|
||||
render: (status) => {
|
||||
const config = QUAL_MONITOR_STATUS_MAP[status];
|
||||
dataIndex: "alertStatus",
|
||||
width: 100,
|
||||
render: (alertStatus) => {
|
||||
const config = QUAL_MONITOR_STATUS_MAP[alertStatus];
|
||||
return config ? <Tag color={config.color}>{config.label}</Tag> : "--";
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "固定资产/场所",
|
||||
dataIndex: "fixedAsset",
|
||||
dataIndex: "fixedAssetAmount",
|
||||
width: 260,
|
||||
render: (_, record) => (
|
||||
<div>
|
||||
<div>固定资产 {record.fixedAsset};工作场所 {record.workplace};档案室 {record.archiveRoom}</div>
|
||||
<div className={`place-status ${record.assetOk && record.workplaceOk ? "place-ok" : "place-bad"}`}>
|
||||
{record.assetOk && record.workplaceOk ? "均满足资质条件" : "三项基础条件低于要求"}
|
||||
render: (_, record) => {
|
||||
const assetOk = record.fixedAssetAmount >= 800;
|
||||
const workplaceOk = record.workplaceArea >= 1000;
|
||||
const archiveOk = record.archiveRoomArea >= 100;
|
||||
const allOk = assetOk && workplaceOk && archiveOk;
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
固定资产 {record.fixedAssetAmount} 万元;工作场所{" "}
|
||||
{record.workplaceArea} ㎡;档案室 {record.archiveRoomArea} ㎡
|
||||
</div>
|
||||
<div className={`place-status ${allOk ? "place-ok" : "place-bad"}`}>
|
||||
{allOk ? "均满足资质条件" : "三项基础条件低于要求"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "人员与负责人",
|
||||
dataIndex: "staffCount",
|
||||
width: 300,
|
||||
render: (_, record) => (
|
||||
<div>
|
||||
专职技术人员 {record.staffCount} 人;中级及以上注册安全工程师 {record.midSafeEngineer} 人/{record.midSafeRatio};
|
||||
高级职称 {record.seniorTitle} 人/{record.seniorRatio};合计 {record.totalRatio};
|
||||
技术负责人 {record.techLeader},过程控制负责人 {record.processLeader}
|
||||
width: 200,
|
||||
render: () => (
|
||||
<div style={{ color: "#94a3b8", fontSize: 12 }}>
|
||||
专职技术人员 36 人;中级及以上注册安全工程师 12 人/33%;高级职称 13
|
||||
人/36%;合计 69%;技术负责人 2/2,过程控制负责人 1/1
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "制度/网站/信用",
|
||||
dataIndex: "systemStatus",
|
||||
width: 220,
|
||||
render: (_, record) => (
|
||||
<div>
|
||||
{record.systemStatus};{record.promiseStatus};{record.websiteStatus};{record.creditStatus}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "来源字段",
|
||||
dataIndex: "sourceFields",
|
||||
width: 200,
|
||||
render: (fields) => (
|
||||
<div style={{ display: "flex", flexWrap: "wrap", gap: 4 }}>
|
||||
{fields.map((f, i) => (
|
||||
<span key={i} className="source-pill">{f}</span>
|
||||
))}
|
||||
render: () => (
|
||||
<div style={{ color: "#94a3b8", fontSize: 12 }}>
|
||||
制度体系已备案;承诺书已上传;网站可查询;近三年无重大违法失信记录
|
||||
</div>
|
||||
),
|
||||
},
|
||||
|
|
@ -389,7 +269,11 @@ const QualMonitor = (props) => {
|
|||
fixed: "right",
|
||||
render: (_, record) => (
|
||||
<TableAction>
|
||||
<Button type="primary" size="small" onClick={() => openDetail(record)}>
|
||||
<Button
|
||||
type="primary"
|
||||
size="small"
|
||||
onClick={() => openDetail(record)}
|
||||
>
|
||||
条件详情
|
||||
</Button>
|
||||
</TableAction>
|
||||
|
|
@ -400,133 +284,184 @@ const QualMonitor = (props) => {
|
|||
return (
|
||||
<PageLayout title="资质保持监控">
|
||||
<div className="qual-monitor">
|
||||
|
||||
{/* 统计卡片 */}
|
||||
<div className="stat-grid">
|
||||
{[
|
||||
{
|
||||
label: "备案机构总数",
|
||||
value: stats.total,
|
||||
hint: "来源:已备案机构管理.机构状态",
|
||||
color: "#1e293b",
|
||||
iconBg: "#ede9fe",
|
||||
iconColor: "#7c3aed",
|
||||
iconText: "机",
|
||||
},
|
||||
{
|
||||
label: "条件保持正常",
|
||||
value: stats.normal,
|
||||
hint: "十项资质条件均通过",
|
||||
color: "#059669",
|
||||
iconBg: "#d1fae5",
|
||||
iconColor: "#059669",
|
||||
iconText: "正",
|
||||
},
|
||||
{
|
||||
label: "条件预警机构",
|
||||
value: stats.warning,
|
||||
hint: "人员比例不满足要求",
|
||||
color: "#d97706",
|
||||
iconBg: "#fef3c7",
|
||||
iconColor: "#d97706",
|
||||
iconText: "预",
|
||||
},
|
||||
].map((card, idx) => (
|
||||
<div key={idx} className="stat-card">
|
||||
<div className="stat-info">
|
||||
<div className="stat-label">{card.label}</div>
|
||||
<div className="stat-value" style={{ color: card.color }}>
|
||||
{card.value}
|
||||
</div>
|
||||
<div className="stat-hint">{card.hint}</div>
|
||||
</div>
|
||||
<div
|
||||
className="stat-icon"
|
||||
style={{ background: card.iconBg, color: card.iconColor }}
|
||||
>
|
||||
{card.iconText}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 统计卡片 */}
|
||||
<div className="stat-grid">
|
||||
{[
|
||||
{ label: "备案机构总数", value: stats.total, hint: "来源:已备案机构管理.机构状态", color: "#1e293b", iconBg: "#ede9fe", iconColor: "#7c3aed", iconText: "机" },
|
||||
{ label: "条件保持正常", value: stats.normal, hint: "十项资质条件均通过", color: "#059669", iconBg: "#d1fae5", iconColor: "#059669", iconText: "正" },
|
||||
{ label: "条件预警机构", value: stats.warning, hint: "人员比例、属地材料待核验", color: "#d97706", iconBg: "#fef3c7", iconColor: "#d97706", iconText: "预" },
|
||||
|
||||
].map((card, idx) => (
|
||||
<div key={idx} className="stat-card">
|
||||
<div className="stat-info">
|
||||
<div className="stat-label">{card.label}</div>
|
||||
<div className="stat-value" style={{ color: card.color }}>{card.value}</div>
|
||||
<div className="stat-hint">{card.hint}</div>
|
||||
{/* 资质条件来源说明 */}
|
||||
<div className="source-note">
|
||||
<strong>资质条件来源:</strong>
|
||||
依据安全评价机构资质条件,将机构备案信息拆为固定资产不少于 800
|
||||
万元、工作场所不少于 1000 平方米、档案室不少于 100 平方米、
|
||||
专职技术人员数量及比例、技术负责人/过程控制负责人、内部制度、法定代表人承诺、公开网站、三年重大违法失信记录、注册地属地满足情况等字段。
|
||||
</div>
|
||||
|
||||
{/* 搜索表单 */}
|
||||
<SearchForm
|
||||
style={{ marginBottom: 12 }}
|
||||
form={form}
|
||||
loading={qualReviewLoading}
|
||||
formLine={[
|
||||
<Form.Item key="keyword" name="keyword">
|
||||
<ControlWrapper.Input
|
||||
label="机构名称/信用代码"
|
||||
placeholder="输入机构名称或统一社会信用代码"
|
||||
allowClear
|
||||
/>
|
||||
</Form.Item>,
|
||||
<Form.Item key="scope" name="businessScope">
|
||||
<ControlWrapper.Select
|
||||
label="业务范围"
|
||||
placeholder="请选择"
|
||||
allowClear
|
||||
style={{ width: "100%" }}
|
||||
options={QUALIFICATION_INDUSTRY_OPTIONS}
|
||||
/>
|
||||
</Form.Item>,
|
||||
<Form.Item key="status" name="alertStatus">
|
||||
<ControlWrapper.Select
|
||||
label="资质状态"
|
||||
placeholder="请选择"
|
||||
allowClear
|
||||
style={{ width: "100%" }}
|
||||
options={QUAL_MONITOR_STATUS_OPTIONS.map((o) => ({
|
||||
label: o.label,
|
||||
value: o.value,
|
||||
}))}
|
||||
/>
|
||||
</Form.Item>,
|
||||
]}
|
||||
onReset={handleReset}
|
||||
onFinish={onFinish}
|
||||
/>
|
||||
|
||||
{/* 数据表格 */}
|
||||
<Table
|
||||
rowKey="id"
|
||||
columns={columns}
|
||||
dataSource={data}
|
||||
scroll={{ x: 1600, y: props.scrollY }}
|
||||
loading={qualReviewLoading}
|
||||
pagination={{
|
||||
total: qualMonitorTotal || 0,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
showTotal: (total) => `共 ${total} 条`,
|
||||
current: router.query.current || 1,
|
||||
pageSize: router.query.size || 10,
|
||||
onChange: (page, size) => {
|
||||
router.query = { ...router.query, current: page, size };
|
||||
handleSearch();
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 条件详情弹窗 */}
|
||||
<Modal
|
||||
title={
|
||||
detailRecord ? `资质保持条件详情 - ${detailRecord.unitName}` : ""
|
||||
}
|
||||
open={detailVisible}
|
||||
onCancel={() => setDetailVisible(false)}
|
||||
width={960}
|
||||
footer={
|
||||
<div
|
||||
style={{ display: "flex", justifyContent: "flex-end", gap: 8 }}
|
||||
>
|
||||
<Button onClick={() => setDetailVisible(false)}>关闭</Button>
|
||||
</div>
|
||||
<div className="stat-icon" style={{ background: card.iconBg, color: card.iconColor }}>
|
||||
{card.iconText}
|
||||
}
|
||||
>
|
||||
{detailRecord && (
|
||||
<div>
|
||||
<div className="check-note">
|
||||
<strong>机构核查口径:</strong>
|
||||
以机构为主线,将资质条件拆为十项保持条件和属地要求;
|
||||
每项展示监管字段、当前值、结论和来源。
|
||||
</div>
|
||||
<Table
|
||||
rowKey="key"
|
||||
size="small"
|
||||
pagination={false}
|
||||
columns={[
|
||||
{ title: "检查项", dataIndex: "label", width: 120 },
|
||||
{ title: "资质要求", dataIndex: "require", width: 280 },
|
||||
{ title: "当前值", dataIndex: "value", width: 260 },
|
||||
{
|
||||
title: "结论",
|
||||
dataIndex: "result",
|
||||
width: 80,
|
||||
render: resultTag,
|
||||
},
|
||||
{
|
||||
title: "来源字段",
|
||||
dataIndex: "source",
|
||||
render: (text) => (
|
||||
<span className="cell-source">{text}</span>
|
||||
),
|
||||
},
|
||||
]}
|
||||
dataSource={QUAL_MONITOR_CONDITION_ITEMS.map((item) => {
|
||||
const detail = detailRecord.conditionDetails[item.key];
|
||||
return detail
|
||||
? { key: item.key, label: item.label, ...detail }
|
||||
: null;
|
||||
}).filter(Boolean)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
)}
|
||||
</Modal>
|
||||
</div>
|
||||
|
||||
{/* 资质条件来源说明 */}
|
||||
<div className="source-note">
|
||||
<strong>资质条件来源:</strong>
|
||||
依据安全评价机构资质条件,将机构备案信息拆为固定资产不少于 800 万元、工作场所不少于 1000 平方米、档案室不少于 100 平方米、
|
||||
专职技术人员数量及比例、技术负责人/过程控制负责人、内部制度、法定代表人承诺、公开网站、三年重大违法失信记录、注册地属地满足情况等字段。
|
||||
</div>
|
||||
|
||||
{/* 搜索表单 */}
|
||||
<SearchForm
|
||||
style={{ marginBottom: 12 }}
|
||||
form={form}
|
||||
loading={qualReviewLoading}
|
||||
formLine={[
|
||||
<Form.Item key="keyword" name="keyword">
|
||||
<ControlWrapper.Input
|
||||
label="机构名称/信用代码"
|
||||
placeholder="输入机构名称或统一社会信用代码"
|
||||
allowClear
|
||||
/>
|
||||
</Form.Item>,
|
||||
<Form.Item key="scope" name="scope">
|
||||
<ControlWrapper.Select
|
||||
label="业务范围"
|
||||
placeholder="请选择"
|
||||
allowClear
|
||||
style={{ width: "100%" }}
|
||||
options={SCOPE_OPTIONS.map((o) => ({ label: o.label, value: o.value }))}
|
||||
/>
|
||||
</Form.Item>,
|
||||
<Form.Item key="status" name="status">
|
||||
<ControlWrapper.Select
|
||||
label="资质状态"
|
||||
placeholder="请选择"
|
||||
allowClear
|
||||
style={{ width: "100%" }}
|
||||
options={STATUS_OPTIONS.map((o) => ({ label: o.label, value: o.value }))}
|
||||
/>
|
||||
</Form.Item>,
|
||||
]}
|
||||
onReset={handleReset}
|
||||
onFinish={onFinish}
|
||||
/>
|
||||
|
||||
|
||||
{/* 数据表格 */}
|
||||
<Table
|
||||
rowKey="id"
|
||||
columns={columns}
|
||||
dataSource={data}
|
||||
scroll={{ x: 1600, y: props.scrollY }}
|
||||
loading={qualReviewLoading}
|
||||
pagination={{
|
||||
total: data.length,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
showTotal: (total) => `共 ${total} 条`,
|
||||
current: router.query.current || 1,
|
||||
pageSize: router.query.size || 10,
|
||||
onChange: (page, size) => {
|
||||
router.query = { ...router.query, current: page, size };
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 条件详情弹窗 */}
|
||||
<Modal
|
||||
title={detailRecord ? `资质保持条件详情 - ${detailRecord.orgName}` : ""}
|
||||
open={detailVisible}
|
||||
onCancel={() => setDetailVisible(false)}
|
||||
width={960}
|
||||
footer={
|
||||
<div style={{ display: "flex", justifyContent: "flex-end", gap: 8 }}>
|
||||
<Button onClick={() => setDetailVisible(false)}>关闭</Button>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{detailRecord && (
|
||||
<div>
|
||||
<div className="check-note">
|
||||
<strong>机构核查口径:</strong>以机构为主线,将资质条件拆为十项保持条件和属地要求;
|
||||
每项展示监管字段、当前值、结论和来源。
|
||||
</div>
|
||||
<Table
|
||||
rowKey="key"
|
||||
size="small"
|
||||
pagination={false}
|
||||
columns={[
|
||||
{ title: "检查项", dataIndex: "label", width: 120 },
|
||||
{ title: "资质要求", dataIndex: "require", width: 280 },
|
||||
{ title: "当前值", dataIndex: "value", width: 260 },
|
||||
{ title: "结论", dataIndex: "result", width: 80, render: resultTag },
|
||||
{ title: "来源字段", dataIndex: "source", render: (text) => <span className="cell-source">{text}</span> },
|
||||
]}
|
||||
dataSource={CONDITION_ITEMS.map((item) => {
|
||||
const detail = detailRecord.conditionDetails[item.key];
|
||||
return detail ? { key: item.key, label: item.label, ...detail } : null;
|
||||
}).filter(Boolean)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</Modal>
|
||||
</div>
|
||||
</PageLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Connect([NS_QUAL_REVIEW], true)(AntdTableFuncControl(QualMonitor));
|
||||
export default Connect(
|
||||
[NS_QUAL_REVIEW],
|
||||
true,
|
||||
)(AntdTableFuncControl(QualMonitor));
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@
|
|||
// 机构信息
|
||||
.org-info {
|
||||
.org-name {
|
||||
font-weight: 500;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.org-meta {
|
||||
|
|
|
|||
|
|
@ -1,17 +1,8 @@
|
|||
/** 企业信息管理:前后端字段与分页格式适配 */
|
||||
|
||||
import { formatDate, formatDateTime, normalizeDateValue, toApiDate, toApiDateTime } from "../../utils/dateFormat";
|
||||
import { resolveUploadFileIds } from "../../utils/mockUpload";
|
||||
import { asId, isIdFieldName, normalizeQueryIds } from "./idUtil";
|
||||
import { withOrgId } from "./orgContext";
|
||||
|
||||
const GENDER_NAME = { 1: "男", 2: "女" };
|
||||
const RESIGN_AUDIT_STATUS_NAME = { 0: "未审核", 1: "已审核", 2: "已退回" };
|
||||
const ENTERPRISE_STATUS_CODE = { 正常: 1, 停业: 2, 注销: 3 };
|
||||
const FILING_TYPE_CODE = { 审核备案: "1", 确认备案: "2" };
|
||||
const FILING_RECORD_STATUS_CODE = { 已备案: 1, 未备案: 2 };
|
||||
const PERSON_TYPE_CODE = { 基础人员: "1", 专职评价师: "2" };
|
||||
|
||||
function parseAttachmentUrls(urls, defaultName = "附件.pdf") {
|
||||
if (!urls) {
|
||||
return [];
|
||||
|
|
@ -27,7 +18,10 @@ function parseAttachmentUrls(urls, defaultName = "附件.pdf") {
|
|||
.filter((item) => item?.url)
|
||||
.map((item) => ({
|
||||
name: item.name || `${defaultName.replace(".pdf", "")}1`,
|
||||
fileName: item.fileName || item.name || `${defaultName.replace(".pdf", "")}1`,
|
||||
fileName:
|
||||
item.fileName ||
|
||||
item.name ||
|
||||
`${defaultName.replace(".pdf", "")}1`,
|
||||
url: item.url,
|
||||
uid: item.uid || undefined,
|
||||
status: item.status || "done",
|
||||
|
|
@ -87,652 +81,8 @@ export function fromSingleResponse(res, mapItem) {
|
|||
};
|
||||
}
|
||||
|
||||
function pick(obj, keys) {
|
||||
const next = {};
|
||||
keys.forEach((key) => {
|
||||
if (obj[key] !== undefined) {
|
||||
next[key] = isIdFieldName(key) ? asId(obj[key]) : obj[key];
|
||||
}
|
||||
});
|
||||
return next;
|
||||
}
|
||||
|
||||
// ─── 机构信息 ───
|
||||
|
||||
export function toOrgInfoForm(data = {}) {
|
||||
return {
|
||||
id: asId(data.id),
|
||||
orgName: data.unitName,
|
||||
creditCode: data.creditCode,
|
||||
safetyIndustryCategory: data.safetyIndustryCategoryName,
|
||||
regionCountyName: data.districtName,
|
||||
regionStreetName: data.townStreet,
|
||||
regionCommunityName: data.villageCommunity,
|
||||
longitude: data.longitude,
|
||||
latitude: data.latitude,
|
||||
registerAddress: data.registerAddress,
|
||||
businessAddress: data.businessAddress,
|
||||
ownershipType: data.ownershipTypeName,
|
||||
gbIndustryCode: data.economyIndustryCode,
|
||||
legalRepresentative: data.legalRepresentative,
|
||||
legalRepPhone: data.legalRepresentativePhone,
|
||||
principal: data.principalName,
|
||||
principalPhone: data.principalPhone,
|
||||
safetyDeptHead: data.safetyDeptManager,
|
||||
safetyDeptPhone: data.safetyDeptManagerPhone,
|
||||
safetyVpPhone: data.safetyDeputyPhone,
|
||||
productionDate: data.productionDate,
|
||||
businessStatus: data.businessStatusName,
|
||||
disclosureUrl: data.infoDisclosureUrl,
|
||||
workplaceArea: data.workplaceArea,
|
||||
archiveRoomArea: data.archiveRoomArea,
|
||||
fullTimeEvaluatorCount: data.fulltimeEvaluatorCount,
|
||||
registeredSafetyEngineerCount: data.registeredEngineerCount,
|
||||
authStatusCode: data.authStatusCode,
|
||||
enterpriseStatus: data.enterpriseStatusName,
|
||||
enterpriseScale: data.enterpriseScaleName,
|
||||
filingType: data.filingTypeName ?? "确认备案",
|
||||
filingRecordStatus: data.filingRecordStatusName ?? "未备案",
|
||||
attachments: parseAttachmentUrls(data.attachmentUrls),
|
||||
};
|
||||
}
|
||||
|
||||
export function fromOrgInfoForm(values = {}, options = {}) {
|
||||
const { isDraft = false } = options;
|
||||
const enterpriseStatus = values.enterpriseStatus;
|
||||
const filingType = values.filingType ?? "确认备案";
|
||||
const filingRecordStatus = values.filingRecordStatus ?? "未备案";
|
||||
return {
|
||||
...pick(values, ["id", "tenantId"]),
|
||||
unitName: values.orgName,
|
||||
creditCode: values.creditCode,
|
||||
safetyIndustryCategoryName: values.safetyIndustryCategory,
|
||||
districtCode: values.regionCountyName,
|
||||
districtName: values.regionCountyName,
|
||||
townStreet: values.regionStreetName,
|
||||
villageCommunity: values.regionCommunityName,
|
||||
longitude: values.longitude,
|
||||
latitude: values.latitude,
|
||||
registerAddress: values.registerAddress,
|
||||
businessAddress: values.businessAddress,
|
||||
ownershipTypeName: values.ownershipType,
|
||||
economyIndustryCode: values.gbIndustryCode,
|
||||
legalRepresentative: values.legalRepresentative,
|
||||
legalRepresentativePhone: values.legalRepPhone,
|
||||
principalName: values.principal,
|
||||
principalPhone: values.principalPhone,
|
||||
safetyDeptManager: values.safetyDeptHead,
|
||||
safetyDeptManagerPhone: values.safetyDeptPhone,
|
||||
safetyDeputyPhone: values.safetyVpPhone,
|
||||
productionDate: values.productionDate,
|
||||
businessStatusName: values.businessStatus,
|
||||
infoDisclosureUrl: values.disclosureUrl,
|
||||
workplaceArea: values.workplaceArea,
|
||||
archiveRoomArea: values.archiveRoomArea,
|
||||
fulltimeEvaluatorCount: values.fullTimeEvaluatorCount,
|
||||
registeredEngineerCount: values.registeredSafetyEngineerCount,
|
||||
authStatusCode: isDraft ? 0 : 1,
|
||||
authStatusName: isDraft ? "草稿" : "已提交",
|
||||
enterpriseStatusCode: ENTERPRISE_STATUS_CODE[enterpriseStatus],
|
||||
enterpriseStatusName: enterpriseStatus,
|
||||
enterpriseScaleCode: values.enterpriseScale,
|
||||
enterpriseScaleName: values.enterpriseScale,
|
||||
filingTypeCode: FILING_TYPE_CODE[filingType] ?? filingType,
|
||||
filingTypeName: filingType,
|
||||
filingRecordStatusCode: FILING_RECORD_STATUS_CODE[filingRecordStatus],
|
||||
filingRecordStatusName: filingRecordStatus,
|
||||
attachmentUrls: resolveUploadFileIds(values.attachments),
|
||||
};
|
||||
}
|
||||
|
||||
/** 监管端机构账号:后端 state 0=启用,1=禁用 */
|
||||
export function isOrgAccountEnabled(record = {}) {
|
||||
return Number(record.state) === 0;
|
||||
}
|
||||
|
||||
export function toOrgAccountRow(data = {}) {
|
||||
return {
|
||||
id: asId(data.id),
|
||||
orgName: data.unitName,
|
||||
district: data.districtName,
|
||||
state: data.state,
|
||||
createTime: formatDate(normalizeDateValue(data.createTime)),
|
||||
};
|
||||
}
|
||||
|
||||
/** 监管端分页查询(不带 orgInfoId;部分筛选项需后端 OrgInfoPageQuery 扩展后生效) */
|
||||
export function toRegulatorOrgAccountPageQuery(params = {}) {
|
||||
const query = {
|
||||
current: params.pageIndex ?? params.current ?? 1,
|
||||
size: params.pageSize ?? params.size ?? 10,
|
||||
};
|
||||
if (params.orgName) {
|
||||
query.unitName = params.orgName;
|
||||
}
|
||||
if (params.district) {
|
||||
query.districtName = params.district;
|
||||
}
|
||||
const state = params.state;
|
||||
if (state !== undefined && state !== null && state !== "" && state !== "全部") {
|
||||
query.state = Number(state);
|
||||
}
|
||||
const range = params.openTimeRange;
|
||||
if (Array.isArray(range) && range.length === 2 && range[0] && range[1]) {
|
||||
query.createTimeStart = range[0].format ? range[0].format("YYYY-MM-DD") : range[0];
|
||||
query.createTimeEnd = range[1].format ? range[1].format("YYYY-MM-DD") : range[1];
|
||||
}
|
||||
return normalizeQueryIds(query);
|
||||
}
|
||||
|
||||
/** 监管端已备案机构列表行 */
|
||||
export function toRegisteredOrgRow(data = {}) {
|
||||
return {
|
||||
id: asId(data.id),
|
||||
orgName: data.unitName,
|
||||
registerAddress: data.registerAddress,
|
||||
serviceEnterpriseCount: 0,
|
||||
evalProjectCount: 0,
|
||||
filingType: data.filingTypeName,
|
||||
filingRecordStatusCode: data.filingRecordStatusCode,
|
||||
filingRecordStatusName: data.filingRecordStatusName,
|
||||
};
|
||||
}
|
||||
|
||||
/** 监管端已备案机构分页查询 */
|
||||
export function toRegisteredOrgPageQuery(params = {}) {
|
||||
const query = {
|
||||
current: params.pageIndex ?? params.current ?? 1,
|
||||
size: params.pageSize ?? params.size ?? 10,
|
||||
};
|
||||
if (params.orgName) {
|
||||
query.unitName = params.orgName;
|
||||
}
|
||||
if (params.registerAddress) {
|
||||
query.registerAddress = params.registerAddress;
|
||||
}
|
||||
const filingTypeCode = params.filingType;
|
||||
if (filingTypeCode !== undefined && filingTypeCode !== null && filingTypeCode !== "") {
|
||||
query.filingTypeCode = String(filingTypeCode);
|
||||
}
|
||||
const filingRecordStatusCode = params.filingRecordStatus;
|
||||
if (filingRecordStatusCode !== undefined && filingRecordStatusCode !== null && filingRecordStatusCode !== "") {
|
||||
query.filingRecordStatusCode = Number(filingRecordStatusCode);
|
||||
}
|
||||
return normalizeQueryIds(query);
|
||||
}
|
||||
|
||||
export function fromRegulatorOrgInfoModify(values = {}, existing = {}) {
|
||||
const merged = {
|
||||
...toOrgInfoForm(existing),
|
||||
...values,
|
||||
id: asId(values.id ?? existing.id),
|
||||
};
|
||||
const payload = fromOrgInfoForm(merged, { isDraft: false });
|
||||
return {
|
||||
...payload,
|
||||
id: asId(values.id ?? existing.id),
|
||||
authStatusCode: existing.authStatusCode ?? payload.authStatusCode,
|
||||
authStatusName: existing.authStatusName ?? payload.authStatusName,
|
||||
tenantId: existing.tenantId ?? payload.tenantId,
|
||||
filingTypeCode: existing.filingTypeCode ?? payload.filingTypeCode,
|
||||
filingTypeName: existing.filingTypeName ?? payload.filingTypeName,
|
||||
filingRecordStatusCode: existing.filingRecordStatusCode ?? payload.filingRecordStatusCode,
|
||||
filingRecordStatusName: existing.filingRecordStatusName ?? payload.filingRecordStatusName,
|
||||
};
|
||||
}
|
||||
|
||||
// ─── 部门 / 岗位 ───
|
||||
|
||||
export function toDepartmentForm(data = {}) {
|
||||
return {
|
||||
id: asId(data.id),
|
||||
parentId: asId(data.parentId),
|
||||
deptName: data.deptName,
|
||||
leaderName: data.managerName,
|
||||
leaderAccount: data.managerAccount,
|
||||
deptLevel: data.deptLevelName ?? data.deptLevelCode,
|
||||
};
|
||||
}
|
||||
|
||||
export function fromDepartmentForm(values = {}) {
|
||||
return {
|
||||
...pick(values, ["id", "parentId", "tenantId"]),
|
||||
deptName: values.deptName,
|
||||
managerName: values.leaderName,
|
||||
managerAccount: values.leaderAccount,
|
||||
deptLevelName: values.deptLevel,
|
||||
deptLevelCode: values.deptLevel,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildDepartmentTree(departments = []) {
|
||||
const map = new Map();
|
||||
departments.forEach((item) => {
|
||||
map.set(asId(item.id), { ...toDepartmentForm(item), children: [] });
|
||||
});
|
||||
const roots = [];
|
||||
departments.forEach((item) => {
|
||||
const node = map.get(asId(item.id));
|
||||
const parentId = asId(item.parentId);
|
||||
if (parentId != null && parentId !== "0" && map.has(parentId)) {
|
||||
map.get(parentId).children.push(node);
|
||||
}
|
||||
else {
|
||||
roots.push(node);
|
||||
}
|
||||
});
|
||||
return roots;
|
||||
}
|
||||
|
||||
export function toPositionForm(data = {}) {
|
||||
return {
|
||||
id: asId(data.id),
|
||||
deptId: asId(data.deptId),
|
||||
deptName: data.deptName,
|
||||
positionName: data.positionName,
|
||||
dutyDesc: data.dutyDesc,
|
||||
remark: data.remark ?? data.remarks,
|
||||
};
|
||||
}
|
||||
|
||||
export function fromPositionForm(values = {}) {
|
||||
return {
|
||||
...pick(values, ["id", "tenantId"]),
|
||||
deptId: asId(values.deptId),
|
||||
positionName: values.positionName,
|
||||
dutyDesc: values.dutyDesc,
|
||||
remark: values.remark,
|
||||
};
|
||||
}
|
||||
|
||||
// ─── 人员 ───
|
||||
|
||||
export function toStaffForm(data = {}) {
|
||||
return {
|
||||
id: asId(data.id),
|
||||
staffName: data.userName,
|
||||
account: data.account,
|
||||
gender: data.genderCode,
|
||||
birthDate: data.birthDate,
|
||||
deptId: asId(data.deptId),
|
||||
positionId: asId(data.postId),
|
||||
idCardNo: data.idCardNo,
|
||||
homeAddress: data.currentAddress,
|
||||
officeAddress: data.officeAddress,
|
||||
educationType: data.educationTypeName,
|
||||
educationLevel: data.educationLevelName,
|
||||
education: data.educationName ?? data.educationCode,
|
||||
graduateSchool: data.graduateSchool,
|
||||
major: data.major,
|
||||
employmentStatus: data.employmentStatusCode,
|
||||
personType: data.personTypeName ?? "基础人员",
|
||||
qualScope: data.qualScope,
|
||||
professionalLevel: data.professionalLevelName,
|
||||
evaluatorCertNo: data.evaluatorCertNo,
|
||||
titleName: data.titleName,
|
||||
registerEngineerFlag: data.registerEngineerFlag ?? 2,
|
||||
publications: data.publications,
|
||||
abilityDeclaration: data.abilityDeclaration,
|
||||
workExperience: data.workExperience,
|
||||
proofMaterials: parseAttachmentUrls(data.proofMaterialUrl, "专业能力证明.pdf"),
|
||||
deptName: data.deptName,
|
||||
positionName: data.postName ?? data.positionName,
|
||||
certNames: data.certNames,
|
||||
};
|
||||
}
|
||||
|
||||
export function fromStaffForm(values = {}) {
|
||||
const genderCode = values.gender;
|
||||
const personType = values.personType ?? "基础人员";
|
||||
const educationType = values.educationType;
|
||||
const educationLevel = values.educationLevel;
|
||||
const educationName = educationType && educationLevel
|
||||
? `${educationType}-${educationLevel}`
|
||||
: values.education;
|
||||
return {
|
||||
...pick(values, ["id", "tenantId"]),
|
||||
userName: values.staffName,
|
||||
account: values.account,
|
||||
genderCode,
|
||||
genderName: GENDER_NAME[genderCode] ?? values.genderName,
|
||||
birthDate: toApiDate(values.birthDate),
|
||||
deptId: asId(values.deptId),
|
||||
postId: asId(values.positionId ?? values.postId),
|
||||
idCardNo: values.idCardNo,
|
||||
currentAddress: values.homeAddress,
|
||||
officeAddress: values.officeAddress,
|
||||
educationName,
|
||||
educationCode: educationName,
|
||||
educationTypeCode: educationType,
|
||||
educationTypeName: educationType,
|
||||
educationLevelCode: educationLevel,
|
||||
educationLevelName: educationLevel,
|
||||
graduateSchool: values.graduateSchool,
|
||||
major: values.major,
|
||||
employmentStatusCode: values.employmentStatus ?? 1,
|
||||
employmentStatusName: values.employmentStatus === 2 ? "离职" : "在职",
|
||||
personTypeCode: PERSON_TYPE_CODE[personType] ?? personType,
|
||||
personTypeName: personType,
|
||||
qualScope: values.qualScope,
|
||||
professionalLevelCode: values.professionalLevel,
|
||||
professionalLevelName: values.professionalLevel,
|
||||
evaluatorCertNo: values.evaluatorCertNo,
|
||||
titleName: values.titleName,
|
||||
registerEngineerFlag: values.registerEngineerFlag ?? 2,
|
||||
publications: values.publications,
|
||||
abilityDeclaration: values.abilityDeclaration,
|
||||
workExperience: values.workExperience,
|
||||
proofMaterialUrl: resolveUploadFileIds(values.proofMaterials),
|
||||
};
|
||||
}
|
||||
|
||||
// ─── 人员证书 ───
|
||||
|
||||
export function toStaffCertForm(data = {}) {
|
||||
const certImgs = data.certAttachmentUrl
|
||||
? parseAttachmentUrls(data.certAttachmentUrl, data.certName || "证书附件.jpg")
|
||||
: data.certImgFiles || [];
|
||||
return {
|
||||
id: asId(data.id),
|
||||
staffId: asId(data.personnelId),
|
||||
certName: data.certName,
|
||||
certCategory: data.certCategoryName ?? data.certCategoryCode,
|
||||
certWorkCategory: data.operationCategoryName ?? data.operationCategoryCode,
|
||||
certNo: data.certNo,
|
||||
issueOrg: data.issueOrg,
|
||||
validStartDate: data.validStartDate,
|
||||
validEndDate: data.validEndDate,
|
||||
reviewDate: data.reviewDate,
|
||||
certImgFiles: certImgs,
|
||||
certImgs,
|
||||
};
|
||||
}
|
||||
|
||||
export function fromStaffCertForm(values = {}) {
|
||||
const certAttachmentUrl = values.certAttachmentUrl
|
||||
|| values.certImgs?.[0]?.url
|
||||
|| values.certImgFiles?.[0]?.url;
|
||||
return {
|
||||
...pick(values, ["id", "tenantId"]),
|
||||
personnelId: asId(values.staffId),
|
||||
certName: values.certName,
|
||||
certCategoryName: values.certCategory,
|
||||
certCategoryCode: values.certCategory,
|
||||
operationCategoryName: values.certWorkCategory,
|
||||
operationCategoryCode: values.certWorkCategory,
|
||||
certNo: values.certNo,
|
||||
issueOrg: values.issueOrg,
|
||||
validStartDate: values.validStartDate,
|
||||
validEndDate: values.validEndDate,
|
||||
reviewDate: values.reviewDate,
|
||||
certAttachmentUrl,
|
||||
};
|
||||
}
|
||||
|
||||
// ─── 机构资质证书 ───
|
||||
|
||||
/** 资质证书 enableFlag(1启用/2禁用) ↔ 前端 enableStatus(1启用/0禁用) */
|
||||
export function mapQualificationEnableStatus(flag) {
|
||||
return Number(flag) === 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
export function mapQualificationEnableFlag(status) {
|
||||
return Number(status) === 1 ? 1 : 2;
|
||||
}
|
||||
|
||||
/** 列表/表单统一判断:优先 enableFlag,兼容 enableStatus */
|
||||
export function isQualificationEnabled(record = {}) {
|
||||
const flag = record.enableFlag ?? record.enableStatus;
|
||||
return mapQualificationEnableStatus(flag) === 1;
|
||||
}
|
||||
|
||||
export function toQualificationForm(data = {}) {
|
||||
const certImgs = data.certImageUrl
|
||||
? parseAttachmentUrls(data.certImageUrl, data.certName || "证书图片.jpg")
|
||||
: data.certImgFiles || [];
|
||||
return {
|
||||
id: asId(data.id),
|
||||
certType: data.licenseTypeName ?? data.licenseTypeCode,
|
||||
certName: data.certName,
|
||||
certNo: data.certNo,
|
||||
issueOrg: data.issueOrg,
|
||||
issueDate: data.issueDate,
|
||||
validStartDate: data.validStartDate,
|
||||
validEndDate: data.validEndDate,
|
||||
remark: data.remark ?? data.remarks,
|
||||
enableFlag: data.enableFlag,
|
||||
enableStatus: mapQualificationEnableStatus(data.enableFlag),
|
||||
issueDate: formatDate(data.issueDate),
|
||||
validStartDate: formatDate(data.validStartDate),
|
||||
validEndDate: formatDate(data.validEndDate),
|
||||
certImgFiles: certImgs,
|
||||
certImgs,
|
||||
};
|
||||
}
|
||||
|
||||
export function fromQualificationForm(values = {}) {
|
||||
const certImageUrl = values.certImageUrl
|
||||
|| values.certImgs?.[0]?.url
|
||||
|| values.certImgFiles?.[0]?.url;
|
||||
return {
|
||||
...pick(values, ["id", "tenantId"]),
|
||||
licenseTypeName: values.certType,
|
||||
licenseTypeCode: values.certType,
|
||||
certName: values.certName,
|
||||
certNo: values.certNo,
|
||||
issueOrg: values.issueOrg,
|
||||
issueDate: toApiDate(values.issueDate),
|
||||
validStartDate: toApiDate(values.validStartDate ?? values.validDate?.[0]),
|
||||
validEndDate: toApiDate(values.validEndDate ?? values.validDate?.[1]),
|
||||
certImageUrl,
|
||||
remark: values.remark,
|
||||
enableFlag: mapQualificationEnableFlag(values.enableStatus),
|
||||
};
|
||||
}
|
||||
|
||||
function isQualificationExpireWarning(dateStr) {
|
||||
if (!dateStr) {
|
||||
return false;
|
||||
}
|
||||
const end = new Date(String(dateStr).replace(/-/g, "/"));
|
||||
if (Number.isNaN(end.getTime())) {
|
||||
return false;
|
||||
}
|
||||
const diff = end.getTime() - Date.now();
|
||||
return diff > 0 && diff < 90 * 24 * 3600 * 1000;
|
||||
}
|
||||
|
||||
/** classGet 分组 Map → 已备案机构详情「申请清单材料」结构 */
|
||||
export function fromQualificationClassGetResponse(res) {
|
||||
const map = res?.data;
|
||||
if (!map || typeof map !== "object") {
|
||||
return [];
|
||||
}
|
||||
return Object.entries(map).map(([code, list], index) => {
|
||||
const items = (Array.isArray(list) ? list : []).map(toQualificationForm);
|
||||
const scopeName = items[0]?.certType || code || `资质${index + 1}`;
|
||||
const expireDate = items.reduce((latest, item) => {
|
||||
const d = item.validEndDate;
|
||||
if (!d) {
|
||||
return latest;
|
||||
}
|
||||
return !latest || d > latest ? d : latest;
|
||||
}, "");
|
||||
return {
|
||||
id: code || String(index),
|
||||
scopeName,
|
||||
expireDate: expireDate || "-",
|
||||
expireWarning: isQualificationExpireWarning(expireDate),
|
||||
materials: items.map((item, idx) => {
|
||||
const previewUrl = item.certImgFiles?.[0]?.url || item.certImgs?.[0]?.url;
|
||||
return {
|
||||
id: item.id || `${code}-${idx}`,
|
||||
name: item.certName || item.certType || "-",
|
||||
required: true,
|
||||
status: previewUrl ? "uploaded" : "pending",
|
||||
previewUrl,
|
||||
raw: item,
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/** 已备案机构详情人员列表行 */
|
||||
export function toRegisteredOrgPersonnelRow(data = {}) {
|
||||
const form = toStaffForm(data);
|
||||
return {
|
||||
id: form.id,
|
||||
name: form.staffName,
|
||||
gender: GENDER_NAME[form.gender] || "-",
|
||||
position: form.positionName,
|
||||
qualScope: form.qualScope,
|
||||
education: form.education || form.educationLevel,
|
||||
registerEngineer: form.registerEngineerFlag === 1 ? 1 : 0,
|
||||
};
|
||||
}
|
||||
|
||||
// ─── 装备 ───
|
||||
|
||||
/** 装备 enableFlag(1启用/2禁用) ↔ 前端 enableStatus(1启用/0禁用) */
|
||||
export function mapEquipEnableStatus(flag) {
|
||||
return Number(flag) === 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
export function mapEquipEnableFlag(status) {
|
||||
return Number(status) === 1 ? 1 : 2;
|
||||
}
|
||||
|
||||
export function isEquipEnabled(record = {}) {
|
||||
const flag = record.enableFlag ?? record.enableStatus;
|
||||
return mapEquipEnableStatus(flag) === 1;
|
||||
}
|
||||
|
||||
export function toEquipForm(data = {}) {
|
||||
const instrumentType = data.instrumentTypeName ?? data.instrumentTypeCode;
|
||||
return {
|
||||
id: asId(data.id),
|
||||
instrumentType,
|
||||
instrumentCategory: instrumentType,
|
||||
deviceType: data.deviceTypeName ?? data.deviceTypeCode,
|
||||
deviceName: data.deviceName,
|
||||
model: data.deviceModel,
|
||||
flowDesc: data.flowDesc,
|
||||
manufacturer: data.manufacturer,
|
||||
minFlow: data.minFlow,
|
||||
maxFlow: data.maxFlow,
|
||||
calibrationUnit: data.calibrationUnit,
|
||||
calibrationInitialValue: data.calibrationInitValue,
|
||||
onSiteCalibrationType: data.fieldCalibrationTypeName ?? data.fieldCalibrationTypeCode,
|
||||
isDualChannel: data.dualChannelFlag,
|
||||
enableFlag: data.enableFlag,
|
||||
enableStatus: mapEquipEnableStatus(data.enableFlag),
|
||||
};
|
||||
}
|
||||
|
||||
export function fromEquipForm(values = {}) {
|
||||
return {
|
||||
...pick(values, ["id", "tenantId"]),
|
||||
deviceName: values.deviceName,
|
||||
deviceModel: values.model,
|
||||
instrumentTypeName: values.instrumentCategory,
|
||||
instrumentTypeCode: values.instrumentCategory,
|
||||
deviceTypeName: values.deviceType,
|
||||
deviceTypeCode: values.deviceType,
|
||||
manufacturer: values.manufacturer,
|
||||
flowDesc: values.flowDesc,
|
||||
minFlow: values.minFlow,
|
||||
maxFlow: values.maxFlow,
|
||||
calibrationUnit: values.calibrationUnit,
|
||||
calibrationInitValue: values.calibrationInitialValue,
|
||||
fieldCalibrationTypeName: values.onSiteCalibrationType,
|
||||
fieldCalibrationTypeCode: values.onSiteCalibrationType,
|
||||
dualChannelFlag: values.isDualChannel,
|
||||
enableFlag: mapEquipEnableFlag(values.enableStatus),
|
||||
};
|
||||
}
|
||||
|
||||
// ─── 人员变更 / 离职 ───
|
||||
|
||||
export function toChangeLogForm(data = {}) {
|
||||
return {
|
||||
id: asId(data.id),
|
||||
staffId: asId(data.personnelId),
|
||||
changeItem: data.changeItem,
|
||||
changeTime: formatDateTime(data.changeTime),
|
||||
createBy: data.operatorName,
|
||||
};
|
||||
}
|
||||
|
||||
export function toResignApplyForm(data = {}) {
|
||||
const auditStatus = data.auditStatusCode ?? data.auditStatus ?? 0;
|
||||
return {
|
||||
id: asId(data.id),
|
||||
personnelId: asId(data.personnelId),
|
||||
applicantName: data.applicantName,
|
||||
applyTime: formatDateTime(data.applyTime),
|
||||
expectedLeaveDate: formatDate(data.expectedResignDate),
|
||||
leaveReason: data.resignReason,
|
||||
remark: data.remark ?? data.remarks,
|
||||
rejectReason: data.rejectReason,
|
||||
auditStatus,
|
||||
auditStatusName: data.auditStatusName ?? RESIGN_AUDIT_STATUS_NAME[auditStatus] ?? "未审核",
|
||||
attachmentId: data.reportFileUrl,
|
||||
account: data.account,
|
||||
deptName: data.deptName,
|
||||
};
|
||||
}
|
||||
|
||||
export function fromResignApplyForm(values = {}) {
|
||||
return {
|
||||
...pick(values, ["id", "tenantId"]),
|
||||
personnelId: asId(values.personnelId),
|
||||
applicantName: values.applicantName,
|
||||
applyTime: toApiDateTime(values.applyTime),
|
||||
expectedResignDate: toApiDate(values.expectedLeaveDate),
|
||||
resignReason: values.leaveReason,
|
||||
remark: values.remark,
|
||||
reportFileUrl: values.attachmentId ?? values.reportFileUrl,
|
||||
auditStatusCode: values.auditStatus ?? 0,
|
||||
auditStatusName: values.auditStatus === 1 ? "已审核" : values.auditStatus === 2 ? "已退回" : "未审核",
|
||||
};
|
||||
}
|
||||
|
||||
export function fromResignAuditForm(values = {}) {
|
||||
const auditStatus = values.auditStatus;
|
||||
return {
|
||||
id: asId(values.id),
|
||||
personnelId: asId(values.personnelId),
|
||||
auditStatusCode: auditStatus,
|
||||
auditStatusName: auditStatus === 1 ? "已审核" : auditStatus === 2 ? "已退回" : "未审核",
|
||||
rejectReason: values.rejectReason,
|
||||
};
|
||||
}
|
||||
|
||||
export function toStaffChangeSummary(personnel = {}) {
|
||||
const employmentCode = Number(personnel.employmentStatusCode ?? personnel.employmentStatus ?? 1);
|
||||
const hasResignApply = personnel.resignApplyId != null && personnel.resignApplyId !== "";
|
||||
const resignStatusRaw = personnel.resignAuditStatus;
|
||||
let resignStatus = null;
|
||||
if (hasResignApply) {
|
||||
resignStatus = resignStatusRaw === null || resignStatusRaw === undefined
|
||||
? 0
|
||||
: Number(resignStatusRaw);
|
||||
}
|
||||
return {
|
||||
id: asId(personnel.id),
|
||||
staffId: asId(personnel.id),
|
||||
account: personnel.account,
|
||||
staffName: personnel.userName ?? personnel.staffName,
|
||||
deptName: personnel.deptName || "",
|
||||
employmentStatus: employmentCode,
|
||||
employmentStatusCode: employmentCode,
|
||||
employmentStatusName: personnel.employmentStatusName
|
||||
|| (employmentCode === 2 ? "离职" : "在职"),
|
||||
changeCount: Number(personnel.changeCount ?? 0),
|
||||
resignApplyId: asId(personnel.resignApplyId),
|
||||
resignAuditStatus: resignStatus,
|
||||
resignAuditStatusName: personnel.resignAuditStatusName
|
||||
?? (resignStatus !== null ? RESIGN_AUDIT_STATUS_NAME[resignStatus] : undefined),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,102 +2,22 @@
|
|||
* 浼佷笟淇℃伅妯″潡鍒濆鍖栦笌涓嬫媺鏁版嵁鎷夊彇锛堢粫杩?DVA锛岄伩鍏?loading 鐘舵€佸鑷撮〉闈㈤噸娓叉煋姝诲惊鐜級
|
||||
*/
|
||||
|
||||
import {
|
||||
fromPageResponse,
|
||||
fromSingleResponse,
|
||||
toDepartmentForm,
|
||||
toOrgInfoForm,
|
||||
toPageQuery,
|
||||
toPositionForm,
|
||||
} from "./adapter";
|
||||
import { apiGet } from "./http";
|
||||
import { clearOrgInfoId, getOrgInfoId, setOrgInfoId } from "./orgContext";
|
||||
|
||||
/** 鏈烘瀯淇℃伅绠$悊椤佃矾寰勶紙鏃犳満鏋勬暟鎹椂鍞竴鍙闂〉锛?*/
|
||||
export const ORG_INFO_PAGE_PATH = "/certificate/container/EnterpriseInfo/OrgInfo";
|
||||
export const MATERIAL_REPORT_PAGE_PATH = "/certificate/container/EnterpriseInfo/MaterialReport";
|
||||
|
||||
let ensureOrgPromise = null;
|
||||
/** 鏈€杩戜竴娆?getInfo 杞崲缁撴灉锛屼緵鏈烘瀯淇℃伅椤靛鐢紝閬垮厤閲嶅璇锋眰 */
|
||||
let lastOrgInfoDetail = null;
|
||||
|
||||
export function isOrgInfoPage(path = window.location.pathname) {
|
||||
return path === ORG_INFO_PAGE_PATH || path.startsWith(`${ORG_INFO_PAGE_PATH}/`) || path === MATERIAL_REPORT_PAGE_PATH || path.startsWith(`${MATERIAL_REPORT_PAGE_PATH}/`);
|
||||
}
|
||||
|
||||
export function getOrgInfoDetail() {
|
||||
return lastOrgInfoDetail;
|
||||
}
|
||||
|
||||
export function setOrgInfoDetailCache(detail) {
|
||||
lastOrgInfoDetail = detail;
|
||||
}
|
||||
|
||||
function fetchOrgInfoContext() {
|
||||
const cachedBefore = getOrgInfoId();
|
||||
return apiGet("/safetyEval/org-info/getInfo", {}, {}, { includeOrgContext: false })
|
||||
.then((res) => {
|
||||
lastOrgInfoDetail = fromSingleResponse(res, toOrgInfoForm);
|
||||
const id = lastOrgInfoDetail?.data?.id ?? res?.data?.id;
|
||||
if (id) {
|
||||
setOrgInfoId(id);
|
||||
return { hasOrg: true, orgInfoId: String(id), detail: lastOrgInfoDetail };
|
||||
}
|
||||
// 本地联调:getInfo 无 SSO 关联时,保留 session 中已有 orgInfoId
|
||||
if (cachedBefore) {
|
||||
return { hasOrg: true, orgInfoId: cachedBefore, detail: lastOrgInfoDetail };
|
||||
}
|
||||
clearOrgInfoId();
|
||||
return { hasOrg: false, orgInfoId: null, detail: lastOrgInfoDetail };
|
||||
})
|
||||
.catch((err) => {
|
||||
console.warn("[ensureOrgContext] getInfo failed:", err);
|
||||
clearOrgInfoId();
|
||||
lastOrgInfoDetail = { success: false, data: null };
|
||||
return { hasOrg: false, orgInfoId: null, detail: lastOrgInfoDetail, networkError: true };
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 纭繚宸茬紦瀛樻満鏋?id锛堝瓙琛ㄥ垎椤?璇锋眰澶?orgInfoId 渚濊禆姝ゅ€硷級
|
||||
* @param {{ force?: boolean }} options force=true 鏃跺拷鐣ョ紦瀛樺苟閲嶆柊 getInfo锛堣繘鍏ヤ紒涓氫俊鎭ā鍧楁椂浣跨敤锛?
|
||||
* @returns {Promise<{ hasOrg: boolean, orgInfoId: string|null, detail?: object, networkError?: boolean }>}
|
||||
*/
|
||||
export function ensureOrgContext(options = {}) {
|
||||
const { force = false } = options;
|
||||
|
||||
if (!force) {
|
||||
const cached = getOrgInfoId();
|
||||
if (cached) {
|
||||
return Promise.resolve({ hasOrg: true, orgInfoId: cached, detail: lastOrgInfoDetail });
|
||||
}
|
||||
}
|
||||
|
||||
if (ensureOrgPromise) {
|
||||
return ensureOrgPromise;
|
||||
}
|
||||
|
||||
ensureOrgPromise = fetchOrgInfoContext().finally(() => {
|
||||
ensureOrgPromise = null;
|
||||
});
|
||||
return ensureOrgPromise;
|
||||
}
|
||||
|
||||
export async function fetchOrgDepartmentPage(params = {}) {
|
||||
await ensureOrgContext();
|
||||
const query = toPageQuery(params, { likeDeptName: "deptName" });
|
||||
const res = await apiGet("/safetyEval/org-department/page", query);
|
||||
return fromPageResponse(res, toDepartmentForm);
|
||||
}
|
||||
|
||||
export async function fetchOrgPositionPage(params = {}) {
|
||||
await ensureOrgContext();
|
||||
const query = toPageQuery(params, {
|
||||
eqDeptId: "deptId",
|
||||
likePositionName: "positionName",
|
||||
});
|
||||
const res = await apiGet("/safetyEval/org-position/page", query);
|
||||
return fromPageResponse(res, toPositionForm);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue