| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919 |
- import Vue from "vue";
- import { get, postJ, putJ, deleteApi } from "@/utils/request";
- import { formatDate } from "@/utils/hr/format";
- /* =========================================================================
- * 字典 / 选项(与 PC 端 kd-aiot-frontend-hr 员工档案表单口径一致)
- * ========================================================================= */
- /**
- * 员工状态:1 正式在职 / 6 试用期 / 7 待岗 / 8 已离职
- * 口径来源:hr_user_status 字典实测(与 PC kd-aiot-frontend-hr 的
- * utils/employee-status-dict.js 一致;swagger 注释「1全职…6离职」与实际数据不符)。
- * 运行时以字典为准,这里只作字典拉取失败时的兜底,避免静默串码。
- */
- export const EMPLOYEE_STATUS_OPTIONS = [
- { value: 1, label: "正式在职" },
- { value: 6, label: "试用期" },
- { value: 7, label: "待岗" },
- { value: 8, label: "已离职" },
- ];
- export const EMPLOYEE_STATUS_LABEL = EMPLOYEE_STATUS_OPTIONS.reduce((acc, item) => {
- acc[item.value] = item.label;
- return acc;
- }, {});
- export const SEX_CODE = { 男: 1, 女: 2, 未知: 0 };
- export const SEX_OPTIONS = ["男", "女", "未知"];
- export function sexLabel(value) {
- const map = { 1: "男", 2: "女", 0: "未知" };
- return map[value] != null ? map[value] : value == null ? "" : String(value);
- }
- export const MARITAL_OPTIONS = ["未婚", "已婚", "离异", "丧偶"];
- export const EDUCATION_OPTIONS = ["小学", "初中", "高中", "中专", "大专", "本科", "硕士", "博士"];
- export const DEGREE_OPTIONS = ["无", "学士", "硕士", "博士"];
- export const ETHNIC_OPTIONS = ["汉族", "土家族", "苗族", "侗族", "回族", "瑶族", "壮族", "满族", "其他"];
- export const SOCIAL_SECURITY_OPTIONS = ["四险", "四险一金", "五险", "五险一金", "六险一金"];
- export const STOP_REASON_OPTIONS = ["离职", "调转", "其他"];
- /* 业务记录枚举:后端原始码 ↔ 中文(与 PC 端 userChange/regularization/resignation/salarySlip 映射一致) */
- export const CHANGE_TYPE_LABELS = {
- DEPARTMENT_TRANSFER: "部门调动",
- POSITION_TRANSFER: "岗位调动",
- GRADE_CHANGE: "职级变更",
- PROMOTION: "晋升",
- DEMOTION: "降职",
- SALARY_CHANGE: "薪资变更",
- MANAGER_CHANGE: "上级变更",
- WORK_LOCATION_CHANGE: "工作地点变更",
- FACTORY_TRANSFER: "工厂调动",
- LEGAL_ENTITY_TRANSFER: "法人实体转移",
- EMPLOYMENT_TYPE_CHANGE: "雇佣类型变更",
- COMPOSITE_CHANGE: "复合变更",
- };
- export const APPROVAL_STATUS_LABELS = {
- DRAFT: "草稿",
- PENDING: "审批中",
- APPROVING: "审批中",
- APPROVED: "已通过",
- REJECTED: "已驳回",
- WITHDRAWN: "已撤回",
- CANCELLED: "已取消",
- };
- export const EFFECTIVE_STATUS_LABELS = {
- PENDING: "待生效",
- PENDING_EFFECTIVE: "待生效",
- SCHEDULED: "已排期",
- EFFECTIVE: "已生效",
- INVALID: "已失效",
- FAILED: "生效失败",
- CANCELLED: "已取消",
- };
- export const REGULARIZATION_TYPE_LABELS = {
- NORMAL: "按期转正",
- EARLY: "提前转正",
- EXTENDED: "延期转正",
- };
- export const REGULARIZATION_RESULT_LABELS = {
- NORMAL: "按期转正",
- EARLY: "提前转正",
- EXTENDED: "延期",
- FAILED: "不通过",
- };
- export const RESIGNATION_TYPE_LABELS = {
- VOLUNTARY: "主动离职",
- NEGOTIATED: "协商解除",
- PROBATION_FAILED: "试用不合格",
- PERFORMANCE_FAILED: "绩效不合格",
- DISMISSED: "辞退",
- CONTRACT_EXPIRED: "合同到期",
- RETIREMENT: "退休",
- OTHER: "其他",
- };
- export const PAYSLIP_STATUS_LABELS = {
- PUBLISHED: "已发布",
- VIEWED: "已查看",
- CONFIRMED: "已确认",
- APPEALED: "已申诉",
- CORRECTED: "已更正",
- CLOSED: "已关闭",
- };
- /** 合同状态 / 签署状态枚举(对齐 PC src/api/hr/contractStatus.js,避免详情里显示英文码) */
- export const CONTRACT_STATUS_LABELS = {
- DRAFT: "草稿",
- PENDING_APPROVAL: "审批中",
- PENDING_SIGN: "待签署",
- SIGNED_WAIT_EFFECTIVE: "已签待生效",
- EFFECTIVE: "生效",
- EXPIRING: "临期",
- EXPIRED: "已到期",
- RENEWED: "已续签",
- CHANGED: "已变更",
- TERMINATED: "已解除",
- ENDED: "已终止",
- CANCELLED: "已作废",
- SIGNING: "签署中",
- PENDING_EFFECTIVE: "已签待生效",
- VOIDED: "已作废",
- ARCHIVED: "已归档",
- };
- export const CONTRACT_SIGNING_STATUS_LABELS = {
- NOT_STARTED: "未签署",
- COMPLETED: "已签署",
- REJECTED: "已拒签",
- EXPIRED: "已过期",
- CANCELLED: "已取消",
- UNSIGNED: "未签署",
- PENDING: "待签署",
- PENDING_SIGN: "待签署",
- SIGNING: "签署中",
- SIGNED: "已签署",
- };
- /** 由出生日期算年龄(接口不返回 age 时兜底,对齐 PC 的 calculateAge) */
- export function calcAgeByBirthday(birthday) {
- const text = String(birthday == null ? "" : birthday).replace(/-/g, "/").slice(0, 10);
- if (!text) return "";
- const date = new Date(text);
- if (isNaN(date.getTime())) return "";
- const now = new Date();
- let age = now.getFullYear() - date.getFullYear();
- const monthDiff = now.getMonth() - date.getMonth();
- if (monthDiff < 0 || (monthDiff === 0 && now.getDate() < date.getDate())) age -= 1;
- return age >= 0 && age < 130 ? String(age) : "";
- }
- /** 证照类型:名称 ↔ 后端编码 */
- export const CERT_TYPE_CODE = {
- 身份证: 1,
- 驾驶证: 2,
- 军官证: 3,
- 护照: 4,
- 特种作业证: 5,
- 职称证: 6,
- 职业资格证: 7,
- 特种设备操作证: 8,
- 其他: 9,
- };
- export const CERT_TYPE_OPTIONS = Object.keys(CERT_TYPE_CODE);
- const CERT_LEVEL_CODE = { 初级: "1", 中级: "2", 高级: "3" };
- const CERT_LEVEL_LABEL = { 1: "初级", 2: "中级", 3: "高级", 初级: "初级", 中级: "中级", 高级: "高级" };
- /** 岗位津贴单位:字典码 ↔ 展示名 */
- export const ALLOWANCE_UNIT_OPTIONS = [
- { value: "month", label: "元/月" },
- { value: "quarter", label: "元/季度" },
- { value: "year", label: "元/年" },
- ];
- const ALLOWANCE_UNIT_LABEL = ALLOWANCE_UNIT_OPTIONS.reduce((acc, item) => {
- acc[item.value] = item.label;
- return acc;
- }, {});
- export function allowanceUnitLabel(value) {
- if (value == null || value === "") return "";
- return ALLOWANCE_UNIT_LABEL[String(value)] || String(value);
- }
- /** 业务记录枚举翻译:命中映射取中文,否则原样返回(空值返回空串) */
- export function enumLabel(map, code) {
- if (code == null || code === "") return "";
- const key = String(code).trim();
- return map[key] || map[key.toUpperCase()] || key;
- }
- /* =========================================================================
- * 字典归一(/system/dict/getByCode/{code} 原始返回结构有多种)
- * ========================================================================= */
- /** 字典返回体 → 数组(兼容 list / records / rows / data 包装与纯数组) */
- function dictList(raw) {
- if (Array.isArray(raw)) return raw;
- if (raw && typeof raw === "object") {
- const inner = raw.list || raw.records || raw.rows || raw.data;
- if (Array.isArray(inner)) return inner;
- }
- return [];
- }
- /**
- * 字典项归一,兼容三类结构:
- * ① 标准字段:{ label / value / name / dictLabel / dictValue / code }
- * ② 单键值对:[{6:"试用期"}, {1:"正式在职"}] —— 数字码在 key 上、文案在 value 上
- * ③ 纯字符串数组
- * @returns {Array<{label:string, value:any, key:string, id:any}>}
- */
- export function normalizeDictItems(raw) {
- const out = [];
- dictList(raw).forEach((item) => {
- if (item === null || item === undefined) return;
- if (typeof item !== "object") {
- const text = String(item).trim();
- if (text) out.push({ label: text, value: text, key: "", id: undefined });
- return;
- }
- const entries = Object.entries(item).filter(([, v]) => v !== null && v !== undefined && v !== "");
- const hasStd =
- item.label != null ||
- item.dictLabel != null ||
- item.name != null ||
- item.value != null ||
- item.dictValue != null ||
- item.code != null;
- if (hasStd) {
- const rawLabel =
- item.label != null
- ? item.label
- : item.dictLabel != null
- ? item.dictLabel
- : item.name != null
- ? item.name
- : item.value != null
- ? item.value
- : item.dictValue != null
- ? item.dictValue
- : "";
- const label = String(rawLabel).trim();
- if (!label) return;
- const id = item.id != null ? item.id : item.dictId;
- const key =
- item.key != null
- ? String(item.key)
- : item.dictCode != null
- ? String(item.dictCode)
- : id != null
- ? String(id)
- : "";
- const value =
- item.value != null
- ? item.value
- : item.dictValue != null
- ? item.dictValue
- : item.code != null
- ? item.code
- : key;
- out.push({ label, value, key, id });
- return;
- }
- // 单键值对:{6:"试用期"}
- if (entries.length === 1) {
- const [k, v] = entries[0];
- const label = String(v).trim();
- if (!label) return;
- out.push({ label, value: k, key: String(k), id: undefined });
- }
- });
- return out;
- }
- /** 员工状态字典 → 下拉项(value 一律用数字状态码,与接口 status 对齐) */
- export function dictToStatusOptions(raw) {
- return normalizeDictItems(raw)
- .map((item) => {
- const key = String(item.key);
- const value = String(item.value);
- const code = /^\d+$/.test(key) ? Number(key) : /^\d+$/.test(value) ? Number(value) : NaN;
- return Number.isFinite(code) ? { label: item.label, value: code } : null;
- })
- .filter(Boolean);
- }
- /** 岗位性质字典 → 下拉项(value 取字典主键,与 PC 表单一致:提交的 positionType 就是它) */
- export function dictToPositionNatureOptions(raw) {
- return normalizeDictItems(raw)
- .map((item) => {
- const value = item.id != null ? String(item.id) : item.key ? String(item.key) : String(item.value || "");
- return value ? { label: item.label, value } : null;
- })
- .filter(Boolean);
- }
- /* =========================================================================
- * 基础工具
- * ========================================================================= */
- function txt(value) {
- return value == null ? "" : String(value);
- }
- function trim(value) {
- return txt(value).trim();
- }
- function date10(value) {
- return value ? formatDate(value) : "";
- }
- /**
- * 主键一律按**字符串**回传。
- * 接口 GET 回来的 id 都是字符串(雪花 id 19 位,超出 Number.MAX_SAFE_INTEGER,
- * 转成 number 末尾几位会被四舍五入),提交时必须原样送字符串,否则会出现
- * 「查回来是 2096754981489143810、存进去变成 2096754981489143800」。
- * 原先这里用的是 num() = Number(value),把 id / 机构 / 部门 / 主管 / 工厂全转成了数字。
- */
- export function keepIdString(value) {
- if (value === null || value === undefined || value === "") return undefined;
- const text = String(value).trim();
- return text || undefined;
- }
- /** 去掉 undefined / null / 空串(保留 0 / false),对齐 PC 端 compact */
- function compact(obj = {}) {
- const out = {};
- Object.keys(obj).forEach((key) => {
- const value = obj[key];
- if (value === undefined || value === null || value === "") return;
- out[key] = value;
- });
- return out;
- }
- function currentUserRaw() {
- return uni.getStorageSync("userInfo") || {};
- }
- /* =========================================================================
- * 读取接口
- * ========================================================================= */
- /**
- * 拉取当前登录人的扩展资料(部门、岗位、角色等)
- * GET /system/account/getLoginUser
- */
- export async function fetchCurrentUser(params = {}, loding = false) {
- const res = await get(Vue.prototype.apiUrl + "/system/account/getLoginUser", params, loding);
- if (res.code == 0) return res.data || null;
- return Promise.reject(new Error(res.message));
- }
- /** 把 profile.postId("1111,2222")解析成岗位名称字符串 */
- async function resolvePostIds(postIdString) {
- if (!postIdString) return "";
- const ids = String(postIdString)
- .split(",")
- .map((s) => s.trim())
- .filter(Boolean);
- if (!ids.length) return "";
- const res = await postJ(Vue.prototype.apiUrl + "/hr/position/page", { pageNum: 1, size: 1000, status: 1 }, false);
- if (Number(res.code) !== 0) return "";
- const pageData = res.data || {};
- const records = Array.isArray(pageData.list) ? pageData.list : Array.isArray(pageData) ? pageData : [];
- return ids
- .map((id) => {
- const match = records.find((r) => String(r.id) === id);
- return match ? String(match.positionName || match.name || "") : "";
- })
- .filter(Boolean)
- .join("、");
- }
- /**
- * 员工档案详情(把 user 子对象摊平到顶层,并回填岗位名称)
- * GET /main/users/:userId/profile
- */
- export async function fetchUserDetail(userId, loding = true) {
- if (!userId && userId !== 0) throw new Error("缺少用户ID");
- const res = await get(Vue.prototype.apiUrl + `/main/users/${userId}/profile`, {}, loding);
- if (Number(res.code) !== 0) return Promise.reject(new Error(res.message));
- const data = res.data;
- if (!data) return null;
- const profile = data.user && typeof data.user === "object" ? { ...data, ...data.user } : data;
- try {
- const names = await resolvePostIds(profile.postId);
- if (names) profile.position = names;
- } catch (error) {
- // 岗位解析失败不影响其他字段
- }
- return profile;
- }
- /** 拉取完整档案聚合视图(基础档案 + 履历 + 业务摘要) */
- export async function fetchUserProfile(userId, loding = true) {
- if (!userId && userId !== 0) throw new Error("缺少用户ID");
- const res = await get(Vue.prototype.apiUrl + `/main/users/${userId}/profile`, {}, loding);
- if (Number(res.code) !== 0) return Promise.reject(new Error(res.message));
- const data = res.data;
- if (!data) return null;
- const profile = data.user && typeof data.user === "object" ? { ...data, ...data.user } : data;
- return { ...data, ...profile };
- }
- /** 拉取主管档案并把姓名回填到 vo.managerName */
- export async function resolveManagerName(vo) {
- if (!vo || typeof vo !== "object") return vo;
- const raw = vo.user && typeof vo.user === "object" ? vo.user : vo;
- const managerId = raw.managerUserId != null ? raw.managerUserId : vo.managerUserId;
- if (managerId === undefined || managerId === null || managerId === "") return vo;
- try {
- const managerVo = await fetchUserDetail(managerId, false);
- if (managerVo) {
- const name = (managerVo.user && managerVo.user.name) || managerVo.name || "";
- raw.managerName = String(name || "");
- }
- } catch (error) {
- // 主管档案解析失败不阻塞主流程
- }
- return vo;
- }
- /** 修改档案成功后,重新拉一次当前用户的 profile 接口并写回本地缓存 */
- export async function refetchMyProfile() {
- const login = currentUserRaw();
- const userId = login.userId || login.id;
- if (!userId && userId !== 0) throw new Error("缺少用户ID,无法刷新档案");
- const vo = await fetchUserDetail(userId);
- if (vo) {
- await resolveManagerName(vo);
- uni.setStorageSync("userInfo", { ...login, ...vo });
- }
- return vo;
- }
- /** 当前登录人 userId */
- export function currentUserId() {
- const login = currentUserRaw();
- return login.userId || login.id || "";
- }
- /** 工厂(厂区)列表,用于「所属工厂」下拉 */
- export async function listFactories(loding = false) {
- try {
- const res = await get(
- Vue.prototype.apiUrl + "/main/factoryarea/page",
- { pageNum: 1, size: 999, type: 1 },
- loding
- );
- if (Number(res.code) !== 0) return [];
- const data = res.data || {};
- const rows = Array.isArray(data.list) ? data.list : Array.isArray(data) ? data : [];
- return rows.map((item) => ({
- id: item.id != null ? item.id : item.factoryId,
- name: item.name || item.factoryName || item.areaName || "",
- }));
- } catch (error) {
- return [];
- }
- }
- /* =========================================================================
- * 工作履历(独立接口 /main/users/{userId}/work-experiences)
- * ========================================================================= */
- export function serializeWorkExperience(item = {}) {
- return compact({
- formerCompanyName: trim(item.company),
- formerDepartment: trim(item.department),
- formerPosition: trim(item.position),
- resignationReason: trim(item.leaveReason),
- workDescription: trim(item.description),
- workStartDate: item.startDate || "",
- workEndDate: item.endDate || "",
- });
- }
- export async function listWorkExperiences(userId, loding = false) {
- const uid = userId || currentUserId();
- if (!uid) return [];
- const res = await get(Vue.prototype.apiUrl + `/main/users/${uid}/work-experiences`, {}, loding);
- if (Number(res.code) !== 0) return [];
- return Array.isArray(res.data) ? res.data : [];
- }
- export async function saveWorkExperience(userId, payload = {}) {
- const uid = userId || currentUserId();
- const res = await postJ(Vue.prototype.apiUrl + `/main/users/${uid}/work-experiences`, payload, false);
- if (res.code == 0) return res.data;
- return Promise.reject(new Error(res.message));
- }
- export async function updateWorkExperience(userId, workExperienceId, payload = {}) {
- const uid = userId || currentUserId();
- const res = await putJ(
- Vue.prototype.apiUrl + `/main/users/${uid}/work-experiences/${workExperienceId}`,
- payload,
- false
- );
- if (res.code == 0) return res.data;
- return Promise.reject(new Error(res.message));
- }
- export async function removeWorkExperience(userId, workExperienceId) {
- const uid = userId || currentUserId();
- const res = await deleteApi(
- Vue.prototype.apiUrl + `/main/users/${uid}/work-experiences/${workExperienceId}`,
- {},
- false
- );
- if (res.code == 0) return res.data;
- return Promise.reject(new Error(res.message));
- }
- /**
- * 以「页面当前列表」为准同步工作履历:缺 id 的新增、有 id 的更新、页面已删除的移除。
- * 返回同步后的最新列表。
- */
- export async function syncWorkExperiences(userId, jobs = [], existing = []) {
- const uid = userId || currentUserId();
- if (!uid) return jobs;
- 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(uid, item.id));
- await Promise.all(removals);
- const saved = await Promise.all(
- next.map((item) => {
- const payload = serializeWorkExperience(item);
- if (item.id) return updateWorkExperience(uid, item.id, payload).then(() => ({ ...item }));
- return saveWorkExperience(uid, payload).then((id) => ({ ...item, id: id || item.id }));
- })
- );
- return saved;
- }
- /* =========================================================================
- * 档案 VO → 页面表单模型
- * ========================================================================= */
- function adaptBankCards(raw = {}) {
- if (Array.isArray(raw.bankCardList) && raw.bankCardList.length) {
- return raw.bankCardList.map((item, index) => ({
- key: `bank_${item.id != null ? item.id : index}`,
- id: item.id,
- bankName: txt(item.openingBank),
- bankBranch: txt(item.bankBranch),
- bankAccount: txt(item.cardNumber),
- bankAccountName: txt(item.accountName || raw.name),
- }));
- }
- if (raw.openingBank || raw.cardNumber) {
- return [
- {
- key: "bank_legacy",
- id: undefined,
- bankName: txt(raw.openingBank),
- bankBranch: txt(raw.bankBranch),
- bankAccount: txt(raw.cardNumber),
- bankAccountName: txt(raw.name),
- },
- ];
- }
- return [];
- }
- function adaptSocialSecurity(raw = {}) {
- if (!Array.isArray(raw.socialSecurityList)) return [];
- return raw.socialSecurityList.map((item, index) => ({
- key: `social_${item.id != null ? item.id : index}`,
- id: item.id,
- type: txt(item.socialType),
- base: item.socialBase === null || item.socialBase === undefined || item.socialBase === "" ? "" : String(item.socialBase),
- insuredDate: date10(item.insuredDate),
- stopDate: date10(item.stopDate),
- stopReason: txt(item.stopRecord),
- isEnabled: Number(item.isEnabled) === 1 ? 1 : 0,
- }));
- }
- function adaptCertificates(raw = {}) {
- const levelLabel = { 1: "初级", 2: "中级", 3: "高级" };
- const typeLabel = Object.keys(CERT_TYPE_CODE).reduce((acc, name) => {
- acc[String(CERT_TYPE_CODE[name])] = name;
- return acc;
- }, {});
- if (!Array.isArray(raw.identityPhotos)) return [];
- return raw.identityPhotos.map((item, index) => ({
- key: `cert_${item.id != null ? item.id : index}`,
- id: item.id,
- hrCertificateId: item.hrCertificateId,
- type: typeLabel[String(item.type)] || "其他",
- number: txt(item.code),
- level: item.level == null || item.level === "" ? "" : levelLabel[item.level] || String(item.level),
- status: Number(item.status) === 0 ? "失效" : "有效",
- startDate: date10(item.validityStartTime),
- endDate: date10(item.validityEndTime),
- issueDate: date10(item.enactorTime),
- attachment: item.fileObj || item.fileIds || item.files || item.attachment || [],
- remark: txt(item.remark),
- holder: txt(item.holder || raw.name),
- holderId: item.holderId != null ? item.holderId : raw.id,
- holderType: item.holderType || "1",
- hasPositionAllowance: Number(item.hasPositionAllowance) === 1 ? 1 : 0,
- positionAllowanceAmount:
- item.positionAllowanceAmount === "" || item.positionAllowanceAmount == null
- ? ""
- : String(item.positionAllowanceAmount),
- positionAllowanceUnit:
- item.positionAllowancePaymentUnit == null ? "" : String(item.positionAllowancePaymentUnit),
- }));
- }
- function adaptWorkExperiences(list = []) {
- return (list || []).map((item, index) => ({
- key: `job_${item.id != null ? item.id : index}`,
- id: item.id,
- startDate: date10(item.workStartDate),
- endDate: date10(item.workEndDate),
- company: txt(item.formerCompanyName),
- position: txt(item.formerPosition),
- department: txt(item.formerDepartment),
- leaveReason: txt(item.resignationReason),
- description: txt(item.workDescription),
- }));
- }
- function adaptContract(raw = {}) {
- const first = Array.isArray(raw.contracts) ? raw.contracts[0] : null;
- if (!first) {
- return { category: "", name: "", signDate: "", startDate: "", endDate: "", status: "", remark: "" };
- }
- return {
- category: txt(first.contractType),
- name: txt(first.contractNo),
- signDate: date10(first.effectiveDate || first.startDate),
- startDate: date10(first.startDate),
- endDate: date10(first.endDate),
- // 状态:优先接口给的中文名,其次按枚举码翻译,最后才原样透出
- status:
- txt(first.contractStatusName) ||
- enumLabel(CONTRACT_STATUS_LABELS, first.contractStatus) ||
- enumLabel(CONTRACT_SIGNING_STATUS_LABELS, first.signingStatus),
- remark: txt(first.remark),
- };
- }
- /** 主岗位(primaryFlag 优先,其次第一条) */
- export function pickPrimaryPosition(raw = {}) {
- const list = Array.isArray(raw.positionList) ? raw.positionList : [];
- if (!list.length) return null;
- return list.find((item) => Number(item.primaryFlag) === 1) || list[0];
- }
- /**
- * 把档案聚合 VO 摊平成页面表单模型(字段命名对齐 PC 表单,便于复用载荷构造)。
- * @param {object} raw 已摊平 user 字段的档案对象
- * @param {object} extras { workExperiences }
- */
- export function buildProfileForm(raw = {}, extras = {}) {
- const primary = pickPrimaryPosition(raw);
- const positionIds = txt(raw.postId)
- .split(",")
- .map((s) => s.trim())
- .filter(Boolean);
- const deptIds = Array.isArray(raw.deptIds) ? raw.deptIds.filter((id) => id != null && id !== "") : [];
- const positionId = primary && primary.positionId != null ? String(primary.positionId) : positionIds[0] || "";
- const birthDate = date10(raw.birthday);
- // 接口不给 age 时按出生日期兜底(PC 表单同步口径)
- const age =
- raw.age == null || raw.age === "" ? calcAgeByBirthday(birthDate) : String(raw.age);
- const statusCode = raw.status == null || raw.status === "" ? NaN : Number(raw.status);
- return {
- id: raw.id != null ? raw.id : raw.userId,
- accountId: raw.accountId,
- account: txt(raw.loginName || raw.account),
- employeeNo: txt(raw.jobNumber),
- name: txt(raw.name || raw.userName),
- gender: sexLabel(raw.sex),
- birthDate,
- age,
- idCard: txt(raw.idCard || raw.idNumber),
- ethnic: txt(raw.ethnic),
- politicalStatus: txt(raw.politicsStatus),
- maritalStatus: txt(raw.maritalStatus),
- nativePlace: txt(raw.birthplace),
- education: txt(raw.educationBackground),
- degree: txt(raw.degree),
- school: txt(raw.school),
- major: txt(raw.major),
- graduationDate: date10(raw.graduateDate),
- censusAddress: txt(raw.censusAddress),
- currentAddress: txt(raw.currentAddress),
- workAddress: txt(raw.address),
- phone: txt(raw.phone),
- email: txt(raw.email),
- wechat: txt(raw.wxId),
- emergencyContact: txt(raw.secondLinkName),
- emergencyRelation: txt(raw.secondLinkRelation),
- emergencyPhone: txt(raw.secondLinkPhone),
- groupId: raw.groupId != null && raw.groupId !== "" ? String(raw.groupId) : "",
- company: txt(raw.groupName),
- factoryId: raw.factoryId != null && raw.factoryId !== "" ? String(raw.factoryId) : raw.factoriesId != null ? String(raw.factoriesId) : "",
- factory: txt(raw.factoryName),
- deptIds,
- deptId: deptIds.length ? String(deptIds[deptIds.length - 1]) : "",
- deptName: txt(raw.deptName || raw.deptNames),
- positionSequenceId:
- primary && primary.positionSequenceId != null ? String(primary.positionSequenceId) : "",
- positionSequenceName: txt(primary && (primary.positionSequenceName || primary.sequenceName)),
- positionId,
- // 岗位名称:优先取岗位明细(positionList)里的名称,与 PC adaptPositionBindings 口径一致
- positionName: txt(
- (Array.isArray(raw.positionList)
- ? raw.positionList
- .map((item) => (item && (item.positionName || item.name)) || "")
- .filter(Boolean)
- .join("、")
- : "") ||
- raw.postName ||
- raw.position
- ),
- // 岗位性质:码与名称分开存(码取字典主键,名称缺失时由页面按字典翻译),对齐 PC 口径
- employmentType: txt(
- (primary && (primary.positionType != null ? primary.positionType : "")) ||
- raw.positionType ||
- raw.employmentType ||
- raw.staffingType ||
- ""
- ),
- employmentTypeName: txt(
- (primary && (primary.positionTypeName || "")) ||
- raw.positionTypeName ||
- raw.employmentTypeName ||
- raw.staffingTypeName ||
- ""
- ),
- staffingLevel: txt((primary && (primary.positionLevelName || primary.positionLevel)) || ""),
- managerId: raw.managerUserId != null && raw.managerUserId !== "" ? String(raw.managerUserId) : "",
- manager: txt(raw.managerUserName || raw.managerName),
- hireDate: date10(raw.joinDate || raw.hireDate),
- regularDate: date10(raw.positiveDate),
- // 员工状态:只认接口原始状态码,取不到就留空(不编造默认值,避免「已离职」被回显成「正式在职」)
- status: Number.isFinite(statusCode) && statusCode > 0 ? statusCode : "",
- statusName: txt(raw.statusName),
- enabled: Number(raw.isEnabled) !== 0,
- keyProcess: txt(
- raw.criticalTaskIds ||
- (Array.isArray(raw.criticalTaskList)
- ? raw.criticalTaskList.map((item) => item.taskName || item.taskId).filter(Boolean).join(",")
- : "")
- ),
- bankAccounts: adaptBankCards(raw),
- socialSecurityRecords: adaptSocialSecurity(raw),
- certificates: adaptCertificates(raw),
- previousJobs: adaptWorkExperiences(extras.workExperiences || []),
- remarks: txt(raw.remark),
- contract: adaptContract(raw),
- };
- }
- /* =========================================================================
- * 表单模型 → /main/user/saveNew 载荷
- * ========================================================================= */
- /** 关键工序字符串 → CriticalTaskDTO 列表(数字视为已存在的 taskId) */
- function buildCriticalTaskList(text, rawList = []) {
- const value = trim(text);
- if (!value) return [];
- const rows = Array.isArray(rawList) ? rawList : [];
- return value
- .split(/[,,]/)
- .map((item) => trim(item))
- .filter(Boolean)
- .map((item) => {
- if (/^\d+$/.test(item)) return { taskId: item, taskName: item };
- const matched = rows.find((row) => String(row.taskName) === item || String(row.taskId) === item);
- if (matched) return compact({ taskId: matched.taskId, taskName: matched.taskName || item });
- return { taskName: item };
- });
- }
- /**
- * 页面表单 → 保存参数(对齐 PC 端 serializeEmployee 的字段口径)。
- * @param {object} form 表单模型
- * @param {object} options { raw, positionList }
- */
- export function buildUserPayload(form = {}, options = {}) {
- const raw = options.raw || {};
- const positionList = Array.isArray(options.positionList) ? options.positionList : [];
- const bankCardList = (form.bankAccounts || [])
- .filter((item) => item.bankName || item.bankBranch || item.bankAccount)
- .map((item) => ({
- openingBank: trim(item.bankName),
- bankBranch: trim(item.bankBranch),
- cardNumber: trim(item.bankAccount),
- accountName: trim(item.bankAccountName || form.name),
- }))
- .filter((item) => item.accountName);
- const socialSecurityList = (form.socialSecurityRecords || [])
- .filter((item) => trim(item.type))
- .map((item) =>
- compact({
- socialType: trim(item.type),
- socialBase: item.base === "" || item.base == null ? undefined : Number(item.base),
- insuredDate: item.insuredDate || "",
- stopDate: item.stopDate || "",
- stopRecord: trim(item.stopReason),
- isEnabled: Number(item.isEnabled) === 1 ? 1 : 0,
- })
- );
- const identityPhotos = (form.certificates || [])
- .filter((item) => trim(item.type) || trim(item.number))
- .map((item) =>
- compact({
- id: keepIdString(item.id),
- type: CERT_TYPE_CODE[trim(item.type)] != null ? CERT_TYPE_CODE[trim(item.type)] : CERT_TYPE_CODE.其他,
- code: trim(item.number),
- level: CERT_LEVEL_CODE[trim(item.level)] || trim(item.level),
- status: item.status === "失效" ? 0 : 1,
- validityStartTime: item.startDate || "",
- validityEndTime: item.endDate || "",
- enactorTime: item.issueDate || "",
- remark: trim(item.remark),
- holderType: item.holderType || "1",
- holder: trim(item.holder || form.name),
- holderId: keepIdString(item.holderId != null ? item.holderId : form.id),
- hasPositionAllowance: Number(item.hasPositionAllowance) === 1 ? 1 : 0,
- positionAllowanceAmount:
- Number(item.hasPositionAllowance) === 1 && item.positionAllowanceAmount !== "" && item.positionAllowanceAmount != null
- ? Number(item.positionAllowanceAmount)
- : undefined,
- positionAllowancePaymentUnit:
- Number(item.hasPositionAllowance) === 1 && item.positionAllowanceUnit
- ? String(item.positionAllowanceUnit)
- : undefined,
- fileObj: Array.isArray(item.attachment) ? item.attachment : undefined,
- hrCertificateId: keepIdString(item.hrCertificateId),
- })
- );
- const deptIds = (Array.isArray(form.deptIds) && form.deptIds.length ? form.deptIds : form.deptId ? [form.deptId] : [])
- .map((id) => keepIdString(id))
- .filter((id) => id != null);
- const payload = compact({
- id: keepIdString(form.id),
- accountId: keepIdString(form.accountId),
- name: trim(form.name),
- jobNumber: trim(form.employeeNo),
- sex: SEX_CODE[form.gender] != null ? SEX_CODE[form.gender] : undefined,
- birthday: form.birthDate || "",
- age: form.age === "" || form.age == null ? undefined : Number(form.age),
- idCard: trim(form.idCard),
- idNumber: trim(form.idCard),
- ethnic: trim(form.ethnic),
- politicsStatus: trim(form.politicalStatus),
- maritalStatus: trim(form.maritalStatus),
- birthplace: trim(form.nativePlace),
- censusAddress: trim(form.censusAddress),
- currentAddress: trim(form.currentAddress),
- address: trim(form.workAddress),
- educationBackground: trim(form.education),
- degree: trim(form.degree),
- school: trim(form.school),
- major: trim(form.major),
- graduateDate: form.graduationDate || "",
- phone: trim(form.phone),
- email: trim(form.email),
- wxId: trim(form.wechat),
- // 员工状态只提交数字状态码;statusName 仅用于页面兜底显示,不回传避免写入过期快照
- status: Number(form.status) > 0 ? Number(form.status) : undefined,
- joinDate: form.hireDate || "",
- positiveDate: form.regularDate || "",
- leaveDate: raw.leaveDate || "",
- isEnabled: form.enabled === false ? 0 : 1,
- bankCardList,
- secondLinkName: trim(form.emergencyContact),
- secondLinkPhone: trim(form.emergencyPhone),
- secondLinkRelation: trim(form.emergencyRelation),
- professionId: raw.professionId,
- workTypeId: raw.workTypeId,
- remark: trim(form.remarks),
- groupId: keepIdString(form.groupId),
- deptIds,
- deptName: trim(form.deptName),
- managerUserId: keepIdString(form.managerId),
- postId: keepIdString(form.positionId),
- postName: trim(form.positionName),
- positionList,
- factoryId: keepIdString(form.factoryId),
- criticalTaskList: buildCriticalTaskList(form.keyProcess, raw.criticalTaskList),
- socialSecurityList,
- identityPhotos,
- socialSecurityAccount: raw.socialSecurityAccount,
- socialSecurityAddress: raw.socialSecurityAddress,
- socialSecurityAddressId: raw.socialSecurityAddressId,
- accumulationFundAccount: raw.accumulationFundAccount,
- });
- return payload;
- }
- /**
- * 新增 / 修改员工档案
- * POST /main/user/saveNew
- */
- export async function saveEmployee(payload = {}) {
- const res = await postJ(Vue.prototype.apiUrl + "/main/user/saveNew", payload, true);
- if (res.code == 0) return res.data;
- return Promise.reject(new Error(res.message));
- }
- export { compact as compactPayload };
|