From 0333d66e4b397c161c6a44ce1e2a121c2cc41082 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期四, 28 五月 2026 09:20:20 +0800
Subject: [PATCH] Merge branch 'dev_NEW_pro' into dev_天津_中兴实强
---
src/pages/oa/_utils/approvalFormField.js | 372 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 372 insertions(+), 0 deletions(-)
diff --git a/src/pages/oa/_utils/approvalFormField.js b/src/pages/oa/_utils/approvalFormField.js
new file mode 100644
index 0000000..813d05e
--- /dev/null
+++ b/src/pages/oa/_utils/approvalFormField.js
@@ -0,0 +1,372 @@
+import { parseTime } from "@/utils/ruoyi";
+
+/** 濉姤瀛楁绫诲瀷锛氫笅鎷� */
+export const SELECT_FIELD_TYPES = new Set(["select", "dropdown", "picker"]);
+
+/** 鏃ユ湡鏃堕棿绫� type */
+const DATE_KIND_BY_TYPE = {
+ date: "date",
+ time: "time",
+ datetime: "datetime",
+ datetimerange: "datetime",
+};
+
+const DEFAULT_FORMAT = {
+ date: "YYYY-MM-DD",
+ time: "HH:mm",
+ datetime: "YYYY-MM-DD HH:mm:ss",
+};
+
+/** 瑙f瀽 formConfig JSON锛堝惈宓屽 formPayload锛屼笌 Web parseInstanceFormConfig 涓�鑷达級 */
+export function parseApprovalFormConfig(raw) {
+ if (!raw) return { prompt: "", fields: [], formPayload: {} };
+ try {
+ const obj = typeof raw === "string" ? JSON.parse(raw) : raw;
+ const payload = obj?.formPayload;
+ return {
+ prompt: obj?.prompt || obj?.summaryPlaceholder || "",
+ summaryPlaceholder: obj?.summaryPlaceholder || "",
+ approvalType: obj?.approvalType || "",
+ fields: Array.isArray(obj?.fields)
+ ? obj.fields
+ : Array.isArray(obj?.formFields)
+ ? obj.formFields
+ : [],
+ formPayload: payload && typeof payload === "object" ? payload : {},
+ };
+ } catch {
+ return { prompt: "", fields: [], formPayload: {} };
+ }
+}
+
+/**
+ * 淇敼瀹℃壒锛氭ā鏉垮瓧娈靛畾涔� + 瀹炰緥宸插~鍊煎悎骞�
+ */
+export function mergeFormConfigForEdit(templateRaw, instanceRaw) {
+ const template = parseApprovalFormConfig(templateRaw);
+ const instance = parseApprovalFormConfig(instanceRaw);
+ const valueMap = {};
+ instance.fields.forEach(field => {
+ if (!field?.key) return;
+ const val = field.value ?? field.defaultValue;
+ if (val !== undefined && val !== null && val !== "") {
+ valueMap[field.key] = val;
+ }
+ });
+ Object.assign(valueMap, instance.formPayload || {});
+ const baseFields = template.fields.length ? template.fields : instance.fields;
+ return {
+ prompt: instance.prompt || template.prompt,
+ fields: baseFields.map(field => ({
+ ...field,
+ value: valueMap[field.key] ?? field.value ?? field.defaultValue ?? "",
+ })),
+ };
+}
+
+/** 鏄惁涓轰笅鎷夌被瀛楁 */
+export function isSelectField(field) {
+ const type = String(field?.type ?? "").toLowerCase();
+ return SELECT_FIELD_TYPES.has(type);
+}
+
+/** 鏄惁涓哄琛屾枃鏈� */
+export function isTextareaField(field) {
+ return String(field?.type ?? "").toLowerCase() === "textarea";
+}
+
+/** 璇诲彇瀛楁閰嶇疆鐨勬棩鏈熸牸寮忥紙鍏煎 format / dateFormat / timeFormat锛� */
+export function getFieldFormatStr(field) {
+ return (
+ field?.format ?? field?.dateFormat ?? field?.timeFormat ?? ""
+ ).trim();
+}
+
+/** 鏃ユ湡鏃堕棿瀛楁绉嶇被锛歞ate | time | datetime | null */
+export function getDateFieldKind(field) {
+ const type = String(field?.type ?? "").toLowerCase();
+ if (DATE_KIND_BY_TYPE[type]) return DATE_KIND_BY_TYPE[type];
+
+ const fmt = getFieldFormatStr(field);
+ if (!fmt) return null;
+ const hasDate = /Y{2,4}|D{1,2}/i.test(fmt);
+ const hasTime = /H{1,2}|h{1,2}|m{1,2}|s{1,2}/i.test(fmt);
+ if (hasDate && hasTime) return "datetime";
+ if (hasTime && !hasDate) return "time";
+ if (hasDate) return "date";
+ return null;
+}
+
+/** 鏄惁涓烘棩鏈�/鏃堕棿绫诲瓧娈碉紙涓嶅惈鏃ユ湡鏃堕棿鑼冨洿锛� */
+export function isDateLikeField(field) {
+ if (isDatetimerangeField(field)) return false;
+ return !!getDateFieldKind(field);
+}
+
+/** @deprecated 浣跨敤 isDateLikeField */
+export function isDateField(field) {
+ return isDateLikeField(field);
+}
+
+/** uView datetime-picker 鐨� mode */
+export function getDatePickerMode(field) {
+ const kind = getDateFieldKind(field);
+ if (kind === "time") return "time";
+ if (kind === "datetime") return "datetime";
+ return "date";
+}
+
+/** moment 椋庢牸鏍煎紡 鈫� parseTime 妯℃澘 */
+export function momentFormatToParsePattern(fmt) {
+ if (!fmt) return null;
+ return fmt
+ .replace(/YYYY/g, "{y}")
+ .replace(/YY/g, "{y}")
+ .replace(/DD/g, "{d}")
+ .replace(/dd/g, "{d}")
+ .replace(/MM/g, "{m}")
+ .replace(/HH/g, "{h}")
+ .replace(/hh/g, "{h}")
+ .replace(/mm/g, "{i}")
+ .replace(/ss/g, "{s}");
+}
+
+/** 灏嗘椂闂存埑/Date 鏍煎紡鍖栦负瀛楁閰嶇疆鏍煎紡 */
+export function formatFieldDateValue(field, dateSource) {
+ const kind = getDateFieldKind(field);
+ if (!kind) return "";
+ const fmt = getFieldFormatStr(field) || DEFAULT_FORMAT[kind];
+ const pattern = momentFormatToParsePattern(fmt);
+ let date;
+ if (typeof dateSource === "number") date = new Date(dateSource);
+ else if (dateSource instanceof Date) date = dateSource;
+ else return String(dateSource ?? "");
+ return parseTime(date, pattern) || "";
+}
+
+/** 灞曠ず鐢細灏嗗凡瀛樺�兼寜閰嶇疆鏍煎紡鍥炴樉 */
+export function formatFieldDisplayValue(field, storedValue) {
+ if (storedValue === undefined || storedValue === null || storedValue === "") {
+ return "";
+ }
+ if (!getDateFieldKind(field)) return String(storedValue);
+ const ts = parseFieldDateToTs(storedValue);
+ if (ts) return formatFieldDateValue(field, ts);
+ return String(storedValue);
+}
+
+/** 灏嗗凡瀛樻棩鏈熷瓧绗︿覆杞负鏃堕棿鎴筹紙渚涢�夋嫨鍣ㄥ垵濮嬪�硷級 */
+export function parseFieldDateToTs(value) {
+ if (value === undefined || value === null || value === "") return null;
+ if (typeof value === "number") return value;
+ const str = String(value).trim();
+ const normalized = str.replace(/-/g, "/").replace("T", " ");
+ const t = new Date(normalized).getTime();
+ return Number.isNaN(t) ? null : t;
+}
+
+/** 鏄惁涓烘暟瀛� */
+export function isNumberField(field) {
+ return String(field?.type ?? "").toLowerCase() === "number";
+}
+
+/**
+ * 灏嗗瓧娈甸厤缃腑鐨勯�夐」瑙勮寖涓� { label, value }[]
+ * 鏀寔锛歰ptions / optionList锛涢」涓哄瓧绗︿覆鎴� { label|name|text, value|key|code }
+ */
+export function normalizeFieldOptions(field) {
+ const raw =
+ field?.options ?? field?.optionList ?? field?.dictOptions ?? field?.items;
+ if (!Array.isArray(raw) || !raw.length) return [];
+
+ return raw
+ .map((item, index) => {
+ if (item == null) return null;
+ if (typeof item === "string" || typeof item === "number") {
+ const text = String(item);
+ return { label: text, value: text };
+ }
+ if (typeof item !== "object") return null;
+
+ const label =
+ item.label ??
+ item.name ??
+ item.text ??
+ item.dictLabel ??
+ item.title;
+ const rawValue =
+ item.value ?? item.key ?? item.code ?? item.dictValue ?? item.id;
+
+ if (label == null && rawValue == null) return null;
+
+ const value =
+ rawValue !== undefined && rawValue !== null ? rawValue : label ?? index;
+ return {
+ label: String(label ?? value),
+ value,
+ };
+ })
+ .filter(Boolean);
+}
+
+/** 鎸夊瓨鍌ㄥ�煎尮閰嶉�夐」灞曠ず鏂囨 */
+export function getFieldOptionLabel(field, storedValue) {
+ if (storedValue === undefined || storedValue === null || storedValue === "") {
+ return "";
+ }
+ const options = normalizeFieldOptions(field);
+ const strVal = String(storedValue);
+ const matched = options.find(
+ opt =>
+ String(opt.value) === strVal ||
+ String(opt.label) === strVal
+ );
+ return matched?.label ?? "";
+}
+
+/** 鍒濆鍖栧~鎶ュ�硷細浼樺厛宸插~ value锛屽叾娆� defaultValue */
+export function getFieldInitialValue(field) {
+ if (field?.value !== undefined && field?.value !== null && field?.value !== "") {
+ return field.value;
+ }
+ if (
+ field?.defaultValue !== undefined &&
+ field?.defaultValue !== null &&
+ field?.defaultValue !== ""
+ ) {
+ return field.defaultValue;
+ }
+ return "";
+}
+
+/** 妯℃澘缂栬緫锛氭帶浠剁被鍨嬮�夐」锛堜笌 Web 绔竴鑷达級 */
+export const FIELD_EDITOR_TYPE_OPTIONS = [
+ { name: "鍗曡鏂囨湰", value: "text" },
+ { name: "澶氳鏂囨湰", value: "textarea" },
+ { name: "鏁板瓧", value: "number" },
+ { name: "鏃ユ湡", value: "date" },
+ { name: "鏃ユ湡鏃堕棿鑼冨洿", value: "datetimerange" },
+ { name: "涓嬫媺閫夋嫨", value: "select" },
+];
+
+/** 涓嬫媺閫夐」鏉ユ簮 */
+export const FIELD_OPTION_SOURCE_OPTIONS = [
+ { name: "鎵嬪姩閰嶇疆", value: "manual" },
+ { name: "浜哄憳鍒楄〃", value: "user" },
+ { name: "閮ㄩ棬鍒楄〃", value: "dept" },
+];
+
+const OPTION_SOURCE_ALIASES = {
+ manual: "manual",
+ user: "user",
+ personnel: "user",
+ userlist: "user",
+ dept: "dept",
+ department: "dept",
+ deptlist: "dept",
+};
+
+export function getFieldEditorTypeLabel(type) {
+ const found = FIELD_EDITOR_TYPE_OPTIONS.find(
+ item => String(item.value) === String(type)
+ );
+ return found?.name || type || "-";
+}
+
+export function getFieldOptionSourceLabel(source) {
+ const key = getFieldOptionSource(source);
+ const found = FIELD_OPTION_SOURCE_OPTIONS.find(item => item.value === key);
+ return found?.name || "鎵嬪姩閰嶇疆";
+}
+
+/** 瑙f瀽閫夐」鏉ユ簮锛歮anual | user | dept */
+export function getFieldOptionSource(fieldOrSource) {
+ const raw =
+ typeof fieldOrSource === "object"
+ ? fieldOrSource?.optionSource
+ : fieldOrSource;
+ const key = String(raw ?? "manual")
+ .trim()
+ .toLowerCase();
+ return OPTION_SOURCE_ALIASES[key] || "manual";
+}
+
+export function isDatetimerangeField(field) {
+ return String(field?.type ?? "").toLowerCase() === "datetimerange";
+}
+
+/** 瑙f瀽鏃ユ湡鏃堕棿鑼冨洿榛樿鍊硷細start,end */
+export function parseDatetimerangeValue(stored) {
+ if (stored === undefined || stored === null || stored === "") {
+ return { start: "", end: "" };
+ }
+ const parts = String(stored)
+ .split(",")
+ .map(s => s.trim());
+ return { start: parts[0] || "", end: parts[1] || "" };
+}
+
+export function joinDatetimerangeValue(start, end) {
+ const s = String(start ?? "").trim();
+ const e = String(end ?? "").trim();
+ if (!s && !e) return "";
+ return `${s},${e}`;
+}
+
+export function formatDatetimerangeDisplay(stored) {
+ const { start, end } = parseDatetimerangeValue(stored);
+ if (!start && !end) return "";
+ if (start && end) return `${start} 鑷� ${end}`;
+ return start || end;
+}
+
+/**
+ * 瑙f瀽涓嬫媺閫夐」锛堝惈浜哄憳/閮ㄩ棬鍔ㄦ�佹潵婧愶級
+ * @param {object} field
+ * @param {{ users?: array, depts?: array }} context
+ */
+export function resolveFieldOptions(field, context = {}) {
+ const source = getFieldOptionSource(field);
+ if (source === "user") {
+ return (context.users || []).map(user => ({
+ label: user.nickName || user.userName || String(user.userId ?? ""),
+ value: user.userId,
+ }));
+ }
+ if (source === "dept") {
+ return (context.depts || []).map(dept => ({
+ label: dept.deptName || dept.name || String(dept.deptId ?? dept.id ?? ""),
+ value: dept.deptId ?? dept.id,
+ }));
+ }
+ return normalizeFieldOptions(field);
+}
+
+export function createEmptyFieldOption() {
+ return { label: "", value: "" };
+}
+
+/** 灏嗙紪杈戣崏绋胯鑼冧负鍙彁浜ょ殑瀛楁瀵硅薄 */
+export function buildFieldConfigPayload(draft, existingKey) {
+ const payload = {
+ key: (draft.key || existingKey || "").trim(),
+ label: (draft.label || "").trim(),
+ type: draft.type || "text",
+ required: !!draft.required,
+ defaultValue: String(draft.defaultValue ?? "").trim(),
+ };
+ if (isSelectField(payload)) {
+ payload.optionSource = getFieldOptionSource(draft.optionSource);
+ if (payload.optionSource === "manual") {
+ payload.options = (draft.options || [])
+ .map(opt => ({
+ label: String(opt?.label ?? "").trim(),
+ value: String(opt?.value ?? "").trim(),
+ }))
+ .filter(opt => opt.label && opt.value);
+ } else {
+ delete payload.options;
+ }
+ }
+ return payload;
+}
--
Gitblit v1.9.3