You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1748 lines
56 KiB
1748 lines
56 KiB
<template> |
|
<div class="alarm-manage"> |
|
<!-- 告警统计卡片 --> |
|
<div class="alarm-stats"> |
|
<div class="stat-card total"> |
|
<div class="stat-icon"> |
|
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"> |
|
<path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z" fill="currentColor"/> |
|
<path d="M688 464H336c-8.8 0-16 7.2-16 16v64c0 8.8 7.2 16 16 16h352c8.8 0 16-7.2 16-16v-64c0-8.8-7.2-16-16-16z" fill="currentColor"/> |
|
</svg> |
|
</div> |
|
<div class="stat-info"> |
|
<div class="stat-value">{{ alarmStats.total }}</div> |
|
<div class="stat-label">告警总数</div> |
|
</div> |
|
</div> |
|
<div class="stat-card pending"> |
|
<div class="stat-icon"> |
|
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"> |
|
<path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z" fill="currentColor"/> |
|
<path d="M672 464H352c-8.8 0-16 7.2-16 16v64c0 8.8 7.2 16 16 16h320c8.8 0 16-7.2 16-16v-64c0-8.8-7.2-16-16-16z" fill="currentColor"/> |
|
<path d="M512 592c-4.4 0-8 3.6-8 8v256c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V600c0-4.4-3.6-8-8-8h-48z" fill="currentColor"/> |
|
</svg> |
|
</div> |
|
<div class="stat-info"> |
|
<div class="stat-value">{{ alarmStats.pending }}</div> |
|
<div class="stat-label">待处理</div> |
|
</div> |
|
</div> |
|
<div class="stat-card processing"> |
|
<div class="stat-icon"> |
|
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"> |
|
<path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z" fill="currentColor"/> |
|
<path d="M512 352c-8.8 0-16 7.2-16 16v224c0 8.8 7.2 16 16 16h48c8.8 0 16-7.2 16-16V368c0-8.8-7.2-16-16-16h-48z" fill="currentColor"/> |
|
</svg> |
|
</div> |
|
<div class="stat-info"> |
|
<div class="stat-value">{{ alarmStats.processing }}</div> |
|
<div class="stat-label">处理中</div> |
|
</div> |
|
</div> |
|
<div class="stat-card resolved"> |
|
<div class="stat-icon"> |
|
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"> |
|
<path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z" fill="currentColor"/> |
|
<path d="M688 464H336c-8.8 0-16 7.2-16 16v64c0 8.8 7.2 16 16 16h352c8.8 0 16-7.2 16-16v-64c0-8.8-7.2-16-16-16z" fill="currentColor"/> |
|
<path d="M512 592c-4.4 0-8 3.6-8 8v256c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V600c0-4.4-3.6-8-8-8h-48z" fill="currentColor"/> |
|
</svg> |
|
</div> |
|
<div class="stat-info"> |
|
<div class="stat-value">{{ alarmStats.resolved }}</div> |
|
<div class="stat-label">已解决</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<!-- 工单系统对接配置 --> |
|
<div class="workorder-config"> |
|
<div class="config-header"> |
|
<div class="config-title"> |
|
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"> |
|
<path d="M832 64H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V96c0-17.7-14.3-32-32-32zm-600 72h560v208H232V136zm560 480H232V408h560v208zm0 272H232V680h560v208z" fill="currentColor"/> |
|
</svg> |
|
工单系统对接配置 |
|
</div> |
|
<el-switch |
|
v-model="workOrderConfig.enabled" |
|
active-text="已启用" |
|
inactive-text="已禁用" |
|
class="custom-switch" |
|
></el-switch> |
|
</div> |
|
|
|
<div class="config-content" v-if="workOrderConfig.enabled"> |
|
<div class="config-row"> |
|
<div class="config-item"> |
|
<label>工单系统类型</label> |
|
<el-select v-model="workOrderConfig.systemType" class="custom-select"> |
|
<el-option label="业主工单系统A" value="systemA"></el-option> |
|
<el-option label="业主工单系统B" value="systemB"></el-option> |
|
<el-option label="第三方工单平台" value="thirdParty"></el-option> |
|
</el-select> |
|
</div> |
|
<div class="config-item"> |
|
<label>API地址</label> |
|
<el-input v-model="workOrderConfig.apiUrl" placeholder="https://api.workorder.com" class="custom-input"></el-input> |
|
</div> |
|
<div class="config-item"> |
|
<label>API密钥</label> |
|
<el-input v-model="workOrderConfig.apiKey" placeholder="请输入API密钥" show-password class="custom-input"></el-input> |
|
</div> |
|
</div> |
|
<div class="config-row"> |
|
<div class="config-item"> |
|
<label>自动推送</label> |
|
<el-switch v-model="workOrderConfig.autoPush" class="custom-switch"></el-switch> |
|
</div> |
|
<div class="config-item"> |
|
<label>推送告警等级</label> |
|
<el-select v-model="workOrderConfig.pushLevel" class="custom-select" multiple> |
|
<el-option label="严重" value="critical"></el-option> |
|
<el-option label="警告" value="warning"></el-option> |
|
<el-option label="信息" value="info"></el-option> |
|
</el-select> |
|
</div> |
|
<div class="config-item"> |
|
<label>工单优先级映射</label> |
|
<el-select v-model="workOrderConfig.priorityMapping" class="custom-select"> |
|
<el-option label="告警等级=工单优先级" value="direct"></el-option> |
|
<el-option label="根据规则映射" value="rule"></el-option> |
|
</el-select> |
|
</div> |
|
</div> |
|
<div class="config-actions"> |
|
<el-button type="primary" @click="testConnection" class="custom-btn"> |
|
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"> |
|
<path d="M864 256H160c-17.7 0-32 14.3-32 32v448c0 17.7 14.3 32 32 32h704c17.7 0 32-14.3 32-32V288c0-17.7-14.3-32-32-32zm0 64H192V384h672v-64z" fill="currentColor"/> |
|
</svg> |
|
测试连接 |
|
</el-button> |
|
<el-button type="success" @click="saveConfig" class="custom-btn"> |
|
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"> |
|
<path d="M864 256H160c-17.7 0-32 14.3-32 32v448c0 17.7 14.3 32 32 32h704c17.7 0 32-14.3 32-32V288c0-17.7-14.3-32-32-32zm0 64H192V384h672v-64z" fill="currentColor"/> |
|
</svg> |
|
保存配置 |
|
</el-button> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<!-- 告警筛选 --> |
|
<div class="alarm-filter"> |
|
<div class="filter-group"> |
|
<div class="filter-item"> |
|
<label>告警级别</label> |
|
<el-select v-model="filterForm.level" placeholder="全部" clearable class="custom-select"> |
|
<el-option label="全部" value=""></el-option> |
|
<el-option label="严重" value="critical"></el-option> |
|
<el-option label="警告" value="warning"></el-option> |
|
<el-option label="信息" value="info"></el-option> |
|
</el-select> |
|
</div> |
|
<div class="filter-item"> |
|
<label>告警状态</label> |
|
<el-select v-model="filterForm.status" placeholder="全部" clearable class="custom-select"> |
|
<el-option label="全部" value=""></el-option> |
|
<el-option label="待处理" value="pending"></el-option> |
|
<el-option label="处理中" value="processing"></el-option> |
|
<el-option label="已解决" value="resolved"></el-option> |
|
</el-select> |
|
</div> |
|
<div class="filter-item"> |
|
<label>工单状态</label> |
|
<el-select v-model="filterForm.workOrderStatus" placeholder="全部" clearable class="custom-select"> |
|
<el-option label="全部" value=""></el-option> |
|
<el-option label="未创建" value="notCreated"></el-option> |
|
<el-option label="待派发" value="pending"></el-option> |
|
<el-option label="进行中" value="inProgress"></el-option> |
|
<el-option label="已完成" value="completed"></el-option> |
|
</el-select> |
|
</div> |
|
<div class="filter-item"> |
|
<label>告警时间</label> |
|
<el-date-picker |
|
v-model="filterForm.dateRange" |
|
type="daterange" |
|
range-separator="至" |
|
start-placeholder="开始日期" |
|
end-placeholder="结束日期" |
|
value-format="yyyy-MM-dd" |
|
class="custom-picker" |
|
></el-date-picker> |
|
</div> |
|
</div> |
|
<div class="filter-actions"> |
|
<el-input |
|
v-model="filterForm.keyword" |
|
placeholder="搜索告警标题/设备/描述" |
|
clearable |
|
class="search-input custom-input" |
|
></el-input> |
|
<el-button type="primary" @click="handleSearch" class="custom-btn">查询</el-button> |
|
<el-button @click="handleReset" class="custom-btn">重置</el-button> |
|
<el-button type="success" @click="handleExport" class="custom-btn">导出</el-button> |
|
</div> |
|
</div> |
|
|
|
<!-- 告警列表 --> |
|
<div class="alarm-list"> |
|
<div class="list-header"> |
|
<div class="list-title">告警列表</div> |
|
<div class="list-info"> |
|
共 {{ filteredAlarms.length }} 条告警 |
|
</div> |
|
</div> |
|
<div class="table-container"> |
|
<table class="alarm-table"> |
|
<thead> |
|
<tr> |
|
<th>告警ID</th> |
|
<th>告警级别</th> |
|
<th>告警标题</th> |
|
<th>设备名称</th> |
|
<th>告警时间</th> |
|
<th>告警状态</th> |
|
<th>工单编号</th> |
|
<th>工单状态</th> |
|
<th>操作</th> |
|
</tr> |
|
</thead> |
|
<tbody> |
|
<tr v-for="alarm in paginatedAlarms" :key="alarm.id"> |
|
<td>{{ alarm.id }}</td> |
|
<td> |
|
<span class="level-badge" :class="alarm.level"> |
|
{{ getLevelText(alarm.level) }} |
|
</span> |
|
</td> |
|
<td class="title-cell">{{ alarm.title }}</td> |
|
<td>{{ alarm.deviceName }}</td> |
|
<td>{{ alarm.alarmTime }}</td> |
|
<td> |
|
<span class="status-badge" :class="alarm.status"> |
|
{{ getStatusText(alarm.status) }} |
|
</span> |
|
</td> |
|
<td> |
|
<span v-if="alarm.workOrderId" class="workorder-link" @click="viewWorkOrder(alarm)"> |
|
{{ alarm.workOrderId }} |
|
</span> |
|
<span v-else class="no-workorder">未创建</span> |
|
</td> |
|
<td> |
|
<span v-if="alarm.workOrderStatus" class="workorder-status" :class="alarm.workOrderStatus"> |
|
{{ getWorkOrderStatusText(alarm.workOrderStatus) }} |
|
</span> |
|
<span v-else>-</span> |
|
</td> |
|
<td class="action-cell"> |
|
<div class="action-buttons"> |
|
<el-button |
|
v-if="!alarm.workOrderId" |
|
type="primary" |
|
size="small" |
|
@click="createWorkOrder(alarm)" |
|
class="action-btn" |
|
> |
|
创建工单 |
|
</el-button> |
|
<el-button |
|
size="small" |
|
@click="viewAlarmDetail(alarm)" |
|
class="action-btn" |
|
> |
|
详情 |
|
</el-button> |
|
<el-dropdown @command="(cmd) => handleAlarmCommand(cmd, alarm)" class="action-dropdown"> |
|
<el-button size="small" class="action-btn"> |
|
更多 |
|
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" style="margin-left: 0.04rem; width: 0.1rem; height: 0.1rem;"> |
|
<path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z" fill="currentColor"/> |
|
<path d="M688 464H336c-8.8 0-16 7.2-16 16v64c0 8.8 7.2 16 16 16h352c8.8 0 16-7.2 16-16v-64c0-8.8-7.2-16-16-16z" fill="currentColor"/> |
|
</svg> |
|
</el-button> |
|
<template #dropdown> |
|
<el-dropdown-menu class="custom-dropdown-menu"> |
|
<el-dropdown-item command="viewWorkOrder" v-if="alarm.workOrderId">查看工单</el-dropdown-item> |
|
<el-dropdown-item command="createWorkOrder" v-if="!alarm.workOrderId">创建工单</el-dropdown-item> |
|
<el-dropdown-item command="resolve" v-if="alarm.status !== 'resolved'" divided>标记已解决</el-dropdown-item> |
|
<el-dropdown-item command="close" v-if="alarm.status !== 'resolved'">关闭告警</el-dropdown-item> |
|
<el-dropdown-item command="reopen" v-if="alarm.status === 'resolved'">重新打开</el-dropdown-item> |
|
<el-dropdown-item command="export" divided>导出报告</el-dropdown-item> |
|
</el-dropdown-menu> |
|
</template> |
|
</el-dropdown> |
|
</div> |
|
</td> |
|
</tr> |
|
</tbody> |
|
</table> |
|
</div> |
|
|
|
<!-- 分页 --> |
|
<div class="pagination-container"> |
|
<el-pagination |
|
v-model:current-page="currentPage" |
|
v-model:page-size="pageSize" |
|
:page-sizes="[10, 20, 50, 100]" |
|
:total="filteredAlarms.length" |
|
layout="total, sizes, prev, pager, next, jumper" |
|
@size-change="handleSizeChange" |
|
@current-change="handleCurrentChange" |
|
class="custom-pagination" |
|
></el-pagination> |
|
</div> |
|
</div> |
|
|
|
<!-- 告警详情弹窗 --> |
|
<el-dialog |
|
v-model="detailDialogVisible" |
|
title="告警详情" |
|
width="800px" |
|
class="custom-dialog" |
|
> |
|
<div class="detail-content" v-if="selectedAlarm"> |
|
<div class="detail-section"> |
|
<div class="section-title">基本信息</div> |
|
<div class="info-grid"> |
|
<div class="info-item"> |
|
<span class="item-label">告警ID:</span> |
|
<span class="item-value">{{ selectedAlarm.id }}</span> |
|
</div> |
|
<div class="info-item"> |
|
<span class="item-label">告警级别:</span> |
|
<span class="item-value level-badge" :class="selectedAlarm.level"> |
|
{{ getLevelText(selectedAlarm.level) }} |
|
</span> |
|
</div> |
|
<div class="info-item"> |
|
<span class="item-label">告警标题:</span> |
|
<span class="item-value">{{ selectedAlarm.title }}</span> |
|
</div> |
|
<div class="info-item"> |
|
<span class="item-label">告警状态:</span> |
|
<span class="item-value status-badge" :class="selectedAlarm.status"> |
|
{{ getStatusText(selectedAlarm.status) }} |
|
</span> |
|
</div> |
|
<div class="info-item"> |
|
<span class="item-label">设备名称:</span> |
|
<span class="item-value">{{ selectedAlarm.deviceName }}</span> |
|
</div> |
|
<div class="info-item"> |
|
<span class="item-label">告警时间:</span> |
|
<span class="item-value">{{ selectedAlarm.alarmTime }}</span> |
|
</div> |
|
<div class="info-item full-width"> |
|
<span class="item-label">告警描述:</span> |
|
<span class="item-value">{{ selectedAlarm.description }}</span> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<div class="detail-section" v-if="selectedAlarm.workOrderId"> |
|
<div class="section-title">关联工单</div> |
|
<div class="workorder-info"> |
|
<div class="info-row"> |
|
<span class="info-label">工单编号:</span> |
|
<span class="info-value workorder-link" @click="viewWorkOrder(selectedAlarm)"> |
|
{{ selectedAlarm.workOrderId }} |
|
</span> |
|
</div> |
|
<div class="info-row"> |
|
<span class="info-label">工单状态:</span> |
|
<span class="info-value workorder-status" :class="selectedAlarm.workOrderStatus"> |
|
{{ getWorkOrderStatusText(selectedAlarm.workOrderStatus) }} |
|
</span> |
|
</div> |
|
<div class="info-row"> |
|
<span class="info-label">工单类型:</span> |
|
<span class="item-value">{{ selectedAlarm.workOrderType }}</span> |
|
</div> |
|
<div class="info-row"> |
|
<span class="info-label">创建时间:</span> |
|
<span class="item-value">{{ selectedAlarm.workOrderCreateTime }}</span> |
|
</div> |
|
<div class="info-row"> |
|
<span class="info-label">处理人:</span> |
|
<span class="item-value">{{ selectedAlarm.workOrderHandler }}</span> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<div class="detail-section"> |
|
<div class="section-title">处理记录</div> |
|
<div class="process-timeline"> |
|
<div |
|
v-for="(record, index) in selectedAlarm.processRecords" |
|
:key="index" |
|
class="timeline-item" |
|
> |
|
<div class="timeline-dot" :class="record.type"></div> |
|
<div class="timeline-content"> |
|
<div class="timeline-title">{{ record.title }}</div> |
|
<div class="timeline-time">{{ record.time }}</div> |
|
<div class="timeline-desc" v-if="record.description">{{ record.description }}</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
<template #footer> |
|
<el-button @click="detailDialogVisible = false" class="custom-btn">关闭</el-button> |
|
</template> |
|
</el-dialog> |
|
|
|
<!-- 创建工单弹窗 --> |
|
<el-dialog |
|
v-model="workOrderDialogVisible" |
|
title="创建工单" |
|
width="600px" |
|
class="custom-dialog" |
|
> |
|
<el-form :model="workOrderForm" :rules="workOrderRules" ref="workOrderFormRef" label-width="100px"> |
|
<el-form-item label="工单标题" prop="title"> |
|
<el-input v-model="workOrderForm.title" placeholder="请输入工单标题" class="custom-input"></el-input> |
|
</el-form-item> |
|
<el-form-item label="工单类型" prop="type"> |
|
<el-select v-model="workOrderForm.type" class="custom-select"> |
|
<el-option label="设备故障" value="deviceFault"></el-option> |
|
<el-option label="系统异常" value="systemError"></el-option> |
|
<el-option label="维护保养" value="maintenance"></el-option> |
|
<el-option label="其他" value="other"></el-option> |
|
</el-select> |
|
</el-form-item> |
|
<el-form-item label="优先级" prop="priority"> |
|
<el-select v-model="workOrderForm.priority" class="custom-select"> |
|
<el-option label="紧急" value="urgent"></el-option> |
|
<el-option label="高" value="high"></el-option> |
|
<el-option label="中" value="medium"></el-option> |
|
<el-option label="低" value="low"></el-option> |
|
</el-select> |
|
</el-form-item> |
|
<el-form-item label="指派人" prop="assignee"> |
|
<el-select v-model="workOrderForm.assignee" class="custom-select"> |
|
<el-option label="张三" value="user1"></el-option> |
|
<el-option label="李四" value="user2"></el-option> |
|
<el-option label="王五" value="user3"></el-option> |
|
</el-select> |
|
</el-form-item> |
|
<el-form-item label="工单描述" prop="description"> |
|
<el-input |
|
v-model="workOrderForm.description" |
|
type="textarea" |
|
:rows="4" |
|
placeholder="请输入工单描述" |
|
class="custom-input" |
|
></el-input> |
|
</el-form-item> |
|
</el-form> |
|
<template #footer> |
|
<el-button @click="workOrderDialogVisible = false" class="custom-btn">取消</el-button> |
|
<el-button type="primary" @click="submitWorkOrder" class="custom-btn">提交工单</el-button> |
|
</template> |
|
</el-dialog> |
|
</div> |
|
</template> |
|
|
|
<script> |
|
export default { |
|
name: "AlarmManage", |
|
data() { |
|
return { |
|
alarmStats: { |
|
total: 156, |
|
pending: 12, |
|
processing: 28, |
|
resolved: 116 |
|
}, |
|
workOrderConfig: { |
|
enabled: true, |
|
systemType: "systemA", |
|
apiUrl: "https://api.workorder.example.com", |
|
apiKey: "", |
|
autoPush: true, |
|
pushLevel: ["critical", "warning"], |
|
priorityMapping: "direct" |
|
}, |
|
filterForm: { |
|
level: "", |
|
status: "", |
|
workOrderStatus: "", |
|
keyword: "", |
|
dateRange: [] |
|
}, |
|
currentPage: 1, |
|
pageSize: 10, |
|
alarmList: [], |
|
detailDialogVisible: false, |
|
workOrderDialogVisible: false, |
|
selectedAlarm: null, |
|
workOrderForm: { |
|
title: "", |
|
type: "", |
|
priority: "", |
|
assignee: "", |
|
description: "" |
|
}, |
|
workOrderRules: { |
|
title: [{ required: true, message: "请输入工单标题", trigger: "blur" }], |
|
type: [{ required: true, message: "请选择工单类型", trigger: "change" }], |
|
priority: [{ required: true, message: "请选择优先级", trigger: "change" }], |
|
assignee: [{ required: true, message: "请选择指派人", trigger: "change" }], |
|
description: [{ required: true, message: "请输入工单描述", trigger: "blur" }] |
|
} |
|
}; |
|
}, |
|
computed: { |
|
filteredAlarms() { |
|
let alarms = [...this.alarmList]; |
|
|
|
if (this.filterForm.level) { |
|
alarms = alarms.filter(a => a.level === this.filterForm.level); |
|
} |
|
|
|
if (this.filterForm.status) { |
|
alarms = alarms.filter(a => a.status === this.filterForm.status); |
|
} |
|
|
|
if (this.filterForm.workOrderStatus) { |
|
alarms = alarms.filter(a => a.workOrderStatus === this.filterForm.workOrderStatus); |
|
} |
|
|
|
if (this.filterForm.keyword) { |
|
const keyword = this.filterForm.keyword.toLowerCase(); |
|
alarms = alarms.filter(a => |
|
a.title.toLowerCase().includes(keyword) || |
|
a.deviceName.toLowerCase().includes(keyword) || |
|
a.description.toLowerCase().includes(keyword) |
|
); |
|
} |
|
|
|
if (this.filterForm.dateRange && this.filterForm.dateRange.length === 2) { |
|
const [start, end] = this.filterForm.dateRange; |
|
alarms = alarms.filter(a => a.alarmTime >= start && a.alarmTime <= end); |
|
} |
|
|
|
return alarms; |
|
}, |
|
paginatedAlarms() { |
|
const start = (this.currentPage - 1) * this.pageSize; |
|
const end = start + this.pageSize; |
|
return this.filteredAlarms.slice(start, end); |
|
} |
|
}, |
|
mounted() { |
|
this.initAlarmList(); |
|
}, |
|
methods: { |
|
initAlarmList() { |
|
const now = new Date(); |
|
this.alarmList = [ |
|
{ |
|
id: "ALM202403040001", |
|
level: "critical", |
|
title: "主机过载告警", |
|
description: "1号主机负载超过额定值105%,持续运行10分钟", |
|
deviceName: "1号主机", |
|
deviceCode: "CH-001", |
|
alarmTime: "2024-03-04 10:30:25", |
|
status: "pending", |
|
workOrderId: "WO202403040001", |
|
workOrderStatus: "inProgress", |
|
workOrderType: "deviceFault", |
|
workOrderCreateTime: "2024-03-04 10:35:00", |
|
workOrderHandler: "张三", |
|
processRecords: [ |
|
{ type: "create", title: "告警产生", time: "2024-03-04 10:30:25", description: "系统检测到1号主机过载" }, |
|
{ type: "push", title: "自动推送到工单系统", time: "2024-03-04 10:30:30", description: "成功创建工单WO202403040001" }, |
|
{ type: "process", title: "工单已派发", time: "2024-03-04 10:45:00", description: "指派给运维人员张三" }, |
|
{ type: "processing", title: "运维人员已接单", time: "2024-03-04 11:00:00", description: "开始处理主机过载问题" } |
|
] |
|
}, |
|
{ |
|
id: "ALM202403040002", |
|
level: "critical", |
|
title: "冷冻泵故障告警", |
|
description: "4号冷冻泵电机过载保护触发", |
|
deviceName: "4号冷冻泵", |
|
deviceCode: "CP-004", |
|
alarmTime: "2024-03-04 09:15:42", |
|
status: "processing", |
|
workOrderId: "WO202403040002", |
|
workOrderStatus: "pending", |
|
workOrderType: "deviceFault", |
|
workOrderCreateTime: "2024-03-04 09:20:00", |
|
workOrderHandler: "李四", |
|
processRecords: [ |
|
{ type: "create", title: "告警产生", time: "2024-03-04 09:15:42", description: "检测到冷冻泵电机过载" }, |
|
{ type: "push", title: "自动推送到工单系统", time: "2024-03-04 09:15:45", description: "成功创建工单WO202403040002" }, |
|
{ type: "pending", title: "等待派发", time: "2024-03-04 09:20:00", description: "工单待派发" } |
|
] |
|
}, |
|
{ |
|
id: "ALM202403040003", |
|
level: "warning", |
|
title: "冷却塔风机效率下降", |
|
description: "1号冷却塔风机效率低于设计值15%", |
|
deviceName: "1号冷却塔", |
|
deviceCode: "CT-001", |
|
alarmTime: "2024-03-04 08:45:18", |
|
status: "pending", |
|
workOrderId: null, |
|
workOrderStatus: null, |
|
processRecords: [ |
|
{ type: "create", title: "告警产生", time: "2024-03-04 08:45:18", description: "检测到风机效率下降" } |
|
] |
|
}, |
|
{ |
|
id: "ALM202403040004", |
|
level: "info", |
|
title: "温度传感器数据异常", |
|
description: "18号温度传感器读数波动较大", |
|
deviceName: "18号温度传感器", |
|
deviceCode: "TS-018", |
|
alarmTime: "2024-03-04 07:30:55", |
|
status: "resolved", |
|
workOrderId: null, |
|
workOrderStatus: null, |
|
processRecords: [ |
|
{ type: "create", title: "告警产生", time: "2024-03-04 07:30:55", description: "传感器读数异常" }, |
|
{ type: "resolve", title: "告警已解决", time: "2024-03-04 08:15:00", description: "传感器已恢复正常" } |
|
] |
|
}, |
|
{ |
|
id: "ALM202403030005", |
|
level: "critical", |
|
title: "主机冷却水流量异常", |
|
description: "2号主机冷却水流量低于设定值50%", |
|
deviceName: "2号主机", |
|
deviceCode: "CH-002", |
|
alarmTime: "2024-03-03 16:22:33", |
|
status: "resolved", |
|
workOrderId: "WO202403030001", |
|
workOrderStatus: "completed", |
|
workOrderType: "deviceFault", |
|
workOrderCreateTime: "2024-03-03 16:30:00", |
|
workOrderHandler: "王五", |
|
processRecords: [ |
|
{ type: "create", title: "告警产生", time: "2024-03-03 16:22:33", description: "检测到冷却水流量异常" }, |
|
{ type: "push", title: "自动推送到工单系统", time: "2024-03-03 16:22:40", description: "成功创建工单WO202403030001" }, |
|
{ type: "process", title: "工单已派发", time: "2024-03-03 16:45:00", description: "指派给运维人员王五" }, |
|
{ type: "processing", title: "运维人员已接单", time: "2024-03-03 17:00:00", description: "开始处理冷却水流量问题" }, |
|
{ type: "resolve", title: "问题已解决", time: "2024-03-03 18:30:00", description: "冷却水流量恢复正常" }, |
|
{ type: "complete", title: "工单已完成", time: "2024-03-03 18:35:00", description: "工单已归档" } |
|
] |
|
}, |
|
{ |
|
id: "ALM202403030006", |
|
level: "warning", |
|
title: "系统EER值低于阈值", |
|
description: "机房整体EER值为4.2,低于设定值5.0", |
|
deviceName: "机房系统", |
|
deviceCode: "SYS-001", |
|
alarmTime: "2024-03-03 14:10:22", |
|
status: "resolved", |
|
workOrderId: "WO202403030002", |
|
workOrderStatus: "completed", |
|
workOrderType: "systemError", |
|
workOrderCreateTime: "2024-03-03 14:20:00", |
|
workOrderHandler: "张三", |
|
processRecords: [ |
|
{ type: "create", title: "告警产生", time: "2024-03-03 14:10:22", description: "系统能效低于阈值" }, |
|
{ type: "push", title: "自动推送到工单系统", time: "2024-03-03 14:10:30", description: "成功创建工单WO202403030002" }, |
|
{ type: "process", title: "工单已派发", time: "2024-03-03 14:30:00", description: "指派给运维人员张三" }, |
|
{ type: "resolve", title: "问题已解决", time: "2024-03-03 15:45:00", description: "EER值已恢复到6.5" }, |
|
{ type: "complete", title: "工单已完成", time: "2024-03-03 15:50:00", description: "工单已归档" } |
|
] |
|
} |
|
]; |
|
|
|
// 添加更多模拟数据 |
|
for (let i = 7; i <= 20; i++) { |
|
const levels = ["critical", "warning", "info"]; |
|
const statuses = ["pending", "processing", "resolved"]; |
|
const workOrderStatuses = ["notCreated", "pending", "inProgress", "completed"]; |
|
const date = new Date(now.getTime() - Math.random() * 7 * 24 * 60 * 60 * 1000); |
|
|
|
this.alarmList.push({ |
|
id: `ALM2024030${String(i).padStart(5, '0')}`, |
|
level: levels[Math.floor(Math.random() * levels.length)], |
|
title: ["设备温度异常", "传感器离线", "电流波动异常", "冷却塔效率下降", "泵运行异常"][Math.floor(Math.random() * 5)], |
|
description: `告警描述详情 ${i}`, |
|
deviceName: `${["主机", "冷冻泵", "冷却泵", "冷却塔", "温度传感器"][Math.floor(Math.random() * 5)]}`, |
|
deviceCode: `DEV-${String(i).padStart(3, '0')}`, |
|
alarmTime: date.toISOString().slice(0, 19).replace('T', ' '), |
|
status: statuses[Math.floor(Math.random() * statuses.length)], |
|
workOrderId: Math.random() > 0.5 ? `WO2024030${String(i).padStart(5, '0')}` : null, |
|
workOrderStatus: Math.random() > 0.3 ? workOrderStatuses[Math.floor(Math.random() * workOrderStatuses.length)] : null, |
|
workOrderType: "deviceFault", |
|
workOrderCreateTime: date.toISOString().slice(0, 19).replace('T', ' '), |
|
workOrderHandler: ["张三", "李四", "王五"][Math.floor(Math.random() * 3)], |
|
processRecords: [ |
|
{ type: "create", title: "告警产生", time: date.toISOString().slice(0, 19).replace('T', ' '), description: "告警已产生" } |
|
] |
|
}); |
|
} |
|
}, |
|
getLevelText(level) { |
|
const map = { |
|
critical: "严重", |
|
warning: "警告", |
|
info: "信息" |
|
}; |
|
return map[level] || level; |
|
}, |
|
getStatusText(status) { |
|
const map = { |
|
pending: "待处理", |
|
processing: "处理中", |
|
resolved: "已解决" |
|
}; |
|
return map[status] || status; |
|
}, |
|
getWorkOrderStatusText(status) { |
|
const map = { |
|
notCreated: "未创建", |
|
pending: "待派发", |
|
inProgress: "进行中", |
|
completed: "已完成" |
|
}; |
|
return map[status] || status; |
|
}, |
|
handleSearch() { |
|
this.currentPage = 1; |
|
}, |
|
handleReset() { |
|
this.filterForm = { |
|
level: "", |
|
status: "", |
|
workOrderStatus: "", |
|
keyword: "", |
|
dateRange: [] |
|
}; |
|
this.currentPage = 1; |
|
}, |
|
handleExport() { |
|
this.$message.success("告警列表导出成功"); |
|
}, |
|
handleSizeChange(size) { |
|
this.pageSize = size; |
|
this.currentPage = 1; |
|
}, |
|
handleCurrentChange(page) { |
|
this.currentPage = page; |
|
}, |
|
viewAlarmDetail(alarm) { |
|
this.selectedAlarm = alarm; |
|
this.detailDialogVisible = true; |
|
}, |
|
createWorkOrder(alarm) { |
|
this.workOrderForm.title = `[告警] ${alarm.title} - ${alarm.deviceName}`; |
|
this.workOrderForm.type = "deviceFault"; |
|
this.workOrderForm.priority = alarm.level === "critical" ? "urgent" : alarm.level === "warning" ? "high" : "medium"; |
|
this.workOrderForm.assignee = ""; |
|
this.workOrderForm.description = alarm.description; |
|
this.workOrderDialogVisible = true; |
|
}, |
|
submitWorkOrder() { |
|
this.$refs.workOrderFormRef.validate((valid) => { |
|
if (valid) { |
|
const alarm = this.alarmList.find(a => a === this.selectedAlarm); |
|
if (alarm) { |
|
alarm.workOrderId = `WO${new Date().getTime()}`; |
|
alarm.workOrderStatus = "pending"; |
|
alarm.workOrderType = this.workOrderForm.type; |
|
alarm.workOrderCreateTime = new Date().toISOString().slice(0, 19).replace('T', ' '); |
|
alarm.workOrderHandler = this.workOrderForm.assignee; |
|
alarm.status = "processing"; |
|
alarm.processRecords.push({ |
|
type: "push", |
|
title: "手动创建工单", |
|
time: new Date().toISOString().slice(0, 19).replace('T', ' '), |
|
description: `成功创建工单${alarm.workOrderId}` |
|
}); |
|
} |
|
this.$message.success("工单创建成功"); |
|
this.workOrderDialogVisible = false; |
|
} |
|
}); |
|
}, |
|
viewWorkOrder(alarm) { |
|
this.$message.info(`查看工单: ${alarm.workOrderId}`); |
|
}, |
|
handleAlarmCommand(command, alarm) { |
|
if (command === "resolve") { |
|
alarm.status = "resolved"; |
|
alarm.processRecords.push({ |
|
type: "resolve", |
|
title: "标记为已解决", |
|
time: new Date().toISOString().slice(0, 19).replace('T', ' '), |
|
description: "用户手动标记为已解决" |
|
}); |
|
this.$message.success("已标记为已解决"); |
|
} else if (command === "close") { |
|
alarm.status = "resolved"; |
|
this.$message.success("告警已关闭"); |
|
} else if (command === "reopen") { |
|
alarm.status = "pending"; |
|
alarm.processRecords.push({ |
|
type: "reopen", |
|
title: "重新打开告警", |
|
time: new Date().toISOString().slice(0, 19).replace('T', ' '), |
|
description: "用户重新打开告警" |
|
}); |
|
this.$message.success("告警已重新打开"); |
|
} else if (command === "createWorkOrder") { |
|
this.createWorkOrder(alarm); |
|
} else if (command === "viewWorkOrder") { |
|
this.viewWorkOrder(alarm); |
|
} else if (command === "export") { |
|
this.$message.success(`导出告警${alarm.id}的报告成功`); |
|
} |
|
}, |
|
testConnection() { |
|
this.$message.success("工单系统连接测试成功"); |
|
}, |
|
saveConfig() { |
|
this.$message.success("配置已保存"); |
|
} |
|
} |
|
}; |
|
</script> |
|
|
|
<style lang="scss" scoped> |
|
.alarm-manage { |
|
padding: 0.16rem; |
|
} |
|
|
|
.alarm-stats { |
|
display: grid; |
|
grid-template-columns: repeat(auto-fit, minmax(2rem, 1fr)); |
|
gap: 0.15rem; |
|
margin-bottom: 0.16rem; |
|
|
|
.stat-card { |
|
display: flex; |
|
align-items: center; |
|
padding: 0.2rem; |
|
background: rgba(255, 255, 255, 0.03); |
|
border-radius: 0.08rem; |
|
|
|
&.total { |
|
background: linear-gradient(135deg, rgba(84, 112, 198, 0.2) 0%, rgba(115, 192, 222, 0.2) 100%); |
|
border: 1px solid rgba(84, 112, 198, 0.3); |
|
} |
|
|
|
&.pending { |
|
background: linear-gradient(135deg, rgba(238, 102, 102, 0.2) 0%, rgba(229, 57, 53, 0.2) 100%); |
|
border: 1px solid rgba(238, 102, 102, 0.3); |
|
} |
|
|
|
&.processing { |
|
background: linear-gradient(135deg, rgba(250, 200, 88, 0.2) 0%, rgba(255, 179, 0, 0.2) 100%); |
|
border: 1px solid rgba(250, 200, 88, 0.3); |
|
} |
|
|
|
&.resolved { |
|
background: linear-gradient(135deg, rgba(145, 204, 117, 0.2) 0%, rgba(102, 187, 106, 0.2) 100%); |
|
border: 1px solid rgba(145, 204, 117, 0.3); |
|
} |
|
|
|
.stat-icon { |
|
width: 0.5rem; |
|
height: 0.5rem; |
|
border-radius: 50%; |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
margin-right: 0.15rem; |
|
background: rgba(255, 255, 255, 0.1); |
|
|
|
svg { |
|
width: 0.26rem; |
|
height: 0.26rem; |
|
fill: #ffffff; |
|
} |
|
} |
|
|
|
.stat-info { |
|
flex: 1; |
|
|
|
.stat-value { |
|
font-size: 0.28rem; |
|
color: #15e1fd; |
|
font-weight: bold; |
|
margin-bottom: 0.03rem; |
|
} |
|
|
|
.stat-label { |
|
font-size: 0.13rem; |
|
color: #ffffff; |
|
opacity: 0.7; |
|
} |
|
} |
|
} |
|
} |
|
|
|
.workorder-config { |
|
display: flex; |
|
justify-content: space-between; |
|
align-items: center; |
|
padding: 0.2rem; |
|
background: rgba(255, 255, 255, 0.05); |
|
border-radius: 0.08rem; |
|
margin-bottom: 0.16rem; |
|
|
|
.config-header { |
|
display: flex; |
|
align-items: center; |
|
gap: 0.12rem; |
|
|
|
.config-title { |
|
font-size: 0.16rem; |
|
color: #ffffff; |
|
font-weight: bold; |
|
|
|
svg { |
|
width: 0.18rem; |
|
height: 0.18rem; |
|
fill: #15e1fd; |
|
} |
|
} |
|
} |
|
} |
|
|
|
.config-content { |
|
background: rgba(255, 255, 255, 0.03); |
|
border-radius: 0.08rem; |
|
padding: 0.2rem; |
|
margin-bottom: 0.16rem; |
|
|
|
.config-row { |
|
display: flex; |
|
gap: 0.15rem; |
|
margin-bottom: 0.15rem; |
|
flex-wrap: wrap; |
|
|
|
.config-item { |
|
display: flex; |
|
flex-direction: column; |
|
gap: 0.08rem; |
|
min-width: 2rem; |
|
|
|
label { |
|
font-size: 0.13rem; |
|
color: #ffffff; |
|
opacity: 0.7; |
|
} |
|
|
|
::v-deep .custom-select, |
|
::v-deep .custom-input { |
|
.el-input__wrapper, |
|
.el-select__wrapper { |
|
background: rgba(255, 255, 255, 0.1); |
|
border: 1px solid rgba(255, 255, 255, 0.2); |
|
|
|
.el-input__inner { |
|
color: #ffffff; |
|
} |
|
} |
|
} |
|
|
|
::v-deep .custom-switch { |
|
.el-switch__core { |
|
background: rgba(255, 255, 255, 0.2); |
|
} |
|
} |
|
} |
|
} |
|
|
|
.config-actions { |
|
display: flex; |
|
gap: 0.1rem; |
|
margin-top: 0.15rem; |
|
|
|
::v-deep .custom-btn { |
|
background: rgba(255, 255, 255, 0.1); |
|
border: 1px solid rgba(255, 255, 255, 0.2); |
|
color: #ffffff; |
|
|
|
&:hover { |
|
background: rgba(255, 255, 255, 0.2); |
|
} |
|
|
|
&.el-button--primary { |
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
border: none; |
|
|
|
&:hover { |
|
background: linear-gradient(135deg, #5a67d8 0%, #6b46c1 100%); |
|
} |
|
} |
|
|
|
&.el-button--success { |
|
background: linear-gradient(135deg, #91CC75 0%, #66BB6A 100%); |
|
border: none; |
|
|
|
&:hover { |
|
background: linear-gradient(135deg, #7CB342 0%, #558B2F 100%); |
|
} |
|
} |
|
|
|
svg { |
|
width: 0.14rem; |
|
height: 0.14rem; |
|
fill: #ffffff; |
|
margin-right: 0.04rem; |
|
} |
|
} |
|
} |
|
} |
|
|
|
.alarm-filter { |
|
display: flex; |
|
justify-content: space-between; |
|
align-items: flex-start; |
|
padding: 0.2rem; |
|
background: rgba(255, 255, 255, 0.05); |
|
border-radius: 0.08rem; |
|
margin-bottom: 0.16rem; |
|
flex-wrap: wrap; |
|
gap: 0.15rem; |
|
|
|
.filter-group { |
|
display: flex; |
|
gap: 0.15rem; |
|
flex-wrap: wrap; |
|
flex: 1; |
|
min-width: 0; |
|
|
|
.filter-item { |
|
display: flex; |
|
flex-direction: column; |
|
gap: 0.08rem; |
|
min-width: 1.8rem; |
|
|
|
label { |
|
font-size: 0.13rem; |
|
color: #ffffff; |
|
opacity: 0.7; |
|
} |
|
|
|
::v-deep .custom-select, |
|
::v-deep .custom-picker { |
|
.el-input__wrapper, |
|
.el-select__wrapper { |
|
background: rgba(255, 255, 255, 0.1); |
|
border: 1px solid rgba(255, 255, 255, 0.2); |
|
|
|
.el-input__inner { |
|
color: #ffffff; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
.filter-actions { |
|
display: flex; |
|
gap: 0.1rem; |
|
|
|
.search-input { |
|
width: 2.5rem; |
|
} |
|
|
|
::v-deep .custom-btn { |
|
background: rgba(255, 255, 255, 0.1); |
|
border: 1px solid rgba(255, 255, 255, 0.2); |
|
color: #ffffff; |
|
|
|
&:hover { |
|
background: rgba(255, 255, 255, 0.2); |
|
} |
|
|
|
&.el-button--primary { |
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
border: none; |
|
|
|
&:hover { |
|
background: linear-gradient(135deg, #5a67d8 0%, #6b46c1 100%); |
|
} |
|
} |
|
|
|
&.el-button--success { |
|
background: linear-gradient(135deg, #91CC75 0%, #66BB6A 100%); |
|
border: none; |
|
|
|
&:hover { |
|
background: linear-gradient(135deg, #7CB342 0%, #558B2F 100%); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
.alarm-list { |
|
background: rgba(255, 255, 255, 0.03); |
|
border-radius: 0.08rem; |
|
padding: 0.2rem; |
|
|
|
.list-header { |
|
display: flex; |
|
justify-content: space-between; |
|
align-items: center; |
|
margin-bottom: 0.2rem; |
|
|
|
.list-title { |
|
font-size: 0.16rem; |
|
color: #ffffff; |
|
font-weight: bold; |
|
padding-left: 0.12rem; |
|
border-left: 0.04rem solid #15e1fd; |
|
} |
|
|
|
.list-info { |
|
font-size: 0.14rem; |
|
color: #ffffff; |
|
opacity: 0.7; |
|
} |
|
} |
|
|
|
.table-container { |
|
overflow-x: auto; |
|
border-radius: 0.04rem; |
|
background: rgba(255, 255, 255, 0.02); |
|
|
|
.alarm-table { |
|
width: 100%; |
|
border-collapse: collapse; |
|
min-width: 10rem; |
|
|
|
thead { |
|
position: sticky; |
|
top: 0; |
|
z-index: 10; |
|
|
|
tr { |
|
background: linear-gradient(135deg, rgba(21, 225, 253, 0.1) 0%, rgba(145, 204, 117, 0.1) 100%); |
|
|
|
th { |
|
padding: 0.14rem 0.12rem; |
|
text-align: left; |
|
font-size: 0.14rem; |
|
color: #15e1fd; |
|
font-weight: 600; |
|
border-bottom: 2px solid rgba(21, 225, 253, 0.3); |
|
white-space: nowrap; |
|
|
|
&:first-child { |
|
padding-left: 0.15rem; |
|
border-top-left-radius: 0.04rem; |
|
} |
|
|
|
&:last-child { |
|
padding-right: 0.15rem; |
|
border-top-right-radius: 0.04rem; |
|
} |
|
} |
|
} |
|
} |
|
|
|
tbody { |
|
tr { |
|
transition: all 0.3s ease; |
|
background: rgba(255, 255, 255, 0.02); |
|
|
|
&:nth-child(even) { |
|
background: rgba(255, 255, 255, 0.04); |
|
} |
|
|
|
&:hover { |
|
background: rgba(21, 225, 253, 0.1); |
|
transform: translateX(0.02rem); |
|
} |
|
|
|
td { |
|
padding: 0.14rem 0.12rem; |
|
font-size: 0.13rem; |
|
color: #ffffff; |
|
border-bottom: 1px solid rgba(255, 255, 255, 0.08); |
|
vertical-align: middle; |
|
|
|
&:first-child { |
|
padding-left: 0.15rem; |
|
} |
|
|
|
&:last-child { |
|
padding-right: 0.15rem; |
|
} |
|
|
|
.level-badge { |
|
display: inline-block; |
|
padding: 0.05rem 0.15rem; |
|
border-radius: 0.05rem; |
|
font-size: 0.12rem; |
|
font-weight: 500; |
|
white-space: nowrap; |
|
box-shadow: 0 0.02rem 0.04rem rgba(0, 0, 0, 0.2); |
|
|
|
&.critical { |
|
background: linear-gradient(135deg, rgba(238, 102, 102, 0.25) 0%, rgba(229, 57, 53, 0.25) 100%); |
|
color: #FF8A80; |
|
border: 1px solid rgba(238, 102, 102, 0.4); |
|
} |
|
|
|
&.warning { |
|
background: linear-gradient(135deg, rgba(250, 200, 88, 0.25) 0%, rgba(255, 179, 0, 0.25) 100%); |
|
color: #FFE082; |
|
border: 1px solid rgba(250, 200, 88, 0.4); |
|
} |
|
|
|
&.info { |
|
background: linear-gradient(135deg, rgba(84, 112, 198, 0.25) 0%, rgba(103, 126, 234, 0.25) 100%); |
|
color: #90CAF9; |
|
border: 1px solid rgba(84, 112, 198, 0.4); |
|
} |
|
} |
|
|
|
.status-badge { |
|
display: inline-block; |
|
padding: 0.05rem 0.15rem; |
|
border-radius: 0.05rem; |
|
font-size: 0.12rem; |
|
font-weight: 500; |
|
white-space: nowrap; |
|
box-shadow: 0 0.02rem 0.04rem rgba(0, 0, 0, 0.2); |
|
|
|
&.pending { |
|
background: linear-gradient(135deg, rgba(238, 102, 102, 0.25) 0%, rgba(229, 57, 53, 0.25) 100%); |
|
color: #FF8A80; |
|
border: 1px solid rgba(238, 102, 102, 0.4); |
|
} |
|
|
|
&.processing { |
|
background: linear-gradient(135deg, rgba(250, 200, 88, 0.25) 0%, rgba(255, 179, 0, 0.25) 100%); |
|
color: #FFE082; |
|
border: 1px solid rgba(250, 200, 88, 0.4); |
|
} |
|
|
|
&.resolved { |
|
background: linear-gradient(135deg, rgba(145, 204, 117, 0.25) 0%, rgba(102, 187, 106, 0.25) 100%); |
|
color: #A5D6A7; |
|
border: 1px solid rgba(145, 204, 117, 0.4); |
|
} |
|
} |
|
|
|
.workorder-link { |
|
color: #15e1fd; |
|
cursor: pointer; |
|
text-decoration: underline; |
|
font-weight: 500; |
|
transition: all 0.3s ease; |
|
|
|
&:hover { |
|
color: #91CC75; |
|
text-shadow: 0 0 0.04rem rgba(145, 204, 117, 0.5); |
|
} |
|
} |
|
|
|
.no-workorder { |
|
color: #ffffff; |
|
opacity: 0.5; |
|
font-style: italic; |
|
} |
|
|
|
.workorder-status { |
|
display: inline-block; |
|
padding: 0.05rem 0.15rem; |
|
border-radius: 0.05rem; |
|
font-size: 0.12rem; |
|
font-weight: 500; |
|
white-space: nowrap; |
|
box-shadow: 0 0.02rem 0.04rem rgba(0, 0, 0, 0.2); |
|
|
|
&.notCreated { |
|
background: rgba(255, 255, 255, 0.1); |
|
color: #ffffff; |
|
opacity: 0.5; |
|
} |
|
|
|
&.pending { |
|
background: linear-gradient(135deg, rgba(238, 102, 102, 0.25) 0%, rgba(229, 57, 53, 0.25) 100%); |
|
color: #FF8A80; |
|
border: 1px solid rgba(238, 102, 102, 0.4); |
|
} |
|
|
|
&.inProgress { |
|
background: linear-gradient(135deg, rgba(250, 200, 88, 0.25) 0%, rgba(255, 179, 0, 0.25) 100%); |
|
color: #FFE082; |
|
border: 1px solid rgba(250, 200, 88, 0.4); |
|
} |
|
|
|
&.completed { |
|
background: linear-gradient(135deg, rgba(145, 204, 117, 0.25) 0%, rgba(102, 187, 106, 0.25) 100%); |
|
color: #A5D6A7; |
|
border: 1px solid rgba(145, 204, 117, 0.4); |
|
} |
|
} |
|
|
|
.title-cell { |
|
max-width: 2.5rem; |
|
overflow: hidden; |
|
text-overflow: ellipsis; |
|
white-space: nowrap; |
|
cursor: pointer; |
|
transition: all 0.3s ease; |
|
|
|
&:hover { |
|
color: #15e1fd; |
|
} |
|
} |
|
|
|
.action-buttons { |
|
display: flex; |
|
align-items: center; |
|
gap: 0.08rem; |
|
flex-wrap: nowrap; |
|
|
|
.action-btn { |
|
padding: 0.05rem 0.12rem; |
|
font-size: 0.12rem; |
|
border-radius: 0.04rem; |
|
font-weight: 500; |
|
transition: all 0.3s ease; |
|
white-space: nowrap; |
|
|
|
&.el-button--primary { |
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
border: none; |
|
color: #ffffff; |
|
box-shadow: 0 0.04rem 0.08rem rgba(102, 126, 234, 0.3); |
|
|
|
&:hover { |
|
background: linear-gradient(135deg, #5a67d8 0%, #6b46c1 100%); |
|
transform: translateY(-0.02rem); |
|
box-shadow: 0 0.06rem 0.12rem rgba(102, 126, 234, 0.4); |
|
} |
|
} |
|
|
|
&.el-button--success { |
|
background: linear-gradient(135deg, #91CC75 0%, #66BB6A 100%); |
|
border: none; |
|
color: #ffffff; |
|
box-shadow: 0 0.04rem 0.08rem rgba(145, 204, 117, 0.3); |
|
|
|
&:hover { |
|
background: linear-gradient(135deg, #7CB342 0%, #558B2F 100%); |
|
transform: translateY(-0.02rem); |
|
box-shadow: 0 0.06rem 0.12rem rgba(145, 204, 117, 0.4); |
|
} |
|
} |
|
|
|
&.el-button--default { |
|
background: rgba(255, 255, 255, 0.1); |
|
border: 1px solid rgba(255, 255, 255, 0.2); |
|
color: #ffffff; |
|
|
|
&:hover { |
|
background: rgba(255, 255, 255, 0.2); |
|
} |
|
} |
|
|
|
&:active { |
|
transform: translateY(0); |
|
} |
|
|
|
svg { |
|
width: 0.1rem; |
|
height: 0.1rem; |
|
fill: #ffffff; |
|
} |
|
} |
|
|
|
.action-dropdown { |
|
::v-deep .el-button--small { |
|
padding: 0.05rem 0.1rem; |
|
font-size: 0.12rem; |
|
background: rgba(255, 255, 255, 0.1); |
|
border: 1px solid rgba(255, 255, 255, 0.2); |
|
color: #ffffff; |
|
border-radius: 0.04rem; |
|
font-weight: 500; |
|
|
|
&:hover { |
|
background: rgba(255, 255, 255, 0.2); |
|
} |
|
|
|
svg { |
|
width: 0.1rem; |
|
height: 0.1rem; |
|
fill: #ffffff; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
.pagination-container { |
|
display: flex; |
|
justify-content: center; |
|
padding-top: 0.2rem; |
|
|
|
::v-deep .custom-pagination { |
|
.el-pagination__total, |
|
.el-pagination__jump, |
|
.el-pager li { |
|
color: #ffffff; |
|
} |
|
|
|
.el-pager li { |
|
background: rgba(255, 255, 255, 0.05); |
|
border: 1px solid rgba(255, 255, 255, 0.1); |
|
|
|
&:hover { |
|
background: rgba(255, 255, 255, 0.1); |
|
} |
|
|
|
&.is-active { |
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
border-color: transparent; |
|
} |
|
} |
|
|
|
.btn-prev, |
|
.btn-next { |
|
background: rgba(255, 255, 255, 0.05); |
|
border: 1px solid rgba(255, 255, 255, 0.1); |
|
color: #ffffff; |
|
|
|
&:hover { |
|
background: rgba(255, 255, 255, 0.1); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
::v-deep .custom-dialog { |
|
.el-dialog { |
|
background: rgba(30, 30, 50, 0.95); |
|
border: 1px solid rgba(255, 255, 255, 0.1); |
|
} |
|
|
|
.el-dialog__header { |
|
background: rgba(255, 255, 255, 0.05); |
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1); |
|
} |
|
|
|
.el-dialog__title { |
|
color: #ffffff; |
|
} |
|
|
|
.el-dialog__headerbtn .el-dialog__close { |
|
color: #ffffff; |
|
} |
|
|
|
.detail-content { |
|
.detail-section { |
|
margin-bottom: 0.3rem; |
|
|
|
.section-title { |
|
font-size: 0.15rem; |
|
color: #15e1fd; |
|
font-weight: bold; |
|
margin-bottom: 0.15rem; |
|
padding-left: 0.12rem; |
|
border-left: 0.04rem solid #15e1fd; |
|
} |
|
|
|
.info-grid { |
|
display: grid; |
|
grid-template-columns: repeat(2, 1fr); |
|
gap: 0.15rem; |
|
|
|
.info-item { |
|
display: flex; |
|
justify-content: space-between; |
|
padding: 0.12rem; |
|
background: rgba(255, 255, 255, 0.03); |
|
border-radius: 0.06rem; |
|
|
|
&.full-width { |
|
grid-column: 1 / -1; |
|
} |
|
|
|
.item-label { |
|
font-size: 0.14rem; |
|
color: #ffffff; |
|
opacity: 0.7; |
|
} |
|
|
|
.item-value { |
|
font-size: 0.14rem; |
|
color: #ffffff; |
|
|
|
&.level-badge { |
|
padding: 0.04rem 0.12rem; |
|
border-radius: 0.04rem; |
|
font-size: 0.12rem; |
|
|
|
&.critical { |
|
background: rgba(238, 102, 102, 0.2); |
|
color: #EE6666; |
|
} |
|
|
|
&.warning { |
|
background: rgba(250, 200, 88, 0.2); |
|
color: #FAC858; |
|
} |
|
|
|
&.info { |
|
background: rgba(84, 112, 198, 0.2); |
|
color: #5470C6; |
|
} |
|
} |
|
|
|
&.status-badge { |
|
padding: 0.04rem 0.12rem; |
|
border-radius: 0.04rem; |
|
font-size: 0.12rem; |
|
|
|
&.pending { |
|
background: rgba(238, 102, 102, 0.2); |
|
color: #EE6666; |
|
} |
|
|
|
&.processing { |
|
background: rgba(250, 200, 88, 0.2); |
|
color: #FAC858; |
|
} |
|
|
|
&.resolved { |
|
background: rgba(145, 204, 117, 0.2); |
|
color: #91CC75; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
.workorder-info { |
|
background: rgba(255, 255, 255, 0.05); |
|
border-radius: 0.06rem; |
|
padding: 0.15rem; |
|
|
|
.info-row { |
|
display: flex; |
|
justify-content: space-between; |
|
margin-bottom: 0.1rem; |
|
|
|
&:last-child { |
|
margin-bottom: 0; |
|
} |
|
|
|
.info-label { |
|
font-size: 0.14rem; |
|
color: #ffffff; |
|
opacity: 0.7; |
|
} |
|
|
|
.info-value { |
|
font-size: 0.14rem; |
|
color: #ffffff; |
|
|
|
&.workorder-link { |
|
color: #15e1fd; |
|
cursor: pointer; |
|
text-decoration: underline; |
|
|
|
&:hover { |
|
color: #91CC75; |
|
} |
|
} |
|
|
|
&.workorder-status { |
|
padding: 0.04rem 0.12rem; |
|
border-radius: 0.04rem; |
|
font-size: 0.12rem; |
|
|
|
&.pending { |
|
background: rgba(238, 102, 102, 0.2); |
|
color: #EE6666; |
|
} |
|
|
|
&.inProgress { |
|
background: rgba(250, 200, 88, 0.2); |
|
color: #FAC858; |
|
} |
|
|
|
&.completed { |
|
background: rgba(145, 204, 117, 0.2); |
|
color: #91CC75; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
.process-timeline { |
|
.timeline-item { |
|
display: flex; |
|
margin-bottom: 0.2rem; |
|
|
|
.timeline-dot { |
|
width: 0.1rem; |
|
height: 0.1rem; |
|
border-radius: 50%; |
|
margin-right: 0.15rem; |
|
margin-top: 0.03rem; |
|
|
|
&.create { |
|
background: #5470C6; |
|
} |
|
|
|
&.push { |
|
background: #91CC75; |
|
} |
|
|
|
&.process, |
|
&.processing { |
|
background: #FAC858; |
|
} |
|
|
|
&.resolve { |
|
background: #73C0DE; |
|
} |
|
|
|
&.complete { |
|
background: #3BA272; |
|
} |
|
|
|
&.pending { |
|
background: #EE6666; |
|
} |
|
|
|
&.reopen { |
|
background: #F37336; |
|
} |
|
} |
|
|
|
.timeline-content { |
|
flex: 1; |
|
|
|
.timeline-title { |
|
font-size: 0.14rem; |
|
color: #ffffff; |
|
font-weight: bold; |
|
margin-bottom: 0.05rem; |
|
} |
|
|
|
.timeline-time { |
|
font-size: 0.12rem; |
|
color: #ffffff; |
|
opacity: 0.6; |
|
margin-bottom: 0.05rem; |
|
} |
|
|
|
.timeline-desc { |
|
font-size: 0.13rem; |
|
color: #ffffff; |
|
opacity: 0.8; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
::v-deep .el-form { |
|
.el-form-item__label { |
|
color: #ffffff; |
|
opacity: 0.7; |
|
} |
|
|
|
.custom-input, |
|
.custom-select { |
|
.el-input__wrapper, |
|
.el-select__wrapper { |
|
background: rgba(255, 255, 255, 0.1); |
|
border: 1px solid rgba(255, 255, 255, 0.2); |
|
|
|
.el-input__inner { |
|
color: #ffffff; |
|
} |
|
} |
|
} |
|
} |
|
|
|
::v-deep .el-dropdown-menu { |
|
background: rgba(30, 30, 50, 0.98); |
|
border: 1px solid rgba(255, 255, 255, 0.15); |
|
box-shadow: 0 0.08rem 0.24rem rgba(0, 0, 0, 0.4); |
|
border-radius: 0.06rem; |
|
padding: 0.08rem 0; |
|
|
|
.el-dropdown-menu__item { |
|
color: #ffffff; |
|
padding: 0.1rem 0.16rem; |
|
font-size: 0.13rem; |
|
transition: all 0.3s ease; |
|
|
|
&:hover { |
|
background: linear-gradient(135deg, rgba(21, 225, 253, 0.15) 0%, rgba(145, 204, 117, 0.15) 100%); |
|
color: #15e1fd; |
|
} |
|
|
|
&.is-divided { |
|
position: relative; |
|
margin-top: 0.08rem; |
|
|
|
&::before { |
|
content: ""; |
|
position: absolute; |
|
top: -0.04rem; |
|
left: 0.1rem; |
|
right: 0.1rem; |
|
height: 1px; |
|
background: rgba(255, 255, 255, 0.1); |
|
} |
|
} |
|
} |
|
} |
|
|
|
@media (max-width: 1485px) { |
|
.alarm-stats { |
|
grid-template-columns: repeat(2, 1fr); |
|
} |
|
|
|
.alarm-filter { |
|
flex-direction: column; |
|
} |
|
|
|
.filter-actions { |
|
width: 100%; |
|
margin-top: 0.1rem; |
|
|
|
.search-input { |
|
flex: 1; |
|
} |
|
} |
|
|
|
.table-container { |
|
.alarm-table { |
|
thead tr th, |
|
tbody tr td { |
|
font-size: 0.12rem; |
|
padding: 0.08rem; |
|
} |
|
} |
|
} |
|
} |
|
</style>
|
|
|