Merge remote-tracking branch 'origin/sub_laptop_5' into main_laptop_1
This commit is contained in:
@@ -101,17 +101,23 @@ export function noticeDroppedEdits(saved: { dropped_edit_chainages?: number[] })
|
||||
);
|
||||
}
|
||||
|
||||
/** 노선 초안 동안의 계획고 초안 칸 — `design_pipeline_page` 의 `DRAFT_ROUTE_ID` 와 같은 값(sub7). */
|
||||
const DRAFT_ROUTE_ID = 0;
|
||||
|
||||
/** 계획고 초안(가게 하나)을 종단 정본에 쓴다. 없으면 아무 일도 하지 않는다. 노선이 바뀌었으면
|
||||
* 옛 노선 칸(`from`)의 초안을 새 노선(`routeId`)에 — 측점이 없는 자리는 서버가 버리고 알림(213-2). */
|
||||
* 노선 초안 칸(`DRAFT_ROUTE_ID`) · 없으면 옛 노선 칸(`from`)의 초안을 새 노선(`routeId`)에 · 두 칸 다
|
||||
* 비움 — 측점이 없는 자리는 서버가 버리고 알림(213-2). */
|
||||
async function flushAlignmentDraft(
|
||||
projectId: string,
|
||||
routeId: number,
|
||||
from: number = routeId,
|
||||
): Promise<void> {
|
||||
const draft = pendingAlignment(from);
|
||||
const moved = from !== routeId;
|
||||
const draft = (moved ? pendingAlignment(DRAFT_ROUTE_ID) : null) ?? pendingAlignment(from);
|
||||
if (!draft) return;
|
||||
noticeDroppedEdits(await saveProfileAlignment(projectId, routeId, draft));
|
||||
markAlignmentSaved(from);
|
||||
if (moved) markAlignmentSaved(DRAFT_ROUTE_ID);
|
||||
}
|
||||
|
||||
/** 실패해도 다음 차례로 넘어가는 창구와 그 이름(보기가 준 일에도 같이 씌운다). */
|
||||
|
||||
@@ -410,9 +410,9 @@ export async function mountProfileView(
|
||||
},
|
||||
});
|
||||
|
||||
/** [노선 반영] 끝 — 서버가 배수유역부터 다시 셌고 그 결과가 새 초기값이다. 새로고침 대신 페이지를
|
||||
/** 노선 저장([저장] · 추천 [적용]) 끝 — 서버가 배수유역부터 다시 셌고 그 결과가 새 초기값이다. 새로고침 대신 페이지를
|
||||
* 통째로 다시 세운다(345-3) — 제자리 고침은 옛 노선 관 목록 등 메모리 · 진행 중 요청이 살아남아
|
||||
* [저장]에 실렸다(PLAN 77). 초안 · 결과 칸은 [노선 반영]이 비웠고 옛 노선 캐시는 여기서 비운다 ·
|
||||
* [저장]에 실렸다(PLAN 77). 초안 · 결과 칸은 노선 저장이 비웠고 옛 노선 캐시는 여기서 비운다 ·
|
||||
* 떠나는 페이지의 늦은 응답은 `host.disposed()` 로 버려진다. */
|
||||
const rebuildAfterReplan = (): void => {
|
||||
clearRouteLatestCache(activeProjectId);
|
||||
@@ -488,7 +488,7 @@ export async function mountProfileView(
|
||||
smooth: latestResponse.surface_params.smooth,
|
||||
tools: [view3d.modal.button],
|
||||
recommend: panel.recommend,
|
||||
// 계산 묶음(346-1) — 노선을 고칠 때마다 종단이 따라옴(미리보기 · [노선 반영]이 정본).
|
||||
// 계산 묶음(346-1) — 노선을 고칠 때마다 종단이 따라옴(미리보기 · [저장]이 정본).
|
||||
draft: routeDraftBridge(activeProjectId, profilePanel, {
|
||||
stationIntervalM: panel.values().stationInterval ?? undefined,
|
||||
onCorridorStale: () => sections.setCorridorPending(true),
|
||||
|
||||
@@ -20,9 +20,9 @@ export function createRecommendPart(): RecommendPart {
|
||||
actions.className = "b05-route__rec-actions";
|
||||
actions.innerHTML = `
|
||||
<button type="button" class="ui-btn ui-btn--ghost" data-act="recommend" disabled
|
||||
title="예상노선 추적 · 영선 · 토공 최소 · 기울기 최소 · 비중 계산 중 하나로 노선을 추천받아 미리 봅니다 — [적용]은 평면에 되돌리기 한 칸">노선 추천</button>
|
||||
title="예상노선 추적 · 영선 · 토공 최소 · 기울기 최소 · 비중 계산 중 하나로 노선을 추천받아 미리 봅니다 — [적용]은 바로 저장 · 새 초기값">노선 추천</button>
|
||||
<button type="button" class="ui-btn ui-btn--ghost" data-act="reset" disabled
|
||||
title="전처리 때 만든 초기값 노선(예상노선 추적)으로 되돌립니다 — 다시 계산은 [노선 반영]에서">초기화</button>`;
|
||||
title="초기값 노선(전처리 · 추천 [적용] 때 정한 것)으로 되돌립니다 — 다시 계산은 [저장]에서">초기화</button>`;
|
||||
const recommendButton = actions.querySelector<HTMLButtonElement>('[data-act="recommend"]')!;
|
||||
const resetButton = actions.querySelector<HTMLButtonElement>('[data-act="reset"]')!;
|
||||
return { recommendButton, resetButton, nowLine, nodes: [nowLine, actions] };
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* 계획노선 편집(평면) — 예상노선(점선) 위에 계획노선(실선)을 고쳐 그린다.
|
||||
*
|
||||
* 자리(345-3) — 모달이 아니라 종단 보기 가운데 위 본문(옛 3D 자리)이고 **페이지 수명**이다.
|
||||
* 이력 · 키 듣기는 보기의 보임 · 숨김 · 떠남(`host`)을 따른다. [노선 반영]을 누르면 배수유역부터
|
||||
* 이력 · 키 듣기는 보기의 보임 · 숨김 · 떠남(`host`)을 따른다. [저장]이 노선을 보내면 배수유역부터
|
||||
* 종·횡단·유토곡선까지 다시 돌고(무거움), 끝나면 부른 쪽이 새로고침 없이 페이지를 다시 세운다.
|
||||
* **편집 중에는 아무 계산도 나가지 않는다**(띠 · 기울기 · 횡단 미리보기 묻기만).
|
||||
*
|
||||
@@ -94,7 +94,7 @@ const CONTOUR_LEVEL_BUDGET = 1200;
|
||||
|
||||
type Vertex = [number, number];
|
||||
|
||||
/** 편집기 곁값. [노선 반영]이 끝나면 `onApplied`를 부른다(화면 다시 세우기). */
|
||||
/** 편집기 곁값. 노선 저장이 끝나면 `onApplied`를 부른다(화면 다시 세우기). */
|
||||
export interface RouteEditOptions {
|
||||
/** 규칙 측점 간격(m) — 좌측 패널이 쥔 값을 그대로 받는다(코드에 굳히지 않는다). */
|
||||
stationIntervalM?: number;
|
||||
@@ -279,7 +279,7 @@ export async function mountRouteEditor(
|
||||
planMinRadiusM: () => minRadiusM,
|
||||
apply: (snapshot, message) => restoreSnapshot(snapshot, message, true),
|
||||
});
|
||||
// [반영 전으로] — 마지막 반영(페이지를 세운 때) 노선 · 그 걸음의 「현재 추천」으로. 되돌리기로 다시 살림.
|
||||
// [저장 전으로] — 마지막 저장(페이지를 세운 때) 노선 · 그 걸음의 「현재 추천」으로. 되돌리기로 다시 살림.
|
||||
overlay.querySelector('[data-act="cancel"]')!.addEventListener("click", () => {
|
||||
const snapshot = history?.reset();
|
||||
if (!snapshot) return showToast("반영 뒤 고친 노선이 없습니다.", "info");
|
||||
|
||||
@@ -150,7 +150,7 @@ export async function carryAfterRouteSave(
|
||||
invalidateSectionDetail(projectId, toRouteId);
|
||||
const detail = await loadSectionDetail(projectId, toRouteId);
|
||||
useStationKeys(projectId, toRouteId, detail);
|
||||
const outside = carryStationDrafts(projectId, fromRouteId, toRouteId);
|
||||
const outside = carryStationDrafts(projectId, fromRouteId, toRouteId, { quiet: true });
|
||||
await saveCachedCrossPatches(projectId, toRouteId);
|
||||
return outside;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/* =============================================================================
|
||||
* B05_Profile_UI_RouteEdit_Draft.ts
|
||||
* 노선 편집기 ↔ 계산 묶음 다리(PLAN 346-1) — 노선을 한 번 고칠 때마다(놓기 · 더함 · 지움 · 곡선 ·
|
||||
* 되돌리기 · 다시하기 · [반영 전으로] · 추천 [적용]) 노선 초안을 넘김. 끄는 동안은 넘기지 않음
|
||||
* 되돌리기 · 다시하기 · [저장 전으로] · 추천 [적용]) 노선 초안을 넘김. 끄는 동안은 넘기지 않음
|
||||
* (사진첩에 쌓이는 때 = 손을 놓은 때).
|
||||
*
|
||||
* 편집기는 사진첩(`_History`)을 `watch` 로 감싸기만 함 — 고친 줄 하나. 저장 안 한 초안은
|
||||
* 다리가 캐시 칸(`route-draft`)에 두고, 편집기가 자료를 읽은 뒤 `restore` 로 한 번 되살림
|
||||
* (새로고침 · 보기 전환에도 남음). 계산 · 가게는 `A00_Common/design/design_pipeline_page.ts` 몫.
|
||||
*
|
||||
* 되돌리기 줄(346-4) — 페이지 사진첩(`_Profile_History`)이 있으면 쌓기 · [반영 전으로] 뒤 사진첩에 한 걸음 ·
|
||||
* 되돌리기 줄(346-4) — 페이지 사진첩(`_Profile_History`)이 있으면 쌓기 · [저장 전으로] 뒤 사진첩에 한 걸음 ·
|
||||
* [되돌리기] · [다시하기]는 사진첩이 함(평면 · 종단 · 횡단 · 관 · 구조물 한 줄) · 사진첩이 되쓴 노선
|
||||
* 초안은 `fromPage` 로 편집기 새 걸음이 됨. 사진첩이 없으면 편집기 사진 그대로.
|
||||
* ========================================================================== */
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
*
|
||||
* 되돌리기 줄은 페이지 사진첩 하나(`_Profile_History` · 346-4) — 계산 묶음 다리가 있으면(`_Draft.watch`)
|
||||
* [되돌리기] · [다시하기] · Ctrl+Z · Y 는 사진첩이 받고, 여기 사진은 고정 · 추천 꼬리표 · 고른 점을
|
||||
* 되살리는 기록(`match`)과 [반영 전으로](`reset`) · 고친 것 있음(`isDirty`)에만 씀.
|
||||
* 되살리는 기록(`match`)과 [저장 전으로](`reset`) · 고친 것 있음(`isDirty`)에만 씀.
|
||||
* ========================================================================== */
|
||||
|
||||
import { onPageHistory } from "./B05_Profile_UI_Profile_History";
|
||||
|
||||
@@ -273,11 +273,13 @@ export function writeKeyed<V>(
|
||||
* 노선이 바뀜(새 측점 목록을 `useStationKeys` 로 세운 뒤) — 옛 노선 고침을 측점 열쇠로 새 노선에 넘김.
|
||||
* 넘김 = 카드 선택 넷(지반 유형 · 측구 종류 · 측구 선택 · 2단 경사) · 암 경계 · 암 경사 · 단면 유형 · 측구
|
||||
* 방향은 새 지형이 정함 · 노견 확장 · 소단 깊이는 안 넘김. 돌려주는 값 = 노선 밖으로 나간 고침 수(측점 단위).
|
||||
* `quiet` = 알림 없이 수만 돌려줌(부르는 쪽이 결과 한 줄에 합칠 때).
|
||||
*/
|
||||
export function carryStationDrafts(
|
||||
projectId: string,
|
||||
fromRouteId: number,
|
||||
toRouteId: number,
|
||||
{ quiet = false }: { quiet?: boolean } = {},
|
||||
): number {
|
||||
const outside = new Set<string>();
|
||||
for (const name of KEYED_STATES) {
|
||||
@@ -301,7 +303,7 @@ export function carryStationDrafts(
|
||||
}
|
||||
writeState(name, next, projectId, toRouteId);
|
||||
}
|
||||
if (outside.size)
|
||||
if (outside.size && !quiet)
|
||||
showToast(`노선 밖 고침 ${outside.size} 건 — 사라진 측점이라 버렸습니다.`, "info");
|
||||
return outside.size;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,10 @@ const STUBS = {
|
||||
},
|
||||
"../B06_Section/B06_Section_Station_Keys": {
|
||||
useStationKeys: (_p, routeId, detail) => calls.push(`keys:${routeId}:${detail.tag}`),
|
||||
carryStationDrafts: (_p, from, to) => (calls.push(`carry:${from}->${to}`), 2),
|
||||
carryStationDrafts: (_p, from, to, opts) => (
|
||||
calls.push(`carry:${from}->${to}${opts?.quiet ? ":quiet" : ""}`),
|
||||
2
|
||||
),
|
||||
},
|
||||
"./B05_Profile_Api_Fetch": { clearRouteLatestCache: () => calls.push("latest:clear") },
|
||||
"./B05_Profile_Api_Replan": {
|
||||
|
||||
@@ -225,6 +225,21 @@ async function run() {
|
||||
{ confirm: true },
|
||||
);
|
||||
out.routeConfirmCalls = [...calls];
|
||||
// ⑨ 노선 초안 동안 계획고 = 노선 0 칸(sub7 DRAFT_ROUTE_ID) — 그 칸이 새 노선에 나가고 두 칸 다 비움
|
||||
b05.replace({ station_offsets: { "30.000": 0.2 }, curve_radii: {} }); // 옛 노선(7) 초안
|
||||
useDesignStore(null, 0).set("alignmentDraft", {
|
||||
station_offsets: { "35.000": 0.4 },
|
||||
curve_radii: {},
|
||||
});
|
||||
calls.length = 0;
|
||||
await saveDesign({
|
||||
projectId: "p1",
|
||||
routeId: () => 7,
|
||||
cross: async (confirm) => calls.push(`cross:${confirm}`),
|
||||
failed: () => calls.push("failed"),
|
||||
});
|
||||
out.draftRouteCalls = calls.filter((c) => c.startsWith("alignment:"));
|
||||
out.draftRouteLeft = { draft: edit.pendingAlignment(0), old: edit.pendingAlignment(7) };
|
||||
routeBody = null;
|
||||
|
||||
// ⑤ [초기화] — 초안 · 한 벌 모두 버림
|
||||
|
||||
@@ -72,7 +72,13 @@ def test_저장_뒤_가게_초안_비움_결과_한_줄(out):
|
||||
|
||||
|
||||
def test_초안_넘김_차례(out):
|
||||
assert out["carryCalls"] == ["invalidate:9", "load:9", "keys:9:9", "carry:7->9", "crossSave:9"]
|
||||
assert out["carryCalls"] == [
|
||||
"invalidate:9",
|
||||
"load:9",
|
||||
"keys:9:9",
|
||||
"carry:7->9:quiet",
|
||||
"crossSave:9",
|
||||
]
|
||||
assert out["outside"] == 2
|
||||
|
||||
|
||||
@@ -107,3 +113,25 @@ def test_노선_반영_단추_없앰_배선():
|
||||
assert "saveRecommendedRoute(" in initial[at : at + 900]
|
||||
replan = _read("B05_Profile/B05_Profile_Api_Replan.ts")
|
||||
assert "body: JSON.stringify(body)" in replan and "as_initial?: boolean;" in replan
|
||||
|
||||
|
||||
def test_넘김_알림은_결과_한_줄에_합침():
|
||||
"""346-3 덧 — [저장] 뒤 「노선 밖 고침」 두 줄 → 한 줄 · 넘김은 조용히 수만 · 기본은 알림 그대로."""
|
||||
keys = _read("B06_Section/B06_Section_Station_Keys.ts")
|
||||
assert "{ quiet = false }: { quiet?: boolean } = {}," in keys
|
||||
assert "if (outside.size && !quiet)" in keys
|
||||
apply = _read("B05_Profile/B05_Profile_UI_RouteEdit_Apply.ts")
|
||||
assert "carryStationDrafts(projectId, fromRouteId, toRouteId, { quiet: true })" in apply
|
||||
|
||||
|
||||
def test_노선_반영_글자_없음():
|
||||
recommend = _read("B05_Profile/B05_Profile_UI_Panel_Recommend.ts")
|
||||
assert "노선 반영" not in recommend and "다시 계산은 [저장]에서" in recommend
|
||||
for rel in (
|
||||
"B05_Profile/B05_Profile_UI_RouteEdit.ts",
|
||||
"B05_Profile/B05_Profile_UI_RouteEdit_History.ts",
|
||||
"B05_Profile/B05_Profile_UI_RouteEdit_Draft.ts",
|
||||
"A00_Common/design/design_view_profile.ts",
|
||||
):
|
||||
text = _read(rel)
|
||||
assert "[노선 반영]" not in text and "[반영 전으로]" not in text, rel
|
||||
|
||||
@@ -126,3 +126,17 @@ def test_확정_중_노선이_바뀌면_다시_세우고_확정은_다시(out):
|
||||
calls = out["routeConfirmCalls"]
|
||||
assert calls[1] == "route" and calls[-2:] == ["rebuild", "overlay:off"]
|
||||
assert "done:true" not in calls and "cross:true" not in calls
|
||||
|
||||
|
||||
def test_노선_초안_칸_계획고가_새_노선으로(out):
|
||||
"""346-3 덧 — 노선 초안 동안 계획고는 노선 0 칸(sub7) · 노선 저장 뒤 그 칸이 새 노선 9 로 · 0 · 7 칸 다 비움."""
|
||||
assert out["draftRouteCalls"] == ['alignment:9:{"35.000":0.4}']
|
||||
assert out["draftRouteLeft"] == {"draft": None, "old": None}
|
||||
design = os.path.join(HERE, "..", "..", "A00_Common", "design")
|
||||
with open(os.path.join(design, "design_pipeline_page.ts"), encoding="utf-8") as f:
|
||||
pipeline = f.read()
|
||||
with open(os.path.join(design, "design_save.ts"), encoding="utf-8") as f:
|
||||
save = f.read()
|
||||
assert "const DRAFT_ROUTE_ID = 0;" in save
|
||||
if "export const DRAFT_ROUTE_ID" in pipeline: # 합친 뒤 — 두 값이 같아야 함
|
||||
assert "export const DRAFT_ROUTE_ID = 0;" in pipeline
|
||||
|
||||
Reference in New Issue
Block a user