feat(M02): 일위대가 수량산출 모달 하나 — 지금 수량 · 손 입력 · 연결 풀기 + 면적산출 시트를 모달 안에 바로(칸 누름 = 이음) · 넣을 값 모달에서 수량 칸 뺌(43-1 · 43-2)
- 새 Labor_QtyModal.ts(로직 줄 수량산출 칸 · 자재 줄 같음 · [확인] = 손 · 고른 칸 · 풀기 · [닫기] = 그대로) · 두 단계 모달([엑셀에서 고르기]) 없앰 - AreaCellPick: 시트 부분을 mountAreaSheet 로 뗌(읽기만 · 실제 값 · 이은 칸 색 · 함께 잇기 · 바꾸기 물음 그대로) · 모달 pickAreaCell 지움 - Labor_Detail: openQty · openMat → openQtyFor · linkCell · Labor_Input: 수량 칸 · openQtyModal 지움(입력값 · 메모만) - 화면 판정 시나리오 resources/tester/ui_checks/43-1_수량산출_모달.js · 시험 test_m02_area_panel 부품 기대값 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MgChuAcyBqUkhHMTXomQC7
This commit is contained in:
@@ -19,7 +19,7 @@ import {
|
||||
type ItemLogic,
|
||||
type Layer,
|
||||
} from "./M02_MasterTemplete_Api_Fetch";
|
||||
import { linkAreaCell, pickAreaCell } from "./M02_MasterTemplete_AreaCellPick";
|
||||
import { linkAreaCell } from "./M02_MasterTemplete_AreaCellPick";
|
||||
import { settle } from "./M02_MasterTemplete_Labor_Calc";
|
||||
import {
|
||||
areaLinks,
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
normalizeLabor,
|
||||
type LaborCalc,
|
||||
type LaborDoc,
|
||||
type LaborQty,
|
||||
type LaborRow,
|
||||
} from "./M02_MasterTemplete_Labor_Api";
|
||||
import {
|
||||
@@ -35,9 +36,10 @@ import {
|
||||
diagramTitle,
|
||||
renderComboDiagram,
|
||||
} from "./M02_MasterTemplete_Labor_Diagram";
|
||||
import { openLaborInput, openQtyModal, type GoArea } from "./M02_MasterTemplete_Labor_Input";
|
||||
import { openLaborInput, type GoArea } from "./M02_MasterTemplete_Labor_Input";
|
||||
import { openQtyModal } from "./M02_MasterTemplete_Labor_QtyModal";
|
||||
import { openLogicPicker } from "./M02_MasterTemplete_Labor_Pick";
|
||||
import { logicTable, matTable } from "./M02_MasterTemplete_Labor_Rows";
|
||||
import { logicTable, matTable, qtyShort } from "./M02_MasterTemplete_Labor_Rows";
|
||||
import { tc } from "./M02_MasterTemplete_Labor_Text";
|
||||
import { keepKnown } from "./M02_Labor_Values_Model";
|
||||
import { pickMaterial } from "./M02_Material_Pick";
|
||||
@@ -174,37 +176,22 @@ export function mountLaborDetail(
|
||||
} else recalc();
|
||||
};
|
||||
|
||||
/** 이은 칸 글 「B6 = 12.5 ㎡」 — 알약이 칸에 안 이어졌으면 null */
|
||||
const linkedText = (pill: string | null | undefined, unit: string): string | null => {
|
||||
const at = pill ? links.get(pill) : undefined;
|
||||
if (!at) return null;
|
||||
return `${at.칸} = ${at.값 === null ? "—" : at.값.toLocaleString("ko-KR")}${unit ? ` ${unit}` : ""}`;
|
||||
};
|
||||
|
||||
/** [엑셀에서 고르기] — 그 열 면적산출 칸을 골라 그 알약을 이음(화면 문서 · 저장은 [저장]) · 이었으면 참 */
|
||||
const pickCell = async (pill: string | null | undefined, label: string): Promise<boolean> => {
|
||||
if (!pill || !area) {
|
||||
showToast(tc(area ? "Labor_PickWait" : "Labor_NoArea"), "error");
|
||||
/** 칸 잇기 · 풀기(`cell` null) — 화면 문서만(저장은 [저장]) · 됐으면 참 */
|
||||
const linkCell = (pill: string, cell: string | null, replace = false): boolean => {
|
||||
if (!area) {
|
||||
showToast(tc("Labor_NoArea"), "error");
|
||||
return false;
|
||||
}
|
||||
const got = await pickAreaCell({
|
||||
doc: area.doc,
|
||||
layer: opt.layer,
|
||||
column: opt.column,
|
||||
pill,
|
||||
label,
|
||||
});
|
||||
if (!got || !alive) return false;
|
||||
const info = pills.get(pill);
|
||||
const next = linkAreaCell(
|
||||
area.doc,
|
||||
pill,
|
||||
got.칸,
|
||||
cell,
|
||||
{
|
||||
...(info?.이름 ? { 이름: info.이름 } : {}),
|
||||
...(info?.단위 ? { 단위: info.단위 } : {}),
|
||||
},
|
||||
got.바꾸기 === true, // 그 칸 다른 알약 — 기본 함께 · [바꾸기]면 뺌(물음은 고르기 모달 안)
|
||||
replace, // 그 칸 다른 알약 — 기본 함께 · [바꾸기]면 뺌(물음은 시트 안)
|
||||
);
|
||||
if (!next) {
|
||||
showToast(tc("Labor_BadCell"), "error");
|
||||
@@ -217,6 +204,37 @@ export function mountLaborDetail(
|
||||
return true;
|
||||
};
|
||||
|
||||
/** 수량산출 모달(43-1) — 로직 줄 · 자재 줄 같음 · 손 · 칸 잇기 · 풀기로 `target.수량` 을 바꿈 */
|
||||
const openQtyFor = (
|
||||
title: string,
|
||||
target: { 수량: LaborQty },
|
||||
line: { 수량?: number | null; 수량출처?: string; 알약?: string | null } | undefined,
|
||||
unit: string,
|
||||
): void => {
|
||||
const pill = line?.알약 ?? null;
|
||||
openQtyModal({
|
||||
title,
|
||||
now: qtyShort(line, unit, cells()),
|
||||
linked: !!pill && links.has(pill),
|
||||
hand: target.수량?.손 ?? null,
|
||||
unit,
|
||||
readOnly: !edit,
|
||||
sheet: { doc: area?.doc ?? null, layer: opt.layer, column: opt.column, pill },
|
||||
goArea: opt.goArea,
|
||||
done: (r) => {
|
||||
if (r.kind === "keep") return;
|
||||
if (r.kind === "hand") target.수량 = { 손: r.손 };
|
||||
else if (r.kind === "follow") target.수량 = null;
|
||||
else {
|
||||
const cell = r.kind === "cell" ? r.pick.칸 : null;
|
||||
if (!pill || !linkCell(pill, cell, r.kind === "cell" && r.pick.바꾸기 === true)) return;
|
||||
target.수량 = null; // 칸을 따름
|
||||
}
|
||||
changed(true);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/** 아직 안 받은 로직 입력 목록만 받음 */
|
||||
const loadLogics = async (): Promise<void> => {
|
||||
const want = cur.담은로직.map((r) => r.로직).filter((k) => !logics.has(k));
|
||||
@@ -247,85 +265,38 @@ export function mountLaborDetail(
|
||||
function openRow(i: number): void {
|
||||
const row: LaborRow = cur.담은로직[i];
|
||||
const logic = logics.get(row.로직) ?? null;
|
||||
const line = lineOf(row.로직);
|
||||
openLaborInput({
|
||||
title: `${row.로직} ${logic?.이름 ?? ""}`.trim(),
|
||||
logic,
|
||||
values: row.입력값,
|
||||
memo: row.메모,
|
||||
seat: {
|
||||
qty: row.수량,
|
||||
linked: linkedText(line?.알약, line?.분모 ?? ""),
|
||||
unit: line?.분모 ?? "",
|
||||
pick: () => pickRow(i),
|
||||
},
|
||||
readOnly: !edit,
|
||||
goArea: opt.goArea,
|
||||
done: (next) => {
|
||||
const values = logic ? keepKnown(logic.입력, next.입력값).kept : next.입력값;
|
||||
const qtyOnly = JSON.stringify(values) === JSON.stringify(row.입력값);
|
||||
const same = JSON.stringify(values) === JSON.stringify(row.입력값);
|
||||
row.입력값 = values;
|
||||
row.메모 = next.메모;
|
||||
row.수량 = next.수량;
|
||||
changed(qtyOnly);
|
||||
changed(same); // 입력값이 그대로면(메모만) 서버 안 부름
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/** 그 줄 알약을 칸에 이음 — 이으면 손 수량은 지움(칸을 따름) */
|
||||
async function pickRow(i: number): Promise<boolean> {
|
||||
const row = cur.담은로직[i];
|
||||
const logic = logics.get(row.로직);
|
||||
if (!(await pickCell(lineOf(row.로직)?.알약, logic?.이름 ?? row.로직))) return false;
|
||||
row.수량 = null;
|
||||
changed(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** 수량산출 칸 누름 — 손 입력 · 엑셀에서 고르기 */
|
||||
/** 수량산출 칸 누름 — 로직 줄 */
|
||||
function openQty(i: number): void {
|
||||
const row = cur.담은로직[i];
|
||||
const line = lineOf(row.로직);
|
||||
openQtyModal(
|
||||
openQtyFor(
|
||||
`${row.로직} ${logics.get(row.로직)?.이름 ?? ""}`.trim(),
|
||||
{
|
||||
qty: row.수량,
|
||||
linked: linkedText(line?.알약, line?.분모 ?? ""),
|
||||
unit: line?.분모 ?? "",
|
||||
pick: () => pickRow(i),
|
||||
},
|
||||
!edit,
|
||||
opt.goArea,
|
||||
(qty) => {
|
||||
row.수량 = qty;
|
||||
changed(true);
|
||||
},
|
||||
row,
|
||||
line,
|
||||
line?.분모 ?? "",
|
||||
);
|
||||
}
|
||||
|
||||
/** 자재 줄 누름 — 수량산출 모달 */
|
||||
function openMat(i: number): void {
|
||||
const mat = cur.자재[i];
|
||||
const line = matOf(mat.코드);
|
||||
openQtyModal(
|
||||
`${mat.코드} ${mat.이름 ?? ""}`.trim(),
|
||||
{
|
||||
qty: mat.수량,
|
||||
linked: linkedText(line?.알약, mat.단위 ?? ""),
|
||||
unit: mat.단위 ?? "",
|
||||
pick: async () => {
|
||||
if (!(await pickCell(line?.알약, mat.이름 ?? mat.코드))) return false;
|
||||
mat.수량 = null;
|
||||
changed(true);
|
||||
return true;
|
||||
},
|
||||
},
|
||||
!edit,
|
||||
opt.goArea,
|
||||
(qty) => {
|
||||
mat.수량 = qty;
|
||||
changed(true);
|
||||
},
|
||||
);
|
||||
openQtyFor(`${mat.코드} ${mat.이름 ?? ""}`.trim(), mat, matOf(mat.코드), mat.단위 ?? "");
|
||||
}
|
||||
|
||||
const addLogic = createButton({
|
||||
|
||||
Reference in New Issue
Block a user