import { get, postJ } from "@/utils/request"; import Vue from "vue"; import { onboardStatus } from "@/pages/hr/data/manage-status"; // 用工类型枚举值 → 中文标签(与后端约定一致) export const EMPLOYMENT_TYPE_LABEL = { FULL_TIME: "全职", INTERN: "实习", PART_TIME: "兼职", OTHER: "其他", }; function ok(res) { return res && Number(res.code) === 0; } function asList(data) { if (Array.isArray(data)) return data; if (data && Array.isArray(data.list)) return data.list; if (data && Array.isArray(data.records)) return data.records; return []; } function dateOnly(value) { if (!value) return ""; return String(value).slice(0, 10); } /** * 把后端入职记录 VO 归一为前端可用形状。 */ export function adaptOnboardingRecord(vo, base = {}) { if (!vo || typeof vo !== "object") return null; const id = String(vo.onboardingId != null ? vo.onboardingId : vo.id != null ? vo.id : base.id || ""); const onboardingStatus = vo.onboardingStatus || base.onboardingStatus || ""; const approvalStatus = vo.approvalStatus || base.approvalStatus || ""; const status = onboardingStatus === "COMPLETED" || approvalStatus === "APPROVED" ? "done" : onboardingStatus === "SUBMITTED" || approvalStatus === "APPROVING" ? "processing" : base.status || "pending"; return { id, onboardingId: id, invitationId: vo.invitationId != null ? vo.invitationId : base.invitationId || "", invitationNo: vo.invitationNo || base.invitationNo || "", employeeNo: vo.employeeNo || base.employeeNo || id, name: vo.employeeName || base.name || "", department: vo.deptName || base.department || "", deptId: vo.deptId != null ? vo.deptId : base.deptId != null ? base.deptId : null, position: vo.positionName || base.position || "", positionId: vo.positionId != null ? vo.positionId : base.positionId != null ? base.positionId : null, level: vo.level || base.level || "", hireDate: dateOnly(vo.plannedJoinDate || base.hireDate || base.expectedEntryDate || ""), company: vo.companyName || base.company || "", legalId: vo.legalId != null ? vo.legalId : base.legalId != null ? base.legalId : null, phone: vo.phone || base.phone || "", email: vo.email || base.email || "", idNumber: vo.idNumber || base.idNumber || "", gender: vo.gender || base.gender || "", age: vo.age != null ? vo.age : base.age || "", employmentType: vo.employmentType || base.employmentType || "FULL_TIME", probationFlag: vo.probationFlag != null ? vo.probationFlag : base.probationFlag != null ? base.probationFlag : 1, probationMonths: vo.probationMonths != null ? vo.probationMonths : base.probationMonths != null ? base.probationMonths : 3, validDays: vo.validDays != null ? vo.validDays : base.validDays != null ? base.validDays : 7, createMode: vo.createMode || base.createMode || "manual", status, flow: onboardingStatus === "SUBMITTED" ? "档案已完善" : base.flow || "待补充档案", offer: vo.offerStatus || base.offer || "未发起", contract: vo.contractStatus || base.contract || "未发起", assets: vo.assetsStatus || base.assets || "未领用", remark: vo.remark || base.remark || "", publicPath: vo.publicPath || base.publicPath || "", publicToken: vo.publicToken || base.publicToken || "", invitationStatus: vo.invitationStatus || base.invitationStatus || "", sourceType: vo.sourceType || "API", rowType: "record", materials: base.materials || {}, socialSecurity: base.socialSecurity || { status: "未办理", city: "北京", deadline: "" }, training: base.training || { status: "未开始", name: "新员工入职培训", duration: "2天", deadline: "" }, workstation: base.workstation || { status: "待分配", location: "" }, itAccount: base.itAccount || { status: "未开通", items: [] }, stage: base.stage || "offer", entryDate: dateOnly(vo.actualJoinDate || base.entryDate || ""), }; } /** 入职记录 → 列表卡片渲染形状 */ export function mapOnboardingForList(record) { if (!record || typeof record !== "object") return null; const id = record.id || record.onboardingId || ""; const name = record.name || record.employeeName || ""; const department = record.department || record.deptName || ""; const position = record.position || record.positionName || ""; const statusInfo = onboardStatus[record.status] || onboardStatus.pending; const hireDate = record.hireDate || record.entryDate || record.expectedEntryDate || ""; const employeeNo = record.employeeNo || id; const isEntered = Boolean(record.entryDate); const materials = record.materials; let materialsLabel = ""; if (materials && typeof materials === "object") { const total = Object.keys(materials).length; if (total) { const done = Object.values(materials).filter(Boolean).length; materialsLabel = `${done}/${total}`; } } return { id, avatar: record.avatar || String(name).charAt(0) || "", name, sub: [department, position].filter(Boolean).join(" · "), status: statusInfo.label, statusClass: statusInfo.class, label1: isEntered ? "入职日期" : "预计入职", value1: isEntered ? record.entryDate : hireDate, label2: isEntered ? "员工编号" : materialsLabel ? "资料进度" : "员工编号", value2: isEntered ? employeeNo : materialsLabel || employeeNo, }; } /** * 员工状态(即岗位性质)枚举:1全职、2兼职、3实习、4正式、5试用 * 直接办理入职时服务端会从启用岗位回填六项岗位快照,前端只传基础项 */ export const USER_STATUS_LABEL = { 1: "全职", 2: "兼职", 3: "实习", 4: "正式", 5: "试用" }; export const USER_STATUS_OPTIONS = [ { value: 1, label: "全职" }, { value: 2, label: "兼职" }, { value: 3, label: "实习" }, ]; /** * 入职登记表单 → 人员入职保存参数(POST /main/onboardings) * 字段对齐 PC serializeOnboardingCreate:带岗位序列/层级/性质快照; * 主键一律原样透传(大整数雪花 id 禁止 Number() 转换,会丢精度),字符串 id 后端可接收。 */ export function buildUserOnboardingPayload(form = {}) { const toId = (v) => { if (v == null || v === "") return undefined; if (typeof v === "number") return Number.isSafeInteger(v) ? v : String(v); return String(v); }; const text = (v) => { const s = String(v == null ? "" : v).trim(); return s || undefined; }; let attachmentFileIds = form.attachmentIds; if (Array.isArray(attachmentFileIds)) attachmentFileIds = attachmentFileIds.filter(Boolean).join(","); attachmentFileIds = attachmentFileIds ? String(attachmentFileIds) : undefined; const status = Number(form.status) || 1; return { name: text(form.name), joinDate: dateOnly(form.hireDate || form.joinDate) || undefined, organizationId: toId(form.legalId), targetDepartmentId: toId(form.deptId), positionSequenceId: toId(form.positionSequenceId), positionSequence: text(form.positionSequence), positionId: toId(form.positionId), positionLevelId: toId(form.positionLevelId), positionLevel: text(form.positionLevel), positionType: text(form.positionType), positionTypeName: text(form.positionTypeName), status, phone: text(form.phone), idNumber: text(form.idNumber), attachmentFileIds, }; } /** 新增人员入职(直接办理)POST /main/onboardings,成功返回新员工用户主键 */ export async function createUserOnboarding(form, loding = true) { const res = await postJ(Vue.prototype.apiUrl + "/main/onboardings", buildUserOnboardingPayload(form), loding); if (!ok(res)) throw new Error(res.message || "入职办理失败"); return res.data; } /** 入职记录列表 GET /hr/ess/admin/onboardings/records */ export async function getOnboardingRecords(loding = true) { const res = await get(Vue.prototype.apiUrl + "/hr/ess/admin/onboardings/records", {}, loding); if (!ok(res)) return []; return asList(res.data); } /** 入职邀请列表 GET /hr/ess/admin/onboardings/invitations */ export async function getOnboardingInvitationsList(loding = true) { const res = await get(Vue.prototype.apiUrl + "/hr/ess/admin/onboardings/invitations", {}, loding); if (!ok(res)) return []; return asList(res.data); } /** 提交入职审批 POST /hr/ess/admin/onboardings/records/{id}/submit */ export async function submitOnboardingRecord(onboardingId, loding = true) { if (!onboardingId) throw new Error("缺少入职单主键"); const res = await postJ( Vue.prototype.apiUrl + `/hr/ess/admin/onboardings/records/${encodeURIComponent(onboardingId)}/submit`, {}, loding ); if (!ok(res)) throw new Error(res.message || "提交失败"); return res.data || {}; } /** 入职记录列表(归一化后) */ export async function loadOnboardingRecords(loding = true) { const rows = await getOnboardingRecords(loding); return rows.map((vo) => adaptOnboardingRecord(vo) || vo).filter(Boolean); } /** 入职记录 / 邀请 详情(归一化后) */ export async function loadOnboardingDetail(id) { if (!id) return null; try { const [records, invitations] = await Promise.all([ getOnboardingRecords(false).catch(() => []), getOnboardingInvitationsList(false).catch(() => []), ]); const key = String(id); const hit = records.find((vo) => String(vo.onboardingId != null ? vo.onboardingId : vo.id) === key) || invitations.find( (vo) => String(vo.onboardingId != null ? vo.onboardingId : vo.id) === key || String(vo.invitationId != null ? vo.invitationId : "") === key ); if (!hit) return null; const record = adaptOnboardingRecord(hit); if (record && !record.createMode) record.createMode = "invite"; return record; } catch (error) { return null; } }