|
|
@@ -0,0 +1,839 @@
|
|
|
+<template>
|
|
|
+ <div class="app-container">
|
|
|
+ <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
|
|
|
+ <el-form-item label="站码" prop="stcd">
|
|
|
+ <el-input v-model="queryParams.stcd" placeholder="请输入站码" clearable @keyup.enter="handleQuery"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="站名" prop="stnm">
|
|
|
+ <el-input v-model="queryParams.stnm" placeholder="请输入站名" clearable @keyup.enter="handleQuery"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="站点类型" prop="tp">
|
|
|
+ <el-select v-model="queryParams.tp" placeholder="请选择站点类型" clearable>
|
|
|
+ <el-option label="水文站" value="ZZ"/>
|
|
|
+ <el-option label="雨量站" value="DRP"/>
|
|
|
+ <el-option label="取水户" value="WIU"/>
|
|
|
+ <el-option label="水库" value="SK"/>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="省份" prop="prov">
|
|
|
+ <el-input v-model="queryParams.prov" placeholder="请输入省份" clearable @keyup.enter="handleQuery"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button type="primary" icon="Search" @click="handleQuery">查询</el-button>
|
|
|
+ <el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
+ <el-row :gutter="10" class="mb8">
|
|
|
+ <el-col :span="1.5">
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ plain
|
|
|
+ icon="Upload"
|
|
|
+ @click="handleImport"
|
|
|
+ size="mini"
|
|
|
+ v-hasPermi="['xajgl:station:upload']"
|
|
|
+ >上传数据
|
|
|
+ </el-button>
|
|
|
+ </el-col>
|
|
|
+ <right-toolbar v-model:showSearch="showSearch" @queryTable="getList">
|
|
|
+ <template #right>
|
|
|
+ <el-button type="primary" icon="Plus" @click="handleAdd" v-hasPermi="['xajgl:station:add']">新增站点
|
|
|
+ </el-button>
|
|
|
+ <el-button type="success" icon="Download" @click="handleExport" v-hasPermi="['xajgl:station:export']">导出
|
|
|
+ </el-button>
|
|
|
+ <el-button type="warning" icon="Upload" @click="handleImport" v-hasPermi="['xajgl:station:import']">导入
|
|
|
+ </el-button>
|
|
|
+ <el-button type="danger" icon="Delete" @click="handleBatchDelete" v-hasPermi="['xajgl:station:remove']"
|
|
|
+ :disabled="selectedRows.length === 0">批量删除
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
+ </right-toolbar>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <el-table v-loading="loading" :data="tableData" @selection-change="handleSelectionChange">
|
|
|
+ <el-table-column type="selection" width="55" align="center"/>
|
|
|
+ <el-table-column type="index" label="序号" width="60" align="center"/>
|
|
|
+ <el-table-column prop="stcd" label="站码" width="120"/>
|
|
|
+ <el-table-column prop="stnm" label="站名" width="220"/>
|
|
|
+ <el-table-column prop="tp" label="类型" width="100">
|
|
|
+ <template #default="scope">{{ getTpLabel(scope.row.tp) }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="lgtd" label="经度" width="130" align="right">
|
|
|
+ <template #default="scope">{{ scope.row.lgtd ? Number(scope.row.lgtd).toFixed(6) : '' }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="lttd" label="纬度" width="130" align="right">
|
|
|
+ <template #default="scope">{{ scope.row.lttd ? Number(scope.row.lttd).toFixed(6) : '' }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="prov" label="省份" width="100"/>
|
|
|
+ <el-table-column prop="city" label="地市" width="100"/>
|
|
|
+ <el-table-column prop="district" label="区县" width="100"/>
|
|
|
+ <el-table-column prop="rvnm" label="河流名称" width="120"/>
|
|
|
+ <el-table-column prop="addvcd" label="行政区划编码" width="120"/>
|
|
|
+ <el-table-column prop="bizCode" label="业务域编码" width="120"/>
|
|
|
+ <el-table-column label="操作" width="260" align="center" fixed="right">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-button size="mini" type="text" icon="View" @click="handleDetail(scope.row)">详情</el-button>
|
|
|
+ <el-button size="mini" type="primary" icon="Edit" @click="handleEdit(scope.row)"
|
|
|
+ v-hasPermi="['xajgl:station:edit']">修改
|
|
|
+ </el-button>
|
|
|
+ <el-button size="mini" type="text" icon="Delete" @click="handleDelete(scope.row)"
|
|
|
+ v-hasPermi="['xajgl:station:remove']">删除
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <pagination
|
|
|
+ v-show="total>0"
|
|
|
+ :total="total"
|
|
|
+ v-model:page="queryParams.pageNum"
|
|
|
+ v-model:limit="queryParams.pageSize"
|
|
|
+ @pagination="getList"
|
|
|
+ />
|
|
|
+
|
|
|
+ <!-- 新增/修改弹窗 -->
|
|
|
+ <el-dialog :title="isEdit ? '修改站点信息' : '新增站点信息'" v-model="formVisible" width="700px" append-to-body>
|
|
|
+ <el-form :model="form" ref="formRef" label-width="120px">
|
|
|
+ <el-form-item label="站码" prop="stcd" required>
|
|
|
+ <el-input v-model="form.stcd" :disabled="isEdit" placeholder="请输入站码"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="站名" prop="stnm" required>
|
|
|
+ <el-input v-model="form.stnm" placeholder="请输入站名"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="站点类型" prop="tp">
|
|
|
+ <el-select v-model="form.tp" placeholder="请选择站点类型">
|
|
|
+ <el-option label="水文站" value="ZZ"/>
|
|
|
+ <el-option label="雨量站" value="DRP"/>
|
|
|
+ <el-option label="取水户" value="WIU"/>
|
|
|
+ <el-option label="水库" value="SK"/>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="经度" prop="lgtd">
|
|
|
+ <el-input v-model="form.lgtd" placeholder="请输入经度" type="number"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="纬度" prop="lttd">
|
|
|
+ <el-input v-model="form.lttd" placeholder="请输入纬度" type="number"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="省份" prop="prov">
|
|
|
+ <el-input v-model="form.prov" placeholder="请输入省份"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="地市" prop="city">
|
|
|
+ <el-input v-model="form.city" placeholder="请输入地市"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="区县" prop="district">
|
|
|
+ <el-input v-model="form.district" placeholder="请输入区县"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="河流名称" prop="rvnm">
|
|
|
+ <el-input v-model="form.rvnm" placeholder="请输入河流名称"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="行政区划编码" prop="addvcd">
|
|
|
+ <el-input v-model="form.addvcd" placeholder="请输入行政区划编码"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="业务域编码" prop="bizCode">
|
|
|
+ <el-input v-model="form.bizCode" placeholder="请输入业务域编码"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <template #footer>
|
|
|
+ <el-button type="primary" @click="submitForm">确定</el-button>
|
|
|
+ <el-button @click="formVisible = false">取消</el-button>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- 详情弹窗 -->
|
|
|
+ <el-dialog title="站点详情" v-model="detailVisible" width="1200px" append-to-body @close="handleDetailClose">
|
|
|
+ <!-- 基本信息 -->
|
|
|
+ <div class="detail-section">
|
|
|
+ <div class="detail-section-title">基本信息</div>
|
|
|
+ <el-descriptions :column="3" border>
|
|
|
+ <el-descriptions-item label="站码">{{ detailData.stcd }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="站名">{{ detailData.stnm }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="站点类型">{{ getTpLabel(detailData.tp) }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="经度">{{ detailData.lgtd }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="纬度">{{ detailData.lttd }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="省份">{{ detailData.prov }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="地市">{{ detailData.city }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="区县">{{ detailData.district }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="河流名称">{{ detailData.rvnm }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="行政区划编码">{{ detailData.addvcd }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="业务域编码">{{ detailData.bizCode }}</el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 水库基础信息(仅当 tp === 'SK' 时显示) -->
|
|
|
+ <div class="detail-section" v-if="detailData.tp === 'SK'">
|
|
|
+ <div class="detail-section-title">水库基础信息</div>
|
|
|
+ <el-descriptions :column="3" border v-loading="skLoading">
|
|
|
+ <el-descriptions-item label="水库类型">{{ skData.skType }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="建设地点">{{ skData.buildAddr }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="所在河流">{{ skData.rvnm }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="集水面积(km²)">{{ skData.catchArea }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="总库容(万m³)">{{ skData.totalCap }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="校核洪水位(m)">{{ skData.checkFloodZ }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="正常高水位(m)">{{ skData.normalZ }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="汛限水位(m)">{{ skData.floodLimitZ }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="死水位(m)">{{ skData.deadZ }}</el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 库容曲线图表(仅当 tp === 'SK' 时显示) -->
|
|
|
+ <div class="detail-section" v-if="detailData.tp === 'SK'">
|
|
|
+ <div class="detail-section-title">库容曲线</div>
|
|
|
+ <div ref="qxChartRef" class="chart-container"></div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 监测数据 -->
|
|
|
+ <div class="detail-section">
|
|
|
+ <div class="detail-section-title">监测数据</div>
|
|
|
+ <el-form :inline="true" class="detail-query-form">
|
|
|
+ <el-form-item label="时间范围">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="detailDateRange"
|
|
|
+ type="daterange"
|
|
|
+ range-separator="至"
|
|
|
+ start-placeholder="开始日期"
|
|
|
+ end-placeholder="结束日期"
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
+ @change="handleDetailDateChange"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button type="primary" icon="Search" @click="loadDetailData">查询</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <el-table v-loading="detailDataLoading" :data="detailDataList" border stripe max-height="320">
|
|
|
+ <el-table-column type="index" label="序号" width="60" align="center"/>
|
|
|
+ <el-table-column prop="tm" label="时间" width="180" align="center">
|
|
|
+ <template #default="scope">{{ formatDate(scope.row.tm) }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="drp" label="降雨量(mm)" width="120" align="center">
|
|
|
+ <template #default="scope">{{ formatDecimal(scope.row.drp) }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="z" label="水位(m)" width="100" align="center">
|
|
|
+ <template #default="scope">{{ formatDecimal(scope.row.z) }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="q" label="流量(m³/s)" width="120" align="center">
|
|
|
+ <template #default="scope">{{ formatDecimal(scope.row.q) }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="inq" label="入库流量(m³/s)" width="140" align="center">
|
|
|
+ <template #default="scope">{{ formatDecimal(scope.row.inq) }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="outq" label="出库流量(m³/s)" width="140" align="center">
|
|
|
+ <template #default="scope">{{ formatDecimal(scope.row.outq) }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="ww" label="下泄水量(万m³)" width="140" align="center">
|
|
|
+ <template #default="scope">{{ formatDecimal(scope.row.ww) }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="w" label="蓄水量(万m³)" width="140" align="center">
|
|
|
+ <template #default="scope">{{ formatDecimal(scope.row.w) }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <!-- 监测数据趋势图 -->
|
|
|
+ <div class="detail-section-title" style="margin-top: 15px;">监测数据趋势图</div>
|
|
|
+ <div ref="trendChartRef" class="chart-container"></div>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- 导入弹窗 -->
|
|
|
+ <el-dialog title="站点数据导入" v-model="importVisible" width="500px" append-to-body>
|
|
|
+ <el-form ref="importForm" label-width="100px">
|
|
|
+ <el-form-item label="上传文件">
|
|
|
+ <el-upload
|
|
|
+ class="upload-demo"
|
|
|
+ drag
|
|
|
+ :http-request="handleHttpRequest"
|
|
|
+ :on-success="handleUploadSuccess"
|
|
|
+ :on-error="handleUploadError"
|
|
|
+ :file-list="fileList"
|
|
|
+ accept=".xlsx,.xls"
|
|
|
+ :auto-upload="false"
|
|
|
+ ref="uploadRef"
|
|
|
+ >
|
|
|
+ <el-icon class="el-icon--upload">
|
|
|
+ <upload-filled/>
|
|
|
+ </el-icon>
|
|
|
+ <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
|
|
+ <template #tip>
|
|
|
+ <div class="el-upload__tip">
|
|
|
+ <el-icon>
|
|
|
+ <warning/>
|
|
|
+ </el-icon>
|
|
|
+ <span>仅支持上传Excel文件(.xlsx,.xls)</span>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-upload>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <span class="text-muted">下载模板:</span>
|
|
|
+ <el-button type="text" @click="downloadTemplate">站点模板</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <template #footer>
|
|
|
+ <el-button type="primary" @click="submitUpload">上传</el-button>
|
|
|
+ <el-button @click="importVisible = false">取消</el-button>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup name="XajStation">
|
|
|
+import {ref, reactive, nextTick, onBeforeUnmount} from 'vue'
|
|
|
+import {ElMessage, ElMessageBox} from 'element-plus'
|
|
|
+import * as echarts from 'echarts'
|
|
|
+import {
|
|
|
+ listStation,
|
|
|
+ getStation,
|
|
|
+ getSkDetail,
|
|
|
+ listQx,
|
|
|
+ listData,
|
|
|
+ addStation,
|
|
|
+ updateStation,
|
|
|
+ delStation,
|
|
|
+ exportStation,
|
|
|
+ importStation,
|
|
|
+ importStationTemplate
|
|
|
+} from '@/api/xajgl/station'
|
|
|
+
|
|
|
+const loading = ref(false)
|
|
|
+const tableData = ref([])
|
|
|
+const total = ref(0)
|
|
|
+const showSearch = ref(true)
|
|
|
+const formVisible = ref(false)
|
|
|
+const importVisible = ref(false)
|
|
|
+const isEdit = ref(false)
|
|
|
+const selectedRows = ref([])
|
|
|
+
|
|
|
+const queryParams = reactive({
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 15,
|
|
|
+ stcd: '',
|
|
|
+ stnm: '',
|
|
|
+ tp: '',
|
|
|
+ prov: ''
|
|
|
+})
|
|
|
+
|
|
|
+const form = reactive({
|
|
|
+ stcd: '',
|
|
|
+ stnm: '',
|
|
|
+ tp: '',
|
|
|
+ lgtd: null,
|
|
|
+ lttd: null,
|
|
|
+ prov: '',
|
|
|
+ city: '',
|
|
|
+ district: '',
|
|
|
+ rvnm: '',
|
|
|
+ addvcd: '',
|
|
|
+ bizCode: ''
|
|
|
+})
|
|
|
+
|
|
|
+const fileList = ref([])
|
|
|
+const uploadRef = ref(null)
|
|
|
+
|
|
|
+// 详情相关
|
|
|
+const detailVisible = ref(false)
|
|
|
+const detailData = ref({})
|
|
|
+const skLoading = ref(false)
|
|
|
+const skData = ref({})
|
|
|
+const detailDataLoading = ref(false)
|
|
|
+const detailDataList = ref([])
|
|
|
+const detailDateRange = ref([])
|
|
|
+const detailQuery = reactive({
|
|
|
+ stcd: '',
|
|
|
+ startTm: '',
|
|
|
+ endTm: ''
|
|
|
+})
|
|
|
+
|
|
|
+// 图表相关
|
|
|
+const qxChartRef = ref(null)
|
|
|
+const trendChartRef = ref(null)
|
|
|
+let qxChartInstance = null
|
|
|
+let trendChartInstance = null
|
|
|
+
|
|
|
+const getList = async () => {
|
|
|
+ loading.value = true
|
|
|
+ try {
|
|
|
+ const response = await listStation(queryParams)
|
|
|
+ tableData.value = response.rows
|
|
|
+ total.value = response.total
|
|
|
+ } catch (error) {
|
|
|
+ console.error(error)
|
|
|
+ } finally {
|
|
|
+ loading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const handleQuery = () => {
|
|
|
+ queryParams.pageNum = 1
|
|
|
+ getList()
|
|
|
+}
|
|
|
+
|
|
|
+const resetQuery = () => {
|
|
|
+ queryParams.stcd = ''
|
|
|
+ queryParams.stnm = ''
|
|
|
+ queryParams.tp = ''
|
|
|
+ queryParams.prov = ''
|
|
|
+ handleQuery()
|
|
|
+}
|
|
|
+
|
|
|
+const handleAdd = () => {
|
|
|
+ isEdit.value = false
|
|
|
+ Object.assign(form, {
|
|
|
+ stcd: '',
|
|
|
+ stnm: '',
|
|
|
+ tp: '',
|
|
|
+ lgtd: null,
|
|
|
+ lttd: null,
|
|
|
+ prov: '',
|
|
|
+ city: '',
|
|
|
+ district: '',
|
|
|
+ rvnm: '',
|
|
|
+ addvcd: '',
|
|
|
+ bizCode: ''
|
|
|
+ })
|
|
|
+ formVisible.value = true
|
|
|
+}
|
|
|
+
|
|
|
+const handleEdit = async (row) => {
|
|
|
+ isEdit.value = true
|
|
|
+ try {
|
|
|
+ const response = await getStation(row.stcd)
|
|
|
+ Object.assign(form, response.data)
|
|
|
+ formVisible.value = true
|
|
|
+ } catch (error) {
|
|
|
+ ElMessage.error('获取站点信息失败')
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const submitForm = async () => {
|
|
|
+ try {
|
|
|
+ if (isEdit.value) {
|
|
|
+ await updateStation(form)
|
|
|
+ ElMessage.success('修改成功')
|
|
|
+ } else {
|
|
|
+ await addStation(form)
|
|
|
+ ElMessage.success('新增成功')
|
|
|
+ }
|
|
|
+ formVisible.value = false
|
|
|
+ getList()
|
|
|
+ } catch (error) {
|
|
|
+ ElMessage.error('操作失败')
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const handleDelete = async (row) => {
|
|
|
+ try {
|
|
|
+ await ElMessageBox.confirm('确定删除该站点吗?', '提示', {type: 'warning'})
|
|
|
+ await delStation({stcd: row.stcd})
|
|
|
+ ElMessage.success('删除成功')
|
|
|
+ getList()
|
|
|
+ } catch (error) {
|
|
|
+ if (error !== 'cancel') {
|
|
|
+ ElMessage.error('删除失败')
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const handleBatchDelete = async () => {
|
|
|
+ try {
|
|
|
+ await ElMessageBox.confirm('确定删除选中的站点吗?', '提示', {type: 'warning'})
|
|
|
+ const data = selectedRows.value.map(item => ({stcd: item.stcd}))
|
|
|
+ await delStation(data)
|
|
|
+ ElMessage.success('批量删除成功')
|
|
|
+ getList()
|
|
|
+ } catch (error) {
|
|
|
+ if (error !== 'cancel') {
|
|
|
+ ElMessage.error('批量删除失败')
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const handleSelectionChange = (val) => {
|
|
|
+ selectedRows.value = val
|
|
|
+}
|
|
|
+
|
|
|
+const handleExport = async () => {
|
|
|
+ try {
|
|
|
+ const response = await exportStation(queryParams)
|
|
|
+ const blob = new Blob([response])
|
|
|
+ const url = window.URL.createObjectURL(blob)
|
|
|
+ const link = document.createElement('a')
|
|
|
+ link.href = url
|
|
|
+ link.download = '新安江站点数据.xlsx'
|
|
|
+ document.body.appendChild(link)
|
|
|
+ link.click()
|
|
|
+ document.body.removeChild(link)
|
|
|
+ window.URL.revokeObjectURL(url)
|
|
|
+ ElMessage.success('导出成功')
|
|
|
+ } catch (error) {
|
|
|
+ ElMessage.error('导出失败')
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const handleImport = () => {
|
|
|
+ fileList.value = []
|
|
|
+ importVisible.value = true
|
|
|
+}
|
|
|
+
|
|
|
+const submitUpload = () => {
|
|
|
+ uploadRef.value.submit()
|
|
|
+}
|
|
|
+
|
|
|
+const handleHttpRequest = (options) => {
|
|
|
+ const {file, onSuccess, onError} = options
|
|
|
+ importStation(file).then(response => {
|
|
|
+ onSuccess(response, file)
|
|
|
+ }).catch(error => {
|
|
|
+ onError(error)
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+const handleUploadSuccess = (response) => {
|
|
|
+ if (response.code === 200) {
|
|
|
+ ElMessage.success(response.msg)
|
|
|
+ importVisible.value = false
|
|
|
+ fileList.value = []
|
|
|
+ getList()
|
|
|
+ } else {
|
|
|
+ ElMessage.error(response.msg)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const handleUploadError = () => {
|
|
|
+ ElMessage.error('导入失败')
|
|
|
+}
|
|
|
+
|
|
|
+const downloadTemplate = async () => {
|
|
|
+ try {
|
|
|
+ const response = await importStationTemplate()
|
|
|
+ const blob = new Blob([response])
|
|
|
+ const url = window.URL.createObjectURL(blob)
|
|
|
+ const link = document.createElement('a')
|
|
|
+ link.href = url
|
|
|
+ link.download = '新安江站点导入模板.xlsx'
|
|
|
+ document.body.appendChild(link)
|
|
|
+ link.click()
|
|
|
+ document.body.removeChild(link)
|
|
|
+ window.URL.revokeObjectURL(url)
|
|
|
+ ElMessage.success('模板下载成功')
|
|
|
+ } catch (error) {
|
|
|
+ ElMessage.error('模板下载失败')
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// ============ 详情弹窗相关 ============
|
|
|
+const handleDetail = async (row) => {
|
|
|
+ detailData.value = row
|
|
|
+ detailQuery.stcd = row.stcd
|
|
|
+ // 默认查询当月数据
|
|
|
+ const now = new Date()
|
|
|
+ const firstDayOfMonth = new Date(now.getFullYear(), now.getMonth(), 1)
|
|
|
+ detailDateRange.value = [formatDateStr(firstDayOfMonth), formatDateStr(now)]
|
|
|
+ detailQuery.startTm = formatDateStr(firstDayOfMonth)
|
|
|
+ detailQuery.endTm = formatDateStr(now)
|
|
|
+ detailVisible.value = true
|
|
|
+
|
|
|
+ // 清空水库数据和监测数据
|
|
|
+ skData.value = {}
|
|
|
+ detailDataList.value = []
|
|
|
+
|
|
|
+ // 如果是水库,加载水库基础信息和库容曲线
|
|
|
+ if (row.tp === 'SK') {
|
|
|
+ await nextTick()
|
|
|
+ loadSkDetail(row.stcd)
|
|
|
+ loadQxList(row.stcd)
|
|
|
+ }
|
|
|
+ // 加载监测数据
|
|
|
+ loadDetailData()
|
|
|
+}
|
|
|
+
|
|
|
+const loadSkDetail = async (stcd) => {
|
|
|
+ skLoading.value = true
|
|
|
+ try {
|
|
|
+ const response = await getSkDetail(stcd)
|
|
|
+ skData.value = response.data || {}
|
|
|
+ } catch (error) {
|
|
|
+ console.error(error)
|
|
|
+ } finally {
|
|
|
+ skLoading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const loadQxList = async (stcd) => {
|
|
|
+ try {
|
|
|
+ const response = await listQx(stcd)
|
|
|
+ await nextTick()
|
|
|
+ initQxChart(response.data || [])
|
|
|
+ } catch (error) {
|
|
|
+ console.error(error)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const loadDetailData = async () => {
|
|
|
+ if (!detailQuery.stcd) return
|
|
|
+ detailDataLoading.value = true
|
|
|
+ try {
|
|
|
+ const response = await listData(detailQuery.stcd, detailQuery.startTm, detailQuery.endTm)
|
|
|
+ detailDataList.value = response.data || []
|
|
|
+ await nextTick()
|
|
|
+ initTrendChart(detailDataList.value)
|
|
|
+ } catch (error) {
|
|
|
+ console.error(error)
|
|
|
+ } finally {
|
|
|
+ detailDataLoading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const handleDetailDateChange = (val) => {
|
|
|
+ if (val && val.length === 2) {
|
|
|
+ detailQuery.startTm = val[0]
|
|
|
+ detailQuery.endTm = val[1]
|
|
|
+ } else {
|
|
|
+ detailQuery.startTm = ''
|
|
|
+ detailQuery.endTm = ''
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 库容曲线图表
|
|
|
+const initQxChart = (data) => {
|
|
|
+ if (!qxChartRef.value) return
|
|
|
+ if (!qxChartInstance) {
|
|
|
+ qxChartInstance = echarts.init(qxChartRef.value)
|
|
|
+ }
|
|
|
+ const rzList = data.map(item => item.rz)
|
|
|
+ const krList = data.map(item => item.kr)
|
|
|
+ qxChartInstance.setOption({
|
|
|
+ title: {text: '水位-库容曲线', left: 'center', textStyle: {fontSize: 14}},
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'axis',
|
|
|
+ formatter: (params) => {
|
|
|
+ const p = params[0]
|
|
|
+ return `水位: ${p.axisValue} m<br/>库容: ${p.data} 万m³`
|
|
|
+ }
|
|
|
+ },
|
|
|
+ grid: {left: '8%', right: '8%', bottom: '12%', top: '18%'},
|
|
|
+ xAxis: {
|
|
|
+ type: 'category',
|
|
|
+ name: '水位(m)',
|
|
|
+ data: rzList,
|
|
|
+ axisLine: {lineStyle: {color: '#646464'}},
|
|
|
+ axisLabel: {color: '#646464', fontSize: 12}
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ type: 'value',
|
|
|
+ name: '库容(万m³)',
|
|
|
+ axisLine: {lineStyle: {color: '#646464'}},
|
|
|
+ axisLabel: {color: '#646464', fontSize: 12},
|
|
|
+ splitLine: {lineStyle: {color: '#e1e5e6'}}
|
|
|
+ },
|
|
|
+ series: [{
|
|
|
+ name: '库容',
|
|
|
+ type: 'line',
|
|
|
+ data: krList,
|
|
|
+ smooth: true,
|
|
|
+ itemStyle: {color: '#1c97e7'},
|
|
|
+ lineStyle: {color: '#1c97e7', width: 2},
|
|
|
+ areaStyle: {
|
|
|
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
+ {offset: 0, color: 'rgba(28, 151, 231, 0.3)'},
|
|
|
+ {offset: 1, color: 'rgba(28, 151, 231, 0.05)'}
|
|
|
+ ])
|
|
|
+ }
|
|
|
+ }]
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 监测数据趋势图
|
|
|
+const initTrendChart = (data) => {
|
|
|
+ if (!trendChartRef.value) return
|
|
|
+ if (!trendChartInstance) {
|
|
|
+ trendChartInstance = echarts.init(trendChartRef.value)
|
|
|
+ }
|
|
|
+ const tmList = data.map(item => item.tm ? formatDate(item.tm) : '')
|
|
|
+ const zList = data.map(item => item.z)
|
|
|
+ const qList = data.map(item => item.q)
|
|
|
+ trendChartInstance.setOption({
|
|
|
+ title: {text: '水位/流量趋势', left: 'center', textStyle: {fontSize: 14}},
|
|
|
+ tooltip: {trigger: 'axis'},
|
|
|
+ legend: {
|
|
|
+ data: ['水位', '流量'],
|
|
|
+ top: 30,
|
|
|
+ textStyle: {color: '#1f1f1f', fontSize: 13}
|
|
|
+ },
|
|
|
+ grid: {left: '8%', right: '8%', bottom: '15%', top: '22%'},
|
|
|
+ xAxis: {
|
|
|
+ type: 'category',
|
|
|
+ data: tmList,
|
|
|
+ axisLine: {lineStyle: {color: '#646464'}},
|
|
|
+ axisLabel: {color: '#646464', fontSize: 12, rotate: 30}
|
|
|
+ },
|
|
|
+ yAxis: [
|
|
|
+ {
|
|
|
+ type: 'value',
|
|
|
+ name: '水位(m)',
|
|
|
+ position: 'left',
|
|
|
+ axisLine: {lineStyle: {color: '#646464'}},
|
|
|
+ axisLabel: {color: '#646464', fontSize: 12},
|
|
|
+ splitLine: {lineStyle: {color: '#e1e5e6'}}
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: 'value',
|
|
|
+ name: '流量(m³/s)',
|
|
|
+ position: 'right',
|
|
|
+ axisLine: {lineStyle: {color: '#646464'}},
|
|
|
+ axisLabel: {color: '#646464', fontSize: 12},
|
|
|
+ splitLine: {show: false}
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '水位',
|
|
|
+ type: 'line',
|
|
|
+ data: zList,
|
|
|
+ smooth: true,
|
|
|
+ itemStyle: {color: '#1c97e7'},
|
|
|
+ lineStyle: {color: '#1c97e7', width: 2}
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '流量',
|
|
|
+ type: 'line',
|
|
|
+ yAxisIndex: 1,
|
|
|
+ data: qList,
|
|
|
+ smooth: true,
|
|
|
+ itemStyle: {color: '#26b8f3'},
|
|
|
+ lineStyle: {color: '#26b8f3', width: 2}
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+const handleDetailClose = () => {
|
|
|
+ if (qxChartInstance) {
|
|
|
+ qxChartInstance.dispose()
|
|
|
+ qxChartInstance = null
|
|
|
+ }
|
|
|
+ if (trendChartInstance) {
|
|
|
+ trendChartInstance.dispose()
|
|
|
+ trendChartInstance = null
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 工具函数
|
|
|
+const getTpLabel = (value) => {
|
|
|
+ const labels = {
|
|
|
+ 'ZZ': '水文站',
|
|
|
+ 'DRP': '雨量站',
|
|
|
+ 'WIU': '取水户',
|
|
|
+ 'SK': '水库'
|
|
|
+ }
|
|
|
+ return labels[value] || value
|
|
|
+}
|
|
|
+
|
|
|
+const formatDateStr = (date) => {
|
|
|
+ const year = date.getFullYear()
|
|
|
+ const month = String(date.getMonth() + 1).padStart(2, '0')
|
|
|
+ const day = String(date.getDate()).padStart(2, '0')
|
|
|
+ return `${year}-${month}-${day}`
|
|
|
+}
|
|
|
+
|
|
|
+const formatDate = (date) => {
|
|
|
+ if (!date) return ''
|
|
|
+ return new Date(date).toLocaleString('zh-CN', {
|
|
|
+ year: 'numeric',
|
|
|
+ month: '2-digit',
|
|
|
+ day: '2-digit',
|
|
|
+ hour: '2-digit',
|
|
|
+ minute: '2-digit',
|
|
|
+ second: '2-digit'
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+const formatDecimal = (value) => {
|
|
|
+ if (value === null || value === undefined) return '-'
|
|
|
+ return parseFloat(value).toFixed(2)
|
|
|
+}
|
|
|
+
|
|
|
+// 窗口resize时重绘图表
|
|
|
+const handleResize = () => {
|
|
|
+ if (qxChartInstance) qxChartInstance.resize()
|
|
|
+ if (trendChartInstance) trendChartInstance.resize()
|
|
|
+}
|
|
|
+
|
|
|
+window.addEventListener('resize', handleResize)
|
|
|
+
|
|
|
+onBeforeUnmount(() => {
|
|
|
+ window.removeEventListener('resize', handleResize)
|
|
|
+ if (qxChartInstance) {
|
|
|
+ qxChartInstance.dispose()
|
|
|
+ qxChartInstance = null
|
|
|
+ }
|
|
|
+ if (trendChartInstance) {
|
|
|
+ trendChartInstance.dispose()
|
|
|
+ trendChartInstance = null
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+getList()
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.app-container {
|
|
|
+ padding: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.mb8 {
|
|
|
+ margin-bottom: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.detail-section {
|
|
|
+ margin-bottom: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.detail-section-title {
|
|
|
+ font-size: 16px;
|
|
|
+ color: #1c97e7;
|
|
|
+ margin-bottom: 12px;
|
|
|
+ padding-left: 10px;
|
|
|
+ border-left: 3px solid #1c97e7;
|
|
|
+ line-height: 24px;
|
|
|
+}
|
|
|
+
|
|
|
+.chart-container {
|
|
|
+ width: 100%;
|
|
|
+ height: 320px;
|
|
|
+}
|
|
|
+
|
|
|
+.detail-query-form {
|
|
|
+ margin-bottom: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.el-table) {
|
|
|
+ font-size: 14px;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.el-table th) {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: bold;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.el-table td) {
|
|
|
+ font-size: 14px;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.el-form-item__label) {
|
|
|
+ font-size: 14px;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.el-input__inner) {
|
|
|
+ font-size: 14px;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.el-button) {
|
|
|
+ font-size: 14px;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.el-pagination) {
|
|
|
+ font-size: 14px;
|
|
|
+ margin-top: 15px;
|
|
|
+}
|
|
|
+</style>
|