From bc39d20d8630e5e6ed8ab6a8707fb86ef406f94f Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期四, 04 六月 2026 14:43:48 +0800
Subject: [PATCH] 新增差旅报销后,对应的费用科目不展示

---
 src/views/officeProcessAutomation/ApproveManage/approve-template/formConfigUtils.js |  301 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 301 insertions(+), 0 deletions(-)

diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-template/formConfigUtils.js b/src/views/officeProcessAutomation/ApproveManage/approve-template/formConfigUtils.js
new file mode 100644
index 0000000..c1f66bd
--- /dev/null
+++ b/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: "" }],
+  };
+}
+
+/** 瑙f瀽鍗曢」榛樿鍊硷紙渚涙彁浜ら〉 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("銆�");
+}

--
Gitblit v1.9.3