/** 填报项类型(与审批提交页 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: "",
|
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),
|
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") {
|
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 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") {
|
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,
|
options: rest.options,
|
}));
|
return {
|
label: row?.templateName || "审批",
|
approvalType: cfg.approvalType || "",
|
summaryPlaceholder: cfg.summaryPlaceholder || "",
|
approvalMode: cfg.approvalMode || "parallel",
|
fields,
|
};
|
}
|
|
export function formConfigFieldsSummary(formConfigData) {
|
const fields = formConfigData?.fields || [];
|
if (!fields.length) return "—";
|
return fields.map((f) => f.label || f.key || "未命名").join("、");
|
}
|