- 고름 대기 칸에 후보(앞 셋)마다 줄 비교 표(줄 · 엑셀 품 · 기초 품 · 꼴 배지) · 후보 고름 칸과 동기 · 칸 글에 같음 · 품 다름 수 · 판정 · 미리 고름 그대로 - 못옮김 잇기 창 · 안 이은 줄 창에 M01 요소 후보 셋 단추(누르면 이음 고름) · 안 이은 줄 창 기초 후보 칸에는 요소 후보가 안 섞이게 가름 - 새 파일 Hopyo_Compare.ts · 새 지킴 시험 test_746_2_hopyo_compare_screen Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
181 lines
7.6 KiB
TypeScript
181 lines
7.6 KiB
TypeScript
/* =============================================================================
|
|
* M02_MasterTemplete_Hopyo_Lost.ts
|
|
* 단가산출 못옮김 잇기 창(PLAN 729-3 · 729-4 이음 · 요구서 내역19 2장 3 · 4) — 실무 블록을 SK 로 옮길 때 우리 M01 에
|
|
* 바로 못 옮긴 엑셀 줄(노임 · 기계 · 자재)을 줄마다 M01 요소로 이음 → SK 더한줄(엑셀 수량) · [뺌] = 놓음(까닭 남김).
|
|
* · 고름 = 캐시 `m02-hopyo-lost:{층}:{주인}:{키}:{번}` `{id: 잇기}` — 서버는 부를 때마다 그 블록 잇기 전부로 다시 셈
|
|
* (앞 잇기 안 남음) → [잇기 만들기] 는 캐시 모두를 보냄 · 바탕 = 그 SK 문서 바탕(로직 · 입력값 · 환산 그대로)
|
|
* · 줄마다 M01 요소 후보 셋 단추(746-2 · 서버 `후보`) — 누르면 그 줄에 이음 고름(찾기와 같은 길)
|
|
* · 남은 못옮김 = 서버 답 그대로 표 · 0 이면 그 SK 를 쓰는 일위대가 줄이 이어짐(남으면 만들기 409)
|
|
* 계산은 서버 — 화면은 고름 · 보이기만 · 저장은 [잇기 만들기] 누를 때만.
|
|
* ========================================================================== */
|
|
|
|
import { createButton, el, showToast } from "@ui/ui_template_elements";
|
|
import { t as L } from "@ui/ui_template_locale";
|
|
import { openModal } from "@ui/ui_template_modal";
|
|
import { createTable } from "@ui/ui_template_table";
|
|
import { createText } from "@ui/ui_template_text";
|
|
import { readTemplate } from "./M02_MasterTemplete_Api_Fetch";
|
|
import { openFindPicker } from "./M02_MasterTemplete_Find_Pick";
|
|
import {
|
|
makeSangeun,
|
|
type HopyoScope,
|
|
type SangeunBuilt,
|
|
type SangeunLink,
|
|
type SangeunLost,
|
|
} from "./M02_MasterTemplete_Hopyo_Api";
|
|
import { elemChoices } from "./M02_MasterTemplete_Hopyo_Compare";
|
|
|
|
const why = (error: unknown): string => (error instanceof Error ? error.message : String(error));
|
|
const NONE = "우리에 없음";
|
|
|
|
export const lostKey = (scope: HopyoScope, key: string, no: number): string =>
|
|
`m02-hopyo-lost:${scope.layer}:${scope.owner ?? ""}:${key}:${no}`;
|
|
|
|
/** 캐시 잇기 `{id: 잇기}` */
|
|
export function loadLinks(k: string): Record<string, SangeunLink> {
|
|
try {
|
|
const got = JSON.parse(sessionStorage.getItem(k) ?? "{}") as unknown;
|
|
return got && typeof got === "object" ? (got as Record<string, SangeunLink>) : {};
|
|
} catch {
|
|
return {};
|
|
}
|
|
}
|
|
const saveLinks = (k: string, v: Record<string, SangeunLink>): void =>
|
|
sessionStorage.setItem(k, JSON.stringify(v));
|
|
|
|
/** 고른 것 글 — 키 또는 뺌 */
|
|
const linkText = (x: SangeunLink | undefined): string =>
|
|
!x ? "" : x.뺌 ? `${L("M02_LostDrop")} · ${x.뺌}` : (x.노임 ?? x.기계 ?? x.자재 ?? "");
|
|
|
|
export interface LostOptions {
|
|
scope: HopyoScope;
|
|
key: string;
|
|
/** 블록 번(단산N) */
|
|
번: number;
|
|
/** 그 블록 SK 키 — 바탕을 읽음 */
|
|
sk: string;
|
|
이름?: string;
|
|
lost: SangeunLost[];
|
|
/** 잇기 만든 뒤(서버 답) */
|
|
done?: (got: SangeunBuilt) => void;
|
|
}
|
|
|
|
/** 못옮김 잇기 창 */
|
|
export function openLost(opt: LostOptions): void {
|
|
const k = lostKey(opt.scope, opt.key, opt.번);
|
|
let lost = opt.lost;
|
|
openModal({
|
|
title: `${L("M02_LostTitle")} — 단산${opt.번} ${opt.이름 ?? ""}`.trim(),
|
|
closeLabel: L("M02_Close"),
|
|
width: "960px",
|
|
dialogClass: "m02-modal",
|
|
mount: (body, _close, actions) => {
|
|
const list = el("div", { attrs: { "data-lost": "" } });
|
|
const put = (id: string, link: SangeunLink | null): void => {
|
|
const all = loadLinks(k);
|
|
if (link) all[id] = link;
|
|
else delete all[id];
|
|
saveLinks(k, all);
|
|
draw();
|
|
};
|
|
const make = createButton({
|
|
label: L("M02_LostMake"),
|
|
variant: "filled",
|
|
onClick: () => void run(),
|
|
});
|
|
make.dataset.lostMake = "";
|
|
actions.append(make);
|
|
body.append(createText(L("M02_LostHint"), "hint", 14), list);
|
|
|
|
async function run(): Promise<void> {
|
|
const links = Object.values(loadLinks(k));
|
|
if (!links.length) return showToast(L("M02_WizSkPickFirst"), "warning");
|
|
make.disabled = true;
|
|
try {
|
|
const doc = (await readTemplate(opt.scope.layer, "sangeun", opt.sk, null, opt.scope.owner))
|
|
.문서 as { 바탕?: { 로직?: string } | null; 입력값?: Record<string, unknown>; 환산?: { 계수?: number } }; // prettier-ignore
|
|
const pick = {
|
|
...(doc.바탕?.로직 ? { 로직: doc.바탕.로직, 입력값: doc.입력값 ?? {} } : {}),
|
|
...(doc.환산?.계수 ? { 환산: { 계수: doc.환산.계수 } } : {}),
|
|
잇기: links,
|
|
};
|
|
const got = await makeSangeun(opt.scope, opt.key, { [opt.번]: pick });
|
|
lost = got.만듦.find((x) => x.번 === opt.번)?.못옮김 ?? [];
|
|
showToast(L("M02_LostLeft").replace("{n}", String(lost.length)), lost.length ? "warning" : "success"); // prettier-ignore
|
|
opt.done?.(got);
|
|
draw();
|
|
} catch (error) {
|
|
showToast(why(error), "error");
|
|
} finally {
|
|
make.disabled = false;
|
|
}
|
|
}
|
|
|
|
function draw(): void {
|
|
const all = loadLinks(k);
|
|
list.dataset.lost = String(lost.length);
|
|
const rows = lost.map((x) => {
|
|
const kind = x.갈래;
|
|
const find = createButton({
|
|
label: kind === "기계" ? L("M02_LeftMachine") : L("M02_LeftFind"),
|
|
size: 24,
|
|
variant: "ghost",
|
|
onClick: () =>
|
|
// 736-2 줄 갈래대로 탭 하나(노임 = 인력 · 기계 = 기계 · 자재 = 재료) → 이음 칸 `노임` · `기계` · `자재`
|
|
openFindPicker({
|
|
layer: opt.scope.layer,
|
|
owner: opt.scope.owner,
|
|
title: `${x.이름} ${x.규격 ?? ""}`.trim(),
|
|
kinds: [kind === "노임" ? "labor" : kind === "기계" ? "machine" : "material"],
|
|
query: x.이름.split(/[\s(]/)[0] ?? "",
|
|
logic: () => showToast(L("M02_GridLinkNoLogic"), "info"),
|
|
element: (e) => put(x.id, { id: x.id, [kind]: e.코드 }),
|
|
machine: (e) => put(x.id, { id: x.id, 기계: e.코드 }),
|
|
}),
|
|
});
|
|
const drop = createButton({
|
|
label: NONE,
|
|
size: 24,
|
|
variant: "ghost",
|
|
onClick: () => put(x.id, { id: x.id, 뺌: NONE }),
|
|
});
|
|
drop.title = L("M02_LostDropTip");
|
|
const clear = createButton({
|
|
label: L("M02_LostClear"),
|
|
size: 24,
|
|
variant: "link",
|
|
onClick: () => put(x.id, null),
|
|
});
|
|
return [
|
|
kind,
|
|
x.이름,
|
|
x.규격 ?? "",
|
|
`${x.수량 ?? ""} ${x.단위 ?? ""}`.trim(),
|
|
x.글 ?? "",
|
|
elemChoices(x.후보 ?? [], (c) => put(x.id, { id: x.id, [kind]: c.키 })) ?? "",
|
|
linkText(all[x.id]),
|
|
el("div", { className: "ui-toolbar", children: [find, drop, ...(all[x.id] ? [clear] : [])] }), // prettier-ignore
|
|
];
|
|
});
|
|
list.replaceChildren(
|
|
createTable(
|
|
[
|
|
{ label: L("M02_LeftKind"), width: "56px" },
|
|
{ label: L("M02_SangeunColName") },
|
|
{ label: L("M02_LeftSpec") },
|
|
{ label: L("M02_SangeunColQty"), width: "90px" },
|
|
{ label: L("M02_LeftWhy") },
|
|
{ label: L("M02_ElemTitle") },
|
|
{ label: L("M02_LeftPicked") },
|
|
{ label: L("M02_LeftPick"), width: "220px" },
|
|
],
|
|
rows,
|
|
{ density: "data", empty: L("M02_LostZero") },
|
|
),
|
|
);
|
|
}
|
|
draw();
|
|
},
|
|
});
|
|
}
|