Ver código fonte

fix water facility query contract and errors

Kazerin 1 semana atrás
pai
commit
69bb19ce5f

+ 25 - 0
src/utils/waterFacilityQuery.js

@@ -0,0 +1,25 @@
+/**
+ * Build the flat query contract used by water-facility list and export APIs.
+ * The legacy addDateRange helper nests beginTime/endTime under params; these
+ * endpoints deliberately expose startTime/endTime at the top level instead.
+ */
+export function buildWaterFacilityQuery(queryParams = {}, dateRange = []) {
+  const query = { ...queryParams }
+  delete query.params
+  delete query.beginTime
+  delete query.endTime
+  delete query.startTime
+
+  const range = Array.isArray(dateRange) ? dateRange : []
+  if (range[0]) query.startTime = range[0]
+  if (range[1]) query.endTime = range[1]
+  return query
+}
+
+export function normalizePipeStatus(status) {
+  return status === null || status === undefined ? '' : String(status)
+}
+
+export function getRequestErrorMessage(error, fallback) {
+  return error?.response?.data?.msg || error?.response?.data?.message || error?.message || fallback
+}

+ 32 - 6
src/views/subSystem/basic/pipeInfo/index.vue

@@ -1,5 +1,9 @@
 <template>
   <div class="app-container">
+    <div v-if="errorMessage" class="facility-error">
+      <el-alert :title="errorMessage" type="error" show-icon :closable="false" />
+      <el-button type="primary" link size="small" @click="retryRequest">重试</el-button>
+    </div>
     <!-- 搜索区域 -->
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
       <el-form-item label="管网编号" prop="pipeCode">
@@ -18,6 +22,12 @@
           <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>
+      <el-form-item label="铺设年份" prop="layingYear">
+        <el-date-picker v-model="queryParams.layingYear" 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
@@ -186,8 +196,8 @@
         <el-descriptions-item label="铺设年份">{{ detailData.layingYear }}</el-descriptions-item>
         <el-descriptions-item label="设计压力(MPa)">{{ detailData.pressureRating }}</el-descriptions-item>
         <el-descriptions-item label="运行状态">
-          <el-tag :type="detailData.status === '0' ? 'success' : 'danger'" size="small">
-            {{ detailData.status === '0' ? '正常' : '停用' }}
+          <el-tag :type="normalizePipeStatus(detailData.status) === '0' ? 'success' : 'danger'" size="small">
+            {{ normalizePipeStatus(detailData.status) === '0' ? '正常' : '停用' }}
           </el-tag>
         </el-descriptions-item>
         <el-descriptions-item label="起点位置" :span="2">{{ detailData.startPoint }}</el-descriptions-item>
