Просмотр исходного кода

feat(gis): sync facility type filter with map layers

Kazerin 1 месяц назад
Родитель
Сommit
7f0cbb5951

+ 9 - 0
memory/sessions/2026-09-16.md

@@ -55,3 +55,12 @@
 - 新增共享样式 `src/views/subSystem/waterSupply/components/waterGisPageTheme.scss`,承载 `.gis-page` 页面骨架、标题区、筛选卡片、卡片标题与地图卡片样式;设施图与设备图通过非 scoped `@use` 引入,避免逐页复制。
 - 报警图保持原样作为参照基准,其本地样式与共享骨架取值一致。
 - 验证:前端全量测试 116/116 通过;生产构建通过。
+
+## 补充:GIS 查询与图层控制同步
+
+- 边界:仅修改供水设施 GIS 一张图的「设施类型筛选 ↔ 图层控制 ↔ 地图渲染」联动;设备图与报警图的筛选早已直接驱动地图点位,本次只补契约测试锁定,不改行为;后端与其他页面不动。
+- 设施图实现:类型选择框 `@change` 触发 `applyFacilityTypeToLayers`,选中某类型时只勾选该图层,清空时恢复全部图层;图层复选框变化时反向同步筛选(只剩一个图层时回填类型,其余视为“全部”)。
+- 地图点位与管线来自 `visibleMapModel`(由 `layerState` 过滤),因此图层控制同时决定地图渲染内容与左侧搜索结果。
+- 重置筛选会恢复全部图层。
+- 新增/更新测试:设施图筛选驱动图层与地图渲染、重置恢复图层、三图点位均来自过滤后的图层状态。
+- 验证:前端全量测试 119/119 通过;生产构建通过。

+ 25 - 2
src/views/subSystem/waterSupply/facility/gis.vue

@@ -22,7 +22,8 @@
                     :prefix-icon="Search" @keyup.enter="applyFilters"/>
         </el-form-item>
         <el-form-item label="设施类型">
-          <el-select v-model="selectedFacilityType" class="status-select" clearable placeholder="全部设施类型">
+          <el-select v-model="selectedFacilityType" class="status-select" clearable placeholder="全部设施类型"
+                     @change="applyFacilityTypeToLayers">
             <el-option v-for="item in facilityTypes" :key="item.value" :label="item.label" :value="item.value"/>
           </el-select>
         </el-form-item>
@@ -49,7 +50,8 @@
           :prefix-icon="Search"
           @keyup.enter="handleSearch"
         />
-        <el-select v-model="selectedFacilityType" class="type-select" placeholder="全部设施类型" clearable>
+        <el-select v-model="selectedFacilityType" class="type-select" placeholder="全部设施类型" clearable
+                   @change="applyFacilityTypeToLayers">
           <el-option v-for="item in facilityTypes" :key="item.value" :label="item.label" :value="item.value" />
         </el-select>
         <el-button type="primary" :icon="Refresh" @click="refreshScene">刷新</el-button>
@@ -405,6 +407,26 @@ async function loadFacilityDetail() {
   }
 }
 
+/**
+ * 设施类型筛选即图层控制:选中某类型时只保留该图层,清空时恢复全部图层。
+ * 地图点位/管线来自 visibleMapModel,因此图层状态会直接决定地图渲染内容。
+ */
+function applyFacilityTypeToLayers(type) {
+  facilityTypes.forEach(item => {
+    layerState[item.value] = type ? item.value === type : true
+  })
+}
+
+/** 手动勾选图层时反向同步筛选:仅剩一个图层时回填类型,其余情况视为“全部”。 */
+function syncFacilityTypeFromLayers() {
+  const enabled = facilityTypes.filter(item => layerState[item.value]).map(item => item.value)
+  selectedFacilityType.value = enabled.length === 1 ? enabled[0] : ''
+}
+
+watch(layerState, () => {
+  syncFacilityTypeFromLayers()
+}, { deep: true })
+
 function applyFilters() {
   if (searchKeyword.value.trim()) {
     handleSearch()
@@ -416,6 +438,7 @@ function applyFilters() {
 function resetFilters() {
   searchKeyword.value = ''
   selectedFacilityType.value = ''
+  applyFacilityTypeToLayers('')
   detailOpen.value = false
   loadGisData()
 }

+ 18 - 0
tests/waterFacilityGis.test.mjs

@@ -94,3 +94,21 @@ test('GIS page exposes REST-backed right detail drawer and empty layer state', (
   assert.match(shellSource, /class="side-panel-body"/u)
   assert.match(shellSource, /overflow-y: auto/u)
 })
+
+test('the facility type filter drives the layer control and the rendered layers', () => {
+  const source = fs.readFileSync('src/views/subSystem/waterSupply/facility/gis.vue', 'utf8')
+
+  assert.match(source, /@change="applyFacilityTypeToLayers"/u)
+  assert.match(source, /function applyFacilityTypeToLayers\(type\)/u)
+  assert.match(source, /layerState\[item\.value\] = type \? item\.value === type : true/u)
+  assert.match(source, /watch\(layerState, \(\) => \{[\s\S]*?syncFacilityTypeFromLayers\(\)/u)
+  assert.match(source, /function syncFacilityTypeFromLayers\(\)/u)
+  assert.match(source, /const visibleMapModel = computed\(\(\) => filterFacilityMapModel\(fullMapModel\.value, layerState\)\)/u)
+  assert.match(source, /const mapPoints = computed\(\(\) => visibleMapModel\.value\.points\.map/u)
+})
+
+test('resetting the facility filters restores every layer', () => {
+  const source = fs.readFileSync('src/views/subSystem/waterSupply/facility/gis.vue', 'utf8')
+
+  assert.match(source, /function resetFilters\(\) \{[\s\S]*?applyFacilityTypeToLayers\(''\)[\s\S]*?\}/u)
+})

+ 10 - 0
tests/waterGisConsistency.test.mjs

@@ -118,4 +118,14 @@ test('the GIS page chrome theme is shared by the facility and device maps', asyn
   assert.match(theme, /\.gis-page \.card-heading/u)
   assert.match(facility, /@use '\.\.\/components\/waterGisPageTheme\.scss'/u)
   assert.match(device, /@use '\.\/waterGisPageTheme\.scss'/u)
+})
+test('every GIS map renders its points from the filtered layer state', async () => {
+  const facility = await readFile(pages.facility, 'utf8')
+  const device = await readFile(pages.device, 'utf8')
+  const alarm = await readFile(pages.alarm, 'utf8')
+
+  assert.match(facility, /const visibleMapModel = computed\(\(\) => filterFacilityMapModel\(fullMapModel\.value, layerState\)\)/u)
+  assert.match(facility, /const mapPoints = computed\(\(\) => visibleMapModel\.value\.points\.map/u)
+  assert.match(device, /const mapPoints = computed\(\(\) => filteredDevices\.value\.map/u)
+  assert.match(alarm, /const gisMapPoints = computed\(\(\) => filteredAlarmPoints\.value\.map/u)
 })