浪潮
1.添加数据采集与解析、报警分析评估、报警优化管理三个页面,前端页面开发并联调
已添加6个文件
1670 ■■■■■ 文件已修改
src/api/alarmManagement/alarmAnalysis.js 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/alarmManagement/alarmOptimization.js 141 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/alarmManagement/dataCollection.js 163 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/alarmManagement/alarmAnalysis/index.vue 374 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/alarmManagement/alarmOptimization/index.vue 458 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/alarmManagement/dataCollection/index.vue 457 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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
  });
}
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'
  });
}
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'
  });
}
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>
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="请输入规则配置JSON" />
        </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>
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="解析成功率(%)" :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="请输入接口名称" />
        </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="请输入接口描述(可选)" />
        </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 å…è®¸åˆ‡æ¢ï¼Œfalse é˜»æ­¢åˆ‡æ¢
  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('确认删除该接口吗?', '提示', { 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>