main_laptop_1 -> main byeonghap (4 hwangyeong 585 commits) #12
@@ -152,6 +152,28 @@ export interface CulvertLink {
|
||||
* 옆 측점에 서야 한다). 고정 기본 10m(5/5)는 소유 벽 제원조차 없을 때의 마지막 값이다.
|
||||
* 손으로 한 번 잡은 단은 그 값으로 고정된다 — 계곡부·능선부에서 단마다 연장이 다르다.
|
||||
*/
|
||||
/** 「단이 못 섰다」 안내를 이미 낸 자리 — 다시 그릴 때마다 뜨지 않게 한 번만 낸다.
|
||||
* 키에 세워진 단 수를 넣어, 지형·조작이 바뀌어 결과가 달라지면 다시 알린다. */
|
||||
const extraLimitNotified = new Set<string>();
|
||||
|
||||
/** 요청 단 수보다 적게 섰음을 알린다 — 조정창을 열지 않은 경로(좌측 폼 등)에서도 뜬다.
|
||||
* 종전에는 `activeRevet` 이 그 벽일 때만 떠서, 다른 길로 단 수를 바꾸면 조용히 잘렸다
|
||||
* (2026-09-07). 문구는 종전 그대로 쓴다. */
|
||||
function noticeExtraLimit(chainageM: number, side: "outlet" | "basin", built: number): void {
|
||||
const key = `${chainageM.toFixed(2)}|${side}|${built}`;
|
||||
if (extraLimitNotified.has(key)) return;
|
||||
extraLimitNotified.add(key);
|
||||
showToast(L("B06_Cross_Extra_Limit").replace("{n}", String(built)), "info");
|
||||
}
|
||||
|
||||
/** 요청대로 다 선 자리는 안내 기록을 지운다 — 다음에 또 모자라면 다시 알린다. */
|
||||
function clearExtraLimitNotice(chainageM: number, side: "outlet" | "basin"): void {
|
||||
const head = `${chainageM.toFixed(2)}|${side}|`;
|
||||
for (const key of [...extraLimitNotified]) {
|
||||
if (key.startsWith(head)) extraLimitNotified.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
export function tierSpanOf(section: CrossSection, key: string): StructureSpan {
|
||||
const stored = section.design?.extra_spans?.[key];
|
||||
if (stored) return { beforeM: Math.max(stored.before_m, 0), afterM: Math.max(stored.after_m, 0) };
|
||||
@@ -470,24 +492,18 @@ export function computeCardCulvert(
|
||||
// 유입측(집수정 계류측·독립 기슭막이)도 유출측과 같은 규칙으로 알린다 — 종전에는
|
||||
// 조용히 잘려서 +를 눌러도 아무 일이 없는 것처럼 보였다(2026-08-29 사용자 지적:
|
||||
// 우측 독립 기슭막이에서 추가2가 안 서는데 토스트가 없다).
|
||||
if (activeRevet === "inlet" || activeRevet?.startsWith("bextra")) {
|
||||
showToast(
|
||||
L("B06_Cross_Extra_Limit").replace("{n}", String(layout.basinExtras.length)),
|
||||
"info",
|
||||
);
|
||||
}
|
||||
noticeExtraLimit(section.chainage_m, "basin", layout.basinExtras.length);
|
||||
extraWalls.syncCount(section.chainage_m, layout.basinExtras.length, "basin");
|
||||
} else if (extraWalls) {
|
||||
clearExtraLimitNotice(section.chainage_m, "basin");
|
||||
}
|
||||
if (extraWalls && extraWalls.countFor(section, "outlet") > layout.extraWalls.length) {
|
||||
// 요청 단 수보다 지형이 허락하는 단이 적다(벽이 원지반에 0.5m 이상 묻히면 성토
|
||||
// 불필요 — 그 아래 단은 못 세운다). 조작 중일 때만 가능한 단 수를 토스트로 알린다.
|
||||
if (activeRevet === "outlet" || activeRevet?.startsWith("extra")) {
|
||||
showToast(
|
||||
L("B06_Cross_Extra_Limit").replace("{n}", String(layout.extraWalls.length)),
|
||||
"info",
|
||||
);
|
||||
}
|
||||
noticeExtraLimit(section.chainage_m, "outlet", layout.extraWalls.length);
|
||||
extraWalls.syncCount(section.chainage_m, layout.extraWalls.length, "outlet");
|
||||
} else if (extraWalls) {
|
||||
clearExtraLimitNotice(section.chainage_m, "outlet");
|
||||
}
|
||||
if (revetOffset) {
|
||||
const roles: Array<[RevetKey, WallAdjust]> = [
|
||||
|
||||
+6
-1
@@ -2,7 +2,12 @@ import { resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = fileURLToPath(new URL(".", import.meta.url));
|
||||
const apiTarget = `http://localhost:${process.env.AISLO_API_PORT ?? "8000"}`;
|
||||
// ⚠ `localhost` 로 두지 말 것 — Node 18+ 는 그것을 **IPv6(::1) 먼저** 물고, 백엔드는
|
||||
// `SERVER_HOST=0.0.0.0`(IPv4 전용)로 뜬다. 그러면 프록시가 `[::1]:<포트>` 로 붙으려다
|
||||
// 실패해 화면의 **모든 `/api` 요청이 「Failed to fetch」** 가 된다 — 서버는 멀쩡한데
|
||||
// 저장이 실패하고, 부팅 때 죽으면 화면이 통째로 백지가 된다(2026-09-07 실측:
|
||||
// `127.0.0.1:8001` 200 · `[::1]:8001` 연결 실패). 숫자로 박아 그 갈림을 없앤다.
|
||||
const apiTarget = `http://127.0.0.1:${process.env.AISLO_API_PORT ?? "8000"}`;
|
||||
|
||||
/**
|
||||
* Vite 설정 — 임도 설계 웹앱 (Vanilla TS + Three.js)
|
||||
|
||||
Reference in New Issue
Block a user