diff --git a/src/views/settings/workflow/component/modelParamUtils.ts b/src/views/settings/workflow/component/modelParamUtils.ts index 28cf8f0..4b944e6 100644 --- a/src/views/settings/workflow/component/modelParamUtils.ts +++ b/src/views/settings/workflow/component/modelParamUtils.ts @@ -193,6 +193,35 @@ export interface ExposedField { refNodeId?: string; // 预留:引用其他节点功能(后续启用) } +// 将模型模板字段级约束(constraint,JsonEditor 风格:min/max/uploadRules/accept/maxSize/maxCount 等) +// 归一化为首页运行表单消费的 fieldConstraint(minValue/maxValue/fileTypes/maxFileSize/maxFileCount)。 +// 模板字段约束实际挂在 constraint 键下(如 video_url.url 的 uploadRules、duration 的 min/numberType), +// 与 collectExposedFields 原读取的 fieldConstraint 键不一致,需在此转换后随 modelFormFields 透传首页。 +function toFieldConstraint(constraint: any): any { + if (!constraint || typeof constraint !== 'object') return undefined; + const out: Record = {}; + // 数字 min/max → 首页 el-input-number 的 minValue/maxValue + if (constraint.min !== undefined && constraint.min !== null) out.minValue = constraint.min; + if (constraint.max !== undefined && constraint.max !== null) out.maxValue = constraint.max; + // 上传约束:uploadRules 数组取第一条;兼容顶层 accept/maxSize/maxCount 单值形式 + const rules = Array.isArray(constraint.uploadRules) ? constraint.uploadRules.filter((r: any) => r && typeof r === 'object') : []; + const rule = rules[0] || null; + const fileTypes = rule?.format ?? constraint.accept; + if (fileTypes !== undefined && fileTypes !== null && fileTypes !== '') { + // 模板 format 可能用中文顿号/空白分隔(如 wan 模型 "wav、mp3"), + // 统一转英文逗号,保证首页 split(',') 与扩展名匹配正确 + out.fileTypes = String(fileTypes) + .replace(/[、\s]+/g, ',') + .replace(/,+/g, ',') + .replace(/^,|,$/g, ''); + } + const maxSize = rule?.maxSize ?? constraint.maxSize; + if (maxSize !== undefined && maxSize !== null) out.maxFileSize = maxSize; + const maxCount = rule?.maxCount ?? constraint.maxCount; + if (maxCount !== undefined && maxCount !== null) out.maxFileCount = maxCount; + return Object.keys(out).length > 0 ? out : undefined; +} + // 收集 runtimeShow === true 的叶子字段,生成暴露清单 // 路径文法(与 restoreRuntimeShow 共用): // object 子字段 → 父路径 + ".attrs." + 子key @@ -234,7 +263,10 @@ export function collectExposedFields(params: any, prefix = ''): ExposedField[] { options: Array.isArray(def.options) ? def.options : undefined, value: def.value, defaultValue: def.defaultValue, - fieldConstraint: def.fieldConstraint && typeof def.fieldConstraint === 'object' ? def.fieldConstraint : undefined, + // 优先透传已有的 fieldConstraint(首页格式);模板字段约束在 constraint 键时归一化补全 + fieldConstraint: + (def.fieldConstraint && typeof def.fieldConstraint === 'object' ? def.fieldConstraint : undefined) ?? + toFieldConstraint(def.constraint), valueSource: (() => { const vs = def.valueSource; // 契约统一数组;兼容旧 DSL 单对象 { nodeId, field }