feat(804-7): 줄 쌓기 「수식」 탭 수정 2차(요구 687 · 688)

- 로직 삭제 = 서버 정본에서 바로(로직 줄 + 수식 문서 · `/logic/remove`) → 로직 목록 · 부르는 곳(다른 수식 문서 · 일위대가 조합 · M02 단가산출 바탕)이 있으면 확인 창 전에 막고 까닭(`/logic/users`)
- 머리 단추 다섯 — [신규 로직] · [로직 복사/생성] · [로직 삭제] · [되돌림] · [저장] · [저장] 한 번 = 기본정보 + 수식 · [되돌림] = 둘 다 저장 뒤 상태 · 줄 쌓기 [고침 버림] · [수식 저장]은 M01 에서 걷음(M02 사본은 그대로)
- 합계 상자 화면 아래 고정 · 원문 손잡이 브라우저 오른쪽 끝
- 왼쪽: 그룹 머리 한 줄(접어도 [+] · X 자리 그대로) · 「비우면 번호 없음」 · 가나다 체크 걷음 · 금액 줄 한 줄 배치 · 이름 단추 = 그 이름 찾은 찾기 창 · 수량 기호 = 값 + 출처 꼬리표 · [+] 메뉴(단가산출 · 율 · 식 · 근거 글 · 틀 찾기 걷음) · 식 글 눌러 그 자리 고침([식 고치기] 걷음) · 단위 = 공용 단위 고르기(친 글도 씀) · 만약 안 탄 갈래 숨김
- 오른쪽 기계 줄: 비목 수 링크 걷음 · 제목 줄 누름 = 시간당 경비 펼침 · 펼친 제목 보통 글
- 요소 키가 기호 이름으로 샌 것(`k_EQ0006`) — 변환기가 자원 이름으로(`k_하베스터`) · 수식 문서 다시 빌드(10 파일 · 78 기호 · 금액 같음)
- 시험 페이지 `tools/line-stack-test` · main.py 마운트 · vite 프록시 · M01 왼쪽 「시험: 줄 쌓기」 걷음
- 단위 표준 표 읽기 = 로그인만(M02 일반 사용자 단위 목록)
- 시험: test_804_7 새로 · 804_2 · 804_4 이음 글 고침(createStackScreen 줄 나뉨) · 552_3 여는 단추 기본 ⋯ 꼴로 고침(부르는 쪽이 [+] 로 바꿀 수 있음)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-08 13:33:36 +09:00
co-authored by Claude Opus 5.5
parent 89292a75df
commit abe722f378
43 changed files with 1025 additions and 2061 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
"""단위 · 금액 표준 표 읽기 — `GET /api/m02/df/standards` → {unit, money, saved}.
부르는 곳 = 단위 고르기(`M02_DF_UnitPick.ts`) · 끝수 표준(`M02_DF_Round.ts`).
표 = `_standard/` 파일(git 이 따라감) · 권한 = 시스템 관리자(`main.py` 한 줄).
표 = `_standard/` 파일(git 이 따라감) · 권한 = 로그인만(읽기만 · `main.py` 한 줄 · M02 줄 쌓기 단위 고르기).
"""
from __future__ import annotations
+14 -2
View File
@@ -1,6 +1,6 @@
/* =============================================================================
* M02_DF_UnitPick.ts
* 단가산출 흐름도 공용 단위 고르기 — 치면 걸러진 목록 · 고르기만(손글 안 받음) · 「-」 = 없음
* 단가산출 흐름도 공용 단위 고르기 — 치면 걸러진 목록 · 고르기만(손글 안 받음 · `free` = 목록에 없는 친 글도 씀) · 「-」 = 없음
* 목록 = 단위 표준(산림품셈 1-2-2) 단위 + 부른 쪽이 주는 마스터 결과단위 등 + 원
* 표준 표 읽는 길은 `setUnitSource` 로 바꿔 끼움(기본 = 서버 /standards) — 다른 판도 이것을 씀
* 글 [한, 영] 짝은 아래 TEXT
@@ -8,6 +8,7 @@
import { currentLanguageIndex } from "@ui/ui_template_locale";
import { createButton, el } from "@ui/ui_template_elements";
import type { UiSize } from "@ui/ui_template_button";
import { cfStandards } from "./M02_DF_Api";
import "./M02_DF_UnitPick.css";
import { createEmpty } from "@ui/ui_template_empty";
@@ -17,6 +18,7 @@ const TEXT = {
None: ["없음", "none"],
Filter: ["단위를 쳐서 찾음", "Type to filter units"],
Empty: ["맞는 단위가 없음 — 목록에서만 고름", "No match — pick from the list only"],
Free: ["「{u}」 그대로 씀", "Use “{u}”"],
Pick: ["단위 고르기", "Pick a unit"],
} as const;
@@ -99,6 +101,10 @@ export interface UnitPickOptions {
extra?: string[];
readOnly?: boolean;
ariaLabel?: string;
/** 크기 단(줄 쌓기 24 등) — 없으면 옛 꼴 */
size?: UiSize;
/** 목록에 없는 친 글도 단위로 씀(줄 쌓기 · 804-7) */
free?: boolean;
}
/** 단위 고르기 — 단추를 누르면 걸러지는 목록 · 목록에서 고르기만 됨 */
@@ -108,6 +114,7 @@ export function createUnitPick(opts: UnitPickOptions): HTMLElement {
const open = createButton({
label: opts.value || NO_UNIT,
variant: "ghost",
size: opts.size,
disabled: opts.readOnly,
onClick: () => toggle(),
});
@@ -136,13 +143,18 @@ export function createUnitPick(opts: UnitPickOptions): HTMLElement {
const q = input.value.trim().toLowerCase();
shown = all.filter((u) => u === NO_UNIT || !q || u.toLowerCase().includes(q));
if (q && !NO_UNIT.includes(q)) shown = shown.filter((u) => u !== NO_UNIT);
const typed = input.value.trim();
if (opts.free && typed && !all.includes(typed)) shown.push(typed); // 맨 끝 「그대로 씀」 줄
at = Math.min(at, Math.max(0, shown.length - 1));
list.replaceChildren(
...(shown.length
? shown.map((u, i) => {
const b = el("button", {
className: `ui-list-row ui-sidebar-row m02-df-unitpick__item${i === at ? " is-on" : ""}${u === (opts.value || NO_UNIT) ? " is-now" : ""}`,
text: label(u),
text:
opts.free && u === input.value.trim() && !all.includes(u)
? tu("Free").replace("{u}", u)
: label(u),
attrs: { type: "button", role: "option" },
});
b.addEventListener("click", () => pick(u));