gaoluyang
2026-06-16 2f58128ac3b1c025a32f64016328992bf9bf5b48
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);
}
/** è§£æžå®žä¾‹ä¸ºåªè¯»å±•示字段(合并 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:approved | 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),
  };
}
/** ä»Žå®žä¾‹è¡Œæå–列表展示字段(label + 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,
  };
}