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/approveListUtils.js |  358 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 358 insertions(+), 0 deletions(-)

diff --git a/src/pages/oa/_utils/approveListUtils.js b/src/pages/oa/_utils/approveListUtils.js
new file mode 100644
index 0000000..538fec9
--- /dev/null
+++ b/src/pages/oa/_utils/approveListUtils.js
@@ -0,0 +1,358 @@
+import { parseTime } from "@/utils/ruoyi";
+import {
+  formatFieldDisplayValue,
+  getFieldOptionLabel,
+  isSelectField,
+  mergeFormConfigForEdit,
+} from "./approvalFormField.js";
+import {
+  appendDotNotationQuery,
+  buildApprovalInstanceSearchDto,
+  formatKnownSelectLabel,
+  resolveInstanceFormPayload,
+  resolveListFieldRawValue,
+} from "./approvalModuleListSearch.js";
+
+export const DETAIL_STORAGE_KEY = "oa_approve_instance_detail_row";
+
+export const INSTANCE_STATUS_TEXT = {
+  PENDING: "杩涜涓�",
+  APPROVED: "宸查�氳繃",
+  REJECTED: "宸查┏鍥�",
+  DRAFT: "鑽夌",
+};
+
+export const INSTANCE_STATUS_TAG = {
+  PENDING: "warning",
+  APPROVED: "success",
+  REJECTED: "error",
+  DRAFT: "info",
+};
+
+export const TASK_STATUS_TEXT = {
+  PENDING: "寰呭鐞�",
+  APPROVED: "宸查�氳繃",
+  REJECTED: "宸查┏鍥�",
+};
+
+export const TASK_STATUS_TAG = {
+  PENDING: "warning",
+  APPROVED: "success",
+  REJECTED: "error",
+};
+
+export function instanceStatusText(status) {
+  return INSTANCE_STATUS_TEXT[status] || status || "-";
+}
+
+export function instanceStatusTagType(status) {
+  return INSTANCE_STATUS_TAG[status] || "info";
+}
+
+export function taskStatusText(status) {
+  const key = String(status || "").toUpperCase();
+  return TASK_STATUS_TEXT[key] || status || "寰呭鐞�";
+}
+
+export function taskStatusTagType(status) {
+  const key = String(status || "").toUpperCase();
+  return TASK_STATUS_TAG[key] || "info";
+}
+
+export function formatDateTime(val) {
+  if (!val) return "-";
+  return parseTime(val, "{y}-{m}-{d} {h}:{i}:{s}") || String(val);
+}
+
+/** 瑙f瀽瀹炰緥涓哄彧璇诲睍绀哄瓧娈碉紙鍚堝苟 formPayload锛屾敮鎸佷紶鏁磋鎴栦粎 formConfig锛� */
+export function resolveInstanceDisplayFields(formConfigOrRow) {
+  const row =
+    formConfigOrRow &&
+    typeof formConfigOrRow === "object" &&
+    (formConfigOrRow.formConfig != null ||
+      formConfigOrRow.formPayload != null ||
+      formConfigOrRow.formFieldDefs != null)
+      ? formConfigOrRow
+      : { formConfig: formConfigOrRow };
+  const { fields } = resolveInstanceFormPayload(row);
+  if (fields.length) return fields.filter(f => f?.key);
+  const merged = mergeFormConfigForEdit("", row.formConfig);
+  return (merged.fields || []).filter(f => f?.key);
+}
+
+export function displayFieldValue(field) {
+  const val = field.value ?? field.defaultValue;
+  if (val === undefined || val === null || val === "") return "-";
+  if (isSelectField(field)) {
+    const fromOptions = getFieldOptionLabel(field, val);
+    if (fromOptions && fromOptions !== "-") return fromOptions;
+    const known = formatKnownSelectLabel(field.key, val);
+    if (known) return known;
+    return String(val);
+  }
+  const known = formatKnownSelectLabel(field?.key, val);
+  if (known) return known;
+  const shown = formatFieldDisplayValue(field, val);
+  return shown || String(val);
+}
+
+const DATETIME_LIST_PROPS = new Set([
+  "startTime",
+  "endTime",
+  "overtimeDate",
+  "applyTime",
+]);
+
+function formatListFieldDisplay(prop, val, field) {
+  if (val === undefined || val === null || val === "") return "-";
+  if (DATETIME_LIST_PROPS.has(prop)) {
+    const shown = formatDateTime(val);
+    if (shown && shown !== "-") return shown;
+  }
+  if (field?.type === "datetimerange") {
+    const shown = formatFieldDisplayValue(field, val);
+    if (shown) return shown;
+  }
+  if (field) return displayFieldValue({ ...field, value: val });
+  const known = formatKnownSelectLabel(prop, val);
+  if (known) return known;
+  return String(val);
+}
+
+/** 瀹℃壒璁板綍 result锛歛pproved | rejected | pending */
+export function mapRecordResult(action) {
+  const s = String(action || "").toUpperCase();
+  if (s === "APPROVED" || s === "APPROVE" || s === "PASS") return "approved";
+  if (s === "REJECTED" || s === "REJECT" || s === "REFUSE") return "rejected";
+  return "pending";
+}
+
+export function recordActionLabel(result) {
+  if (result === "approved") return "閫氳繃";
+  if (result === "rejected") return "椹冲洖";
+  return "寰呭鐞�";
+}
+
+export function mapApprovalRecords(records) {
+  const list = Array.isArray(records) ? records : [];
+  return list.map((r, index) => ({
+    id: r.id ?? index,
+    operatorName: r.approverName || r.operatorName || r.createUserName || "鈥�",
+    result: mapRecordResult(r.approveAction ?? r.action ?? r.status),
+    opinion: r.approveComment || r.comment || r.opinion || "",
+    time: formatDateTime(r.approveTime || r.createTime || r.time),
+  }));
+}
+
+export function getRejectReasonFromRecords(records) {
+  const mapped = mapApprovalRecords(records);
+  const hit = mapped.find(r => r.result === "rejected");
+  return hit?.opinion || "";
+}
+
+/** 鍒楄〃 tasks 鈫� 娴佺▼鑺傜偣锛堜笌 apply 椤佃妭鐐圭粨鏋勬帴杩戯級 */
+export function mapTasksToFlowNodes(tasks) {
+  const list = Array.isArray(tasks) ? tasks : [];
+  if (!list.length) return [];
+
+  const byLevel = new Map();
+  list.forEach(t => {
+    const level = Number(t.levelNo ?? t.taskLevel ?? t.nodeOrder ?? 1);
+    if (!byLevel.has(level)) {
+      byLevel.set(level, {
+        levelNo: level,
+        approveType: t.approveType || "AND",
+        approvers: [],
+      });
+    }
+    const node = byLevel.get(level);
+    node.approvers.push({
+      approverName: t.approverName || "鈥�",
+      taskStatus: t.taskStatus ?? t.status,
+      approveComment: t.approveComment,
+      approveTime: t.approveTime,
+    });
+    if (t.approveType) node.approveType = t.approveType;
+  });
+
+  return [...byLevel.entries()]
+    .sort(([a], [b]) => a - b)
+    .map(([, node]) => node);
+}
+
+/** 缁勮瀹℃壒鎻愪氦 DTO锛堜笌 Web buildApproveInstanceDto 涓�鑷达級 */
+export function buildApproveInstanceDto(id, uiResult, comment) {
+  const opinion = (comment || "").trim();
+  return {
+    id,
+    approveAction: uiResult === "rejected" ? "REJECTED" : "APPROVED",
+    approveComment: opinion || (uiResult === "approved" ? "鍚屾剰" : ""),
+  };
+}
+
+/** 鏄惁鏈汉鍙戣捣鐨勫鎵� */
+export function isOwnApplication(item, userStore) {
+  const uid = userStore?.id;
+  if (item?.applicantId != null && uid != null && uid !== "") {
+    return String(item.applicantId) === String(uid);
+  }
+  const loginName = userStore?.nickName || userStore?.name;
+  if (loginName && item?.applicantName) {
+    return String(item.applicantName).trim() === String(loginName).trim();
+  }
+  return false;
+}
+
+/** 浠呰繘琛屼腑涓旀湰浜哄彂璧锋椂鍙紪杈� */
+export function canModifyInstance(item, userStore) {
+  return item?.status === "PENDING" && isOwnApplication(item, userStore);
+}
+
+/** 寰呭綋鍓嶇敤鎴峰鎵� */
+export function canApproveInstance(item) {
+  return Boolean(item?.isApprove) && item?.status === "PENDING";
+}
+
+export function stashInstanceRow(item) {
+  if (item) {
+    uni.setStorageSync(DETAIL_STORAGE_KEY, item);
+  }
+}
+
+export function loadInstanceRow(id) {
+  const row = uni.getStorageSync(DETAIL_STORAGE_KEY);
+  if (!row || String(row.id) !== String(id)) return null;
+  return row;
+}
+
+export const EDIT_STORAGE_KEY = "oa_approve_instance_edit_row";
+
+/** 涓氬姟鐢宠椤电姸鎬侊細杩涜涓�/宸插畬鎴愪笉鍙慨鏀癸紙涓� Web canEditBusinessInstanceRow 涓�鑷达級 */
+export function normalizeApprovalStatusKey(v) {
+  if (v == null || v === "") return "pending";
+  const upper = String(v).trim().toUpperCase();
+  if (upper === "DRAFT") return "draft";
+  if (upper === "APPROVED" || upper === "PASS") return "approved";
+  if (upper === "REJECTED" || upper === "REJECT" || upper === "REFUSE") {
+    return "rejected";
+  }
+  if (upper === "CANCELLED" || upper === "CANCEL") return "cancelled";
+  if (upper === "PENDING" || upper === "IN_PROGRESS") return "pending";
+  const lower = String(v).trim().toLowerCase();
+  if (["draft", "pending", "approved", "rejected", "cancelled"].includes(lower)) {
+    return lower;
+  }
+  return "pending";
+}
+
+export function canEditBusinessInstanceRow(row) {
+  const key = normalizeApprovalStatusKey(row?.status ?? row?.approvalStatus);
+  return key !== "pending" && key !== "approved";
+}
+
+export function businessStatusText(status) {
+  const key = normalizeApprovalStatusKey(status);
+  if (key === "draft") return "鑽夌";
+  if (key === "pending") return "杩涜涓�";
+  if (key === "approved") return "宸插畬鎴�";
+  if (key === "rejected") return "宸查┏鍥�";
+  if (key === "cancelled") return "宸叉挙閿�";
+  return instanceStatusText(status);
+}
+
+export function businessStatusTagType(status) {
+  const key = normalizeApprovalStatusKey(status);
+  if (key === "approved") return "success";
+  if (key === "rejected") return "error";
+  if (key === "draft" || key === "cancelled") return "info";
+  return "warning";
+}
+
+/** OA 鍒楄〃鑷畾涔夌姸鎬佽鏍� class */
+export function businessStatusClass(status) {
+  return `status-${normalizeApprovalStatusKey(status)}`;
+}
+
+/**
+ * 涓� Web buildApprovalInstanceListParams 涓�鑷�
+ */
+export function buildInstanceListParams({
+  page,
+  businessType,
+  extraDto = {},
+  searchForm,
+}) {
+  const dto = buildApprovalInstanceSearchDto(searchForm, extraDto);
+  const bizType = businessType ?? searchForm?.businessType;
+  if (bizType != null && bizType !== "") {
+    dto.businessType = bizType;
+  }
+
+  const params = {
+    current: page.current,
+    size: page.size,
+    "page.current": page.current,
+    "page.size": page.size,
+    ...dto,
+  };
+  appendDotNotationQuery(params, "approvalInstanceDto", dto);
+  return params;
+}
+
+export function unwrapInstancePage(res) {
+  const data = res?.data ?? res;
+  return {
+    records: Array.isArray(data?.records) ? data.records : [],
+    total: Number(data?.total ?? 0),
+  };
+}
+
+/** 浠庡疄渚嬭鎻愬彇鍒楄〃灞曠ず瀛楁锛坙abel + value锛屽惈 formPayload锛� */
+export function buildFormDisplayRows(row, listFields = []) {
+  const { fields, formPayload } = resolveInstanceFormPayload(row);
+  const fieldByKey = new Map((fields || []).map(f => [f.key, f]));
+  const rows = [];
+  const defs = listFields || [];
+
+  if (defs.length) {
+    defs.forEach(def => {
+      if (!def?.prop) return;
+      const prop = def.prop;
+      const hit = fieldByKey.get(prop);
+      const raw = resolveListFieldRawValue(prop, row, fields, formPayload);
+      rows.push({
+        label: def.label || hit?.label || prop,
+        value: formatListFieldDisplay(prop, raw, hit),
+      });
+    });
+  } else {
+    fields.slice(0, 3).forEach(f => {
+      rows.push({ label: f.label, value: displayFieldValue(f) });
+    });
+  }
+  return rows;
+}
+
+/** 鍒楄〃琛屽寮猴紙淇濈暀鍘熷瀛楁渚涜鎯�/缂栬緫锛� */
+export function mapInstanceListRow(row, listFields = []) {
+  if (!row) return {};
+  const displayRows = buildFormDisplayRows(row, listFields);
+  const extra = {};
+  const { fields, formPayload } = resolveInstanceFormPayload(row);
+  (listFields || []).forEach(def => {
+    if (!def?.prop) return;
+    const hit = fields.find(f => f.key === def.prop);
+    const raw = resolveListFieldRawValue(def.prop, row, fields, formPayload);
+    extra[def.prop] = formatListFieldDisplay(def.prop, raw, hit);
+  });
+  return {
+    ...row,
+    approvalStatus: normalizeApprovalStatusKey(row.status),
+    summary: row.title || row.templateName || "",
+    createTime: formatDateTime(row.applyTime || row.createTime),
+    displayRows,
+    formPayload,
+    formFieldDefs: fields,
+    ...extra,
+  };
+}

--
Gitblit v1.9.3