feat(748-1): 단가산출 상세 줄 모달 — 제목 오른쪽 연결 키 · [적용] [삭제] [닫기] 차례 · 요소 단가 정보 칸 걷음

- 제목 오른쪽 (연결 키 · 예 보통인부 (LB000002)) — 요소 · 고른 품목 · 부른 로직 키
- [적용] · [삭제](옛 빼기 이름 바꿈)를 모달 머리 [닫기] 왼쪽으로 · [기초 빼기] · 별도계상은 고침 칸 아래 그대로
- 고침 칸이 있는 모달은 요소 단가 정보 칸(이름 · 키 · 값 · 출처 · 마스터 보기) 걷음 — 자재품목 물가 넷 · 후보 목록 · 한 줄 풀이는 그대로
- M01 단가산출 기초 상세 모달도 같은 꼴([적용] [닫기])
- 낡은 시험 고침: test_722 모달 edit 인자(close, head)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-02 22:10:18 +09:00
co-authored by Claude Sonnet 5.5
parent 94e330a5f3
commit 6af23d42e1
7 changed files with 53 additions and 20 deletions
@@ -92,8 +92,8 @@ export interface ModalTarget {
usedSlot?: string;
/** 있으면 후보 줄을 눌러 고름(시험 입력 · 저장 안 함) — 고르면 모달 닫힘 */
onPick?: (item: { ref: string; label: string }) => void;
/** 722-2 그 자리 고침 칸(맨 위) — 받는 것 = 모달 닫기 */
edit?: (close: () => void) => HTMLElement;
/** 722-2 그 자리 고침 칸(맨 위) — 받는 것 = 모달 닫기 · 머리 단추 자리(닫기 왼쪽 · 748-1) */
edit?: (close: () => void, head: HTMLElement) => HTMLElement;
}
/** 수량 칸 표 알약 하나가 여는 자료 — 서버 조각 `참조`(키 · 행 · 열)가 곧 찾은 자리 */
@@ -287,7 +287,10 @@ function priceView(ref: string, brief: ElementBrief, usedSlot?: string): HTMLEle
}
/** 모달 겉틀(배경·닫기·포커스) — 몸은 `mount` 이 채움(닫기 함수도 받음) */
function openDialog(title: string, mount: (body: HTMLElement, close: () => void) => void): void {
function openDialog(
title: string,
mount: (body: HTMLElement, close: () => void, head: HTMLElement) => void,
): void {
openModal({
title,
closeLabel: tl("Modal_Close"),
@@ -428,7 +431,7 @@ function pickedOf(cond: PickCond, values: Record<string, string>, fallback: stri
/** 줄을 눌렀을 때 — 그 줄의 단가(재료·인력·기계 또는 다른 로직)를 바로 보임(알약 없이) ·
* 재료 고르기 줄(`cond`)은 잡힌 품목 단가 + 후보 목록 */
export function openMaterialModal(target: ModalTarget): void {
openDialog(target.title, (body, close) => {
openDialog(target.title, (body, close, head) => {
const cond = target.cond;
const element = target.element ?? "";
const base = element.split(".")[0];
@@ -445,6 +448,15 @@ export function openMaterialModal(target: ModalTarget): void {
? target.unitRef || pickedOf(cond, target.values, known?.ref ?? "")
: element;
const logicKey = /^로직\((\w+)/.exec(element)?.[1] ?? "";
// 748-1 연결 키 = 제목 오른쪽 (괄호) · 고침 칸이 있으면 요소 단가 정보 칸은 없음(키 · 값은 제목 · 단가 칸에)
const key = logicKey || (cond ? priceRef : element.includes("{") ? target.unitRef : base);
if (key)
body
.closest(".ui-modal")
?.querySelector("h3")
?.append(el("span", { className: "m01lab__modalkey", text: ` (${key})` }));
const info = (ref: string, brief: ElementBrief, slot?: string): HTMLElement =>
target.edit && !ref.startsWith("MT") ? el("span") : priceView(ref, brief, slot);
const sum = el("p", { className: "m01lab__sum" });
const setSum = (price: ElementBrief | null | undefined): void => {
const unit = price ? `${tl("Modal_UnitPrice")} ${formatPrice(price.값)} × ` : "";
@@ -457,9 +469,7 @@ export function openMaterialModal(target: ModalTarget): void {
/** `prices` 에 있으면 그대로 · 없으면 키로 찾아 옴 — 못 찾으면 「관리자 값 미확정」 */
const lookup = (ref: string, label: string): void => {
const show = (brief: ElementBrief | null): void => {
rate.replaceChildren(
brief ? priceView(ref, brief) : muted(`${label} — ${tl("Pill_NoValue")}`),
);
rate.replaceChildren(brief ? info(ref, brief) : muted(`${label} — ${tl("Pill_NoValue")}`));
setSum(brief);
};
rate.append(muted("…"));
@@ -471,7 +481,7 @@ export function openMaterialModal(target: ModalTarget): void {
.catch(() => show(null));
};
if (known && priceRef) {
rate.append(priceView(priceRef, known, target.usedSlot));
rate.append(info(priceRef, known, target.usedSlot));
} else if (logicKey) {
rate.append(
el("p", {
@@ -505,7 +515,7 @@ export function openMaterialModal(target: ModalTarget): void {
lookup(element, element); // 더한 줄(M02) 등 `prices` 에 없는 요소 — 키로 찾아 옴(725-4)
}
body.replaceChildren(
...(target.edit ? [target.edit(close)] : []),
...(target.edit ? [target.edit(close, head)] : []),
rate,
...(cond || target.kind === "재료" ? [candidateView(target, priceRef, close)] : []),
muted(tl("Modal_Sum")),