import request from "@/utils/request"; import { contractStatusLabel, contractSigningStatusLabel } from "@/api/hr/contractStatus"; function unwrap(response) { const result = response.data; if (result.code == 0) { return result.data; } const error = new Error(result.message || "服务调用失败"); if (response?.__toastShown) error.__toastShown = true; return Promise.reject(error); } function compact(object) { return Object.keys(object).reduce((result, key) => { const value = object[key]; if (value !== undefined && value !== null && value !== "") result[key] = value; return result; }, {}); } function formatDate(value) { if (!value) return ""; const text = String(value); if (/^\d{4}-\d{2}-\d{2}/.test(text)) return text.slice(0, 10); const date = new Date(value); if (Number.isNaN(date.getTime())) return text; const y = date.getFullYear(); const m = String(date.getMonth() + 1).padStart(2, "0"); const d = String(date.getDate()).padStart(2, "0"); return `${y}-${m}-${d}`; } function downloadBlob(response, fallbackName) { const blob = new Blob([response.data], { type: response.headers["content-type"] || "application/octet-stream", }); const disposition = response.headers["content-disposition"] || ""; const matched = disposition.match(/filename\*?=(?:UTF-8''|")?([^";]+)/i); const fileName = matched ? decodeURIComponent(matched[1].replace(/"/g, "")) : fallbackName; const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; link.download = fileName; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); } export async function listOrganizations(params) { return unwrap(await request.get("/main/group/getGroupList", { params })); } /** 工厂区域分页:type=1 工厂 */ export async function getFactoryAreaPage(params = {}) { return unwrap( await request.get("/main/factoryarea/page", { params: { pageNum: 1, size: 999, type: 1, ...params, }, }), ); } export async function getOrganizationTree(params) { return unwrap(await request.get("/main/group/getGroupTree", { params })); } export async function getGroupUserTree(params) { return unwrap(await request.get("/main/group/getGroupUserTree", { params })); } export async function getOrganization(id) { return unwrap(await request.get("/main/group/getById/" + id)); } export async function getOrganizationCode() { return unwrap(await request.get("/main/codemanage/getCode/group_code")); } export async function addOrganization(data) { const response = await request.post("/main/group/save", data); if (response.data.code == 0) { return response.data.data ?? response.data.message; } return Promise.reject(new Error(response.data.message || "新增失败")); } export async function updateOrganization(data) { const response = await request.put("/main/group/update", data); if (response.data.code == 0) { return response.data.data ?? response.data.message; } return Promise.reject(new Error(response.data.message || "修改失败")); } export async function updateDepartmentPosition(data) { return unwrap(await request.put("/fm/tmaindepartmentposition/updateBatch", data)); } export async function saveBatchDepartmentPosition(data) { return unwrap(await request.post("/fm/tmaindepartmentposition/saveBatch", data)); } export async function pageDepartmentPosition(params) { return unwrap(await request.post("/fm/tmaindepartmentposition/page", params)); } export async function removeOrganization(ids) { const response = await request.post("/main/group/delete", ids); if (response.data.code == 0) { return response.data.message; } return Promise.reject(new Error(response.data.message || "删除失败")); } export async function getUserPage(params) { return unwrap( await request.get("/main/user/getUserPage", { params, }), ); } /** 分页拉全量(有上限,避免一次性打爆接口) */ export async function fetchAllUsers(params = {}, options = {}) { const pageSize = options.pageSize || 200; const maxPages = options.maxPages || 50; const list = []; let pageNum = 1; let total = Infinity; while (pageNum <= maxPages && list.length < total) { const data = await getUserPage({ ...params, pageNum, size: pageSize, }); const chunk = Array.isArray(data?.list) ? data.list : []; total = Number(data?.count ?? list.length + chunk.length); list.push(...chunk); if (!chunk.length || chunk.length < pageSize) break; pageNum += 1; } return { list, count: total === Infinity ? list.length : total }; } /** * 关键词联合检索:并行 name / phone / jobNumber,再按 id 去重 * (后端 getUserPage 无统一 keyword 字段) */ export async function searchUsersByKeyword(keyword, baseParams = {}) { const text = String(keyword || "").trim(); if (!text) { return getUserPage(baseParams); } const queries = [{ ...baseParams, name: text }]; if (/^\d+$/.test(text) || /[A-Za-z]/.test(text)) { queries.push({ ...baseParams, jobNumber: text }); } if (/^1\d{10}$/.test(text) || text.includes("*")) { queries.push({ ...baseParams, phone: text }); } const results = await Promise.all(queries.map((params) => getUserPage(params).catch(() => ({ list: [], count: 0 })))); const map = new Map(); results.forEach((data) => { (Array.isArray(data?.list) ? data.list : []).forEach((item) => { if (item?.id != null) map.set(String(item.id), item); }); }); const list = Array.from(map.values()); return { list, count: list.length, pageNum: 1, size: list.length }; } export async function getEmployeeById(id) { return unwrap( await request.get(`/main/user/getById/${encodeURIComponent(String(id))}`), ); } /** 360 度员工人事档案聚合视图 */ export async function getEmployeeProfile(userId) { return unwrap(await request.get(`/main/users/${userId}/profile`)); } export async function getEmployeeHistory(userId) { return unwrap(await request.get(`/main/users/${userId}/history`)); } export async function saveEmployee(data) { return unwrap(await request.post("/main/user/saveNew", data)); } /** * 员工信息首次入库归档后创建个人文档区。 * 仅可调用一次:详情已有 jobNumber 时说明已归档,不可再调。 */ export async function createPersonalDirectory(userId) { const id = String(userId ?? "").trim(); if (!id) return null; return unwrap( await request.post(`/fm/directory/createPersonal/${encodeURIComponent(id)}`), ); } export function hasJobNumber(record = {}) { const jobNumber = record.jobNumber ?? record.user?.jobNumber ?? ""; return Boolean(String(jobNumber).trim()); } export async function removeEmployee(ids) { return unwrap(await request.post("/main/user/removeById", ids)); } export async function exportUsers(payload = {}) { const response = await request.post("/main/user/exportUsers", payload, { responseType: "blob", }); downloadBlob(response, `员工档案_${formatDate(new Date())}.xlsx`); } export async function downloadUserTemplate() { const response = await request.post( "/main/user/downLoadTemplate", {}, { responseType: "blob" }, ); downloadBlob(response, "人员信息导入模板.xlsx"); } export async function importUsers(file) { const formData = new FormData(); formData.append("file", file); const response = await request.post("/main/excelUpload/importUser", formData); if (response.data?.code == 0 || typeof response.data === "string") { return response.data?.data ?? response.data?.message ?? response.data; } return Promise.reject(new Error(response.data?.message || "导入失败")); } export async function listWorkExperiences(userId) { return unwrap(await request.get(`/main/users/${userId}/work-experiences`)); } export async function saveWorkExperience(userId, data) { return unwrap(await request.post(`/main/users/${userId}/work-experiences`, data)); } export async function updateWorkExperience(userId, workExperienceId, data) { return unwrap( await request.put( `/main/users/${userId}/work-experiences/${workExperienceId}`, data, ), ); } export async function removeWorkExperience(userId, workExperienceId) { return unwrap( await request.delete( `/main/users/${userId}/work-experiences/${workExperienceId}`, ), ); } export async function exportOrganizations() { const response = await request.post( "/main/group/exportGroups", {}, { responseType: "blob" }, ); downloadBlob(response, "组织机构列表.xlsx"); } /** 后端 status → 列表展示态 */ export const EMPLOYEE_STATUS_CODE = { 1: "全职", 2: "兼职", 3: "实习", 4: "正式", 5: "试用", 6: "离职", }; const EMPLOYEE_STATUS = { 全职: 1, 兼职: 2, 实习: 3, 正式: 4, 试用: 5, 试用期: 5, 劳务: 4, 离职: 6, }; const EMPLOYEE_SEX = { 男: 1, 女: 2, 未知: 0 }; const SEX_LABEL = { 0: "未知", 1: "男", 2: "女" }; const REWARD_STATUS = { 生效: 2, 已撤销: 4, 已过期: 3, 未生效: 0, 审核中: 1 }; const CERT_TYPE = { 身份证: 1, 驾驶证: 2, 军官证: 3, 护照: 4, 特种作业证: 4, 职称证: 4, 职业资格证: 4, 其他: 4, }; const CERT_LEVEL = { 初级: "1", 中级: "2", 高级: "3" }; const CERT_LEVEL_LABEL = { 1: "初级", 2: "中级", 3: "高级", 初级: "初级", 中级: "中级", 高级: "高级" }; /** 列表/详情统一展示状态:active | probation | inactive | disabled */ export function toListStatus(raw = {}) { if (Number(raw.isEnabled) === 0) return "disabled"; const code = Number(raw.status); if (code === 6) return "inactive"; if (code === 5) return "probation"; return "active"; } export function toFormStatus(raw = {}) { if (Number(raw.isEnabled) === 0) return "离职"; return EMPLOYEE_STATUS_CODE[Number(raw.status)] || "全职"; } function emptyLeaveInfo() { return { enabled: false, position: "", department: "", leaveDate: "", leaveType: "", leaveReason: "", serviceYears: "", handoverStatus: "", }; } function adaptContract(raw) { const first = Array.isArray(raw?.contracts) ? raw.contracts[0] : null; if (!first) { return { category: "", name: "", number: "", signDate: "", startDate: "", endDate: "", files: [], status: "待签署", remark: "" }; } return { category: first.contractType || "", name: first.contractNo || "", number: first.contractNo || "", signDate: formatDate(first.effectiveDate || first.startDate), startDate: formatDate(first.startDate), endDate: formatDate(first.endDate), files: [], status: contractStatusLabel(first.contractStatus, "") || contractSigningStatusLabel(first.signingStatus, "") || "生效", remark: "", }; } function adaptBanks(raw) { if (Array.isArray(raw?.bankCardList) && raw.bankCardList.length) { return raw.bankCardList.map((item) => ({ id: item.id, bankName: item.openingBank || "", bankAccount: item.cardNumber || "", bankAccountName: item.accountName || raw.name || "", })); } if (raw?.openingBank || raw?.cardNumber) { return [ { bankName: raw.openingBank || "", bankAccount: raw.cardNumber || "", bankAccountName: raw.name || "", }, ]; } return []; } function adaptSocial(raw) { if (Array.isArray(raw?.socialSecurityList) && raw.socialSecurityList.length) { return raw.socialSecurityList.map((item) => ({ id: item.id, type: item.socialType || "", base: item.socialBase ?? 0, insuredDate: formatDate(item.insuredDate), stopDate: formatDate(item.stopDate), stopReason: item.stopRecord || "", })); } return []; } function adaptSkills(list = []) { return (list || []).map((item) => ({ id: item.id, category: "", name: item.name || "", level: item.level || "", years: item.years ?? 0, attachment: [], remark: item.remark || "", })); } function adaptTrainings(list = []) { const statusMap = { 0: "未开始", 1: "进行中", 2: "已完成", 4: "取消" }; return (list || []).map((item) => ({ id: item.id, name: item.title || "", description: item.desce || "", startTime: formatDate(item.startTime), endTime: formatDate(item.endTime), dateRange: [formatDate(item.startTime), formatDate(item.endTime)].filter(Boolean), status: statusMap[item.status] || "已完成", organization: "", hours: "", result: "", certificate: "", remark: item.remark || "", })); } function adaptRewards(list = []) { const statusMap = { 0: "未生效", 1: "审核中", 2: "生效", 3: "已过期", 4: "已撤销" }; return (list || []).map((item) => ({ id: item.id, type: Number(item.type) === 2 ? "惩罚" : "奖励", date: formatDate(item.startTime), name: item.desce || "", description: item.desce || "", startDate: formatDate(item.startTime), endDate: formatDate(item.endTime), status: statusMap[item.status] || "生效", level: "", issuer: "", orderNo: "", remark: item.remark || "", })); } function adaptCertificates(list = []) { const typeMap = { 1: "身份证", 2: "驾驶证", 3: "军官证", 4: "护照" }; return (list || []).map((item) => ({ id: item.id, type: typeMap[item.type] || "其他", number: item.code || "", level: CERT_LEVEL_LABEL[item.level] || item.level || "", status: Number(item.status) === 0 ? "失效" : "有效", startDate: formatDate(item.validityStartTime), endDate: formatDate(item.validityEndTime), issueDate: formatDate(item.enactorTime), attachment: [], remark: item.remark || "", })); } function adaptWorkTypes(list = []) { return (list || []).map((item) => ({ id: item.id, type: item.type || "", code: item.code || "", name: item.name || "", level: item.level || "", hourCost: item.hourCost ?? 0, unit: item.unit || "", hourlyWage: item.hourCost ?? 0, wageUnit: item.unit || "", userId: item.userId || item.relatedUserId || "", userName: item.userName || item.relatedUserName || "", })); } function adaptWorkExperiences(list = []) { return (list || []).map((item) => ({ id: item.id, startDate: formatDate(item.workStartDate), endDate: formatDate(item.workEndDate), dateRange: [formatDate(item.workStartDate), formatDate(item.workEndDate)].filter(Boolean), company: item.formerCompanyName || "", position: item.formerPosition || "", department: item.formerDepartment || "", leaveReason: item.resignationReason || "", description: item.workDescription || "", })); } function adaptHistory(list = []) { return (list || []).map((item) => ({ date: formatDate(item.eventTime || item.occurTime || item.createTime), title: item.eventTitle || item.title || item.eventType || "人事事件", detail: item.eventDesc || item.description || item.summary || "", orderNo: item.bizNo || item.orderNo || "", })); } /** 兼容 positionList;无名称时用 postName 按序补齐;positionId 支持逗号串 */ function adaptPositionBindings(raw = {}) { const fallbackNames = String(raw.postName || "") .split(/[,,、]/) .map((item) => item.trim()) .filter(Boolean); if (Array.isArray(raw.positionList) && raw.positionList.length) { return raw.positionList .filter((item) => item && (item.positionId != null || item.positionName)) .flatMap((item, index) => { const rawId = item.positionId != null ? String(item.positionId) : ""; const idParts = rawId .split(/[,,]/) .map((part) => part.trim()) .filter(Boolean); const ids = idParts.length ? idParts : rawId ? [rawId] : []; if (!ids.length && item.positionName) { return [ { id: item.id != null ? String(item.id) : undefined, positionId: "", positionName: item.positionName || fallbackNames[index] || "", positionLevelId: item.positionLevelId != null ? String(item.positionLevelId) : "", positionLevelName: item.positionLevelName || "", positionSequenceId: item.positionSequenceId != null ? String(item.positionSequenceId) : "", positionSequenceName: item.positionSequenceName || "", positionType: item.positionType || "", primaryFlag: Number(item.primaryFlag) === 1 ? 1 : 0, }, ]; } return ids.map((positionId, partIndex) => ({ id: item.id != null ? String(item.id) : undefined, positionId: String(positionId), positionName: item.positionName || fallbackNames[index + partIndex] || fallbackNames[index] || "", positionLevelId: item.positionLevelId != null ? String(item.positionLevelId) : "", positionLevelName: item.positionLevelName || "", positionSequenceId: item.positionSequenceId != null ? String(item.positionSequenceId) : "", positionSequenceName: item.positionSequenceName || "", positionType: item.positionType || "", primaryFlag: partIndex === 0 && Number(item.primaryFlag) === 1 ? 1 : 0, })); }) .sort((a, b) => Number(b.primaryFlag || 0) - Number(a.primaryFlag || 0)); } const ids = String(raw.postId || "") .split(/[,,]/) .map((item) => item.trim()) .filter(Boolean); return ids.map((positionId, index) => ({ positionId: String(positionId), positionName: fallbackNames[index] || "", positionLevelId: "", positionLevelName: "", positionSequenceId: "", positionSequenceName: "", positionType: "", primaryFlag: index === 0 ? 1 : 0, })); } function toApiId(value) { if (value == null || value === "") return undefined; const text = String(value); if (/^\d+$/.test(text) && text.length <= 15) return Number(text); return text; } /** * 后端「用户」→ 员工档案页面模型 * @param {object} raw 用户详情或分页行 * @param {object} [extras] profile / workExperiences 等补充数据 */ export function adaptEmployee(raw = {}, extras = {}) { const deptIds = Array.from( new Set( (Array.isArray(raw.deptIds) ? raw.deptIds : raw.deptId != null && raw.deptId !== "" ? [raw.deptId] : [] ) .map((id) => String(id).trim()) .filter(Boolean), ), ); const deptId = deptIds[0] || ""; const listStatus = toListStatus(raw); const leaveDate = formatDate(raw.leaveDate); const positionBindings = adaptPositionBindings(raw); const primaryPosition = positionBindings.find((item) => Number(item.primaryFlag) === 1) || positionBindings[0] || null; const positionIds = positionBindings.map((item) => item.positionId).filter(Boolean); const positionNames = positionBindings .map((item) => item.positionName) .filter(Boolean); const banks = adaptBanks(raw); const avatar = typeof raw.pic === "string" ? raw.pic : Array.isArray(raw.avatar) && raw.avatar[0] ? raw.avatar[0].url || raw.avatar[0].fileUrl || "" : ""; return { id: raw.id, avatar, name: raw.name || "", employeeNo: raw.jobNumber || "", account: raw.loginName || "", gender: SEX_LABEL[raw.sex] || "未知", birthDate: formatDate(raw.birthday), age: raw.age ?? null, idCard: raw.idCard || raw.idNumber || "", ethnic: raw.ethnic || "", politicalStatus: raw.politicsStatus || "", maritalStatus: raw.maritalStatus || "", nativePlace: raw.birthplace || "", education: raw.educationBackground || "", school: raw.school || "", major: raw.major || "", graduationDate: formatDate(raw.graduateDate), registeredAddress: raw.censusAddress || "", currentAddress: raw.currentAddress || "", workAddress: raw.address || "", phone: raw.phone || "", email: raw.email || "", wechat: raw.wxId || "", emergencyContact: raw.secondLinkName || "", emergencyPhone: raw.secondLinkPhone || "", emergencyRelation: raw.secondLinkRelation || "", groupId: raw.groupId != null && raw.groupId !== "" ? String(raw.groupId) : null, company: raw.groupName || "", legalEntity: raw.groupName || "", factory: raw.factoryName || "", factoryId: raw.factoryId || raw.factoriesId || null, deptIds, deptId: deptId ? String(deptId) : "", deptName: raw.deptName || raw.deptNames || "", position: positionNames.join("、") || raw.postName || "", positionIds, positionBindings, positionId: primaryPosition?.positionId || null, level: primaryPosition?.positionLevelName || "", managerId: raw.managerUserId != null && raw.managerUserId !== "" ? String(raw.managerUserId) : null, manager: raw.managerUserName || "", managerJobNumber: raw.managerJobNumber || "", employmentType: EMPLOYEE_STATUS_CODE[Number(raw.status)] || "全职", status: listStatus, formStatus: toFormStatus(raw), hireDate: formatDate(raw.joinDate || raw.hireDate || raw.entryDate), regularDate: formatDate(raw.positiveDate), enabled: Number(raw.isEnabled) !== 0, keyProcess: raw.criticalTaskIds || (Array.isArray(raw.criticalTaskList) ? raw.criticalTaskList .map((item) => item.taskName || item.taskId) .filter(Boolean) .join(",") : "") || "", remarks: raw.remark || "", bankName: banks[0]?.bankName || "", bankAccount: banks[0]?.bankAccount || "", bankAccountName: banks[0]?.bankAccountName || raw.name || "", bankAccounts: banks, socialSecurityRecords: adaptSocial(raw), skills: adaptSkills(raw.skills), trainingRecords: adaptTrainings(raw.trainRecords), rewardPunishmentRecords: adaptRewards(raw.rewardsPunishments), certificates: adaptCertificates(raw.identityPhotos), workTypes: adaptWorkTypes(raw.professions), previousJobs: adaptWorkExperiences(extras.workExperiences || []), educations: raw.educationBackground ? [ { education: raw.educationBackground || "", degree: "", graduationDate: formatDate(raw.graduateDate), school: raw.school || "", major: raw.major || "", }, ] : [], contract: adaptContract(raw), workHistory: adaptHistory(extras.history || []), attendanceRecords: [], performanceRecords: (extras.performanceRecords || []).map((item) => ({ dateRange: [formatDate(item.periodStart), formatDate(item.periodEnd)].filter(Boolean), cycle: item.cycleName || item.cycle || "", result: item.resultLabel || item.result || "", achievementRate: item.score ?? item.achievementRate, assessmentDate: formatDate(item.publishTime || item.assessmentDate), standard: item.standard || "", remark: item.remark || "", })), awards: [], positionTransfers: (extras.changes || []).map((item) => ({ date: formatDate(item.effectiveDate || item.createTime), originalPosition: item.fromPositionName || "", newPosition: item.toPositionName || "", originalDepartment: item.fromDeptName || "", newDepartment: item.toDeptName || "", orderNo: item.bizNo || item.id || "", })), contractChanges: [], familyMembers: [], files: [], leaveInfo: { ...emptyLeaveInfo(), enabled: listStatus === "inactive", position: positionNames[0] || raw.postName || "", department: raw.deptName || "", leaveDate, }, raw, }; } /** 合并 profile 聚合视图 */ export function adaptEmployeeProfile(profile = {}) { const user = profile.user || {}; return adaptEmployee(user, { workExperiences: profile.workExperiences || [], history: profile.history || [], performanceRecords: profile.performanceRecords || [], changes: profile.changes || [], }); } /** * 员工档案表单 → 后端「员工新增或修改参数」 * 合同 contracts 已废弃,不提交;工作履历走独立接口 */ export function serializeEmployee(form = {}) { const statusText = form.formStatus || form.status; const statusCode = EMPLOYEE_STATUS[statusText] ?? (statusText === "inactive" || statusText === "disabled" ? 6 : statusText === "probation" ? 5 : Number(statusText) || 1); const education = form.educations?.[0] || {}; const bankAccounts = form.bankAccounts || []; const firstBank = bankAccounts[0] || {}; const positionIds = ( form.positionId ? [form.positionId] : Array.isArray(form.positionIds) ? form.positionIds : [] ) .filter(Boolean) .map(String) .slice(0, 1); const professionIds = (form.workTypes || []) .map((item) => item.id) .filter((id) => id != null && id !== ""); const avatarUrl = typeof form.avatar === "string" && /^https?:\/\//i.test(form.avatar) ? form.avatar : undefined; return compact({ id: form.id || undefined, name: form.name || "", jobNumber: form.employeeNo || "", sex: EMPLOYEE_SEX[form.gender] ?? form.gender, birthday: form.birthDate || "", age: Number(form.age) || undefined, idCard: form.idCard || "", idNumber: form.idCard || "", ethnic: form.ethnic || "", politicsStatus: form.politicalStatus || "", maritalStatus: form.maritalStatus || "", birthplace: form.nativePlace || "", censusAddress: form.registeredAddress || "", currentAddress: form.currentAddress || "", address: form.workAddress || "", educationBackground: education.education || form.education || "", school: education.school || form.school || "", major: education.major || form.major || "", graduateDate: education.graduationDate || form.graduationDate || "", phone: form.phone || "", email: form.email || "", wxId: form.wechat || "", status: statusCode, joinDate: form.hireDate || "", positiveDate: form.regularDate || "", leaveDate: form.leaveInfo?.leaveDate || "", isEnabled: form.enabled === false || statusText === "disabled" ? 0 : 1, openingBank: firstBank.bankName || form.bankName || "", cardNumber: firstBank.bankAccount || form.bankAccount || "", bankCardList: bankAccounts.length ? bankAccounts .filter((item) => item.bankAccount || item.bankName) .map((item) => ({ openingBank: item.bankName || "", cardNumber: item.bankAccount || "", accountName: item.bankAccountName || form.name || "", })) : undefined, secondLinkName: form.emergencyContact || "", secondLinkPhone: form.emergencyPhone || "", secondLinkRelation: form.emergencyRelation || "", pic: avatarUrl, professionId: professionIds.length ? professionIds.map(String).join(",") : undefined, workTypeId: professionIds.length ? toApiId(professionIds[0]) : undefined, remark: form.remarks || "", groupId: form.groupId || undefined, deptIds: (Array.isArray(form.deptIds) && form.deptIds.length ? form.deptIds : form.deptId ? [form.deptId] : [] ) .map((id) => toApiId(id)) .filter((id) => id != null), deptName: form.deptName || "", managerUserId: toApiId(form.managerId || form.managerUserId), // 岗位:界面只选岗位;提交仍需 positionList 必填的层级/序列主键(由前端按岗位档案解析) postId: positionIds.length ? positionIds.join(",") : undefined, postName: form.position || undefined, positionList: positionIds.length ? [ (() => { const id = positionIds[0]; const binding = (Array.isArray(form.positionBindings) ? form.positionBindings : []).find( (item) => String(item.positionId) === String(id), ) || {}; return { positionId: toApiId(id), positionLevelId: toApiId(binding.positionLevelId), positionSequenceId: toApiId(binding.positionSequenceId), primaryFlag: 1, }; })(), ] : [], factoryId: form.factoryId ? toApiId(form.factoryId) : undefined, socialSecurityList: (form.socialSecurityRecords || []) .filter((item) => item.type || item.insuredDate) .map((item) => ({ socialType: item.type || "", socialBase: Number(item.base) || 0, insuredDate: item.insuredDate || "", stopDate: item.stopDate || "", stopRecord: item.stopReason || "", })), // skills / trainRecords 已废弃:非空提交会被后端拒绝,技能/培训走独立 HR / EHS 接口 rewardsPunishments: (form.rewardPunishmentRecords || []).map((item) => ({ id: item.id || undefined, type: /惩罚|处分|警告/.test(item.type) ? 2 : 1, desce: item.description || item.name || "", startTime: item.startDate || item.date || "", endTime: item.endDate || "", status: REWARD_STATUS[item.status] ?? 2, remark: item.remark || "", })), identityPhotos: (form.certificates || []) .filter((item) => item.type || item.number) .map((item) => ({ id: item.id || undefined, type: CERT_TYPE[item.type] ?? 4, code: item.number || "", level: CERT_LEVEL[item.level] || item.level || "", status: item.status === "失效" ? 0 : 1, validityStartTime: item.startDate || "", validityEndTime: item.endDate || "", enactorTime: item.issueDate || "", remark: item.remark || "", holderType: "1", holder: form.name || "", })), }); } export function serializeWorkExperience(item = {}) { return compact({ formerCompanyName: item.company || "", formerDepartment: item.department || "", formerPosition: item.position || "", resignationReason: item.leaveReason || "", workDescription: item.description || "", workStartDate: item.startDate || item.dateRange?.[0] || "", workEndDate: item.endDate || item.dateRange?.[1] || "", }); } /** 保存员工后同步入职前工作履历(独立接口) */ export async function syncWorkExperiences(userId, jobs = [], existing = []) { if (!userId) return; const next = (jobs || []).filter((item) => item.company && item.startDate && item.endDate); const keptIds = new Set(next.map((item) => item.id).filter(Boolean).map(String)); const removals = (existing || []) .filter((item) => item.id && !keptIds.has(String(item.id))) .map((item) => removeWorkExperience(userId, item.id)); await Promise.all(removals); await Promise.all( next.map((item) => { const payload = serializeWorkExperience(item); if (item.id) return updateWorkExperience(userId, item.id, payload); return saveWorkExperience(userId, payload); }), ); }