@@ -249,6 +259,7 @@ import { listPipeInfo, getPipeInfo, addPipeInfo, updatePipeInfo, delPipeInfo, li
 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'
 
 export default {
   name: 'WaterPipeInfo',
@@ -289,6 +300,8 @@ export default {
       recycleTotal: 0,
       // 回收站选中数组
       recycleIds: [],
+      errorMessage: '',
+      retryAction: null,
       // 系统状态字典
       sysNormalDisable: [ 
         { value: '0', label: '正常' },
@@ -301,6 +314,8 @@ export default {
         pipeCode: undefined,
         pipeName: undefined,
         pipeMaterial: undefined,
+        pipeDiameter: undefined,
+        layingYear: undefined,
         status: undefined
       },
       // 回收站查询参数
@@ -333,15 +348,22 @@ export default {
     /** 查询管网信息列表 */
     getList() {
       this.loading = true
-      listPipeInfo(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
+      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.loading = false
         console.log("入参线信息列表",this.queryParams)
-      }).catch(() => {
+      }).catch(error => {
         this.loading = false
+        this.errorMessage = getRequestErrorMessage(error, '管网信息查询失败,请稍后重试')
       })
     },
+    normalizePipeStatus,
+    retryRequest() {
+      if (this.retryAction) this.retryAction()
+    },
     /** 管道材质标签类型 */
     materialTagType(material) {
       const map = { '铸铁': '', 'PE': 'success', '钢管': 'warning', '球墨铸铁': 'info', 'PVC': 'danger', '混凝土': '' }
@@ -361,9 +383,13 @@ export default {
     /** 多选框选中数据 */
     /** 导出当前筛选结果 */
     handleExport() {
-      exportWaterPipe(this.addDateRange(this.queryParams, this.dateRange)).then(blob => {
+      this.errorMessage = ''
+      this.retryAction = this.handleExport
+      exportWaterPipe(buildWaterFacilityQuery(this.queryParams, this.dateRange)).then(blob => {
         saveAs(new Blob([blob]), `管网信息_${new Date().toISOString().slice(0, 10)}.xlsx`)
-      }).catch(() => {})
+      }).catch(error => {
+        this.errorMessage = getRequestErrorMessage(error, '管网信息导出失败,请稍后重试')
+      })
     },
     handleSelectionChange(selection) {
       this.ids = selection.map(item => item.id)

+ 29 - 2
src/views/subSystem/basic/pumpStation/index.vue

@@ -1,5 +1,9 @@
 <template>
   <div class="app-container">
+    <div v-if="errorMessage" class="facility-error">
+      <el-alert :title="errorMessage" type="error" show-icon :closable="false" />
+      <el-button type="primary" link size="small" @click="retryRequest">重试</el-button>
+    </div>
     <!-- 搜索区域 -->
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="80px">
       <el-form-item label="泵站编号" prop="stationCode">
@@ -47,6 +51,9 @@
       <!-- <el-col :span="1.5">
         <el-button type="warning" plain icon="Delete" size="small" @click="handleRecycle" v-hasPermi="['waterSupply:pumpStation:remove']">回收站</el-button>
       </el-col> -->
+      <el-col :span="1.5">
+        <el-button type="success" plain icon="Download" size="small" @click="handleExport" v-hasPermi="['waterSupply:waterPumpStation:export']">导出</el-button>
+      </el-col>
       <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
     </el-row>
 
@@ -226,6 +233,9 @@
 
 <script>
 import { listPumpStation, getPumpStation, addPumpStation, updatePumpStation, delPumpStation, listRecyclePumpStation, restorePumpStation, deletePhysicallyPumpStation } from '@/api/pipeNetwork/waterPumpStation'
+import { saveAs } from 'file-saver'
+import { exportWaterPumpStation } from '@/api/pipeNetwork/waterSupply'
+import { buildWaterFacilityQuery, getRequestErrorMessage } from '@/utils/waterFacilityQuery'
 
 export default {
   name: 'WaterPumpStation',
@@ -248,6 +258,8 @@ export default {
       recycleData: [],
       recycleTotal: 0,
       recycleIds: [],
+      errorMessage: '',
+      retryAction: null,
       queryParams: {
         pageNum: 1,
         pageSize: 10,
@@ -279,14 +291,20 @@ export default {
   methods: {
     getList() {
       this.loading = true
-      listPumpStation(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
+      this.errorMessage = ''
+      this.retryAction = this.getList
+      listPumpStation(buildWaterFacilityQuery(this.queryParams, this.dateRange)).then(response => {
         this.tableData = response.data.records
         this.total = response.data.total
         this.loading = false
-      }).catch(() => {
+      }).catch(error => {
         this.loading = false
+        this.errorMessage = getRequestErrorMessage(error, '泵站信息查询失败,请稍后重试')
       })
     },
+    retryRequest() {
+      if (this.retryAction) this.retryAction()
+    },
     statusTagType(status) {
       const map = { '0': 'success', '1': 'danger', '2': 'warning' }
       return map[status] || 'info'
@@ -304,6 +322,15 @@ export default {
       this.resetForm('queryForm')
       this.handleQuery()
     },
+    handleExport() {
+      this.errorMessage = ''
+      this.retryAction = this.handleExport
+      exportWaterPumpStation(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

+ 29 - 2
src/views/subSystem/basic/waterPlant/index.vue

@@ -1,5 +1,9 @@
 <template>
   <div class="app-container">
+    <div v-if="errorMessage" class="facility-error">
+      <el-alert :title="errorMessage" type="error" show-icon :closable="false" />
+      <el-button type="primary" link size="small" @click="retryRequest">重试</el-button>
+    </div>
     <!-- 搜索区域 -->
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="80px">
       <el-form-item label="水厂编号" prop="plantCode">
@@ -46,6 +50,9 @@
       <!-- <el-col :span="1.5">
         <el-button type="warning" plain icon="Delete" size="small" @click="handleRecycle" v-hasPermi="['waterSupply:waterPlant:remove']">回收站</el-button>
       </el-col> -->
+      <el-col :span="1.5">
+        <el-button type="success" plain icon="Download" size="small" @click="handleExport" v-hasPermi="['waterSupply:waterPlant:export']">导出</el-button>
+      </el-col>
       <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
     </el-row>
 
@@ -231,6 +238,9 @@
 
 <script>
 import { listWaterPlant, getWaterPlant, addWaterPlant, updateWaterPlant, delWaterPlant, listRecycleWaterPlant, restoreWaterPlant, deletePhysicallyWaterPlant } from '@/api/pipeNetwork/waterPlantInfo'
+import { saveAs } from 'file-saver'
+import { exportWaterPlant } from '@/api/pipeNetwork/waterSupply'
+import { buildWaterFacilityQuery, getRequestErrorMessage } from '@/utils/waterFacilityQuery'
 
 export default {
   name: 'WaterPlant',
@@ -253,6 +263,8 @@ export default {
       recycleData: [],
       recycleTotal: 0,
       recycleIds: [],
+      errorMessage: '',
+      retryAction: null,
       queryParams: {
         pageNum: 1,
         pageSize: 10,
@@ -284,14 +296,20 @@ export default {
   methods: {
     getList() {
       this.loading = true
-      listWaterPlant(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
+      this.errorMessage = ''
+      this.retryAction = this.getList
+      listWaterPlant(buildWaterFacilityQuery(this.queryParams, this.dateRange)).then(response => {
         this.tableData = response.data.records
         this.total = response.data.total
         this.loading = false
-      }).catch(() => {
+      }).catch(error => {
         this.loading = false
+        this.errorMessage = getRequestErrorMessage(error, '水厂信息查询失败,请稍后重试')
       })
     },
+    retryRequest() {
+      if (this.retryAction) this.retryAction()
+    },
     statusTagType(status) {
       const map = { '0': 'success', '1': 'danger' }
       return map[status] || 'info'
@@ -309,6 +327,15 @@ export default {
       this.resetForm('queryForm')
       this.handleQuery()
     },
+    handleExport() {
+      this.errorMessage = ''
+      this.retryAction = this.handleExport
+      exportWaterPlant(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

+ 29 - 2
src/views/subSystem/basic/waterSource/index.vue

@@ -1,5 +1,9 @@
 <template>
   <div class="app-container">
+    <div v-if="errorMessage" class="facility-error">
+      <el-alert :title="errorMessage" type="error" show-icon :closable="false" />
+      <el-button type="primary" link size="small" @click="retryRequest">重试</el-button>
+    </div>
     <!-- 搜索区域 -->
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="80px">
       <el-form-item label="水源地ID" prop="sourceId">
@@ -46,6 +50,9 @@
       <!-- <el-col :span="1.5">
         <el-button type="warning" plain icon="Delete" size="small" @click="handleRecycle" v-hasPermi="['waterSupply:waterSource:remove']">回收站</el-button>
       </el-col> -->
+      <el-col :span="1.5">
+        <el-button type="success" plain icon="Download" size="small" @click="handleExport" v-hasPermi="['waterSupply:waterSource:export']">导出</el-button>
+      </el-col>
       <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
     </el-row>
 
@@ -236,6 +243,9 @@
 
 <script>
 import { listWaterSource, getWaterSource, addWaterSource, updateWaterSource, delWaterSource, listRecycleWaterSource, restoreWaterSource, deletePhysicallyWaterSource } from '@/api/pipeNetwork/waterSourceInfo'
+import { saveAs } from 'file-saver'
+import { exportWaterSource } from '@/api/pipeNetwork/waterSupply'
+import { buildWaterFacilityQuery, getRequestErrorMessage } from '@/utils/waterFacilityQuery'
 
 export default {
   name: 'WaterSource',
@@ -258,6 +268,8 @@ export default {
       recycleData: [],
       recycleTotal: 0,
       recycleIds: [],
+      errorMessage: '',
+      retryAction: null,
       queryParams: {
         pageNum: 1,
         pageSize: 10,
@@ -289,14 +301,20 @@ export default {
   methods: {
     getList() {
       this.loading = true
-      listWaterSource(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
+      this.errorMessage = ''
+      this.retryAction = this.getList
+      listWaterSource(buildWaterFacilityQuery(this.queryParams, this.dateRange)).then(response => {
         this.tableData = response.data.records
         this.total = response.data.total
         this.loading = false
-      }).catch(() => {
+      }).catch(error => {
         this.loading = false
+        this.errorMessage = getRequestErrorMessage(error, '水源地信息查询失败,请稍后重试')
       })
     },
+    retryRequest() {
+      if (this.retryAction) this.retryAction()
+    },
     statusTagType(status) {
       const map = { '0': 'success', '1': 'danger' }
       return map[status] || 'info'
@@ -314,6 +332,15 @@ export default {
       this.resetForm('queryForm')
       this.handleQuery()
     },
+    handleExport() {
+      this.errorMessage = ''
+      this.retryAction = this.handleExport
+      exportWaterSource(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

+ 29 - 2
src/views/subSystem/basic/waterUserInfo/index.vue

@@ -1,5 +1,9 @@
 <template>
   <div class="app-container">
+    <div v-if="errorMessage" class="facility-error">
+      <el-alert :title="errorMessage" type="error" show-icon :closable="false" />
+      <el-button type="primary" link size="small" @click="retryRequest">重试</el-button>
+    </div>
     <!-- 搜索区域 -->
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="80px">
       <el-form-item label="用户编号" prop="userCode">
@@ -52,6 +56,9 @@
       <!-- <el-col :span="1.5">
         <el-button type="warning" plain icon="Delete" size="small" @click="handleRecycle" v-hasPermi="['waterSupply:waterUser:remove']">回收站</el-button>
       </el-col> -->
+      <el-col :span="1.5">
+        <el-button type="success" plain icon="Download" size="small" @click="handleExport" v-hasPermi="['waterSupply:waterUser:export']">导出</el-button>
+      </el-col>
       <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
     </el-row>
 
@@ -237,6 +244,9 @@
 
 <script>
 import { listWaterUser, getWaterUser, addWaterUser, updateWaterUser, delWaterUser, listRecycleWaterUser, restoreWaterUser, deletePhysicallyWaterUser } from '@/api/pipeNetwork/waterUserInfo'
+import { saveAs } from 'file-saver'
+import { exportWaterUser } from '@/api/pipeNetwork/waterSupply'
+import { buildWaterFacilityQuery, getRequestErrorMessage } from '@/utils/waterFacilityQuery'
 
 export default {
   name: 'WaterUser',
@@ -259,6 +269,8 @@ export default {
       recycleData: [],
       recycleTotal: 0,
       recycleIds: [],
+      errorMessage: '',
+      retryAction: null,
       queryParams: {
         pageNum: 1,
         pageSize: 10,
@@ -290,14 +302,20 @@ export default {
   methods: {
     getList() {
       this.loading = true
-      listWaterUser(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
+      this.errorMessage = ''
+      this.retryAction = this.getList
+      listWaterUser(buildWaterFacilityQuery(this.queryParams, this.dateRange)).then(response => {
         this.tableData = response.data.records
         this.total = response.data.total
         this.loading = false
-      }).catch(() => {
+      }).catch(error => {
         this.loading = false
+        this.errorMessage = getRequestErrorMessage(error, '用水户信息查询失败,请稍后重试')
       })
     },
+    retryRequest() {
+      if (this.retryAction) this.retryAction()
+    },
     statusTagType(status) {
       const map = { '0': 'success', '1': 'warning', '2': 'danger' }
       return map[status] || 'info'
@@ -315,6 +333,15 @@ export default {
       this.resetForm('queryForm')
       this.handleQuery()
     },
+    handleExport() {
+      this.errorMessage = ''
+      this.retryAction = this.handleExport
+      exportWaterUser(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

+ 44 - 0
tests/waterFacilityQuery.test.mjs

@@ -0,0 +1,44 @@
+import test from 'node:test'
+import assert from 'node:assert/strict'
+
+import {
+  buildWaterFacilityQuery,
+  getRequestErrorMessage,
+  normalizePipeStatus
+} from '../src/utils/waterFacilityQuery.js'
+
+test('buildWaterFacilityQuery maps date range to top-level backend contract', () => {
+  const source = { pageNum: 2, pageSize: 20, pipeCode: 'P-1' }
+  const query = buildWaterFacilityQuery(source, ['2026-01-01 00:00:00', '2026-01-31 23:59:59'])
+
+  assert.deepEqual(query, {
+    pageNum: 2,
+    pageSize: 20,
+    pipeCode: 'P-1',
+    startTime: '2026-01-01 00:00:00',
+    endTime: '2026-01-31 23:59:59'
+  })
+  assert.deepEqual(source, { pageNum: 2, pageSize: 20, pipeCode: 'P-1' })
+  assert.equal(query.params, undefined)
+})
+
+test('buildWaterFacilityQuery removes stale dates when range is cleared', () => {
+  const query = buildWaterFacilityQuery({ pageNum: 1, startTime: 'old', endTime: 'old' }, [])
+
+  assert.equal(query.startTime, undefined)
+  assert.equal(query.endTime, undefined)
+  assert.equal(query.beginTime, undefined)
+  assert.equal(query.params, undefined)
+})
+
+test('normalizePipeStatus accepts numeric and string status values', () => {
+  assert.equal(normalizePipeStatus(0), '0')
+  assert.equal(normalizePipeStatus('1'), '1')
+  assert.equal(normalizePipeStatus(null), '')
+})
+
+test('getRequestErrorMessage exposes a useful server error', () => {
+  assert.equal(getRequestErrorMessage({ response: { data: { msg: '数据库不可用' } } }, '查询失败'), '数据库不可用')
+  assert.equal(getRequestErrorMessage(new Error('网络断开'), '查询失败'), '网络断开')
+  assert.equal(getRequestErrorMessage({}, '查询失败'), '查询失败')
+})