Files
Aislo/B09_Estimation/B09_Estimation_UI_Tabs_Labor.ts
T
eomsangdonandClaude Opus 5.5 40ed04b15f feat(ui): 묶음 카드 공용 양식 · B09 묶음 탭 셋 카드 기본(171-2 · 171-3)
- 171-2 새 공용 ui_template_group_cards(.ts · .css) — 머리(호표 · 명칭 · 규격 · 단위 · 합계) · 몸 줄(이름 · 규격 · 수량 · 단위 · 합계 · 비고) · 합계 줄(노무 · 재료 · 경비 · 계)
- 글자 14px 한 벌 · 줄 간격 좁게(공용 카드 16px 끼움 없음) · 숫자 오른쪽 · 줄 금액 = 합계만 + 비목 셋 풍선
- 가로 열 수 1~4(기본 2) · 묶음 12 넘으면 요약 보기(머리 · 합계만 · 머리 누름 = 펼침 · 모두 펼침 · 접기)
- 157-2 산근 카드 파일 갈음(지움)
- 171-3 일위대가표 · 중기사용료 · 단가산출근거 = 카드 기본 · [표로 보기] = 표 그대로 · 보기 · 열 수 기억(b_page_state 두 줄 · 탭마다)
- 값 · 풍선 · 칸 누름 이동 = 표 줄 그대로 · 집계표 · 설계내역서 · B08 대상 밖
- 시험 test_171_2 · test_171_3 · 공용 도우미(가짜 DOM) · test_157_2 갈음

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015KYZ2HWWdUwZWYnn3tQz7X
2026-10-10 08:46:56 +09:00

