| | |
| | | <template> |
| | | <div class="app-container"> |
| | | |
| | | <!-- 统计概览卡片 --> |
| | | <div class="stats-overview"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="6"> |
| | | <el-card class="stats-card"> |
| | | <div class="stats-content"> |
| | | <div class="stats-icon running"> |
| | | <el-icon><VideoPlay /></el-icon> |
| | | </div> |
| | | <div class="stats-info"> |
| | | <div class="stats-value">{{ overviewData.runningDevices }}</div> |
| | | <div class="stats-label">运行设备</div> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-card class="stats-card"> |
| | | <div class="stats-content"> |
| | | <div class="stats-icon stopped"> |
| | | <el-icon><VideoPause /></el-icon> |
| | | </div> |
| | | <div class="stats-info"> |
| | | <div class="stats-value">{{ overviewData.stoppedDevices }}</div> |
| | | <div class="stats-label">停机设备</div> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-card class="stats-card"> |
| | | <div class="stats-content"> |
| | | <div class="stats-icon alarm"> |
| | | <el-icon><Warning /></el-icon> |
| | | </div> |
| | | <div class="stats-info"> |
| | | <div class="stats-value">{{ overviewData.alarmCount }}</div> |
| | | <div class="stats-label">报警数量</div> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-card class="stats-card"> |
| | | <div class="stats-content"> |
| | | <div class="stats-icon maintenance"> |
| | | <el-icon><Tools /></el-icon> |
| | | </div> |
| | | <div class="stats-info"> |
| | | <div class="stats-value">{{ overviewData.maintenanceCount }}</div> |
| | | <div class="stats-label">维护中</div> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | |
| | | <!-- 主要内容区域 --> |
| | | <el-row :gutter="20"> |
| | | <!-- 左侧:设备启停记录 --> |
| | | <el-col :span="12"> |
| | | <el-card class="main-card"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>设备启停记录</span> |
| | | <el-button type="primary" size="small" @click="refreshDeviceRecords"> |
| | | <el-icon><Refresh /></el-icon> |
| | | 刷新 |
| | | </el-button> |
| | | </div> |
| | | </template> |
| | | |
| | | <!-- 设备状态筛选 --> |
| | | <!-- 筛选条件 --> |
| | | <div class="filter-section"> |
| | | <el-radio-group v-model="deviceFilter" @change="filterDeviceRecords"> |
| | | <el-radio-button label="all">全部</el-radio-button> |
| | | <el-radio-button label="start">启动</el-radio-button> |
| | | <el-radio-button label="stop">停机</el-radio-button> |
| | | </el-radio-group> |
| | | <el-select v-model="deviceFilter" placeholder="设备状态筛选" clearable style="width: 200px; margin-right: 10px;"> |
| | | <el-option label="全部" value="all" /> |
| | | <el-option label="运行中" value="start" /> |
| | | <el-option label="停止运行" value="stop" /> |
| | | </el-select> |
| | | </div> |
| | | |
| | | <!-- 设备记录列表 --> |
| | | <div class="device-records"> |
| | | <div |
| | | v-for="record in filteredDeviceRecords" |
| | | :key="record.id" |
| | | class="device-record" |
| | | :class="record.type" |
| | | > |
| | | <div class="record-icon"> |
| | | <el-icon v-if="record.type === 'start'"><VideoPlay /></el-icon> |
| | | <el-icon v-else><VideoPause /></el-icon> |
| | | </div> |
| | | <div class="record-content"> |
| | | <div class="device-name">{{ record.deviceName }}</div> |
| | | <div class="record-time">{{ record.time }}</div> |
| | | <div class="record-status" :class="record.type"> |
| | | {{ record.type === 'start' ? '设备启动' : '设备停机' }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | |
| | | <!-- 右侧:装置开停工信息 --> |
| | | <el-col :span="12"> |
| | | <el-card class="main-card"> |
| | | <!-- 设备启停记录表格 --> |
| | | <el-card class="table-card"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>装置开停工信息</span> |
| | | <el-button type="success" size="small" @click="refreshUnitInfo"> |
| | | <el-icon><Refresh /></el-icon> |
| | | 刷新 |
| | | </el-button> |
| | | </div> |
| | | <span>设备运行记录</span> |
| | | </template> |
| | | |
| | | <!-- 装置状态筛选 --> |
| | | <div class="filter-section"> |
| | | <el-radio-group v-model="unitFilter" @change="filterUnitInfo"> |
| | | <el-radio-button label="all">全部</el-radio-button> |
| | | <el-radio-button label="startup">开工</el-radio-button> |
| | | <el-radio-button label="shutdown">停工</el-radio-button> |
| | | <el-radio-button label="unplanned">非计划停工</el-radio-button> |
| | | </el-radio-group> |
| | | </div> |
| | | |
| | | <!-- 装置信息列表 --> |
| | | <div class="unit-info"> |
| | | <div |
| | | v-for="unit in filteredUnitInfo" |
| | | :key="unit.id" |
| | | class="unit-item" |
| | | :class="unit.status" |
| | | > |
| | | <div class="unit-header"> |
| | | <div class="unit-name">{{ unit.unitName }}</div> |
| | | <div class="unit-status" :class="unit.status"> |
| | | {{ getUnitStatusText(unit.status) }} |
| | | </div> |
| | | </div> |
| | | <div class="unit-details"> |
| | | <div class="detail-item"> |
| | | <span class="label">开始时间:</span> |
| | | <span class="value">{{ unit.startTime }}</span> |
| | | </div> |
| | | <div class="detail-item" v-if="unit.endTime"> |
| | | <span class="label">结束时间:</span> |
| | | <span class="value">{{ unit.endTime }}</span> |
| | | </div> |
| | | <div class="detail-item" v-if="unit.reason"> |
| | | <span class="label">原因:</span> |
| | | <span class="value">{{ unit.reason }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- 报警信息集中展示 --> |
| | | <el-card class="alarm-card"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>报警信息集中展示</span> |
| | | <div class="alarm-actions"> |
| | | <el-button type="warning" size="small" @click="refreshAlarms"> |
| | | <el-icon><Refresh /></el-icon> |
| | | 刷新 |
| | | </el-button> |
| | | <el-button type="danger" size="small" @click="clearAlarms"> |
| | | <el-icon><Delete /></el-icon> |
| | | 清除已处理 |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <!-- 报警级别筛选 --> |
| | | <div class="filter-section"> |
| | | <el-radio-group v-model="alarmFilter" @change="filterAlarms"> |
| | | <el-radio-button label="all">全部</el-radio-button> |
| | | <el-radio-button label="critical">严重</el-radio-button> |
| | | <el-radio-button label="warning">警告</el-radio-button> |
| | | <el-radio-button label="info">信息</el-radio-button> |
| | | </el-radio-group> |
| | | </div> |
| | | |
| | | <!-- 报警信息表格 --> |
| | | <el-table |
| | | :data="filteredAlarms" |
| | | :data="filteredDeviceRecords" |
| | | style="width: 100%" |
| | | :header-cell-style="{ background: '#F0F1F5', color: '#333333' }" |
| | | max-height="400" |
| | | :row-class-name="getRowClassName" |
| | | v-loading="loading" |
| | | > |
| | | <el-table-column |
| | | align="center" |
| | |
| | | width="60" |
| | | /> |
| | | <el-table-column |
| | | label="报警时间" |
| | | prop="alarmTime" |
| | | width="150" |
| | | align="center" |
| | | /> |
| | | <el-table-column |
| | | label="设备名称" |
| | | prop="deviceName" |
| | | width="150" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="报警级别" |
| | | prop="level" |
| | | width="100" |
| | | align="center" |
| | | > |
| | | <template #default="scope"> |
| | | <el-tag |
| | | :type="getAlarmTagType(scope.row.level)" |
| | | size="small" |
| | | > |
| | | {{ scope.row.level }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="报警内容" |
| | | prop="content" |
| | | min-width="200" |
| | | label="规格型号" |
| | | prop="deviceModel" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="处理状态" |
| | | label="设备状态" |
| | | prop="status" |
| | | width="100" |
| | | width="150" |
| | | align="center" |
| | | > |
| | | <template #default="scope"> |
| | | <!-- 超时未启动时显示警告 --> |
| | | <el-tag |
| | | :type="scope.row.status === '已处理' ? 'success' : 'danger'" |
| | | v-if="isOverdue(scope.row)" |
| | | type="warning" |
| | | size="small" |
| | | effect="dark" |
| | | > |
| | | <el-icon><Warning /></el-icon> |
| | | 超时未启动 |
| | | </el-tag> |
| | | <!-- 正常状态时显示设备状态 --> |
| | | <el-tag |
| | | v-else |
| | | :type="getDeviceStatusType(scope.row.status)" |
| | | size="small" |
| | | > |
| | | {{ scope.row.status }} |
| | | <el-icon v-if="scope.row.status === '运行中'"><VideoPlay /></el-icon> |
| | | <el-icon v-else><VideoPause /></el-icon> |
| | | {{ scope.row.status || '未知' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="处理人" |
| | | prop="handler" |
| | | width="100" |
| | | show-overflow-tooltip |
| | | /> |
| | | label="计划运行时间" |
| | | prop="planRuntimeTime" |
| | | width="150" |
| | | align="center" |
| | | > |
| | | <template #default="scope"> |
| | | {{ scope.row.planRuntimeTime || '-' }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="开始运行时间" |
| | | prop="startRuntimeTime" |
| | | width="180" |
| | | align="center" |
| | | > |
| | | <template #default="scope"> |
| | | {{ scope.row.startRuntimeTime || '-' }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="结束运行时间" |
| | | prop="endRuntimeTime" |
| | | width="180" |
| | | align="center" |
| | | > |
| | | <template #default="scope"> |
| | | {{ scope.row.endRuntimeTime || '-' }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="运行时长" |
| | | prop="runtimeDuration" |
| | | width="120" |
| | | align="center" |
| | | > |
| | | <template #default="scope"> |
| | | {{ scope.row.runtimeDuration || '-' }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="操作" |
| | | width="120" |
| | | align="center" |
| | | > |
| | | <template #default="scope"> |
| | | <!-- 超时未启动时显示启动按钮 --> |
| | | <el-button |
| | | v-if="scope.row.status === '未处理'" |
| | | type="primary" |
| | | v-if="isOverdue(scope.row)" |
| | | type="warning" |
| | | size="small" |
| | | @click="handleAlarm(scope.row)" |
| | | @click="changeDeviceStatus(scope.row, '启动运行')" |
| | | > |
| | | 处理 |
| | | <el-icon><VideoPlay /></el-icon> |
| | | 立即启动 |
| | | </el-button> |
| | | <!-- 正常状态时显示对应的操作按钮 --> |
| | | <template v-else> |
| | | <el-button |
| | | v-if="scope.row.status === '运行中'" |
| | | type="danger" |
| | | size="small" |
| | | @click="changeDeviceStatus(scope.row, '停止运行')" |
| | | > |
| | | <el-icon><VideoPause /></el-icon> |
| | | 停止运行 |
| | | </el-button> |
| | | <el-button |
| | | v-else |
| | | type="info" |
| | | type="success" |
| | | size="small" |
| | | disabled |
| | | @click="changeDeviceStatus(scope.row, '启动运行')" |
| | | > |
| | | 已处理 |
| | | <el-icon><VideoPlay /></el-icon> |
| | | 启动运行 |
| | | </el-button> |
| | | </template> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-card> |
| | | |
| | | |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, computed } from 'vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { ref, onMounted, computed } from 'vue' |
| | | import { ElMessage } from 'element-plus' |
| | | import { |
| | | VideoPlay, |
| | | VideoPause, |
| | | Warning, |
| | | Tools, |
| | | Refresh, |
| | | Delete |
| | | Warning |
| | | } from '@element-plus/icons-vue' |
| | | import {editLedger, getLedgerPage} from "@/api/equipmentManagement/ledger.js"; |
| | | |
| | | // 响应式数据 |
| | | const deviceFilter = ref('all') |
| | | const unitFilter = ref('all') |
| | | const alarmFilter = ref('all') |
| | | |
| | | // 概览数据 |
| | | const overviewData = reactive({ |
| | | runningDevices: 15, |
| | | stoppedDevices: 3, |
| | | alarmCount: 8, |
| | | maintenanceCount: 2 |
| | | const loading = ref(false) |
| | | const total = ref(0) |
| | | const queryParams = ref({ |
| | | current: -1, |
| | | size: -1 |
| | | }) |
| | | |
| | | // 移除概览数据,因为现在使用表格展示 |
| | | |
| | | // 设备启停记录数据 |
| | | const deviceRecords = ref([ |
| | | { |
| | | id: 1, |
| | | deviceName: '压缩机A-001', |
| | | type: 'start', |
| | | time: '2024-01-15 08:30:25' |
| | | }, |
| | | { |
| | | id: 2, |
| | | deviceName: '泵B-002', |
| | | type: 'stop', |
| | | time: '2024-01-15 08:25:10' |
| | | }, |
| | | { |
| | | id: 3, |
| | | deviceName: '风机C-003', |
| | | type: 'start', |
| | | time: '2024-01-15 08:20:15' |
| | | }, |
| | | { |
| | | id: 4, |
| | | deviceName: '搅拌器D-004', |
| | | type: 'start', |
| | | time: '2024-01-15 08:15:30' |
| | | }, |
| | | { |
| | | id: 5, |
| | | deviceName: '加热器E-005', |
| | | type: 'stop', |
| | | time: '2024-01-15 08:10:45' |
| | | }, |
| | | { |
| | | id: 6, |
| | | deviceName: '冷却器F-006', |
| | | type: 'start', |
| | | time: '2024-01-15 08:05:20' |
| | | } |
| | | ]) |
| | | const deviceRecords = ref([]) |
| | | const allDeviceRecords = ref([]) // 存储所有原始数据 |
| | | |
| | | // 装置开停工信息数据 |
| | | const unitInfo = ref([ |
| | | { |
| | | id: 1, |
| | | unitName: '反应装置A', |
| | | status: 'startup', |
| | | startTime: '2024-01-15 08:00:00', |
| | | endTime: null, |
| | | reason: null |
| | | }, |
| | | { |
| | | id: 2, |
| | | unitName: '分离装置B', |
| | | status: 'shutdown', |
| | | startTime: '2024-01-15 07:30:00', |
| | | endTime: '2024-01-15 08:00:00', |
| | | reason: '计划维护' |
| | | }, |
| | | { |
| | | id: 3, |
| | | unitName: '精制装置C', |
| | | status: 'unplanned', |
| | | startTime: '2024-01-15 07:45:00', |
| | | endTime: null, |
| | | reason: '设备故障' |
| | | }, |
| | | { |
| | | id: 4, |
| | | unitName: '包装装置D', |
| | | status: 'startup', |
| | | startTime: '2024-01-15 08:15:00', |
| | | endTime: null, |
| | | reason: null |
| | | } |
| | | ]) |
| | | |
| | | // 报警信息数据 |
| | | const alarms = ref([ |
| | | { |
| | | id: 1, |
| | | alarmTime: '2024-01-15 08:30:00', |
| | | deviceName: '压缩机A-001', |
| | | level: '严重', |
| | | content: '温度过高报警', |
| | | status: '未处理', |
| | | handler: '' |
| | | }, |
| | | { |
| | | id: 2, |
| | | alarmTime: '2024-01-15 08:25:00', |
| | | deviceName: '泵B-002', |
| | | level: '警告', |
| | | content: '压力异常', |
| | | status: '已处理', |
| | | handler: '张三' |
| | | }, |
| | | { |
| | | id: 3, |
| | | alarmTime: '2024-01-15 08:20:00', |
| | | deviceName: '风机C-003', |
| | | level: '信息', |
| | | content: '运行时间达到维护周期', |
| | | status: '未处理', |
| | | handler: '' |
| | | }, |
| | | { |
| | | id: 4, |
| | | alarmTime: '2024-01-15 08:15:00', |
| | | deviceName: '搅拌器D-004', |
| | | level: '严重', |
| | | content: '振动异常', |
| | | status: '未处理', |
| | | handler: '' |
| | | }, |
| | | { |
| | | id: 5, |
| | | alarmTime: '2024-01-15 08:10:00', |
| | | deviceName: '加热器E-005', |
| | | level: '警告', |
| | | content: '加热效率下降', |
| | | status: '已处理', |
| | | handler: '李四' |
| | | } |
| | | ]) |
| | | |
| | | // 计算属性 - 过滤后的设备记录 |
| | | // 根据筛选条件过滤数据 |
| | | const filteredDeviceRecords = computed(() => { |
| | | if (deviceFilter.value === 'all') { |
| | | return deviceRecords.value |
| | | let filtered = allDeviceRecords.value |
| | | |
| | | // 根据设备状态筛选 |
| | | if (deviceFilter.value !== 'all') { |
| | | if (deviceFilter.value === 'start') { |
| | | filtered = filtered.filter(device => device.status === '运行中') |
| | | } else if (deviceFilter.value === 'stop') { |
| | | filtered = filtered.filter(device => device.status === '停止运行') |
| | | } |
| | | return deviceRecords.value.filter(record => record.type === deviceFilter.value) |
| | | } |
| | | |
| | | return filtered |
| | | }) |
| | | |
| | | // 计算属性 - 过滤后的装置信息 |
| | | const filteredUnitInfo = computed(() => { |
| | | if (unitFilter.value === 'all') { |
| | | return unitInfo.value |
| | | // 检查设备是否超时未启动 |
| | | const isOverdue = (device) => { |
| | | if (!device.planRuntimeTime || device.status === '运行中' || device.startRuntimeTime) { |
| | | return false |
| | | } |
| | | return unitInfo.value.filter(unit => unit.status === unitFilter.value) |
| | | }) |
| | | |
| | | // 计算属性 - 过滤后的报警信息 |
| | | const filteredAlarms = computed(() => { |
| | | if (alarmFilter.value === 'all') { |
| | | return alarms.value |
| | | const planTime = new Date(device.planRuntimeTime) |
| | | const currentTime = new Date() |
| | | |
| | | return currentTime > planTime |
| | | } |
| | | return alarms.value.filter(alarm => alarm.level === alarmFilter.value) |
| | | }) |
| | | |
| | | // 方法 |
| | | const refreshDeviceRecords = () => { |
| | | ElMessage.success('设备记录已刷新') |
| | | // 这里可以调用API获取最新数据 |
| | | } |
| | | |
| | | const refreshUnitInfo = () => { |
| | | ElMessage.success('装置信息已刷新') |
| | | // 这里可以调用API获取最新数据 |
| | | } |
| | | |
| | | const refreshAlarms = () => { |
| | | ElMessage.success('报警信息已刷新') |
| | | // 这里可以调用API获取最新数据 |
| | | } |
| | | |
| | | const clearAlarms = async () => { |
| | | const getList = async () => { |
| | | loading.value = true |
| | | try { |
| | | await ElMessageBox.confirm('确定要清除所有已处理的报警信息吗?', '确认清除', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | }) |
| | | |
| | | alarms.value = alarms.value.filter(alarm => alarm.status === '未处理') |
| | | ElMessage.success('已清除所有已处理的报警信息') |
| | | } catch { |
| | | // 用户取消操作 |
| | | const response = await getLedgerPage(queryParams.value) |
| | | if (response.code === 200) { |
| | | allDeviceRecords.value = response.data.records || [] |
| | | total.value = response.data.total || 0 |
| | | } |
| | | } catch (error) { |
| | | console.error('获取设备列表失败:', error) |
| | | ElMessage.error('获取设备列表失败') |
| | | } finally { |
| | | loading.value = false |
| | | } |
| | | } |
| | | |
| | | const filterDeviceRecords = () => { |
| | | // 过滤逻辑已在计算属性中处理 |
| | | const changeDeviceStatus = async (device, status) => { |
| | | try { |
| | | const currentTime = new Date().toLocaleString('zh-CN', { |
| | | year: 'numeric', |
| | | month: '2-digit', |
| | | day: '2-digit', |
| | | hour: '2-digit', |
| | | minute: '2-digit', |
| | | second: '2-digit', |
| | | hour12: false |
| | | }).replace(/\//g, '-') |
| | | |
| | | // 更新设备状态和相关时间字段 |
| | | if (status === '启动运行') { |
| | | device.status = '运行中' |
| | | device.startRuntimeTime = currentTime |
| | | device.endRuntimeTime = null // 清空结束时间 |
| | | device.runtimeDuration = null // 清空运行时长 |
| | | } else { |
| | | device.status = '停止运行' |
| | | device.endRuntimeTime = currentTime |
| | | // 计算运行时长 |
| | | if (device.startRuntimeTime) { |
| | | const startTime = new Date(device.startRuntimeTime) |
| | | const endTime = new Date(currentTime) |
| | | const duration = endTime - startTime |
| | | const hours = Math.floor(duration / (1000 * 60 * 60)) |
| | | const minutes = Math.floor((duration % (1000 * 60 * 60)) / (1000 * 60)) |
| | | device.runtimeDuration = `${hours}小时${minutes}分钟` |
| | | } |
| | | } |
| | | const params = { |
| | | id: device.id, |
| | | status: device.status, |
| | | planRuntimeTime: device.planRuntimeTime, |
| | | startRuntimeTime: device.startRuntimeTime, |
| | | endRuntimeTime: device.endRuntimeTime, |
| | | runtimeDuration: device.runtimeDuration, |
| | | } |
| | | // 调用API更新设备状态 |
| | | const response = await editLedger(params) |
| | | if (response.code === 200) { |
| | | ElMessage.success(`${device.deviceName} ${status}成功`) |
| | | // 刷新列表 |
| | | await getList() |
| | | } else { |
| | | ElMessage.error(response.msg || '操作失败') |
| | | } |
| | | } catch (error) { |
| | | console.error('更新设备状态失败:', error) |
| | | ElMessage.error('操作失败') |
| | | } |
| | | } |
| | | |
| | | const filterUnitInfo = () => { |
| | | // 过滤逻辑已在计算属性中处理 |
| | | const getDeviceStatusType = (status) => { |
| | | if (status === '运行中') { |
| | | return 'success' |
| | | } else if (status === '停止运行') { |
| | | return 'danger' |
| | | } else { |
| | | return 'info' |
| | | } |
| | | } |
| | | |
| | | const filterAlarms = () => { |
| | | // 过滤逻辑已在计算属性中处理 |
| | | // 获取表格行的类名 |
| | | const getRowClassName = ({ row }) => { |
| | | if (isOverdue(row)) { |
| | | return 'overdue-row' |
| | | } |
| | | return '' |
| | | } |
| | | |
| | | const getUnitStatusText = (status) => { |
| | | const statusMap = { |
| | | startup: '开工中', |
| | | shutdown: '已停工', |
| | | unplanned: '非计划停工' |
| | | } |
| | | return statusMap[status] || status |
| | | } |
| | | |
| | | const getAlarmTagType = (level) => { |
| | | const typeMap = { |
| | | '严重': 'danger', |
| | | '警告': 'warning', |
| | | '信息': 'info' |
| | | } |
| | | return typeMap[level] || 'info' |
| | | } |
| | | |
| | | const handleAlarm = (alarm) => { |
| | | ElMessageBox.prompt('请输入处理说明', '处理报警', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | inputPlaceholder: '请输入处理说明' |
| | | }).then(({ value }) => { |
| | | alarm.status = '已处理' |
| | | alarm.handler = '当前用户' // 这里应该获取当前登录用户 |
| | | ElMessage.success('报警处理完成') |
| | | }).catch(() => { |
| | | // 用户取消操作 |
| | | }) |
| | | } |
| | | |
| | | // 组件挂载时初始化数据 |
| | | onMounted(() => { |
| | | // 这里可以调用API获取初始数据 |
| | | console.log('运行管理页面已加载') |
| | | getList() |
| | | }) |
| | | </script> |
| | | |
| | |
| | | } |
| | | |
| | | |
| | | .stats-overview { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .stats-card { |
| | | border-radius: 8px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .stats-content { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 10px 0; |
| | | } |
| | | |
| | | .stats-icon { |
| | | width: 50px; |
| | | height: 50px; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | margin-right: 15px; |
| | | font-size: 24px; |
| | | color: #fff; |
| | | } |
| | | |
| | | .stats-icon.running { |
| | | background: linear-gradient(135deg, #67C23A, #85CE61); |
| | | } |
| | | |
| | | .stats-icon.stopped { |
| | | background: linear-gradient(135deg, #F56C6C, #F78989); |
| | | } |
| | | |
| | | .stats-icon.alarm { |
| | | background: linear-gradient(135deg, #E6A23C, #EEBE77); |
| | | } |
| | | |
| | | .stats-icon.maintenance { |
| | | background: linear-gradient(135deg, #409EFF, #66B1FF); |
| | | } |
| | | |
| | | .stats-info { |
| | | flex: 1; |
| | | } |
| | | |
| | | .stats-value { |
| | | font-size: 24px; |
| | | font-weight: bold; |
| | | color: #333; |
| | | line-height: 1; |
| | | margin-bottom: 5px; |
| | | } |
| | | |
| | | .stats-label { |
| | | font-size: 14px; |
| | | color: #666; |
| | | } |
| | | |
| | | .main-card { |
| | | margin-bottom: 20px; |
| | | border-radius: 8px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .card-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | .filter-section { |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .device-records { |
| | | max-height: 400px; |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .device-record { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 12px; |
| | | margin-bottom: 8px; |
| | | border-radius: 6px; |
| | | background: #f8f9fa; |
| | | border-left: 4px solid #ddd; |
| | | } |
| | | |
| | | .device-record.start { |
| | | border-left-color: #67C23A; |
| | | background: #f0f9ff; |
| | | } |
| | | |
| | | .device-record.stop { |
| | | border-left-color: #F56C6C; |
| | | background: #fef0f0; |
| | | } |
| | | |
| | | .record-icon { |
| | | width: 32px; |
| | | height: 32px; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | margin-right: 12px; |
| | | font-size: 16px; |
| | | color: #fff; |
| | | } |
| | | |
| | | .device-record.start .record-icon { |
| | | background: #67C23A; |
| | | } |
| | | |
| | | .device-record.stop .record-icon { |
| | | background: #F56C6C; |
| | | } |
| | | |
| | | .record-content { |
| | | flex: 1; |
| | | } |
| | | |
| | | .device-name { |
| | | font-weight: 500; |
| | | color: #333; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .record-time { |
| | | font-size: 12px; |
| | | color: #666; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .record-status { |
| | | font-size: 12px; |
| | | padding: 2px 8px; |
| | | border-radius: 12px; |
| | | display: inline-block; |
| | | } |
| | | |
| | | .record-status.start { |
| | | background: #e1f3d8; |
| | | color: #67C23A; |
| | | } |
| | | |
| | | .record-status.stop { |
| | | background: #fde2e2; |
| | | color: #F56C6C; |
| | | } |
| | | |
| | | .unit-info { |
| | | max-height: 400px; |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .unit-item { |
| | | margin-bottom: 20px; |
| | | padding: 15px; |
| | | margin-bottom: 12px; |
| | | border-radius: 6px; |
| | | background: #f8f9fa; |
| | | border-left: 4px solid #ddd; |
| | | } |
| | | |
| | | .unit-item.startup { |
| | | border-left-color: #67C23A; |
| | | background: #f0f9ff; |
| | | } |
| | | |
| | | .unit-item.shutdown { |
| | | border-left-color: #409EFF; |
| | | background: #f0f9ff; |
| | | } |
| | | |
| | | .unit-item.unplanned { |
| | | border-left-color: #E6A23C; |
| | | background: #fdf6ec; |
| | | } |
| | | |
| | | .unit-header { |
| | | background: #fff; |
| | | border-radius: 8px; |
| | | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 8px; |
| | | justify-content: flex-start; |
| | | } |
| | | |
| | | .unit-name { |
| | | font-weight: 500; |
| | | color: #333; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .unit-status { |
| | | font-size: 12px; |
| | | padding: 4px 8px; |
| | | border-radius: 12px; |
| | | display: inline-block; |
| | | } |
| | | |
| | | .unit-status.startup { |
| | | background: #e1f3d8; |
| | | color: #67C23A; |
| | | } |
| | | |
| | | .unit-status.shutdown { |
| | | background: #e1f3ff; |
| | | color: #409EFF; |
| | | } |
| | | |
| | | .unit-status.unplanned { |
| | | background: #fdf6ec; |
| | | color: #E6A23C; |
| | | } |
| | | |
| | | .unit-details { |
| | | font-size: 12px; |
| | | color: #666; |
| | | } |
| | | |
| | | .detail-item { |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .detail-item .label { |
| | | font-weight: 500; |
| | | margin-right: 4px; |
| | | } |
| | | |
| | | .alarm-card { |
| | | .table-card { |
| | | margin-bottom: 20px; |
| | | border-radius: 8px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .alarm-actions { |
| | | display: flex; |
| | | gap: 8px; |
| | | } |
| | | |
| | | :deep(.el-card__header) { |
| | | background: #f8f9fa; |
| | | border-bottom: 1px solid #e9ecef; |
| | | font-weight: 500; |
| | | font-size: 16px; |
| | | } |
| | | |
| | | :deep(.el-table .el-table__header-wrapper th) { |
| | |
| | | } |
| | | |
| | | :deep(.el-table .el-table__body-wrapper td) { |
| | | padding: 8px 0; |
| | | padding: 12px 0; |
| | | } |
| | | |
| | | :deep(.el-radio-button__inner) { |
| | | border-radius: 4px; |
| | | :deep(.el-select) { |
| | | width: 100%; |
| | | } |
| | | |
| | | :deep(.el-radio-button:first-child .el-radio-button__inner) { |
| | | border-left: 1px solid #dcdfe6; |
| | | border-radius: 4px; |
| | | :deep(.el-tag) { |
| | | display: inline-flex; |
| | | align-items: center; |
| | | gap: 4px; |
| | | } |
| | | |
| | | :deep(.el-radio-button:last-child .el-radio-button__inner) { |
| | | border-radius: 4px; |
| | | /* 超时未启动行的样式 */ |
| | | :deep(.overdue-row) { |
| | | background-color: #fef0f0 !important; |
| | | border-left: 4px solid #f56c6c; |
| | | } |
| | | |
| | | :deep(.overdue-row:hover) { |
| | | background-color: #fde2e2 !important; |
| | | } |
| | | |
| | | :deep(.overdue-row td) { |
| | | background-color: transparent !important; |
| | | } |
| | | </style> |