Compare commits

...

6 Commits

7 changed files with 487 additions and 210 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
src/assets/img/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
src/assets/img/logo@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
src/assets/img/logo@4x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@ -8,8 +8,8 @@ import merge from 'lodash/merge'
const http = axios.create({
// baseURL: window.SITE_CONFIG['apiURL'],
// baseURL: '/api',
baseURL: process.env.NODE_ENV === 'production' ? '/api' : '/yd-monitor',
baseURL: '/api',
// baseURL: process.env.NODE_ENV === 'production' ? '/api' : '/yd-monitor',
timeout: 1000 * 180,
withCredentials: true
})

View File

@ -21,7 +21,8 @@
</el-form>
<base-table :data="dataList" :table-head-configs="tableConfigs" :max-height="calcMaxHeight(8)" @operate-event="handleOperations" @refreshDataList="getDataList" />
<el-pagination
<!-- <el-pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
:current-page="pageIndex"
@ -29,18 +30,17 @@
:page-size="pageSize"
:total="totalPage"
layout="total, sizes, prev, pager, next, jumper"
></el-pagination>
/> -->
</div>
</template>
<script>
import i18n from '@/i18n'
import BaseTable from '@/components/base-table'
import TableOperateComponent from '@/components/base-table/components/operationComponent'
// import TableOperateComponent from '@/components/base-table/components/operationComponent'
import TableTextComponent from '@/components/base-table/components/detailComponent'
import { calcMaxHeight } from '@/utils'
import { timeFilter } from '@/utils/filters'
import { calcMaxHeight } from '@/utils'
import moment from 'moment'
const tableConfigs = [
@ -48,7 +48,8 @@ const tableConfigs = [
type: 'index',
name: i18n.t('index')
},
{ prop: 'pdlName', name: '产线名称' },
// { prop: 'time', name: '', filter: timeFilter },
{ prop: 'pdName', name: '产线名称' },
{ prop: 'wsName', name: '工序' },
{ prop: 'eqName', name: '设备' },
{ prop: 'mtbf', name: '平均故障间隔时间[MTBF] (h)', width: 220 },
@ -56,15 +57,15 @@ const tableConfigs = [
{ prop: 'workTime', name: '工作时长 (h)' },
{ prop: 'downTime', name: '故障时长 (h)' },
{ prop: 'downCount', name: '故障次数' },
{
prop: 'operations',
name: i18n.t('handle'),
fixed: 'right',
width: 180,
subcomponent: TableOperateComponent,
// options: ['edit', 'delete']
options: ['view-trend'] //
}
// {
// prop: 'operations',
// name: i18n.t('handle'),
// fixed: 'right',
// width: 120,
// subcomponent: TableTextComponent,
// buttonContent: '', //
// emitFullData: true
// }
]
export default {
@ -77,10 +78,9 @@ export default {
calcMaxHeight,
tableConfigs,
timeType: 'range',
rawTime: null, // [] or datetime
rawTime: new Date(),
dataForm: {
type: 1,
ftId: null,
productlines: [],
startTime: null,
entTime: null
@ -97,29 +97,29 @@ export default {
},
created() {
this.getFactoryList()
// this.getProductLineList()
// this.getFactoryList()
this.getProductLineList()
},
watch: {},
methods: {
//
getFactoryList() {
this.$http({
url: this.$http.adornUrl('/monitoring/factory/page'),
method: 'get'
}).then(({ data }) => {
if (data && data.code === 0) {
this.factoryList = data.data.list
/** set default */
if (this.factoryList.length) {
this.dataForm.ftId = this.factoryList[0].id
}
} else {
this.factoryList = []
this.dataForm.ftId = null
}
})
},
// getFactoryList() {
// this.$http({
// url: this.$http.adornUrl('/monitoring/factory/page'),
// method: 'get'
// }).then(({ data }) => {
// if (data && data.code === 0) {
// this.factoryList = data.data.list
// /** set default */
// if (this.factoryList.length) {
// this.dataForm.ftId = this.factoryList[0].id
// }
// } else {
// this.factoryList = []
// this.dataForm.ftId = null
// }
// })
// },
// 线
getProductLineList() {
this.$http({
@ -138,46 +138,39 @@ export default {
})
},
//
getTimeRange() {
let startTime
let endTime
if (this.rawTime instanceof Array) {
startTime = this.rawTime[0] ? moment(this.rawTime[0]).format('YYYY-MM-DDTHH:mm:ss') : '' // axios使
endTime = this.rawTime[1] ? moment(this.rawTime[1]).format('YYYY-MM-DDTHH:mm:ss') : ''
} else {
if (this.rawTime) {
startTime = moment(this.rawTime).format('YYYY-MM-DDTHH:mm:ss')
endTime = moment(startTime)
.add(1, 'd')
.format('YYYY-MM-DDTHH:mm:ss')
} else {
startTime = ''
endTime = ''
}
}
return { startTime, endTime }
},
//
getDataList() {
const { startTime, endTime } = this.getTimeRange()
// this.dataList = Array(10).fill(1)
// return
let startTime = this.rawTime
? moment(this.rawTime)
.set({ date: 1, hour: 0, minute: 0, second: 0, millisecond: 0 })
.format('YYYY-MM-DDTHH:mm:ss')
: ''
let endTime = startTime
? moment(startTime)
.add(1, 'M')
.format('YYYY-MM-DDTHH:mm:ss')
: ''
this.dataListLoading = true
this.$http({
url: this.$http.adornUrl('/monitoring/eqAnalysis/oee'),
url: this.$http.adornUrl('/monitoring/eqAnalysis/mtbrAndMtbr'),
method: 'post',
data: {
startTime,
endTime,
ftId: this.dataForm.ftId,
productlines: this.dataForm.productlines,
limit: this.pageIndex,
page: this.pageSize,
type: 1
}
}).then(({ data: res }) => {
console.log('oee data:', res)
if (res.code === 500) {
this.dataList.splice(0)
this.$message.error(res.msg)
} else {
this.dataList = res.data
}
})
},
//

View File

@ -4,13 +4,15 @@
<el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
<!-- 产线 -->
<el-form-item>
<el-select v-model="dataForm.productlines" :placeholder="'产线'" clearable />
<el-select v-model="dataForm.productlines" :placeholder="'产线'" @change="handleProductLineChange" clearable>
<el-option v-for="productLine in productLineList" :key="productLine.id" :value="productLine.id" :label="productLine.name" />
</el-select>
</el-form-item>
<!-- 工序 -->
<el-form-item>
<!-- <el-select v-model="dataForm.factoryId" :placeholder="$t('eq.name') + ' / ' + $t('eq.code')" clearable></el-select> -->
<el-select v-model="dataForm.ftId" :placeholder="'工序'" clearable>
<el-option v-for="factory in factoryList" :key="factory.id" :value="factory.id" :label="factory.name" />
<el-select v-model="dataForm.wsId" :placeholder="'工序'" clearable>
<el-option v-for="ws in wsList" :key="ws.id" :value="ws.id" :label="ws.name" />
</el-select>
</el-form-item>
<!-- 日期选择 -->
@ -22,110 +24,187 @@
<el-button @click="getDataList()">{{ $t('search') }}</el-button>
<!-- <el-button v-if="$hasPermission('monitoring:equipmentEffiency:save')" type="primary" @click="addOrUpdateHandle()">{{ $t('add') }}</el-button> -->
</el-form-item>
<el-form-item>
<el-button type="success" @click="addEq()">{{ '添加设备' }}</el-button>
</el-form-item>
</el-form>
<base-table :data="dataList" :table-head-configs="tableConfigs" :max-height="calcMaxHeight(8)" @operate-event="handleOperations" @refreshDataList="getDataList" />
<el-pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
:current-page="pageIndex"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize"
:total="totalPage"
layout="total, sizes, prev, pager, next, jumper"
></el-pagination>
<div class="time-chart" style="margin-top: 10px;">
<div
v-show="equipmentCount > 0"
id="time-chart__inner"
ref="time-chart__inner"
class="time-chart__inner"
style="min-height: 50vh;"
:style="{ height: autoHeight + 'px', width: '100%' }"
/>
<div v-show="equipmentCount === 0">请先查询数据</div>
<!-- <div v-show="equipmentCount === 0">{{ $t('module.basicData.visual.hints.searchFirst') }}</div> -->
</div>
<el-dialog :visible.sync="dialogVisible" :title="'添加设备'" width="30%">
<el-select v-model="eqId" style="width: 100%" placeholder="请选择设备" clearable>
<el-option v-for="eq in dialogEqList" :key="eq.id" :label="eq.name" :value="eq.id" />
</el-select>
<div slot="footer">
<el-button @click="dialogVisible = false">{{ '取消' }}</el-button>
<el-button type="primary" @click="dialogConfirm">{{ '确定' }}</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import i18n from '@/i18n'
import * as echarts from 'echarts'
// import i18n from '@/i18n'
import BaseTable from '@/components/base-table'
import TableOperateComponent from '@/components/base-table/components/operationComponent'
import TableTextComponent from '@/components/base-table/components/detailComponent'
import { calcMaxHeight } from '@/utils'
import { timeFilter } from '@/utils/filters'
import moment from 'moment'
// import TableOperateComponent from '@/components/base-table/components/operationComponent'
// import TableTextComponent from '@/components/base-table/components/detailComponent'
// import { timeFilter } from '@/utils/filters'
const tableConfigs = [
{
type: 'index',
name: i18n.t('index')
},
{
// name: i18n.t('createTime'),
prop: 'factoryName',
name: '工厂'
},
{ prop: 'pdName', name: '产线' },
{ prop: 'wsName', name: '工段' },
{ prop: 'eqName', name: '设备' },
{
name: '有效时间(h)',
children: [
{ prop: 'workTime', name: '工作时长(h)', width: 120, filter: val => `${val} 小时` },
{ prop: 'workRate', name: '工作时长比率', width: 120, filter: val => (val * 100).toFixed(2) + '%' }
]
},
{
name: '关机时间(h)',
children: [
{ prop: 'stopTime', name: '停机时长(h)', width: 120, filter: val => `${val} 小时` },
{ prop: 'stopRate', name: '停机比率', width: 120, filter: val => (val * 100).toFixed(2) + '%' }
]
},
{
name: '中断损失',
children: [
{ prop: 'downTime', name: '故障时长(h)', width: 120, filter: val => `${val} 小时` },
{ prop: 'downRate', name: '故障比率', width: 120, filter: val => (val * 100).toFixed(2) + '%' },
{ prop: 'timeEfficiency', name: '时间开动率', width: 120, filter: val => (val * 100).toFixed(2) + '%' }
]
},
{
name: '速度损失',
children: [
{ prop: 'realYield', name: '实际加工速度', width: 120, filter: val => `${val} 小时` },
{ prop: 'designYield', name: '理论加工速度', width: 120, filter: val => `${val} 小时` },
{ prop: 'peEfficiency', name: '速度开动率', width: 120, filter: val => (val * 100).toFixed(2) + '%' }
]
},
{
name: 'OEE',
prop: 'oee',
filter: val => (val * 100).toFixed(2) + '%'
},
{
name: 'TEEP',
prop: 'teep',
filter: val => (val * 100).toFixed(2) + '%'
},
{
prop: 'operations',
name: i18n.t('handle'),
fixed: 'right',
width: 180,
subcomponent: TableOperateComponent,
// options: ['edit', 'delete']
options: ['view-trend'] //
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 = 32
return {
type: 'rect',
shape: 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
}
),
style: api.style()
}
]
}
class ChartOption {
constructor() {
this.color = ['#4caf50', '#ffb300', '#e53935']
this.legend = {
data: [
// i18n.t('module.basicData.visual.echartLegends.working'),
'正常',
'停机',
'故障'
],
bottom: '0%',
selectedMode: false,
textStyle: {
color: '#000'
}
}
this.tooltip = {
formatter: function(params) {
return moment(params.value[1]).format('YYYY-MM-DD HH:mm:ss') + ' - ' + moment(params.value[2]).format('YYYY-MM-DD HH:mm:ss') + ' : ' + params.name
}
}
this.title = {
// text: i18n.t('module.basicData.visual.echartTitles.eqStatus'),
text: '设备状态时序图',
left: 'center'
}
this.xAxis = {
type: 'time',
// min: +new Date().setHours(0, 0, 0, 0),
splitNumber: 24,
interval: 3600 * 1000,
axisTick: {
show: true,
alignWithLabel: true
},
splitLine: {
show: true
},
axisLine: {
show: true
},
axisLabel: {
formatter: function(val) {
const time = new Date(val)
const hour = time.getHours()
const minute = time.getMinutes()
const hours = hour >= 10 ? hour + '' : '0' + hour
const minutes = minute >= 10 ? minute + '' : '0' + minute
return hours + ':' + minutes
}
}
}
this.yAxis = {
// data: Object.keys(eqData).map(item => eqData[item].name)
data: []
}
this.series = [
{ name: /** i18n.t('module.basicData.visual.echartLegends.working') */ '正常', type: 'bar', data: [] },
{ name: '停机', type: 'bar', data: [] },
{ name: '故障', type: 'bar', data: [] },
{
type: 'custom',
renderItem: renderItem,
itemStyle: {
opacity: 0.8
},
encode: {
x: [1, 2],
y: 0
},
data: []
}
]
}
setTitle(title) {
this.title.text = title
}
// date:
setXAxis(date) {
// this.xAxis.min = +new Date(date).setHours(0)
this.xAxis.min = +new Date(date).setHours(0, 0, 0, 0)
this.xAxis.max = this.xAxis.min + 3600 * 1000 * 24
}
setYAxis(data) {
this.yAxis.data = data
}
setData(data) {
this.series[3].data = data
}
}
export default {
data() {
return {
/** hfxny part */
factoryList: [],
wsList: [],
productLineList: [],
/** */
chart: null,
chartOption: new ChartOption(),
equipments: {},
state: ['正常', '停机', '故障'],
colors: ['#4caf50', '#ffb300', '#e53935'],
// queryBuffer: {},
// tableConfigs,
calcMaxHeight,
tableConfigs,
timeType: 'range',
rawTime: null, // [] or datetime
currentLine: null,
rawTime: new Date(),
dataForm: {
type: 1,
ftId: null,
productlines: [],
wsId: null,
productlines: null,
startTime: null,
entTime: null
},
@ -133,16 +212,37 @@ export default {
pageIndex: 1,
pageSize: 10,
totalPage: 0,
dataListLoading: false
dataListLoading: false,
/** dialog */
dialogVisible: false,
eqId: null,
dialogEqList: []
}
},
components: {
BaseTable
},
computed: {
autoHeight: function() {
return Object.keys(this.equipments).length * 100 || 500
},
equipmentCount: function() {
return Object.keys(this.equipments).length
}
},
created() {
this.getFactoryList()
this.getProductLineList()
this.getProductLineList().then(() => {
this.getWorksetionList()
})
this.getEqList()
},
mounted() {
this.$nextTick(() => {
this.initChart()
})
},
updated() {
if (this.chart) this.chart.resize()
},
watch: {
timeType() {
@ -151,27 +251,14 @@ export default {
}
},
methods: {
//
getFactoryList() {
this.$http({
url: this.$http.adornUrl('/monitoring/factory/page'),
method: 'get'
}).then(({ data }) => {
if (data && data.code === 0) {
this.factoryList = data.data.list
/** set default */
if (this.factoryList.length) {
this.dataForm.ftId = this.factoryList[0].id
}
} else {
this.factoryList = []
this.dataForm.ftId = null
}
})
initChart() {
if (!this.chart) {
this.chart = echarts.init(this.$refs['time-chart__inner'])
}
},
// 线
getProductLineList() {
this.$http({
return this.$http({
url: this.$http.adornUrl('/monitoring/productionLine/page'),
method: 'get'
}).then(({ data }) => {
@ -179,7 +266,7 @@ export default {
this.productLineList = data.data.list
/** set default */
if (this.productLineList.length) {
this.dataForm.productlines = [this.productLineList[0].id]
this.dataForm.productlines = this.productLineList[0].id
}
} else {
this.productLineList = []
@ -187,55 +274,246 @@ export default {
})
},
//
getTimeRange() {
let startTime
let endTime
if (this.rawTime instanceof Array) {
startTime = this.rawTime[0] ? moment(this.rawTime[0]).format('YYYY-MM-DDTHH:mm:ss') : '' // axios使
endTime = this.rawTime[1] ? moment(this.rawTime[1]).format('YYYY-MM-DDTHH:mm:ss') : ''
} else {
if (this.rawTime) {
startTime = moment(this.rawTime).format('YYYY-MM-DDTHH:mm:ss')
endTime = moment(startTime)
.add(1, 'd')
.format('YYYY-MM-DDTHH:mm:ss')
//
getWorksetionList() {
// 线
this.$http({
// url: this.$http.adornUrl('/monitoring/workshopSection/list'),
url: this.$http.adornUrl('/monitoring/workshopSection/page'),
method: 'get',
params: this.$http.adornParams({
limit: 99999,
page: 1,
lineId: this.dataForm.productlines ?? ''
})
}).then(({ data: res }) => {
if (res && res.code === 0) {
this.wsList = res.data.list
/** set default */
if (this.wsList.length) {
this.dataForm.wsId = this.wsList[0].id
} else {
this.dataForm.wsId = null
}
} else {
startTime = ''
endTime = ''
this.wsList.splice(0)
}
}
return { startTime, endTime }
})
},
//
transformDataToEquipments(dataList) {
console.log('transformDataToEquipments() datalist: ', dataList)
const equipments = {}
dataList.map(item => {
if (equipments[item.eqId]) {
//
// equipments[item.eqId].name = item.eqName
equipments[item.eqId].status.push({
startTime: +new Date(item.startTime),
endTime: +new Date(item.endTime),
status: this.state[item.status] // 0 1 2
})
} else {
equipments[item.eqId] = {
name: item.eqName,
status: [
{
startTime: +new Date(item.startTime),
endTime: +new Date(item.endTime),
status: this.state[item.status] // 0 1 2
}
]
}
}
})
console.log('created equipments --- ', equipments)
return equipments
},
// echartsseries
transformEquipmentsToSeries(equipments) {
const seriesData = []
Object.entries(equipments).map(([key, item], index) => {
item.status.forEach(status => {
seriesData.push({
name: status.status,
value: [index, status.startTime, status.endTime],
itemStyle: {
normal: {
color: status.status === '正常' ? '#4caf50' : status.status === '停机' ? '#ffb300' : status.status === '故障' ? '#e53935' : null
}
}
})
})
})
return seriesData
},
//
getDataList() {
const { startTime, endTime } = this.getTimeRange()
let startTime = this.rawTime
? moment(this.rawTime)
.set({ hour: 0, minute: 0, second: 0, millisecond: 0 })
.format('YYYY-MM-DDTHH:mm:ss')
: ''
let endTime = startTime
? moment(startTime)
.add(1, 'd')
.format('YYYY-MM-DDTHH:mm:ss')
: ''
this.dataListLoading = true
// this.dataListLoading = true
const condition = {
startTime,
endTime,
productlines: [this.dataForm.productlines],
wsId: this.dataForm.wsId
}
/** fetch data */
this.$http({
url: this.$http.adornUrl('/monitoring/eqAnalysis/oee'),
url: this.$http.adornUrl('/monitoring/eqAnalysis/timeAndStatus'),
method: 'post',
data: {
startTime,
endTime,
ftId: this.dataForm.ftId,
productlines: this.dataForm.productlines,
limit: this.pageIndex,
page: this.pageSize,
type: 1
}
data: condition
}).then(({ data: res }) => {
console.log('oee data:', res)
if (res.length) {
this.dataList = res
this.totalPage = res.length
} else {
if (res.code === 500) {
this.dataList.splice(0)
this.totalPage = 0
this.equipments = {} // echarts
this.$message.error(res.msg)
} else {
/** handle actual data */
this.dataList = res.data
/** test data */
// this.dataList = [
// {
// eqId: 'eq-001',
// eqName: 'A1',
// startTime: '2022-05-04T00:30:34',
// endTime: '2022-05-04T08:30:34',
// status: 0
// },
// {
// eqId: 'eq-001',
// eqName: 'A1',
// startTime: '2022-05-04T08:30:34',
// endTime: '2022-05-04T09:30:34',
// status: 1
// },
// {
// eqId: 'eq-001',
// eqName: 'A1',
// startTime: '2022-05-04T09:30:34',
// endTime: '2022-05-04T11:30:34',
// status: 2
// },
// {
// eqId: 'eq-001',
// eqName: 'A1',
// startTime: '2022-05-04T11:30:34',
// endTime: '2022-05-04T13:30:34',
// status: 1
// }
// ]
this.equipments = this.transformDataToEquipments(this.dataList)
this.chartOption.setYAxis(Object.keys(this.equipments).map(eId => this.equipments[eId].name))
console.log('(((set x axis))): ', this.dataList[0].startTime)
this.chartOption.setXAxis(this.dataList[0].startTime)
this.chartOption.setData(this.transformEquipmentsToSeries(this.equipments))
this.$nextTick(() => {
this.renderChart()
})
}
})
},
handleProductLineChange(val) {
this.getWorksetionList()
},
// echarts
renderChart() {
console.log('>>> chart configs: ', this.chartOption)
this.chart.setOption(this.chartOption)
},
//
getEqList() {
this.$http({
url: this.$http.adornUrl('/monitoring/equipment/page'),
method: 'get',
params: this.$http.adornParams({
page: 1,
limit: 99999
})
}).then(({ data }) => {
if (data && data.code === 0) {
this.dialogEqList = data.data.list
} else {
this.dialogEqList.splice(0)
}
})
},
//
addEq() {
if (this.equipmentCount) {
this.dialogVisible = true
} else {
this.$message.warning('请先查询数据')
}
},
//
dialogConfirm() {
let startTime = this.rawTime
? moment(this.rawTime)
.set({ hour: 0, minute: 0, second: 0, millisecond: 0 })
.format('YYYY-MM-DDTHH:mm:ss')
: ''
let endTime = startTime
? moment(startTime)
.add(1, 'd')
.format('YYYY-MM-DDTHH:mm:ss')
: ''
const condition = {
startTime,
endTime,
productlines: [this.dataForm.productlines],
wsId: this.dataForm.wsId,
eqId: this.eqId
}
/** fetch data */
this.$http({
url: this.$http.adornUrl('/monitoring/eqAnalysis/timeAndStatus'),
method: 'post',
data: condition
}).then(({ data: res }) => {
if (res.code === 500) {
this.$message.error(res.msg)
} else {
/** handle new equipment */
const newEqStatusList = res.data
console.log('添加设备', res)
const newEq = this.transformDataToEquipments(newEqStatusList)
this.$set(this.equipments, Object.keys(newEq)[0], newEq[Object.keys(newEq)[0]])
this.chartOption.setYAxis(Object.keys(this.equipments).map(item => this.equipments[item].name))
this.chartOption.setData(this.transformEquipmentsToSeries(this.equipments))
this.$message.success('新设备数据获取成功')
this.$nextTick(() => {
this.dialogVisible = false
this.renderChart()
})
}
})
},
//
sizeChangeHandle(val) {
this.pageSize = val
@ -264,3 +542,9 @@ export default {
}
}
</script>
<style scoped>
/* .time-chart__inner {
transition: all 300ms ease-out;
} */
</style>