14 小时以前 35722562e9e13f0504acc15b740d042ecb810199
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
/**
 * 质量组件属性校验与默认值收敛
 * <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;
}