From 65d40ca12a61b6b4ec69998612ae95d6cc232219 Mon Sep 17 00:00:00 2001 From: zch Date: Thu, 29 May 2025 13:22:36 +0800 Subject: [PATCH] =?UTF-8?q?feat(ems):=20=E6=B7=BB=E5=8A=A0=E6=8A=A5?= =?UTF-8?q?=E8=AD=A6=E8=A7=84=E5=88=99=E6=8E=AA=E6=96=BD=E6=AD=A5=E9=AA=A4?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增报警规则措施步骤管理页面 - 在记录报警规则页面添加措施步骤弹窗 - 实现报警规则措施步骤的查询、添加、修改、删除等功能 - 优化报警数据页面,增加措施步骤查看功能 --- src/api/ems/base/emsAlarmActionStep.js | 69 +++ src/api/ems/base/emsAlarmActionStepImage.js | 44 ++ src/api/ems/record/recordAlarmRule.js | 8 + src/layout/components/Navbar.vue | 291 +++++++++++-- .../ems/base/emsAlarmActionStep/index.vue | 296 +++++++++++++ .../base/emsAlarmActionStepImage/index.vue | 302 +++++++++++++ .../ems/record/recordAlarmRule/index.vue | 404 +++++++++++++++++- src/views/index.vue | 28 +- 8 files changed, 1379 insertions(+), 63 deletions(-) create mode 100644 src/api/ems/base/emsAlarmActionStep.js create mode 100644 src/api/ems/base/emsAlarmActionStepImage.js create mode 100644 src/views/ems/base/emsAlarmActionStep/index.vue create mode 100644 src/views/ems/base/emsAlarmActionStepImage/index.vue diff --git a/src/api/ems/base/emsAlarmActionStep.js b/src/api/ems/base/emsAlarmActionStep.js new file mode 100644 index 0000000..e63b03e --- /dev/null +++ b/src/api/ems/base/emsAlarmActionStep.js @@ -0,0 +1,69 @@ +import request from '@/utils/request' + +// 查询报警规则具体措施步骤列表 +export function listEmsAlarmActionStep(query) { + return request({ + url: '/ems/base/emsAlarmActionStep/list', + method: 'get', + params: query + }) +} + +// 查询报警规则具体措施步骤详细 +export function getEmsAlarmActionStep(objId) { + return request({ + url: '/ems/base/emsAlarmActionStep/' + objId, + method: 'get' + }) +} + +// 根据报警规则ID查询措施步骤列表(包含图片) +export function getEmsAlarmActionStepsByRuleId(ruleObjId) { + return request({ + url: '/ems/base/emsAlarmActionStep/rule/' + ruleObjId, + method: 'get' + }) +} + +// 根据报警数据信息查询措施步骤列表(包含图片) +export function getEmsAlarmActionStepsByAlarmInfo(monitorId, cause) { + return request({ + url: '/ems/base/emsAlarmActionStep/alarm/' + encodeURIComponent(monitorId) + '/' + encodeURIComponent(cause), + method: 'get' + }) +} + +// 新增报警规则具体措施步骤 +export function addEmsAlarmActionStep(data) { + return request({ + url: '/ems/base/emsAlarmActionStep', + method: 'post', + data: data + }) +} + +// 修改报警规则具体措施步骤 +export function updateEmsAlarmActionStep(data) { + return request({ + url: '/ems/base/emsAlarmActionStep', + method: 'put', + data: data + }) +} + +// 删除报警规则具体措施步骤 +export function delEmsAlarmActionStep(objId) { + return request({ + url: '/ems/base/emsAlarmActionStep/' + objId, + method: 'delete' + }) +} + +// 批量保存措施步骤(包含图片信息) +export function batchSaveActionSteps(ruleObjId, stepList) { + return request({ + url: '/ems/base/emsAlarmActionStep/batchSave/' + ruleObjId, + method: 'post', + data: stepList + }) +} diff --git a/src/api/ems/base/emsAlarmActionStepImage.js b/src/api/ems/base/emsAlarmActionStepImage.js new file mode 100644 index 0000000..762721f --- /dev/null +++ b/src/api/ems/base/emsAlarmActionStepImage.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询报警措施步骤图片列表 +export function listEmsAlarmActionStepImage(query) { + return request({ + url: '/ems/base/emsAlarmActionStepImage/list', + method: 'get', + params: query + }) +} + +// 查询报警措施步骤图片详细 +export function getEmsAlarmActionStepImage(objId) { + return request({ + url: '/ems/base/emsAlarmActionStepImage/' + objId, + method: 'get' + }) +} + +// 新增报警措施步骤图片 +export function addEmsAlarmActionStepImage(data) { + return request({ + url: '/ems/base/emsAlarmActionStepImage', + method: 'post', + data: data + }) +} + +// 修改报警措施步骤图片 +export function updateEmsAlarmActionStepImage(data) { + return request({ + url: '/ems/base/emsAlarmActionStepImage', + method: 'put', + data: data + }) +} + +// 删除报警措施步骤图片 +export function delEmsAlarmActionStepImage(objId) { + return request({ + url: '/ems/base/emsAlarmActionStepImage/' + objId, + method: 'delete' + }) +} diff --git a/src/api/ems/record/recordAlarmRule.js b/src/api/ems/record/recordAlarmRule.js index 6d0eb5e..e46ba79 100644 --- a/src/api/ems/record/recordAlarmRule.js +++ b/src/api/ems/record/recordAlarmRule.js @@ -50,3 +50,11 @@ export function getEmsRecordAlarmRuleTotalCount() { method: 'get' }) } + +// 查询异常告警规则 +export function getEmsRecordAlarmRuleList(query) { + return request({ + url: '/ems/record/recordAlarmRule/getEmsRecordAlarmRuleList', + method: 'get' + }) +} diff --git a/src/layout/components/Navbar.vue b/src/layout/components/Navbar.vue index 15176bd..4d0298b 100644 --- a/src/layout/components/Navbar.vue +++ b/src/layout/components/Navbar.vue @@ -60,55 +60,135 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ +
+ +
+ + + +
+ 第{{ step.stepSequence }}步 +
+ + +
+

