Forráskód Böngészése

新增告警记录

yawuga 11 hónapja
szülő
commit
22384e8427
1 módosított fájl, 917 hozzáadás és 264 törlés
  1. 917 264
      src/views/base/record/index.vue

+ 917 - 264
src/views/base/record/index.vue

@@ -4,7 +4,7 @@
       <el-form-item label="设备编号" prop="deviceId">
         <el-input
           v-model="queryParams.deviceId"
-          placeholder="请输入设备编号"
+          placeholder="请输入设备编号关键词"
           clearable
           @keyup.enter.native="handleQuery"
         />
@@ -12,13 +12,23 @@
       <el-form-item label="设备名称" prop="deviceName">
         <el-input
           v-model="queryParams.deviceName"
-          placeholder="请输入设备名称"
+          placeholder="请输入设备名称关键词"
           clearable
           @keyup.enter.native="handleQuery"
         />
       </el-form-item>
+      <el-form-item label="告警等级" prop="alertLevel">
+        <el-select v-model="queryParams.alertLevel" placeholder="所有告警等级" clearable>
+          <el-option
+            v-for="dict in dict.type.alert_level"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
+        </el-select>
+      </el-form-item>
       <el-form-item label="设备类型" prop="deviceType">
-        <el-select v-model="queryParams.deviceType" placeholder="请选择设备类型" clearable>
+        <el-select v-model="queryParams.deviceType" placeholder="所有设备类型" clearable>
           <el-option
             v-for="dict in dict.type.alert_device_type"
             :key="dict.value"
@@ -27,10 +37,20 @@
           />
         </el-select>
       </el-form-item>
-      <el-form-item label="告警级别" prop="alertLevel">
-        <el-select v-model="queryParams.alertLevel" placeholder="请选择告警级别" clearable>
+      <el-form-item label="所属地块" prop="fieldId">
+        <el-select v-model="queryParams.fieldId" placeholder="所有地块" clearable>
           <el-option
-            v-for="dict in dict.type.alert_level"
+            v-for="dict in dict.type.field_list"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="所属农场" prop="farmId">
+        <el-select v-model="queryParams.farmId" placeholder="所有农场" clearable>
+          <el-option
+            v-for="dict in dict.type.farm_list"
             :key="dict.value"
             :label="dict.label"
             :value="dict.value"
@@ -43,13 +63,13 @@
           style="width: 240px"
           value-format="yyyy-MM-dd"
           type="daterange"
-          range-separator="-"
-          start-placeholder="开始日期"
-          end-placeholder="结束日期"
+          range-separator=""
+          start-placeholder="年/月/日"
+          end-placeholder="年/月/日"
         ></el-date-picker>
       </el-form-item>
       <el-form-item label="处理状态" prop="processStatus">
-        <el-select v-model="queryParams.processStatus" placeholder="请选择处理状态" clearable>
+        <el-select v-model="queryParams.processStatus" placeholder="所有状态" clearable>
           <el-option
             v-for="dict in dict.type.alert_process_status"
             :key="dict.value"
@@ -58,53 +78,71 @@
           />
         </el-select>
       </el-form-item>
-      <el-form-item label="所属农场" prop="farmId">
-        <el-input
-          v-model="queryParams.farmId"
-          placeholder="请输入所属农场"
-          clearable
-          @keyup.enter.native="handleQuery"
-        />
-      </el-form-item>
       <el-form-item>
         <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
 
+    <!-- 告警统计面板 -->
+    <div class="statistics-panel">
+      <el-row :gutter="20">
+        <el-col :span="6">
+          <div class="stat-card total">
+            <div class="stat-header">
+              <span class="stat-title">今日告警总数</span>
+            </div>
+            <div class="stat-content">
+              <div class="stat-number">{{ todayStatistics.total }}</div>
+              <div class="stat-change" :class="todayStatistics.change >= 0 ? 'increase' : 'decrease'">
+                <span>较昨日 {{ todayStatistics.change >= 0 ? '+' : '' }}{{ todayStatistics.change }}</span>
+              </div>
+            </div>
+          </div>
+        </el-col>
+        <el-col :span="6">
+          <div class="stat-card emergency">
+            <div class="stat-header">
+              <span class="stat-title">紧急告警</span>
+            </div>
+                         <div class="stat-content">
+               <div class="stat-number">{{ levelStatistics.emergency.total }}</div>
+               <div class="stat-detail">
+                 <span>未处理: {{ levelStatistics.emergency.unprocessed }}</span>
+               </div>
+             </div>
+          </div>
+        </el-col>
+        <el-col :span="6">
+          <div class="stat-card warning">
+            <div class="stat-header">
+              <span class="stat-title">警告</span>
+            </div>
+                         <div class="stat-content">
+               <div class="stat-number">{{ levelStatistics.warning.total }}</div>
+               <div class="stat-detail">
+                 <span>未处理: {{ levelStatistics.warning.unprocessed }}</span>
+               </div>
+             </div>
+          </div>
+        </el-col>
+        <el-col :span="6">
+          <div class="stat-card info">
+            <div class="stat-header">
+              <span class="stat-title">提示信息</span>
+            </div>
+                         <div class="stat-content">
+               <div class="stat-number">{{ levelStatistics.info.total }}</div>
+               <div class="stat-detail">
+                 <span>未处理: {{ levelStatistics.info.unprocessed }}</span>
+               </div>
+             </div>
+          </div>
+        </el-col>
+      </el-row>
+    </div>
+
     <el-row :gutter="10" class="mb8">
