450 lines
10 KiB
Vue
450 lines
10 KiB
Vue
<!--
|
|
filename: index.vue
|
|
author: liubin
|
|
date: 2023-09-04 09:34:52
|
|
description: 设备状态时序图
|
|
-->
|
|
|
|
<template>
|
|
<div class="app-container">
|
|
<h1>设备状态时序图</h1>
|
|
<!-- 搜索工作栏 -->
|
|
<SearchBar
|
|
:formConfigs="searchBarFormConfig"
|
|
ref="search-bar"
|
|
@headBtnClick="handleSearchBarBtnClick" />
|
|
|
|
<div class="main-area">
|
|
<div class="legend-row">
|
|
<div class="legend">
|
|
<div class="icon running"></div>
|
|
<div>运行中</div>
|
|
</div>
|
|
<!-- <div class="legend">
|
|
<div class="icon waiting"></div>
|
|
<div>待机</div>
|
|
</div> -->
|
|
<div class="legend">
|
|
<div class="icon fault"></div>
|
|
<div>故障</div>
|
|
</div>
|
|
<!-- <div class="legend">
|
|
<div class="icon lack"></div>
|
|
<div>缺料</div>
|
|
</div>
|
|
<div class="legend">
|
|
<div class="icon full"></div>
|
|
<div>满料</div>
|
|
</div> -->
|
|
<div class="legend">
|
|
<div class="icon stop"></div>
|
|
<div>计划停机</div>
|
|
</div>
|
|
</div>
|
|
<div class="graphs" v-if="graphList.length">
|
|
<!-- <div class="graph">
|
|
<h2 class="graph-title">设备1</h2>
|
|
<div class="graph-item running tick" data-time="00:00"></div>
|
|
<div class="graph-item running"></div>
|
|
<div class="graph-item running"></div>
|
|
<div class="graph-item lack tick" data-time="03:00"></div>
|
|
<div class="graph-item full tick" data-time="04:00"></div>
|
|
<div class="graph-item waiting tick" data-time="05:00"></div>
|
|
<div class="graph-item running tick" data-time="06:00"></div>
|
|
<div class="graph-item running"></div>
|
|
<div class="graph-item fault tick" data-time="08:00"></div>
|
|
<div class="graph-item waiting tick" data-time="09:00"></div>
|
|
<div class="graph-item running tick" data-time="10:00"></div>
|
|
<div class="graph-item running"></div>
|
|
<div class="graph-item running"></div>
|
|
<div class="graph-item lack tick" data-time="13:00"></div>
|
|
<div class="graph-item full tick" data-time="14:00"></div>
|
|
<div class="graph-item running tick" data-time="15:00"></div>
|
|
<div class="graph-item running"></div>
|
|
<div class="graph-item running"></div>
|
|
<div class="graph-item fault tick" data-time="18:00"></div>
|
|
<div class="graph-item running tick" data-time="19:00"></div>
|
|
<div class="graph-item running"></div>
|
|
<div class="graph-item running"></div>
|
|
<div class="graph-item running"></div>
|
|
<div class="graph-item stop tick" data-time="23:00"></div>
|
|
</div> -->
|
|
<div class="graph" v-for="eq in graphList" :key="eq.key">
|
|
<h2 class="graph-title">{{ eq.key }}</h2>
|
|
<div
|
|
v-for="blc in eq"
|
|
:key="blc.startTime"
|
|
class="graph-item-fixed tick"
|
|
:class="{
|
|
running: blc.status == 0,
|
|
fault: blc.status == 2,
|
|
stop: blc.status == 1,
|
|
}"
|
|
:style="{ width: blc.duration * 2 + 'px' }"
|
|
:data-time="new Date(blc.startTime).toLocaleTimeString()"></div>
|
|
</div>
|
|
<!-- <div class="graph">
|
|
<h2 class="graph-title">设备3</h2>
|
|
<div class="graph-item"></div>
|
|
<div class="graph-item"></div>
|
|
<div class="graph-item"></div>
|
|
<div class="graph-item"></div>
|
|
<div class="graph-item"></div>
|
|
<div class="graph-item"></div>
|
|
<div class="graph-item"></div>
|
|
<div class="graph-item"></div>
|
|
<div class="graph-item"></div>
|
|
<div class="graph-item"></div>
|
|
<div class="graph-item"></div>
|
|
<div class="graph-item"></div>
|
|
<div class="graph-item"></div>
|
|
<div class="graph-item"></div>
|
|
<div class="graph-item"></div>
|
|
<div class="graph-item"></div>
|
|
<div class="graph-item"></div>
|
|
<div class="graph-item"></div>
|
|
<div class="graph-item"></div>
|
|
<div class="graph-item"></div>
|
|
<div class="graph-item"></div>
|
|
<div class="graph-item"></div>
|
|
<div class="graph-item"></div>
|
|
<div class="graph-item"></div>
|
|
</div> -->
|
|
</div>
|
|
<h2 v-else>请添加设备</h2>
|
|
</div>
|
|
|
|
<!-- 对话框(添加 / 修改) -->
|
|
<base-dialog
|
|
dialogTitle="添加设备"
|
|
:dialogVisible="open"
|
|
width="500px"
|
|
@close="open = false"
|
|
@cancel="open = false"
|
|
@confirm="submitForm">
|
|
<el-select
|
|
v-if="open"
|
|
style="width: 100%"
|
|
v-model="queryParams.equipmentId"
|
|
placeholder="请选择一个设备">
|
|
<el-option
|
|
v-for="eq in eqList"
|
|
:key="eq.id"
|
|
:value="eq.id"
|
|
:label="eq.name"></el-option>
|
|
</el-select>
|
|
</base-dialog>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
export default {
|
|
name: 'SGStatus',
|
|
components: {},
|
|
props: {},
|
|
data() {
|
|
return {
|
|
searchBarFormConfig: [
|
|
{
|
|
type: 'select',
|
|
label: '产线',
|
|
placeholder: '请选择产线',
|
|
selectOptions: [],
|
|
param: 'lineId',
|
|
},
|
|
{
|
|
type: 'select',
|
|
label: '工段',
|
|
placeholder: '请选择工段',
|
|
selectOptions: [],
|
|
param: 'sectionId',
|
|
},
|
|
// 时间段
|
|
{
|
|
type: 'datePicker',
|
|
label: '时间段',
|
|
dateType: 'daterange', // datetimerange
|
|
// format: 'yyyy-MM-dd HH:mm:ss',
|
|
format: 'yyyy-MM-dd',
|
|
valueFormat: 'yyyy-MM-dd HH:mm:ss',
|
|
// valueFormat: 'timestamp',
|
|
rangeSeparator: '-',
|
|
startPlaceholder: '开始日期',
|
|
endPlaceholder: '结束日期',
|
|
defaultTime: ['00:00:00', '23:59:59'],
|
|
param: 'recordTime',
|
|
},
|
|
{
|
|
type: 'button',
|
|
btnName: '查询',
|
|
name: 'search',
|
|
color: 'primary',
|
|
},
|
|
{
|
|
type: 'separate',
|
|
},
|
|
{
|
|
type: 'button',
|
|
btnName: '设备对比',
|
|
name: 'compare',
|
|
color: 'warning',
|
|
plain: true,
|
|
},
|
|
],
|
|
queryParams: {
|
|
lineId: null,
|
|
sectionId: null,
|
|
equipmentId: null,
|
|
recordTime: [],
|
|
},
|
|
graphList: [],
|
|
open: false,
|
|
eqList: [],
|
|
// demo: [
|
|
// [
|
|
// {
|
|
// equipmentName: '下片机',
|
|
// duration: 30,
|
|
// relativeDuration: 0.6,
|
|
// status: 0,
|
|
// startPos: 0,
|
|
// startTime: 1691568181000,
|
|
// },
|
|
// {
|
|
// equipmentName: '下片机',
|
|
// duration: 20,
|
|
// relativeDuration: 0.4,
|
|
// status: 2,
|
|
// startPos: 30,
|
|
// startTime: 1691569981000
|
|
// },
|
|
// ],
|
|
// ],
|
|
};
|
|
},
|
|
computed: {},
|
|
created() {
|
|
this.initProductline();
|
|
this.initWorksection();
|
|
this.initEquipment();
|
|
this.getList();
|
|
},
|
|
methods: {
|
|
/** 重置查询条件 */
|
|
initQuery() {
|
|
this.queryParams.lineId = null;
|
|
this.queryParams.equipmentId = null;
|
|
this.queryParams.sectionId = null;
|
|
this.queryParams.recordTime = [];
|
|
},
|
|
|
|
/** 对象到数组的转换 */
|
|
objectToArray(obj) {
|
|
return Object.keys(obj).map((key) => {
|
|
obj[key].sort((a, b) => a.startTime - b.startTime);
|
|
obj[key].key = key;
|
|
return obj[key];
|
|
});
|
|
},
|
|
|
|
async getList() {
|
|
const { code, data } = await this.$axios({
|
|
url: '/analysis/equipment-analysis/status',
|
|
method: 'get',
|
|
params: this.queryParams,
|
|
});
|
|
if (code == 0) {
|
|
this.graphList = this.objectToArray(data);
|
|
console.log('graph list', this.graphList);
|
|
}
|
|
},
|
|
|
|
/** 准备设备数据 */
|
|
async initEquipment() {
|
|
const { code, data } = await this.$axios({
|
|
url: '/base/equipment/listAll',
|
|
method: 'get',
|
|
});
|
|
if (code == 0) {
|
|
this.eqList = data.map((item) => {
|
|
return {
|
|
name: item.name,
|
|
id: item.id,
|
|
};
|
|
});
|
|
}
|
|
},
|
|
|
|
/** 准备产线数据 */
|
|
async initProductline() {
|
|
const { code, data } = await this.$axios({
|
|
url: '/base/production-line/listAll',
|
|
method: 'get',
|
|
});
|
|
if (code == 0) {
|
|
this.searchBarFormConfig[0].selectOptions = data.map((item) => {
|
|
return {
|
|
name: item.name,
|
|
id: item.id,
|
|
};
|
|
});
|
|
}
|
|
},
|
|
|
|
/** 准备工段数据 */
|
|
async initWorksection() {
|
|
const { code, data } = await this.$axios({
|
|
url: '/base/workshop-section/listAll',
|
|
method: 'get',
|
|
});
|
|
if (code == 0) {
|
|
this.searchBarFormConfig[1].selectOptions = data.map((item) => {
|
|
return {
|
|
name: item.name,
|
|
id: item.id,
|
|
};
|
|
});
|
|
}
|
|
},
|
|
|
|
handleSearchBarBtnClick({ btnName, ...payload }) {
|
|
switch (btnName) {
|
|
case 'search':
|
|
this.queryParams.lineId = payload.lineId || null;
|
|
this.queryParams.sectionId = payload.sectionId || null;
|
|
this.queryParams.equipmentId = payload.equipmentId || null;
|
|
this.queryParams.recordTime = payload.recordTime || null;
|
|
this.getList();
|
|
break;
|
|
case 'compare':
|
|
this.open = true;
|
|
break;
|
|
}
|
|
},
|
|
|
|
async submitForm() {
|
|
const { code, data } = await this.$axios({
|
|
url: '/analysis/equipment-analysis/status',
|
|
method: 'get',
|
|
params: this.queryParams,
|
|
});
|
|
if (code == 0) {
|
|
const newEqlist = this.objectToArray(data);
|
|
if (!newEqlist || newEqlist.length == 0) {
|
|
this.$message.error('该设备没有状态数据');
|
|
return;
|
|
}
|
|
this.graphList.push(newEqlist[0]);
|
|
}
|
|
this.open = false;
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.graph {
|
|
border: 1px solid #ccc;
|
|
padding: 12px 12px 28px 12px;
|
|
margin: 64px 0;
|
|
position: relative;
|
|
display: flex;
|
|
}
|
|
|
|
.graph-title {
|
|
position: absolute;
|
|
top: -64px;
|
|
left: -1px;
|
|
padding: 8px 18px;
|
|
background: #ccc;
|
|
font-size: 18px;
|
|
line-height: 1;
|
|
}
|
|
|
|
.graph-item,
|
|
.graph-item-fixed {
|
|
height: 88px;
|
|
// width: 24px;
|
|
flex: 1;
|
|
// border: 1px solid #ccc;
|
|
position: relative;
|
|
}
|
|
|
|
.graph-item-fixed {
|
|
flex: unset;
|
|
}
|
|
|
|
.graph-item::before,
|
|
.graph-item-fixed::before {
|
|
position: absolute;
|
|
bottom: -16px;
|
|
left: 0;
|
|
content: attr(data-time);
|
|
// font-size - js
|
|
// rotate - js
|
|
// color - js, default:
|
|
color: #777;
|
|
transform-origin: left top;
|
|
transform: rotate(12deg);
|
|
}
|
|
|
|
.graph-item-fixed::after,
|
|
.graph-item::after {
|
|
content: '';
|
|
position: absolute;
|
|
left: 0;
|
|
bottom: -3px;
|
|
display: inline-block;
|
|
}
|
|
|
|
.graph-item.tick::after,
|
|
.graph-item-fixed.tick::after {
|
|
width: 1px;
|
|
height: 6px;
|
|
border-left: 1px solid #777;
|
|
}
|
|
|
|
.running {
|
|
background-color: #84f04e;
|
|
}
|
|
|
|
.waiting {
|
|
background-color: #409eff;
|
|
}
|
|
|
|
.fault {
|
|
background-color: #ea5b5b;
|
|
}
|
|
|
|
.full {
|
|
background-color: #e6a23c;
|
|
}
|
|
|
|
.lack {
|
|
background-color: #a69c8d;
|
|
}
|
|
|
|
.stop {
|
|
background-color: #000c;
|
|
}
|
|
|
|
.legend-row {
|
|
margin: 12px 0;
|
|
display: flex;
|
|
> .legend:not(:last-child) {
|
|
margin-right: 12px;
|
|
}
|
|
|
|
.legend {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.icon {
|
|
width: 16px;
|
|
height: 16px;
|
|
margin-right: 8px;
|
|
}
|
|
}
|
|
</style>
|