Merge branch 'equipment-module-dev' into lb

This commit is contained in:
lb 2023-09-11 15:00:49 +08:00
commit 6e678f7c68
14 changed files with 2447 additions and 366 deletions

View File

@ -31,7 +31,6 @@ export default {
// dialogFormConfig: [], // 占位 // dialogFormConfig: [], // 占位
}; };
}, },
mounted() {},
methods: { methods: {
// 过滤后端传回的详情数据 // 过滤后端传回的详情数据
filterData(data, keys) { filterData(data, keys) {

View File

@ -0,0 +1,98 @@
<!--
filename: pieChart.vue
author: liubin
date: 2023-09-06 15:02:49
description: 饼图
-->
<template>
<div class="pie-chart" :data-eqname="value.equipmentName || 'Default'"></div>
</template>
<script>
import * as echarts from 'echarts';
export default {
name: 'pieChart',
components: {},
props: ['value'],
data() {
return {
chart: null,
config: {
grid: {
top: 0,
left: 0,
right: 0,
bottom: 0,
},
tooltip: {
trigger: 'item',
},
legend: {
top: '0%',
left: 'center',
textStyle: {
fontSize: 10,
},
itemWidth: 10,
itemHeight: 10,
},
series: [
{
name: this.value.equipmentName || 'Default',
type: 'pie',
radius: ['40%', '75%'],
avoidLabelOverlap: false,
label: {
show: false,
position: 'center',
},
data: ['workTime', 'stopTime', 'downTime'].map((v, index) => ({
name: ['工作时长', '停机时长', '故障时长'][index],
value: this.value[v],
})),
// data: [
// { value: 1048, name: 'Search Engine' },
// { value: 735, name: 'Direct' },
// { value: 580, name: 'Email' },
// { value: 484, name: 'Union Ads' },
// { value: 300, name: 'Video Ads' },
// ],
},
],
},
};
},
mounted() {
if (!this.chart) {
this.chart = echarts.init(this.$el);
this.$nextTick(() => {
this.chart.setOption(this.config);
});
}
},
beforeDestroy() {
if (this.chart) this.chart.dispose();
},
methods: {},
};
</script>
<style scoped lang="scss">
.pie-chart {
padding: 12px;
min-height: 320px;
background: #f1f1f1;
position: relative;
}
.pie-chart::before {
content: attr(data-eqname);
font-size: 16px;
line-height: 1;
position: absolute;
top: -16px;
left: 0;
}
</style>

View File

@ -0,0 +1,419 @@
<!--
filename: index.vue
author: liubin
date: 2023-09-04 09:34:52
description: 设备效率分析
-->
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<SearchBar
:formConfigs="searchBarFormConfig"
ref="search-bar"
@headBtnClick="handleSearchBarBtnClick" />
<!-- 列表 -->
<base-table
class="base-table__margin"
:table-props="tableProps"
:table-data="list"
@emitFun="handleEmitFun">
<!-- :page="queryParams.pageNo"
:limit="queryParams.pageSize" -->
<!-- <method-btn
v-if="tableBtn.length"
slot="handleBtn"
label="操作"
:method-list="tableBtn"
@clickBtn="handleTableBtnClick" /> -->
</base-table>
<!-- 分页组件 -->
<!-- <pagination
v-show="total > 0"
:total="total"
:page.sync="queryParams.pageNo"
:limit.sync="queryParams.pageSize"
@pagination="getList" /> -->
<!-- 对话框(添加 / 修改) -->
<base-dialog
:dialogTitle="visualizationOpen ? '设备可视化' : '查看趋势'"
:dialogVisible="open"
:width="visualizationOpen ? '80%' : '700px'"
@closed="closed"
@close="cancel"
@cancel="cancel"
@confirm="submitForm">
<div class="visualization" v-if="visualizationOpen">
<pie-chart v-for="item in list" :key="item.id" :value="item" />
</div>
<div v-if="trendOpen">
<h1>查看趋势</h1>
</div>
</base-dialog>
</div>
</template>
<script>
// import moment from 'moment';
import basicPageMixin from '@/mixins/lb/basicPageMixin';
import PieChart from './components/pieChart.vue';
export default {
name: 'EfficiencyAnalysis',
mixins: [basicPageMixin],
components: { PieChart },
props: {},
data() {
return {
open: false,
visualizationOpen: false,
trendOpen: false,
// tableBtn: [
// this.$auth.hasPermi('base:equipment-group:update')
// ? {
// type: 'edit',
// btnName: '',
// }
// : undefined,
// this.$auth.hasPermi('base:equipment-group:delete')
// ? {
// type: 'delete',
// btnName: '',
// }
// : undefined,
// ].filter((v) => v),
tableProps: [
{ prop: 'factoryName', label: '工厂', align: 'center' },
{ prop: 'lineName', label: '产线', align: 'center' },
{ prop: 'sectionName', label: '工段', align: 'center' },
{ prop: 'equipmentName', label: '设备', align: 'center' },
{
label: '有效时间',
align: 'center',
children: [
{
width: 128,
prop: 'workTime',
label: '工作时长',
align: 'center',
},
{
width: 128,
prop: 'workRate',
label: '百分比',
align: 'center',
filter: (val) => (val != null ? +val.toFixed(3) : '-'),
},
],
},
{
label: '关机时间',
align: 'center',
children: [
{
width: 128,
prop: 'stopTime',
label: '停机时长',
align: 'center',
},
{ width: 128, prop: 'stopRate', label: '百分比', align: 'center' },
],
},
{
label: '中断损失',
align: 'center',
children: [
{
width: 128,
prop: 'downTime',
label: '故障时长',
align: 'center',
filter: (val) => (val != null ? +val.toFixed(3) : '-'),
},
{ width: 128, prop: 'downRate', label: '百分比', align: 'center' },
{
width: 128,
prop: 'timeEfficiency',
label: '时间开动率',
align: 'center',
filter: (val) => (val != null ? +val.toFixed(3) : '-'),
},
],
},
{
label: '速度损失',
align: 'center',
children: [
{
width: 128,
prop: 'realProcSpeed',
label: '实际加工速度',
align: 'center',
},
{
width: 128,
prop: 'designProcSpeed',
label: '理论加工速度',
align: 'center',
},
{
width: 128,
prop: 'peEfficiency',
label: '速度开动率',
align: 'center',
filter: (val) => (val != null ? +val.toFixed(3) : '-'),
},
],
},
{
prop: 'oee',
label: 'OEE',
align: 'center',
filter: (val) => (val != null ? +val.toFixed(3) : '-'),
},
{
prop: 'teep',
label: 'TEEP',
align: 'center',
filter: (val) => (val != null ? +val.toFixed(3) : '-'),
},
// {
// _action: 'view-trend',
// label: '',
// align: 'center',
// subcomponent: {
// props: ['injectData'],
// render: function (h) {
// const _this = this;
// return h(
// 'el-button',
// {
// props: { type: 'text' },
// on: {
// click: function () {
// console.log('inejctdata', _this.injectData);
// _this.$emit('emitData', {
// action: _this.injectData._action,
// // value: _this.injectData.id,
// value: _this.injectData,
// });
// },
// },
// },
// ''
// );
// },
// },
// },
],
searchBarFormConfig: [
{
type: 'select',
label: '工厂',
placeholder: '请选择工厂',
param: 'factoryId',
selectOptions: [],
},
{
type: 'select',
label: '产线',
placeholder: '请选择产线',
param: 'lineId',
selectOptions: [],
},
//
{
type: 'select',
label: '时间类型',
param: 'dateFilterType',
defaultSelect: 0,
selectOptions: [
{ id: 0, name: '按时间段' },
{ id: 1, name: '按日期' },
],
index: 2,
extraOptions: [
{
parent: 'dateFilterType',
//
type: 'datePicker',
label: '时间段',
dateType: 'daterange',
format: 'yyyy-MM-dd',
valueFormat: 'yyyy-MM-dd HH:mm:ss',
defaultTime: ['00:00:00', '00:00:00'],
rangeSeparator: '-',
startPlaceholder: '开始时间',
endPlaceholder: '结束时间',
param: 'recordTime',
},
{
parent: 'dateFilterType',
//
type: 'datePicker',
label: '日期',
dateType: 'date',
placeholder: '选择日期',
format: 'yyyy-MM-dd',
valueFormat: 'yyyy-MM-dd',
param: 'recordTime',
},
],
},
{
type: 'button',
btnName: '查询',
name: 'search',
color: 'primary',
},
{
type: 'separate',
},
{
type: 'button',
btnName: '设备可视化',
name: 'visualization',
plain: true,
color: 'success',
},
// {
// type: 'button',
// btnName: 'OEE',
// name: 'add',
// plain: true,
// color: 'success',
// },
// {
// type: 'button',
// btnName: 'TEEP',
// name: 'add',
// plain: true,
// color: 'warning',
// },
],
//
open: false,
//
queryParams: {
lineId: null,
factoryId: null,
recordTime: [],
},
//
form: {},
list: [],
};
},
created() {
this.getFactory();
this.getLine();
this.getList();
},
methods: {
/** 准备工厂数据 */
async getFactory() {
const { code, data } = await this.$axios({
url: '/base/factory/listAll',
method: 'get',
});
if (code == 0) {
this.searchBarFormConfig[0].selectOptions = data.map((item) => {
return {
name: item.name,
id: item.id,
};
});
}
},
/** 准备产线数据 */
async getLine() {
const { code, data } = await this.$axios({
url: '/base/production-line/listAll',
method: 'get',
});
if (code == 0) {
this.searchBarFormConfig[1].selectOptions = data.map((item) => {
return {
name: item.name,
id: item.id,
};
});
}
},
/** 覆盖 handleEmitFun 的默认实现 */
handleEmitFun({ action, value }) {
switch (action) {
case 'view-trend':
const { id } = value;
this.open = true;
this.trendOpen = true;
break;
}
},
/** 查询列表 */
async getList() {
this.loading = true;
const { code, data } = await this.$axios({
url: '/analysis/equipment-analysis/efficiency',
method: 'get',
params: this.queryParams,
});
if (code == 0) {
console.log('data', data);
this.list = data;
}
},
handleSearchBarBtnClick({ btnName, ...payload }) {
console.log('handleSearchBarBtnClick', btnName, payload);
if (btnName == 'visualization') {
//
this.visualizationOpen = true;
this.open = true;
}
if (btnName == 'search') {
this.queryParams.factoryId = payload.factoryId || null;
this.queryParams.lineId = payload.lineId || null;
if (payload.recordTime != null) {
if (typeof payload.recordTime == 'string') {
if (payload.recordTime.trim() !== '') {
this.queryParams.recordTime = [
`${payload.recordTime} 00:00:00`,
`${payload.recordTime} 23:59:59`,
];
}
} else {
this.queryParams.recordTime = payload.recordTime;
}
} else {
this.queryParams.recordTime = null;
}
this.getList();
}
},
cancel() {
this.open = false;
},
closed() {
this.visualizationOpen = false;
this.trendOpen = false;
},
submitForm() {},
},
};
</script>
<style scoped lang="scss">
.visualization {
display: grid;
grid-template-columns: repeat(3, minmax(240px, 1fr));
}
</style>

View File

@ -0,0 +1,194 @@
<!--
filename: index.vue
author: liubin
date: 2023-09-04 09:34:52
description: 设备异常分析
-->
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<SearchBar
:formConfigs="searchBarFormConfig"
ref="search-bar"
@headBtnClick="handleSearchBarBtnClick" />
<!-- 列表 -->
<base-table
:table-props="tableProps"
:page="queryParams.pageNo"
:limit="queryParams.pageSize"
:table-data="list"
@emitFun="handleEmitFun">
<!-- <method-btn
v-if="tableBtn.length"
slot="handleBtn"
label="操作"
:method-list="tableBtn"
@clickBtn="handleTableBtnClick" /> -->
</base-table>
<!-- 分页组件 -->
<pagination
v-show="total > 0"
:total="total"
:page.sync="queryParams.pageNo"
:limit.sync="queryParams.pageSize"
@pagination="getList" />
</div>
</template>
<script>
import moment from 'moment';
import basicPageMixin from '@/mixins/lb/basicPageMixin';
export default {
name: 'ExceptionAnalysis',
mixins: [basicPageMixin],
components: {},
props: {},
data() {
return {
searchBarKeys: ['name', 'code'],
tableBtn: [
this.$auth.hasPermi('base:equipment-group:update')
? {
type: 'edit',
btnName: '修改',
}
: undefined,
this.$auth.hasPermi('base:equipment-group:delete')
? {
type: 'delete',
btnName: '删除',
}
: undefined,
].filter((v) => v),
tableProps: [
{ prop: 'lineName', label: '产线', align: 'center' },
{ prop: 'sectionName', label: '工段', align: 'center' },
{ prop: 'equipmentName', label: '设备', align: 'center' },
{
width: 188,
prop: 'mtbf',
label: '平均故障间隔时间[MTBF](h)',
align: 'center',
},
{
width: 180,
prop: 'mttr',
label: '平均维修时间[MTTR](h)',
align: 'center',
},
{ prop: 'workTime', label: '工作时长(h)', align: 'center' },
{ prop: 'downTime', label: '故障时长(h)', align: 'center' },
{ prop: 'downCount', label: '故障次数', align: 'center' },
],
searchBarFormConfig: [
{
type: 'select',
label: '请选择月份',
placeholder: '请选择月份',
param: 'month',
selectOptions: Array(12)
.fill(0)
.map((v, i) => ({
id: i + 1,
name: `${i + 1}`,
})),
},
{
__index: 'line',
type: 'select',
label: '产线',
placeholder: '请选择产线',
param: 'lineId',
},
{
type: 'button',
btnName: '查询',
name: 'search',
color: 'primary',
},
],
//
queryParams: {
lineId: null,
factoryId: null,
recordTime: null,
},
};
},
computed: {},
created() {
this.fillLineOptions();
},
methods: {
async fillLineOptions() {
const { data } = await this.$axios({
url: '/base/production-line/listAll',
method: 'get',
});
const cfg = this.searchBarFormConfig.find(
(item) => item.__index == 'line'
);
this.$set(
cfg,
'selectOptions',
data.map((item) => ({
id: item.id,
name: item.name,
}))
);
},
async getList() {
this.loading = true;
//
const { data } = await this.$axios({
url: '/analysis/equipment-analysis/efficiency',
method: 'get',
params: {
lineId: this.queryParams.lineId || null,
recordTime: this.queryParams.recordTime || null,
},
});
},
handleSearchBarBtnClick(btn) {
switch (btn.btnName) {
case 'search':
if (btn.month) {
this.queryParams.recordTime = [
moment()
.month(btn.month - 1)
.format('YYYY-MM')+'-01 00:00:00',
moment()
.month(btn.month)
.format('YYYY-MM')+'-01 00:00:00',
];
} else {
this.queryParams.recordTime = null;
}
this.queryParams.lineId = btn.lineId || null;
this.handleQuery();
break;
}
},
/** 搜索按钮操作 */
handleQuery() {
// this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm('queryForm');
this.handleQuery();
},
},
};
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,49 @@
<!--
filename: lineChart.vue
author: liubin
date: 2023-09-04 13:45:00
description:
-->
<template>
<div class="line-chart"></div>
</template>
<script>
import * as echarts from 'echarts';
export default {
name: 'LineChart',
components: {},
props: ['config'],
data() {
return {
chart: null,
};
},
computed: {},
mounted() {
this.init();
},
beforeDestroy() {
if (this.chart) {
this.chart.dispose();
}
},
methods: {
init() {
console.log('thsi el', this.$el);
if (!this.chart) this.chart = echarts.init(this.$el);
this.chart.setOption(this.config);
},
},
};
</script>
<style scoped lang="scss">
.line-chart {
padding: 0 12px;
background: #e1e1e1;
min-height: 320px;
}
</style>

View File

@ -0,0 +1,343 @@
<!--
filename: index.vue
author: liubin
date: 2023-09-04 09:34:52
description: 设备质量分析
-->
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<SearchBar
:formConfigs="searchBarFormConfig"
ref="search-bar"
@headBtnClick="handleSearchBarBtnClick" />
<!-- 列表 -->
<base-table
:table-props="tableProps"
:page="queryParams.pageNo"
:limit="queryParams.pageSize"
:table-data="list"
@emitFun="handleEmitFun"></base-table>
<!-- 图形分析 dialog -->
<!-- <base-dialog
dialogTitle="图形视角"
:dialogVisible="dialogVisible"
width="60%"
@close="dialogClose"
@cancel="dialogClose"
@confirm="dialogClose">
<LineChart v-if="dialogVisible" :config="lineChartConfig" />
</base-dialog> -->
</div>
</template>
<script>
import LineChart from './components/lineChart.vue';
export default {
name: 'QualityAnalysis',
components: { LineChart },
props: {},
data() {
return {
dialogVisible: false,
urls: {
page: '/analysis/equipment-analysis/quality',
},
mode: 'table', // defaults to 'table'
searchBarFormConfig: [
//
{
__index: 'product',
type: 'select',
label: '产品',
placeholder: '请选择产品',
param: 'productId',
},
// 线
{
__index: 'line',
type: 'select',
label: '产线',
placeholder: '请选择产线',
param: 'lineId',
},
//
{
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: 'tableVersion',
// color: 'text btn-table',
// },
// {
// type: 'separate',
// },
// {
// type: 'button',
// btnName: '',
// name: 'graphVersion',
// color: 'text btn-graph',
// },
],
// props
tableProps: [
{
// width: 160,
prop: 'sectionName',
label: '工段',
align: 'center',
},
{
// width: 160,
prop: 'equipmentName',
label: '设备名称',
align: 'center',
},
{
// width: 160,
prop: 'products',
label: '产品名称',
align: 'center',
},
{
// width: 160,
prop: 'inQuantity',
label: '进片数量',
align: 'center',
},
{
// width: 160,
prop: 'outQuantity',
label: '出片数量',
align: 'center',
},
{
// width: 160,
prop: 'nokQuantity',
label: '破损/不合格数',
align: 'center',
},
{
// width: 160,
prop: 'passRate',
label: '合格率',
align: 'center',
},
],
lineChartConfig: {
grid: {
top: 88,
left: 56,
right: 56,
bottom: 56,
},
legend: {
top: 0,
left: 0,
padding: 5,
icon: 'roundRect',
itemWidth: 12,
itemHeight: 12,
itemGap: 20,
textStyle: {
fontSize: 14,
lineHeight: 14
},
},
xAxis: {
type: 'category',
data: ['设备1', '设备2', '设备3', '设备4', '设备5'],
},
yAxis: {
type: 'value',
name: '合格率',
nameLocation: 'end',
nameTextStyle: {
fontSize: 14,
align: 'right'
},
nameGap: 26
},
series: [
{
name: '产线1',
data: [150, 230, 224, 218, 135],
type: 'line',
smooth: true,
},
{
name: '产线2',
data: [111, 224, 42, 11, 24],
type: 'line',
smooth: true,
},
{
name: '产线3',
data: [218, 112, 331, 44, 99],
type: 'line',
smooth: true,
},
{
name: '产线4',
data: [3, 221, 42, 553, 311],
type: 'line',
smooth: true,
},
],
},
//
queryParams: {
pageNo: 1,
pageSize: 10,
lineId: null,
productId: null,
recordTime: [],
},
list: [],
};
},
computed: {},
created() {
this.fillLineOptions();
this.fillProductOptions();
this.getList();
},
methods: {
async fillLineOptions() {
const { data } = await this.$axios({
url: '/base/production-line/listAll',
method: 'get',
});
const cfg = this.searchBarFormConfig.find(
(item) => item.__index == 'line'
);
this.$set(
cfg,
'selectOptions',
data.map((item) => ({
id: item.id,
name: item.name,
}))
);
},
async fillProductOptions() {
const { data } = await this.$axios({
url: '/base/product/listAll',
method: 'get',
});
const cfg = this.searchBarFormConfig.find(
(item) => item.__index == 'product'
);
this.$set(
cfg,
'selectOptions',
data.map((item) => ({
id: item.id,
name: item.name,
}))
);
},
async getList() {
const { data } = await this.$axios({
url: '/analysis/equipment-analysis/quality',
method: 'get',
params: {
lineId: this.queryParams.lineId || null,
productId: this.queryParams.productId || null,
recordTime: this.queryParams.recordTime || null,
},
});
this.list = data.map((item) => ({
...item,
products: item.products.join(','),
}));
},
handleSearchBarBtnClick(btn) {
console.log('handleSearchBarBtnClick', btn);
switch (btn.btnName) {
case 'search':
this.queryParams.lineId = btn.lineId;
this.queryParams.productId = btn.productId;
this.queryParams.recordTime = btn.recordTime;
this.$nextTick(() => {
this.getList();
});
break;
case 'tableVersion':
this.dialogClose();
break;
case 'graphVersion':
this.dialogShow();
break;
}
},
handleEmitFun({ action, payload }) {},
dialogShow() {
this.dialogVisible = true;
},
dialogClose() {
this.dialogVisible = false;
},
},
};
</script>
<style scoped lang="scss">
:deep(.searchBar) {
.el-button.btn-table {
color: rgb(0, 130, 130);
border: 1px solid rgb(0, 130, 130);
padding: 8px 10px;
border: 1px solid rgb(0, 130, 130);
padding: 8px 10px;
&:hover {
border-color: #fff;
color: #fff;
background: rgb(0, 130, 130);
}
}
.el-button.btn-graph {
color: rgb(130, 0, 130);
border: 1px solid rgb(130, 0, 130);
padding: 8px 10px;
&:hover {
border-color: #fff;
color: #fff;
background: rgb(130, 0, 130);
}
}
}
</style>

View File

@ -0,0 +1,49 @@
<!--
filename: lineChart.vue
author: liubin
date: 2023-09-04 13:45:00
description:
-->
<template>
<div class="line-chart"></div>
</template>
<script>
import * as echarts from 'echarts';
export default {
name: 'LineChart',
components: {},
props: ['config'],
data() {
return {
chart: null,
};
},
computed: {},
mounted() {
this.init();
},
beforeDestroy() {
if (this.chart) {
this.chart.dispose();
}
},
methods: {
init() {
console.log('thsi el', this.$el);
if (!this.chart) this.chart = echarts.init(this.$el);
this.chart.setOption(this.config);
},
},
};
</script>
<style scoped lang="scss">
.line-chart {
padding: 0 12px;
// background: #e1e1e1;
min-height: 320px;
}
</style>

View File

@ -0,0 +1,330 @@
<!--
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="graphs" v-if="graphList.length">
<div class="graph" v-for="(eq, index) in graphList" :key="eq.key">
<h2 class="graph-title">{{ eq.key }}</h2>
<LineChart
:key="eq.key + '__linechart'"
:config="getRealConfig(index)" />
</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>
import LineChart from './components/lineChart.vue';
export default {
name: 'SGProduction',
components: { LineChart },
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: [],
},
open: false,
eqList: [],
graphList: [],
templateConfig: {
grid: {
top: 88,
left: 56,
right: 56,
bottom: 56,
},
legend: {
top: 0,
left: 0,
padding: 5,
icon: 'roundRect',
itemWidth: 12,
itemHeight: 12,
itemGap: 20,
textStyle: {
fontSize: 14,
lineHeight: 14,
},
},
xAxis: {
type: 'category',
data: Array(24)
.fill(1)
.map((item, index) => `${index}:00`),
},
yAxis: {
type: 'value',
name: '产量',
nameLocation: 'end',
nameTextStyle: {
fontSize: 14,
align: 'right',
},
nameGap: 26,
},
series: [
{
name: '产线1',
data: Array(24)
.fill(1)
.map(() => Math.random() * 100),
type: 'line',
smooth: true,
},
],
},
};
},
created() {
this.initProductline();
this.initWorksection();
this.initEquipment();
this.getList();
},
methods: {
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;
}
},
/** 重置查询条件 */
initQuery() {
this.queryParams.lineId = null;
this.queryParams.sectionId = null;
this.queryParams.equipmentId = 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/quantity',
method: 'get',
params: this.queryParams,
});
if (code == 0) {
this.graphList = this.objectToArray(data);
// const eq1 = [
// { totalQuantity: 20, startTime: 1693964578000 },
// { totalQuantity: 43, startTime: 1693964678000 },
// { totalQuantity: 12, startTime: 1693964778000 },
// { totalQuantity: 11, startTime: 1693964878000 },
// { totalQuantity: 98, startTime: 1693965478000 },
// { totalQuantity: 87, startTime: 1693965578000 },
// ];
// eq1.key = 'SS1';
// const eq2 = [
// { totalQuantity: 23, startTime: 1693961578000 },
// { totalQuantity: 42, startTime: 1693964578000 },
// { totalQuantity: 51, startTime: 1693965578000 },
// { totalQuantity: 18, startTime: 1693966578000 },
// { totalQuantity: 77, startTime: 1693966778000 },
// { totalQuantity: 38, startTime: 1693967578000 },
// { totalQuantity: 57, startTime: 1693969578000 },
// ];
// eq2.key = 'SS2';
// this.graphList = [eq1, eq2];
console.log('graph list', this.graphList);
}
},
/** 获得设备产量 */
getEquipmentQuantity(equipmentArr) {
return equipmentArr.map((item) => item.totalQuantity);
},
/** 获得设备产量的时间 */
getTimeinfo(equipmentArr) {
return equipmentArr.map((item) =>
new Date(item.startTime).toLocaleTimeString()
);
},
getRealConfig(index) {
// if (!this.graphList || this.graphList.length == 0) return;
const config = JSON.parse(JSON.stringify(this.templateConfig));
// config.legend.data = this.graphList[index].key;
config.series[0].name = this.graphList[index]?.key;
// console.log("this.graphList?.[index]", this.graphList?.[index]);
config.series[0].data = this.getEquipmentQuantity(
this.graphList?.[index] || []
);
config.xAxis.data = this.getTimeinfo(this.graphList?.[index] || []);
return config;
},
/** 准备设备数据 */
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,
};
});
}
},
async submitForm() {
const { code, data } = await this.$axios({
url: '/analysis/equipment-analysis/quantity',
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"></style>

View File

@ -0,0 +1,449 @@
<!--
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>

View File

@ -12,80 +12,64 @@
ref="search-bar" ref="search-bar"
@headBtnClick="handleSearchBarBtnClick" /> @headBtnClick="handleSearchBarBtnClick" />
<!-- <base-table
:table-props="[
{ type: 'index', label: '序号' },
{ prop: 'name', label: '设备名称', align: 'center' },
{ prop: 'code', label: '设备代码', align: 'center' },
{ prop: 'time', label: '时间', align: 'center' },
]"
:table-data="[
{ index: 1, name: '1', code: 'c1', time: '2021-08-31 09:14:19' },
{ index: 2, name: '2', code: 'c2', time: '2021-08-31 09:14:19' },
{ index: 3, name: '3', code: 'c3', time: '2021-08-31 09:14:19' },
{ index: 4, name: '4', code: 'c4', time: '2021-08-31 09:14:19' },
{ index: 5, name: '5', code: 'c5', time: '2021-08-31 09:14:19' },
]"
:span-method="
({ rowIndex, columnIndex }) => {
if (rowIndex == 1 && columnIndex == 0) {
return [1, 3];
}
return [1, 1];
}
"
@emitFun="(val) => handleEmitFun(table, val)"></base-table> -->
<div class="tables"> <div class="tables">
<base-table <div class="custom-table" v-for="table in tableList" :key="table.key">
:key="1 + '__basetable'" <!-- {{ JSON.stringify(spanMethod) }} -->
:table-props="table1.tableProps"
:page="1"
:limit="999"
:table-data="table1.data"
@emitFun="(val) => handleEmitFun(table1, val)"></base-table>
<base-table
:key="2 + '__basetable'"
:table-props="table2.tableProps"
:page="1"
:limit="999"
:table-data="table2.data"
@emitFun="(val) => handleEmitFun(table2, val)"></base-table>
<base-table
:key="3 + '__basetable'"
:table-props="table3.tableProps"
:page="1"
:limit="999"
:table-data="table3.data"
@emitFun="(val) => handleEmitFun(table3, val)"></base-table>
<base-table
:key="4 + '__basetable'"
:table-props="table4.tableProps"
:page="1"
:limit="999"
:table-data="table4.data"
@emitFun="(val) => handleEmitFun(table4, val)"></base-table>
<base-table
:key="5 + '__basetable'"
:table-props="table5.tableProps"
:page="1"
:limit="999"
:table-data="table5.data"
@emitFun="(val) => handleEmitFun(table5, val)"></base-table>
<base-table
:key="6 + '__basetable'"
:table-props="table6.tableProps"
:page="1"
:limit="999"
:table-data="table6.data"
@emitFun="(val) => handleEmitFun(table6, val)"></base-table>
<base-table
:key="7 + '__basetable'"
:table-props="table7.tableProps"
:page="1"
:limit="999"
:table-data="table7.data"
@emitFun="(val) => handleEmitFun(table7, val)"></base-table>
</div>
<!-- <div class="tables">
<div class="table-wrapper" v-for="table in tableList" :key="table.key">
<div class="table-title">PLC 1</div>
<base-table <base-table
:key="table.key + '__basetable'" :key="table.key + '__basetable'"
:table-props="table.tableProps" :table-props="table.tableProps"
:page="1" :table-data="table.dataManager?.dataList ?? []"
:limit="999" :span-method="spanMethod"
:table-data="table.data" @emitFun="(val) => handleEmitFun(table, val)"></base-table>
@emitFun="(val) => handleEmitFun(table, val)">
</base-table>
<pagination <pagination
:key="table.key + '__pagination'"
v-show="table.total > 0" v-show="table.total > 0"
:total="table.total" :total="table.total"
:page.sync="table.queryParams.pageNo" :page.sync="table.pageNo"
:limit.sync="table.queryParams.pageSize" :limit.sync="table.pageSize"
@pagination="(val) => getListFor(table, val)" /> :page-size="table.pageSize"
:page-sizes="[1, 3, 5, 10, 20]"
@pagination="
({ page, limit, current }) =>
getListFor(table, { page, limit, current })
" />
</div>
<!-- v-show="table.dataManager?.total > 0"
:total="table.dataManager?.total || 0" -->
<!-- @size-change="($event) => handleSizeChange(table, $event)" -->
</div> </div>
</div> -->
</div> </div>
</template> </template>
<script> <script>
import LocalDataManager from './utils/local-data-manager';
import response from './response';
import moment from 'moment';
export default { export default {
name: 'EquipmentFullParams', name: 'EquipmentFullParams',
components: {}, components: {},
@ -138,286 +122,16 @@ export default {
id: null, id: null,
time: [new Date(aWeekAgo), new Date(today)], time: [new Date(aWeekAgo), new Date(today)],
}, },
tableList: [
table1: {
tableProps: [
{ {
prop: 'time', key: 'base-table__key__1',
label: '时间', tableProps: [],
}, list: [],
{ pageNo: 1,
prop: 'plcCode', pageSize: 3,
label: 'PLC编码', total: 0,
},
{
prop: 'val1',
label: '数值1',
},
{
prop: 'bol1',
label: '布尔1',
},
{
prop: 'val2',
label: '数值2',
},
{
prop: 'bol2',
label: '布尔2',
},
{
prop: 'val3',
label: '数值3',
},
{
prop: 'bol3',
label: '布尔3',
},
{
prop: 'val4',
label: '数值4',
},
{
prop: 'bol4',
label: '布尔4',
}, },
], ],
data: [
{
time: 1111111111111111,
plcCode: 2,
val1: 3,
bol1: 4,
val2: 5,
bol2: 6,
},
{
time: 1,
plcCode: 22222222222222,
val1: 3,
bol1: 4,
val2: 5,
bol2: 6,
},
{
time: 1,
plcCode: 2,
val1: 33333333333333,
bol1: 4,
val2: 5,
bol2: 6,
},
{
time: 1,
plcCode: 2,
val1: 3,
bol1: 44444444444444,
val2: 5,
bol2: 6,
},
{
time: 1,
plcCode: 2,
val1: 3,
bol1: 4,
val2: 5555555555555,
bol2: 6,
},
{
time: 1,
plcCode: 2,
val1: 3,
bol1: 4,
val2: 5,
bol2: 6666666666666666666,
},
{ time: 1, plcCode: 2, val1: 3, bol1: 4, val2: 5, bol2: 6 },
{ time: 1, plcCode: 2, val1: 3, bol1: 4, val2: 5, bol2: 6 },
{ time: 1, plcCode: 2, val1: 3, bol1: 4, val2: 5, bol2: 6 },
{ time: 1, plcCode: 2, val1: 3, bol1: 4, val2: 5, bol2: 6 },
{ time: 1, plcCode: 2, val1: 3, bol1: 4, val2: 5, bol2: 6 },
{ time: 1, plcCode: 2, val1: 3, bol1: 4, val2: 5, bol2: 6 },
{ time: 1, plcCode: 2, val1: 3, bol1: 4, val2: 5, bol2: 6 },
{ time: 1, plcCode: 2, val1: 3, bol1: 4, val2: 5, bol2: 6 },
{ time: 1, plcCode: 2, val1: 3, bol1: 4, val2: 5, bol2: 6 },
{ time: 1, plcCode: 2, val1: 3, bol1: 4, val2: 5, bol2: 6 },
{ time: 1, plcCode: 2, val1: 3, bol1: 4, val2: 5, bol2: 6 },
],
},
table2: {
tableProps: [
{
prop: 'time',
label: '时间',
},
{
prop: 'plcCode',
label: 'PLC编码',
},
{
prop: 'val1',
label: '数值1',
},
{
prop: 'bol1',
label: '布尔1',
},
{
prop: 'val2',
label: '数值2',
},
{
prop: 'bol2',
label: '布尔2',
},
],
data: [],
},
table3: {
tableProps: [
{
prop: 'time',
label: '时间',
},
{
prop: 'plcCode',
label: 'PLC编码',
},
{
prop: 'val1',
label: '数值1',
},
{
prop: 'bol1',
label: '布尔1',
},
{
prop: 'val2',
label: '数值2',
},
{
prop: 'bol2',
label: '布尔2',
},
],
data: [],
},
table4: {
tableProps: [
{
prop: 'time',
label: '时间',
},
{
prop: 'plcCode',
label: 'PLC编码',
},
{
prop: 'val1',
label: '数值1',
},
{
prop: 'bol1',
label: '布尔1',
},
{
prop: 'val2',
label: '数值2',
},
{
prop: 'bol2',
label: '布尔2',
},
],
data: [],
},
table5: {
tableProps: [
{
prop: 'time',
label: '时间',
},
{
prop: 'plcCode',
label: 'PLC编码',
},
{
prop: 'val1',
label: '数值1',
},
{
prop: 'bol1',
label: '布尔1',
},
{
prop: 'val2',
label: '数值2',
},
{
prop: 'bol2',
label: '布尔2',
},
],
data: [],
},
table6: {
tableProps: [
{
prop: 'time',
label: '时间',
},
{
prop: 'plcCode',
label: 'PLC编码',
},
{
prop: 'val1',
label: '数值1',
},
{
prop: 'bol1',
label: '布尔1',
},
{
prop: 'val2',
label: '数值2',
},
{
prop: 'bol2',
label: '布尔2',
},
],
data: [],
},
table7: {
tableProps: [
{
prop: 'time',
label: '时间',
},
{
prop: 'plcCode',
label: 'PLC编码',
},
{
prop: 'val1',
label: '数值1',
},
{
prop: 'bol1',
label: '布尔1',
},
{
prop: 'val2',
label: '数值2',
},
{
prop: 'bol2',
label: '布尔2',
},
],
data: [],
},
}; };
}, },
computed: { computed: {
@ -437,8 +151,108 @@ export default {
this.$set(this.searchBarFormConfig[0], 'defaultSelect', this.code); this.$set(this.searchBarFormConfig[0], 'defaultSelect', this.code);
if (this.name) if (this.name)
this.$set(this.searchBarFormConfig[1], 'defaultSelect', this.name); this.$set(this.searchBarFormConfig[1], 'defaultSelect', this.name);
this.handleResponse();
}, },
methods: { methods: {
buildProps(table) {
console.log('building props', table);
// otherList props
const { otherList } = table;
const props = [
{
// type: 'index',
width: 48,
prop: 'index',
label: '序号',
align: 'center',
},
{
width: 160,
prop: 'time',
label: '时间',
align: 'center',
},
{
width: 200,
prop: 'plcCode',
label: 'PLC编码',
align: 'center',
},
];
const firstLineData = {
index: '参考值: [最小]-[最大][/标准]',
time: null, //
plcCode: null, //
};
otherList.forEach((item) => {
props.push({
label: item.name,
align: 'center',
prop: item.name,
width: 128,
});
firstLineData[item.name] = `${item.minValue ?? ''}-${
item.maxValue ?? ''
}${item.defaultValue != null ? '/' + item.defaultValue : ''}`;
});
return { props, firstLineData };
},
handleResponse() {
const { code, data } = response;
if (code == 0) {
console.log('response', code, data);
//
data.forEach((table, index) => {
console.log('handle index:', index, table);
const { props: tableProps, firstLineData } = this.buildProps(table);
this.$set(this.tableList, index, {
key: 'base-table__key__' + index,
tableProps,
list: [firstLineData],
dataManager: null,
pageNo: 1,
pageSize: 5,
total: 0,
});
//
const { data } = table;
data.forEach((row, idx) => {
const listItem = {
index: idx + 1,
time: moment(+row.time).format('YYYY-MM-DD HH:mm:ss'),
plcCode: row.plcCode,
};
row.data.forEach((column) => {
listItem[column.dynamicName] = column.dynamicValue;
});
this.tableList[index].list.push(listItem);
this.tableList[index].total++;
});
//
const { pageNo, pageSize, list } = this.tableList[index];
this.tableList[index].dataManager = new LocalDataManager(
list,
pageNo,
pageSize
);
});
}
},
spanMethod({ row, column, rowIndex, columnIndex }) {
if (rowIndex == 0 && columnIndex == 0) {
return [1, 3];
} else if (rowIndex == 0 && (columnIndex == 1 || columnIndex == 2)) {
return [0, 0];
}
return [1, 1];
},
/** 查询 */ /** 查询 */
async handleQuery() { async handleQuery() {
const { data } = this.$axios({ const { data } = this.$axios({
@ -462,19 +276,11 @@ export default {
console.log('table val', table, val); console.log('table val', table, val);
}, },
/** 构造 props */
buildProps() {
this.tableList.forEach((table) => {
this.buildTableProp(table);
});
},
/** 构造一个 tableProps - 根据动态结构 */
buildTableProp(table) {},
/** 为某个 table 获取 list 数据 */ /** 为某个 table 获取 list 数据 */
getListFor(table, val) { getListFor(table, { page, limit, current }) {
console.log('get list for', table, val); console.log('get list for', table, { page, limit, current });
table.dataManager.pageNo = page ?? current;
table.dataManager.pageSize = limit;
}, },
}, },
}; };
@ -494,4 +300,8 @@ export default {
.tables >>> .baseTable { .tables >>> .baseTable {
overflow-x: hidden; overflow-x: hidden;
} }
.custom-table {
overflow-x: hidden;
}
</style> </style>

View File

@ -0,0 +1,247 @@
export default {
"code": 0,
"data": [
// 一个表格
{
"data": [
// 一行参数
{
"time": "1694056192800",
"plcCode": "PLC_CODE_1",
"data": [
// 一个参数值
{ "id": 'param-1', "parentId": 0, "dynamicName": "P1V1", "dynamicValue": 15 },
{ "id": 'param-2', "parentId": 0, "dynamicName": "P1V2", "dynamicValue": 15 },
{ "id": 'param-3', "parentId": 0, "dynamicName": "P1V3", "dynamicValue": 15 },
{ "id": 'param-4', "parentId": 0, "dynamicName": "P1V4", "dynamicValue": 15 },
{ "id": 'param-5', "parentId": 0, "dynamicName": "P1V5", "dynamicValue": 15 },
{ "id": 'param-6', "parentId": 0, "dynamicName": "P1V6", "dynamicValue": 15 },
]
}
],
"otherList": [
// 一个参数的属性
{
"id": "attr-1",
"plcParamName": "P1V1",
"name": "P1V1", // 和 dynamicName 对应
"minValue": 1,
"maxValue": 100
},
{
"id": "attr-2",
"plcParamName": "P1V2",
"name": "P1V2",
"minValue": 10,
"maxValue": 90,
"defaultValue": 24
},
{
"id": "attr-3",
"plcParamName": "P1V3",
"name": "P1V3",
"minValue": 10,
"maxValue": 98
},
{
"id": "attr-4",
"plcParamName": "P1V4",
"name": "P1V4",
"minValue": 10,
"maxValue": 90
},
{
"id": "attr-5",
"plcParamName": "P1V5",
"name": "P1V5",
"minValue": null,
"maxValue": null
},
{
"id": "attr-6",
"plcParamName": "P1V6",
"name": "P1V6",
"minValue": null,
"maxValue": null,
"defaultValue": false
},
],
"nameData": [],
"otherMap": []
},
// 一个表格
{
"data": [
// 一行参数
{
"time": "1694056100800",
"plcCode": "PLC_CODE_2",
"data": [
// 一个参数值
{ "id": 'param-1', "parentId": 0, "dynamicName": "P1V1", "dynamicValue": 15 },
{ "id": 'param-2', "parentId": 0, "dynamicName": "P1V2", "dynamicValue": 15 },
{ "id": 'param-3', "parentId": 0, "dynamicName": "P1V3", "dynamicValue": 15 },
]
},
{
"time": "1694056102800",
"plcCode": "PLC_CODE_2",
"data": [
// 一个参数值
{ "id": 'param-1', "parentId": 0, "dynamicName": "P1V1", "dynamicValue": 15 },
{ "id": 'param-2', "parentId": 0, "dynamicName": "P1V2", "dynamicValue": 15 },
{ "id": 'param-3', "parentId": 0, "dynamicName": "P1V3", "dynamicValue": 15 },
]
}
],
"otherList": [
// 一个参数的属性
{
"id": "attr-1",
"plcParamName": "P1V1",
"name": "P1V1", // 和 dynamicName 对应
"minValue": 1,
"maxValue": 100
},
{
"id": "attr-2",
"plcParamName": "P1V2",
"name": "P1V2",
"minValue": 10,
"maxValue": 90
},
{
"id": "attr-3",
"plcParamName": "P1V3",
"name": "P1V3",
"minValue": 10,
"maxValue": 98
},
],
"nameData": [],
"otherMap": []
},
// 一个表格
{
"data": [
// 一行参数
{
"time": "1694056100800",
"plcCode": "PLC_CODE_2",
"data": [
// 一个参数值
{ "id": 'param-1', "parentId": 0, "dynamicName": "P1V1", "dynamicValue": 15 },
{ "id": 'param-2', "parentId": 0, "dynamicName": "P1V2", "dynamicValue": 15 },
{ "id": 'param-3', "parentId": 0, "dynamicName": "P1V3", "dynamicValue": 15 },
]
},
{
"time": "1694056102800",
"plcCode": "PLC_CODE_2",
"data": [
// 一个参数值
{ "id": 'param-1', "parentId": 0, "dynamicName": "P1V1", "dynamicValue": 15 },
{ "id": 'param-2', "parentId": 0, "dynamicName": "P1V2", "dynamicValue": 15 },
{ "id": 'param-3', "parentId": 0, "dynamicName": "P1V3", "dynamicValue": 15 },
]
},
{
"time": "1694056109800",
"plcCode": "PLC_CODE_2",
"data": [
// 一个参数值
{ "id": 'param-1', "parentId": 0, "dynamicName": "P1V1", "dynamicValue": 15 },
{ "id": 'param-2', "parentId": 0, "dynamicName": "P1V2", "dynamicValue": 15 },
{ "id": 'param-3', "parentId": 0, "dynamicName": "P1V3", "dynamicValue": 15 },
]
},
{
"time": "1694066109800",
"plcCode": "PLC_CODE_2",
"data": [
// 一个参数值
{ "id": 'param-1', "parentId": 0, "dynamicName": "P1V1", "dynamicValue": 15 },
{ "id": 'param-2', "parentId": 0, "dynamicName": "P1V2", "dynamicValue": 15 },
{ "id": 'param-3', "parentId": 0, "dynamicName": "P1V3", "dynamicValue": 15 },
]
},
{
"time": "1694067109800",
"plcCode": "PLC_CODE_2",
"data": [
// 一个参数值
{ "id": 'param-1', "parentId": 0, "dynamicName": "P1V1", "dynamicValue": 15 },
{ "id": 'param-2', "parentId": 0, "dynamicName": "P1V2", "dynamicValue": 15 },
{ "id": 'param-3', "parentId": 0, "dynamicName": "P1V3", "dynamicValue": 15 },
]
},
{
"time": "1694068109800",
"plcCode": "PLC_CODE_2",
"data": [
// 一个参数值
{ "id": 'param-1', "parentId": 0, "dynamicName": "P1V1", "dynamicValue": 15 },
{ "id": 'param-2', "parentId": 0, "dynamicName": "P1V2", "dynamicValue": 15 },
{ "id": 'param-3', "parentId": 0, "dynamicName": "P1V3", "dynamicValue": 15 },
]
},
{
"time": "1694069109800",
"plcCode": "PLC_CODE_2",
"data": [
// 一个参数值
{ "id": 'param-1', "parentId": 0, "dynamicName": "P1V1", "dynamicValue": 15 },
{ "id": 'param-2', "parentId": 0, "dynamicName": "P1V2", "dynamicValue": 15 },
{ "id": 'param-3', "parentId": 0, "dynamicName": "P1V3", "dynamicValue": 15 },
]
},
{
"time": "1694071109800",
"plcCode": "PLC_CODE_2",
"data": [
// 一个参数值
{ "id": 'param-1', "parentId": 0, "dynamicName": "P1V1", "dynamicValue": 15 },
{ "id": 'param-2', "parentId": 0, "dynamicName": "P1V2", "dynamicValue": 15 },
{ "id": 'param-3', "parentId": 0, "dynamicName": "P1V3", "dynamicValue": 15 },
]
}
],
"otherList": [
// 一个参数的属性
{
"id": "attr-1",
"plcParamName": "P1V1",
"name": "P1V1", // 和 dynamicName 对应
"minValue": 1,
"maxValue": 100
},
{
"id": "attr-2",
"plcParamName": "P1V2",
"name": "P1V2",
"minValue": 10,
"maxValue": 90
},
{
"id": "attr-3",
"plcParamName": "P1V3",
"name": "P1V3",
"minValue": 10,
"maxValue": 98
}
],
"nameData": [],
"otherMap": []
}
]
}

View File

@ -0,0 +1,36 @@
export default class LocalDataManager {
constructor(dataList, pageNo, pageSize) {
this._dataListStore = dataList;
this._pageNo = pageNo;
this._pageSize = pageSize;
}
set pageNo(pageNo) {
console.log('set pageNo', pageNo);
this._pageNo = pageNo;
}
set pageSize(pageSize) {
console.log('set pageSize', pageSize);
this._pageSize = pageSize;
}
get dataList() {
const firstLine = this._dataListStore[0];
const realDataList = this._dataListStore.slice(1);
return [
firstLine,
...realDataList.slice((this._pageNo - 1) * this._pageSize, this._pageNo * this._pageSize)
]
}
get total() {
return this._dataListStore.length;
}
deleteData(id) {
const idx = this._dataListStore.findIndex(item => item.id == id);
this._dataListStore.splice(idx, 1);
// send http request
}
}

View File

@ -7,7 +7,6 @@
<template> <template>
<div class="chart-wrapper"> <div class="chart-wrapper">
<h4>line graph</h4>
<div class="chart" ref="chart"></div> <div class="chart" ref="chart"></div>
</div> </div>
</template> </template>
@ -18,22 +17,65 @@ import * as echarts from 'echarts';
export default { export default {
name: 'LineChartInEquipmentProcessAmount', name: 'LineChartInEquipmentProcessAmount',
components: {}, components: {},
props: {}, props: ['equipmentList'],
data() { data() {
return { return {
chart: null, chart: null,
option: { option: {
xAxis: { grid: {
type: 'category', top: 64,
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], left: 56,
right: 24,
bottom: 56,
},
title: {
show: true,
text: '各设备加工数量',
textStyle: {
color: '#232323',
fontSize: 16,
},
left: 'center',
top: 24,
}, },
yAxis: { yAxis: {
type: 'category',
axisLine: {
show: true,
lineStyle: {
color: '#ccc',
},
},
axisTick: {
show: false,
},
// data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
data: [],
name: '设备名',
nameTextStyle: {
fontSize: 14,
},
},
xAxis: {
type: 'value', type: 'value',
axisLine: {
show: true,
lineStyle: {
color: '#ccc',
},
},
}, },
series: [ series: [
{ {
data: [120, 200, 150, 80, 70, 110, 130], // data: [120, 200, 150, 80, 70, 110, 130],
data: [],
type: 'bar', type: 'bar',
barWidth: 20,
label: {
show: true,
distance: 50,
formatter: '{c}',
},
showBackground: true, showBackground: true,
backgroundStyle: { backgroundStyle: {
color: 'rgba(180, 180, 180, 0.2)', color: 'rgba(180, 180, 180, 0.2)',
@ -44,13 +86,29 @@ export default {
}; };
}, },
mounted() { mounted() {
// console.log('this.eq list', this.equipmentList);
if (!this.chart) this.chart = echarts.init(this.$refs.chart); if (!this.chart) this.chart = echarts.init(this.$refs.chart);
this.chart.setOption(this.option);
this.$nextTick(() => {
this.chart.setOption(this.updateConfig(this.option));
});
}, },
beforeDestroy() { beforeDestroy() {
this.chart.dispose(); this.chart.dispose();
}, },
methods: {}, methods: {
updateConfig(config) {
let nameData = [];
let valueData = [];
this.equipmentList.map((eq) => {
nameData.push(eq.equipmentName);
valueData.push(eq.totalQuantity);
});
config.yAxis.data = nameData;
config.series[0].data = valueData;
return config;
},
},
}; };
</script> </script>
@ -58,8 +116,7 @@ export default {
.chart-wrapper { .chart-wrapper {
height: 100%; height: 100%;
flex: 1; flex: 1;
background: #f1f1f1; // background: #f9f9f9;
padding: 12px;
} }
.chart { .chart {

View File

@ -48,7 +48,8 @@
<div class="graph" style="height: 56vh;" v-else> <div class="graph" style="height: 56vh;" v-else>
<!-- graph --> <!-- graph -->
<Graph /> <Graph v-if="list.length" :equipment-list="list" />
<div v-else style="color: #c7c7c7; text-align: center; margin-top: 20px;">没有设备</div>
</div> </div>
</transition> </transition>
</div> </div>