From a9d97b150701e634bdb751eab277696abd136cca Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期二, 16 六月 2026 14:39:47 +0800
Subject: [PATCH] 君歌app 1.依照web端功能修改

---
 src/pages/oa/_utils/approvalModuleApplyExtras.js |  293 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 293 insertions(+), 0 deletions(-)

diff --git a/src/pages/oa/_utils/approvalModuleApplyExtras.js b/src/pages/oa/_utils/approvalModuleApplyExtras.js
new file mode 100644
index 0000000..820d584
--- /dev/null
+++ b/src/pages/oa/_utils/approvalModuleApplyExtras.js
@@ -0,0 +1,293 @@
+import dayjs from "dayjs";
+import {
+  APPROVAL_MODULE_KEYS,
+  APPROVAL_MODULE_REGISTRY,
+  getModuleMatchingBusinessTypes,
+} from "./approvalModuleRegistry.js";
+import { matchBusinessTypeValue } from "./approvalTemplateType.js";
+import { parseDatetimerangeValue } from "./approvalFormField.js";
+
+/** 浜哄憳涓嬫媺瀛楁璇嗗埆锛堜笌 Web SELECT_OPTION_SOURCE.USER 绛変环锛� */
+export function isUserSelectField(field) {
+  const src = String(field?.optionSource ?? "").toLowerCase();
+  return (
+    src === "user" ||
+    src === "personnel" ||
+    src === "userlist" ||
+    (field?.type === "select" && String(field?.label || "").includes("鐢宠浜�"))
+  );
+}
+
+export function findApplicantTemplateField(fields = []) {
+  return (
+    fields.find(f => String(f?.label || "").includes("鐢宠浜�")) ||
+    fields.find(f => isUserSelectField(f)) ||
+    null
+  );
+}
+
+/* ---------- 璇峰亣 ---------- */
+
+export function isLeaveBalanceField(field) {
+  const label = String(field?.label || "");
+  return label.includes("鍋囨湡浣欓") || field?.key === "leaveBalanceDays";
+}
+
+export function isLeaveDurationField(field) {
+  const label = String(field?.label || "");
+  return label.includes("璇峰亣鏃堕暱") || field?.key === "leaveDurationDays";
+}
+
+export function displayLeaveTemplateFields(fields = []) {
+  return (fields || []).filter(
+    f => !isLeaveBalanceField(f) && !isLeaveDurationField(f)
+  );
+}
+
+export function findLeaveTimeTemplateField(fields = []) {
+  return (
+    fields.find(
+      f => f?.type === "datetimerange" && String(f?.label || "").includes("璇峰亣鏃堕棿")
+    ) ||
+    fields.find(f => f?.type === "datetimerange" && f?.key === "dateRange") ||
+    fields.find(f => f?.type === "datetimerange") ||
+    null
+  );
+}
+
+export function resolveTimeRangeFromPayload(payload, timeField) {
+  if (!timeField?.key) return { start: "", end: "" };
+  const val = payload?.[timeField.key];
+  if (Array.isArray(val) && val.length >= 2) {
+    return { start: val[0] || "", end: val[1] || "" };
+  }
+  return parseDatetimerangeValue(val);
+}
+
+export function computeLeaveDays(startStr, endStr) {
+  if (!startStr || !endStr) return null;
+  const t0 = dayjs(startStr);
+  const t1 = dayjs(endStr);
+  if (!t0.isValid() || !t1.isValid() || !t1.isAfter(t0)) return null;
+  const days = t1.diff(t0, "millisecond") / (24 * 60 * 60 * 1000);
+  return Math.round(days * 100) / 100;
+}
+
+export function computeLeaveDurationDisplay(fields, formPayload) {
+  const timeField = findLeaveTimeTemplateField(fields);
+  const { start, end } = resolveTimeRangeFromPayload(formPayload, timeField);
+  const d = computeLeaveDays(start, end);
+  return d == null ? "" : String(d);
+}
+
+export function validateLeaveBeforeSubmit(fields, formPayload) {
+  const timeField = findLeaveTimeTemplateField(fields);
+  const { start, end } = resolveTimeRangeFromPayload(formPayload, timeField);
+  if (computeLeaveDays(start, end) == null) {
+    return "璇锋鏌ユā鏉夸腑鐨勮鍋囨椂闂达紝缁撴潫鏃堕棿椤绘櫄浜庡紑濮嬫椂闂�";
+  }
+  return "";
+}
+
+/* ---------- 鍔犵彮 ---------- */
+
+export function isOvertimeDurationField(field) {
+  const label = String(field?.label || "");
+  return label.includes("鍔犵彮鏃堕暱") || field?.key === "overtimeHours";
+}
+
+export function displayOvertimeTemplateFields(fields = []) {
+  return (fields || []).filter(f => !isOvertimeDurationField(f));
+}
+
+export function findOvertimeTimeTemplateField(fields = []) {
+  return (
+    fields.find(
+      f => f?.type === "datetimerange" && String(f?.label || "").includes("鍔犵彮鏃堕棿")
+    ) ||
+    fields.find(f => f?.type === "datetimerange") ||
+    null
+  );
+}
+
+export function computeOvertimeHours(startStr, endStr) {
+  if (!startStr || !endStr) return null;
+  const t0 = dayjs(startStr);
+  const t1 = dayjs(endStr);
+  if (!t0.isValid() || !t1.isValid() || !t1.isAfter(t0)) return null;
+  return Math.round((t1.diff(t0, "millisecond") / 3600000) * 100) / 100;
+}
+
+export function computeOvertimeHoursDisplay(fields, formPayload) {
+  const field = findOvertimeTimeTemplateField(fields);
+  const { start, end } = resolveTimeRangeFromPayload(formPayload, field);
+  const h = computeOvertimeHours(start, end);
+  return h == null ? "" : String(h);
+}
+
+export function validateOvertimeBeforeSubmit(fields, formPayload) {
+  const field = findOvertimeTimeTemplateField(fields);
+  const { start, end } = resolveTimeRangeFromPayload(formPayload, field);
+  if (computeOvertimeHours(start, end) == null) {
+    return "璇锋鏌ユā鏉夸腑鐨勫姞鐝椂闂达紝缁撴潫鏃堕棿椤绘櫄浜庡紑濮嬫椂闂�";
+  }
+  return "";
+}
+
+/* ---------- 璋冨矖 ---------- */
+
+export function isOriginalPostField(field) {
+  const label = String(field?.label || "");
+  return (
+    label.includes("鍘熷矖浣�") ||
+    field?.key === "originalPost" ||
+    field?.key === "originalPostName" ||
+    field?.key === "originalPostId"
+  );
+}
+
+export function displayTransferTemplateFields(fields = []) {
+  return (fields || []).filter(f => !isOriginalPostField(f));
+}
+
+export function unwrapUserArray(payload) {
+  if (Array.isArray(payload)) return payload;
+  if (payload?.data && Array.isArray(payload.data)) return payload.data;
+  if (payload?.rows && Array.isArray(payload.rows)) return payload.rows;
+  return [];
+}
+
+export function isActiveUser(u) {
+  if (u?.delFlag === "2" || u?.delFlag === 2) return false;
+  if (u?.status == null) return true;
+  return String(u.status) === "0";
+}
+
+export function firstPostId(user) {
+  if (!user) return undefined;
+  if (Array.isArray(user.postIds) && user.postIds.length) return user.postIds[0];
+  if (user.postId != null && user.postId !== "") return user.postId;
+  return undefined;
+}
+
+export function buildPostIdToNameMap(postRows = []) {
+  const m = {};
+  for (const p of postRows) {
+    const id = p.postId ?? p.value ?? p.id;
+    if (id != null && id !== "") {
+      m[String(id)] = p.postName ?? p.label ?? p.name ?? "";
+    }
+  }
+  return m;
+}
+
+export function resolveOriginalPostName(user, postIdToName = {}) {
+  if (!user) return "";
+  const nameStr = (user.postName ?? user.postname ?? "").toString().trim();
+  if (nameStr) return nameStr;
+  if (Array.isArray(user.posts) && user.posts.length) {
+    return (user.posts[0].postName ?? "").toString() || "鏈懡鍚嶅矖浣�";
+  }
+  const pid = firstPostId(user);
+  if (pid != null && pid !== "") {
+    const n = postIdToName[String(pid)] || "";
+    return n || "褰撳墠宀椾綅锛堟湭鍦ㄥ矖浣嶅瓧鍏镐腑锛�";
+  }
+  return "鏈垎閰嶅矖浣�";
+}
+
+export function userById(users, id) {
+  if (id == null || id === "") return undefined;
+  return (users || []).find(u => String(u.userId ?? u.id) === String(id));
+}
+
+/** 鎸� moduleKey 杩囨护妯℃澘濉姤椤� */
+export function displayTemplateFieldsByModule(moduleKey, fields = []) {
+  if (moduleKey === APPROVAL_MODULE_KEYS.LEAVE) {
+    return displayLeaveTemplateFields(fields);
+  }
+  if (moduleKey === APPROVAL_MODULE_KEYS.OVERTIME) {
+    return displayOvertimeTemplateFields(fields);
+  }
+  if (moduleKey === APPROVAL_MODULE_KEYS.TRANSFER) {
+    return displayTransferTemplateFields(fields);
+  }
+  return fields || [];
+}
+
+/** 淇濆瓨鍓嶅皢涓氬姟鎵╁睍瀛楁鍐欏叆 formValues */
+export function syncModuleExtrasToFormValues(moduleKey, formValues, extras, fields) {
+  if (!moduleKey || !formValues) return;
+  if (moduleKey === APPROVAL_MODULE_KEYS.LEAVE) {
+    if (extras.leaveBalanceDays != null && extras.leaveBalanceDays !== "") {
+      formValues.leaveBalanceDays = extras.leaveBalanceDays;
+    }
+    const days = computeLeaveDurationDisplay(fields, formValues);
+    if (days) formValues.leaveDurationDays = days;
+  }
+  if (moduleKey === APPROVAL_MODULE_KEYS.OVERTIME) {
+    const hours = computeOvertimeHoursDisplay(fields, formValues);
+    if (hours) formValues.overtimeHours = hours;
+  }
+  if (moduleKey === APPROVAL_MODULE_KEYS.TRANSFER && extras.originalPostName) {
+    formValues.originalPostName = extras.originalPostName;
+    formValues.originalPost = extras.originalPostName;
+  }
+}
+
+/** 涓氬姟鎵╁睍鏍¢獙 */
+export function validateModuleExtras(moduleKey, fields, formPayload, extras) {
+  if (moduleKey === APPROVAL_MODULE_KEYS.LEAVE) {
+    if (
+      extras.leaveBalanceDays == null ||
+      extras.leaveBalanceDays === "" ||
+      Number.isNaN(Number(extras.leaveBalanceDays))
+    ) {
+      return "璇峰~鍐欏亣鏈熶綑棰�";
+    }
+    const msg = validateLeaveBeforeSubmit(fields, formPayload);
+    if (msg) return msg;
+  }
+  if (moduleKey === APPROVAL_MODULE_KEYS.OVERTIME) {
+    const msg = validateOvertimeBeforeSubmit(fields, formPayload);
+    if (msg) return msg;
+  }
+  return "";
+}
+
+/** 浠庡疄渚� businessType 鎺ㄦ柇 moduleKey锛堢紪杈戝叆鍙f湭甯� moduleKey 鏃讹級 */
+export function inferModuleKeyFromRow(row, typeOptions = []) {
+  const bt = row?.businessType;
+  if (bt == null || bt === "") return "";
+  for (const key of Object.values(APPROVAL_MODULE_KEYS)) {
+    const types = getModuleMatchingBusinessTypes(key, typeOptions);
+    if (types.some(t => matchBusinessTypeValue(t, bt))) return key;
+    const cfg = APPROVAL_MODULE_REGISTRY[key];
+    if (cfg && matchBusinessTypeValue(cfg.approvalType, bt)) return key;
+  }
+  return "";
+}
+
+/** 缂栬緫鍥炴樉锛氫粠瀹炰緥琛屾仮澶嶆墿灞曞瓧娈� */
+export function loadModuleExtrasFromRow(moduleKey, row, formPayload) {
+  const extras = {
+    leaveBalanceDays: undefined,
+    originalPostName: "",
+  };
+  if (!moduleKey || !row) return extras;
+
+  const payload = formPayload || {};
+  if (moduleKey === APPROVAL_MODULE_KEYS.LEAVE) {
+    const v = payload.leaveBalanceDays ?? row.leaveBalanceDays;
+    extras.leaveBalanceDays =
+      v != null && v !== "" ? Number(v) : undefined;
+  }
+  if (moduleKey === APPROVAL_MODULE_KEYS.TRANSFER) {
+    extras.originalPostName =
+      payload.originalPostName ||
+      payload.originalPost ||
+      row.originalPostName ||
+      "";
+  }
+  return extras;
+}

--
Gitblit v1.9.3