feat: extend map targeting and overlay controls

Support batch feature targeting and clear map highlights and overlays from the More panel. Remove misleading action chevrons and unused configuration export entries.

Validated the isolated patch with lint, typecheck, 36 focused tests and 5 browser cases using mocked data.
This commit is contained in:
2026-10-28 14:47:33 +08:00
parent e57e0f099f
commit 5b710d1247
17 changed files with 666 additions and 202 deletions
@@ -0,0 +1,60 @@
import { expect, test } from "@playwright/test";
import { mockInvestigationPresentation } from "./support/mock-investigation-presentation";
for (const width of [1440, 1024]) {
test(`batch feature location highlights all matches and reports missing IDs at ${width}px`, async ({ page }) => {
await page.setViewportSize({ width, height: 900 });
await mockInvestigationPresentation(page, []);
const queries: string[] = [];
await page.route("https://workbench-map.invalid/**/ows?**", (route) => {
const filter = new URL(route.request().url()).searchParams.get("cql_filter");
if (!filter) return route.fallback();
queries.push(filter);
const features = [
{ type: "Feature", properties: { id: "P-1" }, geometry: { type: "LineString", coordinates: [[121.4, 31.05], [121.42, 31.06]] } },
{ type: "Feature", properties: { id: "P-2" }, geometry: { type: "LineString", coordinates: [[121.5, 31.12], [121.52, 31.14]] } }
].filter((feature) => filter.includes(`'${feature.properties.id}'`));
return route.fulfill({ json: { type: "FeatureCollection", features } });
});
await page.goto("/", { waitUntil: "domcontentloaded" });
await page.waitForFunction(() => Boolean(window.__waterNetworkMap?.getLayer("scada-pressure-hit")));
await page.getByRole("button", { name: "切换地图 Dev Panel" }).click();
const panel = page.getByTestId("map-dev-panel");
const ids = panel.getByRole("textbox", { name: "要素 ID(可多个)" });
await ids.fill("P-1,P-2\nP-1 missing");
await panel.getByRole("button", { name: "定位并高亮", exact: true }).click();
await expect(panel.getByRole("status").filter({ hasText: "本次命中" })).toHaveText("本次命中 2 个,未找到 1 个:missing");
expect(queries).toEqual(['"id" IN (\'P-1\',\'P-2\',\'missing\')']);
const highlights = () => page.evaluate(() => ["P-1", "P-2"].map((id) =>
window.__waterNetworkMap?.getFeatureState({ source: "pipes", sourceLayer: "pipes", id }).highlighted ?? false
));
await expect.poll(highlights).toEqual([true, true]);
await expect.poll(() => page.evaluate(() => {
const bounds = window.__waterNetworkMap?.getBounds();
return bounds?.contains([121.4, 31.05]) && bounds.contains([121.52, 31.14]);
})).toBe(true);
await expect(panel).toHaveJSProperty("scrollWidth", await panel.evaluate((element) => element.clientWidth));
await page.screenshot({ path: `/tmp/map-multi-feature-location-${width}.png` });
await panel.getByRole("button", { name: "清除高亮", exact: true }).click();
await expect.poll(highlights).toEqual([false, false]);
await expect(panel.getByRole("status").filter({ hasText: "本次命中" })).toHaveCount(0);
await panel.getByRole("button", { name: "缩放到要素", exact: true }).click();
await expect(panel.getByRole("status").filter({ hasText: "本次命中" })).toContainText("本次命中 2 个");
await expect.poll(highlights).toEqual([false, false]);
await panel.getByRole("button", { name: "仅高亮", exact: true }).click();
await expect.poll(highlights).toEqual([true, true]);
await ids.fill("missing");
await panel.getByRole("button", { name: "定位并高亮", exact: true }).click();
await expect(panel.getByRole("alert")).toContainText("目标要素不存在");
await expect(panel.getByRole("status").filter({ hasText: "本次命中" })).toHaveText("本次命中 0 个,未找到 1 个:missing");
await expect.poll(highlights).toEqual([true, true]);
await panel.getByRole("button", { name: "清除高亮", exact: true }).click();
await expect.poll(highlights).toEqual([false, false]);
await ids.fill(" ");
await expect(panel.getByRole("button", { name: "定位并高亮", exact: true })).toBeDisabled();
await page.keyboard.press("Escape");
await expect(panel).toBeHidden();
});
}