- 표지 = 기계 줄 맨 위 칸 `비활성: true`(없음 = 켜짐 · 값 묶음 밖) · 틀 검사 = 기계 줄에만 true(check_master OFF) - M01 기계 목록 = 끝 열 「사용」 스위치(초안 → 페이지 [저장] · 자동저장 없음) · 끈 줄 흐림 · 머리 「비활성 같이 보기」(끄면 서버 rows state 「켜짐」) - 뺌 = 요소 찾기 창(elements · 찾아 넣기 · 단가산출 기계 창) · logic-lib rows · M01 시험 계산 기계 고르기 표 · 내 기계 「비슷한 기계에서 시작」 · 불러오기 기계 잇기(M02_Machine_Find 표) · 기계경비식 「기계」 옵션(새 GET /api/m02/df/machine-off · 고른 값은 남기고 「· 비활성 기계」 꼬리) - 셈 길은 그대로 — 끈 기계를 쓰던 단가산출 · 사본 값 같음 - 자료 = 해상장비 79 대(펌프 준설선 13 · 그래브 준설선 10 · 하천골재채취선 7 · 기중기선 5 · 예선 9 · 양묘선 11 · 토운선 6 · 이우선 2 · 대선 16) 끔 - 시험 = test_842_2_machine_off 새로 · test_804_10 옵션 글 단언을 새 글로 고침(끈 기계 거름이 들어간 줄) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
118 lines
4.8 KiB
TypeScript
118 lines
4.8 KiB
TypeScript
/* =============================================================================
|
|
* formula_doc_options.ts
|
|
* 수식 문서 옵션 가로(PLAN 802-3 · 802-4b-2 · 요구 655 「옵션 선택은 상단에 수평」) — 선택 · 입력 기호마다 칸 하나
|
|
* 값 = 화면 값(`given`) › 문서 값 · 고침은 부른 쪽 `set`(M01 = 화면 값만 · M02 사본 = 문서 값) · `lock` = 읽기만
|
|
* 목록이 모두 기계 원문번호(`0101-0007`)면 글 = 「이름 규격 (원문번호)」(요구 692-2 · 값은 그대로 · 이름은 한 번 받아 둠)
|
|
* `view` 있음 + 기계 목록 = 곁에 [조회](827-1 · 기계 창 읽기 전용 · 고른 기계)
|
|
* ========================================================================== */
|
|
|
|
import { createButton } from "@ui/ui_template_button";
|
|
import { createInputField, createSelectField } from "@ui/ui_template_input";
|
|
import { varId, type DocVar, type FormulaDoc } from "../common_util/common_util_formula_doc";
|
|
import { cfMachineNames, cfMachineOff } from "../M02_MasterTemplete/DataFlow/M02_DF_Api";
|
|
import { mt } from "../M02_MasterTemplete/M02_MyMachine_Text";
|
|
import { ft } from "./formula_doc_text";
|
|
|
|
const MACHINE = /^\d{4}-\d{4}$/;
|
|
let names: Record<string, string> = {};
|
|
let off = new Set<string>(); // 842-2 끈 기계 원문번호
|
|
const machineInfo = (): Promise<Record<string, string>> =>
|
|
Promise.all([cfMachineNames(), cfMachineOff()]).then(
|
|
([got, gone]) => ((off = new Set(gone)), got),
|
|
);
|
|
|
|
/** 고르기 글 — 기계 원문번호는 이름 · 규격을 앞에 · 끈 기계는 「· 비활성 기계」(842-2) */
|
|
export const optionText = (x: string, known: Record<string, string> = names): string =>
|
|
known[x] ? `${known[x]} (${x})${off.has(x) ? ` · ${ft("Machine_Off")}` : ""}` : x;
|
|
|
|
/** 고르기 목록 — 끈 기계 뺌(지금 고른 값은 남김 · 842-2) */
|
|
export const liveOptions = (list: string[], now: string): string[] =>
|
|
list.filter((x) => x === now || !off.has(x));
|
|
|
|
/** 목록이 모두 원문번호(빈 칸 빼고)면 이름을 받아 `redraw`(글만 다시) — 수식 탭 옵션 · 단가산출 상세 옵션 */
|
|
export function nameMachines(list: string[], redraw: () => void): void {
|
|
const codes = list.filter(Boolean);
|
|
if (codes.length && codes.every((x) => MACHINE.test(x)) && codes.some((x) => !(x in names)))
|
|
void machineInfo().then((got) => {
|
|
names = got;
|
|
redraw();
|
|
});
|
|
}
|
|
|
|
let asked = false;
|
|
/** 글 값이 기계 원문번호면 「이름 규격 (원문번호)」(838-3 · 요구 859) — 이름이 아직 없으면 한 번 받아 `redraw` · 아니면 null */
|
|
export function machineText(x: unknown, redraw: () => void): string | null {
|
|
if (typeof x !== "string" || !MACHINE.test(x)) return null;
|
|
if (!(x in names) && !asked) {
|
|
asked = true;
|
|
void machineInfo().then((got) => {
|
|
names = { ...names, ...got };
|
|
redraw();
|
|
});
|
|
}
|
|
return optionText(x);
|
|
}
|
|
|
|
export function buildOptions(
|
|
doc: FormulaDoc | null,
|
|
given: Record<string, unknown>,
|
|
lock: boolean,
|
|
set: (key: string, v: DocVar, value: unknown) => void,
|
|
view?: (code: string) => void,
|
|
): HTMLElement[] {
|
|
const out: HTMLElement[] = [];
|
|
for (const v of doc?.변수 ?? []) {
|
|
const kind = v.연결?.갈래;
|
|
const k = v.열쇠;
|
|
if (!k || (kind !== "선택" && kind !== "입력")) continue;
|
|
const label = v.이름 || varId(v);
|
|
const now = k in given ? given[k] : v.연결?.값;
|
|
if (kind === "선택") {
|
|
const list = (v.연결?.목록 ?? []).map(String);
|
|
const items = () =>
|
|
liveOptions(list, String(now ?? "")).map((x) => ({ value: x, text: optionText(x) }));
|
|
const f = createSelectField({
|
|
label,
|
|
options: items(),
|
|
value: String(now ?? ""),
|
|
compact: true,
|
|
size: 28,
|
|
disabled: lock,
|
|
onChange: (x) => set(k, v, x),
|
|
});
|
|
f.root.dataset.option = k;
|
|
nameMachines(list, () => f.setOptions(items(), f.select.value));
|
|
out.push(f.root);
|
|
const codes = list.filter(Boolean);
|
|
if (view && codes.length && codes.every((x) => MACHINE.test(x))) {
|
|
const b = createButton({
|
|
label: mt("Win_View"),
|
|
variant: "ghost",
|
|
size: 28,
|
|
iconStart: "search",
|
|
onClick: () => view(f.select.value),
|
|
});
|
|
b.dataset.optionView = k;
|
|
out.push(b);
|
|
}
|
|
} else {
|
|
const f = createInputField({
|
|
label,
|
|
value: now === undefined || now === null ? "" : String(now),
|
|
compact: true,
|
|
size: 28,
|
|
unit: v.단위 || undefined,
|
|
});
|
|
f.input.disabled = lock;
|
|
f.input.addEventListener("change", () => {
|
|
const t = f.input.value.trim();
|
|
const n = Number(t.replace(/,/g, ""));
|
|
set(k, v, t !== "" && Number.isFinite(n) ? n : t);
|
|
});
|
|
f.root.dataset.option = k;
|
|
out.push(f.root);
|
|
}
|
|
}
|
|
return out;
|
|
}
|