- 实现TV1和TV2双大屏展示 - 集成ECharts数据可视化 - 实现实时数据刷新机制 - 添加刀具寿命管理模块 - 添加工单加工时长统计模块 - 添加加工中心7日工作时长统计模块 - 添加OEE分析、设备状态监控等功能 - 创建技术文档和使用说明书
2249 lines
65 KiB
Vue
2249 lines
65 KiB
Vue
<!-- eslint-disable vue/max-attributes-per-line -->
|
||
<template>
|
||
<div id="screen" class="allShow" :style="{
|
||
width: `${style.width}px`,
|
||
height: `${style.height}px`,
|
||
transform: `${style.transform}`,
|
||
}">
|
||
<button class="route-switch-btn" @click="switchToTV1">切换TV1</button>
|
||
|
||
<div class="title-png">
|
||
<span class="title-text">大陆架零部件智能生产线数字孪生系统</span>
|
||
<div class="df-header__right">
|
||
<div class="df-header__timebar">
|
||
<span class="df-header__year">{{ headerDateCn }}</span>
|
||
<span class="df-header__time">{{ headerTime }}</span>
|
||
</div>
|
||
</div>
|
||
<!-- <img style="
|
||
width: 200px;
|
||
height: 30px;
|
||
position: absolute;
|
||
left: 55px;
|
||
top: 20px;
|
||
" src="/png/logo.png" alt="东方理工大学logo" /> -->
|
||
</div>
|
||
<div class="leftLine"></div>
|
||
<div class="rightLine"></div>
|
||
<!-- <button class="button1" @click="togglePanelLayout" :style="buttonStyle"></button> -->
|
||
<div class="main-content">
|
||
<iframe
|
||
id="dmtIframe"
|
||
:src="iframeAddress"
|
||
style="
|
||
width: 100%;
|
||
height: 100%;
|
||
border: none;
|
||
filter: blur(10);
|
||
z-index: 100;
|
||
" />
|
||
<div v-show="!isPanelCollapsed" class="left-box">
|
||
<div class="realtimeTask-box">
|
||
<div class="left-title-header">
|
||
<span class="left-title-text">生产任务</span>
|
||
</div>
|
||
<div class="realtime-task-grid">
|
||
<div class="realtime-task-item">
|
||
<span class="realtime-task-label">订单号</span>
|
||
<span class="realtime-task-value">{{ realtimeTaskInfo.orderNo }}</span>
|
||
</div>
|
||
<div class="realtime-task-item">
|
||
<span class="realtime-task-label">物料号</span>
|
||
<span class="realtime-task-value">{{ realtimeTaskInfo.materialNo }}</span>
|
||
</div>
|
||
<div class="realtime-task-item">
|
||
<span class="realtime-task-label">数量</span>
|
||
<span class="realtime-task-value">{{ realtimeTaskInfo.quantity }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- 工单加工时长统计模块 -->
|
||
<div class="component-maintenance-box">
|
||
<div class="left-title-header">
|
||
<span class="left-title-text">工单加工时长统计</span>
|
||
</div>
|
||
<div class="component-maintenance-body">
|
||
<div id="workOrderChart" class="component-maintenance-chart" />
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 设备状态模块 -->
|
||
<div class="device-status-box">
|
||
<div class="left-title-header">
|
||
<span class="left-title-text">设备状态</span>
|
||
</div>
|
||
<div class="device-status-body">
|
||
<div class="device-status-table">
|
||
<div class="device-status-row device-status-row--header">
|
||
<div class="device-status-cell">工位</div>
|
||
<div class="device-status-cell">工位名称</div>
|
||
<div class="device-status-cell">手自动</div>
|
||
<div class="device-status-cell">状态</div>
|
||
</div>
|
||
<div v-for="(row, index) in deviceStatusList" :key="index" class="device-status-row">
|
||
<div class="device-status-cell">{{ row.station }}</div>
|
||
<div class="device-status-cell">{{ row.stationName }}</div>
|
||
<div class="device-status-cell">{{ row.mode }}</div>
|
||
<div class="device-status-cell">
|
||
<span class="device-status-pill" :class="`device-status-pill--${row.statusType}`">
|
||
{{ row.statusText }}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div v-show="!isPanelCollapsed" class="right-box">
|
||
<!-- 加工中心7日工作时长统计模块 -->
|
||
<div class="daily-output-box">
|
||
<div class="right-title-header">
|
||
<span class="right-title-text">加工中心7日工作时长统计</span>
|
||
</div>
|
||
<div class="daily-output-body">
|
||
<div id="machiningCenterChart" class="daily-output-chart" />
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 实时状态模块 -->
|
||
<div class="storage-status-box">
|
||
<div class="right-title-header">
|
||
<span class="right-title-text">实时状态</span>
|
||
</div>
|
||
<div class="storage-status-body now-status-body">
|
||
<div class="now-status-fixed-row">
|
||
<div class="now-status-fixed-label">设备状态</div>
|
||
<div class="now-status-indicators">
|
||
<div v-for="item in nowStatusIndicators" :key="item.label" class="now-status-indicator-item">
|
||
<span class="now-status-circle" :class="{ 'now-status-circle--active': item.active }" :style="{
|
||
backgroundColor: item.active ? getNowStatusCircleColor(item.label) : '#909399'
|
||
}"></span>
|
||
<span class="now-status-indicator-label">{{ item.label }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="now-status-scroll-container">
|
||
<scroll
|
||
class="now-status-scroll-wrapper"
|
||
:data="nowStatusList"
|
||
:class-option="nowStatusScrollOption"
|
||
>
|
||
<div class="now-status-scroll-content">
|
||
<div v-for="item in nowStatusList" :key="item.key" class="now-status-row">
|
||
<div class="now-status-row-label">{{ item.label }}</div>
|
||
<div class="now-status-row-value">{{ item.value }}</div>
|
||
</div>
|
||
</div>
|
||
</scroll>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 设备运行履历模块 -->
|
||
<div v-show="!isPanelCollapsed" class="status-timeline-box">
|
||
<div class="status-timeline-header"></div>
|
||
<div class="bb-chart-container bb-chart-container--timeline">
|
||
<div id="dashboardDeviceStatus" class="bb-chart bb-chart--status" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import * as echarts from 'echarts'
|
||
import 'echarts-gl'
|
||
import scroll from 'vue-seamless-scroll'
|
||
export default {
|
||
name: 'DaLuJiaTV2',
|
||
components: { scroll },
|
||
data() {
|
||
return {
|
||
style: {
|
||
width: '1920',
|
||
height: '1080',
|
||
transform: 'scaleY(1) scaleX(1) translate(-50%, -50%)'
|
||
},
|
||
isPanelCollapsed: false,
|
||
workstationStatusLoading: false,
|
||
workstationStatusTimer: null,
|
||
equipmentRuntimeHistoryLoading: false,
|
||
equipmentRuntimeHistoryRefreshTimer: null,
|
||
nowStatusLoading: false,
|
||
nowStatusRefreshTimer: null,
|
||
nowStatusIndicators: [
|
||
{ label: '关机', active: false },
|
||
{ label: '待机', active: false },
|
||
{ label: '运行', active: false },
|
||
{ label: '报警', active: false }
|
||
],
|
||
nowStatusList: [
|
||
{ key: 'status', label: 'status', value: '待机' }
|
||
],
|
||
deviceStatusList: [
|
||
{
|
||
station: 'OP1010',
|
||
stationName: '3D打印机',
|
||
mode: '自动',
|
||
statusText: '运行',
|
||
statusType: 'running'
|
||
},
|
||
{
|
||
station: 'OP1010',
|
||
stationName: '加工单元',
|
||
mode: '自动',
|
||
statusText: '报警',
|
||
statusType: 'alarm'
|
||
},
|
||
{
|
||
station: 'OP1010',
|
||
stationName: '检测',
|
||
mode: '自动',
|
||
statusText: '待机',
|
||
statusType: 'standby'
|
||
},
|
||
{
|
||
station: 'OP1010',
|
||
stationName: '装配',
|
||
mode: '自动',
|
||
statusText: '运行',
|
||
statusType: 'running'
|
||
},
|
||
{
|
||
station: 'OP1010',
|
||
stationName: '打标',
|
||
mode: '自动',
|
||
statusText: '离线',
|
||
statusType: 'offline'
|
||
}
|
||
],
|
||
deviceStatusChart: null, // 设备运行状态图表
|
||
workOrderChart: null, // 工单加工时长统计图表
|
||
machiningCenterChart: null, // 加工中心7日工作时长统计图表
|
||
workOrderLoading: false,
|
||
workOrderRefreshTimer: null,
|
||
realtimeTaskLoading: false,
|
||
realtimeTaskRefreshTimer: null,
|
||
realtimeTaskInfo: {
|
||
orderNo: '--',
|
||
materialNo: '--',
|
||
quantity: '--'
|
||
},
|
||
// 加工中心7日工作时长数据
|
||
machiningCenterDates: [],
|
||
machiningCenterValues: [],
|
||
machiningCenterLoading: false,
|
||
machiningCenterRefreshTimer: null,
|
||
// 工单加工时长数据
|
||
workOrderNames: [],
|
||
workOrderHours: [],
|
||
date: new Date(),
|
||
iframe: '',
|
||
iframeAddress: '',
|
||
handleWindowResize: null,
|
||
lastWorkstationStatusSignature: ''
|
||
}
|
||
},
|
||
created() {
|
||
this.iframe = document.getElementById('dmtIframe')
|
||
this.iframeAddress = window.dt_Config?.iframeAddress
|
||
this.loadEquipmentRuntimeHistory() // 历史_工位设备状态每分钟记录_查询
|
||
this.loadNowStatusData() // 当前_设备数据_查询
|
||
this.loadWorkstationStatus() // 基础_工位_查询
|
||
this.setScale()
|
||
},
|
||
mounted() {
|
||
this.timer = setInterval(() => {
|
||
this.date = new Date()
|
||
}, 1000)
|
||
this.initEcharts()
|
||
this.startEquipmentRuntimeHistoryRefresh() // 每分钟刷新一次设备运行履历
|
||
this.startWorkOrderRefresh() // 每分钟刷新一次工单加工时长统计
|
||
this.startMachiningCenterRefresh() // 每分钟刷新一次加工中心7日工作时长统计
|
||
this.startWorkstationStatusRefresh() // 每2秒刷新一次设备状态(调用存储过程:基础_工位_查询)
|
||
this.startNowStatusRefresh() // 每2秒刷新一次实时状态(调用存储过程:当前_设备数据_查询)
|
||
this.startRealtimeTaskRefresh() // 每2秒刷新一次生产任务实时信息
|
||
this.setScale()
|
||
this.handleWindowResize = () => {
|
||
this.setScale()
|
||
if (this.machiningCenterChart) this.machiningCenterChart.resize()
|
||
if (this.workOrderChart) this.workOrderChart.resize()
|
||
if (this.deviceStatusChart) this.deviceStatusChart.resize()
|
||
}
|
||
window.addEventListener('resize', this.handleWindowResize)
|
||
},
|
||
beforeDestroy() {
|
||
if (this.timer) {
|
||
clearInterval(this.timer)
|
||
this.timer = null
|
||
}
|
||
if (this.machiningCenterRefreshTimer) {
|
||
clearInterval(this.machiningCenterRefreshTimer)
|
||
this.machiningCenterRefreshTimer = null
|
||
}
|
||
if (this.workstationStatusTimer) {
|
||
clearInterval(this.workstationStatusTimer)
|
||
this.workstationStatusTimer = null
|
||
}
|
||
if (this.nowStatusRefreshTimer) {
|
||
clearInterval(this.nowStatusRefreshTimer)
|
||
this.nowStatusRefreshTimer = null
|
||
}
|
||
if (this.equipmentRuntimeHistoryRefreshTimer) {
|
||
clearInterval(this.equipmentRuntimeHistoryRefreshTimer)
|
||
this.equipmentRuntimeHistoryRefreshTimer = null
|
||
}
|
||
if (this.workOrderRefreshTimer) {
|
||
clearInterval(this.workOrderRefreshTimer)
|
||
this.workOrderRefreshTimer = null
|
||
}
|
||
if (this.realtimeTaskRefreshTimer) {
|
||
clearInterval(this.realtimeTaskRefreshTimer)
|
||
this.realtimeTaskRefreshTimer = null
|
||
}
|
||
if (this.handleWindowResize) {
|
||
window.removeEventListener('resize', this.handleWindowResize)
|
||
this.handleWindowResize = null
|
||
}
|
||
if (this.deviceStatusChart) {
|
||
this.deviceStatusChart.dispose()
|
||
this.deviceStatusChart = null
|
||
}
|
||
if (this.workOrderChart) {
|
||
this.workOrderChart.dispose()
|
||
this.workOrderChart = null
|
||
}
|
||
if (this.machiningCenterChart) {
|
||
this.machiningCenterChart.dispose()
|
||
this.machiningCenterChart = null
|
||
}
|
||
},
|
||
computed: {
|
||
nowStatusScrollOption() {
|
||
return {
|
||
step: 0.5,
|
||
limitMoveNum: 6,
|
||
hoverStop: true,
|
||
direction: 1,
|
||
openTouch: false,
|
||
singleHeight: 40,
|
||
waitTime: 1000,
|
||
autoPlay: this.nowStatusList.length > 6
|
||
}
|
||
},
|
||
headerTime() {
|
||
const d = this.date instanceof Date ? this.date : new Date()
|
||
const hh = String(d.getHours()).padStart(2, '0')
|
||
const mm = String(d.getMinutes()).padStart(2, '0')
|
||
const ss = String(d.getSeconds()).padStart(2, '0')
|
||
return `${hh}:${mm}:${ss}`
|
||
},
|
||
headerDate() {
|
||
const d = this.date instanceof Date ? this.date : new Date()
|
||
const y = d.getFullYear()
|
||
const m = String(d.getMonth() + 1).padStart(2, '0')
|
||
const day = String(d.getDate()).padStart(2, '0')
|
||
return `${y}/${m}/${day}`
|
||
},
|
||
headerDateCn() {
|
||
const d = this.date instanceof Date ? this.date : new Date()
|
||
const y = d.getFullYear()
|
||
const m = d.getMonth() + 1
|
||
const day = d.getDate()
|
||
return `${y}-${m}-${day}`
|
||
}
|
||
},
|
||
methods: {
|
||
switchToTV1() {
|
||
if (this.$route.path !== '/') {
|
||
this.$router.push('/')
|
||
}
|
||
},
|
||
getScale() {
|
||
const w = window.innerWidth / this.style.width
|
||
const h = window.innerHeight / this.style.height
|
||
return { x: w, y: h }
|
||
},
|
||
// 实时状态模块:数据库定时刷新
|
||
startNowStatusRefresh() {
|
||
if (this.nowStatusRefreshTimer) {
|
||
clearInterval(this.nowStatusRefreshTimer)
|
||
this.nowStatusRefreshTimer = null
|
||
}
|
||
this.nowStatusRefreshTimer = setInterval(() => {
|
||
this.loadNowStatusData()
|
||
}, 2000)
|
||
this.loadNowStatusData()
|
||
},
|
||
getNowStatusCircleColor(label) {
|
||
const colorMap = {
|
||
关机: 'grey',
|
||
待机: 'orange',
|
||
运行: 'green',
|
||
报警: 'red'
|
||
}
|
||
return colorMap[label] || 'grey'
|
||
},
|
||
updateNowStatusIndicator(statusCode) {
|
||
this.nowStatusIndicators.forEach((item) => {
|
||
item.active = false
|
||
})
|
||
const statusMap = {
|
||
'-1': 0,
|
||
0: 1,
|
||
1: 2,
|
||
2: 3
|
||
}
|
||
const statusIndex = statusMap[String(statusCode).trim()]
|
||
if (statusIndex !== undefined && this.nowStatusIndicators[statusIndex]) {
|
||
this.nowStatusIndicators[statusIndex].active = true
|
||
}
|
||
},
|
||
mapNowStatusText(statusCode) {
|
||
const textMap = {
|
||
'-1': '关机',
|
||
0: '待机',
|
||
1: '运行',
|
||
2: '报警'
|
||
}
|
||
return textMap[String(statusCode).trim()] || String(statusCode || '--').trim()
|
||
},
|
||
applyNowStatusData(tagList) {
|
||
const list = Array.isArray(tagList) ? tagList : []
|
||
const statusRow = list.find((item) => {
|
||
const tagName = item && (item.TagName != null ? item.TagName : item['TagName'])
|
||
return String(tagName || '').trim() === '设备状态'
|
||
})
|
||
const statusValue = statusRow && (statusRow.TagValue != null ? statusRow.TagValue : statusRow['TagValue'])
|
||
this.updateNowStatusIndicator(statusValue)
|
||
this.nowStatusList = list
|
||
.filter((item) => {
|
||
const tagName = item && (item.TagName != null ? item.TagName : item['TagName'])
|
||
return String(tagName || '').trim() !== '设备状态'
|
||
})
|
||
.map((item, index) => {
|
||
const tagName = item && (item.TagName != null ? item.TagName : item['TagName'])
|
||
const rawValue = item && (item.TagValue != null ? item.TagValue : item['TagValue'])
|
||
return {
|
||
key: `${String(tagName || 'tag')}-${index}`,
|
||
label: String(tagName || '--'),
|
||
value: String(rawValue != null ? rawValue : '--')
|
||
}
|
||
})
|
||
},
|
||
loadNowStatusData() {
|
||
if (this.nowStatusLoading) return
|
||
this.nowStatusLoading = true
|
||
const param = []
|
||
const data = this.CreateData('11', '当前_设备数据_查询', param)
|
||
this.ExecDatabase(data)
|
||
.then((response) => {
|
||
const list = response && response.data ? response.data : null
|
||
if (Array.isArray(list)) {
|
||
this.applyNowStatusData(list)
|
||
} else {
|
||
this.nowStatusList = []
|
||
}
|
||
})
|
||
.catch((err) => {
|
||
console.error('实时状态数据获取失败', err)
|
||
this.nowStatusList = []
|
||
})
|
||
.finally(() => {
|
||
this.nowStatusLoading = false
|
||
})
|
||
},
|
||
setScale() {
|
||
const scale = this.getScale()
|
||
this.style.transform =
|
||
'scaleY(' + scale.y + ') scaleX(' + scale.x + ') translate(-50%, -50%)'
|
||
},
|
||
startToolUseTimeRefresh() {
|
||
if (this.toolUseTimeRefreshTimer) {
|
||
clearInterval(this.toolUseTimeRefreshTimer)
|
||
this.toolUseTimeRefreshTimer = null
|
||
}
|
||
this.toolUseTimeRefreshTimer = setInterval(() => {
|
||
this.loadToolUseTimeData()
|
||
}, 60 * 1000)
|
||
},
|
||
startRealtimeTaskRefresh() {
|
||
if (this.realtimeTaskRefreshTimer) {
|
||
clearInterval(this.realtimeTaskRefreshTimer)
|
||
this.realtimeTaskRefreshTimer = null
|
||
}
|
||
this.realtimeTaskRefreshTimer = setInterval(() => {
|
||
this.loadRealtimeTaskData()
|
||
}, 2000)
|
||
this.loadRealtimeTaskData()
|
||
},
|
||
loadRealtimeTaskData() {
|
||
if (this.realtimeTaskLoading) return
|
||
this.realtimeTaskLoading = true
|
||
const param = []
|
||
const data = this.CreateData('11', '接口_生产任务_实时_查询', param)
|
||
this.ExecDatabase(data)
|
||
.then((response) => {
|
||
const list = response && response.data ? response.data : null
|
||
const row = Array.isArray(list) && list.length > 0 ? list[0] : null
|
||
this.realtimeTaskInfo = {
|
||
orderNo: row && row['计划号'] != null ? String(row['计划号']) : '--',
|
||
materialNo: row && row['物料号'] != null ? String(row['物料号']) : '--',
|
||
quantity: row && row['数量'] != null ? String(row['数量']) : '--'
|
||
}
|
||
})
|
||
.catch((err) => {
|
||
console.error('接口_生产任务_实时_查询失败', err)
|
||
this.realtimeTaskInfo = {
|
||
orderNo: '--',
|
||
materialNo: '--',
|
||
quantity: '--'
|
||
}
|
||
})
|
||
.finally(() => {
|
||
this.realtimeTaskLoading = false
|
||
})
|
||
},
|
||
togglePanelLayout() {
|
||
this.isPanelCollapsed = !this.isPanelCollapsed
|
||
},
|
||
initEcharts() {
|
||
// 设备运行履历甘特图
|
||
this.deviceStatusChart = echarts.init(
|
||
document.getElementById('dashboardDeviceStatus'),
|
||
)
|
||
// 工单加工时长统计柱状图
|
||
const workOrderEl = document.getElementById('workOrderChart')
|
||
if (workOrderEl) {
|
||
this.workOrderChart = echarts.init(workOrderEl)
|
||
this.loadWorkOrderData()
|
||
}
|
||
// 加工中心7日工作时长统计柱状图
|
||
const machiningCenterEl = document.getElementById('machiningCenterChart')
|
||
if (machiningCenterEl) {
|
||
this.machiningCenterChart = echarts.init(machiningCenterEl)
|
||
this.initMachiningCenterData()
|
||
this.drawMachiningCenterChart()
|
||
this.loadMachiningCenterHistory()
|
||
}
|
||
},
|
||
openModelInfoPopup({ left, top, ModelId, ModelName }) {
|
||
this.modelInfoPopup.visible = true
|
||
this.modelInfoPopup.loading = true
|
||
this.modelInfoPopup.error = ''
|
||
this.modelInfoPopup.base.ModelId = ModelId || ''
|
||
this.modelInfoPopup.base.ModelName = ModelName || ''
|
||
this.modelInfoPopup.detail = null
|
||
this.modelInfoPopup.left = Number(left) || 0
|
||
this.modelInfoPopup.top = Number(top) || 0
|
||
this.$nextTick(() => {
|
||
this.fixModelInfoPopupPosition()
|
||
})
|
||
this.fetchModelInfoPopupDetail(ModelId)
|
||
},
|
||
closeModelInfoPopup() {
|
||
this.modelInfoPopup.visible = false
|
||
this.modelInfoPopup.loading = false
|
||
this.modelInfoPopup.error = ''
|
||
this.modelInfoPopup.detail = null
|
||
},
|
||
fixModelInfoPopupPosition() {
|
||
const el = this.$refs.modelInfoPopupRef
|
||
if (!el) return
|
||
const rect = el.getBoundingClientRect()
|
||
const margin = 10
|
||
let left = this.modelInfoPopup.left
|
||
let top = this.modelInfoPopup.top
|
||
const maxLeft = window.innerWidth - rect.width - margin
|
||
const maxTop = window.innerHeight - rect.height - margin
|
||
left = Math.max(margin, Math.min(left, maxLeft))
|
||
top = Math.max(margin, Math.min(top, maxTop))
|
||
this.modelInfoPopup.left = left
|
||
this.modelInfoPopup.top = top
|
||
},
|
||
// 设备状态模块:每秒刷新一次(调用存储过程:基础_工位_查询)
|
||
startWorkstationStatusRefresh() {
|
||
if (this.workstationStatusTimer) {
|
||
clearInterval(this.workstationStatusTimer)
|
||
this.workstationStatusTimer = null
|
||
}
|
||
this.workstationStatusTimer = setInterval(() => {
|
||
this.loadWorkstationStatus()
|
||
}, 2000)
|
||
},
|
||
loadWorkstationStatus() {
|
||
if (this.workstationStatusLoading) return
|
||
this.workstationStatusLoading = true
|
||
|
||
const param = [] // 无入参
|
||
const data = this.CreateData('11', '基础_工位_查询', param)
|
||
this.ExecDatabase(data)
|
||
.then((response) => {
|
||
const list = response && response.data ? response.data : null
|
||
if (!Array.isArray(list)) return
|
||
|
||
const mapStatus = (code) => {
|
||
const c = Number(code)
|
||
if (c === -1) return { statusText: '离线', statusType: 'offline' }
|
||
if (c === 0) return { statusText: '待机', statusType: 'standby' }
|
||
if (c === 1) return { statusText: '运行', statusType: 'running' }
|
||
if (c === 2) return { statusText: '报警', statusType: 'alarm' }
|
||
return { statusText: '未知', statusType: 'offline' }
|
||
}
|
||
|
||
const nextDeviceStatusList = list.map((item) => {
|
||
const station =
|
||
item && (item['工位号'] != null ? String(item['工位号']) : '')
|
||
const stationName =
|
||
item &&
|
||
(item['工位名称'] != null ? String(item['工位名称']) : '')
|
||
const statusCode =
|
||
item && (item['状态代码'] != null ? item['状态代码'] : null)
|
||
const st = mapStatus(statusCode)
|
||
return {
|
||
station,
|
||
stationName,
|
||
mode: '自动',
|
||
statusText: st.statusText,
|
||
statusType: st.statusType
|
||
}
|
||
})
|
||
const signature = JSON.stringify(nextDeviceStatusList)
|
||
if (signature !== this.lastWorkstationStatusSignature) {
|
||
this.lastWorkstationStatusSignature = signature
|
||
this.deviceStatusList = nextDeviceStatusList
|
||
}
|
||
})
|
||
.catch((err) => {
|
||
console.error('基础_工位_查询失败', err)
|
||
})
|
||
.then(() => {
|
||
this.workstationStatusLoading = false
|
||
})
|
||
},
|
||
// 每分钟刷新一次日产量历史数据
|
||
startDailyOutputHistoryRefresh() {
|
||
if (this.dailyOutputRefreshTimer) {
|
||
clearInterval(this.dailyOutputRefreshTimer)
|
||
this.dailyOutputRefreshTimer = null
|
||
}
|
||
this.dailyOutputRefreshTimer = setInterval(() => {
|
||
this.loadDailyOutputHistory()
|
||
}, 60 * 1000)
|
||
},
|
||
// 初始化日产量数据(首次为空,由存储过程填充)
|
||
initDailyOutputData() {
|
||
this.dailyOutputDates = []
|
||
this.dailyOutputValues = []
|
||
},
|
||
// 拉取日产量历史数据(调用存储过程:历史_过站记录_日产量查询)
|
||
loadDailyOutputHistory() {
|
||
if (this.dailyOutputLoading) return
|
||
if (!this.dailyOutputChart) return
|
||
this.dailyOutputLoading = true
|
||
|
||
const param4 = [] // 无入参
|
||
const Data4 = this.CreateData('11', '历史_过站记录_日产量查询', param4)
|
||
this.ExecDatabase(Data4)
|
||
.then((response) => {
|
||
const list = response && response.data ? response.data : null
|
||
if (!Array.isArray(list) || list.length === 0) {
|
||
// 查询不到数据:清空数据,在图表中显示“暂无最近七日产量数据”
|
||
this.dailyOutputDates = []
|
||
this.dailyOutputValues = []
|
||
this.drawDailyOutputChart()
|
||
return
|
||
}
|
||
|
||
// 存储过程按“日期 + 工位号”分组,这里忽略工位号,按日期汇总近七日产量
|
||
const parseDate = (rawDate) => {
|
||
if (rawDate == null) return null
|
||
// 优先从字符串中直接截取 YYYY-MM-DD,避免时区造成的日期偏移
|
||
if (typeof rawDate === 'string') {
|
||
const m = rawDate.match(/^(\d{4})[-\/.](\d{1,2})[-\/.](\d{1,2})/)
|
||
if (m) {
|
||
const y = String(m[1]).padStart(4, '0')
|
||
const mo = String(m[2]).padStart(2, '0')
|
||
const da = String(m[3]).padStart(2, '0')
|
||
const key = `${y}-${mo}-${da}`
|
||
return {
|
||
key,
|
||
md: `${mo}-${da}`,
|
||
time: new Date(`${key}T00:00:00`).getTime()
|
||
}
|
||
}
|
||
}
|
||
const dt = rawDate instanceof Date ? rawDate : new Date(rawDate)
|
||
if (!dt || Number.isNaN(dt.getTime())) return null
|
||
const y = String(dt.getFullYear()).padStart(4, '0')
|
||
const mo = String(dt.getMonth() + 1).padStart(2, '0')
|
||
const da = String(dt.getDate()).padStart(2, '0')
|
||
const key = `${y}-${mo}-${da}`
|
||
return {
|
||
key,
|
||
md: `${mo}-${da}`,
|
||
time: new Date(`${key}T00:00:00`).getTime()
|
||
}
|
||
}
|
||
|
||
const sumByDate = new Map() // key: YYYY-MM-DD -> { md, time, sum }
|
||
list.forEach((item) => {
|
||
const rawDate =
|
||
item && (item['日期'] != null ? item['日期'] : item.日期)
|
||
const rawValue =
|
||
item && (item['产量'] != null ? item['产量'] : item.产量)
|
||
const d = parseDate(rawDate)
|
||
if (!d) return
|
||
const num = Number(rawValue)
|
||
if (!Number.isFinite(num)) return
|
||
const prev = sumByDate.get(d.key)
|
||
if (prev) {
|
||
prev.sum += num
|
||
} else {
|
||
sumByDate.set(d.key, { md: d.md, time: d.time, sum: num })
|
||
}
|
||
})
|
||
|
||
const sorted = Array.from(sumByDate.values())
|
||
.sort((a, b) => a.time - b.time)
|
||
.slice(-7)
|
||
const dates = sorted.map((d) => d.md)
|
||
const values = sorted.map((d) => Math.round(Number(d.sum) || 0))
|
||
|
||
this.dailyOutputDates = dates
|
||
this.dailyOutputValues = values
|
||
this.drawDailyOutputChart()
|
||
})
|
||
.catch((err) => {
|
||
console.error('日产量历史查询失败', err)
|
||
this.dailyOutputDates = []
|
||
this.dailyOutputValues = []
|
||
this.drawDailyOutputChart()
|
||
})
|
||
.then(() => {
|
||
this.dailyOutputLoading = false
|
||
})
|
||
},
|
||
// 绘制日产量折线图
|
||
drawDailyOutputChart() {
|
||
if (!this.dailyOutputChart) return
|
||
const data = this.dailyOutputValues || []
|
||
const dates = this.dailyOutputDates || []
|
||
|
||
// 先清空,避免上一次的图形或文字残留
|
||
this.dailyOutputChart.clear()
|
||
|
||
// 如果没有任何数据,就只显示“暂无最近七日产量数据”提示
|
||
if (!data.length || !dates.length) {
|
||
const optionEmpty = {
|
||
backgroundColor: 'transparent',
|
||
animation: false,
|
||
xAxis: { show: false, type: 'category', data: [] },
|
||
yAxis: { show: false, type: 'value' },
|
||
series: [],
|
||
graphic: {
|
||
type: 'text',
|
||
left: 'center',
|
||
top: 'middle',
|
||
style: {
|
||
text: '暂无最近七日产量数据',
|
||
fill: '#fff',
|
||
fontSize: 16,
|
||
fontFamily: 'Microsoft YaHei'
|
||
}
|
||
}
|
||
}
|
||
this.dailyOutputChart.setOption(optionEmpty)
|
||
return
|
||
}
|
||
|
||
const maxValue = Math.max.apply(null, data)
|
||
const maxIndex = data.indexOf(maxValue)
|
||
const lineColor = '#4fc3e8'
|
||
const pointColor = '#dff8ff'
|
||
const topAreaColor = 'rgba(79, 195, 232, 0.30)'
|
||
const option = {
|
||
backgroundColor: 'transparent',
|
||
animation: false,
|
||
grid: {
|
||
top: '18%',
|
||
bottom: '11%',
|
||
left: '8%',
|
||
right: '8%'
|
||
},
|
||
tooltip: {
|
||
trigger: 'axis',
|
||
axisPointer: {
|
||
type: 'line',
|
||
lineStyle: {
|
||
color: 'rgba(79, 195, 232, 0.45)'
|
||
}
|
||
},
|
||
formatter: (params) => {
|
||
const p = params && params[0]
|
||
if (!p) return ''
|
||
return `${p.axisValue}<br/>日产量:${p.value}`
|
||
}
|
||
},
|
||
xAxis: {
|
||
type: 'category',
|
||
boundaryGap: false,
|
||
data: dates,
|
||
axisLabel: {
|
||
color: '#fff',
|
||
fontSize: 10
|
||
},
|
||
axisLine: {
|
||
show: true,
|
||
lineStyle: { color: '#1B3F66' }
|
||
},
|
||
axisTick: { show: false }
|
||
},
|
||
yAxis: {
|
||
type: 'value',
|
||
axisLabel: {
|
||
color: '#fff',
|
||
fontSize: 10
|
||
},
|
||
axisLine: {
|
||
show: true,
|
||
lineStyle: { color: '#1B3F66' }
|
||
},
|
||
splitLine: {
|
||
lineStyle: { color: 'rgba(27, 63, 102, 0.6)' }
|
||
}
|
||
},
|
||
graphic: [],
|
||
series: [
|
||
{
|
||
type: 'line',
|
||
name: '日产量',
|
||
smooth: false,
|
||
showSymbol: true,
|
||
symbol: 'circle',
|
||
symbolSize: 5,
|
||
data: data,
|
||
lineStyle: {
|
||
color: lineColor,
|
||
width: 1
|
||
},
|
||
itemStyle: {
|
||
color: pointColor,
|
||
borderColor: lineColor,
|
||
borderWidth: 2
|
||
},
|
||
areaStyle: {
|
||
color: {
|
||
type: 'linear',
|
||
x: 0,
|
||
y: 0,
|
||
x2: 0,
|
||
y2: 1,
|
||
colorStops: [
|
||
{ offset: 0, color: topAreaColor },
|
||
{ offset: 1, color: 'rgba(79, 195, 232, 0)' }
|
||
]
|
||
}
|
||
},
|
||
markPoint: {
|
||
symbol: 'circle',
|
||
symbolSize: 5,
|
||
itemStyle: {
|
||
color: pointColor,
|
||
borderColor: lineColor,
|
||
borderWidth: 2
|
||
},
|
||
label: {
|
||
show: true,
|
||
position: 'top',
|
||
distance: 10,
|
||
color: '#dff8ff',
|
||
fontSize: 12,
|
||
fontWeight: 'bold',
|
||
formatter: 'Max:{c}'
|
||
},
|
||
data: [
|
||
{
|
||
coord: [dates[maxIndex], maxValue],
|
||
value: maxValue
|
||
}
|
||
]
|
||
},
|
||
emphasis: {
|
||
focus: 'series'
|
||
}
|
||
}
|
||
]
|
||
}
|
||
this.dailyOutputChart.setOption(option)
|
||
},
|
||
|
||
getMaintenanceAxisMax(values) {
|
||
const maxVal = Math.max(...values, 1)
|
||
const roughStep = maxVal / 5
|
||
const pow10 = Math.pow(10, Math.floor(Math.log10(roughStep)))
|
||
const normalized = roughStep / pow10
|
||
let step
|
||
if (normalized <= 1) step = 1 * pow10
|
||
else if (normalized <= 2) step = 2 * pow10
|
||
else if (normalized <= 5) step = 5 * pow10
|
||
else step = 10 * pow10
|
||
return Math.ceil(maxVal / step) * step
|
||
},
|
||
// 每分钟刷新一次设备运行履历(调用存储过程:历史_工位设备状态每分钟记录_查询)
|
||
startEquipmentRuntimeHistoryRefresh() {
|
||
if (this.equipmentRuntimeHistoryRefreshTimer) {
|
||
clearInterval(this.equipmentRuntimeHistoryRefreshTimer)
|
||
this.equipmentRuntimeHistoryRefreshTimer = null
|
||
}
|
||
this.equipmentRuntimeHistoryRefreshTimer = setInterval(() => {
|
||
this.loadEquipmentRuntimeHistory()
|
||
}, 60 * 1000)
|
||
},
|
||
// 查询设备运行履历(调用存储过程:历史_工位设备状态每分钟记录_查询,过去24小时)
|
||
loadEquipmentRuntimeHistory() {
|
||
if (this.equipmentRuntimeHistoryLoading) return
|
||
this.equipmentRuntimeHistoryLoading = true
|
||
const endTime = new Date()
|
||
const startTime = new Date(endTime.getTime() - 24 * 60 * 60 * 1000) // 近24小时
|
||
const fmt = (d) => this.formatDateTime(d)
|
||
const param4 = [
|
||
['开始时间', fmt(startTime)],
|
||
['结束时间', fmt(endTime)],
|
||
['记录条数', '2000']
|
||
]
|
||
const Data4 = this.CreateData(
|
||
'11',
|
||
'历史_工位设备状态每分钟记录_查询',
|
||
param4,
|
||
)
|
||
this.ExecDatabase(Data4)
|
||
.then((response) => {
|
||
const list = response.data
|
||
if (Array.isArray(list) && list.length > 0) {
|
||
this.processEquipmentRuntimeData(list)
|
||
} else {
|
||
this.deviceStatusTable = []
|
||
}
|
||
this.drawDeviceStatusChart()
|
||
})
|
||
.catch((err) => {
|
||
console.error('设备运行履历数据获取失败', err)
|
||
this.deviceStatusTable = []
|
||
this.drawDeviceStatusChart()
|
||
})
|
||
.finally(() => {
|
||
this.equipmentRuntimeHistoryLoading = false
|
||
})
|
||
},
|
||
// 处理设备运行履历数据
|
||
processEquipmentRuntimeData(records) {
|
||
this.deviceStatusTable = records.map((record) => ({
|
||
工位号: record.工位号 || '设备1',
|
||
设备状态: record.设备状态,
|
||
创建时间: record.创建时间
|
||
}))
|
||
},
|
||
// 格式化日期时间
|
||
formatDateTime(date) {
|
||
const year = date.getFullYear()
|
||
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
||
const day = date.getDate().toString().padStart(2, '0')
|
||
const hours = date.getHours().toString().padStart(2, '0')
|
||
const minutes = date.getMinutes().toString().padStart(2, '0')
|
||
const seconds = date.getSeconds().toString().padStart(2, '0')
|
||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
||
},
|
||
drawDeviceStatusChart() {
|
||
if (!this.deviceStatusChart) {
|
||
return
|
||
}
|
||
const hasData =
|
||
Array.isArray(this.deviceStatusTable) &&
|
||
this.deviceStatusTable.length > 0
|
||
if (!hasData) {
|
||
this.deviceStatusChart.clear()
|
||
this.deviceStatusChart.setOption(
|
||
{
|
||
backgroundColor: 'transparent',
|
||
xAxis: { show: false, type: 'time' },
|
||
yAxis: { show: false, type: 'category', data: [] },
|
||
series: [],
|
||
graphic: {
|
||
type: 'text',
|
||
left: 'center',
|
||
top: 'middle',
|
||
style: {
|
||
text: '未查询到设备运行履历',
|
||
fill: '#fff',
|
||
fontSize: 16,
|
||
fontFamily: 'Microsoft YaHei'
|
||
}
|
||
}
|
||
},
|
||
true,
|
||
)
|
||
return
|
||
}
|
||
// 设备状态映射
|
||
const statusMap = {
|
||
'-1': {
|
||
name: '离线',
|
||
color: '#878787'
|
||
},
|
||
0: {
|
||
name: '待机',
|
||
color: '#d49436'
|
||
},
|
||
1: {
|
||
name: '运行',
|
||
color: '#2d8f47'
|
||
},
|
||
2: {
|
||
name: '报警',
|
||
color: '#c23b48'
|
||
}
|
||
}
|
||
// 处理数据,按工位号分组并按时间排序
|
||
const groupedData = {}
|
||
this.deviceStatusTable.forEach((item) => {
|
||
const workstation = item.工位号 || 'OP10'
|
||
if (!groupedData[workstation]) {
|
||
groupedData[workstation] = []
|
||
}
|
||
groupedData[workstation].push(item)
|
||
})
|
||
// 对每组数据按时间排序
|
||
Object.keys(groupedData).forEach((workstation) => {
|
||
groupedData[workstation].sort(
|
||
(a, b) =>
|
||
new Date(a.创建时间).getTime() - new Date(b.创建时间).getTime(),
|
||
)
|
||
})
|
||
// 构建图表数据 - 为每个状态创建一个系列
|
||
const seriesData = []
|
||
const yAxisData = Object.keys(groupedData)
|
||
// 为每个工位和状态创建数据
|
||
yAxisData.forEach((workstation, yIndex) => {
|
||
const records = groupedData[workstation]
|
||
records.forEach((record, index) => {
|
||
const statusInfo = statusMap[String(record.设备状态)] || {
|
||
name: '未知',
|
||
color: '#909399'
|
||
}
|
||
// 计算结束时间
|
||
let endTime
|
||
if (index === records.length - 1) {
|
||
const startTime = new Date(record.创建时间)
|
||
endTime = new Date(startTime.getTime() + 60000) // 默认1分钟
|
||
} else {
|
||
endTime = new Date(records[index + 1].创建时间)
|
||
}
|
||
const startTime = new Date(record.创建时间)
|
||
// 确保开始时间小于结束时间
|
||
if (startTime.getTime() < endTime.getTime()) {
|
||
seriesData.push({
|
||
name: statusInfo.name,
|
||
value: [
|
||
workstation, // y轴类别
|
||
startTime.getTime(), // 开始时间
|
||
endTime.getTime(), // 结束时间
|
||
statusInfo.name // 状态名称
|
||
],
|
||
itemStyle: {
|
||
color: statusInfo.color,
|
||
shadowBlur: 8,
|
||
shadowColor: statusInfo.shadowColor || 'rgba(0, 0, 0, 0.25)',
|
||
shadowOffsetX: 0,
|
||
shadowOffsetY: 0
|
||
}
|
||
})
|
||
}
|
||
})
|
||
})
|
||
// 创建配置选项
|
||
const option = {
|
||
backgroundColor: 'transparent',
|
||
animation: false,
|
||
tooltip: {
|
||
trigger: 'item',
|
||
formatter: function(params) {
|
||
const data = params.value
|
||
const startTime = new Date(data[1])
|
||
const endTime = new Date(data[2])
|
||
const startStr = `${startTime
|
||
.getHours()
|
||
.toString()
|
||
.padStart(2, '0')}:${startTime
|
||
.getMinutes()
|
||
.toString()
|
||
.padStart(2, '0')}`
|
||
const endStr = `${endTime
|
||
.getHours()
|
||
.toString()
|
||
.padStart(2, '0')}:${endTime
|
||
.getMinutes()
|
||
.toString()
|
||
.padStart(2, '0')}`
|
||
return `${data[3]}<br/>${data[0]}<br/>${startStr} ~ ${endStr}`
|
||
},
|
||
backgroundColor: 'rgba(0, 0, 0, 0.8)',
|
||
textStyle: { color: '#fff' },
|
||
borderColor: 'rgba(255, 255, 255, 0.3)',
|
||
borderWidth: 1,
|
||
confine: true,
|
||
position: function(point, params, dom, rect, size) {
|
||
const mouseX = point[0]
|
||
const mouseY = point[1]
|
||
const tooltipWidth = size.contentSize[0]
|
||
const tooltipHeight = size.contentSize[1]
|
||
const chartWidth = size.viewSize[0]
|
||
const chartHeight = size.viewSize[1]
|
||
// 默认显示在鼠标上方
|
||
let x = mouseX - tooltipWidth / 2
|
||
let y = mouseY - tooltipHeight - 10
|
||
// 水平边界检查
|
||
x = Math.max(5, Math.min(x, chartWidth - tooltipWidth - 5))
|
||
// 垂直边界检查 - 确保不超出顶部
|
||
if (y < 0) {
|
||
// 上方放不下,放到鼠标下方
|
||
y = mouseY + 20 // 增加下方偏移量,避免紧贴鼠标
|
||
}
|
||
// 检查是否超出底部
|
||
if (y + tooltipHeight > chartHeight) {
|
||
// 超出底部,尝试放回上方
|
||
y = mouseY - tooltipHeight - 20
|
||
// 如果上方也放不下,就放在图表顶部,留出边距
|
||
if (y < 0) {
|
||
y = 10 // 固定在顶部,留10px边距
|
||
}
|
||
}
|
||
// 最终检查:确保整个tooltip都在可视区域内
|
||
y = Math.max(5, Math.min(y, chartHeight - tooltipHeight - 5))
|
||
return [x, y]
|
||
}
|
||
},
|
||
grid: {
|
||
left: '2%',
|
||
right: '5%',
|
||
bottom: '8%',
|
||
top: '7%',
|
||
containLabel: true
|
||
},
|
||
xAxis: {
|
||
type: 'time',
|
||
axisLine: { show: false, lineStyle: { color: '#fff' }},
|
||
axisLabel: {
|
||
show: true,
|
||
color: '#fff',
|
||
fontSize: 15,
|
||
formatter: function(value) {
|
||
const date = new Date(value)
|
||
return `${date.getHours().toString().padStart(2, '0')}:${date
|
||
.getMinutes()
|
||
.toString()
|
||
.padStart(2, '0')}`
|
||
}
|
||
},
|
||
splitLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.1)' }}
|
||
},
|
||
yAxis: {
|
||
type: 'category',
|
||
data: yAxisData,
|
||
axisLine: { show: false, lineStyle: { color: '#fff' }},
|
||
axisLabel: { color: '#fff', fontSize: 15, show: true },
|
||
splitLine: { show: false }
|
||
},
|
||
series: [
|
||
{
|
||
type: 'custom',
|
||
name: '设备状态',
|
||
renderItem: function(params, api) {
|
||
try {
|
||
const categoryIndex = api.value(0)
|
||
const start = api.coord([api.value(1), categoryIndex])
|
||
const end = api.coord([api.value(2), categoryIndex])
|
||
if (!start || !end || start[0] === end[0]) {
|
||
return null
|
||
}
|
||
const height = api.size([0, 1])[1] * 0.6
|
||
return {
|
||
type: 'rect',
|
||
shape: {
|
||
x: start[0],
|
||
y: start[1] - height / 2,
|
||
width: Math.max(end[0] - start[0], 1),
|
||
height: height
|
||
},
|
||
style: api.style()
|
||
}
|
||
} catch (error) {
|
||
return null
|
||
}
|
||
},
|
||
encode: {
|
||
x: [1, 2],
|
||
y: 0
|
||
},
|
||
data: seriesData
|
||
}
|
||
]
|
||
}
|
||
|
||
this.deviceStatusChart.setOption(option, true)
|
||
},
|
||
// 每分钟刷新一次工单加工时长统计
|
||
startWorkOrderRefresh() {
|
||
if (this.workOrderRefreshTimer) {
|
||
clearInterval(this.workOrderRefreshTimer)
|
||
this.workOrderRefreshTimer = null
|
||
}
|
||
this.workOrderRefreshTimer = setInterval(() => {
|
||
this.loadWorkOrderData()
|
||
}, 60000)
|
||
},
|
||
loadWorkOrderData() {
|
||
if (this.workOrderLoading) return
|
||
if (!this.workOrderChart) return
|
||
|
||
const param = []
|
||
const data = this.CreateData('11', '历史_工位设备状态每分钟记录_工单查询', param)
|
||
this.workOrderLoading = true
|
||
this.ExecDatabase(data)
|
||
.then((response) => {
|
||
const list = response && response.data ? response.data : null
|
||
if (!Array.isArray(list) || list.length === 0) {
|
||
this.workOrderNames = []
|
||
this.workOrderHours = []
|
||
this.drawWorkOrderChart()
|
||
return
|
||
}
|
||
|
||
const names = []
|
||
const hours = []
|
||
list.forEach((item) => {
|
||
const orderNo = item && (item['订单号'] != null ? String(item['订单号']) : '')
|
||
const workTime = item && (item['工作时长'] != null ? Number(item['工作时长']) : 0)
|
||
if (orderNo) {
|
||
names.push(orderNo)
|
||
hours.push(Number.isFinite(workTime) ? workTime : 0)
|
||
}
|
||
})
|
||
|
||
this.workOrderNames = names
|
||
this.workOrderHours = hours
|
||
this.drawWorkOrderChart()
|
||
})
|
||
.catch((error) => {
|
||
console.error('工单加工时长统计数据获取失败', error)
|
||
this.workOrderNames = []
|
||
this.workOrderHours = []
|
||
this.drawWorkOrderChart()
|
||
})
|
||
.finally(() => {
|
||
this.workOrderLoading = false
|
||
})
|
||
},
|
||
drawWorkOrderChart() {
|
||
if (!this.workOrderChart) return
|
||
|
||
const names = this.workOrderNames || []
|
||
const hours = this.workOrderHours || []
|
||
|
||
this.workOrderChart.clear()
|
||
|
||
if (!names.length || !hours.length) {
|
||
this.workOrderChart.setOption({
|
||
backgroundColor: 'transparent',
|
||
xAxis: { show: false, type: 'category', data: [] },
|
||
yAxis: { show: false, type: 'value' },
|
||
series: [],
|
||
graphic: {
|
||
type: 'text',
|
||
left: 'center',
|
||
top: 'middle',
|
||
style: {
|
||
text: '未查询到工单数据',
|
||
fill: '#fff',
|
||
fontSize: 16,
|
||
fontFamily: 'Microsoft YaHei'
|
||
}
|
||
}
|
||
}, true)
|
||
return
|
||
}
|
||
|
||
const maxAxis = this.getMaintenanceAxisMax(hours)
|
||
const buildBarGradient = (startColor, middleColor, endColor) => ({
|
||
type: 'linear',
|
||
x: 0,
|
||
y: 0,
|
||
x2: 1,
|
||
y2: 0,
|
||
colorStops: [
|
||
{ offset: 0, color: startColor },
|
||
{ offset: 0.55, color: middleColor },
|
||
{ offset: 1, color: endColor }
|
||
]
|
||
})
|
||
|
||
const option = {
|
||
backgroundColor: 'transparent',
|
||
animation: false,
|
||
tooltip: {
|
||
trigger: 'axis',
|
||
axisPointer: {
|
||
type: 'shadow'
|
||
},
|
||
backgroundColor: 'rgba(0, 0, 0, 0.8)',
|
||
textStyle: { color: '#fff' },
|
||
borderColor: 'rgba(255, 255, 255, 0.3)',
|
||
borderWidth: 1,
|
||
formatter: function(params) {
|
||
const p = params && params[0]
|
||
if (!p) return ''
|
||
return `${p.name}<br/>工作时长: ${p.value}分钟`
|
||
}
|
||
},
|
||
grid: {
|
||
left: '2%',
|
||
right: '8%',
|
||
bottom: '12%',
|
||
top: '8%',
|
||
containLabel: true
|
||
},
|
||
xAxis: {
|
||
type: 'value',
|
||
max: maxAxis,
|
||
interval: maxAxis / 5,
|
||
axisLabel: {
|
||
color: '#ffffff',
|
||
formatter: '{value}m'
|
||
},
|
||
axisLine: {
|
||
show: false
|
||
},
|
||
splitLine: {
|
||
lineStyle: {
|
||
color: 'rgba(255, 255, 255, 0.1)'
|
||
}
|
||
}
|
||
},
|
||
yAxis: {
|
||
type: 'category',
|
||
data: names,
|
||
inverse: true,
|
||
axisLabel: {
|
||
color: '#fff',
|
||
fontSize: 12,
|
||
interval: 0,
|
||
lineHeight: 16,
|
||
align: 'right',
|
||
formatter: function(value) {
|
||
if (!value) return ''
|
||
if (value.length > 8) {
|
||
const firstLine = value.slice(0, 8)
|
||
const secondLine = value.slice(8)
|
||
return `${firstLine}\n${secondLine}`
|
||
}
|
||
return value
|
||
}
|
||
},
|
||
axisLine: {
|
||
show: false
|
||
},
|
||
axisTick: {
|
||
alignWithLabel: true
|
||
}
|
||
},
|
||
series: [
|
||
{
|
||
type: 'bar',
|
||
name: '工作时长',
|
||
data: hours,
|
||
barWidth: 12,
|
||
itemStyle: {
|
||
borderRadius: [0, 2, 2, 0],
|
||
color: buildBarGradient(
|
||
'rgba(143, 201, 255, 0.10)',
|
||
'#4fc3e8',
|
||
'#dff8ff',
|
||
)
|
||
},
|
||
label: {
|
||
show: true,
|
||
position: 'right',
|
||
color: '#dff8ff',
|
||
fontSize: 11,
|
||
formatter: '{c}min'
|
||
}
|
||
}
|
||
]
|
||
}
|
||
|
||
this.workOrderChart.setOption(option, true)
|
||
},
|
||
// 每分钟刷新一次加工中心7日工作时长统计
|
||
startMachiningCenterRefresh() {
|
||
if (this.machiningCenterRefreshTimer) {
|
||
clearInterval(this.machiningCenterRefreshTimer)
|
||
this.machiningCenterRefreshTimer = null
|
||
}
|
||
this.machiningCenterRefreshTimer = setInterval(() => {
|
||
this.loadMachiningCenterHistory()
|
||
}, 60 * 1000)
|
||
},
|
||
initMachiningCenterData() {
|
||
this.machiningCenterDates = []
|
||
this.machiningCenterValues = []
|
||
},
|
||
loadMachiningCenterHistory() {
|
||
if (this.machiningCenterLoading) return
|
||
if (!this.machiningCenterChart) return
|
||
this.machiningCenterLoading = true
|
||
|
||
const param = []
|
||
const data = this.CreateData('11', '历史_工位设备状态每分钟记录_加工中心查询', param)
|
||
this.ExecDatabase(data)
|
||
.then((response) => {
|
||
const list = response && response.data ? response.data : null
|
||
if (!Array.isArray(list) || list.length === 0) {
|
||
this.machiningCenterDates = []
|
||
this.machiningCenterValues = []
|
||
this.drawMachiningCenterChart()
|
||
return
|
||
}
|
||
|
||
const parseDate = (rawDate) => {
|
||
if (rawDate == null) return null
|
||
if (typeof rawDate === 'string') {
|
||
const m = rawDate.match(/^(\d{4})[-\/.](\d{1,2})[-\/.](\d{1,2})/)
|
||
if (m) {
|
||
const mo = String(m[2]).padStart(2, '0')
|
||
const da = String(m[3]).padStart(2, '0')
|
||
return `${mo}-${da}`
|
||
}
|
||
}
|
||
const dt = rawDate instanceof Date ? rawDate : new Date(rawDate)
|
||
if (!dt || Number.isNaN(dt.getTime())) return null
|
||
const mo = String(dt.getMonth() + 1).padStart(2, '0')
|
||
const da = String(dt.getDate()).padStart(2, '0')
|
||
return `${mo}-${da}`
|
||
}
|
||
|
||
const dates = []
|
||
const values = []
|
||
list.forEach((item) => {
|
||
const rawDate = item && (item['日期'] != null ? item['日期'] : item.日期)
|
||
const rawValue = item && (item['工作时长'] != null ? item['工作时长'] : item.工作时长)
|
||
const dateStr = parseDate(rawDate)
|
||
if (!dateStr) return
|
||
const num = Number(rawValue)
|
||
if (!Number.isFinite(num)) return
|
||
dates.push(dateStr)
|
||
values.push(Math.round(num))
|
||
})
|
||
|
||
this.machiningCenterDates = dates
|
||
this.machiningCenterValues = values
|
||
this.drawMachiningCenterChart()
|
||
})
|
||
.catch((err) => {
|
||
console.error('加工中心7日工作时长查询失败', err)
|
||
this.machiningCenterDates = []
|
||
this.machiningCenterValues = []
|
||
this.drawMachiningCenterChart()
|
||
})
|
||
.finally(() => {
|
||
this.machiningCenterLoading = false
|
||
})
|
||
},
|
||
drawMachiningCenterChart() {
|
||
if (!this.machiningCenterChart) return
|
||
const data = this.machiningCenterValues || []
|
||
const dates = this.machiningCenterDates || []
|
||
|
||
this.machiningCenterChart.clear()
|
||
|
||
if (!data.length || !dates.length) {
|
||
const optionEmpty = {
|
||
backgroundColor: 'transparent',
|
||
animation: false,
|
||
xAxis: { show: false, type: 'category', data: [] },
|
||
yAxis: { show: false, type: 'value' },
|
||
series: [],
|
||
graphic: {
|
||
type: 'text',
|
||
left: 'center',
|
||
top: 'middle',
|
||
style: {
|
||
text: '暂无最近七日工作时长',
|
||
fill: '#fff',
|
||
fontSize: 16,
|
||
fontFamily: 'Microsoft YaHei'
|
||
}
|
||
}
|
||
}
|
||
this.machiningCenterChart.setOption(optionEmpty)
|
||
return
|
||
}
|
||
|
||
const option = {
|
||
backgroundColor: 'transparent',
|
||
animation: false,
|
||
grid: {
|
||
top: '18%',
|
||
bottom: '11%',
|
||
left: '18%',
|
||
right: '8%'
|
||
},
|
||
tooltip: {
|
||
trigger: 'axis',
|
||
axisPointer: {
|
||
type: 'shadow'
|
||
},
|
||
formatter: (params) => {
|
||
const p = params && params[0]
|
||
if (!p) return ''
|
||
return `${p.axisValue}<br/>工作时长:${p.value}分钟`
|
||
}
|
||
},
|
||
xAxis: {
|
||
type: 'category',
|
||
data: dates,
|
||
axisLabel: {
|
||
color: '#fff',
|
||
fontSize: 10
|
||
},
|
||
axisLine: {
|
||
show: true,
|
||
lineStyle: { color: '#1B3F66' }
|
||
},
|
||
axisTick: { show: false }
|
||
},
|
||
yAxis: {
|
||
type: 'value',
|
||
axisLabel: {
|
||
color: '#fff',
|
||
fontSize: 10,
|
||
formatter: '{value}分钟'
|
||
},
|
||
axisLine: {
|
||
show: true,
|
||
lineStyle: { color: '#1B3F66' }
|
||
},
|
||
splitLine: {
|
||
lineStyle: { color: 'rgba(27, 63, 102, 0.6)' }
|
||
}
|
||
},
|
||
series: [
|
||
{
|
||
type: 'bar',
|
||
name: '工作时长',
|
||
data: data,
|
||
barWidth: 20,
|
||
itemStyle: {
|
||
borderRadius: [2, 2, 0, 0],
|
||
color: {
|
||
type: 'linear',
|
||
x: 0,
|
||
y: 1,
|
||
x2: 0,
|
||
y2: 0,
|
||
colorStops: [
|
||
{ offset: 0, color: 'rgba(79, 195, 232, 0.10)' },
|
||
{ offset: 0.55, color: '#4fc3e8' },
|
||
{ offset: 1, color: '#dff8ff' }
|
||
]
|
||
}
|
||
},
|
||
label: {
|
||
show: true,
|
||
position: 'top',
|
||
color: '#dff8ff',
|
||
fontSize: 11,
|
||
fontWeight: 'bold',
|
||
formatter: '{c}'
|
||
}
|
||
}
|
||
]
|
||
}
|
||
this.machiningCenterChart.setOption(option)
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
<style>
|
||
HTML {
|
||
overflow: auto;
|
||
}
|
||
|
||
body {
|
||
margin: 0;
|
||
padding: 0;
|
||
width: 1920px;
|
||
height: 1080px;
|
||
overflow: hidden;
|
||
position: relative;
|
||
font-family: MiSans Bold, serif;
|
||
}
|
||
|
||
.route-switch-btn {
|
||
position: absolute;
|
||
left: 0px;
|
||
top: 0px;
|
||
z-index: 9999;
|
||
width: 78px;
|
||
height: 30px;
|
||
background: rgba(5, 24, 55, 0.72);
|
||
color: #dff8ff;
|
||
font-size: 13px;
|
||
line-height: 28px;
|
||
text-align: center;
|
||
cursor: pointer;
|
||
box-shadow: 0 0 12px rgba(64, 205, 255, 0.35);
|
||
}
|
||
|
||
.route-switch-btn:hover {
|
||
background: rgba(18, 80, 130, 0.88);
|
||
color: #ffffff;
|
||
}
|
||
|
||
/*css主要部分的样式*/
|
||
/*定义滚动条宽高及背景,宽高分别对应横竖滚动条的尺寸*/
|
||
::-webkit-scrollbar {
|
||
width: 0px;
|
||
/*对垂直流动条有效*/
|
||
height: 0px;
|
||
/*对水平流动条有效*/
|
||
}
|
||
|
||
/*定义滚动条的轨道颜色、内阴影及圆角*/
|
||
::-webkit-scrollbar-track {
|
||
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
|
||
background-color: rgb(14, 50, 97);
|
||
/*background-color: #ffffff;*/
|
||
border-radius: 2px;
|
||
}
|
||
|
||
/*定义滑块颜色、内阴影及圆角*/
|
||
::-webkit-scrollbar-thumb {
|
||
border-radius: 5px;
|
||
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
|
||
background-color: rgb(10, 35, 82);
|
||
}
|
||
|
||
/*定义两端按钮的样式*/
|
||
::-webkit-scrollbar-button {
|
||
display: none;
|
||
}
|
||
|
||
/*定义右下角汇合处的样式*/
|
||
::-webkit-scrollbar-corner {
|
||
background: #dedede;
|
||
}
|
||
</style>
|
||
<style scoped="scoped">
|
||
.allShow {
|
||
overflow-y: hidden;
|
||
z-index: 100;
|
||
transform-origin: 0 0;
|
||
position: fixed;
|
||
left: 50%;
|
||
top: 50%;
|
||
transition: 0.3s;
|
||
background: url("/png/背景底图.png") no-repeat center center;
|
||
background-size: cover;
|
||
color: white;
|
||
}
|
||
|
||
.daily-output-box {
|
||
width: 100%;
|
||
height: 300px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.left-title-header {
|
||
width: 100%;
|
||
height: 35px;
|
||
background: url("/png/left.png") no-repeat center center;
|
||
background-size: 100% 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
padding-left: 25px;
|
||
box-sizing: border-box;
|
||
}
|
||
.left-title-text{
|
||
font-size: 20px;
|
||
font-family: "Microsoft YaHei", "微软雅黑", "PingFang SC", sans-serif;
|
||
}
|
||
.box-title {
|
||
font-size: 25px;
|
||
font-weight: 700;
|
||
-webkit-background-clip: text;
|
||
background-clip: text;
|
||
color: transparent;
|
||
font-family: "Microsoft YaHei", "微软雅黑", "PingFang SC", sans-serif;
|
||
}
|
||
|
||
.daily-output-body {
|
||
width: 100%;
|
||
height: calc(100% - 35px);
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.daily-output-chart {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.storage-status-box {
|
||
height: 600px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.right-title-header {
|
||
width: 100%;
|
||
height: 35px;
|
||
background: url("/png/right.png") no-repeat center center;
|
||
background-size: 100% 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: flex-end;
|
||
padding-right: 25px;
|
||
box-sizing: border-box;
|
||
}
|
||
.right-title-text{
|
||
font-size: 20px;
|
||
font-family: "Microsoft YaHei", "微软雅黑", "PingFang SC", sans-serif;
|
||
}
|
||
.storage-status-body {
|
||
width: 100%;
|
||
height: calc(100% - 35px);
|
||
box-sizing: border-box;
|
||
padding: 10px;
|
||
}
|
||
|
||
.now-status-body {
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.now-status-scroll-wrapper {
|
||
height: 100%;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.now-status-fixed-row {
|
||
display: grid;
|
||
grid-template-columns: 95px 1fr;
|
||
align-items: center;
|
||
min-height: 42px;
|
||
margin-bottom: 8px;
|
||
padding: 6px 10px;
|
||
box-sizing: border-box;
|
||
background: rgba(0, 0, 0, 0.3);
|
||
border-radius: 6px;
|
||
}
|
||
|
||
.now-status-fixed-label {
|
||
color: #fff;
|
||
font-size: 16px;
|
||
font-family: "Microsoft YaHei", "微软雅黑", "PingFang SC", sans-serif;
|
||
}
|
||
|
||
.now-status-indicators {
|
||
display: grid;
|
||
grid-template-columns: repeat(4, 1fr);
|
||
gap: 6px;
|
||
}
|
||
|
||
.now-status-indicator-item {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
min-width: 0;
|
||
}
|
||
|
||
.now-status-circle {
|
||
width: 15px;
|
||
height: 15px;
|
||
border-radius: 50%;
|
||
display: inline-block;
|
||
background: grey;
|
||
transition: background-color 0.3s ease;
|
||
}
|
||
|
||
.now-status-circle--active {
|
||
box-shadow: 0 0 8px currentColor;
|
||
}
|
||
|
||
.now-status-indicator-label {
|
||
margin-left: 5px;
|
||
color: #fff;
|
||
font-size: 12px;
|
||
white-space: nowrap;
|
||
font-family: "Microsoft YaHei", "微软雅黑", "PingFang SC", sans-serif;
|
||
}
|
||
|
||
.now-status-scroll-container {
|
||
position: relative;
|
||
height: calc(100% - 50px);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.now-status-scroll-wrapper {
|
||
height: 100%;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.now-status-scroll-content {
|
||
width: 100%;
|
||
}
|
||
|
||
.now-status-row {
|
||
display: grid;
|
||
grid-template-columns: 110px 1fr;
|
||
align-items: center;
|
||
height: 40px;
|
||
padding: 5px 10px;
|
||
box-sizing: border-box;
|
||
color: #fff;
|
||
font-size: 15px;
|
||
border-radius: 4px;
|
||
transition: background-color 0.3s;
|
||
}
|
||
|
||
.now-status-row:hover {
|
||
background: rgba(0, 112, 192, 0.2);
|
||
}
|
||
|
||
.now-status-row-label,
|
||
.now-status-row-value {
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
font-family: "Microsoft YaHei", "微软雅黑", "PingFang SC", sans-serif;
|
||
}
|
||
|
||
.realtimeTask-box {
|
||
width: 100%;
|
||
height: 250px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.realtime-task-grid {
|
||
width: 100%;
|
||
height: calc(100% - 35px);
|
||
display: grid;
|
||
grid-template-rows: repeat(3, 1fr);
|
||
row-gap: 8px;
|
||
padding: 10px;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.realtime-task-item {
|
||
min-width: 0;
|
||
display: grid;
|
||
grid-template-columns: 78px 1fr;
|
||
align-items: center;
|
||
padding: 0 14px;
|
||
box-sizing: border-box;
|
||
background: rgba(0, 0, 0, 0.3);
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.realtime-task-label {
|
||
color: #cfe8ff;
|
||
font-size: 16px;
|
||
font-family: "Microsoft YaHei", "微软雅黑", "PingFang SC", sans-serif;
|
||
}
|
||
|
||
.realtime-task-value {
|
||
color: #ffffff;
|
||
font-size: 18px;
|
||
font-weight: 700;
|
||
text-align: right;
|
||
overflow: hidden;
|
||
white-space: nowrap;
|
||
text-overflow: ellipsis;
|
||
font-family: "Microsoft YaHei", "微软雅黑", "PingFang SC", sans-serif;
|
||
}
|
||
|
||
.component-maintenance-box {
|
||
width: 100%;
|
||
height: 350px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.component-maintenance-body {
|
||
width: 100%;
|
||
height: calc(100% - 35px);
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.component-maintenance-chart {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.device-status-box {
|
||
width: 100%;
|
||
height: 300px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.device-status-body {
|
||
width: 100%;
|
||
height: calc(100% - 35px);
|
||
box-sizing: border-box;
|
||
padding: 8px;
|
||
}
|
||
|
||
.device-status-table {
|
||
width: 100%;
|
||
height: 100%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 3px;
|
||
}
|
||
|
||
.device-status-row {
|
||
flex: 1;
|
||
display: grid;
|
||
grid-template-columns: repeat(4, 1fr);
|
||
align-items: center;
|
||
background: rgba(255, 255, 255, 0.12);
|
||
border-radius: 4px;
|
||
box-sizing: border-box;
|
||
font-size: 13px;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.device-status-row--header {
|
||
background: rgba(79, 131, 180, 0.5);
|
||
}
|
||
|
||
.device-status-cell {
|
||
text-align: center;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
font-size: 15px;
|
||
color: #fff;
|
||
font-family: "Microsoft YaHei", "微软雅黑", "PingFang SC", sans-serif;
|
||
}
|
||
|
||
.device-status-pill {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 2px 13px;
|
||
border-radius: 10px;
|
||
font-size: 15px;
|
||
color: #ffffff;
|
||
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.35);
|
||
}
|
||
|
||
.device-status-pill--running {
|
||
background: radial-gradient(circle at center, rgba(56, 136, 17, 0.3) 0%, rgba(56, 136, 17, 1) 100%);
|
||
}
|
||
|
||
.device-status-pill--alarm {
|
||
background: radial-gradient(circle at center, rgba(210, 73, 86, 0.3) 0%, rgba(210, 73, 86, 1) 100%);
|
||
}
|
||
|
||
.device-status-pill--standby {
|
||
background: radial-gradient(circle at center, rgba(204, 121, 0, 0.3) 0%, rgba(204, 121, 0, 1) 100%);
|
||
}
|
||
|
||
.device-status-pill--offline {
|
||
background: radial-gradient(circle at center, rgba(135, 135, 135, 0.3) 0%, rgba(135, 135, 135, 1) 100%);
|
||
}
|
||
|
||
.df-header__right {
|
||
position: absolute;
|
||
top: 30px;
|
||
right: 30px;
|
||
width: 200px;
|
||
}
|
||
|
||
.df-header__timebar {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 20px;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.df-header__time {
|
||
order: 1;
|
||
/* 确保时间在最左侧 */
|
||
}
|
||
|
||
.df-header__sep {
|
||
opacity: 0.85;
|
||
order: 2;
|
||
/* 分隔符在中间 */
|
||
}
|
||
|
||
.df-header__date-wrapper {
|
||
display: flex;
|
||
flex-direction: column;
|
||
/* 垂直排列 */
|
||
align-items: flex-start;
|
||
/* 右对齐 */
|
||
gap: 2px;
|
||
order: 3;
|
||
}
|
||
|
||
.df-header__year {
|
||
font-size: 16px;
|
||
/* 可以稍小一些 */
|
||
line-height: 1.2;
|
||
}
|
||
|
||
.df-header__week {
|
||
font-size: 14px;
|
||
opacity: 0.85;
|
||
}
|
||
|
||
.other-params-label {
|
||
padding: 10px 0;
|
||
}
|
||
|
||
.other-params-value {
|
||
color: #ffcb6f;
|
||
font-size: 20px;
|
||
text-align: center;
|
||
}
|
||
|
||
.background {
|
||
width: 1920px;
|
||
height: 1080px;
|
||
background-color: rgb(8, 31, 72);
|
||
color: white;
|
||
background-size: 100%;
|
||
}
|
||
|
||
/*表格透明写法*/
|
||
::v-deep .el-table,
|
||
.el-table__expanded-cell {
|
||
background-color: transparent;
|
||
}
|
||
|
||
::v-deep .el-table th {
|
||
background-color: transparent !important;
|
||
color: #fefefe;
|
||
}
|
||
|
||
::v-deep .el-table tr {
|
||
background-color: transparent !important;
|
||
}
|
||
|
||
::v-deep .el-table--enable-row-transition .el-table__body td,
|
||
::v-deep.el-table .cell {
|
||
background-color: transparent;
|
||
color: white;
|
||
border: none;
|
||
padding-left: 0 !important;
|
||
padding-right: 0 !important;
|
||
}
|
||
|
||
::v-deep.el-table th>.cell {
|
||
color: rgba(254, 254, 254, 1);
|
||
font-weight: 700;
|
||
}
|
||
|
||
::v-deep .el-table td,
|
||
.el-table th {
|
||
padding: 8px 0;
|
||
}
|
||
|
||
.el-table__body-wrapper::-webkit-scrollbar {
|
||
/*width: 0;宽度为0隐藏*/
|
||
width: 0;
|
||
}
|
||
|
||
.el-table::before {
|
||
/*left: 0;*/
|
||
/*bottom: 0;*/
|
||
width: 100%;
|
||
height: 0;
|
||
}
|
||
|
||
table {
|
||
border-right: 1px solid #d7d7d7;
|
||
border-bottom: 1px solid #d7d7d7;
|
||
text-align: center;
|
||
}
|
||
|
||
table th {
|
||
border-left: 1px solid #d7d7d7;
|
||
border-top: 1px solid #d7d7d7;
|
||
}
|
||
|
||
table td {
|
||
border-left: 1px solid #d7d7d7;
|
||
border-top: 1px solid #d7d7d7;
|
||
}
|
||
|
||
/* 设备运行履历样式 */
|
||
.bb-chart-container {
|
||
width: 100%;
|
||
height: 110px;
|
||
box-sizing: border-box;
|
||
position: relative;
|
||
min-height: 110px;
|
||
overflow: hidden;
|
||
background: rgba(0, 0, 0, 0.3);
|
||
}
|
||
|
||
.bb-chart-container--timeline {
|
||
height: calc(100% - 35px);
|
||
min-height: 0;
|
||
}
|
||
|
||
.bb-chart {
|
||
width: 100% !important;
|
||
height: 100% !important;
|
||
min-height: 110px;
|
||
}
|
||
|
||
.main-content {
|
||
height: 100%;
|
||
width: 100%;
|
||
position: absolute;
|
||
top: 100px;
|
||
}
|
||
.status-timeline-box {
|
||
position: absolute;
|
||
left: 526px;
|
||
top: 732px;
|
||
width: 907px;
|
||
height: 202px;
|
||
overflow: hidden;
|
||
background-image: url("/png/2.png");
|
||
background-size: cover;
|
||
background-repeat: no-repeat;
|
||
}
|
||
|
||
.status-timeline-header {
|
||
width: 36%;
|
||
height: 35px;
|
||
background: url(/png/小标题-6.png) no-repeat center center;
|
||
background-size: 100% 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
box-sizing: border-box;
|
||
margin-top: 5px;
|
||
margin-left: 16px;
|
||
}
|
||
|
||
.bb-chart--status {
|
||
height: 100%;
|
||
width: 100%;
|
||
min-height: 0;
|
||
}
|
||
|
||
.title-png {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
height: 100px;
|
||
width: 100%;
|
||
background-image: url("/png/top1.png");
|
||
background-size: cover;
|
||
background-repeat: no-repeat;
|
||
background-position: center;
|
||
pointer-events: none;
|
||
display: flex;
|
||
justify-content: center;
|
||
}
|
||
|
||
.title-text {
|
||
color: #fff;
|
||
font-size: 37px;
|
||
font-weight: bold;
|
||
text-align: center;
|
||
margin-top: 15px;
|
||
margin-right: 37px;
|
||
font-family: "Microsoft YaHei", "\5FAE\8F6F\96C5\9ED1", "PingFang SC", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||
}
|
||
.leftLine {
|
||
z-index: 30;
|
||
position: absolute;
|
||
width: 54px;
|
||
height: 1020px;
|
||
top: 30px;
|
||
left: 10px;
|
||
margin-top: 20px;
|
||
background-image: url(/png/左边条.png);
|
||
background-size: 100% 100%;
|
||
background-position: center;
|
||
}
|
||
.rightLine {
|
||
z-index: 30;
|
||
position: absolute;
|
||
width: 54px;
|
||
height: 1020px;
|
||
top: 30px;
|
||
right: 10px;
|
||
margin-top: 20px;
|
||
background-image: url(/png/右边条.png);
|
||
background-size: 100% 100%;
|
||
background-position: center;
|
||
}
|
||
.left-box {
|
||
position: absolute;
|
||
top: 0px;
|
||
left: 30px;
|
||
width: 336px;
|
||
background-image: url(/png/1.png);
|
||
background-size: cover;
|
||
background-repeat: no-repeat;
|
||
min-height: 724px;
|
||
}
|
||
|
||
.right-box {
|
||
position: absolute;
|
||
top: 0px;
|
||
right: 30px;
|
||
width: 336px;
|
||
min-height: 724px;
|
||
background-image: url(/png/1.png);
|
||
background-size: cover;
|
||
background-repeat: no-repeat;
|
||
}
|
||
|
||
.button1 {
|
||
position: absolute;
|
||
top: 100px;
|
||
left: 0;
|
||
width: 88px;
|
||
height: 35px;
|
||
background-size: cover;
|
||
background-repeat: no-repeat;
|
||
background-position: center;
|
||
border: none;
|
||
padding: 0;
|
||
cursor: pointer;
|
||
z-index: 20;
|
||
}
|
||
|
||
.cirle {
|
||
pointer-events: none;
|
||
background-image: url(/png/圆环.png);
|
||
position: absolute;
|
||
inset: 0;
|
||
top: 20px;
|
||
background-size: 95% 95%;
|
||
background-repeat: no-repeat;
|
||
background-position: center;
|
||
border: none;
|
||
padding: 0;
|
||
z-index: 1;
|
||
}
|
||
</style>
|