feat(799-2): M01 로직 상세 · M02 단가산출 상세 옛 탭 넷(시트 · 인력/재료/경비 · 시험 계산 · 텍스트 수식) 걷음 — 보기는 수식 · 단가산출 상세 · 셈 구동 따로
- 셈 구동 떼기: 새 M01_MasterData_UI_Logic_Drive.ts(driveRun · previewText · autoFill · createDrive) — M01 상세는 숨은 시험 계산 칸 단추 대신 구동 함수 · 시험 계산 칸(Logic_Calc)은 로직 복사 화면이 쓰므로 남기고 같은 구동을 부름 · 재료 고름은 rerun 고리 - M02: 새 _Sangeun_Tabs.ts(수식 · 단가산출 상세 보기 고르기) · Detail 의 시험 계산 · 인력/재료/경비 걷음 - 걷은 파일(git rm): M01 LogicLab_Sheet · Logic_Sheet_Model · LogicLab_Formula(.ts .css) · FormulaTokens · LogicLab_Est / M02 Sangeun_Sheet(.ts .css) · SheetEdit · SheetFormula · logic Sheet_Model · Const_Model · Formula_Model · 부품 시트 줄(createCalcSheetRow) · Cover_Model.sheetGroups · Calc 의 M02 고리(answer · shown · held) · 죽은 CSS · 글 - 서버 길 · 문서 칸(변수줄 · 글줄 · 덮개)은 남김 — 금액 그대로 - 낡은 시험: test_790_1(시트 화면 전부) · test_714_3 · test_748_2(+ helper)는 화면이 없어져 지움 · test_794_1 · test_790_2 는 화면 · TS 거울만 덜고 서버 시험 남김 · test_714_2 · 717_2b · 721 · 722(원래 틀린 한 줄 고침) · 734 · 737 · 750_2 · 793_3b · 796_2 · 796_2b 는 보기 둘 · 새 파일에 맞춤 · 새 test_799_2_old_tabs_gone Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,28 +2,25 @@
|
||||
* M01_MasterData_UI_Logic_Calc.ts
|
||||
* 시험 계산 — 받을 값을 넣고 [계산] → 줄별 금액 · 비목 합 · 멈춘 까닭(엔진 글 그대로)
|
||||
* 고친 로직은 저장 전 줄(`row`)을 같이 보내 메모리에서 셈(`POST /calc`) — 파일에 안 씀
|
||||
* 셈 구동(입력 → 서버 → 답) = `_Logic_Drive.ts` — 로직 상세는 이 칸 없이 그 구동만 씀(799-2) · 이 칸은 로직 복사 화면이 씀
|
||||
* ========================================================================== */
|
||||
|
||||
import { createButton, createSelectField, el, showToast } from "@ui/ui_template_elements";
|
||||
import {
|
||||
fetchAuto,
|
||||
pctView,
|
||||
runCalc,
|
||||
runText,
|
||||
searchElements,
|
||||
type CalcAnswer,
|
||||
type CalcLine,
|
||||
type ElementBrief,
|
||||
type TextAnswer,
|
||||
type LogicRow,
|
||||
} from "./M01_MasterData_UI_Logic_Api";
|
||||
import {
|
||||
isMaterialKeyInput,
|
||||
machineControl,
|
||||
materialRows,
|
||||
pickBody,
|
||||
showItems,
|
||||
} from "./M01_MasterData_UI_Logic_CalcPick";
|
||||
import { autoFill, driveRun } from "./M01_MasterData_UI_Logic_Drive";
|
||||
import { formatNumber } from "./M01_MasterData_UI_Logic_Edit";
|
||||
import { formatLineMoney, formatMoney } from "./M01_MasterData_UI_Logic_Money";
|
||||
import { safeReason, tx } from "./M01_MasterData_UI_Logic_Text";
|
||||
@@ -52,12 +49,6 @@ async function keyName(key: string): Promise<string | null> {
|
||||
return name;
|
||||
}
|
||||
|
||||
/** (M02 750-2) SK 셈 답으로 그리는 시험 계산 — 답 + 호표 차례 줄(재료 칸 잡힌 품목 글용) */
|
||||
export interface CalcShown {
|
||||
answer: CalcAnswer;
|
||||
items?: (CalcLine | undefined)[];
|
||||
}
|
||||
|
||||
export interface CalcContext {
|
||||
/** 저장된 키 — 새 로직은 null */
|
||||
savedKey: string | null;
|
||||
@@ -70,16 +61,6 @@ export interface CalcContext {
|
||||
/** 로직마다 넣은 값 — 다시 그려도 남음 */
|
||||
values: Record<string, string>;
|
||||
onLines?: (lines: CalcLine[] | null) => void;
|
||||
/** 794-1 셈 답 통째(중간 값 · 소계) — M01 줄 식 시트 값 */
|
||||
onAnswer?: (answer: CalcAnswer) => void;
|
||||
/** 읽는 식 줄(`/text`)도 같이 받고 싶을 때 — 호출이 `onLines` 보다 먼저 */
|
||||
onText?: (answer: TextAnswer) => void;
|
||||
/** (M02 750-2) 있으면 [계산] = 이 셈(SK 셈 답 하나) · `/calc` · `/text` · 미리보기 안 부름 */
|
||||
answer?: () => Promise<CalcShown | null>;
|
||||
/** (M02 750-2) 이미 받은 SK 셈 답 — 그릴 때 바로 결과로 */
|
||||
shown?: CalcShown | null;
|
||||
/** (M02 750-2) 재료 고름 = SK 문서 `고름` 에도 */
|
||||
held?: (i: number, it: { ref: string; label: string }) => void;
|
||||
}
|
||||
|
||||
const grid = (children: HTMLElement[]): HTMLElement =>
|
||||
@@ -87,12 +68,9 @@ const grid = (children: HTMLElement[]): HTMLElement =>
|
||||
|
||||
const SUMS = ["노무비", "재료비", "경비", "계"];
|
||||
|
||||
/** 로직마다 자동값을 한 번만 받음(다시 그려도 안 부름) */
|
||||
/** 로직마다 첫 셈을 한 번만(다시 그려도 안 부름) */
|
||||
const autoAsked = new WeakSet<Record<string, string>>();
|
||||
|
||||
/** 로직을 열 때마다 새로 부르는 미리보기 — 늦게 온 응답이 새 로직을 덮지 않게 순번으로 거름 */
|
||||
let previewSeq = 0;
|
||||
|
||||
export function buildCalc(host: HTMLElement, ctx: CalcContext): void {
|
||||
// 그릇 폭 280px 고정 칸은 옛 테스트 컨테이너(UI_Test*, 걷힘)의 죽은 CSS 규칙 —
|
||||
// 지금 쓰는 LogicLab 화면은 그 칸에 안 걸려 폭이 이미 넓음(실측 730px) · 따로 안 풂
|
||||
@@ -147,78 +125,23 @@ export function buildCalc(host: HTMLElement, ctx: CalcContext): void {
|
||||
children: [el("span", { text: label }), control],
|
||||
});
|
||||
});
|
||||
const materials = materialRows(ctx.row, ctx.prices ?? {}, ctx.values, again, ctx.held);
|
||||
const materials = materialRows(ctx.row, ctx.prices ?? {}, ctx.values, again);
|
||||
const out = el("div", { className: "m01-logic__calc-out" });
|
||||
const mySeqAtOpen = ctx.onText && !ctx.answer ? previewSeq + 1 : previewSeq; // 이 그리기가 받을 순번
|
||||
let manualRun = false; // [계산] 이 이미 답했으면 뒤늦게 오는 미리보기로 안 덮음
|
||||
if (ctx.onText && !ctx.answer) {
|
||||
const mySeq = ++previewSeq;
|
||||
const draft = ctx.dirty() || ctx.savedKey === null;
|
||||
const body = {
|
||||
key: ctx.savedKey ?? ctx.row.키,
|
||||
inputs: {},
|
||||
...(draft ? { row: ctx.row, file: ctx.file } : {}),
|
||||
};
|
||||
void runText(body).then((answer) => {
|
||||
if (manualRun || mySeq !== previewSeq) return; // [계산] 을 눌렀거나 다른 로직이 이미 열림
|
||||
(answer as TextAnswer & { 계산됨?: boolean }).계산됨 = false;
|
||||
ctx.onText?.(answer);
|
||||
});
|
||||
}
|
||||
if (!fields.length && !autoAsked.has(ctx.values)) {
|
||||
autoAsked.add(ctx.values); // 입력이 없으면 채울 것이 없음 — 열 때 한 번 바로 계산
|
||||
queueMicrotask(() => {
|
||||
if (previewSeq === mySeqAtOpen) again();
|
||||
});
|
||||
queueMicrotask(again);
|
||||
} else if (ctx.savedKey !== null && !autoAsked.has(ctx.values)) {
|
||||
autoAsked.add(ctx.values);
|
||||
void fetchAuto(ctx.savedKey).then((auto) => {
|
||||
const names = (ctx.row.입력 ?? [])
|
||||
.map((spec) => spec.이름)
|
||||
.filter((n) => auto[n] !== undefined && auto[n] !== null && !(ctx.values[n] ?? "").trim());
|
||||
if (!names.length || previewSeq !== mySeqAtOpen) return; // 값이 없거나 다른 로직이 이미 열림
|
||||
for (const n of names) ctx.values[n] = String(auto[n]);
|
||||
buildCalc(host, ctx); // 채운 값을 새로 그리고 바로 계산(결과 · 텍스트 수식까지)
|
||||
void autoFill(ctx).then((filled) => {
|
||||
if (!filled) return;
|
||||
buildCalc(host, ctx); // 채운 값을 새로 그리고 바로 계산
|
||||
again();
|
||||
});
|
||||
}
|
||||
const money = (ctx.row.결과단위 ?? "").startsWith("원");
|
||||
const paint = (s: CalcShown): void => {
|
||||
out.replaceChildren(...answerView(s.answer, false, money));
|
||||
if (s.answer.ok) showItems(host, ctx.values, s.items ?? s.answer.lines ?? []);
|
||||
};
|
||||
const run = async (): Promise<void> => {
|
||||
manualRun = true;
|
||||
if (ctx.answer) {
|
||||
try {
|
||||
const got = await ctx.answer();
|
||||
if (got) paint(got);
|
||||
} catch (error) {
|
||||
showToast(error instanceof Error ? error.message : tx("Load_Failed"), "error");
|
||||
}
|
||||
return;
|
||||
}
|
||||
const inputs: Record<string, unknown> = {};
|
||||
for (const spec of ctx.row.입력 ?? []) {
|
||||
const raw = (ctx.values[spec.이름] ?? "").trim();
|
||||
if (raw === "") continue; // 빈 칸은 안 보냄 — 엔진이 「입력 없음」 으로 멈춤
|
||||
// 고르기는 원래 값(수면 수) 그대로 — 글 "35" 로 보내면 「고르기 밖」
|
||||
const option = spec.고르기?.find((o) => String(o) === raw);
|
||||
inputs[spec.이름] =
|
||||
option !== undefined ? option : Number.isNaN(Number(raw)) ? raw : Number(raw);
|
||||
}
|
||||
const draft = ctx.dirty() || ctx.savedKey === null;
|
||||
try {
|
||||
const body = {
|
||||
key: ctx.savedKey ?? ctx.row.키,
|
||||
inputs,
|
||||
...pickBody(ctx.values),
|
||||
...(draft ? { row: ctx.row, file: ctx.file } : {}),
|
||||
};
|
||||
const answer = await runCalc(body);
|
||||
if (ctx.onText) ctx.onText(await runText(body));
|
||||
ctx.onAnswer?.(answer);
|
||||
ctx.onLines?.(answer.ok ? (answer.lines ?? null) : null);
|
||||
const { answer, draft } = await driveRun(ctx);
|
||||
if (answer.ok && answer.lines) showItems(host, ctx.values, answer.lines);
|
||||
out.replaceChildren(...answerView(answer, draft, money));
|
||||
} catch (error) {
|
||||
@@ -240,7 +163,6 @@ export function buildCalc(host: HTMLElement, ctx: CalcContext): void {
|
||||
runButton,
|
||||
out,
|
||||
);
|
||||
if (ctx.shown) paint(ctx.shown);
|
||||
}
|
||||
|
||||
/** 중간 값 이름이 돈 꼴(경비 · 노무비 · 공구손료 · 금액 · 단가 · 노임) — 1,000 미만이어도 돈 꼴 */
|
||||
|
||||
Reference in New Issue
Block a user