| | |
| | | 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: "datetime", label: "日期时间" }, |
| | | { value: "datetimerange", label: "日期时间范围" }, |
| | | { value: "select", label: "下拉选择" }, |
| | | ]; |
| | |
| | | { key: "dateRange", label: "请假时间", type: "datetimerange", required: true }, |
| | | ], |
| | | }, |
| | | { |
| | | key: "vehicle", |
| | | label: "车辆审批", |
| | | summaryPlaceholder: "请填写车辆使用申请信息", |
| | | fields: [ |
| | | { key: "vehicleNo", label: "车牌号", type: "select", required: true, optionSource: "vehicle_list", placeholder: "请选择车辆" }, |
| | | { key: "driver", label: "驾驶员", type: "text", required: true }, |
| | | { key: "purpose", label: "用车事由", type: "textarea", required: true, rows: 2 }, |
| | | { key: "useDateRange", label: "车辆使用时间", type: "datetimerange", required: true }, |
| | | { key: "destination", label: "目的地", type: "text", required: true }, |
| | | { key: "passengers", label: "乘车人数", type: "number", required: false, min: 1, precision: 0 }, |
| | | { key: "startMileage", label: "起始公里数", type: "number", required: true, min: 0, precision: 1 }, |
| | | { key: "startMileagePhoto", label: "起始公里数照片", type: "image", required: false }, |
| | | { key: "estimatedEndMileage", label: "预计结束公里数", type: "number", required: false, min: 0, precision: 1 }, |
| | | { key: "remark", label: "备注", type: "textarea", required: false, rows: 2 }, |
| | | ], |
| | | }, |
| | | { |
| | | key: "vehicle_return", |
| | | label: "车辆还车审批", |
| | | summaryPlaceholder: "请填写车辆还车信息", |
| | | fields: [ |
| | | { key: "vehicleNo", label: "车牌号", type: "select", required: true, optionSource: "vehicle_list", placeholder: "请选择车辆" }, |
| | | { key: "driver", label: "驾驶员", type: "text", required: true }, |
| | | { key: "originalApprovalNo", label: "原审批单号", type: "text", required: true }, |
| | | { key: "returnDate", label: "还车日期", type: "date", required: true }, |
| | | { key: "endMileage", label: "结束公里数", type: "number", required: true, min: 0, precision: 1 }, |
| | | { key: "endMileagePhoto", label: "结束公里数照片", type: "image", required: false }, |
| | | { key: "actualMileage", label: "实际行驶公里数", type: "number", required: false, min: 0, precision: 1 }, |
| | | { key: "extendDays", label: "延期天数", type: "number", required: false, min: 0, precision: 0 }, |
| | | { key: "extendReason", label: "延期原因", type: "textarea", required: false, rows: 2 }, |
| | | { key: "vehicleStatus", label: "车辆状态", type: "select", required: true, options: [{ label: "完好", value: "good" }, { label: "轻微损坏", value: "minor_damage" }, { label: "需要维修", value: "need_repair" }] }, |
| | | { key: "remark", label: "备注", type: "textarea", required: false, rows: 2 }, |
| | | ], |
| | | }, |
| | | { |
| | | key: "vehicle_extend", |
| | | label: "车辆延期审批", |
| | | summaryPlaceholder: "车辆到期后申请延期使用", |
| | | fields: [ |
| | | { key: "vehicleNo", label: "车牌号", type: "select", required: true, optionSource: "vehicle_list", placeholder: "请选择车辆" }, |
| | | { key: "driver", label: "驾驶员", type: "text", required: true }, |
| | | { key: "originalApprovalNo", label: "原审批单号", type: "text", required: true }, |
| | | { key: "originalEndDate", label: "原到期日期", type: "date", required: true }, |
| | | { key: "extendDays", label: "延期天数", type: "number", required: true, min: 1, precision: 0 }, |
| | | { key: "newEndDate", label: "新到期日期", type: "date", required: true }, |
| | | { key: "extendReason", label: "延期原因", type: "textarea", required: true, rows: 3 }, |
| | | { key: "remark", label: "备注", type: "textarea", required: false, rows: 2 }, |
| | | ], |
| | | }, |
| | | ]; |
| | | |
| | | function newFieldUid() { |
| | |
| | | min: 0, |
| | | precision: 0, |
| | | defaultValue: "", |
| | | optionSource: SELECT_OPTION_SOURCE.STATIC, |
| | | options: [{ label: "", value: "" }], |
| | | }; |
| | | } |
| | |
| | | if (dv === undefined || dv === null || dv === "") { |
| | | if (type === "number") return undefined; |
| | | if (type === "datetimerange") return []; |
| | | if (type === "datetime") return ""; |
| | | return ""; |
| | | } |
| | | if (type === "number") { |
| | |
| | | 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 === "datetime") return String(dv).trim() !== ""; |
| | | if (type === "select") return dv !== ""; |
| | | return String(dv).trim() !== ""; |
| | | } |
| | |
| | | if (f.defaultValue === undefined || f.defaultValue === null) { |
| | | if (type === "number") return undefined; |
| | | if (type === "datetimerange") return []; |
| | | if (type === "datetime") return ""; |
| | | return ""; |
| | | } |
| | | if (type === "datetimerange" && Array.isArray(f.defaultValue)) { |
| | |
| | | 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: "" }], |
| | |
| | | item.precision = f.precision ?? 0; |
| | | } |
| | | if (item.type === "select") { |
| | | item.options = (f.options || []) |
| | | .filter((o) => (o.label || "").trim() || o.value !== "" && o.value != null) |
| | | .map((o) => ({ label: (o.label || "").trim(), value: o.value })); |
| | | 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 = |
| | |
| | | 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 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 默认值) */ |
| | | /** 将后端模板行转为提交页模板结构(含 fields 默认值、附件) */ |
| | | export function buildSubmitTemplateFromRow(row) { |
| | | const cfg = parseFormConfigToData(row?.formConfig); |
| | | const fields = (cfg.fields || []).map(({ _uid, ...rest }) => ({ |
| | |
| | | min: rest.min, |
| | | precision: rest.precision, |
| | | defaultValue: rest.defaultValue, |
| | | optionSource: rest.optionSource, |
| | | options: rest.options, |
| | | })); |
| | | return { |
| | |
| | | summaryPlaceholder: cfg.summaryPlaceholder || "", |
| | | approvalMode: cfg.approvalMode || "parallel", |
| | | fields, |
| | | storageBlobDTOs: mapAttachmentsFromApi(row), |
| | | }; |
| | | } |
| | | |