|
|
@@ -1,17 +1,19 @@
|
|
|
<template>
|
|
|
- <div class="equipment-abnormal">
|
|
|
- <!-- 左侧面板 -->
|
|
|
- <div class="left-panel">
|
|
|
- <!-- 搜索 -->
|
|
|
- <div class="panel-card search-card">
|
|
|
- <div class="panel-title"><el-icon><Search /></el-icon> 快速查询</div>
|
|
|
- <el-input v-model="searchKey" placeholder="设备名称/编号/地址" clearable prefix-icon="Search" size="default" />
|
|
|
- <el-select v-model="levelFilter" placeholder="异常等级" clearable style="width:100%;margin-top:10px">
|
|
|
+ <div class="app-container">
|
|
|
+ <!-- 查询条件 -->
|
|
|
+ <el-form ref="queryRef" :inline="true" :model="queryParams" v-show="showSearch" label-width="80px">
|
|
|
+ <el-form-item label="关键字" prop="searchKey">
|
|
|
+ <el-input v-model="queryParams.searchKey" placeholder="设备名称/编号/地址" clearable style="width: 220px" @keyup.enter="handleQuery" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="异常等级" prop="level">
|
|
|
+ <el-select v-model="queryParams.level" placeholder="全部" clearable style="width: 160px">
|
|
|
<el-option label="严重" value="严重" />
|
|
|
<el-option label="一般" value="一般" />
|
|
|
<el-option label="轻微" value="轻微" />
|
|
|
</el-select>
|
|
|
- <el-select v-model="typeFilter" placeholder="设备类型" clearable style="width:100%;margin-top:10px">
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="设备类型" prop="type">
|
|
|
+ <el-select v-model="queryParams.type" placeholder="全部" clearable style="width: 160px">
|
|
|
<el-option label="阀门" value="阀门" />
|
|
|
<el-option label="调压器" value="调压器" />
|
|
|
<el-option label="流量计" value="流量计" />
|
|
|
@@ -19,102 +21,73 @@
|
|
|
<el-option label="终端节点" value="终端节点" />
|
|
|
<el-option label="检查井" value="检查井" />
|
|
|
</el-select>
|
|
|
- <el-select v-model="statusFilter" placeholder="处理状态" clearable style="width:100%;margin-top:10px">
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="处理状态" prop="status">
|
|
|
+ <el-select v-model="queryParams.status" placeholder="全部" clearable style="width: 160px">
|
|
|
<el-option label="待处理" value="待处理" />
|
|
|
<el-option label="处理中" value="处理中" />
|
|
|
<el-option label="已修复" value="已修复" />
|
|
|
</el-select>
|
|
|
- <div style="display:flex;gap:8px;margin-top:10px">
|
|
|
- <el-button type="primary" size="small" @click="handleQuery" style="flex:1">查询</el-button>
|
|
|
- <el-button size="small" @click="handleReset" style="flex:1">重置</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
|
|
+ <el-button icon="Refresh" @click="handleReset">重置</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
+ <el-row :gutter="10" class="mb8">
|
|
|
+ <right-toolbar v-model:showSearch="showSearch" @queryTable="loadList" />
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 统计卡片 -->
|
|
|
+ <div class="stats-row">
|
|
|
+ <div class="stat-card stat-total">
|
|
|
+ <div class="stat-icon"><el-icon size="22"><WarningFilled /></el-icon></div>
|
|
|
+ <div class="stat-info">
|
|
|
+ <div class="stat-label">异常设备总数</div>
|
|
|
+ <div class="stat-value">{{ stats.total }} <small>台</small></div>
|
|
|
</div>
|
|
|
</div>
|
|
|
-
|
|
|
- <!-- 异常等级分布 -->
|
|
|
- <div class="panel-card">
|
|
|
- <div class="panel-title"><el-icon><PieChart /></el-icon> 异常等级分布</div>
|
|
|
- <div class="level-list">
|
|
|
- <div class="level-item critical">
|
|
|
- <span class="level-dot"></span>
|
|
|
- <span class="level-label">严重</span>
|
|
|
- <span class="level-count">{{ stats.critical }}</span>
|
|
|
- </div>
|
|
|
- <div class="level-item warning">
|
|
|
- <span class="level-dot"></span>
|
|
|
- <span class="level-label">一般</span>
|
|
|
- <span class="level-count">{{ stats.normal }}</span>
|
|
|
- </div>
|
|
|
- <div class="level-item minor">
|
|
|
- <span class="level-dot"></span>
|
|
|
- <span class="level-label">轻微</span>
|
|
|
- <span class="level-count">{{ stats.minor }}</span>
|
|
|
- </div>
|
|
|
+ <div class="stat-card stat-today">
|
|
|
+ <div class="stat-icon"><el-icon size="22"><Clock /></el-icon></div>
|
|
|
+ <div class="stat-info">
|
|
|
+ <div class="stat-label">今日新增</div>
|
|
|
+ <div class="stat-value">{{ stats.today }} <small>台</small></div>
|
|
|
</div>
|
|
|
</div>
|
|
|
-
|
|
|
- <!-- 设备类型统计 -->
|
|
|
- <div class="panel-card">
|
|
|
- <div class="panel-title"><el-icon><Histogram /></el-icon> 设备类型统计</div>
|
|
|
- <div class="type-list">
|
|
|
- <div class="type-item" v-for="item in deviceTypeStats" :key="item.name">
|
|
|
- <span class="type-name">{{ item.name }}</span>
|
|
|
- <span class="type-bar-bg"><span class="type-bar" :style="{width: (stats.total > 0 ? item.count/stats.total*100 : 0)+'%', background: item.color}"></span></span>
|
|
|
- <span class="type-count">{{ item.count }}</span>
|
|
|
- </div>
|
|
|
+ <div class="stat-card stat-rate">
|
|
|
+ <div class="stat-icon"><el-icon size="22"><TrendCharts /></el-icon></div>
|
|
|
+ <div class="stat-info">
|
|
|
+ <div class="stat-label">异常占比</div>
|
|
|
+ <div class="stat-value">{{ stats.rate }} <small>%</small></div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 右侧内容区 -->
|
|
|
- <div class="right-panel">
|
|
|
- <!-- 统计卡片 -->
|
|
|
- <div class="stats-row">
|
|
|
- <div class="stat-card stat-total">
|
|
|
- <div class="stat-icon"><el-icon size="22"><WarningFilled /></el-icon></div>
|
|
|
- <div class="stat-info">
|
|
|
- <div class="stat-label">异常设备总数</div>
|
|
|
- <div class="stat-value">{{ stats.total }} <small>台</small></div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="stat-card stat-today">
|
|
|
- <div class="stat-icon"><el-icon size="22"><Clock /></el-icon></div>
|
|
|
- <div class="stat-info">
|
|
|
- <div class="stat-label">今日新增</div>
|
|
|
- <div class="stat-value">{{ stats.today }} <small>台</small></div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="stat-card stat-rate">
|
|
|
- <div class="stat-icon"><el-icon size="22"><TrendCharts /></el-icon></div>
|
|
|
- <div class="stat-info">
|
|
|
- <div class="stat-label">异常占比</div>
|
|
|
- <div class="stat-value">{{ stats.rate }} <small>%</small></div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="stat-card stat-processed">
|
|
|
- <div class="stat-icon"><el-icon size="22"><CircleCheckFilled /></el-icon></div>
|
|
|
- <div class="stat-info">
|
|
|
- <div class="stat-label">已修复</div>
|
|
|
- <div class="stat-value">{{ stats.processed }} <small>台</small></div>
|
|
|
- </div>
|
|
|
+ <div class="stat-card stat-processed">
|
|
|
+ <div class="stat-icon"><el-icon size="22"><CircleCheckFilled /></el-icon></div>
|
|
|
+ <div class="stat-info">
|
|
|
+ <div class="stat-label">已修复</div>
|
|
|
+ <div class="stat-value">{{ stats.processed }} <small>台</small></div>
|
|
|
</div>
|
|
|
- <div class="stat-card stat-pending">
|
|
|
- <div class="stat-icon"><el-icon size="22"><Warning /></el-icon></div>
|
|
|
- <div class="stat-info">
|
|
|
- <div class="stat-label">待处理</div>
|
|
|
- <div class="stat-value">{{ stats.pending }} <small>台</small></div>
|
|
|
- </div>
|
|
|
+ </div>
|
|
|
+ <div class="stat-card stat-pending">
|
|
|
+ <div class="stat-icon"><el-icon size="22"><Warning /></el-icon></div>
|
|
|
+ <div class="stat-info">
|
|
|
+ <div class="stat-label">待处理</div>
|
|
|
+ <div class="stat-value">{{ stats.pending }} <small>台</small></div>
|
|
|
</div>
|
|
|
- <div class="stat-card stat-process-rate">
|
|
|
- <div class="stat-icon"><el-icon size="22"><DataAnalysis /></el-icon></div>
|
|
|
- <div class="stat-info">
|
|
|
- <div class="stat-label">修复率</div>
|
|
|
- <div class="stat-value">{{ stats.processRate }} <small>%</small></div>
|
|
|
- </div>
|
|
|
+ </div>
|
|
|
+ <div class="stat-card stat-process-rate">
|
|
|
+ <div class="stat-icon"><el-icon size="22"><DataAnalysis /></el-icon></div>
|
|
|
+ <div class="stat-info">
|
|
|
+ <div class="stat-label">修复率</div>
|
|
|
+ <div class="stat-value">{{ stats.processRate }} <small>%</small></div>
|
|
|
</div>
|
|
|
</div>
|
|
|
+ </div>
|
|
|
|
|
|
- <!-- 图表区域 - 上方: 趋势图(全宽) -->
|
|
|
- <div class="chart-card chart-trend">
|
|
|
+ <!-- 趋势图 -->
|
|
|
+ <el-card shadow="never" class="chart-card">
|
|
|
+ <template #header>
|
|
|
<div class="chart-header">
|
|
|
<span class="chart-title">设备异常趋势图</span>
|
|
|
<span class="chart-subtitle">近15天运行数据</span>
|
|
|
@@ -124,88 +97,93 @@
|
|
|
<span class="legend-tag tag-fixed">已修复</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
- <div id="trendChart" class="chart-body chart-body-large" style="height:340px"></div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 图表区域 - 下方: 两个图表并排 -->
|
|
|
- <div class="charts-row">
|
|
|
- <div class="chart-card chart-half">
|
|
|
- <div class="chart-header">
|
|
|
- <span class="chart-title">异常类型分布</span>
|
|
|
- </div>
|
|
|
- <div id="typeChart" class="chart-body" style="height:290px"></div>
|
|
|
- </div>
|
|
|
- <div class="chart-card chart-half">
|
|
|
- <div class="chart-header">
|
|
|
- <span class="chart-title">设备异常等级与处理状态</span>
|
|
|
- </div>
|
|
|
- <div id="levelChart" class="chart-body" style="height:290px"></div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
+ </template>
|
|
|
+ <div id="trendChart" style="height: 340px"></div>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 双图并排 -->
|
|
|
+ <div class="charts-row">
|
|
|
+ <el-card shadow="never" class="chart-card">
|
|
|
+ <template #header>
|
|
|
+ <div class="chart-header"><span class="chart-title">异常类型分布</span></div>
|
|
|
+ </template>
|
|
|
+ <div id="typeChart" style="height: 290px"></div>
|
|
|
+ </el-card>
|
|
|
+ <el-card shadow="never" class="chart-card">
|
|
|
+ <template #header>
|
|
|
+ <div class="chart-header"><span class="chart-title">设备异常等级与处理状态</span></div>
|
|
|
+ </template>
|
|
|
+ <div id="levelChart" style="height: 290px"></div>
|
|
|
+ </el-card>
|
|
|
+ </div>
|
|
|
|
|
|
- <!-- 异常设备列表 -->
|
|
|
- <div class="table-card">
|
|
|
- <div class="table-header">
|
|
|
- <span class="table-title">异常设备列表</span>
|
|
|
- <span class="table-count">共 {{ tableTotal }} 条记录</span>
|
|
|
- </div>
|
|
|
- <el-table :data="paginatedData" border stripe v-loading="loading" max-height="460" size="small"
|
|
|
- @sort-change="handleSortChange" :default-sort="{ prop: 'alarmTime', order: 'descending' }">
|
|
|
- <el-table-column prop="id" label="序号" width="56" align="center" sortable="custom" />
|
|
|
- <el-table-column prop="deviceCode" label="设备编号" min-width="140" show-overflow-tooltip />
|
|
|
- <el-table-column prop="deviceName" label="设备名称" min-width="150" show-overflow-tooltip />
|
|
|
- <el-table-column prop="deviceType" label="设备类型" width="100" />
|
|
|
- <el-table-column prop="level" label="异常等级" width="100" align="center" sortable="custom">
|
|
|
- <template #default="{ row }">
|
|
|
- <el-tag :type="levelTagType(row.level)" size="small">{{ row.level }}</el-tag>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column prop="alarmDesc" label="异常描述" min-width="220" show-overflow-tooltip />
|
|
|
- <el-table-column prop="address" label="安装地址" min-width="200" show-overflow-tooltip />
|
|
|
- <el-table-column prop="alarmTime" label="异常时间" width="168" sortable="custom" />
|
|
|
- <el-table-column prop="processStatus" label="处理状态" width="100" align="center" sortable="custom">
|
|
|
- <template #default="{ row }">
|
|
|
- <el-tag :type="row.processStatus === '已修复' ? 'success' : row.processStatus === '处理中' ? 'warning' : 'danger'" size="small">
|
|
|
- {{ row.processStatus }}
|
|
|
- </el-tag>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="操作" width="150" fixed="right" align="center">
|
|
|
- <template #default="{ row }">
|
|
|
- <el-button type="primary" link size="small" @click="handleDetail(row)">详情</el-button>
|
|
|
- <el-button v-if="row.processStatus === '待处理'" type="warning" link size="small" @click="handleProcess(row)">处理</el-button>
|
|
|
- <el-button v-if="row.processStatus === '处理中'" type="success" link size="small" @click="handleComplete(row)">完成</el-button>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- </el-table>
|
|
|
- <div class="table-footer">
|
|
|
- <el-pagination
|
|
|
- v-model:current-page="currentPage"
|
|
|
- v-model:page-size="pageSize"
|
|
|
- :total="tableTotal"
|
|
|
- layout="total, prev, pager, next"
|
|
|
- :page-sizes="[10, 20]"
|
|
|
- small
|
|
|
- background
|
|
|
- />
|
|
|
+ <!-- 异常设备列表 -->
|
|
|
+ <el-card shadow="never" class="chart-card">
|
|
|
+ <template #header>
|
|
|
+ <div class="chart-header">
|
|
|
+ <span class="chart-title">异常设备列表</span>
|
|
|
+ <span class="chart-subtitle">共 {{ tableTotal }} 条记录</span>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
+ </template>
|
|
|
+ <el-table
|
|
|
+ :data="paginatedData"
|
|
|
+ v-loading="loading"
|
|
|
+ @sort-change="handleSortChange"
|
|
|
+ :default-sort="{ prop: 'alarmTime', order: 'descending' }"
|
|
|
+ >
|
|
|
+ <el-table-column prop="id" label="序号" width="70" align="center" sortable="custom" />
|
|
|
+ <el-table-column prop="deviceCode" label="设备编号" min-width="140" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="deviceName" label="设备名称" min-width="150" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="deviceType" label="设备类型" width="110" />
|
|
|
+ <el-table-column prop="level" label="异常等级" width="100" align="center" sortable="custom">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-tag :type="levelTagType(row.level)" size="small">{{ row.level }}</el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="alarmDesc" label="异常描述" min-width="220" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="address" label="安装地址" min-width="200" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="alarmTime" label="异常时间" width="168" sortable="custom" />
|
|
|
+ <el-table-column prop="processStatus" label="处理状态" width="100" align="center" sortable="custom">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-tag :type="row.processStatus === '已修复' ? 'success' : row.processStatus === '处理中' ? 'warning' : 'danger'" size="small">
|
|
|
+ {{ row.processStatus }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="160" fixed="right" align="center" class-name="small-padding fixed-width">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-button link type="primary" icon="View" @click="handleDetail(row)">详情</el-button>
|
|
|
+ <el-button v-if="row.processStatus === '待处理'" link type="primary" icon="Edit" @click="handleProcess(row)">处理</el-button>
|
|
|
+ <el-button v-if="row.processStatus === '处理中'" link type="success" icon="Select" @click="handleComplete(row)">完成</el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <pagination
|
|
|
+ v-show="tableTotal > 0"
|
|
|
+ :total="tableTotal"
|
|
|
+ v-model:page="currentPage"
|
|
|
+ v-model:limit="pageSize"
|
|
|
+ :page-sizes="[10, 20]"
|
|
|
+ @pagination="loadList"
|
|
|
+ />
|
|
|
+ </el-card>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
|
|
+import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref } from 'vue'
|
|
|
import * as echarts from 'echarts'
|
|
|
-import { Clock, CircleCheckFilled, DataAnalysis, Histogram, PieChart, Search, TrendCharts, Warning, WarningFilled } from '@element-plus/icons-vue'
|
|
|
+import { Clock, CircleCheckFilled, DataAnalysis, TrendCharts, Warning, WarningFilled } from '@element-plus/icons-vue'
|
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
|
import { getEquipmentAbnormalList, getEquipmentAbnormalStats } from '@/api/pipeNetwork/gasMonitor'
|
|
|
|
|
|
+defineOptions({ name: 'EquipmentAbnormal' })
|
|
|
+
|
|
|
const loading = ref(false)
|
|
|
-const searchKey = ref('')
|
|
|
-const levelFilter = ref('')
|
|
|
-const typeFilter = ref('')
|
|
|
-const statusFilter = ref('')
|
|
|
+const showSearch = ref(true)
|
|
|
+const queryRef = ref(null)
|
|
|
+const queryParams = reactive({ searchKey: '', level: '', type: '', status: '' })
|
|
|
const currentPage = ref(1)
|
|
|
const pageSize = ref(10)
|
|
|
const sortProp = ref('alarmTime')
|
|
|
@@ -221,8 +199,6 @@ const stats = ref({
|
|
|
processRate: 0, critical: 0, normal: 0, minor: 0
|
|
|
})
|
|
|
|
|
|
-const deviceTypeStats = ref([])
|
|
|
-
|
|
|
async function loadStats() {
|
|
|
try {
|
|
|
const res = await getEquipmentAbnormalStats()
|
|
|
@@ -234,13 +210,6 @@ async function loadStats() {
|
|
|
processRate: d.processRate || 0, critical: d.critical || 0,
|
|
|
normal: d.normal || 0, minor: d.minor || 0
|
|
|
}
|
|
|
- // 设备类型统计
|
|
|
- const colors = ['#e63946', '#f77f00', '#2d6a4f', '#7f4f24', '#6c757d', '#b08968']
|
|
|
- const typeMap = d.deviceTypeStats || {}
|
|
|
- let idx = 0
|
|
|
- deviceTypeStats.value = Object.entries(typeMap).map(([name, count]) => ({
|
|
|
- name, count, color: colors[idx++ % colors.length]
|
|
|
- }))
|
|
|
// 图表数据
|
|
|
chartData.value = {
|
|
|
trend: d.trend || { dates: [], totalData: [], newData: [], fixedData: [] },
|
|
|
@@ -268,10 +237,10 @@ async function loadList() {
|
|
|
const res = await getEquipmentAbnormalList({
|
|
|
pageNum: currentPage.value,
|
|
|
pageSize: pageSize.value,
|
|
|
- searchKey: searchKey.value || undefined,
|
|
|
- level: levelFilter.value || undefined,
|
|
|
- type: typeFilter.value || undefined,
|
|
|
- status: statusFilter.value || undefined
|
|
|
+ searchKey: queryParams.searchKey || undefined,
|
|
|
+ level: queryParams.level || undefined,
|
|
|
+ type: queryParams.type || undefined,
|
|
|
+ status: queryParams.status || undefined
|
|
|
})
|
|
|
if (res.data) {
|
|
|
tableData.value = (res.data.rows || []).map((r, i) => ({
|
|
|
@@ -310,10 +279,6 @@ function handleSortChange({ prop, order }) {
|
|
|
sortOrder.value = order || 'descending'
|
|
|
}
|
|
|
|
|
|
-// 分页切换时重载
|
|
|
-watch(currentPage, () => loadList())
|
|
|
-watch(pageSize, () => { currentPage.value = 1; loadList() })
|
|
|
-
|
|
|
// ==================== 工具函数 ====================
|
|
|
function levelTagType(level) {
|
|
|
if (level === '严重') return 'danger'
|
|
|
@@ -327,10 +292,8 @@ function handleQuery() {
|
|
|
}
|
|
|
|
|
|
function handleReset() {
|
|
|
- searchKey.value = ''
|
|
|
- levelFilter.value = ''
|
|
|
- typeFilter.value = ''
|
|
|
- statusFilter.value = ''
|
|
|
+ queryRef.value?.resetFields()
|
|
|
+ Object.assign(queryParams, { searchKey: '', level: '', type: '', status: '' })
|
|
|
currentPage.value = 1
|
|
|
loadList()
|
|
|
}
|
|
|
@@ -341,7 +304,7 @@ function handleDetail(row) {
|
|
|
<p><b>设备编号:</b>${row.deviceCode}</p>
|
|
|
<p><b>设备名称:</b>${row.deviceName}</p>
|
|
|
<p><b>设备类型:</b>${row.deviceType}</p>
|
|
|
- <p><b>异常等级:</b><span style="color:${row.level==='严重'?'#e63946':row.level==='一般'?'#f77f00':'#909399'}">${row.level}</span></p>
|
|
|
+ <p><b>异常等级:</b><span style="color:${row.level === '严重' ? '#e63946' : row.level === '一般' ? '#f77f00' : '#909399'}">${row.level}</span></p>
|
|
|
<p><b>异常描述:</b>${row.alarmDesc}</p>
|
|
|
<p><b>安装地址:</b>${row.address}</p>
|
|
|
<p><b>异常时间:</b>${row.alarmTime}</p>
|
|
|
@@ -543,112 +506,34 @@ onBeforeUnmount(() => {
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
-.equipment-abnormal {
|
|
|
- display: flex;
|
|
|
- gap: 16px;
|
|
|
- padding: 16px;
|
|
|
- background: #f0f2f6;
|
|
|
- min-height: calc(100vh - 84px);
|
|
|
-}
|
|
|
-
|
|
|
-/* ========== 左侧面板 ========== */
|
|
|
-.left-panel {
|
|
|
- width: 280px;
|
|
|
- flex-shrink: 0;
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- gap: 14px;
|
|
|
-}
|
|
|
-
|
|
|
-.panel-card {
|
|
|
- background: #fff;
|
|
|
- border-radius: 12px;
|
|
|
- padding: 16px;
|
|
|
- box-shadow: 0 2px 12px rgba(15, 35, 95, 0.04);
|
|
|
-}
|
|
|
-
|
|
|
-.panel-title {
|
|
|
- font-size: 14px;
|
|
|
- font-weight: 700;
|
|
|
- color: #1f2d3d;
|
|
|
- margin-bottom: 14px;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 8px;
|
|
|
- padding-bottom: 10px;
|
|
|
- border-bottom: 1px solid #f0f2f6;
|
|
|
-}
|
|
|
-
|
|
|
-.panel-title .el-icon { color: #1890ff; }
|
|
|
-
|
|
|
-/* 等级列表 */
|
|
|
-.level-list { display: flex; flex-direction: column; gap: 8px; }
|
|
|
-.level-item {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 10px;
|
|
|
- padding: 8px 12px;
|
|
|
- border-radius: 8px;
|
|
|
- background: #f8fafc;
|
|
|
- cursor: pointer;
|
|
|
- transition: all 0.2s;
|
|
|
-}
|
|
|
-.level-item:hover { background: #eef2f8; transform: translateX(4px); }
|
|
|
-.level-dot { width: 10px; height: 10px; border-radius: 50%; }
|
|
|
-.level-item.critical .level-dot { background: #e63946; box-shadow: 0 0 6px rgba(230,57,70,0.4); }
|
|
|
-.level-item.warning .level-dot { background: #f77f00; box-shadow: 0 0 6px rgba(247,127,0,0.4); }
|
|
|
-.level-item.minor .level-dot { background: #457b9d; box-shadow: 0 0 6px rgba(69,123,157,0.4); }
|
|
|
-.level-label { flex: 1; font-size: 13px; color: #4e5969; }
|
|
|
-.level-count { font-size: 18px; font-weight: 700; color: #1f2d3d; }
|
|
|
-
|
|
|
-/* 设备类型列表 */
|
|
|
-.type-list { display: flex; flex-direction: column; gap: 10px; }
|
|
|
-.type-item { display: flex; align-items: center; gap: 10px; }
|
|
|
-.type-name { width: 56px; font-size: 12px; color: #606a78; text-align: right; flex-shrink: 0; }
|
|
|
-.type-bar-bg { flex: 1; height: 8px; background: #f0f2f6; border-radius: 4px; overflow: hidden; }
|
|
|
-.type-bar { height: 100%; border-radius: 4px; transition: width 0.6s ease; }
|
|
|
-.type-count { width: 24px; font-size: 12px; font-weight: 600; color: #1f2d3d; text-align: right; flex-shrink: 0; }
|
|
|
-
|
|
|
-/* ========== 右侧内容区 ========== */
|
|
|
-.right-panel {
|
|
|
- flex: 1;
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- gap: 16px;
|
|
|
- min-width: 0;
|
|
|
-}
|
|
|
-
|
|
|
/* 统计卡片 */
|
|
|
.stats-row {
|
|
|
display: grid;
|
|
|
grid-template-columns: repeat(6, 1fr);
|
|
|
gap: 12px;
|
|
|
+ margin-bottom: 16px;
|
|
|
}
|
|
|
|
|
|
.stat-card {
|
|
|
- background: #fff;
|
|
|
- border-radius: 12px;
|
|
|
- padding: 14px 16px;
|
|
|
- box-shadow: 0 2px 12px rgba(15, 35, 95, 0.04);
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
gap: 14px;
|
|
|
- cursor: pointer;
|
|
|
- transition: all 0.25s;
|
|
|
-}
|
|
|
-.stat-card:hover {
|
|
|
- transform: translateY(-2px);
|
|
|
- box-shadow: 0 6px 20px rgba(15, 35, 95, 0.1);
|
|
|
+ padding: 14px 16px;
|
|
|
+ background: #fff;
|
|
|
+ border: 1px solid #ebeef5;
|
|
|
+ border-radius: 4px;
|
|
|
}
|
|
|
|
|
|
.stat-icon {
|
|
|
- width: 44px; height: 44px;
|
|
|
- border-radius: 10px;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
+ width: 44px;
|
|
|
+ height: 44px;
|
|
|
flex-shrink: 0;
|
|
|
+ border-radius: 10px;
|
|
|
}
|
|
|
+
|
|
|
.stat-total .stat-icon { background: #fef0f0; color: #e63946; }
|
|
|
.stat-today .stat-icon { background: #fff3e6; color: #f77f00; }
|
|
|
.stat-rate .stat-icon { background: #fef5e7; color: #e76f51; }
|
|
|
@@ -656,34 +541,40 @@ onBeforeUnmount(() => {
|
|
|
.stat-pending .stat-icon { background: #eaf2f8; color: #457b9d; }
|
|
|
.stat-process-rate .stat-icon { background: #e8f5e9; color: #2d6a4f; }
|
|
|
|
|
|
-.stat-label { font-size: 12px; color: #9098a6; margin-bottom: 4px; }
|
|
|
-.stat-value { font-size: 24px; font-weight: 700; color: #1f2d3d; line-height: 1.1; }
|
|
|
-.stat-value small { font-size: 12px; color: #9098a6; font-weight: 400; }
|
|
|
+.stat-label {
|
|
|
+ margin-bottom: 4px;
|
|
|
+ font-size: 12px;
|
|
|
+ color: #9098a6;
|
|
|
+}
|
|
|
|
|
|
-/* 图表卡片 */
|
|
|
-.chart-card {
|
|
|
- background: #fff;
|
|
|
- border-radius: 12px;
|
|
|
- padding: 16px 20px;
|
|
|
- box-shadow: 0 2px 12px rgba(15, 35, 95, 0.04);
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
+.stat-value {
|
|
|
+ font-size: 24px;
|
|
|
+ font-weight: 700;
|
|
|
+ line-height: 1.1;
|
|
|
+ color: #1f2d3d;
|
|
|
}
|
|
|
|
|
|
-.chart-trend { /* 全宽 */ }
|
|
|
+.stat-value small {
|
|
|
+ font-size: 12px;
|
|
|
+ font-weight: 400;
|
|
|
+ color: #9098a6;
|
|
|
+}
|
|
|
+
|
|
|
+/* 图表 / 列表卡片 */
|
|
|
+.chart-card {
|
|
|
+ margin-bottom: 16px;
|
|
|
+}
|
|
|
|
|
|
.charts-row {
|
|
|
display: grid;
|
|
|
grid-template-columns: 1fr 1fr;
|
|
|
- gap: 14px;
|
|
|
+ gap: 16px;
|
|
|
}
|
|
|
-.chart-half { flex: 1; }
|
|
|
|
|
|
.chart-header {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
gap: 12px;
|
|
|
- margin-bottom: 6px;
|
|
|
flex-wrap: wrap;
|
|
|
}
|
|
|
|
|
|
@@ -705,65 +596,22 @@ onBeforeUnmount(() => {
|
|
|
}
|
|
|
|
|
|
.legend-tag {
|
|
|
- font-size: 11px;
|
|
|
padding: 2px 10px;
|
|
|
border-radius: 10px;
|
|
|
+ font-size: 11px;
|
|
|
color: #fff;
|
|
|
}
|
|
|
+
|
|
|
.tag-total { background: #e63946; }
|
|
|
.tag-new { background: #f77f00; }
|
|
|
.tag-fixed { background: #2a9d8f; }
|
|
|
|
|
|
-.chart-body {
|
|
|
- flex: 1;
|
|
|
- min-height: 280px;
|
|
|
-}
|
|
|
-.chart-body-large {
|
|
|
- min-height: 330px;
|
|
|
-}
|
|
|
-
|
|
|
-/* 表格卡片 */
|
|
|
-.table-card {
|
|
|
- background: #fff;
|
|
|
- border-radius: 12px;
|
|
|
- padding: 16px 20px;
|
|
|
- box-shadow: 0 2px 12px rgba(15, 35, 95, 0.04);
|
|
|
-}
|
|
|
-
|
|
|
-.table-header {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: space-between;
|
|
|
- margin-bottom: 12px;
|
|
|
-}
|
|
|
-
|
|
|
-.table-title {
|
|
|
- font-size: 15px;
|
|
|
- font-weight: 700;
|
|
|
- color: #1f2d3d;
|
|
|
-}
|
|
|
-
|
|
|
-.table-count {
|
|
|
- font-size: 12px;
|
|
|
- color: #9098a6;
|
|
|
-}
|
|
|
-
|
|
|
-.table-footer {
|
|
|
- display: flex;
|
|
|
- justify-content: flex-end;
|
|
|
- margin-top: 12px;
|
|
|
-}
|
|
|
-
|
|
|
-/* ========== 响应式 ========== */
|
|
|
+/* 响应式 */
|
|
|
@media (max-width: 1600px) {
|
|
|
.stats-row { grid-template-columns: repeat(3, 1fr); }
|
|
|
- .left-panel { width: 260px; }
|
|
|
}
|
|
|
|
|
|
@media (max-width: 1400px) {
|
|
|
- .equipment-abnormal { flex-direction: column; }
|
|
|
- .left-panel { width: 100%; flex-direction: row; flex-wrap: wrap; }
|
|
|
- .left-panel .panel-card { flex: 1; min-width: 240px; }
|
|
|
.charts-row { grid-template-columns: 1fr; }
|
|
|
}
|
|
|
|