B07 WebCAD 도입 Phase 1 PoC 기초 환경 및 뷰어 셸 구성
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
/* =============================================================================
|
||||
* B07_wf4_DesignDetail_Api_Fetch.ts
|
||||
* 4차 워크플로우(상세 설계) API 클라이언트 — WebCAD PoC 임시 계약
|
||||
*
|
||||
* 백엔드 계약 (B07_wf4_DesignDetail_Router.py):
|
||||
* GET /api/b07/poc/sample-drawing → 샘플 종단·횡단 도면 기하 JSON
|
||||
* GET /api/b07/poc/sample-drawing/dxf → 동일 도면 DXF 다운로드
|
||||
*
|
||||
* ⚠️ PoC 전용: Phase 2에서 실제 B06 산출 데이터 계약으로 대체된다.
|
||||
* 규칙: 오류 응답 {status:"error", message:"..."}을 Error로 변환.
|
||||
* ========================================================================== */
|
||||
|
||||
import { API_BASE_URL, API_TIMEOUT_MS } from "@config/config_frontend";
|
||||
|
||||
export interface CadLayerJson {
|
||||
name: string;
|
||||
color_aci: number;
|
||||
}
|
||||
|
||||
export interface CadEntityJson {
|
||||
type: "LWPOLYLINE" | "LINE" | "TEXT";
|
||||
layer: string;
|
||||
/** LWPOLYLINE */
|
||||
points?: [number, number][];
|
||||
closed?: boolean;
|
||||
/** LINE */
|
||||
start?: [number, number];
|
||||
end?: [number, number];
|
||||
/** TEXT */
|
||||
text?: string;
|
||||
insert?: [number, number];
|
||||
height?: number;
|
||||
rotation?: number;
|
||||
}
|
||||
|
||||
export interface CadDrawingJson {
|
||||
layers: CadLayerJson[];
|
||||
entities: CadEntityJson[];
|
||||
}
|
||||
|
||||
/** 공통 fetch 헬퍼: 타임아웃 + 인증 쿠키 + 오류 응답 변환. */
|
||||
async function requestJson<T>(path: string): Promise<T> {
|
||||
const controller = new AbortController();
|
||||
const timeoutId = window.setTimeout(() => controller.abort(), API_TIMEOUT_MS);
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}${path}`, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
signal: controller.signal,
|
||||
});
|
||||
const payload = (await response.json()) as T & { message?: string };
|
||||
if (!response.ok) {
|
||||
throw new Error(payload.message ?? `HTTP ${response.status}`);
|
||||
}
|
||||
return payload;
|
||||
} finally {
|
||||
window.clearTimeout(timeoutId);
|
||||
}
|
||||
}
|
||||
|
||||
/** PoC 샘플 도면(종단·횡단 모사)의 기하 JSON을 조회한다. */
|
||||
export async function fetchPocSampleDrawing(stations?: number): Promise<CadDrawingJson> {
|
||||
const query = stations === undefined ? "" : `?stations=${stations}`;
|
||||
return requestJson<CadDrawingJson>(`/b07/poc/sample-drawing${query}`);
|
||||
}
|
||||
|
||||
/** PoC 샘플 도면 DXF 다운로드 URL (ezdxf 쓰기 왕복 검증용). */
|
||||
export function getPocSampleDrawingDxfUrl(): string {
|
||||
return `${API_BASE_URL}/b07/poc/sample-drawing/dxf`;
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
"""B07 상세 설계 FastAPI 라우터 — WebCAD PoC 임시 API.
|
||||
|
||||
⚠️ PoC 전용: DB·워크플로우 연동 없음. 종단·횡단도 형태를 모사한 샘플 도면을
|
||||
ezdxf(MIT)로 생성하여 (1) 기하 JSON, (2) DXF 파일로 제공한다.
|
||||
브라우저는 DXF를 파싱하지 않고 JSON만 렌더링한다 (GPL 배제 아키텍처).
|
||||
Phase 2 본 구현 시 실제 B06 산출 데이터 기반으로 대체된다.
|
||||
"""
|
||||
|
||||
import io
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
import ezdxf
|
||||
from fastapi import APIRouter, Query
|
||||
from fastapi.responses import Response
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
router = APIRouter(prefix="/api/b07", tags=["B07 Design Detail"])
|
||||
|
||||
# PoC 샘플 도면 레이어 구성 (name, ACI 색번호)
|
||||
_POC_LAYERS: list[tuple[str, int]] = [
|
||||
("GRID", 8), # 격자 — 회색
|
||||
("GROUND", 42), # 자연 지반선 — 갈색 계열
|
||||
("DESIGN", 1), # 계획선 — 빨강
|
||||
("CENTER", 4), # 중심선 — 하늘색
|
||||
("LABEL", 7), # 문자 — 흰색/검정
|
||||
]
|
||||
|
||||
_VERTICAL_EXAGGERATION = 2.0
|
||||
_BASE_ELEVATION = 100.0
|
||||
|
||||
|
||||
def _ground_elevation(station: float) -> float:
|
||||
"""샘플 자연 지반고(측점 거리 기반 결정적 파형)."""
|
||||
import math
|
||||
|
||||
return 102.0 + station * 0.05 + 1.8 * math.sin(station / 37.0) + 0.6 * math.sin(station / 11.0)
|
||||
|
||||
|
||||
def _design_elevation(station: float) -> float:
|
||||
"""샘플 계획고(직선 종단 기울기 4%)."""
|
||||
return 102.5 + station * 0.04
|
||||
|
||||
|
||||
def _profile_y(elevation: float) -> float:
|
||||
"""표고 → 종단면도 Y 좌표(수직 과장 적용)."""
|
||||
return (elevation - _BASE_ELEVATION) * _VERTICAL_EXAGGERATION
|
||||
|
||||
|
||||
def _build_sample_doc(stations: int, interval: float) -> "ezdxf.document.Drawing":
|
||||
"""종단면도 + 횡단면도(3개소)를 모사한 샘플 DXF 문서 생성."""
|
||||
doc = ezdxf.new("R2010", setup=True)
|
||||
for name, aci in _POC_LAYERS:
|
||||
doc.layers.add(name, color=aci)
|
||||
msp = doc.modelspace()
|
||||
|
||||
length = (stations - 1) * interval
|
||||
ground = [(i * interval, _ground_elevation(i * interval)) for i in range(stations)]
|
||||
design = [(i * interval, _design_elevation(i * interval)) for i in range(stations)]
|
||||
top = max(_profile_y(e) for _, e in ground) + 8.0
|
||||
|
||||
# --- 종단면도: 격자(측점 세로선 + 기준 가로선) ---
|
||||
msp.add_lwpolyline(
|
||||
[(0, 0), (length, 0), (length, top), (0, top)],
|
||||
close=True,
|
||||
dxfattribs={"layer": "GRID"},
|
||||
)
|
||||
for x, _ in ground:
|
||||
msp.add_line((x, 0), (x, top), dxfattribs={"layer": "GRID"})
|
||||
|
||||
# --- 지반선·계획선 ---
|
||||
msp.add_lwpolyline([(x, _profile_y(e)) for x, e in ground], dxfattribs={"layer": "GROUND"})
|
||||
msp.add_lwpolyline([(x, _profile_y(e)) for x, e in design], dxfattribs={"layer": "DESIGN"})
|
||||
|
||||
# --- 측점 라벨·표고 문자 ---
|
||||
for index, (x, elevation) in enumerate(ground):
|
||||
msp.add_text(
|
||||
f"No.{index}",
|
||||
height=1.6,
|
||||
dxfattribs={"layer": "LABEL", "insert": (x - 2.0, -4.0)},
|
||||
)
|
||||
msp.add_text(
|
||||
f"{elevation:.2f}",
|
||||
height=1.2,
|
||||
rotation=90,
|
||||
dxfattribs={"layer": "LABEL", "insert": (x + 0.5, _profile_y(elevation) + 1.5)},
|
||||
)
|
||||
msp.add_text(
|
||||
"종단면도 (PoC)",
|
||||
height=3.0,
|
||||
dxfattribs={"layer": "LABEL", "insert": (length / 2 - 12.0, top + 4.0)},
|
||||
)
|
||||
|
||||
# --- 횡단면도 3개소 (종단면도 아래 배치) ---
|
||||
section_base_y = -30.0
|
||||
half_width = 10.0
|
||||
road_half = 2.0
|
||||
for section_no in range(3):
|
||||
cx = section_no * 34.0 + 10.0
|
||||
design_y = section_base_y + 4.0
|
||||
points = []
|
||||
for offset_step in range(-5, 6):
|
||||
offset = offset_step * (half_width / 5.0)
|
||||
rel = 0.12 * offset + 0.02 * offset * offset * (1 if section_no % 2 else -1)
|
||||
points.append((cx + offset, design_y + rel + 1.2))
|
||||
msp.add_lwpolyline(points, dxfattribs={"layer": "GROUND"})
|
||||
msp.add_line(
|
||||
(cx - road_half, design_y),
|
||||
(cx + road_half, design_y),
|
||||
dxfattribs={"layer": "DESIGN"},
|
||||
)
|
||||
msp.add_line(
|
||||
(cx, design_y - 2.5),
|
||||
(cx, design_y + 4.5),
|
||||
dxfattribs={"layer": "CENTER"},
|
||||
)
|
||||
msp.add_text(
|
||||
f"No.{section_no} 횡단",
|
||||
height=1.4,
|
||||
dxfattribs={"layer": "LABEL", "insert": (cx - 4.0, section_base_y - 3.0)},
|
||||
)
|
||||
return doc
|
||||
|
||||
|
||||
def _doc_to_geometry_json(doc: "ezdxf.document.Drawing") -> dict[str, Any]:
|
||||
"""DXF 문서 → 뷰어 렌더링용 기하 JSON (LWPOLYLINE/LINE/TEXT만 사용)."""
|
||||
entities: list[dict[str, Any]] = []
|
||||
for entity in doc.modelspace():
|
||||
kind = entity.dxftype()
|
||||
if kind == "LWPOLYLINE":
|
||||
entities.append(
|
||||
{
|
||||
"type": "LWPOLYLINE",
|
||||
"layer": entity.dxf.layer,
|
||||
"points": [[round(x, 4), round(y, 4)] for x, y, *_ in entity.get_points()],
|
||||
"closed": bool(entity.closed),
|
||||
}
|
||||
)
|
||||
elif kind == "LINE":
|
||||
entities.append(
|
||||
{
|
||||
"type": "LINE",
|
||||
"layer": entity.dxf.layer,
|
||||
"start": [round(entity.dxf.start.x, 4), round(entity.dxf.start.y, 4)],
|
||||
"end": [round(entity.dxf.end.x, 4), round(entity.dxf.end.y, 4)],
|
||||
}
|
||||
)
|
||||
elif kind == "TEXT":
|
||||
entities.append(
|
||||
{
|
||||
"type": "TEXT",
|
||||
"layer": entity.dxf.layer,
|
||||
"text": entity.dxf.text,
|
||||
"insert": [round(entity.dxf.insert.x, 4), round(entity.dxf.insert.y, 4)],
|
||||
"height": entity.dxf.height,
|
||||
"rotation": entity.dxf.rotation,
|
||||
}
|
||||
)
|
||||
return {
|
||||
"layers": [{"name": name, "color_aci": aci} for name, aci in _POC_LAYERS],
|
||||
"entities": entities,
|
||||
}
|
||||
|
||||
|
||||
@router.get("/poc/sample-drawing")
|
||||
async def get_poc_sample_drawing(
|
||||
stations: int = Query(default=11, ge=2, le=2000),
|
||||
interval: float = Query(default=20.0, gt=0, le=100.0),
|
||||
) -> dict[str, Any]:
|
||||
"""샘플 종단·횡단 도면의 기하 JSON을 반환한다 (stations 증가로 부하 테스트 가능)."""
|
||||
doc = _build_sample_doc(stations, interval)
|
||||
payload = _doc_to_geometry_json(doc)
|
||||
logger.info("B07 PoC sample drawing generated: %d entities", len(payload["entities"]))
|
||||
return payload
|
||||
|
||||
|
||||
@router.get("/poc/sample-drawing/dxf")
|
||||
async def download_poc_sample_drawing(
|
||||
stations: int = Query(default=11, ge=2, le=2000),
|
||||
interval: float = Query(default=20.0, gt=0, le=100.0),
|
||||
) -> Response:
|
||||
"""동일 샘플 도면을 DXF 파일로 반환한다 (ezdxf 쓰기 왕복 검증용)."""
|
||||
doc = _build_sample_doc(stations, interval)
|
||||
buffer = io.StringIO()
|
||||
doc.write(buffer)
|
||||
return Response(
|
||||
content=buffer.getvalue().encode("utf-8"),
|
||||
media_type="application/dxf",
|
||||
headers={"Content-Disposition": 'attachment; filename="b07_poc_sample.dxf"'},
|
||||
)
|
||||
@@ -0,0 +1,119 @@
|
||||
/* =============================================================================
|
||||
* B07_wf4_DesignDetail_UI_CadViewer.ts
|
||||
* WebCAD PoC 뷰어 — cad-simple-viewer(MIT) 부트스트랩 + 기하 JSON 렌더링
|
||||
*
|
||||
* 아키텍처 (PLAN.md WebCAD 합의):
|
||||
* - 브라우저는 DXF/DWG를 파싱하지 않는다. GPL 컨버터는 vite 별칭 스텁으로 차단.
|
||||
* - 서버(ezdxf)가 보낸 기하 JSON을 data-model(MIT) API로 도면 DB에 직접 구성.
|
||||
* - AcApDocManager는 싱글톤이므로 뷰어 컨테이너를 모듈 수준에 유지하고
|
||||
* 페이지 재진입 시 새 레이아웃에 재부착한다.
|
||||
* ========================================================================== */
|
||||
|
||||
import { AcApDocManager } from "@mlightcad/cad-simple-viewer";
|
||||
import {
|
||||
AcCmColor,
|
||||
AcDbLayerTableRecord,
|
||||
AcDbLine,
|
||||
AcDbPolyline,
|
||||
AcDbText,
|
||||
AcGePoint2d,
|
||||
AcGePoint3d,
|
||||
} from "@mlightcad/data-model";
|
||||
import type { CadDrawingJson, CadEntityJson } from "./B07_wf4_DesignDetail_Api_Fetch";
|
||||
|
||||
/** 뷰어 캔버스를 담는 영속 컨테이너 (SPA 라우팅 간 유지) */
|
||||
let viewerHolder: HTMLDivElement | null = null;
|
||||
/** 도면 JSON이 이미 DB에 적재되었는지 여부 (PoC: 1회 적재) */
|
||||
let drawingLoaded = false;
|
||||
|
||||
function ensureViewerHolder(): HTMLDivElement {
|
||||
if (!viewerHolder) {
|
||||
viewerHolder = document.createElement("div");
|
||||
viewerHolder.className = "b07-cad-canvas";
|
||||
}
|
||||
return viewerHolder;
|
||||
}
|
||||
|
||||
function ensureDocManager(holder: HTMLDivElement): AcApDocManager {
|
||||
try {
|
||||
return AcApDocManager.instance;
|
||||
} catch {
|
||||
// 최초 1회 생성 — 워커 미배포 환경이므로 MTEXT는 메인 스레드 렌더링 사용.
|
||||
AcApDocManager.createInstance({
|
||||
container: holder,
|
||||
autoResize: true,
|
||||
useMainThreadDraw: true,
|
||||
});
|
||||
return AcApDocManager.instance;
|
||||
}
|
||||
}
|
||||
|
||||
function toEntity(json: CadEntityJson): AcDbLine | AcDbPolyline | AcDbText | null {
|
||||
if (json.type === "LWPOLYLINE" && json.points && json.points.length >= 2) {
|
||||
const polyline = new AcDbPolyline();
|
||||
json.points.forEach(([x, y], index) => {
|
||||
polyline.addVertexAt(index, new AcGePoint2d(x, y));
|
||||
});
|
||||
polyline.closed = json.closed ?? false;
|
||||
polyline.layer = json.layer;
|
||||
return polyline;
|
||||
}
|
||||
if (json.type === "LINE" && json.start && json.end) {
|
||||
const line = new AcDbLine(
|
||||
new AcGePoint3d(json.start[0], json.start[1], 0),
|
||||
new AcGePoint3d(json.end[0], json.end[1], 0),
|
||||
);
|
||||
line.layer = json.layer;
|
||||
return line;
|
||||
}
|
||||
if (json.type === "TEXT" && json.text && json.insert) {
|
||||
const text = new AcDbText();
|
||||
text.textString = json.text;
|
||||
text.position = new AcGePoint3d(json.insert[0], json.insert[1], 0);
|
||||
text.height = json.height ?? 2.5;
|
||||
text.rotation = ((json.rotation ?? 0) * Math.PI) / 180;
|
||||
text.layer = json.layer;
|
||||
return text;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function loadDrawingIntoDatabase(manager: AcApDocManager, drawing: CadDrawingJson): number {
|
||||
const db = manager.curDocument.database;
|
||||
db.createDefaultData();
|
||||
|
||||
for (const layerJson of drawing.layers) {
|
||||
const color = new AcCmColor();
|
||||
color.colorIndex = layerJson.color_aci;
|
||||
db.tables.layerTable.add(new AcDbLayerTableRecord({ name: layerJson.name, color }));
|
||||
}
|
||||
|
||||
const modelSpace = db.tables.blockTable.modelSpace;
|
||||
let appended = 0;
|
||||
for (const entityJson of drawing.entities) {
|
||||
const entity = toEntity(entityJson);
|
||||
if (entity) {
|
||||
modelSpace.appendEntity(entity);
|
||||
appended += 1;
|
||||
}
|
||||
}
|
||||
return appended;
|
||||
}
|
||||
|
||||
/**
|
||||
* PoC CAD 뷰어를 host에 부착하고 서버 기하 JSON을 렌더링한다.
|
||||
* @returns 적재된 엔티티 수 (재진입 시 0 — 이미 적재됨)
|
||||
*/
|
||||
export function mountPocCadViewer(host: HTMLElement, drawing: CadDrawingJson): number {
|
||||
const holder = ensureViewerHolder();
|
||||
host.append(holder);
|
||||
|
||||
const manager = ensureDocManager(holder);
|
||||
let appended = 0;
|
||||
if (!drawingLoaded) {
|
||||
appended = loadDrawingIntoDatabase(manager, drawing);
|
||||
drawingLoaded = true;
|
||||
}
|
||||
manager.curView.zoomToFitDrawing();
|
||||
return appended;
|
||||
}
|
||||
@@ -1,26 +1,90 @@
|
||||
/* =============================================================================
|
||||
* B07_wf4_DesignDetail_UI_Page.ts
|
||||
* 로그인 후 07: 4차 워크플로우 (상세 설계)
|
||||
* 로그인 후 07: 4차 워크플로우 (상세 설계) — WebCAD PoC
|
||||
*
|
||||
* ⚠️ 본문 준비 중 — 워크플로우 셸(헤더+스텝바)만 구성. 추후 구체화.
|
||||
* 레이아웃 (PLAN.md WebCAD 사용자 지시):
|
||||
* - 사이드 패널: 빈 상태 유지 (B06→B07 전달 데이터 스펙 미확정)
|
||||
* - 상세 페이지 영역: CAD 뷰어 (서버 기하 JSON 렌더링, DXF 파싱 없음)
|
||||
* 제약 준수 (frontend.md §2 3단 레이아웃): createWorkflowLayout 재사용.
|
||||
* ========================================================================== */
|
||||
|
||||
import "./B07_wf4_DesignDetail_UI_Style.css";
|
||||
import { ui_locales, currentLanguageIndex } from "@ui/ui_template_locale";
|
||||
import { renderPendingWorkflow, workflowSteps } from "../A00_Common/b_page_scaffold";
|
||||
import { createWorkflowLayout } from "@ui/ui_template_workflow_layout";
|
||||
import { CURRENT_PROJECT_ID_KEY } from "@config/config_frontend";
|
||||
import { workflowSteps } from "../A00_Common/b_page_scaffold";
|
||||
import {
|
||||
fetchWorkflowState,
|
||||
goToWorkflowStage,
|
||||
WORKFLOW_STEP_ROUTES,
|
||||
type WorkflowState,
|
||||
} from "../A00_Common/b_workflow_nav";
|
||||
import { fetchPocSampleDrawing } from "./B07_wf4_DesignDetail_Api_Fetch";
|
||||
import { mountPocCadViewer } from "./B07_wf4_DesignDetail_UI_CadViewer";
|
||||
|
||||
/** locale 헬퍼 */
|
||||
function L(key: keyof typeof ui_locales): string {
|
||||
return ui_locales[key][currentLanguageIndex];
|
||||
}
|
||||
|
||||
/** 빈 사이드 패널 (전달 데이터 확정 후 구성 예정) */
|
||||
function buildEmptySidePanel(): HTMLDivElement {
|
||||
const panel = document.createElement("div");
|
||||
panel.className = "b07-side-empty";
|
||||
const icon = document.createElement("div");
|
||||
icon.className = "b07-side-empty__icon";
|
||||
icon.textContent = "🏗️";
|
||||
const msg = document.createElement("p");
|
||||
msg.className = "b07-side-empty__msg";
|
||||
msg.textContent = L("B07_Cad_Side_Pending");
|
||||
panel.append(icon, msg);
|
||||
return panel;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* 페이지 진입점
|
||||
* -------------------------------------------------------------------------- */
|
||||
export async function renderB07DesignDetail(root: HTMLElement): Promise<void> {
|
||||
await renderPendingWorkflow(root, {
|
||||
const projectId = localStorage.getItem(CURRENT_PROJECT_ID_KEY);
|
||||
let workflowState: WorkflowState | undefined;
|
||||
if (projectId) {
|
||||
try {
|
||||
workflowState = await fetchWorkflowState(projectId);
|
||||
} catch {
|
||||
/* 조회 실패 시 stages 미전달 → 전체 이동 허용 */
|
||||
}
|
||||
}
|
||||
|
||||
const cadHost = document.createElement("div");
|
||||
cadHost.className = "b07-cad-host";
|
||||
const status = document.createElement("div");
|
||||
status.className = "b07-cad-status";
|
||||
status.textContent = L("B07_Cad_Loading");
|
||||
cadHost.append(status);
|
||||
|
||||
const layout = createWorkflowLayout({
|
||||
title: L("B07_Design_Title"),
|
||||
steps: workflowSteps(),
|
||||
activeStep: 4,
|
||||
leftPanel: buildEmptySidePanel(),
|
||||
mainContent: cadHost,
|
||||
stages: workflowState?.stages,
|
||||
currentStage: workflowState?.current_stage,
|
||||
routes: WORKFLOW_STEP_ROUTES,
|
||||
onStepClick: (stepIndex) => {
|
||||
if (projectId) goToWorkflowStage(projectId, WORKFLOW_STEP_ROUTES[stepIndex]);
|
||||
},
|
||||
});
|
||||
layout.root.classList.add("b07-design-layout");
|
||||
root.replaceChildren(layout.root);
|
||||
|
||||
try {
|
||||
const drawing = await fetchPocSampleDrawing();
|
||||
mountPocCadViewer(cadHost, drawing);
|
||||
status.remove();
|
||||
} catch (error) {
|
||||
status.textContent = `${L("B07_Cad_Load_Failed")} ${
|
||||
error instanceof Error ? error.message : ""
|
||||
}`.trim();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/* =============================================================================
|
||||
* B07_wf4_DesignDetail_UI_Style.css
|
||||
* 상세 설계(WebCAD PoC) 화면 스타일 — theme.css 변수만 사용
|
||||
* ========================================================================== */
|
||||
|
||||
/* 워크플로우 레이아웃 높이 (B06 패턴 준수) */
|
||||
.b07-design-layout {
|
||||
height: calc(100vh - var(--spacing-64));
|
||||
height: calc(100dvh - var(--spacing-64));
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.b07-design-layout .ui-workflow-layout__body,
|
||||
.b07-design-layout .ui-workflow-layout__main {
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
/* 빈 사이드 패널 (B06 전달 데이터 확정 후 구성 예정) */
|
||||
.b07-side-empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--spacing-16);
|
||||
height: 100%;
|
||||
min-height: 200px;
|
||||
padding: var(--spacing-24);
|
||||
border: 1px dashed var(--color-border);
|
||||
border-radius: var(--radius-cards);
|
||||
color: var(--color-text-muted);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.b07-side-empty__icon {
|
||||
font-size: 32px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.b07-side-empty__msg {
|
||||
font-size: var(--text-body-sm);
|
||||
}
|
||||
|
||||
/* CAD 뷰어 호스트 (상세 페이지 영역) */
|
||||
.b07-cad-host {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 420px;
|
||||
overflow: hidden;
|
||||
border-radius: var(--radius-cards);
|
||||
background-color: var(--color-surface);
|
||||
}
|
||||
|
||||
.b07-cad-canvas {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
}
|
||||
|
||||
.b07-cad-canvas canvas {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 로딩·오류 상태 메시지 */
|
||||
.b07-cad-status {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--color-text-muted);
|
||||
font-size: var(--text-body-sm);
|
||||
pointer-events: none;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/* =============================================================================
|
||||
* config_cad_gpl_stub.ts
|
||||
* GPL 차단 스텁 — @mlightcad/dxf-json-converter / @mlightcad/libredwg-converter 대체
|
||||
*
|
||||
* cad-simple-viewer(MIT)는 브라우저에서 DXF/DWG 파일을 직접 파싱하기 위해
|
||||
* GPL-3.0 컨버터 패키지를 import 하지만, 상용 배포본에 GPL 코드를 포함하지 않기
|
||||
* 위해 vite.config.ts 별칭으로 이 스텁이 대신 번들된다.
|
||||
*
|
||||
* 도면 파싱은 전적으로 백엔드(ezdxf, MIT)가 수행하고 브라우저는 기하 JSON만
|
||||
* 받으므로, 이 컨버터가 호출될 일은 없다. cad-simple-viewer의 registerConverters()는
|
||||
* 생성자 예외를 try/catch로 흡수하므로 뷰어 코어는 정상 동작한다.
|
||||
* ========================================================================== */
|
||||
|
||||
const GPL_STUB_MESSAGE =
|
||||
"브라우저 내 DXF/DWG 파싱은 비활성화되어 있습니다 (GPL-3.0 패키지 제외). " +
|
||||
"도면은 서버(ezdxf)에서 JSON으로 변환되어 전달됩니다.";
|
||||
|
||||
export class AcDbDxfConverter {
|
||||
constructor() {
|
||||
throw new Error(GPL_STUB_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
export class AcDbLibreDwgConverter {
|
||||
constructor() {
|
||||
throw new Error(GPL_STUB_MESSAGE);
|
||||
}
|
||||
}
|
||||
Generated
+118
@@ -8,6 +8,9 @@
|
||||
"name": "forest-road-webapp",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@mlightcad/cad-simple-viewer": "^1.5.7",
|
||||
"@mlightcad/data-model": "~1.10.7",
|
||||
"lodash-es": "4.17.21",
|
||||
"maplibre-gl": "^5.24.0",
|
||||
"three": "^0.185.0"
|
||||
},
|
||||
@@ -60,6 +63,15 @@
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@lukeed/csprng": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz",
|
||||
"integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/jsonlint-lines-primitives": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.3.tgz",
|
||||
@@ -173,6 +185,59 @@
|
||||
"pbf": "bin/pbf"
|
||||
}
|
||||
},
|
||||
"node_modules/@mlightcad/cad-simple-viewer": {
|
||||
"version": "1.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@mlightcad/cad-simple-viewer/-/cad-simple-viewer-1.5.7.tgz",
|
||||
"integrity": "sha512-6I65HubIkzb1oNJ7k2bF+v9mwnfp2qKUsezLp0ksPbWC5de3+tgu20qK/xB/GXyfl9JnG8NDUIXWtCgOQ+bVsg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@mlightcad/data-model": "^1.10.0",
|
||||
"@mlightcad/dxf-json-converter": "^1.10.0",
|
||||
"lodash-es": "4.17.21",
|
||||
"three": "^0.172.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@mlightcad/common": {
|
||||
"version": "1.10.7",
|
||||
"resolved": "https://registry.npmjs.org/@mlightcad/common/-/common-1.10.7.tgz",
|
||||
"integrity": "sha512-wCDbuE+qdqayDJnlxsGT0eSLo+ORpWPHqezDTHzo4NqFmC7U1GwQuHuKOjtqq5xYfPDm0C6MUqEFVaW8n85gwQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"loglevel": "^1.9.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@mlightcad/data-model": {
|
||||
"version": "1.10.7",
|
||||
"resolved": "https://registry.npmjs.org/@mlightcad/data-model/-/data-model-1.10.7.tgz",
|
||||
"integrity": "sha512-5dDzrs9SYbZVvHhfAc/+cd+ZSlXokf8JA/+MKjjmp75TTeBrYIDFv1fXiNqiz/ULU18h3bG9qoS9uFIhBBVkAg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@mlightcad/common": "1.10.7",
|
||||
"@mlightcad/geometry-engine": "3.10.7",
|
||||
"@mlightcad/graphic-interface": "3.10.7",
|
||||
"iconv-lite": "^0.7.0",
|
||||
"uid": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@mlightcad/geometry-engine": {
|
||||
"version": "3.10.7",
|
||||
"resolved": "https://registry.npmjs.org/@mlightcad/geometry-engine/-/geometry-engine-3.10.7.tgz",
|
||||
"integrity": "sha512-/Eje+NphvACTiKTDTfydUYp284udQ43rEUcpY2PUfmAXh0YiGGYodMLpPjX5G0VinhSQy+X+/F4q+h2Xn99Ehg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@mlightcad/common": "1.10.7"
|
||||
}
|
||||
},
|
||||
"node_modules/@mlightcad/graphic-interface": {
|
||||
"version": "3.10.7",
|
||||
"resolved": "https://registry.npmjs.org/@mlightcad/graphic-interface/-/graphic-interface-3.10.7.tgz",
|
||||
"integrity": "sha512-Yt3v4wg+k5RxOzMcbdKHNlLFcXuTX+wDWlm8UpydjHEcrc0RGzTC3sccecEEit53h/9fRAjId2qsEpl5wL3D8g==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@mlightcad/common": "1.10.7",
|
||||
"@mlightcad/geometry-engine": "3.10.7"
|
||||
}
|
||||
},
|
||||
"node_modules/@napi-rs/wasm-runtime": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz",
|
||||
@@ -591,6 +656,22 @@
|
||||
"integrity": "sha512-latSnyDNt/8zYUB6VIJ6PCh2jBjJX6gnDsoCZ7LyW7GkqrD51EWwa9qCoGixj8YqBtETQK/xY7OmpTF8xz1DdQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz",
|
||||
"integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/json-stringify-pretty-compact": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-4.0.0.tgz",
|
||||
@@ -864,6 +945,25 @@
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash-es": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/loglevel": {
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz",
|
||||
"integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/loglevel"
|
||||
}
|
||||
},
|
||||
"node_modules/maplibre-gl": {
|
||||
"version": "5.24.0",
|
||||
"resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.24.0.tgz",
|
||||
@@ -1077,6 +1177,12 @@
|
||||
"@rolldown/binding-win32-x64-msvc": "1.1.4"
|
||||
}
|
||||
},
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
@@ -1138,6 +1244,18 @@
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/uid": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz",
|
||||
"integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@lukeed/csprng": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz",
|
||||
|
||||
@@ -33,6 +33,7 @@ from B04_wf1_Surface.B04_wf1_Surface_Router_GIS import router as b04_surface_gis
|
||||
from B04_wf1_Surface.B04_wf1_Surface_Router_GIS import tiles_router
|
||||
from B05_wf2_Route.B05_wf2_Route_Router import router as b05_route_router
|
||||
from B06_wf3_ProfileCross.B06_wf3_ProfileCross_Router import router as b06_section_router
|
||||
from B07_wf4_DesignDetail.B07_wf4_DesignDetail_Router import router as b07_design_router
|
||||
from common_util.common_util_auth import require_company, verify_session
|
||||
from common_util.common_util_resource_monitor import sample_resources_loop
|
||||
from config.config_db import close_db_pool, get_db_pool, init_db_pool
|
||||
@@ -265,6 +266,7 @@ app.include_router(b04_surface_gis_router, dependencies=protected_with_company)
|
||||
app.include_router(tiles_router, dependencies=protected_with_company)
|
||||
app.include_router(b05_route_router, dependencies=protected_with_company)
|
||||
app.include_router(b06_section_router, dependencies=protected_with_company)
|
||||
app.include_router(b07_design_router, dependencies=protected_with_company)
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
Generated
+118
@@ -8,6 +8,9 @@
|
||||
"name": "forest-road-webapp",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@mlightcad/cad-simple-viewer": "^1.5.7",
|
||||
"@mlightcad/data-model": "~1.10.7",
|
||||
"lodash-es": "4.17.21",
|
||||
"maplibre-gl": "^5.24.0",
|
||||
"three": "^0.185.0"
|
||||
},
|
||||
@@ -24,6 +27,15 @@
|
||||
"dev": true,
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@lukeed/csprng": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz",
|
||||
"integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/jsonlint-lines-primitives": {
|
||||
"version": "2.0.3",
|
||||
"license": "MIT",
|
||||
@@ -113,6 +125,59 @@
|
||||
"pbf": "bin/pbf"
|
||||
}
|
||||
},
|
||||
"node_modules/@mlightcad/cad-simple-viewer": {
|
||||
"version": "1.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@mlightcad/cad-simple-viewer/-/cad-simple-viewer-1.5.7.tgz",
|
||||
"integrity": "sha512-6I65HubIkzb1oNJ7k2bF+v9mwnfp2qKUsezLp0ksPbWC5de3+tgu20qK/xB/GXyfl9JnG8NDUIXWtCgOQ+bVsg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@mlightcad/data-model": "^1.10.0",
|
||||
"@mlightcad/dxf-json-converter": "^1.10.0",
|
||||
"lodash-es": "4.17.21",
|
||||
"three": "^0.172.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@mlightcad/common": {
|
||||
"version": "1.10.7",
|
||||
"resolved": "https://registry.npmjs.org/@mlightcad/common/-/common-1.10.7.tgz",
|
||||
"integrity": "sha512-wCDbuE+qdqayDJnlxsGT0eSLo+ORpWPHqezDTHzo4NqFmC7U1GwQuHuKOjtqq5xYfPDm0C6MUqEFVaW8n85gwQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"loglevel": "^1.9.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@mlightcad/data-model": {
|
||||
"version": "1.10.7",
|
||||
"resolved": "https://registry.npmjs.org/@mlightcad/data-model/-/data-model-1.10.7.tgz",
|
||||
"integrity": "sha512-5dDzrs9SYbZVvHhfAc/+cd+ZSlXokf8JA/+MKjjmp75TTeBrYIDFv1fXiNqiz/ULU18h3bG9qoS9uFIhBBVkAg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@mlightcad/common": "1.10.7",
|
||||
"@mlightcad/geometry-engine": "3.10.7",
|
||||
"@mlightcad/graphic-interface": "3.10.7",
|
||||
"iconv-lite": "^0.7.0",
|
||||
"uid": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@mlightcad/geometry-engine": {
|
||||
"version": "3.10.7",
|
||||
"resolved": "https://registry.npmjs.org/@mlightcad/geometry-engine/-/geometry-engine-3.10.7.tgz",
|
||||
"integrity": "sha512-/Eje+NphvACTiKTDTfydUYp284udQ43rEUcpY2PUfmAXh0YiGGYodMLpPjX5G0VinhSQy+X+/F4q+h2Xn99Ehg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@mlightcad/common": "1.10.7"
|
||||
}
|
||||
},
|
||||
"node_modules/@mlightcad/graphic-interface": {
|
||||
"version": "3.10.7",
|
||||
"resolved": "https://registry.npmjs.org/@mlightcad/graphic-interface/-/graphic-interface-3.10.7.tgz",
|
||||
"integrity": "sha512-Yt3v4wg+k5RxOzMcbdKHNlLFcXuTX+wDWlm8UpydjHEcrc0RGzTC3sccecEEit53h/9fRAjId2qsEpl5wL3D8g==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@mlightcad/common": "1.10.7",
|
||||
"@mlightcad/geometry-engine": "3.10.7"
|
||||
}
|
||||
},
|
||||
"node_modules/@oxc-project/types": {
|
||||
"version": "0.137.0",
|
||||
"dev": true,
|
||||
@@ -220,6 +285,22 @@
|
||||
"version": "3.4.4",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz",
|
||||
"integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/json-stringify-pretty-compact": {
|
||||
"version": "4.0.0",
|
||||
"license": "MIT"
|
||||
@@ -275,6 +356,25 @@
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash-es": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/loglevel": {
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz",
|
||||
"integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/loglevel"
|
||||
}
|
||||
},
|
||||
"node_modules/maplibre-gl": {
|
||||
"version": "5.24.0",
|
||||
"license": "BSD-3-Clause",
|
||||
@@ -460,6 +560,12 @@
|
||||
"@rolldown/binding-win32-x64-msvc": "1.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"dev": true,
|
||||
@@ -503,6 +609,18 @@
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/uid": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz",
|
||||
"integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@lukeed/csprng": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz",
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
"format": "node ./config/node_modules/prettier/bin/prettier.cjs --write \"../**/*.{ts,css,html}\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@mlightcad/cad-simple-viewer": "^1.5.7",
|
||||
"@mlightcad/data-model": "~1.10.7",
|
||||
"lodash-es": "4.17.21",
|
||||
"maplibre-gl": "^5.24.0",
|
||||
"three": "^0.185.0"
|
||||
},
|
||||
|
||||
@@ -9,6 +9,7 @@ click-plugins==1.1.1.2
|
||||
cligj==0.7.2
|
||||
colorama==0.4.6
|
||||
et_xmlfile==2.0.0
|
||||
ezdxf==1.4.4
|
||||
fastapi==0.104.1
|
||||
bcrypt>=4.1,<5
|
||||
email-validator>=2.1,<3
|
||||
|
||||
+3
-1
@@ -29,7 +29,9 @@
|
||||
"@util/*": ["common_util/*"],
|
||||
"three": ["config/node_modules/@types/three"],
|
||||
"three/*": ["config/node_modules/@types/three/*"],
|
||||
"maplibre-gl": ["config/node_modules/maplibre-gl"]
|
||||
"maplibre-gl": ["config/node_modules/maplibre-gl"],
|
||||
"@mlightcad/cad-simple-viewer": ["config/node_modules/@mlightcad/cad-simple-viewer"],
|
||||
"@mlightcad/data-model": ["config/node_modules/@mlightcad/data-model"]
|
||||
}
|
||||
},
|
||||
"include": ["A00_Common", "A0*", "B0*", "B1*", "ui_template", "config/**/*", "common_util"],
|
||||
|
||||
@@ -799,6 +799,12 @@ export const ui_locales = {
|
||||
|
||||
/* --- B07_wf4_DesignDetail 상세 설계 --- */
|
||||
B07_Design_Title: ["4차 · 상세 설계", "Step 4 · Detailed Design"],
|
||||
B07_Cad_Side_Pending: [
|
||||
"사이드 패널은 전달 데이터 확정 후 구성됩니다.",
|
||||
"Side panel will be configured after the upstream data spec is finalized.",
|
||||
],
|
||||
B07_Cad_Loading: ["도면을 불러오는 중...", "Loading drawing..."],
|
||||
B07_Cad_Load_Failed: ["도면을 불러오지 못했습니다.", "Failed to load drawing."],
|
||||
|
||||
/* --- B08_wf5_Quantity 수량 산출 --- */
|
||||
B08_Quantity_Title: ["5차 · 수량 산출", "Step 5 · Quantity Takeoff"],
|
||||
|
||||
@@ -21,6 +21,15 @@ export default {
|
||||
"@util": resolve(__dirname, "./common_util"),
|
||||
three: resolve(__dirname, "./config/node_modules/three"),
|
||||
"maplibre-gl": resolve(__dirname, "./config/node_modules/maplibre-gl"),
|
||||
// WebCAD(B07) — GPL 컨버터는 스텁으로 차단, MIT 패키지만 실제 번들
|
||||
"@mlightcad/dxf-json-converter": resolve(__dirname, "./config/config_cad_gpl_stub.ts"),
|
||||
"@mlightcad/libredwg-converter": resolve(__dirname, "./config/config_cad_gpl_stub.ts"),
|
||||
"@mlightcad/cad-simple-viewer": resolve(
|
||||
__dirname,
|
||||
"./config/node_modules/@mlightcad/cad-simple-viewer",
|
||||
),
|
||||
"@mlightcad/data-model": resolve(__dirname, "./config/node_modules/@mlightcad/data-model"),
|
||||
"lodash-es": resolve(__dirname, "./config/node_modules/lodash-es"),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
|
||||
Reference in New Issue
Block a user