260710_1
This commit is contained in:
@@ -2,10 +2,10 @@
|
||||
* B04_wf1_Surface_UI_Page.ts
|
||||
* 로그인 후 04: 1차 워크플로우 (지표면 모델 분석)
|
||||
*
|
||||
* 3단 레이아웃 (frontend.md §2):
|
||||
* 상단: 페이지 타이틀 + 진행 단계 스텝바 (createWorkflowShell)
|
||||
* 좌측: 입력 파일 ID + 지면 필터/지표면 표현 선택 + 실행 옵션 폼
|
||||
* 우측: 생성된 지표면 모델 목록 그리드
|
||||
* 데스크톱 IDE 스타일 레이아웃 (createWorkflowLayout):
|
||||
* 헤더: 페이지 타이틀 + 진행 단계 스텝바 (숨김/복원 토글)
|
||||
* 좌측 오버레이 패널: 입력 파일 자동 선택 + 지면 필터/지표면 표현 + 실행 옵션
|
||||
* 우측 메인: 3D 포인트클라우드 뷰어 + 지면 통계 + 지표면 모델 카드
|
||||
*
|
||||
* 이벤트 핸들러 명명 (frontend.md §4): onB04_Surface_[기능]_[액션]
|
||||
* 텍스트는 ui_template_locale에 선(先) 등록 후 참조 (frontend.md §3).
|
||||
@@ -15,9 +15,7 @@ import { CURRENT_PROJECT_ID_KEY } from "@config/config_frontend";
|
||||
import { currentLanguageIndex, ui_locales } from "@ui/ui_template_locale";
|
||||
import {
|
||||
createButton,
|
||||
createInputField,
|
||||
createTag,
|
||||
createWorkflowShell,
|
||||
hideLoadingOverlay,
|
||||
showLoadingOverlay,
|
||||
showToast,
|
||||
@@ -81,180 +79,6 @@ function buildCheckboxGroup(
|
||||
return { root, selected };
|
||||
}
|
||||
|
||||
export function renderB04SurfaceLegacy(root: HTMLElement): void {
|
||||
const shell = createWorkflowShell({
|
||||
title: L("B04_Surface_Title"),
|
||||
steps: workflowSteps(),
|
||||
activeStep: 0,
|
||||
});
|
||||
|
||||
/* ---- 좌측 입력 패널 ---- */
|
||||
const inputFileField = createInputField({
|
||||
label: L("B04_Surface_Field_InputId"),
|
||||
type: "number",
|
||||
min: 1,
|
||||
placeholder: L("B04_Surface_Field_InputId_Placeholder"),
|
||||
});
|
||||
|
||||
const filterGroup = buildCheckboxGroup(L("B04_Surface_Group_Filters"), SOURCE_FILTERS, [
|
||||
"grid_min_z",
|
||||
"csf",
|
||||
"pmf",
|
||||
]);
|
||||
const methodGroup = buildCheckboxGroup(L("B04_Surface_Group_Methods"), MODEL_METHODS, [
|
||||
"dtm",
|
||||
"tin",
|
||||
]);
|
||||
|
||||
const forceLabel = document.createElement("label");
|
||||
forceLabel.className = "b04-surface__check";
|
||||
const forceBox = document.createElement("input");
|
||||
forceBox.type = "checkbox";
|
||||
const forceText = document.createElement("span");
|
||||
forceText.textContent = L("B04_Surface_Field_Force");
|
||||
forceLabel.append(forceBox, forceText);
|
||||
|
||||
const analyzeButton = createButton({
|
||||
label: L("B04_Surface_Btn_Analyze"),
|
||||
variant: "filled",
|
||||
onClick: () => void onB04_Surface_Analyze_Click(),
|
||||
});
|
||||
|
||||
const leftForm = document.createElement("div");
|
||||
leftForm.className = "b04-surface__form";
|
||||
leftForm.append(
|
||||
inputFileField.root,
|
||||
filterGroup.root,
|
||||
methodGroup.root,
|
||||
forceLabel,
|
||||
analyzeButton,
|
||||
);
|
||||
shell.leftPanel.append(leftForm);
|
||||
|
||||
/* ---- 우측 결과 영역 ---- */
|
||||
const resultHeader = document.createElement("div");
|
||||
resultHeader.className = "b04-surface__result-head";
|
||||
const resultTitle = document.createElement("h3");
|
||||
resultTitle.textContent = L("B04_Surface_Result_Title");
|
||||
const refreshButton = createButton({
|
||||
label: L("B04_Surface_Btn_Refresh"),
|
||||
variant: "ghost",
|
||||
onClick: () => void onB04_Surface_Refresh_Click(),
|
||||
});
|
||||
resultHeader.append(resultTitle, refreshButton);
|
||||
|
||||
const modelList = document.createElement("div");
|
||||
modelList.className = "b04-surface__models";
|
||||
|
||||
const resultArea = document.createElement("div");
|
||||
resultArea.className = "b04-surface__result";
|
||||
resultArea.append(resultHeader, modelList);
|
||||
shell.rightArea.append(resultArea);
|
||||
|
||||
function renderModels(models: readonly SurfaceModelSummary[]): void {
|
||||
modelList.replaceChildren();
|
||||
if (models.length === 0) {
|
||||
const empty = document.createElement("p");
|
||||
empty.className = "b04-surface__empty";
|
||||
empty.textContent = L("B04_Surface_Result_Empty");
|
||||
modelList.append(empty);
|
||||
return;
|
||||
}
|
||||
for (const model of models) {
|
||||
const card = document.createElement("div");
|
||||
card.className = "b04-surface__model-card";
|
||||
const head = document.createElement("div");
|
||||
head.className = "b04-surface__model-head";
|
||||
const type = document.createElement("strong");
|
||||
type.textContent = model.model_type;
|
||||
const variant =
|
||||
model.status === "CONFIRMED" ? "success" : model.status === "FAILED" ? "danger" : "neutral";
|
||||
head.append(type, createTag(model.status, variant));
|
||||
|
||||
const meta = document.createElement("div");
|
||||
meta.className = "b04-surface__model-meta";
|
||||
const resolution = document.createElement("span");
|
||||
resolution.textContent = `${L("B04_Surface_Model_Resolution")}: ${model.resolution_m ?? "-"}`;
|
||||
const path = document.createElement("span");
|
||||
path.textContent = `${L("B04_Surface_Model_Path")}: ${model.model_file_path ?? "-"}`;
|
||||
meta.append(resolution, path);
|
||||
|
||||
card.append(head, meta);
|
||||
modelList.append(card);
|
||||
}
|
||||
}
|
||||
|
||||
function getProjectId(): string | null {
|
||||
const projectId = localStorage.getItem(CURRENT_PROJECT_ID_KEY);
|
||||
if (!projectId) {
|
||||
inputFileField.setError(L("B04_Surface_Error_Project"));
|
||||
showToast(L("B04_Surface_Error_Project"), "error");
|
||||
}
|
||||
return projectId;
|
||||
}
|
||||
|
||||
async function onB04_Surface_Analyze_Click(): Promise<void> {
|
||||
const projectId = getProjectId();
|
||||
if (!projectId) return;
|
||||
|
||||
const rawId = inputFileField.input.value.trim();
|
||||
const inputFileId = Number(rawId);
|
||||
if (!rawId || !Number.isInteger(inputFileId) || inputFileId <= 0) {
|
||||
inputFileField.setError(L("B04_Surface_Error_InputId"));
|
||||
return;
|
||||
}
|
||||
if (filterGroup.selected.size === 0 || methodGroup.selected.size === 0) {
|
||||
inputFileField.setError(L("B04_Surface_Error_Selection"));
|
||||
return;
|
||||
}
|
||||
inputFileField.setError();
|
||||
|
||||
showLoadingOverlay();
|
||||
try {
|
||||
const response = await analyzeSurface(projectId, {
|
||||
input_file_id: inputFileId,
|
||||
source_filters: [...filterGroup.selected],
|
||||
methods: [...methodGroup.selected],
|
||||
force: forceBox.checked,
|
||||
});
|
||||
showToast(
|
||||
`${L("B04_Surface_Analyze_Success")} (${response.surface_model_ids.length})`,
|
||||
"success",
|
||||
);
|
||||
await loadModels(projectId);
|
||||
} catch (error) {
|
||||
const detail = error instanceof Error ? error.message : L("B04_Surface_Analyze_Failed");
|
||||
inputFileField.setError(`${L("B04_Surface_Analyze_Failed")} ${detail}`);
|
||||
showToast(L("B04_Surface_Analyze_Failed"), "error");
|
||||
} finally {
|
||||
hideLoadingOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
async function loadModels(projectId: string): Promise<void> {
|
||||
showLoadingOverlay();
|
||||
try {
|
||||
const response = await listSurfaceModels(projectId);
|
||||
renderModels(response.models);
|
||||
} catch {
|
||||
showToast(L("B04_Surface_Load_Failed"), "error");
|
||||
} finally {
|
||||
hideLoadingOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
async function onB04_Surface_Refresh_Click(): Promise<void> {
|
||||
const projectId = getProjectId();
|
||||
if (projectId) await loadModels(projectId);
|
||||
}
|
||||
|
||||
renderModels([]);
|
||||
root.replaceChildren(shell.root);
|
||||
|
||||
const initialProjectId = localStorage.getItem(CURRENT_PROJECT_ID_KEY);
|
||||
if (initialProjectId) void loadModels(initialProjectId);
|
||||
}
|
||||
|
||||
function buildInfoLine(label: string, value: unknown): HTMLElement {
|
||||
const row = document.createElement("div");
|
||||
row.className = "b04-surface__line";
|
||||
|
||||
Reference in New Issue
Block a user