/**
|
* 质量组件属性校验与默认值收敛
|
* <p>
|
* 只在保存 / 发布前调用;设计过程中的中间态(比如刚拖到画布、还没填值)不触发校验。
|
*/
|
import type {
|
QualityComponentDefinition,
|
QualityFieldSchema,
|
QualityProps,
|
} from './types';
|
|
/** 把任意来源的原始值收敛成字段声明的类型 */
|
export function coerceFieldValue(
|
field: QualityFieldSchema,
|
raw: boolean | number | string,
|
): boolean | number | string {
|
switch (field.type) {
|
case 'boolean': {
|
if (typeof raw === 'boolean') {
|
return raw;
|
}
|
return raw === 'true' || raw === '1';
|
}
|
case 'number': {
|
const parsed = Number(raw);
|
if (Number.isFinite(parsed)) {
|
return parsed;
|
}
|
return typeof field.defaultValue === 'number' ? field.defaultValue : 0;
|
}
|
default: {
|
return String(raw);
|
}
|
}
|
}
|
|
/** 以 defaults 打底,用传入值覆盖其中的合法项,得到完整可用的属性集 */
|
export function resolveQualityProps(
|
definition: QualityComponentDefinition,
|
props?: QualityProps,
|
): QualityProps {
|
const resolved: QualityProps = { ...definition.defaults };
|
if (!props) {
|
return resolved;
|
}
|
definition.propertySchema.forEach((field) => {
|
const raw = props[field.key];
|
if (raw === undefined) {
|
return;
|
}
|
resolved[field.key] = coerceFieldValue(field, raw);
|
});
|
return resolved;
|
}
|
|
/**
|
* 校验业务属性,返回错误信息数组;空数组表示通过。
|
* <p>
|
* 提示要能让用户直接定位,所以带上字段显示名与实际值。
|
*/
|
export function validateQualityProps(
|
definition: QualityComponentDefinition,
|
props?: QualityProps,
|
): string[] {
|
const errors: string[] = [];
|
const resolved = resolveQualityProps(definition, props);
|
definition.propertySchema.forEach((field) => {
|
const value = resolved[field.key];
|
const isEmpty = value === undefined || value === '';
|
if (isEmpty) {
|
if (field.required) {
|
errors.push(`${definition.label}的「${field.label}」不能为空`);
|
}
|
return;
|
}
|
if (field.type === 'enum' && field.options) {
|
const matched = field.options.some((option) => String(option.value) === String(value));
|
if (!matched) {
|
errors.push(
|
`${definition.label}的「${field.label}」值「${value}」不在可选项内,请重新选择`,
|
);
|
}
|
return;
|
}
|
if (field.type === 'number' && typeof value === 'number' && !Number.isFinite(value)) {
|
errors.push(`${definition.label}的「${field.label}」必须是数字,当前为「${value}」`);
|
}
|
});
|
// 逐个字段的规则上面已经查完,剩下的是「字段都在、但组合起来不成立」的规则
|
// (列定义串的语法、表头跨列数合计等),只有组件自己知道,必须在这里接上。
|
// 不接的话 definition.validate 就是个没人调用的死方法,写了也不会拦住任何人。
|
errors.push(...(definition.validate?.(resolved) ?? []));
|
return errors;
|
}
|