/**
|
* 报告上下文(Context)
|
* <p>
|
* 模板里的 {{path}} 全部相对这个结构解析:report 是报告级字段,inspectionItems 是检验项数组。
|
* 业务单据 → 上下文的映射由上下文构建器负责,模板本身只认这个标准结构,
|
* 换行业、换单据来源都不需要改模板与组件。
|
*/
|
|
/** 判定结果取值,组件与规则引擎共用 */
|
export const QUALITY_RESULT = {
|
PASS: 'PASS',
|
FAIL: 'FAIL',
|
} as const;
|
|
export type QualityResult = (typeof QUALITY_RESULT)[keyof typeof QUALITY_RESULT];
|
|
/** 判定结果对应的展示文字 */
|
export const QUALITY_RESULT_TEXT: Record<QualityResult, string> = {
|
PASS: '合格',
|
FAIL: '不合格',
|
};
|
|
/** 报告级字段 */
|
export interface ReportContextReport {
|
reportNo: string;
|
reportName: string;
|
sampleNo: string;
|
productCode: string;
|
productName: string;
|
spec: string;
|
batchNo: string;
|
workOrderNo: string;
|
inspectType: string;
|
inspector: string;
|
inspectDate: string;
|
department: string;
|
customerName: string;
|
supplierName: string;
|
/** PASS / FAIL */
|
result: string;
|
/** 合格 / 不合格 / 待判定 */
|
resultText: string;
|
conclusion: string;
|
passRate: string;
|
total: number;
|
passCount: number;
|
failCount: number;
|
/**
|
* 质检单上的原判定(数字字符串 "1"~"4",来自 MES 单据)。
|
* <p>
|
* 与引擎算出的 {@link result} 并存、互不覆盖:单据判定是人工拍的板
|
* (合格 / 特采 / 不合格退货 / 不合格报废,四态),而引擎只有 PASS/FAIL/待判定 三态装不下它。
|
* 仅「由质检单出件」时有值,通用出件的上下文里恒为空串。
|
*/
|
qcResult: string;
|
/** {@link qcResult} 的中文,如「合格」「特采」「不合格退货」「不合格报废」 */
|
qcResultText: string;
|
}
|
|
/**
|
* 检验项的分组信息。
|
* <p>
|
* 检验单里的父子层级(分组头 + 组内子项)只有两项数据能表达:组名合并几行、
|
* 子项行要不要让出合并格。渲染期无法自己算出这两件事 —— 绑定的路径在数组上继续取属性
|
* 会按元素 pluck,`group.children.length` 取不到值 —— 所以必须由上下文构建器逐项下发。
|
* <p>
|
* 这几项在**每一个**检验项上都必须有值(哪怕是空串):绑定的字段缺席时属性会被跳过,
|
* 合并格反而会露出来。没有分组时整个 {@link ReportContextItemGroup} 可以不给。
|
*/
|
export interface ReportContextItemGroup {
|
/** 「子项」列文字:组内子项 = 自己的名字,组头行与独立项 = 空串 */
|
childName: string;
|
/** 组名格的纵向合并行数:组头行 = 1 + 组内子项数,组内子项行与独立项 = 1 */
|
span: number;
|
/**
|
* 组名格的隐藏值:组内子项行 = 'hidden'(该格让位给上方合并格),组头行与独立项 = 空串。
|
* <p>
|
* 字符串而不是布尔:`hidden` 属性靠「在不在」起作用(`hidden="false"` 照样隐藏),
|
* 只有空串能表达「不输出该属性 = 这一格可见」。
|
*/
|
hidden: string;
|
}
|
|
/** 单个检验项 */
|
export interface ReportContextItem {
|
index: number;
|
itemCode: string;
|
itemName: string;
|
standardValue: string;
|
actualValue: string;
|
unit: string;
|
/**
|
* 「检测要求」文本:分组父项 = 指标上的计算公式,其余 = 单据上的标准要求。
|
* <p>
|
* 与 {@link standardValue} 并存而不合并,是因为两者的来源不同:标准要求是检验单行上的值,
|
* 公式是检验项主数据上的值;分组父子项压根没有规格,只有公式。
|
*/
|
requirement?: string;
|
/**
|
* 「检测方法」文本,来自单据行(如 GB 5009.3-2016)。
|
* <p>
|
* 与 {@link requirement} 并存而不合并:检测要求说的是「这一项要达到什么」,
|
* 检测方法说的是「用什么办法测的」,原件里常各占一列。
|
*/
|
checkMethod?: string;
|
/** 分组信息,仅在检验单存在分组结构时逐项下发 */
|
group?: ReportContextItemGroup;
|
/** 规格上限,规则引擎做区间判定时使用 */
|
upperLimit?: number;
|
/** 规格下限 */
|
lowerLimit?: number;
|
/** PASS / FAIL */
|
result: string;
|
/**
|
* 合格 / 不合格 / 待判定 / 无判定规则。
|
* <p>
|
* 后两者都不是结论:待判定是规则跑挂了(仍计入合格率分母),无判定规则是这项没有判定依据(不计入)。
|
*/
|
resultText: string;
|
remark: string;
|
}
|
|
/** 报告上下文 */
|
export interface ReportContext {
|
report: ReportContextReport;
|
inspectionItems: ReportContextItem[];
|
}
|
|
function emptyReport(): ReportContextReport {
|
return {
|
reportNo: '',
|
reportName: '',
|
sampleNo: '',
|
productCode: '',
|
productName: '',
|
spec: '',
|
batchNo: '',
|
workOrderNo: '',
|
inspectType: '',
|
inspector: '',
|
inspectDate: '',
|
department: '',
|
customerName: '',
|
supplierName: '',
|
result: '',
|
resultText: '',
|
conclusion: '',
|
passRate: '',
|
total: 0,
|
passCount: 0,
|
failCount: 0,
|
qcResult: '',
|
qcResultText: '',
|
};
|
}
|
|
/** 空白上下文,字段齐全但没有值,便于逐项填充 */
|
export function createEmptyContext(): ReportContext {
|
return { report: emptyReport(), inspectionItems: [] };
|
}
|
|
/** 用传入值覆盖空白上下文,未传的字段保留空值 */
|
export function createContext(input?: Partial<ReportContext>): ReportContext {
|
const base = createEmptyContext();
|
return {
|
report: { ...base.report, ...input?.report },
|
inspectionItems: input?.inspectionItems ?? base.inspectionItems,
|
};
|
}
|