このコミットが含まれているのは:
lb 2023-10-09 13:32:33 +08:00
コミット 6e390bf12a
4個のファイルの変更7行の追加861行の削除

ファイルの表示

@ -96,18 +96,7 @@ export default class GanttGraph {
this.currentGraphIndex = -2;
this.startTime = new Date(startTime);
// this.startTime = new Date(new Date('2023/10/8').toLocaleDateString());
console.log('<> Gantt Created', this.startTime);
// this.grid.push(this.makeGrid())
// this.xAxis.push(...this.makeXaxis())
// this.yAxis.push(...this.makeYaxis("设备1"))
// this.series.push(...this.makeSeries({ equipmentName: "设备1" }))
// this.grid.push(this.makeGrid())
// this.xAxis.push(...this.makeXaxis())
// this.yAxis.push(...this.makeYaxis("设备2"))
// this.series.push(...this.makeSeries({ equipmentName: "设备2" }))
// console.log('<> Gantt Created', this.startTime);
}
// 构造一个新的 grid
@ -223,8 +212,8 @@ export default class GanttGraph {
value: [0, bgStartTime, bgEndTime, 0],
tooltip: { show: false },
itemStyle: {
color: '#ccc',
opacity: 0.3,
color: '#F2F4F9',
// opacity: 0.3,
}
},
]

ファイルの表示

@ -1,235 +0,0 @@
import * as echarts from 'echarts';
function renderItem(params, api) {
var categoryIndex = api.value(0);
var start = api.coord([api.value(1), categoryIndex]);
var end = api.coord([api.value(2), categoryIndex]);
var height = api.size([0, 1])[1] * 1;
// var height = api.size([0, 1])[1] * 0.8;
// var height = 56;
var rectShape = echarts.graphic.clipRectByRect(
{
x: start[0],
y: start[1] - height / 2,
width: end[0] - start[0],
height: height,
},
{
x: params.coordSys.x,
y: params.coordSys.y,
width: params.coordSys.width,
height: params.coordSys.height,
}
);
return (
rectShape && {
type: 'rect',
transition: ['shape'],
shape: rectShape,
style: api.style(),
}
);
}
/** 颜色配置 */
const types = [
{ name: '运行', color: '#5ad8a6' },
{ name: '故障', color: '#fc9c91' },
{ name: '计划停机', color: '#000' },
];
/** 从时间戳获取 startTime */
function getStartTime(timestamp) {
return new Date(new Date(timestamp).toLocaleDateString()).getTime();
}
export default class GanttGraph {
constructor(el) {
this.chart = null;
this.el = el;
/** 默认配置 */
this.grid = {
top: 32,
left: 128,
right: 128,
bottom: 64,
}
this.tooltip = {
formatter: function (params) {
return (
params.marker +
params.name +
': ' +
new Date(params.value[1]).toLocaleTimeString() +
' - ' +
new Date(params.value[2]).toLocaleTimeString()
);
},
}
this.xAxis = {
type: 'time',
min: getStartTime(1691568181000), // <===
max: getStartTime(1691568181000 + 3600 * 24 * 1000), // <===
splitNumber: 10,
axisLabel: {
// rotate: -15,
formatter: function (val) {
return new Date(val).toLocaleTimeString();
},
},
axisTick: {
show: true,
},
splitLine: {
show: false,
},
}
this.yAxis = [
{
interval: 40,
axisLine: {
lineStyle: {
color: '',
},
},
axisLabel: {
fontSize: 18,
},
axisTick: {
show: false,
},
splitLine: {
show: true,
},
// data: [], // <====
data: ['设备1', '设备2', '设备3', '设备4'],
},
{
axisLine: {
lineStyle: {
color: '',
},
},
data: [],
},
]
this.series = [
{
type: 'custom',
renderItem: renderItem,
itemStyle: {
opacity: 0.8,
},
encode: {
x: [1, 2],
y: 0,
},
// data: [], // <===
data: [
{
name: '运行',
value: [
0,
1691568181000,
1691568181000 + 60 * 60 * 1000,
60 * 10 * 1000,
],
itemStyle: {
normal: {
color: types[0].color,
},
},
},
{
name: '计划停机',
value: [
0,
1691578581000,
1691578581000 + 10 * 60 * 1000,
60 * 10 * 1000,
],
itemStyle: {
normal: {
color: types[2].color,
},
},
},
{
name: '运行',
value: [
1,
1691568181000,
1691568181000 + 60 * 60 * 1000,
60 * 10 * 1000,
],
itemStyle: {
normal: {
color: types[0].color,
},
},
},
{
name: '故障',
value: [
2,
1691538181000,
1691538181000 + 60 * 60 * 1000,
60 * 10 * 1000,
],
itemStyle: {
normal: {
color: types[1].color,
},
},
},
{
name: '运行',
value: [
2,
1691578181000,
1691578181000 + 90 * 60 * 1000,
90 * 10 * 1000,
],
itemStyle: {
normal: {
color: types[0].color,
},
},
},
{
name: '计划停机',
value: [
3,
1691528181000,
1691528181000 + 240 * 60 * 1000,
240 * 10 * 1000,
],
itemStyle: {
normal: {
color: types[2].color,
},
},
},
],
},
]
}
init() {
this.chart = echarts.init(this.el);
this.chart.setOption(this.getOption())
}
getOption() {
const { grid, xAxis, yAxis, series, tooltip } = this;
return {
grid, xAxis, yAxis, series, tooltip
}
}
}

