Merge remote-tracking branch 'origin/dev' into sub_laptop_2

This commit is contained in:
2026-10-05 11:29:04 +09:00
18 changed files with 323 additions and 43 deletions
@@ -43,6 +43,7 @@ import { logicTable, matTable, qtyShort } from "./M02_MasterTemplete_Labor_Rows"
import { tc } from "./M02_MasterTemplete_Labor_Text";
import { keepKnown } from "./M02_Labor_Values_Model";
import { pickMaterial } from "./M02_Material_Pick";
import { createUnitPick, NO_UNIT } from "./DataFlow/M02_DF_UnitPick";
import "../M01_MasterData/M01_MasterData_UI_Logic_Style.css";
import "../M01_MasterData/M01_MasterData_UI_LogicLab_Style.css";
import "@ui/ui_template_general_layout.css";
@@ -341,15 +342,27 @@ export function mountLaborDetail(
}),
});
const unit = createInputField({ type: "text", label: tc("Labor_Unit") });
unit.input.value = cur.단위 ?? "";
unit.input.readOnly = !edit;
unit.input.maxLength = 20;
unit.input.setAttribute("data-labor-field", "단위");
unit.input.addEventListener("input", () => {
cur.단위 = unit.input.value.trim() || null;
opt.onChanged(dirty());
});
// 단위 = 표준 단위 목록에서 고르기(지금 값이 목록에 없어도 선택지로 둠) · 저장은 [저장]
const unit = {
root: el("div", { className: "ui-field", attrs: { "data-labor-field": "단위" } }),
};
const unit0 = cur.단위;
const drawUnit = (): void =>
unit.root.replaceChildren(
el("label", { className: "ui-field__label", text: tc("Labor_Unit") }),
createUnitPick({
value: cur.단위 || NO_UNIT,
extra: [unit0, cur.단위].filter((u): u is string => !!u),
readOnly: !edit,
ariaLabel: tc("Labor_Unit"),
onPick: (u) => {
cur.단위 = u === NO_UNIT ? null : u;
opt.onChanged(dirty());
drawUnit();
},
}),
);
drawUnit();
const origin = createInputField({ type: "text", label: tc("Labor_Origin") });
origin.input.value = cur.본뜬?.키 ? `${cur.본뜬.층} ${cur.본뜬.키}` : tc("Origin_Self");
origin.input.readOnly = true;
@@ -1,7 +1,7 @@
/* =============================================================================
* M02_MasterTemplete_Layer.ts
* M02 한 화면의 뿌리 셋(마스터 · 회사 · 개인 · PLAN 27-1) — 진입 해시
* `#/m02-master-template?open=구조물조합|일위대가|옵션|도면&root=master|company|personal&owner=…&company=…`
* `#/m02-master-template?open=구조물조합|옵션|도면&root=master|company|personal&owner=…&company=…`
* (옛 `?layer=` 도 root 로 읽음) · 고칠 권한(`accessFor` — 서버 `M02_Layer_Access` 와 짝 · 27-1c) ·
* 회사 없음 안내 · 회사용 읽기만일 때 [개인용으로 복사] 막대.
* ========================================================================== */
@@ -16,7 +16,7 @@ import { copyItemToPersonal, fetchStructureCombos } from "./M02_MasterTemplete_A
export type ManageLayer = "system" | "company" | "personal";
/** 해시 · 화면의 뿌리 이름 — 서버 층 이름은 `layerOfRoot` */
export type Root = "master" | "company" | "personal";
export type OpenTarget = "구조물조합" | "일위대가" | "옵션" | "도면";
export type OpenTarget = "구조물조합" | "옵션" | "도면";
export interface Entry {
root: Root;
@@ -27,7 +27,7 @@ export interface Entry {
company: string | null;
}
const OPENS: OpenTarget[] = ["구조물조합", "일위대가", "옵션", "도면"];
const OPENS: OpenTarget[] = ["구조물조합", "옵션", "도면"];
export const layerOfRoot = (root: Root): ManageLayer => (root === "master" ? "system" : root);
@@ -226,7 +226,7 @@ export async function renderM02MasterTemplate(root: HTMLElement): Promise<Mounte
root.innerHTML = "";
root.append(layout);
void main.open(home);
// 진입 해시 `open` — 옵션 · 도면 컨테이너는 마스터에만 · 일위대가는 구조물 조합 하위(세 층) ·
// 진입 해시 `open` — 옵션 · 도면 컨테이너는 마스터에만 ·
// 옵션 화면이 뒤에 열리면 편집 칸 차례(`seq`)가 늦게 온 집계표를 버림
if (entry.open !== "구조물조합") side.reveal(entry.open);
void tree.refresh();
@@ -94,7 +94,7 @@ export interface SideHandle {
/** 고름 표시를 지금 열린 것 한 줄로 — 편집 칸이 무엇을 열든(카드 [도면 수정] 등) 부름 */
paint: () => void;
/** 진입 해시 `open`(PLAN 27-1d) — 그 컨테이너를 펼침(옵션은 열기까지) · 옵션 · 도면은 시스템 층에만 */
reveal: (target: "옵션" | "도면" | "일위대가") => void;
reveal: (target: "옵션" | "도면") => void;
}
/** 서버 이름 규칙과 같게 — 화면에서 먼저 막음 */
@@ -414,14 +414,7 @@ export function buildSide(opt: SideOptions): SideHandle {
}
}
const reveal = (target: "옵션" | "도면" | "일위대가"): void => {
// 일위대가 = 구조물 조합(처음부터 펼침) 안 일위대가 하위를 펼치고 「전체 항목」
if (target === "일위대가") {
columnSections[COLUMN_KINDS.findIndex((k) => k.kind === "labor")].classList.remove(
"is-collapsed",
);
return void selectAll("labor");
}
const reveal = (target: "옵션" | "도면"): void => {
extras?.[target].classList.remove("is-collapsed");
if (target === "옵션") options.body.querySelector<HTMLElement>("button")?.click();
};