165 lines
5.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* =============================================================================
* B09_Estimation_UI_Tabs_Labor.ts
* B09 대가 탭 둘(PLAN 145-2) — 일위대가목록표 · 일위대가표(실무 엑셀 꼴 · 값 = `B09_Estimation_Calc_Labor`).
*
* 목록표 = 호표 한 줄(합계 · 노무비 · 재료비 · 경비) · 줄 누름 = 그 블록으로
* 일위대가표 = 블록(머리 · [품셈 인용] · 줄 · 합계) · 줄 누름 = 단가를 낸 탭 · 줄(대가 줄 = 그 블록)
* 줄 꼴(155-2) = 머리 · 합계 굵게(`is-title`) · 인용 · 줄 = 들여쓰기 「ㄴ」(2단 · 겹 대가는 안 펼침)
* 연결된 구조물 조합 사본이 없으면 빈 표 「연결 없음」
* 빈 줄(166-6) = 합계 = 아는 줄 합 · 옆 「빈 줄 n」 · 풍선 = 빈 줄 이름 · 내역 단가엔 안 넘김
* ========================================================================== */
import type { DataRow } from "@ui/ui_template_data_table";
import type { Row } from "./B09_Estimation_Calc_Base";
import {
money,
qty,
text,
type Link,
type TabBuild,
type TabId,
type TabTable,
} from "./B09_Estimation_UI_Tabs";
import { raw } from "./B09_Estimation_UI_Tabs_Sangeun";
const EMPTY =
"연결 없음 — 연결된 구조물 조합 사본(일위대가)이 없음 · B05 · B06 에서 조합을 고르고 [저장]";
/** 하위 줄 앞 — 들여쓰기 + 「ㄴ」(공용 표가 앞 빈칸을 접지 않음) */
const CHILD = " ㄴ ";
const ROUNDS = "줄 금액 ROUNDDOWN(단가 × 수량, 1) · % 줄 = 밑수 × % / 100 · 합계 ROUNDDOWN 0";
const BLANK = "빈 줄 n = 금액 모르는 줄 · 합계 = 아는 줄 합 · 내역 단가엔 안 넘김";
/** 「빈 줄 n」 작은 글 · 풍선 = 빈 줄 이름 */
const blankNote = (b: Row): string => (b.blank ? `빈 줄 ${b.blank}` : "");
const blankTip = (b: Row | undefined): string | null =>
b?.blank ? `빈 줄 — ${(b.blank_names as string[]).join(" · ")}` : null;
/** 블록 줄 → 단가를 낸 탭 */
const LINE_TAB: Record<string, TabId> = {
labor: "labor",
material: "material",
machine: "machine",
daega: "dg",
sangeun: "sg",
};
export const buildDaegaList: TabBuild = ({ sheets }): TabTable | string => {
const rows: DataRow[] = (sheets.daega || []).map((r: Row) => ({
...r,
no: `대가${r.no}`,
blank_note: blankNote(r),
}));
if (!rows.length) return EMPTY;
return {
columns: [
text("no", "호표", 80),
text("kind", "꼴", 70),
text("name", "명칭", 240),
text("spec", "규격", 200),
text("unit", "단위", 56),
money("sum", "합계"),
text("blank_note", "", 64),
money("labor", "노무비"),
money("material", "재료비"),
money("expense", "경비"),
],
rows,
frozen: 3,
hint: `호표 = 일위대가표 블록 합계 · ${BLANK} · 줄을 누르면 그 블록으로`,
link: (i: number): Link | null => (rows[i] ? { tab: "dg", key: String(rows[i].key) } : null),
tip: (i, key) =>
key === "spec"
? raw(rows[i]?.spec, rows[i]?.spec_raw)
: key === "blank_note" || key === "sum"
? blankTip(rows[i])
: null,
};
};
export const buildDaegaBlocks: TabBuild = ({ sheets }): TabTable | string => {
const blocks: Row[] = sheets.daega_blocks || [];
if (!blocks.length) return EMPTY;
const rows: DataRow[] = [];
const links: Array<Link | null> = [];
const titles = new Set<number>();
for (const b of blocks) {
titles.add(rows.length);
rows.push({
key: b.id,
name: `제 ${b.no} 호표 ${b.name}`,
no: b.no,
title: b.name, // 171-3 묶음 카드 머리
spec: b.spec,
spec_raw: b.spec_raw,
unit: b.unit,
note: b.kind,
});
links.push(null);
if (b.cite) {
rows.push({ key: `${b.id}|cite`, name: `${CHILD}${b.cite}`, note: "품셈 인용" });
links.push(null);
}
b.lines.forEach((ln: Row, n: number) => {
rows.push({
key: `${b.id}|${n}`,
name: `${CHILD}${ln.name || ""}`,
spec: ln.spec || "",
spec_raw: ln.spec_raw,
qty: ln.qty,
unit: ln.unit || "",
sum_price: ln.price,
sum: ln.amount,
labor_price: ln.prices.labor,
labor: ln.amounts.labor,
material_price: ln.prices.material,
material: ln.amounts.material,
expense_price: ln.prices.expense,
expense: ln.amounts.expense,
note: ln.ref_no || ln.why || "",
});
const tab = LINE_TAB[ln.kind];
links.push(tab && ln.key ? { tab, key: String(ln.key) } : null);
});
titles.add(rows.length);
rows.push({
key: `${b.id}|합계`,
name: " 합 계",
sum: b.sum,
labor: b.labor,
material: b.material,
expense: b.expense,
note: blankNote(b),
blank: b.blank,
blank_names: b.blank_names,
});
links.push(null);
}
const pair = (key: string, label: string) => [
money(`${key}_price`, "단가", [label], 1),
money(key, "금액", [label], 1),
];
return {
columns: [
text("name", "명칭", 240),
text("spec", "규격", 160),
qty("qty", "수량"),
text("unit", "단위", 56),
...pair("sum", "합계"),
...pair("labor", "노무비"),
...pair("material", "재료비"),
...pair("expense", "경비"),
text("note", "비고", 120),
],
rows,
frozen: 1,
hint: `단가 = B09 프로젝트 한 벌 굳힌 단가 · ${ROUNDS} · ${BLANK} · 줄 누름 = 단가를 낸 탭(대가 줄 = 그 블록)`,
link: (i: number) => links[i] ?? null,
rowClass: (i: number) => (titles.has(i) ? "is-title" : null),
tip: (i: number, key: string) =>
key === "spec"
? raw(rows[i]?.spec, rows[i]?.spec_raw)
: key === "note" || key === "sum"
? blankTip(rows[i])
: null,
};
};