Merge remote-tracking branch 'origin/sub_laptop_1' into sub_laptop_5
This commit is contained in:
@@ -204,6 +204,19 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 262-2 고정 시점 표지 — 둥근 붉은 알약(고르는 표지 = 기본 핀 + 흰 이름표와 구별) */
|
||||
.b08-milestone__map-start {
|
||||
padding: 3px 10px;
|
||||
border: 2px solid #fff;
|
||||
border-radius: 999px;
|
||||
background: #e60046;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
white-space: nowrap;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* 등록 · 연결선 모달(244-13) — 더 큰 창 · 2열 칸 */
|
||||
.ui-modal.b08-milestone__modal {
|
||||
width: min(960px, 94vw);
|
||||
|
||||
@@ -98,6 +98,8 @@ export interface MapPin {
|
||||
lon: number;
|
||||
lat: number;
|
||||
movable: boolean;
|
||||
/** 262-2 고정 시점 표지 — 끌기 · 맞춤 · 보기 이동에 안 쓰임 · 고르는 표지와 모양이 다름 */
|
||||
fixed?: boolean;
|
||||
}
|
||||
|
||||
export interface EditMap {
|
||||
@@ -127,8 +129,9 @@ export function editMap(
|
||||
loadKakaoMaps(projectId).then(
|
||||
(maps) =>
|
||||
requestAnimationFrame(() => {
|
||||
const moving = pins.filter((p) => !p.fixed);
|
||||
const points = [
|
||||
...pins.map((p) => new maps.LatLng(p.lat, p.lon)),
|
||||
...moving.map((p) => new maps.LatLng(p.lat, p.lon)),
|
||||
...lines.flat().map(([lat, lon]) => new maps.LatLng(lat, lon)),
|
||||
];
|
||||
const c = opts.center?.();
|
||||
@@ -156,6 +159,17 @@ export function editMap(
|
||||
),
|
||||
...pins.flatMap((p) => {
|
||||
const at = new maps.LatLng(p.lat, p.lon);
|
||||
if (p.fixed)
|
||||
return [
|
||||
new maps.CustomOverlay({
|
||||
position: at,
|
||||
content: el("span", {
|
||||
className: "b08-milestone__map-start",
|
||||
text: p.name,
|
||||
}),
|
||||
yAnchor: 0.5,
|
||||
}),
|
||||
];
|
||||
const marker = new maps.Marker({ position: at, title: p.name, draggable: p.movable });
|
||||
if (p.movable)
|
||||
maps.event.addListener(marker, "dragend", () => {
|
||||
@@ -176,7 +190,7 @@ export function editMap(
|
||||
if (fitted) {
|
||||
// 보기 밖 표지만 그쪽으로 옮김(줌 그대로)
|
||||
const view = map.getBounds();
|
||||
const out = pins.find((p) => !view.contain(new maps.LatLng(p.lat, p.lon)));
|
||||
const out = moving.find((p) => !view.contain(new maps.LatLng(p.lat, p.lon)));
|
||||
if (out) map.panTo(new maps.LatLng(out.lat, out.lon));
|
||||
} else if (points.length > 1) {
|
||||
const bounds = new maps.LatLngBounds();
|
||||
|
||||
@@ -106,8 +106,8 @@ export function openPlaceModal(
|
||||
const title = target.add
|
||||
? "요소 추가 — 기준점 · 거래처"
|
||||
: base
|
||||
? `기준점 — ${base.name || "기준점"}`
|
||||
: `거래처 — ${route!.name || "거래처"}`; // 254-3 표 머리 글(항목 정보) 없음
|
||||
? base.name || "기준점"
|
||||
: route!.name || "거래처"; // 262-1 「—」 앞 글자 없음
|
||||
openModal({
|
||||
title,
|
||||
closeLabel: "닫기",
|
||||
@@ -259,7 +259,7 @@ export interface SiteAddress {
|
||||
/** 시점(공사지) 상자 — 중점 측점(km · 저장은 m) · 시점 → 중점 도로 · 아래 카카오 지도(시점 · 중점 표지) */
|
||||
export function openSiteInfo(projectId: string): void {
|
||||
openModal({
|
||||
title: "공사지 — 시점 · 중점",
|
||||
title: "시점 · 중점",
|
||||
closeLabel: "닫기",
|
||||
dialogClass: "b08-milestone__modal",
|
||||
mount: (body, close, actions) => {
|
||||
|
||||
@@ -12,7 +12,7 @@ import { createButton, createInputField, el, showToast } from "@ui/ui_template_e
|
||||
import { searchAddress, type AddressCandidate } from "../B01_Dashboard/B01_Dashboard_Api_Fetch";
|
||||
import { request } from "./B08_Quantity_UI_Hydro";
|
||||
import { editMap } from "./B08_Quantity_UI_Milestone_KakaoMap";
|
||||
import { siteInfo } from "./B08_Quantity_UI_Milestone_State";
|
||||
import { liveMilestone, startPoint } from "./B08_Quantity_UI_Milestone_State";
|
||||
|
||||
export interface Place {
|
||||
address: string;
|
||||
@@ -52,27 +52,35 @@ export function placePicker(projectId: string, initial: Place, name: () => strin
|
||||
list.hidden = true;
|
||||
const where = el("p", { className: "b08-milestone__place" });
|
||||
where.dataset.b08MilestonePlace = "value";
|
||||
// 262-2 시점 = 거리이정표가 쓰는 시점(끌어 옮긴 덮개 · 없으면 노선 시점)
|
||||
const start = (): [number, number] | null => startPoint(liveMilestone(projectId));
|
||||
const map = editMap(projectId, {
|
||||
hint: "지도 누름 = 그 자리를 소재지로 · 표지 끌어 옮김",
|
||||
onClick: (lon, lat) => void moveTo(lon, lat),
|
||||
onMove: (_key, lon, lat) => void moveTo(lon, lat),
|
||||
center: () => siteInfo()?.start_lonlat ?? null,
|
||||
center: start,
|
||||
});
|
||||
|
||||
const draw = (): void => {
|
||||
where.textContent = `소재지 — ${place.address || "주소 찾기 · 지도에서 고르세요"}`;
|
||||
const s = start();
|
||||
map.set(
|
||||
place.lon != null && place.lat != null
|
||||
? [
|
||||
{
|
||||
key: "place",
|
||||
name: name() || "소재지",
|
||||
lon: place.lon,
|
||||
lat: place.lat,
|
||||
movable: true,
|
||||
},
|
||||
]
|
||||
: [],
|
||||
[
|
||||
...(s
|
||||
? [{ key: "start", name: "시점", lon: s[0], lat: s[1], movable: false, fixed: true }]
|
||||
: []),
|
||||
...(place.lon != null && place.lat != null
|
||||
? [
|
||||
{
|
||||
key: "place",
|
||||
name: name() || "소재지",
|
||||
lon: place.lon,
|
||||
lat: place.lat,
|
||||
movable: true,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
[],
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""254-3 B08 거리이정표 상자 모달 제목 · 장소 그림(사용자 162-6 · 8).
|
||||
|
||||
① 상자 누름 모달 제목 = 「거래처 — 이름」 · 「기준점 — 이름」(표 머리 글 「항목 정보」 없음)
|
||||
① 상자 누름 모달 제목 = 이름만(262-1 · 「—」 앞 글자 없음 · 표 머리 글 「항목 정보」 없음)
|
||||
② 장소 그림(256-1 `place_images`) = 상자 안 글 아래 작게 · 그림 없으면 자리 없음(상자 높이 그대로)
|
||||
"""
|
||||
|
||||
@@ -67,7 +67,7 @@ def test_장소_그림_상자_안_글_아래():
|
||||
|
||||
def test_모달_제목_화면_그림():
|
||||
modal = _read(B08 + "UI_Milestone_Modal.ts")
|
||||
assert "`거래처 — ${route!.name" in modal and "`기준점 — ${base.name" in modal
|
||||
assert 'route!.name || "거래처"' in modal and 'base.name || "기준점"' in modal # 262-1
|
||||
assert "항목 정보 —" not in modal
|
||||
ui = _read(B08 + "UI_Milestone_Diagram.ts")
|
||||
assert "placeImageBlobUrl(projectId, id)" in ui and "placeImageKey(id)" in ui
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
"""262 B08 거리이정표 상자 창 제목 · 소재지 지도 시점 표지(사용자 164-1).
|
||||
|
||||
262-1 거래처 · 기준점 창 제목 = 이름만(빈 이름 = 「거래처」 · 「기준점」) · 시점 창 = 「시점 · 중점」 · 요소 추가 창 그대로
|
||||
262-2 소재지 지도 = 시점 표지 고정(끌기 없음 · 맞춤에서 빠짐) · 시점 없으면 표지 없음 · 소재지 없으면 시점 중심
|
||||
"""
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
B08 = "B08_Quantity/B08_Quantity_UI_Milestone"
|
||||
|
||||
|
||||
def _read(name):
|
||||
return (ROOT / f"{B08}{name}").read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def test_제목_이름만():
|
||||
modal = _read("_Modal.ts")
|
||||
title = modal[modal.index("const title = target.add") : modal.index("openModal({")]
|
||||
assert '? base.name || "기준점"' in title and ': route!.name || "거래처"' in title
|
||||
assert '"요소 추가 — 기준점 · 거래처"' in title
|
||||
assert 'title: "시점 · 중점",' in modal and "공사지 —" not in modal
|
||||
assert re.search(r"title: `?[^\n]*(거래처|기준점|공사지) —", modal) is None
|
||||
|
||||
|
||||
def test_시점_표지_고정():
|
||||
place = _read("_Place.ts")
|
||||
assert "startPoint(liveMilestone(projectId))" in place # 거리이정표가 쓰는 시점
|
||||
assert "center: start" in place # 소재지 없음 = 시점 중심
|
||||
assert "movable: false, fixed: true" in place and 'name: "시점"' in place
|
||||
assert "...(s" in place and " : [])," in place # 시점 없음 = 표지 없음
|
||||
kakao = _read("_KakaoMap.ts")
|
||||
assert "fixed?: boolean;" in kakao
|
||||
assert "const moving = pins.filter((p) => !p.fixed);" in kakao
|
||||
assert "...moving.map(" in kakao # 처음 보기 = 소재지 중심(시점은 맞춤에서 빠짐)
|
||||
assert "moving.find(" in kakao # 보기 이동에서도 빠짐
|
||||
assert "if (p.fixed)" in kakao and "b08-milestone__map-start" in kakao
|
||||
css = _read(".css")
|
||||
assert ".b08-milestone__map-start {" in css
|
||||
Reference in New Issue
Block a user