feat(view): ③ screen follows the 시안 — table columns, basis badges, alert buttons

- 철근표 columns as in the 시안: 구분 · 번호 · 규격 · 간격 · 실간격 · 위치 · 방향 ·
  형상 · 길이 · 개수 · 근거 · 비고. Metadata per bar from the rebar documents.
- 근거 badges: 입력 / 기본값 / 계산 / 가정 (the 가정 badge is italic amber).
- 번호 click highlights that row.
- 알림: [보기] opens the member tab; I02 [입력] focuses the fck input.
- Member tabs show the warning count.
- 공통 설정 moved to the right column (알림 → 검토 필요 → 공통 설정), as in the 시안.
- The 시안's "fck를 입력해야 생성" blocker is not used: the defaults are assumed
  values (I02), as decided on 2026-10-22.
- Not in this step: the drawings and the 정면·단면 연동 (시안 도면 뷰어).
- Not verified in a browser.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
ypshimandClaude Haiku 5.5 committed 2026-10-22 12:08:21 +09:00
1 parent 9ba7b55eab
commit cdc0e5ac9c
4 files changed
+138 -16

No files matched your search

+55 -4
View File
@@ -727,29 +727,80 @@ function renderRebar(): void {
const table = el('table', undefined, 'grid-table rebar-table');
const head = el('thead');
const hr = el('tr');
for (const h of ['번호', '규격', '개수', '길이 (mm)', '비고']) hr.appendChild(el('th', h));
for (const h of ['구분', '번호', '규격', '간격', '실간격', '위치', '방향', '형상', '길이 (mm)', '개수', '근거', '비고']) hr.appendChild(el('th', h));
head.appendChild(hr);
table.appendChild(head);
const body = el('tbody');
for (const r of shown) {
const tr = el('tr');
const nameCell = el('td', r.name);
nameCell.classList.add('bar-no');
// Clicking a 번호 highlights that row (시안: 번호를 누르면 그 철근만 강조).
nameCell.addEventListener('click', () => tr.classList.toggle('hl'));
tr.append(
el('td', r.name),
el('td', ROLE[r.name] ?? '—'),
nameCell,
el('td', r.size),
el('td', String(r.count)),
el('td', r.spacing),
el('td', r.actual),
el('td', r.position),
el('td', r.direction),
el('td', r.shape),
el('td', r.length === null ? '—' : String(Math.round(r.length * 10) / 10)),
el('td', String(r.count)),
basisCell(r.basis),
el('td', r.note),
);
body.appendChild(tr);
}
table.appendChild(body);
box.appendChild(table);
for (const w of warnings) issues.appendChild(el('li', `${w.code} ${w.message}`));
// Alerts: [보기] goes to the member, [입력] goes to the 공통 설정 input (시안 ③ 알림).
for (const w of warnings) {
const li = el('li', `${w.code} ${w.message} `);
const btn = el('button', w.target === 'common' ? '입력' : '보기', 'btn small');
btn.type = 'button';
btn.addEventListener('click', () => {
if (w.target === 'common') {
$<HTMLInputElement>('mat-fck').focus();
$<HTMLInputElement>('mat-fck').scrollIntoView({ block: 'center' });
} else {
state.rebarMember = w.target;
renderAll();
}
});
li.appendChild(btn);
issues.appendChild(li);
}
if (issues.childElementCount === 0) issues.appendChild(el('li', '알림이 없습니다.'));
// Warning count on each member tab (시안: 부재 탭 배지).
const counts = (member: RebarRow['member']) => warnings.filter((w) => w.target === member).length;
for (const [i, m] of REBAR_MEMBERS.entries()) {
const badge = tabs.children[i]?.querySelector('button');
const n = counts(m.id);
if (badge && n > 0) badge.appendChild(el('span', String(n), 'badge warn'));
}
$('step3-status').textContent = `계산됨 · 경고 ${warnings.length}`;
renderMaterial();
}
/** 구분 (role) of each bar, as in the 시안 ③ table. */
const ROLE: Record<string, string> = {
S1: '받침블럭', S2: '받침블럭',
C1: '주철근', C2: '주철근', C3: '주철근', C4: '주철근', C5: '주철근', C6: '주철근', C7: '주철근', C8: '주철근',
C9: '전단·띠', C10: '전단·띠', C11: '전단·띠', C12: '측면 수평', C13: '측면 수평', C14: '직각 수평', C15: '계단부', C16: '계단부',
P1: '주철근', P2: '주철근', 커플러: '조립', P3: '띠철근', P5: '띠철근', P4: '띠철근', P6: '크로스타이',
B1: '주철근', B2: '주철근', B3: '주철근', B4: '주철근', B5: '주철근', B6: '주철근', B7: '주철근', B8: '주철근',
'B10-1': '측면 수평', 'B10-2': '측면 수평', B11: '조립 수평', B12: '조립 수평', B9: '전단·띠',
};
/** 근거 badge (시안 ③): 입력 / 기본값 / 계산 / 가정. */
function basisCell(b: RebarRow['basis']): HTMLElement {
const label = { input: '입력', default: '기본값', calc: '계산', assumed: '가정' }[b];
return el('td', label, `basis basis-${b}`);
}
/** ③ 공통 설정 inputs. Changes apply on 'change' and regenerate the rebar screen. */
function renderMaterial(): void {
const m = state.material;