{{ step.description }}

+
+ + +
+
参考图片:
+ +
+ + +
+ 备注:{{ step.remark }} +
+
+
+
+
+
+
+
+
+ +
+ + + +
+
@@ -127,6 +207,7 @@ import RuoYiGit from '@/components/RuoYi/Git' import RuoYiDoc from '@/components/RuoYi/Doc' import settings from '@/settings' import { handleExceptions, listRecordAlarmData } from '@/api/ems/record/recordAlarmData' +import { getEmsAlarmActionStepsByRuleId, getEmsAlarmActionStepsByAlarmInfo } from '@/api/ems/base/emsAlarmActionStep' export default { dicts: ['alarm_type', 'alarm_status'], @@ -150,7 +231,15 @@ export default { pageNum: 1, pageSize: 10, alarmStatus: '1' - } + }, + // 措施步骤相关 + currentAlarmData: null, + showActionSteps: false, + actionSteps: [], + actionStepsLoading: false, + activeTab: 'alarmList', + imagePreviewVisible: false, + previewImageUrl: '' } }, created() { @@ -263,6 +352,34 @@ export default { }) }).catch(() => { }) + }, + handleRowClick(row) { + this.currentAlarmData = row + this.activeTab = 'actionSteps' + this.getActionSteps(row) + }, + getActionSteps(alarmData) { + this.actionStepsLoading = true + getEmsAlarmActionStepsByAlarmInfo(alarmData.monitorId, alarmData.cause).then((response) => { + this.actionSteps = response.data || [] + this.actionStepsLoading = false + }).catch(() => { + this.actionSteps = [] + this.actionStepsLoading = false + }) + }, + viewActionSteps(row) { + this.currentAlarmData = row + this.activeTab = 'actionSteps' + this.getActionSteps(row) + }, + previewImage(url) { + this.previewImageUrl = url + this.imagePreviewVisible = true + }, + closeDialog() { + this.alarmOpen = false + this.activeTab = 'alarmList' } } } @@ -372,4 +489,84 @@ export default { } } } + +// 措施步骤相关样式 +.step-card { + margin-bottom: 20px; + + .step-title { + font-weight: bold; + font-size: 16px; + color: #409EFF; + } + + .step-description { + margin-bottom: 15px; + + p { + line-height: 1.6; + margin: 0; + color: #606266; + } + } + + .step-images { + margin-bottom: 15px; + + .images-title { + font-size: 14px; + color: #909399; + margin-bottom: 10px; + } + + .image-gallery { + display: flex; + flex-wrap: wrap; + gap: 10px; + + .image-item { + cursor: pointer; + border: 2px solid #f0f0f0; + border-radius: 4px; + overflow: hidden; + transition: all 0.3s; + max-width: 200px; + + &:hover { + border-color: #409EFF; + transform: scale(1.02); + } + + img { + width: 100%; + height: 150px; + object-fit: cover; + display: block; + } + + .image-desc { + padding: 8px; + font-size: 12px; + color: #666; + background: #f9f9f9; + text-align: center; + } + } + } + } + + .step-remark { + margin-top: 10px; + } +} + +.no-steps { + text-align: center; + padding: 40px; +} + +.image-preview-container { + text-align: center; +} + diff --git a/src/views/ems/base/emsAlarmActionStep/index.vue b/src/views/ems/base/emsAlarmActionStep/index.vue new file mode 100644 index 0000000..84b6239 --- /dev/null +++ b/src/views/ems/base/emsAlarmActionStep/index.vue @@ -0,0 +1,296 @@ + + + diff --git a/src/views/ems/base/emsAlarmActionStepImage/index.vue b/src/views/ems/base/emsAlarmActionStepImage/index.vue new file mode 100644 index 0000000..b9df025 --- /dev/null +++ b/src/views/ems/base/emsAlarmActionStepImage/index.vue @@ -0,0 +1,302 @@ + + + diff --git a/src/views/ems/record/recordAlarmRule/index.vue b/src/views/ems/record/recordAlarmRule/index.vue index f1c1f49..bc0c809 100644 --- a/src/views/ems/record/recordAlarmRule/index.vue +++ b/src/views/ems/record/recordAlarmRule/index.vue @@ -105,8 +105,16 @@ - + @@ -208,6 +335,8 @@ import { updateRecordAlarmRule } from '@/api/ems/record/recordAlarmRule' import {listBaseMonitorInfo} from "@/api/ems/base/baseMonitorInfo"; +import {getEmsAlarmActionStepsByRuleId, batchSaveActionSteps} from '@/api/ems/base/emsAlarmActionStep' +import {getToken} from '@/utils/auth' export default { name: 'RecordAlarmRule', @@ -276,7 +405,18 @@ export default { { key: 13, label: `创建时间`, visible: true }, { key: 14, label: `更新人`, visible: false }, { key: 15, label: `更新时间`, visible: false } - ] + ], + actionStepsTitle: '', + actionStepsOpen: false, + actionStepsList: [], + actionStepsSaving: false, + currentRuleObjId: '', + uploadAction: process.env.VUE_APP_BASE_API + '/common/upload', + uploadHeaders: { + Authorization: 'Bearer ' + getToken() + }, + previewImageUrl: '', + imagePreviewVisible: false } }, created() { @@ -416,6 +556,266 @@ export default { this.form.monitorField = null; // 如果找不到设备,也清空 } }, + handleActionSteps(row) { + this.actionStepsTitle = '措施管理 - ' + row.ruleName; + this.currentRuleObjId = row.objId; + this.actionStepsOpen = true; + this.getActionSteps(row.objId); + }, + getActionSteps(ruleObjId) { + getEmsAlarmActionStepsByRuleId(ruleObjId).then(response => { + this.actionStepsList = response.data || []; + // 确保每个步骤都有stepImages数组 + this.actionStepsList.forEach(step => { + if (!step.stepImages) { + step.stepImages = []; + } + }); + }).catch(() => { + this.actionStepsList = []; + }); + }, + addActionStep() { + const newSequence = this.actionStepsList.length > 0 + ? Math.max(...this.actionStepsList.map(s => s.stepSequence || 0)) + 1 + : 1; + + const newStep = { + tempId: Date.now() + Math.random(), // 临时ID + ruleObjId: this.currentRuleObjId, + stepSequence: newSequence, + description: '', + remark: '', + stepImages: [] + }; + + this.actionStepsList.push(newStep); + }, + removeActionStep(index) { + this.$confirm('确定要删除这个步骤吗?', '提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning' + }).then(() => { + this.actionStepsList.splice(index, 1); + // 重新排序步骤序号 + this.actionStepsList.forEach((step, idx) => { + step.stepSequence = idx + 1; + }); + }); + }, + handleImageUploadSuccess(res, file, fileList, stepIndex) { + if (res.code === 200) { + const step = this.actionStepsList[stepIndex]; + if (!step.stepImages) { + step.stepImages = []; + } + + const newImage = { + tempId: Date.now() + Math.random(), + imageUrl: res.url, + imageSequence: step.stepImages.length + 1, + description: '' + }; + + step.stepImages.push(newImage); + this.$message.success('图片上传成功'); + } else { + this.$message.error('图片上传失败:' + res.msg); + } + }, + beforeImageUpload(file) { + const isImage = file.type.indexOf('image') !== -1; + const isLt2M = file.size / 1024 / 1024 < 2; + + if (!isImage) { + this.$message.error('只能上传图片文件!'); + return false; + } + if (!isLt2M) { + this.$message.error('上传文件大小不能超过 2MB!'); + return false; + } + return true; + }, + removeStepImage(stepIndex, imgIndex) { + this.$confirm('确定要删除这张图片吗?', '提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning' + }).then(() => { + this.actionStepsList[stepIndex].stepImages.splice(imgIndex, 1); + // 重新排序图片序号 + this.actionStepsList[stepIndex].stepImages.forEach((img, idx) => { + img.imageSequence = idx + 1; + }); + }); + }, + saveActionSteps() { + // 验证数据 + for (let i = 0; i < this.actionStepsList.length; i++) { + const step = this.actionStepsList[i]; + if (!step.description || step.description.trim() === '') { + this.$message.warning(`步骤 ${step.stepSequence} 的描述不能为空`); + return; + } + } + + this.actionStepsSaving = true; + + // 处理数据,移除临时ID + const stepsToSave = this.actionStepsList.map(step => { + const stepData = { ...step }; + delete stepData.tempId; + + if (stepData.stepImages && stepData.stepImages.length > 0) { + stepData.stepImages = stepData.stepImages.map(img => { + const imgData = { ...img }; + delete imgData.tempId; + return imgData; + }); + } + + return stepData; + }); + + batchSaveActionSteps(this.currentRuleObjId, stepsToSave).then(response => { + this.$message.success('措施步骤保存成功'); + this.actionStepsOpen = false; + this.actionStepsSaving = false; + }).catch(() => { + this.actionStepsSaving = false; + }); + }, + cancelActionSteps() { + this.actionStepsOpen = false; + this.actionStepsList = []; + this.currentRuleObjId = ''; + }, + previewImage(url) { + this.previewImageUrl = url; + this.imagePreviewVisible = true; + } } } + + diff --git a/src/views/index.vue b/src/views/index.vue index d799ced..e399433 100644 --- a/src/views/index.vue +++ b/src/views/index.vue @@ -345,18 +345,18 @@ export default { } // 根据数据值判断设备状态 - if (device.temperature && (device.temperature > 40 || device.temperature < -10)) { - return 'error' - } - if (device.humidity && (device.humidity > 90 || device.humidity < 10)) { - return 'warning' - } - if (device.noise && device.noise > 80) { - return 'warning' - } - if (device.concentration && device.concentration > 10) { - return 'error' - } + // if (device.temperature && (device.temperature > 40 || device.temperature < -10)) { + // return 'error' + // } + // if (device.humidity && (device.humidity > 90 || device.humidity < 10)) { + // return 'warning' + // } + // if (device.noise && device.noise > 80) { + // return 'warning' + // } + // if (device.concentration && device.concentration > 10) { + // return 'error' + // } return 'normal' }, @@ -365,8 +365,8 @@ export default { const status = this.getDeviceStatus(device) const statusMap = { 'normal': '正常', - 'warning': '警告', - 'error': '异常', + // 'warning': '警告', + // 'error': '异常', 'offline': '离线' } return statusMap[status] || '未知'