feat(B08): 수리 표 값 차례 기본 < 장 기본 < B05 연결 < 저장 < 초안 · 같은 값 비워 저장 · B05 Qd · T 참고(96-2)
- 저장본 · 초안의 연결 칸 null 은 건너뜀 · [저장] · 초안에서 연결값과 같은 칸은 null(연동 유지) - 연결된 칸 이름 옆 작은 「B05」(풍선 「B05 분석」) · Qd · T 옆에 B05 값 - 초안 version 2 → 3 · 옛 「B05 와 안 이음」 주석 고침 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BVPp2o6Mh2FZJr72yW3ofZ
This commit is contained in:
@@ -164,8 +164,8 @@ export const STATE_REGISTRY = {
|
||||
* 예전에는 버튼을 누를 때마다 서버가 계산해 **바로 저장**했다. 조작은 캐시에 쌓고
|
||||
* [저장]·[확정]에서만 정본으로 나가야 한다(사용자 확정). */
|
||||
crossdesign: { bucket: "draft", scope: "route" },
|
||||
/** B08 홍수량 계산서 입력 `{측점: 입력}` — [저장] 때 B08 자기 파일로 · 배수유역 · B05/B06 값과 안 이음(69-6). */
|
||||
"b08-hydro": { bucket: "draft", scope: "project", version: 2 },
|
||||
/** B08 홍수량 계산서 입력 `{측점: 입력}` — [저장] 때 B08 자기 파일로 · B05 연결값과 같은 칸은 null(연동 · 96). */
|
||||
"b08-hydro": { bucket: "draft", scope: "project", version: 3 },
|
||||
/** 미저장 계획고 편집(▲▼ · 곡선반경) — 설계 가게 `alignmentDraft` 의 칸(계획서 24-2c).
|
||||
* 옛 키는 노선 번호만 붙은 `b05-profile-alignment-draft:{노선}` 이었다. */
|
||||
"alignment-draft": {
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
* B08_Quantity_UI_Hydro.ts
|
||||
* 4탭 수리집수면적표 — 배수지점 측점마다 보조 탭 · 한 장 = 홍수량 계산서(PLAN 62-2 · 69-4~6).
|
||||
*
|
||||
* 측점 목록만 저장된 관 지점(pipe_points)에서 · 입력은 이 탭 자기 값(B05/B06 값과 안 이음)
|
||||
* 측점 목록은 저장된 관 지점(pipe_points) · B05 연결값(A · L · H · 관경 · 내공 · 월류 폭 · 96)이 빈칸을 채움
|
||||
* 값 차례 = 기본 < 장 기본 < 연결 < 저장 < 초안 · 연결값과 같은 칸은 null 로 저장(연동 유지)
|
||||
* 박스암거 = 사각 단면 장(73-11) · 세월교 · 물넘이포장 = 월류 높이 장(73-12) · 그 밖 = 관 장
|
||||
* 처음 값 = 표준 값 · 측점 고유 값은 빈칸 · 지역 · R24 = 공사지 자료(서버)
|
||||
* 조작 = 캐시(sessionStorage) · [저장] = B08 자기 파일(PUT …/quantity/hydro) · 뒤 캐시 비움
|
||||
@@ -23,6 +24,7 @@ import { stationInterval } from "./B08_Quantity_UI_Summary";
|
||||
import {
|
||||
buildHydroSheet,
|
||||
type HydroInputs,
|
||||
type HydroLink,
|
||||
type HydroOptions,
|
||||
type HydroResult,
|
||||
} from "./B08_Quantity_UI_Hydro_Sheet";
|
||||
@@ -34,6 +36,9 @@ interface HydroStation {
|
||||
kind: string;
|
||||
/** 계산서 장 — 시설이 정함(서버 station_rows) */
|
||||
sheet: HydroInputs["sheet"];
|
||||
/** B05 연결값(값이 있는 칸만) · 비교용 B05 Qd · T — 서버 station_rows */
|
||||
linked: HydroLink["values"] | null;
|
||||
b05: HydroLink["b05"];
|
||||
}
|
||||
|
||||
type Drafts = Record<string, Partial<HydroInputs>>;
|
||||
@@ -83,14 +88,26 @@ export async function renderHydro(host: HTMLElement, projectId: string): Promise
|
||||
}
|
||||
|
||||
let drafts: Drafts = readState<Drafts>("b08-hydro", projectId) ?? {};
|
||||
const linkedOf = (station: HydroStation): Record<string, unknown> => station.linked ?? {};
|
||||
/** 저장본 · 초안 한 겹 — 연결값이 있는 칸의 null 은 건너뜀(연결값을 덮지 않게) */
|
||||
const layer = (station: HydroStation, values: Partial<HydroInputs> = {}) =>
|
||||
Object.fromEntries(
|
||||
Object.entries(values).filter(([k, v]) => v != null || !(k in linkedOf(station))),
|
||||
);
|
||||
const inputsOf = (station: HydroStation): HydroInputs =>
|
||||
({
|
||||
...sheet.defaults,
|
||||
...(station.sheet === "pipe" ? {} : sheet.sheet_defaults[station.sheet]),
|
||||
...sheet.saved[keyOf(station)],
|
||||
...drafts[keyOf(station)],
|
||||
...station.linked,
|
||||
...layer(station, sheet.saved[keyOf(station)]),
|
||||
...layer(station, drafts[keyOf(station)]),
|
||||
sheet: station.sheet,
|
||||
}) as HydroInputs;
|
||||
/** 연결값과 같은 칸은 null — 저장 · 초안이 연동을 유지(B05 값이 바뀌면 따라옴) · 다르면 손 값 */
|
||||
const unlinked = (station: HydroStation, inputs: HydroInputs): HydroInputs =>
|
||||
Object.fromEntries(
|
||||
Object.entries(inputs).map(([k, v]) => [k, linkedOf(station)[k] === v ? null : v]),
|
||||
) as unknown as HydroInputs;
|
||||
|
||||
const tabs = document.createElement("div");
|
||||
tabs.className = "b08-quantity__tabs b08-hydro__tabs";
|
||||
@@ -134,13 +151,14 @@ export async function renderHydro(host: HTMLElement, projectId: string): Promise
|
||||
inputs,
|
||||
sheet,
|
||||
(next) => {
|
||||
drafts[keyOf(station)] = next;
|
||||
drafts[keyOf(station)] = unlinked(station, next);
|
||||
writeState("b08-hydro", drafts, projectId);
|
||||
// 단면 가정 · 관 종류는 그림 · 식이 바뀜 — 장을 다시 그림 · 그 밖은 값만
|
||||
if (next.section !== inputs.section || next.pipe_kind !== inputs.pipe_kind) draw(next);
|
||||
else void calc(next, view.show);
|
||||
},
|
||||
station.kind,
|
||||
{ values: station.linked ?? {}, b05: station.b05 },
|
||||
);
|
||||
page.replaceChildren(view.root);
|
||||
void calc(inputs, view.show);
|
||||
@@ -166,7 +184,9 @@ export async function renderHydro(host: HTMLElement, projectId: string): Promise
|
||||
|
||||
/** [저장] — 측점 전부의 지금 입력을 B08 자기 파일로 · 성공하면 캐시를 비우고 저장본으로 다시 엶 */
|
||||
const saveAll = async (): Promise<void> => {
|
||||
const stations = Object.fromEntries(sheet.stations.map((s) => [keyOf(s), inputsOf(s)]));
|
||||
const stations = Object.fromEntries(
|
||||
sheet.stations.map((s) => [keyOf(s), unlinked(s, inputsOf(s))]),
|
||||
);
|
||||
showLoadingOverlay();
|
||||
try {
|
||||
const body = await request<{ saved: Drafts }>(`/projects/${projectId}/quantity/hydro`, {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
* 장(sheet) 갈래 — 박스 = 사각 내공 B · H · 수심비(73-11) · 세월교 · 물넘이 = 월류 폭 → 월류 높이(73-12)
|
||||
* 테두리는 입력 · 선택 칸만(공용 ui-input · ui-select) · 계산 칸은 글자만
|
||||
* 굵게 + 붉은 계열은 중요 결과(Qd · 유출량 · 판정 · 유출율)만 · 빈칸에 기대는 계산 칸은 「-」.
|
||||
* B05 연결값과 같은 칸은 이름 옆 작은 「B05」(풍선 「B05 분석」) · Qd · T 옆에 B05 값 참고(96-2).
|
||||
*
|
||||
* 입력 칸은 한 번 만들고 계산 결과만 제자리에 바꿈 — 고치는 중 칸 포커스를 잃지 않게.
|
||||
* ========================================================================== */
|
||||
@@ -77,6 +78,12 @@ export interface HydroOptions {
|
||||
site: HydroSite;
|
||||
}
|
||||
|
||||
/** B05 배수유역 연결(96) — 칸 값 · 비교용 설계유량 · 도달시간(분) */
|
||||
export interface HydroLink {
|
||||
values: Partial<Record<keyof HydroInputs, number>>;
|
||||
b05: { design_flow_m3s: number | null; tc_minutes: number | null } | null;
|
||||
}
|
||||
|
||||
export interface HydroSheet {
|
||||
root: HTMLElement;
|
||||
/** 계산값 칸을 바꿈 — null 이면 비움 + 까닭 한 줄 */
|
||||
@@ -103,6 +110,14 @@ const DASH = "-";
|
||||
const fmt = (value: number | null | undefined, digits: number, unit = ""): string =>
|
||||
value == null || !Number.isFinite(value) ? DASH : `${value.toFixed(digits)}${unit}`;
|
||||
|
||||
/** 입력 칸이 놓인 줄의 이름 칸 */
|
||||
function sheetLabel(left: HTMLElement, right: HTMLElement, key: string): HTMLElement | null {
|
||||
const input = [left, right]
|
||||
.map((card) => card.querySelector(`input[data-b08-hydro="${key}"]`))
|
||||
.find(Boolean);
|
||||
return input?.closest(".b08-hydro__row")?.querySelector(".b08-hydro__label") ?? null;
|
||||
}
|
||||
|
||||
function node<K extends keyof HTMLElementTagNameMap>(
|
||||
tag: K,
|
||||
className = "",
|
||||
@@ -218,8 +233,15 @@ export function buildHydroSheet(
|
||||
onChange: (next: HydroInputs) => void,
|
||||
/** 시설 이름(박스암거 · 세월교 · 물넘이포장) — 관이면 "" */
|
||||
kind = "",
|
||||
link: HydroLink = { values: {}, b05: null },
|
||||
): HydroSheet {
|
||||
const site = options.site;
|
||||
/** 연결된 칸의 「B05」 표 — 값이 연결값과 같을 때만 보임 */
|
||||
const marks = new Map<Key, HTMLElement>();
|
||||
const markLinked = (key: Key): void => {
|
||||
const mark = marks.get(key);
|
||||
if (mark) mark.hidden = current[key] !== link.values[key];
|
||||
};
|
||||
const out = new Map<string, HTMLElement>();
|
||||
const outCell = (id: string, key = false): HTMLElement => {
|
||||
const cell = node("span", `b08-hydro__out${key ? " is-key" : ""}`, DASH);
|
||||
@@ -229,6 +251,7 @@ export function buildHydroSheet(
|
||||
let current = inputs;
|
||||
const commit = (key: Key, value: unknown): void => {
|
||||
current = { ...current, [key]: value } as HydroInputs;
|
||||
markLinked(key);
|
||||
onChange(current);
|
||||
};
|
||||
const numberInput = (key: Key, placeholder = ""): HTMLInputElement => {
|
||||
@@ -238,6 +261,14 @@ export function buildHydroSheet(
|
||||
input.dataset.b08Hydro = key;
|
||||
const value = inputs[key];
|
||||
input.value = value == null ? "" : String(value);
|
||||
const linked = link.values[key];
|
||||
if (linked != null) {
|
||||
input.title = `B05 분석 ${linked}`;
|
||||
const mark = node("span", "b08-hydro__hint", "B05");
|
||||
mark.title = "B05 분석";
|
||||
mark.dataset.b08HydroLinked = key;
|
||||
marks.set(key, mark);
|
||||
}
|
||||
input.addEventListener("change", () => {
|
||||
// 빈칸은 NULLABLE 만 · 최소 도달시간은 0 까지 · 그 밖은 0 보다 커야 함 — 아니면 되돌림
|
||||
const parsed = input.value === "" ? null : Number(input.value);
|
||||
@@ -283,6 +314,18 @@ export function buildHydroSheet(
|
||||
};
|
||||
const sub = (text: string): HTMLElement => node("h4", "b08-hydro__h2", text);
|
||||
const hint = (text: string): HTMLElement => node("span", "b08-hydro__hint", text);
|
||||
/** B05 분석 값 참고(비교만) — 값이 없으면 칸 없음 */
|
||||
const b05Hint = (
|
||||
key: "design_flow_m3s" | "tc_minutes",
|
||||
text: (value: number) => string,
|
||||
): HTMLElement | undefined => {
|
||||
const value = link.b05?.[key];
|
||||
if (value == null) return undefined;
|
||||
const el = hint(text(value));
|
||||
el.title = "B05 분석";
|
||||
el.dataset.b08HydroB05 = key;
|
||||
return el;
|
||||
};
|
||||
|
||||
// ── 왼쪽 1. 홍수량 계산 ──
|
||||
const factor = select(
|
||||
@@ -332,6 +375,7 @@ export function buildHydroSheet(
|
||||
return label;
|
||||
})(),
|
||||
outCell("design_flow", true),
|
||||
b05Hint("design_flow_m3s", (v) => `B05 ${fmt(v, 2)}`),
|
||||
),
|
||||
row("Q : 홍수량 (㎥/s)", outCell("flow")),
|
||||
row("f : 유출계수", numberInput("runoff")),
|
||||
@@ -339,7 +383,16 @@ export function buildHydroSheet(
|
||||
row("A : 유역면적 (㎢)", numberInput("area_km2", "입력"), outCell("area_ha")),
|
||||
sub("1-2. 홍수 도달시간 — 산지부 (Kirpich)"),
|
||||
formula("T = 0.0663 × L^0.77^ × s^-0.385^ (L : ㎞ · T : hr)"),
|
||||
row("T : 홍수 도달시간 (hr)", outCell("tc_hr"), outCell("tc_note")),
|
||||
row(
|
||||
"T : 홍수 도달시간 (hr)",
|
||||
outCell("tc_hr"),
|
||||
(() => {
|
||||
const note = node("span");
|
||||
const b05 = b05Hint("tc_minutes", (v) => ` B05 ${fmt(v, 1)}분`);
|
||||
note.append(outCell("tc_note"), ...(b05 ? [b05] : []));
|
||||
return note;
|
||||
})(),
|
||||
),
|
||||
row("L : 산정에서 하천시점까지 거리 (m)", numberInput("length_m", "입력")),
|
||||
row("H : 산정에서 하천시점까지 표고차 (m)", numberInput("height_m", "입력")),
|
||||
row("s : 지표면 경사 (H / L)", outCell("slope_ground")),
|
||||
@@ -443,6 +496,13 @@ export function buildHydroSheet(
|
||||
});
|
||||
right.classList.add("b08-hydro__card");
|
||||
|
||||
// 「B05」 표는 그 칸 이름 옆에
|
||||
for (const [key, mark] of marks) {
|
||||
const label = sheetLabel(left, right, key);
|
||||
label?.append(" ", mark);
|
||||
markLinked(key);
|
||||
}
|
||||
|
||||
const status = node("p", "b08-hydro__status");
|
||||
const sheet = node("div", "b08-hydro__sheet");
|
||||
sheet.dataset.b08HydroSheet = stationText;
|
||||
|
||||
Reference in New Issue
Block a user