|
|
@@ -1,73 +1,405 @@
|
|
|
<template>
|
|
|
- <div class="app-container home">
|
|
|
+ <div class="dashboard_container">
|
|
|
+ <!-- 顶部区域:统计卡片 + 右上角按钮 -->
|
|
|
+ <div class="dashboard_header">
|
|
|
+ <div class="stats_area">
|
|
|
+ <el-row :gutter="20" class="dashboard_section">
|
|
|
+ <el-col :span="6" v-for="(item, index) in stats" :key="index">
|
|
|
+ <el-card shadow="hover" :class="['stat_card', item.bgClass]">
|
|
|
+ <div class="stat_content">
|
|
|
+ <el-icon class="stat_bg_icon">
|
|
|
+ <component :is="item.icon" />
|
|
|
+ </el-icon>
|
|
|
+ <div class="stat_text">
|
|
|
+ <div class="stat_number">{{ item.value }}</div>
|
|
|
+ <div class="stat_label">{{ item.label }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="stat_trend">
|
|
|
+ <i :class="[
|
|
|
+ 'iconfont',
|
|
|
+ item.trend >= 0 ? 'icon-TrendUp' : 'icon-TrendDown',
|
|
|
+ item.trend >= 0 ? 'text_success' : 'text_danger',
|
|
|
+ ]"></i>
|
|
|
+ <span :class="item.trend >= 0 ? 'text_success' : 'text_danger'">
|
|
|
+ {{ item.trend >= 0 ? '+' : '' }}{{ item.trend }}
|
|
|
+ </span>
|
|
|
+ <span class="stat_trend_note">
|
|
|
+ {{ item.trend > 0 ? '比昨日增长' : item.trend < 0 ? '较昨日下降' : '持平' }} </span>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+ <!-- 右上角按钮 -->
|
|
|
+ <div class="button_area">
|
|
|
+ <el-card shadow="hover" class="button_card" header="快速导航">
|
|
|
+ <div class="rightTop_btn">
|
|
|
+ <el-button type="primary" class="nav_button" @click="toControl">上海控制页面</el-button>
|
|
|
+ <el-button type="success" class="nav_button" @click="toConfig">上海组态页面</el-button>
|
|
|
+ <el-button type="success" class="nav_button" @click="toConfig">湖南组态页面</el-button>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-row :gutter="20" class="dashboard_section">
|
|
|
+ <el-col :span="16">
|
|
|
+ <el-card shadow="hover" header="设备信息" class="card_block card_block_center">
|
|
|
+ <div class="equipment_info_container">
|
|
|
+ <div class="image_area">
|
|
|
+ <el-image :src="homeBg" fit="contain" style="width: 100%; height: 200px" />
|
|
|
+ </div>
|
|
|
+ <div class="info_area">
|
|
|
+ <el-descriptions :column="2" border>
|
|
|
+ <el-descriptions-item label="设备名称">{{ selectedDevice.equipmentName }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="标识码">{{ selectedDevice.code }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="设备种类">{{ selectedDevice.equipmentType }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="绑定流程">{{ selectedDevice.flowName }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="通讯协议">{{ selectedDevice.protocolName }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="PLC">{{ selectedDevice.plcName }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="设备全称">{{ selectedDevice.title || '暂无' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="状态">
|
|
|
+ <el-tag :type="selectedDevice.status === '0' ? 'success' : 'danger'">
|
|
|
+ {{ selectedDevice.status === '0' ? '启用' : '禁用' }}
|
|
|
+ </el-tag>
|
|
|
+ </el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+
|
|
|
+
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-card shadow="hover" header="告警柱状图" class="card_block card_block_center">
|
|
|
+ <AlarmBarChart />
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 底部公告、日志 -->
|
|
|
+ <el-row :gutter="20" class="dashboard_section">
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-card shadow="hover" header="系统公告" class="card_block ">
|
|
|
+ <el-carousel :interval="3000" type="card" height="130px">
|
|
|
+ <el-carousel-item v-for="(item, i) in announcements" :key="i">
|
|
|
+ <div class="text_center">{{ item }}</div>
|
|
|
+ </el-carousel-item>
|
|
|
+ </el-carousel>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-card shadow="hover" header="最新告警" class="card_block card_block_bottom">
|
|
|
+ <el-timeline>
|
|
|
+ <el-timeline-item v-for="(log, index) in alerts" :key="index" :timestamp="log.updateTime" type="danger">
|
|
|
+ {{ log.message }}
|
|
|
+ </el-timeline-item>
|
|
|
+ </el-timeline>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-card shadow="hover" header="运行日志" class="card_block card_block_bottom">
|
|
|
+ <el-timeline>
|
|
|
+ <el-timeline-item v-for="(log, index) in logs" :key="index" :timestamp="log.time" color="#0bbd87">
|
|
|
+ {{ log.message }}
|
|
|
+ </el-timeline-item>
|
|
|
+ </el-timeline>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
-<script setup name="Index">
|
|
|
+<script setup>
|
|
|
+import { ref, onMounted } from 'vue'
|
|
|
+import { useRoute, useRouter } from 'vue-router'
|
|
|
+import AlarmBarChart from '@/components/HnyzDcs/AlarmBarChartComponent.vue'
|
|
|
+import homeBg from '@/assets/dcs/homeBg.svg'
|
|
|
+import { listEquipment, getDashboardStats } from "@/api/hnyz/equipment";
|
|
|
+import { getLatestAlarmEvent } from "@/api/hnyz/alarmEvent";
|
|
|
+import { getLatestRunLogs } from '@/api/monitor/logininfor';
|
|
|
+import { Cpu, Printer, Finished, Warning } from '@element-plus/icons-vue'
|
|
|
+const route = useRoute()
|
|
|
+const router = useRouter()
|
|
|
+//设备id
|
|
|
+const iconMap = {
|
|
|
+ cpu: Cpu,
|
|
|
+ printer: Printer,
|
|
|
+ finished: Finished,
|
|
|
+ warning: Warning
|
|
|
+}
|
|
|
+const staticStatsConfig = [
|
|
|
+ { key: 'device', label: '设备总数', icon: 'cpu', bgClass: 'bg_blue' },
|
|
|
+ { key: 'register', label: '寄存器总数', icon: 'printer', bgClass: 'bg_green' },
|
|
|
+ { key: 'flow', label: '流程总数', icon: 'finished', bgClass: 'bg_orange' },
|
|
|
+ { key: 'alarm', label: '今日告警信息数', icon: 'warning', bgClass: 'bg_red' }
|
|
|
+]
|
|
|
+
|
|
|
+const selectedDevice = ref({})
|
|
|
+
|
|
|
+const stats = ref([])
|
|
|
+
|
|
|
+onMounted(async () => {
|
|
|
+ // 获取顶部区域 统计数据
|
|
|
+ const res = await getDashboardStats()
|
|
|
+ if (res.code === 200) {
|
|
|
+ stats.value = staticStatsConfig.map(item => {
|
|
|
+ const stat = res.data[item.key] || { value: 0, trend: 0 }
|
|
|
+ return {
|
|
|
+ label: item.label,
|
|
|
+ icon: iconMap[item.icon],
|
|
|
+ bgClass: item.bgClass,
|
|
|
+ value: stat.value,
|
|
|
+ trend: stat.trend,
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ getDeviceInfo()// 获取设备信息
|
|
|
+ getLatestAlerts(5)// 获取最新告警信息
|
|
|
+ getLatestLogs(5)// 获取最新运行日志
|
|
|
+})
|
|
|
+
|
|
|
+// 获取设备信息
|
|
|
+function getDeviceInfo() {
|
|
|
+ const queryParams = {
|
|
|
+ code: route.query.code || 'D1'
|
|
|
+ }
|
|
|
+ listEquipment(queryParams).then(response => {
|
|
|
+ const equipmentInfo = response.rows[0];
|
|
|
+ selectedDevice.value = equipmentInfo;
|
|
|
+ });
|
|
|
+}
|
|
|
+const alerts = ref([])
|
|
|
+// 获取最新告警信息
|
|
|
+function getLatestAlerts(num) {
|
|
|
+ getLatestAlarmEvent(num || 5).then(response => {
|
|
|
+ alerts.value = response.data;
|
|
|
+ console.log(alerts.value)
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+const announcements = ['欢迎使用工业监控系统', '系统将于今晚12点维护', '新功能上线:告警邮件通知']
|
|
|
+
|
|
|
|
|
|
+// const logs = [
|
|
|
+// { time: '10:00:01', message: '系统启动完成' },
|
|
|
+// { time: '10:05:23', message: '用户 admin 登录成功' },
|
|
|
+// ]
|
|
|
+const logs = ref([])
|
|
|
+// 获取最新运行日志
|
|
|
+function getLatestLogs(num) {
|
|
|
+ getLatestRunLogs(num || 5).then(response => {
|
|
|
+ logs.value = response.data;
|
|
|
+ });
|
|
|
+}
|
|
|
+// 按钮跳转
|
|
|
+function toControl() {
|
|
|
+ // 跳转控制页面
|
|
|
+ router.push('/flowSelect')
|
|
|
+}
|
|
|
+function toConfig() {
|
|
|
+ // 跳转组态页面
|
|
|
+ router.push('/m1sj')
|
|
|
+}
|
|
|
</script>
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
-.home {
|
|
|
- blockquote {
|
|
|
- padding: 10px 20px;
|
|
|
- margin: 0 0 20px;
|
|
|
- font-size: 17.5px;
|
|
|
- border-left: 5px solid #eee;
|
|
|
- }
|
|
|
- hr {
|
|
|
- margin-top: 20px;
|
|
|
+.dashboard_container {
|
|
|
+ background-color: #f5f7fa;
|
|
|
+ padding: 20px;
|
|
|
+ min-height: 100vh;
|
|
|
+ box-sizing: border-box;
|
|
|
+ overflow-x: hidden;
|
|
|
+
|
|
|
+ .dashboard_header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
margin-bottom: 20px;
|
|
|
- border: 0;
|
|
|
- border-top: 1px solid #eee;
|
|
|
+
|
|
|
+ .stats_area {
|
|
|
+ width: 75%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .button_area {
|
|
|
+ width: 23%;
|
|
|
+ min-width: 180px;
|
|
|
+
|
|
|
+ .button_card {
|
|
|
+ // height: 100%;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: center;
|
|
|
+ border-radius: 12px;
|
|
|
+
|
|
|
+ .rightTop_btn {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .nav_button {
|
|
|
+ // margin-bottom: 16px;
|
|
|
+ font-weight: 600;
|
|
|
+ font-size: 16px;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+ border-radius: 6px;
|
|
|
+ margin: 0;
|
|
|
+
|
|
|
+ // &:last-child {
|
|
|
+ // margin-bottom: 0;
|
|
|
+ // }
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ transform: scale(1.05);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
- .col-item {
|
|
|
+
|
|
|
+ .dashboard_section {
|
|
|
margin-bottom: 20px;
|
|
|
}
|
|
|
|
|
|
- ul {
|
|
|
- padding: 0;
|
|
|
- margin: 0;
|
|
|
- }
|
|
|
+ .stat_card {
|
|
|
+ position: relative;
|
|
|
+ border-radius: 12px;
|
|
|
+ padding: 16px;
|
|
|
+ color: #333;
|
|
|
+ min-height: 110px;
|
|
|
+ overflow: hidden;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: space-between;
|
|
|
|
|
|
- font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|
|
- font-size: 13px;
|
|
|
- color: #676a6c;
|
|
|
- overflow-x: hidden;
|
|
|
+ .stat_content {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ z-index: 2;
|
|
|
|
|
|
- ul {
|
|
|
- list-style-type: none;
|
|
|
- }
|
|
|
+ .stat_bg_icon {
|
|
|
+ position: absolute;
|
|
|
+ right: 12px;
|
|
|
+ bottom: 12px;
|
|
|
+ font-size: 60px;
|
|
|
+ opacity: 0.08;
|
|
|
+ z-index: 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ .stat_text {
|
|
|
+ z-index: 2;
|
|
|
+
|
|
|
+ .stat_number {
|
|
|
+ font-size: 26px;
|
|
|
+ font-weight: 700;
|
|
|
+ margin-bottom: 4px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .stat_label {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #666;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- h4 {
|
|
|
- margin-top: 0px;
|
|
|
+ .stat_trend {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ font-size: 13px;
|
|
|
+ margin-top: 8px;
|
|
|
+ z-index: 2;
|
|
|
+
|
|
|
+ .stat_trend_note {
|
|
|
+ margin-left: 6px;
|
|
|
+ font-size: 12px;
|
|
|
+ color: #999;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- h2 {
|
|
|
- margin-top: 10px;
|
|
|
- font-size: 26px;
|
|
|
- font-weight: 100;
|
|
|
+ .card_block {
|
|
|
+ border-radius: 10px;
|
|
|
}
|
|
|
|
|
|
- p {
|
|
|
- margin-top: 10px;
|
|
|
+ .card_block_center {
|
|
|
+ height: 320px;
|
|
|
|
|
|
- b {
|
|
|
- font-weight: 700;
|
|
|
+ ::v-deep(.el-card__body) {
|
|
|
+ height: calc(100% - 48px); // 减去 header 高度(大概 48px)
|
|
|
+ overflow-y: auto;
|
|
|
+ padding-top: 10px;
|
|
|
}
|
|
|
+
|
|
|
+ .equipment_info_container {
|
|
|
+ display: flex;
|
|
|
+ gap: 20px;
|
|
|
+ align-items: center;
|
|
|
+ padding: 10px;
|
|
|
+
|
|
|
+ .image_area {
|
|
|
+ flex: 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ .info_area {
|
|
|
+ flex: 2;
|
|
|
+ overflow: auto;
|
|
|
+ max-height: 240px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
}
|
|
|
|
|
|
- .update-log {
|
|
|
- ol {
|
|
|
- display: block;
|
|
|
- list-style-type: decimal;
|
|
|
- margin-block-start: 1em;
|
|
|
- margin-block-end: 1em;
|
|
|
- margin-inline-start: 0;
|
|
|
- margin-inline-end: 0;
|
|
|
- padding-inline-start: 40px;
|
|
|
+ .card_block_bottom {
|
|
|
+ height: 250px;
|
|
|
+
|
|
|
+ ::v-deep(.el-card__body) {
|
|
|
+ height: calc(100% - 48px); // 减去 header 高度(大概 48px)
|
|
|
+ overflow-y: auto;
|
|
|
+ padding-top: 10px;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
+ .device_info {
|
|
|
+ margin-top: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .text_hint {
|
|
|
+ color: #aaa;
|
|
|
+ text-align: center;
|
|
|
+ padding: 10px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .text_center {
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .text_success {
|
|
|
+ color: #67c23a;
|
|
|
+ }
|
|
|
+
|
|
|
+ .text_danger {
|
|
|
+ color: #f56c6c;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 卡片背景配色 */
|
|
|
+ .bg_blue {
|
|
|
+ background-color: #e6f7ff;
|
|
|
+ }
|
|
|
+
|
|
|
+ .bg_green {
|
|
|
+ background-color: #f0f9eb;
|
|
|
+ }
|
|
|
+
|
|
|
+ .bg_orange {
|
|
|
+ background-color: #fff7e6;
|
|
|
+ }
|
|
|
+
|
|
|
+ .bg_red {
|
|
|
+ background-color: #fff0f0;
|
|
|
+ }
|
|
|
}
|
|
|
</style>
|
|
|
-
|