feat(737): 단가산출 상세 옵션 6 열 격자 · 합계 한 줄 바닥 고정 · 텍스트 수식 비목 상자 · 두 보기 같은 제목
- 옵션 칸 = 상자 폭 기준 1 → 2 → 3 → 4 → 6 열(calc_sheet optionGrid · container query) · 공통 옵션 따로 상자 걷고 모든 상자 맨 위 옵션 칸에 같이 - 맨 아래 노무비 · 재료비 · 경비 · 계 = 한 줄 4 칸 · 화면(모달) 바닥 sticky · 메인 칸 overflow clip(굴림 칸이면 안 붙음) - 텍스트 수식 = 비목 구분 상자 · 상자 위 옵션 칸 · 줄마다 붙던 옵션 꼬리표 걷음 - 두 보기 상자 제목 「단가산출 상세」 · 「지금 입력」 꼬리표 걷음 - 신규 지킴 시험 test_737_option_grid_foot Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,13 +3,15 @@
|
||||
* 「텍스트 수식」 컨테이너 — 서버(`POST /text`)가 준 줄을 그대로 찍음(화면에서 다시 계산하지 않음)
|
||||
* 두 칸 — 왼쪽 = 변수 이름 식(`이름글`) · 오른쪽 = 값이 들어간 식(`글`) · 줄은 `짝` 번호로 같은 높이
|
||||
* 한쪽에 올리거나 누르면 같은 줄이 밝아짐 · 비목 제목 · 소계 · 끝수 한 마디 · 까닭 있는 줄은 옅게
|
||||
* 714-3 — 줄이 쓰는 입력(서버 `쓰는입력` · 없으면 표 찾기 행 값 · 이름 조각)을 오른쪽 칸 아래 작은 단추로 · 누르면 그 자리에 고르기/글 칸 →
|
||||
* 737-2 — 비목(노무비 · 재료비 · 경비)마다 구분 상자 · 맨 위 옵션 고르기(그 상자 줄이 쓰는 입력 · 서버 `쓰는입력` · 없으면 표 찾기 행 값 · 이름 조각 + 어느 줄도 안 쓰는 공통 입력 · 6 → 4 → 3 → 2 → 1 열) →
|
||||
* 바꾸면 `onInput`(시험 계산 입력 고침 + 다시 셈) · 정본은 안 바뀜(요구서 4-1 확정 8)
|
||||
* ========================================================================== */
|
||||
|
||||
import { createButton, el } from "@ui/ui_template_elements";
|
||||
import { createCard } from "@ui/ui_template_card";
|
||||
import { createInputField, createSelectField } from "@ui/ui_template_input";
|
||||
import type { LogicInput, TextAnswer, TextLine } from "./M01_MasterData_UI_Logic_Api";
|
||||
import { optionGrid, varRow } from "../A00_Common/calc_sheet";
|
||||
import { formatMoney } from "./M01_MasterData_UI_Logic_Money";
|
||||
import { safeReason, tx } from "./M01_MasterData_UI_Logic_Text";
|
||||
import { tl } from "./M01_MasterData_UI_LogicLab_Text";
|
||||
@@ -118,43 +120,40 @@ export function buildFormula(host: HTMLElement, ctx: FormulaContext): void {
|
||||
);
|
||||
return;
|
||||
}
|
||||
const heads = el("div", {
|
||||
className: "m01lab-fx__head",
|
||||
children: [
|
||||
el("strong", { text: tl("Formula_Left") }),
|
||||
el("strong", { text: tl("Formula_Right") }),
|
||||
],
|
||||
});
|
||||
const heads = (): HTMLElement =>
|
||||
el("div", {
|
||||
className: "m01lab-fx__head",
|
||||
children: [
|
||||
el("strong", { text: tl("Formula_Left") }),
|
||||
el("strong", { text: tl("Formula_Right") }),
|
||||
],
|
||||
});
|
||||
const rows: HTMLElement[] = [];
|
||||
const specs = ctx.onInput ? (ctx.inputs ?? []) : [];
|
||||
const vars = (line: TextLine): HTMLElement[] => {
|
||||
const names = specs.map((x) => x.이름);
|
||||
const seen = new Map(usedInputs(line, names));
|
||||
// 서버 「쓰는입력」(714-1 · 조건 · 재료 고르기 · 중간값까지) 이 있으면 그것 · 없으면 조각에서
|
||||
const used: [string, string][] = line.쓰는입력
|
||||
? line.쓰는입력.filter((n) => names.includes(n)).map((n) => [n, seen.get(n) ?? ""])
|
||||
: [...seen];
|
||||
if (!used.length || !ctx.onInput) return [];
|
||||
// 737-2 옵션 칸 = 비목 상자 맨 위 — 그 상자 줄이 쓰는 입력 + 어느 줄도 안 쓰는 입력(공통 · 737-3)
|
||||
const names = specs.map((x) => x.이름);
|
||||
const usedBy = (line: TextLine): string[] =>
|
||||
line.쓰는입력
|
||||
? line.쓰는입력.filter((n) => names.includes(n))
|
||||
: usedInputs(line, names).map(([n]) => n);
|
||||
const everyUsed = new Set(text.groups.flatMap((g) => g.줄.flatMap(usedBy)));
|
||||
const common = names.filter((n) => !everyUsed.has(n));
|
||||
const options = (g: (typeof text.groups)[number]): HTMLElement[] => {
|
||||
const onInput = ctx.onInput;
|
||||
return [
|
||||
el("div", {
|
||||
className: "m01lab-fx__vars",
|
||||
children: used.map(([name, was]) =>
|
||||
inputChip(
|
||||
specs.find((x) => x.이름 === name)!,
|
||||
ctx.values?.[name] || was,
|
||||
(v) => onInput(name, v),
|
||||
),
|
||||
),
|
||||
}),
|
||||
];
|
||||
if (!onInput) return [];
|
||||
const mine = new Set([...g.줄.flatMap(usedBy), ...common]);
|
||||
return optionGrid(
|
||||
specs
|
||||
.filter((x) => mine.has(x.이름))
|
||||
.map((x) => varRow(x, ctx.values?.[x.이름] ?? "", (v) => onInput(x.이름, v))),
|
||||
);
|
||||
};
|
||||
const calculated = text.계산됨 !== false; // [계산] 뒤 응답은 표식이 안 붙음 — 미리보기만 false
|
||||
const light = (pair: string, on: boolean): void =>
|
||||
rows.forEach((r) => r.dataset.pair === pair && r.classList.toggle("is-on", on));
|
||||
const blocks = text.groups.flatMap((g) => [
|
||||
el("h4", { className: "m01lab__group m01lab-fx__wide", text: g.비목 }),
|
||||
...g.줄.map((line, i) => {
|
||||
// 737-2 비목마다 구분 상자 — 맨 위 옵션 칸 · 아래 두 칸 식 줄들 · 소계
|
||||
const boxes = text.groups.map((g) => {
|
||||
const lines = g.줄.map((line, i) => {
|
||||
const pair = String(line.짝 ?? `${g.비목}${i}`);
|
||||
const row = el("div", {
|
||||
className: `m01lab-fx__row m01lab__text${line.까닭 ? " m01lab__text--off" : ""}`,
|
||||
@@ -166,7 +165,6 @@ export function buildFormula(host: HTMLElement, ctx: FormulaContext): void {
|
||||
children: calculated
|
||||
? [
|
||||
el("div", { text: `${line.이름} : ${line.글}` }),
|
||||
...vars(line),
|
||||
...(line.까닭
|
||||
? [
|
||||
el("div", {
|
||||
@@ -189,20 +187,36 @@ export function buildFormula(host: HTMLElement, ctx: FormulaContext): void {
|
||||
});
|
||||
rows.push(row);
|
||||
return row;
|
||||
}),
|
||||
});
|
||||
const card = createCard({
|
||||
title: g.비목,
|
||||
pad: 12,
|
||||
body: [
|
||||
...options(g),
|
||||
el("div", {
|
||||
className: "m01lab-fx",
|
||||
children: [
|
||||
heads(),
|
||||
...lines,
|
||||
el("p", {
|
||||
className: "m01lab__subtotal-text m01lab-fx__wide",
|
||||
attrs: { "data-text-sub": String(g.소계) },
|
||||
text: `${g.비목} ${tl("Subtotal")} ${formatMoney(g.소계)}${g.끝수 ? ` (${g.끝수})` : ""}`,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
card.classList.add("m01lab__ho-box");
|
||||
card.dataset.fxBox = g.비목;
|
||||
return card;
|
||||
});
|
||||
host.replaceChildren(
|
||||
...boxes,
|
||||
el("p", {
|
||||
className: "m01lab__subtotal-text m01lab-fx__wide",
|
||||
attrs: { "data-text-sub": String(g.소계) },
|
||||
text: `${g.비목} ${tl("Subtotal")} ${formatMoney(g.소계)}${g.끝수 ? ` (${g.끝수})` : ""}`,
|
||||
}),
|
||||
]);
|
||||
blocks.unshift(heads);
|
||||
blocks.push(
|
||||
el("p", {
|
||||
className: "m01lab__total m01lab-fx__wide",
|
||||
className: "m01lab__total",
|
||||
attrs: { "data-text-sum": String(text.계) },
|
||||
text: `${tl("Total")} ${formatMoney(text.계)}`,
|
||||
}),
|
||||
);
|
||||
host.replaceChildren(el("div", { className: "m01lab-fx", children: blocks }));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user