diff --git a/frontend/src/features/plans/RouteMap.test.tsx b/frontend/src/features/plans/RouteMap.test.tsx
index e02aa3a..0292a1a 100644
--- a/frontend/src/features/plans/RouteMap.test.tsx
+++ b/frontend/src/features/plans/RouteMap.test.tsx
@@ -116,15 +116,28 @@ it("uses the same compact order number and full hover label on the fallback map"
providerName="amap"
start={{ label: "家", position: { latitude: 22.54, longitude: 114.05 } }}
markers={markers}
- route={null}
+ route={{
+ task_ids: [7],
+ distance_meters: 1000,
+ duration_seconds: 600,
+ polyline: [
+ { latitude: 22.54, longitude: 114.05 },
+ { latitude: 22.55, longitude: 114.06 },
+ ],
+ }}
selectedTaskId={null}
onSelectTask={onSelectTask}
/>,
);
const marker = screen.getByRole("button", { name: "订单 #12,虚构定位客户,路线第 1 站" });
+ const homeMarker = screen.getByTitle("家(起点与终点)");
+ const routeLine = screen.getByRole("img", { name: "按真实坐标绘制的路线" }).querySelector("polyline");
expect(marker).toHaveTextContent("#12");
expect(marker).toHaveAttribute("title", "订单 #12 · 虚构定位客户 · 路线第 1 站");
+ expect(homeMarker).toHaveClass("bg-emerald-600");
+ expect(homeMarker).not.toHaveClass("bg-slate-900");
+ expect(routeLine).toHaveAttribute("stroke", "#f97316");
expect(screen.queryByText("虚构定位客户")).not.toBeInTheDocument();
fireEvent.click(marker);
expect(onSelectTask).toHaveBeenCalledWith(7);
diff --git a/frontend/src/features/plans/RouteMap.tsx b/frontend/src/features/plans/RouteMap.tsx
index 09e6eb9..fa3a652 100644
--- a/frontend/src/features/plans/RouteMap.tsx
+++ b/frontend/src/features/plans/RouteMap.tsx
@@ -2,6 +2,8 @@ import { useEffect, useMemo, useRef, useState } from "react";
import {
AmapMapProvider,
+ HOME_MARKER_COLOR_CLASS,
+ ROUTE_LINE_COLOR,
hasAmapBrowserKey,
hasAmapBrowserSecurityCode,
markerDisplayOffset,
@@ -90,11 +92,11 @@ function CoordinateCanvas({
- {polyline ? : null}
+ {polyline ? : null}
{start ? (() => {
const projected = project(start.position);
- return 家 · 起终点;
+ return 家 · 起终点;
})() : null}
{markers.map((marker, index) => {
const projected = project(marker.position);
diff --git a/frontend/src/features/plans/mapProvider.test.ts b/frontend/src/features/plans/mapProvider.test.ts
index 44a8c11..9551117 100644
--- a/frontend/src/features/plans/mapProvider.test.ts
+++ b/frontend/src/features/plans/mapProvider.test.ts
@@ -77,6 +77,39 @@ afterEach(() => {
delete window.AMap;
});
+it("uses a warm route line and a green home marker", async () => {
+ const provider = new AmapMapProvider();
+ const controller = await provider.mount(document.createElement("div"), {
+ start: { label: "家", position: { latitude: 22.54, longitude: 114.05 } },
+ markers: [],
+ polyline: [
+ { latitude: 22.54, longitude: 114.05 },
+ { latitude: 22.55, longitude: 114.06 },
+ ],
+ selectedTaskId: null,
+ locationEditing: false,
+ draftPosition: null,
+ }, {
+ onSelectTask: vi.fn(),
+ });
+
+ const homeMarker = markerInstances.find((marker) => marker.options.title === "家");
+ const homeContent = homeMarker?.options.content as HTMLDivElement;
+ expect(homeContent.className).toContain("bg-emerald-600");
+ expect(homeContent.className).not.toContain("bg-slate-900");
+
+ const routeLine = markerInstances.find((marker) => marker.options.strokeColor);
+ expect(routeLine?.options).toMatchObject({
+ strokeColor: "#f97316",
+ strokeOpacity: 0.9,
+ isOutline: true,
+ outlineColor: "#fff7ed",
+ showDir: false,
+ });
+
+ controller.dispose();
+});
+
it("places a GCJ-02 draft pin on map click and updates it after dragging", async () => {
const onDraftPositionChange = vi.fn();
const provider = new AmapMapProvider();
diff --git a/frontend/src/features/plans/mapProvider.ts b/frontend/src/features/plans/mapProvider.ts
index 82d23f4..be158e7 100644
--- a/frontend/src/features/plans/mapProvider.ts
+++ b/frontend/src/features/plans/mapProvider.ts
@@ -9,6 +9,9 @@ export interface MapRenderModel {
draftPosition?: PlanGeoPoint | null;
}
+export const ROUTE_LINE_COLOR = "#f97316";
+export const HOME_MARKER_COLOR_CLASS = "bg-emerald-600";
+
export interface MapInteractionCallbacks {
onSelectTask: (taskId: number) => void;
onDraftPositionChange?: (position: PlanGeoPoint) => void;
@@ -128,18 +131,15 @@ function position(point: PlanGeoPoint): [number, number] {
return [point.longitude, point.latitude];
}
-function markerContent(
+function homeMarkerContent(
label: string,
name: string | null = null,
- selected = false,
- offset: MarkerDisplayOffset = { x: 0, y: 0 },
- muted = false,
): HTMLDivElement {
const content = document.createElement("div");
content.className = [
- "flex items-center gap-1 rounded-full border-2 border-white px-2 py-1",
+ "flex items-center gap-1 rounded-full border-2 border-emerald-100 px-2 py-1",
"max-w-36 text-xs font-bold whitespace-nowrap text-white shadow-md",
- selected ? "bg-amber-600 ring-2 ring-amber-300" : muted ? "bg-slate-400" : "bg-slate-900",
+ HOME_MARKER_COLOR_CLASS,
].join(" ");
const sequence = document.createElement("span");
sequence.textContent = label;
@@ -150,7 +150,6 @@ function markerContent(
customerName.textContent = name;
content.append(customerName);
}
- content.style.transform = `translate(${offset.x}px, ${offset.y}px)`;
return content;
}
@@ -273,7 +272,7 @@ export class AmapMapProvider implements MapProvider {
position: position(model.start.position),
title: model.start.label,
anchor: "center",
- content: markerContent("家", "起终点"),
+ content: homeMarkerContent("家", "起终点"),
}),
);
}
@@ -302,11 +301,15 @@ export class AmapMapProvider implements MapProvider {
overlays.push(
new AMap.Polyline({
path: model.polyline.map(position),
- strokeColor: "#0f172a",
+ strokeColor: ROUTE_LINE_COLOR,
strokeWeight: 5,
- strokeOpacity: 0.82,
+ strokeOpacity: 0.9,
+ isOutline: true,
+ outlineColor: "#fff7ed",
+ borderWeight: 2,
lineJoin: "round",
- showDir: true,
+ lineCap: "round",
+ showDir: false,
}),
);
}