feat(804-3): 줄 쌓기 수정 1차 — 표 값 고르기 · 산출근거 들여쓰기 · 찾아 넣기 창 · 짝 강조 · 기계 이름
- 표 값(요구 678-1): 「표」 꼬리표가 글뿐이라 눌러도 안 열림 → [표 · 고른 줄 · 칸 ▾] 단추 = 품셈 표 창(값 칸 누름 = 그 줄 · 칸) 옵션에 매인 조건(폼타이규격 등) = 그 줄만 고름 · 다른 줄 막힘 + 「위 옵션에서 고름」 안내 · M02 길 표 값 글 수도 고름 - 산출근거 들여쓰기(678-2): 머리 0 · 항목 1 · 가나다 2 · 줄 = 항목 + 1 · 펼친 블록 = 그 줄 + 1(수준 = stackLevels) - 찾아 넣기(678-3): [+ 줄] 인력 · 재료 · 기계 · 다른 단가산출 = M02 [항목 추가] 창 그대로(그 종류만) · 고르면 줄 생김(닫으면 없음) 있는 줄 = [찾아 바꾸기](같은 로직 기계 = 지역 · 보정작업 넘김 그대로) · 옛 찾기 칸 + 고름 목록 · 기종 코드 고르개 걷음 빈 틀 = [찾아 넣기](돈 아닌 단가산출 결과만 — Find_Pick results) - 짝 강조(679-1): 오른쪽 줄 누름 = 왼쪽 카드 강조 + 보이는 자리로 · 왼쪽 누름 = 오른쪽 같은 줄 강조 - 기계 이름(679-2): 기계 줄 = 기종 이름 · 규격 · machine-hourly 답에 로직 키 · 원문번호 더함(기존 칸 그대로) - 모델 common_util_line_stack_pick.ts(boundConds · currentRow · pickCell · addPicked · repick · stackLevels) - 시험 test_804_2 넓힘 3(표 고름 = 값 + 출처 · 서버 셈 같음 · 찾아 넣기 넷 = 요소 키 · 서버 금액 · 다시 고름 · 수준) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
/* =============================================================================
|
||||
* common_util_line_stack_pick.ts
|
||||
* 줄 쌓기 고르기 · 수준(PLAN 804-3 · 요구 678) — 화면 · DOM · fetch 없음(시험이 Node 로 돌림)
|
||||
* 표 칸 고름 = 그 기호 연결을 표 · 칸 + 옵션에 안 매인 조건은 고른 줄 값으로(옵션에 매인 조건은 그대로 = 옵션이 줄을 고름)
|
||||
* 찾아 넣기 고름(인력 · 재료 · 기계 · 다른 단가산출) = 새 금액 줄 + 단가 기호 연결(요소 키 · 단가산출 키 + 넘길 입력)
|
||||
* 산출근거 수준 = 항목(1. 2.) 1 · 가나다 2 · 줄 = 항목 + 1 · 제목 없는 항목 줄 = 1
|
||||
* ========================================================================== */
|
||||
|
||||
import { names, 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 {
|
||||
freshId,
|
||||
isOption,
|
||||
newVar,
|
||||
nodeOf,
|
||||
type MoneyKind,
|
||||
type StackItem,
|
||||
} from "./common_util_line_stack";
|
||||
|
||||
type Row = Record<string, unknown>;
|
||||
|
||||
/** 표 조건 가운데 옵션(선택 · 입력 열쇠)에 매인 것 — {조건 칸: 옵션 열쇠들} */
|
||||
export function boundConds(doc: FormulaDoc, link: Link | undefined): Record<string, string[]> {
|
||||
const opts = new Map(
|
||||
(doc.변수 ?? []).filter(isOption).map((v) => [varId(v), String(v.열쇠)] as const),
|
||||
);
|
||||
const out: Record<string, string[]> = {};
|
||||
for (const [c, t] of Object.entries(link?.조건 ?? {})) {
|
||||
const n = nodeOf(t);
|
||||
const keys = n ? names(n).flatMap((x) => (opts.has(x) ? [opts.get(x)!] : [])) : [];
|
||||
if (keys.length) out[c] = keys;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/** 지금 조건으로 닿는 표 줄(못 찾으면 -1) */
|
||||
export function currentRow(table: DocTable, link: Link | undefined, vals: Row): number {
|
||||
const conds: Row = {};
|
||||
for (const [c, t] of Object.entries(link?.조건 ?? {})) {
|
||||
const n = nodeOf(t);
|
||||
if (n) conds[c] = evalNode(n, vals);
|
||||
}
|
||||
try {
|
||||
return table.줄.indexOf(findRow(table, conds, link?.칸));
|
||||
} catch {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/** 표 줄 조건 값 → 식 글(글 = '…' · 범위 = 안쪽 수) */
|
||||
function literal(v: unknown): string | null {
|
||||
if (typeof v === "number") return String(v);
|
||||
if (Array.isArray(v)) {
|
||||
const [lo, hi] = v as [number | null, number | null];
|
||||
if (lo !== null && hi !== null) return String((lo + hi) / 2);
|
||||
return lo !== null ? String(lo) : hi !== null ? String(hi) : null;
|
||||
}
|
||||
return v === null || v === undefined ? null : `'${String(v).replace(/'/g, "")}'`;
|
||||
}
|
||||
|
||||
/** 표 칸 고름 — 기호 연결 = 표 · 칸 · 옵션에 안 매인 조건은 고른 줄 값 · 그 기호 식 줄은 걷음 */
|
||||
export function pickCell(
|
||||
doc: FormulaDoc,
|
||||
id: string,
|
||||
table: DocTable,
|
||||
row: Row,
|
||||
col: string,
|
||||
): void {
|
||||
let v = (doc.변수 ?? []).find((x) => varId(x) === id);
|
||||
if (!v) (doc.변수 ??= []).push((v = newVar(id, { 갈래: "표" })));
|
||||
const was = v.연결?.갈래 === "표" ? v.연결 : undefined;
|
||||
const bound = boundConds(doc, was);
|
||||
const cond: Record<string, string> = {};
|
||||
for (const c of Object.keys(table.조건 ?? {})) {
|
||||
if (bound[c] || (table.조건?.[c] === "범위" && was?.조건?.[c])) cond[c] = was!.조건![c];
|
||||
else {
|
||||
const t = literal(row[c]);
|
||||
if (t !== null) cond[c] = t;
|
||||
}
|
||||
}
|
||||
v.연결 = { 갈래: "표", 표: table.키 ?? String(was?.표 ?? ""), 조건: cond, 칸: col };
|
||||
doc.식 = (doc.식 ?? []).filter((l) => l.기호 !== id);
|
||||
}
|
||||
|
||||
/** 찾아 넣기에서 고른 것 — 기계 = 시간당 경비 로직 키 + 입력 {기계: '원문번호'} */
|
||||
export interface Picked {
|
||||
키: string;
|
||||
이름: string;
|
||||
규격?: string;
|
||||
단위?: string;
|
||||
입력?: Record<string, string>;
|
||||
}
|
||||
|
||||
const SIGN: Record<string, string> = { 인력: "w", 재료: "P", 기계: "P", 단가산출: "B" };
|
||||
const COST: Record<string, string> = { 인력: "노무비", 재료: "재료비" };
|
||||
|
||||
const linkOf = (kind: MoneyKind, p: Picked): Link =>
|
||||
kind === "인력" || kind === "재료"
|
||||
? { 갈래: "요소", 키: p.키 }
|
||||
: { 갈래: "단가산출", 키: p.키, 입력: { ...(p.입력 ?? {}) } };
|
||||
|
||||
const unitOf = (kind: MoneyKind, p: Picked): string =>
|
||||
kind === "기계" ? "hr" : kind === "인력" ? "인" : (p.단위 ?? "");
|
||||
|
||||
/** 고른 것으로 새 금액 줄(수량 1) — 항목 끝에 */
|
||||
export function addPicked(doc: FormulaDoc, item: StackItem, kind: MoneyKind, p: Picked): string {
|
||||
const id = freshId(doc, SIGN[kind] ?? "B", "새");
|
||||
(doc.변수 ??= []).push(newVar(id, linkOf(kind, p), p.이름));
|
||||
const n = (doc.금액 ??= []).length;
|
||||
doc.금액.push({
|
||||
이름: p.이름,
|
||||
...(p.규격 ? { 규격: p.규격 } : {}),
|
||||
단위: unitOf(kind, p),
|
||||
비목: COST[kind] ?? "묶음",
|
||||
수량: "1",
|
||||
단가: id,
|
||||
} as never);
|
||||
item.줄.push({ 꼴: "금액", i: n });
|
||||
return id;
|
||||
}
|
||||
|
||||
/** 있는 줄 바꿔 고름 — 그 줄 단가 기호 연결 · 줄 이름 · 규격(단위 · 수량은 그대로 · 같은 로직이면 넘길 입력 그대로) */
|
||||
export function repick(doc: FormulaDoc, i: number, id: string, kind: MoneyKind, p: Picked): void {
|
||||
const v = (doc.변수 ?? []).find((x) => varId(x) === id);
|
||||
const line = (doc.금액 ?? [])[i] as { 이름?: string; 규격?: string } | undefined;
|
||||
if (!v || !line) return;
|
||||
const was = v.연결 ?? {};
|
||||
const now = linkOf(kind, p);
|
||||
if (now.갈래 === "요소" && was.지역) now.지역 = was.지역; // 지역 옵션 이음 그대로
|
||||
if (now.갈래 === "단가산출" && was.키 === now.키) now.입력 = { ...was.입력, ...now.입력 }; // 같은 로직 = 다른 넘길 입력 그대로
|
||||
v.연결 = now;
|
||||
v.이름 = p.이름;
|
||||
line.이름 = p.이름;
|
||||
if (p.규격) line.규격 = p.규격;
|
||||
else delete line.규격;
|
||||
}
|
||||
|
||||
/** 산출근거 수준 — 항목 머리 · 그 항목 줄(제목 없는 항목 = 머리 없음 · 줄 1) */
|
||||
export function stackLevels(items: StackItem[]): { item: number; line: number }[] {
|
||||
return items.map((it) => {
|
||||
if (!it.제목.trim()) return { item: 0, line: 1 };
|
||||
const item = it.가나다 ? 2 : 1;
|
||||
return { item, line: item + 1 };
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user