Commit 78a4a8b1 by guibin

更新安灯数据看板

parent be33b028
......@@ -7,10 +7,16 @@ import math
from datetime import datetime, time
from jinja2 import Environment, FileSystemLoader
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
templateloader = FileSystemLoader(searchpath=BASE_DIR + "/static/src/view")
templateloader = FileSystemLoader(searchpath=BASE_DIR + "/static")
env = Environment(loader=templateloader)
class ThtProject(http.Controller):
@http.route('/roke/abnormal_alarm/census', type='http', auth='public', csrf=False, cors="*")
def roke_index_demo_module(self, **kwargs):
template = env.get_template('html/abnormal_alarm/view/index.html')
html = template.render({})
return html
@http.route('/tht/get/equipment/working_time', type='json', auth="none", csrf=False, cors='*')
def get_equipment_working_time(self, **kwargs):
"""获取车间工作时间"""
......@@ -54,7 +60,7 @@ class RokeMesThreeColourLightExt(RokeMesThreeColourLight):
"override": True # 添加额外字段
}
}
template = env.get_template('equipment_status.html')
template = env.get_template('src/view/equipment_status.html')
return template.render(data)
@http.route('/roke/equipment/search', type='json', methods=['POST', 'OPTIONS'], auth="none", csrf=False,
......
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>安灯数据</title>
<meta content="width=device-width,initial-scale=1.0, maximum-scale=1.0,user-scalable=0" name="viewport" />
<!-- /roke_workstation_api/static/html/abnormal_alarm -->
<link rel="stylesheet" href="/roke_workstation_api/static/html/abnormal_alarm/element-ui/index.css" />
<link rel="stylesheet" href="/roke_workstation_api/static/html/abnormal_alarm/css/dark_element_ui.css" />
<script src="/roke_workstation_api/static/html/abnormal_alarm/js/vue.js"></script>
<script src="/roke_workstation_api/static/html/abnormal_alarm/js/axios.min.js"></script>
<script src="/roke_workstation_api/static/html/abnormal_alarm/element-ui/index.js"></script>
<script src="/roke_workstation_api/static/html/abnormal_alarm/js/echarts.min.js"></script>
</head>
<body id="bodyId" style="display: none">
<div id="app" v-loading.body.fullscreen.lock="loading">
<!-- 今日工位呼叫明细 -->
<div class="panelCardBox">
<div class="panelHeaderBox">今日工位呼叫明细</div>
<div ref="panelTableRef" class="panelTableBox" @mouseenter="mouseEnterHandle" @mouseleave="mouseLeaveHandle"
v-show="stationCallRecordList.length">
<div class="panelTableHeader">
<div class="roll_line">
<div>产线</div>
<div>工位</div>
<div>呼叫类型</div>
<div>呼叫时间</div>
<div>处理时间</div>
</div>
</div>
<div ref="vlist" class="vabsolute_content">
<div class="fragment_content"></div>
<div class="fragment_content"></div>
</div>
</div>
<div class="emptyBox" v-show="!stationCallRecordList.length">
<el-empty image="/roke_workstation_api/static/html/abnormal_alarm/images/empty.png" description="暂无数据"
:image-size="100"></el-empty>
</div>
</div>
<!-- 今日产品缺陷走势图 -->
<div class="panelCardBox">
<div class="panelHeaderBox">
<span>今日产品缺陷走势图</span>
<span class="unitBox">单位:个</span>
<el-select v-model="selClassesValue" size="mini" placeholder="选择班次"
@change="selChangeHandle($event,'今日产品缺陷走势图')">
<el-option v-for="item in classesList" :key="item.id" :label="item.name" :value="item.id">
</el-option>
</el-select>
</div>
<div ref="defectTrendRef" style="width: 100%; height: calc(100% - 28px)"></div>
</div>
<!-- 今日呼叫处理情况分析 -->
<div class="panelCardBox">
<div class="panelHeaderBox">今日呼叫处理情况分析</div>
<div class="handleStatuBox">
<div class="statuItemBox" v-for="(item, index) in statusData" :key="index">
<span class="number" :style="{background: item.color}">[[item.count]]</span>
<span class="label">[[item.label]]</span>
</div>
</div>
</div>
<!-- 近一周生产中断等待时长统计 -->
<div class="panelCardBox">
<div class="panelHeaderBox">
<span>近一周生产中断等待时长统计</span>
<span class="unitBox">单位:分钟</span>
<el-select v-model="selProductionLineValue" size="mini" placeholder="选择产线" filterable
@change="selChangeHandle($event,'近一周生产中断等待时长统计')">
<el-option v-for="item in productionLineList" :key="item.id" :label="item.name" :value="item.id">
</el-option>
</el-select>
</div>
<div ref="weeklyWaitTimeRef" style="width: 100%; height: calc(100% - 28px)"></div>
</div>
<!-- 近一周安灯呼叫类型统计 -->
<div class="panelCardBox">
<div class="panelHeaderBox">近一周安灯呼叫类型统计</div>
<div v-show="callTypePieChartData.length>0" ref="callTypePieRef"
style="width: 100%; height: calc(100% - 28px)"></div>
<div class="emptyBox" v-show="!callTypePieChartData.length">
<el-empty image="/roke_workstation_api/static/html/abnormal_alarm/images/empty.png" description="暂无数据"
:image-size="100"></el-empty>
</div>
</div>
<!-- 近一周设备故障频频率 -->
<div class="panelCardBox">
<div class="panelHeaderBox">
<span>近一周设备故障频率</span>
<span class="unitBox">单位:次</span>
<el-select v-model="selEquipmentValue" size="mini" placeholder="选择设备" filterable
@change="selChangeHandle($event,'近一周设备故障频频率')">
<el-option v-for="item in equipmentList" :key="item.id" :label="item.name" :value="item.id">
</el-option>
</el-select>
</div>
<div ref="deviceFailureRef" style="width: 100%; height: calc(100% - 28px)"></div>
</div>
</div>
</body>
<script>
// 发送消息给父页面(关闭odoo的菜单弹窗)
document.addEventListener("click", () => {
window.parent.postMessage("hidePopover", "*")
})
let vue = new Vue({
el: "#app",
delimiters: ["[[", "]]"],
data() {
return {
windowHeight: window.innerHeight, // 页面高度
baseURL: "", // 基地址 http://192.168.8.114:8069
loading: false, // 全局加载效果
stationCallRecordList: [], // 工位呼叫明细数据
containerHeight: 0, // 表体区域高度
perCount: 0, // 可以展示多少行
tableLineHeight: 40, // 表格行高
startIndex: 0, // 片段的起始索引
animationFrameId: null, // 动画帧id
frameCount: 0, // 当前帧数
chartsProject: {}, // 存储图表实例
classesList: [], // 班次列表
selClassesValue: null, // 选中班次的值
defectTrendChartData: { keys: [], values: [] }, // 今日产品缺陷走势图数据
// 今日呼叫处理情况分析数据
statusData: [
{ count: 0, label: '待处理', color: '#F7BA2A' },
{ count: 0, label: '处理中', color: '#67C23A' },
{ count: 0, label: '超时处理', color: '#F56C6C' },
{ count: 0, label: '停线呼叫', color: '#F56C6C' },
{ count: 0, label: '非停线呼叫', color: '#909399' },
{ count: 0, label: '质量异常', color: '#409EFF' },
{ count: 0, label: '工艺异常', color: '#B8E6FF' },
{ count: 0, label: '设备异常', color: '#E6A23C' }
],
productionLineList: [], // 产线列表
selProductionLineValue: null, // 选中产线的值
weeklyWaitTimeChartData: { keys: [], values: [] }, // 近一周生产中断等待时长统计数据
callTypePieChartData: [], // 近一周安灯呼叫类型统计数据
equipmentList: [], // 设备列表
selEquipmentValue: null, // 选中设备的值
deviceFailureChartData: { keys: [], values: [] }, // 近一周设备故障频率数据
}
},
async created() {
this.loading = true
await Promise.allSettled([
this.getStationCallRecordListApi(), // 获取今日工位呼叫明细数据
this.generalQueryApi('班次'), // 获取班次列表
this.generalQueryApi('产线'), // 获取产线列表
this.generalQueryApi('设备'), // 获取设备列表
this.getChartDataApi() // 获取图表数据
]).then(() => {
// 确保DOM完全加载后再初始化图表
this.initAllCharts()
})
this.loading = false
},
mounted() {
this.$nextTick(() => {
document.getElementById("bodyId").style.display = "block"
})
window.addEventListener("resize", this.resizeHandle)
},
methods: {
// 通用查询接口
generalQueryApi(type) {
return new Promise((resolve, reject) => {
let config = {
model: '',
fields: ['id', 'name'],
domain: []
}
if (type == '班次') {
config.model = 'roke.classes'
config.fields = ['id', 'name', 'start_time', 'end_time']
config.domain = [["main_id.is_default", "=", true]]
} else if (type == '产线') {
config.model = 'roke.workshop'
} else if (type == '设备') {
config.model = 'roke.mes.equipment'
}
if (!config.model) return
axios({
method: "POST",
url: this.baseURL + '/roke/workstation/search_read',
data: config,
headers: { "Content-Type": "application/json" }
}).then(async (result) => {
if (result?.data?.result?.code == 0) {
const data = result.data.result.data
if (type == '班次') {
data.forEach(item => {
item.start_time = this.convertNumberToTime(item.start_time)
item.end_time = this.convertNumberToTime(item.end_time)
item.name = `${item.name}(${item.start_time}~${item.end_time})`
})
this.classesList = data
this.selClassesValue = data.length > 0 ? data[0].id : null
// 获取今日产品缺陷走势图
if (this.selClassesValue) await this.getProductDefectChartListApi()
} else if (type == '产线') {
let list = [{ id: 0, name: '所有产线' }]
this.productionLineList = [...list, ...data]
this.selProductionLineValue = 0
} else if (type == '设备') {
let list = [{ id: 0, name: '所有设备' }]
this.equipmentList = [...list, ...data]
this.selEquipmentValue = 0
}
resolve()
} else if (result?.data?.result?.code == 1) {
this.$message.error(result.data.result.message)
reject()
} else if (result?.data?.error) {
this.$message.error(result.data.error.message)
reject()
}
}).catch((error) => {
console.error(error)
reject()
})
})
},
// 获取今日工位呼叫明细数据
getStationCallRecordListApi(isRefresh) {
return new Promise((resolve, reject) => {
if (isRefresh) this.resetAnimationFn()
axios({
method: "POST",
url: this.baseURL + '/get/workstation/everyday/abnormal',
data: {},
headers: { "Content-Type": "application/json" }
}).then((result) => {
if (result?.data?.result?.code == 0) {
const data = result.data.result.data
const general = result.data.result.general
this.statusData.forEach(item => {
if (item.label == '待处理') {
item.count = general.draft
} else if (item.label == '处理中') {
item.count = general.start
} else if (item.label == '超时处理') {
item.count = general.overtime
} else if (item.label == '停线呼叫') {
item.count = general.stop
} else if (item.label == '非停线呼叫') {
item.count = general.running
} else if (item.label == '质量异常') {
item.count = general.quality
} else if (item.label == '工艺异常') {
item.count = general.routing
} else if (item.label == '设备异常') {
item.count = general.equipment
}
})
data.forEach(item => {
item.originating_time = item?.originating_time ? item.originating_time.split(' ')[1] : '/'
item.arrive_time = item?.arrive_time ? item.arrive_time.split(' ')[1] : '/'
})
this.stationCallRecordList = data
// 重新初始化滚动表格
this.initScrollTable()
resolve()
} else if (result?.data?.result?.code == 1) {
this.$message.error(result.data.result.message)
reject()
} else if (result?.data?.error) {
this.$message.error(result.data.error.message)
reject()
}
}).catch((error) => {
this.$message.error(error)
reject()
})
})
},
// 获取今日产品缺陷走势图
getProductDefectChartListApi() {
return new Promise((resolve, reject) => {
axios({
method: "POST",
url: this.baseURL + '/get/workstation/everyday/hourly_unqualified_qty',
data: {
classes: this.selClassesValue // 班次id
},
headers: { "Content-Type": "application/json" }
}).then((result) => {
if (result?.data?.result?.code == 0) {
const data = result.data.result.data
this.defectTrendChartData.keys = Object.keys(data)
this.defectTrendChartData.values = Object.values(data)
resolve()
} else if (result?.data?.result?.code == 1) {
this.$message.error(result.data.result.message)
reject()
} else if (result?.data?.error) {
this.$message.error(result.data.error.message)
reject()
}
}).catch((error) => {
this.$message.error(error)
reject()
})
})
},
// 获取图表数据
getChartDataApi() {
return new Promise((resolve, reject) => {
axios({
method: "POST",
url: this.baseURL + '/get/workstation/week/abnormal',
data: {
workshop_id: this.selProductionLineValue, // 产线id
equipment_id: this.selEquipmentValue, // 设备id
},
headers: { "Content-Type": "application/json" }
}).then((result) => {
if (result?.data?.result?.code == 0) {
const data = result.data.result.data
let dayList = []
let countList = []
let equipmentCountList = []
data.week.forEach(item => {
dayList.push(item.day + '号')
countList.push(item.count)
equipmentCountList.push(item.equipment_count)
})
this.weeklyWaitTimeChartData.keys = dayList
this.deviceFailureChartData.keys = dayList
this.weeklyWaitTimeChartData.values = countList
this.deviceFailureChartData.values = equipmentCountList
let callTypePieChartData = []
if (data.routing) callTypePieChartData.push({ name: '工艺问题', value: data.routing })
if (data.quality) callTypePieChartData.push({ name: '质量问题', value: data.quality })
if (data.starving) callTypePieChartData.push({ name: '缺料问题', value: data.starving })
if (data.equipment) callTypePieChartData.push({ name: '设备维修', value: data.equipment })
if (data.shape) callTypePieChartData.push({ name: '外形问题', value: data.shape })
this.callTypePieChartData = callTypePieChartData
resolve()
} else if (result?.data?.result?.code == 1) {
this.$message.error(result.data.result.message)
reject()
} else if (result?.data?.error) {
this.$message.error(result.data.error.message)
reject()
}
}).catch((error) => {
this.$message.error(error)
reject()
})
})
},
// 页面大小改变事件
resizeHandle() {
this.windowHeight = window.innerHeight
this.chartsResizeFn()
},
// 重置动画方法
resetAnimationFn() {
// 清空动画帧
if (this.animationFrameId) {
window.cancelAnimationFrame(this.animationFrameId)
this.animationFrameId = null
}
// 重置起始索引
this.startIndex = 0
// 清空现有数据
this.stationCallRecordList = []
// 清空现有片段内容
const fragments = this.$refs.vlist.children
Array.from(fragments).forEach(fragment => {
fragment.innerHTML = ''
})
},
// 表格区域鼠标进去事件
mouseEnterHandle() {
// 鼠标放到表格暂停滚动
if (this.animationFrameId) {
window.cancelAnimationFrame(this.animationFrameId)
this.animationFrameId = null
}
},
// 表格区域鼠标离开事件
mouseLeaveHandle() {
// 数据量少于可显示行数时,不启动滚动
if (this.stationCallRecordList.length <= this.perCount) return
// 否则启动滚动
if (!this.animationFrameId) this.loopScrollAnimationFn()
},
// 初始化滚动表格
initScrollTable() {
this.$nextTick(() => {
setTimeout(() => {
// 获取表格高度
this.containerHeight = this.$refs.panelTableRef.offsetHeight - 40
// 计算可以展示多少行
this.perCount = Math.floor(this.containerHeight / this.tableLineHeight)
// 清空现有片段内容
const vlist = this.$refs.vlist
vlist.innerHTML = ''
// 创建第一个片段
const firstFragment = document.createElement('div')
firstFragment.className = 'fragment_content'
vlist.appendChild(firstFragment)
// 如果数据量少于可显示行数,直接显示所有数据不滚动
if (this.stationCallRecordList.length <= this.perCount) {
// 填充所有数据
this.fillScrollTableContentFn(firstFragment, 0, this.stationCallRecordList.length)
return
}
// 数据量足够时,创建第二个片段并启动滚动
const secondFragment = document.createElement('div')
secondFragment.className = 'fragment_content'
vlist.appendChild(secondFragment)
this.loopScrollAnimationFn(true)
}, 1)
})
},
// 循环滚动动画方法
loopScrollAnimationFn(isInit = false) {
// 是否有数据可以显示,如果没有数据,直接返回
if (!this.stationCallRecordList.length > 0) return
if (this.startIndex === 0 && isInit === true) {
// 初始化阶段
// 获取第一个片段DOM元素
const firstFragment = this.$refs.vlist.firstElementChild
// 填充第一个片段的数据
this.fillScrollTableContentFn(firstFragment, this.startIndex)
// 计算下一个片段的起始索引
this.startIndex = this.countStartIndexFn(this.startIndex)
// 获取第二个片段DOM元素
const secondFragment = this.$refs.vlist.lastElementChild
// 填充第二个片段的数据
this.fillScrollTableContentFn(secondFragment, this.startIndex)
// 再次计算下一个片段的起始索引
this.startIndex = this.countStartIndexFn(this.startIndex)
// 设置初始位置为0
this.$refs.vlist.style.transform = 'translateY(0)'
} else {
// 滚动阶段
// 获取滚动容器DOM
const vlist = this.$refs.vlist
// 获取当前 transform 值
let currentTransform = vlist.style.transform
// 解析当前Y轴位置,如果没有则默认为0
let currentY = currentTransform ? parseInt(currentTransform.match(/-?\d+/)?.[0] || 0) : 0
// 帧计数器递增
this.frameCount++
// 每2帧执行一次滚动
if (this.frameCount >= 2) {
// 重置帧计数器
this.frameCount = 0
// 判断是否滚动到了一个片段的高度
if (Math.abs(currentY) >= this.tableLineHeight * this.perCount) {
// 移除第一个片段
vlist.removeChild(vlist.firstElementChild)
// 重置位置到顶部
vlist.style.transform = 'translateY(0)'
// 创建新的片段元素
const newFragment = document.createElement('div')
// 设置片段的类名
newFragment.className = 'fragment_content'
// 填充新片段的数据
this.fillScrollTableContentFn(newFragment, this.startIndex)
// 计算下一个片段的起始索引
this.startIndex = this.countStartIndexFn(this.startIndex)
// 将新片段添加到容器末尾
vlist.appendChild(newFragment)
} else {
// 如果未滚动到片段高度,继续向上滚动1像素
vlist.style.transform = `translateY(${currentY - 1}px)`
}
}
}
// 请求下一帧动画
this.animationFrameId = window.requestAnimationFrame(() => this.loopScrollAnimationFn())
},
// 填充滚动表格内容
fillScrollTableContentFn(ele, startIndex, customCount) {
// 创建文档片段,用于提高性能,避免多次DOM操作
const fragment = document.createDocumentFragment()
// 记录当前数据索引
let currentIndex = startIndex
// 使用自定义数量或默认的perCount
const count = customCount || this.perCount
// 循环创建每一行数据
for (let i = 0; i < count; i++) {
// 获取当前行的数据
const curData = this.stationCallRecordList[currentIndex]
if (!curData) break // 如果没有数据了就停止
// 创建行容器
const newLine = document.createElement('div')
// 设置行的样式类
newLine.className = 'roll_line'
// 创建产线列
const workshop_name = document.createElement('div')
workshop_name.innerText = curData?.workshop_name ? curData.workshop_name : '/'
// 创建工位列
const work_center_name = document.createElement('div')
work_center_name.innerText = curData?.work_center_name ? curData.work_center_name : '/'
// 创建呼叫类型列
const abnormal_item = document.createElement('div')
abnormal_item.innerText = curData?.abnormal_item ? curData.abnormal_item : '/'
// 根据不同的呼叫类型设置不同的样式
if (curData?.abnormal_item === '设备故障') {
abnormal_item.className = 'danger'
} else if (curData?.abnormal_item === '质量问题') {
abnormal_item.className = 'warning'
} else if (curData?.abnormal_item === '其他') {
abnormal_item.className = 'info'
}
// 创建呼叫时间列
const originating_time = document.createElement('div')
originating_time.innerText = curData?.originating_time ? curData.originating_time : '/'
// 创建处理时间列
const arrive_time = document.createElement('div')
arrive_time.innerText = curData?.arrive_time ? curData.arrive_time : '/'
// 将所有列添加到行容器中
newLine.appendChild(workshop_name)
newLine.appendChild(work_center_name)
newLine.appendChild(abnormal_item)
newLine.appendChild(originating_time)
newLine.appendChild(arrive_time)
// 将行添加到文档片段中
fragment.appendChild(newLine)
// 更新索引,如果到达末尾则重新从0开始
currentIndex = currentIndex === this.stationCallRecordList.length - 1 ? 0 : currentIndex + 1
}
// 将整个文档片段一次性添加到DOM中
ele.appendChild(fragment)
},
// 计算下一个片段开始显示的数据索引
countStartIndexFn(startIndex) {
// 数据不够填满下一个片段
if (startIndex + this.perCount >= this.stationCallRecordList.length) {
return this.perCount - (this.stationCallRecordList.length - startIndex)
}
// 数据足够填满下一个片段,直接返回当前索引加上每页显示数量
return this.perCount + startIndex
},
// 数字转换成时间格式方法
convertNumberToTime(num) {
// 提取小时部分,使用 Math.floor 函数向下取整得到整数小时
let hours = Math.floor(num)
// 提取分钟部分,先将小数部分乘以 60 得到分钟数
let minutes = Math.round((num - hours) * 60)
// 将小时和分钟转换为字符串,并确保它们是两位数的格式
// 如果小时数小于 10,在前面补 0
hours = hours.toString().padStart(2, '0')
// 如果分钟数小于 10,在前面补 0
minutes = minutes.toString().padStart(2, '0')
// 返回格式化后的时间字符串
return `${hours}:${minutes}`
},
// 初始化所有图标
initAllCharts() {
this.$nextTick(() => {
setTimeout(() => {
if (this.$refs.defectTrendRef) this.initDefectTrendChart()
if (this.$refs.weeklyWaitTimeRef) this.initWeeklyWaitTimeChart()
if (this.$refs.callTypePieRef) this.initCallTypePieChart()
if (this.$refs.deviceFailureRef) this.initDeviceFailureChart()
}, 1)
})
},
// 初始化今日产品缺陷走势图图表
initDefectTrendChart() {
const chart = echarts.init(this.$refs.defectTrendRef)
this.chartsProject.defectTrendProject = chart
const option = {
tooltip: { trigger: 'item' },
grid: { top: '45px', left: '3%', right: '4%', bottom: '3%', containLabel: true },
xAxis: {
type: 'category',
axisLine: { lineStyle: { color: '#7c808d' } },
axisLabel: { color: '#fff', fontSize: 11 },
data: this.defectTrendChartData.keys
},
yAxis: {
type: 'value',
axisLine: { show: true, lineStyle: { color: '#7c808d' } },
splitLine: { lineStyle: { color: '#7c808d', width: 0.5, opacity: 0.5 } },
axisLabel: { color: '#fff', fontSize: 11, formatter: '{value}' }
},
series: [{
data: this.defectTrendChartData.values,
type: 'line',
smooth: true
}]
}
chart.setOption(option)
},
// 初始化近一周生产中断等待时长统计图表
initWeeklyWaitTimeChart() {
const chart = echarts.init(this.$refs.weeklyWaitTimeRef)
this.chartsProject.weeklyWaitTimeProject = chart
const option = {
tooltip: { trigger: 'item' },
grid: { top: '45px', left: '3%', right: '4%', bottom: '3%', containLabel: true },
xAxis: {
type: 'category',
axisLine: { lineStyle: { color: '#7c808d' } },
axisLabel: { color: '#fff', fontSize: 11 },
data: this.weeklyWaitTimeChartData.keys
},
yAxis: {
type: 'value',
axisLine: { show: true, lineStyle: { color: '#7c808d' } },
splitLine: { lineStyle: { color: '#7c808d', width: 0.5, opacity: 0.5 } },
axisLabel: { color: '#fff', fontSize: 11, formatter: '{value}' }
},
series: [{
data: this.weeklyWaitTimeChartData.values,
type: 'bar'
}]
}
chart.setOption(option)
},
// 初始化近一周安灯呼叫类型统计图表
initCallTypePieChart() {
const chart = echarts.init(this.$refs.callTypePieRef)
this.chartsProject.callTypePieProject = chart
const option = {
tooltip: { trigger: 'item' },
series: [{
type: 'pie',
radius: '70%',
data: this.callTypePieChartData
}]
}
chart.setOption(option)
},
// 初始化近一周设备故障频率图表
initDeviceFailureChart() {
const chart = echarts.init(this.$refs.deviceFailureRef)
this.chartsProject.deviceFailureProject = chart
const option = {
tooltip: { trigger: 'item' },
grid: { top: '45px', left: '3%', right: '4%', bottom: '3%', containLabel: true },
xAxis: {
type: 'category',
axisLine: { lineStyle: { color: '#7c808d' } },
axisLabel: { color: '#fff', fontSize: 11 },
data: this.deviceFailureChartData.keys
},
yAxis: {
type: 'value',
axisLine: { show: true, lineStyle: { color: '#7c808d' } },
splitLine: { lineStyle: { color: '#7c808d', width: 0.5, opacity: 0.5 } },
axisLabel: { color: '#fff', fontSize: 11, formatter: '{value}' }
},
series: [{
data: this.deviceFailureChartData.values,
type: 'bar'
}]
}
chart.setOption(option)
},
// 图标调整大小方法
chartsResizeFn() {
Object.values(this.chartsProject).forEach(chart => {
chart && chart.resize()
})
},
// 下拉框改变事件
async selChangeHandle(el, type) {
this.loading = true
if (type == '今日产品缺陷走势图') {
await this.getProductDefectChartListApi().then(result => {
if (this.$refs.defectTrendRef) this.initDefectTrendChart()
}).catch(error => {
this.$message.error('获取今日产品缺陷走势图数据失败')
})
} else if (type == '近一周生产中断等待时长统计' || type == '近一周设备故障频频率') {
await this.getChartDataApi().then(result => {
if (type == '近一周生产中断等待时长统计') {
if (this.$refs.weeklyWaitTimeRef) this.initWeeklyWaitTimeChart()
} else if (type == '近一周设备故障频频率') {
if (this.$refs.deviceFailureRef) this.initDeviceFailureChart()
}
}).catch(error => {
this.$message.error(`获取${type}数据失败`)
})
}
this.loading = false
},
beforeDestroy() {
window.removeEventListener("resize", this.resizeHandle)
// 销毁所有图表实例
Object.values(this.chartsProject).forEach(chart => {
chart && chart.dispose()
})
if (this.animationFrameId) {
window.cancelAnimationFrame(this.animationFrameId)
}
}
}
})
</script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-thumb {
background: #c0c4cc;
border-radius: 3px;
}
::-webkit-scrollbar-track {
background: #f5f7fa;
}
#app {
display: flex;
align-content: space-between;
flex-wrap: wrap;
width: 100vw;
height: 100vh;
padding: 8px;
background-color: #06114f;
gap: 8px;
.panelCardBox {
width: calc((100% - 16px) / 3);
height: calc(50% - 4px);
border-radius: 4px;
padding: 8px;
border: 1px solid #7c808d;
.panelHeaderBox {
font-size: 14px;
font-weight: bold;
color: #FFF;
display: flex;
align-items: center;
justify-content: space-between;
min-height: 28px;
position: relative;
.text {
font-weight: 400;
border-bottom: 1px solid #303133;
}
.unitBox {
position: absolute;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
z-index: 2;
bottom: -28px;
font-size: 12px;
color: #ccc;
font-weight: 400;
}
}
.panelTableBox {
height: calc(100% - 28px);
width: 100%;
overflow-y: hidden;
position: relative;
border: 1px solid #7c808d;
font-size: 13px;
border-radius: 2px;
.panelTableHeader {
border-bottom: 1px solid #7c808d;
position: relative;
z-index: 2;
.roll_line {
font-weight: bold;
color: #FFF;
border-bottom: none;
}
.roll_line:hover {
background-color: transparent !important;
cursor: default !important;
}
}
.vabsolute_content {
width: 100%;
position: absolute;
top: 40px;
transform: translateY(0);
will-change: transform;
height: 100%;
}
.roll_line {
display: flex;
padding: 0 8px;
box-sizing: border-box;
width: 100%;
height: 40px;
color: #FFF;
align-items: center;
border-bottom: 1px solid #7c808d;
transition: background-color 0.25s ease;
gap: 8px;
}
.roll_line:hover {
background-color: rgba(255, 255, 255, 0.2);
cursor: pointer;
}
.roll_line>div {
flex: 1;
}
.roll_line>div:nth-child(4),
.roll_line>div:nth-child(5) {
min-width: 65px;
max-width: 65px;
}
}
.handleStatuBox {
display: flex;
align-content: flex-start;
flex-wrap: wrap;
gap: 8px;
margin-top: 8px;
min-height: calc(100% - 28px - 8px);
overflow-y: auto;
.statuItemBox {
width: calc((100% - 24px) / 3);
display: flex;
flex-direction: column;
align-items: center;
gap: 3px;
.number {
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
}
.label {
font-size: 12px;
color: #FFF;
}
}
}
.emptyBox {
width: 100%;
height: calc(100% - 28px);
display: flex;
align-items: center;
justify-content: center;
.el-empty {
padding: 0
}
}
}
}
</style>
</html>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment