import type { QualityComponentDefinition } from '../core/types';
|
|
import { QUALITY_REPEAT_ATTR, QUALITY_REPEAT_ROW_ATTR } from '../core/attrs';
|
import { cssStyle, escapeHtml } from '../core/html';
|
import { QUALITY_ICONS } from '../core/icons';
|
import { QUALITY_CATEGORY } from '../core/types';
|
|
/** 检验项数组在报告上下文中的默认路径 */
|
const DEFAULT_ITEMS_PATH = 'inspectionItems';
|
|
/** 循环变量名,行内占位符写作 {{item.xxx}} */
|
const LOOP_VAR = 'item';
|
|
/** 渲染期由重复容器注入的行号变量,写作 {{index}},从 1 开始 */
|
const INDEX_VAR = 'index';
|
|
const CELL_BORDER = '1px solid #d9d9d9';
|
|
interface ColumnSpec {
|
/** 表头文字 */
|
label: string;
|
/** 行内取值表达式 */
|
expr: string;
|
/** 对应的显示开关属性名 */
|
toggle: string;
|
width?: string;
|
}
|
|
const COLUMNS: ColumnSpec[] = [
|
{ label: '序号', expr: `{{${INDEX_VAR}}}`, toggle: 'showIndex', width: '48px' },
|
{ label: '检验项目', expr: `{{${LOOP_VAR}.itemName}}`, toggle: '' },
|
{ label: '标准值', expr: `{{${LOOP_VAR}.standardValue}}`, toggle: 'showStandard' },
|
{ label: '实测值', expr: `{{${LOOP_VAR}.actualValue}}`, toggle: 'showActual', width: '40%' },
|
{ label: '单位', expr: `{{${LOOP_VAR}.unit}}`, toggle: 'showUnit', width: '60px' },
|
{ label: '判定', expr: `{{${LOOP_VAR}.resultText}}`, toggle: 'showResult', width: '72px' },
|
];
|
|
export const qualityTableDefinition: QualityComponentDefinition = {
|
type: 'QualityTable',
|
name: 'qualityTable',
|
label: '检验项目表格',
|
category: QUALITY_CATEGORY.INSPECTION,
|
icon: QUALITY_ICONS.qualityTable,
|
aiHint:
|
'多行检验数据的表格,检验项平铺一层、没有父子关系(序号/检验项目/标准值/实测值/单位/判定)。' +
|
'一个检验项目下挂若干子项(原件里表现为该列纵向合并、后续行写着「↑同上」)时不要用我,' +
|
'改用分组检验项表。' +
|
'报告末尾的落款签署行(检验员/审核人/日期)不要放进我这里,那属于 ReportFooter。',
|
defaults: {
|
title: '检验项目',
|
itemsPath: DEFAULT_ITEMS_PATH,
|
showIndex: true,
|
showStandard: true,
|
showActual: true,
|
showUnit: true,
|
showResult: true,
|
},
|
propertySchema: [
|
{ key: 'title', label: '表格标题', type: 'string', defaultValue: '检验项目' },
|
{
|
key: 'itemsPath',
|
label: '检验项数据源',
|
type: 'string',
|
defaultValue: DEFAULT_ITEMS_PATH,
|
required: true,
|
bindable: true,
|
// 渲染期按这个路径取数组逐行生成,必须是裸路径,不能写成 {{...}}
|
bindAs: 'path',
|
tip: '报告上下文中检验项目数组的路径,渲染时按它逐行生成',
|
},
|
{ key: 'showIndex', label: '显示序号', type: 'boolean', defaultValue: true },
|
{ key: 'showStandard', label: '显示标准值', type: 'boolean', defaultValue: true },
|
{ key: 'showActual', label: '显示实测值', type: 'boolean', defaultValue: true },
|
{ key: 'showUnit', label: '显示单位', type: 'boolean', defaultValue: true },
|
{ key: 'showResult', label: '显示判定', type: 'boolean', defaultValue: true },
|
],
|
dataSchema: [
|
{ key: DEFAULT_ITEMS_PATH, label: '检验项目列表', type: 'string', bindable: true },
|
{ key: INDEX_VAR, label: '行序号', type: 'number', bindable: true },
|
{ key: `${LOOP_VAR}.itemName`, label: '检验项目名称', type: 'string', bindable: true },
|
{ key: `${LOOP_VAR}.standardValue`, label: '标准值', type: 'string', bindable: true },
|
{ key: `${LOOP_VAR}.actualValue`, label: '实测值', type: 'string', bindable: true },
|
{ key: `${LOOP_VAR}.unit`, label: '单位', type: 'string', bindable: true },
|
{ key: `${LOOP_VAR}.resultText`, label: '判定结果', type: 'string', bindable: true },
|
],
|
buildContent: (properties) => {
|
const visible = COLUMNS.filter(
|
(column) => column.toggle === '' || properties[column.toggle] !== false,
|
);
|
const head = visible
|
.map(
|
(column) =>
|
`<th style="${cssStyle({
|
border: CELL_BORDER,
|
padding: '6px 8px',
|
background: '#fafafa',
|
'font-weight': 600,
|
width: column.width,
|
})}">${column.label}</th>`,
|
)
|
.join('');
|
const row = visible
|
.map(
|
(column) =>
|
`<td style="${cssStyle({ border: CELL_BORDER, padding: '6px 8px' })}">${column.expr}</td>`,
|
)
|
.join('');
|
const rawTitle = String(properties.title ?? '').trim();
|
const heading = rawTitle
|
? `<div style="${cssStyle({ 'font-size': '13px', 'font-weight': 600, margin: '0 0 4px' })}">${escapeHtml(rawTitle)}</div>`
|
: '';
|
const itemsPath = String(properties.itemsPath ?? DEFAULT_ITEMS_PATH).trim() || DEFAULT_ITEMS_PATH;
|
const table =
|
`<table style="${cssStyle({ width: '100%', 'border-collapse': 'collapse', 'font-size': '12px' })}">` +
|
`<thead><tr>${head}</tr></thead>` +
|
// 渲染期按 data-qc-repeat 指向的数组复制 data-qc-repeat-row 所在的行
|
`<tbody ${QUALITY_REPEAT_ATTR}="${escapeHtml(itemsPath)}">` +
|
`<tr ${QUALITY_REPEAT_ROW_ATTR}="${LOOP_VAR}">${row}</tr>` +
|
`</tbody></table>`;
|
return {
|
tagName: 'div',
|
components: heading + table,
|
style: { margin: '0 0 10px' },
|
};
|
},
|
validate: (properties) => {
|
const path = String(properties.itemsPath ?? '').trim();
|
const errors: string[] = [];
|
if (!path) {
|
errors.push('检验项目表格的「检验项数据源」不能为空,渲染时无法确定生成哪些行');
|
}
|
if (properties.showStandard === false && properties.showResult === true) {
|
errors.push(
|
'检验项目表格隐藏了「标准值」却保留「判定」列,判定依据将不可见,请一并隐藏判定或恢复标准值',
|
);
|
}
|
return errors;
|
},
|
};
|