From 6541b6583488e2e5fbff59336de5f1658f82eeb2 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sun, 4 Oct 2026 22:08:06 +0900 Subject: [PATCH 1/2] =?UTF-8?q?fix(M02):=20=EA=B5=AC=EC=A1=B0=EB=AC=BC=20?= =?UTF-8?q?=EC=A1=B0=ED=95=A9=20=ED=91=9C=20=E3=80=8C=EC=9D=BC=EC=9C=84?= =?UTF-8?q?=EB=8C=80=EA=B0=80=E3=80=8D=20=EC=A4=84=20=3D=20=EA=B7=B8=20?= =?UTF-8?q?=EC=97=B4=20=EC=9D=BC=EC=9C=84=EB=8C=80=EA=B0=80=EB=B2=88(SU)?= =?UTF-8?q?=20=E2=80=94=20=EC=98=9B=20=EC=A1=B0=ED=95=A9=20=ED=82=A4(UA)?= =?UTF-8?q?=20=EC=B9=B8=20=EC=95=88=20=EC=9D=BD=EC=9D=8C=20=C2=B7=20?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=EA=B8=80=20=EC=A4=84(=EB=8F=84=EB=A9=B4?= =?UTF-8?q?=20=C2=B7=20=EB=A9=B4=EC=A0=81=EC=82=B0=EC=B6=9C=EA=B3=BC=20?= =?UTF-8?q?=EA=B0=99=EC=9D=8C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 표 부품: 일위대가 줄도 번호 글 줄(손으로 못 침 · laborLabel) · 조회 후 선택이 열 칸을 안 고침 - 누르면 다른 줄과 같은 길(일위대가 복사해 가져오기) · 열의 옛 「일위대가」 값은 파일에 그대로 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NPJGtfXo6GMbcpiboQC4uK --- .../M02_MasterTemplete_UI_Main.ts | 1 + resources/tester/test_sheet_drawing_select.py | 16 +++++++++++++++ ui_template/sheet/ui_template_sheet.ts | 15 ++++++-------- ui_template/sheet/ui_template_sheet_ops.ts | 6 +++--- ui_template/sheet/ui_template_sheet_render.ts | 20 ++++++++++--------- 5 files changed, 37 insertions(+), 21 deletions(-) diff --git a/M02_MasterTemplete/M02_MasterTemplete_UI_Main.ts b/M02_MasterTemplete/M02_MasterTemplete_UI_Main.ts index 450fc31a7..d70207bdb 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_UI_Main.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_UI_Main.ts @@ -329,6 +329,7 @@ export function createMain( onChange: () => (dirty = !readOnly), ...(struct ? { + laborLabel: (colId: string) => struct.numbers()[colId]?.일위대가번 ?? "", drawingLabel: (colId: string) => struct.numbers()[colId]?.도번 ?? "", areaLabel: (colId: string) => struct.numbers()[colId]?.면적번 ?? "", combo: { diff --git a/resources/tester/test_sheet_drawing_select.py b/resources/tester/test_sheet_drawing_select.py index 2909841c4..133f770b1 100644 --- a/resources/tester/test_sheet_drawing_select.py +++ b/resources/tester/test_sheet_drawing_select.py @@ -8,6 +8,7 @@ master 만 · 단위 줄 바로 아래 차례(키보드) · project 에는 없 from __future__ import annotations import json +import re import shutil import subprocess from pathlib import Path @@ -70,3 +71,18 @@ def test_text_column_pick_rows_blank(): assert _run()["blank"] == [ [True, False], [True, False], [True, False], [False, False], [False, False], ] # fmt: skip + + +def test_labor_row_shows_su_number(): + """마스터 일위대가 줄 = 그 열 일위대가번(SU) — 열의 옛 `일위대가` 칸(UA 키)은 안 읽음.""" + ops = OPS.read_text(encoding="utf-8") + assert "REF_ROWS: readonly string[] = [KEY_PRICE, KEY_DRAWING, KEY_AREA]" in ops + render = (OPS.parent / "ui_template_sheet_render.ts").read_text(encoding="utf-8") + assert re.search(r"key === KEY_PRICE\s*\?\s*state\.laborLabel", render) + assert "col.일위대가" not in render + sheet = (OPS.parent / "ui_template_sheet.ts").read_text(encoding="utf-8") + assert "col.일위대가" not in sheet # 읽기 · 쓰기 · 조회 후 선택 모두 안 건드림 + main = (ROOT / "M02_MasterTemplete" / "M02_MasterTemplete_UI_Main.ts").read_text( + encoding="utf-8" + ) + assert 'laborLabel: (colId: string) => struct.numbers()[colId]?.일위대가번 ?? ""' in main diff --git a/ui_template/sheet/ui_template_sheet.ts b/ui_template/sheet/ui_template_sheet.ts index 35372ff8a..b8f38ebe6 100644 --- a/ui_template/sheet/ui_template_sheet.ts +++ b/ui_template/sheet/ui_template_sheet.ts @@ -25,7 +25,6 @@ import { addLevel, deleteColumn, isBlankPick, - KEY_PRICE, KEY_UNIT, navRows, PICK_ROWS, @@ -54,6 +53,8 @@ export interface SheetOptions { /** master — 열 더하기 · 지우기 · 머리 · 층 이름 고치기 없음(열 · 머리는 나무가 정함) */ lockHead?: boolean; onChange?: (doc: SheetDoc) => void; + /** 일위대가 줄 칸 글(일위대가번 SU · 열 id) — `drawingLabel` 과 같음(열의 옛 `일위대가` 칸은 안 읽음). */ + laborLabel?: (colId: string) => string; /** 도면 줄 칸 글(마스터 첫 쪽 · 열 id) — 없거나 빈 글이면 「없음」 · 글이 바뀌면 `recalc()` 로 다시 그림. */ drawingLabel?: (colId: string) => string; /** 면적산출 줄 칸 글(번호) — `drawingLabel` 과 같음. */ @@ -61,8 +62,7 @@ export interface SheetOptions { /** 칸을 고름 — 누르기 · 방향키 · Tab. */ onSelect?: (sel: SheetSelection) => void; /** 조회 후 선택 — master 일위대가 · 도면 · 면적산출 칸을 두 번 누르기 · Enter · F2 로 부름. - * 글을 돌려주면 일위대가 칸에 넣음 · 번호 글 줄은 부른 쪽이 가져온 뒤 `recalc()`. - * 없으면 일위대가는 글로 고침 · 번호 글 줄은 읽기만. */ + * 번호 글 줄은 부른 쪽이 가져온 뒤 `recalc()` · 없으면 읽기만. */ onPick?: (sel: SheetSelection) => Promise | void; /** 구조물 조합 표(PLAN 31-1) — 단위 줄 · 안내 글 없음 · 열 폭 자동 · 고른 열은 가장 깊은 이름 칸을 밝힘 · * `onHeadEdit` 를 주면 이름 칸을 두 번 누르기 · F2 · Enter 로 고쳐 부른 쪽에 넘김(표 문서는 안 고침 — @@ -87,6 +87,7 @@ export function createSheet(host: HTMLElement, input: SheetDoc, opts: SheetOptio mode: opts.mode, result: { 계산: {}, 합계: {}, 오류: [] }, sel: null, + laborLabel: opts.laborLabel, drawingLabel: opts.drawingLabel, areaLabel: opts.areaLabel, pick: !!opts.onPick, @@ -236,7 +237,6 @@ export function createSheet(host: HTMLElement, input: SheetDoc, opts: SheetOptio // ── 칸 고치기 ───────────────────────────────────────────────────── const rawText = (key: string, col: SheetColumn): string => { if (key === KEY_UNIT) return col.단위 ?? ""; - if (key === KEY_PRICE) return col.일위대가 ?? ""; if (REF_ROWS.includes(key)) return refText(state, key, col.id); const row = state.doc.줄.find((r) => `d:${r.id}` === key); const formula = row?.식?.[col.id] ?? col.식; @@ -248,15 +248,12 @@ export function createSheet(host: HTMLElement, input: SheetDoc, opts: SheetOptio if (ro || REF_ROWS.includes(key)) return; // 읽기만 const value = text.trim(); if (key === KEY_UNIT) col.단위 = value || null; - else if (key === KEY_PRICE) col.일위대가 = value || null; else setCell(state.doc, key.slice(2), col, text); changed(); }; - /** 조회 후 선택 — 부른 쪽이 고른 글을 돌려주면 일위대가 칸에 넣음(그 사이 문서가 바뀌었으면 새 열에) */ + /** 조회 후 선택 — 부른 쪽이 가져옴(번호 글 줄 · 문서는 안 고침) */ const pick = async (key: string, colId: string): Promise => { - const got = await opts.onPick?.(selectionOf(state.doc, key, colId)); - const col = colOf(colId); - if (key === KEY_PRICE && typeof got === "string" && col) write(key, col, got); + await opts.onPick?.(selectionOf(state.doc, key, colId)); root.focus({ preventScroll: true }); }; /** 칸 고치기 — 두 번 누르기 = 글 전부 고름 · 바로 치기(initial) = 그 글자로 바꿈 · Enter · F2(atEnd) = 커서를 글 끝에 */ diff --git a/ui_template/sheet/ui_template_sheet_ops.ts b/ui_template/sheet/ui_template_sheet_ops.ts index a3d807da6..0f0a51ea2 100644 --- a/ui_template/sheet/ui_template_sheet_ops.ts +++ b/ui_template/sheet/ui_template_sheet_ops.ts @@ -22,11 +22,11 @@ export const KEY_UNIT = "s:unit"; export const KEY_PRICE = "s:price"; export const KEY_DRAWING = "s:drawing"; export const KEY_AREA = "s:area"; -/** 마스터 전용 줄(첫 쪽 단위 줄 아래 차례) — 도면 · 면적산출 줄은 손으로 못 침(번호 글). */ +/** 마스터 전용 줄(첫 쪽 단위 줄 아래 차례) — 일위대가 · 도면 · 면적산출 줄은 손으로 못 침(번호 글). */ export const MASTER_ROWS = [KEY_PRICE, KEY_DRAWING, KEY_AREA] as const; export type MasterRowKey = (typeof MASTER_ROWS)[number]; -/** 번호 글 줄 — 부른 쪽이 준 글(`refLabel`)만 보임. */ -export const REF_ROWS: readonly string[] = [KEY_DRAWING, KEY_AREA]; +/** 번호 글 줄 — 부른 쪽이 준 글(`refLabel`)만 보임(일위대가 = SU · 열의 옛 `일위대가` 칸은 안 읽음). */ +export const REF_ROWS: readonly string[] = [KEY_PRICE, KEY_DRAWING, KEY_AREA]; /** 조회 후 선택 줄 — `onPick` 을 주면 이 줄 칸은 치지 않고 고름. */ export const PICK_ROWS: readonly string[] = MASTER_ROWS; diff --git a/ui_template/sheet/ui_template_sheet_render.ts b/ui_template/sheet/ui_template_sheet_render.ts index 3b5113938..e9992bbf7 100644 --- a/ui_template/sheet/ui_template_sheet_render.ts +++ b/ui_template/sheet/ui_template_sheet_render.ts @@ -17,6 +17,7 @@ import { isBlankPick, isCalcColumn, KEY_AREA, + KEY_PRICE, KEY_UNIT, MASTER_ROWS, type MasterRowKey, @@ -38,7 +39,8 @@ export interface RenderState { mode: SheetMode; result: SheetResult; sel: { r: string; c: string } | null; - /** 도면 · 면적산출 줄 칸 글(열 id) — 없거나 빈 글이면 「없음」. */ + /** 일위대가 · 도면 · 면적산출 줄 칸 글(열 id) — 없거나 빈 글이면 「없음」. */ + laborLabel?: (colId: string) => string; drawingLabel?: (colId: string) => string; areaLabel?: (colId: string) => string; /** 조회 후 선택 줄(`PICK_ROWS`)을 고름으로 받나 — 칸 겉모양만. */ @@ -51,9 +53,13 @@ export interface RenderState { headEditable?: boolean; } -/** 도면 · 면적산출 줄 칸 글. */ +/** 일위대가 · 도면 · 면적산출 줄 칸 글(번호). */ export const refText = (state: RenderState, key: string, colId: string): string => - (key === KEY_AREA ? state.areaLabel : state.drawingLabel)?.(colId)?.trim() || st("Drawing_None"); + (key === KEY_AREA + ? state.areaLabel + : key === KEY_PRICE + ? state.laborLabel + : state.drawingLabel)?.(colId)?.trim() || st("Drawing_None"); /** 열 폭 — 저장 폭(없으면 기본) · 머리 글이 안 잘리는 최소 폭보다 좁지 않게. */ const columnWidth = (doc: SheetDoc, col: SheetColumn, mins: Map): number => @@ -68,7 +74,7 @@ function fitMeta(state: RenderState, cols: SheetColumn[], mins: Map refText(state, key, col.id))]; + const texts = REF_ROWS.map((key) => refText(state, key, col.id)); const need = Math.ceil(Math.max(...texts.map((t) => textWidth(t, font)))) + 14; mins.set(col.id, Math.max(mins.get(col.id) ?? 0, need)); } @@ -191,11 +197,7 @@ export function renderSheet(state: RenderState): HTMLElement { const tr = el("tr", { className: "ui-sheet__meta", children: [gutter("th", st(label))] }); cols.forEach((col, j) => { const blank = isBlankPick(key, col); - const text = blank - ? "" - : REF_ROWS.includes(key) - ? refText(state, key, col.id) - : (col.일위대가 ?? ""); + const text = blank ? "" : refText(state, key, col.id); // 마스터 줄 = 모두 번호 글 const td = bodyCell(key, col, j, text); td.classList.remove("is-num"); if (state.pick && PICK_ROWS.includes(key) && !blank) td.classList.add("is-pick"); From 7cdc7b08da5fd5a61f4b2af7265a72557f690b52 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sun, 4 Oct 2026 22:18:59 +0900 Subject: [PATCH 2/2] =?UTF-8?q?chore(plan):=2040=EC=9E=A5=20M02=20?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=EB=AC=BC=20=EC=A1=B0=ED=95=A9=20=EA=B0=9C?= =?UTF-8?q?=ED=8E=B8=202=EC=B0=A8=20=E2=80=94=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9E=90=20=EC=9A=94=EA=B5=AC=20=EC=9B=90=EB=AC=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01FJqWQpoB1RE7hiaQ2wH8qV --- .../M02_구조물조합개편/사용자요구_1004_2.md | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 docs/raw/M02_구조물조합개편/사용자요구_1004_2.md diff --git a/docs/raw/M02_구조물조합개편/사용자요구_1004_2.md b/docs/raw/M02_구조물조합개편/사용자요구_1004_2.md new file mode 100644 index 000000000..30d4b5a29 --- /dev/null +++ b/docs/raw/M02_구조물조합개편/사용자요구_1004_2.md @@ -0,0 +1,21 @@ +# M02 구조물 조합 개편 2차 — 사용자 요구 (2026-10-04 밤 · 원문 그대로) + +구조물 옵션보이기는 확인했음. 무시할것. + +다음 수정내용 + 1. 일위대가 상세 + - 담은 로직 이라는 명칭을 단가산출 로직 으로 변경 + - 로직들을 선택하면 나오는 모달 창에서 드롭다운 메뉴에 자동값 xx 항목이 나옴. 있을 필요가 있는지 확인 + - 결과 단위의 좌측에 수량 산출 열을 추가하고 결과단위 당 기준 수량을 넣어주고 아래에는 수량산출의 값을 수기입력이나 엑셀모달을 보여줘서 선택할수 있게 반영. + - 로직의 로직 추가, 올리기 내리기 빼기 버튼의 양식은 알약 양식이이 아니라 버튼양식으로 변경, + - 자재 추가도 동일하게 버튼 으로 + 2. 구조물 조합 상세 + - 일위대가 미리보기에서 복사해 가져오기를 가져오기로 변경, 일위대가 수정을 수정으로 이름변경 + - 도면정보에서 도면수정을 수정으로 이름 변경 + - 일위대가 미리보기에서 항목/수량산출/단위/노무비/재료비/경비/합계로 두줄을 하나의 항목 형태로 변경하고 자세한 내용은 상세내용에서 확인하게 변경(위내용 참고) + - 구조물 조합 제목 행에 있는 새항목의 버튼 양식을 알약에서 버튼으로 변경 + - 도면정보/일위대가/면적산출 컨테이너의 구조를 3열 구조로 변경 + - 도면정보/일위대가/면적산출 컨테이너 별로 삭제도 있어야하는게 아닌지. + 3. 면적산출 상세 + - 알약지정은 현재 방법 + 그래그 & 드랍 방식을 추가 + - 지정된 알약정보를 색상 자동지정 및 지정된 셀은 색상으로 구분을 할수 있게 반영, 반짝임은 유지