ファイルの表示

@ -1,607 +0,0 @@
<!--
filename: index.vue
author: liubin
date: 2023-09-04 09:34:52
description: 设备状态时序图
-->
<template>
<div
class="status-timegraph-container"
style="background: #f2f4f9; flex: 1; display: flex; flex-direction: column">
<el-row
class=""
style="
margin-bottom: 12px;
background: #fff;
padding: 16px 16px 0;
border-radius: 8px;
">
<div class="blue-title">生产节拍时序图</div>
<!-- <h1>设备状态时序图</h1> -->
<!-- 搜索工作栏 -->
<SearchBar
:formConfigs="searchBarFormConfig"
ref="search-bar"
:remove-blue="true"
@select-changed="handleSearchBarSelectChange"
@headBtnClick="handleSearchBarBtnClick" />
</el-row>
<el-row
class=""
style="
height: 1px;
flex: 1;
margin-bottom: 12px;
background: #fff;
padding: 16px 16px 32px;
border-radius: 8px;
display: flex;
flex-direction: column;
">
<el-row :gutter="20">
<el-col :span="6">
<div class="blue-title">设备状态时序图</div>
</el-col>
<el-col :span="18" 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>
</el-col>
</el-row>
<div
class="main-area"
style="flex: 1; display: flex; flex-direction: column">
<div
class="graphs"
v-show="graphList.length"
id="status-chart"
style="height: 1px; flex: 1"></div>
<h2 v-if="!graphList || graphList.length == 0" class="no-data-bg"></h2>
</div>
</el-row>
<!-- 对话框(添加 / 修改) -->
<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>
// import * as echarts from 'echarts';
import Gantt from './gantt';
export default {
name: 'SGStatus',
components: {},
props: {},
data() {
return {
chart: null,
searchBarFormConfig: [
{
type: 'select',
label: '产线',
placeholder: '请选择产线',
selectOptions: [],
param: 'lineId',
onchange: true,
},
{
type: 'select',
label: '工段',
placeholder: '请选择工段',
selectOptions: [],
param: 'sectionId',
},
//
{
type: 'datePicker',
label: '时间段',
dateType: 'date',
format: 'yyyy-MM-dd',
valueFormat: 'yyyy-MM-dd HH:mm:ss',
rangeSeparator: '-',
placeholder: '选择日期',
param: 'recordTime',
required: true,
},
{
type: 'button',
btnName: '查询',
name: 'search',
color: 'primary',
},
{
type: 'separate',
},
{
type: 'button',
btnName: '添加对比',
name: 'compare',
color: 'primary',
plain: true,
},
],
queryParams: {
lineId: null,
sectionId: null,
equipmentId: null,
recordTime: null,
},
graphList: [],
open: false,
eqList: [],
startTime: null,
gantt: null
// 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();
},
mounted() {},
watch: {
graphList: {
handler(val) {
if (val && val.length) {
this.$nextTick(() => {
if (!this.chart) this.initChart();
this.setInitialConfig();
this.handleGraphList();
});
}
return;
},
deep: true,
immediate: true,
},
},
methods: {
setInitialConfig() {
console.log('in setInitialConfig', this.chartOption);
this.chart.setOption(this.chartOption);
},
handleGraphList() {
console.log('in handleGraphList:', this.graphList);
return;
const min = this.queryParams.recordTime
? new Date(this.queryParams.recordTime).getTime()
: this.findMin();
console.log('min is', min);
this.chartOption.xAxis.min = getStartTime(min);
this.chartOption.xAxis.max = getStartTime(min + 3600 * 24 * 1000);
this.graphList.forEach((arr) => {
this.chartOption.yAxis[0].data.push(arr.key);
arr.forEach((item) => {
this.chartOption.series[0].data.push({
name: ['运行', '故障', '计划停机'][item.status],
value: [
0,
item.startTime,
item.startTime + item.duration * 60 * 1000,
item.duration * 60 * 1000,
],
itemStyle: {
normal: {
color: types[item.status].color,
},
},
});
});
console.log('chartOptions', this.chartOption);
});
},
findMin() {
let min = 0;
this.graphList.forEach((arr) => {
arr.forEach((item) => {
if (min < item.startTime) min = item.startTime;
});
});
return min;
},
initChart() {
const el = document.getElementById('status-chart');
this.gantt = new Gantt(el);
this.gantt.init();
},
/** 重置查询条件 */
initQuery() {
this.queryParams.lineId = null;
this.queryParams.equipmentId = null;
this.queryParams.sectionId = null;
this.queryParams.recordTime = null;
},
/** 对象到数组的转换 */
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,
};
});
}
},
handleSearchBarSelectChange({ param, value }) {
if (!value) {
this.searchBarFormConfig[1].selectOptions = [];
return;
}
switch (param) {
case 'lineId':
this.$axios({
url: '/base/workshop-section/listByParentId',
method: 'get',
params: {
id: value,
},
}).then(({ code, data }) => {
if (code == 0) {
this.searchBarFormConfig[1].selectOptions = data.map((item) => {
return {
name: item.name,
id: item.id,
};
});
}
});
}
},
handleSearchBarBtnClick({ btnName, ...payload }) {
switch (btnName) {
case 'search':
if (!payload.recordTime || payload.recordTime.length <= 0) {
this.$message.error('请选择时间段');
return;
}
this.startTime = new Date(payload.recordTime).getTime();
this.queryParams.lineId = payload.lineId || null;
this.queryParams.sectionId = payload.sectionId || null;
this.queryParams.equipmentId = payload.equipmentId || null;
this.queryParams.recordTime = payload.recordTime
? [
payload.recordTime,
new Date(
new Date(payload.recordTime).getTime() + 24 * 3600 * 1000
)
.toLocaleDateString()
.split('/')
.map((value, index) => {
if (index == 1 || index == 2) {
return value.padStart(2, '0');
}
return value;
})
.join('-') + ' 00:00:00',
]
: 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;
padding: 0 12px;
font-size: 14px;
line-height: 1;
}
.graph-content {
display: flex;
flex: 1;
padding: 22px 12px;
border: 1px solid #ccc;
border-bottom-width: 2px;
border-top: none;
position: relative;
}
.graph-content::after,
.graph-content::before {
content: '';
position: absolute;
width: 3px;
height: 80%;
background: #fff;
right: -1px;
top: 0;
}
.graph-content::before {
right: unset;
left: -1px;
}
.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: #5ad8a6;
// background-color: #84f04e;
}
.waiting {
background-color: #5ad8a6;
// background-color: #409eff;
}
.fault {
// background-color: #ea5b5b;
background-color: #fc9c91;
}
.full {
// background-color: #e6a23c;
background-color: #598fff;
}
.lack {
// background-color: #a69c8d;
background-color: #7585a2;
}
.stop {
background-color: #000;
}
.legend-row {
margin: 6px 0;
padding-right: 12px;
display: flex;
justify-content: flex-end;
> .legend:not(:last-child) {
margin-right: 12px;
}
.legend {
display: flex;
align-items: center;
}
.icon {
width: 8px;
height: 8px;
border-radius: 2px;
margin-right: 4px;
margin-top: 1px;
}
}
.blue-title {
position: relative;
padding: 4px 0;
padding-left: 12px;
font-size: 14px;
color: #606266;
font-weight: 700;
margin-bottom: 12px;
&::before {
content: '';
position: absolute;
left: 0;
top: 6px;
height: 16px;
width: 4px;
border-radius: 1px;
background: #0b58ff;
}
}
.echarts__status-chart {
background: #ccc;
}
.echarts__status-chart > div {
height: 100% !important;
width: 100% !important;
}
</style>

ファイルの表示

@ -64,8 +64,7 @@
</template>
<script>
// import * as echarts from 'echarts';
import Gantt from './demo';
import Gantt from './chart';
export default {
name: 'SGStatus',
@ -445,7 +444,7 @@ export default {
}
.running {
background-color: #5ad8a6;
background-color: #288AFF;
// background-color: #84f04e;
}
@ -456,7 +455,7 @@ export default {
.fault {
// background-color: #ea5b5b;
background-color: #fc9c91;
background-color: #FC9C91;
}
.full {
@ -470,7 +469,7 @@ export default {
}
.stop {
background-color: #000;
background-color: #FFDC94;
}
.legend-row {