2026-06-30 17:32:29 +08:00
|
|
|
|
import { fetchQualFilingDetail } from "~/api/qualFiling";
|
|
|
|
|
|
import { fromPageResponse, toPageQuery, toStaffForm, toEquipForm } from "~/api/enterpriseInfo/adapter";
|
|
|
|
|
|
import { apiGet } from "~/api/enterpriseInfo/http";
|
|
|
|
|
|
import { asId } from "~/api/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("/safety-eval/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,
|
|
|
|
|
|
}));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function mapStaffRowToFilingPersonnel(row = {}) {
|
|
|
|
|
|
const id = asId(row.id);
|
|
|
|
|
|
return {
|
|
|
|
|
|
id: `local-person-${id}`,
|
|
|
|
|
|
sourcePersonnelId: id,
|
|
|
|
|
|
personName: 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 {
|
|
|
|
|
|
id: `local-equip-${id}`,
|
|
|
|
|
|
sourceEquipmentId: id,
|
|
|
|
|
|
deviceName: row.deviceName,
|
|
|
|
|
|
deviceModel: row.deviceModel,
|
|
|
|
|
|
manufacturer: row.manufacturer,
|
|
|
|
|
|
calibrationReportUrl: row.calibrationReportUrl || "",
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 将本地表单全量同步到后端(暂存 / 提交确认时调用)
|
|
|
|
|
|
*/
|
|
|
|
|
|
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(draftRes?.message || "创建草稿失败");
|
|
|
|
|
|
}
|
|
|
|
|
|
filingId = draftRes.data.id;
|
|
|
|
|
|
basicValues.id = filingId;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const saveRes = await props.qualFilingSaveDraft({ ...basicValues, id: filingId });
|
|
|
|
|
|
if (saveRes?.success === false) {
|
|
|
|
|
|
throw new Error(saveRes?.message || "暂存基本信息失败");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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(commitmentRes?.message || "暂存承诺书失败");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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 }) {
|
2026-07-01 17:57:42 +08:00
|
|
|
|
|
2026-06-30 17:32:29 +08:00
|
|
|
|
return {
|
|
|
|
|
|
...detail,
|
|
|
|
|
|
...basicValues,
|
2026-07-01 17:57:42 +08:00
|
|
|
|
attachmentUrl: basicValues?.attachmentUrl?.map((file) => file.url).join(",") || undefined,
|
2026-06-30 17:32:29 +08:00
|
|
|
|
materials: detail.materials,
|
|
|
|
|
|
personnelList: detail.personnelList,
|
|
|
|
|
|
equipmentList: detail.equipmentList,
|
|
|
|
|
|
commitment: {
|
|
|
|
|
|
...detail.commitment,
|
|
|
|
|
|
...commitmentValues,
|
2026-07-02 11:00:31 +08:00
|
|
|
|
legalRepSignatureUrl: commitmentValues?.legalRepSignatureUrl?.map((file) => file.url).join(",") || undefined,
|
2026-06-30 17:32:29 +08:00
|
|
|
|
},
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|