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", }; /** 解析 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(); } /** 日期时间字段种类:date | 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 }[] * 支持:options / 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 || "手动配置"; } /** 解析选项来源:manual | 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"; } /** 解析日期时间范围默认值: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; } /** * 解析下拉选项(含人员/部门动态来源) * @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; }