- 시설 폼 칸에 옵션 키 표식(data-option-keys) · 끈 키의 칸은 가림 · 칸이 다 가려진 줄은 줄째 - 키가 여럿인 칸(본체 규격 · 유입구 구조)은 키가 다 꺼졌을 때만 가림 · 독립 기슭막이는 유입 · 유출 채널 키로 넓힘 - 숨긴 칸은 칸 값(제안값 채우기 포함)이 아니라 폼이 받은 옛 값을 저장에 그대로 실음 · 이 폼에는 필수 검사가 없음 - 가리기 코드는 새 파일(_Facility_Hidden)로 분리해 Facility.ts 700줄 아래 유지 - 시험 3개 추가 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TULoa94ZFL26KU6ZqVpjkF
152 lines
6.7 KiB
TypeScript
152 lines
6.7 KiB
TypeScript
/* =============================================================================
|
|
* B05_Profile_UI_Drainage_Facility_Ford.ts
|
|
* 물넘이포장·세월교 칸 묶음 — 월류 폭·높이·바닥 경사·포장 두께·길이·련 수 + 개략 단면 줄.
|
|
*
|
|
* 서브폼 본체(`_Drainage_Facility.ts`)가 700줄에 닿아 떼어 냄(2026-09-14).
|
|
* ⚠ 기본값을 칸에 안 채움(2026-09-14 브레인 판정 「기본값을 몰래 확정으로 바꾸지 않는다」):
|
|
* 월류 폭 세월교 10m·물넘이 5m(사용자 확정)는 **회색 제안**, 월류 높이는 설계유량·폭으로 되짚은
|
|
* **필요 최소 수심을 회색 제안**으로만 — 사용자가 적거나 [제안값 넣기]를 누른 때만 값.
|
|
* 적은 높이가 필요 수심보다 낮으면 여전히 되돌림(계산값 미만은 안 받음).
|
|
* ========================================================================== */
|
|
|
|
import {
|
|
FORD_BRIDGE_DEFAULT_WIDTH_M,
|
|
FORD_PAVEMENT_DEFAULT_WIDTH_M,
|
|
} from "@config/config_frontend";
|
|
import {
|
|
fordSection,
|
|
grid,
|
|
labeled,
|
|
numberInput,
|
|
putNumber,
|
|
stepper,
|
|
} from "./B05_Profile_UI_Drainage_Facility_Fields";
|
|
|
|
type FordKind = "ford_pavement" | "ford_bridge";
|
|
|
|
export interface FordFields {
|
|
widthRow: HTMLElement;
|
|
slopeRow: HTMLElement;
|
|
countRow: HTMLElement;
|
|
summary: HTMLElement;
|
|
/** 바뀌면 저장 흐름을 울릴 칸(높이는 자기 검사 뒤 스스로 울림). */
|
|
inputs: HTMLInputElement[];
|
|
setDesignFlow: (designFlow: number | null) => void;
|
|
sync: () => void;
|
|
write: (kind: FordKind, options: Record<string, string | number>) => void;
|
|
read: (kind: FordKind, target: Record<string, string | number>) => void;
|
|
fillSuggested: (kind: FordKind) => void;
|
|
}
|
|
|
|
export function createFordFields(emit: () => void): FordFields {
|
|
// 세월교 — 구체 내 배관 수량(련). 련은 정수라 소수 자릿수를 두지 않는다.
|
|
const count = numberInput("1", "1", "련");
|
|
const countRow = grid(labeled("수량 (련)", stepper(count, 1, 0), "pipe_count"));
|
|
const width = numberInput("0.1");
|
|
const height = numberInput("0.01");
|
|
const widthRow = grid(
|
|
labeled("월류 폭 (m)", stepper(width, 0.1), "ford_width_m"),
|
|
labeled("월류 높이 (m)", stepper(height, 0.1), "ford_height_m"),
|
|
);
|
|
// 물넘이 바닥은 유입(상류)이 높고 유출이 낮게 기운다(2026-08-28 사용자 확정).
|
|
// 비우면 그 측점의 **노면 횡단경사**를 그대로 쓴다 — 횡단도가 판단한다.
|
|
const slope = numberInput("0.1");
|
|
slope.placeholder = "노면 기울기";
|
|
// 포장 두께·노폭 방향 길이 — 수량(㎡ = 월류 폭 × 길이 · 두께로 원단위 고름)이 읽는 칸(A3).
|
|
const thickness = numberInput("1");
|
|
const length = numberInput("0.1");
|
|
const slopeRow = grid(
|
|
labeled("바닥 경사 유입→유출 (%)", stepper(slope, 0.1), "ford_slope_pct"),
|
|
labeled("포장 두께 (㎝)", stepper(thickness, 1, 0), "thickness_cm"),
|
|
labeled("포장 길이 노폭 방향 (m)", stepper(length, 0.1), "length_m"),
|
|
);
|
|
const summary = document.createElement("p");
|
|
summary.className = "b05-drainage__facility-note";
|
|
let designFlowM3s: number | null = null;
|
|
/** 현재 조건(설계유량·월류 폭)의 필요 최소 수심(m). 계산 불가면 null. */
|
|
let minDepthM: number | null = null;
|
|
|
|
const suggestedWidth = (kind: FordKind): number =>
|
|
kind === "ford_bridge" ? FORD_BRIDGE_DEFAULT_WIDTH_M : FORD_PAVEMENT_DEFAULT_WIDTH_M;
|
|
|
|
function sync(): void {
|
|
const widthM = Number.parseFloat(width.value || width.dataset.suggested || "");
|
|
const section = designFlowM3s !== null ? fordSection(designFlowM3s, widthM) : null;
|
|
minDepthM = section ? Number(section.depthM.toFixed(2)) : null;
|
|
height.placeholder = minDepthM !== null ? `제안 필요 수심 ${minDepthM.toFixed(2)}` : "";
|
|
if (designFlowM3s === null) {
|
|
summary.textContent =
|
|
"담당 유역의 설계유량이 아직 없습니다 — [유역 분석] 후 개략 단면이 나옵니다.";
|
|
return;
|
|
}
|
|
const head = `설계유량 ${designFlowM3s.toFixed(3)} ㎥/s`;
|
|
if (!section) {
|
|
summary.textContent = `${head} · 월류 폭을 넣으면 필요 수심·단면을 계산합니다.`;
|
|
return;
|
|
}
|
|
// 필요 최소 수심이다 — 월류 높이는 이 값 아래로 내릴 수 없다.
|
|
summary.textContent =
|
|
`${head} · 필요 수심 ${section.depthM.toFixed(2)} m · ` +
|
|
`필요 단면 ${section.areaM2.toFixed(2)} ㎡ · 유속 ${section.velocityMs.toFixed(2)} m/s — ` +
|
|
`월류 높이는 필요 수심 이상만 입력됩니다.`;
|
|
}
|
|
|
|
// 적은 값이 계산값 미만이면 받지 않는다 — 계산값으로 되돌리고 잠깐 붉힌다(빈 칸은 그대로 빈 칸).
|
|
height.addEventListener("change", () => {
|
|
const value = Number.parseFloat(height.value);
|
|
if (
|
|
minDepthM !== null &&
|
|
height.value !== "" &&
|
|
(!Number.isFinite(value) || value < minDepthM)
|
|
) {
|
|
height.value = minDepthM.toFixed(2);
|
|
height.classList.add("is-invalid");
|
|
window.setTimeout(() => height.classList.remove("is-invalid"), 900);
|
|
}
|
|
emit();
|
|
});
|
|
|
|
return {
|
|
widthRow,
|
|
slopeRow,
|
|
countRow,
|
|
summary,
|
|
inputs: [count, width],
|
|
setDesignFlow(designFlow) {
|
|
designFlowM3s = designFlow ?? null;
|
|
},
|
|
sync,
|
|
write(kind, options) {
|
|
const text = (key: string): string =>
|
|
options[key] !== undefined ? String(options[key]) : "";
|
|
count.value = kind === "ford_bridge" ? text("pipe_count") : "";
|
|
width.value = text("ford_width_m");
|
|
width.dataset.suggested = String(suggestedWidth(kind));
|
|
width.placeholder = `제안 ${suggestedWidth(kind)}`;
|
|
height.value = text("ford_height_m");
|
|
slope.value = text("ford_slope_pct");
|
|
thickness.value = text("thickness_cm");
|
|
length.value = text("length_m");
|
|
},
|
|
read(kind, target) {
|
|
putNumber(target, "ford_width_m", width.value);
|
|
// 월류 높이는 cm 단위 수심이라 0.01m 정밀도로 싣는다(putNumber는 0.1m 반올림).
|
|
const value = Number.parseFloat(height.value);
|
|
if (Number.isFinite(value) && value > 0) target.ford_height_m = Number(value.toFixed(2));
|
|
if (kind === "ford_pavement") {
|
|
putNumber(target, "ford_slope_pct", slope.value);
|
|
putNumber(target, "thickness_cm", thickness.value);
|
|
putNumber(target, "length_m", length.value);
|
|
return;
|
|
}
|
|
const pipes = Number.parseInt(count.value, 10);
|
|
if (Number.isFinite(pipes) && pipes > 0) target.pipe_count = pipes;
|
|
},
|
|
fillSuggested(kind) {
|
|
if (!width.value) width.value = String(suggestedWidth(kind));
|
|
sync();
|
|
if (!height.value && minDepthM !== null) height.value = minDepthM.toFixed(2);
|
|
},
|
|
};
|
|
}
|