/**
|
* 质量组件注册表 → AI 积木清单
|
* <p>
|
* 清单是注册表的**派生视图**:剥掉 icon / buildContent / validate 这些不可序列化的成员,
|
* 只留下模型需要知道的「有哪些组件、每个组件能填哪些字段」。
|
* <p>
|
* 这份清单随请求传给后端拼进提示词,但它<b>不是</b>合法性的判据:真正把关的是装配器
|
* 对着活注册表查 getQualityComponent。模型即便编出清单外的 type,也只会被跳过,
|
* 永远变不成任意 HTML(清单里不含任何 HTML 能力的描述)。
|
*/
|
import type { QualityFieldSchema } from './types';
|
|
import { listQualityComponents } from './registry';
|
|
/** 清单里的一个可填字段,与后端 QcReportComponentSpecVO.Field 一一对应 */
|
export interface QualityCatalogField {
|
key: string;
|
label: string;
|
type: QualityFieldSchema['type'];
|
required?: boolean;
|
bindable?: boolean;
|
/**
|
* 枚举候选项。
|
* <p>
|
* 用 {label, value} 成对给出:只给 label 模型会照着输出中文标签,
|
* 只给 value 又不知道含义。成对给出,它才知道该输出哪个字面量。
|
*/
|
enumOptions?: { label: string; value: number | string }[];
|
}
|
|
/** 一个组件积木 */
|
export interface QualityComponentCatalog {
|
type: string;
|
label: string;
|
/** 组件分类的中文显示名,帮模型理解组件用途 */
|
category: string;
|
/**
|
* 组件的选型说明,来自注册表的 aiHint。
|
* <p>
|
* 只有 label 时,模型面对「报告抬头」这类内容会在 Heading / Text / ReportHeader 之间瞎猜,
|
* 且总会挑字段最简单的基础组件。这段文字就是给它划边界的,不是给人看的。
|
*/
|
hint?: string;
|
fields: QualityCatalogField[];
|
}
|
|
/** 从活注册表生成积木清单,顺序与设计器左侧面板一致 */
|
export function buildQualityComponentCatalog(): QualityComponentCatalog[] {
|
const definitions = listQualityComponents();
|
// 注册表要显式调用 registerAllQualityComponents() 才有内容(设计器在 init 时做)。
|
// 空清单会被后端判为入参非法,报出来的错和真实原因隔了好几层,所以在这里就说清楚。
|
if (definitions.length === 0) {
|
throw new Error('质量组件注册表为空,请先调用 registerAllQualityComponents()');
|
}
|
return definitions.map((definition) => {
|
const item: QualityComponentCatalog = {
|
type: definition.type,
|
label: definition.label,
|
category: definition.category,
|
fields: definition.propertySchema.map(toCatalogField),
|
};
|
// 缺席即省略,与 toCatalogField 同一风格:省掉的字段能明显缩小提示词
|
if (definition.aiHint) {
|
item.hint = definition.aiHint;
|
}
|
return item;
|
});
|
}
|
|
function toCatalogField(field: QualityFieldSchema): QualityCatalogField {
|
const item: QualityCatalogField = {
|
key: field.key,
|
label: field.label,
|
type: field.type,
|
};
|
// 缺席即「否」,省掉的字段能明显缩小提示词
|
if (field.required) {
|
item.required = true;
|
}
|
if (field.bindable) {
|
item.bindable = true;
|
}
|
if (field.options?.length) {
|
item.enumOptions = field.options.map((option) => ({
|
label: option.label,
|
value: option.value,
|
}));
|
}
|
return item;
|
}
|