diff --git a/src/api/enterpriseInfo/adapter.js b/src/api/enterpriseInfo/adapter.js index 5aa4e91..7caf1d8 100644 --- a/src/api/enterpriseInfo/adapter.js +++ b/src/api/enterpriseInfo/adapter.js @@ -1,10 +1,33 @@ /** 企业信息管理:前后端字段与分页格式适配 */ import { formatDate, formatDateTime, 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 []; + } + return String(urls) + .split(",") + .map((url) => url.trim()) + .filter(Boolean) + .map((url, index) => ({ + name: `${defaultName.replace(".pdf", "")}${index + 1}.pdf`, + fileName: `${defaultName.replace(".pdf", "")}${index + 1}.pdf`, + url, + })); +} + +/** 分页查询参数:子表 org_id → org_info.id;机构信息管理 getInfo 不走此方法 */ export function toPageQuery(params = {}, fieldMap = {}) { const query = { current: params.pageIndex ?? params.current ?? 1, @@ -13,10 +36,10 @@ export function toPageQuery(params = {}, fieldMap = {}) { Object.entries(fieldMap).forEach(([from, to]) => { const value = params[from]; if (value !== undefined && value !== null && value !== "") { - query[to] = value; + query[to] = isIdFieldName(to) ? asId(value) : value; } }); - return query; + return withOrgId(normalizeQueryIds(query)); } export function fromPageResponse(res, mapItem) { @@ -45,7 +68,7 @@ function pick(obj, keys) { const next = {}; keys.forEach((key) => { if (obj[key] !== undefined) { - next[key] = obj[key]; + next[key] = isIdFieldName(key) ? asId(obj[key]) : obj[key]; } }); return next; @@ -55,7 +78,7 @@ function pick(obj, keys) { export function toOrgInfoForm(data = {}) { return { - id: data.id, + id: asId(data.id), orgName: data.unitName, creditCode: data.creditCode, safetyIndustryCategory: data.safetyIndustryCategoryName, @@ -83,16 +106,25 @@ export function toOrgInfoForm(data = {}) { 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, @@ -118,6 +150,15 @@ export function fromOrgInfoForm(values = {}, options = {}) { 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), }; } @@ -125,8 +166,8 @@ export function fromOrgInfoForm(values = {}, options = {}) { export function toDepartmentForm(data = {}) { return { - id: data.id, - parentId: data.parentId, + id: asId(data.id), + parentId: asId(data.parentId), deptName: data.deptName, leaderName: data.managerName, leaderAccount: data.managerAccount, @@ -148,13 +189,14 @@ export function fromDepartmentForm(values = {}) { export function buildDepartmentTree(departments = []) { const map = new Map(); departments.forEach((item) => { - map.set(item.id, { ...toDepartmentForm(item), children: [] }); + map.set(asId(item.id), { ...toDepartmentForm(item), children: [] }); }); const roots = []; departments.forEach((item) => { - const node = map.get(item.id); - if (item.parentId != null && item.parentId !== 0 && map.has(item.parentId)) { - map.get(item.parentId).children.push(node); + 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); @@ -165,8 +207,8 @@ export function buildDepartmentTree(departments = []) { export function toPositionForm(data = {}) { return { - id: data.id, - deptId: data.deptId, + id: asId(data.id), + deptId: asId(data.deptId), deptName: data.deptName, positionName: data.positionName, dutyDesc: data.dutyDesc, @@ -175,10 +217,9 @@ export function toPositionForm(data = {}) { } export function fromPositionForm(values = {}) { - const deptId = values.deptId != null && values.deptId !== "" ? Number(values.deptId) : undefined; return { ...pick(values, ["id", "tenantId"]), - deptId, + deptId: asId(values.deptId), positionName: values.positionName, dutyDesc: values.dutyDesc, remark: values.remark, @@ -189,20 +230,32 @@ export function fromPositionForm(values = {}) { export function toStaffForm(data = {}) { return { - id: data.id, + id: asId(data.id), staffName: data.userName, account: data.account, gender: data.genderCode, birthDate: data.birthDate, - deptId: data.deptId, - positionId: data.postId, + 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, @@ -211,23 +264,46 @@ export function toStaffForm(data = {}) { 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", "deptId", "postId"]), + ...pick(values, ["id", "tenantId"]), userName: values.staffName, account: values.account, genderCode, genderName: GENDER_NAME[genderCode] ?? values.genderName, birthDate: toApiDate(values.birthDate), - postId: values.positionId ?? values.postId, + deptId: asId(values.deptId), + postId: asId(values.positionId ?? values.postId), idCardNo: values.idCardNo, currentAddress: values.homeAddress, officeAddress: values.officeAddress, - educationName: values.education, - educationCode: values.education, + 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), }; } @@ -238,8 +314,8 @@ export function toStaffCertForm(data = {}) { ? [{ url: data.certAttachmentUrl, fileName: data.certName }] : data.certImgFiles || []; return { - id: data.id, - staffId: data.personnelId, + id: asId(data.id), + staffId: asId(data.personnelId), certName: data.certName, certCategory: data.certCategoryName ?? data.certCategoryCode, certWorkCategory: data.operationCategoryName ?? data.operationCategoryCode, @@ -259,7 +335,7 @@ export function fromStaffCertForm(values = {}) { || values.certImgFiles?.[0]?.url; return { ...pick(values, ["id", "tenantId"]), - personnelId: values.staffId, + personnelId: asId(values.staffId), certName: values.certName, certCategoryName: values.certCategory, certCategoryCode: values.certCategory, @@ -276,12 +352,27 @@ export function fromStaffCertForm(values = {}) { // ─── 机构资质证书 ─── +/** 资质证书 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 ? [{ url: data.certImageUrl, fileName: data.certName }] : data.certImgFiles || []; return { - id: data.id, + id: asId(data.id), certType: data.licenseTypeName ?? data.licenseTypeCode, certName: data.certName, certNo: data.certNo, @@ -290,7 +381,8 @@ export function toQualificationForm(data = {}) { validStartDate: data.validStartDate, validEndDate: data.validEndDate, remark: data.remark ?? data.remarks, - enableStatus: data.enableFlag, + enableFlag: data.enableFlag, + enableStatus: mapQualificationEnableStatus(data.enableFlag), issueDate: formatDate(data.issueDate), validStartDate: formatDate(data.validStartDate), validEndDate: formatDate(data.validEndDate), @@ -315,18 +407,32 @@ export function fromQualificationForm(values = {}) { validEndDate: toApiDate(values.validEndDate ?? values.validDate?.[1]), certImageUrl, remark: values.remark, - enableFlag: values.enableStatus ?? 1, + enableFlag: mapQualificationEnableFlag(values.enableStatus), }; } // ─── 装备 ─── +/** 装备 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: data.id, - instrumentCategory: instrumentType, + id: asId(data.id), instrumentType, + instrumentCategory: instrumentType, deviceType: data.deviceTypeName ?? data.deviceTypeCode, deviceName: data.deviceName, model: data.deviceModel, @@ -338,7 +444,8 @@ export function toEquipForm(data = {}) { calibrationInitialValue: data.calibrationInitValue, onSiteCalibrationType: data.fieldCalibrationTypeName ?? data.fieldCalibrationTypeCode, isDualChannel: data.dualChannelFlag, - enableStatus: data.enableFlag, + enableFlag: data.enableFlag, + enableStatus: mapEquipEnableStatus(data.enableFlag), }; } @@ -360,7 +467,7 @@ export function fromEquipForm(values = {}) { fieldCalibrationTypeName: values.onSiteCalibrationType, fieldCalibrationTypeCode: values.onSiteCalibrationType, dualChannelFlag: values.isDualChannel, - enableFlag: values.enableStatus ?? 1, + enableFlag: mapEquipEnableFlag(values.enableStatus), }; } @@ -368,8 +475,8 @@ export function fromEquipForm(values = {}) { export function toChangeLogForm(data = {}) { return { - id: data.id, - staffId: data.personnelId, + id: asId(data.id), + staffId: asId(data.personnelId), changeItem: data.changeItem, changeTime: formatDateTime(data.changeTime), createBy: data.operatorName, @@ -379,8 +486,8 @@ export function toChangeLogForm(data = {}) { export function toResignApplyForm(data = {}) { const auditStatus = data.auditStatusCode ?? data.auditStatus ?? 0; return { - id: data.id, - personnelId: data.personnelId, + id: asId(data.id), + personnelId: asId(data.personnelId), applicantName: data.applicantName, applyTime: formatDateTime(data.applyTime), expectedLeaveDate: formatDate(data.expectedResignDate), @@ -396,10 +503,9 @@ export function toResignApplyForm(data = {}) { } export function fromResignApplyForm(values = {}) { - const personnelId = values.personnelId; return { ...pick(values, ["id", "tenantId"]), - personnelId: personnelId != null && personnelId !== "" ? Number(personnelId) : undefined, + personnelId: asId(values.personnelId), applicantName: values.applicantName, applyTime: toApiDateTime(values.applyTime), expectedResignDate: toApiDate(values.expectedLeaveDate), @@ -414,8 +520,8 @@ export function fromResignApplyForm(values = {}) { export function fromResignAuditForm(values = {}) { const auditStatus = values.auditStatus; return { - id: values.id, - personnelId: values.personnelId, + id: asId(values.id), + personnelId: asId(values.personnelId), auditStatusCode: auditStatus, auditStatusName: auditStatus === 1 ? "已审核" : auditStatus === 2 ? "已退回" : "未审核", rejectReason: values.rejectReason, @@ -433,8 +539,8 @@ export function toStaffChangeSummary(personnel = {}) { : Number(resignStatusRaw); } return { - id: personnel.id, - staffId: personnel.id, + id: asId(personnel.id), + staffId: asId(personnel.id), account: personnel.account, staffName: personnel.userName ?? personnel.staffName, deptName: personnel.deptName || "", @@ -443,7 +549,7 @@ export function toStaffChangeSummary(personnel = {}) { employmentStatusName: personnel.employmentStatusName || (employmentCode === 2 ? "离职" : "在职"), changeCount: Number(personnel.changeCount ?? 0), - resignApplyId: personnel.resignApplyId, + resignApplyId: asId(personnel.resignApplyId), resignAuditStatus: resignStatus, resignAuditStatusName: personnel.resignAuditStatusName ?? (resignStatus !== null ? RESIGN_AUDIT_STATUS_NAME[resignStatus] : undefined), diff --git a/src/api/enterpriseInfo/http.js b/src/api/enterpriseInfo/http.js index 331d638..7f17b50 100644 --- a/src/api/enterpriseInfo/http.js +++ b/src/api/enterpriseInfo/http.js @@ -1,4 +1,25 @@ import { Get, Post } from "@cqsjjb/jjb-common-lib/http"; +import { normalizeQueryIds, withIds, asId } from "./idUtil"; +import { buildOrgInfoHeaders } from "./orgContext"; + +/** + * jjb-common-lib 在 jjbCommonHttpConfig.header 存在时会完全替换请求头、不再读 sessionStorage。 + * 因此 token / orgInfoId 都通过每次请求的 headers 参数显式传入。 + * + * @param options.includeOrgContext 默认 true;机构 getInfo 查询应传 false(不带 orgInfoId) + */ +function buildRequestHeaders(extraHeaders = {}, options = {}) { + const { includeOrgContext = true } = options; + const headers = { ...extraHeaders }; + const token = sessionStorage.getItem("token"); + if (token && !headers.token) { + headers.token = token; + } + if (!includeOrgContext) { + return headers; + } + return buildOrgInfoHeaders(headers); +} function parseHttpError(err) { const data = err?.response?.data; @@ -14,9 +35,9 @@ function parseHttpError(err) { } /** 底层 HTTP,供 adapter 层组合调用(不可嵌套 declareRequest) */ -export async function apiGet(path, params = {}) { +export async function apiGet(path, params = {}, headers = {}, options = {}) { try { - return await Get(path, params); + return await Get(path, normalizeQueryIds(params), buildRequestHeaders(headers, options)); } catch (err) { return parseHttpError(err); @@ -24,10 +45,10 @@ export async function apiGet(path, params = {}) { } /** @ 前缀表示 Post 请求体不包裹 request 字段 */ -export async function apiPost(path, data = {}) { +export async function apiPost(path, data = {}, headers = {}) { const url = path.startsWith("@") ? path : `@${path}`; try { - return await Post(url, data); + return await Post(url, withIds(data), buildRequestHeaders(headers)); } catch (err) { return parseHttpError(err); @@ -35,9 +56,9 @@ export async function apiPost(path, data = {}) { } export async function apiPostDelete(path, id) { - const url = `${path}?id=${encodeURIComponent(id)}`; + const url = `${path}?id=${encodeURIComponent(asId(id) ?? "")}`; try { - return await Post(url, {}); + return await Post(url, {}, buildRequestHeaders()); } catch (err) { return parseHttpError(err); diff --git a/src/api/enterpriseInfo/idUtil.js b/src/api/enterpriseInfo/idUtil.js new file mode 100644 index 0000000..16ae3f5 --- /dev/null +++ b/src/api/enterpriseInfo/idUtil.js @@ -0,0 +1,57 @@ +/** + * 雪花 ID 全局处理:禁止 Number(),统一字符串传递。 + * JS Number.MAX_SAFE_INTEGER = 9007199254740991,雪花 id 超出后会精度丢失。 + */ + +export function asId(value) { + if (value == null || value === "") { + return undefined; + } + return String(value); +} + +/** 是否像主键/外键字段名 */ +export function isIdFieldName(key) { + return key === "id" || key.endsWith("Id"); +} + +/** 对象内所有 *Id / id 字段转字符串 */ +export function withIds(obj = {}) { + if (!obj || typeof obj !== "object") { + return obj; + } + const next = { ...obj }; + Object.keys(next).forEach((key) => { + if (isIdFieldName(key) && next[key] != null && next[key] !== "") { + next[key] = asId(next[key]); + } + }); + return next; +} + +/** 分页/查询参数中的 id 字段转字符串 */ +export function normalizeQueryIds(query = {}) { + const next = { ...query }; + Object.keys(next).forEach((key) => { + if (isIdFieldName(key) && next[key] != null && next[key] !== "") { + next[key] = asId(next[key]); + } + }); + return next; +} + +/** 兼容历史 Number() 精度丢失:比较雪花 id 前 16 位 */ +export function sameId(a, b) { + const sa = asId(a); + const sb = asId(b); + if (!sa || !sb) { + return false; + } + if (sa === sb) { + return true; + } + return sa.slice(0, 16) === sb.slice(0, 16); +} + +/** @deprecated 使用 sameId */ +export const sameDeptId = sameId; diff --git a/src/api/enterpriseInfo/orgBootstrap.js b/src/api/enterpriseInfo/orgBootstrap.js new file mode 100644 index 0000000..0738ab9 --- /dev/null +++ b/src/api/enterpriseInfo/orgBootstrap.js @@ -0,0 +1,95 @@ +/** + * 企业信息模块初始化与下拉数据拉取(绕过 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"; + +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}/`); +} + +export function getOrgInfoDetail() { + return lastOrgInfoDetail; +} + +export function setOrgInfoDetailCache(detail) { + lastOrgInfoDetail = detail; +} + +function fetchOrgInfoContext() { + return apiGet("/safety-eval/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 }; + } + 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("/safety-eval/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("/safety-eval/org-position/page", query); + return fromPageResponse(res, toPositionForm); +} diff --git a/src/api/enterpriseInfo/orgContext.js b/src/api/enterpriseInfo/orgContext.js new file mode 100644 index 0000000..17e11c6 --- /dev/null +++ b/src/api/enterpriseInfo/orgContext.js @@ -0,0 +1,48 @@ +/** 当前登录用户关联的机构 id,供企业信息模块接口请求头 orgInfoId 使用 */ + +const ORG_INFO_ID_KEY = "orgInfoId"; + +let memoryOrgInfoId = null; + +export function getOrgInfoId() { + return memoryOrgInfoId || sessionStorage.getItem(ORG_INFO_ID_KEY) || null; +} + +export function setOrgInfoId(id) { + if (id != null && id !== "") { + memoryOrgInfoId = String(id); + sessionStorage.setItem(ORG_INFO_ID_KEY, memoryOrgInfoId); + } +} + +export function clearOrgInfoId() { + memoryOrgInfoId = null; + sessionStorage.removeItem(ORG_INFO_ID_KEY); +} + +/** 分页/查询参数:前端缓存 orgInfoId → 后端字段 orgId */ +export function withOrgId(params = {}) { + if (params.orgId != null && params.orgId !== "") { + return params; + } + const orgInfoId = getOrgInfoId(); + if (orgInfoId) { + return { ...params, orgId: orgInfoId }; + } + return params; +} + +/** 合并业务请求头,仅通过每次请求的 headers 参数传递,不写入 jjbCommonHttpConfig */ +export function buildOrgInfoHeaders(extraHeaders = {}) { + const id = extraHeaders.orgInfoId || getOrgInfoId(); + if (!id) { + return extraHeaders; + } + return { ...extraHeaders, orgInfoId: String(id) }; +} + +// 页面刷新后从 sessionStorage 恢复 +const storedOrgInfoId = sessionStorage.getItem(ORG_INFO_ID_KEY); +if (storedOrgInfoId) { + memoryOrgInfoId = storedOrgInfoId; +} diff --git a/src/api/equipInfo/index.js b/src/api/equipInfo/index.js index 70aa4e3..3c1c76c 100644 --- a/src/api/equipInfo/index.js +++ b/src/api/equipInfo/index.js @@ -3,6 +3,7 @@ import { fromEquipForm, fromPageResponse, fromSingleResponse, + mapEquipEnableFlag, toEquipForm, toPageQuery, } from "../enterpriseInfo/adapter"; @@ -15,6 +16,9 @@ export const equipInfoList = declareRequest("equipInfoLoading", safePageResult(a deviceType: "deviceType", enableStatus: "enableFlag", }); + if (query.enableFlag !== undefined && query.enableFlag !== "") { + query.enableFlag = mapEquipEnableFlag(query.enableFlag); + } const res = await apiGet("/safety-eval/org-equipment/page", query); return fromPageResponse(res, toEquipForm); })); @@ -39,9 +43,12 @@ export const equipInfoRemove = declareRequest("equipInfoLoading", safeAction(asy })); export const equipInfoToggleStatus = declareRequest("equipInfoLoading", safeAction(async ({ id }) => { - const detailRes = await apiGet("/safety-eval/org-equipment/get", { id }); - const current = toEquipForm(detailRes?.data || {}); - const nextStatus = current.enableStatus === 1 ? 0 : 1; - const res = await apiPost("/safety-eval/org-equipment/modify", fromEquipForm({ ...current, id, enableStatus: nextStatus })); - return fromSingleResponse(res, toEquipForm); + const res = await apiGet("/safety-eval/org-equipment/get", { id }); + const data = res?.data || {}; + const nextFlag = Number(data.enableFlag) === 1 ? 2 : 1; + return apiPost("/safety-eval/org-equipment/modify", { + ...data, + id, + enableFlag: nextFlag, + }); })); diff --git a/src/api/orgDepartment/index.js b/src/api/orgDepartment/index.js index a312512..cfeaf7a 100644 --- a/src/api/orgDepartment/index.js +++ b/src/api/orgDepartment/index.js @@ -15,7 +15,7 @@ export const orgDepartmentGet = declareRequest("orgDepartmentLoading", safeActio })); export const orgDepartmentTree = declareRequest("orgDepartmentLoading", safeAction(async () => { - const res = await apiGet("/safety-eval/org-department/page", { current: 1, size: 500 }); + const res = await apiGet("/safety-eval/org-department/page", toPageQuery({ current: 1, size: 500 })); const pageData = fromPageResponse(res, toDepartmentForm); return { success: true, diff --git a/src/api/orgInfo/index.js b/src/api/orgInfo/index.js index 0104b2b..f08f65b 100644 --- a/src/api/orgInfo/index.js +++ b/src/api/orgInfo/index.js @@ -1,35 +1,54 @@ import { declareRequest } from "@cqsjjb/jjb-dva-runtime"; import { fromOrgInfoForm, - fromPageResponse, fromSingleResponse, toOrgInfoForm, } from "../enterpriseInfo/adapter"; import { apiGet, apiPost, safeAction } from "../enterpriseInfo/http"; +import { setOrgInfoDetailCache } from "../enterpriseInfo/orgBootstrap"; +import { setOrgInfoId } from "../enterpriseInfo/orgContext"; + +function persistOrgInfoId(result, rawData) { + const id = result?.data?.id ?? rawData?.id; + if (id) { + setOrgInfoId(id); + } + setOrgInfoDetailCache(result); + return result; +} + +function buildSaveHeaders(payload) { + if (payload?.id) { + setOrgInfoId(payload.id); + return { orgInfoId: String(payload.id) }; + } + return {}; +} export const orgInfoGet = declareRequest("orgInfoLoading", safeAction(async () => { - const pageRes = await apiGet("/safety-eval/org-info/page", { current: 1, size: 1 }); - const pageData = fromPageResponse(pageRes, toOrgInfoForm); - const first = pageData.data?.[0]; - if (!first?.id) { - return { success: true, data: null }; - } - const detailRes = await apiGet("/safety-eval/org-info/get", { id: first.id }); - return fromSingleResponse(detailRes, toOrgInfoForm); + const res = await apiGet("/safety-eval/org-info/getInfo", {}, {}, { includeOrgContext: false }); + const result = fromSingleResponse(res, toOrgInfoForm); + return persistOrgInfoId(result, res?.data); })); export const orgInfoSave = declareRequest("orgInfoLoading", safeAction(async (values) => { const payload = fromOrgInfoForm(values, { isDraft: false }); + const headers = buildSaveHeaders(payload); const res = payload.id - ? await apiPost("/safety-eval/org-info/modify", payload) - : await apiPost("/safety-eval/org-info/save", payload); - return fromSingleResponse(res, toOrgInfoForm); + ? await apiPost("/safety-eval/org-info/modify", payload, headers) + : await apiPost("/safety-eval/org-info/save", payload, headers); + const result = fromSingleResponse(res, toOrgInfoForm); + persistOrgInfoId(result, res?.data); + return result; })); export const orgInfoDraft = declareRequest("orgInfoLoading", safeAction(async (values) => { const payload = fromOrgInfoForm(values, { isDraft: true }); + const headers = buildSaveHeaders(payload); const res = payload.id - ? await apiPost("/safety-eval/org-info/modify", payload) - : await apiPost("/safety-eval/org-info/save", payload); - return fromSingleResponse(res, toOrgInfoForm); + ? await apiPost("/safety-eval/org-info/modify", payload, headers) + : await apiPost("/safety-eval/org-info/save", payload, headers); + const result = fromSingleResponse(res, toOrgInfoForm); + persistOrgInfoId(result, res?.data); + return result; })); diff --git a/src/api/orgQualificationCert/index.js b/src/api/orgQualificationCert/index.js index a184b6f..0e202e6 100644 --- a/src/api/orgQualificationCert/index.js +++ b/src/api/orgQualificationCert/index.js @@ -34,21 +34,21 @@ export const orgQualificationCertRemove = declareRequest("orgQualificationCertLo })); export const orgQualificationCertDisable = declareRequest("orgQualificationCertLoading", safeAction(async ({ id }) => { - const detailRes = await apiGet("/safety-eval/org-qualification/get", { id }); - const payload = fromQualificationForm({ - ...toQualificationForm(detailRes?.data || {}), + const res = await apiGet("/safety-eval/org-qualification/get", { id }); + const data = res?.data || {}; + return apiPost("/safety-eval/org-qualification/modify", { + ...data, id, - enableStatus: 0, + enableFlag: 2, }); - return apiPost("/safety-eval/org-qualification/modify", payload); })); export const orgQualificationCertEnable = declareRequest("orgQualificationCertLoading", safeAction(async ({ id }) => { - const detailRes = await apiGet("/safety-eval/org-qualification/get", { id }); - const payload = fromQualificationForm({ - ...toQualificationForm(detailRes?.data || {}), + const res = await apiGet("/safety-eval/org-qualification/get", { id }); + const data = res?.data || {}; + return apiPost("/safety-eval/org-qualification/modify", { + ...data, id, - enableStatus: 1, + enableFlag: 1, }); - return apiPost("/safety-eval/org-qualification/modify", payload); })); diff --git a/src/enumerate/enterpriseOptions.js b/src/enumerate/enterpriseOptions.js new file mode 100644 index 0000000..2f15fc0 --- /dev/null +++ b/src/enumerate/enterpriseOptions.js @@ -0,0 +1,91 @@ +/** 企业信息管理模块下拉选项 */ + +/** 重庆市辖区(区/县) */ +export const CHONGQING_DISTRICTS = [ + "万州区", "涪陵区", "渝中区", "大渡口区", "江北区", "沙坪坝区", "九龙坡区", "南岸区", + "北碚区", "綦江区", "大足区", "渝北区", "巴南区", "黔江区", "长寿区", "江津区", + "合川区", "永川区", "南川区", "璧山区", "铜梁区", "潼南区", "荣昌区", "开州区", + "梁平区", "武隆区", "城口县", "丰都县", "垫江县", "忠县", "云阳县", "奉节县", + "巫山县", "巫溪县", "石柱土家族自治县", "秀山土家族苗族自治县", "酉阳土家族苗族自治县", + "彭水苗族土家族自治县", +].map((label) => ({ label, value: label })); + +/** 企业状态 */ +export const ENTERPRISE_STATUS_OPTIONS = [ + { label: "正常", value: "正常" }, + { label: "停业", value: "停业" }, + { label: "注销", value: "注销" }, +]; + +/** 企业规模 */ +export const ENTERPRISE_SCALE_OPTIONS = [ + { label: "大", value: "大" }, + { label: "中", value: "中" }, + { label: "小", value: "小" }, + { label: "微型", value: "微型" }, +]; + +/** 备案类型 */ +export const FILING_TYPE_OPTIONS = [ + { label: "审核备案", value: "审核备案" }, + { label: "确认备案", value: "确认备案" }, +]; + +/** 备案状态(机构信息) */ +export const FILING_RECORD_STATUS_OPTIONS = [ + { label: "已备案", value: "已备案" }, + { label: "未备案", value: "未备案" }, +]; + +/** 资质范围 / 证照类型(行业) */ +export const QUALIFICATION_INDUSTRY_OPTIONS = [ + { label: "煤炭开采业", value: "煤炭开采业" }, + { label: "金属、非金属矿及其他矿采选业", value: "金属、非金属矿及其他矿采选业" }, + { label: "陆地石油和天然气开采业", value: "陆地石油和天然气开采业" }, + { label: "陆上油气管道运输业", value: "陆上油气管道运输业" }, + { label: "石油加工业,化学原料、化学品及医药制造业", value: "石油加工业,化学原料、化学品及医药制造业" }, + { label: "烟花爆竹制造业", value: "烟花爆竹制造业" }, + { label: "金属冶炼", value: "金属冶炼" }, +]; + +/** 人员类型 */ +export const PERSON_TYPE_OPTIONS = [ + { label: "基础人员", value: "基础人员" }, + { label: "专职评价师", value: "专职评价师" }, +]; + +/** 职业等级 */ +export const PROFESSIONAL_LEVEL_OPTIONS = [ + { + label: "三级安全评价师(对应职业技能等级三级 / 高级工,入门级)", + value: "三级安全评价师(对应职业技能等级三级 / 高级工,入门级)", + }, + { + label: "二级安全评价师(对应职业技能等级二级 / 技师,中级)", + value: "二级安全评价师(对应职业技能等级二级 / 技师,中级)", + }, + { + label: "一级安全评价师(对应职业技能等级一级 / 高级技师,最高级)", + value: "一级安全评价师(对应职业技能等级一级 / 高级技师,最高级)", + }, +]; + +/** 学历类型 */ +export const EDUCATION_TYPE_OPTIONS = [ + { label: "全日制", value: "全日制" }, + { label: "在职教育", value: "在职教育" }, +]; + +/** 学历层次 */ +export const EDUCATION_LEVEL_OPTIONS = [ + { label: "专科", value: "专科" }, + { label: "本科", value: "本科" }, + { label: "硕士", value: "硕士" }, + { label: "博士", value: "博士" }, +]; + +/** 是否注册安全工程师 */ +export const REGISTER_ENGINEER_OPTIONS = [ + { label: "是", value: 1 }, + { label: "否", value: 2 }, +]; diff --git a/src/main.js b/src/main.js index ecdcb38..2fb5044 100644 --- a/src/main.js +++ b/src/main.js @@ -25,7 +25,7 @@ if (!window.__POWERED_BY_QIANKUN__) { // 本地开发时注入 Token,使 API 请求可携带认证信息 sessionStorage.setItem( "token", - "jjb-saas-auth:oauth:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ7XCJjbGllbnRJZFwiOlwiWlFBUVBKXCIsXCJhY2NvdW50SWRcIjoyMDY5Njc0MjM3ODc0MDEyMTYwLFwidXNlclR5cGVFbnVtXCI6XCJQTEFURk9STVwiLFwidXNlcklkXCI6MjA2OTY3NDIzNzE3MzQzNjQxNixcInRlbmFudElkXCI6MjA2OTU5NjM5NzkyMDg0OTkyMCxcInRlbmFudE5hbWVcIjpcIumHjeW6huWuieWFqOivhOS7t1wiLFwidGVuYW50UGFyZW50SWRzXCI6XCIwLDIwNjk1OTYzOTc5MjA4NDk5MjBcIixcIm5hbWVcIjpcInRlc3QwMVwiLFwiYWNjZXNzVGlja2V0XCI6XCJ2VnVmZjB1ck4xN21yT1BHTHFLcHBDV3BoRXAxTk96eTc5elp4THV1T3VLTUc5M1c4cENNd0NQZTN3MmlcIixcInJlZnJlc2hUaWNrZXRcIjpcIlhIeTRLZjJITXNYVjJCVHdnaWxUU0JXbDMxRGpObnVYVTBhUmRCZTlYRHpVTHVub2RMdnRveFpBeUFHclwiLFwiZXhwaXJlSW5cIjo2MDQ4MDAsXCJyZWZyZXNoRXhwaXJlc0luXCI6NjA0ODAwLFwib3JnSWRcIjoyMDY5NTk2Mzk3OTIwODQ5OTIwLFwib3JnTmFtZVwiOlwi6YeN5bqG5a6J5YWo6K-E5Lu3XCIsXCJvcmdJZHNcIjpbMjA2OTU5NjM5NzkyMDg0OTkyMF0sXCJyb2xlc1R5cGVzXCI6W1wiR0xZSlNcIl0sXCJyb2xlSWRzXCI6WzIwNjk2NzEzMDc1OTg4OTMwNThdLFwic2NvcGVzXCI6W10sXCJycGNUeXBlRW51bVwiOlwiSFRUUFwiLFwiYmluZE1vYmlsZVNpZ25cIjpcIkZBTFNFXCJ9IiwiaXNzIjoicHJvLXNlcnZlciIsImV4cCI6MTc4Mjg4ODYwNH0.Rypiw0-5EWQ8V_r7dr2A6ZHd82lwBcICX2t4WqybcJ0" + "jjb-saas-auth:oauth:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ7XCJjbGllbnRJZFwiOlwiWlFBUVBKXCIsXCJhY2NvdW50SWRcIjoyMDY5Njc0MjM3ODc0MDEyMTYwLFwidXNlclR5cGVFbnVtXCI6XCJQTEFURk9STVwiLFwidXNlcklkXCI6MjA2OTY3NDIzNzE3MzQzNjQxNixcInRlbmFudElkXCI6MjA2OTU5NjM5NzkyMDg0OTkyMCxcInRlbmFudE5hbWVcIjpcIumHjeW6huWuieWFqOivhOS7t1wiLFwidGVuYW50UGFyZW50SWRzXCI6XCIwLDIwNjk1OTYzOTc5MjA4NDk5MjBcIixcIm5hbWVcIjpcInRlc3QwMVwiLFwiYWNjZXNzVGlja2V0XCI6XCJ2Umg1RHRlNWJWcnc3NGxoZVgzRVVGYlVUV2l0MVJFbkVIc2ZWZnlGYnJwWDd1bmNsMGdsSmtJMWhvWWVcIixcInJlZnJlc2hUaWNrZXRcIjpcIlJva2VKeEcwYmIwYk1pMm8xUG53MjJpRkh6V21FSHVtWkFwSFI5dmptYmUyYm9DYkoyNnE3UmxIbUNlUFwiLFwiZXhwaXJlSW5cIjo2MDQ4MDAsXCJyZWZyZXNoRXhwaXJlc0luXCI6NjA0ODAwLFwib3JnSWRcIjoyMDY5NTk2Mzk3OTIwODQ5OTIwLFwib3JnTmFtZVwiOlwi6YeN5bqG5a6J5YWo6K-E5Lu3XCIsXCJvcmdJZHNcIjpbMjA2OTU5NjM5NzkyMDg0OTkyMF0sXCJyb2xlc1R5cGVzXCI6W1wiR0xZSlNcIl0sXCJyb2xlSWRzXCI6WzIwNjk2NzEzMDc1OTg4OTMwNThdLFwic2NvcGVzXCI6W10sXCJycGNUeXBlRW51bVwiOlwiSFRUUFwiLFwiYmluZE1vYmlsZVNpZ25cIjpcIkZBTFNFXCJ9IiwiaXNzIjoicHJvLXNlcnZlciIsImV4cCI6MTc4Mjk3MTE5Nn0.TagXXQBNyBioQQqhRFHwfgStlQmLi1s5StPOp-gSyzw" ); } diff --git a/src/pages/Container/EnterpriseInfo/DepartmentPosition/index.js b/src/pages/Container/EnterpriseInfo/DepartmentPosition/index.js index 83dd297..02d1fbd 100644 --- a/src/pages/Container/EnterpriseInfo/DepartmentPosition/index.js +++ b/src/pages/Container/EnterpriseInfo/DepartmentPosition/index.js @@ -7,12 +7,13 @@ import Search from "zy-react-library/components/Search"; import Table from "zy-react-library/components/Table"; import useTable from "zy-react-library/hooks/useTable"; import { NS_ORG_DEPARTMENT, NS_ORG_POSITION, NS_STAFF_INFO } from "~/enumerate/namespace"; +import { asId, sameId } from "~/api/enterpriseInfo/idUtil"; import { safeListRequest, safeRequest } from "~/utils"; import PageHeader from "../components/PageHeader"; function findDeptNode(nodes = [], id) { for (const node of nodes) { - if (String(node.id) === String(id)) { + if (sameId(node.id, id)) { return node; } if (node.children?.length) { @@ -30,7 +31,7 @@ function toSelectedDept(node) { return null; } return { - id: node.id, + id: asId(node.id), deptName: node.deptName || node.title, leaderAccount: node.leaderAccount, leaderName: node.leaderName, @@ -71,7 +72,7 @@ function DepartmentPositionPage(props) { form: positionForm, transform: (formData) => ({ ...formData, - eqDeptId: selectedDept?.id != null ? Number(selectedDept.id) : undefined, + eqDeptId: asId(selectedDept?.id), }), }, ); @@ -136,7 +137,7 @@ function DepartmentPositionPage(props) { const res = await props.orgDepartmentRemove({ id }); if (res?.success !== false) { message.success("删除成功"); - if (String(selectedDept?.id) === String(id)) { + if (sameId(selectedDept?.id, id)) { setSelectedDept(null); } loadTree(); @@ -174,7 +175,7 @@ function DepartmentPositionPage(props) { }; const openDeptModal = (record) => { - setCurrentId(record?.id || ""); + setCurrentId(asId(record?.id) || ""); setDeptModalOpen(true); }; @@ -183,17 +184,17 @@ function DepartmentPositionPage(props) { message.warning("请先选择部门"); return; } - setCurrentId(record?.id || ""); + setCurrentId(asId(record?.id) || ""); modalForm.resetFields(); if (record) { modalForm.setFieldsValue({ ...record, - deptId: record.deptId || selectedDept.id, + deptId: asId(record.deptId || selectedDept.id), deptName: record.deptName || selectedDept.deptName, }); } else { - modalForm.setFieldsValue({ deptId: selectedDept.id, deptName: selectedDept.deptName }); + modalForm.setFieldsValue({ deptId: asId(selectedDept.id), deptName: selectedDept.deptName }); } setPositionModalOpen(true); }; @@ -204,7 +205,7 @@ function DepartmentPositionPage(props) { if (currentId) { payload.id = currentId; } - payload.deptId = values.deptId || selectedDept?.id; + payload.deptId = asId(values.deptId || selectedDept?.id); const res = await request(payload); if (res?.success !== false) { message.success(currentId ? "编辑成功" : "添加成功"); @@ -232,7 +233,7 @@ function DepartmentPositionPage(props) { /> { diff --git a/src/pages/Container/EnterpriseInfo/EquipInfo/index.js b/src/pages/Container/EnterpriseInfo/EquipInfo/index.js index 4ece7f2..f0fd442 100644 --- a/src/pages/Container/EnterpriseInfo/EquipInfo/index.js +++ b/src/pages/Container/EnterpriseInfo/EquipInfo/index.js @@ -8,11 +8,11 @@ import Table from "zy-react-library/components/Table"; import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; import useTable from "zy-react-library/hooks/useTable"; import { NS_EQUIP_INFO } from "~/enumerate/namespace"; +import { isEquipEnabled } from "~/api/enterpriseInfo/adapter"; import { safeListRequest, safeRequest } from "~/utils"; import { positiveNumberRule } from "~/utils/validators"; import PageHeader from "../components/PageHeader"; -const ENABLE_STATUS = { 1: "启用", 0: "禁用" }; const DUAL_CHANNEL = { 1: "是", 0: "否" }; function EquipInfoPage(props) { @@ -43,17 +43,21 @@ function EquipInfoPage(props) { }); }; - const onToggleStatus = (id, enableStatus) => { + const onToggleStatus = (id, record) => { + const enabled = isEquipEnabled(record); Modal.confirm({ title: "提示", - content: enableStatus === 1 ? "是否停用" : "是否启用", + content: enabled ? "是否停用" : "是否启用", okText: "是", cancelText: "否", onOk: async () => { const res = await props.equipInfoToggleStatus({ id }); if (res?.success !== false) { message.success("操作成功"); - getData(); + await getData(); + } + else { + message.error(res?.message || "操作失败"); } }, }); @@ -75,12 +79,13 @@ function EquipInfoPage(props) { name: "enableStatus", label: "设备状态", render: FORM_ITEM_RENDER_ENUM.SELECT, - options: Object.entries(ENABLE_STATUS).map(([value, label]) => ({ label, value: Number(value) })), + options: [{ label: "启用", value: 1 }, { label: "禁用", value: 0 }], }, ]} onFinish={getData} /> ( - ), }, ]} - {...tableProps} /> {formModalOpen && ( diff --git a/src/pages/Container/EnterpriseInfo/OrgInfo/index.js b/src/pages/Container/EnterpriseInfo/OrgInfo/index.js index 7223ae0..c3705a7 100644 --- a/src/pages/Container/EnterpriseInfo/OrgInfo/index.js +++ b/src/pages/Container/EnterpriseInfo/OrgInfo/index.js @@ -3,9 +3,20 @@ import { Button, Form, message, Space } from "antd"; import { useEffect, useState } from "react"; import FormBuilder from "zy-react-library/components/FormBuilder"; import Page from "zy-react-library/components/Page"; +import Upload from "zy-react-library/components/Upload"; import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import { getOrgInfoDetail } from "~/api/enterpriseInfo/orgBootstrap"; import PageHeader from "../components/PageHeader"; import { NS_ORG_INFO } from "~/enumerate/namespace"; +import { + CHONGQING_DISTRICTS, + ENTERPRISE_SCALE_OPTIONS, + ENTERPRISE_STATUS_OPTIONS, + FILING_RECORD_STATUS_OPTIONS, + FILING_TYPE_OPTIONS, +} from "~/enumerate/enterpriseOptions"; +import { formSelectField } from "~/utils/enterpriseForm"; +import { mockUploadFileList } from "~/utils/mockUpload"; import { creditCodeRule, latitudeRule, @@ -28,7 +39,7 @@ function OrgInfoPage(props) { const loadDetail = async () => { try { - const res = await props.orgInfoGet().catch(() => null); + const res = getOrgInfoDetail(); if (res?.data?.id) { setDetail(res.data); form.setFieldsValue(res.data); @@ -39,6 +50,11 @@ function OrgInfoPage(props) { setDetail({}); setHasExistingData(false); setEditing(true); + form.setFieldsValue({ + filingType: "确认备案", + filingRecordStatus: "未备案", + attachments: mockUploadFileList("资质申请书.pdf"), + }); } } catch (err) { @@ -118,9 +134,10 @@ function OrgInfoPage(props) { rules: [{ required: true, message: "请输入安全生产监管行业类别" }], }, { - name: "regionCountyName", - label: "所属(县、区)", - rules: [{ required: true, message: "请输入所属县区" }], + ...formSelectField("regionCountyName", "属地", CHONGQING_DISTRICTS, { + rules: [{ required: true, message: "请选择属地" }], + colProps: { span: 12 }, + }), }, { name: "regionStreetName", @@ -259,6 +276,18 @@ function OrgInfoPage(props) { componentProps: { ...numberFieldProps, min: 0, precision: 0 }, rules: [nonNegativeIntegerRule("注册安全工程师数量", false)], }, + formSelectField("enterpriseStatus", "企业状态", ENTERPRISE_STATUS_OPTIONS, { required: false }), + formSelectField("enterpriseScale", "企业规模", ENTERPRISE_SCALE_OPTIONS, { required: false }), + formSelectField("filingType", "备案类型", FILING_TYPE_OPTIONS, { required: false }), + formSelectField("filingRecordStatus", "备案状态", FILING_RECORD_STATUS_OPTIONS, { required: false }), + { + name: "attachments", + label: "上传附件", + required: false, + render: ( + + ), + }, ]; return ( diff --git a/src/pages/Container/EnterpriseInfo/PersonnelInfo/List/index.js b/src/pages/Container/EnterpriseInfo/PersonnelInfo/List/index.js index 3705821..3f78678 100644 --- a/src/pages/Container/EnterpriseInfo/PersonnelInfo/List/index.js +++ b/src/pages/Container/EnterpriseInfo/PersonnelInfo/List/index.js @@ -1,12 +1,23 @@ import { Connect } from "@cqsjjb/jjb-dva-runtime"; import { Button, DatePicker, Descriptions, Form, Input, message, Modal, Row, Col, Select, Space } from "antd"; -import { useEffect, useMemo, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import AddIcon from "zy-react-library/components/Icon/AddIcon"; import Page from "zy-react-library/components/Page"; import Search from "zy-react-library/components/Search"; import Table from "zy-react-library/components/Table"; +import Upload from "zy-react-library/components/Upload"; import useTable from "zy-react-library/hooks/useTable"; import { NS_ORG_DEPARTMENT, NS_ORG_POSITION, NS_STAFF_INFO } from "~/enumerate/namespace"; +import { + EDUCATION_LEVEL_OPTIONS, + EDUCATION_TYPE_OPTIONS, + PERSON_TYPE_OPTIONS, + PROFESSIONAL_LEVEL_OPTIONS, + QUALIFICATION_INDUSTRY_OPTIONS, + REGISTER_ENGINEER_OPTIONS, +} from "~/enumerate/enterpriseOptions"; +import { ensureOrgContext, fetchOrgDepartmentPage, fetchOrgPositionPage } from "~/api/enterpriseInfo/orgBootstrap"; +import { asId, sameId } from "~/api/enterpriseInfo/idUtil"; import { getBirthDateFromIdCard, safeListRequest, safeRequest } from "~/utils"; import { toDayjs } from "~/utils/dateFormat"; import { formSelectField } from "~/utils/enterpriseForm"; @@ -14,6 +25,15 @@ import { idCardRule, mobileRule } from "~/utils/validators"; import PageHeader from "../../components/PageHeader"; const GENDER_MAP = { 1: "男", 2: "女" }; +const REGISTER_ENGINEER_MAP = { 1: "是", 2: "否" }; + +function mapPositionOptions(list = []) { + return list.map((p) => ({ + label: p.positionName, + value: asId(p.id), + deptId: asId(p.deptId), + })); +} function PersonnelInfoPage(props) { const [formModalOpen, setFormModalOpen] = useState(false); @@ -24,23 +44,27 @@ function PersonnelInfoPage(props) { const [positionOptions, setPositionOptions] = useState([]); useEffect(() => { + let cancelled = false; (async () => { try { + await ensureOrgContext(); const [deptRes, posRes] = await Promise.all([ - props.orgDepartmentList?.({ pageIndex: 1, pageSize: 200 }), - props.orgPositionList?.({ pageIndex: 1, pageSize: 200 }), + fetchOrgDepartmentPage({ pageIndex: 1, pageSize: 200 }), + fetchOrgPositionPage({ pageIndex: 1, pageSize: 200 }), ]); - setDeptOptions((deptRes?.data || []).map((d) => ({ label: d.deptName, value: d.id }))); - setPositionOptions((posRes?.data || []).map((p) => ({ - label: p.positionName, - value: p.id, - deptId: p.deptId, - }))); + if (cancelled) { + return; + } + setDeptOptions((deptRes?.data || []).map((d) => ({ label: d.deptName, value: asId(d.id) }))); + setPositionOptions(mapPositionOptions(posRes?.data)); } catch (err) { console.warn("[PersonnelInfo] load dept/position options failed:", err); } })(); + return () => { + cancelled = true; + }; }, []); const { tableProps, getData } = useTable(safeListRequest(props.staffInfoList), { @@ -132,17 +156,17 @@ function PersonnelInfoPage(props) { width: 320, render: (_, record) => ( - - - - - + + ), }, @@ -158,7 +182,6 @@ function PersonnelInfoPage(props) { requestEdit={props.staffInfoEdit} requestDetails={props.staffInfoGet} deptOptions={deptOptions} - positionOptions={positionOptions} onCancel={() => { setFormModalOpen(false); setCurrentId(""); @@ -182,36 +205,100 @@ function PersonnelInfoPage(props) { } function StaffFormModal({ - open, currentId, requestAdd, requestEdit, requestDetails, deptOptions, positionOptions, onCancel, onSuccess, + open, currentId, requestAdd, requestEdit, requestDetails, deptOptions, onCancel, onSuccess, }) { const [form] = Form.useForm(); const [submitting, setSubmitting] = useState(false); const [detailLoading, setDetailLoading] = useState(false); + const [deptPositionOptions, setDeptPositionOptions] = useState([]); + const [positionLoading, setPositionLoading] = useState(false); const watchedDeptId = Form.useWatch("deptId", form); + const wasOpenRef = useRef(false); + const inflightDeptRef = useRef(null); + const requestDetailsRef = useRef(requestDetails); + requestDetailsRef.current = requestDetails; - const filteredPositionOptions = useMemo(() => { - if (!watchedDeptId) { - return positionOptions; + const loadPositionsByDept = async (deptId, currentStaff = null) => { + const id = asId(deptId); + if (!id) { + setDeptPositionOptions([]); + return []; } - return positionOptions.filter((p) => p.deptId === watchedDeptId); - }, [positionOptions, watchedDeptId]); + const key = id; + if (inflightDeptRef.current === key) { + return deptPositionOptions; + } + inflightDeptRef.current = key; + setPositionLoading(true); + try { + let res = await fetchOrgPositionPage({ + pageIndex: 1, + pageSize: 200, + eqDeptId: id, + }); + let options = mapPositionOptions(res?.data); + // 兼容历史岗位 dept_id 因 Number() 精度丢失与部门 id 不完全一致 + if (!options.length) { + const allRes = await fetchOrgPositionPage({ pageIndex: 1, pageSize: 500 }); + options = mapPositionOptions(allRes?.data).filter((p) => sameId(p.deptId, id)); + } + const positionId = asId(currentStaff?.positionId); + const positionName = currentStaff?.positionName; + if (positionId && positionName && !options.some((item) => sameId(item.value, positionId))) { + options = [{ label: positionName, value: positionId, deptId: id }, ...options]; + } + setDeptPositionOptions(options); + return options; + } + catch (err) { + console.warn("[PersonnelInfo] load positions by dept failed:", err); + setDeptPositionOptions([]); + return []; + } + finally { + if (inflightDeptRef.current === key) { + inflightDeptRef.current = null; + } + setPositionLoading(false); + } + }; useEffect(() => { - if (!open) { - return; + if (open && !wasOpenRef.current) { + if (!currentId) { + form.resetFields(); + setDeptPositionOptions([]); + form.setFieldsValue({ + personType: "基础人员", + registerEngineerFlag: 2, + }); + } } - if (!currentId) { - form.resetFields(); + if (!open) { + setDeptPositionOptions([]); + } + wasOpenRef.current = open; + }, [open, currentId, form]); + + useEffect(() => { + if (!open || !currentId) { return; } let cancelled = false; setDetailLoading(true); - safeRequest(requestDetails, { id: currentId }).then((res) => { + safeRequest(requestDetailsRef.current, { id: currentId }).then(async (res) => { if (!cancelled && res?.data) { - form.setFieldsValue({ - ...res.data, - birthDate: toDayjs(res.data.birthDate), - }); + const data = res.data; + await loadPositionsByDept(data.deptId, data); + if (!cancelled) { + form.setFieldsValue({ + ...data, + deptId: asId(data.deptId), + positionId: asId(data.positionId), + birthDate: toDayjs(data.birthDate), + proofMaterials: data.proofMaterials?.length ? data.proofMaterials : [], + }); + } } }).finally(() => { if (!cancelled) { @@ -225,6 +312,7 @@ function StaffFormModal({ const handleCancel = () => { form.resetFields(); + setDeptPositionOptions([]); onCancel(); }; @@ -236,11 +324,8 @@ function StaffFormModal({ } } if ("deptId" in changed) { - const posId = form.getFieldValue("positionId"); - const stillValid = positionOptions.some((p) => p.value === posId && p.deptId === changed.deptId); - if (!stillValid) { - form.setFieldValue("positionId", undefined); - } + form.setFieldValue("positionId", undefined); + loadPositionsByDept(changed.deptId); } }; @@ -316,7 +401,9 @@ function StaffFormModal({ + + + + + + + + + + + + + + + + + + + + + +
( - {record.enableStatus === 0 ? ( - - ) : ( + {isQualificationEnabled(record) ? ( + ) : ( + )} ), }, ]} - {...tableProps} /> {addModalOpen && ( @@ -316,11 +324,10 @@ function CertFormModal({ showActionButtons={false} onFinish={handleSubmit} options={[ - { - name: "certType", - label: "证照类型", + formSelectField("certType", "证照类型", QUALIFICATION_INDUSTRY_OPTIONS, { rules: [{ required: true, message: "请选择证照类型" }], - }, + colProps: { span: 24 }, + }), { name: "certName", label: "证书名称", diff --git a/src/pages/Container/EnterpriseInfo/ResignationApply/index.js b/src/pages/Container/EnterpriseInfo/ResignationApply/index.js index ff2f580..9ede9bc 100644 --- a/src/pages/Container/EnterpriseInfo/ResignationApply/index.js +++ b/src/pages/Container/EnterpriseInfo/ResignationApply/index.js @@ -7,6 +7,7 @@ import Search from "zy-react-library/components/Search"; import Table from "zy-react-library/components/Table"; import Upload from "zy-react-library/components/Upload"; import useTable from "zy-react-library/hooks/useTable"; +import { asId } from "~/api/enterpriseInfo/idUtil"; import { NS_STAFF_INFO, NS_STAFF_RESIGNATION_APPLY } from "~/enumerate/namespace"; import { safeListRequest, safeRequest } from "~/utils"; import { formSelectField, getResignAuditStatusLabel } from "~/utils/enterpriseForm"; @@ -34,7 +35,7 @@ function ResignationApplyPage(props) { if (!cancelled) { setStaffOptions((res?.data || []).map((s) => ({ label: `${s.staffName}(${s.account})`, - value: String(s.id), + value: asId(s.id), staffName: s.staffName, account: s.account, }))); @@ -96,7 +97,7 @@ function ResignationApplyPage(props) {