| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402 |
- /**
- * M09 员工自助「统一申请」接口层
- *
- * 员工端:GET /hr/ess/applications (只返回本人申请)
- * GET /hr/ess/applications/{id}
- * 管理端:GET /hr/ess/admin/applications (当前公司组织树内全部申请)
- * GET /hr/ess/admin/applications/{id}
- *
- * 说明:该视图只返回「来源引用 + 脱敏摘要 + 状态 + 当前节点」,
- * 业务明细需按 businessType 走各业务来源接口(本文件的 resolveSourceRoute 负责分发)。
- */
- import { get } from "@/utils/request";
- import Vue from "vue";
- // ============ 业务类型 → 提交页 / 展示元数据 ============
- /**
- * 每种业务类型对应的「提交页」。type 为该页接受的表单类型参数。
- * path 与 pages.json 注册路径保持一致。
- */
- export const ESS_BUSINESS_META = {
- ATTENDANCE_LEAVE: {
- type: "leave",
- path: "/pages/hr/apply/form",
- title: "请假申请",
- icon: "假",
- color: "#157a2c",
- },
- ATTENDANCE_OVERTIME: {
- type: "overtime",
- path: "/pages/hr/apply/form",
- title: "加班申请",
- icon: "班",
- color: "#61cdbb",
- },
- ATTENDANCE_OUTING: {
- type: "field",
- path: "/pages/hr/apply/form",
- title: "外勤申请",
- icon: "外",
- color: "#4b7902",
- },
- ATTENDANCE_TRIP: {
- type: "trip",
- path: "/pages/hr/apply/form",
- title: "出差申请",
- icon: "差",
- color: "#f6bd16",
- },
- ATTENDANCE_CARD_REPAIR: {
- type: "check",
- path: "/pages/hr/apply/form",
- title: "补卡申请",
- icon: "卡",
- color: "#07a44d",
- },
- RESIGNATION: {
- type: "resign",
- path: "/pages/hr/apply/form",
- title: "离职申请",
- icon: "离",
- color: "#ee6666",
- },
- MAIN_USER_CHANGE: {
- type: "transfer",
- path: "/pages/hr/manage/transfer-create",
- title: "调岗申请",
- icon: "岗",
- color: "#9270ca",
- },
- REGULARIZATION: {
- type: "regular",
- path: "/pages/hr/manage/regular-create",
- title: "转正申请",
- icon: "正",
- color: "#269a99",
- },
- };
- // 历史/别名编码 → 标准编码(后端枚举命名不完全统一时兜底)
- const BUSINESS_TYPE_ALIAS = {
- RESIGN: "RESIGNATION",
- EMPLOYEE_RESIGNATION: "RESIGNATION",
- USER_CHANGE: "MAIN_USER_CHANGE",
- USERCHANGE: "MAIN_USER_CHANGE",
- TRANSFER: "MAIN_USER_CHANGE",
- POSITION_TRANSFER: "MAIN_USER_CHANGE",
- REGULAR: "REGULARIZATION",
- PROBATION_REGULARIZATION: "REGULARIZATION",
- ATTENDANCE_REPAIR: "ATTENDANCE_CARD_REPAIR",
- ATTENDANCE_FIELD: "ATTENDANCE_OUTING",
- };
- /** 业务类型编码归一化 */
- export function normalizeBusinessType(value) {
- const code = String(value || "").trim().toUpperCase();
- if (!code) return "";
- return BUSINESS_TYPE_ALIAS[code] || code;
- }
- /** 取业务类型元数据;未知编码返回 null(页面可提示「暂不支持查看」) */
- export function businessMetaOf(businessType) {
- return ESS_BUSINESS_META[normalizeBusinessType(businessType)] || null;
- }
- // ============ 状态字典 ============
- /** 业务状态 businessStatus */
- export const BUSINESS_STATUS_LABELS = {
- DRAFT: "草稿",
- SUBMITTED: "已提交",
- APPROVING: "审批中",
- APPROVED: "已通过",
- REJECTED: "已驳回",
- WITHDRAWN: "已撤回",
- CANCELLED: "已取消",
- COMPLETED: "已完成",
- };
- /** 审批状态 approvalStatus */
- export const APPROVAL_STATUS_LABELS = {
- PENDING: "审批中",
- SUBMITTED: "已提交",
- APPROVING: "审批中",
- APPROVED: "已通过",
- REJECTED: "已驳回",
- WITHDRAWN: "已撤回",
- CANCELLED: "已取消",
- };
- /** 状态标签配色键:pending 审批中 / approved 已通过 / rejected 已驳回 / cancelled 已取消 / draft 草稿 */
- export const ESS_STATUS_CLASS = {
- DRAFT: "draft",
- SUBMITTED: "pending",
- APPROVING: "pending",
- APPROVED: "approved",
- REJECTED: "rejected",
- WITHDRAWN: "cancelled",
- CANCELLED: "cancelled",
- PENDING: "pending",
- COMPLETED: "approved",
- };
- function labelOf(map, code, fallback = "") {
- const key = String(code || "").trim().toUpperCase();
- if (!key) return fallback;
- return map[key] || key;
- }
- // ============ 请求封装 ============
- // 去掉空串 / null / undefined,避免后端把空值当有效筛选条件
- function compact(object = {}) {
- return Object.keys(object).reduce((result, key) => {
- const value = object[key];
- if (value !== "" && value !== null && value !== undefined) result[key] = value;
- return result;
- }, {});
- }
- 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 [];
- }
- /** 大整数主键保持字符串,避免 JS 精度丢失(后端 Long 序列化可能是数字) */
- export function essIdOf(value) {
- if (value === undefined || value === null || value === "") return "";
- return String(value);
- }
- function normalizePage(res) {
- const data = res.data || {};
- const list = asList(data);
- const total = Number(
- data.count !== undefined ? data.count : data.total !== undefined ? data.total : list.length
- );
- return { list, total };
- }
- function pageParams(params = {}) {
- return compact({
- pageNum: params.pageNum || 1,
- size: params.size || 20,
- businessType: normalizeBusinessType(params.businessType) || undefined,
- businessStatus: params.businessStatus,
- approvalStatus: params.approvalStatus,
- keyword: params.keyword,
- // 管理端专用
- applicantUserId: params.applicantUserId,
- organizationId: params.organizationId,
- });
- }
- /**
- * 我的申请分页
- * GET /hr/ess/applications
- */
- export async function listMyApplications(params = {}, loding = true) {
- const res = await get(
- Vue.prototype.apiUrl + "/hr/ess/applications",
- pageParams(params),
- loding
- );
- if (res.code == 0) return normalizePage(res);
- return Promise.reject(new Error(res.message || "查询我的申请失败"));
- }
- /**
- * 我的申请详情
- * GET /hr/ess/applications/{id}
- */
- export async function getMyApplicationDetail(id, loding = true) {
- if (!essIdOf(id)) return null;
- const res = await get(
- Vue.prototype.apiUrl + `/hr/ess/applications/${encodeURIComponent(essIdOf(id))}`,
- {},
- loding
- );
- if (res.code == 0) return res.data || null;
- return Promise.reject(new Error(res.message || "查询申请详情失败"));
- }
- /**
- * 管理端:公司组织树内统一申请分页
- * GET /hr/ess/admin/applications
- */
- export async function listAdminApplications(params = {}, loding = true) {
- const res = await get(
- Vue.prototype.apiUrl + "/hr/ess/admin/applications",
- pageParams(params),
- loding
- );
- if (res.code == 0) return normalizePage(res);
- return Promise.reject(new Error(res.message || "查询申请记录失败"));
- }
- /**
- * 管理端:统一申请详情
- * GET /hr/ess/admin/applications/{id}
- */
- export async function getAdminApplicationDetail(id, loding = true) {
- if (!essIdOf(id)) return null;
- const res = await get(
- Vue.prototype.apiUrl +
- `/hr/ess/admin/applications/${encodeURIComponent(essIdOf(id))}`,
- {},
- loding
- );
- if (res.code == 0) return res.data || null;
- return Promise.reject(new Error(res.message || "查询申请详情失败"));
- }
- // ============ 适配与判定 ============
- function formatTime(value) {
- if (!value) return "";
- const text = String(value).replace("T", " ");
- return text.length >= 16 ? text.slice(0, 16) : text;
- }
- /**
- * 统一申请 VO → 列表/详情展示结构
- */
- export function adaptEssApplication(vo = {}) {
- const businessType = normalizeBusinessType(vo.businessType);
- const meta = ESS_BUSINESS_META[businessType] || null;
- const businessStatus = String(vo.businessStatus || "").toUpperCase();
- const approvalStatus = String(vo.approvalStatus || "").toUpperCase();
- // 状态优先取业务状态;业务状态缺失时退回审批状态
- const statusCode = businessStatus || approvalStatus;
- const statusLabel =
- labelOf(BUSINESS_STATUS_LABELS, businessStatus) ||
- labelOf(APPROVAL_STATUS_LABELS, approvalStatus);
- return {
- id: essIdOf(vo.id),
- applicationNo: vo.applicationNo || "",
- title: vo.title || (meta && meta.title) || vo.businessTypeName || "申请",
- businessType,
- businessTypeName: vo.businessTypeName || (meta && meta.title) || businessType || "",
- businessId: essIdOf(vo.businessId),
- businessStatus,
- approvalStatus,
- statusCode,
- statusLabel: statusLabel || "—",
- statusClass: ESS_STATUS_CLASS[statusCode] || "pending",
- currentNode: vo.currentNode || "",
- maskedSummary: vo.maskedSummary || "",
- applicantName: vo.applicantName || "",
- applicantJobNumber: vo.applicantJobNumber || "",
- applicantPic: vo.applicantPic || "",
- applicantUserId: essIdOf(vo.applicantUserId),
- organizationId: essIdOf(vo.organizationId),
- organizationName: vo.organizationName || "",
- processInstanceId: vo.processInstanceId || "",
- sourceTerminal: vo.sourceTerminal || "",
- createTime: vo.createTime || "",
- createdAt: formatTime(vo.createTime),
- icon: (meta && meta.icon) || "申",
- color: (meta && meta.color) || "#157a2c",
- supported: Boolean(meta),
- raw: vo,
- };
- }
- /**
- * 是否允许「编辑后重新提交」:仅已驳回(含审批状态为已驳回)可编辑。
- * @param {object} row adaptEssApplication 的结果,或原始 VO
- * @param {boolean} admin 管理端查看时恒为只读
- */
- export function canEditEssApplication(row = {}, admin = false) {
- if (admin) return false;
- const businessStatus = String(row.businessStatus || "").toUpperCase();
- const approvalStatus = String(row.approvalStatus || "").toUpperCase();
- return businessStatus === "REJECTED" || approvalStatus === "REJECTED";
- }
- function buildQuery(params = {}) {
- return Object.keys(params)
- .filter((key) => params[key] !== undefined && params[key] !== null && params[key] !== "")
- .map((key) => `${key}=${encodeURIComponent(String(params[key]))}`)
- .join("&");
- }
- /**
- * 列表项 → 目标提交页路由(详情模式)。
- * @param {object} row 原始 VO 或 adaptEssApplication 结果
- * @param {{admin?: boolean}} options
- * @returns {string} 可直接 uni.navigateTo 的 url;不支持的业务类型返回 ""
- */
- export function resolveEssRoute(row = {}, options = {}) {
- const meta = businessMetaOf(row.businessType);
- if (!meta) return "";
- const query = buildQuery({
- mode: "detail",
- id: essIdOf(row.id),
- businessId: essIdOf(row.businessId),
- businessType: normalizeBusinessType(row.businessType),
- processInstanceId: row.processInstanceId || "",
- type: meta.type,
- admin: options.admin ? 1 : "",
- });
- return query ? `${meta.path}?${query}` : meta.path;
- }
- /**
- * 移动端没有详情页的业务类型 → 提示文案。
- * 后端还有一类业务(如「劳动合同申请」)没有移动端详情页,原来只提示一句
- * 「不支持移动端查看」,用户不知道去哪看;这里改成明确指出去 PC 端。
- */
- const PC_ONLY_HINT = {
- // 劳动合同:只有 PC 端 HR 模块能看详情
- LABOR_CONTRACT: "劳动合同查看详情请在 PC 端 HR 查看",
- HR_CONTRACT: "劳动合同查看详情请在 PC 端 HR 查看",
- CONTRACT_APPLY: "劳动合同查看详情请在 PC 端 HR 查看",
- };
- /** 按业务编码 / 展示名称给出「去 PC 端看」的提示文案 */
- export function pcOnlyHintOf(row = {}) {
- const code = String(row.businessType || "").toUpperCase();
- const name = String(row.businessTypeName || row.title || row.businessType || "该类型").trim();
- if (PC_ONLY_HINT[code]) return PC_ONLY_HINT[code];
- // 后端业务编码未统一时,按名称兜底识别劳动合同(该业务只有 PC 端有页面)
- if (/劳动合同/.test(name)) return "劳动合同查看详情请在 PC 端 HR 查看";
- return `${name}查看详情请在 PC 端查看`;
- }
- /**
- * 打开申请详情;不支持的类型给出提示
- * @returns {boolean} 是否已跳转
- */
- export function openEssApplication(row = {}, options = {}) {
- const url = resolveEssRoute(row, options);
- if (!url) {
- uni.showToast({ title: pcOnlyHintOf(row), icon: "none" });
- return false;
- }
- uni.navigateTo({ url });
- return true;
- }
- /**
- * businessType → apply/form 的表单类型(apply/form 用 type 参数决定渲染哪套字段)
- */
- export function formTypeOf(businessType) {
- const meta = businessMetaOf(businessType);
- return meta ? meta.type : "";
- }
- /**
- * 审批状态筛选选项(列表页 tab 用)
- */
- export const APPROVAL_STATUS_FILTERS = [
- { label: "全部", value: "" },
- { label: "审批中", value: "APPROVING" },
- { label: "已通过", value: "APPROVED" },
- { label: "已驳回", value: "REJECTED" },
- ];
|