Merge remote-tracking branches 'origin/sub_laptop_2' and 'origin/sub_laptop_3' into sub_desktop_1
This commit is contained in:
@@ -33,7 +33,7 @@ import { buildCalc } from "./M01_MasterData_UI_Logic_Calc";
|
||||
import { buildList, logicId, type ListItem, type ListMark } from "./M01_MasterData_UI_Logic_List";
|
||||
import type { SideHandle } from "./M01_MasterData_UI_Side";
|
||||
import { tx } from "./M01_MasterData_UI_Logic_Text";
|
||||
import { buildLabDetail } from "./M01_MasterData_UI_LogicLab_Detail";
|
||||
import { buildLabDetail, isOwnLogic } from "./M01_MasterData_UI_LogicLab_Detail";
|
||||
import { buildNewLogic } from "./M01_MasterData_UI_LogicLab_New";
|
||||
import { tn } from "./M01_MasterData_UI_LogicLab_New_Text";
|
||||
import "./M01_MasterData_UI_Logic_Style.css";
|
||||
@@ -56,10 +56,12 @@ interface Opened extends Draft {
|
||||
reasons: string[];
|
||||
lines: CalcLine[] | null;
|
||||
text: TextAnswer | null;
|
||||
ownHost: HTMLElement;
|
||||
values: Record<string, string>;
|
||||
}
|
||||
|
||||
const cacheKey = "m01_logic_drafts_lab";
|
||||
const ownHost = (): HTMLElement => el("div", { className: "m01lab__own" });
|
||||
const clone = <T>(v: T): T => JSON.parse(JSON.stringify(v)) as T;
|
||||
|
||||
function loadDrafts(): Record<string, Draft> {
|
||||
@@ -84,6 +86,7 @@ export async function mountM01LogicLab(
|
||||
let opened: Opened | null = null;
|
||||
let creating = false; // 「식으로 새로 만들기」 화면이 열려 있음
|
||||
let calcInputs = "";
|
||||
let recalc: number | undefined;
|
||||
const editor = el("div", { className: "m01-logic__editor" });
|
||||
const errors = el("div", { className: "m01-logic__reasons", attrs: { hidden: "" } });
|
||||
const calc = el("div", { className: "m01lab__calc" });
|
||||
@@ -134,6 +137,11 @@ export async function mountM01LogicLab(
|
||||
persist();
|
||||
const inputs = JSON.stringify(opened.row.입력 ?? []);
|
||||
if (inputs !== calcInputs) drawCalc();
|
||||
if (isOwnLogic(opened.row)) {
|
||||
// 자체 로직 — 고친 뒤 잠시 쉬면 다시 계산해 호표 소계와 텍스트 수식을 새로 그림
|
||||
window.clearTimeout(recalc);
|
||||
recalc = window.setTimeout(() => calc.querySelector("button")?.click(), 700);
|
||||
}
|
||||
};
|
||||
|
||||
const pick = (o: Opened, row: LogicRow | null): Draft => ({
|
||||
@@ -191,6 +199,7 @@ export async function mountM01LogicLab(
|
||||
lines: current.lines,
|
||||
text: current.text,
|
||||
values: current.values,
|
||||
ownHost: current.ownHost,
|
||||
onChange: touch,
|
||||
calcHost: calc,
|
||||
});
|
||||
@@ -215,6 +224,7 @@ export async function mountM01LogicLab(
|
||||
reasons: [],
|
||||
lines: null,
|
||||
text: null,
|
||||
ownHost: ownHost(),
|
||||
values: {},
|
||||
});
|
||||
return;
|
||||
@@ -233,6 +243,7 @@ export async function mountM01LogicLab(
|
||||
reasons: one.reasons,
|
||||
lines: null,
|
||||
text: null,
|
||||
ownHost: ownHost(),
|
||||
values: {},
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
@@ -15,7 +15,8 @@ import type {
|
||||
NamedFormula,
|
||||
TextAnswer,
|
||||
} from "./M01_MasterData_UI_Logic_Api";
|
||||
import { formatNumber, qtyKind } from "./M01_MasterData_UI_Logic_Edit";
|
||||
import { buildEditor, formatNumber, qtyKind } from "./M01_MasterData_UI_Logic_Edit";
|
||||
import { openPicker } from "./M01_MasterData_UI_Logic_Pick";
|
||||
import { tx } from "./M01_MasterData_UI_Logic_Text";
|
||||
import { buildFormula } from "./M01_MasterData_UI_LogicLab_Formula";
|
||||
import { openMaterialModal } from "./M01_MasterData_UI_LogicLab_Modal";
|
||||
@@ -32,6 +33,8 @@ export interface DetailContext {
|
||||
/** 마지막 시험 계산의 읽는 식 줄(서버가 줌) */
|
||||
text: TextAnswer | null;
|
||||
values: Record<string, string>;
|
||||
/** 자체 로직 편집기 자리 — 다시 그려도 같은 칸을 씀(고치던 값이 안 날아감) */
|
||||
ownHost: HTMLElement;
|
||||
onChange: () => void;
|
||||
/** 시험 계산 칸(`buildCalc` 가 채움) — 넷째 컨테이너 안에 놓음 */
|
||||
calcHost: HTMLElement;
|
||||
@@ -79,6 +82,20 @@ const section = (title: string, body: HTMLElement[], hint = ""): HTMLElement =>
|
||||
|
||||
function infoBox(ctx: DetailContext): HTMLElement {
|
||||
const row = ctx.row;
|
||||
const own = isOwnLogic(row);
|
||||
const field = (label: string, key: "이름" | "원문번호" | "결과단위" | "출처"): HTMLElement => {
|
||||
if (!own) return cell(label, row[key] ?? "");
|
||||
const box = el("input", { className: "m01-logic__input", attrs: { "data-own": key } });
|
||||
box.value = row[key] ?? "";
|
||||
box.addEventListener("input", () => {
|
||||
row[key] = box.value;
|
||||
ctx.onChange();
|
||||
});
|
||||
return el("label", {
|
||||
className: "m01lab__cell",
|
||||
children: [el("span", { className: "m01-logic__muted", text: label }), box],
|
||||
});
|
||||
};
|
||||
const cell = (label: string, value: string): HTMLElement =>
|
||||
el("div", {
|
||||
className: "m01lab__cell",
|
||||
@@ -106,17 +123,20 @@ function infoBox(ctx: DetailContext): HTMLElement {
|
||||
]
|
||||
: [];
|
||||
return section(tl("Info_Title"), [
|
||||
el("p", { className: "m01-logic__muted", text: tl("Info_ReadOnly") }),
|
||||
el("p", {
|
||||
className: "m01-logic__muted",
|
||||
text: tl(own ? "Info_Own" : "Info_ReadOnly"),
|
||||
}),
|
||||
...reasons,
|
||||
el("div", {
|
||||
className: "m01lab__info",
|
||||
children: [
|
||||
cell(tx("Head_File"), ctx.file),
|
||||
cell(tx("Head_Key"), row.키),
|
||||
cell(tx("Head_Number"), row.원문번호),
|
||||
cell(tx("Head_Name"), row.이름),
|
||||
cell(tx("Head_Unit"), row.결과단위),
|
||||
cell(tx("Head_Source"), row.출처),
|
||||
field(tx("Head_Number"), "원문번호"),
|
||||
field(tx("Head_Name"), "이름"),
|
||||
field(tx("Head_Unit"), "결과단위"),
|
||||
field(tx("Head_Source"), "출처"),
|
||||
cell(tx("Head_Owner"), row.소유 ?? ""),
|
||||
el("label", {
|
||||
className: "m01lab__cell m01lab__cell--wide",
|
||||
@@ -254,6 +274,31 @@ function groupTable(group: Group, mine: Entry[], calculated: boolean): HTMLEleme
|
||||
];
|
||||
}
|
||||
|
||||
/** 자체 로직(키 GX…) — 정본은 아님 */
|
||||
export const isOwnLogic = (row: LogicRow): boolean => row.키.startsWith("GX");
|
||||
|
||||
/** 자체 로직은 정본 화면과 같은 편집기를 호표 아래에 폄(머리 칸은 기본정보에서 고치므로 숨김) */
|
||||
function ownEditor(ctx: DetailContext): HTMLElement {
|
||||
if (!ctx.ownHost.childElementCount)
|
||||
buildEditor(ctx.ownHost, {
|
||||
row: ctx.row,
|
||||
file: ctx.file,
|
||||
isNew: false,
|
||||
files: [],
|
||||
reasons: [],
|
||||
prices: ctx.prices,
|
||||
lines: ctx.lines,
|
||||
onChange: ctx.onChange,
|
||||
onFile: () => undefined,
|
||||
onPick: openPicker,
|
||||
});
|
||||
return el("details", {
|
||||
className: "m01lab__edit",
|
||||
attrs: { open: "" },
|
||||
children: [el("summary", { text: tl("Edit_Title") }), ctx.ownHost],
|
||||
});
|
||||
}
|
||||
|
||||
function hoBox(ctx: DetailContext): HTMLElement {
|
||||
const money = !("결과" in ctx.row) && (ctx.row.결과단위 ?? "").startsWith("원");
|
||||
if (!money) {
|
||||
@@ -276,6 +321,7 @@ function hoBox(ctx: DetailContext): HTMLElement {
|
||||
text: `${tl("Total")} ${calculated ? formatNumber(total) : "—"}`,
|
||||
}),
|
||||
);
|
||||
if (isOwnLogic(ctx.row)) blocks.push(ownEditor(ctx));
|
||||
const hint = calculated ? tl("Click_Hint") : `${tl("Click_Hint")} · ${tl("Calc_First")}`;
|
||||
return section(tl("Ho_Title"), blocks, hint);
|
||||
}
|
||||
|
||||
@@ -109,3 +109,11 @@
|
||||
font-weight: 600;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.m01lab__own > :first-child {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.m01lab__edit {
|
||||
margin-top: var(--spacing-12);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,14 @@ const TEXT = {
|
||||
"기본 로직은 품셈 원문 그대로라 여기서 고치지 못함 — 비고만 고칠 수 있음",
|
||||
"Base logic follows the source book; only the note can be edited",
|
||||
],
|
||||
Info_Own: [
|
||||
"자체 로직 — 칸을 고치면 잠시 뒤 호표 소계와 텍스트 수식이 다시 계산됨",
|
||||
"Own logic — edits recalculate the subtotals and formula shortly",
|
||||
],
|
||||
Edit_Title: [
|
||||
"고치기 (호표 · 중간 값 · 덧줄 · 끝수)",
|
||||
"Edit (lines · middles · extras · rounding)",
|
||||
],
|
||||
Ho_Title: ["일위대가 호표", "Unit-cost table"],
|
||||
Formula_Title: ["텍스트 수식", "Text formula"],
|
||||
Formula_Slot: ["시험 계산을 누르면 식이 글로 나옴", "Run the trial calc to see the formula"],
|
||||
|
||||
Reference in New Issue
Block a user