-      <el-col :span="1.5">
-        <el-button
-          type="primary"
-          plain
-          icon="el-icon-plus"
-          size="mini"
-          @click="handleAdd"
-          v-hasPermi="['base:record:add']"
-        >新增</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-          type="success"
-          plain
-          icon="el-icon-edit"
-          size="mini"
-          :disabled="single"
-          @click="handleUpdate"
-          v-hasPermi="['base:record:edit']"
-        >修改</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-          type="danger"
-          plain
-          icon="el-icon-delete"
-          size="mini"
-          :disabled="multiple"
-          @click="handleDelete"
-          v-hasPermi="['base:record:remove']"
-        >删除</el-button>
-      </el-col>
       <el-col :span="1.5">
         <el-button
           type="warning"
@@ -118,21 +156,22 @@
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <el-table v-loading="loading" :data="recordList" @selection-change="handleSelectionChange">
-      <el-table-column type="selection" width="55" align="center" />
+    <el-table v-loading="loading" :data="recordList">
       <el-table-column label="告警ID" align="center" prop="alertId" />
-      <el-table-column label="设备编号" align="center" prop="deviceId" />
-      <el-table-column label="设备名称" align="center" prop="deviceName" />
-      <el-table-column label="设备类型" align="center" prop="deviceType">
+      <el-table-column label="告警等级" align="center" prop="alertLevel">
         <template slot-scope="scope">
-          <dict-tag :options="dict.type.alert_device_type" :value="scope.row.deviceType"/>
+          <dict-tag :options="dict.type.alert_level" :value="scope.row.alertLevel"/>
         </template>
       </el-table-column>
-      <el-table-column label="告警级别" align="center" prop="alertLevel">
+      <el-table-column label="设备名称" align="center" prop="deviceName" />
+      <el-table-column label="设备编号" align="center" prop="deviceId" />
+      <el-table-column label="设备类型" align="center" prop="deviceType">
         <template slot-scope="scope">
-          <dict-tag :options="dict.type.alert_level" :value="scope.row.alertLevel"/>
+          <dict-tag :options="dict.type.alert_device_type" :value="scope.row.deviceType"/>
         </template>
       </el-table-column>
+      <el-table-column label="所属地块" align="center" prop="fieldId" />
+      <el-table-column label="所属农场" align="center" prop="farmId" />
       <el-table-column label="告警内容" align="center" prop="alertContent" />
       <el-table-column label="告警时间" align="center" prop="alertTime" width="180">
         <template slot-scope="scope">
@@ -144,29 +183,28 @@
           <dict-tag :options="dict.type.alert_process_status" :value="scope.row.processStatus"/>
         </template>
       </el-table-column>
+      <el-table-column label="处理人" align="center" prop="processor" />
       <el-table-column label="处理时间" align="center" prop="processTime" width="180">
         <template slot-scope="scope">
           <span>{{ parseTime(scope.row.processTime, '{y}-{m}-{d}') }}</span>
         </template>
       </el-table-column>
-      <el-table-column label="处理人" align="center" prop="processor" />
-      <el-table-column label="所属农场" align="center" prop="farmId" />
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
         <template slot-scope="scope">
           <el-button
             size="mini"
             type="text"
-            icon="el-icon-edit"
-            @click="handleUpdate(scope.row)"
-            v-hasPermi="['base:record:edit']"
-          >修改</el-button>
+            icon="el-icon-view"
+            @click="handleDetail(scope.row)"
+          >查看</el-button>
           <el-button
+            v-if="scope.row.processStatus == 0 || scope.row.processStatus == '0'"
             size="mini"
             type="text"
-            icon="el-icon-delete"
-            @click="handleDelete(scope.row)"
-            v-hasPermi="['base:record:remove']"
-          >删除</el-button>
+            icon="el-icon-edit"
+            @click="handleProcess(scope.row)"
+            v-hasPermi="['base:record:edit']"
+          >处理</el-button>
         </template>
       </el-table-column>
     </el-table>
@@ -179,120 +217,181 @@
       @pagination="getList"
     />
 
