feat: [검사문서] TemplateInspectionContent 절곡(bending) save/restore 지원
- documentRecords prop 추가 (document_data EAV 레코드 복원용)
- getInspectionData()에 bending 분기 추가: 구성품별 field_key 인코딩
(b{idx}_ok/ng, b{idx}_p{pt}_n1, b{idx}_n{n}, b{idx}_judgment, b{idx}_value)
- 비-bending 모드 기존 로직 guard 추가 (if !isBending)
- useEffect로 documentRecords에서 bending cellValues 복원 로직 구현
This commit is contained in:
@@ -54,6 +54,14 @@ interface TemplateInspectionContentProps {
|
||||
readOnly?: boolean;
|
||||
workItems?: WorkItemData[];
|
||||
inspectionDataMap?: InspectionDataMap;
|
||||
/** 기존 document_data EAV 레코드 (문서 로딩 시 복원용) */
|
||||
documentRecords?: Array<{
|
||||
section_id: number | null;
|
||||
column_id: number | null;
|
||||
row_index: number;
|
||||
field_key: string;
|
||||
field_value: string | null;
|
||||
}>;
|
||||
}
|
||||
|
||||
// ===== 유틸 =====
|
||||
@@ -301,7 +309,7 @@ function SectionImage({ section }: { section: { id: number; title?: string; name
|
||||
// ===== 컴포넌트 =====
|
||||
|
||||
export const TemplateInspectionContent = forwardRef<InspectionContentRef, TemplateInspectionContentProps>(
|
||||
function TemplateInspectionContent({ data: order, template, readOnly = false, workItems, inspectionDataMap }, ref) {
|
||||
function TemplateInspectionContent({ data: order, template, readOnly = false, workItems, inspectionDataMap, documentRecords }, ref) {
|
||||
const fullDate = getFullDate();
|
||||
const { primaryAssignee } = getOrderInfo(order);
|
||||
|
||||
@@ -486,6 +494,84 @@ export const TemplateInspectionContent = forwardRef<InspectionContentRef, Templa
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [inspectionDataMap, workItems]);
|
||||
|
||||
// ===== Bending: document_data EAV 레코드에서 복원 =====
|
||||
useEffect(() => {
|
||||
if (!isBending || !documentRecords || documentRecords.length === 0 || bendingProducts.length === 0) return;
|
||||
|
||||
const initial: Record<string, CellValue> = {};
|
||||
|
||||
// field_key 패턴: b{productIdx}_ok, b{productIdx}_ng, b{productIdx}_p{pointIdx}_n1, b{productIdx}_n{n}, b{productIdx}_judgment
|
||||
for (const rec of documentRecords) {
|
||||
const fk = rec.field_key;
|
||||
if (!fk.startsWith('b')) continue;
|
||||
const val = rec.field_value;
|
||||
if (val == null) continue;
|
||||
|
||||
// b{productIdx}_ok / b{productIdx}_ng → check status
|
||||
const checkMatch = fk.match(/^b(\d+)_(ok|ng)$/);
|
||||
if (checkMatch && rec.column_id) {
|
||||
const productIdx = parseInt(checkMatch[1], 10);
|
||||
const cellKey = `b-${productIdx}-${rec.column_id}`;
|
||||
if (checkMatch[2] === 'ok' && val === 'OK') {
|
||||
initial[cellKey] = { ...initial[cellKey], status: 'good' };
|
||||
} else if (checkMatch[2] === 'ng' && val === 'NG') {
|
||||
initial[cellKey] = { ...initial[cellKey], status: 'bad' };
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// b{productIdx}_p{pointIdx}_n1 → gap measurement
|
||||
const gapMatch = fk.match(/^b(\d+)_p(\d+)_n(\d+)$/);
|
||||
if (gapMatch && rec.column_id) {
|
||||
const productIdx = parseInt(gapMatch[1], 10);
|
||||
const pointIdx = parseInt(gapMatch[2], 10);
|
||||
const cellKey = `b-${productIdx}-p${pointIdx}-${rec.column_id}`;
|
||||
initial[cellKey] = { measurements: [val, '', ''] };
|
||||
continue;
|
||||
}
|
||||
|
||||
// b{productIdx}_n{n} → complex measurement (길이/너비)
|
||||
const complexMatch = fk.match(/^b(\d+)_n(\d+)$/);
|
||||
if (complexMatch && rec.column_id) {
|
||||
const productIdx = parseInt(complexMatch[1], 10);
|
||||
const mIdx = parseInt(complexMatch[2], 10) - 1;
|
||||
const cellKey = `b-${productIdx}-${rec.column_id}`;
|
||||
const prev = initial[cellKey]?.measurements || ['', '', ''];
|
||||
const m: [string, string, string] = [...prev] as [string, string, string];
|
||||
m[mIdx] = val;
|
||||
initial[cellKey] = { ...initial[cellKey], measurements: m };
|
||||
continue;
|
||||
}
|
||||
|
||||
// b{productIdx}_judgment → skip (자동 계산, 복원 불필요)
|
||||
if (fk.match(/^b\d+_judgment$/)) continue;
|
||||
|
||||
// b{productIdx}_value → fallback value
|
||||
const valMatch = fk.match(/^b(\d+)_value$/);
|
||||
if (valMatch && rec.column_id) {
|
||||
const productIdx = parseInt(valMatch[1], 10);
|
||||
const cellKey = `b-${productIdx}-${rec.column_id}`;
|
||||
initial[cellKey] = { value: val };
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// overall_result, remark 복원
|
||||
for (const rec of documentRecords) {
|
||||
if (rec.field_key === 'overall_result' && rec.field_value) {
|
||||
// overallResult는 자동 계산이므로 별도 처리 불필요
|
||||
}
|
||||
if (rec.field_key === 'remark' && rec.field_value) {
|
||||
setInadequateContent(rec.field_value);
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(initial).length > 0) {
|
||||
setCellValues(prev => ({ ...prev, ...initial }));
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [documentRecords, isBending, bendingProducts]);
|
||||
|
||||
const updateCell = (key: string, update: Partial<CellValue>) => {
|
||||
setCellValues(prev => ({
|
||||
...prev,
|
||||
@@ -563,7 +649,99 @@ export const TemplateInspectionContent = forwardRef<InspectionContentRef, Templa
|
||||
}
|
||||
|
||||
// ===== 2. 행별 검사 데이터 =====
|
||||
effectiveWorkItems.forEach((wi, rowIdx) => {
|
||||
// Bending 모드: 구성품별 데이터 (개소 단위, field_key에 구성품/포인트 인코딩)
|
||||
if (isBending && bendingProducts.length > 0) {
|
||||
bendingProducts.forEach((product, productIdx) => {
|
||||
for (const col of template.columns) {
|
||||
const label = col.label.trim();
|
||||
const isGapCol = col.id === gapColumnId;
|
||||
|
||||
// text 컬럼 (분류/제품명, 타입) → bendingInfo에서 동적 생성이므로 저장 불필요
|
||||
if (col.column_type === 'text') continue;
|
||||
|
||||
// 판정 컬럼 → 자동 계산 결과 저장
|
||||
if (isJudgmentColumn(label)) {
|
||||
const judgment = getBendingProductJudgment(productIdx);
|
||||
if (judgment) {
|
||||
records.push({
|
||||
section_id: null, column_id: col.id, row_index: 0,
|
||||
field_key: `b${productIdx}_judgment`,
|
||||
field_value: judgment === '적' ? 'OK' : 'NG',
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// 간격 컬럼 (per-point 데이터)
|
||||
if (isGapCol) {
|
||||
product.gapPoints.forEach((_gp, pointIdx) => {
|
||||
const cellKey = `b-${productIdx}-p${pointIdx}-${col.id}`;
|
||||
const cell = cellValues[cellKey];
|
||||
if (cell?.measurements?.[0]) {
|
||||
records.push({
|
||||
section_id: null, column_id: col.id, row_index: 0,
|
||||
field_key: `b${productIdx}_p${pointIdx}_n1`,
|
||||
field_value: cell.measurements[0],
|
||||
});
|
||||
}
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
// 비간격 merged 컬럼
|
||||
const cellKey = `b-${productIdx}-${col.id}`;
|
||||
const cell = cellValues[cellKey];
|
||||
|
||||
// check 컬럼 (절곡상태)
|
||||
if (col.column_type === 'check') {
|
||||
records.push({
|
||||
section_id: null, column_id: col.id, row_index: 0,
|
||||
field_key: `b${productIdx}_ok`,
|
||||
field_value: cell?.status === 'good' ? 'OK' : '',
|
||||
});
|
||||
records.push({
|
||||
section_id: null, column_id: col.id, row_index: 0,
|
||||
field_key: `b${productIdx}_ng`,
|
||||
field_value: cell?.status === 'bad' ? 'NG' : '',
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
// complex 컬럼 (길이/너비 측정)
|
||||
if (col.column_type === 'complex' && col.sub_labels) {
|
||||
let inputIdx = 0;
|
||||
for (const sl of col.sub_labels) {
|
||||
const slLower = sl.toLowerCase();
|
||||
if (slLower.includes('도면') || slLower.includes('기준')) continue;
|
||||
if (slLower.includes('point') || slLower.includes('포인트')) continue;
|
||||
const n = inputIdx + 1;
|
||||
const val = cell?.measurements?.[inputIdx] || null;
|
||||
if (val) {
|
||||
records.push({
|
||||
section_id: null, column_id: col.id, row_index: 0,
|
||||
field_key: `b${productIdx}_n${n}`,
|
||||
field_value: val,
|
||||
});
|
||||
}
|
||||
inputIdx++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// fallback
|
||||
if (cell?.value) {
|
||||
records.push({
|
||||
section_id: null, column_id: col.id, row_index: 0,
|
||||
field_key: `b${productIdx}_value`,
|
||||
field_value: cell.value,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 비-Bending 모드: 개소(WorkItem)별 데이터
|
||||
if (!isBending) effectiveWorkItems.forEach((wi, rowIdx) => {
|
||||
for (const col of template.columns) {
|
||||
// 일련번호 컬럼 → 저장 (mng show에서 표시용)
|
||||
if (isSerialColumn(col.label)) {
|
||||
|
||||
Reference in New Issue
Block a user