src/views/officeProcessAutomation/ApproveManage/approve-template/formConfigUtils.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,301 @@
import { mapAttachmentsFromApi } from "./approveTemplateConstants.js";
import {
  isDynamicOptionSource,
  SELECT_OPTION_SOURCE,
  selectOptionSourceLabel,
} from "./selectOptionSource.js";
export { selectOptionSourceLabel };
/** å¡«æŠ¥é¡¹ç±»åž‹ï¼ˆä¸Žå®¡æ‰¹æäº¤é¡µ field.type ä¸€è‡´ï¼‰ */
export const FORM_FIELD_TYPE_OPTIONS = [
  { value: "text", label: "单行文本" },
  { value: "textarea", label: "多行文本" },
  { value: "number", label: "数字" },
  { value: "date", label: "日期" },
  { value: "datetimerange", label: "日期时间范围" },
  { value: "select", label: "下拉选择" },
];
/** å¸¸ç”¨é¢„设(如费用报销) */
export const FORM_CONFIG_PRESETS = [
  {
    key: "cost_reimburse",
    label: "费用报销",
    summaryPlaceholder: "请填写报销事由、金额等",
    fields: [
      { key: "summary", label: "报销说明", type: "textarea", required: true, rows: 3 },
      { key: "amount", label: "报销金额(元)", type: "number", required: true, min: 0, precision: 2 },
    ],
  },
  {
    key: "travel_reimburse",
    label: "差旅报销",
    summaryPlaceholder: "出差行程与费用说明",
    fields: [
      { key: "summary", label: "差旅说明", type: "textarea", required: true, rows: 3 },
      { key: "amount", label: "报销金额(元)", type: "number", required: true, min: 0, precision: 2 },
      { key: "tripDays", label: "出差天数", type: "number", required: false, min: 0, precision: 0 },
    ],
  },
  {
    key: "leave",
    label: "请假申请",
    summaryPlaceholder: "请填写请假类型与时间",
    fields: [
      {
        key: "leaveType",
        label: "请假类型",
        type: "select",
        required: true,
        options: [
          { label: "年假", value: "annual" },
          { label: "病假", value: "sick" },
          { label: "事假", value: "personal" },
          { label: "调休", value: "compensatory" },
        ],
      },
      { key: "summary", label: "请假事由", type: "textarea", required: true, rows: 2 },
      { key: "dateRange", label: "请假时间", type: "datetimerange", required: true },
    ],
  },
];
function newFieldUid() {
  return `f_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
}
export function createEmptyFormField() {
  return {
    _uid: newFieldUid(),
    key: "",
    label: "",
    type: "text",
    required: true,
    rows: 3,
    min: 0,
    precision: 0,
    defaultValue: "",
    optionSource: SELECT_OPTION_SOURCE.STATIC,
    options: [{ label: "", value: "" }],
  };
}
/** è§£æžå•项默认值(供提交页 formPayload åˆå§‹åŒ–) */
export function resolveFieldDefaultValue(field) {
  const type = field?.type || "text";
  const dv = field?.defaultValue;
  if (dv === undefined || dv === null || dv === "") {
    if (type === "number") return undefined;
    if (type === "datetimerange") return [];
    return "";
  }
  if (type === "number") {
    const n = Number(dv);
    return Number.isNaN(n) ? undefined : n;
  }
  if (type === "datetimerange") {
    return Array.isArray(dv) ? [...dv] : [];
  }
  return dv;
}
function hasDefaultValue(field) {
  const type = field?.type || "text";
  const dv = field?.defaultValue;
  if (dv === undefined || dv === null) return false;
  if (type === "number") return dv !== "" && !Number.isNaN(Number(dv));
  if (type === "datetimerange") return Array.isArray(dv) && dv.length === 2;
  if (type === "select") return dv !== "";
  return String(dv).trim() !== "";
}
/** æ ¹æ®å­—段定义生成 formPayload åˆå§‹å€¼ï¼ˆå«é»˜è®¤å€¼ï¼‰ */
export function buildFormPayloadFromFields(fields) {
  const payload = {};
  (fields || []).forEach((f) => {
    const key = (f.key || "").trim();
    if (!key) return;
    payload[key] = resolveFieldDefaultValue(f);
  });
  return payload;
}
export function createEmptyFormConfigData() {
  return {
    summaryPlaceholder: "",
    fields: [],
  };
}
function parseFormConfigRaw(formConfig) {
  if (!formConfig) return {};
  if (typeof formConfig === "object") return formConfig;
  try {
    return JSON.parse(formConfig);
  } catch {
    return {};
  }
}
function normalizeDefaultValueFromApi(f) {
  const type = f.type || "text";
  if (f.defaultValue === undefined || f.defaultValue === null) {
    if (type === "number") return undefined;
    if (type === "datetimerange") return [];
    return "";
  }
  if (type === "datetimerange" && Array.isArray(f.defaultValue)) {
    return [...f.defaultValue];
  }
  return f.defaultValue;
}
/** æŽ¥å£ formConfig â†’ ç¼–辑器数据 */
export function parseFormConfigToData(formConfig) {
  const raw = parseFormConfigRaw(formConfig);
  const fields = (raw.fields || raw.formFields || []).map((f) => ({
    _uid: newFieldUid(),
    key: f.key || "",
    label: f.label || "",
    type: f.type || "text",
    required: f.required !== false,
    rows: f.rows ?? 3,
    min: f.min ?? 0,
    precision: f.precision ?? 0,
    defaultValue: normalizeDefaultValueFromApi(f),
    optionSource: f.optionSource || SELECT_OPTION_SOURCE.STATIC,
    options: (f.options || []).length
      ? f.options.map((o) => ({ label: o.label || "", value: o.value ?? "" }))
      : [{ label: "", value: "" }],
  }));
  return {
    summaryPlaceholder: raw.summaryPlaceholder || "",
    fields,
  };
}
/** ç¼–辑器数据 â†’ æäº¤ç”¨ JSON å­—符串 */
export function buildFormConfigJson(formConfigData) {
  const data = formConfigData || createEmptyFormConfigData();
  const fields = (data.fields || []).map((f) => {
    const item = {
      key: (f.key || "").trim(),
      label: (f.label || "").trim(),
      type: f.type || "text",
      required: f.required !== false,
    };
    if (item.type === "textarea") item.rows = Number(f.rows) || 3;
    if (item.type === "number") {
      item.min = f.min ?? 0;
      item.precision = f.precision ?? 0;
    }
    if (item.type === "select") {
      const source = f.optionSource || SELECT_OPTION_SOURCE.STATIC;
      item.optionSource = source;
      if (!isDynamicOptionSource(source)) {
        item.options = (f.options || [])
          .filter((o) => (o.label || "").trim() || (o.value !== "" && o.value != null))
          .map((o) => ({ label: (o.label || "").trim(), value: o.value }));
      }
    }
    if (hasDefaultValue(f)) {
      item.defaultValue =
        f.type === "datetimerange" && Array.isArray(f.defaultValue)
          ? f.defaultValue
          : f.defaultValue;
    }
    return item;
  });
  const payload = {
    summaryPlaceholder: (data.summaryPlaceholder || "").trim(),
    fields,
  };
  return JSON.stringify(payload);
}
export function applyFormConfigPreset(presetKey) {
  const preset = FORM_CONFIG_PRESETS.find((p) => p.key === presetKey);
  if (!preset) return createEmptyFormConfigData();
  return parseFormConfigToData({
    summaryPlaceholder: preset.summaryPlaceholder,
    fields: preset.fields,
  });
}
export function validateFormConfigData(formConfigData) {
  const fields = formConfigData?.fields || [];
  if (!fields.length) {
    return { ok: true };
  }
  const keys = new Set();
  for (let i = 0; i < fields.length; i++) {
    const f = fields[i];
    const key = (f.key || "").trim();
    const label = (f.label || "").trim();
    if (!key) return { ok: false, message: `请填写第 ${i + 1} ä¸ªå¡«æŠ¥é¡¹çš„字段标识` };
    if (!label) return { ok: false, message: `请填写第 ${i + 1} ä¸ªå¡«æŠ¥é¡¹çš„æ˜¾ç¤ºåç§°` };
    if (keys.has(key)) return { ok: false, message: `字段标识「${key}」重复,请修改` };
    keys.add(key);
    if (f.type === "select") {
      const source = f.optionSource || SELECT_OPTION_SOURCE.STATIC;
      if (isDynamicOptionSource(source)) continue;
      const opts = (f.options || []).filter((o) => (o.label || "").trim() && o.value !== "" && o.value != null);
      if (!opts.length) return { ok: false, message: `请为「${label}」配置至少一个下拉选项` };
    }
  }
  return { ok: true };
}
export function formFieldTypeLabel(type) {
  return FORM_FIELD_TYPE_OPTIONS.find((x) => x.value === type)?.label || type || "—";
}
export function formatDefaultValueDisplay(field) {
  const dv = field?.defaultValue;
  if (dv === undefined || dv === null || dv === "") return "—";
  if (field?.type === "datetimerange" && Array.isArray(dv)) {
    return dv.length === 2 ? `${dv[0]} ~ ${dv[1]}` : "—";
  }
  if (field?.type === "select") {
    if (isDynamicOptionSource(field.optionSource)) {
      return `${selectOptionSourceLabel(field.optionSource)}:${String(dv)}`;
    }
    const opt = (field.options || []).find((o) => String(o.value) === String(dv));
    return opt?.label || String(dv);
  }
  return String(dv);
}
/** å°†åŽç«¯æ¨¡æ¿è¡Œè½¬ä¸ºæäº¤é¡µæ¨¡æ¿ç»“构(含 fields é»˜è®¤å€¼ã€é™„件) */
export function buildSubmitTemplateFromRow(row) {
  const cfg = parseFormConfigToData(row?.formConfig);
  const fields = (cfg.fields || []).map(({ _uid, ...rest }) => ({
    ...rest,
    key: rest.key,
    label: rest.label,
    type: rest.type,
    required: rest.required,
    rows: rest.rows,
    min: rest.min,
    precision: rest.precision,
    defaultValue: rest.defaultValue,
    optionSource: rest.optionSource,
    options: rest.options,
  }));
  return {
    label: row?.templateName || "审批",
    businessType: row?.businessType ?? cfg.approvalType ?? "",
    approvalType: cfg.approvalType || "",
    summaryPlaceholder: cfg.summaryPlaceholder || "",
    approvalMode: cfg.approvalMode || "parallel",
    fields,
    storageBlobDTOs: mapAttachmentsFromApi(row),
  };
}
export function formConfigFieldsSummary(formConfigData) {
  const fields = formConfigData?.fields || [];
  if (!fields.length) return "—";
  return fields.map((f) => f.label || f.key || "未命名").join("、");
}