23 小时以前 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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/**
 * 报告上下文(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,
  };
}