571 lines
15 KiB
Vue
571 lines
15 KiB
Vue
<template>
|
|
<!-- 设备效率分析 -->
|
|
<div class="mod-config">
|
|
<el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()" class="blueTip" size="small">
|
|
<!-- 产线 -->
|
|
<el-form-item>
|
|
{{ $t('pl.title') }}
|
|
</el-form-item>
|
|
<el-form-item>
|
|
<el-select v-model="dataForm.productlines" :placeholder="$t('pl.title')" @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>
|
|
{{ $t('pl.process') }}
|
|
</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.wsId" :placeholder="$t('pl.process')" clearable>
|
|
<el-option v-for="ws in wsList" :key="ws.id" :value="ws.id" :label="ws.name" />
|
|
</el-select>
|
|
</el-form-item>
|
|
<!-- 日期选择 -->
|
|
<el-form-item>
|
|
{{ $t('time') }}
|
|
</el-form-item>
|
|
<el-form-item>
|
|
<el-date-picker key="date-picker" v-model="rawTime" type="date" :placeholder="$t('time')" format="yyyy-MM-dd" />
|
|
</el-form-item>
|
|
<!-- 按钮 -->
|
|
<el-form-item>
|
|
<el-button class="buttonColor" @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()">{{ $t('pl.add') }}</el-button>
|
|
</el-form-item>
|
|
</el-form>
|
|
|
|
<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">{{ $t('pl.queryFirst') }}</div>
|
|
<!-- <div v-show="equipmentCount === 0">{{ $t('module.basicData.visual.hints.searchFirst') }}</div> -->
|
|
</div>
|
|
|
|
<el-dialog :visible.sync="dialogVisible" :title="$t('pl.add')" width="30%">
|
|
<el-select v-model="eqId" style="width: 100%" :placeholder="$t('pl.choose')" 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">{{ $t('pl.cancel') }}</el-button>
|
|
<el-button type="primary" @click="dialogConfirm">{{ $t('pl.confirm') }}</el-button>
|
|
</div>
|
|
</el-dialog>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import * as echarts from 'echarts'
|
|
import i18n from '@/i18n'
|
|
import BaseTable from '@/components/base-table'
|
|
import { calcMaxHeight } from '@/utils'
|
|
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'
|
|
|
|
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'),
|
|
i18n.t('normal'),
|
|
i18n.t('shutdown'),
|
|
i18n.t('malfunction')
|
|
],
|
|
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: i18n.t('diagram'),
|
|
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') */ i18n.t('normal'), type: 'bar', data: [] },
|
|
{ name: i18n.t('shutdown'), type: 'bar', data: [] },
|
|
{ name: i18n.t('malfunction'), 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 */
|
|
wsList: [],
|
|
productLineList: [],
|
|
chart: null,
|
|
chartOption: new ChartOption(),
|
|
equipments: {},
|
|
state: [this.$t('normal'),this.$t('shutdown'),this.$t('malfunction')],
|
|
colors: ['#4caf50', '#ffb300', '#e53935'],
|
|
// queryBuffer: {},
|
|
// tableConfigs,
|
|
calcMaxHeight,
|
|
timeType: 'range',
|
|
currentLine: null,
|
|
rawTime: new Date(),
|
|
dataForm: {
|
|
wsId: null,
|
|
productlines: null,
|
|
startTime: null,
|
|
entTime: null
|
|
},
|
|
dataList: [],
|
|
pageIndex: 1,
|
|
pageSize: 10,
|
|
totalPage: 0,
|
|
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.getEqList()
|
|
},
|
|
mounted() {
|
|
this.$nextTick(() => {
|
|
this.initChart()
|
|
})
|
|
},
|
|
activated() {
|
|
this.getProductLineList().then(() => {
|
|
this.getWorksetionList()
|
|
})
|
|
},
|
|
updated() {
|
|
if (this.chart) this.chart.resize()
|
|
},
|
|
watch: {
|
|
timeType() {
|
|
// 防止切换日期类型时报错
|
|
this.rawTime = null
|
|
}
|
|
},
|
|
methods: {
|
|
initChart() {
|
|
if (!this.chart) {
|
|
this.chart = echarts.init(this.$refs['time-chart__inner'])
|
|
}
|
|
},
|
|
// 获取产线列表
|
|
getProductLineList() {
|
|
return this.$http({
|
|
url: this.$http.adornUrl('/monitoring/productionLine/list'),
|
|
method: 'get'
|
|
}).then(({ data: res }) => {
|
|
if (res && res.code === 0) {
|
|
this.productLineList = res.data
|
|
/** set default */
|
|
if (this.productLineList.length) {
|
|
this.dataForm.productlines = this.productLineList[0].id
|
|
}
|
|
} else {
|
|
this.productLineList = []
|
|
}
|
|
})
|
|
},
|
|
|
|
// 获取工序列表
|
|
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
|
|
/** select 默认选项set default */
|
|
// if (this.wsList.length) {
|
|
// this.dataForm.wsId = this.wsList[0].id
|
|
// } else {
|
|
// this.dataForm.wsId = null
|
|
// }
|
|
} else {
|
|
this.wsList.splice(0)
|
|
}
|
|
})
|
|
},
|
|
|
|
// 把服务器数据按照设备分组
|
|
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
|
|
},
|
|
|
|
// 把分组好的设备数据转换为echarts需要的series数据
|
|
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 === this.$t('normal') ? '#4caf50' : status.status === this.$t('shutdown') ? '#ffb300' : status.status === this.$t('malfunction') ? '#e53935' : null
|
|
}
|
|
}
|
|
})
|
|
})
|
|
})
|
|
return seriesData
|
|
},
|
|
|
|
// 获取数据列表
|
|
getDataList() {
|
|
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
|
|
const condition = {
|
|
startTime,
|
|
endTime,
|
|
productlines: [this.dataForm.productlines],
|
|
wsId: this.dataForm.wsId
|
|
}
|
|
|
|
/** fetch data */
|
|
this.$http({
|
|
url: this.$http.adornUrl('/monitoring/eqAnalysis/timeAndStatus'),
|
|
method: 'post',
|
|
data: condition
|
|
}).then(({ data: res }) => {
|
|
if (res.code === 500) {
|
|
this.dataList.splice(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(this.$t('pl.queryFirst'))
|
|
}
|
|
},
|
|
|
|
// 确认添加设备
|
|
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.$t('pl.success'))
|
|
this.$nextTick(() => {
|
|
this.dialogVisible = false
|
|
this.renderChart()
|
|
})
|
|
}
|
|
})
|
|
},
|
|
|
|
// 每页数
|
|
sizeChangeHandle(val) {
|
|
this.pageSize = val
|
|
this.pageIndex = 1
|
|
this.getDataList()
|
|
},
|
|
// 当前页
|
|
currentChangeHandle(val) {
|
|
this.pageIndex = val
|
|
this.getDataList()
|
|
},
|
|
// 多选
|
|
selectionChangeHandle(val) {
|
|
this.dataListSelections = val
|
|
},
|
|
handleOperations({ type, data: id }) {
|
|
switch (type) {
|
|
case 'view-detail':
|
|
return this.addOrUpdateHandle(id, true)
|
|
case 'edit':
|
|
return this.addOrUpdateHandle(id)
|
|
case 'delete':
|
|
return this.deleteHandle(id)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
/* .time-chart__inner {
|
|
transition: all 300ms ease-out;
|
|
} */
|
|
.blueTip::before {
|
|
display: inline-block;
|
|
content: '';
|
|
width: 4px;
|
|
height: 24px;
|
|
background: #0b58ff;
|
|
border-radius: 1px;
|
|
margin-right: 8px;
|
|
margin-top: 4px;
|
|
}
|
|
.buttonColor {
|
|
color: #fff;
|
|
background: #0b58ff;
|
|
}
|
|
</style>
|