feat(804-5): 줄 쌓기 표 값 이름 · 산출근거 고정 · 그룹 글 · 짝 강조 채우기 · 틀로 저장 걷음(요구 683~685)

- 표 값 기호 화면 이름 = 고른 줄 이름(Q_수량2^표 → Q_특별인부 · 겹치면 2 · 3) — 왼쪽 · 산출근거 같음 · 칸 글은 화면 이름 ↔ 기호로 오감(문서 기호 그대로)
- 단위 「줄 따름」 안 보임 · 표 값만 든 묶음 머리 = 「표 값」 · [식으로 풀기] → 「식 고치기」
- 산출근거 = 넓을 때 화면 고정 + 자기 스크롤(높이 = 스크롤 영역) · 나란히 놓는 탭 폭 1240 → 1040
- 글 「항목」 → 「그룹」(그룹 이름 · + 그룹 · 그룹 지움)
- 짝 강조 = 왼쪽 카드 · 그룹 머리 · 오른쪽 줄 모두 같은 바탕 채우기(테두리 걷음)
- [틀로 저장] · 내 틀 목록(브라우저 저장) 걷음 · 공식 틀 고르기 = M01 단가산출 결과만
- 시험 test_804_4 표 값 화면 이름 1,518 전수(겹침 없음 · 식 글 왕복 같음) 더함

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-08 08:42:41 +09:00
co-authored by Claude Opus 5.5
parent af576e5a7a
commit 6f678fb91b
10 changed files with 219 additions and 114 deletions
+55 -1
View File
@@ -6,7 +6,15 @@
* 산출근거 수준 = 항목(1. 2.) 1 · 가나다 2 · 줄 = 항목 + 1 · 제목 없는 항목 줄 = 1
* ========================================================================== */
import { names, varId, type FormulaDoc, type Link } from "./common_util_formula_doc";
import {
names,
renameText,
splitId,
symId,
varId,
type FormulaDoc,
type Link,
} from "./common_util_formula_doc";
import { findRow, type DocTable } from "./common_util_formula_doc_table";
import { evalNode } from "./common_util_formula_doc_view";
import {
@@ -14,6 +22,7 @@ import {
isOption,
newVar,
nodeOf,
symbols,
type MoneyKind,
type StackItem,
} from "./common_util_line_stack";
@@ -144,3 +153,48 @@ export function stackLevels(items: StackItem[]): { item: number; line: number }[
return { item, line: item + 1 };
});
}
/** 표 값 기호 화면 이름(804-5 · 요구 683) — 「Q_수량2^표」 → 「Q_작업반장」(고른 줄 글 조건 첫 낱말) · 글 조건 없으면 「^표」만 뗌
* · 「^표」 기호만 · 겹치면 2 · 3 · 문서 기호는 그대로(화면만 · 칸 글은 `aliasText` 로 오감) */
export function tableAlias(doc: FormulaDoc): Record<string, string> {
const taken = new Set([
...symbols(doc),
...(doc.금액 ?? []).map((l) => String(l.기호 ?? "")),
"노무비",
"재료비",
"경비",
]);
const out: Record<string, string> = {};
for (const v of doc.변수 ?? []) {
const link = v.연결;
if (link?.갈래 !== "표") continue;
const id = varId(v);
const [s, lo, up] = splitId(id);
const said = Object.values(link.조건 ?? {})
.map(String)
.find((t) => /^'.+'$/.test(t));
const label = said?.slice(1, -1).split(/[\s(,·/]/)[0] ?? "";
if (up !== "표") continue; // 「^표」 꼬리 기호만(r_인력 등 손 이름은 그대로)
const base = label || lo.replace(/\d+$/, "") || lo; // 번호 꼬리 뗌(겹치면 다시 2 · 3)
for (let n = 1; n < 100; n++) {
const now = symId(s, n > 1 ? `${base}${n}` : base);
const node = nodeOf(now);
if (taken.has(now) || node?.[0] !== "var" || node[1] !== now) continue;
taken.add(now);
out[id] = now;
break;
}
}
return out;
}
/** 식 글의 기호를 화면 이름으로(또는 `back` = 화면 이름을 기호로) */
export function aliasText(text: string, alias: Record<string, string>, back = false): string {
let t = text;
for (const [id, now] of Object.entries(alias))
t = String(back ? renameText(t, now, id) : renameText(t, id, now));
return t;
}
/** 표 칸 단위 「줄 따름」(줄마다 다른 단위) = 화면에 안 보임(요구 683) */
export const shownUnit = (u: unknown): string => (u === "줄 따름" ? "" : String(u ?? ""));