- 수량 칸(요구 701-1) = 늘 식 글 칸(Q_특별인부) + 출처 꼬리표(표 = 표 고르기) · 눌러도 안 바뀜 · 표 값 기호에 수 = 그 값만(직접 입력) · 꼬리표 한 곳(sourceTag) - 글꼴(701-2) = 줄 쌓기 화면이 정함(14px · M02 가 부모 16px 을 받던 것) - M02 머리(701-3) = [초기화] 와 [되돌림] 뜻 다름 → [초기화] 이름 「M01 값으로」 · [단가 갱신][재생성] = 보기 고르기 옆 머리 칸(수식 보기만) - 일위대가 단가산출 줄 모달(701-5) = 페이지 머리와 같음(보기 고르기 · [단가 갱신] · [재생성] · [저장]) - 일위대가 로직 줄 모달(701-4 · 5) = 옛 [옵션] · [단가산출로도 저장] · [항목 추가] · [율 줄 추가] 걷음 · 보기 고르기 없이 상세만 + 안내(온 단가산출) · 줄 문서 키 SK-000000 이 수식 문서를 읽어 404 · 「못 읽음」 이던 것 고침 - 시험: test_804_7 낡은 소스 기대(꼬리표 조건 글) 고침 + 808-1 · test_806_1 808-1 모달 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
123 lines
5.1 KiB
TypeScript
123 lines
5.1 KiB
TypeScript
/* =============================================================================
|
|
* M02_MasterTemplete_Labor_SkEdit.ts
|
|
* 일위대가 단가산출(SK) 참조 줄 모달(PLAN 806-1 · 요구 698-2 · 689) — 일위대가 표(컨테이너 · 구조물 조합 한 부품)에서
|
|
* 단가산출 줄을 고르면 그 단가산출 문서를 그대로 엶 = M02 단가산출 상세(`Sangeun_Detail` · 첫 탭 「수식」 줄 쌓기) 부품 그대로.
|
|
* · [저장] = 수식 저장(`flush`) → 그 SK 문서 저장(`saveTemplate` · 그 층 · 그 주인) → 닫음 → 부르는 쪽이 일위대가 금액을 다시 셈
|
|
* · 그 층을 못 고치면(위 층 · 남의 주인) 읽기만 · [저장] 없음 · 고친 것 있이 닫으면 물음(버리면 수식 캐시도 비움)
|
|
* · 머리(808-1 · 요구 701-4 · 5) = 단가산출 페이지 머리와 같음 — 보기 고르기 · [단가 갱신] · [재생성] · [저장] · [닫기]
|
|
* 짝 모달 = `Labor_RefEdit`(일위조합 SC 줄).
|
|
* ========================================================================== */
|
|
|
|
import { createButton, el, showConfirmDialog, showToast } from "@ui/ui_template_elements";
|
|
import { openModal } from "@ui/ui_template_modal";
|
|
import { createText } from "@ui/ui_template_text";
|
|
import { readTemplate, saveTemplate, type Layer } from "./M02_MasterTemplete_Api_Fetch";
|
|
import type { LaborRef } from "./M02_MasterTemplete_Labor_Calc";
|
|
import { tc } from "./M02_MasterTemplete_Labor_Text";
|
|
import { mountSangeunDetail } from "./M02_MasterTemplete_Sangeun_Detail";
|
|
|
|
export interface SkEditOptions {
|
|
ref: LaborRef;
|
|
/** 「뒷채움돌 … 」 */
|
|
title: string;
|
|
/** 일위대가 머리(이름 · 「1 ㎡ 당」) */
|
|
head: string;
|
|
readOnly: boolean;
|
|
/** 닫힘 — `saved` = 그 SK 문서를 저장함(부르는 쪽이 금액을 다시 셈) */
|
|
closed: (saved: boolean) => void;
|
|
}
|
|
|
|
/** 단가산출 줄인가 — SK 참조 줄만 이 모달(로직 줄 · SC 줄 · 고정 줄은 다른 길) */
|
|
export const isSkRef = (ref: LaborRef | undefined): ref is LaborRef =>
|
|
!!ref && ref.키.startsWith("SK-");
|
|
|
|
/** 열린 단가산출 모달 — 같은 줄을 빨리 두 번 눌러도 하나만(읽는 동안 늦게 열림) */
|
|
const opened = new Set<string>();
|
|
|
|
export async function openSkEdit(opt: SkEditOptions): Promise<void> {
|
|
const layer = opt.ref.층 as Layer;
|
|
const at = `${layer}/${opt.ref.주인 ?? ""}/${opt.ref.키}`;
|
|
if (opened.has(at)) return;
|
|
opened.add(at);
|
|
let doc: unknown;
|
|
let version: string;
|
|
try {
|
|
const got = await readTemplate(layer, "sangeun", opt.ref.키, null, opt.ref.주인);
|
|
doc = got.문서;
|
|
version = got.판 ?? "";
|
|
} catch (error) {
|
|
opened.delete(at);
|
|
return void showToast(error instanceof Error ? error.message : tc("Load_Failed"), "error");
|
|
}
|
|
let dirty = false;
|
|
let saved = false;
|
|
let handle: ReturnType<typeof mountSangeunDetail> | null = null;
|
|
const finish = (): void => {
|
|
handle?.destroy(); // 고친 채 버리면 수식 캐시도 비움
|
|
opened.delete(at);
|
|
opt.closed(saved);
|
|
};
|
|
openModal({
|
|
title: opt.title,
|
|
closeLabel: tc("In_Close"),
|
|
dialogClass: "m02-modal m02-labsk",
|
|
width: "1100px",
|
|
wide: true,
|
|
beforeClose: async () => {
|
|
if (dirty && !saved && !(await showConfirmDialog(tc("Ref_Discard")))) return false;
|
|
finish();
|
|
return true;
|
|
},
|
|
mount: (body, close, head) => {
|
|
const host = el("div", { attrs: { "data-labor-sk": opt.ref.키 } });
|
|
const slot = el("span", { className: "m02-main__tools" }); // 보기 고르기 · [단가 갱신] · [재생성](808-1 · 페이지 머리와 같음)
|
|
handle = mountSangeunDetail(host, doc, {
|
|
layer,
|
|
owner: opt.ref.주인,
|
|
readOnly: opt.readOnly,
|
|
onChanged: (d) => (dirty = d && !opt.readOnly),
|
|
onVersion: (v) => (version = v), // 수식 저장이 파일 판을 바꿈 — 문서 저장이 이어 받음
|
|
source: true, // 페이지와 같은 길 — 수식 저장 단추는 이 [저장] 한 번에 모임
|
|
slot,
|
|
});
|
|
const ok = createButton({
|
|
label: tc("Save"),
|
|
variant: "filled",
|
|
onClick: () =>
|
|
void (async () => {
|
|
if (!handle) return;
|
|
try {
|
|
await handle.flush();
|
|
await saveTemplate(
|
|
layer,
|
|
"sangeun",
|
|
opt.ref.키,
|
|
null,
|
|
version,
|
|
handle.getDoc(),
|
|
opt.ref.주인,
|
|
);
|
|
saved = true;
|
|
showToast(tc("Saved"), "success");
|
|
close();
|
|
finish();
|
|
} catch (error) {
|
|
showToast(error instanceof Error ? error.message : tc("Lib_SaveFailed"), "error");
|
|
}
|
|
})(),
|
|
});
|
|
ok.dataset.skSave = "";
|
|
body.append(
|
|
createText(opt.head, "muted", 14),
|
|
createText(
|
|
opt.readOnly ? `${tc("Ref_Shared")} · ${tc("Ref_ReadOnly")}` : tc("Ref_Shared"),
|
|
"hint",
|
|
),
|
|
host,
|
|
);
|
|
head.append(slot);
|
|
if (!opt.readOnly) head.append(ok); // [저장] = 머리 [닫기] 왼쪽
|
|
},
|
|
});
|
|
}
|