diff --git a/.trellis/tasks/08-26-workbench-selection-save-fix/check.jsonl b/.trellis/tasks/08-26-workbench-selection-save-fix/check.jsonl new file mode 100644 index 0000000..9dd3234 --- /dev/null +++ b/.trellis/tasks/08-26-workbench-selection-save-fix/check.jsonl @@ -0,0 +1 @@ +{"_example": "Fill with {\"file\": \"\", \"reason\": \"\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."} diff --git a/.trellis/tasks/08-26-workbench-selection-save-fix/implement.jsonl b/.trellis/tasks/08-26-workbench-selection-save-fix/implement.jsonl new file mode 100644 index 0000000..9dd3234 --- /dev/null +++ b/.trellis/tasks/08-26-workbench-selection-save-fix/implement.jsonl @@ -0,0 +1 @@ +{"_example": "Fill with {\"file\": \"\", \"reason\": \"\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."} diff --git a/.trellis/tasks/08-26-workbench-selection-save-fix/prd.md b/.trellis/tasks/08-26-workbench-selection-save-fix/prd.md new file mode 100644 index 0000000..a382cce --- /dev/null +++ b/.trellis/tasks/08-26-workbench-selection-save-fix/prd.md @@ -0,0 +1,16 @@ +# 修复工作台选择与保存回归 + +## Goal + +道路选择只更新选择高亮,不重置地图数据或视图;保存修改后可靠保留并重新编译 overrides。 + +## Confirmed Cause + +`MapCanvas` 使用 `[state, selected]` 更新全量 source 和 fit view,选择道路会触发全图重载。Road override 的前后端 schema 一致,因此保存回归需在 UI 操作后验证其状态同步和编译调用。 + +## Acceptance Criteria + +- 选择道路不会清空 source、重新 fit 地图或改变视图。 +- 仅选择高亮 layer 更新;选择相关 OSM 线条样式仍刷新。 +- 暂存、保存、重新编译的 overrides 正确发送并以服务端响应更新状态。 +- 格式化、类型检查和构建通过。 diff --git a/.trellis/tasks/08-26-workbench-selection-save-fix/task.json b/.trellis/tasks/08-26-workbench-selection-save-fix/task.json new file mode 100644 index 0000000..cb0d3f8 --- /dev/null +++ b/.trellis/tasks/08-26-workbench-selection-save-fix/task.json @@ -0,0 +1,26 @@ +{ + "id": "workbench-selection-save-fix", + "name": "workbench-selection-save-fix", + "title": "修复工作台选择与保存回归", + "description": "修复道路选择触发全图层重建,并排查修复 React 工作台保存 overrides 回归", + "status": "in_progress", + "dev_type": null, + "scope": null, + "package": null, + "priority": "P2", + "creator": "dingkang", + "assignee": "dingkang", + "createdAt": "2026-08-26", + "completedAt": null, + "branch": null, + "base_branch": "main", + "worktree_path": null, + "commit": null, + "pr_url": null, + "subtasks": [], + "children": [], + "parent": null, + "relatedFiles": [], + "notes": "", + "meta": {} +} \ No newline at end of file diff --git a/workbench/client/src/components/MapCanvas.tsx b/workbench/client/src/components/MapCanvas.tsx index e6db799..6aa1be9 100644 --- a/workbench/client/src/components/MapCanvas.tsx +++ b/workbench/client/src/components/MapCanvas.tsx @@ -4,7 +4,7 @@ import View from 'ol/View'; import Select from 'ol/interaction/Select'; import { click } from 'ol/events/condition'; import type { Road, WorkbenchState } from '../types/state'; -import { createLayers, updateLayers, type LayerName } from '../map/layers'; +import { createLayers, updateLayers, updateSelectedRoad, type LayerName } from '../map/layers'; interface Props { state: WorkbenchState; @@ -72,7 +72,13 @@ export function MapCanvas({ state, selected, visible, scene, onSelectRoad, onFea updateLayers(layers, state, selected); const extent = layers.osm.getSource()!.getExtent(); if (extent && Number.isFinite(extent[0])) map.getView().fit(extent, { padding: [48, 48, 48, 48], maxZoom: 19 }); - }, [state, selected]); + }, [state]); + useEffect(() => { + const layers = layersRef.current; + if (!layers) return; + updateSelectedRoad(layers, selected); + layers.osm.changed(); + }, [selected]); useEffect(() => { const layers = layersRef.current; if (!layers) return; diff --git a/workbench/client/src/map/layers.ts b/workbench/client/src/map/layers.ts index 0b7669f..65b689c 100644 --- a/workbench/client/src/map/layers.ts +++ b/workbench/client/src/map/layers.ts @@ -157,9 +157,14 @@ export function updateLayers(layers: ReturnType, state: Wor .filter((item) => item.geometry) .map(({ geometry, ...properties }) => ({ type: 'Feature', properties, geometry: geometry! })), }); - put('selectedRoad', { type: 'FeatureCollection', features: [] }); + updateSelectedRoad(layers, selected); +} + +export function updateSelectedRoad(layers: ReturnType, selected: Road | null) { + const source = layers.selectedRoad.getSource()!; + source.clear(); if (selected) - get('selectedRoad').addFeature( + source.addFeature( new Feature({ geometry: new LineString(selected.centerline).transform('EPSG:4326', 'EPSG:3857') }), ); }