Quellcode durchsuchen

refactor(water): migrate pipe info to shared network base

Kazerin vor 1 Monat
Ursprung
Commit
4799f4106f

+ 3 - 0
memory/index.md

@@ -42,6 +42,7 @@ npm run dev       # 默认 http://localhost:80
 - 若干排水请求设置 `X-Silent-Error`,但 Axios 拦截器未处理该标志。
 - 百度地图 AK 仍由 `index.html` 直接配置,环境变量未形成统一生效入口。
 - `package.json` 没有测试、Lint 或类型检查脚本。
+- 供水管网信息查询页已重构接入统一表 `pipe_network_base`(`/PipeNetworkBase/*`,network_type=2);旧表 `water_pipe_info` 的接口、实体、mapper 与前端死代码已全部删除,供水 GIS 图与统计分析 mapper 已迁移至新表(均带 `network_type='2'`),压力分布图改为等级分布(`pipe_network_level` 字典);数据迁移与删表由用户侧执行(2026-09-09)。
 
 ## 长期记忆
 
@@ -57,3 +58,5 @@ npm run dev       # 默认 http://localhost:80
 - [2026-09-01:供水管网数据统计分析 T3](sessions/2026-09-01.md)
 - [2026-09-04:工单处置七态共识跨仓落盘(只改文档)](sessions/2026-09-04.md)
 - [2026-09-05:工单七态编码调整为 1–7;T3/T4(Issue #7/#8)验收关闭并同步共识文档](sessions/2026-09-05.md)
+- [2026-09-08:管网信息查询页重构接入 pipe_network_base(分 4 票执行完成)](sessions/2026-09-08.md)
+- [2026-09-09:water_pipe_info 收尾迁移(E 顺序执行完成,旧表链路全删)](sessions/2026-09-09.md)

+ 51 - 0
memory/sessions/2026-09-08.md

@@ -0,0 +1,51 @@
+# 2026-09-08:管网信息查询页重构 — 弃用 water_pipe_info,接入统一表 pipe_network_base
+
+## 目标
+
+供水设施管理-管网信息查询页从旧表 `app_user.water_pipe_info`(`/api/water/pipe/*`)重构为统一管网表 `app_user.pipe_network_base`(`/PipeNetworkBase/*`),废弃旧表链路(数据迁移由用户侧执行)。边界:仅管网信息查询相关代码,其余系统(含燃气)零改动。
+
+## 用户决策(本任务关键口径)
+
+- `network_type` 设施类型编码:**0-燃气 1-排水 2-供水**;供水页固定写 `2`。
+- 筛选区不缩减:材质/管径/建设年份/创建时间范围全保留 → 后端同步加参。
+- 导入/导出功能保留 → 后端补供水侧端点。
+- `pressureRating(MPa)` 与 `networkLevel`(等级)含义不同,**不做映射**:旧"设计压力"字段弃用,"等级"为独立新字段(字典 `pipe_network_level`)。
+- 存量数据迁移由用户自行执行。
+
+## 执行方式
+
+按 /to-tickets 拆为 4 张垂直票(`.scratch/pipe-network-base-refactor/issues/01..04.md`),线性链 01→02→03→04,分票执行,每票独立过验证门。
+
+## 各票结论(全部 done)
+
+- **01 列表接入**:后端 `PipeNetworkBaseService/Impl/Controller.findByPage` 增量加可选参数 `material(eq)/diameter(eq)/buildYear(buildDate 年份区间)/startTime/endTime(createTime 范围)`(唯一调用方为通用控制器,零回归);前端 `listPipeInfo` 改指 `/PipeNetworkBase/findByPage` 并注入 `networkType=2`,响应按裸 Page 读取(`response.records/total`);筛选区 7 条件全保留(铺设年份→建设年份);状态字典 `sysNormalDisable` 静态值 → `getDicts('pipe_network_status')`;表格列按新实体重排(+等级/管养单位/位置描述,经纬度 4 列合并 2 列,-设计压力/起终点文本);多选选中改取 `networkId`。
+- **02 表单/详情接入**:`getPipeInfo/addPipeInfo/updatePipeInfo` 改指 `getById/save/updateById`;表单字段重排(等级/管养单位/位置描述/建设日期/经纬度/备注),`reset()` 注入 `networkType='2'`;编码查重失败由 request 拦截器弹后端 msg;详情抽屉字段按新实体重映射;抽屉类名/属性顺序逐字保留。
+- **03 导入导出接入**:后端新增 `PipeNetworkImportDTO`(15 列模板)+ `exportData` 全参重载(复用 toExportDTOs)+ `importNetworks`(编码查重,updateSupport=false 跳过已存在)+ 控制器三端点 `GET /export`、`POST /importData`(networkType 默认 "2")、`POST /importTemplate`(sheet"管网信息");权限串沿用 `waterSupply:waterPipe:export/import`;前端导出改 `exportPipeInfo`、导入弹窗两 url 改指新接口(共享组件 WaterFacilityImportDialog 零改动);燃气侧 `/api/pipe-network/export` 行为不变。
+- **04 删除接入 + 回收站移除**:`delPipeInfo` 改指 `DELETE /deleteBatchById?ids=`(逻辑删除 delFlag 0→2);删除确认文案加"删除后不可恢复";回收站弹窗、recycle* data/方法、3 个旧 API 函数、`normalizePipeStatus` 引用全部清除;页面 grep 零残留。
+
+## 文件变更
+
+前端 `E:\city-life-line`(仅 2 个任务文件):
+- `src/api/pipeNetwork/pipeInfo.js`(整体改指 /PipeNetworkBase,仅 6 函数)
+- `src/views/subSystem/basic/pipeInfo/index.vue`(筛选/表格/表单/详情/删除/导入导出接入,回收站移除)
+
+后端 `E:\pipe-ner-server\pipe-network-service`(4 个文件,全部增量):
+- `zksy-system/.../base/service/PipeNetworkBaseService.java`(findByPage 加参 + exportData 重载 + importNetworks)
+- `zksy-system/.../base/service/impl/PipeNetworkBaseServiceImpl.java`(实现 + toExportDTOs 私有复用 + Wrappers)
+- `zksy-system/.../base/dto/PipeNetworkImportDTO.java`(新增)
+- `zksy-admin/.../web/controller/base/PipeNetworkBaseController.java`(findByPage 加参 + export/importData/importTemplate)
+
+## 验证结果
+
+- 后端 `mvn -pl zksy-admin -am compile`:0(T1、T3 各一次)
+- 前端 `node --test tests/*.test.mjs`:T1/T2/T3 门 75/75 绿;T4 门 74/75——唯一失败 `shared gis map canvas uses the common point marker contract`(tests/gisMapCanvas.test.mjs 断言 GisMapCanvas.vue 用 BMapGL.Icon),系用户并行编辑 GIS 组件的中间态,非本任务文件,未触碰
+- `npm run build:prod`:各票均 0
+- `git diff --check`:干净;两仓库 git status 确认改动仅限任务文件(后端 spring.log 为运行日志,未触碰)
+
+## 风险与后续
+
+- 数据迁移(water_pipe_info → pipe_network_base,network_type=2)由用户执行,页面上线前需完成,否则列表为空。
+- 旧表仍有其他消费方:`WaterSupplyGisMapMapper`(供水GIS图)、`PipeNetworkStatisticsMapper`(统计分析)仍读 water_pipe_info,数据迁走后这些视图会为空——另行任务处理。
+- 旧后端接口(/api/water/pipe/*、/api/water/pipe/importData 等)保留未删,可回滚。
+- 本任务代码未提交(遵循不合并/不推送约定,等待用户指示);票文件在 `.scratch/pipe-network-base-refactor/issues/`(可镜像 Gogs)。
+- 本沙箱环境注意:node 全路径 `C:\Program Files\nodejs\node.exe`(不在 PATH);`node --test tests` 目录参数在 Node v24 下不可用,需 `node --test "tests/*.test.mjs"`。

+ 48 - 0
memory/sessions/2026-09-09.md

@@ -0,0 +1,48 @@
+# 2026-09-09:water_pipe_info 收尾迁移(E 顺序执行)
+
+## 目标
+
+延续 09-08 管网信息查询页重构(4 票已完成):确认 `water_pipe_info` 表剩余关联后,按用户确认的 A2 决策(选项 2:压力分布图改为「等级分布」,`network_level` + `pipe_network_level` 字典)执行 E1→E4,删除旧表全部接口与实体类。
+
+## 决策与前提
+
+- 用户已确认按 E 顺序执行,无异议项照做。
+- 数据迁移(`water_pipe_info` → `pipe_network_base`,`network_type='2'`)由用户侧执行,先于代码上线生效;SQL 迁移前 GIS/统计页会查不到管网数据。
+- 所有迁移 SQL 统一追加 `network_type = '2'`(新表为燃气/排水/供水共用表)。
+- `location` 在 KingBase 是保留字,GIS mapper 中必须带引号 `"location"`(与 plant/pumpStation 一致)。
+
+## 变更明细
+
+### 后端(E:\pipe-ner-server\pipe-network-service,未提交)
+
+- **E1a GIS**:
+  - `mapper/WaterSupply/WaterSupplyGisMap/WaterSupplyGisMapMapper.xml`:`selectPipeFeatures` 与 `selectOverview` 的 pipe 段改读 `app_user.pipe_network_base`(`network_type='2'`,输出键保持 id/code/name/status/location + 4 坐标)。
+  - `WaterSupplyGisMapService.getDetail(String, Long)` → `(String, String)`(管网主键为 UUID);实现注入 `PipeNetworkBaseService` 替代 `IWaterPipeInfoService`,pipe 分支 `getById(id)`,其余分支 `Long.valueOf(id)`;控制器 `@PathVariable Long id` → `String id`。
+  - `WaterSupplyGisMapServiceTest` / `WaterSupplyGisMapControllerTest` 调用点 Long→String。
+- **E1b 统计**:
+  - `mapper/WaterSupply/PipeNetworkStatistics/PipeNetworkStatisticsMapper.xml` 6 条管网 SQL 改读 `pipe_network_base`(`network_type='2'`):设施概览 pipe 段、summary(length/diameter)、管材分布(material)、管径分布(diameter)、建设年代分布(`EXTRACT(YEAR FROM build_date)` + `MOD` 十年档,KingBase 兼容)、`selectPipePressureDistribution` → **`selectPipeLevelDistribution`**(`COALESCE(network_level,'未填报')`,即 A2 选项 2);`selectStatusDistribution` pipe 分支改为子查询包裹 `network_type='2'`。
+  - `PipeNetworkStatisticsMapper.java` 同步重命名;`PipeNetworkStatisticsServiceImpl` dashboard key `pressureDistribution` → `levelDistribution`。
+- **E3 删除**(7 个文件):`WaterPipeInfoController`、`WaterPipeInfo` 实体、`WaterPipeInfoMapper`(.java/.xml)、`IWaterPipeInfoService`、`WaterPipeInfoServiceImpl`、`WaterPipeExportDTO`。
+  - `WaterFacilityExportController` 移除 pipeService 字段与 `/pipe/export`、`/pipe/importData`、`/pipe/importTemplate` 三个端点(其余四类设施保留)。
+  - `WaterFacilityImportService` 移除 `importPipes`、pipeService 构造参数/字段、`preparePipe`、`initialize(WaterPipeInfo)`;类注释「五类」→「四类」。
+  - 测试适配:`WaterFacilityQueryServiceTest`(删 pipe 分页/导出测试与 mapper 路径)、`WaterFacilityImportServiceTest`(删 pipe 导入测试、构造器去参)、`FacilityCoordinateModelTest`(pipe 坐标实体改用 `PipeNetworkBase`;mapper 清单去 WaterPipeInfo;GIS location 断言适配)、`WaterFacilityExportControllerContractTest`(去 pipe 断言)。
+  - 保留:`sql/water_supply_facility_gis.sql` 历史迁移脚本中的 `water_pipe_info` ALTER(表尚未物理删除,脚本为历史事实)。
+
+### 前端(E:\city-life-line,未提交)
+
+- **E2** `WaterFacilityDashboard.vue`:`pressureDistribution:'压力等级分布'` → `levelDistribution:'等级分布'`(描述「按等级统计管网数量」);`onMounted` 拉取 `pipe_network_level` 字典,`formatStatisticRow` 对 `levelDistribution` 行做码值→字典标签翻译(未命中回退原值;组件内不得出现「未填报」字面量,有测试约束)。
+- **E4** `src/api/pipeNetwork/waterSupply.js`:删除死代码 `exportWaterPipe`(`/api/water/pipe/export`),全仓 grep 无残留。
+
+## 验证
+
+- `mvn -pl zksy-system,zksy-admin test`:zksy-system **95/95 通过**;zksy-admin 80 测中仅 `CockpitEquipmentDetailAggregateControllerTest` 2 个失败——该测试是用户进行中的驾驶舱设备聚合工作(工作区未跟踪文件),与本任务无关,未触碰。
+- 前端 `node --test "tests/*.test.mjs"`:**75/75 通过**(含此前的 GisMapCanvas Icon 契约测试,用户并行改动已自行通过)。
+- `git diff --check`:前端干净;后端仅 `logs/.../spring.log` 运行时日志有尾随空格(历史噪声,从不入暂存)。
+- 残留 grep:后端 java/xml 中 `WaterPipeInfo|IWaterPipeInfoService|WaterPipeExportDTO|water_pipe_info` 仅剩迁移脚本断言 1 处(预期);前端 `exportWaterPipe|/api/water/pipe` 零残留。
+
+## 风险与后续
+
+- 上线顺序:用户先执行数据迁移 SQL,再部署后端 + 前端,否则 GIS/统计的管网图层为空。
+- 旧 `/api/water/pipe/*` 全部 404 属预期(前端已无调用方)。
+- 两仓改动均未提交、未推送(约定:不 push)。后端用户并行改动(cockpit 相关文件、spring.log)不得混入本任务提交;提交时只暂存本任务文件。
+- `length`/`diameter`/`material`/`build_date` 列名与 `PipeNetworkBase` 实体一致,统计 SQL 与分页共用字段,无额外映射。

+ 15 - 25
src/api/pipeNetwork/pipeInfo.js

@@ -1,25 +1,27 @@
 import request from '@/utils/request'
 
-const urlPrefix = '/api/water/pipe'
+// pipe_network_base 统一管网表接口(设施类型:2-供水设施)
+const basePrefix = '/PipeNetworkBase'
+const WATER_NETWORK_TYPE = '2'
 
 export function listPipeInfo(query) {
   return request({
-    url: urlPrefix + '/page',
+    url: basePrefix + '/findByPage',
     method: 'get',
-    params: query
+    params: { networkType: WATER_NETWORK_TYPE, ...query }
   })
 }
 
 export function getPipeInfo(id) {
   return request({
-    url: urlPrefix + '/' + id,
+    url: basePrefix + '/getById/' + id,
     method: 'get'
   })
 }
 
 export function addPipeInfo(data) {
   return request({
-    url: urlPrefix,
+    url: basePrefix + '/save',
     method: 'post',
     data: data
   })
@@ -27,7 +29,7 @@ export function addPipeInfo(data) {
 
 export function updatePipeInfo(data) {
   return request({
-    url: urlPrefix,
+    url: basePrefix + '/updateById',
     method: 'put',
     data: data
   })
@@ -35,29 +37,17 @@ export function updatePipeInfo(data) {
 
 export function delPipeInfo(ids) {
   return request({
-    url: urlPrefix + '/' + ids,
-    method: 'delete'
+    url: basePrefix + '/deleteBatchById',
+    method: 'delete',
+    params: { ids: ids }
   })
 }
 
-export function listRecyclePipeInfo(query) {
+export function exportPipeInfo(query) {
   return request({
-    url: urlPrefix + '/recycle/list',
+    url: basePrefix + '/export',
     method: 'get',
-    params: query
-  })
-}
-
-export function restorePipeInfo(ids) {
-  return request({
-    url: urlPrefix + '/recycle/restore/' + ids,
-    method: 'post'
-  })
-}
-
-export function deletePhysicallyPipeInfo(ids) {
-  return request({
-    url: urlPrefix + '/recycle/delete/' + ids,
-    method: 'delete'
+    params: { networkType: WATER_NETWORK_TYPE, ...query },
+    responseType: 'blob'
   })
 }

+ 0 - 1
src/api/pipeNetwork/waterSupply.js

@@ -71,5 +71,4 @@ export const getDeviceGisDetail = (id) => get(`/waterSupply/device/gis/detail/${
 export const exportWaterSource = (params) => request({ url: '/api/water/sourceInfo/export', method: 'get', params, responseType: 'blob' })
 export const exportWaterPlant = (params) => request({ url: '/api/water/plantInfo/export', method: 'get', params, responseType: 'blob' })
 export const exportWaterPumpStation = (params) => request({ url: '/api/water/pumpStation/export', method: 'get', params, responseType: 'blob' })
-export const exportWaterPipe = (params) => request({ url: '/api/water/pipe/export', method: 'get', params, responseType: 'blob' })
 export const exportWaterUser = (params) => request({ url: '/api/water/userInfo/export', method: 'get', params, responseType: 'blob' })

+ 110 - 196
src/views/subSystem/basic/pipeInfo/index.vue

@@ -26,14 +26,14 @@
         </div>
       </template>
       <el-form :model="queryParams" ref="queryForm" :inline="true" label-width="78px">
-        <el-form-item label="管网编号" prop="pipeCode">
-          <el-input v-model="queryParams.pipeCode" placeholder="请输入管网编号" clearable size="small" @keyup.enter="handleQuery" />
+        <el-form-item label="管网编号" prop="networkCode">
+          <el-input v-model="queryParams.networkCode" placeholder="请输入管网编号" clearable size="small" @keyup.enter="handleQuery" />
         </el-form-item>
-        <el-form-item label="管网名称" prop="pipeName">
-          <el-input v-model="queryParams.pipeName" placeholder="请输入管网名称" clearable size="small" @keyup.enter="handleQuery" />
+        <el-form-item label="管网名称" prop="networkName">
+          <el-input v-model="queryParams.networkName" placeholder="请输入管网名称" clearable size="small" @keyup.enter="handleQuery" />
         </el-form-item>
-        <el-form-item label="管道材质" prop="pipeMaterial">
-          <el-select v-model="queryParams.pipeMaterial" placeholder="请选择材质" clearable size="small" style="width: 120px">
+        <el-form-item label="管道材质" prop="material">
+          <el-select v-model="queryParams.material" placeholder="请选择材质" clearable size="small" style="width: 120px">
             <el-option label="铸铁" value="铸铁" />
             <el-option label="PE" value="PE" />
             <el-option label="钢管" value="钢管" />
@@ -42,16 +42,16 @@
             <el-option label="混凝土" value="混凝土" />
           </el-select>
         </el-form-item>
-        <el-form-item label="管径(mm)" prop="pipeDiameter">
-          <el-input-number v-model="queryParams.pipeDiameter" :min="0" :precision="0" controls-position="right" size="small" />
+        <el-form-item label="管径(mm)" prop="diameter">
+          <el-input-number v-model="queryParams.diameter" :min="0" :precision="0" controls-position="right" size="small" />
         </el-form-item>
-        <el-form-item label="铺设年份" prop="layingYear">
-          <el-date-picker v-model="queryParams.layingYear" type="year" value-format="YYYY" placeholder="选择年份" size="small" />
+        <el-form-item label="建设年份" prop="buildYear">
+          <el-date-picker v-model="queryParams.buildYear" type="year" value-format="YYYY" placeholder="选择年份" size="small" />
         </el-form-item>
         <el-form-item label="状态" prop="status">
           <el-select v-model="queryParams.status" placeholder="管网状态" clearable size="small" style="width: 120px">
             <el-option
-              v-for="dict in sysNormalDisable"
+              v-for="dict in statusDict"
               :key="dict.value"
               :label="dict.label"
               :value="dict.value" 
@@ -84,30 +84,34 @@
       </template>
       <el-table v-loading="loading" :data="tableData" @selection-change="handleSelectionChange">
         <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="管网编号" align="center" prop="pipeCode" :show-overflow-tooltip="true" min-width="120" />
-        <el-table-column label="管网名称" align="center" prop="pipeName" :show-overflow-tooltip="true" min-width="140" />
-        <el-table-column label="管道材质" align="center" prop="pipeMaterial" width="100">
+        <el-table-column label="管网编号" align="center" prop="networkCode" :show-overflow-tooltip="true" min-width="120" />
+        <el-table-column label="管网名称" align="center" prop="networkName" :show-overflow-tooltip="true" min-width="140" />
+        <el-table-column label="等级" align="center" width="90">
+          <template #default="{ row }">{{ dictLabel(levelDict, row.networkLevel) || '-' }}</template>
+        </el-table-column>
+        <el-table-column label="管养单位" align="center" prop="managementUnit" :show-overflow-tooltip="true" min-width="120" />
+        <el-table-column label="管道材质" align="center" prop="material" width="100">
           <template #default="{ row }">
-            <el-tag :type="materialTagType(row.pipeMaterial)" size="small">{{ row.pipeMaterial || '-' }}</el-tag>
+            <el-tag :type="materialTagType(row.material)" size="small">{{ row.material || '-' }}</el-tag>
           </template>
         </el-table-column>
-        <el-table-column label="管径(mm)" align="center" prop="pipeDiameter" width="100" />
-        <el-table-column label="长度(m)" align="center" prop="pipeLength" width="100" />
-        <el-table-column label="起点位置" align="center" prop="startPoint" :show-overflow-tooltip="true" min-width="140" />
-        <el-table-column label="终点位置" align="center" prop="endPoint" :show-overflow-tooltip="true" min-width="140" />
-        <el-table-column label="起点经度" align="center" prop="startLongitude" width="120" />
-        <el-table-column label="起点纬度" align="center" prop="startLatitude" width="120" />
-        <el-table-column label="终点经度" align="center" prop="endLongitude" width="120" />
-        <el-table-column label="终点纬度" align="center" prop="endLatitude" width="120" />
-        <el-table-column label="铺设年份" align="center" prop="layingYear" width="100" />
-        <el-table-column label="设计压力(MPa)" align="center" prop="pressureRating" width="130" />
+        <el-table-column label="管径(mm)" align="center" prop="diameter" width="100" />
+        <el-table-column label="长度(m)" align="center" prop="length" width="100" />
+        <el-table-column label="位置描述" align="center" prop="location" :show-overflow-tooltip="true" min-width="140" />
+        <el-table-column label="起点经纬度" align="center" width="170">
+          <template #default="{ row }">{{ formatCoord(row.startLongitude, row.startLatitude) }}</template>
+        </el-table-column>
+        <el-table-column label="终点经纬度" align="center" width="170">
+          <template #default="{ row }">{{ formatCoord(row.endLongitude, row.endLatitude) }}</template>
+        </el-table-column>
+        <el-table-column label="建设日期" align="center" prop="buildDate" width="110" />
         <el-table-column label="状态" align="center" width="80">
           <template #default="{ row }">
             <el-tag 
               :type="String(row.status) === '0' ? 'success' : 'danger'"
               size="small"
             >
-              {{ String(row.status) === '0' ? '正常' : '停用' }}
+              {{ dictLabel(statusDict, row.status) || '-' }}
             </el-tag>
           </template>
         </el-table-column>
@@ -132,18 +136,30 @@
       <el-form ref="form" :model="form" :rules="rules" label-width="108px">
         <el-row :gutter="20">
           <el-col :span="12">
-            <el-form-item label="管网编号" prop="pipeCode">
-              <el-input v-model="form.pipeCode" placeholder="请输入管网编号" maxlength="50" />
+            <el-form-item label="管网编号" prop="networkCode">
+              <el-input v-model="form.networkCode" placeholder="请输入管网编号" maxlength="50" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="管网名称" prop="networkName">
+              <el-input v-model="form.networkName" placeholder="请输入管网名称" maxlength="100" />
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="管网名称" prop="pipeName">
-              <el-input v-model="form.pipeName" placeholder="请输入管网名称" maxlength="100" />
+            <el-form-item label="等级" prop="networkLevel">
+              <el-select v-model="form.networkLevel" placeholder="请选择管网等级" clearable style="width: 100%">
+                <el-option v-for="dict in levelDict" :key="dict.value" :label="dict.label" :value="dict.value" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="管养单位" prop="managementUnit">
+              <el-input v-model="form.managementUnit" placeholder="请输入管养单位" maxlength="100" />
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="管道材质" prop="pipeMaterial">
-              <el-select v-model="form.pipeMaterial" placeholder="请选择管道材质" style="width: 100%">
+            <el-form-item label="管道材质" prop="material">
+              <el-select v-model="form.material" placeholder="请选择管道材质" style="width: 100%">
                 <el-option label="铸铁" value="铸铁" />
                 <el-option label="PE" value="PE" />
                 <el-option label="钢管" value="钢管" />
@@ -154,40 +170,30 @@
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="管径(mm)" prop="pipeDiameter">
-              <el-input-number v-model="form.pipeDiameter" :min="0" :precision="0" style="width: 100%" placeholder="请输入管径" />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="管道长度(m)" prop="pipeLength">
-              <el-input-number v-model="form.pipeLength" :min="0" :precision="2" style="width: 100%" placeholder="请输入管道长度" />
+            <el-form-item label="管径(mm)" prop="diameter">
+              <el-input-number v-model="form.diameter" :min="0" :precision="0" style="width: 100%" placeholder="请输入管径" />
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="铺设年份" prop="layingYear">
-              <el-date-picker v-model="form.layingYear" type="year" placeholder="请选择铺设年份" value-format="YYYY" style="width: 100%" />
+            <el-form-item label="管道长度(m)" prop="length">
+              <el-input-number v-model="form.length" :min="0" :precision="2" style="width: 100%" placeholder="请输入管道长度" />
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="设计压力(MPa)" prop="pressureRating">
-              <el-input-number v-model="form.pressureRating" :min="0" :precision="2" style="width: 100%" placeholder="请输入设计压力" />
+            <el-form-item label="建设日期" prop="buildDate">
+              <el-date-picker v-model="form.buildDate" type="date" placeholder="请选择建设日期" value-format="YYYY-MM-DD" style="width: 100%" />
             </el-form-item>
           </el-col>
           <el-col :span="12">
             <el-form-item label="运行状态" prop="status">
               <el-radio-group v-model="form.status">
-                <el-radio v-for="dict in sysNormalDisable" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
+                <el-radio v-for="dict in statusDict" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
               </el-radio-group>
             </el-form-item>
           </el-col>
           <el-col :span="24">
-            <el-form-item label="起点位置" prop="startPoint">
-              <el-input v-model="form.startPoint" placeholder="请输入起点位置" maxlength="200" />
-            </el-form-item>
-          </el-col>
-          <el-col :span="24">
-            <el-form-item label="终点位置" prop="endPoint">
-              <el-input v-model="form.endPoint" placeholder="请输入终点位置" maxlength="200" />
+            <el-form-item label="位置描述" prop="location">
+              <el-input v-model="form.location" placeholder="请输入位置描述" maxlength="200" />
             </el-form-item>
           </el-col>
           <el-col :span="12"><el-form-item label="起点经度" prop="startLongitude"><el-input-number v-model="form.startLongitude" :precision="8" :step="0.00000001" controls-position="right" style="width: 100%" /></el-form-item></el-col>
@@ -210,20 +216,20 @@
     <!-- 详情抽屉 -->
     <el-drawer title="管网详情" v-model="detailVisible" class="detail-drawer" direction="rtl" size="600px" append-to-body>
       <el-descriptions :column="2" border>
-        <el-descriptions-item label="管网编号">{{ detailData.pipeCode }}</el-descriptions-item>
-        <el-descriptions-item label="管网名称">{{ detailData.pipeName }}</el-descriptions-item>
-        <el-descriptions-item label="管道材质">{{ detailData.pipeMaterial }}</el-descriptions-item>
-        <el-descriptions-item label="管径(mm)">{{ detailData.pipeDiameter }}</el-descriptions-item>
-        <el-descriptions-item label="管道长度(m)">{{ detailData.pipeLength }}</el-descriptions-item>
-        <el-descriptions-item label="铺设年份">{{ detailData.layingYear }}</el-descriptions-item>
-        <el-descriptions-item label="设计压力(MPa)">{{ detailData.pressureRating }}</el-descriptions-item>
+        <el-descriptions-item label="管网编号">{{ detailData.networkCode }}</el-descriptions-item>
+        <el-descriptions-item label="管网名称">{{ detailData.networkName }}</el-descriptions-item>
+        <el-descriptions-item label="等级">{{ dictLabel(levelDict, detailData.networkLevel) || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="管养单位">{{ detailData.managementUnit }}</el-descriptions-item>
+        <el-descriptions-item label="管道材质">{{ detailData.material }}</el-descriptions-item>
+        <el-descriptions-item label="管径(mm)">{{ detailData.diameter }}</el-descriptions-item>
+        <el-descriptions-item label="管道长度(m)">{{ detailData.length }}</el-descriptions-item>
+        <el-descriptions-item label="建设日期">{{ detailData.buildDate }}</el-descriptions-item>
         <el-descriptions-item label="运行状态">
-          <el-tag :type="normalizePipeStatus(detailData.status) === '0' ? 'success' : 'danger'" size="small">
-            {{ normalizePipeStatus(detailData.status) === '0' ? '正常' : '停用' }}
+          <el-tag :type="String(detailData.status) === '0' ? 'success' : 'danger'" size="small">
+            {{ dictLabel(statusDict, detailData.status) || '-' }}
           </el-tag>
         </el-descriptions-item>
-        <el-descriptions-item label="起点位置" :span="2">{{ detailData.startPoint }}</el-descriptions-item>
-        <el-descriptions-item label="终点位置" :span="2">{{ detailData.endPoint }}</el-descriptions-item>
+        <el-descriptions-item label="位置描述" :span="2">{{ detailData.location }}</el-descriptions-item>
         <el-descriptions-item label="起点经度">{{ detailData.startLongitude }}</el-descriptions-item>
         <el-descriptions-item label="起点纬度">{{ detailData.startLatitude }}</el-descriptions-item>
         <el-descriptions-item label="终点经度">{{ detailData.endLongitude }}</el-descriptions-item>
@@ -239,55 +245,15 @@
       </template>
     </el-drawer>
 
-    <WaterFacilityImportDialog v-model="importVisible" title="管网信息" upload-url="/api/water/pipe/importData" template-url="/api/water/pipe/importTemplate" @imported="getList" />
-
-    <!-- 回收站对话框 -->
-    <el-dialog title="回收站" v-model:visible="recycleVisible" width="900px" append-to-body>
-      <el-form :model="recycleQuery" ref="recycleForm" :inline="true" size="small">
-        <el-form-item label="管网编号" prop="pipeCode">
-          <el-input v-model="recycleQuery.pipeCode" placeholder="请输入编号" clearable />
-        </el-form-item>
-        <el-form-item label="管网名称" prop="pipeName">
-          <el-input v-model="recycleQuery.pipeName" placeholder="请输入名称" clearable />
-        </el-form-item>
-        <el-form-item>
-          <el-button type="primary" icon="Search" @click="getRecycleList">搜索</el-button>
-          <el-button icon="Refresh" @click="resetRecycleQuery">重置</el-button>
-        </el-form-item>
-      </el-form>
-      <el-table v-loading="recycleLoading" :data="recycleData" @selection-change="handleRecycleSelectionChange">
-        <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="管网编号" align="center" prop="pipeCode" />
-        <el-table-column label="管网名称" align="center" prop="pipeName" />
-        <el-table-column label="管道材质" align="center" prop="pipeMaterial" width="100" />
-        <el-table-column label="管径(mm)" align="center" prop="pipeDiameter" width="100" />
-        <el-table-column label="长度(m)" align="center" prop="pipeLength" width="100" />
-        <el-table-column label="状态" align="center" width="80">
-          <template #default="{ row }">
-            <dict-tag :options="sysNormalDisable" :value="row.status" />
-          </template>
-        </el-table-column>
-        <el-table-column label="删除时间" align="center" prop="updateTime" width="160" />
-        <el-table-column label="操作" align="center" width="150">
-          <template #default="{ row }">
-            <el-button size="small" type="text" icon="RefreshLeft" @click="handleRestore(row)">恢复</el-button>
-            <el-button size="small" type="text" icon="Delete" @click="handleDeletePhysically(row)">
-              <span style="color: #F56C6C">彻底删除</span>
-            </el-button>
-          </template>
-        </el-table-column>
-      </el-table>
-      <pagination v-show="recycleTotal > 0" :total="recycleTotal" v-model:page="recycleQuery.pageNum" v-model:limit="recycleQuery.pageSize" @pagination="getRecycleList" />
-    </el-dialog>
+    <WaterFacilityImportDialog v-model="importVisible" title="管网信息" upload-url="/PipeNetworkBase/importData" template-url="/PipeNetworkBase/importTemplate" @imported="getList" />
   </div>
 </template>
 
 <script>
-import { listPipeInfo, getPipeInfo, addPipeInfo, updatePipeInfo, delPipeInfo, listRecyclePipeInfo, restorePipeInfo, deletePhysicallyPipeInfo } from '@/api/pipeNetwork/pipeInfo'
+import { listPipeInfo, getPipeInfo, addPipeInfo, updatePipeInfo, delPipeInfo, exportPipeInfo } from '@/api/pipeNetwork/pipeInfo'
 import { getDicts } from '@/api/system/dict/data'
 import { saveAs } from 'file-saver'
-import { exportWaterPipe } from '@/api/pipeNetwork/waterSupply'
-import { buildWaterFacilityQuery, getRequestErrorMessage, normalizePipeStatus } from '@/utils/waterFacilityQuery'
+import { buildWaterFacilityQuery, getRequestErrorMessage } from '@/utils/waterFacilityQuery'
 import WaterFacilityImportDialog from '@/views/subSystem/waterSupply/components/WaterFacilityImportDialog.vue'
 
 export default {
@@ -300,8 +266,6 @@ export default {
       loading: true,
       // 选中数组
       ids: [],
-      // 非单个禁用
-      single: true,
       // 非多个禁用
       multiple: true,
       // 显示搜索条件
@@ -321,58 +285,46 @@ export default {
       importVisible: false,
       // 详情数据
       detailData: {},
-      // 回收站显示
-      recycleVisible: false,
-      // 回收站加载
-      recycleLoading: false,
-      // 回收站数据
-      recycleData: [],
-      // 回收站总条数
-      recycleTotal: 0,
-      // 回收站选中数组
-      recycleIds: [],
       errorMessage: '',
       retryAction: null,
-      // 系统状态字典
-      sysNormalDisable: [ 
-        { value: '0', label: '正常' },
-        { value: '1', label: '停用' }
-      ],
+      // 管网状态字典(pipe_network_status:0正常 1异常)
+      statusDict: [],
+      // 管网等级字典(pipe_network_level)
+      levelDict: [],
       // 查询参数
       queryParams: {
         pageNum: 1,
         pageSize: 10,
-        pipeCode: undefined,
-        pipeName: undefined,
-        pipeMaterial: undefined,
-        pipeDiameter: undefined,
-        layingYear: undefined,
+        networkCode: undefined,
+        networkName: undefined,
+        material: undefined,
+        diameter: undefined,
+        buildYear: undefined,
         status: undefined
       },
-      // 回收站查询参数
-      recycleQuery: {
-        pageNum: 1,
-        pageSize: 10,
-        pipeCode: undefined,
-        pipeName: undefined
-      },
       // 表单参数
       form: {},
       // 表单校验
       rules: {
-        pipeCode: [
+        networkCode: [
           { required: true, message: '管网编号不能为空', trigger: 'blur' }
         ],
-        pipeName: [
+        networkName: [
           { required: true, message: '管网名称不能为空', trigger: 'blur' }
         ],
-        pipeMaterial: [
+        material: [
           { required: true, message: '请选择管道材质', trigger: 'change' }
         ]
       }
     }
   },
   created() {
+    getDicts('pipe_network_status').then(response => {
+      this.statusDict = response.data
+    })
+    getDicts('pipe_network_level').then(response => {
+      this.levelDict = response.data
+    })
     this.getList()
   },
   methods: {
@@ -382,16 +334,14 @@ export default {
       this.errorMessage = ''
       this.retryAction = this.getList
       listPipeInfo(buildWaterFacilityQuery(this.queryParams, this.dateRange)).then(response => {
-        this.tableData = response.data.records
-        this.total = response.data.total
+        this.tableData = response.records
+        this.total = response.total
         this.loading = false
-        console.log("入参线信息列表",this.queryParams)
       }).catch(error => {
         this.loading = false
         this.errorMessage = getRequestErrorMessage(error, '管网信息查询失败,请稍后重试')
       })
     },
-    normalizePipeStatus,
     retryRequest() {
       if (this.retryAction) this.retryAction()
     },
@@ -400,6 +350,16 @@ export default {
       const map = { '铸铁': '', 'PE': 'success', '钢管': 'warning', '球墨铸铁': 'info', 'PVC': 'danger', '混凝土': '' }
       return map[material] || ''
     },
+    /** 字典标签 */
+    dictLabel(options, value) {
+      const item = (options || []).find(d => String(d.value) === String(value))
+      return item ? item.label : ''
+    },
+    /** 经纬度展示 */
+    formatCoord(lon, lat) {
+      if (lon === null || lon === undefined || lat === null || lat === undefined) return '-'
+      return lon + ', ' + lat
+    },
     /** 搜索按钮操作 */
     handleQuery() {
       this.queryParams.pageNum = 1
@@ -416,21 +376,16 @@ export default {
     handleExport() {
       this.errorMessage = ''
       this.retryAction = this.handleExport
-      exportWaterPipe(buildWaterFacilityQuery(this.queryParams, this.dateRange)).then(blob => {
+      exportPipeInfo(buildWaterFacilityQuery(this.queryParams, this.dateRange)).then(blob => {
         saveAs(new Blob([blob]), `管网信息_${new Date().toISOString().slice(0, 10)}.xlsx`)
       }).catch(error => {
         this.errorMessage = getRequestErrorMessage(error, '管网信息导出失败,请稍后重试')
       })
     },
     handleSelectionChange(selection) {
-      this.ids = selection.map(item => item.id)
-      this.single = selection.length !== 1
+      this.ids = selection.map(item => item.networkId)
       this.multiple = !selection.length
     },
-    /** 回收站多选框选中 */
-    handleRecycleSelectionChange(selection) {
-      this.recycleIds = selection.map(item => item.id)
-    },
     /** 新增按钮操作 */
     handleAdd() {
       this.reset()
@@ -440,7 +395,7 @@ export default {
     /** 修改按钮操作 */
     handleUpdate(row) {
       this.reset()
-      const id = row.id || this.ids
+      const id = row.networkId || this.ids
       getPipeInfo(id).then(response => {
         this.form = response.data
         this.dialogTitle = '修改管网信息'
@@ -451,7 +406,7 @@ export default {
     handleDetail(row) {
       this.errorMessage = ''
       this.retryAction = () => this.handleDetail(row)
-      getPipeInfo(row.id).then(response => {
+      getPipeInfo(row.networkId).then(response => {
         this.detailData = response.data
         this.detailVisible = true
       }).catch(error => {
@@ -462,7 +417,7 @@ export default {
     submitForm() {
       this.$refs.form.validate(valid => {
         if (valid) {
-          if (this.form.id != undefined) {
+          if (this.form.networkId != undefined) {
             updatePipeInfo(this.form).then(() => {
               this.$modal.msgSuccess('修改成功')
               this.dialogVisible = false
@@ -480,60 +435,19 @@ export default {
     },
     /** 删除按钮操作 */
     handleDelete(row) {
-      const ids = row.id ? [row.id] : this.ids
-      this.$modal.confirm('是否确认删除管网编号为"' + ids.join(',') + '"的数据项?').then(() => {
+      const ids = row.networkId ? [row.networkId] : this.ids
+      this.$modal.confirm('是否确认删除管网编号为"' + ids.join(',') + '"的数据项?删除后不可恢复。').then(() => {
         return delPipeInfo(ids.join(','))
       }).then(() => {
         this.getList()
         this.$modal.msgSuccess('删除成功')
       }).catch(() => {})
     },
-    /** 打开回收站 */
-    handleRecycle() {
-      this.recycleVisible = true
-      this.getRecycleList()
-    },
-    /** 查询回收站列表 */
-    getRecycleList() {
-      this.recycleLoading = true
-      listRecyclePipeInfo(this.recycleQuery).then(response => {
-        this.recycleData = response.data.records
-        this.recycleTotal = response.data.total
-        this.recycleLoading = false
-      }).catch(() => {
-        this.recycleLoading = false
-      })
-    },
-    /** 回收站搜索重置 */
-    resetRecycleQuery() {
-      this.resetForm('recycleForm')
-      this.getRecycleList()
-    },
-    /** 恢复数据 */
-    handleRestore(row) {
-      const ids = row.id || this.recycleIds
-      this.$modal.confirm('是否确认恢复该数据?').then(() => {
-        return restorePipeInfo(Array.isArray(ids) ? ids.join(',') : ids)
-      }).then(() => {
-        this.getRecycleList()
-        this.getList()
-        this.$modal.msgSuccess('恢复成功')
-      }).catch(() => {})
-    },
-    /** 彻底删除 */
-    handleDeletePhysically(row) {
-      const ids = row.id || this.recycleIds
-      this.$modal.confirm('彻底删除后数据不可恢复,是否继续?').then(() => {
-        return deletePhysicallyPipeInfo(Array.isArray(ids) ? ids.join(',') : ids)
-      }).then(() => {
-        this.getRecycleList()
-        this.$modal.msgSuccess('彻底删除成功')
-      }).catch(() => {})
-    },
     /** 表单重置 */
     reset() {
       this.form = {
-        status: '0'
+        status: '0',
+        networkType: '2'
       }
       this.resetForm('form')
     }

+ 14 - 4
src/views/subSystem/waterSupply/components/WaterFacilityDashboard.vue

@@ -102,6 +102,7 @@ import { useRoute } from 'vue-router'
 import { DataAnalysis, DataLine, Histogram, InfoFilled, Location, MapLocation, PieChart, Refresh, Setting } from '@element-plus/icons-vue'
 import { saveAs } from 'file-saver'
 import * as echarts from 'echarts'
+import { getDicts } from '@/api/system/dict/data'
 import { exportPipeNetworkStatistics, getPipeNetworkDashboard, getWaterFacilityDetail, getWaterFacilityFeatures, getWaterFacilityOverview } from '@/api/pipeNetwork/waterSupply'
 import {
   FACILITY_SECTION_LABELS,
@@ -139,7 +140,7 @@ const STATISTIC_LABELS = {
   materialDistribution: '管材分布',
   diameterDistribution: '管径分布',
   layingYearDistribution: '建设年代分布',
-  pressureDistribution: '压力等级分布',
+  levelDistribution: '等级分布',
   statusDistribution: '状态分布',
   waterSourceTypeDistribution: '水源类型分布',
   sourceTypeDistribution: '水源地类型分布',
@@ -163,7 +164,7 @@ const STATISTIC_DESCRIPTIONS = {
   materialDistribution: '按管材统计管网数量',
   diameterDistribution: '按管径区间统计管网数量',
   layingYearDistribution: '按建设年代统计管网数量',
-  pressureDistribution: '按压力等级统计管网数量',
+  levelDistribution: '按等级统计管网数量',
   statusDistribution: '按业务状态统计设施数量',
   waterSourceTypeDistribution: '按水源类型统计水厂数量',
   sourceTypeDistribution: '按水源地类型统计数量',
@@ -186,6 +187,7 @@ const statusFilter = ref(String(route.query.status || ''))
 const loading = ref(false)
 const detailVisible = ref(false)
 const detail = ref({})
+const levelDict = ref([])
 const barChartRef = ref()
 const pieChartRef = ref()
 let barChart
@@ -225,6 +227,11 @@ const statusCategoryLabel = category => {
   return labels.size ? [...labels].join(' / ') : category === 'normal' ? '正常' : '未知'
 }
 
+const resolveLevelLabel = name => {
+  const item = levelDict.value.find(d => String(d.value) === String(name))
+  return item ? item.label : String(name ?? '')
+}
+
 const formatStatisticRow = (section, item) => {
   const [facilityKey, metricKey] = section.split('/')
   const rawMetricKey = metricKey || item.name
@@ -232,9 +239,12 @@ const formatStatisticRow = (section, item) => {
   const facilityLabel = FACILITY_SECTION_LABELS[facilityKey] || facilityKey || '总览'
   const metricLabel = STATISTIC_LABELS[rawMetricKey] || rawMetricKey || '总览'
   const isStatusDistribution = metricKey === 'statusDistribution'
+  const isLevelDistribution = metricKey === 'levelDistribution'
   const name = isStatusDistribution
     ? resolveFacilityStatusLabel(facilityType, item.name)
-    : item.name ?? metricLabel
+    : isLevelDistribution
+      ? resolveLevelLabel(item.name)
+      : item.name ?? metricLabel
 
   return {
     section: `${facilityLabel} - ${metricLabel}`,
@@ -376,7 +386,7 @@ const exportData = async () => {
 const pointStyle = point => ({ left: `${point.mapX}%`, top: `${point.mapY}%`, color: typeColor(point.type) })
 const openFeature = async point => { try { const response = await getWaterFacilityDetail(point.type, point.id?.split(':').pop()); detail.value = unwrapResponse(response) || point; detailVisible.value = true } catch {} }
 const resizeCharts = () => { barChart?.resize(); pieChart?.resize() }
-onMounted(() => { loadData(); window.addEventListener('resize', resizeCharts) })
+onMounted(() => { getDicts('pipe_network_level').then(response => { levelDict.value = response.data || [] }); loadData(); window.addEventListener('resize', resizeCharts) })
 onBeforeUnmount(() => { window.removeEventListener('resize', resizeCharts); barChart?.dispose(); pieChart?.dispose() })
 </script>