- [확정] 성공 길도 [저장]처럼 invalidateSectionDetail — 다음 페이지(B07)가 정본을 받음 - B06 폼 onPipeUpdate — 옮긴 뒤 옵션 · 캐시 얹기 · 집계 항목을 옮긴 자리(맞춘 정수 m)로 찾음(옛: 옛 자리로 찾아 옵션 잃음 · 비운 옛 자리 그림에 얹음) - 시험 test_466_pipe_move_options · test_272_detail_memory 확정 항목 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
255 lines
9.1 KiB
JavaScript
255 lines
9.1 KiB
JavaScript
/* 466 덧(470 N2) 헬퍼 — 진짜 `createB06StructuresPanel` 의 B06 폼 관 고침(`onPipeUpdate`)이 관을 0.51 m 넘게
|
|
* 옮기며 옵션도 고칠 때 옵션이 옮긴 관(초안)에 붙고 캐시 얹기도 새 자리인지. 가짜 모듈 틀 = helper_165_pavement_flow.cjs.
|
|
* 표준출력 JSON 한 벌 · 짝 = test_466_pipe_move_options.py. */
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript"));
|
|
const CATALOG = require("./helper_322_catalog_module.cjs"); // 구조물 표(322-1)
|
|
|
|
const ROOT = path.join(__dirname, "..", "..");
|
|
|
|
function loadTs(file, stubs = {}) {
|
|
const js = ts.transpileModule(fs.readFileSync(file, "utf8"), {
|
|
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
|
|
}).outputText;
|
|
const box = { exports: {} };
|
|
const localRequire = (request) => {
|
|
if (CATALOG.isCatalogRequest(request)) return CATALOG;
|
|
if (stubs[request]) return stubs[request];
|
|
throw new Error(`stub 없음: ${request}`);
|
|
};
|
|
new Function("exports", "module", "require", js)(box.exports, box, localRequire);
|
|
return box.exports;
|
|
}
|
|
|
|
// 세션(structure-pick) — 메모리 한 벌
|
|
const store = new Map();
|
|
const selection = loadTs(path.join(ROOT, "A00_Common", "design", "design_selection.ts"), {
|
|
"../b_page_state": {
|
|
readState: (key, pid) => store.get(`${key}:${pid}`) ?? null,
|
|
writeState: (key, value, pid) => store.set(`${key}:${pid}`, value),
|
|
},
|
|
});
|
|
|
|
const PID = "p165";
|
|
const TYPES = [
|
|
{ type_id: "pavement_concrete", group: "G" },
|
|
{ type_id: "hazard_sign", group: "E" },
|
|
{ type_id: "pipe", group: "A", managed_by: "pipe_points" },
|
|
];
|
|
const PAVE = {
|
|
structure_id: "pave",
|
|
type_id: "pavement_concrete",
|
|
placement: "interval",
|
|
chainage_m: 30,
|
|
start_m: 30,
|
|
end_m: 250,
|
|
};
|
|
const PAVE2 = {
|
|
structure_id: "pave2",
|
|
type_id: "pavement_concrete",
|
|
placement: "interval",
|
|
chainage_m: 100,
|
|
start_m: 100,
|
|
end_m: 200,
|
|
};
|
|
const PIPES = [{ chainage_m: 80.17, facility: "pipe", source: "spacing", options: {} }];
|
|
// 카드 = 20 m 측점 + 관 측점(비정규)
|
|
const CARDS = [...Array.from({ length: 16 }, (_, i) => i * 20), 80.17]
|
|
.sort((a, b) => a - b)
|
|
.map((at) => ({ station_id: `s${at}`, chainage_m: at }));
|
|
const cardAt = (at) => CARDS.find((card) => Math.abs(card.chainage_m - at) < 0.01);
|
|
|
|
/* ── 폼 흉내(B05 패널 · loadForm / loadPipeForm / resetForm 의 알림 차례) ── */
|
|
let binding = null;
|
|
let structures = [];
|
|
const form = { id: null, pipe: null };
|
|
const loadForm = (target) => {
|
|
form.id = target?.structure_id ?? null;
|
|
form.pipe = null;
|
|
binding.onSelect(target);
|
|
};
|
|
const loadPipeForm = (chainageM) => {
|
|
const hadStructure = form.id !== null;
|
|
form.id = null;
|
|
form.pipe = chainageM;
|
|
if (hadStructure) binding.onSelect(null);
|
|
};
|
|
const resetForm = () => {
|
|
const hadPipe = form.pipe !== null;
|
|
form.pipe = null;
|
|
loadForm(null);
|
|
if (hadPipe) binding.onPipeSelect(null);
|
|
};
|
|
const node = { addEventListener() {}, querySelector: () => ({ append() {} }) };
|
|
const section = {
|
|
root: node,
|
|
listRoot: node,
|
|
facility: { root: node, setRevetSideLabels() {}, recount() {} },
|
|
setTypes() {},
|
|
setStructures(next) {
|
|
structures = next;
|
|
},
|
|
setPipeFacilities() {},
|
|
hasSelection: () => form.id !== null || form.pipe !== null,
|
|
selectById(id) {
|
|
if (id === null) {
|
|
if (form.id !== null || form.pipe !== null) resetForm();
|
|
return;
|
|
}
|
|
loadForm(structures.find((entry) => entry.structure_id === id) ?? null);
|
|
},
|
|
selectPipeByChainage(chainageM) {
|
|
if (chainageM === null) {
|
|
if (form.pipe !== null) resetForm();
|
|
return;
|
|
}
|
|
loadPipeForm(chainageM);
|
|
},
|
|
};
|
|
|
|
const panelModule = loadTs(
|
|
path.join(ROOT, "B06_Section", "B06_Section_UI_Page_Structures_Panel.ts"),
|
|
{
|
|
"../A00_Common/design/design_shared": {
|
|
bindDesignView: (_shared, _view, parts) => {
|
|
binding = parts.structures;
|
|
},
|
|
sharedStructures: () => section,
|
|
},
|
|
"../B05_Profile/B05_Profile_Api_Structures": {
|
|
fetchStructureTypes: async () => TYPES,
|
|
fetchStructures: async () => ({ structures: [] }),
|
|
readPendingStructures: () => current.structures,
|
|
pipesToStructureMarks: (pipes) =>
|
|
pipes.map((pipe) => ({
|
|
structure_id: `pipe-${pipe.chainage_m.toFixed(2)}`,
|
|
type_id: pipe.facility,
|
|
chainage_m: pipe.chainage_m,
|
|
start_m: null,
|
|
end_m: null,
|
|
})),
|
|
structureAnchorM: (s) => s.chainage_m ?? s.start_m ?? 0,
|
|
writePendingStructures() {},
|
|
dropStaleStructureTypes: async () => {}, // 정의 판 확인(322-0)
|
|
staleDefinitionNotice: () => null, // 판 알림(322-2)
|
|
},
|
|
"../B05_Profile/B05_Profile_Api_Pipes_Draft": {
|
|
readPendingPipes: () => null,
|
|
writePendingPipes: (_pid, next) => (written = next),
|
|
},
|
|
"../B05_Profile/logic/B05_Profile_UI_Drainage_Facility_Merge": {
|
|
mergeFacilityOptions: (was, next) => ({
|
|
facility: next.facility,
|
|
options: { ...(was.options ?? {}), ...(next.options ?? {}) },
|
|
}),
|
|
},
|
|
"../B05_Profile/B05_Profile_UI_Drainage_Ford_Fill": {
|
|
fordWaitsOf: () => new Set(),
|
|
waitsFordHeight: () => false,
|
|
},
|
|
"../B04_PreProcess/B04_PreProcess_Api_Fetch": {
|
|
fetchDetailPipePoints: async () => ({ pipe_points: PIPES, basins: [] }),
|
|
},
|
|
// 페이지 자료 줄(386-2) — 저장분 한 번
|
|
"../A00_Common/design/design_page_data": {
|
|
pageTypesChecked: async () => false,
|
|
pageStructures: async () => ({ structures: [] }),
|
|
pagePipes: async () => ({ pipe_points: PIPES, basins: [] }),
|
|
},
|
|
"../common_util/common_util_summary_item": {
|
|
linkFields: () => ({}),
|
|
readGroupItems: (x) => x ?? {},
|
|
readSummaryItem: (x) => x ?? null,
|
|
},
|
|
"../B05_Profile/B05_Profile_UI_Structure_Pick_Session": { readStructurePick: () => null },
|
|
"../A00_Common/design/design_selection": selection,
|
|
"@ui/ui_template_elements": { showToast() {} },
|
|
// 관 기준 측점 정수 맞춤(234-1) — 일반 측점 없으면 정수 m 만
|
|
"../common_util/common_util_structure_station": {
|
|
snapStructureStation: (value) => Math.floor(value + 0.5),
|
|
},
|
|
"./B06_Section_UI_Adjust_Dock": {
|
|
adjustDockRoot: () => ({}),
|
|
refreshAdjustSlots() {},
|
|
resetAdjustDock() {},
|
|
setAdjustSideSlots() {},
|
|
},
|
|
"./B06_Section_UI_Page_Structures_Spec": { withSpecDefaults: (o) => o ?? {} },
|
|
// 옛 소수 관 맞춤(322-3 고침) · 관 세션 id(323-5) — 프로젝트 없음 = 옛 자리 이름
|
|
"../B05_Profile/B05_Profile_UI_Structures_Snap": {
|
|
snapNotice() {},
|
|
snapPlan: () => ({ structures: [], pipes: [], skipped: 0 }),
|
|
},
|
|
"../A00_Common/design/design_pipe_ids": {
|
|
pipeIdAt: (at) => `pipe-${at.toFixed(2)}`,
|
|
followMovedPipe() {},
|
|
},
|
|
// 요구 178 — 알약 측점 짝(진짜 `plantsStation` · 272 측점 심기 한 벌)
|
|
"../B05_Profile/B05_Profile_UI_Profile_Structures": loadTs(
|
|
path.join(ROOT, "B05_Profile", "B05_Profile_UI_Profile_Structures.ts"),
|
|
{
|
|
"../A00_Common/touch_bridge": { pickPx: (px) => px }, // 기기대응 4-4 — 마우스 반경 그대로
|
|
"@ui/ui_template_context_menu": { createMapContextMenu() {} },
|
|
"./B05_Profile_UI_IrregularStations": {
|
|
irregularStationId: (id) => `irregular:${id}`,
|
|
isPipeStation: () => false,
|
|
},
|
|
"./B05_Profile_Api_Structures": {
|
|
structureAnchorM: (s) => s.chainage_m ?? s.start_m ?? 0,
|
|
},
|
|
"../A00_Common/design/design_selection": selection,
|
|
"../common_util/common_util_structure_station": loadTs(
|
|
path.join(ROOT, "common_util", "common_util_structure_station.ts"),
|
|
),
|
|
},
|
|
),
|
|
},
|
|
);
|
|
|
|
const current = { structures: [] };
|
|
let panel = null;
|
|
let written = null;
|
|
const applied = [];
|
|
|
|
(async () => {
|
|
const out = {};
|
|
const run = async (from, to, options) => {
|
|
store.clear();
|
|
PIPES.splice(0, PIPES.length, {
|
|
chainage_m: 80.17,
|
|
facility: "pipe",
|
|
source: "spacing",
|
|
options: { pipe_diameter_mm: 800 },
|
|
});
|
|
written = null;
|
|
applied.length = 0;
|
|
panel = panelModule.createB06StructuresPanel({
|
|
projectId: PID,
|
|
shared: { view: () => "cross", lane: { set() {}, setTypes() {}, setStored() {} } },
|
|
detail: () => ({ cross_sections: CARDS }),
|
|
stationInterval: () => 20,
|
|
focusChainage() {},
|
|
markStructure() {},
|
|
onMarks() {},
|
|
applyPipeOptions: (at, patch) => applied.push([at, patch.pipe_diameter_mm ?? null]),
|
|
});
|
|
await panel.load();
|
|
binding.onPipeUpdate(from, to, { facility: "pipe", options });
|
|
return {
|
|
pipes: (written ?? []).map((pipe) => [
|
|
pipe.chainage_m,
|
|
pipe.options?.pipe_diameter_mm ?? null,
|
|
]),
|
|
applied: [...applied],
|
|
};
|
|
};
|
|
out.far = await run(80.17, 100.4, { pipe_diameter_mm: 1200 }); // 옮김 + 옵션
|
|
out.near = await run(80.17, 80, { pipe_diameter_mm: 1000 }); // 0.51 m 안 옮김 + 옵션
|
|
out.stay = await run(80.17, 80.17, { pipe_diameter_mm: 900 }); // 옵션만
|
|
process.stdout.write(JSON.stringify(out));
|
|
})().catch((error) => {
|
|
console.error(error);
|
|
process.exit(1);
|
|
});
|