|
|
@@ -22,18 +22,19 @@
|
|
|
<el-form :inline="true" :model="query">
|
|
|
<el-form-item v-if="view !== 'gis'" label="设备名称"><el-input v-model="query.equipmentName" clearable /></el-form-item>
|
|
|
<el-form-item v-if="view !== 'gis'" label="设备编码"><el-input v-model="query.equipmentCode" clearable /></el-form-item>
|
|
|
- <el-form-item v-if="view === 'ledger'" label="报警状态"><el-select v-model="query.alarmStatus" clearable><el-option label="正常" :value="0" /><el-option label="报警" :value="1" /></el-select></el-form-item>
|
|
|
- <el-form-item v-if="view === 'ledger' || view === 'dispatch' || view === 'abnormal'" label="报警等级"><el-select v-model="query.alertLevel" clearable><el-option v-for="item in levels" :key="item.value" v-bind="item" /></el-select></el-form-item>
|
|
|
+ <el-form-item v-if="view === 'ledger'" label="报警状态"><el-select v-model="query.alarmStatus" clearable class="filter-select" style="width:180px"><el-option label="正常" :value="0" /><el-option label="报警" :value="1" /></el-select></el-form-item>
|
|
|
+ <el-form-item v-if="view === 'ledger' || view === 'dispatch' || view === 'abnormal'" label="报警等级"><el-select v-model="query.alertLevel" clearable class="filter-select" style="width:180px"><el-option v-for="item in levels" :key="item.value" v-bind="item" /></el-select></el-form-item>
|
|
|
<el-form-item><el-button type="primary" @click="loadData">查询</el-button><el-button @click="reset">重置</el-button></el-form-item>
|
|
|
</el-form>
|
|
|
</el-card>
|
|
|
|
|
|
<el-card shadow="never" class="water-panel">
|
|
|
- <div class="card-title"><span><el-icon><Monitor /></el-icon> {{ title }}清单</span><el-button v-if="view !== 'gis'" link type="primary" @click="exportData">导出</el-button></div>
|
|
|
+ <div class="card-title"><span><el-icon><Monitor /></el-icon> {{ title }}清单</span><div class="panel-actions"><el-button v-if="view === 'ledger'" link type="primary" :icon="Upload" @click="openImport">导入</el-button><el-button v-if="view !== 'gis'" link type="primary" :icon="Download" @click="exportData">导出</el-button></div></div>
|
|
|
<el-alert v-if="loadError" :title="loadError" type="error" show-icon :closable="false" class="load-error"><el-button size="small" type="danger" plain @click="loadData">重试</el-button></el-alert>
|
|
|
<el-empty v-if="view === 'gis' && !loading && !loadError && !page.records.length" description="暂无设备 GIS 点位数据" />
|
|
|
<el-table :data="page.records" border stripe v-loading="loading" row-key="equipmentId">
|
|
|
<el-table-column type="index" label="#" width="55" /><el-table-column prop="equipmentCode" label="设备编码" min-width="130" /><el-table-column prop="equipmentName" label="设备名称" min-width="150" /><el-table-column prop="equipmentTypeName" label="设备类型" min-width="120" /><el-table-column prop="equipmentLocation" label="安装位置" min-width="180" show-overflow-tooltip />
|
|
|
+ <el-table-column v-if="view === 'ledger'" label="设备图片" width="110" align="center"><template #default="{ row }"><el-image v-if="row.equipmentImage" :src="row.equipmentImage" :preview-src-list="[row.equipmentImage]" preview-teleported hide-on-click-modal fit="cover" class="device-image" /><span v-else class="image-placeholder">暂无图片</span></template></el-table-column>
|
|
|
<el-table-column v-if="view !== 'maintain'" label="设备状态" width="100"><template #default="{ row }"><el-tag :type="Number(row.currentStatus) === 3 ? 'danger' : 'success'">{{ row.currentStatusText }}</el-tag></template></el-table-column>
|
|
|
<el-table-column v-if="view !== 'maintain'" label="在线状态" width="100"><template #default="{ row }"><el-tag :type="Number(row.onlineStatus) === 1 ? 'success' : 'info'">{{ row.onlineStatusText }}</el-tag></template></el-table-column>
|
|
|
<el-table-column v-if="view !== 'maintain'" label="报警状态" width="100"><template #default="{ row }"><el-tag :type="Number(row.alarmStatus) === 1 ? 'danger' : 'success'">{{ row.alarmStatusText }}</el-tag></template></el-table-column>
|
|
|
@@ -49,6 +50,11 @@
|
|
|
|
|
|
<el-drawer v-model="detailVisible" class="detail-drawer device-detail-drawer" title="设备详情" direction="rtl" size="680px" append-to-body>
|
|
|
<div class="detail-summary"><span class="detail-name">{{ detail.equipmentName || '-' }}</span><el-tag :type="Number(detail.currentStatus) === 3 ? 'danger' : 'success'">{{ detail.currentStatusText || '-' }}</el-tag><el-tag :type="Number(detail.alarmStatus) === 1 ? 'danger' : 'success'">{{ detail.alarmStatusText || '-' }}</el-tag><el-tag v-if="detail.workOrderStatusText && detail.workOrderStatusText !== '暂无工单'" :type="workOrderStatusType(detail.workOrderStatusCode)">{{ detail.workOrderStatusText }}</el-tag></div>
|
|
|
+ <div v-if="view === 'ledger'" class="detail-media">
|
|
|
+ <div class="detail-media-label">设备图片</div>
|
|
|
+ <el-image v-if="detail.equipmentImage" :src="detail.equipmentImage" :preview-src-list="[detail.equipmentImage]" preview-teleported hide-on-click-modal fit="cover" class="detail-image" />
|
|
|
+ <div v-else class="detail-image-placeholder">暂无图片</div>
|
|
|
+ </div>
|
|
|
<el-descriptions :column="2" border>
|
|
|
<el-descriptions-item v-for="item in detailFields" :key="item.label" :label="item.label">{{ item.value }}</el-descriptions-item>
|
|
|
</el-descriptions>
|
|
|
@@ -90,17 +96,30 @@
|
|
|
</div>
|
|
|
<template #footer><el-button @click="dispatchVisible = false">取消</el-button><el-button type="primary" :loading="dispatching" @click="submitDispatch">确认派单</el-button></template>
|
|
|
</el-dialog>
|
|
|
+
|
|
|
+ <el-dialog v-model="importVisible" title="供水设备批量导入" width="520px" append-to-body @closed="resetImport">
|
|
|
+ <el-upload ref="importUploadRef" drag :limit="1" accept=".xlsx,.xls" :auto-upload="false" :file-list="importFileList" :on-change="handleImportChange" :on-remove="handleImportRemove" :disabled="importUploading">
|
|
|
+ <el-icon class="el-icon--upload"><Upload /></el-icon>
|
|
|
+ <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
|
|
+ <template #tip><div class="el-upload__tip import-tip">仅支持 xls、xlsx;设备类型必须属于供水类型树。</div></template>
|
|
|
+ </el-upload>
|
|
|
+ <el-checkbox v-model="importUpdateSupport" class="import-update">更新已存在的设备编码</el-checkbox>
|
|
|
+ <template #footer>
|
|
|
+ <el-button type="primary" :loading="importUploading" @click="submitImport">开始导入</el-button>
|
|
|
+ <el-button @click="importVisible = false">取消</el-button>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
import { computed, onMounted, ref, watch } from 'vue'
|
|
|
import { ElMessage } from 'element-plus'
|
|
|
-import { Monitor, Refresh, Warning, Bell, Tools, DataLine } from '@element-plus/icons-vue'
|
|
|
+import { Monitor, Refresh, Warning, Bell, Tools, DataLine, Upload, Download } from '@element-plus/icons-vue'
|
|
|
import { listUser } from '@/api/system/user'
|
|
|
import { listDept } from '@/api/system/dept'
|
|
|
import { handleTree } from '@/utils/ruoyi'
|
|
|
-import { createAbnormalOrder, exportAbnormalDevices, exportDeviceLedger, listAbnormalDevices, listDeviceLedger, listDeviceGisPoints, listMaintainRecords, deleteMaintainRecord } from '@/api/pipeNetwork/waterSupply'
|
|
|
+import { createAbnormalOrder, exportAbnormalDevices, exportDeviceLedger, importDeviceLedger, listAbnormalDevices, listDeviceLedger, listDeviceGisPoints, listMaintainRecords, deleteMaintainRecord } from '@/api/pipeNetwork/waterSupply'
|
|
|
import { normalizePage, readMonitoringExportError } from '@/utils/waterSupplyModel'
|
|
|
import { buildMaintainOrderPayload, normalizeDeviceRow, canDispatchDevice, normalizeDeviceGisPoints, preserveDevicePageOnError, normalizeAssigneeUser, buildAssigneeQuery, selectSingleAssignee, normalizeDispatchOrderResponse } from '@/utils/waterSupplyUiModel'
|
|
|
import { saveAs } from 'file-saver'
|
|
|
@@ -108,8 +127,9 @@ import { saveAs } from 'file-saver'
|
|
|
const props = defineProps({ title: { type: String, required: true }, subtitle: { type: String, default: '设备信息、运行状态与维护过程管理' }, view: { type: String, default: 'ledger' } })
|
|
|
const { title, subtitle, view } = props
|
|
|
const query = ref({ pageNum: 1, pageSize: 10, equipmentName: '', equipmentCode: '', alarmStatus: undefined, alertLevel: undefined })
|
|
|
+const importVisible = ref(false); const importUploading = ref(false); const importUpdateSupport = ref(false); const importFileList = ref([]); const importUploadRef = ref(null)
|
|
|
const page = ref({ records: [], total: 0 }); const loading = ref(false); const loadError = ref(''); const detailVisible = ref(false); const detail = ref({}); const dispatchVisible = ref(false); const dispatching = ref(false); const dispatchDevice = ref({}); const dispatchForm = ref({ description: '', receiveUserId: undefined }); const users = ref([]); const userLoading = ref(false); const userLoadError = ref(''); const deptLoadError = ref(''); const selectedAssignee = ref(null); const assigneeDraft = ref(null); const deptTreeRef = ref(null); const deptTreeData = ref([]); const deptFilterText = ref(''); const assigneeKeyword = ref(''); const assigneeDeptId = ref(undefined); const assigneePageNum = ref(1); const assigneePageSize = ref(10); const assigneeTotal = ref(0)
|
|
|
-const levels = [{ label: '一级', value: 1 }, { label: '二级', value: 2 }, { label: '三级', value: 3 }, { label: '四级', value: 4 }]; const pageIcon = Monitor; const alarmLevelType = level => ({ 1: 'danger', 2: 'warning', 3: 'primary', 4: 'success' }[Number(level)] || 'info')
|
|
|
+const levels = [{ label: '一级报警', value: 1 }, { label: '二级报警', value: 2 }, { label: '三级报警', value: 3 }, { label: '四级报警', value: 4 }]; const pageIcon = Monitor; const alarmLevelType = level => ({ 1: 'danger', 2: 'warning', 3: 'primary', 4: 'success' }[Number(level)] || 'info')
|
|
|
function canDispatch(row) {
|
|
|
if (view !== 'dispatch' && view !== 'abnormal') return false
|
|
|
return canDispatchDevice(row)
|
|
|
@@ -179,6 +199,37 @@ async function submitDispatch() {
|
|
|
}
|
|
|
}
|
|
|
async function removeRecord(row) { try { await deleteMaintainRecord(row.maintainId || row.id); ElMessage.success('记录已删除'); await loadData() } catch (error) { ElMessage.error('删除失败') } }
|
|
|
+function openImport() { importVisible.value = true }
|
|
|
+function handleImportChange(file, fileList) {
|
|
|
+ if (!/\.(xlsx|xls)$/iu.test(file?.name || '')) {
|
|
|
+ ElMessage.error('仅支持 xls、xlsx 文件')
|
|
|
+ importFileList.value = []
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ importFileList.value = fileList.slice(-1)
|
|
|
+}
|
|
|
+function handleImportRemove(file, fileList) { importFileList.value = fileList }
|
|
|
+function resetImport() {
|
|
|
+ importUploading.value = false
|
|
|
+ importUpdateSupport.value = false
|
|
|
+ importFileList.value = []
|
|
|
+ importUploadRef.value?.clearFiles()
|
|
|
+}
|
|
|
+async function submitImport() {
|
|
|
+ if (!importFileList.value.length) return ElMessage.warning('请选择 xls 或 xlsx 文件')
|
|
|
+ importUploading.value = true
|
|
|
+ try {
|
|
|
+ const response = await importDeviceLedger(importFileList.value[0].raw, importUpdateSupport.value)
|
|
|
+ if (response?.code && response.code !== 200) throw new Error(response.msg || response.message || '导入失败,请检查文件内容')
|
|
|
+ ElMessage.success(response?.msg || response?.data || '导入成功')
|
|
|
+ importVisible.value = false
|
|
|
+ await loadData()
|
|
|
+ } catch (error) {
|
|
|
+ ElMessage.error(error?.response?.data?.msg || error?.response?.data?.message || error?.message || '导入失败,请稍后重试')
|
|
|
+ } finally {
|
|
|
+ importUploading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
async function exportData() {
|
|
|
try {
|
|
|
const response = await (view === 'dispatch' || view === 'abnormal' ? exportAbnormalDevices : exportDeviceLedger)({ ...query.value })
|