-    <!-- 添加或修改告警记录对话框 -->
-    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
-      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
-        <el-form-item label="设备编号" prop="deviceId">
-          <el-input v-model="form.deviceId" placeholder="请输入设备编号" />
-        </el-form-item>
-        <el-form-item label="设备名称" prop="deviceName">
-          <el-input v-model="form.deviceName" placeholder="请输入设备名称" />
-        </el-form-item>
-        <el-form-item label="设备类型" prop="deviceType">
-          <el-select v-model="form.deviceType" placeholder="请选择设备类型">
-            <el-option
-              v-for="dict in dict.type.alert_device_type"
-              :key="dict.value"
-              :label="dict.label"
-              :value="dict.value"
-            ></el-option>
-          </el-select>
+    <!-- 告警详情对话框 -->
+    <el-dialog title="告警详情" :visible.sync="detailOpen" width="600px" append-to-body>
+      <div class="alert-detail">
+        <!-- 主要告警信息卡片 -->
+        <div class="alert-card">
+          <!-- 头部信息 -->
+          <div class="alert-header">
+            <div class="alert-level-tag">
+              <dict-tag :options="dict.type.alert_level" :value="currentRecord.alertLevel" />
+            </div>
+            <div class="alert-time">{{ parseTime(currentRecord.alertTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</div>
+          </div>
+          
+          <!-- 告警内容 -->
+          <div class="alert-content">
+            {{ getAlertContent(currentRecord) }}
+          </div>
+        </div>
+        
+        <!-- 设备信息 -->
+        <div class="section">
+          <div class="section-title">设备信息</div>
+          <div class="device-info">
+            <div class="info-grid">
+              <div class="info-item">
+                <span class="label">设备名称</span>
+                <span class="value">{{ currentRecord.deviceName }}</span>
+              </div>
+              <div class="info-item">
+                <span class="label">设备编号</span>
+                <span class="value">{{ currentRecord.deviceId }}</span>
+              </div>
+              <div class="info-item">
+                <span class="label">设备类型</span>
+                <span class="value">
+                  <dict-tag :options="dict.type.alert_device_type" :value="currentRecord.deviceType" />
+                </span>
+              </div>
+              <div class="info-item">
+                <span class="label">所属地块</span>
+                <span class="value">{{ currentRecord.fieldId }}</span>
+              </div>
+              <div class="info-item">
+                <span class="label">所属农场</span>
+                <span class="value">{{ currentRecord.farmId }}</span>
+              </div>
+            </div>
+          </div>
+        </div>
+        
+        <!-- 处理状态 -->
+        <div class="section">
+          <div class="section-title">处理状态</div>
+          <div class="status-info">
+            <div class="status-item">
+              <span class="label">当前状态</span>
+              <span class="value status">
+                <dict-tag :options="dict.type.alert_process_status" :value="currentRecord.processStatus" />
+              </span>
+            </div>
+            <div class="status-item">
+              <span class="label">处理人</span>
+              <span class="value">{{ currentRecord.processor || '-' }}</span>
+            </div>
+            <div class="status-item">
+              <span class="label">处理时间</span>
+              <span class="value">{{ currentRecord.processTime || '-' }}</span>
+            </div>
+            <div class="status-item full-width">
+              <span class="label">处理记录</span>
+              <div class="process-record">
+                {{ currentRecord.processComment || '暂无处理记录' }}
+              </div>
+            </div>
+          </div>
+        </div>
+        
+        <!-- 附件信息 -->
+        <div class="section">
+          <div class="section-title">附件信息</div>
+          <div class="attachment-info">
+            <div v-if="currentRecord.attachments && currentRecord.attachments.length > 0" class="attachment-list">
+              <div v-for="(attachment, index) in getAttachmentList(currentRecord.attachments)" :key="index" class="attachment-item">
+                <div class="attachment-preview">
+                  <i v-if="isImage(attachment.name)" class="el-icon-picture-outline"></i>
+                  <i v-else class="el-icon-document"></i>
+                </div>
+                <div class="attachment-details">
+                  <div class="attachment-name" :title="attachment.name">{{ attachment.name }}</div>
+                  <div class="attachment-size">{{ formatFileSize(attachment.size) }}</div>
+                </div>
+                <div class="attachment-actions">
+                  <el-button size="mini" type="text" @click="downloadAttachment(attachment)">下载</el-button>
+                  <el-button v-if="isImage(attachment.name)" size="mini" type="text" @click="previewImage(attachment)">预览</el-button>
+                </div>
+              </div>
+            </div>
+            <div v-else class="no-attachment">
+              <i class="el-icon-folder-opened"></i>
+              <span>暂无附件</span>
+            </div>
+          </div>
+        </div>
+      </div>
+      
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="detailOpen = false">关闭</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 处理告警对话框 -->
+    <el-dialog title="处理告警" :visible.sync="processOpen" width="500px" append-to-body>
+      <el-form ref="processForm" :model="processForm" :rules="processRules" label-width="80px">
+        <el-form-item label="告警信息">
+                     <div class="process-alert-info">
+             <div class="info-item">
+               <span class="label">设备编号:</span>
+               <span class="value">{{ processForm.deviceId }}</span>
+             </div>
+             <div class="info-item">
+               <span class="label">设备名称:</span>
+               <span class="value">{{ processForm.deviceName }}</span>
+             </div>
+            <div class="alert-content-preview">
+              {{ getAlertContent(processForm) }}
+            </div>
+          </div>
         </el-form-item>
-        <el-form-item label="告警级别" prop="alertLevel">
-          <el-select v-model="form.alertLevel" placeholder="请选择告警级别">
-            <el-option
-              v-for="dict in dict.type.alert_level"
-              :key="dict.value"
-              :label="dict.label"
-              :value="parseInt(dict.value)"
-            ></el-option>
+        <el-form-item label="处理结果" prop="processStatus">
+          <el-select v-model="processForm.processStatus" placeholder="请选择处理结果">
+            <el-option label="未处理" :value="0"></el-option>
+            <el-option label="已处理" :value="1"></el-option>
+            <el-option label="忽略" :value="2"></el-option>
           </el-select>
         </el-form-item>
-        <el-form-item label="告警参数名称" prop="parameterName">
-          <el-input v-model="form.parameterName" placeholder="请输入告警参数名称" />
-        </el-form-item>
-        <el-form-item label="告警时参数值" prop="parameterValue">
-          <el-input v-model="form.parameterValue" placeholder="请输入告警时参数值" />
-        </el-form-item>
-        <el-form-item label="参数单位" prop="parameterUnit">
-          <el-input v-model="form.parameterUnit" placeholder="请输入参数单位" />
-        </el-form-item>
-        <el-form-item label="阈值" prop="thresholdValue">
-          <el-input v-model="form.thresholdValue" placeholder="请输入阈值" />
-        </el-form-item>
-        <el-form-item label="告警内容">
-          <editor v-model="form.alertContent" :min-height="192"/>
-        </el-form-item>
-        <el-form-item label="告警时间" prop="alertTime">
-          <el-date-picker clearable
-            v-model="form.alertTime"
-            type="date"
-            value-format="yyyy-MM-dd"
-            placeholder="请选择告警时间">
-          </el-date-picker>
-        </el-form-item>
-        <el-form-item label="处理状态" prop="processStatus">
-          <el-radio-group v-model="form.processStatus">
-            <el-radio
-              v-for="dict in dict.type.alert_process_status"
-              :key="dict.value"
-              :label="parseInt(dict.value)"
-            >{{dict.label}}</el-radio>
-          </el-radio-group>
-        </el-form-item>
-        <el-form-item label="处理时间" prop="processTime">
-          <el-date-picker clearable
-            v-model="form.processTime"
-            type="date"
-            value-format="yyyy-MM-dd"
-            placeholder="请选择处理时间">
-          </el-date-picker>
-        </el-form-item>
-        <el-form-item label="处理人" prop="processor">
-          <el-input v-model="form.processor" placeholder="请输入处理人" />
-        </el-form-item>
-        <el-form-item label="处理说明" prop="processComment">
-          <el-input v-model="form.processComment" type="textarea" placeholder="请输入内容" />
+        <el-form-item label="处理备注" prop="processComment">
+          <el-input 
+            v-model="processForm.processComment" 
+            type="textarea" 
+            :rows="4"
+            placeholder="请输入处理备注..."
+            maxlength="500"
+            show-word-limit
+          />
         </el-form-item>
-        <el-form-item label="所属农场" prop="farmId">
-          <el-input v-model="form.farmId" placeholder="请输入所属农场" />
+        <el-form-item label="附件上传">
+          <file-upload v-model="processForm.attachments" :limit="5" />
         </el-form-item>
       </el-form>
       <div slot="footer" class="dialog-footer">
-        <el-button type="primary" @click="submitForm">确 定</el-button>
-        <el-button @click="cancel">取 消</el-button>
+        <el-button type="primary" @click="submitProcess" :loading="processLoading">确 定</el-button>
+        <el-button @click="cancelProcess">取 消</el-button>
       </div>
     </el-dialog>
+
   </div>
 </template>
 
 <script>
-import { listRecord, getRecord, delRecord, addRecord, updateRecord } from "@/api/base/record"
+import { listRecord, getRecord, updateRecord } from "@/api/base/record"
 
 export default {
   name: "Record",
-  dicts: ['alert_process_status', 'alert_level', 'alert_device_type'],
+  dicts: ['alert_process_status', 'alert_level', 'alert_device_type', 'field_list', 'farm_list'],
   data() {
     return {
       // 遮罩层
       loading: true,
-      // 选中数组
-      ids: [],
-      // 非单个禁用
-      single: true,
-      // 非多个禁用
-      multiple: true,
       // 显示搜索条件
       showSearch: true,
       // 总条数
       total: 0,
       // 告警记录表格数据
       recordList: [],
-      // 弹出层标题
-      title: "",
-      // 是否显示弹出层
-      open: false,
-      // 所属农场时间范围
+      // 告警时间范围
       daterangeAlertTime: [],
       // 查询参数
       queryParams: {
@@ -305,46 +404,48 @@ export default {
         alertTime: null,
         processStatus: null,
         farmId: null,
+        fieldId: null,
+      },
+      // 告警详情对话框
+      detailOpen: false,
+      // 当前记录
+      currentRecord: {},
+      // 处理告警对话框
+      processOpen: false,
+      // 处理表单
+      processForm: {},
+      // 处理规则
+      processRules: {
+        processStatus: [{ required: true, message: '请选择处理结果', trigger: 'change' }],
+        processComment: [{ required: true, message: '请输入处理备注', trigger: 'blur' }],
       },
-      // 表单参数
-      form: {},
-      // 表单校验
-      rules: {
-        deviceId: [
-          { required: true, message: "设备编号不能为空", trigger: "blur" }
-        ],
-        deviceName: [
-          { required: true, message: "设备名称不能为空", trigger: "blur" }
-        ],
-        deviceType: [
-          { required: true, message: "设备类型不能为空", trigger: "change" }
-        ],
-        alertLevel: [
-          { required: true, message: "告警级别不能为空", trigger: "change" }
-        ],
-        alertContent: [
-          { required: true, message: "告警内容不能为空", trigger: "blur" }
-        ],
-        alertTime: [
-          { required: true, message: "告警时间不能为空", trigger: "blur" }
-        ],
-        processStatus: [
-          { required: true, message: "处理状态不能为空", trigger: "change" }
-        ],
-        farmId: [
-          { required: true, message: "所属农场不能为空", trigger: "blur" }
-        ],
-        createTime: [
-          { required: true, message: "创建时间不能为空", trigger: "blur" }
-        ],
-        updateTime: [
-          { required: true, message: "更新时间不能为空", trigger: "blur" }
-        ]
+      // 处理按钮加载中
+      processLoading: false,
+      // 今日统计数据
+      todayStatistics: {
+        total: 0,
+        change: 0
+      },
+      // 各等级统计数据
+      levelStatistics: {
+        emergency: {
+          total: 0,
+          unprocessed: 0
+        },
+        warning: {
+          total: 0,
+          unprocessed: 0
+        },
+        info: {
+          total: 0,
+          unprocessed: 0
+        }
       }
     }
   },
   created() {
     this.getList()
+    this.getStatistics()
   },
   methods: {
     /** 查询告警记录列表 */
@@ -359,36 +460,78 @@ export default {
         this.recordList = response.rows
         this.total = response.total
         this.loading = false
+        // 获取列表后更新统计数据
+        this.updateStatisticsFromList()
       })
     },
-    // 取消按钮
-    cancel() {
-      this.open = false
-      this.reset()
+    /** 获取统计数据 */
+    getStatistics() {
+      // 这里可以调用专门的统计API
+      // 暂时使用模拟数据
+      this.todayStatistics = {
+        total: 24,
+        change: 8
+      }
+      this.levelStatistics = {
+        emergency: {
+          total: 5,
+          unprocessed: 2
+        },
+        warning: {
+          total: 12,
+          unprocessed: 4
+        },
+        info: {
+          total: 7,
+          unprocessed: 1
+        }
+      }
     },
-    // 表单重置
-    reset() {
-      this.form = {
-        alertId: null,
-        deviceId: null,
-        deviceName: null,
-        deviceType: null,
-        alertLevel: null,
-        parameterName: null,
-        parameterValue: null,
-        parameterUnit: null,
-        thresholdValue: null,
-        alertContent: null,
-        alertTime: null,
-        processStatus: null,
-        processTime: null,
-        processor: null,
-        processComment: null,
-        farmId: null,
-        createTime: null,
-        updateTime: null
+    /** 从列表数据更新统计 */
+    updateStatisticsFromList() {
+      if (!this.recordList || this.recordList.length === 0) {
+        return
+      }
+      
+      // 重置统计数据
+      this.levelStatistics = {
+        emergency: { total: 0, unprocessed: 0 },
+        warning: { total: 0, unprocessed: 0 },
+        info: { total: 0, unprocessed: 0 }
+      }
+      
+      // 今日日期
+      const today = new Date().toISOString().slice(0, 10)
+      let todayCount = 0
+      
+      // 遍历记录统计
+      this.recordList.forEach(record => {
+        // 统计今日告警
+        if (record.alertTime && record.alertTime.includes(today)) {
+          todayCount++
+        }
+        
+        // 统计各等级告警
+        const level = record.alertLevel
+        const isUnprocessed = record.processStatus == 0 || record.processStatus == '0'
+        
+        // 根据告警等级统计 (假设: 1-紧急, 2-警告, 3-提示)
+        if (level == 1 || level == '1') {
+          this.levelStatistics.emergency.total++
+          if (isUnprocessed) this.levelStatistics.emergency.unprocessed++
+        } else if (level == 2 || level == '2') {
+          this.levelStatistics.warning.total++
+          if (isUnprocessed) this.levelStatistics.warning.unprocessed++
+        } else if (level == 3 || level == '3') {
+          this.levelStatistics.info.total++
+          if (isUnprocessed) this.levelStatistics.info.unprocessed++
+        }
+      })
+      
+      // 更新今日统计 (这里只是示例,实际应该从后端获取完整统计)
+      if (todayCount > 0) {
+        this.todayStatistics.total = todayCount
       }
-      this.resetForm("form")
     },
     /** 搜索按钮操作 */
     handleQuery() {
@@ -401,64 +544,574 @@ export default {
       this.resetForm("queryForm")
       this.handleQuery()
     },
-    // 多选框选中数据
-    handleSelectionChange(selection) {
-      this.ids = selection.map(item => item.alertId)
-      this.single = selection.length!==1
-      this.multiple = !selection.length
+    /** 导出按钮操作 */
+    handleExport() {
+      this.download('base/record/export', {
+        ...this.queryParams
+      }, `record_${new Date().getTime()}.xlsx`)
+    },
+    /** 查看告警详情 */
+    handleDetail(record) {
+      // 可以根据需要调用API获取更详细的信息
+      // getRecord(record.alertId).then(response => {
+      //   this.currentRecord = response.data
+      //   this.detailOpen = true
+      // })
+      
+      // 当前直接使用列表数据显示
+      this.currentRecord = { ...record }
+      this.detailOpen = true
     },
-    /** 新增按钮操作 */
-    handleAdd() {
-      this.reset()
-      this.open = true
-      this.title = "添加告警记录"
+    /** 获取告警标题 */
+    getAlertTitle(record) {
+      // 优先显示设备名称
+      if (record.deviceName) {
+        return record.deviceName
+      }
+      // 如果没有设备名称,显示设备编号
+      if (record.deviceId) {
+        return record.deviceId
+      }
+      // 最后的默认标题
+      return '未知设备'
     },
-    /** 修改按钮操作 */
-    handleUpdate(row) {
-      this.reset()
-      const alertId = row.alertId || this.ids
-      getRecord(alertId).then(response => {
-        this.form = response.data
-        this.open = true
-        this.title = "修改告警记录"
-      })
+    /** 获取告警内容 */
+    getAlertContent(record) {
+      if (!record.alertContent) {
+        return '暂无告警内容'
+      }
+      
+      let content = record.alertContent
+      
+      // 尝试去掉设备名称前缀
+      // 匹配模式:设备XXX + 告警内容
+      const devicePatterns = [
+        // 匹配"设备D1001温度异常"格式
+        /^设备[A-Za-z0-9]+(.+)$/,
+        // 匹配"D1001温度异常"格式  
+        /^[A-Za-z0-9]+(.+)$/,
+        // 匹配设备名称开头的格式
+        /^.+?号(.+)$/,
+        /^.+?站(.+)$/
+      ]
+      
+      // 如果告警内容包含设备信息,尝试去掉设备前缀
+      if (record.deviceId && content.includes(record.deviceId)) {
+        // 去掉设备编号前缀
+        content = content.replace(new RegExp(`^.*?${record.deviceId}`, ''), '')
+      } else if (record.deviceName && content.includes(record.deviceName)) {
+        // 去掉设备名称前缀
+        content = content.replace(new RegExp(`^.*?${record.deviceName}`, ''), '')
+      } else {
+        // 使用通用模式尝试去掉设备前缀
+        for (let pattern of devicePatterns) {
+          const match = content.match(pattern)
+          if (match && match[1]) {
+            content = match[1]
+            break
+          }
+        }
+      }
+      
+      // 清理开头的标点符号
+      content = content.replace(/^[::,,\s]+/, '')
+      
+      return content || record.alertContent
     },
-    /** 提交按钮 */
-    submitForm() {
-      this.$refs["form"].validate(valid => {
+    /** 获取附件列表 */
+    getAttachmentList(attachments) {
+      if (!attachments) return []
+      // 如果attachments是字符串,尝试解析为JSON
+      if (typeof attachments === 'string') {
+        try {
+          return JSON.parse(attachments)
+        } catch (e) {
+          // 如果解析失败,按逗号分割处理
+          return attachments.split(',').map(url => ({
+            name: this.getFileNameFromUrl(url),
+            url: url,
+            size: 0
+          }))
+        }
+      }
+      // 如果是数组直接返回
+      return Array.isArray(attachments) ? attachments : []
+    },
+    /** 从URL中提取文件名 */
+    getFileNameFromUrl(url) {
+      if (!url) return '未知文件'
+      const parts = url.split('/')
+      return parts[parts.length - 1] || '未知文件'
+    },
+    /** 判断是否为图片 */
+    isImage(fileName) {
+      if (!fileName) return false
+      const imageExts = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp']
+      const ext = fileName.toLowerCase().substring(fileName.lastIndexOf('.'))
+      return imageExts.includes(ext)
+    },
+    /** 格式化文件大小 */
+    formatFileSize(size) {
+      if (!size || size === 0) return '未知大小'
+      if (size < 1024) return size + ' B'
+      if (size < 1024 * 1024) return (size / 1024).toFixed(1) + ' KB'
+      return (size / (1024 * 1024)).toFixed(1) + ' MB'
+    },
+    /** 下载附件 */
+    downloadAttachment(attachment) {
+      if (attachment.url) {
+        window.open(attachment.url, '_blank')
+      } else {
+        this.$modal.msgError('附件链接无效')
+      }
+    },
+    /** 预览图片 */
+    previewImage(attachment) {
+      if (attachment.url) {
+        this.$modal.msgInfo('图片预览功能暂未实现,请点击下载查看')
+        // 这里可以集成图片预览组件
+        // this.$imagePreview([attachment.url])
+      }
+    },
+    /** 处理告警 */
+    handleProcess(record) {
+      this.resetProcessForm()
+      this.currentRecord = { ...record }
+      this.processForm = {
+        alertId: record.alertId,
+        deviceId: record.deviceId,
+        deviceName: record.deviceName,
+        deviceType: record.deviceType,
+        fieldId: record.fieldId,
+        farmId: record.farmId,
+        alertContent: record.alertContent,
+        alertLevel: record.alertLevel,
+        alertTime: record.alertTime,
+        processStatus: 0, // 默认设为未处理,用户可以修改
+        processComment: '',
+        attachments: ''
+      }
+      this.processOpen = true
+    },
+    /** 提交处理 */
+    submitProcess() {
+      this.$refs.processForm.validate((valid) => {
         if (valid) {
-          if (this.form.alertId != null) {
-            updateRecord(this.form).then(response => {
-              this.$modal.msgSuccess("修改成功")
-              this.open = false
-              this.getList()
-            })
-          } else {
-            addRecord(this.form).then(response => {
-              this.$modal.msgSuccess("新增成功")
-              this.open = false
-              this.getList()
-            })
+          this.processLoading = true
+          
+          // 准备要更新的数据
+          const updateData = {
+            alertId: this.processForm.alertId,
+            processStatus: this.processForm.processStatus,
+            processComment: this.processForm.processComment,
+            attachments: this.processForm.attachments
           }
+          
+          updateRecord(updateData).then(response => {
+            this.$modal.msgSuccess("处理成功")
+            this.processOpen = false
+            this.processLoading = false
+            this.resetProcessForm()
+            // 刷新列表
+            this.getList()
+          }).catch(() => {
+            this.processLoading = false
+          })
         }
       })
     },
-    /** 删除按钮操作 */
-    handleDelete(row) {
-      const alertIds = row.alertId || this.ids
-      this.$modal.confirm('是否确认删除告警记录编号为"' + alertIds + '"的数据项?').then(function() {
-        return delRecord(alertIds)
-      }).then(() => {
-        this.getList()
-        this.$modal.msgSuccess("删除成功")
-      }).catch(() => {})
+    /** 取消处理 */
+    cancelProcess() {
+      this.processOpen = false
+      this.resetProcessForm()
     },
-    /** 导出按钮操作 */
-    handleExport() {
-      this.download('base/record/export', {
-        ...this.queryParams
-      }, `record_${new Date().getTime()}.xlsx`)
+    /** 重置处理表单 */
+    resetProcessForm() {
+      this.processForm = {
+        alertId: null,
+        deviceId: null,
+        deviceName: null,
+        deviceType: null,
+        fieldId: null,
+        farmId: null,
+        alertContent: null,
+        alertLevel: null,
+        alertTime: null,
+        processStatus: 0,
+        processComment: null,
+        attachments: null
+      }
+      if (this.$refs.processForm) {
+        this.$refs.processForm.resetFields()
+      }
     }
   }
 }
 </script>
+
+<style scoped>
+.alert-detail {
+  padding: 10px;
+}
+
+.alert-card {
+  background-color: #f8f9fa;
+  border-radius: 8px;
+  padding: 20px;
+  margin-bottom: 20px;
+  border: 1px solid #e9ecef;
+  box-shadow: 0 2px 4px rgba(0,0,0,0.05);
+  border-left: 4px solid #f56c6c;
+}
+
+.alert-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 15px;
+  padding-bottom: 12px;
+  border-bottom: 1px solid #e9ecef;
+}
+
+.alert-level-tag {
+  font-weight: bold;
+}
+
+.alert-time {
+  color: #909399;
+  font-size: 14px;
+}
+
+.alert-content {
+  color: #606266;
+  line-height: 1.6;
+  font-size: 14px;
+}
+
+.section {
+  margin-bottom: 20px;
+}
+
+.section-title {
+  font-size: 16px;
+  font-weight: bold;
+  color: #303133;
+  margin-bottom: 15px;
+  padding-bottom: 8px;
+  border-bottom: 1px solid #ebeef5;
+}
+
+.device-info .info-grid {
+  display: grid;
+  grid-template-columns: 1fr 1fr 1fr;
+  gap: 15px;
+}
+
+.info-item {
+  display: flex;
+  flex-direction: column;
+}
+
+.info-item .label {
+  color: #909399;
+  font-size: 14px;
+  margin-bottom: 5px;
+}
+
+.info-item .value {
+  color: #303133;
+  font-weight: 500;
+  font-size: 15px;
+}
+
+.status-info {
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  gap: 15px;
+}
+
+.status-item {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+}
+
+.status-item.full-width {
+  grid-column: 1 / -1;
+  flex-direction: column;
+  align-items: flex-start;
+  gap: 8px;
+}
+
+.status-item .label {
+  color: #606266;
+  font-size: 14px;
+  font-weight: 500;
+  min-width: 80px;
+  flex-shrink: 0;
+}
+
+.status-item .value {
+  color: #303133;
+  font-weight: 600;
+  flex: 1;
+}
+
+.status-item .value.status {
+  display: flex;
+  align-items: center;
+}
+
+.process-record {
+  color: #606266;
+  font-size: 14px;
+  padding: 12px;
+  background-color: #f8f9fa;
+  border-radius: 4px;
+  border: 1px solid #e9ecef;
+  width: 100%;
+  min-height: 50px;
+  display: flex;
+  align-items: center;
+}
+
+.dialog-footer {
+  text-align: right;
+}
+
+.process-alert-info {
+  background-color: #f8f9fa;
+  border-radius: 4px;
+  padding: 15px;
+  margin-bottom: 10px;
+  border: 1px solid #e9ecef;
+}
+
+.process-alert-info .info-item {
+  display: inline-block;
+  margin-right: 20px;
+  margin-bottom: 8px;
+}
+
+.process-alert-info .label {
+  color: #606266;
+  font-size: 14px;
+  margin-right: 5px;
+}
+
+.process-alert-info .value {
+  color: #303133;
+  font-weight: 500;
+}
+
+.alert-content-preview {
+  color: #606266;
+  font-size: 14px;
+  line-height: 1.5;
+  margin-top: 10px;
+  padding-top: 10px;
+  border-top: 1px solid #e9ecef;
+}
+
+.attachment-info {
+  margin-top: 10px;
+}
+
+.attachment-list {
+  background-color: #f8f9fa;
+  border-radius: 4px;
+  padding: 10px;
+  border: 1px solid #e9ecef;
+}
+
+.attachment-item {
+  display: flex;
+  align-items: center;
+  padding: 8px;
+  background-color: #fff;
+  border-radius: 4px;
+  margin-bottom: 8px;
+  border: 1px solid #e9ecef;
+  transition: all 0.3s;
+}
+
+.attachment-item:hover {
+  background-color: #f5f7fa;
+  border-color: #409eff;
+}
+
+.attachment-item:last-child {
+  margin-bottom: 0;
+}
+
+.attachment-preview {
+  width: 32px;
+  height: 32px;
+  margin-right: 12px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background-color: #f0f2f5;
+  border-radius: 4px;
+  color: #606266;
+  font-size: 16px;
+}
+
+.attachment-details {
+  flex: 1;
+  min-width: 0;
+}
+
+.attachment-name {
+  font-size: 14px;
+  font-weight: 500;
+  color: #303133;
+  margin-bottom: 4px;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.attachment-size {
+  font-size: 12px;
+  color: #909399;
+}
+
+.attachment-actions {
+  margin-left: 12px;
+  flex-shrink: 0;
+}
+
+.no-attachment {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  color: #909399;
+  font-size: 14px;
+  padding: 40px 20px;
+  background-color: #f8f9fa;
+  border-radius: 4px;
+  border: 1px dashed #e9ecef;
+}
+
+.no-attachment i {
+  font-size: 32px;
+  margin-bottom: 8px;
+  color: #c0c4cc;
+}
+
+/* 统计面板样式 */
+.statistics-panel {
+  margin-bottom: 16px;
+  padding: 20px;
+  background: #fff;
+  border-radius: 12px;
+  box-shadow: 0 1px 3px rgba(0,0,0,0.08);
+}
+
+/* 统一各部分间距 */
+.app-container .el-form {
+  margin-bottom: 16px;
+}
+
+.app-container .mb8 {
+  margin-bottom: 16px !important;
+}
+
+.app-container .el-table {
+  margin-top: 0;
+}
+
+.stat-card {
+  border-radius: 12px;
+  padding: 20px;
+  height: 140px;
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
+  transition: all 0.3s ease;
+  cursor: pointer;
+  box-shadow: 0 4px 15px rgba(0,0,0,0.1), 0 2px 6px rgba(0,0,0,0.05);
+  position: relative;
+  overflow: hidden;
+  color: #fff;
+}
+
+.stat-card:hover {
+  transform: translateY(-3px);
+  box-shadow: 0 8px 25px rgba(0,0,0,0.15), 0 4px 10px rgba(0,0,0,0.1);
+}
+
+.stat-card.total {
+  background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
+}
+
+.stat-card.emergency {
+  background: linear-gradient(135deg, #ef4444 0%, #f97316 100%);
+}
+
+.stat-card.warning {
+  background: linear-gradient(135deg, #f59e0b 0%, #eab308 100%);
+}
+
+.stat-card.info {
+  background: linear-gradient(135deg, #06b6d4 0%, #3b82f6 100%);
+}
+
+.stat-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 10px;
+}
+
+.stat-title {
+  font-size: 14px;
+  font-weight: 600;
+  color: rgba(255, 255, 255, 0.9);
+  margin-bottom: 0;
+  line-height: 1.2;
+}
+
+.stat-content {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: flex-start;
+}
+
+.stat-number {
+  font-size: 36px;
+  font-weight: 700;
+  line-height: 1.0;
+  margin: 8px 0;
+  color: #fff;
+}
+
+.stat-change {
+  font-size: 12px;
+  font-weight: 500;
+  color: rgba(255, 255, 255, 0.8);
+  line-height: 1.2;
+  margin: 0;
+}
+
+.stat-change.increase {
+  color: rgba(255, 255, 255, 0.9);
+}
+
+.stat-change.decrease {
+  color: rgba(255, 255, 255, 0.9);
+}
+
+.stat-detail {
+  font-size: 12px;
+  font-weight: 500;
+  color: rgba(255, 255, 255, 0.85);
+  line-height: 1.2;
+  margin: 0;
+}
+</style>
+