From a440032dc3c2217730c55b5176d45d146876c52b Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期二, 02 六月 2026 16:50:29 +0800
Subject: [PATCH] 浪潮 1.添加数据采集与解析、报警分析评估、报警优化管理三个页面,前端页面开发并联调
---
src/api/alarmManagement/alarmAnalysis.js | 77 ++
src/api/alarmManagement/alarmOptimization.js | 141 +++++
src/views/alarmManagement/alarmOptimization/index.vue | 458 ++++++++++++++++
src/views/alarmManagement/dataCollection/index.vue | 457 ++++++++++++++++
src/views/alarmManagement/alarmAnalysis/index.vue | 374 +++++++++++++
src/api/alarmManagement/dataCollection.js | 163 +++++
6 files changed, 1,670 insertions(+), 0 deletions(-)
diff --git a/src/api/alarmManagement/alarmAnalysis.js b/src/api/alarmManagement/alarmAnalysis.js
new file mode 100644
index 0000000..fd69824
--- /dev/null
+++ b/src/api/alarmManagement/alarmAnalysis.js
@@ -0,0 +1,77 @@
+import request from '@/utils/request';
+
+// ==================== 鎶ヨ鍒嗘瀽璇勪及妯″潡 API ====================
+
+/**
+ * 瀹炴椂鎶ヨ缁熻
+ */
+export function getRealtimeAlarmStats() {
+ return request({
+ url: '/alarm/analysis/realtime/stats',
+ method: 'get'
+ });
+}
+
+/**
+ * 瀹炴椂鎶ヨ鍒楄〃
+ */
+export function listRealtimeAlarms(query) {
+ return request({
+ url: '/alarm/analysis/realtime/list',
+ method: 'get',
+ params: query
+ });
+}
+
+/**
+ * 纭鎶ヨ
+ */
+export function confirmAlarm(alarmId) {
+ return request({
+ url: '/alarm/analysis/confirm/' + alarmId,
+ method: 'post'
+ });
+}
+
+/**
+ * 鍒嗘瀽鎶ヨ
+ */
+export function analyzeAlarm(alarmId) {
+ return request({
+ url: '/alarm/analysis/analyze/' + alarmId,
+ method: 'get'
+ });
+}
+
+/**
+ * 鎶ヨ瓒嬪娍鏁版嵁
+ */
+export function getAlarmTrend(query) {
+ return request({
+ url: '/alarm/analysis/trend',
+ method: 'get',
+ params: query
+ });
+}
+
+/**
+ * 鎶ヨ绫诲瀷鍒嗗竷
+ */
+export function getAlarmTypeDistribution(query) {
+ return request({
+ url: '/alarm/analysis/type/distribution',
+ method: 'get',
+ params: query
+ });
+}
+
+/**
+ * 鏁呴殰璇婃柇鍒楄〃
+ */
+export function getFaultDiagnosisList(query) {
+ return request({
+ url: '/alarm/analysis/fault/list',
+ method: 'get',
+ params: query
+ });
+}
diff --git a/src/api/alarmManagement/alarmOptimization.js b/src/api/alarmManagement/alarmOptimization.js
new file mode 100644
index 0000000..1d2496a
--- /dev/null
+++ b/src/api/alarmManagement/alarmOptimization.js
@@ -0,0 +1,141 @@
+import request from '@/utils/request';
+
+// ==================== 鎶ヨ浼樺寲绠$悊妯″潡 API ====================
+
+/**
+ * 浼樺寲鏁堟灉缁熻
+ */
+export function getOptimizationStatistics() {
+ return request({
+ url: '/alarm/optimization/statistics',
+ method: 'get'
+ });
+}
+
+/**
+ * 婊嬫壈鎶ヨ绛栫暐鍒楄〃
+ */
+export function listNuisanceStrategies(query) {
+ return request({
+ url: '/alarm/optimization/strategy/list',
+ method: 'get',
+ params: query
+ });
+}
+
+/**
+ * 鏂板婊嬫壈鎶ヨ绛栫暐
+ */
+export function addNuisanceStrategy(data) {
+ return request({
+ url: '/alarm/optimization/strategy',
+ method: 'post',
+ data: data
+ });
+}
+
+/**
+ * 淇敼婊嬫壈鎶ヨ绛栫暐
+ */
+export function updateNuisanceStrategy(data) {
+ return request({
+ url: '/alarm/optimization/strategy',
+ method: 'put',
+ data: data
+ });
+}
+
+/**
+ * 鍒犻櫎婊嬫壈鎶ヨ绛栫暐
+ */
+export function delNuisanceStrategy(strategyId) {
+ return request({
+ url: '/alarm/optimization/strategy/' + strategyId,
+ method: 'delete'
+ });
+}
+
+/**
+ * 淇敼绛栫暐鐘舵��
+ */
+export function changeStrategyStatus(strategyId, status) {
+ return request({
+ url: '/alarm/optimization/strategy/changeStatus',
+ method: 'put',
+ data: { strategyId, status }
+ });
+}
+
+/**
+ * 娌荤悊鍓嶅悗瀵规瘮鏁版嵁
+ */
+export function getOptimizationCompareData(query) {
+ return request({
+ url: '/alarm/optimization/compare',
+ method: 'get',
+ params: query
+ });
+}
+
+/**
+ * 楂橀鎶ヨ鐐逛綅鍒楄〃
+ */
+export function listHighFreqAlarms(query) {
+ return request({
+ url: '/alarm/optimization/highfreq/list',
+ method: 'get',
+ params: query
+ });
+}
+
+/**
+ * 鎶ヨ鍓旈櫎瀹℃壒鍒楄〃
+ */
+export function listRemoveApprovals(query) {
+ return request({
+ url: '/alarm/optimization/approval/list',
+ method: 'get',
+ params: query
+ });
+}
+
+/**
+ * 鎻愪氦鍓旈櫎鐢宠
+ */
+export function submitRemoveApply(data) {
+ return request({
+ url: '/alarm/optimization/approval',
+ method: 'post',
+ data: data
+ });
+}
+
+/**
+ * 瀹℃壒閫氳繃
+ */
+export function approveRemoveApply(applyNo) {
+ return request({
+ url: '/alarm/optimization/approval/approve/' + applyNo,
+ method: 'post'
+ });
+}
+
+/**
+ * 瀹℃壒椹冲洖
+ */
+export function rejectRemoveApply(applyNo) {
+ return request({
+ url: '/alarm/optimization/approval/reject/' + applyNo,
+ method: 'post'
+ });
+}
+
+/**
+ * 鎶ヨ鐐逛綅閫夐」
+ */
+export function listAlarmPointOptions() {
+ return request({
+ url: '/alarm/optimization/point/options',
+ method: 'get'
+ });
+}
diff --git a/src/api/alarmManagement/dataCollection.js b/src/api/alarmManagement/dataCollection.js
new file mode 100644
index 0000000..fb2c467
--- /dev/null
+++ b/src/api/alarmManagement/dataCollection.js
@@ -0,0 +1,163 @@
+import request from '@/utils/request';
+
+// ==================== 鏁版嵁閲囬泦涓庤В鏋愭ā鍧� API ====================
+
+/**
+ * 閲囬泦缁熻姒傝
+ */
+export function getCollectionStatistics() {
+ return request({
+ url: '/alarm/collection/statistics',
+ method: 'get'
+ });
+}
+
+/**
+ * 鎶ヨ鐐逛綅鍙拌处鍒楄〃
+ */
+export function listAlarmPointLedger(query) {
+ return request({
+ url: '/alarm/point/ledger/list',
+ method: 'get',
+ params: query
+ });
+}
+
+/**
+ * 鏂板鎶ヨ鐐逛綅
+ */
+export function addAlarmPoint(data) {
+ return request({
+ url: '/alarm/point/ledger',
+ method: 'post',
+ data: data
+ });
+}
+
+/**
+ * 淇敼鎶ヨ鐐逛綅
+ */
+export function updateAlarmPoint(data) {
+ return request({
+ url: '/alarm/point/ledger',
+ method: 'put',
+ data: data
+ });
+}
+
+/**
+ * 鍒犻櫎鎶ヨ鐐逛綅
+ */
+export function delAlarmPoint(pointId) {
+ return request({
+ url: '/alarm/point/ledger/' + pointId,
+ method: 'delete'
+ });
+}
+
+/**
+ * 淇敼鐐逛綅鐘舵��
+ */
+export function changePointStatus(pointId, status) {
+ return request({
+ url: '/alarm/point/ledger/changeStatus',
+ method: 'put',
+ data: { pointId, status }
+ });
+}
+
+/**
+ * 瀵煎嚭鍙拌处鏁版嵁
+ */
+export function exportPointLedger(query) {
+ return request({
+ url: '/alarm/point/ledger/export',
+ method: 'get',
+ params: query,
+ responseType: 'blob'
+ });
+}
+
+/**
+ * 鏁版嵁閲囬泦鎺ュ彛鍒楄〃
+ */
+export function listDataInterface(query) {
+ return request({
+ url: '/alarm/collection/interface/list',
+ method: 'get',
+ params: query
+ });
+}
+
+/**
+ * 鍚姩鏁版嵁閲囬泦
+ */
+export function startDataCollection(interfaceId) {
+ return request({
+ url: '/alarm/collection/interface/start/' + interfaceId,
+ method: 'post'
+ });
+}
+
+/**
+ * 鍋滄鏁版嵁閲囬泦
+ */
+export function stopDataCollection(interfaceId) {
+ return request({
+ url: '/alarm/collection/interface/stop/' + interfaceId,
+ method: 'post'
+ });
+}
+
+/**
+ * 鏂板鏁版嵁閲囬泦鎺ュ彛
+ */
+export function addDataInterface(data) {
+ return request({
+ url: '/alarm/collection/interface',
+ method: 'post',
+ data: data
+ });
+}
+
+/**
+ * 淇敼鏁版嵁閲囬泦鎺ュ彛
+ */
+export function updateDataInterface(data) {
+ return request({
+ url: '/alarm/collection/interface',
+ method: 'post',
+ data: data
+ });
+}
+
+/**
+ * 鍒犻櫎鏁版嵁閲囬泦鎺ュ彛
+ */
+export function delDataInterface(interfaceId) {
+ return request({
+ url: '/alarm/collection/interface/' + interfaceId,
+ method: 'delete'
+ });
+}
+
+/**
+ * 淇敼鎺ュ彛鐘舵��
+ */
+export function changeInterfaceStatus(interfaceId, status) {
+ return request({
+ url: '/alarm/collection/interface/changeStatus',
+ method: 'put',
+ data: { interfaceId, status }
+ });
+}
+
+/**
+ * 鑾峰彇鏁版嵁閲囬泦鎺ュ彛璇︽儏
+ */
+export function getDataInterfaceDetail(interfaceId) {
+ return request({
+ url: '/alarm/collection/interface/' + interfaceId,
+ method: 'get'
+ });
+}
diff --git a/src/views/alarmManagement/alarmAnalysis/index.vue b/src/views/alarmManagement/alarmAnalysis/index.vue
new file mode 100644
index 0000000..e0ec0e7
--- /dev/null
+++ b/src/views/alarmManagement/alarmAnalysis/index.vue
@@ -0,0 +1,374 @@
+<template>
+ <div class="app-container">
+ <!-- 瀹炴椂鎶ヨ鐘舵�� -->
+ <el-row :gutter="20" class="stat-row">
+ <el-col :span="6">
+ <el-card class="status-card danger">
+ <div class="status-title">绱ф�ユ姤璀�</div>
+ <div class="status-value">{{ alarmStats.urgent }}</div>
+ </el-card>
+ </el-col>
+ <el-col :span="6">
+ <el-card class="status-card warning">
+ <div class="status-title">閲嶈鎶ヨ</div>
+ <div class="status-value">{{ alarmStats.important }}</div>
+ </el-card>
+ </el-col>
+ <el-col :span="6">
+ <el-card class="status-card info">
+ <div class="status-title">涓�鑸姤璀�</div>
+ <div class="status-value">{{ alarmStats.normal }}</div>
+ </el-card>
+ </el-col>
+ <el-col :span="6">
+ <el-card class="status-card success">
+ <div class="status-title">浠婃棩绱</div>
+ <div class="status-value">{{ alarmStats.total }}</div>
+ </el-card>
+ </el-col>
+ </el-row>
+
+ <!-- 瀹炴椂鎶ヨ鐩戞祴 -->
+ <el-card class="box-card">
+ <template #header>
+ <div class="card-header">
+ <span>瀹炴椂鎶ヨ鐩戞祴</span>
+ <el-radio-group v-model="refreshInterval" size="small">
+ <el-radio-button :label="5">5绉掑埛鏂�</el-radio-button>
+ <el-radio-button :label="10">10绉掑埛鏂�</el-radio-button>
+ <el-radio-button :label="30">30绉掑埛鏂�</el-radio-button>
+ </el-radio-group>
+ </div>
+ </template>
+
+ <el-table :data="realtimeAlarms" v-loading="loading" border stripe>
+ <el-table-column prop="alarmTime" label="鎶ヨ鏃堕棿" width="160" align="center" />
+ <el-table-column prop="alarmLevel" label="绾у埆" width="80" align="center">
+ <template #default="{ row }">
+ <el-tag :type="row.alarmLevel === '绱ф��' ? 'danger' : row.alarmLevel === '閲嶈' ? 'warning' : 'info'" size="small">
+ {{ row.alarmLevel }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column prop="pointName" label="鎶ヨ鐐逛綅" min-width="180" />
+ <el-table-column prop="alarmType" label="鎶ヨ绫诲瀷" width="120" align="center" />
+ <el-table-column prop="alarmValue" label="鎶ヨ鍊�" width="100" align="center" />
+ <el-table-column prop="thresholdValue" label="闃堝��" width="100" align="center" />
+ <el-table-column prop="area" label="鎵�灞炲尯鍩�" width="120" align="center" />
+ <el-table-column prop="status" label="鐘舵��" width="100" align="center">
+ <template #default="{ row }">
+ <el-tag :type="row.status === '鏈‘璁�' ? 'danger' : 'success'" size="small">
+ {{ row.status }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="鎿嶄綔" width="150" align="center" fixed="right">
+ <template #default="{ row }">
+ <el-button type="primary" link size="small" @click="handleConfirm(row)">纭</el-button>
+ <el-button type="warning" link size="small" @click="handleAnalyze(row)">鍒嗘瀽</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+
+ <pagination v-show="alarmTotal > 0" :total="alarmTotal" v-model:page="alarmQuery.pageNum" v-model:limit="alarmQuery.pageSize" @pagination="getRealtimeAlarms" />
+ </el-card>
+
+ <!-- 鎶ヨ鍒嗘瀽涓庢晠闅滆瘖鏂� -->
+ <el-row :gutter="20" style="margin-top: 20px;">
+ <el-col :span="12">
+ <el-card class="box-card">
+ <template #header>
+ <span>鎶ヨ瓒嬪娍鍒嗘瀽</span>
+ </template>
+ <Echarts :xAxis="trendXAxis" :yAxis="trendYAxis" :grid="trendGrid" :series="trendSeries" :legend="trendLegend" :tooltip="trendTooltip" :chartStyle="{ height: '300px', width: '100%' }" />
+ </el-card>
+ </el-col>
+ <el-col :span="12">
+ <el-card class="box-card">
+ <template #header>
+ <span>鎶ヨ绫诲瀷鍒嗗竷</span>
+ </template>
+ <Echarts :series="typeSeries" :legend="typeLegend" :tooltip="typeTooltip" :chartStyle="{ height: '300px', width: '100%' }" />
+ </el-card>
+ </el-col>
+ </el-row>
+
+ <!-- 鏁呴殰璇婃柇涓庡缃寚瀵� -->
+ <el-card class="box-card" style="margin-top: 20px;">
+ <template #header>
+ <div class="card-header">
+ <span>鏁呴殰璇婃柇涓庡缃寚瀵�</span>
+ <el-button type="primary" size="small" icon="Search" @click="handleFaultDiagnosis">鏅鸿兘璇婃柇</el-button>
+ </div>
+ </template>
+
+ <el-table :data="faultList" border>
+ <el-table-column type="index" label="搴忓彿" width="60" align="center" />
+ <el-table-column prop="faultType" label="鏁呴殰绫诲瀷" width="150" align="center">
+ <template #default="{ row }">
+ <el-tag type="danger" size="small">{{ row.faultType }}</el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column prop="alarmPoint" label="鍏宠仈鎶ヨ鐐逛綅" min-width="180" />
+ <el-table-column prop="occurrenceCount" label="鍙戠敓娆℃暟" width="100" align="center" />
+ <el-table-column prop="faultReason" label="鍙兘鍘熷洜" min-width="200" />
+ <el-table-column prop="solution" label="澶勭疆寤鸿" min-width="250" />
+ <el-table-column label="鎿嶄綔" width="100" align="center">
+ <template #default="{ row }">
+ <el-button type="primary" link size="small" @click="viewDetail(row)">璇︽儏</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ </el-card>
+
+ <!-- 鎶ヨ鍒嗘瀽寮圭獥 -->
+ <el-dialog title="鎶ヨ璇︽儏鍒嗘瀽" v-model="analyzeVisible" width="700px" append-to-body>
+ <el-descriptions :column="2" border>
+ <el-descriptions-item label="鎶ヨ鐐逛綅">{{ currentAlarm.pointName }}</el-descriptions-item>
+ <el-descriptions-item label="鎶ヨ鏃堕棿">{{ currentAlarm.alarmTime }}</el-descriptions-item>
+ <el-descriptions-item label="鎶ヨ绾у埆">
+ <el-tag :type="currentAlarm.alarmLevel === '绱ф��' ? 'danger' : currentAlarm.alarmLevel === '閲嶈' ? 'warning' : 'info'">
+ {{ currentAlarm.alarmLevel }}
+ </el-tag>
+ </el-descriptions-item>
+ <el-descriptions-item label="鎶ヨ绫诲瀷">{{ currentAlarm.alarmType }}</el-descriptions-item>
+ <el-descriptions-item label="鎶ヨ鍊�">{{ currentAlarm.alarmValue }}</el-descriptions-item>
+ <el-descriptions-item label="鎶ヨ闃堝��">{{ currentAlarm.thresholdValue }}</el-descriptions-item>
+ </el-descriptions>
+
+ <div class="analysis-section">
+ <h4>鏁呴殰鍒嗘瀽</h4>
+ <el-alert :title="analysisResult.faultType" type="error" :description="analysisResult.faultReason" show-icon />
+ </div>
+
+ <div class="analysis-section">
+ <h4>澶勭疆鎸囧</h4>
+ <el-steps direction="vertical" :active="1">
+ <el-step v-for="(step, index) in analysisResult.steps" :key="index" :title="step.title" :description="step.desc" />
+ </el-steps>
+ </div>
+
+ <template #footer>
+ <el-button @click="analyzeVisible = false">鍏抽棴</el-button>
+ <el-button type="primary" @click="handleConfirm(currentAlarm)">纭鎶ヨ</el-button>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, onUnmounted } from 'vue';
+import { ElMessage } from 'element-plus';
+import Echarts from '@/components/Echarts/echarts.vue';
+import Pagination from '@/components/Pagination/index.vue';
+import {
+ getRealtimeAlarmStats,
+ listRealtimeAlarms,
+ confirmAlarm,
+ analyzeAlarm,
+ getAlarmTrend,
+ getAlarmTypeDistribution,
+ getFaultDiagnosisList
+} from '@/api/alarmManagement/alarmAnalysis';
+
+// 鎶ヨ缁熻
+const alarmStats = reactive({
+ urgent: 0,
+ important: 0,
+ normal: 0,
+ total: 0
+});
+
+// 瀹炴椂鎶ヨ鍒楄〃
+const loading = ref(false);
+const realtimeAlarms = ref([]);
+const alarmTotal = ref(0);
+const alarmQuery = reactive({
+ pageNum: 1,
+ pageSize: 10
+});
+
+// 鍒锋柊闂撮殧
+const refreshInterval = ref(10);
+let refreshTimer = null;
+
+// 鏁呴殰鍒楄〃
+const faultList = ref([]);
+
+// 鍒嗘瀽寮圭獥
+const analyzeVisible = ref(false);
+const currentAlarm = ref({});
+const analysisResult = reactive({
+ faultType: '',
+ faultReason: '',
+ steps: []
+});
+
+// 鍥捐〃閰嶇疆
+const trendXAxis = ref([{ type: 'category', data: [] }])
+const trendYAxis = ref([{ type: 'value' }])
+const trendGrid = reactive({ left: '3%', right: '4%', bottom: '3%', containLabel: true })
+const trendSeries = ref([
+ { name: '绱ф��', type: 'line', data: [] },
+ { name: '閲嶈', type: 'line', data: [] },
+ { name: '涓�鑸�', type: 'line', data: [] },
+])
+const trendLegend = reactive({ data: ['绱ф��', '閲嶈', '涓�鑸�'] })
+const trendTooltip = reactive({ trigger: 'axis' })
+
+const typeSeries = ref([{ type: 'pie', radius: '50%', data: [] }])
+const typeLegend = reactive({ orient: 'vertical', left: 'left' })
+const typeTooltip = reactive({ trigger: 'item' })
+
+// 鑾峰彇鎶ヨ缁熻
+async function getAlarmStats() {
+ const res = await getRealtimeAlarmStats();
+ if (res.code === 200) {
+ Object.assign(alarmStats, res.data);
+ }
+}
+
+// 鑾峰彇瀹炴椂鎶ヨ鍒楄〃
+async function getRealtimeAlarms() {
+ loading.value = true;
+ const res = await listRealtimeAlarms(alarmQuery);
+ if (res.code === 200) {
+ realtimeAlarms.value = res.data.records;
+ alarmTotal.value = res.data.total;
+ }
+ loading.value = false;
+}
+
+// 纭鎶ヨ
+async function handleConfirm(row) {
+ const res = await confirmAlarm(row.alarmId);
+ if (res.code === 200) {
+ ElMessage.success('纭鎴愬姛');
+ getRealtimeAlarms();
+ }
+}
+
+// 鍒嗘瀽鎶ヨ
+async function handleAnalyze(row) {
+ currentAlarm.value = row;
+ const res = await analyzeAlarm(row.alarmId);
+ if (res.code === 200) {
+ Object.assign(analysisResult, res.data);
+ }
+ analyzeVisible.value = true;
+}
+
+// 鑾峰彇鎶ヨ瓒嬪娍
+async function getTrendData() {
+ const res = await getAlarmTrend();
+ if (res.code === 200) {
+ const data = res.data;
+ trendXAxis.value[0].data = data.dates
+ trendSeries.value[0].data = data.urgent
+ trendSeries.value[0].itemStyle = { color: '#f56c6c' }
+ trendSeries.value[1].data = data.important
+ trendSeries.value[1].itemStyle = { color: '#e6a23c' }
+ trendSeries.value[2].data = data.normal
+ trendSeries.value[2].itemStyle = { color: '#909399' }
+ }
+}
+
+// 鑾峰彇鎶ヨ绫诲瀷鍒嗗竷
+async function getTypeDistribution() {
+ const res = await getAlarmTypeDistribution();
+ if (res.code === 200) {
+ typeSeries.value[0].data = res.data
+ typeSeries.value[0].emphasis = { itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)' } }
+ }
+}
+
+// 鑾峰彇鏁呴殰璇婃柇鍒楄〃
+async function getFaultList() {
+ const res = await getFaultDiagnosisList();
+ if (res.code === 200) {
+ faultList.value = res.data;
+ }
+}
+
+// 鏅鸿兘璇婃柇
+function handleFaultDiagnosis() {
+ ElMessage.success('鏅鸿兘璇婃柇瀹屾垚');
+ getFaultList();
+}
+
+function viewDetail(row) {
+ ElMessage.info('鏌ョ湅璇︽儏: ' + row.faultType);
+}
+
+// 瀹氭椂鍒锋柊
+function startAutoRefresh() {
+ refreshTimer = setInterval(() => {
+ getAlarmStats();
+ getRealtimeAlarms();
+ }, refreshInterval.value * 1000);
+}
+
+function stopAutoRefresh() {
+ if (refreshTimer) {
+ clearInterval(refreshTimer);
+ refreshTimer = null;
+ }
+}
+
+onMounted(() => {
+ getAlarmStats();
+ getRealtimeAlarms();
+ getTrendData();
+ getTypeDistribution();
+ getFaultList();
+ startAutoRefresh();
+});
+
+onUnmounted(() => {
+ stopAutoRefresh();
+});
+</script>
+
+<style scoped>
+.stat-row {
+ margin-bottom: 20px;
+}
+.status-card {
+ text-align: center;
+}
+.status-card.danger {
+ border-left: 4px solid #f56c6c;
+}
+.status-card.warning {
+ border-left: 4px solid #e6a23c;
+}
+.status-card.info {
+ border-left: 4px solid #909399;
+}
+.status-card.success {
+ border-left: 4px solid #67c23a;
+}
+.status-title {
+ font-size: 14px;
+ color: #666;
+ margin-bottom: 10px;
+}
+.status-value {
+ font-size: 32px;
+ font-weight: bold;
+ color: #303133;
+}
+.card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+.analysis-section {
+ margin-top: 20px;
+}
+.analysis-section h4 {
+ margin: 0 0 15px 0;
+ font-size: 16px;
+ color: #303133;
+}
+</style>
diff --git a/src/views/alarmManagement/alarmOptimization/index.vue b/src/views/alarmManagement/alarmOptimization/index.vue
new file mode 100644
index 0000000..1abbd98
--- /dev/null
+++ b/src/views/alarmManagement/alarmOptimization/index.vue
@@ -0,0 +1,458 @@
+<template>
+ <div class="app-container">
+ <!-- 浼樺寲鏁堟灉缁熻 -->
+ <el-row :gutter="20" class="stat-row">
+ <el-col :span="6">
+ <el-statistic title="宸茶繃婊ゆ棤鏁堟姤璀�" :value="optimizationStats.filteredCount" />
+ </el-col>
+ <el-col :span="6">
+ <el-statistic title="鏈夋晥鎶ヨ鐜�(%)" :value="optimizationStats.validRate" />
+ </el-col>
+ <el-col :span="6">
+ <el-statistic title="骞冲潎鍝嶅簲鏃堕棿(绉�)" :value="optimizationStats.responseTime" />
+ </el-col>
+ <el-col :span="6">
+ <el-statistic title="娌荤悊璇勫垎" :value="optimizationStats.score" />
+ </el-col>
+ </el-row>
+
+ <!-- 婊嬫壈鎶ヨ娌荤悊绛栫暐 -->
+ <el-card class="box-card">
+ <template #header>
+ <div class="card-header">
+ <span>婊嬫壈鎶ヨ娌荤悊绛栫暐</span>
+ <el-button type="primary" size="small" icon="Plus" @click="handleAddStrategy">鏂板绛栫暐</el-button>
+ </div>
+ </template>
+
+ <el-table :data="strategyList" v-loading="loading" border>
+ <el-table-column type="index" label="搴忓彿" width="60" align="center" />
+ <el-table-column prop="strategyName" label="绛栫暐鍚嶇О" min-width="150" />
+ <el-table-column prop="strategyType" label="绛栫暐绫诲瀷" width="120" align="center">
+ <template #default="{ row }">
+ <el-tag size="small">{{ row.strategyType }}</el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column prop="description" label="绛栫暐璇存槑" min-width="200" />
+ <el-table-column prop="filteredCount" label="宸茶繃婊ゆ暟" width="100" align="center" />
+ <el-table-column prop="status" label="鐘舵��" width="80" align="center">
+ <template #default="{ row }">
+ <el-tag :type="row.status === 1 ? 'success' : 'info'" size="small">
+ {{ row.status === 1 ? '鍚敤' : '鍋滅敤' }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="鎿嶄綔" width="150" align="center">
+ <template #default="{ row }">
+ <el-button type="primary" link size="small" @click="handleEditStrategy(row)">缂栬緫</el-button>
+ <el-button type="danger" link size="small" @click="handleDeleteStrategy(row)">鍒犻櫎</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ </el-card>
+
+ <!-- 鍘嗗彶鎶ヨ鏁版嵁鍒嗘瀽 -->
+ <el-row :gutter="20" style="margin-top: 20px;">
+ <el-col :span="12">
+ <el-card class="box-card">
+ <template #header>
+ <span>娌荤悊鍓嶅悗瀵规瘮</span>
+ </template>
+ <Echarts :xAxis="compareXAxis" :yAxis="compareYAxis" :series="compareSeries" :legend="compareLegend" :tooltip="compareTooltip" :chartStyle="{ height: '280px', width: '100%' }" />
+ </el-card>
+ </el-col>
+ <el-col :span="12">
+ <el-card class="box-card">
+ <template #header>
+ <div class="card-header">
+ <span>楂橀鎶ヨ鐐逛綅TOP5</span>
+ </div>
+ </template>
+ <el-table :data="highFreqList" size="small" border>
+ <el-table-column type="index" label="鎺掑悕" width="60" align="center" />
+ <el-table-column prop="pointName" label="鐐逛綅鍚嶇О" min-width="150" />
+ <el-table-column prop="deviceType" label="璁惧绫诲瀷" width="120" align="center" />
+ <el-table-column prop="alarmLevel" label="鎶ヨ绾у埆" width="100" align="center">
+ <template #default="{ row }">
+ <el-tag :type="row.alarmLevel === '绱ф��' ? 'danger' : row.alarmLevel === '閲嶈' ? 'warning' : 'info'" size="small">
+ {{ row.alarmLevel }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ </el-table>
+ </el-card>
+ </el-col>
+ </el-row>
+
+ <!-- 鎶ヨ鍓旈櫎瀹℃壒 -->
+ <el-card class="box-card" style="margin-top: 20px;">
+ <template #header>
+ <div class="card-header">
+ <span>鎶ヨ鍓旈櫎瀹℃壒</span>
+ <el-button type="primary" size="small" icon="Plus" @click="handleApplyRemove">鐢宠鍓旈櫎</el-button>
+ </div>
+ </template>
+
+ <el-form :inline="true" :model="approvalQuery">
+ <el-form-item label="瀹℃壒鐘舵��">
+ <el-select v-model="approvalQuery.status" placeholder="璇烽�夋嫨" clearable style="width: 120px">
+ <el-option label="寰呭鎵�" value="pending" />
+ <el-option label="宸查�氳繃" value="approved" />
+ <el-option label="宸查┏鍥�" value="rejected" />
+ </el-select>
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary" icon="Search" @click="getApprovalList">鏌ヨ</el-button>
+ <el-button icon="Refresh" @click="resetApprovalQuery">閲嶇疆</el-button>
+ </el-form-item>
+ </el-form>
+
+ <el-table :data="approvalList" border>
+ <el-table-column type="index" label="搴忓彿" width="60" align="center" />
+ <el-table-column prop="applyNo" label="鐢宠缂栧彿" width="120" align="center" />
+ <el-table-column prop="pointName" label="鎶ヨ鐐逛綅" min-width="150" />
+ <el-table-column prop="removeReason" label="鍓旈櫎鍘熷洜" min-width="180" />
+ <el-table-column prop="applicant" label="鐢宠浜�" width="100" align="center" />
+ <el-table-column prop="applyTime" label="鐢宠鏃堕棿" width="160" align="center" />
+ <el-table-column prop="status" label="瀹℃壒鐘舵��" width="100" align="center">
+ <template #default="{ row }">
+ <el-tag :type="row.status === 'pending' ? 'warning' : row.status === 'approved' ? 'success' : 'danger'" size="small">
+ {{ row.status === 'pending' ? '寰呭鎵�' : row.status === 'approved' ? '宸查�氳繃' : '宸查┏鍥�' }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="鎿嶄綔" width="150" align="center">
+ <template #default="{ row }">
+ <template v-if="row.status === 'pending'">
+ <el-button type="success" link size="small" @click="handleApprove(row)">閫氳繃</el-button>
+ <el-button type="danger" link size="small" @click="handleReject(row)">椹冲洖</el-button>
+ </template>
+ <span v-else>-</span>
+ </template>
+ </el-table-column>
+ </el-table>
+
+ <pagination v-show="approvalTotal > 0" :total="approvalTotal" v-model:page="approvalQuery.pageNum" v-model:limit="approvalQuery.pageSize" @pagination="getApprovalList" />
+ </el-card>
+
+ <!-- 鏂板/缂栬緫绛栫暐寮圭獥 -->
+ <el-dialog :title="strategyDialogTitle" v-model="strategyDialogVisible" width="500px" append-to-body>
+ <el-form ref="strategyFormRef" :model="strategyForm" :rules="strategyRules" label-width="100px">
+ <el-form-item label="绛栫暐鍚嶇О" prop="strategyName">
+ <el-input v-model="strategyForm.strategyName" placeholder="璇疯緭鍏ョ瓥鐣ュ悕绉�" />
+ </el-form-item>
+ <el-form-item label="绛栫暐绫诲瀷" prop="strategyType">
+ <el-select v-model="strategyForm.strategyType" placeholder="璇烽�夋嫨" style="width: 100%">
+ <el-option label="閲嶅鎶ヨ杩囨护" value="閲嶅鎶ヨ杩囨护" />
+ <el-option label="鎶栧姩鎶ヨ杩囨护" value="鎶栧姩鎶ヨ杩囨护" />
+ <el-option label="鐬�佹姤璀﹁繃婊�" value="鐬�佹姤璀﹁繃婊�" />
+ <el-option label="鏃犳晥鎶ヨ灞忚斀" value="鏃犳晥鎶ヨ灞忚斀" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="绛栫暐璇存槑" prop="description">
+ <el-input v-model="strategyForm.description" type="textarea" :rows="3" placeholder="璇疯緭鍏ョ瓥鐣ヨ鏄�" />
+ </el-form-item>
+ <el-form-item label="瑙勫垯閰嶇疆" prop="ruleConfig">
+ <el-input v-model="strategyForm.ruleConfig" type="textarea" :rows="3" placeholder="璇疯緭鍏ヨ鍒欓厤缃甁SON" />
+ </el-form-item>
+ <el-form-item label="鐘舵��" prop="status">
+ <el-radio-group v-model="strategyForm.status">
+ <el-radio :label="1">鍚敤</el-radio>
+ <el-radio :label="0">鍋滅敤</el-radio>
+ </el-radio-group>
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <el-button @click="strategyDialogVisible = false">鍙栨秷</el-button>
+ <el-button type="primary" @click="submitStrategyForm">纭畾</el-button>
+ </template>
+ </el-dialog>
+
+ <!-- 鐢宠鍓旈櫎寮圭獥 -->
+ <el-dialog title="鐢宠鎶ヨ鍓旈櫎" v-model="applyDialogVisible" width="500px" append-to-body>
+ <el-form ref="applyFormRef" :model="applyForm" :rules="applyRules" label-width="100px">
+ <el-form-item label="鎶ヨ鐐逛綅" prop="pointId">
+ <el-select v-model="applyForm.pointId" placeholder="璇烽�夋嫨鎶ヨ鐐逛綅" style="width: 100%">
+ <el-option v-for="item in pointOptions" :key="item.pointId" :label="item.pointName" :value="item.pointId" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="鍓旈櫎鍘熷洜" prop="removeReason">
+ <el-select v-model="applyForm.removeReason" placeholder="璇烽�夋嫨鍓旈櫎鍘熷洜" style="width: 100%">
+ <el-option label="璇姤璀�" value="璇姤璀�" />
+ <el-option label="璁惧鏁呴殰宸蹭慨澶�" value="璁惧鏁呴殰宸蹭慨澶�" />
+ <el-option label="宸ヨ壓璋冩暣" value="宸ヨ壓璋冩暣" />
+ <el-option label="鍏朵粬鍘熷洜" value="鍏朵粬鍘熷洜" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="璇︾粏璇存槑" prop="description">
+ <el-input v-model="applyForm.description" type="textarea" :rows="3" placeholder="璇疯缁嗚鏄庡墧闄ゅ師鍥�" />
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <el-button @click="applyDialogVisible = false">鍙栨秷</el-button>
+ <el-button type="primary" @click="submitApplyForm">鎻愪氦鐢宠</el-button>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import Echarts from '@/components/Echarts/echarts.vue';
+import Pagination from '@/components/Pagination/index.vue';
+import {
+ getOptimizationStatistics,
+ listNuisanceStrategies,
+ addNuisanceStrategy,
+ updateNuisanceStrategy,
+ delNuisanceStrategy,
+ changeStrategyStatus,
+ getOptimizationCompareData,
+ listRemoveApprovals,
+ submitRemoveApply,
+ approveRemoveApply,
+ rejectRemoveApply
+} from '@/api/alarmManagement/alarmOptimization';
+import { listAlarmPointLedger } from '@/api/alarmManagement/dataCollection';
+
+// 浼樺寲缁熻
+const optimizationStats = reactive({
+ filteredCount: 0,
+ validRate: 0,
+ responseTime: 0,
+ score: 0
+});
+
+// 绛栫暐鍒楄〃
+const loading = ref(false);
+const strategyList = ref([]);
+
+// 瀹℃壒鍒楄〃
+const approvalList = ref([]);
+const approvalTotal = ref(0);
+const approvalQuery = reactive({
+ pageNum: 1,
+ pageSize: 10,
+ status: ''
+});
+
+// 楂橀鎶ヨ鍒楄〃
+const highFreqList = ref([]);
+
+// 绛栫暐寮圭獥
+const strategyDialogVisible = ref(false);
+const strategyDialogTitle = ref('');
+const strategyFormRef = ref(null);
+const strategyForm = reactive({
+ strategyId: undefined,
+ strategyName: '',
+ strategyType: '',
+ description: '',
+ ruleConfig: '',
+ status: 1
+});
+const strategyRules = {
+ strategyName: [{ required: true, message: '绛栫暐鍚嶇О涓嶈兘涓虹┖', trigger: 'blur' }],
+ strategyType: [{ required: true, message: '绛栫暐绫诲瀷涓嶈兘涓虹┖', trigger: 'change' }],
+ description: [{ required: true, message: '绛栫暐璇存槑涓嶈兘涓虹┖', trigger: 'blur' }]
+};
+
+// 鐢宠寮圭獥
+const applyDialogVisible = ref(false);
+const applyFormRef = ref(null);
+const applyForm = reactive({
+ pointId: '',
+ removeReason: '',
+ description: ''
+});
+const applyRules = {
+ pointId: [{ required: true, message: '璇烽�夋嫨鎶ヨ鐐逛綅', trigger: 'change' }],
+ removeReason: [{ required: true, message: '璇烽�夋嫨鍓旈櫎鍘熷洜', trigger: 'change' }],
+ description: [{ required: true, message: '璇峰~鍐欒缁嗚鏄�', trigger: 'blur' }]
+};
+const pointOptions = ref([]);
+
+// 鍥捐〃閰嶇疆
+const compareXAxis = ref([{ type: 'category', data: [] }])
+const compareYAxis = ref([{ type: 'value' }])
+const compareSeries = ref([
+ { name: '娌荤悊鍓�', type: 'bar', data: [], itemStyle: { color: '#f56c6c' } },
+ { name: '娌荤悊鍚�', type: 'bar', data: [], itemStyle: { color: '#67c23a' } },
+])
+const compareLegend = reactive({ data: ['娌荤悊鍓�', '娌荤悊鍚�'] })
+const compareTooltip = reactive({ trigger: 'axis', axisPointer: { type: 'shadow' } })
+
+// 鑾峰彇浼樺寲缁熻
+async function getStatistics() {
+ const res = await getOptimizationStatistics();
+ if (res.code === 200) {
+ Object.assign(optimizationStats, res.data);
+ }
+}
+
+// 鑾峰彇绛栫暐鍒楄〃
+async function getStrategyList() {
+ loading.value = true;
+ const res = await listNuisanceStrategies();
+ if (res.code === 200) {
+ strategyList.value = res.data;
+ }
+ loading.value = false;
+}
+
+// 鑾峰彇瀹℃壒鍒楄〃
+async function getApprovalList() {
+ const res = await listRemoveApprovals(approvalQuery);
+ if (res.code === 200) {
+ approvalList.value = res.data.records;
+ approvalTotal.value = res.data.total;
+ }
+}
+
+// 鑾峰彇楂橀鎶ヨ鍒楄〃锛堣皟鐢ㄦ暟鎹噰闆嗘ā鍧楃殑鏌ヨ鎺ュ彛锛�
+async function getHighFreqList() {
+ const res = await listAlarmPointLedger({ current: 1, size: 5 });
+ if (res.code === 200) {
+ highFreqList.value = res.data.records || res.data || [];
+ }
+}
+
+// 鑾峰彇瀵规瘮鏁版嵁
+async function getCompareData() {
+ const res = await getOptimizationCompareData();
+ if (res.code === 200) {
+ const data = res.data;
+ compareXAxis.value[0].data = data.categories
+ compareSeries.value[0].data = data.before
+ compareSeries.value[1].data = data.after
+ }
+}
+
+// 鑾峰彇鐐逛綅閫夐」锛堣皟鐢ㄦ暟鎹噰闆嗘ā鍧楃殑鏌ヨ鎺ュ彛锛�
+async function getPointOptions() {
+ const res = await listAlarmPointLedger({ current: 1, size: 1000 });
+ if (res.code === 200) {
+ pointOptions.value = res.data.records || res.data || [];
+ }
+}
+
+function handleAddStrategy() {
+ strategyDialogTitle.value = '鏂板绛栫暐';
+ Object.keys(strategyForm).forEach(key => strategyForm[key] = key === 'strategyId' ? undefined : '');
+ strategyDialogVisible.value = true;
+}
+
+function handleEditStrategy(row) {
+ strategyDialogTitle.value = '缂栬緫绛栫暐';
+ Object.assign(strategyForm, row);
+ strategyDialogVisible.value = true;
+}
+
+async function submitStrategyForm() {
+ strategyFormRef.value.validate(async (valid) => {
+ if (valid) {
+ const api = strategyForm.strategyId ? updateNuisanceStrategy : addNuisanceStrategy;
+ const res = await api(strategyForm);
+ if (res.code === 200) {
+ ElMessage.success(strategyForm.strategyId ? '淇敼鎴愬姛' : '鏂板鎴愬姛');
+ strategyDialogVisible.value = false;
+ getStrategyList();
+ }
+ }
+ });
+}
+
+async function handleDeleteStrategy(row) {
+ await ElMessageBox.confirm('纭鍒犻櫎璇ョ瓥鐣ュ悧锛�', '鎻愮ず', { type: 'warning' });
+ const res = await delNuisanceStrategy(row.strategyId);
+ if (res.code === 200) {
+ ElMessage.success('鍒犻櫎鎴愬姛');
+ getStrategyList();
+ }
+}
+
+async function handleStrategyStatusChange(row) {
+ const res = await changeStrategyStatus(row.strategyId, row.status);
+ if (res.code === 200) {
+ ElMessage.success('鐘舵�佷慨鏀规垚鍔�');
+ }
+}
+
+function handleViewMore() {
+ ElMessage.info('鏌ョ湅鏇村楂橀鎶ヨ鐐逛綅');
+}
+
+function handleApplyRemove() {
+ Object.keys(applyForm).forEach(key => applyForm[key] = '');
+ applyDialogVisible.value = true;
+}
+
+async function submitApplyForm() {
+ applyFormRef.value.validate(async (valid) => {
+ if (valid) {
+ const res = await submitRemoveApply(applyForm);
+ if (res.code === 200) {
+ ElMessage.success('鐢宠鎻愪氦鎴愬姛');
+ applyDialogVisible.value = false;
+ getApprovalList();
+ }
+ }
+ });
+}
+
+async function handleApprove(row) {
+ const res = await approveRemoveApply(row.applyNo);
+ if (res.code === 200) {
+ ElMessage.success('瀹℃壒閫氳繃');
+ getApprovalList();
+ }
+}
+
+async function handleReject(row) {
+ const res = await rejectRemoveApply(row.applyNo);
+ if (res.code === 200) {
+ ElMessage.success('宸查┏鍥�');
+ getApprovalList();
+ }
+}
+
+function viewDetail(row) {
+ ElMessage.info('鏌ョ湅璇︽儏: ' + row.applyNo);
+}
+
+function resetApprovalQuery() {
+ approvalQuery.status = '';
+ getApprovalList();
+}
+
+onMounted(() => {
+ getStatistics();
+ getStrategyList();
+ getApprovalList();
+ getHighFreqList();
+ getCompareData();
+ getPointOptions();
+});
+</script>
+
+<style scoped>
+.stat-row {
+ margin-bottom: 20px;
+}
+.stat-row :deep(.el-statistic__content) {
+ font-size: 28px;
+ font-weight: 600;
+ color: #303133;
+}
+.stat-row :deep(.el-statistic__title) {
+ font-size: 14px;
+ color: #606266;
+ margin-bottom: 8px;
+}
+.card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+</style>
diff --git a/src/views/alarmManagement/dataCollection/index.vue b/src/views/alarmManagement/dataCollection/index.vue
new file mode 100644
index 0000000..014d3a0
--- /dev/null
+++ b/src/views/alarmManagement/dataCollection/index.vue
@@ -0,0 +1,457 @@
+<template>
+ <div class="app-container">
+ <!-- 缁熻鍗$墖 -->
+ <el-row :gutter="20" class="stat-row">
+ <el-col :span="6">
+ <el-statistic title="鎶ヨ鐐逛綅鎬绘暟" :value="statistics.totalPoints" />
+ </el-col>
+ <el-col :span="6">
+ <el-statistic title="宸茶繛鎺ヨ澶�" :value="statistics.connectedDevices" />
+ </el-col>
+ <el-col :span="6">
+ <el-statistic title="浠婃棩鏁版嵁閲�(涓囨潯)" :value="statistics.todayDataVolume" />
+ </el-col>
+ <el-col :span="6">
+ <el-statistic title="瑙f瀽鎴愬姛鐜�(%)" :value="statistics.parseSuccessRate" />
+ </el-col>
+ </el-row>
+
+ <!-- 鎶ヨ鐐逛綅鍙拌处 -->
+ <el-card class="box-card">
+ <template #header>
+ <div class="card-header">
+ <span>鎶ヨ鐐逛綅鍙拌处</span>
+ <div>
+ <el-button type="primary" size="small" icon="Plus" @click="handleAdd">鏂板</el-button>
+ <!-- <el-button size="small" icon="Download" @click="handleExport">瀵煎嚭</el-button> -->
+ </div>
+ </div>
+ </template>
+
+ <el-form :inline="true" :model="queryParams">
+ <el-form-item label="鐐逛綅鍚嶇О">
+ <el-input v-model="queryParams.pointName" placeholder="璇疯緭鍏�" clearable />
+ </el-form-item>
+ <el-form-item label="璁惧绫诲瀷">
+ <el-select v-model="queryParams.deviceType" placeholder="璇烽�夋嫨" clearable style="width: 150px">
+ <el-option label="娓╁害浼犳劅鍣�" value="娓╁害浼犳劅鍣�" />
+ <el-option label="鍘嬪姏浼犳劅鍣�" value="鍘嬪姏浼犳劅鍣�" />
+ <el-option label="娴侀噺浼犳劅鍣�" value="娴侀噺浼犳劅鍣�" />
+ <el-option label="娑蹭綅浼犳劅鍣�" value="娑蹭綅浼犳劅鍣�" />
+ </el-select>
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary" icon="Search" @click="getList">鏌ヨ</el-button>
+ <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+ </el-form-item>
+ </el-form>
+
+ <el-table :data="pointList" v-loading="loading" border>
+ <el-table-column type="index" label="搴忓彿" width="60" align="center" />
+ <el-table-column prop="pointCode" label="鐐逛綅缂栫爜" width="120" align="center" />
+ <el-table-column prop="pointName" label="鐐逛綅鍚嶇О" min-width="150" />
+ <el-table-column prop="deviceType" label="璁惧绫诲瀷" width="120" align="center" />
+ <el-table-column prop="alarmLevel" label="鎶ヨ绾у埆" width="100" align="center">
+ <template #default="{ row }">
+ <el-tag :type="row.alarmLevel === '绱ф��' ? 'danger' : row.alarmLevel === '閲嶈' ? 'warning' : 'info'" size="small">
+ {{ row.alarmLevel }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column prop="thresholdValue" label="鎶ヨ闃堝��" width="120" align="center" />
+ <el-table-column prop="area" label="鎵�灞炲尯鍩�" width="120" align="center" />
+ <el-table-column prop="status" label="鐘舵��" width="80" align="center">
+ <template #default="{ row }">
+ <el-tag :type="row.status === 1 ? 'success' : 'info'" size="small">
+ {{ row.status === 1 ? '鍚敤' : '鍋滅敤' }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="鎿嶄綔" width="150" align="center" fixed="right">
+ <template #default="{ row }">
+ <el-button type="primary" link size="small" @click="handleEdit(row)">缂栬緫</el-button>
+ <el-button type="danger" link size="small" @click="handleDelete(row)">鍒犻櫎</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-card>
+
+ <!-- 鏁版嵁閲囬泦閰嶇疆 -->
+ <el-card class="box-card" style="margin-top: 20px;">
+ <template #header>
+ <div class="card-header">
+ <span>鏁版嵁閲囬泦閰嶇疆</span>
+ <el-button type="primary" size="small" icon="Plus" @click="handleAddInterface">鏂板鎺ュ彛</el-button>
+ </div>
+ </template>
+
+ <el-table :data="interfaceList" border>
+ <el-table-column type="index" label="搴忓彿" width="60" align="center" />
+ <el-table-column prop="interfaceName" label="鎺ュ彛鍚嶇О" min-width="150" />
+ <el-table-column prop="interfaceType" label="鎺ュ彛绫诲瀷" width="120" align="center">
+ <template #default="{ row }">
+ <el-tag size="small">{{ row.interfaceType }}</el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column prop="serverAddress" label="鏈嶅姟鍣ㄥ湴鍧�" width="180" />
+ <el-table-column prop="collectFreq" label="閲囬泦棰戠巼" width="100" align="center" />
+ <el-table-column label="鎿嶄綔" width="250" align="center">
+ <template #default="{ row }">
+ <el-button type="primary" link size="small" @click="handleEditInterface(row)">缂栬緫</el-button>
+ <el-button type="danger" link size="small" @click="handleDeleteInterface(row)">鍒犻櫎</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ </el-card>
+
+ <!-- 鏂板/缂栬緫鐐逛綅寮圭獥 -->
+ <el-dialog :title="dialogTitle" v-model="dialogVisible" width="500px" append-to-body>
+ <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
+ <el-form-item label="鐐逛綅缂栫爜" prop="pointCode">
+ <el-input v-model="form.pointCode" placeholder="璇疯緭鍏ョ偣浣嶇紪鐮�" />
+ </el-form-item>
+ <el-form-item label="鐐逛綅鍚嶇О" prop="pointName">
+ <el-input v-model="form.pointName" placeholder="璇疯緭鍏ョ偣浣嶅悕绉�" />
+ </el-form-item>
+ <el-form-item label="璁惧绫诲瀷" prop="deviceType">
+ <el-select v-model="form.deviceType" placeholder="璇烽�夋嫨" style="width: 100%">
+ <el-option label="娓╁害浼犳劅鍣�" value="娓╁害浼犳劅鍣�" />
+ <el-option label="鍘嬪姏浼犳劅鍣�" value="鍘嬪姏浼犳劅鍣�" />
+ <el-option label="娴侀噺浼犳劅鍣�" value="娴侀噺浼犳劅鍣�" />
+ <el-option label="娑蹭綅浼犳劅鍣�" value="娑蹭綅浼犳劅鍣�" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="鎶ヨ绾у埆" prop="alarmLevel">
+ <el-select v-model="form.alarmLevel" placeholder="璇烽�夋嫨" style="width: 100%">
+ <el-option label="绱ф��" value="绱ф��" />
+ <el-option label="閲嶈" value="閲嶈" />
+ <el-option label="涓�鑸�" value="涓�鑸�" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="鎶ヨ闃堝��" prop="thresholdValue">
+ <el-input v-model="form.thresholdValue" placeholder="璇疯緭鍏ユ姤璀﹂槇鍊�" />
+ </el-form-item>
+ <el-form-item label="鎵�灞炲尯鍩�" prop="area">
+ <el-input v-model="form.area" placeholder="璇疯緭鍏ユ墍灞炲尯鍩�" />
+ </el-form-item>
+ <el-form-item label="鐘舵��" prop="status">
+ <el-radio-group v-model="form.status">
+ <el-radio :label="1">鍚敤</el-radio>
+ <el-radio :label="0">鍋滅敤</el-radio>
+ </el-radio-group>
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+ <el-button type="primary" @click="submitForm">纭畾</el-button>
+ </template>
+ </el-dialog>
+
+ <!-- 鏂板/缂栬緫鎺ュ彛寮圭獥 -->
+ <el-dialog :title="interfaceDialogTitle" v-model="interfaceDialogVisible" width="550px" append-to-body>
+ <el-form ref="interfaceFormRef" :model="interfaceForm" :rules="interfaceRules" label-width="100px">
+ <el-form-item label="鎺ュ彛鍚嶇О" prop="interfaceName">
+ <el-input v-model="interfaceForm.interfaceName" placeholder="璇疯緭鍏ユ帴鍙e悕绉�" />
+ </el-form-item>
+ <el-form-item label="鎺ュ彛绫诲瀷" prop="interfaceType">
+ <el-select v-model="interfaceForm.interfaceType" placeholder="璇烽�夋嫨" style="width: 100%">
+ <el-option label="OPC UA" value="OPC UA" />
+ <el-option label="Modbus TCP" value="Modbus TCP" />
+ <el-option label="Modbus RTU" value="Modbus RTU" />
+ <el-option label="MQTT" value="MQTT" />
+ <el-option label="HTTP API" value="HTTP API" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="鏈嶅姟鍣ㄥ湴鍧�" prop="serverAddress">
+ <el-input v-model="interfaceForm.serverAddress" placeholder="濡�: opc.tcp://192.168.1.100:4840" />
+ </el-form-item>
+ <el-form-item label="閲囬泦棰戠巼" prop="collectFreq">
+ <el-input v-model="interfaceForm.collectFreq" placeholder="濡�: 1绉掋��5绉掋��1鍒嗛挓" />
+ </el-form-item>
+ <el-form-item label="鐢ㄦ埛鍚�">
+ <el-input v-model="interfaceForm.username" placeholder="璇疯緭鍏ョ敤鎴峰悕锛堝彲閫夛級" />
+ </el-form-item>
+ <el-form-item label="瀵嗙爜">
+ <el-input v-model="interfaceForm.password" type="password" placeholder="璇疯緭鍏ュ瘑鐮侊紙鍙�夛級" show-password />
+ </el-form-item>
+ <el-form-item label="鎻忚堪">
+ <el-input v-model="interfaceForm.description" type="textarea" :rows="3" placeholder="璇疯緭鍏ユ帴鍙f弿杩帮紙鍙�夛級" />
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <el-button @click="interfaceDialogVisible = false">鍙栨秷</el-button>
+ <el-button type="primary" @click="submitInterfaceForm">纭畾</el-button>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import Pagination from '@/components/Pagination/index.vue';
+import {
+ listAlarmPointLedger,
+ addAlarmPoint,
+ updateAlarmPoint,
+ delAlarmPoint,
+ changePointStatus,
+ exportPointLedger,
+ listDataInterface,
+ startDataCollection,
+ stopDataCollection,
+ getCollectionStatistics,
+ addDataInterface,
+ delDataInterface,
+ changeInterfaceStatus
+} from '@/api/alarmManagement/dataCollection';
+
+// 缁熻鏁版嵁
+const statistics = reactive({
+ totalPoints: 0,
+ connectedDevices: 0,
+ todayDataVolume: 0,
+ parseSuccessRate: 0
+});
+
+// 鏌ヨ鍙傛暟
+const queryParams = reactive({
+ pageNum: 1,
+ pageSize: 10,
+ pointName: '',
+ deviceType: ''
+});
+
+const loading = ref(false);
+const pointList = ref([]);
+const total = ref(0);
+
+// 鎺ュ彛鍒楄〃
+const interfaceList = ref([]);
+
+// 鐐逛綅寮圭獥鐩稿叧
+const dialogVisible = ref(false);
+const dialogTitle = ref('');
+const formRef = ref(null);
+const form = reactive({
+ pointId: undefined,
+ pointCode: '',
+ pointName: '',
+ deviceType: '',
+ alarmLevel: '',
+ thresholdValue: '',
+ area: '',
+ status: 1
+});
+
+const rules = {
+ pointCode: [{ required: true, message: '鐐逛綅缂栫爜涓嶈兘涓虹┖', trigger: 'blur' }],
+ pointName: [{ required: true, message: '鐐逛綅鍚嶇О涓嶈兘涓虹┖', trigger: 'blur' }],
+ deviceType: [{ required: true, message: '璁惧绫诲瀷涓嶈兘涓虹┖', trigger: 'change' }],
+ alarmLevel: [{ required: true, message: '鎶ヨ绾у埆涓嶈兘涓虹┖', trigger: 'change' }],
+ thresholdValue: [{ required: true, message: '鎶ヨ闃堝�间笉鑳戒负绌�', trigger: 'blur' }],
+ area: [{ required: true, message: '鎵�灞炲尯鍩熶笉鑳戒负绌�', trigger: 'blur' }],
+ status: [{ required: true, message: '鐘舵�佷笉鑳戒负绌�', trigger: 'change' }]
+};
+
+// 鎺ュ彛寮圭獥鐩稿叧
+const interfaceDialogVisible = ref(false);
+const interfaceDialogTitle = ref('');
+const interfaceFormRef = ref(null);
+const interfaceForm = reactive({
+ interfaceId: undefined,
+ interfaceName: '',
+ interfaceType: '',
+ serverAddress: '',
+ collectFreq: '',
+ username: '',
+ password: '',
+ description: ''
+});
+
+const interfaceRules = {
+ interfaceName: [{ required: true, message: '鎺ュ彛鍚嶇О涓嶈兘涓虹┖', trigger: 'blur' }],
+ interfaceType: [{ required: true, message: '鎺ュ彛绫诲瀷涓嶈兘涓虹┖', trigger: 'change' }],
+ serverAddress: [{ required: true, message: '鏈嶅姟鍣ㄥ湴鍧�涓嶈兘涓虹┖', trigger: 'blur' }],
+ collectFreq: [{ required: true, message: '閲囬泦棰戠巼涓嶈兘涓虹┖', trigger: 'blur' }]
+};
+
+// 鑾峰彇缁熻鏁版嵁
+async function getStatistics() {
+ const res = await getCollectionStatistics();
+ if (res.code === 200) {
+ Object.assign(statistics, res.data);
+ }
+}
+
+// 鑾峰彇鐐逛綅鍒楄〃
+async function getList() {
+ loading.value = true;
+ const res = await listAlarmPointLedger(queryParams);
+ if (res.code === 200) {
+ pointList.value = res.data.records || res.rows;
+ total.value = res.data.total || res.total;
+ }
+ loading.value = false;
+}
+
+// 鑾峰彇鎺ュ彛鍒楄〃
+async function getInterfaceList() {
+ const res = await listDataInterface();
+ if (res.code === 200) {
+ interfaceList.value = res.data;
+ }
+}
+
+function resetQuery() {
+ queryParams.pointName = '';
+ queryParams.deviceType = '';
+ getList();
+}
+
+function handleAdd() {
+ dialogTitle.value = '鏂板鐐逛綅';
+ Object.keys(form).forEach(key => form[key] = key === 'pointId' ? undefined : '');
+ dialogVisible.value = true;
+}
+
+function handleEdit(row) {
+ dialogTitle.value = '缂栬緫鐐逛綅';
+ Object.assign(form, row);
+ dialogVisible.value = true;
+}
+
+async function submitForm() {
+ formRef.value.validate(async (valid) => {
+ if (valid) {
+ const api = form.pointId ? updateAlarmPoint : addAlarmPoint;
+ const res = await api(form);
+ if (res.code === 200) {
+ ElMessage.success(form.pointId ? '淇敼鎴愬姛' : '鏂板鎴愬姛');
+ dialogVisible.value = false;
+ getList();
+ }
+ }
+ });
+}
+
+async function handleDelete(row) {
+ await ElMessageBox.confirm('纭鍒犻櫎璇ョ偣浣嶅悧锛�', '鎻愮ず', { type: 'warning' });
+ const res = await delAlarmPoint(row.pointId);
+ if (res.code === 200) {
+ ElMessage.success('鍒犻櫎鎴愬姛');
+ getList();
+ }
+}
+
+async function handleBeforeStatusChange(row) {
+ // 杩斿洖 true 鍏佽鍒囨崲锛宖alse 闃绘鍒囨崲
+ try {
+ const newStatus = row.status === 1 ? 0 : 1;
+ const res = await changePointStatus(row.pointId, newStatus);
+ if (res.code === 200) {
+ ElMessage.success('鐘舵�佷慨鏀规垚鍔�');
+ return true;
+ }
+ return false;
+ } catch (error) {
+ return false;
+ }
+}
+
+async function handleStatusChange(row, val) {
+ // 鐘舵�佸凡缁忛�氳繃 before-change 鏇存柊锛岃繖閲屽彧鍋氬埛鏂�
+ getList();
+}
+
+async function handleExport() {
+ await exportPointLedger(queryParams);
+ ElMessage.success('瀵煎嚭鎴愬姛');
+}
+
+function handleAddInterface() {
+ interfaceDialogTitle.value = '鏂板鎺ュ彛';
+ Object.keys(interfaceForm).forEach(key => interfaceForm[key] = key === 'interfaceId' ? undefined : '');
+ interfaceDialogVisible.value = true;
+}
+
+function handleEditInterface(row) {
+ interfaceDialogTitle.value = '缂栬緫鎺ュ彛';
+ Object.assign(interfaceForm, row);
+ interfaceDialogVisible.value = true;
+}
+
+async function submitInterfaceForm() {
+ interfaceFormRef.value.validate(async (valid) => {
+ if (valid) {
+ const res = await addDataInterface(interfaceForm);
+ if (res.code === 200) {
+ ElMessage.success(interfaceForm.interfaceId ? '淇敼鎴愬姛' : '鏂板鎴愬姛');
+ interfaceDialogVisible.value = false;
+ getInterfaceList();
+ }
+ }
+ });
+}
+
+async function handleDeleteInterface(row) {
+ await ElMessageBox.confirm('纭鍒犻櫎璇ユ帴鍙e悧锛�', '鎻愮ず', { type: 'warning' });
+ const res = await delDataInterface(row.interfaceId);
+ if (res.code === 200) {
+ ElMessage.success('鍒犻櫎鎴愬姛');
+ getInterfaceList();
+ }
+}
+
+async function handleInterfaceStatusChange(row) {
+ const res = await changeInterfaceStatus(row.interfaceId, row.status);
+ if (res.code === 200) {
+ ElMessage.success('鐘舵�佷慨鏀规垚鍔�');
+ }
+}
+
+async function handleStart(row) {
+ const res = await startDataCollection(row.interfaceId);
+ if (res.code === 200) {
+ ElMessage.success('鍚姩鎴愬姛');
+ getInterfaceList();
+ }
+}
+
+async function handleStop(row) {
+ const res = await stopDataCollection(row.interfaceId);
+ if (res.code === 200) {
+ ElMessage.success('鍋滄鎴愬姛');
+ getInterfaceList();
+ }
+}
+
+onMounted(() => {
+ getStatistics();
+ getList();
+ getInterfaceList();
+});
+</script>
+
+<style scoped>
+.stat-row {
+ margin-bottom: 20px;
+}
+.stat-row :deep(.el-statistic__content) {
+ font-size: 28px;
+ font-weight: 600;
+ color: #303133;
+}
+.stat-row :deep(.el-statistic__title) {
+ font-size: 14px;
+ color: #606266;
+ margin-bottom: 8px;
+}
+.card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+</style>
--
Gitblit v1.9.3