|
|
|
|
@ -5,7 +5,14 @@
|
|
|
|
|
<div class="page-title">设备工艺参数监控</div>
|
|
|
|
|
<div class="page-actions">
|
|
|
|
|
<span class="page-time">{{ nowTimeStr }}</span>
|
|
|
|
|
<el-switch v-model="autoRefresh" active-text="自动刷新" @change="onAutoRefreshChange" />
|
|
|
|
|
<el-switch
|
|
|
|
|
v-model="autoRefresh"
|
|
|
|
|
active-text="自动刷新开"
|
|
|
|
|
inactive-text="自动刷新关"
|
|
|
|
|
active-color="#13ce66"
|
|
|
|
|
inactive-color="#dcdfe6"
|
|
|
|
|
@change="onAutoRefreshChange"
|
|
|
|
|
/>
|
|
|
|
|
<el-button type="primary" size="mini" icon="el-icon-refresh" @click="refreshData">刷新</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
@ -97,22 +104,6 @@
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 最近记录表格(始终显示) -->
|
|
|
|
|
<div class="recent-records-section">
|
|
|
|
|
<div class="section-header">
|
|
|
|
|
<span class="section-title">最近采集记录</span>
|
|
|
|
|
</div>
|
|
|
|
|
<el-table :data="recentRecords" size="mini" max-height="250">
|
|
|
|
|
<el-table-column v-if="!selectedDevice" label="设备" align="center" prop="deviceCode" width="100" />
|
|
|
|
|
<el-table-column label="参数名称" align="center" prop="paramName" min-width="120" />
|
|
|
|
|
<el-table-column label="参数值" align="center" prop="paramValue" width="100" />
|
|
|
|
|
<el-table-column label="采集时间" align="center" width="160">
|
|
|
|
|
<template slot-scope="scope">
|
|
|
|
|
<span>{{ parseTime(scope.row.collectTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
</el-table>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
@ -131,7 +122,7 @@ export default {
|
|
|
|
|
clockTimer: null,
|
|
|
|
|
// 自动刷新
|
|
|
|
|
autoRefresh: true,
|
|
|
|
|
refreshIntervalMs: 5000,
|
|
|
|
|
refreshIntervalMs: 10000,
|
|
|
|
|
refreshTimer: null,
|
|
|
|
|
// 设备列表
|
|
|
|
|
deviceList: [],
|
|
|
|
|
@ -143,8 +134,6 @@ export default {
|
|
|
|
|
deviceParams: [],
|
|
|
|
|
paramLoading: false,
|
|
|
|
|
paramSearchKey: '',
|
|
|
|
|
// 最近记录
|
|
|
|
|
recentRecords: []
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
computed: {
|
|
|
|
|
@ -172,16 +161,11 @@ export default {
|
|
|
|
|
return name.includes(key) || code.includes(key);
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
// 最近更新时间
|
|
|
|
|
// 最近更新时间(取第一条记录的时间,后端已按时间排序)
|
|
|
|
|
latestUpdateTime() {
|
|
|
|
|
if (this.deviceParams.length === 0) return '-';
|
|
|
|
|
const times = this.deviceParams.map(p => {
|
|
|
|
|
const t = p.recordTime || p.collectTime;
|
|
|
|
|
return t ? new Date(t).getTime() : 0;
|
|
|
|
|
});
|
|
|
|
|
const maxTime = Math.max(...times);
|
|
|
|
|
if (maxTime === 0) return '-';
|
|
|
|
|
return this.parseTime(maxTime, '{y}-{m}-{d} {h}:{i}:{s}');
|
|
|
|
|
const t = this.deviceParams[0].recordTime || this.deviceParams[0].collectTime;
|
|
|
|
|
return t ? this.parseTime(t, '{y}-{m}-{d} {h}:{i}:{s}') : '-';
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
created() {
|
|
|
|
|
@ -247,34 +231,13 @@ export default {
|
|
|
|
|
loadAllLatestParams() {
|
|
|
|
|
this.paramLoading = true;
|
|
|
|
|
getLatestVal({}).then(response => {
|
|
|
|
|
const rows = response.rows || response.data || response || [];
|
|
|
|
|
// 按参数聚合,取最新值
|
|
|
|
|
const paramMap = {};
|
|
|
|
|
rows.forEach(r => {
|
|
|
|
|
const pKey = (r.deviceCode || '') + '_' + (r.paramCode || r.paramName || '未知参数');
|
|
|
|
|
const time = r.recordTime || r.collectTime;
|
|
|
|
|
const ts = time ? new Date(time).getTime() : 0;
|
|
|
|
|
const prev = paramMap[pKey];
|
|
|
|
|
const prevTs = prev ? new Date(prev.recordTime || prev.collectTime || 0).getTime() : -1;
|
|
|
|
|
if (!prev || ts >= prevTs) {
|
|
|
|
|
paramMap[pKey] = r;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
this.deviceParams = Object.values(paramMap).sort((a, b) => {
|
|
|
|
|
const ta = new Date(a.recordTime || a.collectTime || 0).getTime();
|
|
|
|
|
const tb = new Date(b.recordTime || b.collectTime || 0).getTime();
|
|
|
|
|
return tb - ta;
|
|
|
|
|
}).slice(0, 50);
|
|
|
|
|
this.recentRecords = rows.slice().sort((a, b) => {
|
|
|
|
|
const ta = new Date(a.recordTime || a.collectTime || 0).getTime();
|
|
|
|
|
const tb = new Date(b.recordTime || b.collectTime || 0).getTime();
|
|
|
|
|
return tb - ta;
|
|
|
|
|
}).slice(0, 20);
|
|
|
|
|
const rows = response.data || response.rows || [];
|
|
|
|
|
// 后端已聚合,直接使用
|
|
|
|
|
this.deviceParams = rows;
|
|
|
|
|
this.paramLoading = false;
|
|
|
|
|
}).catch(() => {
|
|
|
|
|
this.paramLoading = false;
|
|
|
|
|
this.deviceParams = [];
|
|
|
|
|
this.recentRecords = [];
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
/** 加载设备列表 */
|
|
|
|
|
@ -302,37 +265,14 @@ export default {
|
|
|
|
|
/** 加载设备参数 */
|
|
|
|
|
loadDeviceParams(deviceCode) {
|
|
|
|
|
this.paramLoading = true;
|
|
|
|
|
getLatestVal({ deviceCode: deviceCode }).then(response => {
|
|
|
|
|
const rows = response.rows || response.data || response || [];
|
|
|
|
|
// 按参数聚合,取最新值
|
|
|
|
|
const paramMap = {};
|
|
|
|
|
rows.forEach(r => {
|
|
|
|
|
const pKey = r.paramCode || r.paramName || '未知参数';
|
|
|
|
|
const time = r.recordTime || r.collectTime;
|
|
|
|
|
const ts = time ? new Date(time).getTime() : 0;
|
|
|
|
|
const prev = paramMap[pKey];
|
|
|
|
|
const prevTs = prev ? new Date(prev.recordTime || prev.collectTime || 0).getTime() : -1;
|
|
|
|
|
if (!prev || ts >= prevTs) {
|
|
|
|
|
paramMap[pKey] = r;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
// 转为数组并排序
|
|
|
|
|
this.deviceParams = Object.values(paramMap).sort((a, b) => {
|
|
|
|
|
const an = a.paramName || a.paramCode || '';
|
|
|
|
|
const bn = b.paramName || b.paramCode || '';
|
|
|
|
|
return an.localeCompare(bn);
|
|
|
|
|
});
|
|
|
|
|
// 最近记录(按时间倒序)
|
|
|
|
|
this.recentRecords = rows.slice().sort((a, b) => {
|
|
|
|
|
const ta = new Date(a.recordTime || a.collectTime || 0).getTime();
|
|
|
|
|
const tb = new Date(b.recordTime || b.collectTime || 0).getTime();
|
|
|
|
|
return tb - ta;
|
|
|
|
|
}).slice(0, 20);
|
|
|
|
|
getLatestVal({ deviceCode }).then(response => {
|
|
|
|
|
const rows = response.data || response.rows || [];
|
|
|
|
|
// 后端已聚合,直接使用
|
|
|
|
|
this.deviceParams = rows;
|
|
|
|
|
this.paramLoading = false;
|
|
|
|
|
}).catch(() => {
|
|
|
|
|
this.paramLoading = false;
|
|
|
|
|
this.deviceParams = [];
|
|
|
|
|
this.recentRecords = [];
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -346,9 +286,9 @@ export default {
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
align-items: center;
|
|
|
|
|
padding: 12px 16px;
|
|
|
|
|
background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%);
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
background: #1890ff;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
color: #fff;
|
|
|
|
|
}
|
|
|
|
|
.page-title {
|
|
|
|
|
@ -375,11 +315,11 @@ export default {
|
|
|
|
|
|
|
|
|
|
/* 左侧设备列表面板 */
|
|
|
|
|
.device-list-panel {
|
|
|
|
|
width: 200px;
|
|
|
|
|
width: 180px;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
background: #fff;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
border: 1px solid #e8e8e8;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
@ -408,14 +348,12 @@ export default {
|
|
|
|
|
padding: 6px;
|
|
|
|
|
}
|
|
|
|
|
.device-item {
|
|
|
|
|
padding: 8px 12px;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
padding: 8px 10px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: all 0.2s;
|
|
|
|
|
margin-bottom: 2px;
|
|
|
|
|
border-bottom: 1px solid #f0f0f0;
|
|
|
|
|
}
|
|
|
|
|
.device-item:hover {
|
|
|
|
|
background: #f5f7fa;
|
|
|
|
|
background: #f5f5f5;
|
|
|
|
|
}
|
|
|
|
|
.device-item.active {
|
|
|
|
|
background: #e6f7ff;
|
|
|
|
|
@ -446,9 +384,9 @@ export default {
|
|
|
|
|
/* 设备信息卡片 */
|
|
|
|
|
.device-info-card {
|
|
|
|
|
background: #fff;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
|
|
|
|
padding: 16px;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
border: 1px solid #e8e8e8;
|
|
|
|
|
padding: 12px;
|
|
|
|
|
}
|
|
|
|
|
.device-card-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
@ -488,9 +426,9 @@ export default {
|
|
|
|
|
/* 参数列表区域 */
|
|
|
|
|
.param-list-section {
|
|
|
|
|
background: #fff;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
|
|
|
|
padding: 16px;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
border: 1px solid #e8e8e8;
|
|
|
|
|
padding: 12px;
|
|
|
|
|
}
|
|
|
|
|
.section-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
@ -510,14 +448,9 @@ export default {
|
|
|
|
|
min-height: 100px;
|
|
|
|
|
}
|
|
|
|
|
.param-card {
|
|
|
|
|
background: linear-gradient(135deg, #f5f7fa 0%, #e8ecf1 100%);
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
padding: 12px;
|
|
|
|
|
transition: all 0.2s;
|
|
|
|
|
}
|
|
|
|
|
.param-card:hover {
|
|
|
|
|
transform: translateY(-2px);
|
|
|
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
|
|
|
background: #f5f7fa;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
padding: 10px;
|
|
|
|
|
}
|
|
|
|
|
.param-card-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
@ -577,32 +510,4 @@ export default {
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 最近记录区域 */
|
|
|
|
|
.recent-records-section {
|
|
|
|
|
background: #fff;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
|
|
|
|
padding: 16px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 未选择设备提示 */
|
|
|
|
|
.no-selection {
|
|
|
|
|
flex: 1;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
background: #fff;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
|
|
|
|
color: #999;
|
|
|
|
|
}
|
|
|
|
|
.no-selection i {
|
|
|
|
|
font-size: 64px;
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
color: #d9d9d9;
|
|
|
|
|
}
|
|
|
|
|
.no-selection p {
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
|