feat(quantity): ④ 재료표 범위를 교각 목록의 선택과 맞춤 (전체 또는 고른 교각)
- 교각 목록 맨 위에 "전체 (N기)" 항목 추가 (기본 선택) - 교각 이름을 누르면 그 교각의 재료표만 표시, 제목·합계 문구도 그 교각 기준 - 범위 규칙을 core/quantity-scope.ts로 분리하고 테스트 4건 추가 Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
278d19faa5
commit
7861bc3fdf
4 files changed
+58
-3
No files matched your search
@@ -201,6 +201,7 @@
|
||||
<main class="grid quantity" id="view-quantity" hidden>
|
||||
<aside class="panel piers" aria-labelledby="quantity-pier-title">
|
||||
<p id="quantity-pier-title" class="lbl">교각 목록</p>
|
||||
<button id="quantity-all" class="pier-name" type="button" aria-pressed="true">전체</button>
|
||||
<ul id="quantity-pier-items" class="pier-items"></ul>
|
||||
<p class="pnote" id="quantity-note"></p>
|
||||
</aside>
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/** One pier as the 재료표 scope sees it. */
|
||||
export interface QuantityPier {
|
||||
name: string;
|
||||
hasModel: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* The piers the ④ 재료표 shows: every pier with a model when 전체 is on, otherwise only the chosen pier
|
||||
* (교각 목록에서 고른 교각). A chosen pier without a model shows nothing.
|
||||
*/
|
||||
export function quantityTargetNames(piers: QuantityPier[], all: boolean, active: string | null): string[] {
|
||||
if (all) return piers.filter((p) => p.hasModel).map((p) => p.name);
|
||||
return piers.filter((p) => p.hasModel && p.name === active).map((p) => p.name);
|
||||
}
|
||||
+17
-3
@@ -10,6 +10,7 @@ import { EDITABLE_FIELDS, FIELD_LABELS, overrideKeys, pendingKeys, revertDraft,
|
||||
import { adjustRow, layoutText, resolveRowCounts, rowLayout, totalPiles } from './core/piles';
|
||||
import { applyCopy, COPY_ITEMS, planCopy, type CopyItem } from './core/copy';
|
||||
import { copyCandidates, copyItemsFor, copyItemsWithOverrides, hasCopyItems, selectedTargets, type CopyCandidate } from './core/copy-targets';
|
||||
import { quantityTargetNames } from './core/quantity-scope';
|
||||
import { pileGeometry } from './core/geometry';
|
||||
import { rebarSummary, type RebarRow } from './core/rebar/summary';
|
||||
import { shearSegments } from './core/rebar/coping';
|
||||
@@ -122,6 +123,8 @@ const state = {
|
||||
stage: 'shape' as Stage,
|
||||
/** 전체 보기 on ① 형상 생성: every pier's concrete in one frame (콘크리트만). On by default (승인 2026-10-27). */
|
||||
allView: true,
|
||||
/** ④ 재료표 scope: true = every pier (전체), false = the pier chosen in the list. */
|
||||
quantityAll: true,
|
||||
/** ③ 공통 설정 (fck · fy). */
|
||||
material: { ...DEFAULT_MATERIAL } as Material,
|
||||
/** ② 부재별 fck. A member without an entry uses the 공통 fck (③). Initial values from 교각도 (025·026, 승인 2026-10-27): 기둥·코핑 40, 기초 27. */
|
||||
@@ -437,6 +440,7 @@ function renderPierRows(list: HTMLUListElement, mode: Stage): void {
|
||||
name.setAttribute('aria-pressed', String(pier.input.name === state.active));
|
||||
name.addEventListener('click', () => {
|
||||
state.active = pier.input.name;
|
||||
state.quantityAll = false;
|
||||
state.rebarSelection = new Set();
|
||||
setupScene();
|
||||
renderAll();
|
||||
@@ -1766,8 +1770,10 @@ function renderQuantityLink(box: HTMLElement, rows: RebarRow[]): void {
|
||||
}
|
||||
|
||||
/** ④ 재료표: the 철근재료표 of each target pier, bars by 강종 → 직경 → 번호, with subtotals and the totals. */
|
||||
/** The piers the 재료표 shows: 전체, or the pier chosen in the list. */
|
||||
function quantityTargets(): Pier[] {
|
||||
return state.piers.filter((p) => p.model);
|
||||
const names = new Set(quantityTargetNames(state.piers.map((p) => ({ name: p.input.name, hasModel: p.model !== null })), state.quantityAll, state.active));
|
||||
return state.piers.filter((p) => names.has(p.input.name));
|
||||
}
|
||||
|
||||
/** Length cell of a 재료표 line: the length, or 여러 길이 with the pieces on hover. */
|
||||
@@ -1806,10 +1812,14 @@ function renderQuantityScreen(): void {
|
||||
const body = $<HTMLDivElement>('quantity-body');
|
||||
body.replaceChildren();
|
||||
renderPierRows($<HTMLUListElement>('quantity-pier-items'), 'quantity');
|
||||
const allButton = $<HTMLButtonElement>('quantity-all');
|
||||
const generated = state.piers.filter((p) => p.model).length;
|
||||
allButton.textContent = `전체 (${generated}기)`;
|
||||
allButton.setAttribute('aria-pressed', String(state.quantityAll));
|
||||
const targets = quantityTargets();
|
||||
$('step4-status').textContent = targets.length ? `${targets.length}기` : '예정';
|
||||
$('step4-status').textContent = targets.length ? (state.quantityAll ? `${targets.length}기` : targets[0].input.name) : '예정';
|
||||
$('quantity-note').textContent = targets.length
|
||||
? `${targets.length}기 · 할증 ${state.material.waste}% (기본 0%, 가정). 삿갓 길이는 h − 100으로 가정(Q-K5). 체크한 교각 기준이고, 체크가 없으면 현재 교각입니다.`
|
||||
? `${state.quantityAll ? `전체 ${targets.length}기` : targets[0].input.name} · 할증 ${state.material.waste}% (기본 0%, 가정). 삿갓 길이는 h − 100으로 가정(Q-K5). 교각 목록에서 고른 교각만 보며, 전체를 누르면 모든 교각을 보여 줍니다.`
|
||||
: '계산된 교각이 없습니다. 교각을 생성하세요.';
|
||||
let kg = 0;
|
||||
let kgWithWaste = 0;
|
||||
@@ -1845,6 +1855,10 @@ function exportQuantityWorkbook(): void {
|
||||
XLSX.writeFile(wb, `${state.project.bridge || '교량'}_철근재료표.xlsx`);
|
||||
}
|
||||
|
||||
$<HTMLButtonElement>('quantity-all').addEventListener('click', () => {
|
||||
state.quantityAll = true;
|
||||
renderAll();
|
||||
});
|
||||
$<HTMLButtonElement>('quantity-export').addEventListener('click', exportQuantityWorkbook);
|
||||
$<HTMLButtonElement>('quantity-print').addEventListener('click', () => window.print());
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { quantityTargetNames } from '../src/core/quantity-scope';
|
||||
|
||||
const piers = [
|
||||
{ name: 'P1', hasModel: true },
|
||||
{ name: 'P2', hasModel: true },
|
||||
{ name: 'P3', hasModel: false },
|
||||
];
|
||||
|
||||
describe('quantityTargetNames (④ 재료표 범위)', () => {
|
||||
it('is every pier with a model when 전체 is on', () => {
|
||||
expect(quantityTargetNames(piers, true, 'P2')).toEqual(['P1', 'P2']);
|
||||
});
|
||||
|
||||
it('is only the chosen pier when 전체 is off', () => {
|
||||
expect(quantityTargetNames(piers, false, 'P2')).toEqual(['P2']);
|
||||
});
|
||||
|
||||
it('is empty when the chosen pier has no model', () => {
|
||||
expect(quantityTargetNames(piers, false, 'P3')).toEqual([]);
|
||||
});
|
||||
|
||||
it('is empty when no pier is chosen and 전체 is off', () => {
|
||||
expect(quantityTargetNames(piers, false, null)).toEqual([]);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user