feat(M02): 일위대가 상세를 UI_Main 종류 labor 로 엶 · 보기 갈래 부품 · 수량만 바뀌면 짝 셈으로 바로 곱함(39-3 C7)
- UI_Main: 종류 labor 갈래 한 곳(mountLaborDetail · 「면적산출에서 잇기」 = 물은 뒤 그 열 면적산출) · 번호 칸 일위대가번 - Api_Fetch: Kind 에 labor - 새 Labor_View.ts(createLaborView — 면적산출 오른쪽 패널 · 일위대가 카드용 · 담은 로직 + 자재목록 · show · setQty · onRow) - Labor_Detail: 면적산출 연결 칸 → 「수량 F14 = …」 · 수량만 바뀌면 Labor_Calc.settle 로 서버 없이 · 손 입력 숫자는 수로 - Labor_Api: calc 답 모양은 Labor_Calc.ts 것을 씀 · normalizeLabor · areaLinks - 자재 표 머리 「단위」 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MgChuAcyBqUkhHMTXomQC7
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
/* =============================================================================
|
||||
* M02_MasterTemplete_Labor_View.ts
|
||||
* 일위대가 보기 갈래(39-3 · 고치기 없음) — 그 열 일위대가의 담은 로직 표 + 자재목록 표(기준 · 반영).
|
||||
* 면적산출 상세 오른쪽 패널(sub3) · 구조물 조합 일위대가 카드(sub1)가 씀. 표 모양은 상세와 한 벌(`Labor_Rows`).
|
||||
* · `show(층, 열, 수량?)` = 문서 · 로직 입력 목록 · calc 를 받아 그림(최신 응답만)
|
||||
* · `setQty({알약 키: 칸 값})` = 면적 칸 값이 바뀌면 서버 없이 짝 셈(`Labor_Calc.settle`)으로 바로 곱함
|
||||
* (그 열 면적 알약 값 전부를 줌 — 빠진 키는 「수량 없음」)
|
||||
* · `onRow` = 줄 누름(알약 키 · 이름 · 단위 — 칸 지정 모달 등) · 없으면 누름 없음
|
||||
* ========================================================================== */
|
||||
|
||||
import { el } from "@ui/ui_template_elements";
|
||||
import { readTemplate, type ItemLogic, type Layer } from "./M02_MasterTemplete_Api_Fetch";
|
||||
import {
|
||||
areaLinks,
|
||||
calcLabor,
|
||||
fetchLogicInputs,
|
||||
normalizeLabor,
|
||||
type LaborCalc,
|
||||
type LaborDoc,
|
||||
} from "./M02_MasterTemplete_Labor_Api";
|
||||
import { settle } from "./M02_MasterTemplete_Labor_Calc";
|
||||
import { logicTable, matTable } from "./M02_MasterTemplete_Labor_Rows";
|
||||
import { tc } from "./M02_MasterTemplete_Labor_Text";
|
||||
import "./M02_MasterTemplete_Labor.css";
|
||||
|
||||
export interface LaborViewRow {
|
||||
kind: "logic" | "material";
|
||||
/** 로직 키 · 자재 코드 */
|
||||
key: string;
|
||||
/** 면적산출 알약 키(로직 「이름|단위」 · 자재 「자재|MT키」) — calc 전이면 null */
|
||||
pill: string | null;
|
||||
name: string;
|
||||
unit: string;
|
||||
}
|
||||
|
||||
export interface LaborViewHandle {
|
||||
root: HTMLElement;
|
||||
/** 그 열 일위대가를 읽어 그림 — `수량` = 저장 안 한 면적 칸 값(없으면 저장된 면적산출) */
|
||||
show: (layer: Layer, column: string | null, 수량?: Record<string, number>) => Promise<void>;
|
||||
/** 면적 칸 값이 바뀜(알약 값 전부) — 받은 기준에 바로 곱함(서버 안 부름) */
|
||||
setQty: (수량: Record<string, number>) => void;
|
||||
/** 지금 문서(없으면 null) */
|
||||
doc: () => LaborDoc | null;
|
||||
destroy: () => void;
|
||||
}
|
||||
|
||||
export function createLaborView(onRow?: (row: LaborViewRow) => void): LaborViewHandle {
|
||||
const root = el("div", { className: "m02-labview" });
|
||||
const logics = new Map<string, ItemLogic>();
|
||||
let doc: LaborDoc | null = null;
|
||||
let calc: LaborCalc | null = null;
|
||||
let fail: string | null = null;
|
||||
let cells = new Map<string, string>();
|
||||
let seq = 0;
|
||||
|
||||
const paint = (): void => {
|
||||
if (!doc) return;
|
||||
const d = doc;
|
||||
const pick = (row: LaborViewRow): (() => void) | undefined =>
|
||||
onRow ? () => onRow(row) : undefined;
|
||||
const logicOpen = (i: number): void => {
|
||||
const row = d.담은로직[i];
|
||||
const line = calc?.줄.find((l) => l.로직 === row.로직);
|
||||
pick({
|
||||
kind: "logic",
|
||||
key: row.로직,
|
||||
pill: line?.알약 ?? null,
|
||||
name: logics.get(row.로직)?.이름 ?? row.로직,
|
||||
unit: line?.분모 ?? "",
|
||||
})?.();
|
||||
};
|
||||
const matOpen = (i: number): void => {
|
||||
const mat = d.자재[i];
|
||||
const line = calc?.자재.find((m) => m.코드 === mat.코드);
|
||||
pick({
|
||||
kind: "material",
|
||||
key: mat.코드,
|
||||
pill: line?.알약 ?? null,
|
||||
name: mat.이름 ?? mat.코드,
|
||||
unit: mat.단위 ?? "",
|
||||
})?.();
|
||||
};
|
||||
root.replaceChildren(
|
||||
el("h4", { className: "m02-labview__title", text: tc("Logics_Title") }),
|
||||
logicTable(
|
||||
{ rows: d.담은로직, logics, calc, fail, cells },
|
||||
onRow ? { open: logicOpen } : undefined,
|
||||
),
|
||||
el("h4", { className: "m02-labview__title", text: tc("Labor_Mats") }),
|
||||
matTable({ mats: d.자재, calc, fail, cells }, onRow ? { open: matOpen } : undefined),
|
||||
);
|
||||
};
|
||||
|
||||
const show = async (
|
||||
layer: Layer,
|
||||
column: string | null,
|
||||
수량?: Record<string, number>,
|
||||
): Promise<void> => {
|
||||
const mine = ++seq;
|
||||
doc = null;
|
||||
calc = null;
|
||||
fail = null;
|
||||
if (!column) return void root.replaceChildren();
|
||||
root.replaceChildren(el("p", { className: "m02-labrow__muted", text: tc("Loading") }));
|
||||
try {
|
||||
const got = await readTemplate(layer, "labor", column, null);
|
||||
if (mine !== seq) return;
|
||||
doc = normalizeLabor(got.문서, column);
|
||||
const want = doc.담은로직.map((r) => r.로직).filter((k) => !logics.has(k));
|
||||
const [inputs, area] = await Promise.all([
|
||||
fetchLogicInputs(want),
|
||||
readTemplate(layer, "area", column, null).then(
|
||||
(a) => a.문서,
|
||||
() => null,
|
||||
),
|
||||
]);
|
||||
if (mine !== seq) return;
|
||||
for (const l of inputs.로직) logics.set(l.로직, l);
|
||||
cells = new Map([...areaLinks(area)].map(([k, v]) => [k, v.칸]));
|
||||
paint(); // 금액 「…」
|
||||
calc = await calcLabor(layer, column, null, 수량);
|
||||
} catch (error) {
|
||||
if (mine !== seq) return;
|
||||
if (!doc) {
|
||||
root.replaceChildren(
|
||||
el("p", {
|
||||
className: "m02-labrow__empty",
|
||||
text: error instanceof Error ? error.message : tc("Load_Failed"),
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
fail = error instanceof Error ? error.message : tc("Labor_Calc_Fail");
|
||||
}
|
||||
if (mine === seq) paint();
|
||||
};
|
||||
|
||||
return {
|
||||
root,
|
||||
show,
|
||||
setQty: (수량) => {
|
||||
if (!doc || !calc) return;
|
||||
calc = settle(calc, doc, 수량);
|
||||
paint();
|
||||
},
|
||||
doc: () => doc,
|
||||
destroy: () => {
|
||||
seq += 1;
|
||||
root.remove();
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user